diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..6f32a79
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,48 @@
+name: build
+
+on:
+
+ push:
+ branches:
+ - master
+
+ pull_request:
+ branches:
+ - master
+
+jobs:
+
+ build:
+
+ runs-on: windows-2022
+
+ defaults:
+ run:
+ shell: cmd
+
+ strategy:
+ matrix:
+ arch:
+ - Win32
+ - x64
+
+ steps:
+ - name: Set git to use LF
+ run: |
+ git config --global core.autocrlf false
+ git config --global core.eol lf
+
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup MSVC
+ uses: ilammy/msvc-dev-cmd@v1
+ with:
+ arch: ${{ matrix.arch }}
+
+ - name: Build
+ run: |
+ mkdir build
+ cd build
+ cmake -A ${{ matrix.arch }} -DFOO_SAMPLE=ON ..
+ msbuild -v:m -p:Configuration=Release -p:Platform=${{ matrix.arch }} foosdk.sln
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 00089a9..9ef387a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.10)
project(foosdk)
@@ -13,7 +13,7 @@ option(FOO_SYSTEM_WTL "Use system WTL library" OFF)
option(FOO_SAMPLE "Build foo_sample component" OFF)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4302 /wd4838 /wd4996 /d2notypeopt")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4302 /wd4838 /wd4996 /d2notypeopt")
if(FOO_STATIC_STDLIB)
foreach(FLAGS CMAKE_CXX_FLAGS_DEBUG
diff --git a/README.md b/README.md
index d2da26e..16c3e76 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# foosdk
Foobar2000 SDK with CMake-based build system and few improvements.
-[](https://ci.appveyor.com/project/hyperblast/foosdk)
+[](https://github.com/hyperblast/foosdk/actions/workflows/build.yml)
### Features
- CMake-based build system
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 9fb2a4f..0000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-image: Visual Studio 2022
-
-branches:
- only:
- - master
-
-build:
- project: build.proj
-
-configuration: Release
-
-platform:
-- Win32
-- x64
-
-environment:
- BuildFlags: -DFOO_SAMPLE=ON
-
-test_script: ""
diff --git a/build.proj b/build.proj
deleted file mode 100644
index f6978f5..0000000
--- a/build.proj
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
- Debug
- Win32
- $(MSBuildProjectDirectory)\build
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/cmake/FileLists.cmake b/cmake/FileLists.cmake
index c30465e..cf338ca 100644
--- a/cmake/FileLists.cmake
+++ b/cmake/FileLists.cmake
@@ -47,6 +47,7 @@ set(
sdk/pfc/cpuid.cpp
sdk/pfc/crashWithMessage.cpp
sdk/pfc/filehandle.cpp
+ sdk/pfc/filetimetools.cpp
sdk/pfc/guid.cpp
sdk/pfc/other.cpp
sdk/pfc/pathUtils.cpp
@@ -91,7 +92,9 @@ set(
sdk/pfc/cpuid.h
sdk/pfc/debug.h
sdk/pfc/event.h
+ sdk/pfc/event_std.h
sdk/pfc/filehandle.h
+ sdk/pfc/filetimetools.h
sdk/pfc/fixed_map.h
sdk/pfc/fpu.h
sdk/pfc/guid.h
@@ -126,6 +129,8 @@ set(
sdk/pfc/SmartStrStr-twoCharMappings.h
sdk/pfc/SmartStrStr.h
sdk/pfc/sort.h
+ sdk/pfc/sort2.h
+ sdk/pfc/sortstring.h
sdk/pfc/splitString.h
sdk/pfc/splitString2.h
sdk/pfc/stdsort.h
@@ -184,7 +189,6 @@ set(
sdk/foobar2000/SDK/filesystem.cpp
sdk/foobar2000/SDK/filesystem_helper.cpp
sdk/foobar2000/SDK/foosort.cpp
- sdk/foobar2000/SDK/foosortstring.cpp
sdk/foobar2000/SDK/fsItem.cpp
sdk/foobar2000/SDK/guids.cpp
sdk/foobar2000/SDK/hasher_md5.cpp
@@ -237,10 +241,12 @@ set(
sdk/foobar2000/SDK/audioEncoder.h
sdk/foobar2000/SDK/audio_postprocessor.h
sdk/foobar2000/SDK/autoplaylist.h
+ sdk/foobar2000/SDK/callback_merit.h
sdk/foobar2000/SDK/cfg_var.h
sdk/foobar2000/SDK/cfg_var_legacy.h
sdk/foobar2000/SDK/chapterizer.h
sdk/foobar2000/SDK/commandline.h
+ sdk/foobar2000/SDK/commonObjects-Apple.h
sdk/foobar2000/SDK/commonObjects.h
sdk/foobar2000/SDK/completion_notify.h
sdk/foobar2000/SDK/component_client.h
@@ -353,11 +359,13 @@ set(
sdk/foobar2000/SDK/threadsLite.h
sdk/foobar2000/SDK/timer.h
sdk/foobar2000/SDK/titleformat.h
+ sdk/foobar2000/SDK/titleformat_object.h
sdk/foobar2000/SDK/toolbarDropDown.h
sdk/foobar2000/SDK/track_property.h
sdk/foobar2000/SDK/tracks.h
sdk/foobar2000/SDK/ui_edit_context.h
sdk/foobar2000/SDK/ui_element.h
+ sdk/foobar2000/SDK/ui_element_mac.h
sdk/foobar2000/SDK/ui_element_typable_window_manager.h
sdk/foobar2000/SDK/ui.h
sdk/foobar2000/SDK/unpack.h
@@ -418,6 +426,8 @@ set(
sdk/foobar2000/helpers/audio_render_float.h
sdk/foobar2000/helpers/AutoComplete.h
sdk/foobar2000/helpers/BumpableElem.h
+ sdk/foobar2000/helpers/callback_merit.h
+ sdk/foobar2000/helpers/callInMainThreadHelper.h
sdk/foobar2000/helpers/CDialogResizeHelper.h
sdk/foobar2000/helpers/cfg_dsp_chain_config.h
sdk/foobar2000/helpers/cfg_obj.h
@@ -425,16 +435,18 @@ set(
sdk/foobar2000/helpers/cfg_var_import.h
sdk/foobar2000/helpers/CListControlFb2kColors.h
sdk/foobar2000/helpers/CmdThread.h
+ sdk/foobar2000/helpers/CModelessDialogMessages.h
sdk/foobar2000/helpers/CPropVariant.h
sdk/foobar2000/helpers/CSingleThreadWrapper.h
sdk/foobar2000/helpers/CTableEditHelper-Legacy.h
sdk/foobar2000/helpers/DarkMode.h
sdk/foobar2000/helpers/dsp_dialog.h
sdk/foobar2000/helpers/duration_counter.h
+ sdk/foobar2000/helpers/fb2kWorkerTool.h
sdk/foobar2000/helpers/fb2k_threads.h
sdk/foobar2000/helpers/fb2k_wfx.h
sdk/foobar2000/helpers/fileReadAhead.h
- sdk/foobar2000/helpers/file_readonly.h
+ sdk/foobar2000/helpers/file_streamstub.h
sdk/foobar2000/helpers/foobar2000+atl.h
sdk/foobar2000/helpers/foobar2000-lite+atl.h
sdk/foobar2000/helpers/fullFileBuffer.h
@@ -478,7 +490,9 @@ set(
sdk/foobar2000/helpers/ProcessUtils.h
sdk/foobar2000/helpers/ProfileCache.h
sdk/foobar2000/helpers/readers.h
+ sdk/foobar2000/helpers/readers_lite.h
sdk/foobar2000/helpers/reader_pretend_nonseekable.h
+ sdk/foobar2000/helpers/readWriteLock.h
sdk/foobar2000/helpers/rethrow.h
sdk/foobar2000/helpers/seekabilizer.h
sdk/foobar2000/helpers/StdAfx.h
@@ -518,6 +532,7 @@ set(
sdk/libPPUI/CDialogResizeHelper.cpp
sdk/libPPUI/CPowerRequest.cpp
sdk/libPPUI/DarkMode.cpp
+ sdk/libPPUI/EditBoxFix.cpp
sdk/libPPUI/gdiplus_helpers.cpp
sdk/libPPUI/GDIUtils.cpp
sdk/libPPUI/IDataObjectUtils.cpp
@@ -530,6 +545,7 @@ set(
sdk/libPPUI/TypeFind.cpp
sdk/libPPUI/win32_op.cpp
sdk/libPPUI/win32_utility.cpp
+ sdk/libPPUI/wtl-pp.cpp
)
set(
@@ -568,14 +584,17 @@ set(
sdk/libPPUI/CPropVariant.h
sdk/libPPUI/CWindowCreateAndDelete.h
sdk/libPPUI/CDialogResizeHelper.h
+ sdk/libPPUI/DarkMode-CHyperLink.h
sdk/libPPUI/DarkMode.h
sdk/libPPUI/DarkModeEx.h
+ sdk/libPPUI/EditBoxFixes.h
sdk/libPPUI/gdi-types-portable.h
sdk/libPPUI/gdiplus-helpers-webp.h
sdk/libPPUI/gdiplus_helpers.h
sdk/libPPUI/GDIUtils.h
sdk/libPPUI/gesture.h
sdk/libPPUI/hookWindowMessages.h
+ sdk/libPPUI/HyperLinkCtrl.h
sdk/libPPUI/IDataObjectUtils.h
sdk/libPPUI/ImageEncoder.h
sdk/libPPUI/ImplementOnFinalMessage.h
@@ -606,7 +625,7 @@ set(
SAMPLE_SOURCES
sdk/foobar2000/foo_sample/contextmenu.cpp
sdk/foobar2000/foo_sample/decode.cpp
- sdk/foobar2000/foo_sample/dsp.cpp
+ sdk/foobar2000/foo_sample/dsp_sample.cpp
sdk/foobar2000/foo_sample/initquit.cpp
sdk/foobar2000/foo_sample/input_raw.cpp
sdk/foobar2000/foo_sample/listcontrol-advanced.cpp
@@ -628,6 +647,7 @@ set(
set(
SAMPLE_HEADERS
+ sdk/foobar2000/foo_sample/dsp_sample.h
sdk/foobar2000/foo_sample/playback_stream_capture.h
sdk/foobar2000/foo_sample/resource.h
sdk/foobar2000/foo_sample/stdafx.h
diff --git a/sdk/foobar2000/SDK/abort_callback.cpp b/sdk/foobar2000/SDK/abort_callback.cpp
index 67f62cf..dea973a 100644
--- a/sdk/foobar2000/SDK/abort_callback.cpp
+++ b/sdk/foobar2000/SDK/abort_callback.cpp
@@ -19,7 +19,7 @@ bool abort_callback::sleep_ex(double p_timeout_seconds) const {
return !pfc::event::g_wait_for(get_abort_event(),p_timeout_seconds);
}
-bool abort_callback::waitForEvent( pfc::eventHandle_t evtHandle, double timeOut ) {
+bool abort_callback::waitForEvent( pfc::eventHandle_t evtHandle, double timeOut ) const {
int status = pfc::event::g_twoEventWait( this->get_abort_event(), evtHandle, timeOut );
switch(status) {
case 1: throw exception_aborted();
@@ -33,20 +33,42 @@ bool abort_callback_usehandle::is_aborting() const {
return pfc::event::g_wait_for( get_abort_event(), 0 );
}
-bool abort_callback::waitForEvent(pfc::event& evt, double timeOut) {
+bool abort_callback::waitForEvent(pfc::event& evt, double timeOut) const {
return waitForEvent(evt.get_handle(), timeOut);
}
-void abort_callback::waitForEvent(pfc::eventHandle_t evtHandle) {
+void abort_callback::waitForEvent(pfc::eventHandle_t evtHandle) const {
bool status = waitForEvent(evtHandle, -1); (void)status;
PFC_ASSERT(status); // should never return false
}
-void abort_callback::waitForEvent(pfc::event& evt) {
+void abort_callback::waitForEvent(pfc::event& evt) const {
bool status = waitForEvent(evt, -1); (void)status;
PFC_ASSERT(status); // should never return false
}
+bool abort_callback::waitForEventNoThrow(pfc::eventHandle_t evtHandle) const {
+ int status = pfc::event::g_twoEventWait(this->get_abort_event(), evtHandle, -1);
+ switch (status) {
+ case 1: return false;
+ case 2: return true;
+ default: uBugCheck();
+ }
+}
+
+bool abort_callback::waitForEventNoThrow(pfc::event& evt) const {
+ return waitForEventNoThrow(evt.get_handle());
+}
+
namespace fb2k {
abort_callback_dummy noAbort;
}
+
+abort_callback_event abort_callback_clone::clone(abort_callback_event arg) {
+ return pfc::fileHandleDup(arg);
+}
+
+void abort_callback_clone::close(abort_callback_event arg) {
+ return pfc::fileHandleClose(arg);
+}
+
diff --git a/sdk/foobar2000/SDK/abort_callback.h b/sdk/foobar2000/SDK/abort_callback.h
index 37ba34f..f2519d5 100644
--- a/sdk/foobar2000/SDK/abort_callback.h
+++ b/sdk/foobar2000/SDK/abort_callback.h
@@ -38,18 +38,25 @@ class NOVTABLE abort_callback
void sleep(double p_timeout_seconds) const;
//! Sleeps p_timeout_seconds or less when aborted, returns true when execution should continue, false when not.
bool sleep_ex(double p_timeout_seconds) const;
+ bool sleepNoThrow(double p_timeout_seconds) const { return sleep_ex(p_timeout_seconds); }
//! Waits for an event. Returns true if event is now signaled, false if the specified period has elapsed and the event did not become signaled. \n
//! Throws exception_aborted if aborted.
- bool waitForEvent( pfc::eventHandle_t evtHandle, double timeOut );
+ bool waitForEvent( pfc::eventHandle_t evtHandle, double timeOut ) const;
//! Waits for an event. Returns true if event is now signaled, false if the specified period has elapsed and the event did not become signaled. \n
//! Throws exception_aborted if aborted.
- bool waitForEvent(pfc::event& evt, double timeOut);
+ bool waitForEvent(pfc::event& evt, double timeOut) const;
//! Waits for an event. Returns once the event became signaled; throw exception_aborted if abort occurred first.
- void waitForEvent(pfc::eventHandle_t evtHandle);
+ void waitForEvent(pfc::eventHandle_t evtHandle) const;
//! Waits for an event. Returns once the event became signaled; throw exception_aborted if abort occurred first.
- void waitForEvent(pfc::event& evt);
+ void waitForEvent(pfc::event& evt) const;
+
+ bool waitForEventNoThrow(pfc::eventHandle_t evt) const;
+ bool waitForEventNoThrow(pfc::event& evt) const;
+
+ abort_callback( const abort_callback & ) = delete;
+ void operator=( const abort_callback & ) = delete;
protected:
abort_callback() {}
~abort_callback() {}
@@ -57,49 +64,59 @@ class NOVTABLE abort_callback
-//! Implementation of abort_callback interface.
+//! Standard implementation of abort_callback interface.
class abort_callback_impl : public abort_callback {
public:
- abort_callback_impl() : m_aborting(false) {}
+ abort_callback_impl() {}
inline void abort() {set_state(true);}
inline void set() {set_state(true);}
inline void reset() {set_state(false);}
void set_state(bool p_state) {m_aborting = p_state; m_event.set_state(p_state);}
- bool is_aborting() const {return m_aborting;}
+ bool is_aborting() const override {return m_aborting;}
- abort_callback_event get_abort_event() const {return m_event.get_handle();}
+ abort_callback_event get_abort_event() const override {return m_event.get_handle();}
private:
abort_callback_impl(const abort_callback_impl &) = delete;
const abort_callback_impl & operator=(const abort_callback_impl&) = delete;
- volatile bool m_aborting;
+ volatile bool m_aborting = false;
pfc::event m_event;
};
-
+//! Alternate abort_callback implementation, supply your own event handle to signal abort. \n
+//! Slightly less efficient (is_aborting() polls the event instead of reading a bool variable).
class abort_callback_usehandle : public abort_callback {
public:
abort_callback_usehandle( abort_callback_event handle ) : m_handle(handle) {}
- bool is_aborting() const;
- abort_callback_event get_abort_event() const { return m_handle; }
-private:
+ bool is_aborting() const override;
+ abort_callback_event get_abort_event() const override { return m_handle; }
+protected:
const abort_callback_event m_handle;
};
-
+
+class abort_callback_clone : public abort_callback_usehandle {
+public:
+ abort_callback_clone(abort_callback_event handle) : abort_callback_usehandle(clone(handle)) {}
+ abort_callback_clone(abort_callback & arg) : abort_callback_usehandle(clone(arg.get_handle())) {}
+ ~abort_callback_clone() { close(m_handle); }
+
+ static abort_callback_event clone(abort_callback_event);
+ static void close(abort_callback_event);
+};
+
//! Dummy abort_callback that never gets aborted. \n
-//! Slightly more efficient than the regular one especially when you need to regularly create temporary instances of it.
+//! Note that there's no need to create instances of it, use shared fb2k::noAbort object instead.
class abort_callback_dummy : public abort_callback {
public:
- abort_callback_dummy() : m_event(GetInfiniteWaitEvent()) {}
- bool is_aborting() const { return false; }
+ bool is_aborting() const override { return false; }
- abort_callback_event get_abort_event() const { return m_event;}
+ abort_callback_event get_abort_event() const override { return m_event;}
private:
- const abort_callback_event m_event;
+ const abort_callback_event m_event = GetInfiniteWaitEvent();
};
}
@@ -117,6 +134,7 @@ using namespace foobar2000_io;
namespace fb2k {
- // A shared abort_callback_dummy instance
+ //! A shared abort_callback_dummy instance. \n
+ //! Use when some function requires an abort_callback& and you don't have one: somefunc(fb2k::noAbort);
extern abort_callback_dummy noAbort;
}
diff --git a/sdk/foobar2000/SDK/advconfig.cpp b/sdk/foobar2000/SDK/advconfig.cpp
index 81509c6..e423eed 100644
--- a/sdk/foobar2000/SDK/advconfig.cpp
+++ b/sdk/foobar2000/SDK/advconfig.cpp
@@ -69,6 +69,7 @@ bool advconfig_entry_checkbox_impl::get_state_() const {
#ifdef FOOBAR2000_HAVE_CFG_VAR_LEGACY
void advconfig_entry_checkbox_impl::set_data_raw(stream_reader* p_stream, t_size p_sizehint, abort_callback& p_abort) {
+ (void)p_sizehint;
uint8_t v;
if (p_stream->read(&v, 1, p_abort) == 1) {
set_state(v != 0);
@@ -76,10 +77,6 @@ void advconfig_entry_checkbox_impl::set_data_raw(stream_reader* p_stream, t_size
}
#endif
-pfc::string8 fb2k::advconfig_autoName(const GUID& id) {
- return pfc::format("advconfig.unnamed.", pfc::print_guid(id));
-}
-
void advconfig_entry_string_impl::reset() {
fb2k::configStore::get()->deleteConfigString(m_varName);
}
@@ -97,6 +94,7 @@ void advconfig_entry_string_impl::set_state(const char* p_string, t_size p_lengt
#ifdef FOOBAR2000_HAVE_CFG_VAR_LEGACY
void advconfig_entry_string_impl::set_data_raw(stream_reader* p_stream, t_size p_sizehint, abort_callback& p_abort) {
+ (void)p_sizehint;
pfc::string8_fastalloc temp;
p_stream->read_string_raw(temp, p_abort);
this->set_state(temp);
diff --git a/sdk/foobar2000/SDK/advconfig.h b/sdk/foobar2000/SDK/advconfig.h
index 12da216..714043d 100644
--- a/sdk/foobar2000/SDK/advconfig.h
+++ b/sdk/foobar2000/SDK/advconfig.h
@@ -24,21 +24,22 @@ class NOVTABLE advconfig_entry : public service_base {
static const GUID guid_root;
static const GUID guid_branch_tagging,guid_branch_decoding,guid_branch_tools,guid_branch_playback,guid_branch_display,guid_branch_debug, guid_branch_tagging_general, guid_branch_converter;
+
// \since 2.0
- static const GUID guid_branch_vis;
+ static const GUID guid_branch_vis, guid_branch_general;
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(advconfig_entry);
};
-//! Creates a new branch in Advanced Preferences. \n
+//! Declares a new branch in Advanced Preferences. \n
//! Implementation: see advconfig_branch_impl / advconfig_branch_factory.
class NOVTABLE advconfig_branch : public advconfig_entry {
public:
FB2K_MAKE_SERVICE_INTERFACE(advconfig_branch,advconfig_entry);
};
-//! Creates a checkbox/radiocheckbox entry in Advanced Preferences. \n
+//! Declares a checkbox/radiocheckbox entry in Advanced Preferences. \n
//! The difference between checkboxes and radiocheckboxes is different icon (obviously) and that checking a radiocheckbox unchecks all other radiocheckboxes in the same branch. \n
//! Implementation: see advconfig_entry_checkbox_impl / advconfig_checkbox_factory_t.
class NOVTABLE advconfig_entry_checkbox : public advconfig_entry {
@@ -52,6 +53,7 @@ class NOVTABLE advconfig_entry_checkbox : public advconfig_entry {
FB2K_MAKE_SERVICE_INTERFACE(advconfig_entry_checkbox,advconfig_entry);
};
+//! Extension to advconfig_entry_checkbox, adds default state and preferences flags.
class NOVTABLE advconfig_entry_checkbox_v2 : public advconfig_entry_checkbox {
FB2K_MAKE_SERVICE_INTERFACE(advconfig_entry_checkbox_v2, advconfig_entry_checkbox)
public:
@@ -59,7 +61,7 @@ class NOVTABLE advconfig_entry_checkbox_v2 : public advconfig_entry_checkbox {
virtual t_uint32 get_preferences_flags() {return 0;} //signals whether changing this setting should trigger playback restart or app restart; see: preferences_state::* constants
};
-//! Creates a string/integer editbox entry in Advanced Preferences.\n
+//! Declares a string/integer editbox entry in Advanced Preferences.\n
//! Implementation: see advconfig_entry_string_impl / advconfig_string_factory.
class NOVTABLE advconfig_entry_string : public advconfig_entry {
public:
@@ -69,31 +71,23 @@ class NOVTABLE advconfig_entry_string : public advconfig_entry {
void get_default_state_(pfc::string_base & out);
- enum {
- flag_is_integer = 1 << 0,
- flag_is_signed = 1 << 1,
- };
+ static constexpr uint32_t
+ flag_is_integer = 1 << 0,
+ flag_is_signed = 1 << 1,
+ // Since 2.2: hint to treat these fields as file/folder paths, providing hints if suitable
+ flag_is_file_path = 1 << 2,
+ flag_is_folder_path = 1 << 3,
+ // Since 2.2: multiple values, semicolon delimited
+ flag_semicolon_delimited = 1 << 4;
FB2K_MAKE_SERVICE_INTERFACE(advconfig_entry_string,advconfig_entry);
};
+//! Extension to advconfig_entry_string, adds default state, validation and preferences flags.
class NOVTABLE advconfig_entry_string_v2 : public advconfig_entry_string {
FB2K_MAKE_SERVICE_INTERFACE(advconfig_entry_string_v2, advconfig_entry_string)
public:
virtual void get_default_state(pfc::string_base & out) = 0;
- virtual void validate(pfc::string_base & val) {}
+ virtual void validate(pfc::string_base& val) { (void)val; }
virtual t_uint32 get_preferences_flags() {return 0;} //signals whether changing this setting should trigger playback restart or app restart; see: preferences_state::* constants
};
-
-
-//! Not currently used, reserved for future use.
-class NOVTABLE advconfig_entry_enum : public advconfig_entry {
-public:
- virtual t_size get_value_count() = 0;
- virtual void enum_value(pfc::string_base & p_out,t_size p_index) = 0;
- virtual t_size get_state() = 0;
- virtual void set_state(t_size p_value) = 0;
-
- FB2K_MAKE_SERVICE_INTERFACE(advconfig_entry_enum,advconfig_entry);
-};
-
diff --git a/sdk/foobar2000/SDK/advconfig_impl.h b/sdk/foobar2000/SDK/advconfig_impl.h
index 8079f81..6e8065e 100644
--- a/sdk/foobar2000/SDK/advconfig_impl.h
+++ b/sdk/foobar2000/SDK/advconfig_impl.h
@@ -1,5 +1,7 @@
#pragma once
+// advconfig_impl.h : mainline (foobar2000 v2.0) implementation of advconfig objects
+
#include "advconfig.h"
//! Standard implementation of advconfig_branch. \n
@@ -29,6 +31,7 @@ class advconfig_branch_impl : public advconfig_branch {
namespace fb2k {
pfc::string8 advconfig_autoName(const GUID& id);
+ pfc::string8 advconfig_autoName(const GUID& id, const char * specified);
}
@@ -102,9 +105,12 @@ class advconfig_checkbox_factory_t : public advconfig_checkbox_factory_(p_name, varName, p_guid, p_parent, p_priority, p_initialstate, p_prefFlags) {}
advconfig_string_factory(const char* p_name, const GUID& p_guid, const GUID& p_parent, double p_priority, const char* p_initialstate, t_uint32 p_prefFlags = 0) : service_factory_single_t(p_name, fb2k::advconfig_autoName(p_guid), p_guid, p_parent, p_priority, p_initialstate, p_prefFlags) {}
+ advconfig_string_factory(advconfig_entry_string_desc const& arg) : service_factory_single_t(arg) {}
void get(pfc::string_base& out) { get_static_instance().get_state(out); }
+ pfc::string8 get() { pfc::string8 temp; get(temp); return temp; }
void set(const char* in) { get_static_instance().set_state(in); }
};
diff --git a/sdk/foobar2000/SDK/advconfig_impl_legacy.h b/sdk/foobar2000/SDK/advconfig_impl_legacy.h
index b20e2c0..ce136e4 100644
--- a/sdk/foobar2000/SDK/advconfig_impl_legacy.h
+++ b/sdk/foobar2000/SDK/advconfig_impl_legacy.h
@@ -1,8 +1,22 @@
#pragma once
+// advconfig_impl_legacy.h : legacy (foobar2000 v1.x compatible) implementation of advconfig objects
+
#include "cfg_var_legacy.h"
using namespace cfg_var_legacy;
+namespace fb2k {
+ pfc::string8 advconfig_autoName(const GUID&);
+}
+
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+#define ADVCONFIG_DOWNGRADE { this->get_static_instance().downgrade_set_name(configStoreName); }
+#define ADVCONFIG_DOWNGRADE_AUTO { this->get_static_instance().downgrade_set_name( fb2k::advconfig_autoName(p_guid) ); }
+#else
+#define ADVCONFIG_DOWNGRADE {(void)configStoreName;}
+#define ADVCONFIG_DOWNGRADE_AUTO
+#endif
+
//! Standard implementation of advconfig_entry_checkbox. \n
class advconfig_entry_checkbox_impl : public advconfig_entry_checkbox_v2 {
public:
@@ -22,6 +36,9 @@ class advconfig_entry_checkbox_impl : public advconfig_entry_checkbox_v2 {
bool get_state_() const { return m_state; }
bool get_default_state_() const { return m_initialstate; }
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+ void downgrade_set_name(const char * arg) { m_state.downgrade_set_name(arg); }
+#endif
private:
const pfc::string8 m_name;
const bool m_initialstate;
@@ -53,14 +70,26 @@ class advconfig_checkbox_factory_common : public service_factory_single_t
class advconfig_checkbox_factory_t : public advconfig_checkbox_factory_common {
public:
advconfig_checkbox_factory_t(const char* p_name, const GUID& p_guid, const GUID& p_parent, double p_priority, bool p_initialstate)
- : advconfig_checkbox_factory_common(p_name, p_guid, p_parent, p_priority, p_initialstate, p_is_radio, prefFlags) {}
-};
-
-
-//! Standard advconfig_entry_string implementation. Use advconfig_string_factory to register your own string entries in Advanced Preferences instead of using this class directly.
-class advconfig_entry_string_impl : public advconfig_entry_string_v2 {
-public:
- advconfig_entry_string_impl(const char* p_name, const GUID& p_guid, const GUID& p_parent, double p_priority, const char* p_initialstate, t_uint32 p_prefFlags)
- : m_name(p_name), m_parent(p_parent), m_priority(p_priority), m_initialstate(p_initialstate), m_state(p_guid, p_initialstate), m_prefFlags(p_prefFlags) {}
- void get_name(pfc::string_base& p_out) { p_out = m_name; }
- GUID get_guid() { return m_state.get_guid(); }
- GUID get_parent() { return m_parent; }
- void reset() { core_api::ensure_main_thread(); m_state = m_initialstate; }
- double get_sort_priority() { return m_priority; }
- void get_state(pfc::string_base& p_out) { core_api::ensure_main_thread(); p_out = m_state; }
- void set_state(const char* p_string, t_size p_length = ~0) { core_api::ensure_main_thread(); m_state.set_string(p_string, p_length); }
- t_uint32 get_flags() { return 0; }
- void get_default_state(pfc::string_base& out) { out = m_initialstate; }
- t_uint32 get_preferences_flags() { return m_prefFlags; }
-private:
- const pfc::string8 m_initialstate, m_name;
- cfg_string m_state;
- const double m_priority;
- const GUID m_parent;
- const t_uint32 m_prefFlags;
-};
-
-//! Service factory helper around standard advconfig_entry_string implementation. Use this class to register your own string entries in Advanced Preferences. \n
-//! Usage: static advconfig_string_factory mystring(name, itemID, branchID, priority, initialValue);
-class advconfig_string_factory : public service_factory_single_t {
-public:
- advconfig_string_factory(const char* p_name, const GUID& p_guid, const GUID& p_parent, double p_priority, const char* p_initialstate, t_uint32 p_prefFlags = 0)
- : service_factory_single_t(p_name, p_guid, p_parent, p_priority, p_initialstate, p_prefFlags) {}
-
- void get(pfc::string_base& out) { get_static_instance().get_state(out); }
- void set(const char* in) { get_static_instance().set_state(in); }
+ : advconfig_checkbox_factory_common(p_name, p_guid, p_parent, p_priority, p_initialstate, p_is_radio, prefFlags) {
+ ADVCONFIG_DOWNGRADE_AUTO;
+ }
+ advconfig_checkbox_factory_t(const char* p_name, const char * configStoreName, const GUID& p_guid, const GUID& p_parent, double p_priority, bool p_initialstate)
+ : advconfig_checkbox_factory_common(p_name, p_guid, p_parent, p_priority, p_initialstate, p_is_radio, prefFlags) {
+ ADVCONFIG_DOWNGRADE;
+ }
};
-
//! Special advconfig_entry_string implementation - implements integer entries. Use advconfig_integer_factory to register your own integer entries in Advanced Preferences instead of using this class directly.
template
class advconfig_entry_integer_impl_ : public advconfig_entry_string_v2 {
@@ -132,9 +131,12 @@ class advconfig_entry_integer_impl_ : public advconfig_entry_string_v2 {
format(out, m_initval);
}
void validate(pfc::string_base& val) {
- format(val, pfc::clip_t(myATOI(val, ~0), m_min, m_max));
+ format(val, pfc::clip_t(myATOI(val, SIZE_MAX), m_min, m_max));
}
t_uint32 get_preferences_flags() { return m_prefFlags; }
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+ void downgrade_set_name(const char * arg) { m_state.downgrade_set_name(arg); }
+#endif
private:
static void format(pfc::string_base& out, int_t v) {
if (is_signed()) out = pfc::format_int(v).get_ptr();
@@ -164,7 +166,14 @@ class advconfig_integer_factory_ : public service_factory_single_t >(p_name, p_guid, p_parent, p_priority, p_initialstate, p_min, p_max, p_prefFlags) {}
+ : service_factory_single_t >(p_name, p_guid, p_parent, p_priority, p_initialstate, p_min, p_max, p_prefFlags) {
+ ADVCONFIG_DOWNGRADE_AUTO;
+ }
+
+ advconfig_integer_factory_(const char* p_name, const char * configStoreName, const GUID& p_guid, const GUID& p_parent, double p_priority, t_uint64 p_initialstate, t_uint64 p_min, t_uint64 p_max, t_uint32 p_prefFlags = 0)
+ : service_factory_single_t >(p_name, p_guid, p_parent, p_priority, p_initialstate, p_min, p_max, p_prefFlags) {
+ ADVCONFIG_DOWNGRADE;
+ }
int_t get() const { return this->get_static_instance().get_state_int(); }
void set(int_t val) { this->get_static_instance().set_state_int(val); }
@@ -177,10 +186,10 @@ typedef advconfig_integer_factory_ advconfig_integer_factory;
typedef advconfig_integer_factory_ advconfig_signed_integer_factory;
-//! Special version if advconfig_entry_string_impl that allows the value to be retrieved from worker threads.
-class advconfig_entry_string_impl_MT : public advconfig_entry_string_v2 {
+//! Standard advconfig_entry_string implementation
+class advconfig_entry_string_impl : public advconfig_entry_string_v2 {
public:
- advconfig_entry_string_impl_MT(const char* p_name, const GUID& p_guid, const GUID& p_parent, double p_priority, const char* p_initialstate, t_uint32 p_prefFlags)
+ advconfig_entry_string_impl(const char* p_name, const GUID& p_guid, const GUID& p_parent, double p_priority, const char* p_initialstate, t_uint32 p_prefFlags)
: m_name(p_name), m_parent(p_parent), m_priority(p_priority), m_initialstate(p_initialstate), m_state(p_guid, p_initialstate), m_prefFlags(p_prefFlags) {}
void get_name(pfc::string_base& p_out) { p_out = m_name; }
GUID get_guid() { return m_state.get_guid(); }
@@ -194,13 +203,16 @@ class advconfig_entry_string_impl_MT : public advconfig_entry_string_v2 {
inReadSync(m_sync);
p_out = m_state;
}
- void set_state(const char* p_string, t_size p_length = ~0) {
+ void set_state(const char* p_string, t_size p_length = SIZE_MAX) {
inWriteSync(m_sync);
m_state.set_string(p_string, p_length);
}
t_uint32 get_flags() { return 0; }
void get_default_state(pfc::string_base& out) { out = m_initialstate; }
t_uint32 get_preferences_flags() { return m_prefFlags; }
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+ void downgrade_set_name(const char * arg) { m_state.downgrade_set_name(arg); }
+#endif
private:
const pfc::string8 m_initialstate, m_name;
cfg_string m_state;
@@ -210,17 +222,26 @@ class advconfig_entry_string_impl_MT : public advconfig_entry_string_v2 {
const t_uint32 m_prefFlags;
};
-//! Special version if advconfig_string_factory that allows the value to be retrieved from worker threads.
-class advconfig_string_factory_MT : public service_factory_single_t {
+//! Service factory helper around standard advconfig_entry_string implementation. Use this class to register your own string entries in Advanced Preferences. \n
+//! Usage: static advconfig_string_factory mystring(name, itemID, branchID, priority, initialValue);
+class advconfig_string_factory : public service_factory_single_t {
public:
- advconfig_string_factory_MT(const char* p_name, const GUID& p_guid, const GUID& p_parent, double p_priority, const char* p_initialstate, t_uint32 p_prefFlags = 0)
- : service_factory_single_t(p_name, p_guid, p_parent, p_priority, p_initialstate, p_prefFlags) {}
+ advconfig_string_factory(const char* p_name, const GUID& p_guid, const GUID& p_parent, double p_priority, const char* p_initialstate, t_uint32 p_prefFlags = 0)
+ : service_factory_single_t(p_name, p_guid, p_parent, p_priority, p_initialstate, p_prefFlags) {
+ ADVCONFIG_DOWNGRADE_AUTO;
+ }
+ advconfig_string_factory(const char* p_name, const char * configStoreName, const GUID& p_guid, const GUID& p_parent, double p_priority, const char* p_initialstate, t_uint32 p_prefFlags = 0)
+ : service_factory_single_t(p_name, p_guid, p_parent, p_priority, p_initialstate, p_prefFlags) {
+ ADVCONFIG_DOWNGRADE;
+ }
void get(pfc::string_base& out) { get_static_instance().get_state(out); }
void set(const char* in) { get_static_instance().set_state(in); }
};
-
+// No more separate _MT versions, readWriteLock overhead is irrelevant
+typedef advconfig_entry_string_impl advconfig_entry_string_impl_MT;
+typedef advconfig_string_factory advconfig_string_factory_MT;
/*
@@ -235,3 +256,7 @@ class advconfig_string_factory_MT : public service_factory_single_t album_art_extractor_instance::query_image_(const GUID & what, abort_callback& a) {
@@ -26,7 +26,7 @@ service_ptr_t album_art_extractor_instance::query_image_(const GUID
}
bool album_art_extractor_instance::have_entry(const GUID & what, abort_callback & abort) {
- try { query(what, abort); return true; } catch(exception_album_art_not_found) { return false; }
+ try { query(what, abort); return true; } catch(exception_album_art_not_found const &) { return false; }
}
void album_art_editor_instance::remove_all_() {
@@ -37,8 +37,8 @@ void album_art_editor_instance::remove_all_() {
for( size_t walk = 0; walk < album_art_ids::num_types(); ++ walk ) {
try {
this->remove( album_art_ids::query_type( walk ) );
- } catch(exception_io_data) {}
- catch(exception_album_art_not_found) {}
+ } catch(exception_io_data const &) {}
+ catch(exception_album_art_not_found const &) {}
}
}
}
@@ -107,7 +107,7 @@ album_art_extractor_instance_ptr album_art_extractor::g_open(file_ptr p_filehint
album_art_extractor_instance_ptr album_art_extractor::g_open_allowempty(file_ptr p_filehint,const char * p_path,abort_callback & p_abort) {
try {
return g_open(p_filehint, p_path, p_abort);
- } catch(exception_album_art_not_found) {
+ } catch(exception_album_art_not_found const &) {
return new service_impl_t();
}
}
@@ -173,6 +173,12 @@ const char * album_art_ids::query_name(size_t idx) {
return names[idx];
}
+const char* album_art_ids::name_of_ex(const GUID& id, const char* def) {
+ auto ret = name_of(id);
+ if ( ret == nullptr ) ret = def;
+ return ret;
+}
+
const char * album_art_ids::name_of(const GUID & id) {
for( size_t w = 0; w < num_types(); ++w ) {
if ( query_type(w) == id ) return query_name(w);
@@ -192,6 +198,13 @@ const char * album_art_ids::capitalized_name_of( const GUID & id) {
return nullptr;
}
+GUID album_art_ids::by_name(const char* arg) {
+ for (size_t w = 0; w < num_types(); ++w) {
+ if (pfc::stringEqualsI_ascii(query_name(w), arg)) return query_type(w);
+ }
+ return pfc::guid_null;
+}
+
bool album_art_path_list::equals(album_art_path_list const& v1, album_art_path_list const& v2) {
const size_t n = v1.get_count();
if (n != v2.get_count()) return false;
diff --git a/sdk/foobar2000/SDK/album_art.h b/sdk/foobar2000/SDK/album_art.h
index 2800797..7e4e229 100644
--- a/sdk/foobar2000/SDK/album_art.h
+++ b/sdk/foobar2000/SDK/album_art.h
@@ -27,9 +27,12 @@ namespace album_art_ids {
// returns lowercase name
const char * query_name( size_t );
const char * name_of( const GUID & );
+ const char * name_of_ex( const GUID &, const char * def = "undefined");
// returns Capitalized name
const char * query_capitalized_name( size_t );
const char * capitalized_name_of( const GUID & );
+
+ GUID by_name(const char*);
};
PFC_DECLARE_EXCEPTION(exception_album_art_not_found,exception_io_not_found,"Attached picture not found");
@@ -69,6 +72,7 @@ class NOVTABLE album_art_editor_instance : public album_art_extractor_instance {
void remove_all_();
};
+//! Extension to album_art_editor_instance, adds remove_all().
class NOVTABLE album_art_editor_instance_v2 : public album_art_editor_instance {
FB2K_MAKE_SERVICE_INTERFACE(album_art_editor_instance_v2, album_art_editor_instance);
public:
diff --git a/sdk/foobar2000/SDK/album_art_helpers.h b/sdk/foobar2000/SDK/album_art_helpers.h
index 09510f7..e3fc3aa 100644
--- a/sdk/foobar2000/SDK/album_art_helpers.h
+++ b/sdk/foobar2000/SDK/album_art_helpers.h
@@ -43,6 +43,7 @@ class album_art_extractor_instance_simple : public album_art_extractor_instance
void set(const GUID & p_what,album_art_data_ptr p_content) {m_content.set(p_what,p_content);}
bool have_item(const GUID & p_what) {return m_content.have_item(p_what);}
album_art_data_ptr query(const GUID & p_what,abort_callback & p_abort) {
+ p_abort.check();
album_art_data_ptr temp;
if (!m_content.query(p_what,temp)) throw exception_album_art_not_found();
return temp;
@@ -67,6 +68,7 @@ class album_art_extractor_impl_stdtags : public album_art_extractor_v2 {
}
bool is_our_path(const char * p_path,const char * p_extension) override {
+ (void)p_path;
return m_extensions.have_item(p_extension);
}
@@ -94,6 +96,7 @@ class album_art_editor_impl_stdtags : public album_art_editor_v2 {
}
bool is_our_path(const char * p_path,const char * p_extension) override {
+ (void)p_path;
return m_extensions.have_item(p_extension);
}
@@ -161,6 +164,6 @@ class album_art_path_list_impl : public album_art_path_list {
//! album_art_path_list implementation helper
class album_art_path_list_dummy : public album_art_path_list {
public:
- const char * get_path(t_size index) const {FB2K_BugCheck();}
- t_size get_count() const {return 0;}
+ const char * get_path(t_size) const override {FB2K_BugCheck();}
+ t_size get_count() const override {return 0;}
};
diff --git a/sdk/foobar2000/SDK/app_close_blocker.cpp b/sdk/foobar2000/SDK/app_close_blocker.cpp
index 03c5bd3..58d35e2 100644
--- a/sdk/foobar2000/SDK/app_close_blocker.cpp
+++ b/sdk/foobar2000/SDK/app_close_blocker.cpp
@@ -34,3 +34,39 @@ void fb2k::splitTask( pfc::thread::arg_t const & arg, std::function f)
(void)taskref; // retain until here
} );
}
+
+abort_callback& fb2k::mainAborter() {
+ return async_task_manager::get()->get_aborter();
+}
+
+app_close_blocking_task_impl::app_close_blocking_task_impl(const char * name) : m_name(name) {
+ PFC_ASSERT( core_api::is_main_thread() );
+ app_close_blocking_task_manager::get()->register_task(this);
+}
+app_close_blocking_task_impl::app_close_blocking_task_impl(pfc::string8&& name) : m_name(std::move(name)) {
+ PFC_ASSERT(core_api::is_main_thread());
+ app_close_blocking_task_manager::get()->register_task(this);
+}
+
+app_close_blocking_task_impl::~app_close_blocking_task_impl() {
+ PFC_ASSERT( core_api::is_main_thread() );
+ app_close_blocking_task_manager::get()->unregister_task(this);
+}
+
+void app_close_blocking_task_impl::query_task_name(pfc::string_base & out) {
+ out = m_name;
+}
+
+void app_close_blocking_task_impl_dynamic::toggle_blocking(bool state) {
+ PFC_ASSERT( core_api::is_main_thread() );
+ if (state != m_taskActive) {
+ auto api = app_close_blocking_task_manager::get();
+ if (state) api->register_task(this);
+ else api->unregister_task(this);
+ m_taskActive = state;
+ }
+}
+
+void app_close_blocking_task_impl_dynamic::query_task_name(pfc::string_base& out) {
+ out = m_name;
+}
diff --git a/sdk/foobar2000/SDK/app_close_blocker.h b/sdk/foobar2000/SDK/app_close_blocker.h
index 9c96122..2137c78 100644
--- a/sdk/foobar2000/SDK/app_close_blocker.h
+++ b/sdk/foobar2000/SDK/app_close_blocker.h
@@ -19,6 +19,9 @@ class NOVTABLE app_close_blocker : public service_base
//! Implementation: it's recommended that you derive from app_close_blocking_task_impl class instead of deriving from app_close_blocking_task directly, it manages registration/unregistration behind-the-scenes.
class NOVTABLE app_close_blocking_task {
public:
+ //! Retrieves user-friendly name of the task to be shown to the user, should the user try to close foobar2000 while the task is active. \n
+ //! Implementation note: this will NOT be called from register_task() or unregister_task(), only in response to user attempting to close foobar2000. \n
+ //! Common helper implementations of app_close_blocking_task register from base class constructor while intended query_task_name() override is not yet in place.
virtual void query_task_name(pfc::string_base & out) = 0;
protected:
@@ -28,42 +31,47 @@ class NOVTABLE app_close_blocking_task {
PFC_CLASS_NOT_COPYABLE_EX(app_close_blocking_task);
};
-//! Entrypoint class for registering app_close_blocking_task instances. Introduced in 0.9.5.1. \n
-//! Usage: static_api_ptr_t(). May fail if user runs pre-0.9.5.1. It's recommended that you use app_close_blocking_task_impl class instead of calling app_close_blocking_task_manager directly.
+//! Entrypoint class for registering app_close_blocking_task instances. \n
+//! You can use app_close_blocking_task_impl to call this automatically with your object.
class NOVTABLE app_close_blocking_task_manager : public service_base {
FB2K_MAKE_SERVICE_COREAPI(app_close_blocking_task_manager);
public:
+ //! Registers a task object. \n
+ //! Main thread only.
virtual void register_task(app_close_blocking_task * task) = 0;
+ //! Unregisters a task object. \n
+ //! Main thread only.
virtual void unregister_task(app_close_blocking_task * task) = 0;
};
//! Helper; implements standard functionality required by app_close_blocking_task implementations - registers/unregisters the task on construction/destruction.
class app_close_blocking_task_impl : public app_close_blocking_task {
public:
- app_close_blocking_task_impl() { app_close_blocking_task_manager::get()->register_task(this);}
- ~app_close_blocking_task_impl() { app_close_blocking_task_manager::get()->unregister_task(this);}
+ app_close_blocking_task_impl(const char * name = "");
+ app_close_blocking_task_impl(pfc::string8&& name);
+ ~app_close_blocking_task_impl();
- void query_task_name(pfc::string_base & out) { out = ""; }
+ //! Override me, or provide name to constructor
+ void query_task_name(pfc::string_base & out) override;
+
+ app_close_blocking_task_impl( const app_close_blocking_task_impl & ) = delete;
+ void operator=(const app_close_blocking_task_impl & ) = delete;
+private:
+ const pfc::string8 m_name;
};
class app_close_blocking_task_impl_dynamic : public app_close_blocking_task {
public:
- app_close_blocking_task_impl_dynamic() : m_taskActive() {}
+ app_close_blocking_task_impl_dynamic(const char * name = "") : m_name(name) {}
~app_close_blocking_task_impl_dynamic() { toggle_blocking(false); }
- void query_task_name(pfc::string_base & out) { out = ""; }
+ //! Override me, or provide name to constructor
+ void query_task_name(pfc::string_base & out) override;
-protected:
- void toggle_blocking(bool state) {
- if (state != m_taskActive) {
- auto api = app_close_blocking_task_manager::get();
- if (state) api->register_task(this);
- else api->unregister_task(this);
- m_taskActive = state;
- }
- }
+ void toggle_blocking(bool state);
private:
- bool m_taskActive;
+ bool m_taskActive = false;
+ const pfc::string8 m_name;
};
diff --git a/sdk/foobar2000/SDK/archive.h b/sdk/foobar2000/SDK/archive.h
index 7d88ec0..3c1af67 100644
--- a/sdk/foobar2000/SDK/archive.h
+++ b/sdk/foobar2000/SDK/archive.h
@@ -5,6 +5,10 @@
namespace foobar2000_io {
class archive;
+ //! Callback passed to archive listing methods. \n
+ //! For backwards compatibility, this inherits with abort_callback as well. \n
+ //! When implementiong, you must override abort_callback methods redirecting them to your abort_callback. \n
+ //! It is recommended to use lambda-based archive_list helper instead of implementing this interface.
class NOVTABLE archive_callback : public abort_callback {
public:
virtual bool on_entry(archive * owner,const char * url,const t_filestats & p_stats,const service_ptr_t & p_reader) = 0;
@@ -17,10 +21,13 @@ namespace foobar2000_io {
typedef std::function list_func_t;
//! Lists archive contents. \n
- //! May be called with any path, not only path accepted by is_our_archive.
+ //! May be called with any path, not only path accepted by is_our_archive. \n
+ //! It is strongly recommended to use the lambda_based archive_list() helper instead of calling this directly.
+ //! @param p_reader Optional reader to use, if the caller already has one. Implementation will open the file if no reader is supplied.
+ //! @param p_want_readers Flag to tell if the callback wants a reader object for each file in the archive, or just wants to list contents.
virtual void archive_list(const char * p_path,const service_ptr_t & p_reader,archive_callback & p_callback,bool p_want_readers) = 0;
- //! Helper implemented on top of the other archive_list, uses lambda instead of callback.
+ //! Helper implemented on top of the other archive_list, uses lambda instead of callback, avoids having to implement archive_callback.
void archive_list(const char * path, file::ptr, list_func_t, bool wantReaders, abort_callback&);
//! Optional method to weed out unsupported formats prior to calling archive_list. \n
@@ -50,9 +57,15 @@ namespace foobar2000_io {
//! Returns a list of extensions, colon delimited, e.g.: "zip,rar,7z"
virtual void list_extensions(pfc::string_base & out) = 0;
};
+ //! \since 2.1
+ class NOVTABLE archive_v4 : public archive_v3 {
+ FB2K_MAKE_SERVICE_INTERFACE(archive_v4, archive_v3)
+ public:
+ virtual fb2k::arrayRef archive_list_v4( fsItemFilePtr item, file::ptr readerOptional, abort_callback & a) = 0;
+ };
//! Root class for archive implementations. Derive from this instead of from archive directly.
- class NOVTABLE archive_impl : public service_multi_inherit {
+ class NOVTABLE archive_impl : public service_multi_inherit {
protected:
//do not override these
bool get_canonical_path(const char * path,pfc::string_base & out) override;
@@ -75,6 +88,9 @@ namespace foobar2000_io {
void list_extensions(pfc::string_base & out) override { out = get_archive_type(); }
bool supports_content_types() override { return false; }
char pathSeparator() override { return '/'; }
+ void extract_filename_ext(const char * path, pfc::string_base & outFN) override;
+ bool get_display_name_short(const char* in, pfc::string_base& out) override;
+ fb2k::arrayRef archive_list_v4( fsItemFilePtr item, file::ptr readerOptional, abort_callback & a ) override;
protected:
//override these
virtual const char * get_archive_type()=0;//eg. "zip", must be lowercase
diff --git a/sdk/foobar2000/SDK/audio_chunk.cpp b/sdk/foobar2000/SDK/audio_chunk.cpp
index d40bca2..b1b6007 100644
--- a/sdk/foobar2000/SDK/audio_chunk.cpp
+++ b/sdk/foobar2000/SDK/audio_chunk.cpp
@@ -2,17 +2,35 @@
#include "mem_block_container.h"
#include "audio_chunk.h"
-void audio_chunk::set_data(const audio_sample * src,t_size samples,unsigned nch,unsigned srate,unsigned channel_config)
-{
- t_size size = samples * nch;
- set_data_size(size);
+void audio_chunk::allocate(size_t size, bool bQuicker) {
+ if (bQuicker) {
+ const size_t before = this->get_data_size();
+ const size_t allow_waste = pfc::max_t(size, 4096);
+ const size_t upper = (size + allow_waste > size) ? size + allow_waste : SIZE_MAX;
+ if (before >= size && before <= upper) return;
+ }
+ this->set_data_size(size);
+}
+
+void audio_chunk::set_data(const audio_sample* src, size_t samples, spec_t const & spec, bool bQuicker) {
+ t_size size = samples * spec.chanCount;
+ allocate(size, bQuicker);
if (src)
- pfc::memcpy_t(get_data(),src,size);
+ pfc::memcpy_t(get_data(), src, size);
else
- pfc::memset_t(get_data(),(audio_sample)0,size);
+ pfc::memset_t(get_data(), (audio_sample)0, size);
set_sample_count(samples);
- set_channels(nch,channel_config);
- set_srate(srate);
+ set_spec(spec);
+}
+
+void audio_chunk::set_data(const audio_sample* src, size_t samples, unsigned nch, unsigned srate, unsigned channel_config)
+{
+ set_data(src, samples, makeSpec(srate, nch, channel_config));
+}
+
+void audio_chunk::set_data(const audio_sample* src, size_t samples, unsigned nch, unsigned srate) {
+
+ set_data(src, samples, makeSpec(srate, nch));
}
inline bool check_exclusive(unsigned val, unsigned mask)
@@ -280,25 +298,27 @@ static void process_float_multi_swap(audio_sample * p_out,const t_float * p_in,c
}
}
-void audio_chunk::set_data_32(const float* src, t_size samples, unsigned nch, unsigned srate) {
+void audio_chunk::set_data_32(const float* src, size_t samples, spec_t const& spec) {
#if audio_sample_size == 32
- set_data(src, samples, nch, srate);
+ set_data(src, samples, spec);
#else
- t_size size = samples * nch;
+ t_size size = samples * spec.chanCount;
set_data_size(size);
if (src)
audio_math::convert(src, get_data(), size);
else
pfc::memset_t(get_data(), (audio_sample)0, size);
set_sample_count(samples);
- set_channels(nch);
- set_srate(srate);
+ set_spec(spec);
#endif
}
+void audio_chunk::set_data_32(const float* src, size_t samples, unsigned nch, unsigned srate) {
+ set_data_32(src, samples, makeSpec(srate, nch) );
+}
void audio_chunk::set_data_floatingpoint_ex(const void * ptr,t_size size,unsigned srate,unsigned nch,unsigned bps,unsigned flags,unsigned p_channel_config)
{
- PFC_ASSERT(bps==32 || bps==64 || bps == 16 || bps == 24);
+ PFC_ASSERT(is_supported_floatingpoint(bps));
PFC_ASSERT( check_exclusive(flags,FLAG_LITTLE_ENDIAN|FLAG_BIG_ENDIAN) );
PFC_ASSERT( ! (flags & (FLAG_SIGNED|FLAG_UNSIGNED) ) );
@@ -499,14 +519,14 @@ template class sampleToInt {
clipHi = ( (int_t) 1 << (d.bpsValid-1)) - 1;
scale = (float) ( (int64_t) 1 << (d.bpsValid - 1) ) * d.scale;
if (d.useUpperBits) {
- shift = d.bps - d.bpsValid;
+ shift = (int8_t)( d.bps - d.bpsValid );
} else {
shift = 0;
}
}
inline int_t operator() (audio_sample s) const {
int_t v;
- if (sizeof(int_t) > 4) v = (int_t) audio_math::rint64( s * scale );
+ if constexpr (sizeof(int_t) > 4) v = (int_t) audio_math::rint64( s * scale );
else v = (int_t)audio_math::rint32( s * scale );
return pfc::clip_t( v, clipLo, clipHi) << shift;
}
@@ -604,6 +624,7 @@ audio_chunk::spec_t audio_chunk::makeSpec(uint32_t rate, uint32_t channels) {
}
audio_chunk::spec_t audio_chunk::makeSpec(uint32_t rate, uint32_t channels, uint32_t mask) {
+ PFC_ASSERT(mask == 0 || pfc::countBits32(mask) == channels);
spec_t spec = {};
spec.sampleRate = rate; spec.chanCount = channels; spec.chanMask = mask;
return spec;
@@ -655,10 +676,10 @@ WAVEFORMATEX audio_chunk::spec_t::toWFX() const {
WAVEFORMATEX wfx = {};
wfx.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
- wfx.nChannels = chanCount;
+ wfx.nChannels = (WORD) chanCount;
wfx.nSamplesPerSec = sampleRate;
wfx.nAvgBytesPerSec = sampleRate * chanCount * sampleWidth;
- wfx.nBlockAlign = chanCount * sampleWidth;
+ wfx.nBlockAlign = (WORD)( chanCount * sampleWidth );
wfx.wBitsPerSample = sampleWidth * 8;
return wfx;
}
@@ -683,11 +704,11 @@ WAVEFORMATEX audio_chunk::spec_t::toWFXWithBPS(uint32_t bps) const {
WAVEFORMATEX wfx = {};
wfx.wFormatTag = WAVE_FORMAT_PCM;
- wfx.nChannels = chanCount;
+ wfx.nChannels = (WORD)chanCount;
wfx.nSamplesPerSec = sampleRate;
wfx.nAvgBytesPerSec = sampleRate * chanCount * sampleWidth;
- wfx.nBlockAlign = chanCount * sampleWidth;
- wfx.wBitsPerSample = sampleWidth * 8;
+ wfx.nBlockAlign = (WORD)( chanCount * sampleWidth );
+ wfx.wBitsPerSample = (WORD)( sampleWidth * 8 );
return wfx;
}
@@ -699,7 +720,7 @@ WAVEFORMATEXTENSIBLE audio_chunk::spec_t::toWFXEXWithBPS(uint32_t bps) const {
wfxe.Format = toWFXWithBPS(bps);
wfxe.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wfxe.Format.cbSize = sizeof(wfxe) - sizeof(wfxe.Format);
- wfxe.Samples.wValidBitsPerSample = sampleWidth * 8;
+ wfxe.Samples.wValidBitsPerSample = (WORD)( sampleWidth * 8 );
wfxe.dwChannelMask = audio_chunk::g_channel_config_to_wfx(this->chanMask);
wfxe.SubFormat = isFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM;
diff --git a/sdk/foobar2000/SDK/audio_chunk.h b/sdk/foobar2000/SDK/audio_chunk.h
index cded076..4e22c88 100644
--- a/sdk/foobar2000/SDK/audio_chunk.h
+++ b/sdk/foobar2000/SDK/audio_chunk.h
@@ -1,6 +1,7 @@
#pragma once
#include
+#include
#include "exception_io.h"
#ifdef _WIN32
@@ -12,6 +13,7 @@ PFC_DECLARE_EXCEPTION(exception_unexpected_audio_format_change, exception_io_dat
//! Interface to container of a chunk of audio data. See audio_chunk_impl for an implementation.
class NOVTABLE audio_chunk {
public:
+ struct spec_t; // forward decl
enum {
sample_rate_min = 1000, sample_rate_max = 20000000
@@ -40,18 +42,22 @@ class NOVTABLE audio_chunk {
channel_top_back_center = 1<<16,
channel_top_back_right = 1<<17,
+ channels_back_left_right = channel_back_left | channel_back_right,
+ channels_side_left_right = channel_side_left | channel_side_right,
+
channel_config_mono = channel_front_center,
channel_config_stereo = channel_front_left | channel_front_right,
- channel_config_3point0 = channel_front_left | channel_front_right | channel_front_center,
- channel_config_4point0 = channel_front_left | channel_front_right | channel_back_left | channel_back_right,
- channel_config_4point1 = channel_front_left | channel_front_right | channel_back_left | channel_back_right | channel_lfe,
- channel_config_5point0 = channel_front_left | channel_front_right | channel_front_center | channel_back_left | channel_back_right,
- channel_config_5point1 = channel_front_left | channel_front_right | channel_front_center | channel_lfe | channel_back_left | channel_back_right,
- channel_config_5point1_side = channel_front_left | channel_front_right | channel_front_center | channel_lfe | channel_side_left | channel_side_right,
- channel_config_7point1 = channel_config_5point1 | channel_side_left | channel_side_right,
+ channel_config_2point1 = channel_config_stereo | channel_lfe,
+ channel_config_3point0 = channel_config_stereo | channel_front_center,
+ channel_config_4point0 = channel_config_stereo | channels_back_left_right,
+ channel_config_4point0_side = channel_config_stereo | channels_side_left_right,
+ channel_config_4point1 = channel_config_4point0 | channel_lfe,
+ channel_config_5point0 = channel_config_4point0 | channel_front_center,
+ channel_config_6point0 = channel_config_4point0 | channels_side_left_right,
+ channel_config_5point1 = channel_config_4point0 | channel_front_center | channel_lfe,
+ channel_config_5point1_side = channel_config_4point0_side | channel_front_center | channel_lfe,
+ channel_config_7point1 = channel_config_5point1 | channels_side_left_right,
- channels_back_left_right = channel_back_left | channel_back_right,
- channels_side_left_right = channel_side_left | channel_side_right,
defined_channel_count = 18,
};
@@ -62,14 +68,14 @@ class NOVTABLE audio_chunk {
static unsigned g_guess_channel_config_xiph(unsigned count);
//! Helper function; translates audio_chunk channel map to WAVEFORMATEXTENSIBLE channel map.
- static uint32_t g_channel_config_to_wfx(unsigned p_config);
+ static constexpr uint32_t g_channel_config_to_wfx(unsigned p_config) { return p_config;}
//! Helper function; translates WAVEFORMATEXTENSIBLE channel map to audio_chunk channel map.
- static unsigned g_channel_config_from_wfx(uint32_t p_wfx);
+ static constexpr unsigned g_channel_config_from_wfx(uint32_t p_wfx) { return p_wfx;}
//! Extracts flag describing Nth channel from specified map. Usable to figure what specific channel in a stream means.
static unsigned g_extract_channel_flag(unsigned p_config,unsigned p_index);
//! Counts channels specified by channel map.
- static unsigned g_count_channels(unsigned p_config);
+ static constexpr unsigned g_count_channels(unsigned p_config) { return pfc::countBits32(p_config); }
//! Calculates index of a channel specified by p_flag in a stream where channel map is described by p_config.
static unsigned g_channel_index_from_flag(unsigned p_config,unsigned p_flag);
@@ -78,6 +84,7 @@ class NOVTABLE audio_chunk {
static unsigned g_find_channel_idx(unsigned p_flag);
static void g_formatChannelMaskDesc(unsigned flags, pfc::string_base & out);
static pfc::string8 g_formatChannelMaskDesc(unsigned flags);
+ static const char* g_channelMaskName(unsigned flags);
@@ -92,7 +99,8 @@ class NOVTABLE audio_chunk {
//! Resizes audio data buffer to specified size. Throws std::bad_alloc on failure.
virtual void set_data_size(t_size p_new_size) = 0;
//! Sanity helper, same as set_data_size.
- void allocate(size_t size) { set_data_size( size ); }
+ //! @param bQuicker Avoid memory allocation, permit up to 2x memory used
+ void allocate(size_t size, bool bQuicker = false);
//! Retrieves sample rate of contained audio data.
virtual unsigned get_srate() const = 0;
@@ -148,7 +156,7 @@ class NOVTABLE audio_chunk {
#if PFC_DEBUG
void assert_valid(const char * ctx) const;
#else
- void assert_valid(const char * ctx) const {}
+ void assert_valid(const char* ctx) const { (void)ctx; }
#endif
@@ -169,10 +177,9 @@ class NOVTABLE audio_chunk {
}
//! Helper, sets chunk data to contents of specified buffer, with specified number of channels / sample rate / channel map.
- void set_data(const audio_sample * src,t_size samples,unsigned nch,unsigned srate,unsigned channel_config);
-
- //! Helper, sets chunk data to contents of specified buffer, with specified number of channels / sample rate, using default channel map for specified channel count.
- inline void set_data(const audio_sample * src,t_size samples,unsigned nch,unsigned srate) {set_data(src,samples,nch,srate,g_guess_channel_config(nch));}
+ void set_data(const audio_sample * src,size_t samples,unsigned nch,unsigned srate,unsigned channel_config);
+ void set_data(const audio_sample* src, size_t samples, spec_t const& spec, bool bQucker = false);
+ void set_data(const audio_sample* src, t_size samples, unsigned nch, unsigned srate);
void set_data_int16(const int16_t * src,t_size samples,unsigned nch,unsigned srate,unsigned channel_config);
@@ -200,8 +207,10 @@ class NOVTABLE audio_chunk {
void set_data_fixedpoint_ms(const void * ptr, size_t bytes, unsigned sampleRate, unsigned channels, unsigned bps, unsigned channelConfig);
void set_data_floatingpoint_ex(const void * ptr,t_size bytes,unsigned p_sample_rate,unsigned p_channels,unsigned p_bits_per_sample,unsigned p_flags,unsigned p_channel_config);//signed/unsigned flags dont apply
+ static bool is_supported_floatingpoint(unsigned bps) { return bps == 32 || bps == 64 || bps == 16 || bps == 24; }
void set_data_32(const float* src, t_size samples, unsigned nch, unsigned srate);
+ void set_data_32(const float* src, t_size samples, spec_t const & spec );
//! Appends silent samples at the end of the chunk. \n
//! The chunk may be empty prior to this call, its sample rate & channel count will be set to the specified values then. \n
@@ -213,10 +222,10 @@ class NOVTABLE audio_chunk {
void pad_with_silence_ex(t_size samples,unsigned hint_nch,unsigned hint_srate);
//! Appends silent samples at the end of the chunk. \n
//! The chunk must have valid sample rate & channel count prior to this call.
- //! @param Number of silent samples to append.s
+ //! @param samples Number of silent samples to append.
void pad_with_silence(t_size samples);
//! Inserts silence at the beginning of the audio chunk.
- //! @param Number of silent samples to insert.
+ //! @param samples Number of silent samples to insert.
void insert_silence_fromstart(t_size samples);
//! Helper; removes N first samples from the chunk. \n
//! If the chunk contains fewer samples than requested, it becomes empty.
@@ -232,7 +241,7 @@ class NOVTABLE audio_chunk {
//! Any existing audio sdata will be discarded. \n
//! Expects sample rate and channel count to be set first. \n
//! Also allocates memory for the requested amount of data see: set_data_size().
- //! @param samples Desired duration in seconds.
+ //! @param seconds Desired duration in seconds.
void set_silence_seconds( double seconds );
//! Helper; skips first samples of the chunk updating a remaining to-skip counter.
@@ -261,8 +270,9 @@ class NOVTABLE audio_chunk {
void scale(audio_sample p_value);
//! Helper; copies content of another audio chunk to this chunk.
- void copy(const audio_chunk & p_source) {
- set_data(p_source.get_data(),p_source.get_sample_count(),p_source.get_channels(),p_source.get_srate(),p_source.get_channel_config());
+ //! @param bQuicker Avoid memory allocation, permit up to 2x memory used
+ void copy(const audio_chunk & p_source, bool bQuicker = false) {
+ set_data(p_source.get_data(),p_source.get_sample_count(),p_source.get_spec(), bQuicker);
}
const audio_chunk & operator=(const audio_chunk & p_source) {
diff --git a/sdk/foobar2000/SDK/audio_chunk_channel_config.cpp b/sdk/foobar2000/SDK/audio_chunk_channel_config.cpp
index 4c2f399..1751b3a 100644
--- a/sdk/foobar2000/SDK/audio_chunk_channel_config.cpp
+++ b/sdk/foobar2000/SDK/audio_chunk_channel_config.cpp
@@ -50,37 +50,9 @@ static struct {DWORD m_wfx; unsigned m_native; } const g_translation_table[] =
#endif
#endif
-// foobar2000 channel flags are 1:1 identical to Windows WFX ones.
-uint32_t audio_chunk::g_channel_config_to_wfx(unsigned p_config)
-{
- return p_config;
-#if 0
- DWORD ret = 0;
- unsigned n;
- for(n=0;n>= 1;
++idx;
}
-}
\ No newline at end of file
+}
+
+namespace {
+ struct maskDesc_t {
+ const char* name;
+ unsigned mask;
+ };
+ static constexpr maskDesc_t maskDesc[] = {
+ {"mono", audio_chunk::channel_config_mono},
+ {"stereo", audio_chunk::channel_config_stereo},
+ {"stereo (rear)", audio_chunk::channel_back_left | audio_chunk::channel_back_right},
+ {"stereo (side)", audio_chunk::channel_side_left | audio_chunk::channel_side_right},
+ {"2.1", audio_chunk::channel_config_2point1},
+ {"3.0", audio_chunk::channel_config_3point0},
+ {"4.0", audio_chunk::channel_config_4point0},
+ {"4.1", audio_chunk::channel_config_4point1},
+ {"5.0", audio_chunk::channel_config_5point0},
+ {"5.1", audio_chunk::channel_config_5point1},
+ {"5.1 (side)", audio_chunk::channel_config_5point1_side},
+ {"6.1", audio_chunk::channel_config_5point1 | audio_chunk::channel_back_center},
+ {"6.1 (side)", audio_chunk::channel_config_5point1_side | audio_chunk::channel_back_center},
+ {"7.1", audio_chunk::channel_config_7point1},
+ };
+}
+
+const char* audio_chunk::g_channelMaskName(unsigned flags) {
+ for (auto& walk : maskDesc) {
+ if (flags == walk.mask) return walk.name;
+ }
+ return nullptr;
+}
+
+static_assert( pfc::countBits32(audio_chunk::channel_config_mono) == 1 );
+static_assert( pfc::countBits32(audio_chunk::channel_config_stereo) == 2 );
+static_assert( pfc::countBits32(audio_chunk::channel_config_4point0) == 4 );
+static_assert( pfc::countBits32(audio_chunk::channel_config_4point0_side) == 4 );
+static_assert( pfc::countBits32(audio_chunk::channel_config_4point1) == 5 );
+static_assert( pfc::countBits32(audio_chunk::channel_config_5point0) == 5 );
+static_assert( pfc::countBits32(audio_chunk::channel_config_5point1) == 6 );
+static_assert( pfc::countBits32(audio_chunk::channel_config_5point1_side) == 6 );
+static_assert( pfc::countBits32(audio_chunk::channel_config_6point0) == 6);
+static_assert( pfc::countBits32(audio_chunk::channel_config_7point1) == 8 );
diff --git a/sdk/foobar2000/SDK/audio_postprocessor.h b/sdk/foobar2000/SDK/audio_postprocessor.h
index d70f6b3..31af5df 100644
--- a/sdk/foobar2000/SDK/audio_postprocessor.h
+++ b/sdk/foobar2000/SDK/audio_postprocessor.h
@@ -4,7 +4,6 @@
#include "mem_block_container.h"
//! This class handles conversion of audio data (audio_chunk) to various linear PCM types, with optional dithering.
-
class NOVTABLE audio_postprocessor : public service_base
{
public:
diff --git a/sdk/foobar2000/SDK/callback_merit.h b/sdk/foobar2000/SDK/callback_merit.h
new file mode 100644
index 0000000..ee65142
--- /dev/null
+++ b/sdk/foobar2000/SDK/callback_merit.h
@@ -0,0 +1,24 @@
+#pragma once
+
+namespace fb2k {
+ // callback_merit_t controls in what order callbacks are executed. \n
+ // In specific corner cases, you want your callback executed before other callbacks of the same kind.
+ typedef double callback_merit_t;
+
+ // Note REVERSE sort. HIGHER merit called first.
+ static constexpr callback_merit_t callback_merit_default = 0;
+ static constexpr callback_merit_t callback_merit_indexer = 1000; // indexer: does nothing else than updating internal state, called early before UI updates, in case UI updates might rely on indexed data.
+ static constexpr callback_merit_t callback_merit_serializer = 2000; // serializer: does nothing else than saving new state, called early.
+
+ //! Special class that can be optionally implemented by 'static' callbacks, such as library_callback, to control callback merit. \n
+ //! See also: callback_merit_t \n
+ //! Some callback classes support get_callback_merit() natively, such as metadb_io_callback_v2. \n
+ //! With callbacks registered dynamically, other means of controlling merit are provided.
+ class callback_with_merit : public service_base {
+ FB2K_MAKE_SERVICE_INTERFACE(callback_with_merit, service_base);
+ public:
+ virtual callback_merit_t get_callback_merit() = 0;
+ };
+
+ callback_merit_t callback_merit_of(service_ptr obj);
+}
\ No newline at end of file
diff --git a/sdk/foobar2000/SDK/cfg_var.cpp b/sdk/foobar2000/SDK/cfg_var.cpp
index 633aa0d..ab14c9e 100644
--- a/sdk/foobar2000/SDK/cfg_var.cpp
+++ b/sdk/foobar2000/SDK/cfg_var.cpp
@@ -2,10 +2,23 @@
#include "cfg_var.h"
#include "configStore.h"
+namespace fb2k {
+ pfc::string8 formatCfgVarName(const GUID& guid) {
+ return pfc::format("cfg_var.", pfc::print_guid(guid));
+ }
+ pfc::string8 advconfig_autoName(const GUID& id) {
+ return pfc::format("advconfig.unnamed.", pfc::print_guid(id));
+ }
+ pfc::string8 advconfig_autoName(const GUID& id, const char* specified) {
+ if (specified) return specified;
+ return advconfig_autoName(id);
+ }
+}
namespace cfg_var_modern {
#ifdef FOOBAR2000_HAVE_CFG_VAR_LEGACY
void cfg_string::set_data_raw(stream_reader* p_stream, t_size p_sizehint, abort_callback& p_abort) {
+ (void)p_sizehint;
pfc::string8_fastalloc temp;
p_stream->read_string_raw(temp, p_abort);
this->set(temp);
@@ -23,6 +36,7 @@ namespace cfg_var_modern {
}
void cfg_bool::set_data_raw(stream_reader* p_stream, t_size p_sizehint, abort_callback& p_abort) {
+ (void)p_sizehint;
uint8_t b;
if (p_stream->read(&b, 1, p_abort) == 1) {
this->set(b != 0);
@@ -88,7 +102,7 @@ namespace cfg_var_modern {
pfc::string8 cfg_var_common::formatVarName(const GUID& guid) {
- return pfc::format("cfg_var.", pfc::print_guid(guid));
+ return fb2k::formatCfgVarName( guid );
}
pfc::string8 cfg_var_common::formatName() const {
diff --git a/sdk/foobar2000/SDK/cfg_var_legacy.cpp b/sdk/foobar2000/SDK/cfg_var_legacy.cpp
index 58fbce6..44806bf 100644
--- a/sdk/foobar2000/SDK/cfg_var_legacy.cpp
+++ b/sdk/foobar2000/SDK/cfg_var_legacy.cpp
@@ -1,9 +1,18 @@
#include "foobar2000-sdk-pch.h"
#include "cfg_var_legacy.h"
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+#include "configStore.h"
+#include
+#endif
+
#ifdef FOOBAR2000_HAVE_CFG_VAR_LEGACY
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+namespace fb2k {
+ pfc::string8 formatCfgVarName(const GUID&);
+}
+#endif // FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
namespace cfg_var_legacy {
-
cfg_var_reader* cfg_var_reader::g_list = NULL;
cfg_var_writer* cfg_var_writer::g_list = NULL;
@@ -22,19 +31,31 @@ namespace cfg_var_legacy {
guid = pfc::byteswap_if_be_t(guid);
p_stream->read_lendian_t(size, p_abort);
- cfg_var_reader* var;
- if (vars.query(guid, var)) {
+ auto iter = vars.find(guid);
+ if ( iter.is_valid() ) {
stream_reader_limited_ref wrapper(p_stream, size);
try {
- var->set_data_raw(&wrapper, size, p_abort);
- } catch (exception_io_data) {}
+ iter->m_value->set_data_raw(&wrapper, size, p_abort);
+ } catch (exception_io_data const&) {}
wrapper.flush_remaining(p_abort);
} else {
p_stream->skip_object(size, p_abort);
}
}
}
-
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+ static std::once_flag downgrade_once;
+ void cfg_var_reader::downgrade_main() {
+ std::call_once(downgrade_once, [] {
+ auto api = fb2k::configStore::tryGet();
+ if (api.is_valid()) {
+ for (cfg_var_reader* walk = g_list; walk != NULL; walk = walk->m_next) {
+ walk->downgrade_check(api);
+ }
+ }
+ });
+ }
+#endif
void cfg_var_writer::config_write_file(stream_writer* p_stream, abort_callback& p_abort) {
cfg_var_writer* ptr;
pfc::array_t temp;
@@ -58,10 +79,80 @@ namespace cfg_var_legacy {
}
void cfg_string::set_data_raw(stream_reader* p_stream, t_size p_sizehint, abort_callback& p_abort) {
+ (void)p_sizehint;
pfc::string8_fastalloc temp;
p_stream->read_string_raw(temp, p_abort);
set_string(temp);
}
+
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+ int64_t downgrade_this(fb2k::configStore::ptr api, const char * name, int64_t current) {
+ int64_t v = api->getConfigInt(name, INT64_MAX);
+ if (v == INT64_MAX) {
+ v = api->getConfigInt(name, INT64_MIN);
+ if ( v == INT64_MIN ) return current;
+ }
+ api->deleteConfigInt(name);
+ return v;
+ }
+ uint64_t downgrade_this(fb2k::configStore::ptr api, const char * name, uint64_t current) {
+ return (uint64_t) downgrade_this(api, name, (int64_t) current);
+ }
+ int32_t downgrade_this(fb2k::configStore::ptr api, const char * name, int32_t current) {
+ return (int32_t) downgrade_this(api, name, (int64_t) current);
+ }
+ uint32_t downgrade_this(fb2k::configStore::ptr api, const char * name, uint32_t current) {
+ return (uint32_t) downgrade_this(api, name, (int64_t) current);
+ }
+ bool downgrade_this(fb2k::configStore::ptr api, const char * name, bool current) {
+ return downgrade_this(api, name, (int64_t)(current?1:0)) != 0;
+ }
+ double downgrade_this(fb2k::configStore::ptr api, const char * name, double current) {
+ double v = api->getConfigFloat(name, -1);
+ if (v == -1) {
+ v = api->getConfigFloat(name, 0);
+ if ( v == 0 ) return current;
+ }
+ api->deleteConfigFloat(name);
+ return v;
+ }
+ GUID downgrade_this(fb2k::configStore::ptr api, const char * name, GUID current) {
+ auto blob = api->getConfigBlob( name );
+ if (blob.is_valid() && blob->size() == sizeof(GUID)) {
+ GUID ret;
+ memcpy(&ret, blob->get_ptr(), sizeof(ret));
+ api->deleteConfigBlob( name );
+ return ret;
+ }
+ return current;
+ }
+
+ void cfg_string::downgrade_check(fb2k::configStore::ptr api) {
+ const auto name = this->downgrade_name();
+ auto v = api->getConfigString(name, nullptr);
+ if (v.is_valid()) {
+ this->set(v->c_str());
+ api->deleteConfigString(name);
+ }
+ }
+ void cfg_string_mt::downgrade_check(fb2k::configStore::ptr api) {
+ const auto name = this->downgrade_name();
+ auto v = api->getConfigString(name, nullptr);
+ if (v.is_valid()) {
+ this->set(v->c_str());
+ api->deleteConfigString(name);
+ }
+ }
+
+ pfc::string8 cfg_var_reader::downgrade_name() const {
+ if (m_downgrade_name.length() > 0) {
+ return m_downgrade_name;
+ } else {
+ return fb2k::formatCfgVarName(this->m_guid);
+ }
+ }
+#endif
+
}
#endif // FOOBAR2000_HAVE_CFG_VAR_LEGACY
diff --git a/sdk/foobar2000/SDK/cfg_var_legacy.h b/sdk/foobar2000/SDK/cfg_var_legacy.h
index f45c3dc..02b044d 100644
--- a/sdk/foobar2000/SDK/cfg_var_legacy.h
+++ b/sdk/foobar2000/SDK/cfg_var_legacy.h
@@ -4,6 +4,11 @@
#include
#include "filesystem.h" // stream_reader, stream_writer
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+#include "configStore.h"
+#include "initquit.h"
+#endif
+
namespace cfg_var_legacy {
#define CFG_VAR_ASSERT_SAFEINIT PFC_ASSERT(!core_api::are_services_available());/*imperfect check for nonstatic instantiation*/
@@ -19,6 +24,22 @@ namespace cfg_var_legacy {
//! @param p_sizehint Number of bytes contained in the stream; reading past p_sizehint bytes will fail (EOF).
virtual void set_data_raw(stream_reader* p_stream, t_size p_sizehint, abort_callback& p_abort) = 0;
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+ private:
+ pfc::string8 m_downgrade_name;
+ public:
+ pfc::string8 downgrade_name() const;
+ void downgrade_set_name( const char * arg ) { m_downgrade_name = arg; }
+
+ //! Implementation of config downgrade for this var, see FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE for more info. \n
+ //! Most components should not use this.
+ virtual void downgrade_check(fb2k::configStore::ptr api) {}
+ //! Config downgrade main for your component, see FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE for more info. \n
+ //! Put FOOBAR2000_IMPLEMENT_CFG_VAR_DOWNGRADE somewhere in your component to call on startup, or call from your code as early as possible after config read. \n
+ //! If you call it more than once, spurious calls will be ignored.
+ static void downgrade_main();
+#endif
+
//! For internal use only, do not call.
static void config_read_file(stream_reader* p_stream, abort_callback& p_abort);
@@ -62,6 +83,16 @@ namespace cfg_var_legacy {
GUID get_guid() const { return cfg_var_reader::m_guid; }
};
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+ int64_t downgrade_this( fb2k::configStore::ptr api, const char*, int64_t current);
+ uint64_t downgrade_this( fb2k::configStore::ptr api, const char*, uint64_t current);
+ int32_t downgrade_this( fb2k::configStore::ptr api, const char*, int32_t current);
+ uint32_t downgrade_this( fb2k::configStore::ptr api, const char*, uint32_t current);
+ bool downgrade_this( fb2k::configStore::ptr api, const char*, bool current);
+ double downgrade_this( fb2k::configStore::ptr api, const char*, double current);
+ GUID downgrade_this( fb2k::configStore::ptr api, const char*, GUID current);
+#endif
+
//! Generic integer config variable class. Template parameter can be used to specify integer type to use.\n
//! Note that cfg_var class and its derivatives may be only instantiated statically (as static objects or members of other static objects), NEVER dynamically (operator new, local variables, members of objects instantiated as such).
template
@@ -69,13 +100,17 @@ namespace cfg_var_legacy {
private:
t_inttype m_val;
protected:
- void get_data_raw(stream_writer* p_stream, abort_callback& p_abort) { p_stream->write_lendian_t(m_val, p_abort); }
- void set_data_raw(stream_reader* p_stream, t_size, abort_callback& p_abort) {
+ void get_data_raw(stream_writer* p_stream, abort_callback& p_abort) override { p_stream->write_lendian_t(m_val, p_abort); }
+ void set_data_raw(stream_reader* p_stream, t_size, abort_callback& p_abort) override {
t_inttype temp;
p_stream->read_lendian_t(temp, p_abort);//alter member data only on success, this will throw an exception when something isn't right
m_val = temp;
}
-
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+ void downgrade_check(fb2k::configStore::ptr api) override {
+ m_val = downgrade_this(api, this->downgrade_name(), m_val);
+ }
+#endif
public:
//! @param p_guid GUID of the variable, used to identify variable implementations owning specific configuration file entries when reading the configuration file back. You must generate a new GUID every time you declare a new cfg_var.
//! @param p_default Default value of the variable.
@@ -87,58 +122,64 @@ namespace cfg_var_legacy {
inline operator t_inttype() const { return m_val; }
inline t_inttype get_value() const { return m_val; }
+ inline t_inttype get() const { return m_val; }
};
typedef cfg_int_t cfg_int;
typedef cfg_int_t cfg_uint;
- //! Since relevant byteswapping functions also understand GUIDs, this can be used to declare a cfg_guid.
- typedef cfg_int_t cfg_guid;
- typedef cfg_int_t cfg_bool;
- typedef cfg_int_t cfg_float;
- typedef cfg_int_t cfg_double;
+ typedef cfg_int_t cfg_guid; // ANNOYING OLD DESIGN. THIS DOESN'T BELONG HERE BUT CANNOT BE CHANGED WITHOUT BREAKING PEOPLE'S STUFF. BLARFGH.
+ typedef cfg_int_t cfg_bool; // See above.
+ typedef cfg_int_t cfg_float; // See above %$!@#$
+ typedef cfg_int_t cfg_double; // ....
//! String config variable. Stored in the stream with int32 header containing size in bytes, followed by non-null-terminated UTF-8 data.\n
//! Note that cfg_var class and its derivatives may be only instantiated statically (as static objects or members of other static objects), NEVER dynamically (operator new, local variables, members of objects instantiated as such).
class cfg_string : public cfg_var, public pfc::string8 {
protected:
- void get_data_raw(stream_writer* p_stream, abort_callback& p_abort);
- void set_data_raw(stream_reader* p_stream, t_size p_sizehint, abort_callback& p_abort);
+ void get_data_raw(stream_writer* p_stream, abort_callback& p_abort) override;
+ void set_data_raw(stream_reader* p_stream, t_size p_sizehint, abort_callback& p_abort) override;
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+ void downgrade_check(fb2k::configStore::ptr) override;
+#endif
public:
//! @param p_guid GUID of the variable, used to identify variable implementations owning specific configuration file entries when reading the configuration file back. You must generate a new GUID every time you declare a new cfg_var.
//! @param p_defaultval Default/initial value of the variable.
explicit inline cfg_string(const GUID& p_guid, const char* p_defaultval) : cfg_var(p_guid), pfc::string8(p_defaultval) {}
- inline const cfg_string& operator=(const cfg_string& p_val) { set_string(p_val); return *this; }
- inline const cfg_string& operator=(const char* p_val) { set_string(p_val); return *this; }
+ const cfg_string& operator=(const cfg_string& p_val) { set_string(p_val); return *this; }
+ const cfg_string& operator=(const char* p_val) { set_string(p_val); return *this; }
+ const cfg_string& operator=(pfc::string8 && p_val) { set(std::move(p_val)); return *this; }
+
inline operator const char* () const { return get_ptr(); }
const pfc::string8& get() const { return *this; }
+ void set( const char * arg ) { this->set_string(arg); }
+ void set(pfc::string8&& arg) { pfc::string8 * pThis = this; *pThis = std::move(arg); }
};
class cfg_string_mt : public cfg_var {
protected:
- void get_data_raw(stream_writer* p_stream, abort_callback& p_abort) {
+ void get_data_raw(stream_writer* p_stream, abort_callback& p_abort) override {
pfc::string8 temp;
get(temp);
p_stream->write_object(temp.get_ptr(), temp.length(), p_abort);
}
- void set_data_raw(stream_reader* p_stream, t_size, abort_callback& p_abort) {
+ void set_data_raw(stream_reader* p_stream, t_size, abort_callback& p_abort) override {
pfc::string8_fastalloc temp;
p_stream->read_string_raw(temp, p_abort);
set(temp);
}
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+ void downgrade_check(fb2k::configStore::ptr) override;
+#endif
public:
cfg_string_mt(const GUID& id, const char* defVal) : cfg_var(id), m_val(defVal) {}
- void get(pfc::string_base& out) const {
- inReadSync(m_sync);
- out = m_val;
- }
- void set(const char* val, t_size valLen = ~0) {
- inWriteSync(m_sync);
- m_val.set_string(val, valLen);
- }
+ void get(pfc::string_base& out) const { inReadSync(m_sync); out = m_val; }
+ pfc::string8 get() const { inReadSync(m_sync); return m_val; }
+ void set(const char* val, t_size valLen = SIZE_MAX) { inWriteSync(m_sync); m_val.set_string(val, valLen); }
+ void set( pfc::string8 && val ) { inWriteSync(m_sync); m_val = std::move(val); }
private:
mutable pfc::readWriteLock m_sync;
pfc::string8 m_val;
@@ -315,7 +356,10 @@ namespace cfg_var_legacy {
}
}
};
-}
+#if FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+#define FOOBAR2000_IMPLEMENT_CFG_VAR_DOWNGRADE FB2K_RUN_ON_INIT(cfg_var_reader::downgrade_main)
+#endif // FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE
+} // cfg_var_legacy
#else
namespace cfg_var_legacy {
// Dummy class
@@ -326,3 +370,7 @@ namespace cfg_var_legacy {
};
}
#endif
+
+#ifndef FOOBAR2000_IMPLEMENT_CFG_VAR_DOWNGRADE
+#define FOOBAR2000_IMPLEMENT_CFG_VAR_DOWNGRADE
+#endif
\ No newline at end of file
diff --git a/sdk/foobar2000/SDK/chapterizer.h b/sdk/foobar2000/SDK/chapterizer.h
index b066d78..14d3e6d 100644
--- a/sdk/foobar2000/SDK/chapterizer.h
+++ b/sdk/foobar2000/SDK/chapterizer.h
@@ -17,11 +17,15 @@ class NOVTABLE chapter_list {
//! Sets number of chapters.
virtual void set_chapter_count(t_size p_count) = 0;
//! Modifies description of specified chapter.
- //! @param p_chapter_index Index of chapter to modify, greater or equal zero and less than get_chapter_count() value. If p_chapter value is out of valid range, results are undefined (e.g. crash).
+ //! @param p_chapter Index of chapter to modify, greater or equal zero and less than get_chapter_count() value. If p_chapter value is out of valid range, results are undefined (e.g. crash).
//! @param p_info New chapter description. Note that length part of file_info is used to calculate chapter marks.
virtual void set_info(t_size p_chapter,const file_info & p_info) = 0;
+ //! Gets first track pregap - offset into audio at which first track begins.
+ //! Not every chapterizer supports this, see chapterizer::supports_pregaps()
virtual double get_pregap() const = 0;
+ //! Sets first track pregap - offset into audio at which first track begins.
+ //! Not every chapterizer supports this, see chapterizer::supports_pregaps()
virtual void set_pregap(double val) = 0;
//! Copies contents of specified chapter_list object to this object.
@@ -38,10 +42,9 @@ class NOVTABLE chapter_list {
template
class chapter_list_impl_t : public chapter_list {
public:
- chapter_list_impl_t() : m_pregap() {}
+ chapter_list_impl_t() {}
typedef chapter_list_impl_t t_self;
chapter_list_impl_t(const chapter_list & p_source) : m_pregap() {copy(p_source);}
-
const t_self & operator=(const chapter_list & p_source) {copy(p_source); return *this;}
t_size get_chapter_count() const {return m_infos.get_size();}
@@ -55,7 +58,7 @@ class chapter_list_impl_t : public chapter_list {
void set_pregap(double val) {PFC_ASSERT(val >= 0); m_pregap = val;}
private:
pfc::array_t m_infos;
- double m_pregap;
+ double m_pregap = 0;
};
typedef chapter_list_impl_t<> chapter_list_impl;
@@ -67,7 +70,6 @@ class NOVTABLE chapterizer : public service_base {
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(chapterizer);
public:
//! Tests whether specified path is supported by this implementation.
- //! @param p_ext Extension of the file being processed.
virtual bool is_our_path(const char * p_path) = 0;
//! Writes new chapter list to specified file.
@@ -81,6 +83,7 @@ class NOVTABLE chapterizer : public service_base {
//! @param p_abort abort_callback object signaling user aborting the operation.
virtual void get_chapters(const char * p_path,chapter_list & p_list,abort_callback & p_abort) = 0;
+ //! @returns Whether this chapterizer supports altering pregap before first track, see chapter_list::get_pregap() & set_pregap()
virtual bool supports_pregaps() = 0;
//! Static helper, tries to find chapterizer interface that supports specified file.
diff --git a/sdk/foobar2000/SDK/commandline.h b/sdk/foobar2000/SDK/commandline.h
index 5e230d2..6fadb7b 100644
--- a/sdk/foobar2000/SDK/commandline.h
+++ b/sdk/foobar2000/SDK/commandline.h
@@ -1,4 +1,6 @@
#pragma once
+
+//! Service for handling commandline arguments passed to foobar2000.exe
class NOVTABLE commandline_handler : public service_base
{
public:
@@ -9,23 +11,25 @@ class NOVTABLE commandline_handler : public service_base
RESULT_PROCESSED_EXPECT_FILES,//command processed, we want to takeover file urls after this command
};
virtual result on_token(const char * token)=0;
- virtual void on_file(const char * url) {};//optional
+ virtual void on_file(const char* url) { (void)url; };//optional
virtual void on_files_done() {};//optional
virtual bool want_directories() {return false;}
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(commandline_handler);
};
-class commandline_handler_metadb_handle : public commandline_handler//helper
-{
+//! Helper automatically turning passed file locations into metadb_handle objects (audio track references)
+class commandline_handler_metadb_handle : public commandline_handler {
protected:
- virtual void on_file(const char * url);
- virtual bool want_directories() {return true;}
+ void on_file(const char * url) override final;
+ bool want_directories() override {return true;}
public:
- virtual result on_token(const char * token)=0;
- virtual void on_files_done() {};
-
- virtual void on_file(const metadb_handle_ptr & ptr)=0;
+ //! Override me
+ virtual result on_token(const char * token) override = 0;
+ //! Override me
+ virtual void on_files_done() override {};
+ //! Override me
+ virtual void on_file(const metadb_handle_ptr & ptr) = 0;
};
/*
diff --git a/sdk/foobar2000/SDK/commonObjects-Apple.h b/sdk/foobar2000/SDK/commonObjects-Apple.h
new file mode 100644
index 0000000..2d36045
--- /dev/null
+++ b/sdk/foobar2000/SDK/commonObjects-Apple.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#ifdef __APPLE__
+
+namespace fb2k {
+ class NSObjectWrapper : public service_base {
+ FB2K_MAKE_SERVICE_INTERFACE(NSObjectWrapper, service_base);
+ public:
+ virtual void * get_() = 0;
+#ifdef __OBJC__
+ id get() { return (__bridge id) get_(); }
+#endif
+
+ };
+#ifdef __OBJC__
+ service_ptr wrapNSObject(id);
+ id unwrapNSObject(service_ptr);
+#endif
+}
+
+#endif
diff --git a/sdk/foobar2000/SDK/commonObjects-Apple.mm b/sdk/foobar2000/SDK/commonObjects-Apple.mm
new file mode 100644
index 0000000..2e6dac3
--- /dev/null
+++ b/sdk/foobar2000/SDK/commonObjects-Apple.mm
@@ -0,0 +1,29 @@
+#include "foobar2000-sdk-pch.h"
+#include "commonObjects-Apple.h"
+#include
+
+namespace {
+ class NSObjectWrapperImpl : public fb2k::NSObjectWrapper {
+ public:
+ id obj;
+ void * get_() override {
+ return (__bridge void*) obj;
+ }
+ };
+}
+namespace fb2k {
+ service_ptr wrapNSObject(id arg) {
+ if (!arg) return nullptr;
+ auto ret = fb2k::service_new();
+ ret->obj = arg;
+ return ret;
+ }
+ id unwrapNSObject(service_ptr arg) {
+ id ret = nil;
+ fb2k::NSObjectWrapper::ptr obj;
+ if ( obj &= arg ) {
+ ret = obj->get();
+ }
+ return ret;
+ }
+}
diff --git a/sdk/foobar2000/SDK/commonObjects.cpp b/sdk/foobar2000/SDK/commonObjects.cpp
index db1c930..41ab317 100644
--- a/sdk/foobar2000/SDK/commonObjects.cpp
+++ b/sdk/foobar2000/SDK/commonObjects.cpp
@@ -436,7 +436,7 @@ namespace fb2k {
auto obj = this->itemAt( n );
if ( obj == item ) return n;
}
- return pfc_infinite;
+ return SIZE_MAX;
}
array::ptr array::subset( pfc::bit_array const & mask ) const {
diff --git a/sdk/foobar2000/SDK/completion_notify.cpp b/sdk/foobar2000/SDK/completion_notify.cpp
index 2d2700a..fa971eb 100644
--- a/sdk/foobar2000/SDK/completion_notify.cpp
+++ b/sdk/foobar2000/SDK/completion_notify.cpp
@@ -56,7 +56,7 @@ namespace {
class completion_notify_func : public completion_notify {
public:
- void on_completion(unsigned p_code) {
+ void on_completion(unsigned p_code) noexcept override {
m_func(p_code);
}
@@ -67,7 +67,7 @@ namespace {
namespace fb2k {
completion_notify::ptr makeCompletionNotify( completionNotifyFunc_t func ) {
- service_ptr_t n = new service_impl_t< completion_notify_func >;
+ auto n = fb2k::service_new< completion_notify_func >();
n->m_func = func;
return n;
}
diff --git a/sdk/foobar2000/SDK/completion_notify.h b/sdk/foobar2000/SDK/completion_notify.h
index 6d92f56..eaefff6 100644
--- a/sdk/foobar2000/SDK/completion_notify.h
+++ b/sdk/foobar2000/SDK/completion_notify.h
@@ -7,6 +7,7 @@ class completion_notify : public service_base {
public:
//! Called when an async operation has been completed. Note that on_completion is always called from main thread. You can use on_completion_async() helper if you need to signal completion while your context is in another thread.\n
//! IMPLEMENTATION WARNING: If process being completed creates a window taking caller's window as parent, you must not destroy the parent window inside on_completion(). If you need to do so, use PostMessage() or main_thread_callback to delay the deletion.
+ //! IMPLEMENTATION NOTE: on_completion() couldn't be declared noexcept in base class for historical reasons, but it's recommended that your overrides of on_completion() are noexcept.
//! @param p_code Context-specific status code. Possible values depend on the operation being performed.
virtual void on_completion(unsigned p_code) = 0;
@@ -22,7 +23,7 @@ class completion_notify : public service_base {
//! Implementation helper.
class completion_notify_dummy : public completion_notify {
public:
- void on_completion(unsigned) {}
+ void on_completion(unsigned) override {}
};
//! Implementation helper.
diff --git a/sdk/foobar2000/SDK/componentversion.cpp b/sdk/foobar2000/SDK/componentversion.cpp
index f6055e2..1651a0f 100644
--- a/sdk/foobar2000/SDK/componentversion.cpp
+++ b/sdk/foobar2000/SDK/componentversion.cpp
@@ -10,6 +10,7 @@ bool component_installation_validator::test_my_name(const char * fn) {
path += pfc::scan_filename(path);
bool retVal = ( strcmp(path, fn) == 0 );
PFC_ASSERT( retVal );
+ if (!retVal) uAddDebugEvent(pfc::format("Component rename detected: ", fn, " >> ", path));
return retVal;
}
bool component_installation_validator::have_other_file(const char * fn) {
@@ -26,11 +27,11 @@ bool component_installation_validator::have_other_file(const char * fn) {
FB2K_console_formatter() << "Component integrity check error: " << e << " (on: " << fn << ")";
throw;
}
- } catch(exception_io_denied) {
+ } catch(exception_io_denied const &) {
- } catch(exception_io_sharing_violation) {
+ } catch(exception_io_sharing_violation const &) {
- } catch(exception_io_file_corrupted) { // happens
+ } catch(exception_io_file_corrupted const &) { // happens
return false;
} catch(...) {
uBugCheck();
diff --git a/sdk/foobar2000/SDK/componentversion.h b/sdk/foobar2000/SDK/componentversion.h
index ae3afa0..6edf038 100644
--- a/sdk/foobar2000/SDK/componentversion.h
+++ b/sdk/foobar2000/SDK/componentversion.h
@@ -1,3 +1,5 @@
+#pragma once
+
//! Entrypoint interface for declaring component's version information. Instead of implementing this directly, use DECLARE_COMPONENT_VERSION().
class NOVTABLE componentversion : public service_base {
public:
@@ -66,7 +68,6 @@ class componentversion_impl_copy_factory : public __componentversion_impl_copy_f
static componentversion_impl_copy_factory g_componentversion_service(NAME,VERSION,ABOUT);
-#ifdef _WIN32
//! \since 1.0
//! Allows components to cleanly abort app startup in case the installation appears to have become corrupted.
class component_installation_validator : public service_base {
@@ -79,6 +80,7 @@ class component_installation_validator : public service_base {
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(component_installation_validator)
};
+#ifdef _WIN32
//! Simple implementation of component_installation_validator that makes sure that our component DLL has not been renamed around by idiot users.
class component_installation_validator_filename : public component_installation_validator {
public:
diff --git a/sdk/foobar2000/SDK/configStore.cpp b/sdk/foobar2000/SDK/configStore.cpp
index 85e92ce..a5544a1 100644
--- a/sdk/foobar2000/SDK/configStore.cpp
+++ b/sdk/foobar2000/SDK/configStore.cpp
@@ -80,7 +80,9 @@ configEventHandle_t configEvent::operator+=(std::function< void() > f) const {
void configEvent::operator-=(configEventHandle_t h) const {
auto obj = reinterpret_cast(h);
- static_api_ptr_t()->removeNotify(m_name, obj);
+ if ( core_api::are_services_available() ) {
+ static_api_ptr_t()->removeNotify(m_name, obj);
+ }
delete obj;
}
@@ -153,7 +155,9 @@ void appearanceChangeNotifyRef::set( std::function f ) {
}
void appearanceChangeNotifyRef::clear() {
if (isSet()) {
- static_api_ptr_t()->removeNotify2(handle);
+ if ( core_api::are_services_available() ) {
+ static_api_ptr_t()->removeNotify2(handle);
+ }
handle = nullptr;
}
}
diff --git a/sdk/foobar2000/SDK/configStore.h b/sdk/foobar2000/SDK/configStore.h
index c6eabc0..4d57a04 100644
--- a/sdk/foobar2000/SDK/configStore.h
+++ b/sdk/foobar2000/SDK/configStore.h
@@ -18,35 +18,23 @@ typedef void * configStoreNotifyHandle_t;
//! Interface to access foobar2000's configuration store, backed by SQLite database. \n
//! get* methods can be called at any time. set*/delete* \n
//! set*/delete* methods will trigger immediate commit when invoked without a transaction scope. \n
-//! Use acquireTransactionScope() to commit multiple operations together. \n
-//! Transactions scope use a global reference counter; commit happens when all transaction scopes have exited. \n
//! Use commitBlocking() to commit synchronously to be sure that the data has been flushed before continuing execution.
class configStore : public service_base {
FB2K_MAKE_SERVICE_COREAPI( configStore );
public:
+ //! Causes multiple writes to be chained together. \n
+ //! Use of this is no longer essential since late foobar2000 v2.0 betas, as delay-write cache is always used. \n
+ //! You can still use it to guarantee that multiple updates are written together, that is either all or none are saved, should the system or application crash.
virtual fb2k::objRef acquireTransactionScope() = 0;
+
+ //! Synchronously flushes changes to disk. Doesn't return until changes have actually been written. \n
+ //! Use of this is strongly recommended against.
virtual void commitBlocking() = 0;
virtual int64_t getConfigInt( const char * name, int64_t defVal = 0 ) = 0;
virtual void setConfigInt( const char * name, int64_t val ) = 0;
virtual void deleteConfigInt(const char * name) = 0;
- //! Helper around getConfigInt.
- bool getConfigBool( const char * name, bool defVal = false );
- //! Helper around setConfigInt.
- void setConfigBool( const char * name, bool val );
- //! Helper around setConfigInt.
- bool toggleConfigBool (const char * name, bool defVal = false );
- //! Helper around deleteConfigInt.
- void deleteConfigBool( const char * name );
-
- //! Helper around getConfigString.
- GUID getConfigGUID(const char* name, const GUID& defVal = pfc::guid_null);
- //! Helper around setConfigString.
- void setConfigGUID(const char* name, const GUID& val);
- //! Helper around deleteConfigString.
- void deleteConfigGUID(const char* name);
-
virtual fb2k::stringRef getConfigString( const char * name, fb2k::stringRef defaVal = fb2k::string::stringWithString("")) = 0;
virtual void setConfigString( const char * name, const char * value ) = 0;
virtual void deleteConfigString(const char * name) = 0;
@@ -70,10 +58,27 @@ class configStore : public service_base {
objRef addNotify( const char * name, std::function f );
void addPermanentNotify( const char * name, std::function f );
+ //! Helper around getConfigInt.
+ bool getConfigBool( const char * name, bool defVal = false );
+ //! Helper around setConfigInt.
+ void setConfigBool( const char * name, bool val );
+ //! Helper around setConfigInt.
+ bool toggleConfigBool (const char * name, bool defVal = false );
+ //! Helper around deleteConfigInt.
+ void deleteConfigBool( const char * name );
+
+ //! Helper around getConfigString.
+ GUID getConfigGUID(const char* name, const GUID& defVal = pfc::guid_null);
+ //! Helper around setConfigString.
+ void setConfigGUID(const char* name, const GUID& val);
+ //! Helper around deleteConfigString.
+ void deleteConfigGUID(const char* name);
+
//! For internal core use, notifies everyone interested about off-process change to configuration data.
virtual void callNotify(const char * name) = 0;
fb2k::stringRef getConfigString( const char * name, const char * defVal ) { return getConfigString(name, defVal ? fb2k::makeString(defVal) : nullptr); }
+ fb2k::stringRef getConfigString( const char * name, std::nullptr_t ) { return getConfigString(name, fb2k::stringRef ( nullptr ) ); }
};
struct configEventHandle_;
@@ -125,4 +130,48 @@ stringRef val = api->getConfigString( "myComponent.foo", "defaultVal" );
// use api->commitBlocking()
}
*/
+
+
+//! \since 2.2
+class configStore2 : public configStore {
+ FB2K_MAKE_SERVICE_COREAPI_EXTENSION(configStore2, configStore);
+public:
+ // Use flagSuppressCache to prevent value from being cached. Prevents memory usage creep if querying lots of uniquely named variables.
+ static constexpr uint32_t flagSuppressCache = 1;
+
+ virtual int64_t getConfigInt2( const char * name, int64_t defVal = 0, uint32_t flags = 0 ) = 0;
+ virtual void setConfigInt2( const char * name, int64_t val, uint32_t flags = 0 ) = 0;
+ virtual void deleteConfigInt2(const char * name, uint32_t flags = 0) = 0;
+
+ virtual fb2k::stringRef getConfigString2( const char * name, fb2k::stringRef defaVal = fb2k::string::stringWithString(""), uint32_t flags = 0) = 0;
+ virtual void setConfigString2( const char * name, const char * value, uint32_t flags = 0 ) = 0;
+ virtual void deleteConfigString2(const char * name, uint32_t flags = 0) = 0;
+
+ virtual fb2k::memBlockRef getConfigBlob2( const char * name, fb2k::memBlockRef defVal = fb2k::memBlock::empty(), uint32_t flags = 0) = 0;
+ virtual void setConfigBlob2(const char* name, const void* ptr, size_t bytes, uint32_t flags = 0) = 0;
+ virtual void setConfigBlob2(const char* name, fb2k::memBlockRef val, uint32_t flags = 0) = 0;
+ virtual void deleteConfigBlob2(const char * name, uint32_t flags = 0) = 0;
+
+ virtual double getConfigFloat2( const char * name, double defVal = 0, uint32_t flags = 0) = 0;
+ virtual void setConfigFloat2( const char * name, double val, uint32_t flags = 0) = 0;
+ virtual void deleteConfigFloat2( const char * name, uint32_t flags = 0) = 0;
+
+
+ int64_t getConfigInt( const char * name, int64_t defVal ) override final { return getConfigInt2(name, defVal); }
+ void setConfigInt( const char * name, int64_t val ) override final { setConfigInt2(name, val); }
+ void deleteConfigInt(const char * name) override final { deleteConfigInt2(name); }
+
+ fb2k::stringRef getConfigString( const char * name, fb2k::stringRef defaVal) override final { return getConfigString2(name, defaVal); }
+ void setConfigString( const char * name, const char * value ) override final { setConfigString2(name, value); }
+ void deleteConfigString(const char * name) override final { deleteConfigString2(name); }
+
+ fb2k::memBlockRef getConfigBlob( const char * name, fb2k::memBlockRef defVal ) override final { return getConfigBlob2(name, defVal); }
+ void setConfigBlob(const char* name, const void* ptr, size_t bytes) override final { setConfigBlob2(name, ptr, bytes); }
+ void setConfigBlob(const char* name, fb2k::memBlockRef val) override final { setConfigBlob2(name, val); }
+ void deleteConfigBlob(const char * name) override final { deleteConfigBlob2(name); }
+
+ double getConfigFloat( const char * name, double defVal ) override final { return getConfigFloat2(name, defVal); }
+ void setConfigFloat( const char * name, double val ) override final { setConfigFloat2(name, val); }
+ void deleteConfigFloat( const char * name ) override final { deleteConfigFloat2(name); }
+};
} // namespace fb2k
diff --git a/sdk/foobar2000/SDK/config_object.cpp b/sdk/foobar2000/SDK/config_object.cpp
index edf4b01..8315b2a 100644
--- a/sdk/foobar2000/SDK/config_object.cpp
+++ b/sdk/foobar2000/SDK/config_object.cpp
@@ -104,6 +104,7 @@ namespace {
class stream_writer_string : public stream_writer {
public:
void write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
+ p_abort.check();
m_out.add_string((const char*)p_buffer,p_bytes);
}
stream_writer_string(pfc::string_base & p_out) : m_out(p_out) {m_out.reset();}
@@ -114,6 +115,7 @@ namespace {
class stream_writer_fixedbuffer : public stream_writer {
public:
void write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
+ p_abort.check();
if (p_bytes > 0) {
if (p_bytes > m_bytes - m_bytes_read) throw pfc::exception_overflow();
memcpy((t_uint8*)m_out,p_buffer,p_bytes);
@@ -131,7 +133,8 @@ namespace {
class stream_writer_get_length : public stream_writer {
public:
- void write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
+ void write(const void * ,t_size p_bytes,abort_callback & p_abort) override {
+ p_abort.check();
m_length += p_bytes;
}
stream_writer_get_length(t_size & p_length) : m_length(p_length) {m_length = 0;}
diff --git a/sdk/foobar2000/SDK/config_object_impl.h b/sdk/foobar2000/SDK/config_object_impl.h
index 7a9fef3..59f3da5 100644
--- a/sdk/foobar2000/SDK/config_object_impl.h
+++ b/sdk/foobar2000/SDK/config_object_impl.h
@@ -39,7 +39,7 @@ class config_object_impl : public config_object, private cfg_var_legacy::cfg_var
config_object_impl(const GUID & p_guid,const void * p_data,t_size p_bytes);
private:
#ifdef FOOBAR2000_HAVE_CFG_VAR_LEGACY
- void set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) override {set_data(p_stream,p_abort,false);}
+ void set_data_raw(stream_reader * p_stream,t_size,abort_callback & p_abort) override {set_data(p_stream,p_abort,false);}
#endif
pfc::string8 formatName() const;
@@ -82,9 +82,9 @@ class config_object_string_factory : public config_object_factory {
class config_object_notify_impl_simple : public config_object_notify
{
public:
- t_size get_watched_object_count() {return 1;}
- GUID get_watched_object(t_size p_index) {return m_guid;}
- void on_watched_object_changed(const service_ptr_t & p_object) {m_func(p_object);}
+ t_size get_watched_object_count() override {return 1;}
+ GUID get_watched_object(t_size) override {return m_guid;}
+ void on_watched_object_changed(const service_ptr_t & p_object) override {m_func(p_object);}
typedef void (*t_func)(const service_ptr_t &);
diff --git a/sdk/foobar2000/SDK/console_manager.h b/sdk/foobar2000/SDK/console_manager.h
index d14ce0a..262684e 100644
--- a/sdk/foobar2000/SDK/console_manager.h
+++ b/sdk/foobar2000/SDK/console_manager.h
@@ -6,7 +6,7 @@ namespace fb2k {
class NOVTABLE console_notify {
public:
virtual void onConsoleRefresh() = 0;
- virtual void onConsoleLines(size_t oldLinesGone, arrayRef newLines, arrayRef newLinesTS) { onConsoleRefresh(); }
+ virtual void onConsoleLines(size_t oldLinesGone, arrayRef newLines, arrayRef newLinesTS) { (void)oldLinesGone; (void)newLines; (void)newLinesTS; onConsoleRefresh(); }
};
//! \since 2.0
class NOVTABLE console_manager : public service_base {
@@ -17,7 +17,9 @@ namespace fb2k {
virtual fb2k::arrayRef getLinesTimestamped() = 0;
virtual void addNotify(console_notify* notify) = 0;
virtual void removeNotify(console_notify* notify) = 0;
+ //! Obsolete, done implicitly by toggling logging, do not use.
virtual void saveBacklog() = 0;
+ //! Always true, reserved for future use.
virtual bool isVerbose() = 0;
};
} // namespace fb2k
diff --git a/sdk/foobar2000/SDK/contextmenu.h b/sdk/foobar2000/SDK/contextmenu.h
index 563da75..5a6d864 100644
--- a/sdk/foobar2000/SDK/contextmenu.h
+++ b/sdk/foobar2000/SDK/contextmenu.h
@@ -1,5 +1,6 @@
#pragma once
#include "metadb_handle.h"
+#include
//! Reserved for future use.
typedef void * t_glyph;
@@ -27,7 +28,7 @@ class NOVTABLE contextmenu_item_node {
virtual bool get_display_data(pfc::string_base & p_out,unsigned & p_displayflags,metadb_handle_list_cref p_data,const GUID & p_caller) = 0;
virtual t_type get_type() = 0;
virtual void execute(metadb_handle_list_cref p_data,const GUID & p_caller) = 0;
- virtual t_glyph get_glyph(metadb_handle_list_cref p_data,const GUID & p_caller) {return 0;}//RESERVED
+ virtual t_glyph get_glyph(metadb_handle_list_cref p_data, const GUID& p_caller) { (void)p_data; (void)p_caller; return 0; }//RESERVED
virtual t_size get_children_count() = 0;
virtual contextmenu_item_node * get_child(t_size p_index) = 0;
virtual bool get_description(pfc::string_base & p_out) = 0;
@@ -64,35 +65,36 @@ class NOVTABLE contextmenu_item_node_root_leaf : public contextmenu_item_node_ro
class NOVTABLE contextmenu_item_node_popup : public contextmenu_item_node
{
public:
- t_type get_type() {return TYPE_POPUP;}
- void execute(metadb_handle_list_cref data,const GUID & caller) {}
- bool get_description(pfc::string_base & p_out) {return false;}
+ t_type get_type() override {return TYPE_POPUP;}
+ void execute(metadb_handle_list_cref data, const GUID& caller) override { (void)data; (void)caller; }
+ bool get_description(pfc::string_base& p_out) override { (void)p_out; return false; }
};
class NOVTABLE contextmenu_item_node_root_popup : public contextmenu_item_node_root
{
public:
- t_type get_type() {return TYPE_POPUP;}
- void execute(metadb_handle_list_cref data,const GUID & caller) {}
- bool get_description(pfc::string_base & p_out) {return false;}
+ t_type get_type() override {return TYPE_POPUP;}
+ void execute(metadb_handle_list_cref data, const GUID& caller) override { (void)data; (void)caller; }
+ bool get_description(pfc::string_base& p_out) override { (void)p_out; return false; }
};
class contextmenu_item_node_separator : public contextmenu_item_node
{
public:
- t_type get_type() {return TYPE_SEPARATOR;}
- void execute(metadb_handle_list_cref data,const GUID & caller) {}
- bool get_description(pfc::string_base & p_out) {return false;}
- t_size get_children_count() {return 0;}
- bool get_display_data(pfc::string_base & p_out,unsigned & p_displayflags,metadb_handle_list_cref p_data,const GUID & p_caller)
+ t_type get_type() override {return TYPE_SEPARATOR;}
+ void execute(metadb_handle_list_cref data, const GUID& caller) override { (void)data; (void)caller; }
+ bool get_description(pfc::string_base& p_out) override { (void)p_out; return false; }
+ t_size get_children_count() override {return 0;}
+ bool get_display_data(pfc::string_base & p_out,unsigned & p_displayflags,metadb_handle_list_cref p_data,const GUID & p_caller) override
{
+ (void)p_data; (void)p_caller;
p_displayflags = 0;
p_out = "---";
return true;
}
- contextmenu_item_node * get_child(t_size) {return NULL;}
- GUID get_guid() {return pfc::guid_null;}
- bool is_mappable_shortcut() {return false;}
+ contextmenu_item_node * get_child(t_size) override {return NULL;}
+ GUID get_guid() override {return pfc::guid_null;}
+ bool is_mappable_shortcut() override {return false;}
};
/*!
@@ -117,7 +119,7 @@ class NOVTABLE contextmenu_item : public service_base {
//! Retrieves human-readable name of the context menu item.
virtual void get_item_name(unsigned p_index,pfc::string_base & p_out) = 0;
//! Obsolete since v1.0, don't use or override in new components.
- virtual void get_item_default_path(unsigned p_index,pfc::string_base & p_out) {p_out = "";}
+ virtual void get_item_default_path(unsigned p_index, pfc::string_base& p_out) { (void)p_index; p_out = ""; }
//! Retrieves item's description to show in the status bar. Set p_out to the string to be displayed and return true if you provide a description, return false otherwise.
virtual bool get_item_description(unsigned p_index,pfc::string_base & p_out) = 0;
//! Controls default state of context menu preferences for this item: \n
@@ -169,11 +171,12 @@ class NOVTABLE contextmenu_item_simple : public contextmenu_item_v2 {
// Functions to be overridden by implementers (some are not mandatory).
- virtual t_enabled_state get_enabled_state(unsigned p_index) {return contextmenu_item::DEFAULT_ON;}
+ virtual t_enabled_state get_enabled_state(unsigned p_index) { (void)p_index; return contextmenu_item::DEFAULT_ON; }
virtual unsigned get_num_items() = 0;
virtual void get_item_name(unsigned p_index,pfc::string_base & p_out) = 0;
virtual void context_command(unsigned p_index,metadb_handle_list_cref p_data,const GUID& p_caller) = 0;
virtual bool context_get_display(unsigned p_index,metadb_handle_list_cref p_data,pfc::string_base & p_out,unsigned & p_displayflags,const GUID & p_caller) {
+ (void)p_caller; (void)p_data; (void)p_displayflags;
PFC_ASSERT(p_index>=0 && p_index(guid, parent, name, sortPriority) {}
};
+
+
+
+class contextmenu_item_lambda : public contextmenu_item_simple {
+public:
+ typedef std::function func_t;
+ contextmenu_item_lambda(func_t f, const char* n, const GUID& g, const GUID& pg, const char* d = nullptr, double sp = 0) : m_func(f), m_name(n), m_guid(g), m_parentGuid(pg), m_desc(d), m_sortPriority(sp) {}
+
+ unsigned get_num_items() override { return 1; }
+ void get_item_name(unsigned p_index, pfc::string_base& p_out) override { (void)p_index; p_out = m_name; }
+ void context_command(unsigned p_index, metadb_handle_list_cref p_data, const GUID& p_caller) override { (void)p_index; (void)p_caller; m_func(p_data); }
+ GUID get_item_guid(unsigned p_index) override { (void)p_index; return m_guid; }
+ bool get_item_description(unsigned p_index, pfc::string_base& p_out) override {
+ (void)p_index;
+ if (m_desc == nullptr) return false;
+ p_out = m_desc;
+ return true;
+ }
+ double get_sort_priority() override { return m_sortPriority; }
+ GUID get_parent() override { return m_parentGuid; }
+private:
+ const std::function m_func;
+ const char* const m_name;
+ const GUID m_guid, m_parentGuid;
+ const char* const m_desc;
+ const double m_sortPriority;
+};
+
+#define FB2K_DECLARE_CONTEXT_MENU_ITEM(func, name, guid, parent, desc, sort) FB2K_SERVICE_FACTORY_PARAMS(contextmenu_item_lambda, func, name, guid, parent, desc, sort)
diff --git a/sdk/foobar2000/SDK/contextmenu_manager.h b/sdk/foobar2000/SDK/contextmenu_manager.h
index 9c7e322..7b71321 100644
--- a/sdk/foobar2000/SDK/contextmenu_manager.h
+++ b/sdk/foobar2000/SDK/contextmenu_manager.h
@@ -121,7 +121,7 @@ class NOVTABLE contextmenu_manager : public service_base
virtual void init_context_ex(metadb_handle_list_cref data,unsigned flags,const GUID & caller)=0;
virtual bool init_context_now_playing(unsigned flags)=0;//returns false if not playing
- bool execute_by_id(unsigned id);
+ bool execute_by_id(unsigned id) noexcept;
bool get_description_by_id(unsigned id,pfc::string_base & out);
diff --git a/sdk/foobar2000/SDK/core_api.h b/sdk/foobar2000/SDK/core_api.h
index afb42ff..df75f88 100644
--- a/sdk/foobar2000/SDK/core_api.h
+++ b/sdk/foobar2000/SDK/core_api.h
@@ -10,7 +10,8 @@ namespace core_api {
const char * get_my_file_name();
//! Retrieves full path of calling dll, e.g. c:\blah\foobar2000\foo_asdf.dll . No file:// prefix, this path can interop with win32 API calls.
const char * get_my_full_path();
- //! Retrieves main app window. WARNING: this is provided for parent of dialog windows and such only; using it for anything else (such as hooking windowproc to alter app behaviors) is absolutely illegal.
+ //! Retrieves main app window. WARNING: this is provided for parent of dialog windows and such only; using it for anything else (such as hooking windowproc to alter app behaviors) is absolutely illegal. \n
+ //! Becomes valid when main window has been fully initialized. Returns NULL during creation of main window's embedded elements.
fb2k::hwnd_t get_main_window();
//! Tests whether services are available at this time. They are not available only during DLL startup or shutdown (e.g. inside static object constructors or destructors).
bool are_services_available();
@@ -39,10 +40,13 @@ namespace core_api {
bool is_quiet_mode_enabled();
};
+#define FB2K_SUPPORT_LOW_MEM_MODE (SIZE_MAX <= UINT32_MAX)
+
namespace fb2k {
-#ifdef _WIN32
- inline bool isDebugModeActive() { return !! PFC_DEBUG ;}
-#else
bool isDebugModeActive();
+#if FB2K_SUPPORT_LOW_MEM_MODE
+ bool isLowMemModeActive();
+#else
+ inline constexpr bool isLowMemModeActive() { return false; }
#endif
}
diff --git a/sdk/foobar2000/SDK/decode_postprocessor.h b/sdk/foobar2000/SDK/decode_postprocessor.h
index 2a31034..8767139 100644
--- a/sdk/foobar2000/SDK/decode_postprocessor.h
+++ b/sdk/foobar2000/SDK/decode_postprocessor.h
@@ -20,8 +20,12 @@ class decode_postprocessor_instance : public service_base {
};
//! @returns True if the chunk list has been altered by the call, false if not - to tell possible other running instances whether the stream has already been altered or not.
virtual bool run(dsp_chunk_list & p_chunk_list,t_uint32 p_flags,abort_callback & p_abort) = 0;
+ //! Manipulate live info (bit depth, bitrate, whatever) that might be altered by us.
virtual bool get_dynamic_info(file_info & p_out) = 0;
+ //! Called after seek.
virtual void flush() = 0;
+ //! Return >0 to signal that, after each flush(), you require of audio before your output becomes valid. \n
+ //! This causes decoder to seek to position- instead of then discard first of your output.
virtual double get_buffer_ahead() = 0;
};
diff --git a/sdk/foobar2000/SDK/dsp.cpp b/sdk/foobar2000/SDK/dsp.cpp
index 98addd5..1f596c7 100644
--- a/sdk/foobar2000/SDK/dsp.cpp
+++ b/sdk/foobar2000/SDK/dsp.cpp
@@ -22,40 +22,41 @@ void dsp_chunk_list::add_chunk(const audio_chunk * chunk) {
if (dst) dst->copy(*chunk);
}
-t_size dsp_chunk_list_impl::get_count() const {return m_data.get_count();}
+t_size dsp_chunk_list_impl::get_count() const {return m_data.size();}
-audio_chunk * dsp_chunk_list_impl::get_item(t_size n) const {return nmax) idx = max;
- pfc::rcptr_t ret;
- if (m_recycled.get_count()>0)
+ chunk_ptr_t ret;
+ if (!m_recycled.empty())
{
t_size best;
- if (hint_size>0)
+ if (hint_size > 0)
{
best = 0;
- t_size best_found = m_recycled[0]->get_data_size(), n, total = m_recycled.get_count();
- for(n=1;nget_data_size(), n, total = m_recycled.size();
+ for (n = 1; n < total; n++)
{
- if (best_found==hint_size) break;
+ if (best_found == hint_size) break;
t_size size = m_recycled[n]->get_data_size();
int delta_old = abs((int)best_found - (int)hint_size), delta_new = abs((int)size - (int)hint_size);
if (delta_new < delta_old)
@@ -64,18 +65,18 @@ audio_chunk * dsp_chunk_list_impl::insert_item(t_size idx,t_size hint_size)
best = n;
}
}
- }
- else best = m_recycled.get_count()-1;
+ } else best = m_recycled.size() - 1;
- ret = m_recycled.remove_by_idx(best);
+ ret = std::move(m_recycled[best]);
+ m_recycled.erase(m_recycled.begin() + best);
ret->set_sample_count(0);
ret->set_channels(0);
ret->set_srate(0);
- }
- else ret = pfc::rcnew_t();
- if (idx==max) m_data.add_item(ret);
- else m_data.insert_item(ret,idx);
- return &*ret;
+ } else ret = std::make_unique();
+ auto pRet = &*ret;
+ if (idx == max) m_data.push_back(std::move(ret));
+ else m_data.insert(m_data.begin() + idx, std::move(ret));
+ return pRet;
}
void dsp_chunk_list::remove_bad_chunks()
@@ -119,10 +120,17 @@ bool dsp_entry_hidden::g_instantiate( dsp::ptr & out, const dsp_preset & preset
return i->instantiate(out, preset);
}
-bool dsp_entry::g_instantiate(service_ptr_t & p_out,const dsp_preset & p_preset)
+bool dsp_entry::g_instantiate(service_ptr_t & p_out,const dsp_preset & p_preset, unsigned flags )
{
service_ptr_t ptr;
if (!g_get_interface(ptr,p_preset.get_owner())) return false;
+ if ( flags != 0 ) {
+ dsp_entry_v4::ptr v4;
+ if (v4 &= ptr) {
+ p_out = v4->instantiate_v4(p_preset, flags);
+ return true;
+ }
+ }
return ptr->instantiate(p_out,p_preset);
}
@@ -216,17 +224,59 @@ void dsp_chain_config::remove_all()
remove_mask(pfc::bit_array_true());
}
-void dsp_chain_config::instantiate(service_list_t & p_out)
-{
- p_out.remove_all();
- t_size n, m = get_count();
- for(n=0;n temp;
- auto const & preset = this->get_item(n);
- if (dsp_entry::g_instantiate(temp,preset) || dsp_entry_hidden::g_instantiate(temp, preset))
- p_out.add_item(temp);
- }
+size_t dsp_chain_config::find_first_of_type( const GUID & dspID ) const {
+ const size_t count = this->get_count();
+ for(size_t w = 0; w < count; ++w) {
+ if (this->get_item(w).get_owner() == dspID) return w;
+ }
+ return SIZE_MAX;
+}
+
+bool dsp_chain_config::contains_dsp( const GUID & dspID ) const {
+ return find_first_of_type( dspID ) != pfc_infinite;
+}
+
+bool dsp_chain_config::enable_dsp( const GUID & dspID ) {
+ if (this->contains_dsp( dspID )) return false;
+ dsp_preset_impl preset;
+ dsp_entry::g_get_default_preset( preset, dspID );
+ insert_item( preset, 0 );
+ return true;
+}
+
+bool dsp_chain_config::disable_dsp( const GUID & dspID ) {
+ const size_t count = this->get_count();
+ if (count == 0) return false;
+ bool rv = false;
+ pfc::bit_array_bittable mask( count );
+ for(size_t w = 0; w < count; ++ w) {
+ if (this->get_item(w).get_owner() == dspID ) {
+ rv = true;
+ mask.set(w, true);
+ }
+ }
+ if (rv) this->remove_mask( mask );
+ return rv;
+}
+
+bool dsp_chain_config::enable_dsp( const dsp_preset & preset ) {
+ dsp_chain_config & cfg = *this;
+ bool found = false;
+ bool changed = false;
+ t_size n,m = cfg.get_count();
+ for(n=0;n 0) p_stream->read_object(m_data.ptr(),p_bytes,p_abort);
}
+void dsp_chain_config::add_items(const dsp_chain_config & p_source) {
+ t_size n, m = p_source.get_count();
+ for(n=0;nshow_config_popup_v3(parent, callback);
- } catch (pfc::exception_not_implemented) {
+ } catch (pfc::exception_not_implemented const &) {
}
}
@@ -457,12 +511,6 @@ void dsp_entry::g_show_config_popup_v2(const dsp_preset & p_preset,fb2k::hwnd_t
}
#endif
-#ifndef _WIN32
-service_ptr dsp_entry::show_config_popup( fb2k::hwnd_t, dsp_preset_edit_callback_v2::ptr ) {
- throw pfc::exception_not_implemented();
-}
-#endif
-
service_ptr_t dsp_entry::g_get_interface(const GUID& guid) {
for (auto ptr : enumerate()) {
if (ptr->get_guid() == guid) return ptr;
@@ -483,12 +531,14 @@ bool dsp_entry::g_get_interface(service_ptr_t & p_out,const GUID & p_
bool resampler_entry::g_get_interface(service_ptr_t & p_out,unsigned p_srate_from,unsigned p_srate_to)
{
-#if FOOBAR2000_TARGET_VERSION >= 79
+#if defined(FOOBAR2000_DESKTOP) && FOOBAR2000_TARGET_VERSION >= 79
auto r = resampler_manager::get()->get_resampler( p_srate_from, p_srate_to );
bool v = r.is_valid();
if ( v ) p_out = std::move(r);
return v;
#else
+
+#ifdef FOOBAR2000_DESKTOP
{
resampler_manager::ptr api;
if ( resampler_manager::tryGet(api) ) {
@@ -498,6 +548,7 @@ bool resampler_entry::g_get_interface(service_ptr_t & p_out,uns
return v;
}
}
+#endif
resampler_entry::ptr ptr_resampler;
service_enum_t e;
@@ -590,7 +641,7 @@ pfc::string8 dsp_chain_config::get_name_list() const {
return output;
}
-void dsp::run_abortable(dsp_chunk_list * p_chunk_list,const metadb_handle_ptr & p_cur_file,int p_flags,abort_callback & p_abort) {
+void dsp::run_abortable(dsp_chunk_list * p_chunk_list,const dsp_track_t & p_cur_file,int p_flags,abort_callback & p_abort) {
service_ptr_t this_v2;
if (this->service_query_t(this_v2)) this_v2->run_v2(p_chunk_list,p_cur_file,p_flags,p_abort);
else run(p_chunk_list,p_cur_file,p_flags);
@@ -628,6 +679,23 @@ bool dsp_entry_v2::show_config_popup(dsp_preset & p_data,fb2k::hwnd_t p_parent)
}
#endif
+#ifdef FOOBAR2000_MOBILE
+void dsp_entry::g_show_config_popup( menu_context_ptr ctx, dsp_preset_edit_callback_v2::ptr callback) {
+ GUID dspID;
+ {
+ dsp_preset_impl temp;
+ callback->get_preset( temp );
+ dspID = temp.get_owner();
+ }
+
+ dsp_entry::ptr entry;
+ if (!g_get_interface( entry, dspID)) return;
+ if (!entry->have_config_popup()) return;
+ entry->show_config_popup( ctx, callback );
+}
+#endif // FOOBAR2000_MOBILE
+
+#ifdef FOOBAR2000_DESKTOP
void resampler_manager::make_chain_(dsp_chain_config& outChain, unsigned rateFrom, unsigned rateTo, float qualityScale) {
resampler_manager_v2::ptr v2;
if (v2 &= this) {
@@ -643,6 +711,7 @@ void resampler_manager::make_chain_(dsp_chain_config& outChain, unsigned rateFro
}
}
}
+#endif
void dsp_preset_edit_callback_v2::reset() {
dsp_preset_impl temp; get_preset( temp );
@@ -668,4 +737,25 @@ void dsp_entry::get_display_name_(const dsp_preset& arg, pfc::string_base& out)
get_name(out);
}
+bool dsp_entry::enumerate_default_presets_(dsp_chain_config& ret) {
+ ret.remove_all();
+ dsp_entry_v5::ptr v5;
+ if (v5 &= this) {
+ bool rv = v5->enumerate_default_presets(ret);
+#if PFC_DEBUG
+ for (size_t walk = 0; walk < ret.get_count(); ++walk) {
+ PFC_ASSERT(ret.get_item(walk).get_owner() == get_guid());
+ }
+#endif
+ return rv;
+ }
+ return false;
+}
+
+bool dsp_entry::match_preset_subclass_(dsp_preset const& x, dsp_preset const& y) {
+ dsp_entry_v5::ptr v5;
+ if (v5 &= this) return v5->match_preset_subclass(x, y);
+ return true;
+}
+
#endif // FOOBAR2000_HAVE_DSP
diff --git a/sdk/foobar2000/SDK/dsp.h b/sdk/foobar2000/SDK/dsp.h
index 95d6e14..03c8541 100644
--- a/sdk/foobar2000/SDK/dsp.h
+++ b/sdk/foobar2000/SDK/dsp.h
@@ -4,7 +4,20 @@
#ifdef FOOBAR2000_HAVE_DSP
-class dsp_preset; // forward declaration
+#ifdef FOOBAR2000_MOBILE
+#include "dsp-context.h"
+#endif
+
+#include
+#include
+
+class dsp_preset; class dsp_chain_config; // forward declaration
+
+#ifdef FOOBAR2000_HAVE_METADB
+typedef metadb_handle_ptr dsp_track_t;
+#else
+typedef trackRef dsp_track_t;
+#endif
//! Interface to a DSP chunk list. A DSP chunk list object is passed to the DSP chain each time, since DSPs are allowed to remove processed chunks or insert new ones.
class NOVTABLE dsp_chunk_list {
@@ -31,13 +44,19 @@ class NOVTABLE dsp_chunk_list {
class dsp_chunk_list_impl : public dsp_chunk_list//implementation
{
- pfc::list_t > m_data, m_recycled;
+ typedef std::unique_ptr chunk_ptr_t;
+ std::vector m_data, m_recycled;
public:
+ dsp_chunk_list_impl() {}
+ dsp_chunk_list_impl(const dsp_chunk_list_impl&) = delete;
+ void operator=(const dsp_chunk_list_impl&) = delete;
t_size get_count() const;
audio_chunk * get_item(t_size n) const;
void remove_by_idx(t_size idx);
void remove_mask(const bit_array & mask);
audio_chunk * insert_item(t_size idx,t_size hint_size=0);
+
+ audio_chunk_impl* get_item_(size_t n) const { return m_data[n].get(); }
};
//! Instance of a DSP.\n
@@ -55,7 +74,7 @@ class NOVTABLE dsp : public service_base {
//! @param p_chunk_list List of chunks to process. The implementation may alter the list in any way, inserting chunks of different sample rate / channel configuration etc.
//! @param p_cur_file Optional, location of currently decoded file. May be null.
//! @param p_flags Flags. Can be null, or a combination of END_OF_TRACK and FLUSH constants.
- virtual void run(dsp_chunk_list * p_chunk_list,const metadb_handle_ptr & p_cur_file,int p_flags)=0;
+ virtual void run(dsp_chunk_list * p_chunk_list,const dsp_track_t & p_cur_file,int p_flags)=0;
//! Flushes the DSP (reinitializes / drops any buffered data). Called after seeking, etc.
virtual void flush() = 0;
@@ -68,7 +87,7 @@ class NOVTABLE dsp : public service_base {
//! Signaling this will often break regular gapless playback so don't use it unless you have reasons to.
virtual bool need_track_change_mark() = 0;
- void run_abortable(dsp_chunk_list * p_chunk_list,const metadb_handle_ptr & p_cur_file,int p_flags,abort_callback & p_abort);
+ void run_abortable(dsp_chunk_list * p_chunk_list,const dsp_track_t & p_cur_file,int p_flags,abort_callback & p_abort);
//! Attempts to apply preset without recreating the DSP, if supported.
//! @returns True on success, false if not supported (DSP needs re-creating).
@@ -81,9 +100,9 @@ class NOVTABLE dsp : public service_base {
class NOVTABLE dsp_v2 : public dsp {
public:
//! Abortable version of dsp::run(). See dsp::run() for descriptions of parameters.
- virtual void run_v2(dsp_chunk_list * p_chunk_list,const metadb_handle_ptr & p_cur_file,int p_flags,abort_callback & p_abort) = 0;
+ virtual void run_v2(dsp_chunk_list * p_chunk_list,const dsp_track_t & p_cur_file,int p_flags,abort_callback & p_abort) = 0;
private:
- void run(dsp_chunk_list * p_chunk_list,const metadb_handle_ptr & p_cur_file,int p_flags) {
+ void run(dsp_chunk_list * p_chunk_list,const dsp_track_t & p_cur_file,int p_flags) {
run_v2(p_chunk_list,p_cur_file,p_flags,fb2k::noAbort);
}
@@ -114,19 +133,19 @@ class dsp_impl_base_t : public t_baseclass {
typedef dsp_impl_base_t t_self;
dsp_chunk_list * m_list = nullptr;
t_size m_chunk_ptr = 0;
- metadb_handle* m_cur_file = nullptr;
- void run_v2(dsp_chunk_list * p_list,const metadb_handle_ptr & p_cur_file,int p_flags,abort_callback & p_abort) override;
+ dsp_track_t m_cur_file = nullptr;
+ void run_v2(dsp_chunk_list * p_list,const dsp_track_t & p_cur_file,int p_flags,abort_callback & p_abort) override;
protected:
//! Call only from on_chunk / on_endoftrack (on_endoftrack will give info on track being finished).\n
//! May return false when there's no known track and the metadb_handle ptr will be empty/null.
- bool get_cur_file(metadb_handle_ptr & p_out) const {p_out = m_cur_file; return p_out.is_valid();}
- metadb_handle_ptr get_cur_file() const { return m_cur_file; }
+ bool get_cur_file(dsp_track_t & p_out) const {p_out = m_cur_file; return p_out.is_valid();}
+ dsp_track_t get_cur_file() const { return m_cur_file; }
dsp_impl_base_t() {}
//! Inserts a new chunk of audio data. \n
//! You can call this only from on_chunk(), on_endofplayback() and on_endoftrack(). You're NOT allowed to call this from flush() which should just drop any queued data.
- //! @param hint_size Optional, amount of buffer space that you require (in audio_samples). This is just a hint for memory allocation logic and will not cause the framework to allocate the chunk for you.
+ //! @param p_hint_size Optional, amount of buffer space that you require (in audio_samples). This is just a hint for memory allocation logic and will not cause the framework to allocate the chunk for you.
//! @returns A pointer to the newly allocated chunk. Pass the audio data you want to insert to this chunk object. The chunk is owned by the framework, you can't delete it etc.
audio_chunk * insert_chunk(t_size p_hint_size = 0) {
PFC_ASSERT(m_list != NULL);
@@ -176,9 +195,9 @@ class dsp_impl_base_t : public t_baseclass {
};
template
-void dsp_impl_base_t::run_v2(dsp_chunk_list * p_list,const metadb_handle_ptr & p_cur_file,int p_flags,abort_callback & p_abort) {
+void dsp_impl_base_t::run_v2(dsp_chunk_list * p_list,const dsp_track_t & p_cur_file,int p_flags,abort_callback & p_abort) {
pfc::vartoggle_t l_list_toggle(m_list,p_list);
- pfc::vartoggle_t l_cur_file_toggle(m_cur_file,p_cur_file.get_ptr());
+ auto track_toggle = pfc::autoToggle(m_cur_file, p_cur_file);
for(m_chunk_ptr = 0;m_chunk_ptrget_count();m_chunk_ptr++) {
audio_chunk * c = m_list->get_item(m_chunk_ptr);
@@ -326,21 +345,33 @@ class NOVTABLE dsp_entry : public service_base {
virtual bool instantiate(service_ptr_t & p_out,const dsp_preset & p_preset) = 0;
virtual GUID get_guid() = 0;
virtual bool have_config_popup() = 0;
+
+#ifdef FOOBAR2000_MOBILE
+ virtual void show_config_popup( fb2k::dspConfigContext_t parent, dsp_preset_edit_callback_v2::ptr callback ) {}
+#endif
+
+#ifdef FOOBAR2000_DESKTOP
#ifdef _WIN32
//! Shows configuration popup. Call from main thread only! \n
//! Blocks until done. Returns true if preset has been altered, false otherwise.
- virtual bool show_config_popup(dsp_preset & p_data,fb2k::hwnd_t p_parent) = 0;
-#else
- //! Shows configuration popup. Main thread only!
- virtual service_ptr show_config_popup( fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback );
+ //! Legacy method, replaced in dsp_entry_v2 and newer.
+ virtual bool show_config_popup(dsp_preset& p_data, fb2k::hwnd_t p_parent) { (void)p_data; (void)p_parent; return false; }
+#else // non-Windows desktop
+ //! Shows configuration popup. Main thread only! \n
+ //! Mac: returns NSObjectWrapper holding NSViewController
+ virtual service_ptr show_config_popup(fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback) { (void)parent; (void)callback; throw pfc::exception_not_implemented(); }
#endif
+#endif // FOOBAR2000_DESKTOP
//! Obsolete method, hidden DSPs now use a different entry class.
bool is_user_accessible() { return true; }
+ static constexpr unsigned flag_playback = 1 << 0,
+ flag_conversion = 1 << 1;
+
static bool g_get_interface(service_ptr_t & p_out,const GUID & p_guid);
static service_ptr_t g_get_interface(const GUID&);
- static bool g_instantiate(service_ptr_t & p_out,const dsp_preset & p_preset);
+ static bool g_instantiate(service_ptr_t & p_out,const dsp_preset & p_preset, unsigned flags = 0);
static bool g_instantiate_default(service_ptr_t & p_out,const GUID & p_guid);
static bool g_name_from_guid(pfc::string_base & p_out,const GUID & p_guid);
static bool g_dsp_exists(const GUID & p_guid);
@@ -367,8 +398,14 @@ class NOVTABLE dsp_entry : public service_base {
service_ptr show_config_popup_v3_(fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback);
#endif
+#ifdef FOOBAR2000_MOBILE
+ static void g_show_config_popup( fb2k::dspConfigContext_t parent, dsp_preset_edit_callback_v2::ptr callback);
+#endif
+
bool get_display_name_supported();
void get_display_name_(const dsp_preset& arg, pfc::string_base& out);
+ bool enumerate_default_presets_(dsp_chain_config& ret);
+ bool match_preset_subclass_(dsp_preset const& x, dsp_preset const& y);
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(dsp_entry);
};
@@ -378,9 +415,6 @@ class NOVTABLE dsp_entry_v2 : public dsp_entry {
#ifdef _WIN32
//! Shows configuration popup. Main thread only!
virtual void show_config_popup_v2(const dsp_preset & p_data,fb2k::hwnd_t p_parent,dsp_preset_edit_callback & p_callback) = 0;
-#endif
-
-#ifdef _WIN32
// Obsolete method, redirected to show_config_popup_v2() by default, no need to implement.
bool show_config_popup(dsp_preset& p_data, fb2k::hwnd_t p_parent) override;
#endif
@@ -389,6 +423,7 @@ class NOVTABLE dsp_entry_v2 : public dsp_entry {
FB2K_MAKE_SERVICE_INTERFACE(dsp_entry_v2,dsp_entry);
};
+//! \since Late 1.6.x
class NOVTABLE dsp_entry_v3 : public dsp_entry_v2 {
FB2K_MAKE_SERVICE_INTERFACE(dsp_entry_v3, dsp_entry_v2);
public:
@@ -397,7 +432,7 @@ class NOVTABLE dsp_entry_v3 : public dsp_entry_v2 {
#ifdef _WIN32
//! Shows configuration popup, asynchronous version - creates dialog then returns immediately. \n
- //! Since not every DSP implements this, caller must be prepated to call legacy blocking show_config_popup methods instead. \n
+ //! Since not every DSP implements this, caller must be prepared to call legacy blocking show_config_popup methods instead. \n
//! show_config_popup_v3() may throw pfc::exception_not_implemented() to signal host that this DSP doesn't support this method yet. \n
//! Main thread only! \n
//! @returns Object to retain by host, to be released to request the dialog to be closed.
@@ -405,6 +440,26 @@ class NOVTABLE dsp_entry_v3 : public dsp_entry_v2 {
#endif
};
+//! \since 2.1
+class NOVTABLE dsp_entry_v4 : public dsp_entry_v3 {
+ FB2K_MAKE_SERVICE_INTERFACE(dsp_entry_v4, dsp_entry_v3);
+public:
+ virtual dsp::ptr instantiate_v4( const dsp_preset & arg, unsigned flags ) = 0;
+};
+
+//! \since 2.2
+class NOVTABLE dsp_entry_v5 : public dsp_entry_v4 {
+ FB2K_MAKE_SERVICE_INTERFACE(dsp_entry_v5, dsp_entry_v4);
+public:
+ //! If your DSP implementation is meant to preset as multiple item in available DSP list, implement this method. \n
+ //! @returns True if preset list has been returned (your DSP will be hidden if blank), false if your DSP is meant to be shown as just one item.
+ virtual bool enumerate_default_presets(dsp_chain_config& ret) { (void)ret; return false; }
+ //! Can possibly reach state Y by editing state X, and vice versa? \n
+ //! If this DSP has no configuration UI, this should just test if the presets are identical.
+ //! Frontend will use this to pin running presets to one of available DSP list items.
+ virtual bool match_preset_subclass(dsp_preset const& x, dsp_preset const& y) { (void)x; (void)y; return true; }
+};
+
class NOVTABLE dsp_entry_hidden : public service_base {
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(dsp_entry_hidden);
public:
@@ -422,14 +477,14 @@ class NOVTABLE dsp_entry_hidden : public service_base {
template
class dsp_entry_impl_nopreset_t : public t_entry {
public:
- void get_name(pfc::string_base & p_out) {T::g_get_name(p_out);}
- bool get_default_preset(dsp_preset & p_out)
+ void get_name(pfc::string_base & p_out) override {T::g_get_name(p_out);}
+ bool get_default_preset(dsp_preset & p_out) override
{
p_out.set_owner(T::g_get_guid());
p_out.set_data(0,0);
return true;
}
- bool instantiate(service_ptr_t & p_out,const dsp_preset & p_preset)
+ bool instantiate(service_ptr_t & p_out,const dsp_preset & p_preset) override
{
if (p_preset.get_owner() == T::g_get_guid() && p_preset.get_data_size() == 0)
{
@@ -438,10 +493,9 @@ class dsp_entry_impl_nopreset_t : public t_entry {
}
else return false;
}
- GUID get_guid() {return T::g_get_guid();}
+ GUID get_guid() override {return T::g_get_guid();}
- bool have_config_popup() {return false;}
- bool show_config_popup(dsp_preset & p_data,fb2k::hwnd_t p_parent) {return false;}
+ bool have_config_popup() override {return false;}
};
template
@@ -458,18 +512,21 @@ class dsp_entry_common_t : public interface_t {
GUID get_guid() override { return T::g_get_guid(); }
bool have_config_popup() override { return T::g_have_config_popup(); }
+#ifdef FOOBAR2000_MOBILE
+ void show_config_popup( fb2k::dspConfigContext_t parent, dsp_preset_edit_callback_v2::ptr callback ) override { T::g_show_config_popup(parent, callback); }
+#endif
+#if defined(FOOBAR2000_DESKTOP) && !defined(_WIN32)
+ service_ptr show_config_popup( fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback ) override {
+ return T::g_show_config_popup(parent, callback);
+ }
+#endif
};
template
class dsp_entry_impl_t : public dsp_entry_common_t {
public:
-
#ifdef _WIN32
- bool show_config_popup(dsp_preset & p_data,fb2k::hwnd_t p_parent) override {return T::g_show_config_popup(p_data,p_parent);}
-#else
- service_ptr show_config_popup( fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback ) override {
- return T::g_show_config_popup(parent, callback);
- }
+ bool show_config_popup(dsp_preset & p_data,fb2k::hwnd_t p_parent) override {return T::g_show_config_popup(p_data,p_parent);}
#endif
};
@@ -478,10 +535,6 @@ class dsp_entry_v2_impl_t : public dsp_entry_common_t {
public:
#ifdef _WIN32
void show_config_popup_v2(const dsp_preset & p_data,fb2k::hwnd_t p_parent,dsp_preset_edit_callback & p_callback) override {T::g_show_config_popup(p_data,p_parent,p_callback);}
-#else
- service_ptr show_config_popup( fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback ) override {
- return T::g_show_config_popup(parent, callback);
- }
#endif
};
@@ -499,32 +552,53 @@ class dsp_entry_v3_impl_t : public dsp_entry_v2_impl_t {
#endif
};
-template
+template
+class dsp_entry_v4_impl_t : public dsp_entry_v3_impl_t< dsp_t, entry_t > {
+public:
+ dsp::ptr instantiate_v4( const dsp_preset & arg, unsigned flags ) override {
+ PFC_ASSERT( arg.get_owner() == dsp_t::g_get_guid() );
+ return new service_impl_t< dsp_t > ( arg, flags );
+ }
+};
+
+template
+class dsp_entry_v5_impl_t : public dsp_entry_v4_impl_t< dsp_t, entry_t > {
+public:
+ bool enumerate_default_presets(dsp_chain_config& ret) {
+ return dsp_t::g_enumerate_default_presets(ret);
+ }
+ bool match_preset_subclass(dsp_preset const& x, dsp_preset const& y) {
+ return dsp_t::g_match_preset_subclass(x, y);
+ }
+};
+
+template
class dsp_entry_hidden_t : public dsp_entry_hidden {
public:
- bool instantiate(service_ptr_t & p_out,const dsp_preset & p_preset) {
- if (p_preset.get_owner() == T::g_get_guid()) {
- p_out = new service_impl_t(p_preset);
+ bool instantiate(service_ptr_t & p_out,const dsp_preset & p_preset) override {
+ if (p_preset.get_owner() == dsp_t::g_get_guid()) {
+ p_out = new service_impl_t(p_preset);
return true;
} else return false;
}
- GUID get_guid() {return T::g_get_guid();}
-#if 0
- void get_name( pfc::string_base& out ) {out = ""; }
- bool get_default_preset(dsp_preset & p_out) { return false; }
- bool have_config_popup() { return false; }
- bool show_config_popup(dsp_preset & p_data,HWND p_parent) { uBugCheck(); }
- void show_config_popup_v2(const dsp_preset & p_data,HWND p_parent,dsp_preset_edit_callback & p_callback) { uBugCheck(); }
-
- bool is_user_accessible() { return false; }
-#endif
+ GUID get_guid() override {return dsp_t::g_get_guid();}
};
+template
+class implement_dsp_entry;
+
+template class implement_dsp_entry : public dsp_entry_impl_t {};
+template class implement_dsp_entry : public dsp_entry_v2_impl_t {};
+template class implement_dsp_entry : public dsp_entry_v3_impl_t {};
+template class implement_dsp_entry : public dsp_entry_v4_impl_t {};
+template class implement_dsp_entry : public dsp_entry_v5_impl_t {};
+template class implement_dsp_entry : public dsp_entry_hidden_t {};
+
template
class dsp_factory_nopreset_t : public service_factory_single_t > {};
-template
-class dsp_factory_t : public service_factory_single_t > {};
+template
+class dsp_factory_t : public service_factory_single_t > {};
template
class dsp_factory_hidden_t : public service_factory_single_t< dsp_entry_hidden_t > {};
@@ -542,6 +616,7 @@ class NOVTABLE dsp_chain_config
void remove_all();
void add_item(const dsp_preset & p_data);
void copy(const dsp_chain_config & p_source);
+ void add_items(const dsp_chain_config & p_source);
const dsp_chain_config & operator=(const dsp_chain_config & p_source) {copy(p_source); return *this;}
@@ -550,15 +625,23 @@ class NOVTABLE dsp_chain_config
fb2k::memBlock::ptr to_blob() const;
void from_blob(const void* p, size_t size);
void from_blob(fb2k::memBlock::ptr);
-
- void instantiate(service_list_t & p_out);
-
+
pfc::string8 get_name_list() const;
void get_name_list(pfc::string_base & p_out) const;
static bool equals(dsp_chain_config const & v1, dsp_chain_config const & v2);
static bool equals_debug(dsp_chain_config const& v1, dsp_chain_config const& v2);
+ //! Helpers to enable/disable specific DSP in this chain. Return true if the chain has been altered, false otherwise.
+ bool enable_dsp( const GUID & dspID );
+ //! Helpers to enable/disable specific DSP in this chain. Return true if the chain has been altered, false otherwise.
+ bool disable_dsp( const GUID & dspID );
+ //! Helpers to enable/disable specific DSP in this chain. Return true if the chain has been altered, false otherwise.
+ bool enable_dsp( const dsp_preset & preset );
+
+ size_t find_first_of_type( const GUID & dspID ) const;
+ bool contains_dsp( const GUID & dspID ) const;
+
pfc::string8 debug() const;
bool operator==(const dsp_chain_config & other) const {return equals(*this, other);}
@@ -646,4 +729,9 @@ class dsp_preset_builder : public stream_writer_formatter<> {
stream_writer_buffer_simple _m_stream;
};
+#ifdef __APPLE__
+// NSObjectWrapper
+#include "commonObjects-Apple.h"
+#endif
+
#endif
diff --git a/sdk/foobar2000/SDK/dsp_manager.cpp b/sdk/foobar2000/SDK/dsp_manager.cpp
index 9d0822d..f4a8687 100644
--- a/sdk/foobar2000/SDK/dsp_manager.cpp
+++ b/sdk/foobar2000/SDK/dsp_manager.cpp
@@ -22,7 +22,7 @@ bool dsp_manager::need_track_change_mark() const {
return false;
}
-void dsp_manager::dsp_run(t_dsp_chain::const_iterator p_iter,dsp_chunk_list * p_list,const metadb_handle_ptr & cur_file,unsigned flags,double & latency,abort_callback & p_abort)
+void dsp_manager::dsp_run(t_dsp_chain::const_iterator p_iter,dsp_chunk_list * p_list,const dsp_track_t & cur_file,unsigned flags,double & latency,abort_callback & p_abort)
{
p_list->remove_bad_chunks();
@@ -30,7 +30,7 @@ void dsp_manager::dsp_run(t_dsp_chain::const_iterator p_iter,dsp_chunk_list * p_
TRACK_CODE("dsp::get_latency",latency += p_iter->m_dsp->get_latency());
}
-double dsp_manager::run(dsp_chunk_list * p_list,const metadb_handle_ptr & p_cur_file,unsigned p_flags,abort_callback & p_abort) {
+double dsp_manager::run(dsp_chunk_list * p_list,const dsp_track_t & p_cur_file,unsigned p_flags,abort_callback & p_abort) {
TRACK_CALL_TEXT("dsp_manager::run");
try {
@@ -96,9 +96,9 @@ double dsp_manager::run(dsp_chunk_list * p_list,const metadb_handle_ptr & p_cur_
}
}
- for(t_dsp_chain::iterator iter = newchain.first(); iter.is_valid(); ++iter) {
- if (iter->m_dsp.is_empty()) {
- if (!dsp_entry::g_instantiate(iter->m_dsp,iter->m_preset) && !dsp_entry_hidden::g_instantiate(iter->m_dsp, iter->m_preset)) uBugCheck();
+ for( auto & iter : newchain ) {
+ if (iter.m_dsp.is_empty()) {
+ if (!dsp_entry::g_instantiate(iter.m_dsp,iter.m_preset, m_creationFlags) && !dsp_entry_hidden::g_instantiate(iter.m_dsp, iter.m_preset)) uBugCheck();
}
}
diff --git a/sdk/foobar2000/SDK/dsp_manager.h b/sdk/foobar2000/SDK/dsp_manager.h
index 311ac49..80cacb6 100644
--- a/sdk/foobar2000/SDK/dsp_manager.h
+++ b/sdk/foobar2000/SDK/dsp_manager.h
@@ -6,13 +6,14 @@
//! Helper class for running audio data through a DSP chain.
class dsp_manager {
public:
- dsp_manager() {}
+ //! @param creationFlags See dsp_entry::flag_*
+ dsp_manager(unsigned creationFlags = 0) : m_creationFlags(creationFlags) {}
//! Alters the DSP chain configuration. Should be called before the first run() to set the configuration but can be also called anytime later between run() calls.
void set_config( const dsp_chain_config & p_data );
//! Runs DSP on the specified chunk list.
//! @returns Current DSP latency in seconds.
- double run(dsp_chunk_list * p_list,const metadb_handle_ptr & p_cur_file,unsigned p_flags,abort_callback & p_abort);
+ double run(dsp_chunk_list * p_list,dsp_track_t const & p_cur_file,unsigned p_flags,abort_callback & p_abort);
//! Flushes the DSP (e.g. when seeking).
void flush();
@@ -24,6 +25,7 @@ class dsp_manager {
bool need_track_change_mark() const;
private:
+ const unsigned m_creationFlags;
struct t_dsp_chain_entry {
service_ptr_t m_dsp;
dsp_preset_impl m_preset;
@@ -35,7 +37,7 @@ class dsp_manager {
dsp_chain_config_impl m_config;
bool m_config_changed = false;
- void dsp_run(t_dsp_chain::const_iterator p_iter,dsp_chunk_list * list,const metadb_handle_ptr & cur_file,unsigned flags,double & latency,abort_callback&);
+ void dsp_run(t_dsp_chain::const_iterator p_iter,dsp_chunk_list * list,const dsp_track_t & cur_file,unsigned flags,double & latency,abort_callback&);
dsp_manager(const dsp_manager &) = delete;
const dsp_manager & operator=(const dsp_manager&) = delete;
diff --git a/sdk/foobar2000/SDK/file.h b/sdk/foobar2000/SDK/file.h
index 9f04ed7..161da8a 100644
--- a/sdk/foobar2000/SDK/file.h
+++ b/sdk/foobar2000/SDK/file.h
@@ -30,6 +30,9 @@ namespace foobar2000_io
inline bool operator==(const t_filestats& param) const { return m_size == param.m_size && m_timestamp == param.m_timestamp; }
inline bool operator!=(const t_filestats& param) const { return m_size != param.m_size || m_timestamp != param.m_timestamp; }
+
+ pfc::string8 describe() const;
+ pfc::string8 debug() const { return describe(); }
};
struct t_filestats2 {
@@ -86,8 +89,13 @@ namespace foobar2000_io
bool operator!=(const t_filestats2& other) const { return !equals(*this, other); }
pfc::string8 describe() const;
+ pfc::string8 debug() const { return describe(); }
+
+ bool haveSize() const { return m_size != filesize_invalid; }
+ bool haveTimestamp() const { return m_timestamp != filetimestamp_invalid; }
+ bool haveTimestampCreate() const { return m_timestampCreate != filetimestamp_invalid; }
};
- enum {
+ static constexpr uint32_t
stats2_size = 1 << 0,
stats2_timestamp = 1 << 1,
stats2_timestampCreate = 1 << 2,
@@ -98,8 +106,7 @@ namespace foobar2000_io
stats2_remote = 1 << 6,
stats2_flags = (stats2_fileOrFolder | stats2_readOnly | stats2_hidden | stats2_remote),
stats2_legacy = (stats2_size | stats2_timestamp),
- stats2_all = 0xFFFFFFFF,
- };
+ stats2_all = 0xFFFFFFFF;
//! Invalid/unknown file stats constant. See: t_filestats.
static constexpr t_filestats filestats_invalid = t_filestats();
@@ -156,7 +163,7 @@ namespace foobar2000_io
//! Helper function; reads a string (with a 32-bit header indicating length in bytes followed by UTF-8 encoded data without a null terminator).
void read_string(pfc::string_base& p_out, abort_callback& p_abort);
//! Helper function; alternate way of storing strings; assumes string takes space up to end of stream.
- void read_string_raw(pfc::string_base& p_out, abort_callback& p_abort);
+ void read_string_raw(pfc::string_base& p_out, abort_callback& p_abort, size_t sanity = SIZE_MAX);
//! Helper function; reads a string (with a 32-bit header indicating length in bytes followed by UTF-8 encoded data without a null terminator).
pfc::string read_string(abort_callback& p_abort);
@@ -283,6 +290,7 @@ namespace foobar2000_io
//! Retrieves mime type of the file.
//! @param p_out Receives content type string on success.
virtual bool get_content_type(pfc::string_base& p_out) = 0;
+ pfc::string8 get_content_type();
//! Hint, returns whether the file is already fully buffered into memory.
virtual bool is_in_memory() { return false; }
@@ -324,6 +332,7 @@ namespace foobar2000_io
//! Security helper; fails early with exception_io_data_truncation if it is not possible to read this amount of bytes from this file at this position.
void probe_remaining(t_filesize bytes, abort_callback& p_abort);
+ bool probe_remaining_ex(t_filesize bytes, abort_callback& p_abort);
//! Helper; throws exception_io_object_not_seekable if file is not seekable.
void ensure_seekable();
@@ -368,9 +377,19 @@ namespace foobar2000_io
//! file_v2 wrapper.
t_filestats2 get_stats2_(uint32_t f, abort_callback& a);
+ //! file_v2 wrapper.
+ void set_stats(t_filestats2 const&, abort_callback&);
+
//! file_v2 wrapper.
t_filetimestamp get_time_created(abort_callback& a);
+ //! Alternate version of read() intended for network resources.\n
+ //! See: stream_receive::receive(); \n
+ //! If not implemented by this object, uses plain read().
+ size_t receive(void*, size_t, abort_callback&);
+
+ void commit(abort_callback& a) {flushFileBuffers(a);}
+
FB2K_MAKE_SERVICE_INTERFACE(file, service_base);
};
@@ -443,23 +462,11 @@ namespace foobar2000_io
//! Implementation helper - contains dummy implementations of methods that modify the file
template class file_readonly_t : public t_base {
public:
- void resize(t_filesize p_size, abort_callback& p_abort) { throw exception_io_denied(); }
- void write(const void* p_buffer, t_size p_bytes, abort_callback& p_abort) { throw exception_io_denied(); }
+ void resize(t_filesize p_size, abort_callback& p_abort) override { throw exception_io_denied(); }
+ void write(const void* p_buffer, t_size p_bytes, abort_callback& p_abort) override { throw exception_io_denied(); }
};
typedef file_readonly_t file_readonly;
- class file_streamstub : public file_readonly {
- public:
- t_size read(void*, t_size, abort_callback&) { return 0; }
- t_filesize get_size(abort_callback&) { return filesize_invalid; }
- t_filesize get_position(abort_callback&) { return 0; }
- bool get_content_type(pfc::string_base&) { return false; }
- bool is_remote() { return true; }
- void reopen(abort_callback&) {}
- void seek(t_filesize, abort_callback&) { throw exception_io_object_not_seekable(); }
- bool can_seek() { return false; }
- };
-
//! \since 2.0
class file_v2 : public file {
FB2K_MAKE_SERVICE_INTERFACE(file_v2, file);
@@ -472,10 +479,12 @@ namespace foobar2000_io
virtual t_filestats2 get_stats2(uint32_t s2flags, abort_callback&) = 0;
- virtual size_t lowLevelIO(const GUID& guid, size_t arg1, void* arg2, size_t arg2size, abort_callback& abort) { return 0; }
+ virtual size_t lowLevelIO(const GUID& guid, size_t arg1, void* arg2, size_t arg2size, abort_callback& abort) { (void)guid; (void)arg1; (void)arg2; (void)arg2size; (void)abort; return 0; }
- // Old method wrapped to get_stats2()
+ // Old methods wrapped to get_stats2()
t_filetimestamp get_timestamp(abort_callback& p_abort) override;
+ bool is_remote() override;
+ t_filesize get_size(abort_callback& p_abort) override;
};
//! \since 1.6.7
@@ -486,4 +495,16 @@ namespace foobar2000_io
virtual void get_connected_path(pfc::string_base& out) = 0;
};
-}
\ No newline at end of file
+
+ //! \since 2.1
+ //! Extension to file object, implemented by network readers. Adds receive() method.
+ class stream_receive : public service_base {
+ FB2K_MAKE_SERVICE_INTERFACE(stream_receive, service_base);
+ public:
+ //! Alternate version of read() intended for network resources.\n
+ //! Returns as soon as any data is available (usually less than requested), or EOF has been reached (0 returned).
+ virtual size_t receive(void*, size_t, abort_callback&) = 0;
+
+ size_t read_using_receive(void*, size_t, abort_callback&);
+ };
+}
diff --git a/sdk/foobar2000/SDK/fileDialog.h b/sdk/foobar2000/SDK/fileDialog.h
index ab3bfb1..87b0f24 100644
--- a/sdk/foobar2000/SDK/fileDialog.h
+++ b/sdk/foobar2000/SDK/fileDialog.h
@@ -3,18 +3,21 @@
#include
namespace fb2k {
-
+
+ typedef std::function fileDialogReply_t;
+ typedef std::function fileDialogGetPath_t;
+
class NOVTABLE fileDialogNotify : public service_base {
FB2K_MAKE_SERVICE_INTERFACE( fileDialogNotify, service_base );
public:
//! Called when user has cancelled the dialog.
virtual void dialogCancelled() = 0;
//! Called when the user has dismissed the dialog having selected some content.
- //! @param fsItems Array of fsItemBase objects or strings, depending on the platform. Should accept either form. Typically, file dialogs will handle fsItems but Add Location will handle path strings. Special case: playlist format chooser sends chosen format name as a string (one array item).
+ //! @param items Array of fsItemBase objects or strings, depending on the platform. Should accept either form. Typically, file dialogs will handle fsItems but Add Location will handle path strings. Special case: playlist format chooser sends chosen format name as a string (one array item).
virtual void dialogOK2( arrayRef items ) = 0;
- static fileDialogNotify::ptr create( std::function recv );
- };
+ static fileDialogNotify::ptr create( fileDialogReply_t recv );
+ };
class NOVTABLE fileDialogSetup : public service_base {
FB2K_MAKE_SERVICE_INTERFACE( fileDialogSetup, service_base );
@@ -52,6 +55,10 @@ namespace fb2k {
//! For an example, on Windows most filedialogs work synchronously while on OSX all of them work asynchronously.
//! @param notify Notify object invoked upon dialog completion.
virtual void run(fileDialogNotify::ptr notify) = 0;
+
+ //! Helper, creates fileDialogNotify for you.
+ void run (fileDialogReply_t reply);
+ void runSimple (fileDialogGetPath_t reply);
};
class NOVTABLE fileDialog : public service_base {
diff --git a/sdk/foobar2000/SDK/file_cached_impl.cpp b/sdk/foobar2000/SDK/file_cached_impl.cpp
index 276df20..5ac9c79 100644
--- a/sdk/foobar2000/SDK/file_cached_impl.cpp
+++ b/sdk/foobar2000/SDK/file_cached_impl.cpp
@@ -143,6 +143,7 @@ class file_cached_impl_v2 : public service_multi_inherit< file_v2, service_multi
}
t_filesize get_position(abort_callback & p_abort) override {
p_abort.check();
+ PFC_ASSERT( m_position <= m_size );
return m_position;
}
void set_eof(abort_callback & p_abort) {
@@ -166,7 +167,7 @@ class file_cached_impl_v2 : public service_multi_inherit< file_v2, service_multi
FB2K_DebugLog() << "Skip-seeking: " << p_position;
#endif
t_filesize skipped = this->skip_( delta, p_abort );
- PFC_ASSERT( skipped == delta ); (void) skipped;
+ PFC_ASSERT( skipped == (t_filesize)delta ); (void) skipped;
return;
}
@@ -233,7 +234,7 @@ class file_cached_impl : public service_multi_inherit< file_v2, service_multi_in
m_buffer.set_size(blocksize);
}
size_t get_cache_block_size() override {return m_buffer.get_size();}
- void suggest_grow_cache(size_t suggestSize) override {}
+ void suggest_grow_cache(size_t) override {}
void initialize(service_ptr_t p_base,abort_callback & p_abort) {
m_base = p_base;
m_can_seek = m_base->can_seek();
diff --git a/sdk/foobar2000/SDK/file_info.cpp b/sdk/foobar2000/SDK/file_info.cpp
index 11179f0..62dfb98 100644
--- a/sdk/foobar2000/SDK/file_info.cpp
+++ b/sdk/foobar2000/SDK/file_info.cpp
@@ -11,28 +11,25 @@
static constexpr char info_WAVEFORMATEXTENSIBLE_CHANNEL_MASK[] = "WAVEFORMATEXTENSIBLE_CHANNEL_MASK";
-const float replaygain_info::peak_invalid = -1;
-const float replaygain_info::gain_invalid = -1000;
-
t_size file_info::meta_find_ex(const char * p_name,t_size p_name_length) const
{
t_size n, m = meta_get_count();
for(n=0;nmeta_find(name);
+ if ( idx != SIZE_MAX ) {
+ if (field_value_equals(*this, idx, source, walk)) continue;
+ }
+
+ copy_meta_single(source, walk);
+ changed = true;
+ }
+ return changed;
+}
+
void file_info::copy_meta_single(const file_info & p_source,t_size p_index)
{
copy_meta_single_rename(p_source,p_index,p_source.meta_enum_name(p_index));
@@ -109,7 +122,7 @@ void file_info::copy_meta_single_nocheck(const file_info & p_source,t_size p_ind
{
const char * name = p_source.meta_enum_name(p_index);
t_size n, m = p_source.meta_enum_value_count(p_index);
- t_size new_index = pfc_infinite;
+ t_size new_index = SIZE_MAX;
for(n=0;n= max) return 0;
return meta_enum_value(index,p_index);
}
const char * file_info::info_get_ex(const char * p_name,t_size p_name_length) const
{
- t_size index = info_find_ex(p_name,p_name_length);
- if (index == pfc_infinite) return 0;
+ auto index = info_find_ex(p_name,p_name_length);
+ if (index == SIZE_MAX) return 0;
return info_enum_value(index);
}
@@ -290,7 +303,24 @@ unsigned file_info::info_get_decoded_bps() const
val = info_get_int("bitspersample");
if (is_valid_bps(val)) return (unsigned)val;
return 0;
+}
+bool file_info::info_get_codec_long(pfc::string_base& out, const char * delim) const {
+ const char * codec;
+ codec = this->info_get("codec_long");
+ if (codec != nullptr) {
+ out = codec; return true;
+ }
+ codec = this->info_get("codec");
+ if (codec != nullptr) {
+ out = codec;
+ const char * profile = this->info_get("codec_profile");
+ if (profile != nullptr) {
+ out << delim << profile;
+ }
+ return true;
+ }
+ return false;
}
void file_info::reset()
@@ -311,19 +341,19 @@ void file_info::reset_replaygain()
void file_info::copy_meta_single_rename_ex(const file_info & p_source,t_size p_index,const char * p_new_name,t_size p_new_name_length)
{
t_size n, m = p_source.meta_enum_value_count(p_index);
- t_size new_index = pfc_infinite;
+ t_size new_index = SIZE_MAX;
for(n=0;nis_album_gain_present()) this->m_album_gain -= (float)deltaDB;
+ if (this->is_track_gain_present()) this->m_track_gain -= (float)deltaDB;
+ const auto scale = audio_math::gain_to_scale(deltaDB);
+ if (this->is_album_peak_present()) this->m_album_peak *= (float)scale;
+ if (this->is_track_peak_present()) this->m_track_peak *= (float)scale;
+}
+
bool file_info::are_meta_fields_identical(t_size p_index1,t_size p_index2) const
{
const t_size count = meta_enum_value_count(p_index1);
@@ -408,8 +446,8 @@ void file_info::meta_format_entry(t_size index, pfc::string_base & out, const ch
bool file_info::meta_format(const char * p_name,pfc::string_base & p_out, const char * separator) const {
p_out.reset();
- t_size index = meta_find(p_name);
- if (index == pfc_infinite) return false;
+ auto index = meta_find(p_name);
+ if (index == SIZE_MAX) return false;
meta_format_entry(index, p_out, separator);
return true;
}
@@ -420,6 +458,20 @@ void file_info::info_calculate_bitrate(uint64_t p_filesize,double p_length)
if ( b > 0 ) info_set_bitrate(b);
}
+void file_info::info_set_bitspersample(uint32_t val, bool isFloat) {
+ // Bits per sample semantics
+ // "bitspersample" is set to integer value of bits per sample
+ // "bitspersample_extra" is used for bps of 32 or 64, either "floating-point" or "fixed-point"
+ // bps other than 32 or 64 are implicitly fixed-point as floating-point for such makes no sense
+
+ info_set_int("bitspersample", val);
+ if ( isFloat || val == 32 || val == 64 ) {
+ info_set("bitspersample_extra", isFloat ? "floating-point" : "fixed-point");
+ } else {
+ info_remove("bitspersample_extra");
+ }
+}
+
bool file_info::is_encoding_float() const {
auto bs = info_get_int("bitspersample");
auto extra = info_get("bitspersample_extra");
@@ -455,6 +507,11 @@ bool file_info::is_encoding_lossy() const {
return false;
}
+bool file_info::is_encoding_lossless() const {
+ const char* encoding = info_get("encoding");
+ return encoding != nullptr && pfc::stringEqualsI_ascii(encoding, "lossless");
+}
+
bool file_info::g_is_meta_equal(const file_info & p_item1,const file_info & p_item2) {
const t_size count = p_item1.meta_get_count();
if (count != p_item2.meta_get_count()) {
@@ -525,7 +582,7 @@ bool file_info::g_is_info_equal(const file_info & p_item1,const file_info & p_it
}
for(t_size n1=0; n1 0) FB2K_console_formatter() << "Duration: " << pfc::format_time_ex(get_length(), 6);
pfc::string_formatter temp;
- for(t_size metaWalk = 0; metaWalk < meta_get_count(); ++metaWalk) {
+ const auto numMeta = meta_get_count(), numInfo = info_get_count();
+ if (numMeta == 0) {
+ FB2K_console_formatter() << "Meta is blank";
+ } else for(t_size metaWalk = 0; metaWalk < numMeta; ++metaWalk) {
const char * name = meta_enum_name( metaWalk );
const auto valCount = meta_enum_value_count( metaWalk );
for ( size_t valWalk = 0; valWalk < valCount; ++valWalk ) {
@@ -601,7 +661,9 @@ void file_info::to_console() const {
FB2K_console_formatter() << "Meta: " << meta_enum_name(metaWalk) << " = " << temp;
*/
}
- for(t_size infoWalk = 0; infoWalk < info_get_count(); ++infoWalk) {
+ if (numInfo == 0) {
+ FB2K_console_formatter() << "Info is blank";
+ } else for(t_size infoWalk = 0; infoWalk < numInfo; ++infoWalk) {
FB2K_console_formatter() << "Info: " << info_enum_name(infoWalk) << " = " << info_enum_value(infoWalk);
}
}
@@ -734,11 +796,11 @@ void file_info::from_stream( stream_reader * stream, abort_callback & abort ) {
for(;;) {
in.read_string_nullterm( tempName );
if (tempName.length() == 0) break;
- size_t metaIndex = pfc_infinite;
+ size_t metaIndex = SIZE_MAX;
for(;;) {
in.read_string_nullterm( tempValue );
if (tempValue.length() == 0) break;
- if (metaIndex == pfc_infinite) metaIndex = this->meta_add( tempName, tempValue );
+ if (metaIndex == SIZE_MAX) metaIndex = this->meta_add( tempName, tempValue );
else this->meta_add_value( metaIndex, tempValue );
}
}
@@ -801,11 +863,11 @@ void file_info::from_mem( const void * memPtr, size_t memSize ) {
for(;;) {
const char * metaName = _readString( walk, remaining );
if (*metaName == 0) break;
- size_t metaIndex = pfc_infinite;
+ size_t metaIndex = SIZE_MAX;
for(;;) {
const char * metaValue = _readString( walk, remaining );
if (*metaValue == 0) break;
- if (metaIndex == pfc_infinite) metaIndex = this->meta_add( metaName, metaValue );
+ if (metaIndex == SIZE_MAX) metaIndex = this->meta_add( metaName, metaValue );
else this->meta_add_value( metaIndex, metaName );
}
}
@@ -856,7 +918,6 @@ bool file_info::unicode_normalize_C() {
const size_t total = this->meta_get_count();
bool changed = false;
for (size_t mwalk = 0; mwalk < total; ++mwalk) {
- const char* name = this->meta_enum_name(mwalk);
const size_t totalV = this->meta_enum_value_count(mwalk);
for (size_t vwalk = 0; vwalk < totalV; ++vwalk) {
const char* val = this->meta_enum_value(mwalk, vwalk);
@@ -870,4 +931,86 @@ bool file_info::unicode_normalize_C() {
}
}
return changed;
-}
\ No newline at end of file
+}
+
+void file_info::meta_enumerate(meta_enumerate_t cb) const {
+ const size_t nMeta = this->meta_get_count();
+ for (size_t metaWalk = 0; metaWalk < nMeta; ++metaWalk) {
+ const char* name = this->meta_enum_name(metaWalk);
+ const size_t nValue = this->meta_enum_value_count(metaWalk);
+ for (size_t valueWalk = 0; valueWalk < nValue; ++valueWalk) {
+ const char* value = this->meta_enum_value(metaWalk, valueWalk);
+ cb(name, value);
+ }
+ }
+}
+
+bool file_info::meta_value_exists( const char * name, const char * findValue, bool insensitive ) const {
+ const auto idx = this->meta_find(name);
+ if ( idx != SIZE_MAX ) {
+ const auto count = this->meta_enum_value_count(idx);
+ for( size_t walk = 0; walk < count; ++ walk) {
+ auto value = this->meta_enum_value(idx, walk);
+ if ( insensitive ) {
+ if (pfc::stringEqualsI_utf8(value, findValue)) return true;
+ } else {
+ if ( strcmp(value, findValue) == 0 ) return true;
+ }
+ }
+ }
+ return false;
+}
+
+const char * file_info::meta_get_title( const char * fallback) const {
+ auto ret = meta_get("title", 0);
+ return ret?ret:fallback;
+}
+
+#ifdef FOOBAR2000_MOBILE
+#include "album_art.h"
+#include "hasher_md5.h"
+
+void file_info::info_set_pictures( const GUID * guids, size_t size ) {
+ this->info_set("pictures", album_art_ids::ids_to_string(guids, size) );
+}
+
+pfc::array_t file_info::info_get_pictures( ) const {
+ return album_art_ids::string_to_ids( this->info_get( "pictures" ) );
+}
+
+bool file_info::info_have_picture( const GUID & arg ) const {
+ for( auto & walk : info_get_pictures() ) {
+ if ( walk == arg ) return true;
+ }
+ return false;
+}
+
+uint64_t file_info::makeMetaHash() const {
+ pfc::string_formatter temp;
+
+ auto doMeta = [&] ( const char * meta ) {
+ const char * p = meta_get(meta, 0);
+ if (p != nullptr) temp << p;
+ temp << "\n";
+ };
+ auto doMetaInt = [&] ( const char * meta ) {
+ const char * p = meta_get(meta, 0);
+ if (p != nullptr) {
+ auto s = strchr(p, '/' ); if ( s != nullptr ) p = s+1;
+ while(*p == '0') ++p;
+ temp << p;
+ }
+ temp << "\n";
+ };
+ doMeta("title");
+ doMeta("artist");
+ doMeta("album");
+ doMetaInt("tracknumber");
+ doMetaInt("discnumber");
+
+ if (temp.length() == 5) return 0;
+
+ return hasher_md5::get()->process_single( temp.c_str(), temp.length( ) ).xorHalve();
+}
+
+#endif // FOOBAR2000_MOBILE
diff --git a/sdk/foobar2000/SDK/file_info.h b/sdk/foobar2000/SDK/file_info.h
index 67a08a9..63e6b56 100644
--- a/sdk/foobar2000/SDK/file_info.h
+++ b/sdk/foobar2000/SDK/file_info.h
@@ -1,16 +1,18 @@
#pragma once
#include "audio_chunk.h"
+#include
//! Structure containing ReplayGain scan results from some playable object, also providing various helper methods to manipulate those results.
struct replaygain_info
{
- float m_album_gain,m_track_gain;
- float m_album_peak,m_track_peak;
+ static constexpr float peak_invalid = -1, gain_invalid = -1000;
+
+ float m_album_gain = gain_invalid, m_track_gain = gain_invalid;
+ float m_album_peak = peak_invalid, m_track_peak = peak_invalid;
enum {text_buffer_size = 16 };
typedef char t_text_buffer[text_buffer_size];
- static const float peak_invalid, gain_invalid;
static bool g_format_gain(float p_value,char p_buffer[text_buffer_size]);
static bool g_format_peak(float p_value,char p_buffer[text_buffer_size]);
@@ -21,6 +23,10 @@ struct replaygain_info
inline bool format_album_peak(char p_buffer[text_buffer_size]) const {return g_format_peak(m_album_peak,p_buffer);}
inline bool format_track_peak(char p_buffer[text_buffer_size]) const {return g_format_peak(m_track_peak,p_buffer);}
+
+ typedef std::function for_each_t;
+ void for_each(for_each_t) const;
+
static float g_parse_gain_text(const char * p_text, t_size p_text_len = SIZE_MAX);
void set_album_gain_text(const char * p_text,t_size p_text_len = SIZE_MAX);
void set_track_gain_text(const char * p_text,t_size p_text_len = SIZE_MAX);
@@ -51,6 +57,12 @@ struct replaygain_info
static bool g_equal(const replaygain_info & item1,const replaygain_info & item2);
void reset();
+ void clear() { reset(); }
+ void clear_gain() { m_album_gain = m_track_gain = gain_invalid; }
+ void clear_peak() { m_album_peak = m_track_peak = peak_invalid; }
+
+ // Alter gain/peak info, if available, by dB - after file gain has been altered by other means
+ void adjust(double deltaDB);
};
class format_rg_gain {
@@ -156,6 +168,8 @@ class NOVTABLE file_info {
bool meta_format(const char * p_name,pfc::string_base & p_out, const char * separator = ", ") const;
void meta_format_entry(t_size index, pfc::string_base & p_out, const char * separator = ", ") const;//same as meta_format but takes index instead of meta name.
+ typedef std::function meta_enumerate_t;
+ void meta_enumerate(meta_enumerate_t) const;
bool info_exists_ex(const char * p_name,t_size p_name_length) const;
void info_remove_index(t_size p_index);
@@ -163,15 +177,16 @@ class NOVTABLE file_info {
bool info_remove_ex(const char * p_name,t_size p_name_length);
const char * info_get_ex(const char * p_name,t_size p_name_length) const;
- inline t_size meta_find(const char * p_name) const {return meta_find_ex(p_name,SIZE_MAX);}
- inline bool meta_exists(const char * p_name) const {return meta_exists_ex(p_name,SIZE_MAX);}
- inline void meta_remove_field(const char * p_name) {meta_remove_field_ex(p_name,SIZE_MAX);}
- inline t_size meta_set(const char * p_name,const char * p_value) {return meta_set_ex(p_name,SIZE_MAX,p_value,SIZE_MAX);}
+ inline t_size meta_find(const char* p_name) const { PFC_ASSERT(p_name != nullptr); return meta_find_ex(p_name, SIZE_MAX); }
+ inline bool meta_exists(const char* p_name) const { PFC_ASSERT(p_name != nullptr); return meta_exists_ex(p_name, SIZE_MAX); }
+ bool meta_value_exists( const char * name, const char * value, bool insensitive = false ) const;
+ inline void meta_remove_field(const char* p_name) { PFC_ASSERT(p_name != nullptr); meta_remove_field_ex(p_name, SIZE_MAX); }
+ inline t_size meta_set(const char* p_name, const char* p_value) { PFC_ASSERT(p_name != nullptr && p_value != nullptr); return meta_set_ex(p_name, SIZE_MAX, p_value, SIZE_MAX); }
inline void meta_insert_value(t_size p_index,t_size p_value_index,const char * p_value) {meta_insert_value_ex(p_index,p_value_index,p_value,SIZE_MAX);}
inline void meta_add_value(t_size p_index,const char * p_value) {meta_add_value_ex(p_index,p_value,SIZE_MAX);}
- inline const char* meta_get(const char * p_name,t_size p_index) const {return meta_get_ex(p_name,SIZE_MAX,p_index);}
- inline t_size meta_get_count_by_name(const char * p_name) const {return meta_get_count_by_name_ex(p_name,SIZE_MAX);}
- inline t_size meta_add(const char * p_name,const char * p_value) {return meta_add_ex(p_name,SIZE_MAX,p_value,SIZE_MAX);}
+ inline const char* meta_get(const char* p_name, t_size p_index) const { PFC_ASSERT(p_name != nullptr); return meta_get_ex(p_name, SIZE_MAX, p_index); }
+ inline t_size meta_get_count_by_name(const char* p_name) const { PFC_ASSERT(p_name != nullptr); return meta_get_count_by_name_ex(p_name, SIZE_MAX); }
+ inline t_size meta_add(const char* p_name, const char* p_value) { PFC_ASSERT(p_name != nullptr && p_value != nullptr); return meta_add_ex(p_name, SIZE_MAX, p_value, SIZE_MAX); }
inline void meta_modify_value(t_size p_index,t_size p_value_index,const char * p_value) {meta_modify_value_ex(p_index,p_value_index,p_value,SIZE_MAX);}
@@ -201,6 +216,7 @@ class NOVTABLE file_info {
inline void copy_meta_single_rename(const file_info & p_source,t_size p_index,const char * p_new_name) {copy_meta_single_rename_ex(p_source,p_index,p_new_name,SIZE_MAX);}
void overwrite_info(const file_info & p_source);
void overwrite_meta(const file_info & p_source);
+ bool overwrite_meta_if_changed( const file_info & source );
t_int64 info_get_int(const char * name) const;
t_int64 info_get_length_samples() const;
@@ -213,9 +229,9 @@ class NOVTABLE file_info {
void info_set_replaygain_album_peak(float value);
inline t_int64 info_get_bitrate_vbr() const {return info_get_int("bitrate_dynamic");}
- inline void info_set_bitrate_vbr(t_int64 val) {info_set_int("bitrate_dynamic",val);}
+ inline void info_set_bitrate_vbr(t_int64 val_kbps) {info_set_int("bitrate_dynamic",val_kbps);}
inline t_int64 info_get_bitrate() const {return info_get_int("bitrate");}
- inline void info_set_bitrate(t_int64 val) { PFC_ASSERT(val > 0); info_set_int("bitrate", val); }
+ inline void info_set_bitrate(t_int64 val_kbps) { PFC_ASSERT(val_kbps > 0); info_set_int("bitrate", val_kbps); }
//! Set number of channels
@@ -231,9 +247,17 @@ class NOVTABLE file_info {
//! Returns channel mask value. 0 if not set, use default for the channel count then.
uint32_t info_get_wfx_chanMask() const;
+ //! Is a lossy codec?
bool is_encoding_lossy() const;
+ //! Is explicitly reported as lossless codec?
+ bool is_encoding_lossless() const;
+ //! Is lossless/PCM that can't be sanely represented in this fb2k build due to audio_sample limitations? \n
+ //! Always returns false in 64-bit fb2k.
bool is_encoding_overkill() const;
+ //! Floating-point PCM used?
bool is_encoding_float() const;
+ //! Helper; sets bit depth of lossless/PCM format.
+ void info_set_bitspersample(uint32_t val, bool isFloat = false);
//! Sets bitrate value using file size in bytes and duration.
void info_calculate_bitrate(uint64_t p_filesize,double p_length);
@@ -241,6 +265,12 @@ class NOVTABLE file_info {
//! Returns decoder-output bit depth - what sample format is being converted to foobar2000 audio_sample. 0 if unknown.
unsigned info_get_decoded_bps() const;
+ //! Foramts long codec name ( codec + profile )
+ bool info_get_codec_long( pfc::string_base & out, const char * delim = " / ") const;
+
+ //! Simplified title getter, returns fallback value if title not set, useful for debugging.
+ const char * meta_get_title( const char * fallback = "(untitled)") const;
+
private:
void merge(const pfc::list_base_const_t & p_sources);
public:
@@ -296,6 +326,14 @@ class NOVTABLE file_info {
//! Normalize values to Unicode form C
//! @returns true if changed, false otherwise
bool unicode_normalize_C();
+
+
+#ifdef FOOBAR2000_MOBILE
+ void info_set_pictures( const GUID * guids, size_t size );
+ pfc::array_t info_get_pictures( ) const;
+ bool info_have_picture(const GUID&) const;
+ uint64_t makeMetaHash() const;
+#endif
protected:
file_info() {}
~file_info() {}
diff --git a/sdk/foobar2000/SDK/file_info_const_impl.cpp b/sdk/foobar2000/SDK/file_info_const_impl.cpp
index 2a93b00..7ae3129 100644
--- a/sdk/foobar2000/SDK/file_info_const_impl.cpp
+++ b/sdk/foobar2000/SDK/file_info_const_impl.cpp
@@ -84,7 +84,7 @@ namespace {
inline int test(t_size p_index) const
{
- return pfc::stricmp_ascii_ex(m_meta[m_hintmap[p_index]].m_name,~0,m_name,m_name_length);
+ return pfc::stricmp_ascii_ex(m_meta[m_hintmap[p_index]].m_name,SIZE_MAX,m_name,m_name_length);
}
private:
@@ -260,8 +260,8 @@ t_size file_info_const_impl::meta_find_ex(const char * p_name,t_size p_name_leng
{
#ifdef __file_info_const_impl_have_hintmap__
if (m_hintmap != NULL) {
- t_size result = ~0;
- if (!pfc::bsearch_inline_t(m_meta_count,bsearch_callback_hintmap_impl(m_meta,m_hintmap,p_name,p_name_length),result)) return ~0;
+ t_size result = SIZE_MAX;
+ if (!pfc::bsearch_inline_t(m_meta_count,bsearch_callback_hintmap_impl(m_meta,m_hintmap,p_name,p_name_length),result)) return SIZE_MAX;
else return m_hintmap[result];
} else {
return file_info::meta_find_ex(p_name,p_name_length);
diff --git a/sdk/foobar2000/SDK/file_info_const_impl.h b/sdk/foobar2000/SDK/file_info_const_impl.h
index 4f884f4..96af35a 100644
--- a/sdk/foobar2000/SDK/file_info_const_impl.h
+++ b/sdk/foobar2000/SDK/file_info_const_impl.h
@@ -4,22 +4,22 @@
class file_info_readonly : public file_info {
[[noreturn]] static void verboten() { FB2K_BugCheck(); }
protected:
- void set_length(double p_length) override { verboten(); }
- void set_replaygain(const replaygain_info & p_info) override { verboten(); }
+ void set_length(double) override { verboten(); }
+ void set_replaygain(const replaygain_info &) override { verboten(); }
- t_size meta_set_ex(const char * p_name, t_size p_name_length, const char * p_value, t_size p_value_length) override { verboten(); }
- void meta_insert_value_ex(t_size p_index, t_size p_value_index, const char * p_value, t_size p_value_length) override { verboten(); }
- void meta_remove_mask(const bit_array & p_mask) override { verboten(); }
- void meta_reorder(const t_size * p_order) override { verboten(); }
- void meta_remove_values(t_size p_index, const bit_array & p_mask) override { verboten(); }
- void meta_modify_value_ex(t_size p_index, t_size p_value_index, const char * p_value, t_size p_value_length) override { verboten(); }
+ t_size meta_set_ex(const char *, t_size, const char *, t_size) override { verboten(); }
+ void meta_insert_value_ex(t_size, t_size, const char *, t_size) override { verboten(); }
+ void meta_remove_mask(const bit_array &) override { verboten(); }
+ void meta_reorder(const t_size *) override { verboten(); }
+ void meta_remove_values(t_size, const bit_array &) override { verboten(); }
+ void meta_modify_value_ex(t_size, t_size, const char *, t_size) override { verboten(); }
- t_size info_set_ex(const char * p_name, t_size p_name_length, const char * p_value, t_size p_value_length) override { verboten(); }
- void info_remove_mask(const bit_array & p_mask) override { verboten(); }
+ t_size info_set_ex(const char *, t_size, const char *, t_size) override { verboten(); }
+ void info_remove_mask(const bit_array &) override { verboten(); }
- t_size meta_set_nocheck_ex(const char * p_name, t_size p_name_length, const char * p_value, t_size p_value_length) override { verboten(); }
- t_size info_set_nocheck_ex(const char * p_name, t_size p_name_length, const char * p_value, t_size p_value_length) override { verboten(); }
+ t_size meta_set_nocheck_ex(const char *, t_size, const char *, t_size) override { verboten(); }
+ t_size info_set_nocheck_ex(const char *, t_size, const char *, t_size) override { verboten(); }
};
#define __file_info_const_impl_have_hintmap__
diff --git a/sdk/foobar2000/SDK/file_info_filter_impl.h b/sdk/foobar2000/SDK/file_info_filter_impl.h
index 2759ed5..3fbbef8 100644
--- a/sdk/foobar2000/SDK/file_info_filter_impl.h
+++ b/sdk/foobar2000/SDK/file_info_filter_impl.h
@@ -20,6 +20,7 @@ class file_info_filter_impl : public file_info_filter {
}
bool apply_filter(metadb_handle_ptr p_location, t_filestats p_stats, file_info & p_info) {
+ (void)p_stats;
t_size index;
if (m_handles.bsearch_t(pfc::compare_t, p_location, index)) {
p_info = m_infos[index];
diff --git a/sdk/foobar2000/SDK/file_info_impl.cpp b/sdk/foobar2000/SDK/file_info_impl.cpp
index 430803e..bb20f5b 100644
--- a/sdk/foobar2000/SDK/file_info_impl.cpp
+++ b/sdk/foobar2000/SDK/file_info_impl.cpp
@@ -91,12 +91,12 @@ void file_info_impl::info_remove_mask(const bit_array & p_mask)
}
-file_info_impl::file_info_impl(const file_info & p_source) : m_length(0)
+file_info_impl::file_info_impl(const file_info & p_source)
{
copy(p_source);
}
-file_info_impl::file_info_impl(const file_info_impl & p_source) : m_length(0)
+file_info_impl::file_info_impl(const file_info_impl & p_source)
{
copy(p_source);
}
@@ -107,10 +107,7 @@ const file_info_impl & file_info_impl::operator=(const file_info_impl & p_source
return *this;
}
-file_info_impl::file_info_impl() : m_length(0)
-{
- m_replaygain.reset();
-}
+file_info_impl::file_info_impl() {}
double file_info_impl::get_length() const
{
@@ -156,11 +153,19 @@ void file_info_impl_utils::info_storage::remove_mask(const bit_array & p_mask) {
}
+size_t file_info_impl_utils::meta_storage::add_blank(const char* name) {
+ meta_entry e;
+ e.m_name = name;
+ const auto ret = m_data.size();
+ m_data.add_item(std::move(e));
+ return ret;
+}
t_size file_info_impl_utils::meta_storage::add_entry(const char * p_name,t_size p_name_length,const char * p_value,t_size p_value_length)
{
- meta_entry temp(p_name,p_name_length,p_value,p_value_length);
- return pfc::append_swap_t(m_data,temp);
+ const auto ret = m_data.size();
+ m_data.add_item(meta_entry(p_name, p_name_length, p_value, p_value_length));
+ return ret;
}
void file_info_impl_utils::meta_storage::insert_value(t_size p_index,t_size p_value_index,const char * p_value,t_size p_value_length)
@@ -244,5 +249,5 @@ void file_info_impl_utils::info_storage::copy_from(const file_info & p_info)
t_size n, count;
count = p_info.info_get_count();
m_info.set_count(count);
- for(n=0;ninfo_set(n, tag.info_enum_value( iWalk ) );
}
}
+
+#ifdef FOOBAR2000_FILE_INFO_PICTURES
+ {
+ auto p = tag.info_get("pictures");
+ if ( p != nullptr ) this->info_set("pictures", p);
+ }
+#endif
}
void file_info::_add_tag(const file_info & otherTag) {
@@ -188,4 +195,11 @@ void file_info::_add_tag(const file_info & otherTag) {
}
}
}
+
+#ifdef FOOBAR2000_FILE_INFO_PICTURES
+ if (this->info_get("pictures") == nullptr) {
+ auto p = otherTag.info_get("pictures");
+ if ( p != nullptr ) info_set("pictures", p);
+ }
+#endif
}
diff --git a/sdk/foobar2000/SDK/file_operation_callback.h b/sdk/foobar2000/SDK/file_operation_callback.h
index 89677f4..5c8f18e 100644
--- a/sdk/foobar2000/SDK/file_operation_callback.h
+++ b/sdk/foobar2000/SDK/file_operation_callback.h
@@ -55,9 +55,9 @@ class file_operation_callback_dynamic_impl_base : public file_operation_callback
file_operation_callback_dynamic_impl_base() {file_operation_callback_dynamic_manager::get()->register_callback(this);}
~file_operation_callback_dynamic_impl_base() {file_operation_callback_dynamic_manager::get()->unregister_callback(this);}
- void on_files_deleted_sorted(const pfc::list_base_const_t & p_items) {}
- void on_files_moved_sorted(const pfc::list_base_const_t & p_from,const pfc::list_base_const_t & p_to) {}
- void on_files_copied_sorted(const pfc::list_base_const_t & p_from,const pfc::list_base_const_t & p_to) {}
+ void on_files_deleted_sorted(const pfc::list_base_const_t& p_items) override { (void)p_items; }
+ void on_files_moved_sorted(const pfc::list_base_const_t& p_from, const pfc::list_base_const_t& p_to) override { (void)p_from; (void)p_to; }
+ void on_files_copied_sorted(const pfc::list_base_const_t& p_from, const pfc::list_base_const_t& p_to) override { (void)p_from; (void)p_to; }
PFC_CLASS_NOT_COPYABLE_EX(file_operation_callback_dynamic_impl_base);
};
diff --git a/sdk/foobar2000/SDK/filesystem.cpp b/sdk/foobar2000/SDK/filesystem.cpp
index 9736cdc..f82f449 100644
--- a/sdk/foobar2000/SDK/filesystem.cpp
+++ b/sdk/foobar2000/SDK/filesystem.cpp
@@ -29,7 +29,7 @@ void unpacker::g_open(service_ptr_t & p_out,const service_ptr_t & p,
}
void file::seek_probe(t_filesize p_position, abort_callback & p_abort) {
- try { seek(p_position, p_abort); } catch(exception_io_seek_out_of_range) {throw exception_io_data();}
+ try { seek(p_position, p_abort); } catch(exception_io_seek_out_of_range const &) {throw exception_io_data();}
}
void file::seek_ex(t_sfilesize p_position, file::t_seek_mode p_mode, abort_callback &p_abort) {
@@ -53,7 +53,7 @@ static void makeBuffer(pfc::array_t & buffer, size_t size) {
try {
buffer.set_size_discard( size );
return;
- } catch(std::bad_alloc) {
+ } catch(std::bad_alloc const &) {
if (size < 256) throw;
size >>= 1;
}
@@ -84,7 +84,7 @@ void file::g_transfer_object(stream_reader * p_src,stream_writer * p_dst,t_files
void filesystem::g_get_canonical_path(const char * path,pfc::string_base & out)
{
- TRACK_CALL_TEXT("filesystem::g_get_canonical_path");
+ // TRACK_CALL_TEXT("filesystem::g_get_canonical_path");
for (auto ptr : enumerate()) {
if (ptr->get_canonical_path(path, out)) return;
}
@@ -92,9 +92,28 @@ void filesystem::g_get_canonical_path(const char * path,pfc::string_base & out)
out = path;
}
+void filesystem::g_get_display_path(const char* path, pfc::string_base& out, filesystem::ptr& reuseMe) {
+
+ if (reuseMe.is_valid() && reuseMe->is_our_path(path)) {
+ if (!reuseMe->get_display_path(path, out)) {
+ // should not get here
+ out = path;
+ }
+ } else {
+ if (!g_get_interface(reuseMe, path)) {
+ out = path;
+ return;
+ }
+ if (!reuseMe->get_display_path(path, out)) {
+ // should not get here
+ out = path;
+ }
+ }
+}
+
void filesystem::g_get_display_path(const char * path,pfc::string_base & out)
{
- TRACK_CALL_TEXT("filesystem::g_get_display_path");
+ // TRACK_CALL_TEXT("filesystem::g_get_display_path");
service_ptr_t ptr;
if (!g_get_interface(ptr,path))
{
@@ -108,9 +127,9 @@ void filesystem::g_get_display_path(const char * path,pfc::string_base & out)
}
}
-pfc::string8 filesystem::g_get_native_path( const char * path ) {
+pfc::string8 filesystem::g_get_native_path( const char * path, abort_callback & a ) {
pfc::string8 ret;
- g_get_native_path( path, ret );
+ g_get_native_path( path, ret, a);
return ret;
}
@@ -136,6 +155,10 @@ bool filesystem::g_get_native_path( const char * path, pfc::string_base & out, a
return strstr( path, "://" ) == NULL;
}
+filesystem::ptr filesystem::getLocalFS() {
+ return get("file://dummy");
+}
+
filesystem::ptr filesystem::tryGet(const char* path) {
filesystem::ptr rv;
g_get_interface(rv, path);
@@ -148,20 +171,91 @@ filesystem::ptr filesystem::g_get_interface(const char * path) {
return rv;
}
+
+#define USE_FSCACHE 1
+#if USE_FSCACHE
+#include
+
+static pfc::readWriteLock fsCacheGuard;
+
+typedef size_t protoHash_t;
+static protoHash_t protoHash(const char * URL) {
+ const char* delim = strstr(URL, "://");
+ if (delim == nullptr) return 0;
+
+ union {
+ protoHash_t hash;
+ char chars[sizeof(protoHash_t)];
+ } u;
+ u.hash = 0;
+ unsigned i = 0;
+ for (const char* walk = URL; walk != delim; ++walk) {
+ u.chars[i] ^= pfc::ascii_tolower_lookup(*walk);
+ i = (i + 1) % std::size(u.chars);
+ }
+ return u.hash;
+}
+
+// Do not use service_ptr in static objects, do not try to release them in static object destructor
+static std::unordered_multimap< protoHash_t, filesystem* > fsCache;
+
+static bool read_fs_cache(protoHash_t key, const char * path, filesystem::ptr& ret) {
+ auto range = fsCache.equal_range(key);
+ for (auto walk = range.first; walk != range.second; ++walk) {
+ if (walk->second->is_our_path(path)) {
+ ret = walk->second;
+ return true;
+ }
+ }
+ return false;
+}
+
bool filesystem::g_get_interface(service_ptr_t & p_out,const char * path)
{
PFC_ASSERT( path != nullptr );
PFC_ASSERT( path[0] != 0 );
+ const auto key = protoHash(path);
+
+ {
+ PFC_INSYNC_READ(fsCacheGuard);
+ if (read_fs_cache(key, path, p_out)) return true;
+ }
+
+ for (auto ptr : enumerate()) {
+ if (ptr->is_our_path(path)) {
+ {
+ PFC_INSYNC_WRITE(fsCacheGuard);
+ filesystem::ptr dummy; // make sure it didn't just get added
+ if (!read_fs_cache(key, path, dummy)) {
+ auto addref = ptr;
+ fsCache.insert({ key, addref.detach()});
+ }
+ }
+ p_out = std::move(ptr);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+#else
+bool filesystem::g_get_interface(service_ptr_t& p_out, const char* path)
+{
+ PFC_ASSERT(path != nullptr);
+ PFC_ASSERT(path[0] != 0);
+
for (auto ptr : enumerate()) {
if (ptr->is_our_path(path)) {
- p_out = ptr;
+ p_out = std::move(ptr);
return true;
}
}
return false;
}
+#endif
void filesystem::g_open(service_ptr_t & p_out,const char * path,t_open_mode mode,abort_callback & p_abort)
{
@@ -180,7 +274,7 @@ bool filesystem::g_exists(const char * p_path,abort_callback & p_abort)
bool dummy;
try {
g_get_stats(p_path,stats,dummy,p_abort);
- } catch(exception_io_not_found) {return false;}
+ } catch(exception_io_not_found const &) {return false;}
return true;
}
@@ -190,7 +284,7 @@ bool filesystem::g_exists_writeable(const char * p_path,abort_callback & p_abort
bool writeable;
try {
g_get_stats(p_path,stats,writeable,p_abort);
- } catch(exception_io_not_found) {return false;}
+ } catch(exception_io_not_found const &) {return false;}
return writeable;
}
@@ -226,6 +320,7 @@ void filesystem::g_move(const char * src,const char * dst,abort_callback & p_abo
}
void filesystem::g_link(const char * p_src,const char * p_dst,abort_callback & p_abort) {
+ p_abort.check();
pfc::string8 srcN, dstN;
if (!extract_native_path(p_src, srcN) || !extract_native_path(p_dst, dstN)) throw exception_io_no_handler_for_path();
#ifdef _WIN32
@@ -329,7 +424,7 @@ namespace {
abort_callback_event get_abort_event() const override { return m_abort.get_abort_event(); }
archive_callback_lambda(abort_callback& a) : m_abort(a) {}
- bool on_entry(archive* owner, const char* url, const t_filestats& p_stats, const service_ptr_t& p_reader) override {
+ bool on_entry(archive*, const char* url, const t_filestats& p_stats, const service_ptr_t& p_reader) override {
f(url, p_stats, p_reader);
return true;
}
@@ -350,6 +445,21 @@ bool archive::is_our_archive( const char * path ) {
return true; // accept all files
}
+void archive_impl::extract_filename_ext(const char * path, pfc::string_base & outFN) {
+ pfc::string8 dummy, subpath;
+ if (archive_impl::g_parse_unpack_path(path, dummy, subpath)) {
+ outFN = pfc::filename_ext_v2( subpath );
+ } else {
+ PFC_ASSERT(!"???");
+ filesystem_v3::extract_filename_ext(path, outFN);
+ }
+}
+
+bool archive_impl::get_display_name_short(const char* in, pfc::string_base& out) {
+ extract_filename_ext(in ,out);
+ return true;
+}
+
bool archive_impl::get_canonical_path(const char * path,pfc::string_base & out)
{
if (is_our_path(path))
@@ -405,15 +515,18 @@ void archive_impl::open(service_ptr_t & p_out,const char * path,t_open_mod
void archive_impl::remove(const char * path,abort_callback & p_abort) {
- throw exception_io_denied();
+ (void)p_abort; (void)path;
+ pfc::throw_exception_with_message< exception_io_denied> ("Cannot delete files within archives");
}
void archive_impl::move(const char * src,const char * dst,abort_callback & p_abort) {
- throw exception_io_denied();
+ (void)p_abort; (void)src; (void)dst;
+ pfc::throw_exception_with_message< exception_io_denied> ("Cannot move files within archives");
}
void archive_impl::move_overwrite(const char* src, const char* dst, abort_callback& abort) {
- throw exception_io_denied();
+ (void)abort; (void)src; (void)dst;
+ pfc::throw_exception_with_message< exception_io_denied> ("Cannot move files within archives");
}
bool archive_impl::is_remote(const char * src) {
@@ -494,6 +607,27 @@ void archive_impl::g_make_unpack_path(pfc::string_base & path,const char * archi
void archive_impl::make_unpack_path(pfc::string_base & path,const char * archive,const char * file) {g_make_unpack_path(path,archive,file,get_archive_type());}
+fb2k::arrayRef archive_impl::archive_list_v4( fsItemFilePtr item, file::ptr readerOptional, abort_callback & a ) {
+
+ const auto baseStats = item->getStatsOpportunist();
+ PFC_ASSERT( ! baseStats.is_folder() );
+ auto ret = fb2k::arrayMutable::arrayWithCapacity(256);
+
+ auto reader = readerOptional;
+ if ( reader.is_empty() ) reader = item->openRead(a);
+ try {
+ this->archive_list( item->canonicalPath()->c_str(), reader, [&] ( const char * URL, t_filestats const & stats, file::ptr ) {
+ t_filestats2 stats2 = t_filestats2::from_legacy( stats );
+ stats2.set_file(); stats2.set_remote( baseStats.is_remote() ); stats2.set_readonly(true);
+ archive * blah = this; // multi inheritance fix, more than one path to filesystem which has makeItemFileStd()
+ ret->add(blah->makeItemFileStd(URL, stats2));
+ }, false, a);
+ } catch( exception_io_data const & ) {
+ if ( ret->count() == 0 ) throw;
+ }
+ return ret->makeConst();
+
+}
namespace {
@@ -502,7 +636,7 @@ namespace {
bool m_isempty;
public:
directory_callback_isempty() : m_isempty(true) {}
- bool on_entry(filesystem * owner,abort_callback & p_abort,const char * url,bool is_subdirectory,const t_filestats & p_stats)
+ bool on_entry(filesystem *,abort_callback &,const char *,bool,const t_filestats &) override
{
m_isempty = false;
return false;
@@ -513,7 +647,7 @@ namespace {
class directory_callback_dummy : public directory_callback
{
public:
- bool on_entry(filesystem * owner,abort_callback & p_abort,const char * url,bool is_subdirectory,const t_filestats & p_stats) {return false;}
+ bool on_entry(filesystem *,abort_callback &,const char *,bool,const t_filestats &) override {return false;}
};
}
@@ -557,14 +691,15 @@ namespace {
m_target.fix_dir_separator();
}
- bool on_entry(filesystem * owner,abort_callback & p_abort,const char * url,bool is_subdirectory,const t_filestats & p_stats) {
+ bool on_entry(filesystem * owner,abort_callback & p_abort,const char * url,bool is_subdirectory,const t_filestats & p_stats) override {
+ (void)p_stats;
const char * fn = url + pfc::scan_filename(url);
t_size truncat = m_target.length();
m_target += fn;
if (is_subdirectory) {
try {
m_fs->create_directory(m_target,p_abort);
- } catch(exception_io_already_exists) {}
+ } catch(exception_io_already_exists const &) {}
m_target.end_with_slash();
owner->list_directory(url,*this,p_abort);
} else {
@@ -632,7 +767,7 @@ void filesystem::copy_directory_contents(const char* p_src, const char* p_dst, a
void filesystem::copy_directory(const char * src, const char * dst, abort_callback & p_abort) {
try {
this->create_directory( dst, p_abort );
- } catch(exception_io_already_exists) {}
+ } catch(exception_io_already_exists const &) {}
this->copy_directory_contents(src, dst, p_abort);
}
@@ -640,7 +775,7 @@ void filesystem::g_copy_directory(const char * src,const char * dst,abort_callba
filesystem::ptr dstFS = filesystem::g_get_interface(dst);
try {
dstFS->create_directory( dst, p_abort );
- } catch(exception_io_already_exists) {}
+ } catch(exception_io_already_exists const &) {}
directory_callback_impl_copy cb(dst, dstFS);
g_list_directory(src,cb,p_abort);
}
@@ -665,7 +800,7 @@ void filesystem::g_copy(const char * src,const char * dst,abort_callback & p_abo
try {
file::g_copy_timestamps(r_src, r_dst, p_abort);
- } catch (exception_io) {}
+ } catch (exception_io const &) {}
}
void stream_reader::read_object(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
@@ -710,7 +845,7 @@ void file::g_transfer_file(const service_ptr_t & p_from,const service_ptr_
p_to->seek(0,p_abort);
p_to->set_eof(p_abort);
if (length == filesize_invalid) {
- g_transfer(p_from, p_to, ~0, p_abort);
+ g_transfer(p_from, p_to, filesize_invalid, p_abort);
} else if (length > 0) {
g_transfer_object(p_from,p_to,length,p_abort);
}
@@ -729,22 +864,25 @@ file::ptr filesystem::g_open_tempmem() {
}
void archive_impl::list_directory(const char * p_path,directory_callback & p_out,abort_callback & p_abort) {
+ (void)p_path; (void)p_out; (void)p_abort;
throw exception_io_not_found();
}
void archive_impl::list_directory_ex(const char* p_path, directory_callback& p_out, unsigned listMode, abort_callback& p_abort) {
+ (void)p_path; (void)p_out; (void)listMode; (void)p_abort;
throw exception_io_not_found();
}
void archive_impl::list_directory_v3(const char* path, directory_callback_v3& callback, unsigned listMode, abort_callback& p_abort) {
+ (void)path; (void)callback; (void)listMode; (void)p_abort;
throw exception_io_not_found();
}
-void archive_impl::create_directory(const char * path,abort_callback &) {
+void archive_impl::create_directory(const char *,abort_callback &) {
throw exception_io_denied();
}
-void archive_impl::make_directory(const char* path, abort_callback& abort, bool* didCreate) {
+void archive_impl::make_directory(const char*, abort_callback&, bool*) {
throw exception_io_denied();
}
@@ -812,17 +950,20 @@ void stream_reader::read_string(pfc::string_base & p_out,abort_callback & p_abor
read_string_ex(p_out,length,p_abort);
}
-void stream_reader::read_string_raw(pfc::string_base & p_out,abort_callback & p_abort) {
- enum {delta = 256};
+void stream_reader::read_string_raw(pfc::string_base & p_out,abort_callback & p_abort, size_t sanity) {
+ enum {delta = 1024};
char buffer[delta];
p_out.reset();
+ size_t didRead = 0;
for(;;) {
- t_size delta_done;
- delta_done = read(buffer,delta,p_abort);
+ auto delta_done = read(buffer,delta,p_abort);
p_out.add_string(buffer,delta_done);
if (delta_done < delta) break;
+ didRead += delta;
+ if (didRead > sanity) throw exception_io_data();
}
}
+
void stream_writer::write_string(const char * p_string,t_size p_len,abort_callback & p_abort) {
t_uint32 len = pfc::downcast_guarded(pfc::strlen_max(p_string,p_len));
write_lendian_t(len,p_abort);
@@ -830,7 +971,7 @@ void stream_writer::write_string(const char * p_string,t_size p_len,abort_callba
}
void stream_writer::write_string(const char * p_string,abort_callback & p_abort) {
- write_string(p_string,~0,p_abort);
+ write_string(p_string,SIZE_MAX,p_abort);
}
void stream_writer::write_string_raw(const char * p_string,abort_callback & p_abort) {
@@ -965,6 +1106,10 @@ PFC_NORETURN void foobar2000_io::exception_io_from_win32(DWORD p_code) {
case ERROR_INVALID_FUNCTION:
// Happens when trying to link files on FAT32 etc
throw exception_io_unsupported_feature();
+#if 0
+ case ERROR_BAD_LENGTH:
+ FB2K_BugCheckEx("ERROR_BAD_LENGTH");
+#endif
default:
throw exception_io_win32_ex(p_code);
}
@@ -994,8 +1139,10 @@ PFC_NORETURN void foobar2000_io::exception_io_from_nix(int code) {
// Should not actually get here
PFC_ASSERT(!"Trying to seek a nonseekable stream");
throw exception_io_object_not_seekable();
-
-
+ case ENOTDIR:
+ throw exception_io_not_directory();
+ case ENAMETOOLONG:
+ pfc::throw_exception_with_message("Name too long");
default:
pfc::throw_exception_with_message< exception_io>( PFC_string_formatter() << "Unknown I/O error (#" << code << ")");
}
@@ -1034,14 +1181,18 @@ t_filesize file::get_remaining(abort_callback & p_abort) {
return length - position;
}
-void file::probe_remaining(t_filesize bytes, abort_callback & p_abort) {
+bool file::probe_remaining_ex(t_filesize bytes, abort_callback& p_abort) {
t_filesize length = get_size(p_abort);
- if (length != ~0) {
+ if (length != filesize_invalid) {
t_filesize remaining = length - get_position(p_abort);
- if (remaining < bytes) throw exception_io_data_truncation();
+ if (remaining < bytes) return false;
}
+ return true;
}
+void file::probe_remaining(t_filesize bytes, abort_callback & p_abort) {
+ if (!probe_remaining_ex(bytes, p_abort)) throw exception_io_data_truncation();
+}
t_filesize file::g_transfer(service_ptr_t p_src,service_ptr_t p_dst,t_filesize p_bytes,abort_callback & p_abort) {
return g_transfer(pfc::implicit_cast(p_src.get_ptr()),pfc::implicit_cast(p_dst.get_ptr()),p_bytes,p_abort);
@@ -1124,8 +1275,13 @@ bool foobar2000_io::_extract_native_path_ptr(const char * & p_fspath) {
return true;
}
bool foobar2000_io::extract_native_path(const char * p_fspath,pfc::string_base & p_native) {
- if (!_extract_native_path_ptr(p_fspath)) return false;
+ if (strstr(p_fspath, "://") != nullptr) {
+ if (!_extract_native_path_ptr(p_fspath)) return false;
+ }
p_native = p_fspath;
+#ifndef _WIN32
+ expandHomeDir( p_native );
+#endif
return true;
}
@@ -1140,6 +1296,30 @@ bool foobar2000_io::extract_native_path_ex(const char * p_fspath, pfc::string_ba
return true;
}
+static bool extract_native_path_fsv3(const char* in, pfc::string_base& out, abort_callback& a) {
+ if (foobar2000_io::extract_native_path(in, out)) return true;
+ filesystem_v3::ptr v3;
+ if (v3 &= filesystem::tryGet(in)) {
+ auto n = v3->getNativePath(in, a);
+ if ( n.is_valid() ) {
+ out = n->c_str(); return true;
+ }
+ }
+ return false;
+}
+
+bool foobar2000_io::extract_native_path_archive_aware_ex(const char* in, pfc::string_base& out, abort_callback& a) {
+ if (extract_native_path_fsv3(in, out, a)) return true;
+
+ if (archive_impl::g_is_unpack_path(in)) {
+ pfc::string8 arc, dummy;
+ if (archive_impl::g_parse_unpack_path(in, arc, dummy)) {
+ return extract_native_path_fsv3(arc, out, a);
+ }
+ }
+ return false;
+}
+
bool foobar2000_io::extract_native_path_archive_aware(const char * in, pfc::string_base & out) {
if (foobar2000_io::extract_native_path(in, out)) return true;
if (archive_impl::g_is_unpack_path(in)) {
@@ -1158,8 +1338,7 @@ pfc::string stream_reader::read_string(abort_callback & p_abort) {
}
pfc::string stream_reader::read_string_ex(t_size p_len,abort_callback & p_abort) {
pfc::string temp;
- read_object(temp.lock_buffer(p_len),p_len,p_abort);
- temp.unlock_buffer();
+ this->read_string_ex(temp, p_len, p_abort);
return temp;
}
@@ -1167,11 +1346,11 @@ pfc::string stream_reader::read_string_ex(t_size p_len,abort_callback & p_abort)
void filesystem::remove_directory_content(const char * path, abort_callback & abort) {
class myCallback : public directory_callback {
public:
- bool on_entry(filesystem * p_owner,abort_callback & p_abort,const char * p_url,bool p_is_subdirectory,const t_filestats & p_stats) {
+ bool on_entry(filesystem * p_owner,abort_callback & p_abort,const char * p_url,bool p_is_subdirectory,const t_filestats &) {
if (p_is_subdirectory) p_owner->list_directory(p_url, *this, p_abort);
try {
p_owner->remove(p_url, p_abort);
- } catch(exception_io_not_found) {}
+ } catch(exception_io_not_found const &) {}
return true;
}
};
@@ -1193,7 +1372,7 @@ void filesystem::remove_object_recur(const char * path, abort_callback & abort)
// the classic way
try {
remove_directory_content(path, abort);
- } catch(exception_io_not_found) {}
+ } catch(exception_io_not_found const &) {}
remove(path, abort);
}
@@ -1211,11 +1390,11 @@ void foobar2000_io::purgeOldFiles(const char * directory, t_filetimestamp period
class myCallback : public directory_callback {
public:
myCallback(t_filetimestamp period) : m_base(filetimestamp_from_system_timer() - period) {}
- bool on_entry(filesystem * p_owner,abort_callback & p_abort,const char * p_url,bool p_is_subdirectory,const t_filestats & p_stats) {
+ bool on_entry(filesystem *,abort_callback & p_abort,const char * p_url,bool p_is_subdirectory,const t_filestats & p_stats) {
if (!p_is_subdirectory && p_stats.m_timestamp < m_base) {
try {
filesystem::g_remove_timeout(p_url, 1, p_abort);
- } catch(exception_io_not_found) {}
+ } catch(exception_io_not_found const &) {}
}
return true;
}
@@ -1267,7 +1446,7 @@ bool foobar2000_io::matchContentType(const char * fullString, const char * ourTy
if (lim != ~0) {
while(lim > 0 && fullString[lim-1] == ' ') --lim;
}
- return pfc::stricmp_ascii_ex(fullString,lim, ourType, ~0) == 0;
+ return pfc::stricmp_ascii_ex(fullString,lim, ourType, SIZE_MAX) == 0;
}
const char * foobar2000_io::contentTypeFromExtension( const char * ext ) {
@@ -1360,7 +1539,7 @@ bool foobar2000_io::testIfHasProtocol( const char * input ) {
bool foobar2000_io::matchProtocol(const char * fullString, const char * protocolName) {
const t_size len = strlen(protocolName);
- if (pfc::stricmp_ascii_ex(fullString, len, protocolName, len) != 0) return false;
+ if (!pfc::stringEqualsI_ascii_ex(fullString, len, protocolName, len)) return false;
return fullString[len] == ':' && fullString[len+1] == '/' && fullString[len+2] == '/';
}
void foobar2000_io::substituteProtocol(pfc::string_base & out, const char * fullString, const char * protocolName) {
@@ -1382,7 +1561,7 @@ void filesystem::move_overwrite(const char * src, const char * dst, abort_callba
}
try {
this->remove(dst, abort);
- } catch (exception_io_not_found) {}
+ } catch (exception_io_not_found const &) {}
this->move(src, dst, abort);
}
@@ -1404,7 +1583,7 @@ void filesystem::make_directory(const char * path, abort_callback & abort, bool
try {
create_directory( path, abort );
rv = true;
- } catch(exception_io_already_exists) {
+ } catch(exception_io_already_exists const &) {
}
if (didCreate != nullptr) * didCreate = rv;
}
@@ -1426,6 +1605,34 @@ bool filesystem::directory_exists(const char * path, abort_callback & abort) {
return true;
} catch (exception_io const &) { return false; }
}
+
+bool filesystem::exists(const char* path, abort_callback& a) {
+ // for rare cases of code test if EITHER FILE OR FOLDER exists at path
+ filesystem_v3::ptr v3;
+ if (v3 &= this) {
+ try {
+ v3->get_stats2(path, stats2_fileOrFolder, a);
+ return true;
+ } catch (exception_io_not_found const &) { return false; }
+ }
+ filesystem_v2::ptr v2;
+ if (v2 &= this) {
+ return v2->file_exists(path, a) || v2->directory_exists(path, a);
+ }
+
+ try {
+ t_filestats stats; bool writable;
+ get_stats(path, stats, writable, a);
+ return true;
+ } catch (exception_io const &) { }
+ try {
+ directory_callback_dummy cb;
+ list_directory(path, cb, a);
+ return true;
+ } catch (exception_io const &) { }
+ return false;
+}
+
bool filesystem::file_exists(const char * path, abort_callback & abort) {
filesystem_v2::ptr v2;
if ( v2 &= this ) {
@@ -1435,7 +1642,7 @@ bool filesystem::file_exists(const char * path, abort_callback & abort) {
t_filestats stats; bool writable;
get_stats(path, stats, writable, abort );
return true;
- } catch(exception_io) { return false; }
+ } catch(exception_io const &) { return false; }
}
char filesystem::pathSeparator() {
@@ -1540,8 +1747,12 @@ void filesystem::read_whole_file_fallback(const char * path, mem_block_container
}
bool filesystem::is_transacted() {
+#if FB2K_SUPPORT_TRANSACTED_FILESYSTEM
filesystem_transacted::ptr p;
return ( p &= this );
+#else
+ return false;
+#endif
}
void filesystem::rewrite_file(const char* path, abort_callback& abort, double opTimeout, const void* payload, size_t bytes) {
@@ -1592,7 +1803,7 @@ void filesystem::rewrite_directory(const char * path, abort_callback & abort, do
// folder.new folder already existed? clear contents
try {
retryFileDelete(opTimeout, abort, [&] { this->remove_directory_content(fnNew, abort); });
- } catch(exception_io_not_found) {}
+ } catch(exception_io_not_found const &) {}
}
// write to folder.new
@@ -1604,12 +1815,12 @@ void filesystem::rewrite_directory(const char * path, abort_callback & abort, do
if (this->directory_exists(fnOld, abort)) {
try {
retryFileDelete(opTimeout, abort, [&] { this->remove_object_recur(fnOld, abort); });
- } catch (exception_io_not_found) {}
+ } catch(exception_io_not_found const &) {}
}
try {
retryFileMove(opTimeout, abort, [&] { this->move( path, fnOld, abort ); } ) ;
haveOld = true;
- } catch(exception_io_not_found) {}
+ } catch(exception_io_not_found const &) {}
}
// move folder.new to folder
@@ -1621,7 +1832,7 @@ void filesystem::rewrite_directory(const char * path, abort_callback & abort, do
// delete folder.old if we made one
try {
retryFileDelete( opTimeout, abort, [&] { this->remove_object_recur( fnOld, abort); } );
- } catch (exception_io_not_found) {}
+ } catch (exception_io_not_found const &) {}
}
}
}
@@ -1652,7 +1863,7 @@ bool filesystem_v2::make_directory_check(const char * path, abort_callback & abo
return rv;
}
-
+#if FB2K_SUPPORT_TRANSACTED_FILESYSTEM
filesystem_transacted::ptr filesystem_transacted::create( const char * pathFor ) {
service_enum_t e;
filesystem_transacted_entry::ptr p;
@@ -1664,13 +1875,17 @@ filesystem_transacted::ptr filesystem_transacted::create( const char * pathFor )
}
return nullptr;
}
+#endif
bool filesystem::commit_if_transacted(abort_callback &abort) {
+ (void)abort;
bool rv = false;
+#if FB2K_SUPPORT_TRANSACTED_FILESYSTEM
filesystem_transacted::ptr t;
if ( t &= this ) {
t->commit( abort ); rv = true;
}
+#endif
return rv;
}
@@ -1742,7 +1957,7 @@ service_ptr file::get_metadata_(abort_callback& a) {
return ret;
}
-drivespace_t filesystem_v3::getDriveSpace(const char* pathAt, abort_callback& abort) {
+drivespace_t filesystem_v3::getDriveSpace(const char*, abort_callback&) {
throw pfc::exception_not_implemented();
}
@@ -1782,7 +1997,20 @@ void filesystem_v3::get_stats(const char* p_path, t_filestats& p_stats, bool& p_
t_filetimestamp file_v2::get_timestamp(abort_callback& p_abort) {
return this->get_stats2(stats2_timestamp, p_abort).m_timestamp;
}
+bool file_v2::is_remote() {
+ return this->get_stats2(stats2_remote, fb2k::noAbort).is_remote();
+}
+
+t_filesize file_v2::get_size(abort_callback& p_abort) {
+ return this->get_stats2(stats2_size, p_abort).m_size;
+}
+pfc::string8 file::get_content_type() {
+ pfc::string8 ret;
+ if (!this->get_content_type(ret)) ret.clear();
+ return ret;
+
+}
t_filestats2 file::get_stats2_(uint32_t f, abort_callback& a) {
t_filestats2 ret;
@@ -1842,10 +2070,10 @@ t_filetimestamp file::get_time_created(abort_callback& a) {
bool filesystem::get_display_name_short_(const char* path, pfc::string_base& out) {
- {
+ try {
filesystem_v3::ptr v3;
if (v3 &= this) return v3->get_display_name_short(path, out);
- }
+ } catch(...) {return false;} // handle nonsense path etc
pfc::string8 temp;
extract_filename_ext(path, temp);
@@ -1893,13 +2121,13 @@ void filesystem_v3::list_directory_ex(const char* p_path, directory_callback& p_
bool filesystem_v3::directory_exists(const char* path, abort_callback& abort) {
try {
return get_stats2(path, stats2_fileOrFolder, abort).is_folder();
- } catch (exception_io_not_found) { return false; }
+ } catch (exception_io_not_found const &) { return false; }
}
bool filesystem_v3::file_exists(const char* path, abort_callback& abort) {
try {
return get_stats2(path, stats2_fileOrFolder, abort).is_file();
- } catch (exception_io_not_found) { return false; }
+ } catch (exception_io_not_found const &) { return false; }
}
@@ -1907,7 +2135,7 @@ namespace {
class directory_callback_v3_to_lambda : public directory_callback_v3 {
public:
filesystem::list_callback_t f;
- bool on_entry(filesystem* owner, const char* URL, t_filestats2 const& stats, abort_callback& abort) override {
+ bool on_entry(filesystem*, const char* URL, t_filestats2 const& stats, abort_callback&) override {
f(URL, stats);
return true;
}
@@ -1920,7 +2148,7 @@ namespace {
unsigned m_listMode = 0;
bool on_entry(filesystem* p_owner, abort_callback& p_abort, const char* p_url, bool p_is_subdirectory, const t_filestats& p_stats) override {
-
+ (void)p_owner; p_abort.check();
if (m_enforceListMode) {
if (p_is_subdirectory) {
if ( (m_listMode & listMode::folders) == 0 ) return true;
@@ -1974,18 +2202,13 @@ fsItemBase::ptr filesystem_v3::findItem(const char* path, abort_callback& p_abor
try {
#endif
pfc::string8 canonical;
- get_canonical_path(path, canonical);
-
- if (this->is_remote(canonical)) {
- // Do not perform expensive checks for remote filesystems
- return makeItemFileStd(canonical);
- }
-
- if (this->file_exists(canonical, p_abort)) {
- return makeItemFileStd(canonical);
- }
- if (this->directory_exists(canonical, p_abort)) {
- return makeItemFolderStd(canonical);
+ if (get_canonical_path(path, canonical)) {
+ auto stats = this->get_stats2(path, stats2_all, p_abort);
+ if ( stats.is_folder() ) {
+ return makeItemFolderStd(canonical, stats);
+ } else {
+ return makeItemFileStd(canonical, stats );
+ }
}
throw exception_io_not_found();
#if PFC_DEBUG
@@ -2003,9 +2226,11 @@ fsItemFile::ptr filesystem_v3::findItemFile(const char* path, abort_callback& p_
try {
#endif
pfc::string8 canonical;
- get_canonical_path(path, canonical);
- if (this->is_remote(canonical) || this->file_exists(canonical, p_abort)) {
- return makeItemFileStd(canonical);
+ if (get_canonical_path(path, canonical)) {
+ auto stats = this->get_stats2( canonical, stats2_all, p_abort);
+ if ( stats.is_file() ) {
+ return makeItemFileStd(canonical, stats );
+ }
}
throw exception_io_not_found();
#if PFC_DEBUG
@@ -2023,9 +2248,11 @@ fsItemFolder::ptr filesystem_v3::findItemFolder(const char* path, abort_callback
try {
#endif
pfc::string8 canonical;
- if (!get_canonical_path(path, canonical)) throw exception_io_not_found();
- if (this->is_remote(canonical) || this->directory_exists(canonical, p_abort)) {
- return makeItemFolderStd(canonical);
+ if (get_canonical_path(path, canonical)) {
+ auto stats = this->get_stats2( canonical, stats2_all, p_abort);
+ if ( stats.is_folder() ) {
+ return makeItemFolderStd(canonical, stats );
+ }
}
throw exception_io_not_found();
#if PFC_DEBUG
@@ -2057,7 +2284,7 @@ fb2k::stringRef filesystem_v3::fileNameSanity(const char* fn) {
return ret;
}
-void filesystem_v3::readStatsMulti(fb2k::arrayRef items, uint32_t s2flags, t_filestats2* outStats, abort_callback& abort) {
+static void readStatsMultiStd(fb2k::arrayRef items, uint32_t s2flags, t_filestats2* outStats, abort_callback& abort) {
const size_t count = items->size();
for (size_t w = 0; w < count; ++w) {
abort.check();
@@ -2065,14 +2292,31 @@ void filesystem_v3::readStatsMulti(fb2k::arrayRef items, uint32_t s2flags, t_fil
try {
fsItemPtr f; f ^= items->itemAt(w);
out = f->getStats2(s2flags, abort);
- } catch (exception_aborted) {
+ } catch (exception_aborted const &) {
throw;
- } catch (exception_io) {
+ } catch (exception_io const &) {
out = filestats2_invalid;
}
}
}
+void filesystem_v3::readStatsMulti(fb2k::arrayRef items, uint32_t s2flags, t_filestats2* outStats, abort_callback& abort) {
+ readStatsMultiStd(items, s2flags, outStats, abort);
+}
+
+pfc::string8 t_filestats::describe() const {
+ pfc::string8 ret;
+ ret << "size: ";
+ if (m_size != filesize_invalid) ret << m_size;
+ else ret << "N/A";
+ ret << "\n";
+ ret << "last-modified: ";
+ if (m_timestamp != filetimestamp_invalid) ret << m_timestamp;
+ else ret << "N/A";
+ ret << "\n";
+ return ret;
+}
+
pfc::string8 t_filestats2::describe() const {
pfc::string8 ret;
ret << "size: ";
@@ -2097,13 +2341,10 @@ pfc::string8 t_filestats2::describe() const {
t_filestats foobar2000_io::nixMakeFileStats(const struct stat & st) {
t_filestats out = filestats_invalid;
out.m_size = st.st_size;
-#if defined(__ANDROID__)
- out.m_timestamp = pfc::fileTimeUtoW(st.st_mtime /* + st.st_mtime_nsec / 1000000000 */ );
-#elif !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
- out.m_timestamp = pfc::fileTimeUtoW(st.st_mtimespec );
- // out.m_timestampCreate = pfc::fileTimeUtoW( st.st_birthtimespec );
-#else
- out.m_timestamp = pfc::fileTimeUtoW(st.st_mtime);
+#ifdef __APPLE__
+ out.m_timestamp = pfc::fileTimeUtoW(st.st_mtimespec);
+#else // Linux
+ out.m_timestamp = pfc::fileTimeUtoW(st.st_mtim);
#endif
return out;
}
@@ -2118,10 +2359,15 @@ bool foobar2000_io::nixQueryDirectory( const struct stat & st ) {
t_filestats2 foobar2000_io::nixMakeFileStats2(const struct stat &st) {
t_filestats2 ret = t_filestats2::from_legacy( nixMakeFileStats( st ) );
- ret.m_timestampCreate = pfc::fileTimeUtoW( st.st_birthtimespec );
+#ifdef __APPLE__
+ ret.m_timestampCreate = pfc::fileTimeUtoW(st.st_birthtimespec);
+#else // Linux
+ ret.m_timestampCreate = pfc::fileTimeUtoW(st.st_ctim);
+#endif
ret.set_readonly(nixQueryReadonly(st));
- if ( nixQueryDirectory( st ) ) ret.set_folder();
- else ret.set_file();
+ if ( st.st_mode & S_IFDIR ) ret.set_folder();
+ else if (st.st_mode & S_IFREG ) ret.set_file();
+ ret.set_remote(false);
return ret;
}
@@ -2176,3 +2422,98 @@ bool filesystem::g_get_display_name_short( const char * path, pfc::string_base &
out = path;
return false;
}
+
+
+bool filesystem::g_compare_paths(const char* p1, const char* p2, int& result) {
+ if (strcmp(p1, p2) == 0) {
+ result = 0; return true;
+ }
+
+ {
+ auto s1 = strstr(p1, "://");
+ auto s2 = strstr(p2, "://");
+ if (s1 == nullptr || s2 == nullptr) {
+ PFC_ASSERT(!"Invalid arguments");
+ return false;
+ }
+ size_t prefix = s1 - p1;
+ if (prefix != (size_t)(s2 - p2)) return false; // protocol mismatch
+ if (memcmp(p1, p2, prefix) != 0) return false; // protocol mismatch
+ }
+
+ filesystem::ptr fs;
+ if (!g_get_interface(fs, p1)) {
+ PFC_ASSERT(!"Invalid arguments");
+ return false;
+ }
+ pfc::string8 temp1(p1), temp2(p2);
+ auto delim = fs->pathSeparator();
+ temp1.end_with(delim); temp2.end_with(delim);
+ if (strcmp(temp1, temp2) == 0) { result = 0; return true; }
+
+ //result 1 if p2 is a subpath of p1, -1 if p1 is a subpath of p2
+ if (pfc::string_has_prefix(temp1, temp2)) {
+ // temp1 starts with temp2
+ // p1 a subfolder of p2
+ result = -1;
+ return true;
+ } else if (pfc::string_has_prefix(temp2, temp1)) {
+ // temp2 starts with temp1
+ // p2 a subfolder of p1
+ result = 1;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+size_t file::receive(void* ptr, size_t bytes, abort_callback& a) {
+ stream_receive::ptr obj;
+ if (obj &= this) return obj->receive(ptr, bytes, a);
+ else return this->read(ptr, bytes, a);
+}
+
+void filesystem::g_readStatsMulti(fb2k::arrayRef items, uint32_t s2flags, t_filestats2* outStats, abort_callback& abort) {
+ if (items->size() == 0) return;
+ fsItemPtr aFile; aFile ^= items->itemAt(0);
+ filesystem_v3::ptr fs;
+ if (fs &= aFile->getFS()) {
+ fs->readStatsMulti(items, s2flags, outStats, abort);
+ } else {
+ readStatsMultiStd(items, s2flags, outStats, abort);
+ }
+}
+
+void file::set_stats(t_filestats2 const& stats, abort_callback& a) {
+ if (stats.haveTimestamp() || stats.haveTimestampCreate()) {
+ filetimes_t ft;
+ ft.creation = stats.m_timestampCreate;
+ ft.lastWrite = stats.m_timestamp;
+ this->setFileTimes(ft, a);
+ }
+}
+
+fb2k::stringRef filesystem::fileNameSanity_(const char* fn) {
+ filesystem_v3::ptr v3;
+ if (v3 &= this) return v3->fileNameSanity(fn);
+ throw pfc::exception_not_implemented();
+}
+
+drivespace_t filesystem::getDriveSpace_(const char* pathAt, abort_callback& abort) {
+ filesystem_v3::ptr v3;
+ if (v3 &= this) return v3->getDriveSpace(pathAt, abort);
+ throw pfc::exception_not_implemented();
+}
+
+size_t stream_receive::read_using_receive(void* ptr_, size_t bytes, abort_callback& a) {
+ size_t walk = 0;
+ auto ptr = reinterpret_cast(ptr_);
+ while(walk < bytes) {
+ size_t want = bytes-walk;
+ size_t delta = this->receive(ptr+walk, want, a);
+ PFC_ASSERT( delta <= want );
+ if ( delta == 0 ) break;
+ walk += delta;
+ }
+ return walk;
+}
\ No newline at end of file
diff --git a/sdk/foobar2000/SDK/filesystem.h b/sdk/foobar2000/SDK/filesystem.h
index d686387..4f02adb 100644
--- a/sdk/foobar2000/SDK/filesystem.h
+++ b/sdk/foobar2000/SDK/filesystem.h
@@ -75,8 +75,8 @@ namespace foobar2000_io
//! Helper
t_filestats get_stats( const char * path, abort_callback & abort );
- virtual bool relative_path_create(const char * file_path,const char * playlist_path,pfc::string_base & out) {return false;}
- virtual bool relative_path_parse(const char * relative_path,const char * playlist_path,pfc::string_base & out) {return false;}
+ virtual bool relative_path_create(const char* file_path, const char* playlist_path, pfc::string_base& out) { (void)file_path; (void)playlist_path; (void)out; return false; }
+ virtual bool relative_path_parse(const char* relative_path, const char* playlist_path, pfc::string_base& out) { (void)relative_path; (void)playlist_path; (void)out; return false; }
//! Creates a directory.
virtual void create_directory(const char * p_path,abort_callback & p_abort) = 0;
@@ -89,17 +89,19 @@ namespace foobar2000_io
static void g_get_canonical_path(const char * path,pfc::string_base & out);
static void g_get_display_path(const char * path,pfc::string_base & out);
+ static void g_get_display_path(const char* path, pfc::string_base& out, filesystem::ptr & reuseMe);
//! Retrieves a shortened display name for this file. By default this is implemented by returning filename.ext portion of the path.
static bool g_get_display_name_short( const char * path, pfc::string_base & out );
//! Extracts the native filesystem path, sets out to the input path if native path cannot be extracted so the output is always set.
//! @returns True if native path was extracted successfully, false otherwise (but output is set anyway).
static bool g_get_native_path( const char * path, pfc::string_base & out, abort_callback & a = fb2k::noAbort);
- static pfc::string8 g_get_native_path( const char * path );
+ static pfc::string8 g_get_native_path( const char * path, abort_callback & a = fb2k::noAbort );
static bool g_get_interface(service_ptr_t & p_out,const char * path);//path is AFTER get_canonical_path
static filesystem::ptr g_get_interface(const char * path);// throws exception_io_no_handler_for_path on failure
- static filesystem::ptr get( const char * path ) { return g_get_interface(path); } // shortened
- static filesystem::ptr tryGet(const char* path);
+ static filesystem::ptr get( const char * path ) { return g_get_interface(path); } // shortened; never returns null, throws on failure
+ static filesystem::ptr getLocalFS(); // returns local filesystem object
+ static filesystem::ptr tryGet(const char* path); // returns null if not found instead of throwing
static bool g_is_remote(const char * p_path);//path is AFTER get_canonical_path
static bool g_is_recognized_and_remote(const char * p_path);//path is AFTER get_canonical_path
static bool g_is_remote_safe(const char * p_path) {return g_is_recognized_and_remote(p_path);}
@@ -169,8 +171,13 @@ namespace foobar2000_io
//! Bool retval version of make_directory().
bool make_directory_check( const char * path, abort_callback & abort );
+ //! Returns whether a directory exists at path, false if doesn't exist or not a directory.
bool directory_exists(const char * path, abort_callback & abort);
+ //! Returns whether a file exists at path, false if doesn't exist or not a file.
bool file_exists( const char * path, abort_callback & abort );
+ //! Returns whether either a file or a directory exists at path. Effectively directory_exists() || file_exists(), but somewhat more efficient.
+ bool exists(const char* path, abort_callback& a);
+
char pathSeparator();
//! Extracts the filename.ext portion of the path. \n
//! The filename is ready to be presented to the user - URL decoding and such (similar to get_display_path()) is applied.
@@ -220,11 +227,28 @@ namespace foobar2000_io
fsItemFolder::ptr makeItemFolderStd(const char* pathCanonical, t_filestats2 const& stats = filestats2_invalid );
fsItemFile::ptr makeItemFileStd(const char* pathCanonical, t_filestats2 const& stats = filestats2_invalid );
+ fsItemBase::ptr findItem_(const char* path, abort_callback& p_abort);
+ fsItemFile::ptr findItemFile_(const char* path, abort_callback& p_abort);
+ fsItemFolder::ptr findItemFolder_(const char* path, abort_callback& p_abort);
typedef std::function list_callback_t;
void list_directory_(const char* path, list_callback_t cb, unsigned listMode,abort_callback& a);
+ //! Compares two paths determining if one is a subpath of another,
+ //! Returns false if the paths are unrelated.
+ //! Returns true if the paths are related, and then: result is set 0 if they are equal, 1 if p2 is a subpath of p1, -1 if p1 is a subpath of p2
+ static bool g_compare_paths(const char* p1, const char* p2, int& result);
+
+ //! Batch file stats read. Some filesystems provide an optimized implementation of this.
+ static void g_readStatsMulti(fb2k::arrayRef items, uint32_t s2flags, t_filestats2* outStats, abort_callback& abort);
+
+ //! See filesystem_v3::fileNameSanity(). Throws pfc::exception_not_implemented() if not available.
+ fb2k::stringRef fileNameSanity_(const char* fn);
+
+ //! See filesystem_v3::getDriveSpace(). Throws pfc::exception_not_implemented() if not available.
+ drivespace_t getDriveSpace_(const char* pathAt, abort_callback& abort);
+
protected:
static bool get_parent_helper(const char * path, char separator, pfc::string_base & out);
void read_whole_file_fallback( const char * path, mem_block_container & out, pfc::string_base & outContentType, size_t maxBytes, abort_callback & abort );
@@ -282,7 +306,7 @@ namespace foobar2000_io
//! Optional method to return a native filesystem path to this item, null if N/A.
//! Aborter provided for corner cases, normally not needed.
- virtual fb2k::stringRef getNativePath(const char* in, abort_callback& a) { return nullptr; }
+ virtual fb2k::stringRef getNativePath(const char* in, abort_callback& a) { (void)in; (void)a; return nullptr; }
// Old method wrapped to get_stats2()
void get_stats(const char* p_path, t_filestats& p_stats, bool& p_is_writeable, abort_callback& p_abort) override;
@@ -381,7 +405,7 @@ namespace foobar2000_io
directory_callback_retrieveListRecur(t_list & p_list) : m_list(p_list) {}
bool on_entry(filesystem * owner,abort_callback & p_abort,const char * path, bool isSubdir, const t_filestats&) {
if (isSubdir) {
- try { owner->list_directory(path,*this,p_abort); } catch(exception_io) {}
+ try { owner->list_directory(path,*this,p_abort); } catch(exception_io const &) {}
} else {
m_list.add_item(path);
}
@@ -418,6 +442,7 @@ namespace foobar2000_io
bool extract_native_path_ex(const char * p_fspath, pfc::string_base & p_native);//prepends \\?\ where needed
bool extract_native_path_archive_aware( const char * fspatch, pfc::string_base & out );
+ bool extract_native_path_archive_aware_ex( const char * fspatch, pfc::string_base & out, abort_callback & a );
template
pfc::string getPathDisplay(const T& source) {
diff --git a/sdk/foobar2000/SDK/filesystem_helper.cpp b/sdk/foobar2000/SDK/filesystem_helper.cpp
index 2011574..7ae5b0f 100644
--- a/sdk/foobar2000/SDK/filesystem_helper.cpp
+++ b/sdk/foobar2000/SDK/filesystem_helper.cpp
@@ -278,3 +278,21 @@ pfc::string8 file_path_display(const char* src) {
filesystem::g_get_display_path(src, ret);
return ret;
}
+
+pfc::string8 fb2k::filename_ext( const char * path, filesystem::ptr & fs) {
+ if ( fs.is_empty() || ! fs->is_our_path( path ) ) {
+ fs = filesystem::tryGet( path );
+ }
+ if ( fs.is_valid() ) return fs->extract_filename_ext( path );
+ // UGLY FALLBACK
+ return pfc::string_filename_ext( path );
+
+}
+pfc::string8 fb2k::filename_ext( const char * path ) {
+ filesystem::ptr no_reuse;
+ return filename_ext( path, no_reuse );
+}
+
+pfc::string8 fb2k::filename( const char * path ) {
+ return pfc::remove_ext_v2( filename_ext( path ) );
+}
diff --git a/sdk/foobar2000/SDK/filesystem_helper.h b/sdk/foobar2000/SDK/filesystem_helper.h
index 79ff32a..02e86d0 100644
--- a/sdk/foobar2000/SDK/filesystem_helper.h
+++ b/sdk/foobar2000/SDK/filesystem_helper.h
@@ -26,7 +26,7 @@ namespace foobar2000_io {
public:
listDirectoryCallbackImpl() {}
listDirectoryCallbackImpl( listDirectoryFunc_t f ) : m_func(f) {}
- bool on_entry(filesystem * p_owner, abort_callback & p_abort, const char * p_url, bool p_is_subdirectory, const t_filestats & p_stats) {
+ bool on_entry(filesystem *, abort_callback &, const char * p_url, bool p_is_subdirectory, const t_filestats & p_stats) override {
m_func(p_url, p_stats, p_is_subdirectory);
return true;
}
@@ -43,6 +43,15 @@ namespace foobar2000_io {
pfc::string8 file_path_canonical(const char* src);
pfc::string8 file_path_display(const char* src);
+namespace fb2k {
+ //! Sane replacement for pfc::string_filename_ext(), which isn't safe to use in cross-platform code.
+ //! @returns Filename with extension extracted from path.
+ pfc::string8 filename_ext( const char * path );
+ pfc::string8 filename_ext( const char * path, filesystem::ptr & fs_reuse);
+ //! Sane replacement for pfc::string_filename(), which isn't safe to use in cross-platform code
+ //! @returns Filename without extension extracted from path.
+ pfc::string8 filename( const char * path );
+}
class stream_reader_memblock_ref : public stream_reader
{
@@ -65,17 +74,20 @@ class stream_reader_memblock_ref : public stream_reader
}
t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
+ p_abort.check();
t_size delta = pfc::min_t(p_bytes, get_remaining());
memcpy(p_buffer,m_data+m_pointer,delta);
m_pointer += delta;
return delta;
}
void read_object(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
+ p_abort.check();
if (p_bytes > get_remaining()) throw exception_io_data_truncation();
memcpy(p_buffer,m_data+m_pointer,p_bytes);
m_pointer += p_bytes;
}
t_filesize skip(t_filesize p_bytes,abort_callback & p_abort) {
+ p_abort.check();
t_size remaining = get_remaining();
if (p_bytes >= remaining) {
m_pointer = m_data_size; return remaining;
@@ -84,6 +96,7 @@ class stream_reader_memblock_ref : public stream_reader
}
}
void skip_object(t_filesize p_bytes,abort_callback & p_abort) {
+ p_abort.check();
if (p_bytes > get_remaining()) {
throw exception_io_data_truncation();
} else {
@@ -228,7 +241,7 @@ class stream_reader_chunk : public stream_reader
unsigned char m_buffer[255];
};
-class stream_reader_dummy : public stream_reader { t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {return 0;} };
+class stream_reader_dummy : public stream_reader { t_size read(void *,t_size,abort_callback &) override {return 0;} };
@@ -365,7 +378,7 @@ template class _IsTypeByte {
};
template stream_reader_formatter & operator>>(stream_reader_formatter & p_stream,TVal (& p_array)[Count]) {
- if (_IsTypeByte::value) {
+ if constexpr (_IsTypeByte::value) {
p_stream.read_raw(p_array,Count);
} else {
for(t_size walk = 0; walk < Count; ++walk) p_stream >> p_array[walk];
@@ -374,7 +387,7 @@ template stream_reader_formatter stream_writer_formatter & operator<<(stream_writer_formatter & p_stream,TVal const (& p_array)[Count]) {
- if (_IsTypeByte::value) {
+ if constexpr (_IsTypeByte::value) {
p_stream.write_raw(p_array,Count);
} else {
for(t_size walk = 0; walk < Count; ++walk) p_stream << p_array[walk];
@@ -549,7 +562,7 @@ template class _stream_writer_formatter_translator {
pfc::lores_timer timer; timer.start(); \
for(;;) { \
try { {OP;} break; } \
- catch(EXCEPTION) { if (timer.query() > TIMEOUT) throw;} \
+ catch(const EXCEPTION &) { if (timer.query() > TIMEOUT) throw;} \
ABORT.sleep(0.05); \
} \
}
@@ -559,8 +572,8 @@ template class _stream_writer_formatter_translator {
pfc::lores_timer timer; timer.start(); \
for(;;) { \
try { {OP;} break; } \
- catch(EXCEPTION1) { if (timer.query() > TIMEOUT) throw;} \
- catch(EXCEPTION2) { if (timer.query() > TIMEOUT) throw;} \
+ catch(const EXCEPTION1 &) { if (timer.query() > TIMEOUT) throw;} \
+ catch(const EXCEPTION2 &) { if (timer.query() > TIMEOUT) throw;} \
ABORT.sleep(0.05); \
} \
}
@@ -570,9 +583,9 @@ template class _stream_writer_formatter_translator {
pfc::lores_timer timer; timer.start(); \
for(;;) { \
try { {OP;} break; } \
- catch(EXCEPTION1) { if (timer.query() > TIMEOUT) throw;} \
- catch(EXCEPTION2) { if (timer.query() > TIMEOUT) throw;} \
- catch(EXCEPTION3) { if (timer.query() > TIMEOUT) throw;} \
+ catch(const EXCEPTION1 &) { if (timer.query() > TIMEOUT) throw;} \
+ catch(const EXCEPTION2 &) { if (timer.query() > TIMEOUT) throw;} \
+ catch(const EXCEPTION3 &) { if (timer.query() > TIMEOUT) throw;} \
ABORT.sleep(0.05); \
} \
}
diff --git a/sdk/foobar2000/SDK/filesystem_transacted.h b/sdk/foobar2000/SDK/filesystem_transacted.h
index 9574ecc..e93eaee 100644
--- a/sdk/foobar2000/SDK/filesystem_transacted.h
+++ b/sdk/foobar2000/SDK/filesystem_transacted.h
@@ -2,42 +2,6 @@
#include "filesystem.h"
-//! Object cannot be opened in transacted mode.
-PFC_DECLARE_EXCEPTION(exception_io_transactions_unsupported, exception_io, "Transactions unsupported on this volume");
-PFC_DECLARE_EXCEPTION(exception_io_transactional_conflict, exception_io, "Transactional conflict");
-PFC_DECLARE_EXCEPTION(exception_io_transaction_aborted, exception_io, "Transaction aborted");
-
-//! An instance of a filesystem transaction. Inherits from filesystem API and provides all the methods. \n
-//! To perform a transacted filesystem update, you must call methods on this object specifically - not static methods of filesystem class, not methods of a filesystem instance obtained from someplace else. \n
-//! Call commit() when done, then release the object. If you release the object without having called commit(), the update will be rolled back. \n
-//! Please keep in mind that you must not explicitly rely on this API and always provide a fallback mechanism. \n
-//! A transacted operation may be impossible for the following reasons: \n
-//! Too old foobar2000 version - filesystem_transacted was first published at version 1.4 beta 7 - obtaining a filesystem_transacted instance will fail. \n
-//! Too old Windows OS - transacted APIs are available starting from Vista, not available on XP - obtaining a filesystem_transacted instance will fail. \n
-//! Functionality disabled by user - obtaining a filesystem_transacted instance will fail. \n
-//! The volume you're trying to work with does not support transacted updates - network share, non-NTFS USB stick, etc - create() will succeed but operations will fail with exception_io_transactions_unsupported. \n
-class filesystem_transacted : public filesystem_v2 {
- FB2K_MAKE_SERVICE_INTERFACE(filesystem_transacted, filesystem_v2);
-public:
- //! Commits the transaction. You should release this filesystem_transacted object when done. \n
- //! If you don't call commit, all operations made with this filesystem_transacted instance will be rolled back.
- virtual void commit(abort_callback & abort) = 0;
-
- //! Helper to obtain a new instance. Will return null if filesystem_transacted is unavailable.
- static filesystem_transacted::ptr create( const char * pathFor );
-};
-
-//! \since 1.4
-//! An entrypoint interface to create filesystem_transacted instances. Use filesystem_transacted::create() instead of calling this directly.
-class filesystem_transacted_entry : public service_base {
- FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(filesystem_transacted_entry);
-public:
- //! May return null if transacted ops are not available in this location for one reason or another.
- virtual filesystem_transacted::ptr create( const char * pathFor ) = 0;
-
- virtual bool is_our_path( const char * path ) = 0;
-};
-
// Since 1.5, transacted filesystem is no longer supported
// as it adds extra complexity without actually solving any problems.
// Even Microsoft recommends not to use this API.
diff --git a/sdk/foobar2000/SDK/foobar2000-versions.h b/sdk/foobar2000/SDK/foobar2000-versions.h
index 2e0bca5..b445675 100644
--- a/sdk/foobar2000/SDK/foobar2000-versions.h
+++ b/sdk/foobar2000/SDK/foobar2000-versions.h
@@ -2,21 +2,40 @@
// foobar2000-versions.h
// foobar2000 SDK version and target API levels are declared in this header
+#ifdef _WIN32
+// Windows
+
// This SDK does NOT SUPPORT targets older than API 80 / foobar2000 v1.5
#define FOOBAR2000_TARGET_VERSION 80 // 1.5, 1.6
// #define FOOBAR2000_TARGET_VERSION 81 // 2.0
+
#ifdef _M_IX86
#define FOOBAR2000_TARGET_VERSION_COMPATIBLE 72
#else
// x64 & ARM64 targets
-// Allow components made with new foobar2000 v1.6 SDK with x64 & ARM64 targets
+// Allow components made with special foobar2000 v1.6 SDK with x64 & ARM64 targets
#define FOOBAR2000_TARGET_VERSION_COMPATIBLE 80
#endif
+
+#else // _WIN32
+// Not Windows
+#define FOOBAR2000_TARGET_VERSION 81
+#define FOOBAR2000_TARGET_VERSION_COMPATIBLE 81
+#endif // _WIN32
+
// Can safely use foobar2000 v2.0 features?
#define FOOBAR2020 (FOOBAR2000_TARGET_VERSION>=81)
// Use this to determine what foobar2000 SDK version is in use, undefined for releases older than 2018
-#define FOOBAR2000_SDK_VERSION 20221116
\ No newline at end of file
+#define FOOBAR2000_SDK_VERSION 20241203
+
+// cfg_var downgrade support, experimental, intended for specific components only.
+// Allows new style configStore data to be imported back to old foobar2000 friendly cfg_vars.
+// Intended to retain config when reverting FOOBAR2000_TARGET_VERSION value of 81 or newer to 80.
+// Takes effect with FOOBAR2000_TARGET_VERSION 80 only.
+// Place FOOBAR2000_IMPLEMENT_CFG_VAR_DOWNGRADE somewhere in your code to declare init calls for cfg_var downgrade. Or, if you wish to call manually, call cfg_var_reader::downgrade_main() before accessing your cfg_vars.
+// Spurious calls to cfg_var_reader::downgrade_main() will be ignored, only first one will take effect.
+#define FOOBAR2000_SUPPORT_CFG_VAR_DOWNGRADE 0
\ No newline at end of file
diff --git a/sdk/foobar2000/SDK/foobar2000-winver.h b/sdk/foobar2000/SDK/foobar2000-winver.h
index c0c9bd7..9376f22 100644
--- a/sdk/foobar2000/SDK/foobar2000-winver.h
+++ b/sdk/foobar2000/SDK/foobar2000-winver.h
@@ -21,6 +21,10 @@
#define FOOBAR2000_HAVE_KEYBOARD_SHORTCUTS
#endif
+#ifdef __APPLE__
+#define FOOBAR2000_SUPPORT_DLLS 1
+#define FOOBAR2000_MAC
+#endif
#ifndef FOOBAR2000_SUPPORT_DLLS
#define FOOBAR2000_SUPPORT_DLLS 0
diff --git a/sdk/foobar2000/SDK/foobar2000.h b/sdk/foobar2000/SDK/foobar2000.h
index 6e5796a..59fb9f6 100644
--- a/sdk/foobar2000/SDK/foobar2000.h
+++ b/sdk/foobar2000/SDK/foobar2000.h
@@ -116,4 +116,8 @@
#include "ui_edit_context.h"
#include "toolbarDropDown.h"
+#include "commonObjects-Apple.h"
+#include "ui_element_mac.h"
+
#endif //_FOOBAR2000_H_
+
diff --git a/sdk/foobar2000/SDK/foobar2000_SDK.vcxproj b/sdk/foobar2000/SDK/foobar2000_SDK.vcxproj
index 3f8a9d1..56b3983 100644
--- a/sdk/foobar2000/SDK/foobar2000_SDK.vcxproj
+++ b/sdk/foobar2000/SDK/foobar2000_SDK.vcxproj
@@ -37,6 +37,7 @@
{E8091321-D79D-4575-86EF-064EA1A4A20D}
foobar2000_SDK
+ 10.0
@@ -44,14 +45,14 @@
false
Unicode
true
- v143
+ v142
StaticLibrary
false
Unicode
true
- v143
+ v142
StaticLibrary
@@ -71,13 +72,13 @@
StaticLibrary
false
Unicode
- v143
+ v142
StaticLibrary
false
Unicode
- v143
+ v142
StaticLibrary
@@ -346,6 +347,7 @@
+
@@ -354,6 +356,7 @@
+
@@ -464,6 +467,7 @@
+
@@ -512,7 +516,6 @@
-
diff --git a/sdk/foobar2000/SDK/foobar2000_SDK.vcxproj.filters b/sdk/foobar2000/SDK/foobar2000_SDK.vcxproj.filters
index 73c200e..5d72358 100644
--- a/sdk/foobar2000/SDK/foobar2000_SDK.vcxproj.filters
+++ b/sdk/foobar2000/SDK/foobar2000_SDK.vcxproj.filters
@@ -401,6 +401,15 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
@@ -589,9 +598,6 @@
Source Files
-
- Source Files
-
Source Files
diff --git a/sdk/foobar2000/SDK/foobar2000_SDK.xcodeproj/project.pbxproj b/sdk/foobar2000/SDK/foobar2000_SDK.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..728fa95
--- /dev/null
+++ b/sdk/foobar2000/SDK/foobar2000_SDK.xcodeproj/project.pbxproj
@@ -0,0 +1,1115 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 0F75F4942A6B1CA800A45078 /* foosort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3C82A6B1CA000A45078 /* foosort.cpp */; };
+ 0F75F4952A6B1CA800A45078 /* genrand.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3C92A6B1CA000A45078 /* genrand.h */; };
+ 0F75F4962A6B1CA800A45078 /* ui_element_typable_window_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3CA2A6B1CA000A45078 /* ui_element_typable_window_manager.h */; };
+ 0F75F4972A6B1CA800A45078 /* titleformat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3CB2A6B1CA000A45078 /* titleformat.cpp */; };
+ 0F75F4982A6B1CA800A45078 /* file_info_merge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3CC2A6B1CA000A45078 /* file_info_merge.cpp */; };
+ 0F75F4992A6B1CA800A45078 /* audio_chunk.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3CD2A6B1CA000A45078 /* audio_chunk.h */; };
+ 0F75F49A2A6B1CA800A45078 /* output.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3CE2A6B1CA000A45078 /* output.h */; };
+ 0F75F49B2A6B1CA800A45078 /* file_operation_callback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3CF2A6B1CA000A45078 /* file_operation_callback.cpp */; };
+ 0F75F49C2A6B1CA800A45078 /* abort_callback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3D02A6B1CA000A45078 /* abort_callback.cpp */; };
+ 0F75F49D2A6B1CA800A45078 /* playlist_loader.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3D12A6B1CA000A45078 /* playlist_loader.h */; };
+ 0F75F49E2A6B1CA800A45078 /* threaded_process.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3D22A6B1CA000A45078 /* threaded_process.h */; };
+ 0F75F49F2A6B1CA800A45078 /* service_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3D32A6B1CA000A45078 /* service_impl.h */; };
+ 0F75F4A02A6B1CA800A45078 /* main_thread_callback.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3D42A6B1CA000A45078 /* main_thread_callback.h */; };
+ 0F75F4A12A6B1CA800A45078 /* filesystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3D52A6B1CA000A45078 /* filesystem.cpp */; };
+ 0F75F4A22A6B1CA800A45078 /* stdafx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3D62A6B1CA000A45078 /* stdafx.cpp */; };
+ 0F75F4A32A6B1CA800A45078 /* progress_meter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3D72A6B1CA000A45078 /* progress_meter.h */; };
+ 0F75F4A42A6B1CA800A45078 /* app_close_blocker.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3D82A6B1CA000A45078 /* app_close_blocker.h */; };
+ 0F75F4A52A6B1CA800A45078 /* mem_block_container.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3D92A6B1CA000A45078 /* mem_block_container.cpp */; };
+ 0F75F4A62A6B1CA800A45078 /* filesystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3DA2A6B1CA000A45078 /* filesystem.h */; };
+ 0F75F4A72A6B1CA800A45078 /* commonObjects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3DB2A6B1CA000A45078 /* commonObjects.cpp */; };
+ 0F75F4A82A6B1CA800A45078 /* threadPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3DC2A6B1CA000A45078 /* threadPool.h */; };
+ 0F75F4A92A6B1CA800A45078 /* noInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3DD2A6B1CA000A45078 /* noInfo.h */; };
+ 0F75F4AA2A6B1CA800A45078 /* unpack.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3DE2A6B1CA000A45078 /* unpack.h */; };
+ 0F75F4AB2A6B1CA800A45078 /* output.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3DF2A6B1CA000A45078 /* output.cpp */; };
+ 0F75F4AC2A6B1CA800A45078 /* menu_helpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3E02A6B1CA000A45078 /* menu_helpers.cpp */; };
+ 0F75F4AD2A6B1CA800A45078 /* service.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3E12A6B1CA000A45078 /* service.h */; };
+ 0F75F4AE2A6B1CA800A45078 /* file_info_const_impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3E22A6B1CA100A45078 /* file_info_const_impl.cpp */; };
+ 0F75F4AF2A6B1CA800A45078 /* chapterizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3E32A6B1CA100A45078 /* chapterizer.h */; };
+ 0F75F4B02A6B1CA800A45078 /* audio_chunk_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3E42A6B1CA100A45078 /* audio_chunk_impl.h */; };
+ 0F75F4B12A6B1CA800A45078 /* audio_chunk_channel_config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3E52A6B1CA100A45078 /* audio_chunk_channel_config.cpp */; };
+ 0F75F4B22A6B1CA800A45078 /* event_logger.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3E62A6B1CA100A45078 /* event_logger.h */; };
+ 0F75F4B32A6B1CA800A45078 /* foobar2000-pfc.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3E72A6B1CA100A45078 /* foobar2000-pfc.h */; };
+ 0F75F4B42A6B1CA800A45078 /* http_client.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3E82A6B1CA100A45078 /* http_client.h */; };
+ 0F75F4B52A6B1CA800A45078 /* hasher_md5.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3E92A6B1CA100A45078 /* hasher_md5.h */; };
+ 0F75F4B62A6B1CA800A45078 /* component.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3EA2A6B1CA100A45078 /* component.h */; };
+ 0F75F4B72A6B1CA800A45078 /* forward_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3EB2A6B1CA100A45078 /* forward_types.h */; };
+ 0F75F4B82A6B1CA800A45078 /* replaygain.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3EC2A6B1CA100A45078 /* replaygain.h */; };
+ 0F75F4B92A6B1CA800A45078 /* metadb.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3ED2A6B1CA100A45078 /* metadb.h */; };
+ 0F75F4BA2A6B1CA800A45078 /* menu_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3EE2A6B1CA100A45078 /* menu_manager.cpp */; };
+ 0F75F4BB2A6B1CA800A45078 /* resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3EF2A6B1CA100A45078 /* resampler.h */; };
+ 0F75F4BC2A6B1CA800A45078 /* initquit.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3F02A6B1CA100A45078 /* initquit.h */; };
+ 0F75F4BD2A6B1CA800A45078 /* foobar2000-winver.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3F12A6B1CA100A45078 /* foobar2000-winver.h */; };
+ 0F75F4BE2A6B1CA800A45078 /* timer.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3F22A6B1CA100A45078 /* timer.h */; };
+ 0F75F4BF2A6B1CA800A45078 /* playlist.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3F32A6B1CA100A45078 /* playlist.h */; };
+ 0F75F4C02A6B1CA800A45078 /* track_property.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3F42A6B1CA100A45078 /* track_property.cpp */; };
+ 0F75F4C12A6B1CA800A45078 /* ui.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3F52A6B1CA100A45078 /* ui.cpp */; };
+ 0F75F4C22A6B1CA800A45078 /* titleformat.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3F62A6B1CA100A45078 /* titleformat.h */; };
+ 0F75F4C32A6B1CA800A45078 /* file_info_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3F72A6B1CA100A45078 /* file_info_impl.h */; };
+ 0F75F4C42A6B1CA800A45078 /* filesystem_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3F82A6B1CA100A45078 /* filesystem_helper.cpp */; };
+ 0F75F4C52A6B1CA800A45078 /* filesystem_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3F92A6B1CA100A45078 /* filesystem_helper.h */; };
+ 0F75F4C62A6B1CA800A45078 /* audio_chunk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3FA2A6B1CA100A45078 /* audio_chunk.cpp */; };
+ 0F75F4C72A6B1CA800A45078 /* console_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3FB2A6B1CA100A45078 /* console_manager.h */; };
+ 0F75F4C82A6B1CA800A45078 /* filesystem_transacted.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3FC2A6B1CA100A45078 /* filesystem_transacted.h */; };
+ 0F75F4C92A6B1CA800A45078 /* service.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3FD2A6B1CA100A45078 /* service.cpp */; };
+ 0F75F4CA2A6B1CA800A45078 /* threaded_process.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3FE2A6B1CA200A45078 /* threaded_process.cpp */; };
+ 0F75F4CB2A6B1CA800A45078 /* coreDarkMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F3FF2A6B1CA200A45078 /* coreDarkMode.h */; };
+ 0F75F4CC2A6B1CA800A45078 /* cfg_var_legacy.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4002A6B1CA200A45078 /* cfg_var_legacy.h */; };
+ 0F75F4CD2A6B1CA800A45078 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4012A6B1CA200A45078 /* menu.h */; };
+ 0F75F4CE2A6B1CA800A45078 /* file_info_filter_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4022A6B1CA200A45078 /* file_info_filter_impl.h */; };
+ 0F75F4CF2A6B1CA800A45078 /* advconfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4032A6B1CA200A45078 /* advconfig.cpp */; };
+ 0F75F4D02A6B1CA800A45078 /* packet_decoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4042A6B1CA200A45078 /* packet_decoder.h */; };
+ 0F75F4D12A6B1CA800A45078 /* decode_postprocessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4052A6B1CA200A45078 /* decode_postprocessor.h */; };
+ 0F75F4D22A6B1CA800A45078 /* exception_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4062A6B1CA200A45078 /* exception_io.h */; };
+ 0F75F4D32A6B1CA800A45078 /* search_tools.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4072A6B1CA200A45078 /* search_tools.h */; };
+ 0F75F4D42A6B1CA800A45078 /* metadb_callbacks.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4082A6B1CA200A45078 /* metadb_callbacks.h */; };
+ 0F75F4D52A6B1CA800A45078 /* configStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4092A6B1CA200A45078 /* configStore.cpp */; };
+ 0F75F4D62A6B1CA800A45078 /* modeless_dialog.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F40A2A6B1CA200A45078 /* modeless_dialog.h */; };
+ 0F75F4D72A6B1CA800A45078 /* foosort.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F40B2A6B1CA200A45078 /* foosort.h */; };
+ 0F75F4D82A6B1CA800A45078 /* console.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F40C2A6B1CA200A45078 /* console.cpp */; };
+ 0F75F4D92A6B1CA800A45078 /* playback_control.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F40D2A6B1CA200A45078 /* playback_control.h */; };
+ 0F75F4DA2A6B1CA800A45078 /* playback_stream_capture.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F40E2A6B1CA200A45078 /* playback_stream_capture.h */; };
+ 0F75F4DB2A6B1CA800A45078 /* file_info.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F40F2A6B1CA200A45078 /* file_info.cpp */; };
+ 0F75F4DC2A6B1CA800A45078 /* dsp.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4102A6B1CA200A45078 /* dsp.h */; };
+ 0F75F4DD2A6B1CA800A45078 /* library_callbacks.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4112A6B1CA200A45078 /* library_callbacks.h */; };
+ 0F75F4DE2A6B1CA800A45078 /* threadsLite.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4122A6B1CA200A45078 /* threadsLite.h */; };
+ 0F75F4DF2A6B1CA800A45078 /* mem_block_container.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4132A6B1CA200A45078 /* mem_block_container.h */; };
+ 0F75F4E02A6B1CA800A45078 /* foobar2000-all.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4142A6B1CA200A45078 /* foobar2000-all.h */; };
+ 0F75F4E12A6B1CA800A45078 /* image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4152A6B1CA200A45078 /* image.cpp */; };
+ 0F75F4E22A6B1CA800A45078 /* utility.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4162A6B1CA200A45078 /* utility.cpp */; };
+ 0F75F4E32A6B1CA800A45078 /* track_property.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4172A6B1CA200A45078 /* track_property.h */; };
+ 0F75F4E42A6B1CA800A45078 /* cfg_var.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4182A6B1CA200A45078 /* cfg_var.cpp */; };
+ 0F75F4E52A6B1CA800A45078 /* shortcut_actions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4192A6B1CA200A45078 /* shortcut_actions.h */; };
+ 0F75F4E72A6B1CA800A45078 /* exceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F41B2A6B1CA200A45078 /* exceptions.h */; };
+ 0F75F4E82A6B1CA800A45078 /* component_client.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F41C2A6B1CA200A45078 /* component_client.h */; };
+ 0F75F4E92A6B1CA800A45078 /* playable_location.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F41D2A6B1CA200A45078 /* playable_location.cpp */; };
+ 0F75F4EA2A6B1CA800A45078 /* metadb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F41E2A6B1CA200A45078 /* metadb.cpp */; };
+ 0F75F4EB2A6B1CA800A45078 /* input_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F41F2A6B1CA200A45078 /* input_impl.h */; };
+ 0F75F4EC2A6B1CA800A45078 /* service_compat.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4202A6B1CA200A45078 /* service_compat.h */; };
+ 0F75F4ED2A6B1CA800A45078 /* keyValueIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4212A6B1CA300A45078 /* keyValueIO.h */; };
+ 0F75F4EE2A6B1CA800A45078 /* tracks.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4222A6B1CA300A45078 /* tracks.h */; };
+ 0F75F4EF2A6B1CA800A45078 /* playlistColumnProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4232A6B1CA300A45078 /* playlistColumnProvider.h */; };
+ 0F75F4F02A6B1CA800A45078 /* imageLoaderLite.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4242A6B1CA300A45078 /* imageLoaderLite.h */; };
+ 0F75F4F12A6B1CA800A45078 /* playlist.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4252A6B1CA300A45078 /* playlist.cpp */; };
+ 0F75F4F22A6B1CA800A45078 /* preferences_page.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4262A6B1CA300A45078 /* preferences_page.cpp */; };
+ 0F75F4F32A6B1CA800A45078 /* config_io_callback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4272A6B1CA300A45078 /* config_io_callback.cpp */; };
+ 0F75F4F42A6B1CA800A45078 /* packet_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4282A6B1CA300A45078 /* packet_decoder.cpp */; };
+ 0F75F4F52A6B1CA800A45078 /* ole_interaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4292A6B1CA300A45078 /* ole_interaction.h */; };
+ 0F75F4F62A6B1CA800A45078 /* info_lookup_handler.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F42A2A6B1CA300A45078 /* info_lookup_handler.h */; };
+ 0F75F4F72A6B1CA800A45078 /* library_index.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F42B2A6B1CA300A45078 /* library_index.h */; };
+ 0F75F4F82A6B1CA800A45078 /* audioEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F42C2A6B1CA300A45078 /* audioEncoder.h */; };
+ 0F75F4F92A6B1CA800A45078 /* commandline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F42D2A6B1CA300A45078 /* commandline.cpp */; };
+ 0F75F4FA2A6B1CA800A45078 /* image.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F42E2A6B1CA300A45078 /* image.h */; };
+ 0F75F4FB2A6B1CA800A45078 /* link_resolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F42F2A6B1CA300A45078 /* link_resolver.cpp */; };
+ 0F75F4FC2A6B1CA800A45078 /* metadb_handle.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4302A6B1CA300A45078 /* metadb_handle.h */; };
+ 0F75F4FD2A6B1CA800A45078 /* advconfig_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4312A6B1CA300A45078 /* advconfig_impl.h */; };
+ 0F75F4FE2A6B1CA800A45078 /* foobar2000-versions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4322A6B1CA300A45078 /* foobar2000-versions.h */; };
+ 0F75F4FF2A6B1CA800A45078 /* componentversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4332A6B1CA300A45078 /* componentversion.h */; };
+ 0F75F5002A6B1CA800A45078 /* contextmenu.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4342A6B1CA300A45078 /* contextmenu.h */; };
+ 0F75F5012A6B1CA800A45078 /* advconfig_impl_legacy.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4352A6B1CA300A45078 /* advconfig_impl_legacy.h */; };
+ 0F75F5022A6B1CA800A45078 /* file.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4362A6B1CA300A45078 /* file.h */; };
+ 0F75F5032A6B1CA800A45078 /* ui_element.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4372A6B1CA300A45078 /* ui_element.cpp */; };
+ 0F75F5042A6B1CA800A45078 /* guids.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4382A6B1CA300A45078 /* guids.cpp */; };
+ 0F75F5052A6B1CA800A45078 /* tag_processor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4392A6B1CA300A45078 /* tag_processor.cpp */; };
+ 0F75F5062A6B1CA800A45078 /* config_object.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F43A2A6B1CA300A45078 /* config_object.h */; };
+ 0F75F5072A6B1CA800A45078 /* config_object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F43B2A6B1CA300A45078 /* config_object.cpp */; };
+ 0F75F5082A6B1CA800A45078 /* ui.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F43C2A6B1CA300A45078 /* ui.h */; };
+ 0F75F5092A6B1CA800A45078 /* link_resolver.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F43D2A6B1CA300A45078 /* link_resolver.h */; };
+ 0F75F50A2A6B1CA800A45078 /* playable_location.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F43E2A6B1CA300A45078 /* playable_location.h */; };
+ 0F75F50B2A6B1CA800A45078 /* file_lock_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F43F2A6B1CA300A45078 /* file_lock_manager.h */; };
+ 0F75F50C2A6B1CA800A45078 /* configStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4402A6B1CA300A45078 /* configStore.h */; };
+ 0F75F50D2A6B1CA800A45078 /* input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4412A6B1CA300A45078 /* input.cpp */; };
+ 0F75F50E2A6B1CA800A45078 /* contextmenu_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4422A6B1CA300A45078 /* contextmenu_manager.h */; };
+ 0F75F50F2A6B1CA800A45078 /* config_io_callback.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4432A6B1CA400A45078 /* config_io_callback.h */; };
+ 0F75F5102A6B1CA800A45078 /* menu_common.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4442A6B1CA400A45078 /* menu_common.h */; };
+ 0F75F5112A6B1CA800A45078 /* completion_notify.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4452A6B1CA400A45078 /* completion_notify.cpp */; };
+ 0F75F5122A6B1CA800A45078 /* metadb_info_container_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4462A6B1CA400A45078 /* metadb_info_container_impl.h */; };
+ 0F75F5132A6B1CA800A45078 /* abort_callback.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4472A6B1CA400A45078 /* abort_callback.h */; };
+ 0F75F5142A6B1CA800A45078 /* completion_notify.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4482A6B1CA400A45078 /* completion_notify.h */; };
+ 0F75F5152A6B1CA800A45078 /* toolbarDropDown.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4492A6B1CA400A45078 /* toolbarDropDown.h */; };
+ 0F75F5162A6B1CA800A45078 /* dsp_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F44A2A6B1CA400A45078 /* dsp_manager.h */; };
+ 0F75F5172A6B1CA800A45078 /* messageBox.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F44B2A6B1CA400A45078 /* messageBox.h */; };
+ 0F75F5182A6B1CA800A45078 /* ui_edit_context.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F44C2A6B1CA400A45078 /* ui_edit_context.h */; };
+ 0F75F5192A6B1CA800A45078 /* ui_element.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F44D2A6B1CA400A45078 /* ui_element.h */; };
+ 0F75F51A2A6B1CA800A45078 /* menu_item.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F44E2A6B1CA400A45078 /* menu_item.cpp */; };
+ 0F75F51B2A6B1CA800A45078 /* callback_merit.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F44F2A6B1CA400A45078 /* callback_merit.h */; };
+ 0F75F51C2A6B1CA800A45078 /* foosortstring.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4502A6B1CA400A45078 /* foosortstring.h */; };
+ 0F75F51D2A6B1CA800A45078 /* imageViewer.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4512A6B1CA400A45078 /* imageViewer.h */; };
+ 0F75F51E2A6B1CA800A45078 /* popup_message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4522A6B1CA400A45078 /* popup_message.cpp */; };
+ 0F75F51F2A6B1CA800A45078 /* fsItem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4532A6B1CA400A45078 /* fsItem.cpp */; };
+ 0F75F5202A6B1CA800A45078 /* cfg_var_legacy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4542A6B1CA400A45078 /* cfg_var_legacy.cpp */; };
+ 0F75F5212A6B1CA800A45078 /* vis.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4552A6B1CA500A45078 /* vis.h */; };
+ 0F75F5222A6B1CA800A45078 /* console.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4562A6B1CA500A45078 /* console.h */; };
+ 0F75F5232A6B1CA800A45078 /* componentversion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4572A6B1CA500A45078 /* componentversion.cpp */; };
+ 0F75F5242A6B1CA800A45078 /* chapterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4582A6B1CA500A45078 /* chapterizer.cpp */; };
+ 0F75F5252A6B1CA800A45078 /* components_menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4592A6B1CA500A45078 /* components_menu.h */; };
+ 0F75F5262A6B1CA800A45078 /* metadb_handle_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F45A2A6B1CA500A45078 /* metadb_handle_list.cpp */; };
+ 0F75F5272A6B1CA800A45078 /* preferences_page.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F45B2A6B1CA500A45078 /* preferences_page.h */; };
+ 0F75F5282A6B1CA800A45078 /* core_api.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F45C2A6B1CA500A45078 /* core_api.h */; };
+ 0F75F5292A6B1CA800A45078 /* config_object_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F45D2A6B1CA500A45078 /* config_object_impl.h */; };
+ 0F75F52A2A6B1CA800A45078 /* configCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F45E2A6B1CA500A45078 /* configCache.h */; };
+ 0F75F52B2A6B1CA800A45078 /* metadb_display_field_provider.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F45F2A6B1CA500A45078 /* metadb_display_field_provider.h */; };
+ 0F75F52C2A6B1CA800A45078 /* dsp-frontend.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4602A6B1CA500A45078 /* dsp-frontend.h */; };
+ 0F75F52D2A6B1CA800A45078 /* foobar2000-sdk-pch.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4612A6B1CA500A45078 /* foobar2000-sdk-pch.h */; };
+ 0F75F52E2A6B1CA800A45078 /* file_cached_impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4622A6B1CA500A45078 /* file_cached_impl.cpp */; };
+ 0F75F52F2A6B1CA800A45078 /* replaygain_info.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4632A6B1CA500A45078 /* replaygain_info.cpp */; };
+ 0F75F5302A6B1CA800A45078 /* metadb_index.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4642A6B1CA500A45078 /* metadb_index.h */; };
+ 0F75F5312A6B1CA800A45078 /* mainmenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4652A6B1CA500A45078 /* mainmenu.cpp */; };
+ 0F75F5322A6B1CA800A45078 /* app_close_blocker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4662A6B1CA500A45078 /* app_close_blocker.cpp */; };
+ 0F75F5332A6B1CA800A45078 /* file_info_impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4672A6B1CA500A45078 /* file_info_impl.cpp */; };
+ 0F75F5342A6B1CA800A45078 /* tag_processor_id3v2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4682A6B1CA500A45078 /* tag_processor_id3v2.cpp */; };
+ 0F75F5352A6B1CA800A45078 /* audio_postprocessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4692A6B1CA500A45078 /* audio_postprocessor.h */; };
+ 0F75F5362A6B1CA800A45078 /* file_info.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F46A2A6B1CA500A45078 /* file_info.h */; };
+ 0F75F5372A6B1CA800A45078 /* replaygain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F46B2A6B1CA500A45078 /* replaygain.cpp */; };
+ 0F75F5382A6B1CA800A45078 /* input.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F46C2A6B1CA500A45078 /* input.h */; };
+ 0F75F5392A6B1CA800A45078 /* file_format_sanitizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F46D2A6B1CA600A45078 /* file_format_sanitizer.h */; };
+ 0F75F53A2A6B1CA800A45078 /* commonObjects.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F46E2A6B1CA600A45078 /* commonObjects.h */; };
+ 0F75F53B2A6B1CA800A45078 /* powerManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F46F2A6B1CA600A45078 /* powerManager.h */; };
+ 0F75F53C2A6B1CA800A45078 /* popup_message.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4702A6B1CA600A45078 /* popup_message.h */; };
+ 0F75F53D2A6B1CA800A45078 /* archive.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4712A6B1CA600A45078 /* archive.h */; };
+ 0F75F53E2A6B1CA800A45078 /* system_time_keeper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4722A6B1CA600A45078 /* system_time_keeper.h */; };
+ 0F75F53F2A6B1CA800A45078 /* play_callback.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4732A6B1CA600A45078 /* play_callback.h */; };
+ 0F75F5402A6B1CA800A45078 /* file_info_const_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4742A6B1CA600A45078 /* file_info_const_impl.h */; };
+ 0F75F5412A6B1CA800A45078 /* file_operation_callback.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4752A6B1CA600A45078 /* file_operation_callback.h */; };
+ 0F75F5422A6B1CA800A45078 /* album_art.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4762A6B1CA600A45078 /* album_art.cpp */; };
+ 0F75F5432A6B1CA800A45078 /* main_thread_callback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4772A6B1CA600A45078 /* main_thread_callback.cpp */; };
+ 0F75F5442A6B1CA800A45078 /* replaygain_scanner.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4782A6B1CA600A45078 /* replaygain_scanner.h */; };
+ 0F75F5452A6B1CA800A45078 /* tag_processor.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4792A6B1CA600A45078 /* tag_processor.h */; };
+ 0F75F5462A6B1CA800A45078 /* input_file_type.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F47A2A6B1CA700A45078 /* input_file_type.cpp */; };
+ 0F75F5472A6B1CA800A45078 /* album_art.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F47B2A6B1CA700A45078 /* album_art.h */; };
+ 0F75F5482A6B1CA800A45078 /* advconfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F47C2A6B1CA700A45078 /* advconfig.h */; };
+ 0F75F5492A6B1CA800A45078 /* fsitem.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F47D2A6B1CA700A45078 /* fsitem.h */; };
+ 0F75F54A2A6B1CA800A45078 /* icon_remap.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F47E2A6B1CA700A45078 /* icon_remap.h */; };
+ 0F75F54B2A6B1CA800A45078 /* keyValueIOimpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F47F2A6B1CA700A45078 /* keyValueIOimpl.h */; };
+ 0F75F54C2A6B1CA800A45078 /* fileDialog.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4802A6B1CA700A45078 /* fileDialog.h */; };
+ 0F75F54D2A6B1CA800A45078 /* autoplaylist.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4812A6B1CA700A45078 /* autoplaylist.h */; };
+ 0F75F54E2A6B1CA800A45078 /* input_file_type.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4822A6B1CA700A45078 /* input_file_type.h */; };
+ 0F75F54F2A6B1CA800A45078 /* album_art_helpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4832A6B1CA700A45078 /* album_art_helpers.h */; };
+ 0F75F5502A6B1CA800A45078 /* hasher_md5.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4842A6B1CA700A45078 /* hasher_md5.cpp */; };
+ 0F75F5512A6B1CA800A45078 /* message_loop.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4852A6B1CA700A45078 /* message_loop.h */; };
+ 0F75F5522A6B1CA800A45078 /* playlist_loader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4862A6B1CA700A45078 /* playlist_loader.cpp */; };
+ 0F75F5532A6B1CA800A45078 /* service_by_guid.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4872A6B1CA700A45078 /* service_by_guid.h */; };
+ 0F75F5542A6B1CA800A45078 /* library_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4882A6B1CA700A45078 /* library_manager.h */; };
+ 0F75F5552A6B1CA800A45078 /* foobar2000-lite.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4892A6B1CA700A45078 /* foobar2000-lite.h */; };
+ 0F75F5562A6B1CA800A45078 /* commandline.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F48A2A6B1CA700A45078 /* commandline.h */; };
+ 0F75F5572A6B1CA800A45078 /* coreversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F48B2A6B1CA700A45078 /* coreversion.h */; };
+ 0F75F5582A6B1CA800A45078 /* dsp_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F48C2A6B1CA700A45078 /* dsp_manager.cpp */; };
+ 0F75F5592A6B1CA800A45078 /* playback_control.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F48D2A6B1CA700A45078 /* playback_control.cpp */; };
+ 0F75F55A2A6B1CA800A45078 /* foobar2000.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F48E2A6B1CA700A45078 /* foobar2000.h */; };
+ 0F75F55B2A6B1CA800A45078 /* file_info_filter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F48F2A6B1CA700A45078 /* file_info_filter.h */; };
+ 0F75F55C2A6B1CA800A45078 /* metadb_handle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4902A6B1CA800A45078 /* metadb_handle.cpp */; };
+ 0F75F55D2A6B1CA800A45078 /* dsp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F4912A6B1CA800A45078 /* dsp.cpp */; };
+ 0F75F55E2A6B1CA800A45078 /* cfg_var.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4922A6B1CA800A45078 /* cfg_var.h */; };
+ 0F75F55F2A6B1CA800A45078 /* menu_helpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F4932A6B1CA800A45078 /* menu_helpers.h */; };
+ 0FCA711C2AA2210C001CB0F2 /* commonObjects-Apple.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0FCA711A2AA2210C001CB0F2 /* commonObjects-Apple.mm */; };
+ 0FDB0DFC2CEB60A900178906 /* ui_element_mac.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FDB0DFB2CEB60A500178906 /* ui_element_mac.h */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 0F75F3C82A6B1CA000A45078 /* foosort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = foosort.cpp; sourceTree = ""; };
+ 0F75F3C92A6B1CA000A45078 /* genrand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = genrand.h; sourceTree = ""; };
+ 0F75F3CA2A6B1CA000A45078 /* ui_element_typable_window_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ui_element_typable_window_manager.h; sourceTree = ""; };
+ 0F75F3CB2A6B1CA000A45078 /* titleformat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = titleformat.cpp; sourceTree = ""; };
+ 0F75F3CC2A6B1CA000A45078 /* file_info_merge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_info_merge.cpp; sourceTree = ""; };
+ 0F75F3CD2A6B1CA000A45078 /* audio_chunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audio_chunk.h; sourceTree = ""; };
+ 0F75F3CE2A6B1CA000A45078 /* output.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = output.h; sourceTree = ""; };
+ 0F75F3CF2A6B1CA000A45078 /* file_operation_callback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_operation_callback.cpp; sourceTree = ""; };
+ 0F75F3D02A6B1CA000A45078 /* abort_callback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = abort_callback.cpp; sourceTree = ""; };
+ 0F75F3D12A6B1CA000A45078 /* playlist_loader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = playlist_loader.h; sourceTree = ""; };
+ 0F75F3D22A6B1CA000A45078 /* threaded_process.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = threaded_process.h; sourceTree = ""; };
+ 0F75F3D32A6B1CA000A45078 /* service_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = service_impl.h; sourceTree = ""; };
+ 0F75F3D42A6B1CA000A45078 /* main_thread_callback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = main_thread_callback.h; sourceTree = ""; };
+ 0F75F3D52A6B1CA000A45078 /* filesystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filesystem.cpp; sourceTree = ""; };
+ 0F75F3D62A6B1CA000A45078 /* stdafx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stdafx.cpp; sourceTree = ""; };
+ 0F75F3D72A6B1CA000A45078 /* progress_meter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = progress_meter.h; sourceTree = ""; };
+ 0F75F3D82A6B1CA000A45078 /* app_close_blocker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = app_close_blocker.h; sourceTree = ""; };
+ 0F75F3D92A6B1CA000A45078 /* mem_block_container.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mem_block_container.cpp; sourceTree = ""; };
+ 0F75F3DA2A6B1CA000A45078 /* filesystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filesystem.h; sourceTree = ""; };
+ 0F75F3DB2A6B1CA000A45078 /* commonObjects.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = commonObjects.cpp; sourceTree = ""; };
+ 0F75F3DC2A6B1CA000A45078 /* threadPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = threadPool.h; sourceTree = ""; };
+ 0F75F3DD2A6B1CA000A45078 /* noInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = noInfo.h; sourceTree = ""; };
+ 0F75F3DE2A6B1CA000A45078 /* unpack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unpack.h; sourceTree = ""; };
+ 0F75F3DF2A6B1CA000A45078 /* output.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = output.cpp; sourceTree = ""; };
+ 0F75F3E02A6B1CA000A45078 /* menu_helpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = menu_helpers.cpp; sourceTree = "