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. -[![Build Status](https://ci.appveyor.com/api/projects/status/github/hyperblast/foosdk?branch=master&svg=true)](https://ci.appveyor.com/project/hyperblast/foosdk) +[![Build Status](https://github.com/hyperblast/foosdk/actions/workflows/build.yml/badge.svg)](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 = ""; }; + 0F75F3E12A6B1CA000A45078 /* service.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = service.h; sourceTree = ""; }; + 0F75F3E22A6B1CA100A45078 /* file_info_const_impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_info_const_impl.cpp; sourceTree = ""; }; + 0F75F3E32A6B1CA100A45078 /* chapterizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = chapterizer.h; sourceTree = ""; }; + 0F75F3E42A6B1CA100A45078 /* audio_chunk_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audio_chunk_impl.h; sourceTree = ""; }; + 0F75F3E52A6B1CA100A45078 /* audio_chunk_channel_config.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audio_chunk_channel_config.cpp; sourceTree = ""; }; + 0F75F3E62A6B1CA100A45078 /* event_logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = event_logger.h; sourceTree = ""; }; + 0F75F3E72A6B1CA100A45078 /* foobar2000-pfc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "foobar2000-pfc.h"; sourceTree = ""; }; + 0F75F3E82A6B1CA100A45078 /* http_client.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = http_client.h; sourceTree = ""; }; + 0F75F3E92A6B1CA100A45078 /* hasher_md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hasher_md5.h; sourceTree = ""; }; + 0F75F3EA2A6B1CA100A45078 /* component.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = component.h; sourceTree = ""; }; + 0F75F3EB2A6B1CA100A45078 /* forward_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = forward_types.h; sourceTree = ""; }; + 0F75F3EC2A6B1CA100A45078 /* replaygain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = replaygain.h; sourceTree = ""; }; + 0F75F3ED2A6B1CA100A45078 /* metadb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metadb.h; sourceTree = ""; }; + 0F75F3EE2A6B1CA100A45078 /* menu_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = menu_manager.cpp; sourceTree = ""; }; + 0F75F3EF2A6B1CA100A45078 /* resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resampler.h; sourceTree = ""; }; + 0F75F3F02A6B1CA100A45078 /* initquit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = initquit.h; sourceTree = ""; }; + 0F75F3F12A6B1CA100A45078 /* foobar2000-winver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "foobar2000-winver.h"; sourceTree = ""; }; + 0F75F3F22A6B1CA100A45078 /* timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = timer.h; sourceTree = ""; }; + 0F75F3F32A6B1CA100A45078 /* playlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = playlist.h; sourceTree = ""; }; + 0F75F3F42A6B1CA100A45078 /* track_property.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = track_property.cpp; sourceTree = ""; }; + 0F75F3F52A6B1CA100A45078 /* ui.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ui.cpp; sourceTree = ""; }; + 0F75F3F62A6B1CA100A45078 /* titleformat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = titleformat.h; sourceTree = ""; }; + 0F75F3F72A6B1CA100A45078 /* file_info_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_info_impl.h; sourceTree = ""; }; + 0F75F3F82A6B1CA100A45078 /* filesystem_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filesystem_helper.cpp; sourceTree = ""; }; + 0F75F3F92A6B1CA100A45078 /* filesystem_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filesystem_helper.h; sourceTree = ""; }; + 0F75F3FA2A6B1CA100A45078 /* audio_chunk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audio_chunk.cpp; sourceTree = ""; }; + 0F75F3FB2A6B1CA100A45078 /* console_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = console_manager.h; sourceTree = ""; }; + 0F75F3FC2A6B1CA100A45078 /* filesystem_transacted.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filesystem_transacted.h; sourceTree = ""; }; + 0F75F3FD2A6B1CA100A45078 /* service.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = service.cpp; sourceTree = ""; }; + 0F75F3FE2A6B1CA200A45078 /* threaded_process.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = threaded_process.cpp; sourceTree = ""; }; + 0F75F3FF2A6B1CA200A45078 /* coreDarkMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coreDarkMode.h; sourceTree = ""; }; + 0F75F4002A6B1CA200A45078 /* cfg_var_legacy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cfg_var_legacy.h; sourceTree = ""; }; + 0F75F4012A6B1CA200A45078 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = ""; }; + 0F75F4022A6B1CA200A45078 /* file_info_filter_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_info_filter_impl.h; sourceTree = ""; }; + 0F75F4032A6B1CA200A45078 /* advconfig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = advconfig.cpp; sourceTree = ""; }; + 0F75F4042A6B1CA200A45078 /* packet_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = packet_decoder.h; sourceTree = ""; }; + 0F75F4052A6B1CA200A45078 /* decode_postprocessor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decode_postprocessor.h; sourceTree = ""; }; + 0F75F4062A6B1CA200A45078 /* exception_io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = exception_io.h; sourceTree = ""; }; + 0F75F4072A6B1CA200A45078 /* search_tools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = search_tools.h; sourceTree = ""; }; + 0F75F4082A6B1CA200A45078 /* metadb_callbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metadb_callbacks.h; sourceTree = ""; }; + 0F75F4092A6B1CA200A45078 /* configStore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = configStore.cpp; sourceTree = ""; }; + 0F75F40A2A6B1CA200A45078 /* modeless_dialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modeless_dialog.h; sourceTree = ""; }; + 0F75F40B2A6B1CA200A45078 /* foosort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = foosort.h; sourceTree = ""; }; + 0F75F40C2A6B1CA200A45078 /* console.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = console.cpp; sourceTree = ""; }; + 0F75F40D2A6B1CA200A45078 /* playback_control.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = playback_control.h; sourceTree = ""; }; + 0F75F40E2A6B1CA200A45078 /* playback_stream_capture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = playback_stream_capture.h; sourceTree = ""; }; + 0F75F40F2A6B1CA200A45078 /* file_info.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_info.cpp; sourceTree = ""; }; + 0F75F4102A6B1CA200A45078 /* dsp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsp.h; sourceTree = ""; }; + 0F75F4112A6B1CA200A45078 /* library_callbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = library_callbacks.h; sourceTree = ""; }; + 0F75F4122A6B1CA200A45078 /* threadsLite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = threadsLite.h; sourceTree = ""; }; + 0F75F4132A6B1CA200A45078 /* mem_block_container.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mem_block_container.h; sourceTree = ""; }; + 0F75F4142A6B1CA200A45078 /* foobar2000-all.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "foobar2000-all.h"; sourceTree = ""; }; + 0F75F4152A6B1CA200A45078 /* image.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = image.cpp; sourceTree = ""; }; + 0F75F4162A6B1CA200A45078 /* utility.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = utility.cpp; sourceTree = ""; }; + 0F75F4172A6B1CA200A45078 /* track_property.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = track_property.h; sourceTree = ""; }; + 0F75F4182A6B1CA200A45078 /* cfg_var.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cfg_var.cpp; sourceTree = ""; }; + 0F75F4192A6B1CA200A45078 /* shortcut_actions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shortcut_actions.h; sourceTree = ""; }; + 0F75F41B2A6B1CA200A45078 /* exceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = exceptions.h; sourceTree = ""; }; + 0F75F41C2A6B1CA200A45078 /* component_client.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = component_client.h; sourceTree = ""; }; + 0F75F41D2A6B1CA200A45078 /* playable_location.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = playable_location.cpp; sourceTree = ""; }; + 0F75F41E2A6B1CA200A45078 /* metadb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = metadb.cpp; sourceTree = ""; }; + 0F75F41F2A6B1CA200A45078 /* input_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = input_impl.h; sourceTree = ""; }; + 0F75F4202A6B1CA200A45078 /* service_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = service_compat.h; sourceTree = ""; }; + 0F75F4212A6B1CA300A45078 /* keyValueIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = keyValueIO.h; sourceTree = ""; }; + 0F75F4222A6B1CA300A45078 /* tracks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tracks.h; sourceTree = ""; }; + 0F75F4232A6B1CA300A45078 /* playlistColumnProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = playlistColumnProvider.h; sourceTree = ""; }; + 0F75F4242A6B1CA300A45078 /* imageLoaderLite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = imageLoaderLite.h; sourceTree = ""; }; + 0F75F4252A6B1CA300A45078 /* playlist.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = playlist.cpp; sourceTree = ""; }; + 0F75F4262A6B1CA300A45078 /* preferences_page.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = preferences_page.cpp; sourceTree = ""; }; + 0F75F4272A6B1CA300A45078 /* config_io_callback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = config_io_callback.cpp; sourceTree = ""; }; + 0F75F4282A6B1CA300A45078 /* packet_decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = packet_decoder.cpp; sourceTree = ""; }; + 0F75F4292A6B1CA300A45078 /* ole_interaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ole_interaction.h; sourceTree = ""; }; + 0F75F42A2A6B1CA300A45078 /* info_lookup_handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = info_lookup_handler.h; sourceTree = ""; }; + 0F75F42B2A6B1CA300A45078 /* library_index.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = library_index.h; sourceTree = ""; }; + 0F75F42C2A6B1CA300A45078 /* audioEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audioEncoder.h; sourceTree = ""; }; + 0F75F42D2A6B1CA300A45078 /* commandline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = commandline.cpp; sourceTree = ""; }; + 0F75F42E2A6B1CA300A45078 /* image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = image.h; sourceTree = ""; }; + 0F75F42F2A6B1CA300A45078 /* link_resolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = link_resolver.cpp; sourceTree = ""; }; + 0F75F4302A6B1CA300A45078 /* metadb_handle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metadb_handle.h; sourceTree = ""; }; + 0F75F4312A6B1CA300A45078 /* advconfig_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = advconfig_impl.h; sourceTree = ""; }; + 0F75F4322A6B1CA300A45078 /* foobar2000-versions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "foobar2000-versions.h"; sourceTree = ""; }; + 0F75F4332A6B1CA300A45078 /* componentversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = componentversion.h; sourceTree = ""; }; + 0F75F4342A6B1CA300A45078 /* contextmenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = contextmenu.h; sourceTree = ""; }; + 0F75F4352A6B1CA300A45078 /* advconfig_impl_legacy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = advconfig_impl_legacy.h; sourceTree = ""; }; + 0F75F4362A6B1CA300A45078 /* file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file.h; sourceTree = ""; }; + 0F75F4372A6B1CA300A45078 /* ui_element.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ui_element.cpp; sourceTree = ""; }; + 0F75F4382A6B1CA300A45078 /* guids.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = guids.cpp; sourceTree = ""; }; + 0F75F4392A6B1CA300A45078 /* tag_processor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tag_processor.cpp; sourceTree = ""; }; + 0F75F43A2A6B1CA300A45078 /* config_object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config_object.h; sourceTree = ""; }; + 0F75F43B2A6B1CA300A45078 /* config_object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = config_object.cpp; sourceTree = ""; }; + 0F75F43C2A6B1CA300A45078 /* ui.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ui.h; sourceTree = ""; }; + 0F75F43D2A6B1CA300A45078 /* link_resolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = link_resolver.h; sourceTree = ""; }; + 0F75F43E2A6B1CA300A45078 /* playable_location.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = playable_location.h; sourceTree = ""; }; + 0F75F43F2A6B1CA300A45078 /* file_lock_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_lock_manager.h; sourceTree = ""; }; + 0F75F4402A6B1CA300A45078 /* configStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = configStore.h; sourceTree = ""; }; + 0F75F4412A6B1CA300A45078 /* input.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = input.cpp; sourceTree = ""; }; + 0F75F4422A6B1CA300A45078 /* contextmenu_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = contextmenu_manager.h; sourceTree = ""; }; + 0F75F4432A6B1CA400A45078 /* config_io_callback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config_io_callback.h; sourceTree = ""; }; + 0F75F4442A6B1CA400A45078 /* menu_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu_common.h; sourceTree = ""; }; + 0F75F4452A6B1CA400A45078 /* completion_notify.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = completion_notify.cpp; sourceTree = ""; }; + 0F75F4462A6B1CA400A45078 /* metadb_info_container_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metadb_info_container_impl.h; sourceTree = ""; }; + 0F75F4472A6B1CA400A45078 /* abort_callback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = abort_callback.h; sourceTree = ""; }; + 0F75F4482A6B1CA400A45078 /* completion_notify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = completion_notify.h; sourceTree = ""; }; + 0F75F4492A6B1CA400A45078 /* toolbarDropDown.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = toolbarDropDown.h; sourceTree = ""; }; + 0F75F44A2A6B1CA400A45078 /* dsp_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsp_manager.h; sourceTree = ""; }; + 0F75F44B2A6B1CA400A45078 /* messageBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = messageBox.h; sourceTree = ""; }; + 0F75F44C2A6B1CA400A45078 /* ui_edit_context.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ui_edit_context.h; sourceTree = ""; }; + 0F75F44D2A6B1CA400A45078 /* ui_element.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ui_element.h; sourceTree = ""; }; + 0F75F44E2A6B1CA400A45078 /* menu_item.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = menu_item.cpp; sourceTree = ""; }; + 0F75F44F2A6B1CA400A45078 /* callback_merit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = callback_merit.h; sourceTree = ""; }; + 0F75F4502A6B1CA400A45078 /* foosortstring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = foosortstring.h; sourceTree = ""; }; + 0F75F4512A6B1CA400A45078 /* imageViewer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = imageViewer.h; sourceTree = ""; }; + 0F75F4522A6B1CA400A45078 /* popup_message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = popup_message.cpp; sourceTree = ""; }; + 0F75F4532A6B1CA400A45078 /* fsItem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fsItem.cpp; sourceTree = ""; }; + 0F75F4542A6B1CA400A45078 /* cfg_var_legacy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cfg_var_legacy.cpp; sourceTree = ""; }; + 0F75F4552A6B1CA500A45078 /* vis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vis.h; sourceTree = ""; }; + 0F75F4562A6B1CA500A45078 /* console.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = console.h; sourceTree = ""; }; + 0F75F4572A6B1CA500A45078 /* componentversion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = componentversion.cpp; sourceTree = ""; }; + 0F75F4582A6B1CA500A45078 /* chapterizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = chapterizer.cpp; sourceTree = ""; }; + 0F75F4592A6B1CA500A45078 /* components_menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = components_menu.h; sourceTree = ""; }; + 0F75F45A2A6B1CA500A45078 /* metadb_handle_list.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = metadb_handle_list.cpp; sourceTree = ""; }; + 0F75F45B2A6B1CA500A45078 /* preferences_page.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = preferences_page.h; sourceTree = ""; }; + 0F75F45C2A6B1CA500A45078 /* core_api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core_api.h; sourceTree = ""; }; + 0F75F45D2A6B1CA500A45078 /* config_object_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config_object_impl.h; sourceTree = ""; }; + 0F75F45E2A6B1CA500A45078 /* configCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = configCache.h; sourceTree = ""; }; + 0F75F45F2A6B1CA500A45078 /* metadb_display_field_provider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metadb_display_field_provider.h; sourceTree = ""; }; + 0F75F4602A6B1CA500A45078 /* dsp-frontend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "dsp-frontend.h"; sourceTree = ""; }; + 0F75F4612A6B1CA500A45078 /* foobar2000-sdk-pch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "foobar2000-sdk-pch.h"; sourceTree = ""; }; + 0F75F4622A6B1CA500A45078 /* file_cached_impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_cached_impl.cpp; sourceTree = ""; }; + 0F75F4632A6B1CA500A45078 /* replaygain_info.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = replaygain_info.cpp; sourceTree = ""; }; + 0F75F4642A6B1CA500A45078 /* metadb_index.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metadb_index.h; sourceTree = ""; }; + 0F75F4652A6B1CA500A45078 /* mainmenu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mainmenu.cpp; sourceTree = ""; }; + 0F75F4662A6B1CA500A45078 /* app_close_blocker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = app_close_blocker.cpp; sourceTree = ""; }; + 0F75F4672A6B1CA500A45078 /* file_info_impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_info_impl.cpp; sourceTree = ""; }; + 0F75F4682A6B1CA500A45078 /* tag_processor_id3v2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tag_processor_id3v2.cpp; sourceTree = ""; }; + 0F75F4692A6B1CA500A45078 /* audio_postprocessor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audio_postprocessor.h; sourceTree = ""; }; + 0F75F46A2A6B1CA500A45078 /* file_info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_info.h; sourceTree = ""; }; + 0F75F46B2A6B1CA500A45078 /* replaygain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = replaygain.cpp; sourceTree = ""; }; + 0F75F46C2A6B1CA500A45078 /* input.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = input.h; sourceTree = ""; }; + 0F75F46D2A6B1CA600A45078 /* file_format_sanitizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_format_sanitizer.h; sourceTree = ""; }; + 0F75F46E2A6B1CA600A45078 /* commonObjects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = commonObjects.h; sourceTree = ""; }; + 0F75F46F2A6B1CA600A45078 /* powerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = powerManager.h; sourceTree = ""; }; + 0F75F4702A6B1CA600A45078 /* popup_message.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = popup_message.h; sourceTree = ""; }; + 0F75F4712A6B1CA600A45078 /* archive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive.h; sourceTree = ""; }; + 0F75F4722A6B1CA600A45078 /* system_time_keeper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = system_time_keeper.h; sourceTree = ""; }; + 0F75F4732A6B1CA600A45078 /* play_callback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = play_callback.h; sourceTree = ""; }; + 0F75F4742A6B1CA600A45078 /* file_info_const_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_info_const_impl.h; sourceTree = ""; }; + 0F75F4752A6B1CA600A45078 /* file_operation_callback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_operation_callback.h; sourceTree = ""; }; + 0F75F4762A6B1CA600A45078 /* album_art.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = album_art.cpp; sourceTree = ""; }; + 0F75F4772A6B1CA600A45078 /* main_thread_callback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main_thread_callback.cpp; sourceTree = ""; }; + 0F75F4782A6B1CA600A45078 /* replaygain_scanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = replaygain_scanner.h; sourceTree = ""; }; + 0F75F4792A6B1CA600A45078 /* tag_processor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tag_processor.h; sourceTree = ""; }; + 0F75F47A2A6B1CA700A45078 /* input_file_type.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = input_file_type.cpp; sourceTree = ""; }; + 0F75F47B2A6B1CA700A45078 /* album_art.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = album_art.h; sourceTree = ""; }; + 0F75F47C2A6B1CA700A45078 /* advconfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = advconfig.h; sourceTree = ""; }; + 0F75F47D2A6B1CA700A45078 /* fsitem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsitem.h; sourceTree = ""; }; + 0F75F47E2A6B1CA700A45078 /* icon_remap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = icon_remap.h; sourceTree = ""; }; + 0F75F47F2A6B1CA700A45078 /* keyValueIOimpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = keyValueIOimpl.h; sourceTree = ""; }; + 0F75F4802A6B1CA700A45078 /* fileDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fileDialog.h; sourceTree = ""; }; + 0F75F4812A6B1CA700A45078 /* autoplaylist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = autoplaylist.h; sourceTree = ""; }; + 0F75F4822A6B1CA700A45078 /* input_file_type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = input_file_type.h; sourceTree = ""; }; + 0F75F4832A6B1CA700A45078 /* album_art_helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = album_art_helpers.h; sourceTree = ""; }; + 0F75F4842A6B1CA700A45078 /* hasher_md5.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hasher_md5.cpp; sourceTree = ""; }; + 0F75F4852A6B1CA700A45078 /* message_loop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = message_loop.h; sourceTree = ""; }; + 0F75F4862A6B1CA700A45078 /* playlist_loader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = playlist_loader.cpp; sourceTree = ""; }; + 0F75F4872A6B1CA700A45078 /* service_by_guid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = service_by_guid.h; sourceTree = ""; }; + 0F75F4882A6B1CA700A45078 /* library_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = library_manager.h; sourceTree = ""; }; + 0F75F4892A6B1CA700A45078 /* foobar2000-lite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "foobar2000-lite.h"; sourceTree = ""; }; + 0F75F48A2A6B1CA700A45078 /* commandline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = commandline.h; sourceTree = ""; }; + 0F75F48B2A6B1CA700A45078 /* coreversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coreversion.h; sourceTree = ""; }; + 0F75F48C2A6B1CA700A45078 /* dsp_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsp_manager.cpp; sourceTree = ""; }; + 0F75F48D2A6B1CA700A45078 /* playback_control.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = playback_control.cpp; sourceTree = ""; }; + 0F75F48E2A6B1CA700A45078 /* foobar2000.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = foobar2000.h; sourceTree = ""; }; + 0F75F48F2A6B1CA700A45078 /* file_info_filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_info_filter.h; sourceTree = ""; }; + 0F75F4902A6B1CA800A45078 /* metadb_handle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = metadb_handle.cpp; sourceTree = ""; }; + 0F75F4912A6B1CA800A45078 /* dsp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsp.cpp; sourceTree = ""; }; + 0F75F4922A6B1CA800A45078 /* cfg_var.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cfg_var.h; sourceTree = ""; }; + 0F75F4932A6B1CA800A45078 /* menu_helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu_helpers.h; sourceTree = ""; }; + 0FCA711A2AA2210C001CB0F2 /* commonObjects-Apple.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "commonObjects-Apple.mm"; sourceTree = ""; }; + 0FCA711E2AA2715E001CB0F2 /* commonObjects-Apple.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "commonObjects-Apple.h"; sourceTree = ""; }; + 0FDB0DFB2CEB60A500178906 /* ui_element_mac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ui_element_mac.h; sourceTree = ""; }; + B166962019ACC1450001728F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + B166962E19ACC1450001728F /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + B166963119ACC1450001728F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + B1DD3657198A721800EF7043 /* libfoobar2000_SDK.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libfoobar2000_SDK.a; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B1DD3654198A721800EF7043 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B166961F19ACC1450001728F /* Frameworks */ = { + isa = PBXGroup; + children = ( + B166962019ACC1450001728F /* Foundation.framework */, + B166962E19ACC1450001728F /* XCTest.framework */, + B166963119ACC1450001728F /* UIKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + B1DD364E198A721800EF7043 = { + isa = PBXGroup; + children = ( + B1DD3670198A725100EF7043 /* Source */, + B166961F19ACC1450001728F /* Frameworks */, + B1DD3658198A721800EF7043 /* Products */, + ); + sourceTree = ""; + }; + B1DD3658198A721800EF7043 /* Products */ = { + isa = PBXGroup; + children = ( + B1DD3657198A721800EF7043 /* libfoobar2000_SDK.a */, + ); + name = Products; + sourceTree = ""; + }; + B1DD3670198A725100EF7043 /* Source */ = { + isa = PBXGroup; + children = ( + 0FDB0DFB2CEB60A500178906 /* ui_element_mac.h */, + 0F75F3D02A6B1CA000A45078 /* abort_callback.cpp */, + 0F75F4472A6B1CA400A45078 /* abort_callback.h */, + 0F75F4352A6B1CA300A45078 /* advconfig_impl_legacy.h */, + 0F75F4312A6B1CA300A45078 /* advconfig_impl.h */, + 0F75F4032A6B1CA200A45078 /* advconfig.cpp */, + 0F75F47C2A6B1CA700A45078 /* advconfig.h */, + 0F75F4832A6B1CA700A45078 /* album_art_helpers.h */, + 0F75F4762A6B1CA600A45078 /* album_art.cpp */, + 0F75F47B2A6B1CA700A45078 /* album_art.h */, + 0F75F4662A6B1CA500A45078 /* app_close_blocker.cpp */, + 0F75F3D82A6B1CA000A45078 /* app_close_blocker.h */, + 0F75F4712A6B1CA600A45078 /* archive.h */, + 0F75F3E52A6B1CA100A45078 /* audio_chunk_channel_config.cpp */, + 0F75F3E42A6B1CA100A45078 /* audio_chunk_impl.h */, + 0F75F3FA2A6B1CA100A45078 /* audio_chunk.cpp */, + 0F75F3CD2A6B1CA000A45078 /* audio_chunk.h */, + 0F75F4692A6B1CA500A45078 /* audio_postprocessor.h */, + 0F75F42C2A6B1CA300A45078 /* audioEncoder.h */, + 0F75F4812A6B1CA700A45078 /* autoplaylist.h */, + 0F75F44F2A6B1CA400A45078 /* callback_merit.h */, + 0F75F4542A6B1CA400A45078 /* cfg_var_legacy.cpp */, + 0F75F4002A6B1CA200A45078 /* cfg_var_legacy.h */, + 0F75F4182A6B1CA200A45078 /* cfg_var.cpp */, + 0F75F4922A6B1CA800A45078 /* cfg_var.h */, + 0F75F4582A6B1CA500A45078 /* chapterizer.cpp */, + 0F75F3E32A6B1CA100A45078 /* chapterizer.h */, + 0F75F42D2A6B1CA300A45078 /* commandline.cpp */, + 0F75F48A2A6B1CA700A45078 /* commandline.h */, + 0F75F3DB2A6B1CA000A45078 /* commonObjects.cpp */, + 0F75F46E2A6B1CA600A45078 /* commonObjects.h */, + 0F75F4452A6B1CA400A45078 /* completion_notify.cpp */, + 0F75F4482A6B1CA400A45078 /* completion_notify.h */, + 0F75F41C2A6B1CA200A45078 /* component_client.h */, + 0F75F3EA2A6B1CA100A45078 /* component.h */, + 0F75F4592A6B1CA500A45078 /* components_menu.h */, + 0F75F4572A6B1CA500A45078 /* componentversion.cpp */, + 0F75F4332A6B1CA300A45078 /* componentversion.h */, + 0F75F4272A6B1CA300A45078 /* config_io_callback.cpp */, + 0F75F4432A6B1CA400A45078 /* config_io_callback.h */, + 0F75F45D2A6B1CA500A45078 /* config_object_impl.h */, + 0F75F43B2A6B1CA300A45078 /* config_object.cpp */, + 0F75F43A2A6B1CA300A45078 /* config_object.h */, + 0F75F45E2A6B1CA500A45078 /* configCache.h */, + 0F75F4092A6B1CA200A45078 /* configStore.cpp */, + 0F75F4402A6B1CA300A45078 /* configStore.h */, + 0F75F3FB2A6B1CA100A45078 /* console_manager.h */, + 0F75F40C2A6B1CA200A45078 /* console.cpp */, + 0F75F4562A6B1CA500A45078 /* console.h */, + 0F75F4422A6B1CA300A45078 /* contextmenu_manager.h */, + 0F75F4342A6B1CA300A45078 /* contextmenu.h */, + 0F75F45C2A6B1CA500A45078 /* core_api.h */, + 0F75F3FF2A6B1CA200A45078 /* coreDarkMode.h */, + 0F75F48B2A6B1CA700A45078 /* coreversion.h */, + 0F75F4052A6B1CA200A45078 /* decode_postprocessor.h */, + 0F75F48C2A6B1CA700A45078 /* dsp_manager.cpp */, + 0F75F44A2A6B1CA400A45078 /* dsp_manager.h */, + 0F75F4602A6B1CA500A45078 /* dsp-frontend.h */, + 0F75F4912A6B1CA800A45078 /* dsp.cpp */, + 0F75F4102A6B1CA200A45078 /* dsp.h */, + 0F75F3E62A6B1CA100A45078 /* event_logger.h */, + 0F75F4062A6B1CA200A45078 /* exception_io.h */, + 0F75F41B2A6B1CA200A45078 /* exceptions.h */, + 0F75F4622A6B1CA500A45078 /* file_cached_impl.cpp */, + 0F75F46D2A6B1CA600A45078 /* file_format_sanitizer.h */, + 0F75F3E22A6B1CA100A45078 /* file_info_const_impl.cpp */, + 0F75F4742A6B1CA600A45078 /* file_info_const_impl.h */, + 0F75F4022A6B1CA200A45078 /* file_info_filter_impl.h */, + 0F75F48F2A6B1CA700A45078 /* file_info_filter.h */, + 0F75F4672A6B1CA500A45078 /* file_info_impl.cpp */, + 0F75F3F72A6B1CA100A45078 /* file_info_impl.h */, + 0F75F3CC2A6B1CA000A45078 /* file_info_merge.cpp */, + 0F75F40F2A6B1CA200A45078 /* file_info.cpp */, + 0F75F46A2A6B1CA500A45078 /* file_info.h */, + 0F75F43F2A6B1CA300A45078 /* file_lock_manager.h */, + 0F75F3CF2A6B1CA000A45078 /* file_operation_callback.cpp */, + 0F75F4752A6B1CA600A45078 /* file_operation_callback.h */, + 0F75F4362A6B1CA300A45078 /* file.h */, + 0F75F4802A6B1CA700A45078 /* fileDialog.h */, + 0F75F3F82A6B1CA100A45078 /* filesystem_helper.cpp */, + 0F75F3F92A6B1CA100A45078 /* filesystem_helper.h */, + 0F75F3FC2A6B1CA100A45078 /* filesystem_transacted.h */, + 0F75F3D52A6B1CA000A45078 /* filesystem.cpp */, + 0F75F3DA2A6B1CA000A45078 /* filesystem.h */, + 0F75F4142A6B1CA200A45078 /* foobar2000-all.h */, + 0F75F4892A6B1CA700A45078 /* foobar2000-lite.h */, + 0F75F3E72A6B1CA100A45078 /* foobar2000-pfc.h */, + 0F75F4612A6B1CA500A45078 /* foobar2000-sdk-pch.h */, + 0F75F4322A6B1CA300A45078 /* foobar2000-versions.h */, + 0F75F3F12A6B1CA100A45078 /* foobar2000-winver.h */, + 0F75F48E2A6B1CA700A45078 /* foobar2000.h */, + 0F75F3C82A6B1CA000A45078 /* foosort.cpp */, + 0F75F40B2A6B1CA200A45078 /* foosort.h */, + 0F75F4502A6B1CA400A45078 /* foosortstring.h */, + 0F75F3EB2A6B1CA100A45078 /* forward_types.h */, + 0F75F4532A6B1CA400A45078 /* fsItem.cpp */, + 0F75F47D2A6B1CA700A45078 /* fsitem.h */, + 0F75F3C92A6B1CA000A45078 /* genrand.h */, + 0F75F4382A6B1CA300A45078 /* guids.cpp */, + 0F75F4842A6B1CA700A45078 /* hasher_md5.cpp */, + 0F75F3E92A6B1CA100A45078 /* hasher_md5.h */, + 0F75F3E82A6B1CA100A45078 /* http_client.h */, + 0F75F47E2A6B1CA700A45078 /* icon_remap.h */, + 0F75F4152A6B1CA200A45078 /* image.cpp */, + 0F75F42E2A6B1CA300A45078 /* image.h */, + 0F75F4242A6B1CA300A45078 /* imageLoaderLite.h */, + 0F75F4512A6B1CA400A45078 /* imageViewer.h */, + 0F75F42A2A6B1CA300A45078 /* info_lookup_handler.h */, + 0F75F3F02A6B1CA100A45078 /* initquit.h */, + 0F75F47A2A6B1CA700A45078 /* input_file_type.cpp */, + 0F75F4822A6B1CA700A45078 /* input_file_type.h */, + 0F75F41F2A6B1CA200A45078 /* input_impl.h */, + 0F75F4412A6B1CA300A45078 /* input.cpp */, + 0F75F46C2A6B1CA500A45078 /* input.h */, + 0F75F4212A6B1CA300A45078 /* keyValueIO.h */, + 0F75F47F2A6B1CA700A45078 /* keyValueIOimpl.h */, + 0F75F4112A6B1CA200A45078 /* library_callbacks.h */, + 0F75F42B2A6B1CA300A45078 /* library_index.h */, + 0F75F4882A6B1CA700A45078 /* library_manager.h */, + 0F75F42F2A6B1CA300A45078 /* link_resolver.cpp */, + 0F75F43D2A6B1CA300A45078 /* link_resolver.h */, + 0F75F4772A6B1CA600A45078 /* main_thread_callback.cpp */, + 0F75F3D42A6B1CA000A45078 /* main_thread_callback.h */, + 0F75F4652A6B1CA500A45078 /* mainmenu.cpp */, + 0F75F3D92A6B1CA000A45078 /* mem_block_container.cpp */, + 0F75F4132A6B1CA200A45078 /* mem_block_container.h */, + 0F75F4442A6B1CA400A45078 /* menu_common.h */, + 0F75F3E02A6B1CA000A45078 /* menu_helpers.cpp */, + 0F75F4932A6B1CA800A45078 /* menu_helpers.h */, + 0F75F44E2A6B1CA400A45078 /* menu_item.cpp */, + 0F75F3EE2A6B1CA100A45078 /* menu_manager.cpp */, + 0F75F4012A6B1CA200A45078 /* menu.h */, + 0F75F4852A6B1CA700A45078 /* message_loop.h */, + 0F75F44B2A6B1CA400A45078 /* messageBox.h */, + 0F75F4082A6B1CA200A45078 /* metadb_callbacks.h */, + 0F75F45F2A6B1CA500A45078 /* metadb_display_field_provider.h */, + 0F75F45A2A6B1CA500A45078 /* metadb_handle_list.cpp */, + 0F75F4902A6B1CA800A45078 /* metadb_handle.cpp */, + 0F75F4302A6B1CA300A45078 /* metadb_handle.h */, + 0F75F4642A6B1CA500A45078 /* metadb_index.h */, + 0F75F4462A6B1CA400A45078 /* metadb_info_container_impl.h */, + 0F75F41E2A6B1CA200A45078 /* metadb.cpp */, + 0F75F3ED2A6B1CA100A45078 /* metadb.h */, + 0F75F40A2A6B1CA200A45078 /* modeless_dialog.h */, + 0F75F3DD2A6B1CA000A45078 /* noInfo.h */, + 0F75F4292A6B1CA300A45078 /* ole_interaction.h */, + 0F75F3DF2A6B1CA000A45078 /* output.cpp */, + 0F75F3CE2A6B1CA000A45078 /* output.h */, + 0F75F4282A6B1CA300A45078 /* packet_decoder.cpp */, + 0F75F4042A6B1CA200A45078 /* packet_decoder.h */, + 0F75F4732A6B1CA600A45078 /* play_callback.h */, + 0F75F41D2A6B1CA200A45078 /* playable_location.cpp */, + 0F75F43E2A6B1CA300A45078 /* playable_location.h */, + 0F75F48D2A6B1CA700A45078 /* playback_control.cpp */, + 0F75F40D2A6B1CA200A45078 /* playback_control.h */, + 0F75F40E2A6B1CA200A45078 /* playback_stream_capture.h */, + 0F75F4862A6B1CA700A45078 /* playlist_loader.cpp */, + 0F75F3D12A6B1CA000A45078 /* playlist_loader.h */, + 0F75F4252A6B1CA300A45078 /* playlist.cpp */, + 0F75F3F32A6B1CA100A45078 /* playlist.h */, + 0F75F4232A6B1CA300A45078 /* playlistColumnProvider.h */, + 0F75F4522A6B1CA400A45078 /* popup_message.cpp */, + 0F75F4702A6B1CA600A45078 /* popup_message.h */, + 0F75F46F2A6B1CA600A45078 /* powerManager.h */, + 0F75F4262A6B1CA300A45078 /* preferences_page.cpp */, + 0F75F45B2A6B1CA500A45078 /* preferences_page.h */, + 0F75F3D72A6B1CA000A45078 /* progress_meter.h */, + 0F75F4632A6B1CA500A45078 /* replaygain_info.cpp */, + 0F75F4782A6B1CA600A45078 /* replaygain_scanner.h */, + 0F75F46B2A6B1CA500A45078 /* replaygain.cpp */, + 0F75F3EC2A6B1CA100A45078 /* replaygain.h */, + 0F75F3EF2A6B1CA100A45078 /* resampler.h */, + 0F75F4072A6B1CA200A45078 /* search_tools.h */, + 0F75F4872A6B1CA700A45078 /* service_by_guid.h */, + 0F75F4202A6B1CA200A45078 /* service_compat.h */, + 0F75F3D32A6B1CA000A45078 /* service_impl.h */, + 0F75F3FD2A6B1CA100A45078 /* service.cpp */, + 0F75F3E12A6B1CA000A45078 /* service.h */, + 0F75F4192A6B1CA200A45078 /* shortcut_actions.h */, + 0F75F3D62A6B1CA000A45078 /* stdafx.cpp */, + 0F75F4722A6B1CA600A45078 /* system_time_keeper.h */, + 0F75F4682A6B1CA500A45078 /* tag_processor_id3v2.cpp */, + 0F75F4392A6B1CA300A45078 /* tag_processor.cpp */, + 0F75F4792A6B1CA600A45078 /* tag_processor.h */, + 0F75F3FE2A6B1CA200A45078 /* threaded_process.cpp */, + 0F75F3D22A6B1CA000A45078 /* threaded_process.h */, + 0F75F3DC2A6B1CA000A45078 /* threadPool.h */, + 0F75F4122A6B1CA200A45078 /* threadsLite.h */, + 0F75F3F22A6B1CA100A45078 /* timer.h */, + 0F75F3CB2A6B1CA000A45078 /* titleformat.cpp */, + 0F75F3F62A6B1CA100A45078 /* titleformat.h */, + 0F75F4492A6B1CA400A45078 /* toolbarDropDown.h */, + 0F75F3F42A6B1CA100A45078 /* track_property.cpp */, + 0F75F4172A6B1CA200A45078 /* track_property.h */, + 0F75F4222A6B1CA300A45078 /* tracks.h */, + 0F75F44C2A6B1CA400A45078 /* ui_edit_context.h */, + 0F75F3CA2A6B1CA000A45078 /* ui_element_typable_window_manager.h */, + 0F75F4372A6B1CA300A45078 /* ui_element.cpp */, + 0F75F44D2A6B1CA400A45078 /* ui_element.h */, + 0F75F3F52A6B1CA100A45078 /* ui.cpp */, + 0F75F43C2A6B1CA300A45078 /* ui.h */, + 0F75F3DE2A6B1CA000A45078 /* unpack.h */, + 0F75F4162A6B1CA200A45078 /* utility.cpp */, + 0F75F4552A6B1CA500A45078 /* vis.h */, + 0FCA711A2AA2210C001CB0F2 /* commonObjects-Apple.mm */, + 0FCA711E2AA2715E001CB0F2 /* commonObjects-Apple.h */, + ); + name = Source; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + B1DD3655198A721800EF7043 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0F75F5092A6B1CA800A45078 /* link_resolver.h in Headers */, + 0F75F4D42A6B1CA800A45078 /* metadb_callbacks.h in Headers */, + 0F75F4D12A6B1CA800A45078 /* decode_postprocessor.h in Headers */, + 0F75F50E2A6B1CA800A45078 /* contextmenu_manager.h in Headers */, + 0F75F4D32A6B1CA800A45078 /* search_tools.h in Headers */, + 0F75F4C72A6B1CA800A45078 /* console_manager.h in Headers */, + 0F75F4C82A6B1CA800A45078 /* filesystem_transacted.h in Headers */, + 0F75F4CE2A6B1CA800A45078 /* file_info_filter_impl.h in Headers */, + 0F75F5222A6B1CA800A45078 /* console.h in Headers */, + 0F75F4EC2A6B1CA800A45078 /* service_compat.h in Headers */, + 0F75F4EF2A6B1CA800A45078 /* playlistColumnProvider.h in Headers */, + 0F75F4DF2A6B1CA800A45078 /* mem_block_container.h in Headers */, + 0F75F4D62A6B1CA800A45078 /* modeless_dialog.h in Headers */, + 0F75F5532A6B1CA800A45078 /* service_by_guid.h in Headers */, + 0F75F5182A6B1CA800A45078 /* ui_edit_context.h in Headers */, + 0F75F50B2A6B1CA800A45078 /* file_lock_manager.h in Headers */, + 0F75F54F2A6B1CA800A45078 /* album_art_helpers.h in Headers */, + 0F75F4D02A6B1CA800A45078 /* packet_decoder.h in Headers */, + 0F75F4B02A6B1CA800A45078 /* audio_chunk_impl.h in Headers */, + 0F75F4FD2A6B1CA800A45078 /* advconfig_impl.h in Headers */, + 0F75F53C2A6B1CA800A45078 /* popup_message.h in Headers */, + 0F75F4D22A6B1CA800A45078 /* exception_io.h in Headers */, + 0F75F52A2A6B1CA800A45078 /* configCache.h in Headers */, + 0F75F4992A6B1CA800A45078 /* audio_chunk.h in Headers */, + 0F75F4E72A6B1CA800A45078 /* exceptions.h in Headers */, + 0F75F4DC2A6B1CA800A45078 /* dsp.h in Headers */, + 0F75F54C2A6B1CA800A45078 /* fileDialog.h in Headers */, + 0F75F5102A6B1CA800A45078 /* menu_common.h in Headers */, + 0F75F4CB2A6B1CA800A45078 /* coreDarkMode.h in Headers */, + 0F75F5082A6B1CA800A45078 /* ui.h in Headers */, + 0F75F55B2A6B1CA800A45078 /* file_info_filter.h in Headers */, + 0F75F5292A6B1CA800A45078 /* config_object_impl.h in Headers */, + 0F75F54D2A6B1CA800A45078 /* autoplaylist.h in Headers */, + 0F75F4C32A6B1CA800A45078 /* file_info_impl.h in Headers */, + 0F75F4952A6B1CA800A45078 /* genrand.h in Headers */, + 0F75F4962A6B1CA800A45078 /* ui_element_typable_window_manager.h in Headers */, + 0F75F5062A6B1CA800A45078 /* config_object.h in Headers */, + 0F75F51C2A6B1CA800A45078 /* foosortstring.h in Headers */, + 0F75F55A2A6B1CA800A45078 /* foobar2000.h in Headers */, + 0F75F5002A6B1CA800A45078 /* contextmenu.h in Headers */, + 0F75F4CC2A6B1CA800A45078 /* cfg_var_legacy.h in Headers */, + 0F75F4BB2A6B1CA800A45078 /* resampler.h in Headers */, + 0F75F4DE2A6B1CA800A45078 /* threadsLite.h in Headers */, + 0F75F5282A6B1CA800A45078 /* core_api.h in Headers */, + 0F75F5152A6B1CA800A45078 /* toolbarDropDown.h in Headers */, + 0F75F5172A6B1CA800A45078 /* messageBox.h in Headers */, + 0F75F49E2A6B1CA800A45078 /* threaded_process.h in Headers */, + 0F75F4FA2A6B1CA800A45078 /* image.h in Headers */, + 0F75F4BF2A6B1CA800A45078 /* playlist.h in Headers */, + 0F75F4EB2A6B1CA800A45078 /* input_impl.h in Headers */, + 0F75F5012A6B1CA800A45078 /* advconfig_impl_legacy.h in Headers */, + 0F75F5412A6B1CA800A45078 /* file_operation_callback.h in Headers */, + 0F75F4B42A6B1CA800A45078 /* http_client.h in Headers */, + 0F75F4F82A6B1CA800A45078 /* audioEncoder.h in Headers */, + 0F75F4DA2A6B1CA800A45078 /* playback_stream_capture.h in Headers */, + 0F75F5512A6B1CA800A45078 /* message_loop.h in Headers */, + 0F75F55F2A6B1CA800A45078 /* menu_helpers.h in Headers */, + 0F75F4C22A6B1CA800A45078 /* titleformat.h in Headers */, + 0F75F4ED2A6B1CA800A45078 /* keyValueIO.h in Headers */, + 0F75F5212A6B1CA800A45078 /* vis.h in Headers */, + 0F75F51D2A6B1CA800A45078 /* imageViewer.h in Headers */, + 0F75F4F62A6B1CA800A45078 /* info_lookup_handler.h in Headers */, + 0F75F5302A6B1CA800A45078 /* metadb_index.h in Headers */, + 0F75F4B22A6B1CA800A45078 /* event_logger.h in Headers */, + 0F75F50A2A6B1CA800A45078 /* playable_location.h in Headers */, + 0F75F53E2A6B1CA800A45078 /* system_time_keeper.h in Headers */, + 0F75F4D92A6B1CA800A45078 /* playback_control.h in Headers */, + 0F75F4F02A6B1CA800A45078 /* imageLoaderLite.h in Headers */, + 0F75F5402A6B1CA800A45078 /* file_info_const_impl.h in Headers */, + 0F75F52C2A6B1CA800A45078 /* dsp-frontend.h in Headers */, + 0F75F5482A6B1CA800A45078 /* advconfig.h in Headers */, + 0F75F54B2A6B1CA800A45078 /* keyValueIOimpl.h in Headers */, + 0F75F53A2A6B1CA800A45078 /* commonObjects.h in Headers */, + 0F75F5492A6B1CA800A45078 /* fsitem.h in Headers */, + 0F75F4AF2A6B1CA800A45078 /* chapterizer.h in Headers */, + 0F75F5442A6B1CA800A45078 /* replaygain_scanner.h in Headers */, + 0F75F4A82A6B1CA800A45078 /* threadPool.h in Headers */, + 0F75F5142A6B1CA800A45078 /* completion_notify.h in Headers */, + 0F75F4A02A6B1CA800A45078 /* main_thread_callback.h in Headers */, + 0F75F5192A6B1CA800A45078 /* ui_element.h in Headers */, + 0F75F5272A6B1CA800A45078 /* preferences_page.h in Headers */, + 0F75F5542A6B1CA800A45078 /* library_manager.h in Headers */, + 0F75F4AA2A6B1CA800A45078 /* unpack.h in Headers */, + 0F75F4FE2A6B1CA800A45078 /* foobar2000-versions.h in Headers */, + 0F75F4A32A6B1CA800A45078 /* progress_meter.h in Headers */, + 0F75F5022A6B1CA800A45078 /* file.h in Headers */, + 0F75F4F52A6B1CA800A45078 /* ole_interaction.h in Headers */, + 0F75F5392A6B1CA800A45078 /* file_format_sanitizer.h in Headers */, + 0F75F5572A6B1CA800A45078 /* coreversion.h in Headers */, + 0F75F50C2A6B1CA800A45078 /* configStore.h in Headers */, + 0F75F53D2A6B1CA800A45078 /* archive.h in Headers */, + 0F75F4D72A6B1CA800A45078 /* foosort.h in Headers */, + 0F75F5122A6B1CA800A45078 /* metadb_info_container_impl.h in Headers */, + 0F75F4E32A6B1CA800A45078 /* track_property.h in Headers */, + 0F75F5162A6B1CA800A45078 /* dsp_manager.h in Headers */, + 0F75F53B2A6B1CA800A45078 /* powerManager.h in Headers */, + 0F75F4E82A6B1CA800A45078 /* component_client.h in Headers */, + 0F75F52D2A6B1CA800A45078 /* foobar2000-sdk-pch.h in Headers */, + 0F75F5562A6B1CA800A45078 /* commandline.h in Headers */, + 0F75F4B52A6B1CA800A45078 /* hasher_md5.h in Headers */, + 0F75F4A42A6B1CA800A45078 /* app_close_blocker.h in Headers */, + 0F75F4FF2A6B1CA800A45078 /* componentversion.h in Headers */, + 0F75F4B92A6B1CA800A45078 /* metadb.h in Headers */, + 0F75F54A2A6B1CA800A45078 /* icon_remap.h in Headers */, + 0F75F4FC2A6B1CA800A45078 /* metadb_handle.h in Headers */, + 0F75F4BD2A6B1CA800A45078 /* foobar2000-winver.h in Headers */, + 0F75F4BE2A6B1CA800A45078 /* timer.h in Headers */, + 0F75F4B72A6B1CA800A45078 /* forward_types.h in Headers */, + 0F75F4F72A6B1CA800A45078 /* library_index.h in Headers */, + 0F75F4EE2A6B1CA800A45078 /* tracks.h in Headers */, + 0F75F5552A6B1CA800A45078 /* foobar2000-lite.h in Headers */, + 0F75F49A2A6B1CA800A45078 /* output.h in Headers */, + 0F75F4CD2A6B1CA800A45078 /* menu.h in Headers */, + 0F75F4B32A6B1CA800A45078 /* foobar2000-pfc.h in Headers */, + 0F75F53F2A6B1CA800A45078 /* play_callback.h in Headers */, + 0F75F5132A6B1CA800A45078 /* abort_callback.h in Headers */, + 0F75F4BC2A6B1CA800A45078 /* initquit.h in Headers */, + 0F75F5472A6B1CA800A45078 /* album_art.h in Headers */, + 0F75F55E2A6B1CA800A45078 /* cfg_var.h in Headers */, + 0F75F49F2A6B1CA800A45078 /* service_impl.h in Headers */, + 0F75F4A62A6B1CA800A45078 /* filesystem.h in Headers */, + 0F75F4B62A6B1CA800A45078 /* component.h in Headers */, + 0F75F5352A6B1CA800A45078 /* audio_postprocessor.h in Headers */, + 0F75F52B2A6B1CA800A45078 /* metadb_display_field_provider.h in Headers */, + 0F75F5362A6B1CA800A45078 /* file_info.h in Headers */, + 0F75F4E52A6B1CA800A45078 /* shortcut_actions.h in Headers */, + 0F75F4C52A6B1CA800A45078 /* filesystem_helper.h in Headers */, + 0F75F4DD2A6B1CA800A45078 /* library_callbacks.h in Headers */, + 0F75F50F2A6B1CA800A45078 /* config_io_callback.h in Headers */, + 0F75F49D2A6B1CA800A45078 /* playlist_loader.h in Headers */, + 0F75F54E2A6B1CA800A45078 /* input_file_type.h in Headers */, + 0F75F5452A6B1CA800A45078 /* tag_processor.h in Headers */, + 0F75F5382A6B1CA800A45078 /* input.h in Headers */, + 0F75F4E02A6B1CA800A45078 /* foobar2000-all.h in Headers */, + 0F75F5252A6B1CA800A45078 /* components_menu.h in Headers */, + 0F75F51B2A6B1CA800A45078 /* callback_merit.h in Headers */, + 0F75F4AD2A6B1CA800A45078 /* service.h in Headers */, + 0FDB0DFC2CEB60A900178906 /* ui_element_mac.h in Headers */, + 0F75F4B82A6B1CA800A45078 /* replaygain.h in Headers */, + 0F75F4A92A6B1CA800A45078 /* noInfo.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + B1DD3656198A721800EF7043 /* foobar2000_SDK */ = { + isa = PBXNativeTarget; + buildConfigurationList = B1DD365B198A721800EF7043 /* Build configuration list for PBXNativeTarget "foobar2000_SDK" */; + buildPhases = ( + B1DD3653198A721800EF7043 /* Sources */, + B1DD3654198A721800EF7043 /* Frameworks */, + B1DD3655198A721800EF7043 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = foobar2000_SDK; + productName = foobar2000_SDK; + productReference = B1DD3657198A721800EF7043 /* libfoobar2000_SDK.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B1DD364F198A721800EF7043 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1250; + ORGANIZATIONNAME = "___FULLUSERNAME___"; + }; + buildConfigurationList = B1DD3652198A721800EF7043 /* Build configuration list for PBXProject "foobar2000_SDK" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = B1DD364E198A721800EF7043; + productRefGroup = B1DD3658198A721800EF7043 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B1DD3656198A721800EF7043 /* foobar2000_SDK */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + B1DD3653198A721800EF7043 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0F75F5042A6B1CA800A45078 /* guids.cpp in Sources */, + 0F75F5242A6B1CA800A45078 /* chapterizer.cpp in Sources */, + 0F75F50D2A6B1CA800A45078 /* input.cpp in Sources */, + 0F75F5372A6B1CA800A45078 /* replaygain.cpp in Sources */, + 0F75F5462A6B1CA800A45078 /* input_file_type.cpp in Sources */, + 0FCA711C2AA2210C001CB0F2 /* commonObjects-Apple.mm in Sources */, + 0F75F5332A6B1CA800A45078 /* file_info_impl.cpp in Sources */, + 0F75F4C62A6B1CA800A45078 /* audio_chunk.cpp in Sources */, + 0F75F4D82A6B1CA800A45078 /* console.cpp in Sources */, + 0F75F4C02A6B1CA800A45078 /* track_property.cpp in Sources */, + 0F75F4BA2A6B1CA800A45078 /* menu_manager.cpp in Sources */, + 0F75F5322A6B1CA800A45078 /* app_close_blocker.cpp in Sources */, + 0F75F5112A6B1CA800A45078 /* completion_notify.cpp in Sources */, + 0F75F49B2A6B1CA800A45078 /* file_operation_callback.cpp in Sources */, + 0F75F5422A6B1CA800A45078 /* album_art.cpp in Sources */, + 0F75F5262A6B1CA800A45078 /* metadb_handle_list.cpp in Sources */, + 0F75F5072A6B1CA800A45078 /* config_object.cpp in Sources */, + 0F75F5432A6B1CA800A45078 /* main_thread_callback.cpp in Sources */, + 0F75F4A72A6B1CA800A45078 /* commonObjects.cpp in Sources */, + 0F75F51E2A6B1CA800A45078 /* popup_message.cpp in Sources */, + 0F75F52F2A6B1CA800A45078 /* replaygain_info.cpp in Sources */, + 0F75F5232A6B1CA800A45078 /* componentversion.cpp in Sources */, + 0F75F5592A6B1CA800A45078 /* playback_control.cpp in Sources */, + 0F75F4E22A6B1CA800A45078 /* utility.cpp in Sources */, + 0F75F4942A6B1CA800A45078 /* foosort.cpp in Sources */, + 0F75F5052A6B1CA800A45078 /* tag_processor.cpp in Sources */, + 0F75F4E12A6B1CA800A45078 /* image.cpp in Sources */, + 0F75F5202A6B1CA800A45078 /* cfg_var_legacy.cpp in Sources */, + 0F75F51F2A6B1CA800A45078 /* fsItem.cpp in Sources */, + 0F75F4C12A6B1CA800A45078 /* ui.cpp in Sources */, + 0F75F4CF2A6B1CA800A45078 /* advconfig.cpp in Sources */, + 0F75F4F32A6B1CA800A45078 /* config_io_callback.cpp in Sources */, + 0F75F4F42A6B1CA800A45078 /* packet_decoder.cpp in Sources */, + 0F75F4DB2A6B1CA800A45078 /* file_info.cpp in Sources */, + 0F75F4AC2A6B1CA800A45078 /* menu_helpers.cpp in Sources */, + 0F75F4CA2A6B1CA800A45078 /* threaded_process.cpp in Sources */, + 0F75F5032A6B1CA800A45078 /* ui_element.cpp in Sources */, + 0F75F4A12A6B1CA800A45078 /* filesystem.cpp in Sources */, + 0F75F4A52A6B1CA800A45078 /* mem_block_container.cpp in Sources */, + 0F75F5312A6B1CA800A45078 /* mainmenu.cpp in Sources */, + 0F75F49C2A6B1CA800A45078 /* abort_callback.cpp in Sources */, + 0F75F52E2A6B1CA800A45078 /* file_cached_impl.cpp in Sources */, + 0F75F5522A6B1CA800A45078 /* playlist_loader.cpp in Sources */, + 0F75F4AE2A6B1CA800A45078 /* file_info_const_impl.cpp in Sources */, + 0F75F4982A6B1CA800A45078 /* file_info_merge.cpp in Sources */, + 0F75F4F22A6B1CA800A45078 /* preferences_page.cpp in Sources */, + 0F75F4C92A6B1CA800A45078 /* service.cpp in Sources */, + 0F75F4EA2A6B1CA800A45078 /* metadb.cpp in Sources */, + 0F75F4A22A6B1CA800A45078 /* stdafx.cpp in Sources */, + 0F75F51A2A6B1CA800A45078 /* menu_item.cpp in Sources */, + 0F75F4FB2A6B1CA800A45078 /* link_resolver.cpp in Sources */, + 0F75F4AB2A6B1CA800A45078 /* output.cpp in Sources */, + 0F75F4F12A6B1CA800A45078 /* playlist.cpp in Sources */, + 0F75F4E42A6B1CA800A45078 /* cfg_var.cpp in Sources */, + 0F75F55D2A6B1CA800A45078 /* dsp.cpp in Sources */, + 0F75F4B12A6B1CA800A45078 /* audio_chunk_channel_config.cpp in Sources */, + 0F75F4D52A6B1CA800A45078 /* configStore.cpp in Sources */, + 0F75F5342A6B1CA800A45078 /* tag_processor_id3v2.cpp in Sources */, + 0F75F4E92A6B1CA800A45078 /* playable_location.cpp in Sources */, + 0F75F5502A6B1CA800A45078 /* hasher_md5.cpp in Sources */, + 0F75F55C2A6B1CA800A45078 /* metadb_handle.cpp in Sources */, + 0F75F5582A6B1CA800A45078 /* dsp_manager.cpp in Sources */, + 0F75F4C42A6B1CA800A45078 /* filesystem_helper.cpp in Sources */, + 0F75F4F92A6B1CA800A45078 /* commandline.cpp in Sources */, + 0F75F4972A6B1CA800A45078 /* titleformat.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + B1DD3659198A721800EF7043 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "foobar2000-sdk-pch.h"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + .., + ../.., + ); + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MACOSX_DEPLOYMENT_TARGET = 11.0; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + B1DD365A198A721800EF7043 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "foobar2000-sdk-pch.h"; + GCC_PREPROCESSOR_DEFINITIONS = "NDEBUG=1"; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + .., + ../.., + ); + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MACOSX_DEPLOYMENT_TARGET = 11.0; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Release; + }; + B1DD365C198A721800EF7043 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + B1DD365D198A721800EF7043 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B1DD3652198A721800EF7043 /* Build configuration list for PBXProject "foobar2000_SDK" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B1DD3659198A721800EF7043 /* Debug */, + B1DD365A198A721800EF7043 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B1DD365B198A721800EF7043 /* Build configuration list for PBXNativeTarget "foobar2000_SDK" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B1DD365C198A721800EF7043 /* Debug */, + B1DD365D198A721800EF7043 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B1DD364F198A721800EF7043 /* Project object */; +} diff --git a/sdk/foobar2000/SDK/foosort.cpp b/sdk/foobar2000/SDK/foosort.cpp index 5436e5f..aa81f1b 100644 --- a/sdk/foobar2000/SDK/foosort.cpp +++ b/sdk/foobar2000/SDK/foosort.cpp @@ -128,7 +128,7 @@ namespace { newsort(base2, count2, con2); break; } - } catch (exception_aborted) {} + } catch (exception_aborted const &) {} }, 2); m_abort.check(); } else { diff --git a/sdk/foobar2000/SDK/foosortstring.cpp b/sdk/foobar2000/SDK/foosortstring.cpp deleted file mode 100644 index 50cdb22..0000000 --- a/sdk/foobar2000/SDK/foosortstring.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "foobar2000-sdk-pch.h" -#include "foosortstring.h" - -#ifdef _WIN32 -#include -#endif - -namespace fb2k { -#ifdef _WIN32 - sortString_t makeSortString(const char* in) { - wchar_t* out = new wchar_t[pfc::stringcvt::estimate_utf8_to_wide(in) + 1]; - out[0] = ' ';//StrCmpLogicalW bug workaround. - pfc::stringcvt::convert_utf8_to_wide_unchecked(out + 1, in); - return sortString_t(out); - } - int sortStringCompare(sortString_t const& s1, sortString_t const& s2) { - return StrCmpLogicalW(s1.get(), s2.get()); - } -#else - int sortStringCompare(const char* str1, const char* str2) { - return pfc::naturalSortCompare(str1, str2); - } -#endif -} \ No newline at end of file diff --git a/sdk/foobar2000/SDK/foosortstring.h b/sdk/foobar2000/SDK/foosortstring.h index 62da612..1216982 100644 --- a/sdk/foobar2000/SDK/foosortstring.h +++ b/sdk/foobar2000/SDK/foosortstring.h @@ -1,18 +1,12 @@ #pragma once +#include #ifdef _WIN32 #include // std::unique_ptr<> #endif namespace fb2k { -#ifdef _WIN32 - typedef std::unique_ptr sortString_t; - sortString_t makeSortString(const char* str); - int sortStringCompare(sortString_t const & s1, sortString_t const & s2); -#else - typedef pfc::string8 sortString_t; - inline sortString_t makeSortString(const char* str) { return str; } - inline sortString_t makeSortString(pfc::string8 && str) { return std::move(str); } - int sortStringCompare(const char* str1, const char* str2); -#endif -} \ No newline at end of file + using pfc::sortString_t; + using pfc::sortStringCompare; + using pfc::makeSortString; +} diff --git a/sdk/foobar2000/SDK/fsItem.cpp b/sdk/foobar2000/SDK/fsItem.cpp index 83a65b0..a644bef 100644 --- a/sdk/foobar2000/SDK/fsItem.cpp +++ b/sdk/foobar2000/SDK/fsItem.cpp @@ -72,9 +72,8 @@ fsItemPtr fsItemBase::copyTo(fsItemFolderPtr folder, const char* desiredName, un } fsItemPtr fsItemBase::copyTo(fsItemFolderPtr folder, unsigned createMode, abort_callback& aborter) { - auto temp = pfc::string_filename_ext(this->canonicalPath()->c_str()); - if (temp.length() == 0) throw pfc::exception_invalid_params(); - return this->copyTo(folder, temp.c_str(), createMode, aborter); + auto temp = this->nameWithExt(); + return this->copyTo(folder, temp->c_str(), createMode, aborter); } fsItemPtr fsItemBase::moveTo(fsItemFolderPtr folder, const char* desiredName, unsigned createMode, abort_callback& aborter) { @@ -106,11 +105,11 @@ fb2k::memBlockRef fsItemFile::readWhole(size_t sizeSanity, abort_callback& abort namespace { static void uniqueFn(pfc::string8& fn, unsigned add) { if (add > 0) { - auto fnOnly = pfc::string_filename(fn); - auto ext = pfc::string_extension(fn); - fn.reset(); - fn << fnOnly << " (" << add << ")"; - if (ext.length() > 0) fn << "." << ext.get_ptr(); + pfc::string8 fnOnly = pfc::remove_ext_v2(fn); + pfc::string8 ext = pfc::extract_ext_v2(fn); + fn = std::move(fnOnly); + fn << " (" << add << ")"; + if (ext.length() > 0) fn << "." << ext; } } @@ -119,6 +118,7 @@ namespace { public: fsItemFileStd(filesystem::ptr fs, stringRef canonicalPath, t_filestats2 const & opportunistStats) : m_fs(fs), m_path(canonicalPath), m_opportunistStats(opportunistStats) { m_opportunistStats.set_folder(false); + m_opportunistStats.set_remote(fs->is_remote(canonicalPath->c_str())); } filesystem::ptr getFS() override { return m_fs; } @@ -165,7 +165,7 @@ namespace { try { if (createMode == createMode::allowExisting) m_fs->move_overwrite(m_path->c_str(), dst, aborter); else m_fs->move(m_path->c_str(), dst, aborter); - } catch (exception_io_already_exists) { + } catch (exception_io_already_exists const &) { bDidExist = true; } @@ -184,7 +184,9 @@ namespace { PFC_ASSERT(!"Should not get here"); break; } - return m_fs->makeItemFileStd(dst); + auto stats = m_opportunistStats; + stats.set_file(); + return m_fs->makeItemFileStd(dst, stats); } } private: @@ -196,6 +198,7 @@ namespace { public: fsItemFolderStd(filesystem::ptr fs, stringRef canonicalPath, t_filestats2 const & opportunistStats) : m_fs(fs), m_path(canonicalPath), m_opportunistStats(opportunistStats) { m_opportunistStats.set_folder(true); + m_opportunistStats.set_remote(fs->is_remote(canonicalPath->c_str())); } filesystem::ptr getFS() override { return m_fs; } @@ -239,7 +242,7 @@ namespace { fsItemFile::ptr findChildFile(const char* fileName, abort_callback& aborter) override { auto sub = subPath(fileName); - auto stats = m_fs->get_stats2_(sub->c_str(), stats2_fileOrFolder, aborter); + auto stats = m_fs->get_stats2_(sub->c_str(), stats2_fileOrFolder | foobar2000_io::stats2_remote, aborter); if (!stats.is_folder()) { return m_fs->makeItemFileStd(sub->c_str(), stats); } @@ -247,14 +250,15 @@ namespace { } fsItemFolder::ptr findChildFolder(const char* fileName, abort_callback& aborter) override { auto sub = subPath(fileName); - if (m_fs->directory_exists(sub->c_str(), aborter)) { - return m_fs->makeItemFolderStd(sub->c_str()); + auto stats = m_fs->get_stats2_(sub->c_str(), stats2_fileOrFolder | foobar2000_io::stats2_remote, aborter); + if (stats.is_folder()) { + return m_fs->makeItemFolderStd(sub->c_str(), stats); } throw exception_io_not_found(); } fsItemBase::ptr findChild(const char* fileName, abort_callback& aborter) override { auto sub = subPath(fileName); - auto stats = m_fs->get_stats2_(sub->c_str(), stats2_fileOrFolder, aborter); + auto stats = m_fs->get_stats2_(sub->c_str(), stats2_fileOrFolder | foobar2000_io::stats2_remote, aborter); if ( stats.is_folder() ) { return m_fs->makeItemFileStd(sub->c_str(), stats ); } else { @@ -266,7 +270,13 @@ namespace { pfc::string8 fn(fileName); uniqueFn(fn, add); auto sub = subPath(fn); - const bool bDidExist = m_fs->file_exists(sub->c_str(), aborter); + t_filestats2 stats; + + bool bDidExist = false; + try { + stats = m_fs->get_stats2_( sub->c_str(), stats2_all, aborter ); + bDidExist = stats.is_file(); + } catch(exception_io_not_found const &) {} switch (createMode) { case createMode::allowExisting: break; // OK @@ -287,9 +297,9 @@ namespace { // FIX ME this should be atomic with exists() check file::ptr creator; m_fs->open(creator, sub->c_str(), filesystem::open_mode_write_new, aborter); - // creator->commit(aborter); + stats = creator->get_stats2_( stats2_all, aborter ); } - return m_fs->makeItemFileStd(sub->c_str()); + return m_fs->makeItemFileStd(sub->c_str(), stats); } } fsItemFolder::ptr createFolder(const char* fileName, unsigned createMode, abort_callback& aborter) override { @@ -299,7 +309,7 @@ namespace { bool bDidExist = false; try { m_fs->create_directory(sub->c_str(), aborter); - } catch (exception_io_already_exists) { + } catch (exception_io_already_exists const &) { bDidExist = true; } switch (createMode) { @@ -317,7 +327,8 @@ namespace { PFC_ASSERT(!"Should not get here"); break; } - return m_fs->makeItemFolderStd(sub->c_str()); + // Inherit opportunist stats + return m_fs->makeItemFolderStd(sub->c_str(), this->m_opportunistStats); } } fsItemPtr moveTo(fsItemFolderPtr folder, const char* desiredName, unsigned createMode, abort_callback& aborter) override { @@ -330,7 +341,7 @@ namespace { try { if (createMode == createMode::allowExisting) m_fs->move_overwrite(m_path->c_str(), dst, aborter); else m_fs->move(m_path->c_str(), dst, aborter); - } catch (exception_io_already_exists) { + } catch (exception_io_already_exists const &) { bDidExist = true; } @@ -350,7 +361,7 @@ namespace { break; } - return m_fs->makeItemFolderStd(dst); + return m_fs->makeItemFolderStd(dst, m_opportunistStats); } } @@ -373,36 +384,46 @@ fsItemFolder::ptr filesystem::makeItemFolderStd(const char* pathCanonical, t_fil fsItemFile::ptr filesystem::makeItemFileStd(const char* pathCanonical, t_filestats2 const & opportunistStats) { return new service_impl_t(this, makeString(pathCanonical), opportunistStats); } - -fsItemBase::ptr fsItemBase::fromPath(const char* path, abort_callback& aborter) { - auto fs = filesystem::get(path); - +fsItemBase::ptr filesystem::findItem_(const char* path, abort_callback& p_abort) { filesystem_v3::ptr v3; - if (v3 &= fs) return v3->findItem(path, aborter); + if (v3 &= this) { + return v3->findItem(path, p_abort); + } + auto stats = this->get_stats2_(path, stats2_fileOrFolder, p_abort); + if (stats.is_folder()) return this->makeItemFolderStd(path, stats); + else return this->makeItemFileStd(path, stats); - auto stats = fs->get_stats2_(path, stats2_fileOrFolder, aborter); - if (stats.is_folder()) return fs->makeItemFolderStd(path, stats); - else return fs->makeItemFileStd(path, stats); } +fsItemFile::ptr filesystem::findItemFile_(const char* path, abort_callback& p_abort) { + filesystem_v3::ptr v3; + if (v3 &= this) return v3->findItemFile(path, p_abort); -fsItemFile::ptr fsItemFile::fromPath(const char* path, abort_callback& aborter) { - auto fs = filesystem::get(path); - + auto stats = this->get_stats2_(path, stats2_fileOrFolder, p_abort); + if (!stats.is_folder()) return this->makeItemFileStd(path, stats); + throw exception_io_not_found(); +} +fsItemFolder::ptr filesystem::findItemFolder_(const char* path, abort_callback& p_abort) { filesystem_v3::ptr v3; - if ( v3 &= fs ) return v3->findItemFile(path, aborter); + if (v3 &= this) return v3->findItemFolder(path, p_abort); - auto stats = fs->get_stats2_(path, stats2_fileOrFolder, aborter); - if (!stats.is_folder()) return fs->makeItemFileStd(path, stats); + auto stats = this->get_stats2_(path, stats2_fileOrFolder, p_abort); + if (stats.is_folder()) return this->makeItemFolderStd(path, stats); throw exception_io_not_found(); } -fsItemFolder::ptr fsItemFolder::fromPath(const char* path, abort_callback& aborter) { - auto fs = filesystem::get(path); - filesystem_v3::ptr v3; - if (v3 &= fs) return v3->findItemFolder(path, aborter); +fsItemBase::ptr fsItemBase::fromPath(const char* path, abort_callback& aborter) { + return filesystem::get(path)->findItem_(path, aborter); +} - auto stats = fs->get_stats2_(path, stats2_fileOrFolder, aborter); - if (stats.is_folder()) return fs->makeItemFolderStd(path, stats); - throw exception_io_not_found(); +fsItemFile::ptr fsItemFile::fromPath(const char* path, abort_callback& aborter) { + return filesystem::get(path)->findItemFile_(path, aborter); +} + +fsItemFolder::ptr fsItemFolder::fromPath(const char* path, abort_callback& aborter) { + return filesystem::get(path)->findItemFolder_(path, aborter); +} + +t_filestats fsItemBase::getStats(abort_callback& a) { + return getStats2(stats2_all, a).to_legacy(); } diff --git a/sdk/foobar2000/SDK/fsitem.h b/sdk/foobar2000/SDK/fsitem.h index ca9d498..1fcc4af 100644 --- a/sdk/foobar2000/SDK/fsitem.h +++ b/sdk/foobar2000/SDK/fsitem.h @@ -3,8 +3,14 @@ // fsItem API // Alternate, object-based way of accessing the local filesystem. // It is recommended to use fsItem methods to operate on user-specified media folders. -// In some cases, most notably Android and UWP, accessing user's documents/media over paths requires expensive resolving of objects representing them. +// In some cases, notably Android and WinPhone/UWP, accessing user's documents/media over paths requires expensive resolving of objects representing them. // fsItem can cache platform specific resources necessary to manipulate the files, allowing efficient opening of files returned by directory enumeration. +// +// Note that as of 2023, fsItem is just a convenience API. +// WinPhone/UWP has been dropped; slow and buggy Android DocumentFile (*) wrapper is being abandoned. On neither of these platforms foobar2000 supports loading components. +// Nothing in current-generation foobar2000 gains performance from using fsItem methods over plain filesystem with paths. +// +// (*) Android DocumentFile is slow and buggy, not the wrapper. Google sucks. #include "file.h" #include "commonObjects.h" @@ -84,6 +90,8 @@ namespace foobar2000_io { virtual service_ptr_t getFS() = 0; static fsItemBase::ptr fromPath(const char* path, abort_callback& aborter); + + t_filestats getStats(abort_callback& a); }; class fsItemFile : public fsItemBase { diff --git a/sdk/foobar2000/SDK/genrand.h b/sdk/foobar2000/SDK/genrand.h index 256668e..70aae87 100644 --- a/sdk/foobar2000/SDK/genrand.h +++ b/sdk/foobar2000/SDK/genrand.h @@ -3,6 +3,7 @@ class NOVTABLE genrand_service : public service_base { public: + void seedAuto() {seed( 0 ) ;} //! Seeds the PRNG with specified value. \n //! Default value of zero seeds automatically using available system functions. virtual void seed(unsigned val = 0) = 0; diff --git a/sdk/foobar2000/SDK/guids.cpp b/sdk/foobar2000/SDK/guids.cpp index 37e5ba7..193c481 100644 --- a/sdk/foobar2000/SDK/guids.cpp +++ b/sdk/foobar2000/SDK/guids.cpp @@ -50,6 +50,8 @@ FOOGUIDDECL const GUID dsp_v3::class_guid = { 0x3743c03a, 0xae20, 0x40e3, { 0x90 FOOGUIDDECL const GUID dsp_entry_v2::class_guid = { 0x9ec5d45e, 0x10f5, 0x46a7, { 0x95, 0x46, 0xf3, 0xac, 0xec, 0x68, 0xb1, 0x49 } }; FOOGUIDDECL const GUID dsp_entry_hidden::class_guid = { 0xe3f0c95f, 0x7a0b, 0x48bc, { 0x96, 0x58, 0x71, 0xa4, 0x69, 0x68, 0x88, 0x76 } }; FOOGUIDDECL const GUID dsp_entry_v3::class_guid = { 0xb4d5a127, 0x5e59, 0x466d, { 0x93, 0x6b, 0x39, 0x5e, 0xdf, 0xb9, 0x37, 0xa4 } }; +FOOGUIDDECL const GUID dsp_entry_v4::class_guid = { 0xf13a5ce9, 0xd6e8, 0x4ad8, { 0x9e, 0xba, 0x9d, 0x6a, 0x34, 0x84, 0x29, 0x61 } }; +FOOGUIDDECL const GUID dsp_entry_v5::class_guid = { 0x7325e6ce, 0x945b, 0x4f58, { 0xa7, 0x62, 0xfc, 0xa1, 0x38, 0x4c, 0xe5, 0xeb } }; FOOGUIDDECL const GUID dsp_preset_edit_callback_v2::class_guid = { 0x904173f6, 0x345e, 0x47d0, { 0x88, 0x60, 0x63, 0xa2, 0xc9, 0xd0, 0x9, 0x52 } }; @@ -194,6 +196,10 @@ FOOGUIDDECL const GUID archive_v2::class_guid = FOOGUIDDECL const GUID archive_v3::class_guid = { 0x8d3f8b2d, 0xa866, 0x4f1f, { 0x9a, 0x4f, 0xff, 0x23, 0x92, 0x9e, 0xd6, 0xda } }; +// {FD3AE540-4C46-43AD-A331-2337737A64C6} +FOOGUIDDECL const GUID archive_v4::class_guid = +{ 0xfd3ae540, 0x4c46, 0x43ad, { 0xa3, 0x31, 0x23, 0x37, 0x73, 0x7a, 0x64, 0xc6 } }; + // {B2F9FC40-3E55-4b23-A2C9-22BAAD8795B1} FOOGUIDDECL const GUID file::class_guid = { 0xb2f9fc40, 0x3e55, 0x4b23, { 0xa2, 0xc9, 0x22, 0xba, 0xad, 0x87, 0x95, 0xb1 } }; @@ -233,15 +239,16 @@ FOOGUIDDECL const GUID titleformat_compiler::class_guid = FOOGUIDDECL const GUID titleformat_compiler_v2::class_guid = { 0xa81de54, 0x8131, 0x4c43, { 0x92, 0xef, 0x15, 0xc5, 0x8b, 0xd4, 0x72, 0x7a } }; -#ifdef _WIN32 FOOGUIDDECL const GUID user_interface::class_guid = { 0x1add4dc4, 0xb278, 0x4a0c, { 0xa1, 0x5, 0x26, 0x29, 0xf4, 0xb3, 0x12, 0xf4 } }; FOOGUIDDECL const GUID user_interface_v2::class_guid = { 0x5f5ac82b, 0x44a7, 0x4872,{ 0xb7, 0x86, 0x1c, 0x1f, 0xc6, 0x56, 0x6b, 0x60 } }; +#ifdef _WIN32 FOOGUIDDECL const GUID user_interface_v2::cap_suppress_core_shellhook = { 0xe5950632, 0x750d, 0x4265,{ 0x8c, 0xea, 0x6c, 0xf2, 0x77, 0x91, 0x44, 0x40 } }; FOOGUIDDECL const GUID user_interface_v2::cap_suppress_core_uvc = { 0xe35156b2, 0xfbac, 0x450d,{ 0xa3, 0xf7, 0xf1, 0xda, 0x46, 0xb6, 0xdf, 0x8d } }; +#endif FOOGUIDDECL const GUID user_interface_v3::class_guid = { 0x4fa87b73, 0x7e4, 0x4814, { 0x8f, 0x70, 0x66, 0x28, 0x7a, 0x18, 0x8b, 0xaa } }; -#endif +FOOGUIDDECL const GUID user_interface_v4::class_guid = { 0xd9069882, 0x51ed, 0x4f05, { 0x82, 0x46, 0x9b, 0x50, 0x82, 0xe0, 0x25, 0x26 } }; // {994C0D0E-319E-45f3-92FC-518616E73ADC} FOOGUIDDECL const GUID contextmenu_item::caller_now_playing = @@ -317,6 +324,10 @@ FOOGUIDDECL const GUID packet_decoder::owner_MP4_FLAC = FOOGUIDDECL const GUID packet_decoder::owner_MP4_Opus = { 0x595ff6b0, 0xf860, 0x43cf, { 0xa5, 0x4c, 0x6d, 0x8d, 0x29, 0xe1, 0x2f, 0x62 } }; +// {E10C6D93-3271-4AAA-B0E6-231AB14BEFFA} +FOOGUIDDECL const GUID packet_decoder::owner_MP4_MPEGH = +{ 0xe10c6d93, 0x3271, 0x4aaa, { 0xb0, 0xe6, 0x23, 0x1a, 0xb1, 0x4b, 0xef, 0xfa } }; + // {AF5B7CB0-A08E-404a-A3C0-5C5EA1A8A05C} FOOGUIDDECL const GUID packet_decoder::owner_ADTS = { 0xaf5b7cb0, 0xa08e, 0x404a, { 0xa3, 0xc0, 0x5c, 0x5e, 0xa1, 0xa8, 0xa0, 0x5c } }; @@ -843,8 +854,10 @@ FOOGUIDDECL const GUID preferences_page::guid_keyboard_shortcuts = { 0xa4dbfa19, FOOGUIDDECL const GUID preferences_page_v2::class_guid = { 0xce4ebc9e, 0xab20, 0x46f9, { 0x92, 0x5f, 0x88, 0x3b, 0x8, 0x4f, 0x5, 0x69 } }; FOOGUIDDECL const GUID preferences_branch_v2::class_guid = { 0x167ebeb9, 0x8334, 0x4b21, { 0xaf, 0x58, 0xa7, 0x40, 0xa5, 0xd5, 0xb6, 0x66 } }; +#ifdef _WIN32 FOOGUIDDECL const GUID preferences_page_callback::class_guid = { 0x3d26e08e, 0x861c, 0x4599, { 0x9c, 0x89, 0xaa, 0xa7, 0x19, 0xaf, 0x50, 0x70 } }; FOOGUIDDECL const GUID preferences_page_instance::class_guid = { 0x6893a996, 0xa816, 0x49fe, { 0x82, 0xce, 0xc, 0xb8, 0x4, 0xa4, 0xcf, 0xec } }; +#endif FOOGUIDDECL const GUID preferences_page_v3::class_guid = { 0xd6d0f741, 0x9f17, 0x4df8, { 0x9d, 0x5c, 0x87, 0xf2, 0x8b, 0x1f, 0xe, 0x64 } }; FOOGUIDDECL const GUID preferences_page_v4::class_guid = { 0x76227dab, 0xc740, 0x4d49, { 0xa2, 0xe2, 0x50, 0x80, 0x13, 0xe, 0xf6, 0xba } }; @@ -864,7 +877,6 @@ FOOGUIDDECL const GUID input_decoder_v4::class_guid = { 0x7bbf10b5, 0x2064, 0x4d FOOGUIDDECL const GUID input_params::seeking_expensive = { 0x10973582, 0x1aae, 0x4169, { 0xb9, 0x76, 0xb5, 0xbe, 0xf9, 0x4b, 0x7a, 0x71 } }; FOOGUIDDECL const GUID input_params::set_preferred_sample_rate = { 0x7dc7f926, 0x9b9f, 0x4a73, { 0xa2, 0x37, 0x83, 0xe9, 0x93, 0x38, 0x41, 0x75 } }; FOOGUIDDECL const GUID input_params::query_position = { 0xa9d79933, 0x5438, 0x480f, { 0x85, 0xf3, 0xb6, 0xb8, 0xee, 0x1e, 0xfa, 0xe9 } }; -FOOGUIDDECL const GUID input_params::continue_stream = { 0x673050df, 0x11f6, 0x4039, { 0x8b, 0x4, 0x6c, 0x31, 0x98, 0x91, 0x4b, 0x89 } }; FOOGUIDDECL const GUID input_params::is_tag_write_safe = { 0x9f44a346, 0x73f0, 0x459b, { 0x98, 0x47, 0xf4, 0x86, 0x9b, 0x29, 0xbc, 0x93 } }; FOOGUIDDECL const GUID input_info_reader::class_guid = { 0x8e9bb1d4, 0xa52b, 0x4df6, { 0xa9, 0x29, 0x1a, 0xae, 0x40, 0x75, 0x38, 0x8a } }; FOOGUIDDECL const GUID input_info_reader_v2::class_guid = { 0x42486bca, 0xd37, 0x4e02, { 0xb7, 0x72, 0x4d, 0x30, 0xd6, 0xf8, 0x50, 0xbe } }; @@ -901,6 +913,10 @@ FOOGUIDDECL const GUID playback_queue_callback::class_guid = FOOGUIDDECL const GUID main_thread_callback_manager::class_guid = { 0x1131d64b, 0x4cb, 0x43ee, { 0x95, 0xeb, 0x24, 0xd1, 0x8b, 0x75, 0x32, 0x48 } }; +// {38986E70-A333-456D-8FEE-0AFBDFD4F919} +FOOGUIDDECL const GUID main_thread_callback_manager_v2::class_guid = +{ 0x38986e70, 0xa333, 0x456d, { 0x8f, 0xee, 0xa, 0xfb, 0xdf, 0xd4, 0xf9, 0x19 } }; + // {87D2C583-7AFB-4e58-B21E-FBD3E6E8F5E7} FOOGUIDDECL const GUID main_thread_callback::class_guid = { 0x87d2c583, 0x7afb, 0x4e58, { 0xb2, 0x1e, 0xfb, 0xd3, 0xe6, 0xe8, 0xf5, 0xe7 } }; @@ -1095,6 +1111,7 @@ FOOGUIDDECL const GUID standard_commands::guid_seek_back_10min = FOOGUIDDECL const GUID standard_commands::guid_library_configure = { 0xa305602d, 0x4716, 0x4fc2, { 0x91, 0x57, 0xc6, 0x33, 0x9c, 0x4b, 0x1c, 0xe9 } }; FOOGUIDDECL const GUID standard_commands::guid_library_rescan = { 0xb21d432e, 0xb14d, 0x4c8d, { 0xaf, 0xda, 0xf, 0x45, 0xc7, 0xb6, 0x22, 0x26 } }; +FOOGUIDDECL const GUID standard_commands::guid_internet_radio = { 0x58712f87, 0x3d7b, 0x4f2e, { 0x88, 0xe5, 0x7d, 0xea, 0xe1, 0xa5, 0xf2, 0x3d } }; // {97215C5E-7228-4237-B52C-A2B5504EF726} FOOGUIDDECL const GUID playback_statistics_collector::class_guid = @@ -1112,6 +1129,7 @@ FOOGUIDDECL const GUID advconfig_entry::guid_branch_tools = { 0x35365484, 0xcc58 FOOGUIDDECL const GUID advconfig_entry::guid_branch_playback = { 0xc48d430d, 0x112, 0x4922, { 0x97, 0x23, 0x28, 0x38, 0xc7, 0xd9, 0x7d, 0xd7 } }; FOOGUIDDECL const GUID advconfig_entry::guid_branch_display = { 0x6c4bc1c8, 0xbaf4, 0x40c3, { 0x9d, 0xb1, 0x9, 0x50, 0x7f, 0xc, 0xc, 0xb9 } }; FOOGUIDDECL const GUID advconfig_entry::guid_branch_vis = { 0xea1017c6, 0x298, 0x446c, { 0xb6, 0xc7, 0xbd, 0xf9, 0x1e, 0xd8, 0x12, 0xee } }; +FOOGUIDDECL const GUID advconfig_entry::guid_branch_general = { 0x534dc6e3, 0x29a2, 0x42f4, { 0xab, 0x49, 0x5a, 0x2, 0x71, 0x9b, 0x1d, 0xa } }; FOOGUIDDECL const GUID advconfig_entry::guid_branch_debug = { 0xc375447d, 0x58e6, 0x4fd5, { 0xbd, 0xd8, 0x99, 0xfa, 0xef, 0x7b, 0x30, 0x9f } }; FOOGUIDDECL const GUID advconfig_entry::guid_branch_tagging_general = { 0x1a7757de, 0x55bd, 0x4c25, { 0xb2, 0xf9, 0xc6, 0x3a, 0x85, 0x0, 0xba, 0xed } }; FOOGUIDDECL const GUID advconfig_entry::guid_branch_converter = { 0x96ea6eae, 0xe848, 0x44d9, { 0x99, 0xd6, 0x4d, 0xa6, 0xeb, 0x39, 0x81, 0x8f } }; @@ -1120,12 +1138,10 @@ FOOGUIDDECL const GUID advconfig_entry::guid_branch_converter = { 0x96ea6eae, 0x FOOGUIDDECL const GUID playback_control_v2::class_guid = { 0x1eb67bda, 0x1345, 0x49ae, { 0x8e, 0x89, 0x50, 0x5, 0xd9, 0x1, 0x62, 0x89 } }; - - -FOOGUIDDECL const GUID advconfig_entry_enum::class_guid = { 0xb1451540, 0x98ec, 0x4d36, { 0x9f, 0x19, 0xe3, 0x10, 0xfb, 0xa7, 0xab, 0x5a } }; - FOOGUIDDECL const GUID info_lookup_handler::class_guid = { 0x4fcfdab7, 0x55b5, 0x47d6, { 0xb1, 0x9d, 0xa4, 0xdc, 0x9f, 0xd7, 0x69, 0x4c } }; FOOGUIDDECL const GUID info_lookup_handler_v2::class_guid = { 0x3c9728a5, 0x89e5, 0x4560, { 0xb6, 0x8b, 0x9b, 0x9b, 0x90, 0x1f, 0x0, 0x7b } }; +FOOGUIDDECL const GUID info_lookup_handler_v3::class_guid = { 0x7d337e3c, 0x5f83, 0x4dee, { 0x9f, 0x39, 0xe4, 0x3b, 0xc1, 0x51, 0xe4, 0xa1 } }; + FOOGUIDDECL const GUID completion_notify::class_guid = { 0xdf26d586, 0xf7ec, 0x40c3, { 0x9f, 0xe8, 0x2e, 0xa0, 0x72, 0x5d, 0x76, 0xc0 } }; @@ -1311,11 +1327,10 @@ FOOGUIDDECL const GUID mainmenu_node::class_guid = { 0xabba6512, 0x377d, 0x414f, FOOGUIDDECL const GUID system_time_keeper::class_guid = { 0xdc5d4938, 0x7927, 0x48ba, { 0xbf, 0x84, 0xda, 0x2f, 0xb1, 0xb, 0x36, 0xf2 } }; -#ifdef _WIN32 FOOGUIDDECL const GUID component_installation_validator::class_guid = { 0x639283e, 0x3004, 0x4e5c, { 0xb1, 0xb3, 0x6d, 0xff, 0x8, 0xa7, 0x92, 0x84 } }; -#endif FOOGUIDDECL const GUID playback_stream_capture::class_guid = { 0x9423439e, 0x8cd5, 0x45d7, { 0xaa, 0x6d, 0x4b, 0x98, 0xc, 0x22, 0x93, 0x3e } }; +FOOGUIDDECL const GUID playback_stream_capture_v2::class_guid = { 0x801e6865, 0x6040, 0x42a6, { 0x8c, 0x43, 0x57, 0x91, 0x8a, 0xdc, 0xee, 0xb7 } }; FOOGUIDDECL const GUID http_request::class_guid = { 0x48580056, 0x2c5f, 0x45a8, { 0xb8, 0x6e, 0x5, 0x83, 0x55, 0x3e, 0xaa, 0x4f } }; FOOGUIDDECL const GUID http_request_post::class_guid = { 0xe254b804, 0xeac5, 0x4be0, { 0x99, 0x4d, 0x53, 0x1c, 0x17, 0xea, 0xfd, 0x37 } }; @@ -1331,6 +1346,7 @@ FOOGUIDDECL const GUID metadb_index_transaction::class_guid = { 0xe4b7e77e, 0xbe FOOGUIDDECL const GUID init_stage_callback::class_guid = { 0xaf51c159, 0x6326, 0x4da5, { 0x90, 0xb0, 0xf1, 0x1f, 0x99, 0x64, 0xcc, 0x2e } }; FOOGUIDDECL const GUID metadb_io_edit_callback::class_guid = { 0x2388a50f, 0x33d, 0x4b7c, { 0x91, 0xf2, 0x51, 0x53, 0x69, 0x43, 0xe9, 0xed } }; +FOOGUIDDECL const GUID metadb_io_edit_callback_v2::class_guid = { 0x8a4ac70, 0xaf77, 0x4a73, { 0xa6, 0xe9, 0xd5, 0xbc, 0x6, 0x9e, 0x6, 0x15 } }; FOOGUIDDECL const GUID progress_meter_instance::class_guid = { 0x13915b24, 0xefef, 0x42b5, { 0xae, 0x1d, 0x55, 0x24, 0x5e, 0x22, 0x22, 0xc5 } }; FOOGUIDDECL const GUID progress_meter::class_guid = { 0x5e98da34, 0xa9c7, 0x4925, { 0xb0, 0xec, 0x90, 0x9d, 0xe0, 0x16, 0xfa, 0x68 } }; @@ -1365,7 +1381,8 @@ FOOGUIDDECL const GUID output_v2::class_guid = { 0x4f679e4b, 0x79e0, 0x4fc9, { 0 FOOGUIDDECL const GUID output_v3::class_guid = { 0x3b764d8e, 0x6c1c, 0x40bd, { 0x9a, 0x7e, 0xac, 0x3, 0x2a, 0x3e, 0x25, 0xf1 } }; FOOGUIDDECL const GUID output_v4::class_guid = { 0x2c7a21a, 0xcc12, 0x48f3, { 0x89, 0x2e, 0xa7, 0x98, 0xb0, 0xc8, 0xaa, 0x49 } }; FOOGUIDDECL const GUID output_v5::class_guid = { 0x735e6e3f, 0x95d8, 0x45ca, { 0xad, 0x1a, 0xca, 0x88, 0x1f, 0x1d, 0x5c, 0xfa } }; - +FOOGUIDDECL const GUID output_v6::class_guid = { 0xcf632053, 0xd612, 0x4c33, { 0xa6, 0x90, 0xfe, 0x68, 0x64, 0x5b, 0x3b, 0xc9 } }; +FOOGUIDDECL const GUID output_v7::class_guid = { 0x6e7f41d9, 0x3143, 0x40c2, { 0xb7, 0xbf, 0xa4, 0xbd, 0xf9, 0x10, 0xbc, 0x1a } }; FOOGUIDDECL const GUID output_manager::class_guid = { 0x6cc5827e, 0x2c89, 0x42ff, { 0x83, 0x51, 0x76, 0xa9, 0x2e, 0x2f, 0x34, 0x50 } }; @@ -1388,12 +1405,17 @@ FOOGUIDDECL const GUID ui_element_instance_callback_v3::class_guid = { 0x6d15c0c FOOGUIDDECL const GUID ui_element_popup_host_v2::class_guid = { 0x8caac11e, 0x52b6, 0x47f7,{ 0x97, 0xc9, 0x2c, 0x87, 0xdb, 0xdb, 0x2e, 0x5b } }; FOOGUIDDECL const GUID ui_config_manager::class_guid = { 0x199050a6, 0xd0fe, 0x47af, { 0x9b, 0xd7, 0xfc, 0x74, 0x59, 0x9e, 0x47, 0xbd } }; +FOOGUIDDECL const GUID ui_config_manager_v2::class_guid = { 0x2ed12611, 0xda05, 0x4ab9, { 0x87, 0x5b, 0xfb, 0x1b, 0x5c, 0x39, 0xcd, 0xc2 } }; FOOGUIDDECL const GUID ui_edit_context::class_guid = { 0xf9ba651b, 0x52dd, 0x466f,{ 0xaa, 0x77, 0xa9, 0x7a, 0x74, 0x98, 0x80, 0x7e } }; FOOGUIDDECL const GUID ui_edit_context_manager::class_guid = { 0x3807f161, 0xaa17, 0x47df,{ 0x80, 0xf1, 0xe, 0xfc, 0xd2, 0x19, 0xb7, 0xa1 } }; FOOGUIDDECL const GUID ui_edit_context_playlist::class_guid = { 0x6dec364d, 0x29f2, 0x47c8,{ 0xaf, 0x93, 0xbd, 0x35, 0x56, 0x3f, 0xa2, 0x25 } }; +#ifdef __APPLE__ +FOOGUIDDECL const GUID ui_element_mac::class_guid = { 0x4b00554f, 0xf457, 0x41b5, { 0xa8, 0x58, 0xa9, 0x33, 0x5d, 0x8e, 0x6, 0xa } }; +#endif + #ifdef FOOBAR2000_HAVE_FILE_FORMAT_SANITIZER FOOGUIDDECL const GUID file_format_sanitizer::class_guid = { 0x933dc1b8, 0xbce8, 0x4a0a, { 0xa4, 0x53, 0xbf, 0x4e, 0x6b, 0xcd, 0xb0, 0xf9 } }; FOOGUIDDECL const GUID file_format_sanitizer_stdtags::class_guid = { 0x613bceb4, 0x54c5, 0x43e8, { 0x9e, 0xc, 0xb7, 0xec, 0x15, 0x9b, 0x85, 0x11 } }; @@ -1401,8 +1423,6 @@ FOOGUIDDECL const GUID file_format_sanitizer_v2::class_guid = { 0xe61986b7, 0x12 #endif // FOOBAR2000_HAVE_FILE_FORMAT_SANITIZER -FOOGUIDDECL const GUID filesystem_transacted::class_guid = { 0x35637f92, 0x6b65, 0x47b8,{ 0x9e, 0xe, 0x48, 0x47, 0x97, 0x62, 0x34, 0x3 } }; -FOOGUIDDECL const GUID filesystem_transacted_entry::class_guid = { 0xfd6e6849, 0x7006, 0x44db,{ 0x9b, 0x87, 0x45, 0x58, 0x3c, 0xf7, 0x5d, 0x18 } }; FOOGUIDDECL const GUID config_io_callback_v3::class_guid = { 0x8e633dd, 0x625e, 0x402c,{ 0xbe, 0xab, 0x74, 0x9, 0xd0, 0x1d, 0x41, 0xdf } }; FOOGUIDDECL const GUID filesystem_v2::class_guid = { 0xdaf04ce2, 0x36a5, 0x4346,{ 0x80, 0x7f, 0x77, 0x8c, 0x5b, 0x5c, 0x26, 0xaa } }; FOOGUIDDECL const GUID filesystem_v3::class_guid = { 0x7166094e, 0xbc64, 0x4c97, { 0xaa, 0x53, 0x62, 0xbd, 0x7a, 0x34, 0xb8, 0xf6 } }; @@ -1437,8 +1457,11 @@ FOOGUIDDECL const GUID file_lowLevelIO::guid_setFileTimes = { 0x46501e0d, 0x644d FOOGUIDDECL const GUID async_task_manager::class_guid = { 0xea055f49, 0x7c6d, 0x4695, { 0x8c, 0xf, 0xeb, 0xbd, 0x92, 0xdb, 0xa7, 0xa7 } }; FOOGUIDDECL const GUID fb2k::configStore::class_guid = { 0x97aad7fd, 0xb216, 0x4286, { 0x9e, 0xb3, 0x18, 0x8d, 0x35, 0xee, 0x84, 0x1c } }; +FOOGUIDDECL const GUID fb2k::configStore2::class_guid = { 0xd0c958db, 0x59c0, 0x4c5e, { 0x8c, 0xf2, 0xd, 0xb5, 0xd7, 0x9e, 0x9a, 0x1e } }; +#if FOOBAR2020 FOOGUIDDECL const GUID fb2k::timerManager::class_guid = { 0xa93c35e3, 0x3a61, 0x40b2, { 0xa5, 0x29, 0x26, 0x49, 0xd9, 0xab, 0x7d, 0x6 } }; +#endif // FOOBAR2020 FOOGUIDDECL const GUID read_ahead_tools::class_guid = { 0x709671bf, 0x449a, 0x4dc8, { 0x9f, 0xcf, 0x84, 0xb3, 0xbc, 0xec, 0x98, 0x4d } }; @@ -1494,3 +1517,24 @@ FOOGUIDDECL const GUID contextmenu_manager_v2::class_guid = { 0x8e56ee90, 0x37fb FOOGUIDDECL const GUID fb2k::console_manager::class_guid = { 0x39c3406b, 0xe2f, 0x43f3, { 0x87, 0xf2, 0xa6, 0xa3, 0x79, 0x2a, 0x98, 0x8 } }; FOOGUIDDECL const GUID fb2k::toolbarDropDown::class_guid = { 0x59aabce2, 0xce9f, 0x428b, { 0x8f, 0x10, 0xe, 0x24, 0xf3, 0xb6, 0xc7, 0x6d } }; + +FOOGUIDDECL const GUID search_index_manager::class_guid = { 0x281b998c, 0xb379, 0x4533, { 0x95, 0xe0, 0x90, 0xa2, 0x89, 0x30, 0x2a, 0x4a } }; +FOOGUIDDECL const GUID search_index::class_guid = { 0x581f032e, 0x8e4c, 0x4a46, { 0xbb, 0xa4, 0xef, 0xc, 0xc6, 0x5c, 0x98, 0xfa } }; + + +FOOGUIDDECL const GUID fb2k::callback_with_merit::class_guid = { 0x27c64500, 0x5481, 0x477a, { 0x9b, 0x22, 0x5d, 0x4c, 0x4d, 0xa6, 0x2, 0x88 } }; +FOOGUIDDECL const GUID playlist_manager_v6::class_guid = { 0x8fd46228, 0x9ba3, 0x4bb1, { 0xa0, 0x9c, 0xe8, 0x5b, 0x9e, 0x45, 0x8a, 0x58 } }; +FOOGUIDDECL const GUID library_manager_v6::class_guid = { 0x92eea180, 0x93d0, 0x44ef, { 0xbc, 0x43, 0xb6, 0x88, 0xae, 0x41, 0xb7, 0xad } }; +FOOGUIDDECL const GUID play_callback_manager_v2::class_guid = { 0x7e59a4e6, 0x810d, 0x433b, { 0xb6, 0x9, 0x12, 0xf4, 0xde, 0x7f, 0x7f, 0x47 } }; +FOOGUIDDECL const GUID metadb_hint_list_v4::class_guid = { 0xe7908ec9, 0xa26a, 0x418e, { 0xac, 0x13, 0xb4, 0x17, 0x67, 0x8c, 0xa, 0x4 } }; +FOOGUIDDECL const GUID metadb_pre_update_callback::class_guid = { 0xc81865af, 0x1da2, 0x4361, { 0x92, 0x88, 0xac, 0xf7, 0x83, 0x89, 0xe1, 0x9c } }; + +FOOGUIDDECL const GUID stream_receive::class_guid = { 0x1d36b5af, 0xaa6b, 0x450d, { 0xa4, 0x1f, 0x58, 0x19, 0x13, 0x4e, 0xdb, 0x57 } }; + +#ifdef __APPLE__ +FOOGUIDDECL const GUID fb2k::NSObjectWrapper::class_guid = { 0x6ec55805, 0x96f7, 0x4a8f, { 0x92, 0xb3, 0xbd, 0x33, 0xee, 0x10, 0x53, 0x17 } }; +#endif + +FOOGUIDDECL const GUID tag_processor_trailing_v2::class_guid = { 0x62b128c6, 0xc179, 0x4585, { 0x95, 0xfc, 0x30, 0x1, 0x95, 0xee, 0x99, 0x7a } }; +FOOGUIDDECL const GUID tag_processor_id3v2_v2::class_guid = { 0x9b7b982f, 0xbaf0, 0x4225, { 0xa5, 0x60, 0xa7, 0xac, 0x51, 0xd0, 0xce, 0xed } }; + diff --git a/sdk/foobar2000/SDK/hasher_md5.h b/sdk/foobar2000/SDK/hasher_md5.h index ad186f1..dccf922 100644 --- a/sdk/foobar2000/SDK/hasher_md5.h +++ b/sdk/foobar2000/SDK/hasher_md5.h @@ -11,9 +11,11 @@ struct hasher_md5_result { t_uint64 xorHalve() const; GUID asGUID() const; pfc::string8 asString() const; + pfc::string8 toString() const { return asString(); } GUID toGUID() const; static hasher_md5_result null() {hasher_md5_result h = {}; return h;} + static int compare(hasher_md5_result const & h1, hasher_md5_result const & h2) { return memcmp(&h1, &h2, sizeof(hasher_md5_result)); } }; FB2K_STREAM_READER_OVERLOAD(hasher_md5_result) { @@ -25,6 +27,8 @@ FB2K_STREAM_WRITER_OVERLOAD(hasher_md5_result) { inline bool operator==(const hasher_md5_result & p_item1,const hasher_md5_result & p_item2) {return memcmp(&p_item1,&p_item2,sizeof(hasher_md5_result)) == 0;} inline bool operator!=(const hasher_md5_result & p_item1,const hasher_md5_result & p_item2) {return memcmp(&p_item1,&p_item2,sizeof(hasher_md5_result)) != 0;} +inline bool operator>(const hasher_md5_result & p_item1, const hasher_md5_result & p_item2) { return memcmp(&p_item1, &p_item2, sizeof(hasher_md5_result)) > 0; } +inline bool operator<(const hasher_md5_result & p_item1, const hasher_md5_result & p_item2) { return memcmp(&p_item1, &p_item2, sizeof(hasher_md5_result)) < 0; } namespace pfc { template<> class traits_t : public traits_rawobject {}; diff --git a/sdk/foobar2000/SDK/image.cpp b/sdk/foobar2000/SDK/image.cpp index c973f44..263b9e9 100644 --- a/sdk/foobar2000/SDK/image.cpp +++ b/sdk/foobar2000/SDK/image.cpp @@ -96,8 +96,8 @@ namespace fb2k { } - void imageLocation_t::setPath( const char * path ) { - this->path = makeString(path); + void imageLocation_t::setPath( const char * path_ ) { + this->path = makeString(path_); } void imageLocation_t::setPath(stringRef path_) { this->path = path_; @@ -122,8 +122,8 @@ namespace fb2k { return nullptr; } - void imageLocation_t::setEmbedded( const char * path, const GUID & albumArtID ) { - setPath( PFC_string_formatter() << "embedded://" << pfc::print_guid(albumArtID) << "," << path); + void imageLocation_t::setEmbedded( const char * path_, const GUID & albumArtID ) { + setPath( PFC_string_formatter() << "embedded://" << pfc::print_guid(albumArtID) << "," << path_); } bool imageLocation_t::equals(const fb2k::imageLocation_t &l1, const fb2k::imageLocation_t &l2) { if ( l1.path.is_empty() && l2.path.is_empty() ) return true; diff --git a/sdk/foobar2000/SDK/info_lookup_handler.h b/sdk/foobar2000/SDK/info_lookup_handler.h index 56822aa..300e66f 100644 --- a/sdk/foobar2000/SDK/info_lookup_handler.h +++ b/sdk/foobar2000/SDK/info_lookup_handler.h @@ -5,20 +5,22 @@ class NOVTABLE info_lookup_handler : public service_base { enum { flag_album_lookup = 1 << 0, flag_track_lookup = 1 << 1, + //! \since 2.2: supports lookup_noninteractive() call; before 2.2, lookup_noninteractive was assumed supported if info_lookup_handler_v2 was implemented. + flag_noninteractive = 1 << 2, }; //! Retrieves human-readable name of the lookup handler to display in user interface. virtual void get_name(pfc::string_base & p_out) = 0; - //! Returns one or more of flag_track_lookup, and flag_album_lookup. + //! Returns one or more of flag_track_lookup, flag_album_lookup, flag_noninteractive. virtual t_uint32 get_flags() = 0; virtual fb2k::hicon_t get_icon(int p_width, int p_height) = 0; //! Performs a lookup. Creates a modeless dialog and returns immediately. - //! @param p_items Items to look up. - //! @param p_notify Callback to notify caller when the operation has completed. Call on_completion with status code 0 to signal failure/abort, or with code 1 to signal success / new infos in metadb. - //! @param p_parent Parent window for the lookup dialog. Caller will typically disable the window while lookup is in progress and enable it back when completion is signaled. + //! @param items Items to look up. + //! @param notify Callback to notify caller when the operation has completed. Call on_completion with status code 0 to signal failure/abort, or with code 1 to signal success / new infos in metadb. + //! @param parent Parent window for the lookup dialog. Caller will typically disable the window while lookup is in progress and enable it back when completion is signaled. virtual void lookup(metadb_handle_list_cref items,completion_notify::ptr notify,fb2k::hwnd_t parent) = 0; FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(info_lookup_handler); @@ -31,3 +33,13 @@ class NOVTABLE info_lookup_handler_v2 : public info_lookup_handler { virtual double merit() {return 0;} virtual void lookup_noninteractive(metadb_handle_list_cref items, completion_notify::ptr notify, fb2k::hwnd_t parent) = 0; }; + +//! Since 2.2 +class NOVTABLE info_lookup_handler_v3 : public info_lookup_handler_v2 { + FB2K_MAKE_SERVICE_INTERFACE(info_lookup_handler_v3, info_lookup_handler_v2); +public: + //! Some handlers depend on user settings to access multiple actual online services. \n + //! Use this method to retrieve individual handlers for specific services, with proper name, icon, etc. + //! @returns Array of info_lookup_handler objects, null if there are no subhandlers and this object should be used. + virtual fb2k::arrayRef subhandlers() { return nullptr; } +}; \ No newline at end of file diff --git a/sdk/foobar2000/SDK/initquit.h b/sdk/foobar2000/SDK/initquit.h index bc34b1f..411f63a 100644 --- a/sdk/foobar2000/SDK/initquit.h +++ b/sdk/foobar2000/SDK/initquit.h @@ -38,3 +38,41 @@ class NOVTABLE init_stage_callback : public service_base { static void dispatch(t_uint32 stage) {FB2K_FOR_EACH_SERVICE(init_stage_callback, on_init_stage(stage));} }; + +//! Helper for FB2K_RUN_ON_INIT_QUIT() +class initquit_lambda : public initquit { +public: + initquit_lambda(std::function i, std::function q) : m_init(i), m_quit(q) {} + void on_init() override { if (m_init) m_init(); } + void on_quit() override { if (m_quit) m_quit(); } +private: + const std::function m_init, m_quit; +}; + +//! Helper macros to skip implementing initquit.\n +//! Usage: \n +//! void myfunc() { ... } \n +//! FB2K_RUN_ON_INIT(myFunc); +#define FB2K_RUN_ON_INIT_QUIT(funcInit, funcQuit) FB2K_SERVICE_FACTORY_PARAMS(initquit_lambda, funcInit, funcQuit) +#define FB2K_RUN_ON_INIT(func) FB2K_RUN_ON_INIT_QUIT(func, nullptr) +#define FB2K_RUN_ON_QUIT(func) FB2K_RUN_ON_INIT_QUIT(nullptr, func) + +//! Helper for FB2K_ON_INIT_STAGE +class init_stage_callback_lambda : public init_stage_callback { +public: + init_stage_callback_lambda(std::function f, uint32_t stage) : m_func(f), m_stage(stage) {} + + void on_init_stage(t_uint32 stage) override { + PFC_ASSERT(m_func != nullptr); + if (stage == m_stage) m_func(); + } + + const std::function m_func; + const uint32_t m_stage; +}; + +//! Helper macro to skip implementing init_stage_callback.\n +//! Usage: \n +//! void myfunc() {...} \n +//! FB2K_ON_INIT_STAGE(myfunc, init_stages::after_ui_init); +#define FB2K_ON_INIT_STAGE(func, stage) FB2K_SERVICE_FACTORY_PARAMS(init_stage_callback_lambda, func, stage) diff --git a/sdk/foobar2000/SDK/input.cpp b/sdk/foobar2000/SDK/input.cpp index 9dbf0cd..e01eae2 100644 --- a/sdk/foobar2000/SDK/input.cpp +++ b/sdk/foobar2000/SDK/input.cpp @@ -199,11 +199,11 @@ service_ptr input_entry::g_open_from_list(input_entry_list_t const & p_list, con auto ret = p_list[n]->open(whatFor, p_filehint, p_path, logger, p_abort); if (outGUID != nullptr) * outGUID = input_get_guid(p_list[n]); return ret; - } catch (exception_io_no_handler_for_path) { + } catch (exception_io_no_handler_for_path const &) { //do nothing, skip over - } catch(exception_io_unsupported_format) { + } catch(exception_io_unsupported_format const &) { if (!errUnsupported) errUnsupported = std::current_exception(); - } catch (exception_io_data) { + } catch (exception_io_data const &) { if (!errData) errData = std::current_exception(); } } @@ -267,7 +267,7 @@ service_ptr input_entry::g_open(const GUID & whatFor, file::ptr p_filehint, cons if (g_find_inputs_by_content_type(list, content_type, p_from_redirect)) { try { return g_open_from_list(list, whatFor, l_file, p_path, logger, p_abort); - } catch (exception_io_unsupported_format) { + } catch (exception_io_unsupported_format const &) { #if PFC_DEBUG FB2K_DebugLog() << "Failed to open by content type, using fallback"; #endif @@ -312,7 +312,7 @@ void input_entry::g_open_for_info_write_timeout(service_ptr_t try { g_open_for_info_write(p_instance,p_filehint,p_path,p_abort,p_from_redirect); break; - } catch(exception_io_sharing_violation) { + } catch(exception_io_sharing_violation const &) { if (timer.query() > p_timeout) throw; p_abort.sleep(0.01); } @@ -350,13 +350,15 @@ void input_open_file_helper(service_ptr_t & p_file,const char * p_path,t_i } uint32_t input_entry::g_flags_for_path( const char * path, uint32_t mask ) { -#if FOOBAR2000_TARGET_VERSION >= 80 +#if defined(FOOBAR2000_DESKTOP) && FOOBAR2000_TARGET_VERSION >= 80 return input_manager_v3::get()->flags_for_path(path, mask); #else +#ifdef FOOBAR2000_DESKTOP input_manager_v3::ptr api; if ( input_manager_v3::tryGet(api) ) { return api->flags_for_path(path, mask); } +#endif uint32_t ret = 0; service_enum_t e; input_entry::ptr p; auto ext = pfc::string_extension(path); @@ -368,14 +370,16 @@ uint32_t input_entry::g_flags_for_path( const char * path, uint32_t mask ) { #endif } uint32_t input_entry::g_flags_for_content_type( const char * ct, uint32_t mask ) { -#if FOOBAR2000_TARGET_VERSION >= 80 +#if defined(FOOBAR2000_DESKTOP) && FOOBAR2000_TARGET_VERSION >= 80 return input_manager_v3::get()->flags_for_content_type(ct, mask); #else +#ifdef FOOBAR2000_DESKTOP input_manager_v3::ptr api; if ( input_manager_v3::tryGet(api) ) { return api->flags_for_content_type( ct, mask ); } - uint32_t ret = 0; +#endif + uint32_t ret = 0; service_enum_t e; input_entry::ptr p; while(e.next(p)) { uint32_t f = p->get_flags() & mask; @@ -425,7 +429,31 @@ t_filestats2 input_info_reader::get_stats2_(const char* fallbackPath, uint32_t f try { auto fs = filesystem::tryGet(fallbackPath); if (fs.is_valid()) ret = fs->get_stats2_(fallbackPath, f, a); - } catch (exception_io) {} + } catch (exception_io const &) {} } return ret; +} + +GUID input_entry::get_guid_() { + auto ret = pfc::guid_null; + input_entry_v2::ptr v2; + if ( v2 &= this ) ret = v2->get_guid(); + return ret; +} + +const char* input_entry::get_name_() { + const char * ret = ""; + input_entry_v2::ptr v2; + if ( v2 &= this ) ret = v2->get_name(); + return ret; +} + +input_entry::ptr input_entry::g_find_by_guid(const GUID& guid) { + for (auto ptr : enumerate()) { + input_entry_v2::ptr v2; + if (v2 &= ptr) { + if ( guid == v2->get_guid() ) return v2; + } + } + return nullptr; } \ No newline at end of file diff --git a/sdk/foobar2000/SDK/input.h b/sdk/foobar2000/SDK/input.h index be857e7..7873700 100644 --- a/sdk/foobar2000/SDK/input.h +++ b/sdk/foobar2000/SDK/input.h @@ -7,12 +7,23 @@ PFC_DECLARE_EXCEPTION(exception_tagging_unsupported, exception_io_data, "Tagging of this file format is not supported") enum { + // Seek commands won't be issued, can skip initializing seektables etc that sequential decode doesn't need. input_flag_no_seeking = 1 << 0, + // Do not loop, also ignore user looping settings. input_flag_no_looping = 1 << 1, + // Opening for actual playback, not for conversion/scan/test. input_flag_playback = 1 << 2, + // Testing integrity, perform additional checks and report errors. Not mutually exlusive with input_flag_playback! input_flag_testing_integrity = 1 << 3, + // OK to perform cheap but inaccurate seeking. + // Note that seeking should be ALWAYS sample accurate. This setting is a hint for decoding formats that are expensive to seek properly, indicating that no obvious harm will come from taking shortcuts. input_flag_allow_inaccurate_seeking = 1 << 4, + // Suppress decode_postprocessor use. Handled by decode_postprocessor framework. input_flag_no_postproc = 1 << 5, + // DSD decoders only: Send DSD as DoP. If not set, DSD should be decimated to PCM. + input_flag_dop = 1 << 6, + // Auotmated test suite running, disregard user options if possible + input_flag_canonical_decode = 1 << 7, input_flag_simpledecode = input_flag_no_seeking|input_flag_no_looping, }; @@ -97,8 +108,7 @@ class NOVTABLE input_decoder : public input_info_reader //! @returns false to keep old info, or true to indicate that changes have been made to p_info and those should be displayed. virtual bool get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) = 0; - //! Called from playback thread before sleeping. - //! @param p_abort abort_callback object signaling user aborting the operation. + //! Obsolete, do not use, do not rely on. virtual void on_idle(abort_callback & p_abort) = 0; @@ -154,17 +164,7 @@ class input_params { //! Return 1 if position was written to arg2, 0 if n/a. static const GUID query_position; - struct continue_stream_t { - file::ptr reader; - const char * path; - }; - //! Tells the decoder to continue decoding from another URL, without flushing etc. Mainly used by HLS streams. - //! arg2: continue_stream_t - //! Return 1 to acknowledge, 0 if unsupported. - //! A call to decode_initialize() will follow if you return 1; perform actual file open from there. - static const GUID continue_stream; - - //! Asks whether it is OK to externally rewrite tags on this file without closing and reopening the decoder. \n + //! Asks whether it is OK to externally rewrite tags on this file without closing and reopening the decoder. \n //! Return 1 if the decoder reads all relevant content in open() without leaving the file open afterwards, 0 otherwise (the default). static const GUID is_tag_write_safe; }; @@ -277,6 +277,10 @@ class NOVTABLE input_entry : public service_base { static uint32_t g_flags_for_path( const char * pathFor, uint32_t mask = UINT32_MAX ); static uint32_t g_flags_for_content_type( const char * ct, uint32_t mask = UINT32_MAX ); + + GUID get_guid_(); + const char * get_name_(); + static input_entry::ptr g_find_by_guid( const GUID & ); }; //! \since 1.4 diff --git a/sdk/foobar2000/SDK/input_file_type.cpp b/sdk/foobar2000/SDK/input_file_type.cpp index ef072fc..116cb02 100644 --- a/sdk/foobar2000/SDK/input_file_type.cpp +++ b/sdk/foobar2000/SDK/input_file_type.cpp @@ -25,6 +25,26 @@ static void formatMaskList(pfc::string_base & out, t_fnList const & in, const ch } } +void input_file_type::for_each_media_ext( std::function fn, bool bDotExt) { + for( auto p : input_file_type::enumerate()) { + const unsigned cnt = p->get_count(); + for (unsigned w = 0; w < cnt; ++w) { + pfc::string8 maskCombined; + p->get_mask(w, maskCombined); + + pfc::chain_list_v2_t masks; + pfc::splitStringByChar(masks, maskCombined, ';'); + for( auto i : masks ) { + const char * m = i.get_ptr(); + if (pfc::strcmp_partial(m, "*.") == 0) { + const char * ext = m + 1; // .ext + if (!bDotExt) ++ext; + fn(ext); + } + } + } + } +} void input_file_type::make_filetype_support_fingerprint(pfc::string_base & str) { pfc::string_formatter out; pfc::avltree_t names; diff --git a/sdk/foobar2000/SDK/input_file_type.h b/sdk/foobar2000/SDK/input_file_type.h index 4a09af2..b2a91ed 100644 --- a/sdk/foobar2000/SDK/input_file_type.h +++ b/sdk/foobar2000/SDK/input_file_type.h @@ -1,4 +1,5 @@ #pragma once +#include //! Entrypoint interface for registering media file types that can be opened through "open file" dialogs or associated with foobar2000 application in Windows shell. \n //! Instead of implementing this directly, use DECLARE_FILE_TYPE() / DECLARE_FILE_TYPE_EX() macros. @@ -8,6 +9,8 @@ class input_file_type : public service_base { virtual bool get_name(unsigned idx,pfc::string_base & out)=0;//eg. "MPEG files" virtual bool get_mask(unsigned idx,pfc::string_base & out)=0;//eg. "*.MP3;*.MP2"; separate with semicolons virtual bool is_associatable(unsigned idx) = 0; + + static void for_each_media_ext( std::function, bool bDotExt = false); #if FOOBAR2000_TARGET_VERSION >= 76 static void build_openfile_mask(pfc::string_base & out,bool b_include_playlists=true, bool b_include_archives = false); @@ -51,10 +54,10 @@ class input_file_type_impl : public input_file_type bool m_associatable; public: input_file_type_impl(const char * p_name, const char * p_mask,bool p_associatable) : name(p_name), mask(p_mask), m_associatable(p_associatable) {} - unsigned get_count() {return 1;} - bool get_name(unsigned idx,pfc::string_base & out) {if (idx==0) {out = name; return true;} else return false;} - bool get_mask(unsigned idx,pfc::string_base & out) {if (idx==0) {out = mask; return true;} else return false;} - bool is_associatable(unsigned idx) {return m_associatable;} + unsigned get_count() override {return 1;} + bool get_name(unsigned idx,pfc::string_base & out) override {if (idx==0) {out = name; return true;} else return false;} + bool get_mask(unsigned idx,pfc::string_base & out) override {if (idx==0) {out = mask; return true;} else return false;} + bool is_associatable(unsigned idx) override { (void)idx; return m_associatable; } }; @@ -77,12 +80,14 @@ class input_file_type_factory : private service_factory_single_transparent_t p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort); @@ -53,13 +54,20 @@ class input_impl bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta); //! See: input_decoder::get_dynamic_info_track(). Valid after decode_initialize(). bool decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta); - //! See: input_decoder::on_idle(). Valid after decode_initialize(). + //! Obsolete, do not use, do not rely on. void decode_on_idle(abort_callback & p_abort); - //! See: input_info_writer::set_info(). Valid after open() with input_open_info_write reason. + //! See: input_info_writer::set_info(). Valid after open() with input_open_info_write reason. \n + //! If your implementation does not support writing tags, throw exception_tagging_unsupported from open() and retag_* methods will never be called. void retag_set_info(t_uint32 p_subsong,const file_info & p_info,abort_callback & p_abort); - //! See: input_info_writer::commit(). Valid after open() with input_open_info_write reason. - void retag_commit(abort_callback & p_abort); + //! See: input_info_writer::commit(). Valid after open() with input_open_info_write reason. \n + //! If your implementation does not support writing tags, throw exception_tagging_unsupported from open() and retag_* methods will never be called. + void retag_commit(abort_callback & p_abort); + + //! See: input_info_writer_v2::remove_tags(). Valid after open() with input_open_info_write reason. \n + //! If possible, entirely remove tags from the file (truncate ID3/APE tags away etc); otherwise set blank metadata. \n + //! If your implementation does not support writing tags, throw exception_tagging_unsupported from open() and remove_tags() will never be called. + void remove_tags(abort_callback & p_abort); //! See: input_entry::is_our_content_type(). static bool g_is_our_content_type(const char * p_content_type); @@ -90,18 +98,18 @@ class input_impl //! Inherit from this and you implement input_decoder_v4 without having to provide all the methods you don't actually need. class input_stubs { public: - bool decode_run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort) { throw pfc::exception_not_implemented(); } - void set_logger(event_logger::ptr ptr) {} - void set_pause(bool paused) {} + bool decode_run_raw(audio_chunk& p_chunk, mem_block_container& p_raw, abort_callback& p_abort) { (void)p_chunk; (void)p_raw; (void)p_abort; throw pfc::exception_not_implemented(); } + void set_logger(event_logger::ptr ptr) { (void)ptr; } + void set_pause(bool paused) { (void)paused; } bool flush_on_pause() { return false; } - size_t extended_param(const GUID & type, size_t arg1, void * arg2, size_t arg2size) { return 0; } + size_t extended_param(const GUID& type, size_t arg1, void* arg2, size_t arg2size) { (void)type, (void)arg1; (void)arg2; (void)arg2size; return 0; } static GUID g_get_preferences_guid() {return pfc::guid_null;} static bool g_is_low_merit() { return false; } - static bool g_fallback_is_our_payload(const void* bytes, size_t bytesAvail, t_filesize bytesWhole) { return false; } + static bool g_fallback_is_our_payload(const void* bytes, size_t bytesAvail, t_filesize bytesWhole) { (void)bytes; (void)bytesAvail; (void)bytes; (void)bytesWhole; return false; } - bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta) { return false; } - bool decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) { return false; } - void decode_on_idle(abort_callback & p_abort) { } + bool decode_get_dynamic_info(file_info& p_out, double& p_timestamp_delta) { (void)p_out; (void)p_timestamp_delta; return false; } + bool decode_get_dynamic_info_track(file_info& p_out, double& p_timestamp_delta) { (void)p_out; (void)p_timestamp_delta; return false; } + void decode_on_idle(abort_callback& p_abort) { (void)p_abort; } //! These typedefs indicate which interfaces your class actually supports. You can override them to support non default input API interfaces without specifying input_factory parameters. @@ -121,7 +129,8 @@ class input_singletrack_impl //! - input_open_info_read - info retrieval methods are valid; \n //! - input_open_decode - info retrieval and decoding methods are valid; \n //! - input_open_info_write - info retrieval and retagging methods are valid; \n - //! Note that info retrieval methods are valid in all cases, and they may be called at any point of decoding/retagging process. Results of info retrieval methods (other than get_subsong_count() / get_subsong()) between retag_set_info() and retag_commit() are undefined however; those should not be called during that period. + //! Note that info retrieval methods are valid in all cases, and they may be called at any point of decoding/retagging process. Results of info retrieval methods (other than get_subsong_count() / get_subsong()) between retag_set_info() and retag_commit() are undefined however; those should not be called during that period. \n + //! If your implementation does not support writing tags, throw exception_tagging_unsupported in response to input_open_info_write and do not implement other tag writing methods (they will not be called). //! @param p_abort abort_callback object signaling user aborting the operation. void open(service_ptr_t p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort); @@ -144,12 +153,18 @@ class input_singletrack_impl bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta); //! See: input_decoder::get_dynamic_info_track(). Valid after decode_initialize(). bool decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta); - //! See: input_decoder::on_idle(). Valid after decode_initialize(). + //! Obsolete, do not use, do not rely on. void decode_on_idle(abort_callback & p_abort); - //! See: input_info_writer::set_info(). Note that input_info_writer::commit() call isn't forwarded because it's useless in case of non-multitrack-enabled inputs. Valid after open() with input_open_info_write reason. + //! See: input_info_writer::set_info(). Note that input_info_writer::commit() call isn't forwarded because it's useless in case of non-multitrack-enabled inputs. Valid after open() with input_open_info_write reason. \n + //! If your implementation does not support writing tags, throw exception_tagging_unsupported from open() and retag() will never be called. void retag(const file_info & p_info,abort_callback & p_abort); + //! See: input_info_writer_v2::remove_tags(). Valid after open() with input_open_info_write reason. \n + //! If possible, entirely remove tags from the file (truncate ID3/APE tags away etc); otherwise set blank metadata. \n + //! If your implementation does not support writing tags, throw exception_tagging_unsupported from open() and remove_tags() will never be called. + void remove_tags(abort_callback & p_abort); + //! See: input_entry::is_our_content_type(). static bool g_is_our_content_type(const char * p_content_type); //! See: input_entry::is_our_path(). diff --git a/sdk/foobar2000/SDK/library_callbacks.h b/sdk/foobar2000/SDK/library_callbacks.h index a36c32b..da455b3 100644 --- a/sdk/foobar2000/SDK/library_callbacks.h +++ b/sdk/foobar2000/SDK/library_callbacks.h @@ -1,6 +1,7 @@ #pragma once #include "library_manager.h" #include "metadb_callbacks.h" +#include "callback_merit.h" //! Callback service receiving notifications about Media Library content changes. Methods called only from main thread.\n //! Use library_callback_factory_t template to register. @@ -30,7 +31,6 @@ class NOVTABLE library_callback_v2 : public library_callback { FB2K_MAKE_SERVICE_INTERFACE(library_callback_v2, library_callback); }; - template class library_callback_factory_t : public service_factory_single_t {}; @@ -88,6 +88,7 @@ class library_callback_v2_dynamic_impl_base : public library_callback_v2_dynamic void on_items_added(metadb_handle_list_cref) override {} void on_items_removed(metadb_handle_list_cref) override {} void on_items_modified_v2(metadb_handle_list_cref, metadb_io_callback_v2_data&) override {} + void on_library_initialized() override {} PFC_CLASS_NOT_COPYABLE_EX(library_callback_v2_dynamic_impl_base); }; diff --git a/sdk/foobar2000/SDK/library_index.h b/sdk/foobar2000/SDK/library_index.h index 52067e9..eec04fa 100644 --- a/sdk/foobar2000/SDK/library_index.h +++ b/sdk/foobar2000/SDK/library_index.h @@ -2,23 +2,16 @@ #include "search_tools.h" -//! \since 2.0 -//! Provides access to indexed searches of media library content, \n -//! which in typical scenarios are much faster than running whole library content via search filters. \n -//! Not every search query is supported by library index - in particular, queries relying on title formatting cannot be optimized. \n -//! In such cases, search() will fall through to legacy search filter use. \n -//! Note that if your query looks like: cond1 AND cond2 AND cond3 (...), and only some of the conditions cannot be searched using index, \n -//! search() will use the supported subset of your query to only feed relevant subset of your library to slow search filters. +//! OBSOLETE, DO NOT USE \n +//! Please use search_index instead. class NOVTABLE library_index : public service_base { FB2K_MAKE_SERVICE_COREAPI(library_index); public: - //! Same as using search filters, provided for consistency. Use search() instead. virtual void hit_test(search_filter::ptr pattern, metadb_handle_list_cref items, bool* out, abort_callback & a) = 0; enum { flag_sort = 1 << 0, }; - //! @returns list of metadb_handles. Safe to use arr->as_list_of() to get a pfc::list_base_const_t virtual fb2k::arrayRef search(search_filter::ptr pattern, uint32_t flags, abort_callback& abort) = 0; }; \ No newline at end of file diff --git a/sdk/foobar2000/SDK/library_manager.h b/sdk/foobar2000/SDK/library_manager.h index ba8b93c..b7ceb83 100644 --- a/sdk/foobar2000/SDK/library_manager.h +++ b/sdk/foobar2000/SDK/library_manager.h @@ -1,4 +1,5 @@ #pragma once +#include "callback_merit.h" class library_callback_dynamic; class library_callback_v2_dynamic; @@ -106,7 +107,7 @@ class NOVTABLE library_manager_v4 : public library_manager_v3 { FB2K_MAKE_SERVICE_COREAPI_EXTENSION(library_manager_v4, library_manager_v3); }; -//! \since 2.0 beta 13 +//! \since 2.0 class NOVTABLE library_manager_v5 : public library_manager_v4 { FB2K_MAKE_SERVICE_COREAPI_EXTENSION(library_manager_v5, library_manager_v4); public: @@ -115,6 +116,15 @@ class NOVTABLE library_manager_v5 : public library_manager_v4 { //! Extensible status query method. Returns 0 for unrecognized commands. virtual size_t library_status(const GUID& arg, size_t arg1, void* arg2, size_t arg2bytes) = 0; + + bool is_current_callback_from_hook() { return library_status(status_current_callback_from_hook, 0, nullptr, 0) != 0; } +}; + +//! \since 2.0 +class NOVTABLE library_manager_v6 : public library_manager_v5 { + FB2K_MAKE_SERVICE_COREAPI_EXTENSION(library_manager_v6, library_manager_v5); +public: + virtual void set_callback_merit(library_callback_v2_dynamic*, fb2k::callback_merit_t) = 0; }; //! Implement this service to appear on "library viewers" list in Media Library preferences page.\n diff --git a/sdk/foobar2000/SDK/main_thread_callback.cpp b/sdk/foobar2000/SDK/main_thread_callback.cpp index 77492f8..80e1676 100644 --- a/sdk/foobar2000/SDK/main_thread_callback.cpp +++ b/sdk/foobar2000/SDK/main_thread_callback.cpp @@ -15,14 +15,13 @@ void main_thread_callback_add(main_thread_callback::ptr ptr) { namespace { typedef std::function func_t; - class mtcallback_func : public main_thread_callback { - public: - mtcallback_func(func_t const & f) : m_f(f) {} - - void callback_run() { - m_f(); - } - + class mtcallback_func : public main_thread_callback { + public: + mtcallback_func(func_t const& f) : m_f(f) {} + + void callback_run() noexcept override { + m_f(); + } private: func_t m_f; }; @@ -32,6 +31,13 @@ void fb2k::inMainThread( std::function f ) { main_thread_callback_add( new service_impl_t(f)); } +void fb2k::inMainThread(std::function f, abort_callback& a) { + auto abort = std::make_shared< abort_callback_clone >(a); + inMainThread([f, abort] { + if (!abort->is_set()) f(); + }); +} + void fb2k::inMainThread2( std::function f ) { if ( core_api::is_main_thread() ) { f(); @@ -42,6 +48,11 @@ void fb2k::inMainThread2( std::function f ) { void fb2k::inMainThreadSynchronous(std::function f, abort_callback & abort) { abort.check(); + + if (core_api::is_main_thread()) { + f(); return; + } + auto evt = std::make_shared(); auto f2 = [f, evt] { f(); @@ -49,4 +60,15 @@ void fb2k::inMainThreadSynchronous(std::function f, abort_callback & ab }; inMainThread(f2); abort.waitForEvent(*evt); +} + +void fb2k::inMainThreadSynchronous2(std::function f) { + // Have new API? + auto api = main_thread_callback_manager_v2::tryGet(); + if (api.is_valid()) { + api->run_synchronously( fb2k::service_new(f) ); + return; + } + + inMainThreadSynchronous(f, fb2k::noAbort); } \ No newline at end of file diff --git a/sdk/foobar2000/SDK/main_thread_callback.h b/sdk/foobar2000/SDK/main_thread_callback.h index a7dd95c..a350e7c 100644 --- a/sdk/foobar2000/SDK/main_thread_callback.h +++ b/sdk/foobar2000/SDK/main_thread_callback.h @@ -28,12 +28,23 @@ There is no need to use this API directly - use fb2k::inMainThread() with a lamb */ class NOVTABLE main_thread_callback_manager : public service_base { public: - //! Queues a callback object. This can be called from any thread, implementation ensures multithread safety. Implementation will call p_callback->callback_run() once later. To get it called repeatedly, you would need to add your callback again. + //! Queues a callback object. This can be called from any thread, implementation ensures multithread safety. Implementation will call p_callback->callback_run() once later. To get it called repeatedly, you would need to add your callback again. \n + //! Queued callbacks will be called in the same order as they were queued (FIFO). virtual void add_callback(service_ptr_t p_callback) = 0; FB2K_MAKE_SERVICE_COREAPI(main_thread_callback_manager); }; +//! \since 2.0 +//! Additional method added to help recovering from method-called-in-wrong-thread scenarios. +class NOVTABLE main_thread_callback_manager_v2 : public main_thread_callback_manager { + FB2K_MAKE_SERVICE_COREAPI_EXTENSION(main_thread_callback_manager_v2, main_thread_callback_manager) +public: + //! Uses win32 SendMessage() to invoke your callback synchronously, blocks until done. \n + //! If multiple threads call this, order of callbacks is undefined. \n + //! Callbacks run_synchronously() callbacks take precedence over add_callback(). + virtual void run_synchronously( main_thread_callback::ptr ) = 0; +}; // ====================================================================================================== // Obsolete helpers - they still work, but it's easier to just use fb2k::inMainThread(). @@ -52,125 +63,4 @@ template static void mai main_thread_callback_add(new service_impl_t(p1, p2)); } -// Proxy class - friend this to allow callInMainThread to access your private methods -class callInMainThread { -public: - template - static void callThis(host_t * host, param_t & param) { - host->inMainThread(param); - } - template - static void callThis( host_t * host ) { - host->inMainThread(); - } -}; - -// Internal class, do not use. -template -class _callInMainThreadSvc_t : public main_thread_callback { -public: - _callInMainThreadSvc_t(service_t * host, param_t const & param) : m_host(host), m_param(param) {} - void callback_run() { - callInMainThread::callThis(m_host.get_ptr(), m_param); - } -private: - service_ptr_t m_host; - param_t m_param; -}; - - -// Main thread callback helper. You can use this to easily invoke inMainThread(someparam) on your class without writing any wrapper code. -// Requires myservice_t to be a fb2k service class with reference counting. -template -static void callInMainThreadSvc(myservice_t * host, param_t const & param) { - typedef _callInMainThreadSvc_t impl_t; - service_ptr_t obj = new service_impl_t(host, param); - main_thread_callback_manager::get()->add_callback( obj ); -} - - - - -//! Helper class to call methods of your class (host class) in main thread with convenience. \n -//! Deals with the otherwise ugly scenario of your class becoming invalid while a method is queued. \n -//! Have this as a member of your class, then use m_mthelper.add( this, somearg ) ; to defer a call to this->inMainThread(somearg). \n -//! If your class becomes invalid before inMainThread is executed, the pending callback is discarded. \n -//! You can optionally call shutdown() to invalidate all pending callbacks early (in a destructor of your class - without waiting for callInMainThreadHelper destructor to do the job. \n -//! In order to let callInMainThreadHelper access your private methods, declare friend class callInMainThread. \n -//! Note that callInMainThreadHelper is expected to be created and destroyed in main thread. -class callInMainThreadHelper { -public: - - typedef std::function< void () > func_t; - - typedef pfc::rcptr_t< bool > killswitch_t; - - class entryFunc : public main_thread_callback { - public: - entryFunc( func_t const & func, killswitch_t ks ) : m_ks(ks), m_func(func) {} - - void callback_run() { - if (!*m_ks) m_func(); - } - - private: - killswitch_t m_ks; - func_t m_func; - }; - - template - class entry : public main_thread_callback { - public: - entry( host_t * host, arg_t const & arg, killswitch_t ks ) : m_ks(ks), m_host(host), m_arg(arg) {} - void callback_run() { - if (!*m_ks) callInMainThread::callThis( m_host, m_arg ); - } - private: - killswitch_t m_ks; - host_t * m_host; - arg_t m_arg; - }; - template - class entryVoid : public main_thread_callback { - public: - entryVoid( host_t * host, killswitch_t ks ) : m_ks(ks), m_host(host) {} - void callback_run() { - if (!*m_ks) callInMainThread::callThis( m_host ); - } - private: - killswitch_t m_ks; - host_t * m_host; - }; - - void add(func_t f) { - add_( new service_impl_t< entryFunc > ( f, m_ks ) ); - } - - template - void add( host_t * host, arg_t const & arg) { - add_( new service_impl_t< entry >( host, arg, m_ks ) ); - } - template - void add( host_t * host ) { - add_( new service_impl_t< entryVoid >( host, m_ks ) ); - } - void add_( main_thread_callback::ptr cb ) { - main_thread_callback_add( cb ); - } - - callInMainThreadHelper() { - m_ks.new_t(); - * m_ks = false; - } - void shutdown() { - PFC_ASSERT( core_api::is_main_thread() ); - * m_ks = true; - } - ~callInMainThreadHelper() { - shutdown(); - } - -private: - killswitch_t m_ks; - -}; +// More legacy helper code moved to helpers/callInMainThreadHelper.h \ No newline at end of file diff --git a/sdk/foobar2000/SDK/mainmenu.cpp b/sdk/foobar2000/SDK/mainmenu.cpp index 372ae0b..6647c06 100644 --- a/sdk/foobar2000/SDK/mainmenu.cpp +++ b/sdk/foobar2000/SDK/mainmenu.cpp @@ -7,12 +7,18 @@ bool mainmenu_commands::g_execute_dynamic(const GUID & p_guid, const GUID & p_su mainmenu_commands_v2::ptr v2; if (!ptr->service_query_t(v2)) return false; if (!v2->is_command_dynamic(index)) return false; - return v2->dynamic_execute(index, p_subGuid, p_callback); + bool rv = false; + fb2k::crashOnException([&] { + rv = v2->dynamic_execute(index, p_subGuid, p_callback);; + }, "mainmenu_commands::dynamic_execute"); + return rv; } bool mainmenu_commands::g_execute(const GUID & p_guid,service_ptr_t p_callback) { mainmenu_commands::ptr ptr; t_uint32 index; if (!menu_item_resolver::g_resolve_main_command(p_guid, ptr, index)) return false; - ptr->execute(index, p_callback); + fb2k::crashOnException( [&] { + ptr->execute(index, p_callback); + }, "mainmenu_commands::execute"); return true; } diff --git a/sdk/foobar2000/SDK/menu.h b/sdk/foobar2000/SDK/menu.h index d9fa494..50bd4ee 100644 --- a/sdk/foobar2000/SDK/menu.h +++ b/sdk/foobar2000/SDK/menu.h @@ -26,17 +26,17 @@ class NOVTABLE mainmenu_group_popup_v2 : public mainmenu_group_popup { class NOVTABLE mainmenu_commands : public service_base { public: - enum { - flag_disabled = 1<<0, - flag_checked = 1<<1, - flag_radiochecked = 1<<2, + static constexpr uint32_t + flag_disabled = 1 << 0, + flag_checked = 1 << 1, + flag_radiochecked = 1 << 2, //! \since 1.0 //! Replaces the old return-false-from-get_display() behavior - use this to make your command hidden by default but accessible when holding shift. - flag_defaulthidden = 1<<3, + flag_defaulthidden = 1 << 3, sort_priority_base = 0x10000, sort_priority_dontcare = 0x80000000u, - sort_priority_last = ~0, - }; + sort_priority_last = UINT32_MAX; + //! Retrieves number of implemented commands. Index parameter of other methods must be in 0....command_count-1 range. virtual t_uint32 get_command_count() = 0; @@ -143,19 +143,22 @@ class mainmenu_group_popup_impl : public mainmenu_group_popup { GUID m_guid,m_parent; t_uint32 m_priority; pfc::string8 m_name; }; -typedef service_factory_single_t __mainmenu_group_factory; -typedef service_factory_single_t __mainmenu_group_popup_factory; +typedef service_factory_single_t _mainmenu_group_factory; +typedef service_factory_single_t _mainmenu_group_popup_factory; -class mainmenu_group_factory : public __mainmenu_group_factory { +class mainmenu_group_factory : public _mainmenu_group_factory { public: - mainmenu_group_factory(const GUID & p_guid,const GUID & p_parent,t_uint32 p_priority) : __mainmenu_group_factory(p_guid,p_parent,p_priority) {} + mainmenu_group_factory(const GUID & p_guid,const GUID & p_parent,t_uint32 p_priority) : _mainmenu_group_factory(p_guid,p_parent,p_priority) {} }; -class mainmenu_group_popup_factory : public __mainmenu_group_popup_factory { +class mainmenu_group_popup_factory : public _mainmenu_group_popup_factory { public: - mainmenu_group_popup_factory(const GUID & p_guid,const GUID & p_parent,t_uint32 p_priority,const char * p_name) : __mainmenu_group_popup_factory(p_guid,p_parent,p_priority,p_name) {} + mainmenu_group_popup_factory(const GUID & p_guid,const GUID & p_parent,t_uint32 p_priority,const char * p_name) : _mainmenu_group_popup_factory(p_guid,p_parent,p_priority,p_name) {} }; +#define FB2K_DECLARE_MAINMENU_GROUP( guid, parent, priority, name ) FB2K_SERVICE_FACTORY_PARAMS(mainmenu_group_impl, guid, parent, priority, name ); +#define FB2K_DECLARE_MAINMENU_GROUP_POPUP( guid, parent, priority, name ) FB2K_SERVICE_FACTORY_PARAMS(mainmenu_group_popup_impl, guid, parent, priority, name ); + template class mainmenu_commands_factory_t : public service_factory_single_t {}; @@ -183,25 +186,25 @@ class NOVTABLE mainmenu_node : public service_base { //! Valid only if type is type_command. virtual GUID get_guid() = 0; //! Valid only if type is type_command. - virtual bool get_description(pfc::string_base & out) {return false;} + virtual bool get_description(pfc::string_base& out) { (void)out; return false; } }; class mainmenu_node_separator : public mainmenu_node { public: - t_uint32 get_type() {return type_separator;} - void get_display(pfc::string_base & text, t_uint32 & flags) {text = ""; flags = 0;} - t_size get_children_count() {return 0;} - ptr get_child(t_size index) {throw pfc::exception_invalid_params();} - void execute(service_ptr_t) {} - GUID get_guid() {return pfc::guid_null;} + t_uint32 get_type() override {return type_separator;} + void get_display(pfc::string_base & text, t_uint32 & flags) override {text = ""; flags = 0;} + t_size get_children_count() override {return 0;} + ptr get_child(t_size index) override { (void)index; throw pfc::exception_invalid_params(); } + void execute(service_ptr_t) override {} + GUID get_guid() override {return pfc::guid_null;} }; class mainmenu_node_command : public mainmenu_node { public: - t_uint32 get_type() {return type_command;} - t_size get_children_count() {return 0;} - ptr get_child(t_size index) {throw pfc::exception_invalid_params();} + t_uint32 get_type() override {return type_command;} + t_size get_children_count() override {return 0;} + ptr get_child(t_size index) override { (void)index; throw pfc::exception_invalid_params(); } /* void get_display(pfc::string_base & text, t_uint32 & flags); void execute(service_ptr_t callback); @@ -212,9 +215,9 @@ class mainmenu_node_command : public mainmenu_node { class mainmenu_node_group : public mainmenu_node { public: - t_uint32 get_type() {return type_group;} - void execute(service_ptr_t callback) {} - GUID get_guid() {return pfc::guid_null;} + t_uint32 get_type() override {return type_group;} + void execute(service_ptr_t callback) override { (void)callback; } + GUID get_guid() override {return pfc::guid_null;} /* void get_display(pfc::string_base & text, t_uint32 & flags); t_size get_children_count(); @@ -243,7 +246,7 @@ class NOVTABLE mainmenu_commands_v3 : public mainmenu_commands_v2 { public: //! @param main Command ID //! @param sub Reserved for future use. - virtual void menu_state_changed(const GUID& main, const GUID & sub) {} + virtual void menu_state_changed(const GUID& main, const GUID& sub) { (void)main; (void)sub; } state_callback() {} state_callback(state_callback const&) = delete; void operator=(state_callback const&) = delete; diff --git a/sdk/foobar2000/SDK/menu_common.h b/sdk/foobar2000/SDK/menu_common.h index 046d9d5..85b30af 100644 --- a/sdk/foobar2000/SDK/menu_common.h +++ b/sdk/foobar2000/SDK/menu_common.h @@ -45,7 +45,7 @@ class menu_tree_item : public service_base { //! Sub-command GUID for dynamically created items. Typically null GUID as simple items do not have sub command GUIDs. virtual GUID subCommandGuid() { return pfc::guid_null; } - virtual bool get_icon(fb2k::imageLocation_t& outLoc) { return false; } + virtual bool get_icon(fb2k::imageLocation_t& outLoc) { (void)outLoc; return false; } bool isSeparator() { return type() == itemSeparator; } bool isCommand() { return type() == itemCommand; } @@ -56,3 +56,11 @@ class menu_tree_item : public service_base { typedef menu_tree_item mainmenu_tree_item; +#define FB2K_MENU_CAPS_TITLE 1 +#define FB2K_MENU_CAPS_SENTENCE 2 + +#ifdef _WIN32 +#define FB2K_MENU_CAPS FB2K_MENU_CAPS_SENTENCE +#else +#define FB2K_MENU_CAPS FB2K_MENU_CAPS_TITLE +#endif diff --git a/sdk/foobar2000/SDK/menu_helpers.cpp b/sdk/foobar2000/SDK/menu_helpers.cpp index 6cdc7ab..63ba2bf 100644 --- a/sdk/foobar2000/SDK/menu_helpers.cpp +++ b/sdk/foobar2000/SDK/menu_helpers.cpp @@ -94,7 +94,7 @@ bool menu_helpers::guid_from_name(const char * p_name,unsigned p_name_len,GUID & for(n=0;nget_item_name(n,nametemp); - if (!strcmp_ex(nametemp,~0,p_name,p_name_len)) + if (!strcmp_ex(nametemp,SIZE_MAX,p_name,p_name_len)) { p_out = ptr->get_item_guid(n); return true; @@ -136,7 +136,7 @@ const char * menu_helpers::guid_to_name_table::search(const GUID & p_guid) assert(dataptr < m_data.get_size()); ptr->get_item_name(n,nametemp); - m_data[dataptr].m_name = strdup(nametemp); + m_data[dataptr].m_name = pfc::strDup(nametemp); m_data[dataptr].m_guid = ptr->get_item_guid(n); dataptr++; } @@ -178,7 +178,7 @@ menu_helpers::guid_to_name_table::~guid_to_name_table() int menu_helpers::name_to_guid_table::entry_compare_search(const entry & entry1,const search_entry & entry2) { - return stricmp_utf8_ex(entry1.m_name,~0,entry2.m_name,entry2.m_name_len); + return stricmp_utf8_ex(entry1.m_name,SIZE_MAX,entry2.m_name,entry2.m_name_len); } int menu_helpers::name_to_guid_table::entry_compare(const entry & entry1,const entry & entry2) @@ -202,7 +202,7 @@ bool menu_helpers::name_to_guid_table::search(const char * p_name,unsigned p_nam assert(dataptr < m_data.get_size()); ptr->get_item_name(n,nametemp); - m_data[dataptr].m_name = strdup(nametemp); + m_data[dataptr].m_name = pfc::strDup(nametemp); m_data[dataptr].m_guid = ptr->get_item_guid(n); dataptr++; } @@ -285,3 +285,53 @@ bool menu_item_resolver::g_resolve_context_command(const GUID & id, contextmenu_ bool menu_item_resolver::g_resolve_main_command(const GUID & id, mainmenu_commands::ptr & out, t_uint32 & out_index) { return menu_item_resolver::get()->resolve_main_command(id, out, out_index); } + +static bool char_is_separator(char x) +{ + for( auto s : {' ', ',', '/', '-'}) { + if (x==s) return true; + } + return false; +} + +static bool capitalize_exception( const char * ptr, size_t len ) { + for( auto e : {"for", "to", "from", "with", "by", "in", "at", "and", "or", "on", "a", "of", "as" }) { + if ( len == strlen(e) && memcmp(ptr, e, len) == 0 ) return true; + } + return false; +} +#if FB2K_MENU_CAPS == FB2K_MENU_CAPS_TITLE +static pfc::string8 capitalizeThis( const char * arg ) { + pfc::string8 work; work.prealloc( 256 ); + auto base = arg; + while(*base) { + auto ptr = base; + while(*ptr && ! char_is_separator(*ptr) ) ++ptr; + if ( ptr > base ) { + if ( !capitalize_exception( base, ptr-base ) ) { + unsigned c = 0; + auto d = pfc::utf8_decode_char(base, c, ptr-base); + if ( d > 0 ) { + c = uCharUpper( c ); + work.add_char( c ); + base += d; + } + } + work.add_string_nc(base, ptr - base); + } + while(*ptr && char_is_separator(*ptr)) { + work.add_byte(*ptr++); + } + base = ptr; + } + return work; +} +#endif // #if FB2K_MENU_CAPS == FB2K_MENU_CAPS_TITLE + +void fb2k::capitalizeMenuLabel( pfc::string_base & inOut ) { +#if FB2K_MENU_CAPS == FB2K_MENU_CAPS_TITLE + inOut = capitalizeThis( inOut ); +#else + (void) inOut; +#endif +} diff --git a/sdk/foobar2000/SDK/menu_helpers.h b/sdk/foobar2000/SDK/menu_helpers.h index d944bbc..26b88fd 100644 --- a/sdk/foobar2000/SDK/menu_helpers.h +++ b/sdk/foobar2000/SDK/menu_helpers.h @@ -108,7 +108,8 @@ class standard_commands guid_seek_back_1s, guid_seek_back_5s, guid_seek_back_10s, guid_seek_back_30s, guid_seek_back_1min, guid_seek_back_2min, guid_seek_back_5min, guid_seek_back_10min, - guid_library_configure, guid_library_rescan + guid_library_configure, guid_library_rescan, + guid_internet_radio ; static bool run_main(const GUID & guid); @@ -187,3 +188,9 @@ class standard_commands static inline bool main_playlist_moveforward() {return run_main(guid_main_playlist_moveforward);} static inline bool main_saveconfig() {return run_main(guid_main_saveconfig);} }; + +namespace fb2k { + // Turn sentence-capitalized into title-capitalized if needed + // No-op on systems using sentence capitalization + void capitalizeMenuLabel( pfc::string_base & inOut ); +} diff --git a/sdk/foobar2000/SDK/menu_manager.cpp b/sdk/foobar2000/SDK/menu_manager.cpp index 5065f3f..3f15aff 100644 --- a/sdk/foobar2000/SDK/menu_manager.cpp +++ b/sdk/foobar2000/SDK/menu_manager.cpp @@ -414,7 +414,7 @@ bool keyboard_shortcut_manager::is_typing_message(const MSG * msg) { #endif // _WIN32 -bool contextmenu_manager::execute_by_id(unsigned id) { +bool contextmenu_manager::execute_by_id(unsigned id) noexcept { contextmenu_node * ptr = find_by_id(id); if (ptr == NULL) return false; ptr->execute(); diff --git a/sdk/foobar2000/SDK/message_loop.h b/sdk/foobar2000/SDK/message_loop.h index d6756fd..2906941 100644 --- a/sdk/foobar2000/SDK/message_loop.h +++ b/sdk/foobar2000/SDK/message_loop.h @@ -1,17 +1,26 @@ #pragma once #ifdef _WIN32 -class NOVTABLE message_filter -{ +//! A message filter class. Allows you to filter incoming messages in main app loop. Use message_loop API to register/unregister, \n +//! or derive from message_filter_impl_base to have your class registered/deregistered automatically with lifetime. +//! //! Use with caution. Will not be executed during modal loops. +class NOVTABLE message_filter { public: + //! Notifies you about incoming message. \n + //! You can alter the message (not generally recommended), let others handle it (return false) \n + //! or signal that you've handled it (return true) to suppress further processing. virtual bool pretranslate_message(MSG * p_msg) = 0; }; +//! An idle handler class. Executed when main loop is about to enter idle state. \n +//! Use with caution. Will not be executed during modal loops. class NOVTABLE idle_handler { public: virtual bool on_idle() = 0; }; +//! Entrypoint API for registering message_filter and idle_handler objects. \n +//! Usage: message_loop::get()->add_message_filter(myfilter); class NOVTABLE message_loop : public service_base { public: @@ -33,35 +42,23 @@ class NOVTABLE message_loop_v2 : public message_loop { class message_filter_impl_base : public message_filter { public: - message_filter_impl_base() {message_loop::get()->add_message_filter(this);} - message_filter_impl_base(t_uint32 lowest, t_uint32 highest) { - message_loop_v2::get()->add_message_filter_ex(this, lowest, highest); - } - ~message_filter_impl_base() {message_loop::get()->remove_message_filter(this);} - bool pretranslate_message(MSG * p_msg) {return false;} + message_filter_impl_base(); + message_filter_impl_base(t_uint32 lowest, t_uint32 highest); + ~message_filter_impl_base(); + + bool pretranslate_message(MSG * ) override {return false;} PFC_CLASS_NOT_COPYABLE(message_filter_impl_base,message_filter_impl_base); }; class message_filter_impl_accel : public message_filter_impl_base { protected: - bool pretranslate_message(MSG * p_msg) { - if (m_wnd != NULL) { - if (GetActiveWindow() == m_wnd) { - if (TranslateAccelerator(m_wnd,m_accel.get(),p_msg) != 0) { - return true; - } - } - } - return false; - } + bool pretranslate_message(MSG * p_msg) override; public: - message_filter_impl_accel(HINSTANCE p_instance,const TCHAR * p_accel) : m_wnd(NULL) { - m_accel.load(p_instance,p_accel); - } + message_filter_impl_accel(HINSTANCE p_instance,const TCHAR * p_accel); void set_wnd(HWND p_wnd) {m_wnd = p_wnd;} private: - HWND m_wnd; + HWND m_wnd = NULL; win32_accelerator m_accel; PFC_CLASS_NOT_COPYABLE(message_filter_impl_accel,message_filter_impl_accel); @@ -69,19 +66,13 @@ class message_filter_impl_accel : public message_filter_impl_base { class message_filter_remap_f1 : public message_filter_impl_base { public: - bool pretranslate_message(MSG * p_msg) { - if (IsOurMsg(p_msg) && m_wnd != NULL && GetActiveWindow() == m_wnd) { - ::PostMessage(m_wnd, WM_SYSCOMMAND, SC_CONTEXTHELP, -1); - return true; - } - return false; - } + bool pretranslate_message(MSG * p_msg) override; void set_wnd(HWND wnd) {m_wnd = wnd;} private: static bool IsOurMsg(const MSG * msg) { return msg->message == WM_KEYDOWN && msg->wParam == VK_F1; } - HWND m_wnd; + HWND m_wnd = NULL; }; class idle_handler_impl_base : public idle_handler { diff --git a/sdk/foobar2000/SDK/metadb.cpp b/sdk/foobar2000/SDK/metadb.cpp index bea4b5e..fce8cd4 100644 --- a/sdk/foobar2000/SDK/metadb.cpp +++ b/sdk/foobar2000/SDK/metadb.cpp @@ -80,6 +80,7 @@ void metadb_io_v2::update_info_async_simple(const pfc::list_base_const_ton_files_rechaptered(local); @@ -115,23 +117,34 @@ metadb_hint_list_v3::ptr metadb_hint_list_v3::create() { return ret; } +metadb_hint_list_v4::ptr metadb_hint_list_v4::create() { + metadb_hint_list_v4::ptr ret; + ret ^= metadb_hint_list::create(); + return ret; +} + void metadb_io_callback_dynamic::register_callback() { + PFC_ASSERT(core_api::is_main_thread()); metadb_io_v3::get()->register_callback(this); } void metadb_io_callback_dynamic::unregister_callback() { + PFC_ASSERT(core_api::is_main_thread()); metadb_io_v3::get()->unregister_callback(this); } void metadb_io_callback_v2_dynamic::register_callback() { + PFC_ASSERT(core_api::is_main_thread()); metadb_io_v5::get()->register_callback_v2(this); } void metadb_io_callback_v2_dynamic::unregister_callback() { + PFC_ASSERT(core_api::is_main_thread()); metadb_io_v5::get()->unregister_callback_v2(this); } bool metadb_io_callback_v2_dynamic::try_register_callback() { + PFC_ASSERT(core_api::is_main_thread()); auto api = metadb_io_v5::tryGet(); if (api.is_empty()) return false; api->register_callback_v2(this); @@ -139,6 +152,23 @@ bool metadb_io_callback_v2_dynamic::try_register_callback() { } void metadb_io_callback_v2_dynamic::try_unregister_callback() { + PFC_ASSERT(core_api::is_main_thread()); auto api = metadb_io_v5::tryGet(); if (api.is_valid()) api->unregister_callback_v2(this); } + +metadb_io_callback_dynamic_impl_base::metadb_io_callback_dynamic_impl_base() { + register_callback(); +} + +metadb_io_callback_dynamic_impl_base::~metadb_io_callback_dynamic_impl_base() { + unregister_callback(); +} + +metadb_io_callback_v2_dynamic_impl_base::metadb_io_callback_v2_dynamic_impl_base() { + register_callback(); +} + +metadb_io_callback_v2_dynamic_impl_base::~metadb_io_callback_v2_dynamic_impl_base() { + unregister_callback(); +} diff --git a/sdk/foobar2000/SDK/metadb.h b/sdk/foobar2000/SDK/metadb.h index 5d5f5d4..f3411ab 100644 --- a/sdk/foobar2000/SDK/metadb.h +++ b/sdk/foobar2000/SDK/metadb.h @@ -141,6 +141,18 @@ class NOVTABLE metadb_hint_list_v3 : public metadb_hint_list_v2 { virtual void add_hint_forced_reader(const char * p_path,service_ptr_t const & p_reader,abort_callback & p_abort) = 0; }; +//! \since 2.0 +//! Allows dispatching of metadb_io_edit_callback from your code. +class NOVTABLE metadb_hint_list_v4 : public metadb_hint_list_v3 { + FB2K_MAKE_SERVICE_INTERFACE( metadb_hint_list_v4, metadb_hint_list_v3 ); +public: + static metadb_hint_list_v4::ptr create(); + + virtual void before_edit( const char * path, service_ptr_t reader, abort_callback & a ) = 0; + virtual void after_edit( const char * path, service_ptr_t reader, abort_callback & a ) = 0; + +}; + //! New in 0.9.3. Extends metadb_io functionality with nonblocking versions of tag read/write functions, and some other utility features. class NOVTABLE metadb_io_v2 : public metadb_io { @@ -168,23 +180,27 @@ class NOVTABLE metadb_io_v2 : public metadb_io { op_flag_detect_rechapter = 1 << 5, }; - //! Preloads information from the specified tracks. + //! Preloads information from the specified tracks. \n + //! Use from main thread only (starts a threaded_process to show a progress dialog). //! @param p_list List of items to process. //! @param p_op_flags Can be null, or one or more of op_flag_* enum values combined, altering behaviors of the operation. //! @param p_notify Called when the task is completed. Status code is one of t_load_info_state values. Can be null if caller doesn't care. virtual void load_info_async(metadb_handle_list_cref p_list,t_load_info_type p_type,fb2k::hwnd_t p_parent_window,t_uint32 p_op_flags,completion_notify_ptr p_notify) = 0; - //! Updates tags of the specified tracks. + //! Updates tags of the specified tracks. \n + //! Use from main thread only (starts a threaded_process to show a progress dialog). //! @param p_list List of items to process. //! @param p_op_flags Can be null, or one or more of op_flag_* enum values combined, altering behaviors of the operation. //! @param p_notify Called when the task is completed. Status code is one of t_update_info values. Can be null if caller doesn't care. //! @param p_filter Callback handling actual file_info alterations. Typically used to replace entire meta part of file_info, or to alter something else such as ReplayGain while leaving meta intact. virtual void update_info_async(metadb_handle_list_cref p_list,service_ptr_t p_filter,fb2k::hwnd_t p_parent_window,t_uint32 p_op_flags,completion_notify_ptr p_notify) = 0; - //! Rewrites tags of the specified tracks; similar to update_info_async() but using last known/cached file_info values rather than values passed by caller. + //! Rewrites tags of the specified tracks; similar to update_info_async() but using last known/cached file_info values rather than values passed by caller. \n + //! Use from main thread only (starts a threaded_process to show a progress dialog). //! @param p_list List of items to process. //! @param p_op_flags Can be null, or one or more of op_flag_* enum values combined, altering behaviors of the operation. //! @param p_notify Called when the task is completed. Status code is one of t_update_info values. Can be null if caller doesn't care. virtual void rewrite_info_async(metadb_handle_list_cref p_list,fb2k::hwnd_t p_parent_window,t_uint32 p_op_flags,completion_notify_ptr p_notify) = 0; - //! Strips all tags / metadata fields from the specified tracks. + //! Strips all tags / metadata fields from the specified tracks. \n + //! Use from main thread only (starts a threaded_process to show a progress dialog). //! @param p_list List of items to process. //! @param p_op_flags Can be null, or one or more of op_flag_* enum values combined, altering behaviors of the operation. //! @param p_notify Called when the task is completed. Status code is one of t_update_info values. Can be null if caller doesn't care. @@ -194,7 +210,8 @@ class NOVTABLE metadb_io_v2 : public metadb_io { //! Contrary to other metadb_io methods, this can be safely called in a worker thread. You only need to call the hint list's on_done() method in main thread to finalize. virtual metadb_hint_list::ptr create_hint_list() = 0; - //! Updates tags of the specified tracks. Helper; uses update_info_async internally. + //! Updates tags of the specified tracks. Helper; uses update_info_async internally. \n + //! Use from main thread only (starts a threaded_process to show a progress dialog). //! @param p_list List of items to process. //! @param p_op_flags Can be null, or one or more of op_flag_* enum values combined, altering behaviors of the operation. //! @param p_notify Called when the task is completed. Status code is one of t_update_info values. Can be null if caller doesn't care. @@ -202,10 +219,12 @@ class NOVTABLE metadb_io_v2 : public metadb_io { void update_info_async_simple(metadb_handle_list_cref p_list,const pfc::list_base_const_t & p_new_info, fb2k::hwnd_t p_parent_window,t_uint32 p_op_flags,completion_notify_ptr p_notify); //! Helper to be called after a file has been rechaptered. \n - //! Forcibly reloads info then tells playlist_manager to update all affected playlists. + //! Forcibly reloads info then tells playlist_manager to update all affected playlists. \n + //! Call from main thread only. void on_file_rechaptered( const char * path, metadb_handle_list_cref newItems ); //! Helper to be called after a file has been rechaptered. \n - //! Forcibly reloads info then tells playlist_manager to update all affected playlists. + //! Forcibly reloads info then tells playlist_manager to update all affected playlists. \n + //! Call from main thread only. void on_files_rechaptered( metadb_handle_list_cref newHandles ); FB2K_MAKE_SERVICE_COREAPI_EXTENSION(metadb_io_v2,metadb_io); @@ -215,7 +234,13 @@ class NOVTABLE metadb_io_v2 : public metadb_io { //! \since 0.9.5 class NOVTABLE metadb_io_v3 : public metadb_io_v2 { public: + //! Registers a callback object to receive notifications about metadb_io operations. \n + //! See: metadb_io_callback_dynamic \n + //! Call from main thread only. virtual void register_callback(metadb_io_callback_dynamic * p_callback) = 0; + //! Unregisters a callback object to receive notifications about metadb_io operations. \n + //! See: metadb_io_callback_dynamic \n + //! Call from main thread only. virtual void unregister_callback(metadb_io_callback_dynamic * p_callback) = 0; FB2K_MAKE_SERVICE_COREAPI_EXTENSION(metadb_io_v3,metadb_io_v2); @@ -230,15 +255,18 @@ class NOVTABLE metadb_io_v4 : public metadb_io_v3 { public: //! Creates an update-info task, that can be either fed to threaded_process API, or invoked by yourself respecting threaded_process semantics. \n //! May return null pointer if the operation has been refused (by user settings or such). \n - //! Useful for performing the operation with your own in-dialog progress display instead of the generic progress popup. + //! Useful for performing the operation with your own in-dialog progress display instead of the generic progress popup. \n + //! Main thread only. virtual service_ptr_t spawn_update_info( metadb_handle_list_cref items, service_ptr_t p_filter, uint32_t opFlags, completion_notify_ptr reply ) = 0; //! Creates an remove-info task, that can be either fed to threaded_process API, or invoked by yourself respecting threaded_process semantics. \n //! May return null pointer if the operation has been refused (by user settings or such). \n - //! Useful for performing the operation with your own in-dialog progress display instead of the generic progress popup. + //! Useful for performing the operation with your own in-dialog progress display instead of the generic progress popup. \n + //! Main thread only. virtual service_ptr_t spawn_remove_info( metadb_handle_list_cref items, uint32_t opFlags, completion_notify_ptr reply) = 0; //! Creates an load-info task, that can be either fed to threaded_process API, or invoked by yourself respecting threaded_process semantics. \n //! May return null pointer if the operation has been refused (for an example no loading is needed for these items). \n - //! Useful for performing the operation with your own in-dialog progress display instead of the generic progress popup. + //! Useful for performing the operation with your own in-dialog progress display instead of the generic progress popup. \n + //! Main thread only. virtual service_ptr_t spawn_load_info( metadb_handle_list_cref items, t_load_info_type opType, uint32_t opFlags, completion_notify_ptr reply) = 0; }; @@ -246,7 +274,11 @@ class NOVTABLE metadb_io_v4 : public metadb_io_v3 { class NOVTABLE metadb_io_v5 : public metadb_io_v4 { FB2K_MAKE_SERVICE_COREAPI_EXTENSION(metadb_io_v5, metadb_io_v4); public: + //! Register a metadb_io_callback_v2_dynamic object to receive notifications about metadb_io events. \n + //! Main thread only. virtual void register_callback_v2(metadb_io_callback_v2_dynamic*) = 0; + //! Unregister a metadb_io_callback_v2_dynamic object. \n + //! Main thread only. virtual void unregister_callback_v2(metadb_io_callback_v2_dynamic*) = 0; }; @@ -346,7 +378,7 @@ class metadb_v2 : public metadb { void queryMultiParallel_(metadb_handle_list_cref items, std::function< void(size_t, const rec_t&) > f) { class qmc_impl : public queryMultiParallelCallback_t { public: - void onInfo(size_t idx, const rec_t& rec, void* ctx) override {m_f(idx, rec);} + void onInfo(size_t idx, const rec_t& rec, void*) override {m_f(idx, rec);} decltype(f) m_f; }; diff --git a/sdk/foobar2000/SDK/metadb_callbacks.h b/sdk/foobar2000/SDK/metadb_callbacks.h index d14024c..ad833c6 100644 --- a/sdk/foobar2000/SDK/metadb_callbacks.h +++ b/sdk/foobar2000/SDK/metadb_callbacks.h @@ -1,9 +1,11 @@ #pragma once +#include "callback_merit.h" //! Callback service receiving notifications about metadb contents changes. class NOVTABLE metadb_io_callback : public service_base { public: - //! Called when metadb contents change. (Or, one of display hook component requests display update). + //! Called when metadb contents change. (Or, one of display hook component requests display update). \n + //! Main thread only. //! @param p_items_sorted List of items that have been updated. The list is always sorted by pointer value, to allow fast bsearch to test whether specific item has changed. //! @param p_fromhook Set to true when actual file contents haven't changed but one of metadb_display_field_provider implementations requested an update so output of metadb_handle::format_title() etc has changed. virtual void on_changed_sorted(metadb_handle_list_cref p_items_sorted, bool p_fromhook) = 0; @@ -14,6 +16,7 @@ class NOVTABLE metadb_io_callback : public service_base { //! Dynamically-registered version of metadb_io_callback. See metadb_io_callback for documentation, register instances using metadb_io_v3::register_callback(). It's recommended that you use the metadb_io_callback_dynamic_impl_base helper class to manage registration/unregistration. class NOVTABLE metadb_io_callback_dynamic { public: + //! See metadb_io_callback::on_changed_sorted() virtual void on_changed_sorted(metadb_handle_list_cref p_items_sorted, bool p_fromhook) = 0; void register_callback(); void unregister_callback(); @@ -23,10 +26,10 @@ class NOVTABLE metadb_io_callback_dynamic { //! metadb_io_callback_dynamic implementation helper. class metadb_io_callback_dynamic_impl_base : public metadb_io_callback_dynamic { public: - void on_changed_sorted(metadb_handle_list_cref p_items_sorted, bool p_fromhook) override {} + void on_changed_sorted(metadb_handle_list_cref p_items_sorted, bool p_fromhook) override { (void)p_items_sorted; (void)p_fromhook; } - metadb_io_callback_dynamic_impl_base() { register_callback(); } - ~metadb_io_callback_dynamic_impl_base() { unregister_callback(); } + metadb_io_callback_dynamic_impl_base(); + ~metadb_io_callback_dynamic_impl_base(); PFC_CLASS_NOT_COPYABLE_EX(metadb_io_callback_dynamic_impl_base) }; @@ -42,6 +45,15 @@ class NOVTABLE metadb_io_edit_callback : public service_base { virtual void on_edited(metadb_handle_list_cref items, t_infosref before, t_infosref after) = 0; }; +//! \since 2.0 +class NOVTABLE metadb_io_edit_callback_v2 : public metadb_io_edit_callback { + FB2K_MAKE_SERVICE_INTERFACE(metadb_io_edit_callback_v2, metadb_io_edit_callback) +public: + //! With original on_edited(), the implementation could not tell what the info in metadb was before, 'before' parameter being actual infos freshly read from the file prior to writing. \n + //! on_edited_v2() clarifies this, additional argument passes old metadb state to deal with cases where it was different than file contents. + virtual void on_edited_v2(metadb_handle_list_cref items, t_infosref before, t_infosref after, t_infosref beforeInMetadb) = 0; +}; + //! \since 2.0 //! Parameter for on_changed_sorted_v2() class NOVTABLE metadb_io_callback_v2_data { @@ -51,20 +63,32 @@ class NOVTABLE metadb_io_callback_v2_data { }; //! \since 2.0 +//! Extended version of metadb_io_callback. class NOVTABLE metadb_io_callback_v2 : public metadb_io_callback { FB2K_MAKE_SERVICE_INTERFACE(metadb_io_callback_v2, metadb_io_callback); public: virtual void on_changed_sorted_v2(metadb_handle_list_cref itemsSorted, metadb_io_callback_v2_data & data, bool bFromHook) = 0; - //! Reserved for future use. - virtual double get_callback_merit() { return 0; } + //! Controls callback merit, see: fb2k::callback_merit_t + virtual fb2k::callback_merit_t get_callback_merit() { return fb2k::callback_merit_default; } }; +//! \since 2.0 +//! NEW interface introduced in late 2.0. \n +//! Invoked *BEFORE* actual update, with incoming info. \n +//! Note that incoming info may be partial (either main info or browse info not set), in such cases the info will remain unchanged. +class NOVTABLE metadb_pre_update_callback : public service_base { + FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT( metadb_pre_update_callback ); +public: + virtual void will_update( metadb_handle_list_cref itemsSorted, metadb_io_callback_v2_data & data) = 0; +}; +//! \since 2.0 +//! Extended version of metadb_io_callback_dynamic. class NOVTABLE metadb_io_callback_v2_dynamic { public: virtual void on_changed_sorted_v2(metadb_handle_list_cref itemsSorted, metadb_io_callback_v2_data & data, bool bFromHook) = 0; - //! Reserved for future use. - virtual double get_callback_merit() { return 0; } + //! Controls callback merit, see: fb2k::callback_merit_t + virtual fb2k::callback_merit_t get_callback_merit() { return fb2k::callback_merit_default; } bool try_register_callback(); void try_unregister_callback(); void register_callback(); void unregister_callback(); @@ -72,10 +96,10 @@ class NOVTABLE metadb_io_callback_v2_dynamic { class metadb_io_callback_v2_dynamic_impl_base : public metadb_io_callback_v2_dynamic { public: - void on_changed_sorted_v2(metadb_handle_list_cref itemsSorted, metadb_io_callback_v2_data & data, bool bFromHook) override {} + void on_changed_sorted_v2(metadb_handle_list_cref itemsSorted, metadb_io_callback_v2_data& data, bool bFromHook) override { (void)itemsSorted; (void)data; (void)bFromHook; } - metadb_io_callback_v2_dynamic_impl_base() { register_callback(); } - ~metadb_io_callback_v2_dynamic_impl_base() { unregister_callback(); } + metadb_io_callback_v2_dynamic_impl_base(); + ~metadb_io_callback_v2_dynamic_impl_base(); PFC_CLASS_NOT_COPYABLE_EX(metadb_io_callback_v2_dynamic_impl_base) }; diff --git a/sdk/foobar2000/SDK/metadb_handle.h b/sdk/foobar2000/SDK/metadb_handle.h index 45f35d8..6479472 100644 --- a/sdk/foobar2000/SDK/metadb_handle.h +++ b/sdk/foobar2000/SDK/metadb_handle.h @@ -185,6 +185,9 @@ class metadb_handle_v2 : public metadb_handle { virtual void formatTitle_v2(const rec_t& rec, titleformat_hook* p_hook, pfc::string_base& p_out, const service_ptr_t& p_script, titleformat_text_filter* p_filter) = 0; }; +typedef pfc::list_base_t* metadb_handle_list_ptr; +typedef pfc::list_base_const_t const * metadb_handle_list_cptr; + typedef pfc::list_base_t & metadb_handle_list_ref; typedef pfc::list_base_const_t const & metadb_handle_list_cref; @@ -205,6 +208,7 @@ namespace metadb_handle_list_helper { t_size bsearch_by_pointer(const pfc::list_base_const_t & p_list,const metadb_handle_ptr & val); double calc_total_duration(metadb_handle_list_cref p_list); + pfc::string8 format_total_size(metadb_handle_list_cref p_list); //! New method to deal with slower metadb in foobar2000 v2 double calc_total_duration_v2(metadb_handle_list_cref p_list, unsigned maxThreads, abort_callback & aborter); @@ -221,6 +225,16 @@ namespace metadb_handle_list_helper { void sort_by_format_get_order_v2( metadb_handle_list_cref p_list, size_t * order, const service_ptr_t & script, titleformat_hook * hook, int direction, abort_callback & aborter ); void sort_by_format_v2(metadb_handle_list_ref p_list, const service_ptr_t & script, titleformat_hook * hook, int direction, abort_callback & aborter); + struct sorter_t { + service_ptr_t < titleformat_object > obj; + int direction = 1; + titleformat_hook* hook = nullptr; + }; + + //! Late-2023 addition (new fb2k not required) \n + //! Multilayer stablesort using single info query pass, with multiple sort objects that can have different directions. + //! @param inOutOrder input & output order, please set to a valid permutration (such as identity) on input. + void sort_by_format_get_order_v3(metadb_handle_list_cref p_list, size_t* inOutOrder, sorter_t const * sorters, size_t nSorters, abort_callback& aborter); }; template class t_alloc = pfc::alloc_fast > @@ -258,6 +272,7 @@ class metadb_handle_list_t : public service_list_t { inline t_size bsearch_by_pointer(const metadb_handle_ptr & val) const {return metadb_handle_list_helper::bsearch_by_pointer(*this,val);} inline double calc_total_duration() const {return metadb_handle_list_helper::calc_total_duration(*this);} + pfc::string8 format_total_size() const { return metadb_handle_list_helper::format_total_size(*this); } inline void sort_by_path() {metadb_handle_list_helper::sort_by_path(*this);} diff --git a/sdk/foobar2000/SDK/metadb_handle_list.cpp b/sdk/foobar2000/SDK/metadb_handle_list.cpp index c9c6c8e..a3899bd 100644 --- a/sdk/foobar2000/SDK/metadb_handle_list.cpp +++ b/sdk/foobar2000/SDK/metadb_handle_list.cpp @@ -9,24 +9,35 @@ namespace { + struct custom_sort_data_multi { + static constexpr unsigned numLocal = 4; + void setup(size_t count) { + if (count > numLocal) texts2 = std::make_unique< fb2k::sortString_t[] >( count - numLocal ); + } + fb2k::sortString_t& operator[] (size_t which) { + return which < numLocal ? texts1[which] : texts2[which - numLocal]; + } + const fb2k::sortString_t& operator[] (size_t which) const { + return which < numLocal ? texts1[which] : texts2[which - numLocal]; + } + + fb2k::sortString_t texts1[numLocal]; + std::unique_ptr< fb2k::sortString_t[] > texts2; + size_t index; + }; struct custom_sort_data { fb2k::sortString_t text; - t_size index; + size_t index; }; -} + template + static int custom_sort_compare(const custom_sort_data& elem1, const custom_sort_data& elem2) { + int ret = direction * fb2k::sortStringCompare(elem1.text, elem2.text); + if (ret == 0) ret = pfc::sgn_t((t_ssize)elem1.index - (t_ssize)elem2.index); + return ret; + } -template -static int custom_sort_compare(const custom_sort_data & elem1, const custom_sort_data & elem2 ) { - int ret = direction * fb2k::sortStringCompare(elem1.text,elem2.text); - if (ret == 0) ret = pfc::sgn_t((t_ssize)elem1.index - (t_ssize)elem2.index); - return ret; } - -template -static int _custom_sort_compare(const void * v1, const void * v2) { - return custom_sort_compare(*reinterpret_cast(v1),*reinterpret_cast(v2)); -} void metadb_handle_list_helper::sort_by_format(metadb_handle_list_ref p_list,const char * spec,titleformat_hook * p_hook) { service_ptr_t script; @@ -56,11 +67,11 @@ namespace { tfhook_sort() { m_API->seed(); } - bool process_field(titleformat_text_out * p_out,const char * p_name,t_size p_name_length,bool & p_found_flag) { + bool process_field(titleformat_text_out *,const char *,t_size,bool &) override { return false; } - bool process_function(titleformat_text_out * p_out,const char * p_name,t_size p_name_length,titleformat_hook_function_params * p_params,bool & p_found_flag) { - if (stricmp_utf8_ex(p_name, p_name_length, "rand", ~0) == 0) { + bool process_function(titleformat_text_out * p_out,const char * p_name,t_size p_name_length,titleformat_hook_function_params * p_params,bool & p_found_flag) override { + if (stricmp_utf8_ex(p_name, p_name_length, "rand", SIZE_MAX) == 0) { t_size param_count = p_params->get_param_count(); t_uint32 val; if (param_count == 1) { @@ -155,13 +166,12 @@ void metadb_handle_list_helper::remove_duplicates(metadb_handle_list_ref p_list) void metadb_handle_list_helper::sort_by_pointer_remove_duplicates(metadb_handle_list_ref p_list) { - t_size count = p_list.get_count(); + const t_size count = p_list.get_count(); if (count>0) { sort_by_pointer(p_list); bool b_found = false; - t_size n; - for(n=0;n,val,blah)) return blah; - else return ~0; + else return SIZE_MAX; } @@ -274,29 +283,40 @@ double metadb_handle_list_helper::calc_total_duration_v2(metadb_handle_list_cref return ret; } - - pfc::refcounter walk = 0, walkSums = 0; pfc::array_t sums; sums.resize(numThreads); sums.fill_null(); - auto worker = [&] { - double ret = 0; - for(;;) { - size_t idx = walk++; - if (idx >= count || aborter.is_set()) break; + { + pfc::refcounter walk = 0, walkSums = 0; - double temp = p_list.get_item(idx)->get_length(); - if (temp > 0) ret += temp; - } - sums[walkSums++] = ret; - }; + auto worker = [&] { + double ret = 0; + for (;;) { + size_t idx = walk++; + if (idx >= count || aborter.is_set()) break; - fb2k::cpuThreadPool::runMultiHelper(worker, numThreads); + double temp = p_list.get_item(idx)->get_length(); + if (temp > 0) ret += temp; + } + sums[walkSums++] = ret; + }; + + fb2k::cpuThreadPool::runMultiHelper(worker, numThreads); + } aborter.check(); double ret = 0; for (size_t walk = 0; walk < numThreads; ++walk) ret += sums[walk]; return ret; } +pfc::string8 metadb_handle_list_helper::format_total_size(metadb_handle_list_cref p_list) { + pfc::string8 temp; + bool unknown = false; + t_filesize val = metadb_handle_list_helper::calc_total_size_ex(p_list,unknown); + if (unknown) temp << "> "; + temp << pfc::format_file_size_short(val); + return temp; +} + double metadb_handle_list_helper::calc_total_duration(metadb_handle_list_cref p_list) { double ret = 0; @@ -319,14 +339,34 @@ void metadb_handle_list_helper::sort_by_format_v2(metadb_handle_list_ref p_list, } void metadb_handle_list_helper::sort_by_format_get_order_v2(metadb_handle_list_cref p_list, size_t * order, const service_ptr_t & p_script, titleformat_hook * p_hook, int p_direction, abort_callback & aborter) { + sorter_t s = { p_script, p_direction, p_hook }; + size_t total = p_list.get_count(); + for (size_t walk = 0; walk < total; ++walk) order[walk] = walk; + sort_by_format_get_order_v3(p_list, order, &s, 1, aborter); +} + +void metadb_handle_list_helper::sort_by_format_get_order_v3(metadb_handle_list_cref p_list, size_t* order,sorter_t const* sorters, size_t nSorters, abort_callback& aborter) { // pfc::hires_timer timer; timer.start(); + typedef custom_sort_data_multi data_t; + const t_size count = p_list.get_count(); if (count == 0) return; - auto data = std::make_unique< custom_sort_data[] >(count); + + PFC_ASSERT(pfc::permutation_is_valid(order, count)); + + auto data = std::make_unique< data_t[] >(count); #if FOOBAR2000_TARGET_VERSION >= 81 - if ( p_script->requires_metadb_info_()) { + bool need_info = false; + for (size_t iSorter = 0; iSorter < nSorters; ++iSorter) { + auto& s = sorters[iSorter]; + PFC_ASSERT(s.direction == -1 || s.direction == 1); + if (s.obj->requires_metadb_info_()) { + need_info = true; break; + } + } + if (need_info) { // FB2K_console_formatter() << "sorting with queryMultiParallelEx_<>"; struct qmpc_context { qmpc_context() { @@ -337,14 +377,22 @@ void metadb_handle_list_helper::sort_by_format_get_order_v2(metadb_handle_list_c }; metadb_v2::get()->queryMultiParallelEx_< qmpc_context >(p_list, [&](size_t idx, metadb_v2::rec_t const& rec, qmpc_context& ctx) { aborter.check(); - if (p_hook) { - titleformat_hook_impl_splitter hookSplitter(&ctx.myHook, p_hook); - p_list[idx]->formatTitle_v2_(rec, &hookSplitter, ctx.temp, p_script, nullptr); - } else { - p_list[idx]->formatTitle_v2_(rec, &ctx.myHook, ctx.temp, p_script, nullptr); + auto& out = data[idx]; + out.setup(nSorters); + out.index = order[idx]; + + auto h = p_list[idx]; + + for (size_t iSorter = 0; iSorter < nSorters; ++iSorter) { + auto& s = sorters[iSorter]; + if (s.hook) { + titleformat_hook_impl_splitter hookSplitter(&ctx.myHook, s.hook); + h->formatTitle_v2_(rec, &hookSplitter, ctx.temp, s.obj, nullptr); + } else { + h->formatTitle_v2_(rec, &ctx.myHook, ctx.temp, s.obj, nullptr); + } + out[iSorter] = fb2k::makeSortString(ctx.temp); } - data[idx].index = idx; - data[idx].text = fb2k::makeSortString(ctx.temp); }); } else { // FB2K_console_formatter() << "sorting with blank metadb info"; @@ -358,18 +406,25 @@ void metadb_handle_list_helper::sort_by_format_get_order_v2(metadb_handle_list_c aborter.check(); size_t idx = walk++; if (idx >= count) return; - - if (p_hook) { - titleformat_hook_impl_splitter hookSplitter(&myHook, p_hook); - p_list[idx]->formatTitle_v2_(rec, &hookSplitter, temp, p_script, nullptr); - } else { - p_list[idx]->formatTitle_v2_(rec, &myHook, temp, p_script, nullptr); + + auto& out = data[idx]; + out.setup(nSorters); + out.index = order[idx]; + + for (size_t iSorter = 0; iSorter < nSorters; ++iSorter) { + auto& s = sorters[iSorter]; + if (s.hook) { + titleformat_hook_impl_splitter hookSplitter(&myHook, s.hook); + p_list[idx]->formatTitle_v2_(rec, &hookSplitter, temp, s.obj, nullptr); + } else { + p_list[idx]->formatTitle_v2_(rec, &myHook, temp, s.obj, nullptr); + } + + out[iSorter] = fb2k::makeSortString(temp); } - data[idx].index = idx; - data[idx].text = fb2k::makeSortString(temp); } - }, api->numRunsSanity( (count + 1999)/2000 ) ); - + }, api->numRunsSanity((count + 1999) / 2000)); + } #else { @@ -377,17 +432,27 @@ void metadb_handle_list_helper::sort_by_format_get_order_v2(metadb_handle_list_c auto work = [&] { tfhook_sort myHook; - titleformat_hook_impl_splitter hookSplitter(&myHook, p_hook); - titleformat_hook * const hookPtr = p_hook ? pfc::implicit_cast(&hookSplitter) : &myHook; pfc::string8_fastalloc temp; temp.prealloc(512); - const t_size total = p_list.get_size(); - for ( ;; ) { + for (;; ) { const t_size index = (counter)++; - if (index >= total || aborter.is_set()) break; - data[index].index = index; - p_list[index]->format_title(hookPtr, temp, p_script, 0); - data[index].text = fb2k::makeSortString(temp); + if (index >= count || aborter.is_set()) break; + + auto& out = data[index]; + out.setup(nSorters); + out.index = order[index]; + + for (size_t iSorter = 0; iSorter < nSorters; ++iSorter) { + auto& s = sorters[iSorter]; + if (s.hook) { + titleformat_hook_impl_splitter hookSplitter(&myHook, s.hook); + p_list[index]->format_title(&hookSplitter, temp, s.obj, 0); + } else { + p_list[index]->format_title(&myHook, temp, s.obj, 0); + } + + out[iSorter] = fb2k::makeSortString(temp); + } } }; @@ -402,30 +467,37 @@ void metadb_handle_list_helper::sort_by_format_get_order_v2(metadb_handle_list_c aborter.check(); // console::formatter() << "metadb_handle sort: prepared in " << pfc::format_time_ex(timer.query(),6); - + { + auto compare = [&](data_t const& elem1, data_t const& elem2) -> int { + for (size_t iSorter = 0; iSorter < nSorters; ++iSorter) { + int v = fb2k::sortStringCompare(elem1[iSorter], elem2[iSorter]); + if (v) return v * sorters[iSorter].direction; + } + + return pfc::sgn_t((t_ssize)elem1.index - (t_ssize)elem2.index); + }; + typedef decltype(data) container_t; - auto compare = p_direction > 0 ? custom_sort_compare<1> : custom_sort_compare<-1>; typedef decltype(compare) compare_t; pfc::sort_callback_impl_simple_wrap_t cb(data, compare); - - //pfc::sort_t(data, p_direction > 0 ? custom_sort_compare<1> : custom_sort_compare<-1>, count); - size_t concurrency = pfc::getOptimalWorkerThreadCountEx( count / 4096 ); - fb2k::sort( cb, count, concurrency, aborter ); + size_t concurrency = pfc::getOptimalWorkerThreadCountEx(count / 4096); + fb2k::sort(cb, count, concurrency, aborter); } - + //qsort(data.get_ptr(),count,sizeof(custom_sort_data),p_direction > 0 ? _custom_sort_compare<1> : _custom_sort_compare<-1>); // console::formatter() << "metadb_handle sort: sorted in " << pfc::format_time_ex(timer.query(),6); - for (t_size n = 0; nprocess_samples_v2(c); + this->process_samples(c); + return c.get_sample_count(); +} + +void output_impl::on_flush_internal() { + m_eos = false; m_sent_force_play = false; m_incoming_ptr = 0; m_incoming.set_size(0); +} + +void output_impl::flush() { + on_flush_internal(); on_flush(); } + void output_impl::flush_changing_track() { - m_incoming_ptr = 0; - m_incoming.set_size(0); + on_flush_internal(); on_flush_changing_track(); } @@ -84,28 +96,49 @@ void output_impl::update(bool & p_ready) { p_ready = update_v2() > 0; } size_t output_impl::update_v2() { + + // Clear preemptively + m_can_write = 0; + on_update(); - if (m_incoming_spec != m_active_spec && m_incoming_ptr < m_incoming.get_size()) { + + // No data yet, nothing to do, want data, can't signal how much because we don't know the format + if (!m_incoming_spec.is_valid()) return SIZE_MAX; + + // First chunk in or format change + if (m_incoming_spec != m_active_spec) { if (get_latency_samples() == 0) { + // Ready for new format + m_sent_force_play = false; open(m_incoming_spec); m_active_spec = m_incoming_spec; } else { - force_play(); + // Previous format still playing, accept no more data + this->send_force_play(); + return 0; } } - size_t retCanWriteSamples = 0; - if (m_incoming_spec == m_active_spec && m_incoming_ptr < m_incoming.get_size()) { - t_size cw = can_write_samples() * m_incoming_spec.chanCount; - t_size delta = pfc::min_t(m_incoming.get_size() - m_incoming_ptr,cw); + + // opened for m_incoming_spec stream + + // Store & update m_can_write on our end + // We don't know what can_write_samples() actually does, could be expensive, avoid calling it repeatedly + m_can_write = this->can_write_samples(); + + if (m_incoming_ptr < m_incoming.get_size()) { + t_size delta = pfc::min_t(m_incoming.get_size() - m_incoming_ptr, m_can_write * m_incoming_spec.chanCount); if (delta > 0) { - write(audio_chunk_temp_impl(m_incoming.get_ptr()+m_incoming_ptr,delta / m_incoming_spec.chanCount,m_incoming_spec.sampleRate,m_incoming_spec.chanCount,m_incoming_spec.chanMask)); + PFC_ASSERT(!m_sent_force_play); + write(audio_chunk_temp_impl(m_incoming.get_ptr() + m_incoming_ptr, delta / m_incoming_spec.chanCount, m_incoming_spec.sampleRate, m_incoming_spec.chanCount, m_incoming_spec.chanMask)); m_incoming_ptr += delta; + if (m_eos && this->queue_empty()) { + this->send_force_play(); + } } - retCanWriteSamples = (cw - delta) / m_incoming_spec.chanCount; - } else if ( m_incoming_ptr == m_incoming.get_size() ) { - retCanWriteSamples = SIZE_MAX; - } - return retCanWriteSamples; + + m_can_write -= delta / m_incoming_spec.chanCount; + } + return m_can_write; } double output_impl::get_latency() { @@ -118,34 +151,62 @@ double output_impl::get_latency() { } return ret; } -void output_impl::process_samples(const audio_chunk & p_chunk) { - pfc::dynamic_assert(m_incoming_ptr == m_incoming.get_size()); - auto spec = p_chunk.get_spec(); + +void output_impl::force_play() { + if ( m_eos ) return; + m_eos = true; + if (queue_empty()) send_force_play(); +} +void output_impl::send_force_play() { + if (m_sent_force_play) return; + m_sent_force_play = true; + this->on_force_play(); +} + +static void spec_sanity(audio_chunk::spec_t const& spec) { if (!spec.is_valid()) pfc::throw_exception_with_message< exception_io_data >("Invalid audio stream specifications"); - m_incoming_spec = spec; - t_size length = p_chunk.get_used_size(); - m_incoming.set_data_fromptr(p_chunk.get_data(),length); +} + +size_t output_impl::process_samples_v2(const audio_chunk& p_chunk) { + PFC_ASSERT(queue_empty()); + PFC_ASSERT(!m_eos); + const auto spec = p_chunk.get_spec(); + if (m_incoming_spec != spec) { + spec_sanity(spec); + m_incoming_spec = spec; + return 0; + } + + auto in = p_chunk.get_sample_count(); + if (in > m_can_write) in = m_can_write; + if (in > 0) { + write(audio_chunk_partial_ref(p_chunk, 0, in)); + m_can_write -= in; + } + return in; +} + +void output_impl::process_samples(const audio_chunk & p_chunk) { + PFC_ASSERT(queue_empty()); + PFC_ASSERT( !m_eos ); + const auto spec = p_chunk.get_spec(); + size_t taken = 0; + if (m_incoming_spec == spec) { + // Try bypassing intermediate buffer + taken = this->process_samples_v2(p_chunk); + if (taken == p_chunk.get_sample_count()) return; // all written, success + taken *= spec.chanCount; + } else { + spec_sanity(spec); + m_incoming_spec = spec; + } + // Queue what's left for update() to eat later + m_incoming.set_data_fromptr(p_chunk.get_data() + taken, p_chunk.get_used_size() - taken); m_incoming_ptr = 0; } void output_v3::get_injected_dsps( dsp_chain_config & dsps ) { dsps.remove_all(); -#if 0 // DEPRECATED - unsigned rate = this->get_forced_sample_rate(); - if (rate != 0) { -#if PFC_DEBUG - FB2K_console_formatter() << "output_v3::get_injected_dsps() : requesting resampling to " << rate << " Hz"; -#endif - dsp_preset_impl temp; - if (resampler_entry::g_create_preset( temp, 0, rate, 0 )) { - dsps.insert_item( temp, dsps.get_count() ); - } else { -#if PFC_DEBUG - FB2K_console_formatter() << "output_v3::get_injected_dsps() : resampler could not be created"; -#endif - } - } -#endif } size_t output_v4::update_v2() { diff --git a/sdk/foobar2000/SDK/output.h b/sdk/foobar2000/SDK/output.h index d7fe1b5..7c00897 100644 --- a/sdk/foobar2000/SDK/output.h +++ b/sdk/foobar2000/SDK/output.h @@ -58,10 +58,12 @@ class NOVTABLE output : public service_base { public: //! Retrieves amount of audio data queued for playback, in seconds. virtual double get_latency() = 0; - //! Sends new samples to the device. Allowed to be called only when update() indicates that the device is ready. - //! update() should be called AGAIN after each process_samples() to know if the device is ready for more. + //! Sends new samples to the device. Allowed to be called only when update() indicates that the device is ready. \n + //! update() should be called AGAIN after each process_samples() to know if the device is ready for more. \n + //! This method SHOULD NOT block, only copy passed chunk and return immediately. virtual void process_samples(const audio_chunk & p_chunk) = 0; - //! Updates playback; queries whether the device is ready to receive new data. + //! Updates playback; queries whether the device is ready to receive new data. \n + //! This method SHOULD NOT block, only update internal state and return immediately. //! @param p_ready On success, receives value indicating whether the device is ready for next process_samples() call. virtual void update(bool & p_ready) = 0; //! Pauses/unpauses playback. @@ -81,6 +83,8 @@ class NOVTABLE output : public service_base { size_t update_v2_(); //! Helper, see output_v4::get_trigger_event() pfc::eventHandle_t get_trigger_event_(); + //! Helper, see output_v6::process_samples_v2() + size_t process_samples_v2_(const audio_chunk&); //! Helper for output_entry implementation. static uint32_t g_extra_flags() { return 0; } @@ -95,7 +99,7 @@ class NOVTABLE output_v2 : public output { //! Obsolete, do not use. virtual void on_track_mark() {} //! Obsolete, do not use. - virtual void enable_fading(bool state) {} + virtual void enable_fading(bool) { } //! Called when flushing due to manual track change rather than seek-within-track virtual void flush_changing_track() {flush();} }; @@ -125,11 +129,12 @@ class NOVTABLE output_v4 : public output_v3 { //! Returns whether the audio stream is currently being played or not. \n //! Typically, for a short period of time, initially sent data is not played until a sufficient amount is queued to initiate playback without glitches. \n //! For old outputs that do not implement this, the value can be assumed to be true. - virtual bool is_progressing() {return true;} + virtual bool is_progressing() = 0; //! Improved version of update(); returns 0 if the output isn't ready to receive any new data, otherwise an advisory number of samples - at the current stream format - that the output expects to take now. \n //! If the caller changes the stream format, the value is irrelevant. \n - //! The output may return SIZE_MAX to indicate that it can take data but does not currently have any hints to tell how much. + //! The output may return SIZE_MAX to indicate that it can take data but does not currently have any hints to tell how much. \n + //! This method SHOULD NOT block, only update output state and return immediately. virtual size_t update_v2(); }; @@ -140,6 +145,25 @@ class output_v5 : public output_v4 { virtual unsigned get_forced_channel_mask() { return 0; } }; +//! \since 2.2 +class output_v6 : public output_v5 { + FB2K_MAKE_SERVICE_INTERFACE(output_v6, output_v5); +public: + //! Extended process_samples(), allowed to read only part of the chunk if out of buffer space to take whole. + //! @returns Number of samples actually taken. + virtual size_t process_samples_v2(const audio_chunk&) = 0; +}; + +//! \since 2.25 +//! foobar2000 v2.25 functionality draft, use only for testing live info delivery in v2.25 beta. +class output_v7 : public output_v6 { + FB2K_MAKE_SERVICE_INTERFACE(output_v7, output_v6); +public: + //! Optional, gets notified about current playback metadata. + //! @param audioSource can be any type (metadb_handle, metadb_info_container, possibly other), use operator &= to determine type. + virtual void hint_source(fb2k::objRef audioSource) { (void)audioSource; } +}; + class NOVTABLE output_entry : public service_base { FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(output_entry); public: @@ -154,7 +178,7 @@ class NOVTABLE output_entry : public service_base { #ifdef _WIN32 //! Obsolete, do not use. - virtual void advanced_settings_popup(HWND p_parent,POINT p_menupoint) {} + virtual void advanced_settings_popup(HWND,POINT) {} #endif enum { @@ -220,34 +244,63 @@ class output_entry_impl_t : public E template class output_factory_t : public service_factory_single_t > {}; -class output_impl : public output_v5 { +//! Helper base class for output implementations. \n +//! This is the preferred way of implementing output. \n +//! This is NOT a public interface and its layout changes between foobar2000 SDK versions, do not assume other outputs to implement it. +class output_impl : public output_v7 { protected: - output_impl() : m_incoming_ptr(0) {} + output_impl() {} + + //! Called periodically. You can update your state in this method. Can do nothing if not needed. virtual void on_update() = 0; - //! Will never get more input than as returned by can_write_samples(). + //! Writes an audio chunk to your output. \n + //! Will never get more than last can_write_samples() asked for. \n + //! Format being send will match last open(). virtual void write(const audio_chunk & p_data) = 0; + //! @returns How many samples write() can take at this moment. \n + //! It's called immediately after on_update() and can reuse value calculated in last on_update(). virtual t_size can_write_samples() = 0; + //! @returns Current latency, delay between last written sample and currently heard audio. virtual t_size get_latency_samples() = 0; + //! Flush output, after seek etc. virtual void on_flush() = 0; + //! Flush output due to manual track change in progress. \n + //! Same as on_flush() by default. virtual void on_flush_changing_track() {on_flush();} + //! Called before first chunk and on stream format change. \n + //! Following write() calls will deliver chunks in the same format as specified here. virtual void open(audio_chunk::spec_t const & p_spec) = 0; - virtual void pause(bool p_state) = 0; - virtual void force_play() = 0; - virtual void volume_set(double p_val) = 0; + + //! Override this, not force_play(). \n + //! output_impl will defer call to on_force_play() until out of data in its buffer. + virtual void on_force_play() = 0; + + // base class virtual methods which derived class must also implement + // virtual void pause(bool p_state) = 0; + // virtual void volume_set(double p_val) = 0; + // virtual bool is_progressing() = 0; protected: void on_need_reopen() {m_active_spec.clear(); } private: - void flush(); - void flush_changing_track(); - void update(bool & p_ready); - size_t update_v2(); - double get_latency(); - void process_samples(const audio_chunk & p_chunk); + void flush() override final; + void flush_changing_track() override final; + void update(bool & p_ready) override final; + size_t update_v2() override final; + double get_latency() override final; + void process_samples(const audio_chunk & p_chunk) override final; + size_t process_samples_v2(const audio_chunk&) override final; + void force_play() override final; + void on_flush_internal(); + void send_force_play(); + + bool queue_empty() const { return m_incoming_ptr == m_incoming.get_size(); } pfc::array_t m_incoming; - t_size m_incoming_ptr; + size_t m_incoming_ptr = 0, m_can_write = 0; audio_chunk::spec_t m_incoming_spec,m_active_spec; + bool m_eos = false; // EOS issued by caller / no more data expected until a flush + bool m_sent_force_play = false; // set if sent on_force_play() }; diff --git a/sdk/foobar2000/SDK/packet_decoder.cpp b/sdk/foobar2000/SDK/packet_decoder.cpp index 07e9edc..0fbd813 100644 --- a/sdk/foobar2000/SDK/packet_decoder.cpp +++ b/sdk/foobar2000/SDK/packet_decoder.cpp @@ -16,7 +16,7 @@ void packet_decoder::g_open(service_ptr_t & p_out,bool p_decode, try { ptr->open(p_out, p_decode, p_owner, p_param1, p_param2, p_param2size, p_abort); return; - } catch (exception_io_data) { + } catch (exception_io_data const &) { rethrow = std::current_exception(); } } diff --git a/sdk/foobar2000/SDK/packet_decoder.h b/sdk/foobar2000/SDK/packet_decoder.h index 06cf8e1..3de689b 100644 --- a/sdk/foobar2000/SDK/packet_decoder.h +++ b/sdk/foobar2000/SDK/packet_decoder.h @@ -7,17 +7,17 @@ class NOVTABLE packet_decoder : public service_base { protected: //! Prototype of function that must be implemented by packet_decoder implementation but is not accessible through packet_decoder interface itself. //! Determines whether specific packet_decoder implementation supports specified decoder setup data. - static bool g_is_our_setup(const GUID & p_owner,t_size p_param1,const void * p_param2,t_size p_param2size) {return false;} + static bool g_is_our_setup(const GUID& p_owner, t_size p_param1, const void* p_param2, t_size p_param2size) { (void)p_owner; (void)p_param1; (void)p_param2; (void)p_param2size; return false; } //! Prototype of function that must be implemented by packet_decoder implementation but is not accessible through packet_decoder interface itself. //! Initializes packet decoder instance with specified decoder setup data. This is called only once, before any other methods. //! @param p_decode If set to true, decode() and reset_after_seek() calls can be expected later. If set to false, those methods will not be called on this packet_decoder instance - for an example when caller is only retrieving information about the file rather than preparing to decode it. - void open(const GUID & p_owner,bool p_decode,t_size p_param1,const void * p_param2,t_size p_param2size,abort_callback & p_abort) {throw exception_io_data();} + void open(const GUID& p_owner, bool p_decode, t_size p_param1, const void* p_param2, t_size p_param2size, abort_callback& p_abort) { (void)p_owner; (void)p_decode; (void)p_param1; (void)p_param2; (void)p_param2size; (void)p_abort; throw exception_io_data(); } public: //! Prototype of function that must be implemented by packet_decoder implementation but is not accessible through packet_decoder interface itself. //! Returns true if this is not the preferred decoder for this format, another one should be used if found. - static bool g_is_supported_partially(const GUID& p_owner, t_size p_param1, const void* p_param2, t_size p_param2size) { return false; } + static bool g_is_supported_partially(const GUID& p_owner, t_size p_param1, const void* p_param2, t_size p_param2size) { (void)p_owner; (void)p_param1; (void)p_param2; (void)p_param2size; return false; } //! Forwards additional information about stream being decoded. \n @@ -51,7 +51,7 @@ class NOVTABLE packet_decoder : public service_base { //! Static helper, creates a packet_decoder instance and initializes it with specific decoder setup data. static void g_open(service_ptr_t & p_out,bool p_decode,const GUID & p_owner,t_size p_param1,const void * p_param2,t_size p_param2size,abort_callback & p_abort); - static const GUID owner_MP4,owner_matroska,owner_MP3,owner_MP2,owner_MP1,owner_MP4_ALAC,owner_ADTS,owner_ADIF, owner_Ogg, owner_MP4_AMR, owner_MP4_AMR_WB, owner_MP4_AC3, owner_MP4_EAC3, owner_MP4_FLAC, owner_MP4_Opus; + static const GUID owner_MP4,owner_matroska,owner_MP3,owner_MP2,owner_MP1,owner_MP4_ALAC,owner_ADTS,owner_ADIF, owner_Ogg, owner_MP4_AMR, owner_MP4_AMR_WB, owner_MP4_AC3, owner_MP4_EAC3, owner_MP4_FLAC, owner_MP4_Opus, owner_MP4_MPEGH; struct matroska_setup { diff --git a/sdk/foobar2000/SDK/play_callback.h b/sdk/foobar2000/SDK/play_callback.h index 920bd7b..c9b0115 100644 --- a/sdk/foobar2000/SDK/play_callback.h +++ b/sdk/foobar2000/SDK/play_callback.h @@ -1,4 +1,8 @@ #pragma once +#include "callback_merit.h" + +// IMPLEMENTATION NOTE: all callback methods declared here could not be declared noexcept for historical reasons, but it's strongly recommended that your overrides of these methods are noexcept. + /*! Class receiving notifications about playback events. Note that all methods are called only from app's main thread. Use play_callback_manager to register your dynamically created instances. Statically registered version is available too - see play_callback_static. @@ -65,11 +69,18 @@ class NOVTABLE play_callback_manager : public service_base { virtual void unregister_callback(play_callback * p_callback) = 0; }; +//! \since 2.0 +class NOVTABLE play_callback_manager_v2 : public play_callback_manager { + FB2K_MAKE_SERVICE_COREAPI_EXTENSION(play_callback_manager_v2, play_callback_manager); +public: + virtual void set_callback_merit(play_callback*, fb2k::callback_merit_t) = 0; +}; + //! Implementation helper. class play_callback_impl_base : public play_callback { public: play_callback_impl_base(unsigned p_flags = 0xFFFFFFFF) { - play_callback_manager::get()->register_callback(this,p_flags,false); + if (p_flags != 0) play_callback_manager::get()->register_callback(this,p_flags,false); } ~play_callback_impl_base() { play_callback_manager::get()->unregister_callback(this); @@ -77,18 +88,18 @@ class play_callback_impl_base : public play_callback { void play_callback_reregister(unsigned flags, bool refresh = false) { auto api = play_callback_manager::get(); api->unregister_callback(this); - api->register_callback(this,flags,refresh); + if (flags != 0) api->register_callback(this,flags,refresh); } - void on_playback_starting(play_control::t_track_command p_command,bool p_paused) {} - void on_playback_new_track(metadb_handle_ptr p_track) {} - void on_playback_stop(play_control::t_stop_reason p_reason) {} - void on_playback_seek(double p_time) {} - void on_playback_pause(bool p_state) {} - void on_playback_edited(metadb_handle_ptr p_track) {} - void on_playback_dynamic_info(const file_info & p_info) {} - void on_playback_dynamic_info_track(const file_info & p_info) {} - void on_playback_time(double p_time) {} - void on_volume_change(float p_new_val) {} + void on_playback_starting(play_control::t_track_command p_command, bool p_paused) override { (void)p_command; (void)p_paused; } + void on_playback_new_track(metadb_handle_ptr p_track) override { (void)p_track; } + void on_playback_stop(play_control::t_stop_reason p_reason) override { (void)p_reason; } + void on_playback_seek(double p_time) override { (void)p_time; } + void on_playback_pause(bool p_state) override { (void)p_state; } + void on_playback_edited(metadb_handle_ptr p_track) override { (void)p_track; } + void on_playback_dynamic_info(const file_info& p_info) override { (void)p_info; } + void on_playback_dynamic_info_track(const file_info& p_info) override { (void)p_info; } + void on_playback_time(double p_time) override { (void)p_time; } + void on_volume_change(float p_new_val) override { (void)p_new_val; } PFC_CLASS_NOT_COPYABLE_EX(play_callback_impl_base) }; @@ -133,21 +144,21 @@ class playback_event_notify : private play_callback_impl_base { protected: virtual void on_playback_event() {} private: - void on_playback_starting(play_control::t_track_command p_command,bool p_paused) {on_playback_event();} - void on_playback_new_track(metadb_handle_ptr p_track) {on_playback_event();} - void on_playback_stop(play_control::t_stop_reason p_reason) {on_playback_event();} - void on_playback_seek(double p_time) {on_playback_event();} - void on_playback_pause(bool p_state) {on_playback_event();} - void on_playback_edited(metadb_handle_ptr p_track) {on_playback_event();} - void on_playback_dynamic_info(const file_info & p_info) {on_playback_event();} - void on_playback_dynamic_info_track(const file_info & p_info) {on_playback_event();} - void on_playback_time(double p_time) {on_playback_event();} - void on_volume_change(float p_new_val) {on_playback_event();} + void on_playback_starting(play_control::t_track_command,bool) override {on_playback_event();} + void on_playback_new_track(metadb_handle_ptr) override {on_playback_event();} + void on_playback_stop(play_control::t_stop_reason) override {on_playback_event();} + void on_playback_seek(double) override {on_playback_event();} + void on_playback_pause(bool) override {on_playback_event();} + void on_playback_edited(metadb_handle_ptr) override {on_playback_event();} + void on_playback_dynamic_info(const file_info &) override {on_playback_event();} + void on_playback_dynamic_info_track(const file_info &) override {on_playback_event();} + void on_playback_time(double) override {on_playback_event();} + void on_volume_change(float) override {on_playback_event();} }; class playback_volume_notify : private play_callback_impl_base { public: playback_volume_notify() : play_callback_impl_base(flag_on_volume_change) {} // override me - void on_volume_change(float p_new_val) {} + void on_volume_change(float p_new_val) override { (void)p_new_val; } }; diff --git a/sdk/foobar2000/SDK/playback_control.h b/sdk/foobar2000/SDK/playback_control.h index 1f31a68..e2d050d 100644 --- a/sdk/foobar2000/SDK/playback_control.h +++ b/sdk/foobar2000/SDK/playback_control.h @@ -194,3 +194,12 @@ class playback_control_v3 : public playback_control_v2 { //for compatibility with old code typedef playback_control play_control; + +namespace fb2k { + metadb_handle_ptr nowPlaying(bool bAudible = false); + metadb_handle_ptr nowPlayingAudible(); + bool isNowPlaying(metadb_handle_ptr const&, bool bAudible = false); + bool isNowPlayingAudible(metadb_handle_ptr const&); + bool isNowPlaying(const playable_location&, bool bAudible = false); + bool isNowPlayingAudible(const playable_location&); +} \ No newline at end of file diff --git a/sdk/foobar2000/SDK/playback_stream_capture.h b/sdk/foobar2000/SDK/playback_stream_capture.h index 4550811..c3c7d12 100644 --- a/sdk/foobar2000/SDK/playback_stream_capture.h +++ b/sdk/foobar2000/SDK/playback_stream_capture.h @@ -6,7 +6,8 @@ class NOVTABLE playback_stream_capture_callback { public: //! Delivers a real-time chunk of audio data. \n //! Audio is roughly synchronized with what can currently be heard. This API is provided for utility purposes such as streaming; if you want to implement a visualisation, use the visualisation_manager API instead. \n - //! Called only from the main thread. + //! Contrary to visualisation methods, this guarantees that all played audio data is coming thru. \n + //! Called only from the main thread. \n virtual void on_chunk(const audio_chunk &) = 0; protected: playback_stream_capture_callback() {} @@ -18,8 +19,47 @@ class NOVTABLE playback_stream_capture_callback { class NOVTABLE playback_stream_capture : public service_base { FB2K_MAKE_SERVICE_COREAPI(playback_stream_capture) public: + //! Register a playback_stream_capture_callback. \n //! Possible to call only from the main thread. virtual void add_callback(playback_stream_capture_callback * ) = 0; + //! Un-register a playback_stream_capture_callback. \n //! Possible to call only from the main thread. virtual void remove_callback(playback_stream_capture_callback * ) = 0; }; + +//! \since 2.0. +//! Implemented by core. +class NOVTABLE playback_stream_capture_v2 : public playback_stream_capture { + FB2K_MAKE_SERVICE_COREAPI_EXTENSION(playback_stream_capture_v2, playback_stream_capture); +public: + //! @param requestInterval Interval, in seconds, in which the callback expects to be called. \n + //! Set to -1 to use defaults. \n + //! Note that if many callbacks are registered, they all get called at once; one callback requesting lower interval lowers the interval for all. \n + //! Non negative values are clamped to allowed range, that is, request of zero results in lowest possible update interval. + virtual void add_callback_v2(playback_stream_capture_callback* cb, double requestInterval = -1) = 0; +}; + +class playback_stream_capture_callback_impl : public playback_stream_capture_callback { +public: + void on_chunk(const audio_chunk&) override {} + + //! @param interval requested update interval, see playback_stream_capture_v2::add_callback_v2() + playback_stream_capture_callback_impl(double interval = -1) { + PFC_ASSERT(core_api::is_main_thread()); +#if FOOBAR2020 + playback_stream_capture_v2::get()->add_callback_v2(this, interval); +#else + auto api = playback_stream_capture::get(); + playback_stream_capture_v2::ptr v2; + if (v2 &= api) v2->add_callback_v2(this, interval); + else api->add_callback(this); +#endif + } + ~playback_stream_capture_callback_impl() { + PFC_ASSERT(core_api::is_main_thread()); + playback_stream_capture::get()->remove_callback(this); + } + + playback_stream_capture_callback_impl(const playback_stream_capture_callback_impl&) = delete; + void operator=(const playback_stream_capture_callback_impl&) = delete; +}; diff --git a/sdk/foobar2000/SDK/playlist.cpp b/sdk/foobar2000/SDK/playlist.cpp index 9ac2b34..157d86d 100644 --- a/sdk/foobar2000/SDK/playlist.cpp +++ b/sdk/foobar2000/SDK/playlist.cpp @@ -14,7 +14,8 @@ namespace { enum_items_callback_retrieve_item() : m_item(0) {} bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) { - assert(m_item.is_empty()); + (void)p_index; (void)b_selected; + PFC_ASSERT(m_item.is_empty()); m_item = p_location; return false; } @@ -28,6 +29,7 @@ namespace { enum_items_callback_retrieve_selection() : m_state(false) {} bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) { + (void)p_index; (void)p_location; m_state = b_selected; return false; } @@ -41,6 +43,7 @@ namespace { enum_items_callback_retrieve_selection_mask(bit_array_var & p_out) : m_out(p_out) {} bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) { + (void)p_location; m_out.set(p_index,b_selected); return true; } @@ -53,6 +56,7 @@ namespace { enum_items_callback_retrieve_all_items(pfc::list_base_t & p_out) : m_out(p_out) {m_out.remove_all();} bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) { + (void)p_index; (void)b_selected; m_out.add_item(p_location); return true; } @@ -65,6 +69,7 @@ namespace { enum_items_callback_retrieve_selected_items(pfc::list_base_t & p_out) : m_out(p_out) {m_out.remove_all();} bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) { + (void)p_index; if (b_selected) m_out.add_item(p_location); return true; } @@ -77,6 +82,7 @@ namespace { enum_items_callback_count_selection(t_size p_max) : m_max(p_max), m_counter(0) {} bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) { + (void)p_index; (void)p_location; if (b_selected) { if (++m_counter >= m_max) return false; @@ -150,7 +156,7 @@ bool playlist_manager::playlist_move_selection(t_size p_playlist,int p_delta) { t_size playlist_manager::activeplaylist_get_item_count() { t_size playlist = get_active_playlist(); - if (playlist == pfc_infinite) return 0; + if (playlist == SIZE_MAX) return 0; else return playlist_get_item_count(playlist); } @@ -171,14 +177,14 @@ void playlist_manager::activeplaylist_enum_items(enum_items_func f, const bit_ar t_size playlist_manager::activeplaylist_get_focus_item() { t_size playlist = get_active_playlist(); - if (playlist == pfc_infinite) return pfc_infinite; + if (playlist == SIZE_MAX) return SIZE_MAX; else return playlist_get_focus_item(playlist); } bool playlist_manager::activeplaylist_get_name(pfc::string_base & p_out) { t_size playlist = get_active_playlist(); - if (playlist == pfc_infinite) return false; + if (playlist == SIZE_MAX) return false; else return playlist_get_name(playlist,p_out); } @@ -186,53 +192,53 @@ bool playlist_manager::activeplaylist_get_name(pfc::string_base & p_out) bool playlist_manager::activeplaylist_reorder_items(const t_size * order,t_size count) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) return playlist_reorder_items(playlist,order,count); + if (playlist != SIZE_MAX) return playlist_reorder_items(playlist,order,count); else return false; } void playlist_manager::activeplaylist_set_selection(const bit_array & affected,const bit_array & status) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) playlist_set_selection(playlist,affected,status); + if (playlist != SIZE_MAX) playlist_set_selection(playlist,affected,status); } bool playlist_manager::activeplaylist_remove_items(const bit_array & mask) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) return playlist_remove_items(playlist,mask); + if (playlist != SIZE_MAX) return playlist_remove_items(playlist,mask); else return false; } bool playlist_manager::activeplaylist_replace_item(t_size p_item,const metadb_handle_ptr & p_new_item) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) return playlist_replace_item(playlist,p_item,p_new_item); + if (playlist != SIZE_MAX) return playlist_replace_item(playlist,p_item,p_new_item); else return false; } void playlist_manager::activeplaylist_set_focus_item(t_size p_item) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) playlist_set_focus_item(playlist,p_item); + if (playlist != SIZE_MAX) playlist_set_focus_item(playlist,p_item); } t_size playlist_manager::activeplaylist_insert_items(t_size p_base,const pfc::list_base_const_t & data,const bit_array & p_selection) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) return playlist_insert_items(playlist,p_base,data,p_selection); - else return pfc_infinite; + if (playlist != SIZE_MAX) return playlist_insert_items(playlist,p_base,data,p_selection); + else return SIZE_MAX; } void playlist_manager::activeplaylist_ensure_visible(t_size p_item) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) playlist_ensure_visible(playlist,p_item); + if (playlist != SIZE_MAX) playlist_ensure_visible(playlist,p_item); } bool playlist_manager::activeplaylist_rename(const char * p_name,t_size p_name_len) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) return playlist_rename(playlist,p_name,p_name_len); + if (playlist != SIZE_MAX) return playlist_rename(playlist,p_name,p_name_len); else return false; } @@ -252,32 +258,32 @@ metadb_handle_ptr playlist_manager::activeplaylist_get_item_handle(t_size p_item bool playlist_manager::activeplaylist_get_item_handle(metadb_handle_ptr & p_out,t_size p_item) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) return playlist_get_item_handle(p_out,playlist,p_item); + if (playlist != SIZE_MAX) return playlist_get_item_handle(p_out,playlist,p_item); else return false; } void playlist_manager::activeplaylist_move_selection(int p_delta) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) playlist_move_selection(playlist,p_delta); + if (playlist != SIZE_MAX) playlist_move_selection(playlist,p_delta); } void playlist_manager::activeplaylist_get_selection_mask(bit_array_var & out) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) playlist_get_selection_mask(playlist,out); + if (playlist != SIZE_MAX) playlist_get_selection_mask(playlist,out); } void playlist_manager::activeplaylist_get_all_items(pfc::list_base_t & out) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) playlist_get_all_items(playlist,out); + if (playlist != SIZE_MAX) playlist_get_all_items(playlist,out); } void playlist_manager::activeplaylist_get_selected_items(pfc::list_base_t & out) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) playlist_get_selected_items(playlist,out); + if (playlist != SIZE_MAX) playlist_get_selected_items(playlist,out); } bool playlist_manager::remove_playlist(t_size idx) @@ -298,7 +304,7 @@ void playlist_manager::playlist_clear(t_size p_playlist) void playlist_manager::activeplaylist_clear() { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) playlist_clear(playlist); + if (playlist != SIZE_MAX) playlist_clear(playlist); } bool playlist_manager::playlist_update_content(t_size playlist, metadb_handle_list_cref content, bool bUndoBackup) { @@ -354,13 +360,13 @@ bool playlist_manager::playlist_update_content(t_size playlist, metadb_handle_li } bool playlist_manager::playlist_add_items(t_size playlist,const pfc::list_base_const_t & data,const bit_array & p_selection) { - return playlist_insert_items(playlist,pfc_infinite,data,p_selection) != pfc_infinite; + return playlist_insert_items(playlist, SIZE_MAX, data, p_selection) != SIZE_MAX; } bool playlist_manager::activeplaylist_add_items(const pfc::list_base_const_t & data,const bit_array & p_selection) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) return playlist_add_items(playlist,data,p_selection); + if (playlist != SIZE_MAX) return playlist_add_items(playlist,data,p_selection); else return false; } @@ -369,13 +375,13 @@ bool playlist_manager::playlist_insert_items_filter(t_size p_playlist,t_size p_b metadb_handle_list temp; if (!playlist_incoming_item_filter::get()->filter_items(p_data,temp)) return false; - return playlist_insert_items(p_playlist,p_base,temp, pfc::bit_array_val(p_select)) != pfc_infinite; + return playlist_insert_items(p_playlist,p_base,temp, pfc::bit_array_val(p_select)) != SIZE_MAX; } bool playlist_manager::activeplaylist_insert_items_filter(t_size p_base,const pfc::list_base_const_t & p_data,bool p_select) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) return playlist_insert_items_filter(playlist,p_base,p_data,p_select); + if (playlist != SIZE_MAX) return playlist_insert_items_filter(playlist,p_base,p_data,p_select); else return false; } @@ -383,33 +389,33 @@ bool playlist_manager::playlist_insert_locations(t_size p_playlist,t_size p_base { metadb_handle_list temp; if (!playlist_incoming_item_filter::get()->process_locations(p_urls,temp,true,0,0,p_parentwnd)) return false; - return playlist_insert_items(p_playlist,p_base,temp, pfc::bit_array_val(p_select)) != pfc_infinite; + return playlist_insert_items(p_playlist,p_base,temp, pfc::bit_array_val(p_select)) != SIZE_MAX; } bool playlist_manager::activeplaylist_insert_locations(t_size p_base,const pfc::list_base_const_t & p_urls,bool p_select,fb2k::hwnd_t p_parentwnd) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) return playlist_insert_locations(playlist,p_base,p_urls,p_select,p_parentwnd); + if (playlist != SIZE_MAX) return playlist_insert_locations(playlist,p_base,p_urls,p_select,p_parentwnd); else return false; } bool playlist_manager::playlist_add_items_filter(t_size p_playlist,const pfc::list_base_const_t & p_data,bool p_select) { - return playlist_insert_items_filter(p_playlist,pfc_infinite,p_data,p_select); + return playlist_insert_items_filter(p_playlist,SIZE_MAX,p_data,p_select); } bool playlist_manager::activeplaylist_add_items_filter(const pfc::list_base_const_t & p_data,bool p_select) { - return activeplaylist_insert_items_filter(pfc_infinite,p_data,p_select); + return activeplaylist_insert_items_filter(SIZE_MAX,p_data,p_select); } bool playlist_manager::playlist_add_locations(t_size p_playlist,const pfc::list_base_const_t & p_urls,bool p_select,fb2k::hwnd_t p_parentwnd) { - return playlist_insert_locations(p_playlist,pfc_infinite,p_urls,p_select,p_parentwnd); + return playlist_insert_locations(p_playlist,SIZE_MAX,p_urls,p_select,p_parentwnd); } bool playlist_manager::activeplaylist_add_locations(const pfc::list_base_const_t & p_urls,bool p_select,fb2k::hwnd_t p_parentwnd) { - return activeplaylist_insert_locations(pfc_infinite,p_urls,p_select,p_parentwnd); + return activeplaylist_insert_locations(SIZE_MAX,p_urls,p_select,p_parentwnd); } void playlist_manager::reset_playing_playlist() @@ -425,26 +431,26 @@ void playlist_manager::playlist_clear_selection(t_size p_playlist) void playlist_manager::activeplaylist_clear_selection() { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) playlist_clear_selection(playlist); + if (playlist != SIZE_MAX) playlist_clear_selection(playlist); } void playlist_manager::activeplaylist_undo_backup() { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) playlist_undo_backup(playlist); + if (playlist != SIZE_MAX) playlist_undo_backup(playlist); } bool playlist_manager::activeplaylist_undo_restore() { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) return playlist_undo_restore(playlist); + if (playlist != SIZE_MAX) return playlist_undo_restore(playlist); else return false; } bool playlist_manager::activeplaylist_redo_restore() { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) return playlist_redo_restore(playlist); + if (playlist != SIZE_MAX) return playlist_redo_restore(playlist); else return false; } @@ -459,13 +465,13 @@ void playlist_manager::playlist_remove_selection(t_size p_playlist,bool p_crop) void playlist_manager::activeplaylist_remove_selection(bool p_crop) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) playlist_remove_selection(playlist,p_crop); + if (playlist != SIZE_MAX) playlist_remove_selection(playlist,p_crop); } void playlist_manager::activeplaylist_item_format_title(t_size p_item,titleformat_hook * p_hook,pfc::string_base & out,const service_ptr_t & p_script,titleformat_text_filter * p_filter,play_control::t_display_level p_playback_info_level) { t_size playlist = get_active_playlist(); - if (playlist == pfc_infinite) out = "NJET"; + if (playlist == SIZE_MAX) out = "NJET"; else playlist_item_format_title(playlist,p_item,p_hook,out,p_script,p_filter,p_playback_info_level); } @@ -477,7 +483,7 @@ void playlist_manager::playlist_set_selection_single(t_size p_playlist,t_size p_ void playlist_manager::activeplaylist_set_selection_single(t_size p_item,bool p_state) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) playlist_set_selection_single(playlist,p_item,p_state); + if (playlist != SIZE_MAX) playlist_set_selection_single(playlist,p_item,p_state); } t_size playlist_manager::playlist_get_selection_count(t_size p_playlist,t_size p_max) @@ -490,21 +496,21 @@ t_size playlist_manager::playlist_get_selection_count(t_size p_playlist,t_size p t_size playlist_manager::activeplaylist_get_selection_count(t_size p_max) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) return playlist_get_selection_count(playlist,p_max); + if (playlist != SIZE_MAX) return playlist_get_selection_count(playlist,p_max); else return 0; } bool playlist_manager::playlist_get_focus_item_handle(metadb_handle_ptr & p_out,t_size p_playlist) { t_size index = playlist_get_focus_item(p_playlist); - if (index == pfc_infinite) return false; + if (index == SIZE_MAX) return false; return playlist_get_item_handle(p_out,p_playlist,index); } bool playlist_manager::activeplaylist_get_focus_item_handle(metadb_handle_ptr & p_out) { t_size playlist = get_active_playlist(); - if (playlist != pfc_infinite) return playlist_get_focus_item_handle(p_out,playlist); + if (playlist != SIZE_MAX) return playlist_get_focus_item_handle(p_out,playlist); else return false; } @@ -516,7 +522,7 @@ t_size playlist_manager::find_playlist(const char * p_name,t_size p_name_length) if (!playlist_get_name(n,temp)) break; if (stricmp_utf8_ex(temp,temp.length(),p_name,p_name_length) == 0) return n; } - return pfc_infinite; + return SIZE_MAX; } t_size playlist_manager::find_or_create_playlist_unlocked(const char * p_name, t_size p_name_length) { @@ -524,25 +530,25 @@ t_size playlist_manager::find_or_create_playlist_unlocked(const char * p_name, t pfc::string_formatter temp; for(n=0;n namebuffer; namebuffer << new_playlist_text << " (" << walk << ")"; - if (find_playlist(namebuffer,pfc_infinite) == pfc_infinite) return create_playlist(namebuffer,pfc_infinite,p_index); + if (find_playlist(namebuffer, SIZE_MAX) == SIZE_MAX) return create_playlist(namebuffer,SIZE_MAX,p_index); } } @@ -599,8 +605,9 @@ namespace { t_size m_found; public: enum_items_callback_remove_list(const metadb_handle_list & p_data,bit_array_var & p_table) : m_data(p_data), m_table(p_table), m_found(0) {} - bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) + bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) override { + (void)b_selected; bool found = m_data.bsearch_by_pointer(p_location) != pfc_infinite; m_table.set(p_index,found); if (found) m_found++; @@ -647,7 +654,7 @@ bool playlist_manager::get_all_items(pfc::list_base_t & out) t_uint32 playlist_manager::activeplaylist_lock_get_filter_mask() { t_size playlist = get_active_playlist(); - if (playlist == pfc_infinite) return ~0; + if (playlist == SIZE_MAX) return UINT32_MAX; else return playlist_lock_get_filter_mask(playlist); } @@ -816,7 +823,8 @@ namespace { public: enum_items_callback_get_selected_count() : m_found() {} t_size get_count() const {return m_found;} - bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) { + bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) override { + (void)p_index; (void)p_location; if (b_selected) m_found++; return true; } @@ -835,7 +843,8 @@ namespace { public: enum_items_callback_find_item(metadb_handle_ptr p_lookingFor) : m_lookingFor(p_lookingFor) {} t_size result() const {return m_result;} - bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) { + bool on_item(t_size p_index,const metadb_handle_ptr & p_location,bool b_selected) override { + (void)b_selected; if (p_location == m_lookingFor) { m_result = p_index; return false; @@ -869,7 +878,7 @@ bool playlist_manager::playlist_find_item(t_size p_playlist,metadb_handle_ptr p_ enum_items_callback_find_item callback(p_item); playlist_enum_items(p_playlist,callback,pfc::bit_array_true()); t_size result = callback.result(); - if (result == pfc_infinite) return false; + if (result == SIZE_MAX) return false; p_result = result; return true; } @@ -877,24 +886,24 @@ bool playlist_manager::playlist_find_item_selected(t_size p_playlist,metadb_hand enum_items_callback_find_item_selected callback(p_item); playlist_enum_items(p_playlist,callback,pfc::bit_array_true()); t_size result = callback.result(); - if (result == pfc_infinite) return false; + if (result == SIZE_MAX) return false; p_result = result; return true; } t_size playlist_manager::playlist_set_focus_by_handle(t_size p_playlist,metadb_handle_ptr p_item) { t_size index; - if (!playlist_find_item(p_playlist,p_item,index)) index = pfc_infinite; + if (!playlist_find_item(p_playlist,p_item,index)) index = SIZE_MAX; playlist_set_focus_item(p_playlist,index); return index; } bool playlist_manager::activeplaylist_find_item(metadb_handle_ptr p_item,t_size & p_result) { t_size playlist = get_active_playlist(); - if (playlist == pfc_infinite) return false; + if (playlist == SIZE_MAX) return false; return playlist_find_item(playlist,p_item,p_result); } t_size playlist_manager::activeplaylist_set_focus_by_handle(metadb_handle_ptr p_item) { t_size playlist = get_active_playlist(); - if (playlist == pfc_infinite) return pfc_infinite; + if (playlist == SIZE_MAX) return SIZE_MAX; return playlist_set_focus_by_handle(playlist,p_item); } @@ -914,7 +923,7 @@ t_size playlist_manager_v3::recycler_find_by_id(t_uint32 id) { for(t_size walk = 0; walk < total; ++walk) { if (id == recycler_get_id(walk)) return walk; } - return ~0; + return SIZE_MAX; } @@ -976,5 +985,24 @@ void playlist_manager::on_files_rechaptered( metadb_handle_list_cref newHandles void playlist_manager::on_file_rechaptered(const char* path, metadb_handle_list_cref newItems) { // obsolete method + (void)path; on_files_rechaptered(newItems); } + +namespace { + class process_locations_notify_lambda : public process_locations_notify { + public: + process_locations_notify::func_t f; + void on_completion(metadb_handle_list_cref p_items) override { + PFC_ASSERT(f != nullptr); + f(p_items); + } + void on_aborted() override {} + }; +} +process_locations_notify::ptr process_locations_notify::create(func_t arg) { + PFC_ASSERT(arg != nullptr); + auto ret = fb2k::service_new< process_locations_notify_lambda >(); + ret->f = arg; + return ret; +} \ No newline at end of file diff --git a/sdk/foobar2000/SDK/playlist.h b/sdk/foobar2000/SDK/playlist.h index b3fb186..4b81d3d 100644 --- a/sdk/foobar2000/SDK/playlist.h +++ b/sdk/foobar2000/SDK/playlist.h @@ -3,6 +3,7 @@ #include "titleformat.h" #include "playback_control.h" #include +#include "callback_merit.h" //! This interface allows filtering of playlist modification operations.\n //! Implemented by components "locking" playlists; use playlist_manager::playlist_lock_install() etc to takeover specific playlist with your instance of playlist_lock. @@ -538,7 +539,6 @@ class NOVTABLE playlist_manager_v4 : public playlist_manager_v3 { }; //! \since 2.0 -//! Internal, do not use class NOVTABLE playlist_manager_v5 : public playlist_manager_v4 { FB2K_MAKE_SERVICE_COREAPI_EXTENSION(playlist_manager_v5, playlist_manager_v4) public: @@ -546,6 +546,17 @@ class NOVTABLE playlist_manager_v5 : public playlist_manager_v4 { virtual size_t find_playlist_by_guid(const GUID&) = 0; }; +//! \since 2.0 beta 8 +class NOVTABLE playlist_manager_v6 : public playlist_manager_v5 { + FB2K_MAKE_SERVICE_COREAPI_EXTENSION(playlist_manager_v6, playlist_manager_v5); +public: + virtual void set_callback_merit(class playlist_callback*, fb2k::callback_merit_t) = 0; + virtual void set_callback_merit(class playlist_callback_single*, fb2k::callback_merit_t) = 0; + +}; + +// IMPLEMENTATION NOTE: all playlist_callback methods could not be declared noexcept for historical reasons, but it's strongly recommended that your overrides of these methods are noexcept. + class NOVTABLE playlist_callback { public: @@ -675,30 +686,30 @@ class playlist_callback_impl_base : public playlist_callback { playlist_manager::get()->modify_callback(this,p_flags); } //dummy implementations - avoid possible pure virtual function calls! - void on_items_added(t_size p_playlist,t_size p_start, metadb_handle_list_cref p_data,const bit_array & p_selection) {} - void on_items_reordered(t_size p_playlist,const t_size * p_order,t_size p_count) {} - void on_items_removing(t_size p_playlist,const bit_array & p_mask,t_size p_old_count,t_size p_new_count) {} - void on_items_removed(t_size p_playlist,const bit_array & p_mask,t_size p_old_count,t_size p_new_count) {} - void on_items_selection_change(t_size p_playlist,const bit_array & p_affected,const bit_array & p_state) {} - void on_item_focus_change(t_size p_playlist,t_size p_from,t_size p_to) {} + void on_items_added(t_size p_playlist, t_size p_start, metadb_handle_list_cref p_data, const bit_array& p_selection) override { (void)p_playlist; (void)p_start; (void)p_data; (void)p_selection; } + void on_items_reordered(t_size p_playlist, const t_size* p_order, t_size p_count) override { (void)p_playlist; (void)p_order; (void)p_count; } + void on_items_removing(t_size p_playlist, const bit_array& p_mask, t_size p_old_count, t_size p_new_count) override { (void)p_playlist; (void)p_mask; (void)p_old_count; (void)p_new_count; } + void on_items_removed(t_size p_playlist,const bit_array & p_mask,t_size p_old_count,t_size p_new_count) override { (void)p_playlist; (void)p_mask; (void)p_old_count; (void)p_new_count; } + void on_items_selection_change(t_size p_playlist, const bit_array& p_affected, const bit_array& p_state) override { (void)p_playlist; (void)p_affected; (void)p_state; } + void on_item_focus_change(t_size p_playlist, t_size p_from, t_size p_to) override { (void)p_playlist; (void)p_from; (void)p_to; } - void on_items_modified(t_size p_playlist,const bit_array & p_mask) {} - void on_items_modified_fromplayback(t_size p_playlist,const bit_array & p_mask,play_control::t_display_level p_level) {} + void on_items_modified(t_size p_playlist, const bit_array& p_mask) override { (void)p_playlist; (void)p_mask; } + void on_items_modified_fromplayback(t_size p_playlist, const bit_array& p_mask, play_control::t_display_level p_level) override { (void)p_playlist; (void)p_mask; (void)p_level; } - void on_items_replaced(t_size p_playlist,const bit_array & p_mask,const pfc::list_base_const_t & p_data) {} + void on_items_replaced(t_size p_playlist, const bit_array& p_mask, const pfc::list_base_const_t& p_data) override { (void)p_playlist; (void)p_mask; (void)p_data; } - void on_item_ensure_visible(t_size p_playlist,t_size p_idx) {} + void on_item_ensure_visible(t_size p_playlist, t_size p_idx) override { (void)p_playlist; (void)p_idx; } - void on_playlist_activate(t_size p_old,t_size p_new) {} - void on_playlist_created(t_size p_index,const char * p_name,t_size p_name_len) {} - void on_playlists_reorder(const t_size * p_order,t_size p_count) {} - void on_playlists_removing(const bit_array & p_mask,t_size p_old_count,t_size p_new_count) {} - void on_playlists_removed(const bit_array & p_mask,t_size p_old_count,t_size p_new_count) {} - void on_playlist_renamed(t_size p_index,const char * p_new_name,t_size p_new_name_len) {} + void on_playlist_activate(t_size p_old, t_size p_new) override { (void)p_old; (void)p_new; } + void on_playlist_created(t_size p_index, const char* p_name, t_size p_name_len) override { (void)p_index; (void)p_name; (void)p_name_len; } + void on_playlists_reorder(const t_size* p_order, t_size p_count) override { (void)p_order; (void)p_count; } + void on_playlists_removing(const bit_array& p_mask, t_size p_old_count, t_size p_new_count) override { (void)p_mask; (void)p_old_count; (void)p_new_count; } + void on_playlists_removed(const bit_array& p_mask, t_size p_old_count, t_size p_new_count) override { (void)p_mask; (void)p_old_count; (void)p_new_count; } + void on_playlist_renamed(t_size p_index, const char* p_new_name, t_size p_new_name_len) override { (void)p_index; (void)p_new_name; (void)p_new_name_len; } - void on_default_format_changed() {} - void on_playback_order_changed(t_size p_new_index) {} - void on_playlist_locked(t_size p_playlist,bool p_locked) {} + void on_default_format_changed() override {} + void on_playback_order_changed(t_size p_new_index) override { (void)p_new_index; } + void on_playlist_locked(t_size p_playlist, bool p_locked) override { (void)p_playlist; (void)p_locked; } }; //! playlist_callback_single implementation helper - registers itself on creation / unregisters on destruction. Must not be instantiated statically! @@ -715,23 +726,23 @@ class playlist_callback_single_impl_base : public playlist_callback_single { } //dummy implementations - avoid possible pure virtual function calls! - void on_items_added(t_size p_base, metadb_handle_list_cref p_data,const bit_array & p_selection) {} - void on_items_reordered(const t_size * p_order,t_size p_count) {} - void on_items_removing(const bit_array & p_mask,t_size p_old_count,t_size p_new_count) {} - void on_items_removed(const bit_array & p_mask,t_size p_old_count,t_size p_new_count) {} - void on_items_selection_change(const bit_array & p_affected,const bit_array & p_state) {} - void on_item_focus_change(t_size p_from,t_size p_to) {} - void on_items_modified(const bit_array & p_mask) {} - void on_items_modified_fromplayback(const bit_array & p_mask,play_control::t_display_level p_level) {} - void on_items_replaced(const bit_array & p_mask,const pfc::list_base_const_t & p_data) {} - void on_item_ensure_visible(t_size p_idx) {} - - void on_playlist_switch() {} - void on_playlist_renamed(const char * p_new_name,t_size p_new_name_len) {} - void on_playlist_locked(bool p_locked) {} - - void on_default_format_changed() {} - void on_playback_order_changed(t_size p_new_index) {} + void on_items_added(t_size p_base, metadb_handle_list_cref p_data, const bit_array& p_selection) override { (void)p_base; (void)p_data; (void)p_selection; } + void on_items_reordered(const t_size* p_order, t_size p_count) override { (void)p_order; (void)p_count; } + void on_items_removing(const bit_array& p_mask, t_size p_old_count, t_size p_new_count) override { (void)p_mask; (void)p_old_count; (void)p_new_count; } + void on_items_removed(const bit_array& p_mask, t_size p_old_count, t_size p_new_count) override { (void)p_mask; (void)p_old_count; (void)p_new_count; } + void on_items_selection_change(const bit_array& p_affected, const bit_array& p_state) override { (void)p_affected; (void)p_state; } + void on_item_focus_change(t_size p_from, t_size p_to) override { (void)p_from; (void)p_to; } + void on_items_modified(const bit_array& p_mask) override { (void)p_mask; } + void on_items_modified_fromplayback(const bit_array& p_mask, play_control::t_display_level p_level) override { (void)p_mask; (void)p_level; } + void on_items_replaced(const bit_array& p_mask, const pfc::list_base_const_t& p_data) override { (void)p_mask; (void)p_data; } + void on_item_ensure_visible(t_size p_idx) override { (void)p_idx; } + + void on_playlist_switch() override {} + void on_playlist_renamed(const char* p_new_name, t_size p_new_name_len) override { (void)p_new_name; (void)p_new_name_len; } + void on_playlist_locked(bool p_locked) override { (void)p_locked; } + + void on_default_format_changed() override {} + void on_playback_order_changed(t_size p_new_index) override { (void)p_new_index; } PFC_CLASS_NOT_COPYABLE(playlist_callback_single_impl_base,playlist_callback_single_impl_base); }; @@ -812,11 +823,13 @@ class NOVTABLE playlist_incoming_item_filter : public service_base { //! For use with playlist_incoming_item_filter_v2::process_locations_async(). //! \since 0.9.3 class NOVTABLE process_locations_notify : public service_base { + FB2K_MAKE_SERVICE_INTERFACE(process_locations_notify, service_base); public: - virtual void on_completion(const pfc::list_base_const_t & p_items) = 0; + virtual void on_completion(metadb_handle_list_cref p_items) = 0; virtual void on_aborted() = 0; - FB2K_MAKE_SERVICE_INTERFACE(process_locations_notify,service_base); + typedef std::function func_t; + static process_locations_notify::ptr create(func_t); }; typedef service_ptr_t process_locations_notify_ptr; @@ -927,6 +940,6 @@ class playlist_lock_change_notify : private playlist_callback_single_impl_base { return (api->playlist_lock_get_filter_mask(active) & what) == 0; } private: - void on_playlist_switch() {on_lock_state_change();} - void on_playlist_locked(bool p_locked) {on_lock_state_change();} + void on_playlist_switch() override {on_lock_state_change();} + void on_playlist_locked(bool) override {on_lock_state_change();} }; diff --git a/sdk/foobar2000/SDK/playlistColumnProvider.h b/sdk/foobar2000/SDK/playlistColumnProvider.h index bb26bc9..45aea1a 100644 --- a/sdk/foobar2000/SDK/playlistColumnProvider.h +++ b/sdk/foobar2000/SDK/playlistColumnProvider.h @@ -1,18 +1,30 @@ #pragma once namespace fb2k { + //! Declares a column to be made available in Default UI playlist view, \n + //! without user having to manually enter title formatting patterns. class playlistColumnProvider : public service_base { FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(playlistColumnProvider); public: + //! Number of columns published by this object. virtual size_t numColumns() = 0; + //! Unique identifier of a column. virtual GUID columnID(size_t col) = 0; + //! Formatting pattern used for displaying a column. virtual fb2k::stringRef columnFormatSpec(size_t col) = 0; + //! Optional; sorting pattern used for this column. Return null to use display pattern for sorting. virtual fb2k::stringRef columnSortScript(size_t col) = 0; + //! Name of the column shown to the user. virtual fb2k::stringRef columnName(size_t col) = 0; + //! Display flags (alignment). \n + //! See flag_* constants. virtual unsigned columnFlags(size_t col) = 0; - static constexpr unsigned flag_alignLeft = 0; - static constexpr unsigned flag_alignRight = 1 << 0; - static constexpr unsigned flag_alignCenter = 1 << 1; + static constexpr unsigned + flag_alignLeft = 0, flag_alignRight = 1 << 0, flag_alignCenter = 1 << 1, // alignment + flag_numeric = 1 << 2, // prefer fixed width font, not all renderers support this + flag_positionDependant = 1 << 3, // value changes with position in playlist, mainly used by list index etc + flag_glyphs = 1 << 4; // internal/reserved + static constexpr unsigned flag_alignMask = (flag_alignLeft|flag_alignRight|flag_alignCenter); }; } diff --git a/sdk/foobar2000/SDK/playlist_loader.cpp b/sdk/foobar2000/SDK/playlist_loader.cpp index 6dab850..2c47296 100644 --- a/sdk/foobar2000/SDK/playlist_loader.cpp +++ b/sdk/foobar2000/SDK/playlist_loader.cpp @@ -5,26 +5,12 @@ #include "file_info_impl.h" #include "input.h" #include "advconfig.h" +#include +#include +#include -#if FOOBAR2000_TARGET_VERSION >= 76 -static void process_path_internal(const char * p_path,const service_ptr_t & p_reader,playlist_loader_callback::ptr callback, abort_callback & abort,playlist_loader_callback::t_entry_type type,const t_filestats & p_stats); - -namespace { - class archive_callback_impl : public archive_callback { - public: - archive_callback_impl(playlist_loader_callback::ptr p_callback, abort_callback & p_abort) : m_callback(p_callback), m_abort(p_abort) {} - bool on_entry(archive * owner,const char * p_path,const t_filestats & p_stats,const service_ptr_t & p_reader) - { - process_path_internal(p_path,p_reader,m_callback,m_abort,playlist_loader_callback::entry_directory_enumerated,p_stats); - return !m_abort.is_aborting(); - } - bool is_aborting() const {return m_abort.is_aborting();} - abort_callback_event get_abort_event() const {return m_abort.get_abort_event();} - private: - const playlist_loader_callback::ptr m_callback; - abort_callback & m_abort; - }; -} +constexpr unsigned allowRecurseBase = 2; // max. 2 archive levels - mitigate droste.zip stack overflow +static void process_path_internal(const char * p_path,const service_ptr_t & p_reader,playlist_loader_callback::ptr callback, abort_callback & abort,playlist_loader_callback::t_entry_type type,const t_filestats & p_stats, unsigned allowRecurse ); bool playlist_loader::g_try_load_playlist(file::ptr fileHint,const char * p_path,playlist_loader_callback::ptr p_callback, abort_callback & p_abort) { // Determine if this file is a playlist or not (which usually means that it's a media file) @@ -40,7 +26,9 @@ bool playlist_loader::g_try_load_playlist(file::ptr fileHint,const char * p_path filesystem::ptr fs; if (filesystem::g_get_interface(fs,filepath)) { if (fs->supports_content_types()) { - fs->open(l_file,filepath,filesystem::open_mode_read,p_abort); + try { + fs->open(l_file,filepath,filesystem::open_mode_read,p_abort); + } catch(exception_io const &) { return false; } // fall thru } } } @@ -67,7 +55,7 @@ bool playlist_loader::g_try_load_playlist(file::ptr fileHint,const char * p_path try { TRACK_CODE("playlist_loader::open",l->open(filepath,l_file,p_callback, p_abort)); return true; - } catch(exception_io_unsupported_format) { + } catch(exception_io_unsupported_format const &) { l_file->reopen(p_abort); } } @@ -82,7 +70,7 @@ bool playlist_loader::g_try_load_playlist(file::ptr fileHint,const char * p_path try { TRACK_CODE("playlist_loader::open",l->open(filepath,l_file,p_callback,p_abort)); return true; - } catch(exception_io_unsupported_format) { + } catch(exception_io_unsupported_format const &) { l_file->reopen(p_abort); } } @@ -126,10 +114,10 @@ static void index_tracks_helper(const char * p_path,const service_ptr_t & service_ptr_t instance; try { input_entry::g_open_for_info_read(instance,p_reader,p_path,p_abort); - } catch(exception_io_unsupported_format) { + } catch(exception_io_unsupported_format const &) { // specifically bail throw; - } catch(exception_io) { + } catch(exception_io const &) { // broken file or some other error, open() failed - show it anyway metadb_handle_ptr handle; p_callback->handle_create(handle, make_playable_location(p_path, 0)); @@ -182,9 +170,9 @@ static void track_indexer__g_get_tracks_wrap(const char * p_path,const service_p bool fail = false; try { index_tracks_helper(p_path,p_reader,p_stats,p_type,p_callback,p_abort, got_input); - } catch(exception_aborted) { + } catch(exception_aborted const &) { throw; - } catch(exception_io_unsupported_format) { + } catch(exception_io_unsupported_format const &) { fail = true; } catch(std::exception const & e) { fail = true; @@ -216,67 +204,78 @@ namespace { return false; } - // SPECIAL HACK - // filesystem service does not present file hidden attrib but we want to weed files/folders out - // so check separately on all native paths (inefficient but meh) class directory_callback_myimpl : public directory_callback { public: - directory_callback_myimpl() : m_addHidden(queryAddHidden()) {} + void main(const char* folder, abort_callback& abort) { + visit(folder); + + abort.check(); + const uint32_t flags = listMode::filesAndFolders | (queryAddHidden() ? listMode::hidden : 0); + + auto workHere = [&] (folder_t const & f) { + filesystem_v2::ptr v2; + if (v2 &= f.m_fs) { + v2->list_directory_ex(f.m_folder.c_str(), *this, flags, abort); + } else { + f.m_fs->list_directory(f.m_folder.c_str(), *this, abort); + } + }; + + workHere( folder_t { folder, filesystem::get(folder) } ); + + for (;; ) { + abort.check(); + auto iter = m_foldersPending.begin(); + if ( iter == m_foldersPending.end() ) break; + auto f = std::move(*iter); m_foldersPending.erase(iter); + + try { + workHere( f ); + } catch (exception_io const & e) { + FB2K_console_formatter() << "Error walking directory (" << e << "): " << f.m_folder.c_str(); + } + } + } bool on_entry(filesystem * owner,abort_callback & p_abort,const char * url,bool is_subdirectory,const t_filestats & p_stats) { p_abort.check(); + if (!visit(url)) return true; filesystem_v2::ptr v2; v2 &= owner; if ( is_subdirectory ) { - try { - if (v2.is_valid()) { - v2->list_directory_ex(url, *this, flags(), p_abort); - } else { - owner->list_directory(url, *this, p_abort); - } - } catch (exception_io const & e) { - FB2K_console_formatter() << "Error walking directory (" << e << "): " << url; - } + m_foldersPending.emplace_back( folder_t { url, owner } ); } else { - // In fb2k 1.4 the default filesystem is v2 and performs hidden file checks -#if FOOBAR2000_TARGET_VERSION < 79 - if ( ! m_addHidden && v2.is_empty() ) { - const char * n = url; - if (_extract_native_path_ptr(n)) { - DWORD att = uGetFileAttributes(n); - if (att == ~0 || (att & FILE_ATTRIBUTE_HIDDEN) != 0) return true; - } - } -#endif - auto i = m_entries.insert_last(); - i->m_path = url; - i->m_stats = p_stats; + m_entries.emplace_back( entry_t { url, p_stats } ); } return true; } - uint32_t flags() const { - uint32_t flags = listMode::filesAndFolders; - if (m_addHidden) flags |= listMode::hidden; - return flags; - } - - const bool m_addHidden; struct entry_t { - pfc::string8 m_path; + std::string m_path; t_filestats m_stats; }; - pfc::chain_list_v2_t m_entries; + std::list m_entries; + bool visit(const char* path) { + return m_visited.insert( path ).second; + } + std::unordered_set m_visited; + + struct folder_t { + std::string m_folder; + filesystem::ptr m_fs; + }; + std::list m_foldersPending; }; } -static void process_path_internal(const char * p_path,const service_ptr_t & p_reader,playlist_loader_callback::ptr callback, abort_callback & abort,playlist_loader_callback::t_entry_type type,const t_filestats & p_stats) +static void process_path_internal(const char * p_path,const service_ptr_t & p_reader,playlist_loader_callback::ptr callback, abort_callback & abort,playlist_loader_callback::t_entry_type type,const t_filestats & p_stats, unsigned allowRecurse) { + if (allowRecurse == 0) return; //p_path must be canonical abort.check(); @@ -288,27 +287,24 @@ static void process_path_internal(const char * p_path,const service_ptr_t if (p_reader.is_empty() && type != playlist_loader_callback::entry_directory_enumerated) { try { directory_callback_myimpl results; - auto fs = filesystem::get(p_path); - filesystem_v2::ptr v2; - if ( v2 &= fs ) v2->list_directory_ex(p_path, results, results.flags(), abort ); - else fs->list_directory(p_path, results, abort); - for( auto i = results.m_entries.first(); i.is_valid(); ++i ) { + results.main( p_path, abort ); + for( auto & i : results.m_entries ) { try { - process_path_internal(i->m_path, 0, callback, abort, playlist_loader_callback::entry_directory_enumerated, i->m_stats); - } catch (exception_aborted) { + process_path_internal(i.m_path.c_str(), 0, callback, abort, playlist_loader_callback::entry_directory_enumerated, i.m_stats, allowRecurse); + } catch (exception_aborted const &) { throw; } catch (std::exception const& e) { - FB2K_console_formatter() << "Error walking path (" << e << "): " << file_path_display(i->m_path); + FB2K_console_formatter() << "Error walking path (" << e << "): " << file_path_display(i.m_path.c_str()); } catch (...) { - FB2K_console_formatter() << "Error walking path (bad exception): " << file_path_display(i->m_path); + FB2K_console_formatter() << "Error walking path (bad exception): " << file_path_display(i.m_path.c_str()); } } return; // successfully enumerated directory - go no further - } catch(exception_aborted) { + } catch(exception_aborted const &) { throw; - } catch (exception_io_not_directory) { + } catch (exception_io_not_directory const &) { // disregard - } catch(exception_io_not_found) { + } catch(exception_io_not_found const &) { // disregard } catch (std::exception const& e) { FB2K_console_formatter() << "Error walking directory (" << e << "): " << p_path; @@ -317,8 +313,7 @@ static void process_path_internal(const char * p_path,const service_ptr_t } } - { - archive_callback_impl archive_results(callback, abort); + if (allowRecurse > 1) { for (auto f : filesystem::enumerate()) { abort.check(); service_ptr_t arch; @@ -326,9 +321,12 @@ static void process_path_internal(const char * p_path,const service_ptr_t if (p_reader.is_valid()) p_reader->reopen(abort); try { - TRACK_CODE("archive::archive_list",arch->archive_list(p_path,p_reader,archive_results,true)); + archive::list_func_t archive_results = [callback, &abort, allowRecurse](const char* p_path, const t_filestats& p_stats, file::ptr p_reader) { + process_path_internal(p_path,p_reader,callback,abort,playlist_loader_callback::entry_directory_enumerated,p_stats,allowRecurse - 1); + }; + TRACK_CODE("archive::archive_list",arch->archive_list(p_path,p_reader,archive_results,/*want readers*/true, abort)); return; - } catch(exception_aborted) {throw;} + } catch(exception_aborted const &) {throw;} catch(...) { // Something failed hard // Is is_our_archive() meaningful? @@ -358,7 +356,7 @@ static void process_path_internal(const char * p_path,const service_ptr_t track_indexer__g_get_tracks_wrap(temp,0,filestats_invalid,playlist_loader_callback::entry_from_playlist,callback, abort); return;//success - } catch(exception_aborted) {throw;} + } catch(exception_aborted const &) {throw;} catch(...) {} } } @@ -371,16 +369,19 @@ static void process_path_internal(const char * p_path,const service_ptr_t namespace { class plcallback_simple : public playlist_loader_callback { public: - void on_progress(const char* p_path) override {} + void on_progress(const char* p_path) override { (void)p_path; } void on_entry(const metadb_handle_ptr& p_item, t_entry_type p_type, const t_filestats& p_stats, bool p_fresh) override { + (void)p_type; (void)p_stats; (void)p_fresh; m_items += p_item; } bool want_info(const metadb_handle_ptr& p_item, t_entry_type p_type, const t_filestats& p_stats, bool p_fresh) override { + (void)p_type; (void)p_stats; (void)p_fresh; return p_item->should_reload(p_stats, p_fresh); } void on_entry_info(const metadb_handle_ptr& p_item, t_entry_type p_type, const t_filestats& p_stats, const file_info& p_info, bool p_fresh) override { + (void)p_type; m_items += p_item; m_hints->add_hint(p_item, p_info, p_stats, p_fresh); } @@ -389,12 +390,17 @@ namespace { m_metadb->handle_create(p_out, p_location); } - bool is_path_wanted(const char* path, t_entry_type type) override { return true; } + bool is_path_wanted(const char* path, t_entry_type type) override { + (void)path; (void)type; + return true; + } bool want_browse_info(const metadb_handle_ptr& p_item, t_entry_type p_type, t_filetimestamp ts) override { + (void)p_item; (void)p_type; (void)ts; return true; } void on_browse_info(const metadb_handle_ptr& p_item, t_entry_type p_type, const file_info& info, t_filetimestamp ts) override { + (void)p_type; metadb_hint_list_v2::ptr v2; if (v2 &= m_hints) v2->add_hint_browse(p_item, info, ts); } @@ -419,7 +425,7 @@ void playlist_loader::g_process_path(const char * p_filename,playlist_loader_cal auto filename = file_path_canonical(p_filename); - process_path_internal(filename,0,callback,abort, type,filestats_invalid); + process_path_internal(filename,0,callback,abort, type,filestats_invalid, allowRecurseBase); } void playlist_loader::g_save_playlist(const char * p_filename,const pfc::list_base_const_t & data,abort_callback & p_abort) @@ -440,7 +446,7 @@ void playlist_loader::g_save_playlist(const char * p_filename,const pfc::list_ba try { TRACK_CODE("playlist_loader::write",l->write(filename,r,data,p_abort)); return; - } catch(exception_io_data) {} + } catch(exception_io_data const &) {} } } while(e.next(l)); throw exception_io_data(); @@ -458,5 +464,3 @@ bool playlist_loader::g_process_path_ex(const char * filename,playlist_loader_ca g_process_path(filename,callback,abort,type); return false; } - -#endif \ No newline at end of file diff --git a/sdk/foobar2000/SDK/playlist_loader.h b/sdk/foobar2000/SDK/playlist_loader.h index c70a3a1..04d4e34 100644 --- a/sdk/foobar2000/SDK/playlist_loader.h +++ b/sdk/foobar2000/SDK/playlist_loader.h @@ -1,6 +1,5 @@ #pragma once -#if FOOBAR2000_TARGET_VERSION >= 76 //! Callback interface receiving item locations from playlist loader. \n //! Typically, you call one of standard services such as playlist_incoming_item_filter instead of implementing this interface and calling playlist_loader methods directly. class NOVTABLE playlist_loader_callback : public service_base { @@ -133,5 +132,3 @@ class NOVTABLE playlist_loader : public service_base { template class playlist_loader_factory_t : public service_factory_single_t {}; - -#endif \ No newline at end of file diff --git a/sdk/foobar2000/SDK/popup_message.cpp b/sdk/foobar2000/SDK/popup_message.cpp index 3e4ec03..8682df8 100644 --- a/sdk/foobar2000/SDK/popup_message.cpp +++ b/sdk/foobar2000/SDK/popup_message.cpp @@ -135,6 +135,7 @@ int popup_message_v3::messageBoxReply(uint32_t status) { return -1; } void popup_message_v3::messageBoxAsync(fb2k::hwnd_t parent, const char* msg, const char* title, unsigned flags, std::function reply) { + PFC_ASSERT( core_api::is_main_thread() ); auto q = setupMessageBox(parent, msg, title, flags); if (reply) { q.reply = fb2k::makeCompletionNotify([reply](unsigned code) { @@ -144,6 +145,7 @@ void popup_message_v3::messageBoxAsync(fb2k::hwnd_t parent, const char* msg, con this->show_query(q); } int popup_message_v3::messageBox(fb2k::hwnd_t parent, const char* msg, const char* title, unsigned flags) { + PFC_ASSERT( core_api::is_main_thread() ); auto q = setupMessageBox(parent, msg, title, flags); uint32_t status = this->show_query_modal(q); return messageBoxReply(status); diff --git a/sdk/foobar2000/SDK/popup_message.h b/sdk/foobar2000/SDK/popup_message.h index 72e69cf..f6ca598 100644 --- a/sdk/foobar2000/SDK/popup_message.h +++ b/sdk/foobar2000/SDK/popup_message.h @@ -6,8 +6,8 @@ //! This interface allows you to show generic nonmodal noninteractive dialog with a text message. This should be used instead of MessageBox where possible.\n //! Usage: use popup_message::g_show / popup_message::g_show_ex static helpers, or popup_message::get() to obtain an instance.\n +//! Thread safety: OK to call from worker threads (will delegate UI ops to main thread automatically). \n //! Note that all strings are UTF-8. - class NOVTABLE popup_message : public service_base { public: enum t_icon {icon_information, icon_error, icon_query}; @@ -46,6 +46,9 @@ class NOVTABLE popup_message : public service_base { #define EXCEPTION_TO_POPUP_MESSAGE(CODE,LABEL) try { CODE; } catch(std::exception const & e) {popup_message::g_complain(LABEL,e);} //! \since 1.1 +//! Extendsion to popup_message API. \n +//! Thread safety: OK to call from worker threads (will delegate UI ops to main thread automatically). \n +//! Note that all strings are UTF-8. class NOVTABLE popup_message_v2 : public service_base { FB2K_MAKE_SERVICE_COREAPI(popup_message_v2); public: @@ -58,6 +61,8 @@ class NOVTABLE popup_message_v2 : public service_base { }; namespace fb2k { + //! \since 2.0 + //! Not really implemented for Windows yet. class popup_toast : public service_base { FB2K_MAKE_SERVICE_COREAPI( popup_toast ); public: @@ -71,8 +76,10 @@ namespace fb2k { class toastFormatter : public pfc::string_formatter { public: - ~toastFormatter() { - if ( this->length() > 0 ) showToast( c_str() ); + ~toastFormatter() noexcept { + try { + if ( this->length() > 0 ) showToast( c_str() ); + } catch(...) {} } }; } @@ -81,6 +88,8 @@ namespace fb2k { //! \since 1.5 +//! MessageBox-like dialog, only non-blocking and with dark mode support under foobar2000 v2.0. \n +//! Call from main thread only (contrary to popup_message / popup_message_v2) !!! class NOVTABLE popup_message_v3 : public service_base { FB2K_MAKE_SERVICE_COREAPI(popup_message_v3); public: diff --git a/sdk/foobar2000/SDK/powerManager.h b/sdk/foobar2000/SDK/powerManager.h index 0d96e2a..9ba99a0 100644 --- a/sdk/foobar2000/SDK/powerManager.h +++ b/sdk/foobar2000/SDK/powerManager.h @@ -1,6 +1,7 @@ #pragma once namespace fb2k { + //! \since 2.0 class powerManager : public service_base { public: enum { @@ -10,11 +11,13 @@ namespace fb2k { flagDisplay = flagStrong }; - //! Blocks device sleep for the duration of returned object's lifetime. - //! By default we ask politely but can be still put to sleep by the OS. Specify flagStrong to force the device into awake state (possibly at cost of keeping the screen up). + //! Blocks device sleep for the duration of returned object's lifetime. \n + //! By default we ask politely but can be still put to sleep by the OS. Specify flagStrong to force the device into awake state (possibly at cost of keeping the screen up). \n + //! Thread safety: OK to call from any thread. virtual objRef makeTask(const char* name, unsigned flags) = 0; - //! Returns whether we're running on AC power (not on battery). + //! Returns whether we're running on AC power (not on battery). \n + //! Thread safety: OK to call from any thread. virtual bool haveACPower() = 0; objRef makeTaskWeak(const char* name) { return makeTask(name, 0); } diff --git a/sdk/foobar2000/SDK/preferences_page.cpp b/sdk/foobar2000/SDK/preferences_page.cpp index 85bb535..7103502 100644 --- a/sdk/foobar2000/SDK/preferences_page.cpp +++ b/sdk/foobar2000/SDK/preferences_page.cpp @@ -3,8 +3,12 @@ #include "coreversion.h" void preferences_page::get_help_url_helper(pfc::string_base & out, const char * category, const GUID & id, const char * name) { - out.reset(); - out << "http://help.foobar2000.org/" << core_version_info::g_get_version_string() << "/" << category << "/" << pfc::print_guid(id) << "/" << name; + out = "https://help.foobar2000.org/"; + pfc::urlEncodeAppend(out, core_version_info::g_get_version_string()); + out << "/"; + pfc::urlEncodeAppend(out, category); + out << "/" << pfc::print_guid(id) << "/"; + pfc::urlEncodeAppend(out, name); } bool preferences_page::get_help_url(pfc::string_base & p_out) { get_help_url_helper(p_out,"preferences",get_guid(), get_name()); diff --git a/sdk/foobar2000/SDK/preferences_page.h b/sdk/foobar2000/SDK/preferences_page.h index 3a01e96..b6132d1 100644 --- a/sdk/foobar2000/SDK/preferences_page.h +++ b/sdk/foobar2000/SDK/preferences_page.h @@ -26,18 +26,28 @@ class preferences_state { //! In 1.0 and newer you should always derive from preferences_page_v3 rather than from preferences_page directly. class NOVTABLE preferences_page : public service_base { public: +#ifdef _WIN32 //! Obsolete. - virtual fb2k::hwnd_t create(fb2k::hwnd_t p_parent) { uBugCheck(); } + virtual fb2k::hwnd_t create(fb2k::hwnd_t p_parent) { (void)p_parent; uBugCheck(); } +#endif + +#ifdef __APPLE__ + //! Returns fb2k::NSObjectWrapper holding your NSViewController + virtual service_ptr instantiate( ) = 0; +#endif + //! Retrieves name of the preferences page to be displayed in preferences tree (static string). virtual const char * get_name() = 0; //! Retrieves GUID of the page. virtual GUID get_guid() = 0; //! Retrieves GUID of parent page/branch of this page. See preferences_page::guid_* constants for list of standard parent GUIDs. Can also be a GUID of another page or a branch (see: preferences_branch). virtual GUID get_parent_guid() = 0; +#ifdef _WIN32 //! Obsolete. virtual bool reset_query() { return false; } //! Obsolete. virtual void reset() {} +#endif //! Retrieves help URL. Without overriding it, it will redirect to foobar2000 wiki. virtual bool get_help_url(pfc::string_base & p_out); @@ -108,6 +118,7 @@ class preferences_branch_factory : public _preferences_branch_factory { }; +#ifdef _WIN32 class preferences_page_callback : public service_base { FB2K_MAKE_SERVICE_INTERFACE(preferences_page_callback, service_base) public: @@ -132,13 +143,16 @@ class preferences_page_instance : public service_base { //! Resets this page's content to the default values. Does not apply any changes - lets user preview the changes before hitting "apply". virtual void reset() = 0; }; +#endif //! \since 1.0 //! Implements a preferences page. class preferences_page_v3 : public preferences_page_v2 { FB2K_MAKE_SERVICE_INTERFACE(preferences_page_v3, preferences_page_v2) public: +#ifdef _WIN32 virtual preferences_page_instance::ptr instantiate(fb2k::hwnd_t parent, preferences_page_callback::ptr callback) = 0; +#endif }; //! \since 1.5 diff --git a/sdk/foobar2000/SDK/replaygain_info.cpp b/sdk/foobar2000/SDK/replaygain_info.cpp index eeda9cb..98c9e71 100644 --- a/sdk/foobar2000/SDK/replaygain_info.cpp +++ b/sdk/foobar2000/SDK/replaygain_info.cpp @@ -51,10 +51,7 @@ bool replaygain_info::g_format_peak(float p_value,char p_buffer[text_buffer_size void replaygain_info::reset() { - m_album_gain = gain_invalid; - m_track_gain = gain_invalid; - m_album_peak = peak_invalid; - m_track_peak = peak_invalid; + *this = replaygain_info(); } #define meta_album_gain "replaygain_album_gain" @@ -65,32 +62,32 @@ void replaygain_info::reset() bool replaygain_info::g_is_meta_replaygain(const char * p_name,t_size p_name_len) { return - stricmp_utf8_ex(p_name,p_name_len,meta_album_gain,~0) == 0 || - stricmp_utf8_ex(p_name,p_name_len,meta_album_peak,~0) == 0 || - stricmp_utf8_ex(p_name,p_name_len,meta_track_gain,~0) == 0 || - stricmp_utf8_ex(p_name,p_name_len,meta_track_peak,~0) == 0; + stricmp_utf8_ex(p_name,p_name_len,meta_album_gain,SIZE_MAX) == 0 || + stricmp_utf8_ex(p_name,p_name_len,meta_album_peak,SIZE_MAX) == 0 || + stricmp_utf8_ex(p_name,p_name_len,meta_track_gain,SIZE_MAX) == 0 || + stricmp_utf8_ex(p_name,p_name_len,meta_track_peak,SIZE_MAX) == 0; } bool replaygain_info::set_from_meta_ex(const char * p_name,t_size p_name_len,const char * p_value,t_size p_value_len) { RG_FPU(); - if (stricmp_utf8_ex(p_name,p_name_len,meta_album_gain,~0) == 0) + if (stricmp_utf8_ex(p_name,p_name_len,meta_album_gain,SIZE_MAX) == 0) { m_album_gain = (float)pfc::string_to_float(p_value,p_value_len); return true; } - else if (stricmp_utf8_ex(p_name,p_name_len,meta_album_peak,~0) == 0) + else if (stricmp_utf8_ex(p_name,p_name_len,meta_album_peak,SIZE_MAX) == 0) { m_album_peak = (float)pfc::string_to_float(p_value,p_value_len); if (m_album_peak < 0) m_album_peak = 0; return true; } - else if (stricmp_utf8_ex(p_name,p_name_len,meta_track_gain,~0) == 0) + else if (stricmp_utf8_ex(p_name,p_name_len,meta_track_gain,SIZE_MAX) == 0) { m_track_gain = (float)pfc::string_to_float(p_value,p_value_len); return true; } - else if (stricmp_utf8_ex(p_name,p_name_len,meta_track_peak,~0) == 0) + else if (stricmp_utf8_ex(p_name,p_name_len,meta_track_peak,SIZE_MAX) == 0) { m_track_peak = (float)pfc::string_to_float(p_value,p_value_len); if (m_track_peak < 0) m_track_peak = 0; @@ -164,3 +161,12 @@ replaygain_info replaygain_info::g_merge(replaygain_info r1,replaygain_info r2) if (!ret.is_track_peak_present()) ret.m_track_peak = r2.m_track_peak; return ret; } + + +void replaygain_info::for_each(for_each_t f) const { + t_text_buffer buffer; + if (format_track_gain(buffer)) f(meta_track_gain, buffer); + if (format_track_peak(buffer)) f(meta_track_peak, buffer); + if (format_album_gain(buffer)) f(meta_album_gain, buffer); + if (format_album_peak(buffer)) f(meta_album_peak, buffer); +} \ No newline at end of file diff --git a/sdk/foobar2000/SDK/search_tools.h b/sdk/foobar2000/SDK/search_tools.h index 6e2e740..4b7b232 100644 --- a/sdk/foobar2000/SDK/search_tools.h +++ b/sdk/foobar2000/SDK/search_tools.h @@ -97,3 +97,49 @@ class search_filter_manager_v3 : public search_filter_manager_v2 { //! This method INVALIDATES passed objects. Do not try to use them afterwards. virtual search_filter_v4::ptr combine(pfc::list_base_const_t const & arg, combine_t how, completion_notify::ptr changeNotify, t_uint32 flags) = 0; }; + +//! \since 2.0 +class search_index : public service_base { + FB2K_MAKE_SERVICE_INTERFACE(search_index, service_base) +public: + enum { + flag_sort = 1 << 0, + }; + + //! Searches Tracks in this index for tracks matching criteria. + //! @returns list of metadb_handles. Safe to use arr->as_list_of() to get a pfc::list_base_const_t + //! @param subset Optional: pass subset of tracks in this index to search - whole index is searched if nullptr is passed. + //! @param flags Optional: set flag_sort to sort output + //! Thread safety: call from any thread. + virtual fb2k::arrayRef search(search_filter::ptr pattern, metadb_handle_list_cptr subset, uint32_t flags, abort_callback& abort) = 0; + //! Performs hit test on a group of tracks that are a subset of tracks in this index. \n + //! Thread safety: call from any thread. + virtual void test(search_filter::ptr pattern, metadb_handle_list_cref items, bool* out, abort_callback& abort) = 0; + + //! Add tracks to a custom index. \n + //! Illegal to call on library or playlist indexes. \n + //! Thread safety: call from any thread. + virtual void add_tracks(metadb_handle_list_cref, metadb_io_callback_v2_data* dataIfAvail) = 0; + //! Remove tracks from a custom index. \n + //! Illegal to call on library or playlist indexes. \n + //! Thread safety: call from any thread. + virtual void remove_tracks(metadb_handle_list_cref) = 0; +}; + +//! \since 2.0 +class search_index_manager : public service_base { + FB2K_MAKE_SERVICE_COREAPI(search_index_manager); +public: + //! Create a custom index on any data set. \n + //! OK to call from any thread. + virtual search_index::ptr create_index(metadb_handle_list_cref items, metadb_io_callback_v2_data* dataIfAvail) = 0; + + //! Create a search index referencing a playlist. \n + //! Specify null GUID to follow active playlist (typical playlist search). \n + //! Call from main thread to obtain index, then can use obtained object from any thread. + virtual search_index::ptr create_playlist_index(const GUID& playlistID = pfc::guid_null) = 0; + + //! Returns a shared object indexing user's media library. \n + //! Call from main thread to obtain index, then can use obtained object from any thread. + virtual search_index::ptr get_library_index() = 0; +}; \ No newline at end of file diff --git a/sdk/foobar2000/SDK/service.h b/sdk/foobar2000/SDK/service.h index 956b903..0b7b9ea 100644 --- a/sdk/foobar2000/SDK/service.h +++ b/sdk/foobar2000/SDK/service.h @@ -32,9 +32,24 @@ class service_base; template class service_ptr_base_t { + typedef service_ptr_base_t self_t; public: inline T* get_ptr() const throw() {return m_ptr;} typedef T obj_t; + + + inline bool operator==(const self_t & other) const noexcept {return this->m_ptr == other.m_ptr;} + inline bool operator!=(const self_t & other) const noexcept {return this->m_ptr != other.m_ptr;} + + inline bool operator>(const self_t & other) const noexcept {return this->m_ptr > other.m_ptr;} + inline bool operator<(const self_t & other) const noexcept {return this->m_ptr < other.m_ptr;} + + inline bool operator==(T * other) const noexcept {return this->m_ptr == other;} + inline bool operator!=(T * other) const noexcept {return this->m_ptr != other;} + + inline bool operator>(T * other) const noexcept {return this->m_ptr > other;} + inline bool operator<(T * other) const noexcept {return this->m_ptr < other;} + protected: T * m_ptr; }; @@ -124,18 +139,6 @@ class service_ptr_t : public service_ptr_base_t { inline bool is_valid() const throw() {return this->m_ptr != NULL;} inline bool is_empty() const throw() {return this->m_ptr == NULL;} - inline bool operator==(const service_ptr_base_t & p_item) const throw() {return this->m_ptr == p_item.get_ptr();} - inline bool operator!=(const service_ptr_base_t & p_item) const throw() {return this->m_ptr != p_item.get_ptr();} - - inline bool operator>(const service_ptr_base_t & p_item) const throw() {return this->m_ptr > p_item.get_ptr();} - inline bool operator<(const service_ptr_base_t & p_item) const throw() {return this->m_ptr < p_item.get_ptr();} - - inline bool operator==(T * p_item) const throw() {return this->m_ptr == p_item;} - inline bool operator!=(T * p_item) const throw() {return this->m_ptr != p_item;} - - inline bool operator>(T * p_item) const throw() {return this->m_ptr > p_item;} - inline bool operator<(T * p_item) const throw() {return this->m_ptr < p_item;} - template inline t_self & operator<<(service_ptr_t & p_source) throw() {attach(p_source.detach());return *this;} template @@ -266,18 +269,6 @@ class service_nnptr_t : public service_ptr_base_t { inline bool is_valid() const throw() {return true;} inline bool is_empty() const throw() {return false;} - inline bool operator==(const service_ptr_base_t & p_item) const throw() {return this->m_ptr == p_item.get_ptr();} - inline bool operator!=(const service_ptr_base_t & p_item) const throw() {return this->m_ptr != p_item.get_ptr();} - - inline bool operator>(const service_ptr_base_t & p_item) const throw() {return this->m_ptr > p_item.get_ptr();} - inline bool operator<(const service_ptr_base_t & p_item) const throw() {return this->m_ptr < p_item.get_ptr();} - - inline bool operator==(T * p_item) const throw() {return this->m_ptr == p_item;} - inline bool operator!=(T * p_item) const throw() {return this->m_ptr != p_item;} - - inline bool operator>(T * p_item) const throw() {return this->m_ptr > p_item;} - inline bool operator<(T * p_item) const throw() {return this->m_ptr < p_item;} - inline T* _duplicate_ptr() const throw() {//should not be used ! temporary ! service_add_ref_safe(this->m_ptr); return this->m_ptr; @@ -591,7 +582,7 @@ void _standard_api_get_internal(service_ptr & out, const GUID & classID); bool _standard_api_try_get_internal(service_ptr & out, const GUID & classID); template inline void standard_api_create_t(service_ptr_t & p_out) { - if (pfc::is_same_type::value) { + if constexpr (pfc::is_same_type::value) { _standard_api_create_internal(p_out._as_base_ptr(), T::class_guid); FB2K_ASSERT_VALID_SERVICE_PTR(p_out); } else { @@ -622,7 +613,7 @@ inline bool static_api_test_t() { typedef typename T::t_interface_entrypoint EP; service_class_helper_t helper; if (helper.get_count() != 1) return false; - if (!pfc::is_same_type::value) { + if constexpr (!pfc::is_same_type::value) { service_ptr_t t; if (!helper.create(0)->service_query_t(t)) return false; } @@ -674,7 +665,7 @@ class service_enum_t { template bool next(service_ptr_t & p_out) { pfc::assert_same_type(); - if (pfc::is_same_type::value) { + if constexpr (pfc::is_same_type::value) { return _next(reinterpret_cast&>(p_out)); } else { service_ptr_t temp; @@ -740,7 +731,7 @@ namespace fb2k { service_ptr_t std_api_get() { typedef typename api_t::t_interface_entrypoint entrypoint_t; service_ptr_t ret; - if (pfc::is_same_type::value) { + if constexpr (pfc::is_same_type::value) { _standard_api_get_internal(ret._as_base_ptr(), api_t::class_guid); } else { ret ^= std_api_get(); @@ -753,7 +744,7 @@ namespace fb2k { template bool std_api_try_get( service_ptr_t & ret ) { typedef typename api_t::t_interface_entrypoint entrypoint_t; - if (pfc::is_same_type::value) { + if constexpr (pfc::is_same_type::value) { return _standard_api_try_get_internal(ret._as_base_ptr(), api_t::class_guid); } else { service_ptr_t temp; @@ -774,6 +765,17 @@ class service_factory_t : public service_factory_base_t +class service_factory_singleton_t : public service_factory_base_t { +public: + void instance_create(service_ptr_t & p_out) override { + this->pass_instance(p_out, &FB2K_SERVICE_SINGLETON(T) ); + } + + inline T& get_static_instance() { return &FB2K_SERVICE_SINGLETON(T); } + inline const T& get_static_instance() const { return &FB2K_SERVICE_SINGLETON(T); } +}; + template class service_factory_single_t : public service_factory_base_t { service_impl_single_t g_instance; @@ -843,7 +845,7 @@ class service_factory_single_transparent_t : public service_factory_base_t _FB2K_UNIQUE_NAME(g_factory_) FB2K_SERVICE_FACTORY_ATTR; +#define FB2K_SERVICE_FACTORY( TYPE ) static ::service_factory_singleton_t< TYPE > _FB2K_UNIQUE_NAME(g_factory_) FB2K_SERVICE_FACTORY_ATTR; #define FB2K_SERVICE_FACTORY_LATEINIT( TYPE ) static ::service_factory_single_v2_t< TYPE > _FB2K_UNIQUE_NAME(g_factory_) FB2K_SERVICE_FACTORY_ATTR; #define FB2K_SERVICE_FACTORY_PARAMS( TYPE, ... ) static ::service_factory_single_t< TYPE > _FB2K_UNIQUE_NAME(g_factory_) ( __VA_ARGS__ ); #define FB2K_SERVICE_FACTORY_DYNAMIC( TYPE ) static ::service_factory_t< TYPE > _FB2K_UNIQUE_NAME(g_factory_) FB2K_SERVICE_FACTORY_ATTR; diff --git a/sdk/foobar2000/SDK/service_impl.h b/sdk/foobar2000/SDK/service_impl.h index 1304643..88415a0 100644 --- a/sdk/foobar2000/SDK/service_impl.h +++ b/sdk/foobar2000/SDK/service_impl.h @@ -135,4 +135,6 @@ namespace fb2k { service_ptr_t service_new(arg_t && ... arg) { return new service_impl_t< obj_t > ( std::forward (arg) ... ); } -} \ No newline at end of file +} + +#define FB2K_SERVICE_SINGLETON(class_t) PFC_SINGLETON( service_impl_single_t< class_t> ) \ No newline at end of file diff --git a/sdk/foobar2000/SDK/system_time_keeper.h b/sdk/foobar2000/SDK/system_time_keeper.h index 27b9cec..25f559e 100644 --- a/sdk/foobar2000/SDK/system_time_keeper.h +++ b/sdk/foobar2000/SDK/system_time_keeper.h @@ -24,7 +24,7 @@ class system_time_keeper : public service_base { class system_time_callback_impl : public system_time_callback { public: - system_time_callback_impl() : m_registered() {} + system_time_callback_impl() {} ~system_time_callback_impl() {stop_timer();} void stop_timer() { @@ -40,10 +40,11 @@ class system_time_callback_impl : public system_time_callback { m_registered = true; } - void on_time_changed(t_filetimestamp newVal) {} + //! Override me + void on_time_changed(t_filetimestamp) override {} PFC_CLASS_NOT_COPYABLE_EX(system_time_callback_impl) private: - bool m_registered; + bool m_registered = false; }; diff --git a/sdk/foobar2000/SDK/tag_processor.cpp b/sdk/foobar2000/SDK/tag_processor.cpp index 4e08483..22bd73a 100644 --- a/sdk/foobar2000/SDK/tag_processor.cpp +++ b/sdk/foobar2000/SDK/tag_processor.cpp @@ -18,6 +18,21 @@ void tag_processor_trailing::write_apev2_id3v1(const service_ptr_t & p_fil } +t_filesize tag_processor_trailing::read_v2_(const file::ptr & file, file_info& outInfo, abort_callback& abort) { + { + tag_processor_trailing_v2::ptr v2; + if (v2 &= this) return v2->read_v2(file, outInfo, abort); + } + // no new API, emulate with old + try { + t_filesize ret = filesize_invalid; + this->read_ex(file, outInfo, ret, abort); + PFC_ASSERT(ret != filesize_invalid); + return ret; + } catch (exception_io_data const&) { + return filesize_invalid; + } +} enum { @@ -125,47 +140,53 @@ void tag_processor::read_trailing_ex(const service_ptr_t & p_file,file_inf tag_processor_trailing::get()->read_ex(p_file,p_info,p_tagoffset,p_abort); } +t_filesize tag_processor::read_trailing_nothrow(const service_ptr_t& p_file, file_info& p_info, abort_callback& p_abort) { + return tag_processor_trailing::get()->read_v2_(p_file, p_info, p_abort); +} + void tag_processor::read_id3v2(const service_ptr_t & p_file,file_info & p_info,abort_callback & p_abort) { tag_processor_id3v2::get()->read(p_file,p_info,p_abort); } -void tag_processor::read_id3v2_trailing(const service_ptr_t & p_file,file_info & p_info,abort_callback & p_abort) +void tag_processor::read_id3v2_trailing(const service_ptr_t& p_file, file_info& p_info, abort_callback& p_abort) { + if (!read_id3v2_trailing_nothrow(p_file, p_info, p_abort)) throw exception_tag_not_found(); +} + +bool tag_processor::read_id3v2_trailing_nothrow(const service_ptr_t & p_file,file_info & p_info,abort_callback & p_abort) { file_info_impl id3v2, trailing; - bool have_id3v2 = true, have_trailing = true; - try { - read_id3v2(p_file,id3v2,p_abort); - } catch(exception_io_data) { - have_id3v2 = false; - } - - if (have_id3v2) { - // Disregard empty ID3v2 - if (id3v2.meta_get_count() == 0 && id3v2.get_replaygain().get_value_count() == 0) { - have_id3v2 = false; - } - } - if (!have_id3v2 || !p_file->is_remote()) try { - read_trailing(p_file,trailing,p_abort); - } catch(exception_io_data) { - have_trailing = false; + const bool have_id3v2 = tag_processor_id3v2::get()->read_v2_(p_file, id3v2, p_abort); + const bool have_id3v2_text = have_id3v2 && id3v2.meta_get_count() > 0; + + bool have_trailing = false; + if (!have_id3v2_text || !p_file->is_remote()) { + have_trailing = tag_processor_trailing::get()->read_v2_(p_file, trailing, p_abort) != filesize_invalid; } - if (!have_id3v2 && !have_trailing) throw exception_tag_not_found(); + if (!have_id3v2 && !have_trailing) return false; if (have_id3v2) { p_info._set_tag(id3v2); if (have_trailing) p_info._add_tag(trailing); + if (! have_id3v2_text ) p_info.copy_meta(trailing); } else { p_info._set_tag(trailing); } + + return true; } void tag_processor::skip_id3v2(const service_ptr_t & p_file,t_filesize & p_size_skipped,abort_callback & p_abort) { tag_processor_id3v2::g_skip(p_file,p_size_skipped,p_abort); } +t_filesize tag_processor::skip_id3v2(file::ptr const & f, abort_callback & a) { + t_filesize ret = 0; + skip_id3v2(f, ret, a); + return ret; +} + bool tag_processor::is_id3v1_sufficient(const file_info & p_info) { return tag_processor_trailing::get()->is_id3v1_sufficient(p_info); @@ -174,4 +195,4 @@ bool tag_processor::is_id3v1_sufficient(const file_info & p_info) void tag_processor::truncate_to_id3v1(file_info & p_info) { tag_processor_trailing::get()->truncate_to_id3v1(p_info); -} \ No newline at end of file +} diff --git a/sdk/foobar2000/SDK/tag_processor.h b/sdk/foobar2000/SDK/tag_processor.h index d90a348..bb69e97 100644 --- a/sdk/foobar2000/SDK/tag_processor.h +++ b/sdk/foobar2000/SDK/tag_processor.h @@ -20,7 +20,7 @@ class NOVTABLE tag_write_callback { class tag_write_callback_dummy : public tag_write_callback { public: - bool open_temp_file(service_ptr_t & p_out,abort_callback & p_abort) {return false;} + bool open_temp_file(service_ptr_t& p_out, abort_callback& p_abort) override { (void)p_out; (void)p_abort; return false; } }; //! For internal use - call tag_processor namespace methods instead. @@ -39,9 +39,19 @@ class NOVTABLE tag_processor_id3v2 : public service_base static void g_remove_ex(tag_write_callback & p_callback,const service_ptr_t & p_file,t_filesize & p_size_removed,abort_callback & p_abort); static uint32_t g_tagsize(const void* pHeader10bytes); + bool read_v2_(file::ptr const& file, file_info& outInfo, abort_callback& abort); + FB2K_MAKE_SERVICE_COREAPI(tag_processor_id3v2); }; +//! \since 2.2 +class NOVTABLE tag_processor_id3v2_v2 : public tag_processor_id3v2 { + FB2K_MAKE_SERVICE_COREAPI_EXTENSION(tag_processor_id3v2_v2, tag_processor_id3v2); +public: + //! Returns bool (valid tag found or not) instead of throwing exception_tag_not_found. + virtual bool read_v2(file::ptr const& file, file_info& outInfo, abort_callback& abort) = 0; +}; + //! For internal use - call tag_processor namespace methods instead. class NOVTABLE tag_processor_trailing : public service_base { @@ -62,10 +72,19 @@ class NOVTABLE tag_processor_trailing : public service_base void write_apev2(const service_ptr_t & p_file,const file_info & p_info,abort_callback & p_abort); void write_apev2_id3v1(const service_ptr_t & p_file,const file_info & p_info,abort_callback & p_abort); + t_filesize read_v2_(const file::ptr & file, file_info& outInfo, abort_callback& abort); FB2K_MAKE_SERVICE_COREAPI(tag_processor_trailing); }; +//! \since 2.2 +class NOVTABLE tag_processor_trailing_v2 : public tag_processor_trailing { + FB2K_MAKE_SERVICE_COREAPI_EXTENSION(tag_processor_trailing_v2, tag_processor_trailing); +public: + //! Returns tag offset, filesize_invalid if not found - does not throw exception_tag_not_found. + virtual t_filesize read_v2(const file::ptr & file, file_info& outInfo, abort_callback& abort) = 0; +}; + namespace tag_processor { //! Strips all recognized tags from the file and writes an ID3v1 tag with specified info. void write_id3v1(const service_ptr_t & p_file,const file_info & p_info,abort_callback & p_abort); @@ -85,19 +104,24 @@ namespace tag_processor { void remove_trailing(const service_ptr_t & p_file,abort_callback & p_abort); //! Removes ID3v2 tags from the file. Returns true when a tag was removed, false when the file was not altered. bool remove_id3v2(const service_ptr_t & p_file,abort_callback & p_abort); - //! Removes ID3v2 and trailing tags from specified file (not to be confused with trailing ID3v2 which are not yet supported). + //! Removes ID3v2 and trailing tags from specified file (not to be confused with trailing ID3v2 which are not supported). void remove_id3v2_trailing(const service_ptr_t & p_file,abort_callback & p_abort); - //! Reads trailing tags from the file. + //! Reads trailing tags from the file. Throws exception_tag_not_found if no tag was found. void read_trailing(const service_ptr_t & p_file,file_info & p_info,abort_callback & p_abort); - //! Reads trailing tags from the file. Extended version, returns offset at which parsed tags start. \n - //! p_tagoffset set to offset of found tags, to EOF if no tags were found. + //! Reads trailing tags from the file. Extended version, returns offset at which parsed tags start. Throws exception_tag_not_found if no tag was found. + //! p_tagoffset set to offset of found tags on success. void read_trailing_ex(const service_ptr_t & p_file,file_info & p_info,t_filesize & p_tagoffset,abort_callback & p_abort); - //! Reads ID3v2 tags from specified file. + //! Non-throwing version of read_trailing, returns offset at which tags begin, filesize_invalid if no tags found instead of throwing exception_tag_not_found. + t_filesize read_trailing_nothrow(const service_ptr_t& p_file, file_info& p_info, abort_callback& p_abort); + //! Reads ID3v2 tags from specified file. Throws exception_tag_not_found if no tag was found. void read_id3v2(const service_ptr_t & p_file,file_info & p_info,abort_callback & p_abort); - //! Reads ID3v2 and trailing tags from specified file (not to be confused with trailing ID3v2 which are not yet supported). + //! Reads ID3v2 and trailing tags from specified file (not to be confused with trailing ID3v2 which are not supported). Throws exception_tag_not_found if neither tag type was found. void read_id3v2_trailing(const service_ptr_t & p_file,file_info & p_info,abort_callback & p_abort); + //! Non-throwing version of read_id3v2_trailing, returns bool indicating whether any tag was read instead of throwing exception_tag_not_found. + bool read_id3v2_trailing_nothrow(const service_ptr_t& p_file, file_info& p_info, abort_callback& p_abort); void skip_id3v2(const service_ptr_t & p_file,t_filesize & p_size_skipped,abort_callback & p_abort); + t_filesize skip_id3v2(file::ptr const & f, abort_callback & a); bool is_id3v1_sufficient(const file_info & p_info); void truncate_to_id3v1(file_info & p_info); diff --git a/sdk/foobar2000/SDK/tag_processor_id3v2.cpp b/sdk/foobar2000/SDK/tag_processor_id3v2.cpp index 71ea6b7..6472436 100644 --- a/sdk/foobar2000/SDK/tag_processor_id3v2.cpp +++ b/sdk/foobar2000/SDK/tag_processor_id3v2.cpp @@ -103,7 +103,7 @@ void tag_processor_id3v2::g_skip_at(const service_ptr_t & p_file,t_filesiz try { p_file->seek ( p_base + ret, p_abort ); - } catch(exception_io_seek_out_of_range) { + } catch(exception_io_seek_out_of_range const &) { p_file->seek( p_base, p_abort ); p_size_skipped = 0; return; @@ -111,3 +111,17 @@ void tag_processor_id3v2::g_skip_at(const service_ptr_t & p_file,t_filesiz p_size_skipped = ret; } + +bool tag_processor_id3v2::read_v2_(file::ptr const& file, file_info& outInfo, abort_callback& abort) { + { + tag_processor_id3v2_v2::ptr v2; + if (v2 &= this) return v2->read_v2(file, outInfo, abort); + } + // emulate new behavior with old API + try { + this->read(file, outInfo, abort); + return true; + } catch (exception_io_data const&) { + return false; + } +} \ No newline at end of file diff --git a/sdk/foobar2000/SDK/threaded_process.cpp b/sdk/foobar2000/SDK/threaded_process.cpp index 16796f6..f86a108 100644 --- a/sdk/foobar2000/SDK/threaded_process.cpp +++ b/sdk/foobar2000/SDK/threaded_process.cpp @@ -29,11 +29,13 @@ void threaded_process_status::set_progress_secondary_float(double p_state) bool threaded_process::g_run_modal(service_ptr_t p_callback,unsigned p_flags,fb2k::hwnd_t p_parent,const char * p_title,t_size p_title_len) { + PFC_ASSERT( core_api::is_main_thread() ); return threaded_process::get()->run_modal(p_callback,p_flags,p_parent,p_title,p_title_len); } bool threaded_process::g_run_modeless(service_ptr_t p_callback,unsigned p_flags,fb2k::hwnd_t p_parent,const char * p_title,t_size p_title_len) { + PFC_ASSERT( core_api::is_main_thread() ); return threaded_process::get()->run_modeless(p_callback,p_flags,p_parent,p_title,p_title_len); } @@ -57,8 +59,9 @@ void threaded_process_status::set_items(pfc::list_base_const_t cons if (count == 1) { set_item_path(items[0]); } pfc::string8 acc; + filesystem::ptr fs; for (size_t w = 0; w < count; ++w) { - pfc::string8 name = pfc::string_filename_ext(items[w]); + pfc::string8 name = fb2k::filename_ext(items[w], fs); if (w > 0 && acc.length() + name.length() > set_items_max_characters) { acc << " and " << (count - w) << " more"; break; @@ -76,8 +79,9 @@ void threaded_process_status::set_items(metadb_handle_list_cref items) { if ( count == 1 ) { set_item_path(items[0]->get_path()); } pfc::string8 acc; + filesystem::ptr fs; for( size_t w = 0; w < count; ++w ) { - pfc::string8 name = pfc::string_filename_ext(items[w]->get_path()); + pfc::string8 name = fb2k::filename_ext(items[w]->get_path(), fs); if ( w > 0 && acc.length() + name.length() > set_items_max_characters) { acc << " and " << (count-w) << " more"; break; diff --git a/sdk/foobar2000/SDK/threaded_process.h b/sdk/foobar2000/SDK/threaded_process.h index 3fd92cc..0e935fe 100644 --- a/sdk/foobar2000/SDK/threaded_process.h +++ b/sdk/foobar2000/SDK/threaded_process.h @@ -7,15 +7,15 @@ class threaded_process_status { enum {progress_min = 0, progress_max = 5000}; //! Sets the primary progress bar state; scale from progress_min to progress_max. - virtual void set_progress(t_size p_state) {} + virtual void set_progress(t_size p_state) { (void)p_state; } //! Sets the secondary progress bar state; scale from progress_min to progress_max. - virtual void set_progress_secondary(t_size p_state) {} + virtual void set_progress_secondary(t_size p_state) { (void)p_state; } //! Sets the currently progressed item label. When working with files, you should use set_file_path() instead. - virtual void set_item(const char * p_item,t_size p_item_len = SIZE_MAX) {} + virtual void set_item(const char* p_item, t_size p_item_len = SIZE_MAX) { (void)p_item; (void)p_item_len; } //! Sets the currently progressed item label; treats the label as a file path. - virtual void set_item_path(const char * p_item,t_size p_item_len = SIZE_MAX) {} + virtual void set_item_path(const char* p_item, t_size p_item_len = SIZE_MAX) { (void)p_item; (void)p_item_len; } //! Sets the title of the dialog. You normally don't need this function unless you want to override the title you set when initializing the threaded_process. - virtual void set_title(const char * p_title,t_size p_title_len = SIZE_MAX) {} + virtual void set_title(const char* p_title, t_size p_title_len = SIZE_MAX) { (void)p_title; (void)p_title_len; } //! Should not be used. virtual void force_update() {} //! Returns whether the process is paused. @@ -54,11 +54,11 @@ class NOVTABLE threaded_process_callback : public service_base { //! Called from the main thread before spawning the worker thread. \n //! Note that you should not access the window handle passed to on_init() in the worker thread later on. - virtual void on_init(ctx_t p_wnd) {} + virtual void on_init(ctx_t p_wnd) { (void)p_wnd; } //! Called from the worker thread. Do all the hard work here. virtual void run(threaded_process_status & p_status,abort_callback & p_abort) = 0; //! Called after the worker thread has finished executing. - virtual void on_done(ctx_t p_wnd,bool p_was_aborted) {} + virtual void on_done(ctx_t p_wnd, bool p_was_aborted) { (void)p_wnd; (void)p_was_aborted; } //! Safely prevent destruction from worker threads. static bool serviceRequiresMainThreadDestructor() { return true; } @@ -96,14 +96,16 @@ class NOVTABLE threaded_process : public service_base { }; //! Runs a synchronous threaded_process operation - the function does not return until the operation has completed, though the app UI is not frozen and the operation is abortable. \n - //! This API is obsolete and should not be used. Please use run_modeless() instead if possible. + //! This API is obsolete and should not be used. Please use run_modeless() instead if possible. \n + //! Call from main thread only. //! @param p_callback Interface to your threaded_process client. //! @param p_flags Flags describing requested dialog functionality. See threaded_process::flag_* constants. //! @param p_parent Parent window for the progress dialog - typically core_api::get_main_window(). //! @param p_title Initial title of the dialog. //! @returns True if the operation has completed normally, false if the user has aborted the operation. In case of a catastrophic failure such as dialog creation failure, exceptions will be thrown. virtual bool run_modal(service_ptr_t p_callback,unsigned p_flags,fb2k::hwnd_t p_parent,const char * p_title,t_size p_title_len = SIZE_MAX) = 0; - //! Runs an asynchronous threaded_process operation. + //! Runs an asynchronous threaded_process operation. \n + //! Call from main thread only. //! @param p_callback Interface to your threaded_process client. //! @param p_flags Flags describing requested dialog functionality. See threaded_process::flag_* constants. //! @param p_parent Parent window for the progress dialog - typically core_api::get_main_window(). diff --git a/sdk/foobar2000/SDK/threadsLite.h b/sdk/foobar2000/SDK/threadsLite.h index 963a5d1..1a25565 100644 --- a/sdk/foobar2000/SDK/threadsLite.h +++ b/sdk/foobar2000/SDK/threadsLite.h @@ -3,6 +3,9 @@ namespace fb2k { //! pfc::splitThread() + async_task_manager::acquire void splitTask(std::function); void splitTask(pfc::thread::arg_t const&, std::function); + abort_callback& mainAborter(); + + void inCpuWorkerThread(std::function f); } @@ -13,11 +16,24 @@ namespace fb2k { // ====================================================================================================== namespace fb2k { //! Queue a call in main thread. Returns immediately. \n - //! You can call this from any thread, including main thread - to execute some code outside the current call stack / global fb2k callbacks / etc. + //! You can call this from any thread, including main thread - to execute some code outside the current call stack / global fb2k callbacks / etc. \n + //! Guaranteed FIFO order of execution. See also: main_thread_callback::add_callback(). void inMainThread(std::function f); //! Call f synchronously if called from main thread, queue call if called from another. void inMainThread2(std::function f); - //! Synchronous version. + //! Clone abort_callback, suppress call if original abort_callback becomes set prior to reaching main thread. + void inMainThread(std::function f, abort_callback&); + + //! Synchronous / abortable version. May exit *before* f() finishes, if abort becomes set. void inMainThreadSynchronous(std::function f, abort_callback& abort); + + //! Synchronous blocking version. \n + //! Uses new foobar2000 v2.0 methods if available, synchronizing to main thread via SendMessage(). \n + //! Introduced to help recovering from method-called-from-wrong-context scenarios. Does *not* guarentee FIFO execution order contrary to plain inMainThread(). + void inMainThreadSynchronous2(std::function f); + + //! Helper class for threads that call fb2k objects. Mainly needed for Android shims. You can safely ignore this. \n + //! Guaranteed to have startHere(), isActive() and waitTillDone() methods only. + typedef pfc::thread2 thread; } diff --git a/sdk/foobar2000/SDK/timer.h b/sdk/foobar2000/SDK/timer.h index edab718..08a3f9f 100644 --- a/sdk/foobar2000/SDK/timer.h +++ b/sdk/foobar2000/SDK/timer.h @@ -2,6 +2,7 @@ #include #include "completion_notify.h" +#if FOOBAR2020 namespace fb2k { //! \since 2.0 class NOVTABLE timerManager : public service_base { @@ -13,3 +14,4 @@ namespace fb2k { objRef registerTimer( double interval, std::function func ); void callLater( double timeAfter, std::function< void () > func ); } +#endif // FOOBAR2020 \ No newline at end of file diff --git a/sdk/foobar2000/SDK/titleformat.cpp b/sdk/foobar2000/SDK/titleformat.cpp index 6ad37bf..e58dcca 100644 --- a/sdk/foobar2000/SDK/titleformat.cpp +++ b/sdk/foobar2000/SDK/titleformat.cpp @@ -176,6 +176,7 @@ void titleformat_compiler::compile_safe_ex(titleformat_object::ptr & p_out,const void titleformat_text_filter_nontext_chars::write(const GUID & p_inputtype,pfc::string_receiver & p_out,const char * p_data,t_size p_data_length) { + (void)p_inputtype; for(t_size walk = 0;;) { t_size base = walk; while(walk < p_data_length && !isReserved(p_data[walk]) && p_data[walk] != 0) walk++; diff --git a/sdk/foobar2000/SDK/titleformat.h b/sdk/foobar2000/SDK/titleformat.h index 2a70fd5..caf0405 100644 --- a/sdk/foobar2000/SDK/titleformat.h +++ b/sdk/foobar2000/SDK/titleformat.h @@ -1,12 +1,14 @@ #pragma once +#include "titleformat_object.h" + namespace titleformat_inputtypes { extern const GUID meta, unknown; }; class NOVTABLE titleformat_text_out { public: - virtual void write(const GUID & p_inputtype,const char * p_data,t_size p_data_length = ~0) = 0; + virtual void write(const GUID & p_inputtype,const char * p_data,t_size p_data_length = SIZE_MAX) = 0; void write_int(const GUID & p_inputtype,t_int64 val); void write_int_padded(const GUID & p_inputtype,t_int64 val,t_int64 maxval); protected: @@ -39,20 +41,6 @@ class NOVTABLE titleformat_hook virtual bool process_field(titleformat_text_out * p_out,const char * p_name,t_size p_name_length,bool & p_found_flag) = 0; virtual bool process_function(titleformat_text_out * p_out,const char * p_name,t_size p_name_length,titleformat_hook_function_params * p_params,bool & p_found_flag) = 0; }; -//! Represents precompiled executable title-formatting script. Use titleformat_compiler to instantiate; do not reimplement. -class NOVTABLE titleformat_object : public service_base -{ -public: - virtual void run(titleformat_hook * p_source,pfc::string_base & p_out,titleformat_text_filter * p_filter)=0; - - void run_hook(const playable_location & p_location,const file_info * p_source,titleformat_hook * p_hook,pfc::string_base & p_out,titleformat_text_filter * p_filter); - void run_simple(const playable_location & p_location,const file_info * p_source,pfc::string_base & p_out); - - //! Helper, see titleformat_object_v2::requires_metadb_info() - bool requires_metadb_info_(); - - FB2K_MAKE_SERVICE_INTERFACE(titleformat_object,service_base); -}; //! \since 2.0 class NOVTABLE titleformat_object_v2 : public titleformat_object { @@ -133,7 +121,7 @@ class titleformat_text_out_impl_filter_chars : public titleformat_text_out class titleformat_text_out_impl_string : public titleformat_text_out { public: titleformat_text_out_impl_string(pfc::string_receiver & p_string) : m_string(p_string) {} - void write(const GUID & p_inputtype,const char * p_data,t_size p_data_length) {m_string.add_string(p_data,p_data_length);} + void write(const GUID &,const char * p_data,t_size p_data_length) override {m_string.add_string(p_data,p_data_length);} private: pfc::string_receiver & m_string; }; @@ -199,14 +187,14 @@ class titleformat_hook_impl_list : public titleformat_hook { public: titleformat_hook_impl_list(t_size p_index /* zero-based! */,t_size p_total) : m_index(p_index), m_total(p_total) {} - bool process_field(titleformat_text_out * p_out,const char * p_name,t_size p_name_length,bool & p_found_flag) { + bool process_field(titleformat_text_out * p_out,const char * p_name,t_size p_name_length,bool & p_found_flag) override { if ( - pfc::stricmp_ascii_ex(p_name,p_name_length,"list_index",~0) == 0 + pfc::stricmp_ascii_ex(p_name,p_name_length,"list_index",SIZE_MAX) == 0 ) { p_out->write_int_padded(titleformat_inputtypes::unknown,m_index+1, m_total); p_found_flag = true; return true; } else if ( - pfc::stricmp_ascii_ex(p_name,p_name_length,"list_total",~0) == 0 + pfc::stricmp_ascii_ex(p_name,p_name_length,"list_total",SIZE_MAX) == 0 ) { p_out->write_int(titleformat_inputtypes::unknown,m_total); p_found_flag = true; return true; @@ -215,7 +203,7 @@ class titleformat_hook_impl_list : public titleformat_hook { } } - bool process_function(titleformat_text_out * p_out,const char * p_name,t_size p_name_length,titleformat_hook_function_params * p_params,bool & p_found_flag) {return false;} + bool process_function(titleformat_text_out *,const char *,t_size,titleformat_hook_function_params *,bool &) override {return false;} private: t_size m_index, m_total; @@ -226,25 +214,25 @@ class string_formatter_tf : public pfc::string_base { public: string_formatter_tf(titleformat_text_out * out, const GUID & inputType = titleformat_inputtypes::meta) : m_out(out), m_inputType(inputType) {} - const char * get_ptr() const { + const char * get_ptr() const override { verboten(); } - void add_string(const char * p_string,t_size p_length) { + void add_string(const char * p_string,t_size p_length) override { m_out->write(m_inputType,p_string,p_length); } - void set_string(const char * p_string,t_size p_length) { + void set_string(const char *,t_size) override { verboten(); } - void truncate(t_size len) { + void truncate(t_size) override { verboten(); } - t_size get_length() const { + t_size get_length() const override { verboten(); } - char * lock_buffer(t_size p_requested_length) { + char * lock_buffer(t_size) override { verboten(); } - void unlock_buffer() { + void unlock_buffer() override { verboten(); } diff --git a/sdk/foobar2000/SDK/titleformat_object.h b/sdk/foobar2000/SDK/titleformat_object.h new file mode 100644 index 0000000..e3d7498 --- /dev/null +++ b/sdk/foobar2000/SDK/titleformat_object.h @@ -0,0 +1,20 @@ +#pragma once + +// titleformat_object extracted from titleformat.h as it's more commonly used than other titleformat.h stuff + +class file_info; class titleformat_hook; class titleformat_text_filter; + +//! Represents precompiled executable title-formatting script. Use titleformat_compiler to instantiate; do not reimplement. +class NOVTABLE titleformat_object : public service_base +{ +public: + virtual void run(titleformat_hook * p_source,pfc::string_base & p_out,titleformat_text_filter * p_filter)=0; + + void run_hook(const playable_location & p_location,const file_info * p_source,titleformat_hook * p_hook,pfc::string_base & p_out,titleformat_text_filter * p_filter); + void run_simple(const playable_location & p_location,const file_info * p_source,pfc::string_base & p_out); + + //! Helper, see titleformat_object_v2::requires_metadb_info() + bool requires_metadb_info_(); + + FB2K_MAKE_SERVICE_INTERFACE(titleformat_object,service_base); +}; diff --git a/sdk/foobar2000/SDK/ui.cpp b/sdk/foobar2000/SDK/ui.cpp index 6532287..cbca598 100644 --- a/sdk/foobar2000/SDK/ui.cpp +++ b/sdk/foobar2000/SDK/ui.cpp @@ -19,6 +19,7 @@ bool ui_drop_item_callback::g_is_accepted_type(interface IDataObject * pDataObje } return false; } +#endif // _WIN32 bool user_interface::g_find(service_ptr_t & p_out,const GUID & p_guid) { @@ -30,7 +31,6 @@ bool user_interface::g_find(service_ptr_t & p_out,const GUID & p } return false; } -#endif // _WIN32 // ui_edit_context.h code diff --git a/sdk/foobar2000/SDK/ui.h b/sdk/foobar2000/SDK/ui.h index acc2c64..4f1bd4e 100644 --- a/sdk/foobar2000/SDK/ui.h +++ b/sdk/foobar2000/SDK/ui.h @@ -1,20 +1,23 @@ #pragma once -#ifdef _WIN32 //! Entrypoint service for user interface modules. Implement when registering an UI module. Do not call existing implementations; only core enumerates / dispatches calls. To control UI behaviors from other components, use ui_control API. \n //! Use user_interface_factory_t<> to register, e.g static user_interface_factory_t g_myclass_factory; class NOVTABLE user_interface : public service_base { FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(user_interface); public: +#ifdef _WIN32 //!HookProc usage: \n //! in your windowproc, call HookProc first, and if it returns true, return LRESULT value it passed to you typedef BOOL (WINAPI * HookProc_t)(HWND wnd,UINT msg,WPARAM wp,LPARAM lp,LRESULT * ret); - +#else + typedef void* HookProc_t; // RESERVED +#endif //! Retrieves name (UTF-8 null-terminated string) of the UI module. virtual const char * get_name()=0; - //! Initializes the UI module - creates the main app window, etc. Failure should be signaled by appropriate exception (std::exception or a derivative). - virtual HWND init(HookProc_t hook)=0; + //! Initializes the UI module - creates the main app window, etc. Failure should be signaled by appropriate exception (std::exception or a derivative). \n + //! Mac OS: return NSWindow cast to hwnd_t + virtual fb2k::hwnd_t init(HookProc_t hook) = 0; //! Deinitializes the UI module - destroys the main app window, etc. virtual void shutdown()=0; //! Activates main app window. @@ -31,7 +34,7 @@ class NOVTABLE user_interface : public service_base { //! Disables statusbar text override. virtual void revert_statusbar_text() = 0; - //! Shows now-playing item somehow (e.g. system notification area popup). + //! Shows now-playing item somehow (e.g. system tray popup). virtual void show_now_playing() = 0; static bool g_find(service_ptr_t & p_out,const GUID & p_guid); @@ -48,6 +51,7 @@ class NOVTABLE user_interface_v2 : public user_interface { //! Allows the core to ask the UI module about a specific feature. virtual bool query_capability( const GUID & cap ) = 0; +#ifdef _WIN32 //! Suppress core's shellhook window for intercepting systemwide WM_APPCOMMAND? \n //! Recommended: false - return true only if your UI does this on its own. static const GUID cap_suppress_core_shellhook; @@ -56,16 +60,25 @@ class NOVTABLE user_interface_v2 : public user_interface { //! Note that cap_suppress_core_shellhook is queried first, as core can't use UVC if this UI does global WM_APPCOMMAND handling on its own. \n //! Returning true from cap_suppress_core_shellhook implies the same from cap_suppress_core_uvc. static const GUID cap_suppress_core_uvc; +#endif }; class ui_config_manager; + //! \since 2.0 class NOVTABLE user_interface_v3 : public user_interface_v2 { FB2K_MAKE_SERVICE_INTERFACE(user_interface_v3, user_interface_v2); public: virtual service_ptr_t< ui_config_manager > get_config_manager() = 0; }; -#endif // _WIN32 + +//! \since 2.1 +class NOVTABLE user_interface_v4 : public user_interface_v3 { + FB2K_MAKE_SERVICE_INTERFACE(user_interface_v4, user_interface_v3); +public: + static constexpr uint32_t flagHide = 1; + virtual fb2k::hwnd_t init_v4(HookProc_t hook, uint32_t flags) = 0; +}; //! Interface class allowing you to override UI statusbar text. There may be multiple callers trying to override statusbar text; backend decides which one succeeds so you will not always get what you want. Statusbar text override is automatically cancelled when the object is released.\n //! Use ui_control::override_status_text_create() to instantiate. @@ -238,7 +251,7 @@ class ui_selection_callback_impl_base : public ui_selection_callback { } //avoid pure virtual function calls in rare cases - provide a dummy implementation - void on_selection_changed(metadb_handle_list_cref p_selection) {} + void on_selection_changed(metadb_handle_list_cref p_selection) override { (void)p_selection; } PFC_CLASS_NOT_COPYABLE_EX(ui_selection_callback_impl_base); private: @@ -266,7 +279,7 @@ class ui_selection_callback_impl_base_ex : public ui_selection_callback { } //avoid pure virtual function calls in rare cases - provide a dummy implementation - void on_selection_changed(metadb_handle_list_cref p_selection) {} + void on_selection_changed(metadb_handle_list_cref p_selection) override {} PFC_CLASS_NOT_COPYABLE(ui_selection_callback_impl_base_ex, ui_selection_callback_impl_base_ex); private: diff --git a/sdk/foobar2000/SDK/ui_element.cpp b/sdk/foobar2000/SDK/ui_element.cpp index e7ccbe1..51e9241 100644 --- a/sdk/foobar2000/SDK/ui_element.cpp +++ b/sdk/foobar2000/SDK/ui_element.cpp @@ -239,17 +239,20 @@ ui_element_replace_dialog_notify::ptr ui_element_replace_dialog_notify::create(s } bool ui_config_manager::is_dark_mode() { + PFC_ASSERT(core_api::is_main_thread()); t_ui_color clr = 0xFFFFFF; if (this->query_color(ui_color_darkmode, clr)) return clr == 0; return false; } bool ui_config_manager::g_is_dark_mode() { + PFC_ASSERT(core_api::is_main_thread()); auto api = tryGet(); if (api.is_valid()) return api->is_dark_mode(); else return false; } #ifdef _WIN32 t_ui_color ui_config_manager::getSysColor(int sysColorIndex) { + PFC_ASSERT(core_api::is_main_thread()); GUID guid = ui_color_from_sys_color_index(sysColorIndex); if (guid != pfc::guid_null) { t_ui_color ret = 0; @@ -260,11 +263,13 @@ t_ui_color ui_config_manager::getSysColor(int sysColorIndex) { #endif ui_config_callback_impl::ui_config_callback_impl() { + PFC_ASSERT(core_api::is_main_thread()); auto api = ui_config_manager::tryGet(); if (api.is_valid()) api->add_callback(this); } ui_config_callback_impl::~ui_config_callback_impl() { + PFC_ASSERT(core_api::is_main_thread()); auto api = ui_config_manager::tryGet(); if (api.is_valid()) api->remove_callback(this); } diff --git a/sdk/foobar2000/SDK/ui_element.h b/sdk/foobar2000/SDK/ui_element.h index 85921cd..9543717 100644 --- a/sdk/foobar2000/SDK/ui_element.h +++ b/sdk/foobar2000/SDK/ui_element.h @@ -185,7 +185,7 @@ template class ui_element_instance_callback_impl : public u void on_min_max_info_change() { if (m_receiver != NULL) m_receiver->on_min_max_info_change(); } - void on_alt_pressed(bool p_state) {} + void on_alt_pressed(bool p_state) { (void)p_state; } bool query_color(const GUID & p_what,t_ui_color & p_out) { if (m_receiver != NULL) return m_receiver->query_color(p_what,p_out); @@ -228,13 +228,13 @@ template class ui_element_instance_callback_impl : public u class ui_element_instance_callback_receiver { public: virtual void on_min_max_info_change() {} - virtual bool query_color(const GUID & p_what,t_ui_color & p_out) {return false;} - virtual bool request_activation(service_ptr_t p_item) {return false;} + virtual bool query_color(const GUID& p_what, t_ui_color& p_out) { (void)p_what; (void)p_out; return false; } + virtual bool request_activation(service_ptr_t p_item) { (void)p_item; return false; } virtual bool is_edit_mode_enabled() {return false;} virtual void request_replace(service_ptr_t p_item) {} virtual t_ui_font query_font_ex(const GUID&) {return NULL;} virtual bool is_elem_visible(service_ptr_t elem) {return true;} - virtual t_size host_notify(ui_element_instance * source, const GUID & what, t_size param1, const void * param2, t_size param2size) {return 0;} + virtual t_size host_notify(ui_element_instance* source, const GUID& what, t_size param1, const void* param2, t_size param2size) { (void)source; (void)what; (void)param1; (void)param2; (void)param2size; return 0; } ui_element_instance_callback_ptr ui_element_instance_callback_get_ptr() { if (m_callback.is_empty()) m_callback = new service_impl_t(this); return m_callback; @@ -294,18 +294,18 @@ class NOVTABLE ui_element_instance : public service_base { virtual ui_element_min_max_info get_min_max_info(); //! Used by host to notify the element about various events. See ui_element_notify_* GUIDs for possible p_what parameter; meaning of other parameters depends on p_what value. Container classes should dispatch all notifications to their children. - virtual void notify(const GUID & p_what, t_size p_param1, const void * p_param2, t_size p_param2size) {} + virtual void notify(const GUID& p_what, t_size p_param1, const void* p_param2, t_size p_param2size) { (void)p_what; (void)p_param1; (void)p_param2; (void)p_param2size; } #ifdef _WIN32 //! @param p_point Context menu point in screen coordinates. Always within out window's rect. //! @return True to request edit_mode_context_menu_build() call to add our own items to the menu, false if we can't supply a context menu for this point. - virtual bool edit_mode_context_menu_test(const POINT & p_point,bool p_fromkeyboard) {return false;} - virtual void edit_mode_context_menu_build(const POINT & p_point,bool p_fromkeyboard,HMENU p_menu,unsigned p_id_base) {} - virtual void edit_mode_context_menu_command(const POINT & p_point,bool p_fromkeyboard,unsigned p_id,unsigned p_id_base) {} + virtual bool edit_mode_context_menu_test(const POINT& p_point, bool p_fromkeyboard) { (void)p_point; (void)p_fromkeyboard; return false; } + virtual void edit_mode_context_menu_build(const POINT& p_point, bool p_fromkeyboard, HMENU p_menu, unsigned p_id_base) { (void)p_point; (void)p_fromkeyboard; (void)p_menu; (void)p_id_base; } + virtual void edit_mode_context_menu_command(const POINT& p_point, bool p_fromkeyboard, unsigned p_id, unsigned p_id_base) { (void)p_point; (void)p_fromkeyboard; (void)p_id; (void)p_id_base; } //! @param p_point Receives the point to spawn context menu over when user has pressed the context menu key; in screen coordinates. - virtual bool edit_mode_context_menu_get_focus_point(POINT & p_point) {return false;} + virtual bool edit_mode_context_menu_get_focus_point(POINT& p_point) { (void)p_point; return false; } - virtual bool edit_mode_context_menu_get_description(unsigned p_id,unsigned p_id_base,pfc::string_base & p_out) {return false;} + virtual bool edit_mode_context_menu_get_description(unsigned p_id, unsigned p_id_base, pfc::string_base& p_out) { (void)p_id; (void)p_id_base; (void)p_out; return false; } #endif // _WIN32 //! Helper. @@ -382,7 +382,7 @@ class NOVTABLE ui_element : public service_base { virtual t_ui_icon get_icon() {return NULL;} //! Retrieves a human-readable description of the element. - virtual bool get_description(pfc::string_base & p_out) {return false;} + virtual bool get_description(pfc::string_base& p_out) { (void)p_out; return false; } //! Retrieves a human-readable description of the element's function to use for grouping in the element list. The default implementation relies on get_subclass() return value. virtual bool get_element_group(pfc::string_base & p_out); @@ -431,7 +431,7 @@ class NOVTABLE ui_element_v2 : public ui_element { //! @param defSize Default window size @ 96DPI. If screen DPI is different, it will be rescaled appropriately. //! @param title Window title to use. //! @returns True when implemented, false when not - caller will automatically determine default size and window title then. - virtual bool get_popup_specs(ui_size & defSize, pfc::string_base & title) {return false;} + virtual bool get_popup_specs(ui_size& defSize, pfc::string_base& title) { (void)defSize; (void)title; return false; } FB2K_MAKE_SERVICE_INTERFACE(ui_element_v2, ui_element) @@ -529,7 +529,7 @@ class NOVTABLE ui_element_common_methods_v3 : public ui_element_common_methods_v FB2K_MAKE_SERVICE_COREAPI_EXTENSION(ui_element_common_methods_v3, ui_element_common_methods_v2); public: //! Creates a "Replace UI Element" or "Add New UI Element" dialog. - //! @param parent Parent *element* window handle, the dialog will be a child of its parent popup window but centered on top of the specified window. + //! @param wndElem Parent *element* window handle, the dialog will be a child of its parent popup window but centered on top of the specified window. //! @param elemReplacing GUID of element being replaced; specify null to show "Add UI Element" dialog. //! @param notify Callback object receiving OK/Cancel notifications. //! @returns Handle to the newly created dialog. You can just destroy this window if you need to abort the dialog programatically. @@ -612,13 +612,19 @@ class NOVTABLE ui_config_callback { class NOVTABLE ui_config_manager : public service_base { FB2K_MAKE_SERVICE_COREAPI(ui_config_manager); public: + //! Registers a callback to receive notifications about colors/fonts change. \n + //! Main thread only! virtual void add_callback(ui_config_callback*) = 0; + //! Unregisters a callback to receive notifications about colors/fonts change. \n + //! Main thread only! virtual void remove_callback(ui_config_callback*) = 0; - //! Queries actual color to be used for the specified ui_color_* element. + //! Queries actual color to be used for the specified ui_color_* element. \n + //! Main thread only! \n //! @returns True if color is user-overridden, false if system-default color should be used. virtual bool query_color(const GUID& p_what, t_ui_color& p_out) = 0; //! Queries font to be used for the specified ui_font_* element. \n + //! Main thread only! \n //! The returned font handle is valid until the next font change callback cycle *completes*, that is, during a font change callback, both old and new handle are momentarily valid. virtual t_ui_font query_font(const GUID& p_what) = 0; @@ -629,13 +635,24 @@ class NOVTABLE ui_config_manager : public service_base { t_ui_color getSysColor(int sysColorIndex); #endif - //! Special method that's safe to call without checking if ui_config_manager exists, that is, it works on foobar2000 < 2.0. - //! Returns false if ui_conifg_manager doesn't exist and therefore dark mode isn't supported by this foobar2000 revision. + //! Special method that's safe to call without checking if ui_config_manager exists, that is, it works on foobar2000 < 2.0. \n + //! Returns false if ui_conifg_manager doesn't exist and therefore dark mode isn't supported by this foobar2000 revision. \n + //! Main thread only! static bool g_is_dark_mode(); }; //! \since 2.0 -//! Does nothing (fails to register quietly) if used in fb2k prior to 2.0 +class NOVTABLE ui_config_manager_v2 : public ui_config_manager { + FB2K_MAKE_SERVICE_COREAPI_EXTENSION(ui_config_manager_v2, ui_config_manager) +public: + //! Tells ui_config_manager about system theme having changed. \n + //! Intended for keeping track of live dark mode toggle when foo_ui_std is not the active UI. Do not use. + virtual void notify_system_theme_changed() = 0; +}; + +//! \since 2.0 +//! Does nothing (fails to register quietly) if used in fb2k prior to 2.0 \n +//! Use in main thread only! class ui_config_callback_impl : public ui_config_callback { public: ui_config_callback_impl(); diff --git a/sdk/foobar2000/SDK/ui_element_mac.h b/sdk/foobar2000/SDK/ui_element_mac.h new file mode 100644 index 0000000..a811074 --- /dev/null +++ b/sdk/foobar2000/SDK/ui_element_mac.h @@ -0,0 +1,18 @@ +#pragma once + +#ifdef __APPLE__ +// Mac UI element entrypoint +class ui_element_mac : public service_base { + FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(ui_element_mac); +public: + //! @param arg wrapped NSDictionary*, see wrapNSObject + unwrapNSObject + //! @returns wrapped NSViewController, see wrapNSObject + unwrapNSObject + virtual service_ptr instantiate( service_ptr arg ) = 0; + //! Tests if this element matches specified name in user's view configuration. + virtual bool match_name( const char * name ) = 0; + //! Returns user-readable name. Reserved for future use. + virtual fb2k::stringRef get_name() = 0; + //! Returns GUID. Reserved for future use. + virtual GUID get_guid() = 0; +}; +#endif diff --git a/sdk/foobar2000/SDK/utility.cpp b/sdk/foobar2000/SDK/utility.cpp index 9562186..9e1d92e 100644 --- a/sdk/foobar2000/SDK/utility.cpp +++ b/sdk/foobar2000/SDK/utility.cpp @@ -126,24 +126,28 @@ namespace fb2k { } +#if FOOBAR2020 // timer.h functionality #include "timer.h" #include namespace fb2k { void callLater(double timeAfter, std::function< void() > func) { + PFC_ASSERT( core_api::is_main_thread() ); auto releaseMe = std::make_shared(); *releaseMe = registerTimer(timeAfter, [=] { - if (releaseMe->is_valid()) { - releaseMe->release(); + if (releaseMe->is_valid()) { // ensure we get here once func(); + releaseMe->release(); // warning: this should destroy objects that called us, hence func() call must go first } }); } objRef registerTimer(double interval, std::function func) { + PFC_ASSERT( core_api::is_main_thread() ); return static_api_ptr_t()->addTimer(interval, makeCompletionNotify([func](unsigned) { func(); })); } } +#endif // FOOBAR2020 // autoplaylist.h functionality #include "autoplaylist.h" @@ -257,6 +261,7 @@ void fb2k::keyValueIO::putInt( const char * name, int val ) { // fileDialog.h functionality #include "fileDialog.h" +#include "fsitem.h" namespace { using namespace fb2k; @@ -277,6 +282,27 @@ fb2k::fileDialogNotify::ptr fb2k::fileDialogNotify::create( std::functionrun(fb2k::fileDialogNotify::create(reply)); +} +void fb2k::fileDialogSetup::runSimple(fileDialogGetPath_t reply) { + fb2k::fileDialogReply_t wrapper = [reply] (fb2k::arrayRef arg) { + if ( arg.is_empty() ) {PFC_ASSERT(!"???"); return; } + if ( arg->size() != 1 ) { PFC_ASSERT(!"???"); return; } + auto obj = arg->itemAt(0); + fsItemBase::ptr fsitem; + if ( fsitem &= obj ) { + reply( fsitem->canonicalPath() ); return; + } + fb2k::stringRef str; + if ( str &= obj ) { + reply(str); return; + } + PFC_ASSERT( !"???" ); + }; + this->run(wrapper); +} + #include "input_file_type.h" void fb2k::fileDialogSetup::setAudioFileTypes() { @@ -293,3 +319,87 @@ void search_filter_v2::test_multi_here(metadb_handle_list& ref, abort_callback& this->test_multi_ex(ref, mask.get_ptr(), abort); ref.filter_mask(mask.get_ptr()); } + + + +// core_api.h + +namespace fb2k { + bool isDebugModeActive() { +#if PFC_DEBUG + return true; +#else + auto api = fb2k::configStore::tryGet(); + if (api.is_empty()) return false; + return api->getConfigBool("core.debugMode"); +#endif + } + +#if FB2K_SUPPORT_LOW_MEM_MODE + static bool _isLowMemModeActive() { + auto api = fb2k::configStore::tryGet(); + if (api.is_empty()) return false; + return api->getConfigBool("core.lowMemMode"); + } + + bool isLowMemModeActive() { + static bool cached = _isLowMemModeActive(); + return cached; + } +#endif +} + +// callback_merit.h +namespace fb2k { + callback_merit_t callback_merit_of(service_ptr obj) { + { + callback_with_merit::ptr q; + if (q &= obj) return q->get_callback_merit(); + } + { + metadb_io_callback_v2::ptr q; + if (q &= obj) return q->get_callback_merit(); + } + return callback_merit_default; + } +} + +#ifdef _WIN32 +#include "message_loop.h" +message_filter_impl_base::message_filter_impl_base() { + PFC_ASSERT( core_api::is_main_thread() ); + message_loop::get()->add_message_filter(this); +} +message_filter_impl_base::message_filter_impl_base(t_uint32 lowest, t_uint32 highest) { + PFC_ASSERT( core_api::is_main_thread() ); + message_loop_v2::get()->add_message_filter_ex(this, lowest, highest); +} +message_filter_impl_base::~message_filter_impl_base() { + PFC_ASSERT( core_api::is_main_thread() ); + message_loop::get()->remove_message_filter(this); +} + +bool message_filter_impl_accel::pretranslate_message(MSG * p_msg) { + if (m_wnd != NULL) { + if (GetActiveWindow() == m_wnd) { + if (TranslateAccelerator(m_wnd,m_accel.get(),p_msg) != 0) { + return true; + } + } + } + return false; +} + +message_filter_impl_accel::message_filter_impl_accel(HINSTANCE p_instance,const TCHAR * p_accel) { + m_accel.load(p_instance,p_accel); +} + +bool message_filter_remap_f1::pretranslate_message(MSG * p_msg) { + if (IsOurMsg(p_msg) && m_wnd != NULL && GetActiveWindow() == m_wnd) { + ::PostMessage(m_wnd, WM_SYSCOMMAND, SC_CONTEXTHELP, -1); + return true; + } + return false; +} + +#endif diff --git a/sdk/foobar2000/foo_sample/IO.cpp b/sdk/foobar2000/foo_sample/IO.cpp index e103538..febad04 100644 --- a/sdk/foobar2000/foo_sample/IO.cpp +++ b/sdk/foobar2000/foo_sample/IO.cpp @@ -1,12 +1,12 @@ #include "stdafx.h" #include #include +#include void RunIOTest() { try { - abort_callback_dummy noAbort; auto request = http_client::get()->create_request("GET"); - request->run("https://www.foobar2000.org", noAbort); + request->run("https://www.foobar2000.org", fb2k::noAbort); } catch (std::exception const & e) { popup_message::g_show( PFC_string_formatter() << "Network test failure:\n" << e, "Information"); return; @@ -21,7 +21,7 @@ namespace { // anon namespace local classes for good measure m_lstFiles.init_from_list( items ); } - void on_init(HWND p_wnd) override { + void on_init(ctx_t p_wnd) override { // Main thread, called before run() gets started } void run(threaded_process_status & p_status, abort_callback & p_abort) override { @@ -135,7 +135,7 @@ namespace { // anon namespace local classes for good measure // .. and delete the incomplete file try { - abort_callback_dummy noAbort; // we might be being aborted, don't let that prevent deletion + auto & noAbort = fb2k::noAbort; // we might be being aborted, don't let that prevent deletion m_outFS->remove( outPath, noAbort ); } catch(...) { // disregard errors - just report original copy error @@ -146,7 +146,7 @@ namespace { // anon namespace local classes for good measure } - void on_done(HWND p_wnd, bool p_was_aborted) override { + void on_done(ctx_t p_wnd, bool p_was_aborted) override { // All done, main thread again if (! p_was_aborted && m_errorLog.length() > 0 ) { @@ -171,8 +171,19 @@ namespace { // anon namespace local classes for good measure }; } +void RunCopyFilesHere(metadb_handle_list_cref data, const char * copyTo, fb2k::hwnd_t wndParent) { + + // Create worker object, a threaded_process_callback implementation. + auto worker = fb2k::service_new(data, copyTo); + const uint32_t flags = threaded_process::flag_show_abort | threaded_process::flag_show_progress | threaded_process::flag_show_item; + // Start the process asynchronously. + threaded_process::get()->run_modeless( worker, flags, wndParent, "Sample Component: Copying Files" ); + + // Our worker is now running. +} void RunCopyFiles(metadb_handle_list_cref data) { +#ifdef _WIN32 // Detect modal dialog wars. // If another modal dialog is active, bump it instead of allowing our modal dialog (uBrowseForFolder) to run. // Suppress this if the relevant code is intended to be launched by a modal dialog. @@ -183,18 +194,21 @@ void RunCopyFiles(metadb_handle_list_cref data) { // shared.dll method if (!uBrowseForFolder( wndParent, "Choose destination folder", copyTo )) return; - // shared.dll methods are win32 API wrappers and return plain paths with no protocol prepended - // Prefix with file:// before passing to fb2k filesystem methods. - // Actually the standard fb2k filesystem implementation recognizes paths even without the prefix, but we enforce it here as a good practice. - pfc::string8 copyTo2 = PFC_string_formatter() << "file://" << copyTo; - - // Create worker object, a threaded_process_callback implementation. - auto worker = fb2k::service_new(data, copyTo2); - const uint32_t flags = threaded_process::flag_show_abort | threaded_process::flag_show_progress | threaded_process::flag_show_item; - // Start the process asynchronously. - threaded_process::get()->run_modeless( worker, flags, wndParent, "Sample Component: Copying Files" ); - - // Our worker is now running. + // shared.dll methods are win32 API wrappers and return plain paths with no protocol prepended + // Prefix with file:// before passing to fb2k filesystem methods. + // Actually the standard fb2k filesystem implementation recognizes paths even without the prefix, but we enforce it here as a good practice. + pfc::string8 copyTo2 = PFC_string_formatter() << "file://" << copyTo; + + RunCopyFilesHere(data, copyTo2, wndParent); +#else + auto tracksCopy = std::make_shared( data ); + auto setup = fb2k::fileDialog::get()->setupOpenFolder(); + setup->setTitle("Choose destination folder"); + setup->runSimple( [tracksCopy] (fb2k::stringRef path) { + RunCopyFilesHere(*tracksCopy, path->c_str(), core_api::get_main_window()); + } ); + +#endif } @@ -278,7 +292,7 @@ namespace { // Touchy subject // Should we let the user abort an incomplete tag write? // Let's better not - abort_callback_dummy noAbort; + auto & noAbort = fb2k::noAbort; // This can be called many times for files with multiple subsongs writer->set_info( subsong, info, noAbort ); @@ -314,7 +328,7 @@ namespace { } void RunAlterTagsLL(metadb_handle_list_cref data) { - const HWND wndParent = core_api::get_main_window(); + const auto wndParent = core_api::get_main_window(); // Our worker object, a threaded_process_callback subclass. auto worker = fb2k::service_new< processLLtags > ( data ); diff --git a/sdk/foobar2000/foo_sample/Mac/fooSampleDSPView.h b/sdk/foobar2000/foo_sample/Mac/fooSampleDSPView.h new file mode 100644 index 0000000..1824b31 --- /dev/null +++ b/sdk/foobar2000/foo_sample/Mac/fooSampleDSPView.h @@ -0,0 +1,12 @@ +// +// fooSampleDSPView.h +// foo_sample +// +// Created by P on 01/09/2023. +// + +#import + + +@interface fooSampleDSPView : NSViewController +@end diff --git a/sdk/foobar2000/foo_sample/Mac/fooSampleDSPView.mm b/sdk/foobar2000/foo_sample/Mac/fooSampleDSPView.mm new file mode 100644 index 0000000..b692d3b --- /dev/null +++ b/sdk/foobar2000/foo_sample/Mac/fooSampleDSPView.mm @@ -0,0 +1,41 @@ +#import "stdafx.h" +#import "fooSampleDSPView.h" +#import "dsp_sample.h" + +@interface fooSampleDSPView () +@property (nonatomic) dsp_preset_edit_callback_v2::ptr callback; +@property (nonatomic) NSNumber * gain; +@end + +@implementation fooSampleDSPView + +- (instancetype)init { + // IMPORTANT: feed OUR NSBundle, bundleForClass works well for this + return [self initWithNibName: @"fooSampleDSPView" bundle:[NSBundle bundleForClass: [self class]]]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do view setup here. + + dsp_preset_impl preset; + _callback->get_preset(preset); + self.gain = [NSNumber numberWithFloat: parse_preset(preset)]; +} + +- (IBAction)onEdit:(id)sender { + [self apply]; +} + +- (void) apply { + dsp_preset_impl preset; + make_preset( self.gain.floatValue , preset); + _callback->set_preset( preset ); +} +@end + +service_ptr ConfigureSampleDSP( fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback ) { + fooSampleDSPView * dialog = [fooSampleDSPView new]; + dialog.callback = callback; + return fb2k::wrapNSObject( dialog ); +} diff --git a/sdk/foobar2000/foo_sample/Mac/fooSampleDSPView.xib b/sdk/foobar2000/foo_sample/Mac/fooSampleDSPView.xib new file mode 100644 index 0000000..2e44f4c --- /dev/null +++ b/sdk/foobar2000/foo_sample/Mac/fooSampleDSPView.xib @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sdk/foobar2000/foo_sample/Mac/fooSampleMacPreferences.h b/sdk/foobar2000/foo_sample/Mac/fooSampleMacPreferences.h new file mode 100644 index 0000000..b8ac208 --- /dev/null +++ b/sdk/foobar2000/foo_sample/Mac/fooSampleMacPreferences.h @@ -0,0 +1,12 @@ +// +// fooSampleMacPreferences.h +// foo_sample +// +// Created by P on 01/09/2023. +// + +#import + +@interface fooSampleMacPreferences : NSViewController + +@end diff --git a/sdk/foobar2000/foo_sample/Mac/fooSampleMacPreferences.mm b/sdk/foobar2000/foo_sample/Mac/fooSampleMacPreferences.mm new file mode 100644 index 0000000..b0b80aa --- /dev/null +++ b/sdk/foobar2000/foo_sample/Mac/fooSampleMacPreferences.mm @@ -0,0 +1,58 @@ +#import "stdafx.h" +#import "fooSampleMacPreferences.h" + +namespace foo_sample { + extern cfg_uint cfg_bogoSetting1, cfg_bogoSetting2; +} + +@interface fooSampleMacPreferences () +@property (nonatomic) NSNumber* bogo1; +@property (nonatomic) NSNumber* bogo2; +@end + +@implementation fooSampleMacPreferences + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do view setup here. +} +- (instancetype)init { + // IMPORTANT: feed OUR NSBundle, bundleForClass works well for this + self = [self initWithNibName: @"fooSampleMacPreferences" bundle:[NSBundle bundleForClass: [self class]]]; + [self loadSettings]; + return self; +} +- (void) loadSettings { + self.bogo1 = [NSNumber numberWithUnsignedLong: foo_sample::cfg_bogoSetting1]; + self.bogo2 = [NSNumber numberWithUnsignedLong: foo_sample::cfg_bogoSetting2]; +} +- (IBAction)onBogo1:(id)sender { + foo_sample::cfg_bogoSetting1 = self.bogo1.unsignedLongValue; +} +- (IBAction)onBogo2:(id)sender { + foo_sample::cfg_bogoSetting2 = self.bogo2.unsignedLongValue; +} + + +@end + + + + + +namespace { +class preferences_page_sample : public preferences_page { + public: + service_ptr instantiate() override { + return fb2k::wrapNSObject( [ fooSampleMacPreferences new ] ); + } + const char * get_name() override {return "Sample Component";} + GUID get_guid() override { + // This is our GUID. Replace with your own when reusing the code. + return GUID { 0x7702c93e, 0x24dc, 0x48ed, { 0x8d, 0xb1, 0x3f, 0x27, 0xb3, 0x8c, 0x7c, 0xc9 } }; + } + GUID get_parent_guid() override {return guid_tools;} + }; + + FB2K_SERVICE_FACTORY(preferences_page_sample); +} diff --git a/sdk/foobar2000/foo_sample/Mac/fooSampleMacPreferences.xib b/sdk/foobar2000/foo_sample/Mac/fooSampleMacPreferences.xib new file mode 100644 index 0000000..82af698 --- /dev/null +++ b/sdk/foobar2000/foo_sample/Mac/fooSampleMacPreferences.xib @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sdk/foobar2000/foo_sample/contextmenu.cpp b/sdk/foobar2000/foo_sample/contextmenu.cpp index dd788a8..f2ae310 100644 --- a/sdk/foobar2000/foo_sample/contextmenu.cpp +++ b/sdk/foobar2000/foo_sample/contextmenu.cpp @@ -32,7 +32,7 @@ namespace { // anon namespace local classes for good measure static void RunAlterTags(metadb_handle_list_cref data) { // Simple alter-file-tags functionality - const HWND wndParent = core_api::get_main_window(); + const auto wndParent = core_api::get_main_window(); // Filter object that applies our edits to the file tags auto filter = fb2k::service_new(); @@ -51,6 +51,7 @@ static void RunAlterTags(metadb_handle_list_cref data) { // Simple context menu item class. class myitem : public contextmenu_item_simple { + typedef contextmenu_item_simple super_t; public: enum { cmd_test1 = 0, @@ -102,14 +103,14 @@ class myitem : public contextmenu_item_simple { 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) { switch(p_index) { case cmd_test1: - if (!__super::context_get_display(p_index, p_data, p_out, p_displayflags, p_caller)) return false; + if (!super_t::context_get_display(p_index, p_data, p_out, p_displayflags, p_caller)) return false; // Example context sensitive label: append the count of selected items to the label. p_out << " : " << p_data.get_count() << " item"; if (p_data.get_count() != 1) p_out << "s"; p_out << " selected"; return true; default: - return __super::context_get_display(p_index, p_data, p_out, p_displayflags, p_caller); + return super_t::context_get_display(p_index, p_data, p_out, p_displayflags, p_caller); } } GUID get_item_guid(unsigned p_index) { diff --git a/sdk/foobar2000/foo_sample/decode.cpp b/sdk/foobar2000/foo_sample/decode.cpp index 1f4ab1d..c2536ec 100644 --- a/sdk/foobar2000/foo_sample/decode.cpp +++ b/sdk/foobar2000/foo_sample/decode.cpp @@ -4,8 +4,8 @@ class calculate_peak_process : public threaded_process_callback { public: calculate_peak_process(metadb_handle_list_cref items) : m_items(items), m_peak() {} - void on_init(HWND p_wnd) {} - void run(threaded_process_status & p_status,abort_callback & p_abort) { + void on_init(ctx_t p_wnd) override {} + void run(threaded_process_status & p_status,abort_callback & p_abort) override { try { const t_uint32 decode_flags = input_flag_no_seeking | input_flag_no_looping; // tell the decoders that we won't seek and that we don't want looping on formats that support looping. input_helper input; // this object manages lowlevel input_decoder calls for us. @@ -41,7 +41,7 @@ class calculate_peak_process : public threaded_process_callback { m_failMsg = e.what(); } } - void on_done(HWND p_wnd,bool p_was_aborted) { + void on_done(ctx_t p_wnd,bool p_was_aborted) override { if (!p_was_aborted) { if (!m_failMsg.is_empty()) { popup_message::g_complain("Peak scan failure", m_failMsg); diff --git a/sdk/foobar2000/foo_sample/dsp.cpp b/sdk/foobar2000/foo_sample/dsp_sample.cpp similarity index 84% rename from sdk/foobar2000/foo_sample/dsp.cpp rename to sdk/foobar2000/foo_sample/dsp_sample.cpp index 2b49e3d..0818773 100644 --- a/sdk/foobar2000/foo_sample/dsp.cpp +++ b/sdk/foobar2000/foo_sample/dsp_sample.cpp @@ -1,19 +1,26 @@ #include "stdafx.h" +#include "dsp_sample.h" + +#ifdef _WIN32 +#include #include "resource.h" static void RunDSPConfigPopup(const dsp_preset & p_data,HWND p_parent,dsp_preset_edit_callback & p_callback); +#endif + +#ifdef __APPLE__ +// fooSampleDSPView.mm +service_ptr ConfigureSampleDSP( fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback ); +#endif class dsp_sample : public dsp_impl_base { public: - dsp_sample(dsp_preset const & in) : m_gain(0) { - parse_preset(m_gain, in); + dsp_sample(dsp_preset const & in) : m_gain(parse_preset(in)) { } static GUID g_get_guid() { - //This is our GUID. Generate your own one when reusing this code. - static const GUID guid = { 0x890827b, 0x67df, 0x4c27, { 0xba, 0x1a, 0x4f, 0x95, 0x8d, 0xf, 0xb5, 0xd0 } }; - return guid; + return dsp_sample_common::guid; } static void g_get_name(pfc::string_base & p_out) { p_out = "Sample DSP";} @@ -62,18 +69,17 @@ class dsp_sample : public dsp_impl_base make_preset(0, p_out); return true; } +#ifdef _WIN32 static void g_show_config_popup(const dsp_preset & p_data,HWND p_parent,dsp_preset_edit_callback & p_callback) { ::RunDSPConfigPopup(p_data, p_parent, p_callback); } +#endif // _WIN32 +#ifdef __APPLE__ + static service_ptr g_show_config_popup( fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback ) { + return ConfigureSampleDSP( parent, callback ); + } +#endif // __APPLE__ static bool g_have_config_popup() {return true;} - static void make_preset(float gain, dsp_preset & out) { - dsp_preset_builder builder; builder << gain; builder.finish(g_get_guid(), out); - } - static void parse_preset(float & gain, const dsp_preset & in) { - try { - dsp_preset_parser parser(in); parser >> gain; - } catch(exception_io_data) {gain = 0;} - } private: float m_gain; }; @@ -81,6 +87,7 @@ class dsp_sample : public dsp_impl_base // Use dsp_factory_nopreset_t<> instead of dsp_factory_t<> if your DSP does not provide preset/configuration functionality. static dsp_factory_t g_dsp_sample_factory; +#ifdef _WIN32 class CMyDSPPopup : public CDialogImpl { public: @@ -105,12 +112,12 @@ class CMyDSPPopup : public CDialogImpl { private: BOOL OnInitDialog(CWindow, LPARAM) { + m_dark.AddDialogWithControls(m_hWnd); m_slider = GetDlgItem(IDC_SLIDER); m_slider.SetRange(0, RangeTotal); { - float val; - dsp_sample::parse_preset(val, m_initData); + float val = parse_preset(m_initData); m_slider.SetPos( pfc::clip_t( pfc::rint32(val), RangeMin, RangeMax ) - RangeMin ); RefreshLabel(val); } @@ -127,7 +134,7 @@ class CMyDSPPopup : public CDialogImpl { { dsp_preset_impl preset; - dsp_sample::make_preset(val, preset); + make_preset(val, preset); m_callback.on_preset_changed(preset); } RefreshLabel(val); @@ -142,6 +149,7 @@ class CMyDSPPopup : public CDialogImpl { dsp_preset_edit_callback & m_callback; CTrackBarCtrl m_slider; + fb2k::CDarkModeHooks m_dark; }; static void RunDSPConfigPopup(const dsp_preset & p_data,HWND p_parent,dsp_preset_edit_callback & p_callback) { @@ -152,3 +160,4 @@ static void RunDSPConfigPopup(const dsp_preset & p_data,HWND p_parent,dsp_preset p_callback.on_preset_changed(p_data); } } +#endif diff --git a/sdk/foobar2000/foo_sample/dsp_sample.h b/sdk/foobar2000/foo_sample/dsp_sample.h new file mode 100644 index 0000000..1e81044 --- /dev/null +++ b/sdk/foobar2000/foo_sample/dsp_sample.h @@ -0,0 +1,20 @@ +#pragma once + +namespace dsp_sample_common { + //This is our GUID. Generate your own one when reusing this code. + static constexpr GUID guid = { 0x890827b, 0x67df, 0x4c27, { 0xba, 0x1a, 0x4f, 0x95, 0x8d, 0xf, 0xb5, 0xd0 } }; + + static void make_preset(float gain, dsp_preset & out) { + dsp_preset_builder builder; builder << gain; builder.finish(guid, out); + } + static float parse_preset(const dsp_preset & in) { + try { + float gain; + dsp_preset_parser parser(in); parser >> gain; + return gain; + } catch(exception_io_data const &) {return 0;} + } + +} + +using namespace dsp_sample_common; diff --git a/sdk/foobar2000/foo_sample/foo_sample.vcxproj b/sdk/foobar2000/foo_sample/foo_sample.vcxproj index 7ac8e6d..1ebf459 100644 --- a/sdk/foobar2000/foo_sample/foo_sample.vcxproj +++ b/sdk/foobar2000/foo_sample/foo_sample.vcxproj @@ -38,19 +38,20 @@ {85FBFD09-0099-4FE9-9DB6-78DB6F60F817} foo_input_raw Win32Proj + 10.0 DynamicLibrary Unicode true - v143 + v142 DynamicLibrary Unicode true - v143 + v142 DynamicLibrary @@ -67,12 +68,12 @@ DynamicLibrary Unicode - v143 + v142 DynamicLibrary Unicode - v143 + v142 DynamicLibrary @@ -191,7 +192,6 @@ true Windows - false ../shared/shared-$(Platform).lib @@ -214,7 +214,6 @@ true Windows - false ../shared/shared-$(Platform).lib @@ -299,7 +298,6 @@ Windows true true - false ../shared/shared-$(Platform).lib @@ -327,7 +325,6 @@ Windows true true - false ../shared/shared-$(Platform).lib @@ -336,7 +333,7 @@ - + @@ -365,6 +362,7 @@ + @@ -393,6 +391,5 @@ - - + \ No newline at end of file diff --git a/sdk/foobar2000/foo_sample/foo_sample.vcxproj.filters b/sdk/foobar2000/foo_sample/foo_sample.vcxproj.filters index c9aa418..158409b 100644 --- a/sdk/foobar2000/foo_sample/foo_sample.vcxproj.filters +++ b/sdk/foobar2000/foo_sample/foo_sample.vcxproj.filters @@ -21,9 +21,6 @@ Source Files - - Source Files - Source Files @@ -75,6 +72,9 @@ Source Files + + Source Files + @@ -86,6 +86,9 @@ Header Files + + Header Files + diff --git a/sdk/foobar2000/foo_sample/foo_sample.xcodeproj/project.pbxproj b/sdk/foobar2000/foo_sample/foo_sample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..9e757a8 --- /dev/null +++ b/sdk/foobar2000/foo_sample/foo_sample.xcodeproj/project.pbxproj @@ -0,0 +1,450 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 0F1FDDBA2AA0ADDF00DE8967 /* initquit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F1FDDB62AA0ADDF00DE8967 /* initquit.cpp */; }; + 0F1FDDBB2AA0ADDF00DE8967 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F1FDDB72AA0ADDF00DE8967 /* main.cpp */; }; + 0F1FDDC02AA0AE1E00DE8967 /* libfoobar2000_SDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F1FDDBD2AA0AE1E00DE8967 /* libfoobar2000_SDK.a */; }; + 0F1FDDC12AA0AE1E00DE8967 /* libfoobar2000_SDK_helpers.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F1FDDBE2AA0AE1E00DE8967 /* libfoobar2000_SDK_helpers.a */; }; + 0F1FDDC22AA0AE1E00DE8967 /* libpfc-Mac.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F1FDDBF2AA0AE1E00DE8967 /* libpfc-Mac.a */; }; + 0F1FDDC82AA0B1F800DE8967 /* libshared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F1FDDC72AA0B1F800DE8967 /* libshared.a */; }; + 0F1FDDCA2AA0B1FE00DE8967 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F1FDDC92AA0B1FE00DE8967 /* Cocoa.framework */; }; + 0F1FDDDE2AA0B34200DE8967 /* libfoobar2000_component_client.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F1FDDDD2AA0B34200DE8967 /* libfoobar2000_component_client.a */; }; + 0F62440A2AA1E59E004FEC96 /* preferences.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F6244072AA1E4F4004FEC96 /* preferences.cpp */; }; + 0F6D10F12AA3C35C00774EED /* fooDecibelFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F6D10EF2AA3C35C00774EED /* fooDecibelFormatter.m */; }; + 0FBE14512AA1E85F00B1F71E /* NSView+embed.m in Sources */ = {isa = PBXBuildFile; fileRef = 0FBE14452AA1E85F00B1F71E /* NSView+embed.m */; }; + 0FBE14552AA1E8C800B1F71E /* fooSampleMacPreferences.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0FBE14532AA1E8C800B1F71E /* fooSampleMacPreferences.mm */; }; + 0FBE14562AA1E8C800B1F71E /* fooSampleMacPreferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0FBE14542AA1E8C800B1F71E /* fooSampleMacPreferences.xib */; }; + 0FBE14622AA1F74200B1F71E /* rating.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FBE14582AA1F74200B1F71E /* rating.cpp */; }; + 0FBE14632AA1F74200B1F71E /* contextmenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FBE14592AA1F74200B1F71E /* contextmenu.cpp */; }; + 0FBE14642AA1F74200B1F71E /* input_raw.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FBE145A2AA1F74200B1F71E /* input_raw.cpp */; }; + 0FBE14652AA1F74200B1F71E /* decode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FBE145B2AA1F74200B1F71E /* decode.cpp */; }; + 0FBE14662AA1F74200B1F71E /* dsp_sample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FBE145C2AA1F74200B1F71E /* dsp_sample.cpp */; }; + 0FBE14672AA1F74200B1F71E /* mainmenu-dynamic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FBE145D2AA1F74200B1F71E /* mainmenu-dynamic.cpp */; }; + 0FBE14692AA1F74200B1F71E /* IO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FBE145F2AA1F74200B1F71E /* IO.cpp */; }; + 0FBE146A2AA1F74200B1F71E /* playback_stream_capture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FBE14602AA1F74200B1F71E /* playback_stream_capture.cpp */; }; + 0FBE146C2AA1FF1500B1F71E /* ui_and_threads.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FBE146B2AA1FF1500B1F71E /* ui_and_threads.cpp */; }; + 0FCA71142AA21F69001CB0F2 /* fooSampleDSPView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0FCA71122AA21F69001CB0F2 /* fooSampleDSPView.mm */; }; + 0FCA71152AA21F69001CB0F2 /* fooSampleDSPView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0FCA71132AA21F69001CB0F2 /* fooSampleDSPView.xib */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0F1FDDAD2AA0AD9B00DE8967 /* foo_sample.component */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = foo_sample.component; sourceTree = BUILT_PRODUCTS_DIR; }; + 0F1FDDB42AA0ADDF00DE8967 /* mainmenu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mainmenu.cpp; sourceTree = ""; }; + 0F1FDDB52AA0ADDF00DE8967 /* playback_state.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = playback_state.cpp; sourceTree = ""; }; + 0F1FDDB62AA0ADDF00DE8967 /* initquit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = initquit.cpp; sourceTree = ""; }; + 0F1FDDB72AA0ADDF00DE8967 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; + 0F1FDDBD2AA0AE1E00DE8967 /* libfoobar2000_SDK.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libfoobar2000_SDK.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 0F1FDDBE2AA0AE1E00DE8967 /* libfoobar2000_SDK_helpers.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libfoobar2000_SDK_helpers.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 0F1FDDBF2AA0AE1E00DE8967 /* libpfc-Mac.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libpfc-Mac.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 0F1FDDC62AA0AF8400DE8967 /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = ""; }; + 0F1FDDC72AA0B1F800DE8967 /* libshared.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libshared.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 0F1FDDC92AA0B1FE00DE8967 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 0F1FDDDD2AA0B34200DE8967 /* libfoobar2000_component_client.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libfoobar2000_component_client.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 0F6244072AA1E4F4004FEC96 /* preferences.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = preferences.cpp; sourceTree = ""; }; + 0F6D10EF2AA3C35C00774EED /* fooDecibelFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = fooDecibelFormatter.m; sourceTree = ""; }; + 0F6D10F02AA3C35C00774EED /* fooDecibelFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fooDecibelFormatter.h; sourceTree = ""; }; + 0F7F817F2AB87BA70051262F /* foobar2000-mac-class-suffix.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "foobar2000-mac-class-suffix.h"; sourceTree = ""; }; + 0FBE14402AA1E85F00B1F71E /* NSView+embed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSView+embed.h"; sourceTree = ""; }; + 0FBE14452AA1E85F00B1F71E /* NSView+embed.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSView+embed.m"; sourceTree = ""; }; + 0FBE14522AA1E8C800B1F71E /* fooSampleMacPreferences.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fooSampleMacPreferences.h; sourceTree = ""; }; + 0FBE14532AA1E8C800B1F71E /* fooSampleMacPreferences.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = fooSampleMacPreferences.mm; sourceTree = ""; }; + 0FBE14542AA1E8C800B1F71E /* fooSampleMacPreferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = fooSampleMacPreferences.xib; sourceTree = ""; }; + 0FBE14582AA1F74200B1F71E /* rating.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rating.cpp; sourceTree = ""; }; + 0FBE14592AA1F74200B1F71E /* contextmenu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = contextmenu.cpp; sourceTree = ""; }; + 0FBE145A2AA1F74200B1F71E /* input_raw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = input_raw.cpp; sourceTree = ""; }; + 0FBE145B2AA1F74200B1F71E /* decode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = decode.cpp; sourceTree = ""; }; + 0FBE145C2AA1F74200B1F71E /* dsp_sample.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsp_sample.cpp; sourceTree = ""; }; + 0FBE145D2AA1F74200B1F71E /* mainmenu-dynamic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "mainmenu-dynamic.cpp"; sourceTree = ""; }; + 0FBE145E2AA1F74200B1F71E /* readme.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = readme.txt; sourceTree = ""; }; + 0FBE145F2AA1F74200B1F71E /* IO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IO.cpp; sourceTree = ""; }; + 0FBE14602AA1F74200B1F71E /* playback_stream_capture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = playback_stream_capture.cpp; sourceTree = ""; }; + 0FBE14612AA1F74200B1F71E /* playback_stream_capture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = playback_stream_capture.h; sourceTree = ""; }; + 0FBE146B2AA1FF1500B1F71E /* ui_and_threads.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ui_and_threads.cpp; sourceTree = ""; }; + 0FCA71112AA21F69001CB0F2 /* fooSampleDSPView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fooSampleDSPView.h; sourceTree = ""; }; + 0FCA71122AA21F69001CB0F2 /* fooSampleDSPView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = fooSampleDSPView.mm; sourceTree = ""; }; + 0FCA71132AA21F69001CB0F2 /* fooSampleDSPView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = fooSampleDSPView.xib; sourceTree = ""; }; + 0FD323BF2AA3C45C00C0C2F7 /* dsp_sample.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = dsp_sample.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0F1FDDAA2AA0AD9B00DE8967 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0F1FDDDE2AA0B34200DE8967 /* libfoobar2000_component_client.a in Frameworks */, + 0F1FDDCA2AA0B1FE00DE8967 /* Cocoa.framework in Frameworks */, + 0F1FDDC82AA0B1F800DE8967 /* libshared.a in Frameworks */, + 0F1FDDC02AA0AE1E00DE8967 /* libfoobar2000_SDK.a in Frameworks */, + 0F1FDDC12AA0AE1E00DE8967 /* libfoobar2000_SDK_helpers.a in Frameworks */, + 0F1FDDC22AA0AE1E00DE8967 /* libpfc-Mac.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0F1FDDA42AA0AD9B00DE8967 = { + isa = PBXGroup; + children = ( + 0F7F817F2AB87BA70051262F /* foobar2000-mac-class-suffix.h */, + 0FBE146B2AA1FF1500B1F71E /* ui_and_threads.cpp */, + 0FBE14592AA1F74200B1F71E /* contextmenu.cpp */, + 0FBE145B2AA1F74200B1F71E /* decode.cpp */, + 0FBE145C2AA1F74200B1F71E /* dsp_sample.cpp */, + 0FD323BF2AA3C45C00C0C2F7 /* dsp_sample.h */, + 0FBE145A2AA1F74200B1F71E /* input_raw.cpp */, + 0FBE145F2AA1F74200B1F71E /* IO.cpp */, + 0FBE145D2AA1F74200B1F71E /* mainmenu-dynamic.cpp */, + 0FBE14602AA1F74200B1F71E /* playback_stream_capture.cpp */, + 0FBE14612AA1F74200B1F71E /* playback_stream_capture.h */, + 0FBE14582AA1F74200B1F71E /* rating.cpp */, + 0FBE145E2AA1F74200B1F71E /* readme.txt */, + 0FBE14572AA1F41A00B1F71E /* Mac */, + 0F6244072AA1E4F4004FEC96 /* preferences.cpp */, + 0F1FDDC62AA0AF8400DE8967 /* stdafx.h */, + 0F1FDDB62AA0ADDF00DE8967 /* initquit.cpp */, + 0F1FDDB72AA0ADDF00DE8967 /* main.cpp */, + 0F1FDDB42AA0ADDF00DE8967 /* mainmenu.cpp */, + 0F1FDDB52AA0ADDF00DE8967 /* playback_state.cpp */, + 0FBE14362AA1E85F00B1F71E /* helpers-mac */, + 0F1FDDAE2AA0AD9B00DE8967 /* Products */, + 0F1FDDBC2AA0AE1E00DE8967 /* Frameworks */, + ); + sourceTree = ""; + }; + 0F1FDDAE2AA0AD9B00DE8967 /* Products */ = { + isa = PBXGroup; + children = ( + 0F1FDDAD2AA0AD9B00DE8967 /* foo_sample.component */, + ); + name = Products; + sourceTree = ""; + }; + 0F1FDDBC2AA0AE1E00DE8967 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0F1FDDDD2AA0B34200DE8967 /* libfoobar2000_component_client.a */, + 0F1FDDC92AA0B1FE00DE8967 /* Cocoa.framework */, + 0F1FDDC72AA0B1F800DE8967 /* libshared.a */, + 0F1FDDBD2AA0AE1E00DE8967 /* libfoobar2000_SDK.a */, + 0F1FDDBE2AA0AE1E00DE8967 /* libfoobar2000_SDK_helpers.a */, + 0F1FDDBF2AA0AE1E00DE8967 /* libpfc-Mac.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 0FBE14362AA1E85F00B1F71E /* helpers-mac */ = { + isa = PBXGroup; + children = ( + 0F6D10F02AA3C35C00774EED /* fooDecibelFormatter.h */, + 0F6D10EF2AA3C35C00774EED /* fooDecibelFormatter.m */, + 0FBE14402AA1E85F00B1F71E /* NSView+embed.h */, + 0FBE14452AA1E85F00B1F71E /* NSView+embed.m */, + ); + name = "helpers-mac"; + path = "../helpers-mac"; + sourceTree = ""; + }; + 0FBE14572AA1F41A00B1F71E /* Mac */ = { + isa = PBXGroup; + children = ( + 0FBE14522AA1E8C800B1F71E /* fooSampleMacPreferences.h */, + 0FBE14532AA1E8C800B1F71E /* fooSampleMacPreferences.mm */, + 0FBE14542AA1E8C800B1F71E /* fooSampleMacPreferences.xib */, + 0FCA71112AA21F69001CB0F2 /* fooSampleDSPView.h */, + 0FCA71122AA21F69001CB0F2 /* fooSampleDSPView.mm */, + 0FCA71132AA21F69001CB0F2 /* fooSampleDSPView.xib */, + ); + path = Mac; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 0F1FDDAC2AA0AD9B00DE8967 /* foo_sample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0F1FDDB12AA0AD9B00DE8967 /* Build configuration list for PBXNativeTarget "foo_sample" */; + buildPhases = ( + 0F1FDDA92AA0AD9B00DE8967 /* Sources */, + 0F1FDDAA2AA0AD9B00DE8967 /* Frameworks */, + 0F1FDDAB2AA0AD9B00DE8967 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = foo_sample; + productName = foo_sample; + productReference = 0F1FDDAD2AA0AD9B00DE8967 /* foo_sample.component */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0F1FDDA52AA0AD9B00DE8967 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1500; + TargetAttributes = { + 0F1FDDAC2AA0AD9B00DE8967 = { + CreatedOnToolsVersion = 14.3.1; + }; + }; + }; + buildConfigurationList = 0F1FDDA82AA0AD9B00DE8967 /* Build configuration list for PBXProject "foo_sample" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 0F1FDDA42AA0AD9B00DE8967; + productRefGroup = 0F1FDDAE2AA0AD9B00DE8967 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0F1FDDAC2AA0AD9B00DE8967 /* foo_sample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 0F1FDDAB2AA0AD9B00DE8967 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0FBE14562AA1E8C800B1F71E /* fooSampleMacPreferences.xib in Resources */, + 0FCA71152AA21F69001CB0F2 /* fooSampleDSPView.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 0F1FDDA92AA0AD9B00DE8967 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0FBE14692AA1F74200B1F71E /* IO.cpp in Sources */, + 0FBE14552AA1E8C800B1F71E /* fooSampleMacPreferences.mm in Sources */, + 0FBE14672AA1F74200B1F71E /* mainmenu-dynamic.cpp in Sources */, + 0FBE14512AA1E85F00B1F71E /* NSView+embed.m in Sources */, + 0FBE14632AA1F74200B1F71E /* contextmenu.cpp in Sources */, + 0FCA71142AA21F69001CB0F2 /* fooSampleDSPView.mm in Sources */, + 0FBE14652AA1F74200B1F71E /* decode.cpp in Sources */, + 0F6D10F12AA3C35C00774EED /* fooDecibelFormatter.m in Sources */, + 0FBE146C2AA1FF1500B1F71E /* ui_and_threads.cpp in Sources */, + 0FBE14662AA1F74200B1F71E /* dsp_sample.cpp in Sources */, + 0F1FDDBB2AA0ADDF00DE8967 /* main.cpp in Sources */, + 0FBE14642AA1F74200B1F71E /* input_raw.cpp in Sources */, + 0F62440A2AA1E59E004FEC96 /* preferences.cpp in Sources */, + 0F1FDDBA2AA0ADDF00DE8967 /* initquit.cpp in Sources */, + 0FBE14622AA1F74200B1F71E /* rating.cpp in Sources */, + 0FBE146A2AA1F74200B1F71E /* playback_stream_capture.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 0F1FDDAF2AA0AD9B00DE8967 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = stdafx.h; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + .., + ../.., + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 0F1FDDB02AA0AD9B00DE8967 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = stdafx.h; + GCC_PREPROCESSOR_DEFINITIONS = "NDEBUG=1"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + .., + ../.., + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + }; + name = Release; + }; + 0F1FDDB22AA0AD9B00DE8967 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSPrincipalClass = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.foobar2000.foo-sample"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + WRAPPER_EXTENSION = component; + }; + name = Debug; + }; + 0F1FDDB32AA0AD9B00DE8967 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSPrincipalClass = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.foobar2000.foo-sample"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + WRAPPER_EXTENSION = component; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0F1FDDA82AA0AD9B00DE8967 /* Build configuration list for PBXProject "foo_sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0F1FDDAF2AA0AD9B00DE8967 /* Debug */, + 0F1FDDB02AA0AD9B00DE8967 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0F1FDDB12AA0AD9B00DE8967 /* Build configuration list for PBXNativeTarget "foo_sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0F1FDDB22AA0AD9B00DE8967 /* Debug */, + 0F1FDDB32AA0AD9B00DE8967 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0F1FDDA52AA0AD9B00DE8967 /* Project object */; +} diff --git a/sdk/foobar2000/foo_sample/foo_sample.xcworkspace/contents.xcworkspacedata b/sdk/foobar2000/foo_sample/foo_sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..be803c3 --- /dev/null +++ b/sdk/foobar2000/foo_sample/foo_sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/sdk/foobar2000/foo_sample/foobar2000-mac-class-suffix.h b/sdk/foobar2000/foo_sample/foobar2000-mac-class-suffix.h new file mode 100644 index 0000000..11fef1c --- /dev/null +++ b/sdk/foobar2000/foo_sample/foobar2000-mac-class-suffix.h @@ -0,0 +1 @@ +#define FOOBAR2000_MAC_CLASS_SUFFIX _foo_sample diff --git a/sdk/foobar2000/foo_sample/initquit.cpp b/sdk/foobar2000/foo_sample/initquit.cpp index bcd131b..62ddeb8 100644 --- a/sdk/foobar2000/foo_sample/initquit.cpp +++ b/sdk/foobar2000/foo_sample/initquit.cpp @@ -1,15 +1,17 @@ #include "stdafx.h" -// Sample initquit implementation. See also: initquit class documentation in relevant header. +namespace { + // Sample initquit implementation. See also: initquit class documentation in relevant header. + class myinitquit : public initquit { + public: + void on_init() { + console::print("Sample component: on_init()"); + } + void on_quit() { + console::print("Sample component: on_quit()"); + } + }; -class myinitquit : public initquit { -public: - void on_init() { - console::print("Sample component: on_init()"); - } - void on_quit() { - console::print("Sample component: on_quit()"); - } -}; + FB2K_SERVICE_FACTORY( myinitquit ); -static initquit_factory_t g_myinitquit_factory; +} // namespace diff --git a/sdk/foobar2000/foo_sample/main.cpp b/sdk/foobar2000/foo_sample/main.cpp index 44a6e31..bd4c0ca 100644 --- a/sdk/foobar2000/foo_sample/main.cpp +++ b/sdk/foobar2000/foo_sample/main.cpp @@ -9,3 +9,6 @@ DECLARE_COMPONENT_VERSION("Sample Component","1.0","about message goes here"); // This will prevent users from renaming your component around (important for proper troubleshooter behaviors) or loading multiple instances of it. VALIDATE_COMPONENT_FILENAME("foo_sample.dll"); + +// Activate cfg_var downgrade functionality if enabled. Relevant only when cycling from newer FOOBAR2000_TARGET_VERSION to older. +FOOBAR2000_IMPLEMENT_CFG_VAR_DOWNGRADE; diff --git a/sdk/foobar2000/foo_sample/mainmenu-dynamic.cpp b/sdk/foobar2000/foo_sample/mainmenu-dynamic.cpp index a3abad7..6243266 100644 --- a/sdk/foobar2000/foo_sample/mainmenu-dynamic.cpp +++ b/sdk/foobar2000/foo_sample/mainmenu-dynamic.cpp @@ -44,7 +44,7 @@ class sample_command : public mainmenu_node_command { // fb2k hasher_md5 API even provides a convenient method to return MD5 hashes cast to GUIDs for this. return api->get_result_guid( state ); } - bool get_description(pfc::string_base & out) { + bool get_description(pfc::string_base & out) override { out = PFC_string_formatter() << "This is a test menu item #" << m_index << "."; return true; } @@ -84,6 +84,7 @@ class sample_group : public mainmenu_node_group { }; class mainmenu_sample_dynamic : public mainmenu_commands_v2 { + typedef mainmenu_commands_v2 super_t; public: // mainmenu_commands_v2 methods t_uint32 get_command_count() override { return 1; } @@ -111,10 +112,10 @@ class mainmenu_sample_dynamic : public mainmenu_commands_v2 { // please implement it here. // ... or just skip implementing this method entirely. - return __super::dynamic_execute( index, subID, callback ); + return super_t::dynamic_execute( index, subID, callback ); } }; static service_factory_single_t g_mainmenu_sample_dynamic; -} // namespace \ No newline at end of file +} // namespace diff --git a/sdk/foobar2000/foo_sample/mainmenu.cpp b/sdk/foobar2000/foo_sample/mainmenu.cpp index da54fe7..8ab8327 100644 --- a/sdk/foobar2000/foo_sample/mainmenu.cpp +++ b/sdk/foobar2000/foo_sample/mainmenu.cpp @@ -106,7 +106,7 @@ class mainmenu_commands_sample : public mainmenu_commands { } bool get_display(t_uint32 p_index,pfc::string_base & p_text,t_uint32 & p_flags) override { // OPTIONAL method - bool rv = __super::get_display(p_index, p_text, p_flags); + bool rv = mainmenu_commands::get_display(p_index, p_text, p_flags); if (rv) switch(p_index) { case cmd_playback_stream_capture: // Add checkmark if capture is in progress diff --git a/sdk/foobar2000/foo_sample/playback_stream_capture.cpp b/sdk/foobar2000/foo_sample/playback_stream_capture.cpp index 51cf587..30cf471 100644 --- a/sdk/foobar2000/foo_sample/playback_stream_capture.cpp +++ b/sdk/foobar2000/foo_sample/playback_stream_capture.cpp @@ -27,9 +27,8 @@ namespace { if (!g_wav_writer.is_open() && ! core_api::is_shutting_down() ) { wav_writer_setup setup; setup.initialize(chunk, 16, false, false); - GUID g; CoCreateGuid(&g); pfc::string_formatter fn; - fn << "capture-" << pfc::print_guid(g) << ".wav"; + fn << "capture-" << pfc::print_guid(pfc::createGUID()) << ".wav"; pfc::string_formatter path = g_outputPath; path.add_filename( fn ); // pretty method to add file path components with auto inserted delimiter g_wav_writer.open(path, setup, abort); @@ -99,7 +98,25 @@ namespace { FB2K_SERVICE_FACTORY( play_callback_psc ); } +static void startCaptureDialogReply( fb2k::arrayRef location ) { + if ( g_active ) return; // already capturing + if ( location.is_empty() ) return; + if ( location->size() != 1 ) return; + auto obj = location->itemAt(0); + fsItemFolderPtr folder; + fb2k::stringRef strFolder; + if ( strFolder &= obj ) { + // OK + } else if ( folder &= obj ) { + strFolder = folder->canonicalPath(); + } + if ( strFolder.is_valid() ) { + g_outputPath = strFolder->c_str(); + StartCapture(); + } +} void ToggleCapture() { +#ifdef _WIN32 // Block modal dialog recursions. // Folder picker below is a modal dialog, don't ever call it if there's another modal dialog in progress. // Also prevents this function from recursing into itself if someone manages to hit the menu item while already picking folder. @@ -115,6 +132,16 @@ void ToggleCapture() { StartCapture(); } } +#else + if ( g_active ) { + StopCapture(); + } else { + auto dlg = fb2k::fileDialog::get()->setupOpenFolder(); + dlg->setTitle( "Choose output folder" ); + dlg->run( fb2k::fileDialogNotify::create( startCaptureDialogReply ) ); + } + +#endif } bool IsCaptureRunning() { diff --git a/sdk/foobar2000/foo_sample/preferences.cpp b/sdk/foobar2000/foo_sample/preferences.cpp index 97d6852..168a164 100644 --- a/sdk/foobar2000/foo_sample/preferences.cpp +++ b/sdk/foobar2000/foo_sample/preferences.cpp @@ -1,9 +1,13 @@ #include "stdafx.h" -#include "resource.h" -#include + #include #include + +#ifdef _WIN32 +#include "resource.h" +#include #include +#endif // _WIN32 // Sample preferences interface: two meaningless configuration settings accessible through a preferences page and one accessible through advanced preferences. @@ -15,26 +19,58 @@ // These GUIDs identify the variables within our component's configuration file. -static const GUID guid_cfg_bogoSetting1 = { 0xbd5c777, 0x735c, 0x440d, { 0x8c, 0x71, 0x49, 0xb6, 0xac, 0xff, 0xce, 0xb8 } }; -static const GUID guid_cfg_bogoSetting2 = { 0x752f1186, 0x9f61, 0x4f91, { 0xb3, 0xee, 0x2f, 0x25, 0xb1, 0x24, 0x83, 0x5d } }; +static constexpr GUID guid_cfg_bogoSetting1 = { 0xbd5c777, 0x735c, 0x440d, { 0x8c, 0x71, 0x49, 0xb6, 0xac, 0xff, 0xce, 0xb8 } }; +static constexpr GUID guid_cfg_bogoSetting2 = { 0x752f1186, 0x9f61, 0x4f91, { 0xb3, 0xee, 0x2f, 0x25, 0xb1, 0x24, 0x83, 0x5d } }; // This GUID identifies our Advanced Preferences branch (replace with your own when reusing code). -static const GUID guid_advconfig_branch = { 0x28564ced, 0x4abf, 0x4f0c, { 0xa4, 0x43, 0x98, 0xda, 0x88, 0xe2, 0xcd, 0x39 } }; +static constexpr GUID guid_advconfig_branch = { 0x28564ced, 0x4abf, 0x4f0c, { 0xa4, 0x43, 0x98, 0xda, 0x88, 0xe2, 0xcd, 0x39 } }; // This GUID identifies our Advanced Preferences setting (replace with your own when reusing code) as well as this setting's storage within our component's configuration file. -static const GUID guid_cfg_bogoSetting3 = { 0xf7008963, 0xed60, 0x4084, { 0xa8, 0x5d, 0xd1, 0xcd, 0xc5, 0x51, 0x22, 0xca } }; +static constexpr GUID guid_cfg_bogoSetting3 = { 0xf7008963, 0xed60, 0x4084, { 0xa8, 0x5d, 0xd1, 0xcd, 0xc5, 0x51, 0x22, 0xca } }; +static constexpr GUID guid_cfg_bogoSetting4 = { 0x99e206f8, 0xd7be, 0x47e6, { 0xb5, 0xfe, 0xf4, 0x41, 0x5f, 0x6c, 0x16, 0xed } }; +static constexpr GUID guid_cfg_bogoSetting5 = { 0x7369ad25, 0x9e81, 0x4e2f, { 0x8b, 0xe7, 0x95, 0xcb, 0x81, 0x99, 0x9b, 0x1b } }; + +static constexpr GUID guid_cfg_bogoRadio = { 0x4c18b9ab, 0xf823, 0x4d05, { 0xb6, 0x18, 0x14, 0xb9, 0x4c, 0xad, 0xa2, 0xaa } }; +static constexpr GUID guid_cfg_bogoRadio1 = { 0xdd0a3a95, 0x1546, 0x4f25, { 0xa6, 0x8c, 0x23, 0xcf, 0x3d, 0xa5, 0x8f, 0x59 } }; +static constexpr GUID guid_cfg_bogoRadio2 = { 0xc35e1689, 0xb96f, 0x4bf4, { 0x95, 0xfb, 0xb7, 0x8e, 0x83, 0xc5, 0x2c, 0x7d } }; +static constexpr GUID guid_cfg_bogoRadio3 = { 0x790fe179, 0x9908, 0x47e2, { 0x9f, 0xde, 0xce, 0xe1, 0x3f, 0x9a, 0xfc, 0x5b } }; +// defaults +constexpr int default_cfg_bogo1 = 1337, + default_cfg_bogo2 = 666, + default_cfg_bogo3 = 42; +static constexpr char default_cfg_bogo4[] = "foobar"; +static constexpr bool default_cfg_bogo5 = false; + +// Advanced preferences order enum { - default_cfg_bogoSetting1 = 1337, - default_cfg_bogoSetting2 = 666, - default_cfg_bogoSetting3 = 42, + order_bogo3, + order_bogo4, + order_bogo5, + order_bogoRadio }; -static cfg_uint cfg_bogoSetting1(guid_cfg_bogoSetting1, default_cfg_bogoSetting1), cfg_bogoSetting2(guid_cfg_bogoSetting2, default_cfg_bogoSetting2); +enum { + order_bogoRadio1, + order_bogoRadio2, + order_bogoRadio3, +}; -static advconfig_branch_factory g_advconfigBranch("Sample Component", guid_advconfig_branch, advconfig_branch::guid_branch_tools, 0); -static advconfig_integer_factory cfg_bogoSetting3("Bogo setting 3", guid_cfg_bogoSetting3, guid_advconfig_branch, 0, default_cfg_bogoSetting3, 0 /*minimum value*/, 9999 /*maximum value*/); +namespace foo_sample { // accessed also from Mac specific code hence not static + cfg_uint cfg_bogoSetting1(guid_cfg_bogoSetting1, default_cfg_bogo1), cfg_bogoSetting2(guid_cfg_bogoSetting2, default_cfg_bogo2); +} +using namespace foo_sample; +static advconfig_branch_factory g_advconfigBranch("Sample Component", guid_advconfig_branch, advconfig_branch::guid_branch_tools, 0); +static advconfig_integer_factory cfg_bogoSetting3("Bogo setting 3","foo_sample.bogo3", guid_cfg_bogoSetting3, guid_advconfig_branch, order_bogo3, default_cfg_bogo3, 0 /*minimum value*/, 9999 /*maximum value*/); +static advconfig_string_factory cfg_bogoSetting4("Bogo setting 4", "foo_sample.bogo4", guid_cfg_bogoSetting4, guid_advconfig_branch, order_bogo4, default_cfg_bogo4); +static advconfig_checkbox_factory cfg_bogoSetting5("Bogo setting 5", "foo_sample.bogo5", guid_cfg_bogoSetting5, guid_advconfig_branch, order_bogo5, default_cfg_bogo5); +static advconfig_branch_factory cfg_bogoRadioBranch("Bogo radio", guid_cfg_bogoRadio, guid_advconfig_branch, order_bogoRadio); +static advconfig_radio_factory cfg_bogoRadio1("Radio 1", "foo_sample.bogoRaidio.1", guid_cfg_bogoRadio1, guid_cfg_bogoRadio, order_bogoRadio1, true); +static advconfig_radio_factory cfg_bogoRadio2("Radio 2", "foo_sample.bogoRaidio.2", guid_cfg_bogoRadio2, guid_cfg_bogoRadio, order_bogoRadio2, false); +static advconfig_radio_factory cfg_bogoRadio3("Radio 3", "foo_sample.bogoRaidio.3", guid_cfg_bogoRadio3, guid_cfg_bogoRadio, order_bogoRadio3, false); + +#ifdef _WIN32 class CMyPreferences : public CDialogImpl, public preferences_page_instance { public: //Constructor - invoked by preferences_page_impl helpers - don't do Create() in here, preferences_page_impl does this for us @@ -93,8 +129,8 @@ t_uint32 CMyPreferences::get_state() { } void CMyPreferences::reset() { - SetDlgItemInt(IDC_BOGO1, default_cfg_bogoSetting1, FALSE); - SetDlgItemInt(IDC_BOGO2, default_cfg_bogoSetting2, FALSE); + SetDlgItemInt(IDC_BOGO1, default_cfg_bogo1, FALSE); + SetDlgItemInt(IDC_BOGO2, default_cfg_bogo2, FALSE); OnChanged(); } @@ -120,10 +156,11 @@ class preferences_page_myimpl : public preferences_page_impl { const char * get_name() {return "Sample Component";} GUID get_guid() { // This is our GUID. Replace with your own when reusing the code. - static const GUID guid = { 0x7702c93e, 0x24dc, 0x48ed, { 0x8d, 0xb1, 0x3f, 0x27, 0xb3, 0x8c, 0x7c, 0xc9 } }; - return guid; + return GUID { 0x7702c93e, 0x24dc, 0x48ed, { 0x8d, 0xb1, 0x3f, 0x27, 0xb3, 0x8c, 0x7c, 0xc9 } }; } GUID get_parent_guid() {return guid_tools;} }; static preferences_page_factory_t g_preferences_page_myimpl_factory; +#endif // _WIN32 + diff --git a/sdk/foobar2000/foo_sample/rating.cpp b/sdk/foobar2000/foo_sample/rating.cpp index a57d956..861f03b 100644 --- a/sdk/foobar2000/foo_sample/rating.cpp +++ b/sdk/foobar2000/foo_sample/rating.cpp @@ -78,11 +78,10 @@ namespace { // Static cached ptr to metadb_index_manager // Cached because we'll be calling it a lot on per-track basis, let's not pass it everywhere to low level functions // Obtaining the pointer from core is reasonably efficient - log(n) to the number of known service classes, but not good enough for something potentially called hundreds of times - static metadb_index_manager::ptr g_cachedAPI; static metadb_index_manager::ptr theAPI() { - auto ret = g_cachedAPI; - if ( ret.is_empty() ) ret = metadb_index_manager::get(); // since fb2k SDK v1.4, core API interfaces have a static get() method - return ret; + // Raw ptr instead service_ptr, don't release on DLL unload, would cause issues + static metadb_index_manager * cached = metadb_index_manager::get().detach(); + return cached; } // An init_stage_callback to hook ourselves into the metadb @@ -91,8 +90,7 @@ namespace { public: void on_init_stage(t_uint32 stage) { if (stage == init_stages::before_config_read) { - auto api = metadb_index_manager::get(); - g_cachedAPI = api; + auto api = theAPI(); // Important, handle the exceptions here! // This will fail if the files holding our data are somehow corrupted. try { @@ -108,16 +106,8 @@ namespace { } } }; - class initquit_impl : public initquit { - public: - void on_quit() { - // Cleanly kill g_cachedAPI before reaching static object destructors or else - g_cachedAPI.release(); - } - }; static service_factory_single_t g_init_stage_callback_impl; - static service_factory_single_t g_initquit_impl; typedef uint32_t rating_t; static const rating_t rating_invalid = 0; @@ -149,7 +139,7 @@ namespace { return ret; - } catch (exception_io_data) { + } catch (exception_io_data const &) { // we get here as a result of stream formatter data error // fall thru to return a blank record } diff --git a/sdk/foobar2000/foo_sample/stdafx.h b/sdk/foobar2000/foo_sample/stdafx.h index 46f3b55..989b384 100644 --- a/sdk/foobar2000/foo_sample/stdafx.h +++ b/sdk/foobar2000/foo_sample/stdafx.h @@ -1,2 +1,7 @@ +#ifdef __cplusplus #include +#endif +#ifdef __OBJC__ +#include +#endif diff --git a/sdk/foobar2000/foo_sample/ui_and_threads.cpp b/sdk/foobar2000/foo_sample/ui_and_threads.cpp index 970585b..b143a26 100644 --- a/sdk/foobar2000/foo_sample/ui_and_threads.cpp +++ b/sdk/foobar2000/foo_sample/ui_and_threads.cpp @@ -4,6 +4,8 @@ // Modern multi threading with C++ // Or: how I learned to stop worrying and love the lambdas +#ifdef _WIN32 + #include // shared_ptr #include #include @@ -122,7 +124,7 @@ namespace { // anon namespace local classes for good measure // In worker thread! try { work( shared, aborter ); - } catch(exception_aborted) { + } catch(exception_aborted const &) { return; // user abort? } catch(std::exception const & e) { // should not really get here @@ -284,3 +286,11 @@ void RunUIAndThreads(metadb_handle_list_cref data) { // Equivalent to new CDemoDialog(data), with modeless registration and auto lifetime fb2k::newDialog( data ); } + +#else + +void RunUIAndThreads(metadb_handle_list_cref data) { + popup_message::g_showToast("Not implemented for Mac OS yet"); +} + +#endif diff --git a/sdk/foobar2000/foobar2000_component_client/component_client.cpp b/sdk/foobar2000/foobar2000_component_client/component_client.cpp index 6d0d6d8..7ad217a 100644 --- a/sdk/foobar2000/foobar2000_component_client/component_client.cpp +++ b/sdk/foobar2000/foobar2000_component_client/component_client.cpp @@ -2,8 +2,9 @@ #include #include +#ifdef _WIN32 static HINSTANCE g_hIns; - +#endif static pfc::string_simple g_name,g_full_path; static bool g_services_available = false, g_initialized = false; @@ -13,12 +14,13 @@ static bool g_services_available = false, g_initialized = false; namespace core_api { +#ifdef _WIN32 HINSTANCE get_my_instance() { return g_hIns; } - - HWND get_main_window() +#endif + fb2k::hwnd_t get_main_window() { PFC_ASSERT( g_foobar2000_api != NULL ); return g_foobar2000_api->get_main_window(); @@ -79,43 +81,40 @@ namespace { class foobar2000_client_impl : public foobar2000_client, private foobar2000_component_globals { public: - t_uint32 get_version() {return FOOBAR2000_CLIENT_VERSION;} - pservice_factory_base get_service_list() {return service_factory_base::__internal__list;} + t_uint32 get_version() override {return FOOBAR2000_CLIENT_VERSION;} + pservice_factory_base get_service_list() override {return service_factory_base::__internal__list;} - void get_config(stream_writer * p_stream,abort_callback & p_abort) { + void get_config(stream_writer * p_stream,abort_callback & p_abort) override { #ifdef FOOBAR2000_HAVE_CFG_VAR_LEGACY cfg_var_legacy::cfg_var::config_write_file(p_stream,p_abort); #endif } - void set_config(stream_reader * p_stream,abort_callback & p_abort) { + void set_config(stream_reader * p_stream,abort_callback & p_abort) override { #ifdef FOOBAR2000_HAVE_CFG_VAR_LEGACY cfg_var_legacy::cfg_var::config_read_file(p_stream,p_abort); #endif } - void set_library_path(const char * path,const char * name) { + void set_library_path(const char * path,const char * name) override { g_full_path = path; g_name = name; } - void services_init(bool val) { + void services_init(bool val) override { if (val) g_initialized = true; g_services_available = val; } - bool is_debug() { -#ifdef _DEBUG - return true; -#else - return false; -#endif + bool is_debug() override { + return PFC_DEBUG != 0; } }; } static foobar2000_client_impl g_client; +#ifdef _WIN32 extern "C" { __declspec(dllexport) foobar2000_client * _cdecl foobar2000_get_interface(foobar2000_api * p_api,HINSTANCE hIns) @@ -126,3 +125,13 @@ extern "C" return &g_client; } } +#endif + +#ifdef __APPLE__ +extern "C" { + __attribute__ ((visibility ("default"))) foobar2000_client * foobar2000_get_interface(foobar2000_api * p_api, void * /*reserved*/) { + g_foobar2000_api = p_api; + return &g_client; + } +} +#endif diff --git a/sdk/foobar2000/foobar2000_component_client/foobar2000_component_client.vcxproj b/sdk/foobar2000/foobar2000_component_client/foobar2000_component_client.vcxproj index 51a2fdb..339e7ff 100644 --- a/sdk/foobar2000/foobar2000_component_client/foobar2000_component_client.vcxproj +++ b/sdk/foobar2000/foobar2000_component_client/foobar2000_component_client.vcxproj @@ -37,6 +37,7 @@ {71AD2674-065B-48F5-B8B0-E1F9D3892081} foobar2000_component_client + 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 diff --git a/sdk/foobar2000/foobar2000_component_client/foobar2000_component_client.xcodeproj/project.pbxproj b/sdk/foobar2000/foobar2000_component_client/foobar2000_component_client.xcodeproj/project.pbxproj new file mode 100644 index 0000000..7029197 --- /dev/null +++ b/sdk/foobar2000/foobar2000_component_client/foobar2000_component_client.xcodeproj/project.pbxproj @@ -0,0 +1,282 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 0F1FDDDC2AA0B33A00DE8967 /* component_client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F1FDDDB2AA0B33A00DE8967 /* component_client.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0F1FDDD42AA0B2FB00DE8967 /* libfoobar2000_component_client.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libfoobar2000_component_client.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 0F1FDDDB2AA0B33A00DE8967 /* component_client.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = component_client.cpp; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0F1FDDD22AA0B2FB00DE8967 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0F1FDDCB2AA0B2FB00DE8967 = { + isa = PBXGroup; + children = ( + 0F1FDDDB2AA0B33A00DE8967 /* component_client.cpp */, + 0F1FDDD52AA0B2FB00DE8967 /* Products */, + ); + sourceTree = ""; + }; + 0F1FDDD52AA0B2FB00DE8967 /* Products */ = { + isa = PBXGroup; + children = ( + 0F1FDDD42AA0B2FB00DE8967 /* libfoobar2000_component_client.a */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 0F1FDDD02AA0B2FB00DE8967 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 0F1FDDD32AA0B2FB00DE8967 /* foobar2000_component_client */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0F1FDDD82AA0B2FB00DE8967 /* Build configuration list for PBXNativeTarget "foobar2000_component_client" */; + buildPhases = ( + 0F1FDDD02AA0B2FB00DE8967 /* Headers */, + 0F1FDDD12AA0B2FB00DE8967 /* Sources */, + 0F1FDDD22AA0B2FB00DE8967 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = foobar2000_component_client; + productName = foobar2000_component_client; + productReference = 0F1FDDD42AA0B2FB00DE8967 /* libfoobar2000_component_client.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0F1FDDCC2AA0B2FB00DE8967 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1430; + TargetAttributes = { + 0F1FDDD32AA0B2FB00DE8967 = { + CreatedOnToolsVersion = 14.3.1; + }; + }; + }; + buildConfigurationList = 0F1FDDCF2AA0B2FB00DE8967 /* Build configuration list for PBXProject "foobar2000_component_client" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 0F1FDDCB2AA0B2FB00DE8967; + productRefGroup = 0F1FDDD52AA0B2FB00DE8967 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0F1FDDD32AA0B2FB00DE8967 /* foobar2000_component_client */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 0F1FDDD12AA0B2FB00DE8967 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0F1FDDDC2AA0B33A00DE8967 /* component_client.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 0F1FDDD62AA0B2FB00DE8967 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + .., + ../.., + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 0F1FDDD72AA0B2FB00DE8967 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + .., + ../.., + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + }; + name = Release; + }; + 0F1FDDD92AA0B2FB00DE8967 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 4S876G9VCD; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 0F1FDDDA2AA0B2FB00DE8967 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 4S876G9VCD; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0F1FDDCF2AA0B2FB00DE8967 /* Build configuration list for PBXProject "foobar2000_component_client" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0F1FDDD62AA0B2FB00DE8967 /* Debug */, + 0F1FDDD72AA0B2FB00DE8967 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0F1FDDD82AA0B2FB00DE8967 /* Build configuration list for PBXNativeTarget "foobar2000_component_client" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0F1FDDD92AA0B2FB00DE8967 /* Debug */, + 0F1FDDDA2AA0B2FB00DE8967 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0F1FDDCC2AA0B2FB00DE8967 /* Project object */; +} diff --git a/sdk/foobar2000/helpers-mac/CFObject.h b/sdk/foobar2000/helpers-mac/CFObject.h new file mode 100644 index 0000000..314af3d --- /dev/null +++ b/sdk/foobar2000/helpers-mac/CFObject.h @@ -0,0 +1,4 @@ +#pragma once +#include +using pfc::CFObject; + diff --git a/sdk/foobar2000/helpers-mac/NSComboBox+fb2k.h b/sdk/foobar2000/helpers-mac/NSComboBox+fb2k.h new file mode 100644 index 0000000..0912fbb --- /dev/null +++ b/sdk/foobar2000/helpers-mac/NSComboBox+fb2k.h @@ -0,0 +1,9 @@ +#import + +#import +#import + +namespace fb2k { + void comboSetupHistory(NSComboBox *, cfg_dropdown_history & var); + void comboAddToHistory(NSComboBox *, cfg_dropdown_history & var); +} diff --git a/sdk/foobar2000/helpers-mac/NSComboBox+fb2k.mm b/sdk/foobar2000/helpers-mac/NSComboBox+fb2k.mm new file mode 100644 index 0000000..34fc235 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/NSComboBox+fb2k.mm @@ -0,0 +1,23 @@ +#import "NSComboBox+fb2k.h" + +namespace fb2k { + void comboSetupHistory(NSComboBox * box, cfg_dropdown_history & var) { + [box removeAllItems]; + pfc::string8 temp; var.get_state( temp ); + NSString * str = [NSString stringWithUTF8String: temp.c_str()]; + if ( str.length == 0 ) return; + NSArray * arr = [str componentsSeparatedByCharactersInSet: NSCharacterSet.newlineCharacterSet]; + if ( arr.count == 0 ) return; + for( NSString * str in arr ) { + if ( str.length > 0 ) [box addItemWithObjectValue: str]; + } + box.stringValue = arr.firstObject; + + [box.menu addItem: NSMenuItem.separatorItem]; + } + + void comboAddToHistory(NSComboBox * box, cfg_dropdown_history & var) { + NSString * str = box.stringValue; + if ( str.length > 0 ) var.add_item( str.UTF8String ); + } +} // namespace fb2k diff --git a/sdk/foobar2000/helpers-mac/NSEvent+ppstuff.h b/sdk/foobar2000/helpers-mac/NSEvent+ppstuff.h new file mode 100644 index 0000000..5724d12 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/NSEvent+ppstuff.h @@ -0,0 +1,13 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSEvent (ppstuff) + +@property (readonly) BOOL pp_isKeyDown; +@property (readonly) BOOL pp_isCmdKeyDown; +@property (readonly) BOOL pp_isShiftKeyDown; +@property (readonly) NSEventModifierFlags pp_modifierFlagsDI; +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk/foobar2000/helpers-mac/NSEvent+ppstuff.m b/sdk/foobar2000/helpers-mac/NSEvent+ppstuff.m new file mode 100644 index 0000000..990bc35 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/NSEvent+ppstuff.m @@ -0,0 +1,17 @@ +#import "NSEvent+ppstuff.h" + +@implementation NSEvent (ppstuff) + +- (BOOL)pp_isKeyDown { + return self.type == NSEventTypeKeyDown; +} +- (BOOL)pp_isCmdKeyDown { + return self.pp_isKeyDown && self.pp_modifierFlagsDI == NSEventModifierFlagCommand; +} +- (BOOL)pp_isShiftKeyDown { + return self.pp_isKeyDown && self.pp_modifierFlagsDI == NSEventModifierFlagShift; +} +- (NSEventModifierFlags)pp_modifierFlagsDI { + return self.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; +} +@end diff --git a/sdk/foobar2000/helpers-mac/NSFont+pp.h b/sdk/foobar2000/helpers-mac/NSFont+pp.h new file mode 100644 index 0000000..2b7a3a9 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/NSFont+pp.h @@ -0,0 +1,16 @@ +// +// NSFont+pp.h +// foobar2000 +// +// Created by Piotr Pawłowski on 30/06/2024. +// Copyright © 2024 Piotr Pawłowski. All rights reserved. +// + +#import + +@interface NSFont (pp) +// Recommended font for all text data, because default fixed-width digit behavior is horribly annoying +// PROTIP if NSTableView is ignoring font alterations, make sure row height is set explicitly +@property (class, readonly) NSFont* pp_monospacedDigitFont; +@property (class, readonly) NSFont* pp_monospacedFont; +@end diff --git a/sdk/foobar2000/helpers-mac/NSFont+pp.m b/sdk/foobar2000/helpers-mac/NSFont+pp.m new file mode 100644 index 0000000..04344f7 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/NSFont+pp.m @@ -0,0 +1,27 @@ +// +// NSFont+pp.m +// foobar2000 +// +// Created by Piotr Pawłowski on 30/06/2024. +// Copyright © 2024 Piotr Pawłowski. All rights reserved. +// + +#import "NSFont+pp.h" + +static NSFont * g_monospacedDigitFont; +static NSFont * g_monospacedFont; + +@implementation NSFont (pp) ++ (NSFont*) pp_monospacedDigitFont { + assert( NSThread.isMainThread ); + if ( g_monospacedDigitFont == nil ) g_monospacedDigitFont = [NSFont monospacedDigitSystemFontOfSize: NSFont.systemFontSize weight: NSFontWeightRegular]; + return g_monospacedDigitFont; +} ++ (NSFont*) pp_monospacedFont { + assert( NSThread.isMainThread ); + if ( g_monospacedFont == nil ) { + g_monospacedFont = [NSFont monospacedSystemFontOfSize: NSFont.systemFontSize weight: NSFontWeightRegular]; + } + return g_monospacedFont; +} +@end diff --git a/sdk/foobar2000/helpers-mac/NSMenu+ppaddons.h b/sdk/foobar2000/helpers-mac/NSMenu+ppaddons.h new file mode 100644 index 0000000..27c27c4 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/NSMenu+ppaddons.h @@ -0,0 +1,10 @@ +#import + +@interface NSMenu (ppaddons) + +- (NSMenuItem*) pp_addItemWithTitle: (NSString*) title action: (SEL) action target: (id) target; +- (void) pp_addSeparator; +- (NSMenuItem*) pp_addSubMenu: (NSMenu*) menu withTitle: (NSString*) title; +- (void) pp_popUpForView:(NSView *)view; + +@end diff --git a/sdk/foobar2000/helpers-mac/NSMenu+ppaddons.m b/sdk/foobar2000/helpers-mac/NSMenu+ppaddons.m new file mode 100644 index 0000000..d7ef891 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/NSMenu+ppaddons.m @@ -0,0 +1,39 @@ +#import "NSMenu+ppaddons.h" + +@implementation NSMenu (ppaddons) + +- (NSMenuItem*) pp_addItemWithTitle: (NSString*) title action: (SEL) action target: (id) target { + NSMenuItem * item = [[NSMenuItem alloc] initWithTitle: title action: action keyEquivalent: @""]; + item.target = target; + [self addItem: item]; + return item; +} + +- (void) pp_addSeparator { + [self addItem: [NSMenuItem separatorItem]]; +} + +- (NSMenuItem*) pp_addSubMenu: (NSMenu*) menu withTitle: (NSString*) title { + NSMenuItem * item = [[NSMenuItem alloc] init]; + item.title = title; + item.submenu = menu; + [self addItem: item]; + return item; +} + +- (void)pp_popUpForView:(NSView *)view { + BOOL pullsDown = YES; + NSMenu *popMenu = [self copy]; + NSRect frame = [view frame]; + frame.origin.x = 0.0; + frame.origin.y = 0.0; + + if (pullsDown) [popMenu insertItemWithTitle:@"" action:NULL keyEquivalent:@"" atIndex:0]; + + NSPopUpButtonCell *popUpButtonCell = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:pullsDown]; + [popUpButtonCell setMenu:popMenu]; + if (!pullsDown) [popUpButtonCell selectItem:nil]; + [popUpButtonCell performClickWithFrame:frame inView:view]; +} + +@end diff --git a/sdk/foobar2000/helpers-mac/NSView+embed.h b/sdk/foobar2000/helpers-mac/NSView+embed.h new file mode 100644 index 0000000..527be40 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/NSView+embed.h @@ -0,0 +1,19 @@ +#import + +#if 0 +@interface NSView (embed) +- (void) embedInView: (NSView*) superview; // uses autoresizingMask +- (void) embedInViewV2: (NSView*) superview; // adds autolayout constraints +@end +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +void NSViewEmbed( NSView * superview, NSView * childview ); +void NSViewEmbedConstraints( NSView * superview, NSView * childview ); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sdk/foobar2000/helpers-mac/NSView+embed.m b/sdk/foobar2000/helpers-mac/NSView+embed.m new file mode 100644 index 0000000..b4dd3d8 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/NSView+embed.m @@ -0,0 +1,35 @@ +#import "NSView+embed.h" + +#if 0 +@implementation NSView (embed) + +- (void)embedInView:(NSView *)superview { + NSViewEmbed( superview, self ); +} +- (void)embedInViewV2:(NSView *)superview { + NSViewEmbedConstraints( superview, self ); +} + +@end +#endif + +void NSViewEmbed( NSView * superview, NSView * childview ) { + if ( childview == nil || superview == nil ) return; + childview.autoresizingMask = NSViewHeightSizable | NSViewWidthSizable; + childview.frame = superview.bounds; + [superview addSubview: childview]; +} + +void NSViewEmbedConstraints( NSView * superview, NSView * childview ) { + if ( childview == nil || superview == nil ) return; + childview.autoresizingMask = 0; + [superview addSubview: childview]; + NSMutableArray * constraints = [NSMutableArray arrayWithCapacity: 4]; + static const NSLayoutAttribute params[4] = { NSLayoutAttributeLeft, NSLayoutAttributeRight, NSLayoutAttributeTop, NSLayoutAttributeBottom }; + for( unsigned i = 0; i < 4; ++ i) { + NSLayoutAttribute a = params[i]; + [constraints addObject: [NSLayoutConstraint constraintWithItem: childview attribute:a relatedBy:NSLayoutRelationEqual toItem:superview attribute:a multiplier:1 constant:0]]; + } + + [superview addConstraints: constraints]; +} diff --git a/sdk/foobar2000/helpers-mac/NSView+ppsubviews.h b/sdk/foobar2000/helpers-mac/NSView+ppsubviews.h new file mode 100644 index 0000000..9128567 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/NSView+ppsubviews.h @@ -0,0 +1,24 @@ +#import + +#if 0 +@interface NSView (ppsubviews) +- (NSView*) recurFindSubViewOfClass: (Class) cls identifier: (NSString*) identifier; +- (NSView*) findSubViewOfClass: (Class) cls identifier: (NSString*) identifier; +- (NSView*) findSubViewOfClass: (Class) cls; +- (NSButton*) findButton; +- (NSTextView*) findTextView; +- (NSTextField*) findTextField; +- (NSImageView*) findImageView; +@end +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +__kindof NSView * NSViewFindSubView( NSView * parent, Class clsOrNull, NSUserInterfaceItemIdentifier idOrNull ); +__kindof NSView * NSViewFindSubViewRecursive( NSView * parent, Class clsOrNull, NSUserInterfaceItemIdentifier idOrNull ); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/sdk/foobar2000/helpers-mac/NSView+ppsubviews.m b/sdk/foobar2000/helpers-mac/NSView+ppsubviews.m new file mode 100644 index 0000000..7926e5c --- /dev/null +++ b/sdk/foobar2000/helpers-mac/NSView+ppsubviews.m @@ -0,0 +1,59 @@ +#import "NSView+ppsubviews.h" + +#if 0 +@implementation NSView (ppsubviews) + +- (NSView*) recurFindSubViewOfClass: (Class) cls identifier: (NSString*) identifier { + return NSViewFindSubViewRecursive ( self, cls, identifier ); +} + +- (NSView*) findSubViewOfClass: (Class) cls identifier: (NSString*) identifier { + return NSViewFindSubView( self, cls, identifier ); +} +- (NSView *)findSubViewOfClass:(Class)cls { + return NSViewFindSubView( self, cls, nil ); +} +- (NSButton *)findButton { + return (NSButton*) [self findSubViewOfClass: [NSButton class]]; +} +- (NSTextView *)findTextView { + return (NSTextView*) [self findSubViewOfClass: [NSTextView class]]; +} +- (NSTextField *) findTextField { + return (NSTextField*) [self findSubViewOfClass: [NSTextField class]]; +} +- (NSImageView*) findImageView { + return (NSImageView*) [self findSubViewOfClass: [NSImageView class]]; +} +@end +#endif + +__kindof NSView * NSViewFindSubView( NSView * parent, Class cls, NSUserInterfaceItemIdentifier identifier ) { + for (__kindof NSView * v in parent.subviews) { + if ( (cls == nil || [v isKindOfClass: cls]) && ( identifier == nil || [ v.identifier isEqualToString: identifier ] ) ) { + return v; + } + } + return nil; +} + +__kindof NSView * NSViewFindSubViewRecursive( NSView * parent, Class cls, NSUserInterfaceItemIdentifier identifier ) { + @autoreleasepool { + NSMutableArray<__kindof NSView*> * arrAll = [NSMutableArray arrayWithArray: parent.subviews]; + + for ( NSUInteger w = 0; w < arrAll.count; ++ w ) { + __kindof NSView * thisView = arrAll[w]; + + if ( (cls == nil || [thisView isKindOfClass: cls]) && ( identifier == nil || [thisView.identifier isEqualToString: identifier] ) ) { + return thisView; + } + [arrAll addObjectsFromArray: thisView.subviews]; + + if ( w >= 200 ) { + [arrAll removeObjectsInRange: NSMakeRange(0, w) ]; + w = 0; + } + } + return nil; + } +} diff --git a/sdk/foobar2000/helpers-mac/fb2k-platform.h b/sdk/foobar2000/helpers-mac/fb2k-platform.h new file mode 100644 index 0000000..32bb5a9 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/fb2k-platform.h @@ -0,0 +1,43 @@ +#pragma once + +#import +#import + +namespace fb2k { + // May return null on bad input. + NSString * strToPlatform( const char * ); + // May return null on bad input. + NSString * strToPlatform( const char * , size_t ); + // May return null on bad input. + NSString * strToPlatform( stringRef ); + // Never returns null - returns passed string in case of failure + NSString * strToPlatform( const char *, NSString * returnIfError ); + + stringRef strFromPlatform( NSString * ); + + + stringRef urlFromPlatform( id url /* can be NSString or NSURL */ ); + NSURL * urlToPlatform(const char * arg); + + + typedef NSImage* platformImage_t; + platformImage_t imageToPlatform( fb2k::objRef ); + + + // These two functions do the same, openWebBrowser() was added for compatiblity with fb2k mobile + void openWebBrowser(const char * URL); + void openURL( const char * URL); + + NSFont * fontFromParams(NSDictionary *, NSFont * base = nil); + BOOL testFontParams(NSDictionary *); + CGFloat tableViewRowHeightForFont( NSFont * ); + void tableViewPrepareForFont( NSTableView * tableView, NSFont * font ); + +} + +namespace pfc { + string8 strFromPlatform(NSString*); + NSString * strToPlatform( const char * ); + NSString * strToPlatform(string8 const&); + string8 strFromPlatform(CFStringRef); +} diff --git a/sdk/foobar2000/helpers-mac/fb2k-platform.mm b/sdk/foobar2000/helpers-mac/fb2k-platform.mm new file mode 100644 index 0000000..f8683c8 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/fb2k-platform.mm @@ -0,0 +1,118 @@ +#include "fb2k-platform.h" +#include "NSFont+pp.h" + +namespace fb2k { + NSString * strToPlatform( const char * s, size_t len ) { + pfc::string8 temp( s, len ); + return strToPlatform( temp ); + } + NSString * strToPlatform( const char * arg, NSString * returnIfError ) { + if ( arg ) @try { + NSString * ret = [NSString stringWithUTF8String: arg]; + if ( ret ) return ret; + } @catch(NSException *) {} + return returnIfError; + } + NSString * strToPlatform( const char * s ) { + return [NSString stringWithUTF8String: s]; + } + NSString * strToPlatform( stringRef s ) { + if ( s.is_empty( ) ) return nil; + return strToPlatform( s->c_str() ); + } + stringRef strFromPlatform( NSString * s ) { + return makeString( s.UTF8String ); + } + + stringRef urlFromPlatform( id obj ) { + if ( [obj isKindOfClass: [NSURL class] ] ) { + NSURL * URL = obj; + obj = URL.absoluteString; + if ([obj hasPrefix:@"file://"]) + obj = URL.path; + } + if ( [obj respondsToSelector: @selector(UTF8String)]) { + pfc::string8 temp; + filesystem::g_get_canonical_path( [obj UTF8String], temp ); + return makeString( temp ); + } + return nullptr; + } + + NSURL * urlToPlatform(const char * arg) { + pfc::string8 native; + if (filesystem::g_get_native_path(arg, native)) { + return [NSURL fileURLWithPath: [NSString stringWithUTF8String: native ] ]; + } + return [NSURL URLWithString: [NSString stringWithUTF8String: arg]]; + } + + void openURL( const char * URL ) { + @autoreleasepool { + [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: [NSString stringWithUTF8String: URL]]]; + } + } + void openWebBrowser( const char * URL) { + openURL(URL); + } + + NSImage * imageToPlatform( fb2k::objRef obj ) { + fb2k::image::ptr img; + if ( img &= obj ) { + return (__bridge NSImage *) img->getNative(); + } + return nil; + } + BOOL testFontParams(NSDictionary * arg) { + return arg[@"font-name"] || arg[@"font-size"]; + } + NSFont * fontFromParams(NSDictionary * arg, NSFont * base) { + NSString * fontName = arg[@"font-name"]; + NSString * fontSize = arg[@"font-size"]; + NSFont * font = nil; + if ( fontName && fontSize ) { + font = [NSFont fontWithName: fontName size: fontSize.floatValue]; + } else if ( fontName ) { + font = [NSFont fontWithName: fontName size: base ? base.pointSize : NSFont.systemFontSize]; + } else if ( fontSize ) { + if ( base ) { + font = [ base fontWithSize: fontSize.floatValue ]; + } else { + font = [NSFont monospacedDigitSystemFontOfSize: fontSize.floatValue weight: NSFontWeightRegular]; + } + } + if ( font ) return font; + if ( base ) return base; + // Reuse shared font object + return [NSFont pp_monospacedDigitFont]; + } + void tableViewPrepareForFont( NSTableView * tableView, NSFont * font ) { + + // Must use NSTableViewRowSizeStyleCustom + // Other options either discard our font or break down with multiple cell templates used (autolayout) + // Height is fixed anyway, better precalculate it + assert( tableView.rowSizeStyle == NSTableViewRowSizeStyleCustom ); + + tableView.rowHeight = tableViewRowHeightForFont(font); + } + CGFloat tableViewRowHeightForFont( NSFont * f ) { + return (CGFloat) round( f.pointSize * 1.5 ); + } +} + +namespace pfc { + string8 strFromPlatform(NSString* str) { + string8 ret; + if ( str ) ret = str.UTF8String; + return ret; + } + NSString * strToPlatform( const char * str) { + return fb2k::strToPlatform( str ); + } + NSString * strToPlatform(string8 const& str) { + return fb2k::strToPlatform( str.c_str() ); + } + string8 strFromPlatform(CFStringRef str) { + return strFromPlatform( (__bridge NSString*) str ); + } +} diff --git a/sdk/foobar2000/helpers-mac/fooDecibelFormatter.h b/sdk/foobar2000/helpers-mac/fooDecibelFormatter.h new file mode 100644 index 0000000..9c079d9 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/fooDecibelFormatter.h @@ -0,0 +1,12 @@ +#import + +#import "foobar2000-mac-helpers.h" + +#define fooDecibelFormatter FB2K_OBJC_CLASS(fooDecibelFormatter) + +@interface fooDecibelFormatter : NSFormatter + +@property (nonatomic) NSNumber * minValue; +@property (nonatomic) NSNumber * maxValue; +@property (nonatomic) BOOL showSignAlways; +@end diff --git a/sdk/foobar2000/helpers-mac/fooDecibelFormatter.m b/sdk/foobar2000/helpers-mac/fooDecibelFormatter.m new file mode 100644 index 0000000..06fb01f --- /dev/null +++ b/sdk/foobar2000/helpers-mac/fooDecibelFormatter.m @@ -0,0 +1,58 @@ +#import "fooDecibelFormatter.h" + +@implementation fooDecibelFormatter + +- (NSString*) formatNumber: (id) obj { + + double v = [ obj doubleValue ]; + + if ( self.showSignAlways ) { + if (v == 0) return [NSString stringWithUTF8String: "\xc2\xb1" "0"]; + if ( v > 0 ) { + if (v == (double) (int) v) return [NSString stringWithFormat: @"+%i", (int)v]; + else return [NSString stringWithFormat: @"+%.02f", v]; + } + } + + if (v == (double) (int) v) return [NSString stringWithFormat: @"%i", (int)v]; + else return [NSString stringWithFormat: @"%.02f", v]; +} + +- (NSString *)stringForObjectValue:(id)obj { + return [NSString stringWithFormat: @"%@ dB", [self formatNumber: obj]]; +} + +- (NSAttributedString *)attributedStringForObjectValue:(id)obj withDefaultAttributes:(NSDictionary *)attrs { + return nil; +} + +- (NSString *)editingStringForObjectValue:(id)obj { + return [self stringForObjectValue: obj]; + // return [self formatNumber: obj]; +} + +- (BOOL)getObjectValue:(out __autoreleasing id *)obj forString:(NSString *)string errorDescription:(out NSString *__autoreleasing *)error { + if (error) *error = nil; + + { + NSMutableCharacterSet * trimMe = [NSMutableCharacterSet whitespaceAndNewlineCharacterSet]; + [trimMe addCharactersInString: [NSString stringWithUTF8String: "+\xc2\xb1" ]]; + string = [ string.lowercaseString stringByTrimmingCharactersInSet: trimMe ]; + } + + if ( [string hasSuffix: @"db"]) { + string = [ [string substringToIndex: string.length-2] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet] ]; + } + + double v = string.doubleValue; + if (self.minValue) { + if (v < self.minValue.doubleValue) v = self.minValue.doubleValue; + } + if (self.maxValue) { + if (v > self.maxValue.doubleValue) v = self.maxValue.doubleValue; + } + *obj = [NSNumber numberWithDouble: v]; + return YES; +} + +@end diff --git a/sdk/foobar2000/helpers-mac/fooPreferencesCommon.h b/sdk/foobar2000/helpers-mac/fooPreferencesCommon.h new file mode 100644 index 0000000..ba9af3d --- /dev/null +++ b/sdk/foobar2000/helpers-mac/fooPreferencesCommon.h @@ -0,0 +1,12 @@ +#import +#import + +typedef NSViewController fooPreferencesCommon; + +template +class preferences_mac_common : public preferences_page_v4 { +public: + service_ptr instantiate() override { + return fb2k::wrapNSObject( [obj_t new] ); + } +}; diff --git a/sdk/foobar2000/helpers-mac/fooTimeFormatter.h b/sdk/foobar2000/helpers-mac/fooTimeFormatter.h new file mode 100644 index 0000000..db449c4 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/fooTimeFormatter.h @@ -0,0 +1,15 @@ +// +// fooTimeFormatter.h +// foo_abx +// +// Created by P on 06/09/2023. +// + +#import +#import "foobar2000-mac-helpers.h" + +#define fooTimeFormatter FB2K_OBJC_CLASS(fooTimeFormatter) + +@interface fooTimeFormatter : NSFormatter +@property (nonatomic) NSNumber * digits; +@end diff --git a/sdk/foobar2000/helpers-mac/fooTimeFormatter.mm b/sdk/foobar2000/helpers-mac/fooTimeFormatter.mm new file mode 100644 index 0000000..38e2981 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/fooTimeFormatter.mm @@ -0,0 +1,37 @@ +// +// fooTimeFormatter.m +// foo_abx +// +// Created by P on 06/09/2023. +// + +#import "stdafx.h" +#import "fooTimeFormatter.h" + +@implementation fooTimeFormatter + +- (NSString *)stringForObjectValue:(id)obj { + double v = 0; + if ( [obj respondsToSelector: @selector(doubleValue)]) v = [obj doubleValue]; + else if ( [obj respondsToSelector: @selector(longValue)]) v = [obj longValue]; + else if ( [obj respondsToSelector: @selector(intValue)]) v = [obj intValue]; + + unsigned digits = 3; + if ( _digits ) digits = _digits.unsignedIntValue; + return [NSString stringWithUTF8String: pfc::format_time_ex(v, digits)]; +} + +- (NSString *)editingStringForObjectValue:(id)obj { + return [self stringForObjectValue: obj]; +} +- (BOOL)getObjectValue:(out id _Nullable __autoreleasing *)obj forString:(NSString *)string errorDescription:(out NSString * _Nullable __autoreleasing *)error { + if ( string == nil ) return NO; + try { + double v = pfc::parse_timecode( string.UTF8String ); + *obj = [NSNumber numberWithDouble: v]; + return YES; + } catch(...) { + return NO; + } +} +@end diff --git a/sdk/foobar2000/helpers-mac/fooWindowWithCancel.h b/sdk/foobar2000/helpers-mac/fooWindowWithCancel.h new file mode 100644 index 0000000..9d41a9d --- /dev/null +++ b/sdk/foobar2000/helpers-mac/fooWindowWithCancel.h @@ -0,0 +1,8 @@ +#import +#import "foobar2000-mac-helpers.h" + +#define fooWindowWithCancel FB2K_OBJC_CLASS(fooWindowWithCancel) + +@interface fooWindowWithCancel : NSWindow + +@end diff --git a/sdk/foobar2000/helpers-mac/fooWindowWithCancel.m b/sdk/foobar2000/helpers-mac/fooWindowWithCancel.m new file mode 100644 index 0000000..8c70d8f --- /dev/null +++ b/sdk/foobar2000/helpers-mac/fooWindowWithCancel.m @@ -0,0 +1,10 @@ +#import "fooWindowWithCancel.h" + +@implementation fooWindowWithCancel +- (void) cancelOperation: (id) sender { + id d = self.delegate; + if ( [d respondsToSelector: @selector(cancelOperation:)]) { + [d cancelOperation: sender]; + } +} +@end diff --git a/sdk/foobar2000/helpers-mac/foobar2000-mac-helpers.h b/sdk/foobar2000/helpers-mac/foobar2000-mac-helpers.h new file mode 100644 index 0000000..5c65212 --- /dev/null +++ b/sdk/foobar2000/helpers-mac/foobar2000-mac-helpers.h @@ -0,0 +1,16 @@ +#pragma once + +// Mitigation for Objective-C class name clashes +// Your component must have its own foobar2000-mac-class-suffix.h, with a single line: +// #define FOOBAR2000_MAC_CLASS_SUFFIX _foo_mycomponentname +// This suffixes all common class names with _foo_mycomponentname preventing clashes + +#include "foobar2000-mac-class-suffix.h" + +#ifndef FOOBAR2000_MAC_CLASS_SUFFIX +#error PLEASE DECLARE FOOBAR2000_MAC_CLASS_SUFFIX PROPERLY +#endif + +#define FB2K_OBJC_CLASS(X) _FB2K_OBJC_CONCAT(X, FOOBAR2000_MAC_CLASS_SUFFIX) +#define _FB2K_OBJC_CONCAT(a, b) _FB2K_OBJC_CONCAT_INNER(a, b) +#define _FB2K_OBJC_CONCAT_INNER(a, b) a ## b diff --git a/sdk/foobar2000/helpers/CListControlFb2kColors.h b/sdk/foobar2000/helpers/CListControlFb2kColors.h index d1f3290..a5175fb 100644 --- a/sdk/foobar2000/helpers/CListControlFb2kColors.h +++ b/sdk/foobar2000/helpers/CListControlFb2kColors.h @@ -3,6 +3,7 @@ // foobar2000 v2.0+ only #if FOOBAR2000_TARGET_VERSION >= 81 +// One-line fix for UI colors in CListControl, use CListControlFb2kColors< myCListControSubClass > to use fb2k colors template class CListControlFb2kColors : public parent_t, protected ui_config_callback_impl { public: @@ -22,3 +23,23 @@ class CListControlFb2kColors : public parent_t, protected ui_config_callback_imp }; #endif + +// For use in UI elements +// Not completely selfcontained, host must pass ui_element_instance_callback and call ui_colors_changed() +template +class CListControlFb2kColorsUIElem : public parent_t { +public: + template + CListControlFb2kColorsUIElem(ui_element_instance_callback::ptr callback, arg_t && ... args) : parent_t(std::forward(args) ...), m_callback(callback) { + this->SetDarkMode(m_callback->is_dark_mode()); + } + void ui_colors_changed() { // host must call this in response to notify() + this->SetDarkMode(m_callback->is_dark_mode()); + this->Invalidate(); + } +protected: + COLORREF GetSysColorHook(int colorIndex) const override { + return m_callback->getSysColor(colorIndex); + } + const ui_element_instance_callback_ptr m_callback; +}; diff --git a/sdk/foobar2000/helpers/CModelessDialogMessages.h b/sdk/foobar2000/helpers/CModelessDialogMessages.h new file mode 100644 index 0000000..27cdf25 --- /dev/null +++ b/sdk/foobar2000/helpers/CModelessDialogMessages.h @@ -0,0 +1,17 @@ +#pragma once +#include + +class CModelessDialogMessages { +public: + static BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM, LPARAM, LRESULT&) { + switch (uMsg) { + case WM_INITDIALOG: + modeless_dialog_manager::g_add(hWnd); break; + case WM_DESTROY: + modeless_dialog_manager::g_remove(hWnd); break; + } + return FALSE; + } +}; + +#define FB2K_MODELESS_DIALOG_MESSAGES() CHAIN_MSG_MAP(CModelessDialogMessages) diff --git a/sdk/foobar2000/helpers/CallForwarder.h b/sdk/foobar2000/helpers/CallForwarder.h index d1ed43f..257458d 100644 --- a/sdk/foobar2000/helpers/CallForwarder.h +++ b/sdk/foobar2000/helpers/CallForwarder.h @@ -1,6 +1,5 @@ #pragma once -#include - +#include "callInMainThreadHelper.h" namespace CF { template class _inMainThread : public main_thread_callback { public: diff --git a/sdk/foobar2000/helpers/DarkMode.h b/sdk/foobar2000/helpers/DarkMode.h index 05a21f2..1c6a3e8 100644 --- a/sdk/foobar2000/helpers/DarkMode.h +++ b/sdk/foobar2000/helpers/DarkMode.h @@ -11,6 +11,11 @@ // Keeps track of dark mode preference changes at runtime // Does nothing if used in foobar2000 older than 2.0 +// IMPORTANT +// See also: SDK/coreDarkMode.h +// Using CCoreDarkMode lets you invoke foobar2000's instance of this code instead of static linking it, resulting in much smaller component binary. +// Using CDarkModeHooks directly is good mainly for debugging or troubleshooting. + namespace fb2k { bool isDarkMode(); diff --git a/sdk/foobar2000/helpers/ProfileCache.h b/sdk/foobar2000/helpers/ProfileCache.h index e13f2e5..041cd97 100644 --- a/sdk/foobar2000/helpers/ProfileCache.h +++ b/sdk/foobar2000/helpers/ProfileCache.h @@ -15,7 +15,7 @@ namespace ProfileCache { try { fLocal = fsLocal->openWriteExisting(path, abort, timeoutVal ); fetch = fLocal->get_timestamp(abort) < filetimestamp_from_system_timer() - acceptableAge; - } catch(exception_io_not_found) { + } catch(exception_io_not_found const &) { fLocal = fsLocal->openWriteNew(path, abort, timeoutVal); fetch = true; } @@ -25,7 +25,7 @@ namespace ProfileCache { file::ptr fRemote; filesystem::g_open(fRemote, webURL, filesystem::open_mode_read, abort); file::g_transfer_file(fRemote, fLocal, abort ); - } catch(exception_io) { + } catch(exception_io const &) { fLocal.release(); try { retryOnSharingViolation(timeoutVal, abort, [&] {fsLocal->remove(path, abort);} ); diff --git a/sdk/foobar2000/helpers/ThreadUtils.cpp b/sdk/foobar2000/helpers/ThreadUtils.cpp index 5d806c6..e9604b1 100644 --- a/sdk/foobar2000/helpers/ThreadUtils.cpp +++ b/sdk/foobar2000/helpers/ThreadUtils.cpp @@ -7,21 +7,20 @@ namespace ThreadUtils { bool CRethrow::exec( std::function f ) throw() { - m_rethrow = nullptr; + m_exception = nullptr; bool rv = false; try { f(); rv = true; } catch( ... ) { - auto e = std::current_exception(); - m_rethrow = [e] { std::rethrow_exception(e); }; + m_exception = std::current_exception(); } return rv; } void CRethrow::rethrow() const { - if ( m_rethrow ) m_rethrow(); + if (m_exception) std::rethrow_exception(m_exception); } } #ifdef _WIN32 diff --git a/sdk/foobar2000/helpers/ThreadUtils.h b/sdk/foobar2000/helpers/ThreadUtils.h index eedad77..bb7ea9d 100644 --- a/sdk/foobar2000/helpers/ThreadUtils.h +++ b/sdk/foobar2000/helpers/ThreadUtils.h @@ -19,10 +19,10 @@ namespace ThreadUtils { // Do not use, broken version of MultiWaitAbortable_MsgLoop2 retained for compatibility (returns 1 based index) t_size MultiWaitAbortable_MsgLoop(const HANDLE* ev, t_size evCount, abort_callback& abort); - void SleepAbortable_MsgLoop(abort_callback & abort, DWORD timeout /*must not be INFINITE*/); - bool WaitAbortable_MsgLoop(HANDLE ev, abort_callback & abort, DWORD timeout /*must not be INFINITE*/); + void SleepAbortable_MsgLoop(abort_callback & abort, DWORD timeoutMS); + bool WaitAbortable_MsgLoop(HANDLE ev, abort_callback & abort, DWORD timeoutMS); - DWORD MultiWait_MsgLoop(const HANDLE* ev, DWORD evCount, DWORD timeout); + DWORD MultiWait_MsgLoop(const HANDLE* ev, DWORD evCount, DWORD timeoutMS); DWORD MultiWait_MsgLoop(const HANDLE* ev, DWORD evCount); // Drop-in replacement for pfc::event::g_wait_for() diff --git a/sdk/foobar2000/helpers/VolumeMap.cpp b/sdk/foobar2000/helpers/VolumeMap.cpp index 8334bef..11a8184 100644 --- a/sdk/foobar2000/helpers/VolumeMap.cpp +++ b/sdk/foobar2000/helpers/VolumeMap.cpp @@ -1,8 +1,8 @@ #include "StdAfx.h" #include "VolumeMap.h" -static const double powval = 2.0; -static const double silence = -100.0; +static constexpr double powval = 2.0; +static constexpr double silence = -100.0; double VolumeMap::SliderToDB2(double slider) { double v = SliderToDB(slider); diff --git a/sdk/foobar2000/helpers/WindowPositionUtils.cpp b/sdk/foobar2000/helpers/WindowPositionUtils.cpp index 5683ff8..86fdae5 100644 --- a/sdk/foobar2000/helpers/WindowPositionUtils.cpp +++ b/sdk/foobar2000/helpers/WindowPositionUtils.cpp @@ -1,6 +1,7 @@ #include "StdAfx.h" #include "WindowPositionUtils.h" +#define FB2K_WPU_DEBUG 0 namespace { static BOOL GetParentWndRect(CWindow wndParent, CRect& rc) { if (!wndParent.IsIconic()) { @@ -49,8 +50,10 @@ namespace { bool cfgDialogPositionData::grabFrom(CWindow wnd) { CRect rc; - if (!GetClientRectAsSC(wnd, rc)) return false; - const CSize DPI = QueryScreenDPIEx(); + if (!GetClientRectAsSC(wnd, rc)) { + return false; + } + const CSize DPI = QueryScreenDPIEx(wnd); m_dpiX = DPI.cx; m_dpiY = DPI.cy; m_width = rc.Width(); m_height = rc.Height(); m_posX = m_posY = posInvalid; @@ -61,10 +64,23 @@ bool cfgDialogPositionData::grabFrom(CWindow wnd) { m_posX = rc.left - rcParent.left; m_posY = rc.top - rcParent.top; } + } else { + m_posX = rc.left; m_posY = rc.top; } return true; } +pfc::string8 cfgDialogPositionData::debug() const { + pfc::string_formatter ret; + if (m_width != sizeInvalid) ret << "W: " << m_width << "\n"; + if (m_height != sizeInvalid) ret << "H: " << m_height << "\n"; + if (m_posX != posInvalid) ret << "X: " << m_posX << "\n"; + if (m_posY != posInvalid) ret << "Y: " << m_posY << "\n"; + if (m_dpiX != dpiInvalid) ret << "DPI-X: " << m_dpiX << "\n"; + if (m_dpiY != dpiInvalid) ret << "DPI-Y: " << m_dpiY << "\n"; + return ret; +} + cfgDialogPositionData cfgDialogPositionData::reDPI( CSize screenDPI ) const { cfgDialogPositionData v = *this; if (screenDPI.cx == 0 || screenDPI.cy == 0) { @@ -96,7 +112,14 @@ bool cfgDialogPositionData::overrideDefaultSize(t_uint32 width, t_uint32 height) } bool cfgDialogPositionData::applyTo(CWindow wnd) const { - auto v = reDPI(QueryScreenDPIEx()); +#if FB2K_WPU_DEBUG + FB2K_console_formatter() << "cfgDialogPositionData::applyTo(0x" << pfc::format_window( wnd ) << ")"; + FB2K_console_formatter() << "data:\n" << this->debug(); +#endif + const auto v = reDPI(QueryScreenDPIEx(wnd)); +#if FB2K_WPU_DEBUG + FB2K_console_formatter() << "after reDPI:\n" << v.debug(); +#endif CWindow wndParent = wnd.GetParent(); UINT flags = SWP_NOACTIVATE | SWP_NOZORDER; CRect rc; @@ -117,6 +140,10 @@ bool cfgDialogPositionData::applyTo(CWindow wnd) const { rc.MoveToXY(center.x - rc.Width() / 2, center.y - rc.Height() / 2); } } + } else { + if (v.m_posX != v.posInvalid && v.m_posY != v.posInvalid ) { + rc.MoveToXY( v.m_posX, v.m_posY ); + } } if (!AdjustWindowRectHelper(wnd, rc)) return FALSE; @@ -135,12 +162,67 @@ bool cfgDialogPositionData::applyTo(CWindow wnd) const { return wnd.SetWindowPos(NULL, rc, flags); } -void cfgDialogPosition::AddWindow(CWindow wnd) { +void cfgDialogPosition::read_from_window(HWND wnd) { + cfgDialogPositionData data; + if (data.grabFrom(wnd)) this->set(data); +} + +bool cfgDialogPosition::apply_to_window(HWND wnd) { auto data = this->get(); - data.applyTo(wnd); + return data.applyTo(wnd); } -void cfgDialogPosition::RemoveWindow(CWindow wnd) { - cfgDialogPositionData data; - if (data.grabFrom(wnd)) this->set(data); +bool cfgWindowPositionData::grabFrom(CWindow wnd) { + WINDOWPLACEMENT wp = {sizeof(wp)}; + bool rv = !! wnd.GetWindowPlacement(&wp); + if (rv) { + if ( !wnd.IsWindowVisible() ) wp.showCmd = SW_HIDE; + this->m_wp = wp; + m_dpi = QueryScreenDPIEx(wnd); + PFC_ASSERT( m_dpi.cx > 0 && m_dpi.cy > 0 ); + } + return rv; +} + +static void reDPI(WINDOWPLACEMENT& wp, SIZE from, SIZE to) { + wp.rcNormalPosition.left = MulDiv(wp.rcNormalPosition.left, to.cx, from.cx); + wp.rcNormalPosition.right = MulDiv(wp.rcNormalPosition.right, to.cx, from.cx); + wp.rcNormalPosition.top = MulDiv(wp.rcNormalPosition.top, to.cy, from.cy); + wp.rcNormalPosition.bottom = MulDiv(wp.rcNormalPosition.bottom, to.cy, from.cy); +} + +bool applyWindowPlacement(HWND window, WINDOWPLACEMENT const& data, bool allowHidden); // window_placement_helper.cpp + +bool cfgWindowPositionData::applyTo(CWindow wnd, bool allowHidden) const { + WINDOWPLACEMENT wp = m_wp; + if ( wp.length == 0 ) return false; + auto dpi = QueryScreenDPIEx(wnd); + if (dpi.cx != m_dpi.cx || dpi.cy != m_dpi.cy) { + reDPI( wp, m_dpi, dpi ); + } + return applyWindowPlacement(wnd, wp, allowHidden); } + +void cfgWindowPosition::read_from_window(HWND wnd) { + // grabFrom might work partially, fail to obtain size due to window being hidden, use last values + cfgWindowPositionData data = get(); + if ( data.grabFrom( wnd ) ) set(data); +} + +bool cfgWindowPosition::apply_to_window(HWND wnd, bool allowHidden) { + auto data = get(); + return data.applyTo( wnd, allowHidden ); +} + +void cfgWindowPosition::windowCreated(HWND wnd, bool allowHidden, DWORD showHow) { + auto data = get(); + switch (showHow) { + case SW_HIDE: + case SW_MINIMIZE: + data.m_wp.showCmd = showHow; + break; + } + if (!data.applyTo(wnd, allowHidden)) { + ::ShowWindow( wnd, showHow); + } +} \ No newline at end of file diff --git a/sdk/foobar2000/helpers/WindowPositionUtils.h b/sdk/foobar2000/helpers/WindowPositionUtils.h index 80fe227..a4d6566 100644 --- a/sdk/foobar2000/helpers/WindowPositionUtils.h +++ b/sdk/foobar2000/helpers/WindowPositionUtils.h @@ -102,28 +102,38 @@ class cfgDialogSizeTracker : public cfgWindowSizeTracker { }; struct cfgDialogPositionData { - enum { - posInvalid = 0x80000000, + static constexpr int32_t + posInvalid = 0x80000000; + static constexpr uint32_t sizeInvalid = 0xFFFFFFFF, - dpiInvalid = 0, - }; + dpiInvalid = 0; - uint32_t m_width = (uint32_t)sizeInvalid, m_height = (uint32_t)sizeInvalid; - int32_t m_posX = (int32_t)posInvalid, m_posY = (int32_t)posInvalid; - uint32_t m_dpiX = (uint32_t)dpiInvalid, m_dpiY = (uint32_t)dpiInvalid; + uint32_t m_width = sizeInvalid, m_height = sizeInvalid; + int32_t m_posX = posInvalid, m_posY = posInvalid; + uint32_t m_dpiX = dpiInvalid, m_dpiY = dpiInvalid; cfgDialogPositionData reDPI(CSize) const; bool grabFrom(CWindow wnd); bool applyTo(CWindow wnd) const; bool overrideDefaultSize(t_uint32 width, t_uint32 height); + + pfc::string8 debug() const; +}; + +struct cfgWindowPositionData { + WINDOWPLACEMENT m_wp = {}; + SIZE m_dpi = {}; + + bool grabFrom(CWindow wnd); + bool applyTo(CWindow wnd, bool allowHidden = false) const; }; FB2K_STREAM_READER_OVERLOAD(cfgDialogPositionData) { stream >> value.m_width >> value.m_height; try { stream >> value.m_posX >> value.m_posY >> value.m_dpiX >> value.m_dpiY; - } catch (exception_io_data) { + } catch (exception_io_data const &) { value.m_posX = value.m_posY = cfgDialogPositionData::posInvalid; value.m_dpiX = value.m_dpiY = cfgDialogPositionData::dpiInvalid; } @@ -133,172 +143,29 @@ FB2K_STREAM_WRITER_OVERLOAD(cfgDialogPositionData) { return stream << value.m_width << value.m_height << value.m_posX << value.m_posY << value.m_dpiX << value.m_dpiY; } -#if 0 -class cfgDialogPositionData { +class cfgDialogPosition : public cfg_struct_t { public: - cfgDialogPositionData_ v; - cfgDialogPositionData() {} - - void OverrideDefaultSize(t_uint32 width, t_uint32 height) { - if (v.m_width == v.sizeInvalid && v.m_height == v.sizeInvalid) { - v.m_width = width; v.m_height = height; v.m_posX = v.m_posY = v.posInvalid; - v.m_dpiX = v.m_dpiY = 96; - } - } - - void AddWindow(CWindow wnd) { - TryFetchConfig(); - m_windows += wnd; - ApplyConfig(wnd); - } - void RemoveWindow(CWindow wnd) { - if (m_windows.contains(wnd)) { - StoreConfig(wnd); m_windows -= wnd; - } - } - void FetchConfig() {TryFetchConfig();} - -private: - BOOL ApplyConfig(CWindow wnd) { - ApplyDPI(); - CWindow wndParent = wnd.GetParent(); - UINT flags = SWP_NOACTIVATE | SWP_NOZORDER; - CRect rc; - if (!GetClientRectAsSC(wnd,rc)) return FALSE; - if (v.m_width != v.sizeInvalid && v.m_height != v.sizeInvalid && (wnd.GetWindowLong(GWL_STYLE) & WS_SIZEBOX) != 0) { - rc.right = rc.left + v.m_width; - rc.bottom = rc.top + v.m_height; - } else { - flags |= SWP_NOSIZE; - } - if (wndParent != NULL) { - CRect rcParent; - if (GetParentWndRect(wndParent, rcParent)) { - if (v.m_posX != v.posInvalid && v.m_posY != v.posInvalid) { - rc.MoveToXY( rcParent.TopLeft() + CPoint(v.m_posX, v.m_posY) ); - } else { - CPoint center = rcParent.CenterPoint(); - rc.MoveToXY( center.x - rc.Width() / 2, center.y - rc.Height() / 2); - } - } - } - if (!AdjustWindowRectHelper(wnd, rc)) return FALSE; - - DeOverlap(wnd, rc); - - { - CRect rcAdjust(0,0,1,1); - if (wndParent != NULL) { - CRect temp; - if (wndParent.GetWindowRect(temp)) rcAdjust = temp; - } - AdjustRectToScreenArea(rc, rcAdjust); - } - - - return wnd.SetWindowPos(NULL, rc, flags); - } - struct DeOverlapState { - CWindow m_thisWnd; - CPoint m_topLeft; - bool m_match; - }; - static BOOL CALLBACK MyEnumChildProc(HWND wnd, LPARAM param) { - DeOverlapState * state = reinterpret_cast(param); - if (wnd != state->m_thisWnd && IsWindowVisible(wnd) ) { - CRect rc; - if (GetWindowRect(wnd, rc)) { - if (rc.TopLeft() == state->m_topLeft) { - state->m_match = true; return FALSE; - } - } - } - return TRUE; - } - static bool DeOverlapTest(CWindow wnd, CPoint topLeft) { - DeOverlapState state = {}; - state.m_thisWnd = wnd; state.m_topLeft = topLeft; state.m_match = false; - EnumThreadWindows(GetCurrentThreadId(), MyEnumChildProc, reinterpret_cast(&state)); - return state.m_match; - } - static int DeOverlapDelta() { - return pfc::max_t(GetSystemMetrics(SM_CYCAPTION),1); - } - static void DeOverlap(CWindow wnd, CRect & rc) { - const int delta = DeOverlapDelta(); - for(;;) { - if (!DeOverlapTest(wnd, rc.TopLeft())) break; - rc.OffsetRect(delta,delta); - } - } - BOOL StoreConfig(CWindow wnd) { - CRect rc; - if (!GetClientRectAsSC(wnd, rc)) return FALSE; - const CSize DPI = QueryScreenDPIEx(); - v.m_dpiX = DPI.cx; v.m_dpiY = DPI.cy; - v.m_width = rc.Width(); v.m_height = rc.Height(); - v.m_posX = v.m_posY = v.posInvalid; - CWindow parent = wnd.GetParent(); - if (parent != NULL) { - CRect rcParent; - if (GetParentWndRect(parent, rcParent)) { - v.m_posX = rc.left - rcParent.left; - v.m_posY = rc.top - rcParent.top; - } - } - return TRUE; - } - void TryFetchConfig() { - for(auto walk = m_windows.cfirst(); walk.is_valid(); ++walk) { - if (StoreConfig(*walk)) break; - } - } - - void ApplyDPI() { - const CSize screenDPI = QueryScreenDPIEx(); - if (screenDPI.cx == 0 || screenDPI.cy == 0) { - PFC_ASSERT(!"Should not get here - something seriously wrong with the OS"); - return; - } - if (v.m_dpiX != v.dpiInvalid && v.m_dpiX != screenDPI.cx) { - if (v.m_width != v.sizeInvalid) v.m_width = MulDiv(v.m_width, screenDPI.cx, v.m_dpiX); - if (v.m_posX != v.posInvalid) v.m_posX = MulDiv(v.m_posX, screenDPI.cx, v.m_dpiX); - } - if (v.m_dpiY != v.dpiInvalid && v.m_dpiY != screenDPI.cy) { - if (v.m_height != v.sizeInvalid) v.m_height = MulDiv(v.m_height, screenDPI.cy, v.m_dpiY); - if (v.m_posY != v.posInvalid) v.m_posY = MulDiv(v.m_posY, screenDPI.cy, v.m_dpiY); - } - v.m_dpiX = screenDPI.cx; - v.m_dpiY = screenDPI.cy; - } - CSize GrabDPI() const { - CSize DPI(96,96); - if (v.m_dpiX != v.dpiInvalid) DPI.cx = v.m_dpiX; - if (v.m_dpiY != v.dpiInvalid) DPI.cy = v.m_dpiY; - return DPI; - } + cfgDialogPosition(const GUID& id) : cfg_struct_t(id) {} - static BOOL GetParentWndRect(CWindow wndParent, CRect & rc) { - if (!wndParent.IsIconic()) { - return wndParent.GetWindowRect(rc); - } - WINDOWPLACEMENT pl = {sizeof(pl)}; - if (!wndParent.GetWindowPlacement(&pl)) return FALSE; - rc = pl.rcNormalPosition; - return TRUE; - } + //! Read and save size data from HWND. + void read_from_window(HWND); + //! Apply saved size data to HWND. + bool apply_to_window(HWND); - pfc::avltree_t m_windows; -public: + void AddWindow(HWND wnd) { apply_to_window(wnd); } + void RemoveWindow(HWND wnd) { read_from_window(wnd); } }; -#endif -class cfgDialogPosition : public cfg_struct_t { +class cfgWindowPosition : public cfg_struct_t { public: - cfgDialogPosition(const GUID& id) : cfg_struct_t(id) {} - - void AddWindow(CWindow wnd); - void RemoveWindow(CWindow wnd); + cfgWindowPosition(const GUID & id) : cfg_struct_t(id) {} + + //! Read and save size data from HWND. + void read_from_window(HWND); + //! Apply saved size data to HWND. + bool apply_to_window(HWND, bool allowHidden = false); + //! New window created, show it with saved metrics. + void windowCreated(HWND, bool allowHidden = false, DWORD showHow = SW_SHOW); }; class cfgDialogPositionTracker { diff --git a/sdk/foobar2000/helpers/albumArtCache.h b/sdk/foobar2000/helpers/albumArtCache.h index ac213f5..bbd2dfc 100644 --- a/sdk/foobar2000/helpers/albumArtCache.h +++ b/sdk/foobar2000/helpers/albumArtCache.h @@ -46,7 +46,7 @@ namespace fb2k { m_imageLoader = imageLoader::get()->beginLoad(loc, this->imageSize, recv); if (m_imageLoader.is_valid() && timeOut > 0) { - m_timeOutTimer = fb2k::registerTimer( timeOut, [=] { + m_timeOutTimer = fb2k::registerTimer( timeOut, [=, this] { m_timeOutTimer.release(); if (onLoaded) onLoaded(); if (asyncRecv) asyncRecv(nullptr); diff --git a/sdk/foobar2000/helpers/atl-misc.h b/sdk/foobar2000/helpers/atl-misc.h index 62a26e9..f7e6b73 100644 --- a/sdk/foobar2000/helpers/atl-misc.h +++ b/sdk/foobar2000/helpers/atl-misc.h @@ -1,8 +1,8 @@ #pragma once -#ifdef _WIN32 - #include "win32_misc.h" + +#ifdef _WIN32 #include #include #include @@ -249,7 +249,7 @@ namespace fb2k { static void AppendMenuPopup(HMENU menu, UINT flags, CMenu & popup, const TCHAR * label) { PFC_ASSERT( flags & MF_POPUP ); - WIN32_OP( CMenuHandle(menu).AppendMenu(flags, popup, label) ); + WIN32_OP_D( CMenuHandle(menu).AppendMenu(flags, popup, label) ); popup.Detach(); } diff --git a/sdk/foobar2000/helpers/callInMainThreadHelper.h b/sdk/foobar2000/helpers/callInMainThreadHelper.h new file mode 100644 index 0000000..c1399a6 --- /dev/null +++ b/sdk/foobar2000/helpers/callInMainThreadHelper.h @@ -0,0 +1,127 @@ +#pragma once +#include + +// ====================================================================================================== +// Obsolete helpers - they still work, but it's easier to just use fb2k::inMainThread(). +// ====================================================================================================== + + +// Proxy class - friend this to allow callInMainThread to access your private methods +class callInMainThread { +public: + template + static void callThis(host_t * host, param_t & param) { + host->inMainThread(param); + } + template + static void callThis( host_t * host ) { + host->inMainThread(); + } +}; + +// Internal class, do not use. +template +class _callInMainThreadSvc_t : public main_thread_callback { +public: + _callInMainThreadSvc_t(service_t * host, param_t const & param) : m_host(host), m_param(param) {} + void callback_run() { + callInMainThread::callThis(m_host.get_ptr(), m_param); + } +private: + service_ptr_t m_host; + param_t m_param; +}; + + +// Main thread callback helper. You can use this to easily invoke inMainThread(someparam) on your class without writing any wrapper code. +// Requires myservice_t to be a fb2k service class with reference counting. +template +static void callInMainThreadSvc(myservice_t * host, param_t const & param) { + typedef _callInMainThreadSvc_t impl_t; + service_ptr_t obj = new service_impl_t(host, param); + main_thread_callback_manager::get()->add_callback( obj ); +} + +//! Helper class to call methods of your class (host class) in main thread with convenience. \n +//! Deals with the otherwise ugly scenario of your class becoming invalid while a method is queued. \n +//! Have this as a member of your class, then use m_mthelper.add( this, somearg ) ; to defer a call to this->inMainThread(somearg). \n +//! If your class becomes invalid before inMainThread is executed, the pending callback is discarded. \n +//! You can optionally call shutdown() to invalidate all pending callbacks early (in a destructor of your class - without waiting for callInMainThreadHelper destructor to do the job. \n +//! In order to let callInMainThreadHelper access your private methods, declare friend class callInMainThread. \n +//! Note that callInMainThreadHelper is expected to be created and destroyed in main thread. +class callInMainThreadHelper { +public: + + typedef std::function< void () > func_t; + + typedef pfc::rcptr_t< bool > killswitch_t; + + class entryFunc : public main_thread_callback { + public: + entryFunc( func_t const & func, killswitch_t ks ) : m_ks(ks), m_func(func) {} + + void callback_run() { + if (!*m_ks) m_func(); + } + + private: + killswitch_t m_ks; + func_t m_func; + }; + + template + class entry : public main_thread_callback { + public: + entry( host_t * host, arg_t const & arg, killswitch_t ks ) : m_ks(ks), m_host(host), m_arg(arg) {} + void callback_run() { + if (!*m_ks) callInMainThread::callThis( m_host, m_arg ); + } + private: + killswitch_t m_ks; + host_t * m_host; + arg_t m_arg; + }; + template + class entryVoid : public main_thread_callback { + public: + entryVoid( host_t * host, killswitch_t ks ) : m_ks(ks), m_host(host) {} + void callback_run() { + if (!*m_ks) callInMainThread::callThis( m_host ); + } + private: + killswitch_t m_ks; + host_t * m_host; + }; + + void add(func_t f) { + add_( new service_impl_t< entryFunc > ( f, m_ks ) ); + } + + template + void add( host_t * host, arg_t const & arg) { + add_( new service_impl_t< entry >( host, arg, m_ks ) ); + } + template + void add( host_t * host ) { + add_( new service_impl_t< entryVoid >( host, m_ks ) ); + } + void add_( main_thread_callback::ptr cb ) { + main_thread_callback_add( cb ); + } + + callInMainThreadHelper() { + m_ks.new_t(); + * m_ks = false; + } + void shutdown() { + PFC_ASSERT( core_api::is_main_thread() ); + * m_ks = true; + } + ~callInMainThreadHelper() { + shutdown(); + } + +private: + killswitch_t m_ks; + +}; diff --git a/sdk/foobar2000/helpers/callback_merit.h b/sdk/foobar2000/helpers/callback_merit.h new file mode 100644 index 0000000..a0a9685 --- /dev/null +++ b/sdk/foobar2000/helpers/callback_merit.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include + +namespace fb2k { + // Call global callbacks in correct order + // INTENDED FOR CORE USE ONLY + // Not aware of dynamically registered callbacks, if there are such for the same event type + // Usage: for_each_callback_by_merit< metadb_io_edit_callback > ( [] ( metadb_io_edit_callback::ptr ) { ...} ); + template + void for_each_callback_by_merit(std::function)> f) { + struct rec_t { + class_t* obj; // non-autoptr INTENDED, avoid destruction order bugs on shutdown + fb2k::callback_merit_t merit; + }; + auto generator = [] { + std::vector ret; ret.reserve(64); + for (auto ptr : class_t::enumerate()) { + rec_t r; + r.merit = fb2k::callback_merit_of(ptr); + r.obj = ptr.detach(); + ret.push_back( std::move(r) ); + } + std::sort(ret.begin(), ret.end(), [](rec_t const& e1, rec_t const& e2) { return e1.merit > e2.merit; }); + return ret; + }; + static std::vector cache = generator(); + for (auto& walk : cache) f(walk.obj); + } +} diff --git a/sdk/foobar2000/helpers/cfg_obj.h b/sdk/foobar2000/helpers/cfg_obj.h index db37004..d2c96f4 100644 --- a/sdk/foobar2000/helpers/cfg_obj.h +++ b/sdk/foobar2000/helpers/cfg_obj.h @@ -36,7 +36,7 @@ namespace cfg_var_modern { try { stream_reader_formatter<> fmt(*p_stream, p_abort); fmt >> o; - } catch (exception_io_data) { return; } + } catch (...) { return; } set(std::move(o)); } #endif @@ -54,7 +54,7 @@ namespace cfg_var_modern { try { stream_reader_formatter_simple<> source(blob->data(), blob->size()); source >> v; - } catch (exception_io_data) { + } catch (...) { v = m_initVal; // revert } } diff --git a/sdk/foobar2000/helpers/cfg_objList.h b/sdk/foobar2000/helpers/cfg_objList.h index 293bd9a..7da35a5 100644 --- a/sdk/foobar2000/helpers/cfg_objList.h +++ b/sdk/foobar2000/helpers/cfg_objList.h @@ -23,17 +23,20 @@ namespace cfg_var_modern { if (gone > 0) save(); return gone; } + //! Returns number of items removed. template size_t remove_if(pred_t&& p) { init(); PFC_INSYNC_WRITE(m_listGuard); - size_t gone = pfc::remove_if_t(m_list, std::forward(p)); + const auto nBefore = m_list.size(); + const size_t nAfter = pfc::remove_if_t(m_list, std::forward(p)); + const auto gone = nAfter - nBefore; if (gone > 0) save(); return gone; } bool remove_item(obj_t const& v) { return remove_if([&v](const obj_t& arg) { return v == arg; }) != 0; } - size_t size() { init(); return m_list.size(); } + size_t size() { init(); PFC_INSYNC_READ(m_listGuard);return m_list.size(); } size_t get_count() { return size(); } size_t get_size() { return size(); } @@ -110,16 +113,16 @@ namespace cfg_var_modern { void init() { std::call_once(m_init, [this] { auto blob = fb2k::configStore::get()->getConfigBlob(formatName(), nullptr); - if (blob.is_valid()) { + if (blob.is_valid()) try { stream_reader_formatter_simple<> reader(blob->data(), blob->size()); std::vector data; uint32_t count; reader >> count; data.resize(count); for (auto& v : data) reader >> v; set_(std::move(data), false); - } else { - set_(m_defaults, false); - } - }); + return; + } catch(...) {} // fall through, set defaults + set_(m_defaults, false); + }); } template void set_(arg_t&& arg, bool bSave = true) { diff --git a/sdk/foobar2000/helpers/create_directory_helper.cpp b/sdk/foobar2000/helpers/create_directory_helper.cpp index ef9e50b..1a7f88c 100644 --- a/sdk/foobar2000/helpers/create_directory_helper.cpp +++ b/sdk/foobar2000/helpers/create_directory_helper.cpp @@ -1,15 +1,23 @@ #include "StdAfx.h" #include "create_directory_helper.h" #include +#include namespace create_directory_helper { static void create_path_internal(const char * p_path,t_size p_base,abort_callback & p_abort) { pfc::string8_fastalloc temp; + auto fs = filesystem::get(p_path); + auto api_lock = file_lock_manager::get(); for(t_size walk = p_base; p_path[walk]; walk++) { if (p_path[walk] == '\\') { temp.set_string(p_path,walk); - try {filesystem::g_create_directory(temp.get_ptr(),p_abort);} catch(exception_io_already_exists) {} + // 2024-03 Google Drive bug: + // Creating the same folder concurrently from multiple threads causes erratic behavior + // Thread that got here first behaves OK, others get "already exists" and return, but creating files in the folder fail with "path not found" + // Block other threads trying to do the same until we've finished + const auto lock = api_lock->acquire_write(temp, p_abort); + fs->make_directory(temp, p_abort); } } } @@ -90,7 +98,7 @@ namespace create_directory_helper { if (!last_char_is_dir_sep) { - if (really_create_dirs) try{filesystem::g_create_directory(out,p_abort);}catch(exception_io_already_exists){} + if (really_create_dirs) try{filesystem::g_create_directory(out,p_abort);}catch(exception_io_already_exists const &){} out.add_char('\\'); last_char_is_dir_sep = true; } diff --git a/sdk/foobar2000/helpers/cue_creator.cpp b/sdk/foobar2000/helpers/cue_creator.cpp index 6ca36e6..36502d3 100644 --- a/sdk/foobar2000/helpers/cue_creator.cpp +++ b/sdk/foobar2000/helpers/cue_creator.cpp @@ -2,23 +2,14 @@ #include "cue_creator.h" #include "../SDK/chapterizer.h" -namespace { - - class format_meta - { - public: - format_meta(const file_info & p_source,const char * p_name,bool p_allow_space = true) - { - p_source.meta_format(p_name,m_buffer); - m_buffer.replace_byte('\"','\''); - uReplaceString(m_buffer,pfc::string8(m_buffer),pfc_infinite,"\x0d\x0a",2,"\\",1,false); - if (!p_allow_space) m_buffer.replace_byte(' ','_'); - m_buffer.replace_nontext_chars(); - } - inline operator const char*() const {return m_buffer;} - private: - pfc::string8_fastalloc m_buffer; - }; +static pfc::string8 format_meta(const file_info& p_source, const char* p_name, bool p_allow_space = true) { + pfc::string8 temp, ret; + p_source.meta_format(p_name, temp); + temp.replace_byte('\"', '\''); + uReplaceString(ret, temp, pfc_infinite, "\x0d\x0a", 2, "\\", 1, false); + if (!p_allow_space) ret.replace_byte(' ', '_'); + ret.replace_nontext_chars(); + return ret; } static bool is_meta_same_everywhere(const cue_creator::t_entry_list & p_list,const char * p_meta) @@ -26,14 +17,14 @@ static bool is_meta_same_everywhere(const cue_creator::t_entry_list & p_list,con pfc::string8_fastalloc reference,temp; bool first = true; - for(auto iter = p_list.first(); iter.is_valid(); ++ iter ) { - if ( ! iter->isTrackAudio() ) continue; + for(auto & iter : p_list) { + if ( ! iter.isTrackAudio() ) continue; if ( first ) { first = false; - if (!iter->m_infos.meta_format(p_meta,reference)) return false; + if (!iter.m_infos.meta_format(p_meta,reference)) return false; } else { - if (!iter->m_infos.meta_format(p_meta,temp)) return false; + if (!iter.m_infos.meta_format(p_meta,temp)) return false; if (strcmp(temp,reference)!=0) return false; } } @@ -77,6 +68,12 @@ namespace cue_creator if (comment_global) { p_out << "REM COMMENT " << format_meta(firstTrack->m_infos,"comment") << g_eol; } + if (is_meta_same_everywhere(p_data, "discnumber")) { + p_out << "REM DISCNUMBER " << format_meta(firstTrack->m_infos, "discnumber") << g_eol; + } + if (is_meta_same_everywhere(p_data, "totaldiscs")) { + p_out << "REM TOTALDISCS " << format_meta(firstTrack->m_infos, "totaldiscs") << g_eol; + } if (catalog_global) { p_out << "CATALOG " << format_meta(firstTrack->m_infos,"catalog") << g_eol; } diff --git a/sdk/foobar2000/helpers/cue_parser.cpp b/sdk/foobar2000/helpers/cue_parser.cpp index d4ed561..7eec463 100644 --- a/sdk/foobar2000/helpers/cue_parser.cpp +++ b/sdk/foobar2000/helpers/cue_parser.cpp @@ -27,20 +27,15 @@ static bool is_linebreak(char c) } static void validate_file_type(const char * p_type,t_size p_type_length) { - if ( - //standard types - stricmp_utf8_ex(p_type,p_type_length,"WAVE",pfc_infinite) != 0 && - stricmp_utf8_ex(p_type,p_type_length,"MP3",pfc_infinite) != 0 && - stricmp_utf8_ex(p_type,p_type_length,"AIFF",pfc_infinite) != 0 && - //common user-entered types - stricmp_utf8_ex(p_type,p_type_length,"APE",pfc_infinite) != 0 && - stricmp_utf8_ex(p_type,p_type_length,"FLAC",pfc_infinite) != 0 && - stricmp_utf8_ex(p_type,p_type_length,"WV",pfc_infinite) != 0 && - stricmp_utf8_ex(p_type,p_type_length,"WAVPACK",pfc_infinite) != 0 && - // BINARY - stricmp_utf8_ex(p_type,p_type_length,"BINARY",pfc_infinite) != 0 - ) - pfc::throw_exception_with_message< exception_cue >(PFC_string_formatter() << "expected WAVE, MP3 or AIFF, got : \"" << pfc::string_part(p_type,p_type_length) << "\""); + const char* const allowedTypes[] = { + "WAVE", "MP3", "AIFF", // standard typers + "APE", "FLAC", "WV", "WAVPACK", "MP4", // common user-entered types + "BINARY" // BINARY + }; + for (auto walk : allowedTypes) { + if (pfc::stringEqualsI_ascii_ex(p_type, p_type_length, walk, SIZE_MAX)) return; + } + pfc::throw_exception_with_message< exception_cue >(PFC_string_formatter() << "expected WAVE, MP3 or AIFF, got : \"" << pfc::string_part(p_type,p_type_length) << "\""); } namespace { @@ -72,9 +67,9 @@ namespace { protected: static bool is_known_meta(const char * p_name,t_size p_length) { - static const char * metas[] = {"genre","date","discid","comment","replaygain_track_gain","replaygain_track_peak","replaygain_album_gain","replaygain_album_peak"}; + static const char * metas[] = {"genre","date","discid","comment","replaygain_track_gain","replaygain_track_peak","replaygain_album_gain","replaygain_album_peak", "discnumber", "totaldiscs"}; for (const char* m : metas) { - if (!stricmp_utf8_ex(p_name, p_length, m, pfc_infinite)) return true; + if (!stricmp_utf8_ex(p_name, p_length, m, SIZE_MAX)) return true; } return false; } diff --git a/sdk/foobar2000/helpers/cuesheet_index_list.cpp b/sdk/foobar2000/helpers/cuesheet_index_list.cpp index 5e77766..d323134 100644 --- a/sdk/foobar2000/helpers/cuesheet_index_list.cpp +++ b/sdk/foobar2000/helpers/cuesheet_index_list.cpp @@ -3,10 +3,6 @@ #include "cue_parser.h" // exception_bad_cuesheet -#ifndef _MSC_VER -#define sprintf_s sprintf -#endif - bool t_cuesheet_index_list::is_valid() const { if (m_positions[1] < m_positions[0]) return false; for(t_size n = 2; n < count && m_positions[n] > 0; n++) { @@ -34,7 +30,7 @@ void t_cuesheet_index_list::to_infos(file_info & p_out) const for(unsigned n=2;n 0) p_out.info_set(namebuffer,cuesheet_format_index_time(position)); @@ -63,7 +59,7 @@ bool t_cuesheet_index_list::from_infos(file_info const & p_in,double p_base) for(unsigned n=2;n ( std::move(work) ) ); kickWork(); } void flush() { @@ -30,7 +30,7 @@ namespace fb2k { } void kickWork() { PFC_ASSERT( core_api::is_main_thread() ); - if (!m_working && m_workQueue.size() > 0) { + if (!m_working && !m_workQueue.empty()) { m_working = true; auto iter = m_workQueue.begin(); auto work = std::move(*iter); m_workQueue.erase(iter); @@ -38,15 +38,17 @@ namespace fb2k { auto a = m_abort; fb2k::inCpuWorkerThread( [ work, pThis, a] { try { - if ( work.work ) work.work(); + // release lambdas early, synchronously from same context as they're executed + { auto f = std::move(work->work); if (f) f(); } a->check(); fb2k::inMainThread( [work, pThis, a] { if ( ! a->is_set() ) { - work.done(); + // release lambdas early, synchronously from same context as they're executed + { auto f = std::move(work->done); if (f) f(); } pThis->workDone(); } }); - } catch(exception_aborted) {} + } catch(exception_aborted const &) {} } ); } } @@ -61,7 +63,7 @@ namespace fb2k { std::shared_ptr m_abort = std::make_shared(); bool m_working = false; - std::list m_workQueue; + std::list< std::shared_ptr > m_workQueue; }; } diff --git a/sdk/foobar2000/helpers/file_list_helper.h b/sdk/foobar2000/helpers/file_list_helper.h index af721da..fcf33dc 100644 --- a/sdk/foobar2000/helpers/file_list_helper.h +++ b/sdk/foobar2000/helpers/file_list_helper.h @@ -2,13 +2,15 @@ namespace file_list_helper { + typedef pfc::list_base_const_t base_t; + //list guaranteed to be sorted by metadb::path_compare - class file_list_from_metadb_handle_list : public pfc::list_base_const_t { + class file_list_from_metadb_handle_list : public base_t { public: file_list_from_metadb_handle_list() {} file_list_from_metadb_handle_list( metadb_handle_list_cref lst, bool bDisplayPaths = false ); - static t_size g_get_count(const list_base_const_t & p_list, t_size max = ~0); + static t_size g_get_count(const list_base_const_t & p_list, t_size max = SIZE_MAX); void init_from_list(const list_base_const_t & p_list); void init_from_list_display(const list_base_const_t & p_list); @@ -22,7 +24,5 @@ namespace file_list_helper void _add(const char * p_what); pfc::ptr_list_t m_data; }; - - }; diff --git a/sdk/foobar2000/helpers/file_readonly.h b/sdk/foobar2000/helpers/file_readonly.h deleted file mode 100644 index 03cb87c..0000000 --- a/sdk/foobar2000/helpers/file_readonly.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// fb2k mobile compat - -#include "../SDK/filesystem_helper.h" \ No newline at end of file diff --git a/sdk/foobar2000/helpers/file_streamstub.h b/sdk/foobar2000/helpers/file_streamstub.h new file mode 100644 index 0000000..877eb0f --- /dev/null +++ b/sdk/foobar2000/helpers/file_streamstub.h @@ -0,0 +1,19 @@ +#pragma once + +//! Stub implementation of file object, no file content, only info. +class file_streamstub : public file_readonly { +public: + t_size read(void*, t_size, abort_callback&) override { return 0; } + t_filesize get_size(abort_callback&) override { return filesize_invalid; } + t_filesize get_position(abort_callback&) override { return 0; } + bool get_content_type(pfc::string_base& out) override { + if (m_contentType.length() > 0) { out = m_contentType; return true; } else return false; + } + bool is_remote() override { return m_remote; } + void reopen(abort_callback&) override {} + void seek(t_filesize, abort_callback&) override { throw exception_io_object_not_seekable(); } + bool can_seek() override { return false; } + + pfc::string8 m_contentType; + bool m_remote = true; +}; diff --git a/sdk/foobar2000/helpers/file_win32_wrapper.cpp b/sdk/foobar2000/helpers/file_win32_wrapper.cpp index 9f65ea9..b79ae0d 100644 --- a/sdk/foobar2000/helpers/file_win32_wrapper.cpp +++ b/sdk/foobar2000/helpers/file_win32_wrapper.cpp @@ -6,16 +6,9 @@ namespace file_win32_helpers { t_filesize get_size(HANDLE p_handle) { - union { - t_uint64 val64; - t_uint32 val32[2]; - } u; - - u.val64 = 0; - SetLastError(NO_ERROR); - u.val32[0] = GetFileSize(p_handle,reinterpret_cast(&u.val32[1])); - if (GetLastError()!=NO_ERROR) exception_io_from_win32(GetLastError()); - return u.val64; + LARGE_INTEGER v = {}; + WIN32_IO_OP(GetFileSizeEx(p_handle, &v)); + return make_uint64(v); } void seek(HANDLE p_handle,t_sfilesize p_position,file::t_seek_mode p_mode) { union { @@ -225,6 +218,94 @@ namespace file_win32_helpers { return 0; } + t_filestats2 stats2_from_handle(HANDLE h, const wchar_t * fallbackPath, uint32_t flags, abort_callback& a) { + a.check(); + // Sadly GetFileInformationByHandle() is UNRELIABLE with certain net shares + BY_HANDLE_FILE_INFORMATION info = {}; + if (GetFileInformationByHandle(h, &info)) { + return file_win32_helpers::translate_stats2(info); + } + + a.check(); + t_filestats2 ret; + + // ALWAYS get size, fail if bad handle + ret.m_size = get_size(h); + + if (flags & (stats2_timestamp | stats2_timestampCreate)) { + static_assert(sizeof(t_filetimestamp) == sizeof(FILETIME), "struct sanity"); + FILETIME ftCreate = {}, ftWrite = {}; + if (GetFileTime(h, &ftCreate, nullptr, &ftWrite)) { + ret.m_timestamp = make_uint64(ftWrite); ret.m_timestampCreate = make_uint64(ftCreate); + } + } + if (flags & stats2_flags) { + // No other way to get this from handle? + if (fallbackPath != nullptr && *fallbackPath != 0) { + DWORD attr = GetFileAttributes(fallbackPath); + if (attr != INVALID_FILE_ATTRIBUTES) { + attribs_from_win32(ret, attr); + } + } + } + return ret; + } + void attribs_from_win32(t_filestats2& out, DWORD in) { + out.set_readonly((in & FILE_ATTRIBUTE_READONLY) != 0); + out.set_folder((in & FILE_ATTRIBUTE_DIRECTORY) != 0); + out.set_hidden((in & FILE_ATTRIBUTE_HIDDEN) != 0); + out.set_system((in & FILE_ATTRIBUTE_SYSTEM) != 0); + out.set_remote(false); + } + + + // Seek penalty query, effectively: is this an SSD? + // Credit: + // https://devblogs.microsoft.com/oldnewthing/20201023-00/?p=104395 + static bool queryVolumeSeekPenalty(HANDLE hVolume, bool& out) { + STORAGE_PROPERTY_QUERY query = {}; + query.PropertyId = StorageDeviceSeekPenaltyProperty; + query.QueryType = PropertyStandardQuery; + DWORD count = 1; + DEVICE_SEEK_PENALTY_DESCRIPTOR result = {}; + if (!DeviceIoControl(hVolume, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), &result, sizeof(result), &count, nullptr)) { + return false; + } + out = result.IncursSeekPenalty; + return true; + } + + static HANDLE GetVolumeHandleForFile(PCWSTR filePath) { + wchar_t volumePath[MAX_PATH] = {}; + WIN32_OP_D(GetVolumePathName(filePath, volumePath, ARRAYSIZE(volumePath))); + + wchar_t volumeName[MAX_PATH] = {}; + WIN32_OP_D(GetVolumeNameForVolumeMountPoint(volumePath, volumeName, ARRAYSIZE(volumeName))); + + auto length = wcslen(volumeName); + if ( length == 0 ) { + PFC_ASSERT(!"???"); + return NULL; + } + if (length && volumeName[length - 1] == L'\\') { + volumeName[length - 1] = L'\0'; + } + + HANDLE ret; + WIN32_OP_D( ret = CreateFile(volumeName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr) ); + return ret; + } + bool querySeekPenalty(const wchar_t* nativePath, bool& out) { + CHandle h; + h.Attach( GetVolumeHandleForFile( nativePath ) ); + if (!h) return false; + return queryVolumeSeekPenalty(h, out); + } + bool querySeekPenalty(const char* fb2k_path, bool& out) { + const char * path = fb2k_path; + if ( matchProtocol(path, "file")) path = afterProtocol(path); + return querySeekPenalty(pfc::wideFromUTF8(path), out); + } } #endif // _WIN32 diff --git a/sdk/foobar2000/helpers/file_win32_wrapper.h b/sdk/foobar2000/helpers/file_win32_wrapper.h index 27067ca..8d8c6d4 100644 --- a/sdk/foobar2000/helpers/file_win32_wrapper.h +++ b/sdk/foobar2000/helpers/file_win32_wrapper.h @@ -1,6 +1,7 @@ #pragma once #include +#include #ifdef _WIN32 namespace file_win32_helpers { @@ -15,14 +16,18 @@ namespace file_win32_helpers { size_t readStreamOverlapped(HANDLE handle, HANDLE myEvent, void * out, size_t outBytes, abort_callback & abort); HANDLE createFile(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile, abort_callback & abort); size_t lowLevelIO(HANDLE hFile, const GUID & guid, size_t arg1, void * arg2, size_t arg2size, bool canWrite, abort_callback & abort); + bool querySeekPenalty(const char * fb2k_path, bool & out); + bool querySeekPenalty(const wchar_t * nativePath, bool & out); - - static t_uint64 make_uint64(t_uint32 p_low, t_uint32 p_high) { + static uint64_t make_uint64(t_uint32 p_low, t_uint32 p_high) { return ((t_uint64)p_low) + ((t_uint64)p_high << 32); } - - static t_uint64 make_uint64(FILETIME const& ft) { - return make_uint64(ft.dwLowDateTime, ft.dwHighDateTime); + static uint64_t make_uint64(LARGE_INTEGER const& i) { + return reinterpret_cast(i); + } + static uint64_t make_uint64(FILETIME const& ft) { + return reinterpret_cast(ft); +// return make_uint64(ft.dwLowDateTime, ft.dwHighDateTime); } template @@ -33,44 +38,47 @@ namespace file_win32_helpers { return ret; } + void attribs_from_win32(t_filestats2& out, DWORD in); template static t_filestats2 translate_stats2(const t_info& p_info) { t_filestats2 ret; ret.m_size = make_uint64(p_info.nFileSizeLow, p_info.nFileSizeHigh); ret.m_timestamp = make_uint64(p_info.ftLastWriteTime); ret.m_timestampCreate = make_uint64(p_info.ftCreationTime); - ret.set_readonly((p_info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0); - ret.set_folder((p_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); - ret.set_hidden((p_info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0); - ret.set_system((p_info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0); - ret.set_remote(false); + attribs_from_win32(ret, p_info.dwFileAttributes); return ret; } + + t_filestats2 stats2_from_handle(HANDLE, const wchar_t * fallbackPath, uint32_t flags, abort_callback &a); }; template class file_win32_wrapper_t : public service_multi_inherit { + typedef file_win32_wrapper_t self_t; public: - file_win32_wrapper_t(HANDLE p_handle) : m_handle(p_handle), m_position(0) - { - } + file_win32_wrapper_t(HANDLE handle, pfc::wstringLite && path) : m_handle(handle), m_path(std::move(path)) {} static file::ptr g_CreateFile(const char * p_path,DWORD p_access,DWORD p_sharemode,LPSECURITY_ATTRIBUTES p_security_attributes,DWORD p_createmode,DWORD p_flags,HANDLE p_template) { + auto pathW = pfc::wideFromUTF8(p_path); SetLastError(NO_ERROR); - HANDLE handle = uCreateFile(p_path,p_access,p_sharemode,p_security_attributes,p_createmode,p_flags,p_template); + HANDLE handle = CreateFile(pathW,p_access,p_sharemode,p_security_attributes,p_createmode,p_flags,p_template); if (handle == INVALID_HANDLE_VALUE) { const DWORD code = GetLastError(); if (p_access & GENERIC_WRITE) win32_file_write_failure(code, p_path); else exception_io_from_win32(code); } try { - return g_create_from_handle(handle); + return g_create_from_handle(handle, std::move(pathW)); } catch(...) {CloseHandle(handle); throw;} } - static service_ptr_t g_create_from_handle(HANDLE p_handle) { - return new service_impl_t >(p_handle); + static service_ptr_t g_create_from_handle(HANDLE handle, pfc::wstringLite && path) { + return new service_impl_t(handle, std::move(path)); + } + static service_ptr_t g_create_from_handle(HANDLE handle) { + pfc::wstringLite blank; + g_create_from_handle(handle, std::move(blank)); } @@ -175,18 +183,15 @@ class file_win32_wrapper_t : public service_multi_inherit class file_win32_wrapper_overlapped_t : public service_multi_inherit< file_v2, file_lowLevelIO > { + typedef file_win32_wrapper_overlapped_t self_t; public: - file_win32_wrapper_overlapped_t(HANDLE file) : m_handle(file), m_position() { + file_win32_wrapper_overlapped_t(HANDLE file, pfc::wstringLite && path) : m_handle(file), m_path(std::move(path)) { WIN32_OP( (m_event = CreateEvent(NULL, TRUE, FALSE, NULL)) != NULL ); } ~file_win32_wrapper_overlapped_t() {CloseHandle(m_event); CloseHandle(m_handle);} @@ -257,40 +264,42 @@ class file_win32_wrapper_overlapped_t : public service_multi_inherit< file_v2, f t_filestats2 get_stats2(uint32_t f, abort_callback& a) { a.check(); if (p_writeable) FlushFileBuffers(m_handle); - SetLastError(0); - BY_HANDLE_FILE_INFORMATION info = {}; - if (!GetFileInformationByHandle(m_handle, &info)) exception_io_from_win32(GetLastError()); - return file_win32_helpers::translate_stats2(info); + return file_win32_helpers::stats2_from_handle(m_handle, m_path, f, a); } t_filetimestamp get_timestamp(abort_callback & p_abort) { p_abort.check_e(); if (p_writeable) FlushFileBuffers(m_handle); SetLastError(ERROR_SUCCESS); - t_filetimestamp temp; - if (!GetFileTime(m_handle,0,0,(FILETIME*)&temp)) exception_io_from_win32(GetLastError()); - return temp; + FILETIME temp; + if (!GetFileTime(m_handle,0,0,&temp)) exception_io_from_win32(GetLastError()); + return file_win32_helpers::make_uint64(temp); } bool is_remote() {return false;} static file::ptr g_CreateFile(const char * p_path,DWORD p_access,DWORD p_sharemode,LPSECURITY_ATTRIBUTES p_security_attributes,DWORD p_createmode,DWORD p_flags,HANDLE p_template) { + auto pathW = pfc::wideFromUTF8(p_path); p_flags |= FILE_FLAG_OVERLAPPED; SetLastError(NO_ERROR); - HANDLE handle = uCreateFile(p_path,p_access,p_sharemode,p_security_attributes,p_createmode,p_flags,p_template); + HANDLE handle = CreateFile(pathW,p_access,p_sharemode,p_security_attributes,p_createmode,p_flags,p_template); if (handle == INVALID_HANDLE_VALUE) { const DWORD code = GetLastError(); if (p_access & GENERIC_WRITE) win32_file_write_failure(code, p_path); else exception_io_from_win32(code); } try { - return g_create_from_handle(handle); + return g_create_from_handle(handle, std::move(pathW)); } catch(...) {CloseHandle(handle); throw;} } + static file::ptr g_create_from_handle(HANDLE p_handle, pfc::wstringLite && path) { + return new service_impl_t(p_handle, std::move(path)); + } static file::ptr g_create_from_handle(HANDLE p_handle) { - return new service_impl_t >(p_handle); + pfc::wstringLite blank; + return g_create_from_handle(p_handle, std::move(blank)); } size_t lowLevelIO(const GUID & guid, size_t arg1, void * arg2, size_t arg2size, abort_callback & abort) override { @@ -299,7 +308,8 @@ class file_win32_wrapper_overlapped_t : public service_multi_inherit< file_v2, f protected: HANDLE m_event, m_handle; - t_filesize m_position; + t_filesize m_position = 0; + pfc::wstringLite m_path; }; #endif // _WIN32 diff --git a/sdk/foobar2000/helpers/filetimetools.cpp b/sdk/foobar2000/helpers/filetimetools.cpp index ec6ef44..21aec0a 100644 --- a/sdk/foobar2000/helpers/filetimetools.cpp +++ b/sdk/foobar2000/helpers/filetimetools.cpp @@ -2,323 +2,27 @@ #include "filetimetools.h" -#include +#include -typedef exception_io_data exception_time_error; - -#ifndef _WIN32 -namespace { - typedef uint16_t WORD; - - typedef struct _SYSTEMTIME { - WORD wYear; - WORD wMonth; - WORD wDayOfWeek; - WORD wDay; - WORD wHour; - WORD wMinute; - WORD wSecond; - WORD wMilliseconds; - } SYSTEMTIME, * PSYSTEMTIME, * LPSYSTEMTIME; - -} -static void SystemTimeToNix(const SYSTEMTIME& st, struct tm& Time) { - memset(&Time, 0, sizeof(Time)); - Time.tm_sec = st.wSecond; - Time.tm_min = st.wMinute; - Time.tm_hour = st.wHour; - Time.tm_mday = st.wDay; - Time.tm_mon = st.wMonth - 1; - Time.tm_year = st.wYear - 1900; -} - -static t_filetimestamp ExportSystemTime(const SYSTEMTIME& st) { - struct tm Time; - SystemTimeToNix(st, Time); - return pfc::fileTimeUtoW(mktime(&Time)); -} - -static t_filetimestamp ExportSystemTimeLocal(const SYSTEMTIME& st) { - struct tm Time, Local; - SystemTimeToNix(st, Time); - time_t t = mktime(&Time); - localtime_r(&t, &Local); - return pfc::fileTimeUtoW(mktime(&Local)); -} -static void SystemTimeFromNix(SYSTEMTIME& st, struct tm const& Time) { - memset(&st, 0, sizeof(st)); - st.wSecond = Time.tm_sec; - st.wMinute = Time.tm_min; - st.wHour = Time.tm_hour; - st.wDay = Time.tm_mday; - st.wDayOfWeek = Time.tm_wday; - st.wMonth = Time.tm_mon + 1; - st.wYear = Time.tm_year + 1900; -} - -static bool MakeSystemTime(SYSTEMTIME& st, t_filetimestamp ts) { - time_t t = (time_t)pfc::fileTimeWtoU(ts); - struct tm Time; - if (gmtime_r(&t, &Time) == NULL) return false; - SystemTimeFromNix(st, Time); - return true; -} - -static bool MakeSystemTimeLocal(SYSTEMTIME& st, t_filetimestamp ts) { - time_t t = (time_t)pfc::fileTimeWtoU(ts); - struct tm Time; - if (localtime_r(&t, &Time) == NULL) return false; - SystemTimeFromNix(st, Time); - return true; -} - -#else -static t_filetimestamp ExportSystemTime(const SYSTEMTIME& st) { - t_filetimestamp base; - if (!SystemTimeToFileTime(&st, (FILETIME*)&base)) throw exception_time_error(); - return base; -} -static t_filetimestamp ExportSystemTimeLocal(const SYSTEMTIME& st) { -#ifdef FOOBAR2000_DESKTOP_WINDOWS - t_filetimestamp base, out; - if (!SystemTimeToFileTime(&st, (FILETIME*)&base)) throw exception_time_error(); - if (!LocalFileTimeToFileTime((const FILETIME*)&base, (FILETIME*)&out)) throw exception_time_error(); - return out; -#else - SYSTEMTIME UTC; - if (!TzSpecificLocalTimeToSystemTime(NULL, &st, &UTC)) throw exception_time_error(); - return ExportSystemTime(UTC); -#endif -} -static bool MakeSystemTime(SYSTEMTIME& st, t_filetimestamp ts) { - if (ts == filetimestamp_invalid) return false; - return !!FileTimeToSystemTime((const FILETIME*)&ts, &st); - -} -static bool MakeSystemTimeLocal(SYSTEMTIME& st, t_filetimestamp ts) { - if (ts == filetimestamp_invalid) return false; -#ifdef FOOBAR2000_DESKTOP_WINDOWS - FILETIME ft; - if (FileTimeToLocalFileTime((FILETIME*)&ts, &ft)) { - if (FileTimeToSystemTime(&ft, &st)) { - return true; - } - } - return false; -#else - SYSTEMTIME UTC; - if (FileTimeToSystemTime((FILETIME*)&ts, &UTC)) { - if (SystemTimeToTzSpecificLocalTime(NULL, &UTC, &st)) return true; - } - return false; -#endif -} -#endif // _WIN32 - -static bool is_spacing(char c) { return c == ' ' || c == 10 || c == 13 || c == '\t'; } - -static unsigned ParseDateElem(const char* ptr, t_size len) { - unsigned ret = 0; - for (t_size walk = 0; walk < len; ++walk) { - const char c = ptr[walk]; - if (c < '0' || c > '9') throw exception_time_error(); - ret = ret * 10 + (unsigned)(c - '0'); - } - return ret; -} - -static bool st_sanity(SYSTEMTIME const& st) { - return st.wYear >= 1601 && st.wMonth >= 1 && st.wMonth <= 12 && st.wDay >= 1 && st.wDay <= 31 && st.wHour < 24 && st.wMinute < 60 && st.wSecond < 60 && st.wMilliseconds < 1000; -} -static t_filetimestamp filetimestamp_from_string_internal(const char* date, bool local) { - // Accepted format - // YYYY-MM-DD HH:MM:SS - try { - SYSTEMTIME st = {}; - st.wDay = 1; st.wMonth = 1; - - unsigned walk = 0; - auto worker = [&](unsigned n) { - auto ret = ParseDateElem(date + walk, n); - walk += n; - if (ret > UINT16_MAX) throw exception_time_error(); - return (WORD)ret;; - }; - - auto skip = [&](char c) { - if (date[walk] == c) ++walk; - }; - - auto skipSpacing = [&] { - while (is_spacing(date[walk])) ++walk; - }; - skipSpacing(); - st.wYear = worker(4); - skip('-'); - st.wMonth = worker(2); - skip('-'); - st.wDay = worker(2); - skipSpacing(); - st.wHour = worker(2); - skip(':'); - st.wMinute = worker(2); - skip(':'); - st.wSecond = worker(2); - if (date[walk] == '.') { - double v = pfc::string_to_float(date + walk); - st.wMilliseconds = (WORD)floor(v * 1000.f); // don't ever round up, don't want to handle ms of 1000 - } - - if (!st_sanity(st)) throw exception_time_error(); - - if (local) { - return ExportSystemTimeLocal(st); - } else { - return ExportSystemTime(st); - } - } catch (exception_time_error) { - return filetimestamp_invalid; - } -} +// Stub - functionality moved to PFC namespace foobar2000_io { - t_filetimestamp filetimestamp_from_string(const char* date) { - return filetimestamp_from_string_internal(date, true); - } - - t_filetimestamp filetimestamp_from_string_utc(const char* date) { - return filetimestamp_from_string_internal(date, false); - } - - static constexpr char g_invalidMsg[] = ""; - - pfc::string_formatter format_filetimestamp(t_filetimestamp p_timestamp) { - try { - SYSTEMTIME st; - if (MakeSystemTimeLocal(st, p_timestamp)) { - pfc::string_formatter buffer; - buffer - << pfc::format_uint(st.wYear, 4) << "-" << pfc::format_uint(st.wMonth, 2) << "-" << pfc::format_uint(st.wDay, 2) << " " - << pfc::format_uint(st.wHour, 2) << ":" << pfc::format_uint(st.wMinute, 2) << ":" << pfc::format_uint(st.wSecond, 2); - return buffer; - } - } catch (...) {} - return g_invalidMsg; - } - - pfc::string_formatter format_filetimestamp_ms(t_filetimestamp p_timestamp) { - try { - SYSTEMTIME st; - if (MakeSystemTimeLocal(st, p_timestamp)) { - pfc::string_formatter buffer; - buffer - << pfc::format_uint(st.wYear, 4) << "-" << pfc::format_uint(st.wMonth, 2) << "-" << pfc::format_uint(st.wDay, 2) << " " - << pfc::format_uint(st.wHour, 2) << ":" << pfc::format_uint(st.wMinute, 2) << ":" << pfc::format_uint(st.wSecond, 2) << "." << pfc::format_uint(st.wMilliseconds, 3); - return buffer; - } - } catch (...) {} - return g_invalidMsg; - } - - pfc::string_formatter format_filetimestamp_utc(t_filetimestamp p_timestamp) { - try { - SYSTEMTIME st; - if (MakeSystemTime(st, p_timestamp)) { - pfc::string_formatter buffer; - buffer - << pfc::format_uint(st.wYear, 4) << "-" << pfc::format_uint(st.wMonth, 2) << "-" << pfc::format_uint(st.wDay, 2) << " " - << pfc::format_uint(st.wHour, 2) << ":" << pfc::format_uint(st.wMinute, 2) << ":" << pfc::format_uint(st.wSecond, 2); - return buffer; - } - } catch (...) {} - return g_invalidMsg; - } - -} // namespace foobar2000_io - -namespace { - struct dateISO_t { - unsigned Y, M, D; - unsigned h, m, s; - double sfrac; - int tzdelta; - }; - - dateISO_t read_ISO_8601(const char* dateISO) { - dateISO_t ret = {}; - // 2022-01-26T13:44:51.200000Z - // 2010-02-19T14:54:23.031+08:00 - // 2022-01-27T11:01:49+00:00 - // 2022-01-27T11:01:49Z - // 20220127T110149Z - - unsigned walk = 0; - auto worker = [&](unsigned n) { - auto ret = ParseDateElem(dateISO + walk, n); - walk += n; - return ret; - }; - auto skip = [&](char c) { - if (dateISO[walk] == c) ++walk; - }; - auto expect = [&](char c) { - if (dateISO[walk] != c) throw exception_time_error(); - ++walk; - }; - ret.Y = worker(4); - skip('-'); - ret.M = worker(2); - skip('-'); - ret.D = worker(2); - expect('T'); - ret.h = worker(2); - skip(':'); - ret.m = worker(2); - skip(':'); - ret.s = worker(2); - if (dateISO[walk] == '.') { - unsigned base = walk; - ++walk; - while (pfc::char_is_numeric(dateISO[walk])) ++walk; - ret.sfrac = pfc::string_to_float(dateISO + base, walk - base); - } - if (dateISO[walk] == '+' || dateISO[walk] == '-') { - bool neg = dateISO[walk] == '-'; - ++walk; - unsigned tz_h = worker(2); - if (tz_h >= 24) throw exception_time_error(); - skip(':'); - unsigned tz_m = worker(2); - if (tz_m >= 60) throw exception_time_error(); - tz_m += tz_h * 60; - ret.tzdelta = neg ? (int)tz_m : -(int)tz_m; // reversed! it's a timezone offset, have to *add* it if timezone has a minus - } - return ret; - } -} - -t_filetimestamp foobar2000_io::filetimestamp_from_string_ISO_8601(const char* dateISO) { - try { - auto elems = read_ISO_8601(dateISO); - - SYSTEMTIME st = {}; - st.wDay = 1; st.wMonth = 1; - st.wYear = elems.Y; - st.wMonth = elems.M; - st.wDay = elems.D; - st.wHour = elems.h; - st.wMinute = elems.m; - st.wSecond = elems.s; - st.wMilliseconds = (WORD)floor(elems.sfrac * 1000.f); - - if (!st_sanity(st)) throw exception_time_error(); - - auto ret = ExportSystemTime(st); - - ret += filetimestamp_1second_increment * elems.tzdelta * 60; - - return ret; - } catch (...) { - return filetimestamp_invalid; - } + t_filetimestamp filetimestamp_from_string(const char* date) { + return pfc::filetimestamp_from_string( date ); + } + t_filetimestamp filetimestamp_from_string_utc(const char* date) { + return pfc::filetimestamp_from_string_utc( date ); + } + + pfc::string_formatter format_filetimestamp(t_filetimestamp p_timestamp) { + return pfc::format_filetimestamp( p_timestamp ); + } + + pfc::string_formatter format_filetimestamp_utc(t_filetimestamp p_timestamp) { + return pfc::format_filetimestamp_utc( p_timestamp ); + } + + pfc::string_formatter format_filetimestamp_ms(t_filetimestamp p_timestamp) { + return pfc::format_filetimestamp_ms( p_timestamp ); + } } diff --git a/sdk/foobar2000/helpers/filetimetools.h b/sdk/foobar2000/helpers/filetimetools.h index 041ea83..3be3676 100644 --- a/sdk/foobar2000/helpers/filetimetools.h +++ b/sdk/foobar2000/helpers/filetimetools.h @@ -3,8 +3,6 @@ namespace foobar2000_io { t_filetimestamp filetimestamp_from_string(const char * date); t_filetimestamp filetimestamp_from_string_utc(const char* date); - // From ISO 8601 time - t_filetimestamp filetimestamp_from_string_ISO_8601(const char* date); //! Warning: this formats according to system timezone settings, created strings should be used for display only, never for storage. pfc::string_formatter format_filetimestamp(t_filetimestamp p_timestamp); @@ -12,5 +10,4 @@ namespace foobar2000_io { pfc::string_formatter format_filetimestamp_utc(t_filetimestamp p_timestamp); //! Local timestamp with milliseconds pfc::string_formatter format_filetimestamp_ms(t_filetimestamp p_timestamp); - } diff --git a/sdk/foobar2000/helpers/foobar2000_SDK_helpers.xcodeproj/project.pbxproj b/sdk/foobar2000/helpers/foobar2000_SDK_helpers.xcodeproj/project.pbxproj new file mode 100644 index 0000000..56f136b --- /dev/null +++ b/sdk/foobar2000/helpers/foobar2000_SDK_helpers.xcodeproj/project.pbxproj @@ -0,0 +1,819 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 0F75F5EA2A6B1DB200A45078 /* dialog_resize_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5622A6B1DAE00A45078 /* dialog_resize_helper.h */; }; + 0F75F5EB2A6B1DB200A45078 /* input_logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5632A6B1DAE00A45078 /* input_logging.h */; }; + 0F75F5EC2A6B1DB200A45078 /* cfg_var_import.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5642A6B1DAE00A45078 /* cfg_var_import.cpp */; }; + 0F75F5ED2A6B1DB200A45078 /* readers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5652A6B1DAE00A45078 /* readers.cpp */; }; + 0F75F5EE2A6B1DB200A45078 /* text_file_loader_v2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5662A6B1DAE00A45078 /* text_file_loader_v2.cpp */; }; + 0F75F5EF2A6B1DB200A45078 /* VisUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5672A6B1DAE00A45078 /* VisUtils.h */; }; + 0F75F5F02A6B1DB200A45078 /* file_move_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5682A6B1DAE00A45078 /* file_move_helper.h */; }; + 0F75F5F12A6B1DB200A45078 /* filetimetools.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5692A6B1DAE00A45078 /* filetimetools.h */; }; + 0F75F5F22A6B1DB200A45078 /* create_directory_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F56A2A6B1DAE00A45078 /* create_directory_helper.cpp */; }; + 0F75F5F32A6B1DB200A45078 /* writer_wav.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F56B2A6B1DAE00A45078 /* writer_wav.h */; }; + 0F75F5F42A6B1DB200A45078 /* VisUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F56C2A6B1DAE00A45078 /* VisUtils.cpp */; }; + 0F75F5F52A6B1DB200A45078 /* COM_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F56D2A6B1DAE00A45078 /* COM_utils.h */; }; + 0F75F5F62A6B1DB200A45078 /* VolumeMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F56E2A6B1DAE00A45078 /* VolumeMap.h */; }; + 0F75F5F72A6B1DB200A45078 /* file_win32_wrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F56F2A6B1DAE00A45078 /* file_win32_wrapper.h */; }; + 0F75F5F82A6B1DB200A45078 /* foobar2000-lite+atl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5702A6B1DAE00A45078 /* foobar2000-lite+atl.h */; }; + 0F75F5F92A6B1DB200A45078 /* metadb_handle_array.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5712A6B1DAE00A45078 /* metadb_handle_array.h */; }; + 0F75F5FA2A6B1DB200A45078 /* atl-misc.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5722A6B1DAE00A45078 /* atl-misc.h */; }; + 0F75F5FB2A6B1DB200A45078 /* CSingleThreadWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5732A6B1DAE00A45078 /* CSingleThreadWrapper.h */; }; + 0F75F5FC2A6B1DB200A45078 /* image_load_save.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5742A6B1DAE00A45078 /* image_load_save.h */; }; + 0F75F5FD2A6B1DB200A45078 /* reader_pretend_nonseekable.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5752A6B1DAF00A45078 /* reader_pretend_nonseekable.h */; }; + 0F75F5FF2A6B1DB200A45078 /* meta_table_builder.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5772A6B1DAF00A45078 /* meta_table_builder.h */; }; + 0F75F6002A6B1DB200A45078 /* fb2k_threads.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5782A6B1DAF00A45078 /* fb2k_threads.h */; }; + 0F75F6012A6B1DB200A45078 /* playlist_position_reference_tracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5792A6B1DAF00A45078 /* playlist_position_reference_tracker.h */; }; + 0F75F6022A6B1DB200A45078 /* file_list_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F57A2A6B1DAF00A45078 /* file_list_helper.h */; }; + 0F75F6032A6B1DB200A45078 /* inplace_edit.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F57B2A6B1DAF00A45078 /* inplace_edit.h */; }; + 0F75F6042A6B1DB200A45078 /* album_art_helpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F57C2A6B1DAF00A45078 /* album_art_helpers.cpp */; }; + 0F75F6052A6B1DB200A45078 /* AutoComplete.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F57D2A6B1DAF00A45078 /* AutoComplete.h */; }; + 0F75F6062A6B1DB200A45078 /* ProfileCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F57E2A6B1DAF00A45078 /* ProfileCache.h */; }; + 0F75F6072A6B1DB200A45078 /* fileReadAhead.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F57F2A6B1DAF00A45078 /* fileReadAhead.h */; }; + 0F75F6082A6B1DB200A45078 /* cfg_dsp_chain_config.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5802A6B1DAF00A45078 /* cfg_dsp_chain_config.h */; }; + 0F75F6092A6B1DB200A45078 /* metadb_io_callback_v2_data.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5812A6B1DAF00A45078 /* metadb_io_callback_v2_data.h */; }; + 0F75F60A2A6B1DB200A45078 /* cue_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5822A6B1DAF00A45078 /* cue_parser.cpp */; }; + 0F75F60C2A6B1DB200A45078 /* ui_element_helpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5842A6B1DAF00A45078 /* ui_element_helpers.h */; }; + 0F75F60D2A6B1DB200A45078 /* CListControlFb2kColors.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5852A6B1DAF00A45078 /* CListControlFb2kColors.h */; }; + 0F75F60E2A6B1DB200A45078 /* stream_buffer_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5862A6B1DAF00A45078 /* stream_buffer_helper.h */; }; + 0F75F60F2A6B1DB200A45078 /* advconfig_runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5872A6B1DAF00A45078 /* advconfig_runtime.h */; }; + 0F75F6102A6B1DB200A45078 /* readWriteLock.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5882A6B1DAF00A45078 /* readWriteLock.h */; }; + 0F75F6112A6B1DB200A45078 /* writer_wav.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5892A6B1DAF00A45078 /* writer_wav.cpp */; }; + 0F75F6122A6B1DB200A45078 /* fb2kWorkerTool.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F58A2A6B1DAF00A45078 /* fb2kWorkerTool.h */; }; + 0F75F6132A6B1DB200A45078 /* cfg_objList.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F58B2A6B1DAF00A45078 /* cfg_objList.h */; }; + 0F75F6142A6B1DB200A45078 /* CDialogResizeHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F58C2A6B1DAF00A45078 /* CDialogResizeHelper.h */; }; + 0F75F6152A6B1DB200A45078 /* file_info_const_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F58D2A6B1DAF00A45078 /* file_info_const_impl.h */; }; + 0F75F6162A6B1DB200A45078 /* file_list_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F58E2A6B1DAF00A45078 /* file_list_helper.cpp */; }; + 0F75F6172A6B1DB200A45078 /* albumArtCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F58F2A6B1DAF00A45078 /* albumArtCache.h */; }; + 0F75F6182A6B1DB200A45078 /* rethrow.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5902A6B1DAF00A45078 /* rethrow.h */; }; + 0F75F6192A6B1DB200A45078 /* cfg_var_import.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5912A6B1DAF00A45078 /* cfg_var_import.h */; }; + 0F75F61A2A6B1DB200A45078 /* album_art_helpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5922A6B1DB000A45078 /* album_art_helpers.h */; }; + 0F75F61B2A6B1DB200A45078 /* create_directory_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5932A6B1DB000A45078 /* create_directory_helper.h */; }; + 0F75F61C2A6B1DB200A45078 /* input_helpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5942A6B1DB000A45078 /* input_helpers.h */; }; + 0F75F61D2A6B1DB200A45078 /* notifyList.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5952A6B1DB000A45078 /* notifyList.h */; }; + 0F75F6202A6B1DB200A45078 /* win-MulDiv.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5982A6B1DB000A45078 /* win-MulDiv.h */; }; + 0F75F6212A6B1DB200A45078 /* metadb_info_container_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5992A6B1DB000A45078 /* metadb_info_container_impl.h */; }; + 0F75F6222A6B1DB200A45078 /* metadb_io_hintlist.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F59A2A6B1DB000A45078 /* metadb_io_hintlist.h */; }; + 0F75F6232A6B1DB200A45078 /* track_property_callback_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F59B2A6B1DB000A45078 /* track_property_callback_impl.h */; }; + 0F75F6242A6B1DB200A45078 /* filetimetools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F59C2A6B1DB000A45078 /* filetimetools.cpp */; }; + 0F75F6252A6B1DB200A45078 /* ProcessUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F59D2A6B1DB000A45078 /* ProcessUtils.h */; }; + 0F75F6262A6B1DB200A45078 /* readers.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F59E2A6B1DB000A45078 /* readers.h */; }; + 0F75F6272A6B1DB200A45078 /* VolumeMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F59F2A6B1DB000A45078 /* VolumeMap.cpp */; }; + 0F75F6282A6B1DB200A45078 /* cfg_guidlist.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5A02A6B1DB000A45078 /* cfg_guidlist.cpp */; }; + 0F75F6292A6B1DB200A45078 /* dropdown_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5A12A6B1DB000A45078 /* dropdown_helper.cpp */; }; + 0F75F62A2A6B1DB200A45078 /* callback_merit.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5A22A6B1DB000A45078 /* callback_merit.h */; }; + 0F75F62B2A6B1DB200A45078 /* cue_creator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5A32A6B1DB000A45078 /* cue_creator.cpp */; }; + 0F75F62C2A6B1DB200A45078 /* CPropVariant.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5A42A6B1DB000A45078 /* CPropVariant.h */; }; + 0F75F62D2A6B1DB200A45078 /* text_file_loader_v2.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5A52A6B1DB000A45078 /* text_file_loader_v2.h */; }; + 0F75F62E2A6B1DB200A45078 /* helpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5A62A6B1DB000A45078 /* helpers.h */; }; + 0F75F6302A6B1DB200A45078 /* dynamic_bitrate_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5A82A6B1DB000A45078 /* dynamic_bitrate_helper.h */; }; + 0F75F6312A6B1DB200A45078 /* packet_decoder_mp3_common.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5A92A6B1DB000A45078 /* packet_decoder_mp3_common.h */; }; + 0F75F6322A6B1DB200A45078 /* foobar2000+atl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5AA2A6B1DB000A45078 /* foobar2000+atl.h */; }; + 0F75F6332A6B1DB200A45078 /* CallForwarder.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5AB2A6B1DB000A45078 /* CallForwarder.h */; }; + 0F75F6342A6B1DB200A45078 /* StdAfx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5AC2A6B1DB000A45078 /* StdAfx.cpp */; }; + 0F75F6352A6B1DB200A45078 /* CTableEditHelper-Legacy.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5AD2A6B1DB100A45078 /* CTableEditHelper-Legacy.h */; }; + 0F75F6362A6B1DB200A45078 /* window_placement_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5AE2A6B1DB100A45078 /* window_placement_helper.h */; }; + 0F75F6372A6B1DB200A45078 /* input_helper_cue.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5AF2A6B1DB100A45078 /* input_helper_cue.h */; }; + 0F75F6382A6B1DB200A45078 /* cuesheet_index_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5B02A6B1DB100A45078 /* cuesheet_index_list.cpp */; }; + 0F75F6392A6B1DB200A45078 /* win-systemtime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5B12A6B1DB100A45078 /* win-systemtime.cpp */; }; + 0F75F63A2A6B1DB200A45078 /* dynamic_bitrate_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5B22A6B1DB100A45078 /* dynamic_bitrate_helper.cpp */; }; + 0F75F63B2A6B1DB200A45078 /* bitreader_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5B32A6B1DB100A45078 /* bitreader_helper.h */; }; + 0F75F63C2A6B1DB200A45078 /* StdAfx.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5B42A6B1DB100A45078 /* StdAfx.h */; }; + 0F75F63D2A6B1DB200A45078 /* packet_decoder_aac_common.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5B52A6B1DB100A45078 /* packet_decoder_aac_common.h */; }; + 0F75F63E2A6B1DB200A45078 /* CmdThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5B62A6B1DB100A45078 /* CmdThread.h */; }; + 0F75F63F2A6B1DB200A45078 /* readers_lite.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5B72A6B1DB100A45078 /* readers_lite.h */; }; + 0F75F6402A6B1DB200A45078 /* text_file_loader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5B82A6B1DB100A45078 /* text_file_loader.cpp */; }; + 0F75F6412A6B1DB200A45078 /* cue_creator.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5B92A6B1DB100A45078 /* cue_creator.h */; }; + 0F75F6422A6B1DB200A45078 /* ThreadUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5BA2A6B1DB100A45078 /* ThreadUtils.cpp */; }; + 0F75F6432A6B1DB200A45078 /* file_streamstub.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5BB2A6B1DB100A45078 /* file_streamstub.h */; }; + 0F75F6442A6B1DB200A45078 /* tag_write_callback_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5BC2A6B1DB100A45078 /* tag_write_callback_impl.h */; }; + 0F75F6452A6B1DB200A45078 /* win32_misc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5BD2A6B1DB100A45078 /* win32_misc.cpp */; }; + 0F75F6462A6B1DB200A45078 /* WindowPositionUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5BE2A6B1DB100A45078 /* WindowPositionUtils.h */; }; + 0F75F6472A6B1DB200A45078 /* packet_decoder_mp3_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5BF2A6B1DB100A45078 /* packet_decoder_mp3_common.cpp */; }; + 0F75F6492A6B1DB200A45078 /* duration_counter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5C12A6B1DB100A45078 /* duration_counter.h */; }; + 0F75F64A2A6B1DB200A45078 /* cue_parser.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5C22A6B1DB100A45078 /* cue_parser.h */; }; + 0F75F64B2A6B1DB200A45078 /* dialog_resize_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5C32A6B1DB100A45078 /* dialog_resize_helper.cpp */; }; + 0F75F64C2A6B1DB200A45078 /* seekabilizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5C42A6B1DB100A45078 /* seekabilizer.cpp */; }; + 0F75F64D2A6B1DB200A45078 /* fullFileBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5C52A6B1DB100A45078 /* fullFileBuffer.h */; }; + 0F75F64E2A6B1DB200A45078 /* win32_dialog.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5C62A6B1DB100A45078 /* win32_dialog.h */; }; + 0F75F64F2A6B1DB200A45078 /* cuesheet_index_list.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5C72A6B1DB100A45078 /* cuesheet_index_list.h */; }; + 0F75F6502A6B1DB200A45078 /* callInMainThreadHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5C82A6B1DB100A45078 /* callInMainThreadHelper.h */; }; + 0F75F6512A6B1DB200A45078 /* icon_remapping_wildcard.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5C92A6B1DB100A45078 /* icon_remapping_wildcard.h */; }; + 0F75F6522A6B1DB200A45078 /* audio_render_float.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5CA2A6B1DB100A45078 /* audio_render_float.h */; }; + 0F75F6542A6B1DB200A45078 /* advconfig_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5CC2A6B1DB100A45078 /* advconfig_impl.h */; }; + 0F75F6552A6B1DB200A45078 /* input_fix_seeking.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5CD2A6B1DB100A45078 /* input_fix_seeking.h */; }; + 0F75F6562A6B1DB200A45078 /* win-systemtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5CE2A6B1DB100A45078 /* win-systemtime.h */; }; + 0F75F6572A6B1DB200A45078 /* stream_buffer_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5CF2A6B1DB100A45078 /* stream_buffer_helper.cpp */; }; + 0F75F6582A6B1DB200A45078 /* dsp_dialog.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5D02A6B1DB100A45078 /* dsp_dialog.h */; }; + 0F75F6592A6B1DB200A45078 /* BumpableElem.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5D12A6B1DB100A45078 /* BumpableElem.h */; }; + 0F75F65A2A6B1DB200A45078 /* cfg_obj.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5D22A6B1DB100A45078 /* cfg_obj.h */; }; + 0F75F65B2A6B1DB200A45078 /* packet_decoder_aac_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5D32A6B1DB200A45078 /* packet_decoder_aac_common.cpp */; }; + 0F75F65C2A6B1DB200A45078 /* mp3_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5D42A6B1DB200A45078 /* mp3_utils.h */; }; + 0F75F65D2A6B1DB200A45078 /* cue_parser_embedding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5D52A6B1DB200A45078 /* cue_parser_embedding.cpp */; }; + 0F75F65E2A6B1DB200A45078 /* input_helpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5D62A6B1DB200A45078 /* input_helpers.cpp */; }; + 0F75F65F2A6B1DB200A45078 /* ThreadUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5D72A6B1DB200A45078 /* ThreadUtils.h */; }; + 0F75F6602A6B1DB200A45078 /* track_property_callback_impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5D82A6B1DB200A45078 /* track_property_callback_impl.cpp */; }; + 0F75F6622A6B1DB200A45078 /* fb2k_wfx.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5DA2A6B1DB200A45078 /* fb2k_wfx.h */; }; + 0F75F6632A6B1DB200A45078 /* DarkMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5DB2A6B1DB200A45078 /* DarkMode.h */; }; + 0F75F6642A6B1DB200A45078 /* win32_misc.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5DC2A6B1DB200A45078 /* win32_misc.h */; }; + 0F75F6652A6B1DB200A45078 /* winmm-types.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5DD2A6B1DB200A45078 /* winmm-types.h */; }; + 0F75F6662A6B1DB200A45078 /* metadb_handle_set.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5DE2A6B1DB200A45078 /* metadb_handle_set.h */; }; + 0F75F6672A6B1DB200A45078 /* mp3_utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5DF2A6B1DB200A45078 /* mp3_utils.cpp */; }; + 0F75F6682A6B1DB200A45078 /* file_win32_wrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5E02A6B1DB200A45078 /* file_win32_wrapper.cpp */; }; + 0F75F6692A6B1DB200A45078 /* seekabilizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5E12A6B1DB200A45078 /* seekabilizer.h */; }; + 0F75F66A2A6B1DB200A45078 /* file_move_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5E22A6B1DB200A45078 /* file_move_helper.cpp */; }; + 0F75F66B2A6B1DB200A45078 /* cfg_guidlist.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5E32A6B1DB200A45078 /* cfg_guidlist.h */; }; + 0F75F66C2A6B1DB200A45078 /* input_helper_cue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F5E42A6B1DB200A45078 /* input_helper_cue.cpp */; }; + 0F75F66D2A6B1DB200A45078 /* text_file_loader.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5E52A6B1DB200A45078 /* text_file_loader.h */; }; + 0F75F66E2A6B1DB200A45078 /* file_cached.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5E62A6B1DB200A45078 /* file_cached.h */; }; + 0F75F66F2A6B1DB200A45078 /* input_stream_info_reader.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5E72A6B1DB200A45078 /* input_stream_info_reader.h */; }; + 0F75F6712A6B1DB200A45078 /* dropdown_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F75F5E92A6B1DB200A45078 /* dropdown_helper.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0F75F5622A6B1DAE00A45078 /* dialog_resize_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dialog_resize_helper.h; sourceTree = ""; }; + 0F75F5632A6B1DAE00A45078 /* input_logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = input_logging.h; sourceTree = ""; }; + 0F75F5642A6B1DAE00A45078 /* cfg_var_import.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cfg_var_import.cpp; sourceTree = ""; }; + 0F75F5652A6B1DAE00A45078 /* readers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = readers.cpp; sourceTree = ""; }; + 0F75F5662A6B1DAE00A45078 /* text_file_loader_v2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = text_file_loader_v2.cpp; sourceTree = ""; }; + 0F75F5672A6B1DAE00A45078 /* VisUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VisUtils.h; sourceTree = ""; }; + 0F75F5682A6B1DAE00A45078 /* file_move_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_move_helper.h; sourceTree = ""; }; + 0F75F5692A6B1DAE00A45078 /* filetimetools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filetimetools.h; sourceTree = ""; }; + 0F75F56A2A6B1DAE00A45078 /* create_directory_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = create_directory_helper.cpp; sourceTree = ""; }; + 0F75F56B2A6B1DAE00A45078 /* writer_wav.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = writer_wav.h; sourceTree = ""; }; + 0F75F56C2A6B1DAE00A45078 /* VisUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VisUtils.cpp; sourceTree = ""; }; + 0F75F56D2A6B1DAE00A45078 /* COM_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = COM_utils.h; sourceTree = ""; }; + 0F75F56E2A6B1DAE00A45078 /* VolumeMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VolumeMap.h; sourceTree = ""; }; + 0F75F56F2A6B1DAE00A45078 /* file_win32_wrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_win32_wrapper.h; sourceTree = ""; }; + 0F75F5702A6B1DAE00A45078 /* foobar2000-lite+atl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "foobar2000-lite+atl.h"; sourceTree = ""; }; + 0F75F5712A6B1DAE00A45078 /* metadb_handle_array.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metadb_handle_array.h; sourceTree = ""; }; + 0F75F5722A6B1DAE00A45078 /* atl-misc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "atl-misc.h"; sourceTree = ""; }; + 0F75F5732A6B1DAE00A45078 /* CSingleThreadWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSingleThreadWrapper.h; sourceTree = ""; }; + 0F75F5742A6B1DAE00A45078 /* image_load_save.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = image_load_save.h; sourceTree = ""; }; + 0F75F5752A6B1DAF00A45078 /* reader_pretend_nonseekable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = reader_pretend_nonseekable.h; sourceTree = ""; }; + 0F75F5762A6B1DAF00A45078 /* window_placement_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = window_placement_helper.cpp; sourceTree = ""; }; + 0F75F5772A6B1DAF00A45078 /* meta_table_builder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = meta_table_builder.h; sourceTree = ""; }; + 0F75F5782A6B1DAF00A45078 /* fb2k_threads.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fb2k_threads.h; sourceTree = ""; }; + 0F75F5792A6B1DAF00A45078 /* playlist_position_reference_tracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = playlist_position_reference_tracker.h; sourceTree = ""; }; + 0F75F57A2A6B1DAF00A45078 /* file_list_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_list_helper.h; sourceTree = ""; }; + 0F75F57B2A6B1DAF00A45078 /* inplace_edit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inplace_edit.h; sourceTree = ""; }; + 0F75F57C2A6B1DAF00A45078 /* album_art_helpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = album_art_helpers.cpp; sourceTree = ""; }; + 0F75F57D2A6B1DAF00A45078 /* AutoComplete.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoComplete.h; sourceTree = ""; }; + 0F75F57E2A6B1DAF00A45078 /* ProfileCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProfileCache.h; sourceTree = ""; }; + 0F75F57F2A6B1DAF00A45078 /* fileReadAhead.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fileReadAhead.h; sourceTree = ""; }; + 0F75F5802A6B1DAF00A45078 /* cfg_dsp_chain_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cfg_dsp_chain_config.h; sourceTree = ""; }; + 0F75F5812A6B1DAF00A45078 /* metadb_io_callback_v2_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metadb_io_callback_v2_data.h; sourceTree = ""; }; + 0F75F5822A6B1DAF00A45078 /* cue_parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cue_parser.cpp; sourceTree = ""; }; + 0F75F5832A6B1DAF00A45078 /* ui_element_helpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ui_element_helpers.cpp; sourceTree = ""; }; + 0F75F5842A6B1DAF00A45078 /* ui_element_helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ui_element_helpers.h; sourceTree = ""; }; + 0F75F5852A6B1DAF00A45078 /* CListControlFb2kColors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CListControlFb2kColors.h; sourceTree = ""; }; + 0F75F5862A6B1DAF00A45078 /* stream_buffer_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stream_buffer_helper.h; sourceTree = ""; }; + 0F75F5872A6B1DAF00A45078 /* advconfig_runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = advconfig_runtime.h; sourceTree = ""; }; + 0F75F5882A6B1DAF00A45078 /* readWriteLock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = readWriteLock.h; sourceTree = ""; }; + 0F75F5892A6B1DAF00A45078 /* writer_wav.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = writer_wav.cpp; sourceTree = ""; }; + 0F75F58A2A6B1DAF00A45078 /* fb2kWorkerTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fb2kWorkerTool.h; sourceTree = ""; }; + 0F75F58B2A6B1DAF00A45078 /* cfg_objList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cfg_objList.h; sourceTree = ""; }; + 0F75F58C2A6B1DAF00A45078 /* CDialogResizeHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDialogResizeHelper.h; sourceTree = ""; }; + 0F75F58D2A6B1DAF00A45078 /* file_info_const_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_info_const_impl.h; sourceTree = ""; }; + 0F75F58E2A6B1DAF00A45078 /* file_list_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_list_helper.cpp; sourceTree = ""; }; + 0F75F58F2A6B1DAF00A45078 /* albumArtCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = albumArtCache.h; sourceTree = ""; }; + 0F75F5902A6B1DAF00A45078 /* rethrow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rethrow.h; sourceTree = ""; }; + 0F75F5912A6B1DAF00A45078 /* cfg_var_import.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cfg_var_import.h; sourceTree = ""; }; + 0F75F5922A6B1DB000A45078 /* album_art_helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = album_art_helpers.h; sourceTree = ""; }; + 0F75F5932A6B1DB000A45078 /* create_directory_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = create_directory_helper.h; sourceTree = ""; }; + 0F75F5942A6B1DB000A45078 /* input_helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = input_helpers.h; sourceTree = ""; }; + 0F75F5952A6B1DB000A45078 /* notifyList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = notifyList.h; sourceTree = ""; }; + 0F75F5962A6B1DB000A45078 /* inplace_edit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = inplace_edit.cpp; sourceTree = ""; }; + 0F75F5972A6B1DB000A45078 /* image_load_save.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = image_load_save.cpp; sourceTree = ""; }; + 0F75F5982A6B1DB000A45078 /* win-MulDiv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "win-MulDiv.h"; sourceTree = ""; }; + 0F75F5992A6B1DB000A45078 /* metadb_info_container_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metadb_info_container_impl.h; sourceTree = ""; }; + 0F75F59A2A6B1DB000A45078 /* metadb_io_hintlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metadb_io_hintlist.h; sourceTree = ""; }; + 0F75F59B2A6B1DB000A45078 /* track_property_callback_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = track_property_callback_impl.h; sourceTree = ""; }; + 0F75F59C2A6B1DB000A45078 /* filetimetools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filetimetools.cpp; sourceTree = ""; }; + 0F75F59D2A6B1DB000A45078 /* ProcessUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessUtils.h; sourceTree = ""; }; + 0F75F59E2A6B1DB000A45078 /* readers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = readers.h; sourceTree = ""; }; + 0F75F59F2A6B1DB000A45078 /* VolumeMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VolumeMap.cpp; sourceTree = ""; }; + 0F75F5A02A6B1DB000A45078 /* cfg_guidlist.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cfg_guidlist.cpp; sourceTree = ""; }; + 0F75F5A12A6B1DB000A45078 /* dropdown_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dropdown_helper.cpp; sourceTree = ""; }; + 0F75F5A22A6B1DB000A45078 /* callback_merit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = callback_merit.h; sourceTree = ""; }; + 0F75F5A32A6B1DB000A45078 /* cue_creator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cue_creator.cpp; sourceTree = ""; }; + 0F75F5A42A6B1DB000A45078 /* CPropVariant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CPropVariant.h; sourceTree = ""; }; + 0F75F5A52A6B1DB000A45078 /* text_file_loader_v2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = text_file_loader_v2.h; sourceTree = ""; }; + 0F75F5A62A6B1DB000A45078 /* helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = helpers.h; sourceTree = ""; }; + 0F75F5A72A6B1DB000A45078 /* WindowPositionUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WindowPositionUtils.cpp; sourceTree = ""; }; + 0F75F5A82A6B1DB000A45078 /* dynamic_bitrate_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dynamic_bitrate_helper.h; sourceTree = ""; }; + 0F75F5A92A6B1DB000A45078 /* packet_decoder_mp3_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = packet_decoder_mp3_common.h; sourceTree = ""; }; + 0F75F5AA2A6B1DB000A45078 /* foobar2000+atl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "foobar2000+atl.h"; sourceTree = ""; }; + 0F75F5AB2A6B1DB000A45078 /* CallForwarder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallForwarder.h; sourceTree = ""; }; + 0F75F5AC2A6B1DB000A45078 /* StdAfx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StdAfx.cpp; sourceTree = ""; }; + 0F75F5AD2A6B1DB100A45078 /* CTableEditHelper-Legacy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CTableEditHelper-Legacy.h"; sourceTree = ""; }; + 0F75F5AE2A6B1DB100A45078 /* window_placement_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = window_placement_helper.h; sourceTree = ""; }; + 0F75F5AF2A6B1DB100A45078 /* input_helper_cue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = input_helper_cue.h; sourceTree = ""; }; + 0F75F5B02A6B1DB100A45078 /* cuesheet_index_list.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cuesheet_index_list.cpp; sourceTree = ""; }; + 0F75F5B12A6B1DB100A45078 /* win-systemtime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "win-systemtime.cpp"; sourceTree = ""; }; + 0F75F5B22A6B1DB100A45078 /* dynamic_bitrate_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dynamic_bitrate_helper.cpp; sourceTree = ""; }; + 0F75F5B32A6B1DB100A45078 /* bitreader_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bitreader_helper.h; sourceTree = ""; }; + 0F75F5B42A6B1DB100A45078 /* StdAfx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StdAfx.h; sourceTree = ""; }; + 0F75F5B52A6B1DB100A45078 /* packet_decoder_aac_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = packet_decoder_aac_common.h; sourceTree = ""; }; + 0F75F5B62A6B1DB100A45078 /* CmdThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CmdThread.h; sourceTree = ""; }; + 0F75F5B72A6B1DB100A45078 /* readers_lite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = readers_lite.h; sourceTree = ""; }; + 0F75F5B82A6B1DB100A45078 /* text_file_loader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = text_file_loader.cpp; sourceTree = ""; }; + 0F75F5B92A6B1DB100A45078 /* cue_creator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cue_creator.h; sourceTree = ""; }; + 0F75F5BA2A6B1DB100A45078 /* ThreadUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadUtils.cpp; sourceTree = ""; }; + 0F75F5BB2A6B1DB100A45078 /* file_streamstub.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_streamstub.h; sourceTree = ""; }; + 0F75F5BC2A6B1DB100A45078 /* tag_write_callback_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tag_write_callback_impl.h; sourceTree = ""; }; + 0F75F5BD2A6B1DB100A45078 /* win32_misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = win32_misc.cpp; sourceTree = ""; }; + 0F75F5BE2A6B1DB100A45078 /* WindowPositionUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowPositionUtils.h; sourceTree = ""; }; + 0F75F5BF2A6B1DB100A45078 /* packet_decoder_mp3_common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = packet_decoder_mp3_common.cpp; sourceTree = ""; }; + 0F75F5C02A6B1DB100A45078 /* CTableEditHelper-Legacy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "CTableEditHelper-Legacy.cpp"; sourceTree = ""; }; + 0F75F5C12A6B1DB100A45078 /* duration_counter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = duration_counter.h; sourceTree = ""; }; + 0F75F5C22A6B1DB100A45078 /* cue_parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cue_parser.h; sourceTree = ""; }; + 0F75F5C32A6B1DB100A45078 /* dialog_resize_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dialog_resize_helper.cpp; sourceTree = ""; }; + 0F75F5C42A6B1DB100A45078 /* seekabilizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = seekabilizer.cpp; sourceTree = ""; }; + 0F75F5C52A6B1DB100A45078 /* fullFileBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fullFileBuffer.h; sourceTree = ""; }; + 0F75F5C62A6B1DB100A45078 /* win32_dialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = win32_dialog.h; sourceTree = ""; }; + 0F75F5C72A6B1DB100A45078 /* cuesheet_index_list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cuesheet_index_list.h; sourceTree = ""; }; + 0F75F5C82A6B1DB100A45078 /* callInMainThreadHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = callInMainThreadHelper.h; sourceTree = ""; }; + 0F75F5C92A6B1DB100A45078 /* icon_remapping_wildcard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = icon_remapping_wildcard.h; sourceTree = ""; }; + 0F75F5CA2A6B1DB100A45078 /* audio_render_float.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audio_render_float.h; sourceTree = ""; }; + 0F75F5CB2A6B1DB100A45078 /* win32_dialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = win32_dialog.cpp; sourceTree = ""; }; + 0F75F5CC2A6B1DB100A45078 /* advconfig_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = advconfig_impl.h; sourceTree = ""; }; + 0F75F5CD2A6B1DB100A45078 /* input_fix_seeking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = input_fix_seeking.h; sourceTree = ""; }; + 0F75F5CE2A6B1DB100A45078 /* win-systemtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "win-systemtime.h"; sourceTree = ""; }; + 0F75F5CF2A6B1DB100A45078 /* stream_buffer_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stream_buffer_helper.cpp; sourceTree = ""; }; + 0F75F5D02A6B1DB100A45078 /* dsp_dialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsp_dialog.h; sourceTree = ""; }; + 0F75F5D12A6B1DB100A45078 /* BumpableElem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BumpableElem.h; sourceTree = ""; }; + 0F75F5D22A6B1DB100A45078 /* cfg_obj.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cfg_obj.h; sourceTree = ""; }; + 0F75F5D32A6B1DB200A45078 /* packet_decoder_aac_common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = packet_decoder_aac_common.cpp; sourceTree = ""; }; + 0F75F5D42A6B1DB200A45078 /* mp3_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mp3_utils.h; sourceTree = ""; }; + 0F75F5D52A6B1DB200A45078 /* cue_parser_embedding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cue_parser_embedding.cpp; sourceTree = ""; }; + 0F75F5D62A6B1DB200A45078 /* input_helpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = input_helpers.cpp; sourceTree = ""; }; + 0F75F5D72A6B1DB200A45078 /* ThreadUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadUtils.h; sourceTree = ""; }; + 0F75F5D82A6B1DB200A45078 /* track_property_callback_impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = track_property_callback_impl.cpp; sourceTree = ""; }; + 0F75F5D92A6B1DB200A45078 /* AutoComplete.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AutoComplete.cpp; sourceTree = ""; }; + 0F75F5DA2A6B1DB200A45078 /* fb2k_wfx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fb2k_wfx.h; sourceTree = ""; }; + 0F75F5DB2A6B1DB200A45078 /* DarkMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DarkMode.h; sourceTree = ""; }; + 0F75F5DC2A6B1DB200A45078 /* win32_misc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = win32_misc.h; sourceTree = ""; }; + 0F75F5DD2A6B1DB200A45078 /* winmm-types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "winmm-types.h"; sourceTree = ""; }; + 0F75F5DE2A6B1DB200A45078 /* metadb_handle_set.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metadb_handle_set.h; sourceTree = ""; }; + 0F75F5DF2A6B1DB200A45078 /* mp3_utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mp3_utils.cpp; sourceTree = ""; }; + 0F75F5E02A6B1DB200A45078 /* file_win32_wrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_win32_wrapper.cpp; sourceTree = ""; }; + 0F75F5E12A6B1DB200A45078 /* seekabilizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = seekabilizer.h; sourceTree = ""; }; + 0F75F5E22A6B1DB200A45078 /* file_move_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_move_helper.cpp; sourceTree = ""; }; + 0F75F5E32A6B1DB200A45078 /* cfg_guidlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cfg_guidlist.h; sourceTree = ""; }; + 0F75F5E42A6B1DB200A45078 /* input_helper_cue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = input_helper_cue.cpp; sourceTree = ""; }; + 0F75F5E52A6B1DB200A45078 /* text_file_loader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = text_file_loader.h; sourceTree = ""; }; + 0F75F5E62A6B1DB200A45078 /* file_cached.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_cached.h; sourceTree = ""; }; + 0F75F5E72A6B1DB200A45078 /* input_stream_info_reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = input_stream_info_reader.h; sourceTree = ""; }; + 0F75F5E82A6B1DB200A45078 /* DarkMode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DarkMode.cpp; sourceTree = ""; }; + 0F75F5E92A6B1DB200A45078 /* dropdown_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dropdown_helper.h; sourceTree = ""; }; + B12D1DB11991061A0087CEF3 /* libfoobar2000_SDK_helpers.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libfoobar2000_SDK_helpers.a; sourceTree = BUILT_PRODUCTS_DIR; }; + B166964A19ACC1560001728F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + B166965819ACC1560001728F /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + B166965B19ACC1560001728F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B12D1DAE1991061A0087CEF3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B12D1DA81991061A0087CEF3 = { + isa = PBXGroup; + children = ( + B12D1DBE1991063D0087CEF3 /* Source */, + B166964919ACC1560001728F /* Frameworks */, + B12D1DB21991061A0087CEF3 /* Products */, + ); + sourceTree = ""; + }; + B12D1DB21991061A0087CEF3 /* Products */ = { + isa = PBXGroup; + children = ( + B12D1DB11991061A0087CEF3 /* libfoobar2000_SDK_helpers.a */, + ); + name = Products; + sourceTree = ""; + }; + B12D1DBE1991063D0087CEF3 /* Source */ = { + isa = PBXGroup; + children = ( + 0F75F5CC2A6B1DB100A45078 /* advconfig_impl.h */, + 0F75F5872A6B1DAF00A45078 /* advconfig_runtime.h */, + 0F75F57C2A6B1DAF00A45078 /* album_art_helpers.cpp */, + 0F75F5922A6B1DB000A45078 /* album_art_helpers.h */, + 0F75F58F2A6B1DAF00A45078 /* albumArtCache.h */, + 0F75F5722A6B1DAE00A45078 /* atl-misc.h */, + 0F75F5CA2A6B1DB100A45078 /* audio_render_float.h */, + 0F75F5D92A6B1DB200A45078 /* AutoComplete.cpp */, + 0F75F57D2A6B1DAF00A45078 /* AutoComplete.h */, + 0F75F5B32A6B1DB100A45078 /* bitreader_helper.h */, + 0F75F5D12A6B1DB100A45078 /* BumpableElem.h */, + 0F75F5A22A6B1DB000A45078 /* callback_merit.h */, + 0F75F5AB2A6B1DB000A45078 /* CallForwarder.h */, + 0F75F5C82A6B1DB100A45078 /* callInMainThreadHelper.h */, + 0F75F58C2A6B1DAF00A45078 /* CDialogResizeHelper.h */, + 0F75F5802A6B1DAF00A45078 /* cfg_dsp_chain_config.h */, + 0F75F5A02A6B1DB000A45078 /* cfg_guidlist.cpp */, + 0F75F5E32A6B1DB200A45078 /* cfg_guidlist.h */, + 0F75F5D22A6B1DB100A45078 /* cfg_obj.h */, + 0F75F58B2A6B1DAF00A45078 /* cfg_objList.h */, + 0F75F5642A6B1DAE00A45078 /* cfg_var_import.cpp */, + 0F75F5912A6B1DAF00A45078 /* cfg_var_import.h */, + 0F75F5852A6B1DAF00A45078 /* CListControlFb2kColors.h */, + 0F75F5B62A6B1DB100A45078 /* CmdThread.h */, + 0F75F56D2A6B1DAE00A45078 /* COM_utils.h */, + 0F75F5A42A6B1DB000A45078 /* CPropVariant.h */, + 0F75F56A2A6B1DAE00A45078 /* create_directory_helper.cpp */, + 0F75F5932A6B1DB000A45078 /* create_directory_helper.h */, + 0F75F5732A6B1DAE00A45078 /* CSingleThreadWrapper.h */, + 0F75F5C02A6B1DB100A45078 /* CTableEditHelper-Legacy.cpp */, + 0F75F5AD2A6B1DB100A45078 /* CTableEditHelper-Legacy.h */, + 0F75F5A32A6B1DB000A45078 /* cue_creator.cpp */, + 0F75F5B92A6B1DB100A45078 /* cue_creator.h */, + 0F75F5D52A6B1DB200A45078 /* cue_parser_embedding.cpp */, + 0F75F5822A6B1DAF00A45078 /* cue_parser.cpp */, + 0F75F5C22A6B1DB100A45078 /* cue_parser.h */, + 0F75F5B02A6B1DB100A45078 /* cuesheet_index_list.cpp */, + 0F75F5C72A6B1DB100A45078 /* cuesheet_index_list.h */, + 0F75F5E82A6B1DB200A45078 /* DarkMode.cpp */, + 0F75F5DB2A6B1DB200A45078 /* DarkMode.h */, + 0F75F5C32A6B1DB100A45078 /* dialog_resize_helper.cpp */, + 0F75F5622A6B1DAE00A45078 /* dialog_resize_helper.h */, + 0F75F5A12A6B1DB000A45078 /* dropdown_helper.cpp */, + 0F75F5E92A6B1DB200A45078 /* dropdown_helper.h */, + 0F75F5D02A6B1DB100A45078 /* dsp_dialog.h */, + 0F75F5C12A6B1DB100A45078 /* duration_counter.h */, + 0F75F5B22A6B1DB100A45078 /* dynamic_bitrate_helper.cpp */, + 0F75F5A82A6B1DB000A45078 /* dynamic_bitrate_helper.h */, + 0F75F5782A6B1DAF00A45078 /* fb2k_threads.h */, + 0F75F5DA2A6B1DB200A45078 /* fb2k_wfx.h */, + 0F75F58A2A6B1DAF00A45078 /* fb2kWorkerTool.h */, + 0F75F5E62A6B1DB200A45078 /* file_cached.h */, + 0F75F58D2A6B1DAF00A45078 /* file_info_const_impl.h */, + 0F75F58E2A6B1DAF00A45078 /* file_list_helper.cpp */, + 0F75F57A2A6B1DAF00A45078 /* file_list_helper.h */, + 0F75F5E22A6B1DB200A45078 /* file_move_helper.cpp */, + 0F75F5682A6B1DAE00A45078 /* file_move_helper.h */, + 0F75F5BB2A6B1DB100A45078 /* file_streamstub.h */, + 0F75F5E02A6B1DB200A45078 /* file_win32_wrapper.cpp */, + 0F75F56F2A6B1DAE00A45078 /* file_win32_wrapper.h */, + 0F75F57F2A6B1DAF00A45078 /* fileReadAhead.h */, + 0F75F59C2A6B1DB000A45078 /* filetimetools.cpp */, + 0F75F5692A6B1DAE00A45078 /* filetimetools.h */, + 0F75F5702A6B1DAE00A45078 /* foobar2000-lite+atl.h */, + 0F75F5AA2A6B1DB000A45078 /* foobar2000+atl.h */, + 0F75F5C52A6B1DB100A45078 /* fullFileBuffer.h */, + 0F75F5A62A6B1DB000A45078 /* helpers.h */, + 0F75F5C92A6B1DB100A45078 /* icon_remapping_wildcard.h */, + 0F75F5972A6B1DB000A45078 /* image_load_save.cpp */, + 0F75F5742A6B1DAE00A45078 /* image_load_save.h */, + 0F75F5962A6B1DB000A45078 /* inplace_edit.cpp */, + 0F75F57B2A6B1DAF00A45078 /* inplace_edit.h */, + 0F75F5CD2A6B1DB100A45078 /* input_fix_seeking.h */, + 0F75F5E42A6B1DB200A45078 /* input_helper_cue.cpp */, + 0F75F5AF2A6B1DB100A45078 /* input_helper_cue.h */, + 0F75F5D62A6B1DB200A45078 /* input_helpers.cpp */, + 0F75F5942A6B1DB000A45078 /* input_helpers.h */, + 0F75F5632A6B1DAE00A45078 /* input_logging.h */, + 0F75F5E72A6B1DB200A45078 /* input_stream_info_reader.h */, + 0F75F5772A6B1DAF00A45078 /* meta_table_builder.h */, + 0F75F5712A6B1DAE00A45078 /* metadb_handle_array.h */, + 0F75F5DE2A6B1DB200A45078 /* metadb_handle_set.h */, + 0F75F5992A6B1DB000A45078 /* metadb_info_container_impl.h */, + 0F75F5812A6B1DAF00A45078 /* metadb_io_callback_v2_data.h */, + 0F75F59A2A6B1DB000A45078 /* metadb_io_hintlist.h */, + 0F75F5DF2A6B1DB200A45078 /* mp3_utils.cpp */, + 0F75F5D42A6B1DB200A45078 /* mp3_utils.h */, + 0F75F5952A6B1DB000A45078 /* notifyList.h */, + 0F75F5D32A6B1DB200A45078 /* packet_decoder_aac_common.cpp */, + 0F75F5B52A6B1DB100A45078 /* packet_decoder_aac_common.h */, + 0F75F5BF2A6B1DB100A45078 /* packet_decoder_mp3_common.cpp */, + 0F75F5A92A6B1DB000A45078 /* packet_decoder_mp3_common.h */, + 0F75F5792A6B1DAF00A45078 /* playlist_position_reference_tracker.h */, + 0F75F59D2A6B1DB000A45078 /* ProcessUtils.h */, + 0F75F57E2A6B1DAF00A45078 /* ProfileCache.h */, + 0F75F5752A6B1DAF00A45078 /* reader_pretend_nonseekable.h */, + 0F75F5B72A6B1DB100A45078 /* readers_lite.h */, + 0F75F5652A6B1DAE00A45078 /* readers.cpp */, + 0F75F59E2A6B1DB000A45078 /* readers.h */, + 0F75F5882A6B1DAF00A45078 /* readWriteLock.h */, + 0F75F5902A6B1DAF00A45078 /* rethrow.h */, + 0F75F5C42A6B1DB100A45078 /* seekabilizer.cpp */, + 0F75F5E12A6B1DB200A45078 /* seekabilizer.h */, + 0F75F5AC2A6B1DB000A45078 /* StdAfx.cpp */, + 0F75F5B42A6B1DB100A45078 /* StdAfx.h */, + 0F75F5CF2A6B1DB100A45078 /* stream_buffer_helper.cpp */, + 0F75F5862A6B1DAF00A45078 /* stream_buffer_helper.h */, + 0F75F5BC2A6B1DB100A45078 /* tag_write_callback_impl.h */, + 0F75F5662A6B1DAE00A45078 /* text_file_loader_v2.cpp */, + 0F75F5A52A6B1DB000A45078 /* text_file_loader_v2.h */, + 0F75F5B82A6B1DB100A45078 /* text_file_loader.cpp */, + 0F75F5E52A6B1DB200A45078 /* text_file_loader.h */, + 0F75F5BA2A6B1DB100A45078 /* ThreadUtils.cpp */, + 0F75F5D72A6B1DB200A45078 /* ThreadUtils.h */, + 0F75F5D82A6B1DB200A45078 /* track_property_callback_impl.cpp */, + 0F75F59B2A6B1DB000A45078 /* track_property_callback_impl.h */, + 0F75F5832A6B1DAF00A45078 /* ui_element_helpers.cpp */, + 0F75F5842A6B1DAF00A45078 /* ui_element_helpers.h */, + 0F75F56C2A6B1DAE00A45078 /* VisUtils.cpp */, + 0F75F5672A6B1DAE00A45078 /* VisUtils.h */, + 0F75F59F2A6B1DB000A45078 /* VolumeMap.cpp */, + 0F75F56E2A6B1DAE00A45078 /* VolumeMap.h */, + 0F75F5982A6B1DB000A45078 /* win-MulDiv.h */, + 0F75F5B12A6B1DB100A45078 /* win-systemtime.cpp */, + 0F75F5CE2A6B1DB100A45078 /* win-systemtime.h */, + 0F75F5CB2A6B1DB100A45078 /* win32_dialog.cpp */, + 0F75F5C62A6B1DB100A45078 /* win32_dialog.h */, + 0F75F5BD2A6B1DB100A45078 /* win32_misc.cpp */, + 0F75F5DC2A6B1DB200A45078 /* win32_misc.h */, + 0F75F5762A6B1DAF00A45078 /* window_placement_helper.cpp */, + 0F75F5AE2A6B1DB100A45078 /* window_placement_helper.h */, + 0F75F5A72A6B1DB000A45078 /* WindowPositionUtils.cpp */, + 0F75F5BE2A6B1DB100A45078 /* WindowPositionUtils.h */, + 0F75F5DD2A6B1DB200A45078 /* winmm-types.h */, + 0F75F5892A6B1DAF00A45078 /* writer_wav.cpp */, + 0F75F56B2A6B1DAE00A45078 /* writer_wav.h */, + ); + name = Source; + sourceTree = ""; + }; + B166964919ACC1560001728F /* Frameworks */ = { + isa = PBXGroup; + children = ( + B166964A19ACC1560001728F /* Foundation.framework */, + B166965819ACC1560001728F /* XCTest.framework */, + B166965B19ACC1560001728F /* UIKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + B12D1DAF1991061A0087CEF3 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0F75F6092A6B1DB200A45078 /* metadb_io_callback_v2_data.h in Headers */, + 0F75F62E2A6B1DB200A45078 /* helpers.h in Headers */, + 0F75F5F52A6B1DB200A45078 /* COM_utils.h in Headers */, + 0F75F6122A6B1DB200A45078 /* fb2kWorkerTool.h in Headers */, + 0F75F6082A6B1DB200A45078 /* cfg_dsp_chain_config.h in Headers */, + 0F75F6362A6B1DB200A45078 /* window_placement_helper.h in Headers */, + 0F75F61B2A6B1DB200A45078 /* create_directory_helper.h in Headers */, + 0F75F5FC2A6B1DB200A45078 /* image_load_save.h in Headers */, + 0F75F5F82A6B1DB200A45078 /* foobar2000-lite+atl.h in Headers */, + 0F75F61C2A6B1DB200A45078 /* input_helpers.h in Headers */, + 0F75F62D2A6B1DB200A45078 /* text_file_loader_v2.h in Headers */, + 0F75F6352A6B1DB200A45078 /* CTableEditHelper-Legacy.h in Headers */, + 0F75F6442A6B1DB200A45078 /* tag_write_callback_impl.h in Headers */, + 0F75F61A2A6B1DB200A45078 /* album_art_helpers.h in Headers */, + 0F75F6322A6B1DB200A45078 /* foobar2000+atl.h in Headers */, + 0F75F65F2A6B1DB200A45078 /* ThreadUtils.h in Headers */, + 0F75F6592A6B1DB200A45078 /* BumpableElem.h in Headers */, + 0F75F64D2A6B1DB200A45078 /* fullFileBuffer.h in Headers */, + 0F75F60C2A6B1DB200A45078 /* ui_element_helpers.h in Headers */, + 0F75F6252A6B1DB200A45078 /* ProcessUtils.h in Headers */, + 0F75F66D2A6B1DB200A45078 /* text_file_loader.h in Headers */, + 0F75F6072A6B1DB200A45078 /* fileReadAhead.h in Headers */, + 0F75F60D2A6B1DB200A45078 /* CListControlFb2kColors.h in Headers */, + 0F75F6132A6B1DB200A45078 /* cfg_objList.h in Headers */, + 0F75F6692A6B1DB200A45078 /* seekabilizer.h in Headers */, + 0F75F6562A6B1DB200A45078 /* win-systemtime.h in Headers */, + 0F75F6312A6B1DB200A45078 /* packet_decoder_mp3_common.h in Headers */, + 0F75F64A2A6B1DB200A45078 /* cue_parser.h in Headers */, + 0F75F6502A6B1DB200A45078 /* callInMainThreadHelper.h in Headers */, + 0F75F6652A6B1DB200A45078 /* winmm-types.h in Headers */, + 0F75F6662A6B1DB200A45078 /* metadb_handle_set.h in Headers */, + 0F75F6522A6B1DB200A45078 /* audio_render_float.h in Headers */, + 0F75F65A2A6B1DB200A45078 /* cfg_obj.h in Headers */, + 0F75F6182A6B1DB200A45078 /* rethrow.h in Headers */, + 0F75F5EA2A6B1DB200A45078 /* dialog_resize_helper.h in Headers */, + 0F75F6232A6B1DB200A45078 /* track_property_callback_impl.h in Headers */, + 0F75F6372A6B1DB200A45078 /* input_helper_cue.h in Headers */, + 0F75F6552A6B1DB200A45078 /* input_fix_seeking.h in Headers */, + 0F75F64F2A6B1DB200A45078 /* cuesheet_index_list.h in Headers */, + 0F75F5F02A6B1DB200A45078 /* file_move_helper.h in Headers */, + 0F75F6222A6B1DB200A45078 /* metadb_io_hintlist.h in Headers */, + 0F75F5F32A6B1DB200A45078 /* writer_wav.h in Headers */, + 0F75F6582A6B1DB200A45078 /* dsp_dialog.h in Headers */, + 0F75F6102A6B1DB200A45078 /* readWriteLock.h in Headers */, + 0F75F63C2A6B1DB200A45078 /* StdAfx.h in Headers */, + 0F75F6002A6B1DB200A45078 /* fb2k_threads.h in Headers */, + 0F75F65C2A6B1DB200A45078 /* mp3_utils.h in Headers */, + 0F75F66B2A6B1DB200A45078 /* cfg_guidlist.h in Headers */, + 0F75F6142A6B1DB200A45078 /* CDialogResizeHelper.h in Headers */, + 0F75F63E2A6B1DB200A45078 /* CmdThread.h in Headers */, + 0F75F5FB2A6B1DB200A45078 /* CSingleThreadWrapper.h in Headers */, + 0F75F6632A6B1DB200A45078 /* DarkMode.h in Headers */, + 0F75F5EF2A6B1DB200A45078 /* VisUtils.h in Headers */, + 0F75F6542A6B1DB200A45078 /* advconfig_impl.h in Headers */, + 0F75F6262A6B1DB200A45078 /* readers.h in Headers */, + 0F75F6032A6B1DB200A45078 /* inplace_edit.h in Headers */, + 0F75F5EB2A6B1DB200A45078 /* input_logging.h in Headers */, + 0F75F6202A6B1DB200A45078 /* win-MulDiv.h in Headers */, + 0F75F6062A6B1DB200A45078 /* ProfileCache.h in Headers */, + 0F75F6302A6B1DB200A45078 /* dynamic_bitrate_helper.h in Headers */, + 0F75F61D2A6B1DB200A45078 /* notifyList.h in Headers */, + 0F75F5FD2A6B1DB200A45078 /* reader_pretend_nonseekable.h in Headers */, + 0F75F6512A6B1DB200A45078 /* icon_remapping_wildcard.h in Headers */, + 0F75F5F12A6B1DB200A45078 /* filetimetools.h in Headers */, + 0F75F63B2A6B1DB200A45078 /* bitreader_helper.h in Headers */, + 0F75F6012A6B1DB200A45078 /* playlist_position_reference_tracker.h in Headers */, + 0F75F62C2A6B1DB200A45078 /* CPropVariant.h in Headers */, + 0F75F6622A6B1DB200A45078 /* fb2k_wfx.h in Headers */, + 0F75F60E2A6B1DB200A45078 /* stream_buffer_helper.h in Headers */, + 0F75F6462A6B1DB200A45078 /* WindowPositionUtils.h in Headers */, + 0F75F6412A6B1DB200A45078 /* cue_creator.h in Headers */, + 0F75F62A2A6B1DB200A45078 /* callback_merit.h in Headers */, + 0F75F6212A6B1DB200A45078 /* metadb_info_container_impl.h in Headers */, + 0F75F6172A6B1DB200A45078 /* albumArtCache.h in Headers */, + 0F75F66E2A6B1DB200A45078 /* file_cached.h in Headers */, + 0F75F6712A6B1DB200A45078 /* dropdown_helper.h in Headers */, + 0F75F5F92A6B1DB200A45078 /* metadb_handle_array.h in Headers */, + 0F75F63D2A6B1DB200A45078 /* packet_decoder_aac_common.h in Headers */, + 0F75F66F2A6B1DB200A45078 /* input_stream_info_reader.h in Headers */, + 0F75F6332A6B1DB200A45078 /* CallForwarder.h in Headers */, + 0F75F6642A6B1DB200A45078 /* win32_misc.h in Headers */, + 0F75F6192A6B1DB200A45078 /* cfg_var_import.h in Headers */, + 0F75F6052A6B1DB200A45078 /* AutoComplete.h in Headers */, + 0F75F64E2A6B1DB200A45078 /* win32_dialog.h in Headers */, + 0F75F5F62A6B1DB200A45078 /* VolumeMap.h in Headers */, + 0F75F5FA2A6B1DB200A45078 /* atl-misc.h in Headers */, + 0F75F60F2A6B1DB200A45078 /* advconfig_runtime.h in Headers */, + 0F75F6152A6B1DB200A45078 /* file_info_const_impl.h in Headers */, + 0F75F63F2A6B1DB200A45078 /* readers_lite.h in Headers */, + 0F75F6022A6B1DB200A45078 /* file_list_helper.h in Headers */, + 0F75F6432A6B1DB200A45078 /* file_streamstub.h in Headers */, + 0F75F6492A6B1DB200A45078 /* duration_counter.h in Headers */, + 0F75F5FF2A6B1DB200A45078 /* meta_table_builder.h in Headers */, + 0F75F5F72A6B1DB200A45078 /* file_win32_wrapper.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + B12D1DB01991061A0087CEF3 /* foobar2000_SDK_helpers */ = { + isa = PBXNativeTarget; + buildConfigurationList = B12D1DB51991061A0087CEF3 /* Build configuration list for PBXNativeTarget "foobar2000_SDK_helpers" */; + buildPhases = ( + B12D1DAD1991061A0087CEF3 /* Sources */, + B12D1DAE1991061A0087CEF3 /* Frameworks */, + B12D1DAF1991061A0087CEF3 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = foobar2000_SDK_helpers; + productName = foobar2000_SDK_helpers; + productReference = B12D1DB11991061A0087CEF3 /* libfoobar2000_SDK_helpers.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B12D1DA91991061A0087CEF3 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1250; + ORGANIZATIONNAME = "___FULLUSERNAME___"; + }; + buildConfigurationList = B12D1DAC1991061A0087CEF3 /* Build configuration list for PBXProject "foobar2000_SDK_helpers" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = B12D1DA81991061A0087CEF3; + productRefGroup = B12D1DB21991061A0087CEF3 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B12D1DB01991061A0087CEF3 /* foobar2000_SDK_helpers */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + B12D1DAD1991061A0087CEF3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0F75F6672A6B1DB200A45078 /* mp3_utils.cpp in Sources */, + 0F75F63A2A6B1DB200A45078 /* dynamic_bitrate_helper.cpp in Sources */, + 0F75F62B2A6B1DB200A45078 /* cue_creator.cpp in Sources */, + 0F75F6292A6B1DB200A45078 /* dropdown_helper.cpp in Sources */, + 0F75F60A2A6B1DB200A45078 /* cue_parser.cpp in Sources */, + 0F75F6472A6B1DB200A45078 /* packet_decoder_mp3_common.cpp in Sources */, + 0F75F6422A6B1DB200A45078 /* ThreadUtils.cpp in Sources */, + 0F75F64B2A6B1DB200A45078 /* dialog_resize_helper.cpp in Sources */, + 0F75F6272A6B1DB200A45078 /* VolumeMap.cpp in Sources */, + 0F75F65E2A6B1DB200A45078 /* input_helpers.cpp in Sources */, + 0F75F6392A6B1DB200A45078 /* win-systemtime.cpp in Sources */, + 0F75F6112A6B1DB200A45078 /* writer_wav.cpp in Sources */, + 0F75F6682A6B1DB200A45078 /* file_win32_wrapper.cpp in Sources */, + 0F75F5ED2A6B1DB200A45078 /* readers.cpp in Sources */, + 0F75F5EC2A6B1DB200A45078 /* cfg_var_import.cpp in Sources */, + 0F75F6572A6B1DB200A45078 /* stream_buffer_helper.cpp in Sources */, + 0F75F6282A6B1DB200A45078 /* cfg_guidlist.cpp in Sources */, + 0F75F5F42A6B1DB200A45078 /* VisUtils.cpp in Sources */, + 0F75F5EE2A6B1DB200A45078 /* text_file_loader_v2.cpp in Sources */, + 0F75F6042A6B1DB200A45078 /* album_art_helpers.cpp in Sources */, + 0F75F6602A6B1DB200A45078 /* track_property_callback_impl.cpp in Sources */, + 0F75F64C2A6B1DB200A45078 /* seekabilizer.cpp in Sources */, + 0F75F65D2A6B1DB200A45078 /* cue_parser_embedding.cpp in Sources */, + 0F75F66C2A6B1DB200A45078 /* input_helper_cue.cpp in Sources */, + 0F75F6402A6B1DB200A45078 /* text_file_loader.cpp in Sources */, + 0F75F6242A6B1DB200A45078 /* filetimetools.cpp in Sources */, + 0F75F6382A6B1DB200A45078 /* cuesheet_index_list.cpp in Sources */, + 0F75F5F22A6B1DB200A45078 /* create_directory_helper.cpp in Sources */, + 0F75F66A2A6B1DB200A45078 /* file_move_helper.cpp in Sources */, + 0F75F6342A6B1DB200A45078 /* StdAfx.cpp in Sources */, + 0F75F6162A6B1DB200A45078 /* file_list_helper.cpp in Sources */, + 0F75F65B2A6B1DB200A45078 /* packet_decoder_aac_common.cpp in Sources */, + 0F75F6452A6B1DB200A45078 /* win32_misc.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + B12D1DB31991061A0087CEF3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = StdAfx.h; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + ../.., + .., + ); + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MACOSX_DEPLOYMENT_TARGET = 11.0; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + B12D1DB41991061A0087CEF3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = StdAfx.h; + GCC_PREPROCESSOR_DEFINITIONS = "NDEBUG=1"; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + ../.., + .., + ); + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MACOSX_DEPLOYMENT_TARGET = 11.0; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Release; + }; + B12D1DB61991061A0087CEF3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + B12D1DB71991061A0087CEF3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B12D1DAC1991061A0087CEF3 /* Build configuration list for PBXProject "foobar2000_SDK_helpers" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B12D1DB31991061A0087CEF3 /* Debug */, + B12D1DB41991061A0087CEF3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B12D1DB51991061A0087CEF3 /* Build configuration list for PBXNativeTarget "foobar2000_SDK_helpers" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B12D1DB61991061A0087CEF3 /* Debug */, + B12D1DB71991061A0087CEF3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B12D1DA91991061A0087CEF3 /* Project object */; +} diff --git a/sdk/foobar2000/helpers/foobar2000_sdk_helpers.vcxproj b/sdk/foobar2000/helpers/foobar2000_sdk_helpers.vcxproj index 8068941..26a97da 100644 --- a/sdk/foobar2000/helpers/foobar2000_sdk_helpers.vcxproj +++ b/sdk/foobar2000/helpers/foobar2000_sdk_helpers.vcxproj @@ -37,19 +37,20 @@ {EE47764E-A202-4F85-A767-ABDAB4AFF35F} foobar2000_sdk_helpers + 10.0 StaticLibrary false Unicode - v143 + v142 StaticLibrary false Unicode - v143 + v142 StaticLibrary @@ -68,14 +69,14 @@ false Unicode true - v143 + v142 StaticLibrary false Unicode true - v143 + v142 StaticLibrary @@ -506,6 +507,8 @@ + + @@ -513,16 +516,18 @@ + + - + @@ -566,7 +571,9 @@ + + @@ -589,6 +596,5 @@ - - + \ No newline at end of file diff --git a/sdk/foobar2000/helpers/foobar2000_sdk_helpers.vcxproj.filters b/sdk/foobar2000/helpers/foobar2000_sdk_helpers.vcxproj.filters index d2efd9a..eaed348 100644 --- a/sdk/foobar2000/helpers/foobar2000_sdk_helpers.vcxproj.filters +++ b/sdk/foobar2000/helpers/foobar2000_sdk_helpers.vcxproj.filters @@ -352,9 +352,6 @@ Header Files - - Header Files - Header Files @@ -403,5 +400,26 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/sdk/foobar2000/helpers/input_helpers.cpp b/sdk/foobar2000/helpers/input_helpers.cpp index 42f4028..9265d48 100644 --- a/sdk/foobar2000/helpers/input_helpers.cpp +++ b/sdk/foobar2000/helpers/input_helpers.cpp @@ -5,15 +5,23 @@ #include "file_list_helper.h" #include "fileReadAhead.h" #include +#include "readers_lite.h" +#define LOCAL_DEBUG_PRINT(...) // PFC_DEBUG_PRINT(__VA_ARGS__) -input_helper::ioFilter_t input_helper::ioFilter_full_buffer(t_filesize val ) { +#define FILE_DECODEDAUDIO_PRINT(...) LOCAL_DEBUG_PRINT("file_decodeaudio(", pfc::format_ptr(this), "): ", __VA_ARGS__) + +input_helper::ioFilter_t input_helper::ioFilter_full_buffer(t_filesize val) { if (val == 0) return nullptr; return [val] ( file_ptr & f, const char * path, abort_callback & aborter) { if (!filesystem::g_is_remote_or_unrecognized(path)) { - fullFileBuffer a; - f = a.open(path, aborter, f, val); - return true; + if (f.is_empty()) filesystem::g_open_read(f, path, aborter); + if (!f->is_in_memory() && !f->is_remote() && f->can_seek() && f->get_size(aborter) <= val) { + try { + f = createFileMemMirrorAsync(f, nullptr, aborter); + return true; + } catch (std::bad_alloc const &) {} // keep orig file object + } } return false; }; @@ -34,6 +42,15 @@ input_helper::ioFilter_t input_helper::ioFilter_block_buffer(size_t arg) { }; } +static bool looksLikePlaylist(filesystem::ptr const& fs, const char* path) { + auto ext = fs->get_extension(path); + // match against common internet playlist extensions + // yes, this could query fb2k services instead, but uses a fixed list by design + for (auto walk : { "m3u", "pls", "m3u8", "asx" }) { + if (pfc::stringEqualsI_ascii(walk, ext)) return true; + } + return false; +} static input_helper::ioFilter_t makeReadAhead(size_t arg, bool bRemote) { if (arg == 0) return nullptr; @@ -42,6 +59,7 @@ static input_helper::ioFilter_t makeReadAhead(size_t arg, bool bRemote) { filesystem::ptr fs; if (!filesystem::g_get_interface(fs, p_path)) return false; if (bRemote != fs->is_remote(p_path)) return false; + if (looksLikePlaylist(fs, p_path)) return false; fs->open(p_file, p_path, filesystem::open_mode_read, p_abort); } else if (bRemote != p_file->is_remote()) return false; if (p_file->is_in_memory()) return false; @@ -58,9 +76,13 @@ input_helper::ioFilter_t input_helper::ioFilter_local_read_ahead(size_t arg) { return makeReadAhead(arg, false); } -void input_helper::open(service_ptr_t p_filehint,metadb_handle_ptr p_location,unsigned p_flags,abort_callback & p_abort,bool p_from_redirect,bool p_skip_hints) +void input_helper::open(trackRef location, abort_callback & abort, decodeOpen_t const & other) { + this->open( trackGetLocation(location), abort, other); +} + +void input_helper::open(service_ptr_t p_filehint,trackRef p_location,unsigned p_flags,abort_callback & p_abort,bool p_from_redirect,bool p_skip_hints) { - open(p_filehint,p_location->get_location(),p_flags,p_abort,p_from_redirect,p_skip_hints); + open(p_filehint,trackGetLocation( p_location ),p_flags,p_abort,p_from_redirect,p_skip_hints); } bool input_helper::test_if_lockless(abort_callback& a) { @@ -74,15 +96,14 @@ bool input_helper::test_if_lockless(abort_callback& a) { try { fileOpenWriteExisting(m_path, a); return true; - } catch (exception_io) {} + } catch (exception_io const &) {} } } #endif return false; } void input_helper::fileOpenTools(service_ptr_t & p_file,const char * p_path,input_helper::ioFilters_t const & filters, abort_callback & p_abort) { - for( auto walk = filters.begin(); walk != filters.end(); ++ walk ) { - auto f = *walk; + for( auto & f : filters ) { if (f) { if (f(p_file, p_path, p_abort)) break; } @@ -116,12 +137,16 @@ bool input_helper::open_path(const char * path, abort_callback & abort, decodeOp m_input ^= input_entry::g_open(input_decoder::class_guid, l_file, path, m_logger, abort, other.m_from_redirect ); ); -#ifdef FOOBAR2000_HAVE_METADB if (!other.m_skip_hints) { try { + if ( other.m_infoHook ) { + other.m_infoHook( m_input, path, abort ); + } +#ifdef FOOBAR2000_HAVE_METADB metadb_io::get()->hint_reader(m_input.get_ptr(), path, abort); +#endif } - catch (exception_io_data) { + catch (exception_io_data const &) { //Don't fail to decode when this barfs, might be barfing when reading info from another subsong than the one we're trying to decode etc. m_input.release(); if (l_file.is_valid()) l_file->reopen(abort); @@ -130,7 +155,6 @@ bool input_helper::open_path(const char * path, abort_callback & abort, decodeOp ); } } -#endif if (other.m_shim) m_input = other.m_shim(m_input, path, abort); @@ -199,7 +223,7 @@ bool input_helper::run_raw_v2(audio_chunk& p_chunk, mem_block_container& p_raw, try { return v2->run_raw(p_chunk, p_raw, p_abort); // UGLY: it may STILL FAIL with exception_not_implemented due to some underlying code not being able to handle the request!! - } catch (pfc::exception_not_implemented) {} + } catch (pfc::exception_not_implemented const &) {} } if (!m_input->run(p_chunk, p_abort)) return false; @@ -290,7 +314,7 @@ void input_helper::g_set_info(const playable_location & p_location,file_info & p instance->commit(p_abort); } - +#ifdef FOOBAR2000_HAVE_METADB bool dead_item_filter::run(const pfc::list_base_const_t & p_list,bit_array_var & p_mask) { file_list_helper::file_list_from_metadb_handle_list path_list; path_list.init_from_list(p_list); @@ -355,6 +379,8 @@ bool input_helper::g_mark_dead(const pfc::list_base_const_t & return filter.run(p_list,p_mask); } +#endif // #ifdef FOOBAR2000_HAVE_METADB + void input_info_read_helper::open(const char * p_path,abort_callback & p_abort) { if (m_input.is_empty() || playable_location::path_compare(m_path,p_path) != 0) { @@ -372,6 +398,7 @@ void input_info_read_helper::get_info(const playable_location & p_location,file_ TRACK_CODE("input_info_reader::get_info",m_input->get_info(p_location.get_subsong_index(),p_info,p_abort)); } +#ifdef FOOBAR2000_HAVE_METADB void input_info_read_helper::get_info_check(const playable_location & p_location,file_info & p_info,t_filestats & p_stats,bool & p_reloaded,abort_callback & p_abort) { open(p_location.get_path(),p_abort); t_filestats newstats; @@ -384,18 +411,20 @@ void input_info_read_helper::get_info_check(const playable_location & p_location p_reloaded = false; } } - - - +#endif // #ifdef FOOBAR2000_HAVE_METADB // openAudioData code +static constexpr openAudioDataFormat formatNative = (sizeof(audio_sample) == sizeof(double)) ? openAudioDataFormat::float64 : openAudioDataFormat::float32; + namespace { class file_decodedaudio : public file_readonly { public: - void init(const playable_location & loc, input_helper::decodeOpen_t const & arg, abort_callback & aborter) { + void init(const playable_location & loc, input_helper::decodeOpen_t const & arg, openAudioDataFormat format, abort_callback & aborter) { + FILE_DECODEDAUDIO_PRINT("opening: ", loc ); m_length = -1; m_lengthKnown = false; + m_format = format; m_subsong = loc.get_subsong(); m_decoder ^= input_entry::g_open( input_decoder::class_guid, arg.m_hint, loc.get_path(), arg.m_logger, aborter, arg.m_from_redirect); m_seekable = ( arg.m_flags & input_flag_no_seeking ) == 0 && m_decoder->can_seek(); @@ -404,6 +433,7 @@ namespace { readChunk(aborter, true); } void init(const playable_location & loc, bool bSeekable, file::ptr fileHint, abort_callback & aborter) { + FILE_DECODEDAUDIO_PRINT("opening: ", loc); m_length = -1; m_lengthKnown = false; m_subsong = loc.get_subsong(); input_entry::g_open_for_decoding(m_decoder, fileHint, loc.get_path(), aborter); @@ -412,7 +442,7 @@ namespace { readChunk(aborter, true); } - void reopen(abort_callback & aborter) { + void reopen(abort_callback & aborter) override { // Have valid chunk and it is the first chunk in the stream? Reset read pointers and bail, no need to reopen if (m_chunk.get_sample_count() > 0 && m_chunkBytesPtr == m_currentPosition) { @@ -426,22 +456,22 @@ namespace { readChunk(aborter); } - bool is_remote() { + bool is_remote() override { return false; } - t_filetimestamp get_timestamp(abort_callback & p_abort) { + t_filetimestamp get_timestamp(abort_callback & p_abort) override { return m_decoder->get_file_stats(p_abort).m_timestamp; } - void on_idle(abort_callback & p_abort) { + void on_idle(abort_callback & p_abort) override { m_decoder->on_idle(p_abort); } - bool get_content_type(pfc::string_base & p_out) { + bool get_content_type(pfc::string_base & p_out) override { return false; } - bool can_seek() { + bool can_seek() override { return m_seekable; } - void seek(t_filesize p_position, abort_callback & p_abort) { + void seek(t_filesize p_position, abort_callback & p_abort) override { if (!m_seekable) throw exception_io_object_not_seekable(); @@ -461,8 +491,7 @@ namespace { if (s != filesize_invalid) { if (p_position > s) throw exception_io_seek_out_of_range(); if (p_position == s) { - m_chunk.reset(); - m_chunkBytesPtr = 0; + m_chunk.reset(); m_curChunkBytes = 0; m_chunkBytesPtr = 0; m_currentPosition = p_position; m_eof = true; return; @@ -472,7 +501,7 @@ namespace { - const size_t row = m_spec.chanCount * sizeof(audio_sample); + const auto row = m_spec.chanCount * sampleBytes(); t_filesize samples = p_position / row; const double seekTime = audio_math::samples_to_time(samples, m_spec.sampleRate); m_decoder->seek(seekTime, p_abort); @@ -491,15 +520,16 @@ namespace { m_eof = false; } - t_filesize get_size(abort_callback & aborter) { + t_filesize get_size(abort_callback & aborter) override { const double l = length(aborter); if (l <= 0) return filesize_invalid; - return audio_math::time_to_samples(l, m_spec.sampleRate) * m_spec.chanCount * sizeof(audio_sample); + return audio_math::time_to_samples(l, m_spec.sampleRate) * m_spec.chanCount * sampleBytes(); } - t_filesize get_position(abort_callback & p_abort) { + t_filesize get_position(abort_callback & p_abort) override { return m_currentPosition; } - t_size read(void * p_buffer, t_size p_bytes, abort_callback & p_abort) { + t_size read(void * p_buffer, t_size p_bytes, abort_callback & p_abort) override { + FILE_DECODEDAUDIO_PRINT("read(", p_bytes, ")"); size_t done = 0; for (;;) { p_abort.check(); @@ -507,7 +537,7 @@ namespace { const size_t inChunk = curChunkBytes(); const size_t inChunkRemaining = inChunk - m_chunkBytesPtr; const size_t delta = pfc::min_t(inChunkRemaining, (p_bytes - done)); - memcpy((uint8_t*)p_buffer + done, (const uint8_t*)m_chunk.get_data() + m_chunkBytesPtr, delta); + memcpy((uint8_t*)p_buffer + done, (const uint8_t*)m_curChunkPtr + m_chunkBytesPtr, delta); m_chunkBytesPtr += delta; done += delta; m_currentPosition += delta; @@ -516,6 +546,7 @@ namespace { } if (!readChunk(p_abort)) break; } + FILE_DECODEDAUDIO_PRINT("read(", p_bytes, ") returning ", done); return done; } audio_chunk::spec_t const & get_spec() const { return m_spec; } @@ -529,22 +560,47 @@ namespace { m_currentPosition = 0; } size_t curChunkBytes() const { - return m_chunk.get_used_size() * sizeof(audio_sample); + return m_curChunkBytes; } bool readChunk(abort_callback & aborter, bool initial = false) { m_chunkBytesPtr = 0; + FILE_DECODEDAUDIO_PRINT("readChunk()"); for (;;) { if (m_eof || !m_decoder->run(m_chunk, aborter)) { if (initial) throw std::runtime_error("Decoder produced no data"); m_eof = true; - m_chunk.reset(); + m_chunk.reset(); m_curChunkBytes = 0; + FILE_DECODEDAUDIO_PRINT("readChunk() EOF"); return false; } if (m_chunk.is_valid()) break; } + PFC_ASSERT(m_chunk.get_peak() < 100); audio_chunk::spec_t spec = m_chunk.get_spec(); if (initial) m_spec = spec; else if (m_spec != spec) throw std::runtime_error("Sample format change in mid stream"); + + auto inPtr = m_chunk.get_data(); + const size_t inCount = m_chunk.get_used_size(); + m_curChunkBytes = inCount * sampleBytes(); + FILE_DECODEDAUDIO_PRINT("readChunk(): ", m_chunk.get_sample_count(), " samples, ", m_curChunkBytes, " bytes"); + if ( m_format == formatNative ) { + m_curChunkPtr = inPtr; + } else { + m_altFormatBuffer.grow_size(m_curChunkBytes); + m_curChunkPtr = m_altFormatBuffer.get_ptr(); + switch (m_format) { + case openAudioDataFormat::float32: + { auto out = reinterpret_cast(m_curChunkPtr); for (size_t walk = 0; walk < inCount; ++walk) out[walk] = (float)inPtr[walk]; } + break; + case openAudioDataFormat::float64: + { auto out = reinterpret_cast(m_curChunkPtr); for (size_t walk = 0; walk < inCount; ++walk) out[walk] = (double)inPtr[walk]; } + break; + default: + PFC_ASSERT(!"???"); + } + } + return true; } double length(abort_callback & aborter) { @@ -556,6 +612,9 @@ namespace { } return m_length; } + unsigned sampleBytes() const { + return m_format == openAudioDataFormat::float32 ? 4 : 8; + } audio_chunk_fast_impl m_chunk; size_t m_chunkBytesPtr; audio_chunk::spec_t m_spec; @@ -567,13 +626,18 @@ namespace { bool m_eof; t_filesize m_currentPosition; unsigned m_flags = 0; + openAudioDataFormat m_format = formatNative; + + pfc::array_t m_altFormatBuffer; + void* m_curChunkPtr = nullptr; + size_t m_curChunkBytes = 0; }; } -openAudioData_t openAudioData2(playable_location const & loc, input_helper::decodeOpen_t const & openArg, abort_callback & aborter) { +openAudioData_t openAudioData3(playable_location const& loc, input_helper::decodeOpen_t const& openArg, openAudioDataFormat format, abort_callback& aborter) { service_ptr_t f; f = new service_impl_t < file_decodedaudio >; - f->init(loc, openArg, aborter); + f->init(loc, openArg, format, aborter); openAudioData_t oad = {}; oad.audioData = f; @@ -581,6 +645,10 @@ openAudioData_t openAudioData2(playable_location const & loc, input_helper::deco return oad; } +openAudioData_t openAudioData2(playable_location const & loc, input_helper::decodeOpen_t const & openArg, abort_callback & aborter) { + return openAudioData3(loc, openArg, formatNative, aborter); +} + openAudioData_t openAudioData(playable_location const & loc, bool bSeekable, file::ptr fileHint, abort_callback & aborter) { service_ptr_t f; f = new service_impl_t < file_decodedaudio > ; f->init(loc, bSeekable, fileHint, aborter); diff --git a/sdk/foobar2000/helpers/input_helpers.h b/sdk/foobar2000/helpers/input_helpers.h index d16b636..d35c263 100644 --- a/sdk/foobar2000/helpers/input_helpers.h +++ b/sdk/foobar2000/helpers/input_helpers.h @@ -3,12 +3,14 @@ #include #include #include +#include class input_helper { public: input_helper(); typedef std::function shim_t; + typedef std::function infoHook_t; typedef std::function< bool ( file::ptr &, const char *, abort_callback & ) > ioFilter_t; typedef std::list ioFilters_t; @@ -28,15 +30,17 @@ class input_helper { ioFilters_t m_ioFilters; event_logger::ptr m_logger; + infoHook_t m_infoHook; shim_t m_shim; }; - void open(service_ptr_t p_filehint,metadb_handle_ptr p_location,unsigned p_flags,abort_callback & p_abort,bool p_from_redirect = false,bool p_skip_hints = false); void open(service_ptr_t p_filehint,const playable_location & p_location,unsigned p_flags,abort_callback & p_abort,bool p_from_redirect = false,bool p_skip_hints = false); void attach(input_decoder::ptr dec, const char * path); void open(const playable_location & location, abort_callback & abort, decodeOpen_t const & other); - void open(metadb_handle_ptr location, abort_callback & abort, decodeOpen_t const & other) {this->open(location->get_location(), abort, other);} + + void open(service_ptr_t p_filehint,trackRef p_location,unsigned p_flags,abort_callback & p_abort,bool p_from_redirect = false,bool p_skip_hints = false); + void open(trackRef location, abort_callback & abort, decodeOpen_t const & other); //! Multilevel open helpers. @@ -92,8 +96,10 @@ class input_helper { static void g_set_info(const playable_location & p_location,file_info & p_info,abort_callback & p_abort,bool p_from_redirect = false); +#ifdef FOOBAR2000_HAVE_METADB static bool g_mark_dead(const pfc::list_base_const_t & p_list,bit_array_var & p_mask,abort_callback & p_abort); - +#endif + static void fileOpenTools(service_ptr_t& p_file, const char* p_path, ioFilters_t const& filters, abort_callback& p_abort); bool test_if_lockless(abort_callback&); @@ -107,18 +113,22 @@ class input_helper { event_logger::ptr m_logger; }; +#ifdef FOOBAR2000_HAVE_METADB class NOVTABLE dead_item_filter : public abort_callback { public: virtual void on_progress(t_size p_position,t_size p_total) = 0; bool run(const pfc::list_base_const_t & p_list,bit_array_var & p_mask); }; +#endif class input_info_read_helper { public: input_info_read_helper() {} void get_info(const playable_location & p_location,file_info & p_info,t_filestats & p_stats,abort_callback & p_abort); +#ifdef FOOBAR2000_HAVE_METADB void get_info_check(const playable_location & p_location,file_info & p_info,t_filestats & p_stats,bool & p_reloaded,abort_callback & p_abort); +#endif private: void open(const char * p_path,abort_callback & p_abort); @@ -127,8 +137,6 @@ class input_info_read_helper { }; - - //! openAudioData return value, see openAudioData() struct openAudioData_t { file::ptr audioData; // audio data stream @@ -144,3 +152,11 @@ struct openAudioData_t { //! However, if you want 100% functionality regardless of file format being worked with, mirror the content to a temp file and let go of the file object returned by openAudioData(). openAudioData_t openAudioData(playable_location const & loc, bool bSeekable, file::ptr fileHint, abort_callback & aborter); openAudioData_t openAudioData2(playable_location const & loc, input_helper::decodeOpen_t const & openArg, abort_callback & aborter); + + +//! openAudioData3 allows explicit format: float32 or float64, for use cases that need such. +enum class openAudioDataFormat { + float32, + float64 +}; +openAudioData_t openAudioData3(playable_location const& loc, input_helper::decodeOpen_t const& openArg, openAudioDataFormat format, abort_callback& aborter); diff --git a/sdk/foobar2000/helpers/meta_table_builder.h b/sdk/foobar2000/helpers/meta_table_builder.h index 459883a..396643b 100644 --- a/sdk/foobar2000/helpers/meta_table_builder.h +++ b/sdk/foobar2000/helpers/meta_table_builder.h @@ -105,12 +105,10 @@ class meta_table_builder { from_info_overwrite(p_info); from_RG_overwrite(p_info.get_replaygain()); } - void from_RG_overwrite(replaygain_info info) { - replaygain_info::t_text_buffer buffer; - if (info.format_album_gain(buffer)) set("replaygain_album_gain", buffer); - if (info.format_track_gain(buffer)) set("replaygain_track_gain", buffer); - if (info.format_album_peak(buffer)) set("replaygain_album_peak", buffer); - if (info.format_track_peak(buffer)) set("replaygain_track_peak", buffer); + void from_RG_overwrite(replaygain_info const & info) { + info.for_each([&](const char* key, const char* value) { + set(key, value); + }); } void from_info_overwrite(const file_info & p_info) { for(t_size metawalk = 0, metacount = p_info.meta_get_count(); metawalk < metacount; ++metawalk ) { diff --git a/sdk/foobar2000/helpers/metadb_handle_set.h b/sdk/foobar2000/helpers/metadb_handle_set.h index 8cf1993..e59c242 100644 --- a/sdk/foobar2000/helpers/metadb_handle_set.h +++ b/sdk/foobar2000/helpers/metadb_handle_set.h @@ -21,10 +21,8 @@ class metadb_handle_set { if (rv) p->service_release(); return rv; } - - size_t get_count() const { - return m_content.size(); - } + size_t size() const {return m_content.size();} + size_t get_count() const {return m_content.size(); } template bool contains(ptr_t const & item) const { return m_content.count(&*item) != 0; @@ -44,28 +42,40 @@ class metadb_handle_set { bool added = m_content.insert(p).second; if (!added) p->service_release(); } - void remove_all() { - for (auto iter = m_content.begin(); iter != m_content.end(); ++iter) { - metadb_handle * p = (*iter); - p->service_release(); - } + void clear() { + for (auto p : m_content) p->service_release(); m_content.clear(); } + void remove_all() { clear();} template void enumerate(callback_t & cb) const { - for (auto iter = m_content.begin(); iter != m_content.end(); ++iter) { - cb(*iter); - } + for (auto iter : m_content) cb(iter); } typedef std::set impl_t; typedef impl_t::const_iterator const_iterator; const_iterator begin() const { return m_content.begin(); } const_iterator end() const { return m_content.end(); } -private: - std::set m_content; + metadb_handle_list to_list() const { + metadb_handle_list ret; ret.prealloc(m_content.size()); + for (auto h : m_content) { + ret.add_item(h); + } + return ret; + } + metadb_handle_set(const metadb_handle_set& src) { _copy(src); } + void operator=(const metadb_handle_set& src) { _copy(src); } + metadb_handle_set(metadb_handle_set&& src) noexcept { _move(src); } + void operator=(metadb_handle_set&& src) noexcept { _move(src); } private: - metadb_handle_set(const metadb_handle_set &) = delete; - void operator=(const metadb_handle_set&) = delete; + void _copy(const metadb_handle_set& src) { + m_content = src.m_content; + for (auto h : m_content) h->service_add_ref(); + } + void _move(metadb_handle_set& src) { + m_content = std::move(src.m_content); src.m_content.clear(); + } + + impl_t m_content; }; diff --git a/sdk/foobar2000/helpers/mp3_utils.cpp b/sdk/foobar2000/helpers/mp3_utils.cpp index e58c395..6961abe 100644 --- a/sdk/foobar2000/helpers/mp3_utils.cpp +++ b/sdk/foobar2000/helpers/mp3_utils.cpp @@ -239,13 +239,13 @@ static t_uint16 grabFrameCRC(const t_uint8 * frameData, t_size sideInfoLen) { } t_uint16 mp3_utils::ExtractFrameCRC(const t_uint8 * frameData, t_size frameSize, TMPEGFrameInfo const & info) { - PFC_ASSERT( frameSize >= info.m_bytes && info.m_crc ); + PFC_ASSERT(frameSize >= info.m_bytes && info.m_crc); (void)info; (void)frameSize; return ((t_uint16)frameData[4] << 8) | (t_uint16)frameData[5]; } t_uint16 mp3_utils::CalculateFrameCRC(const t_uint8 * frameData, t_size frameSize, TMPEGFrameInfo const & info) { - PFC_ASSERT( frameSize >= info.m_bytes && info.m_crc ); + PFC_ASSERT(frameSize >= info.m_bytes && info.m_crc); (void)frameSize; t_size sideInfoLen = 0; if (info.m_mpegversion == MPEG_1) diff --git a/sdk/foobar2000/helpers/packet_decoder_aac_common.cpp b/sdk/foobar2000/helpers/packet_decoder_aac_common.cpp index bb4ac28..1b1b9fc 100644 --- a/sdk/foobar2000/helpers/packet_decoder_aac_common.cpp +++ b/sdk/foobar2000/helpers/packet_decoder_aac_common.cpp @@ -267,3 +267,46 @@ const char * packet_decoder_aac_common::objectTypeStr( unsigned ot ) { const char * packet_decoder_aac_common::audioSpecificConfig_t::objectTypeStr() const { return packet_decoder_aac_common::objectTypeStr( this->m_objectType ); } + +pfc::array_t packet_decoder_aac_common::buildASC(audioSpecificConfig_t const& arg) { + pfc::array_t ret; ret.resize(5); memset(ret.get_ptr(), 0, ret.get_size()); + unsigned pos = 0; + auto write = [&](unsigned v, unsigned bits) { + bitreader_helper::write_int(ret.get_ptr(), pos, bits, v); + pos += bits; + }; + + if (arg.m_objectType < 32) { + write(arg.m_objectType, 5); + } else { + write(31, 5); + write(arg.m_objectType - 32, 6); + } + + { + bool stdRate = false; + for (unsigned i = 0; i < std::size(aac_sample_rates); ++i) { + if (arg.m_sampleRate == aac_sample_rates[i]) { + write(i, 4); + stdRate = true; break; + } + } + if (!stdRate) { + write(arg.m_sampleRate, 24); + } + + write(arg.m_channels, 4); + } + + ret.set_size((pos + 7) / 8); + return ret; +} + +pfc::array_t packet_decoder_aac_common::buildSafeASC(unsigned rate) { + if (rate == 0) rate = 44100; + audioSpecificConfig_t info = {}; + info.m_sampleRate = rate; + info.m_objectType = 1; // 1 = main, 2 = LC + info.m_channels = 2; // stereo + return buildASC(info); +} \ No newline at end of file diff --git a/sdk/foobar2000/helpers/packet_decoder_aac_common.h b/sdk/foobar2000/helpers/packet_decoder_aac_common.h index b074dbc..c8d1371 100644 --- a/sdk/foobar2000/helpers/packet_decoder_aac_common.h +++ b/sdk/foobar2000/helpers/packet_decoder_aac_common.h @@ -37,4 +37,10 @@ class packet_decoder_aac_common : public packet_decoder { static audioSpecificConfig_t parseASC(const void *, size_t); static unsigned get_ASC_object_type(const void *, size_t); + + static pfc::array_t buildASC(audioSpecificConfig_t const&); + + // If no sane ASC was provided by container, make something up to initialize decoder and attempt decoding. + static pfc::array_t buildSafeASC(unsigned rate = 0); + }; diff --git a/sdk/foobar2000/helpers/readWriteLock.h b/sdk/foobar2000/helpers/readWriteLock.h new file mode 100644 index 0000000..279fa39 --- /dev/null +++ b/sdk/foobar2000/helpers/readWriteLock.h @@ -0,0 +1,77 @@ +#pragma once + + +namespace fb2k { + //! fb2k::readWriteLock: abortable readWriteLock, allowing multiple concurrent readers while not writing, or one writer while not reading. \n + //! Safe to release locks in different threads than obtained, contrary to system object such as SRW locks. + class readWriteLock { + pfc::mutex m_guard; + size_t m_readers = 0, m_writers = 0; + typedef std::shared_ptr < pfc::event> eventRef_t; + eventRef_t m_currentEvent; + public: + + void shutdown() { + for (;;) { + eventRef_t waitFor; + { + PFC_INSYNC(m_guard); + if (m_readers == 0 && m_writers == 0) return; + PFC_ASSERT(m_currentEvent); + waitFor = m_currentEvent; + } + waitFor->wait_for(-1); + } + } + + service_ptr beginRead(abort_callback& a) { + for (;;) { + a.check(); + eventRef_t waitFor; + { + PFC_INSYNC(m_guard); + if (m_writers == 0) { + if (!m_currentEvent) m_currentEvent = std::make_shared(); + ++m_readers; + return fb2k::callOnRelease([this] { + PFC_INSYNC(m_guard); + PFC_ASSERT(m_currentEvent); + PFC_ASSERT(m_readers > 0 && m_writers == 0); + if (--m_readers == 0) { + m_currentEvent->set_state(true); m_currentEvent = nullptr; + } + }); + } + PFC_ASSERT(m_currentEvent); + waitFor = m_currentEvent; + } + a.waitForEvent(*waitFor); + } + } + service_ptr beginWrite(abort_callback& a) { + for (;;) { + a.check(); + eventRef_t waitFor; + { + PFC_INSYNC(m_guard); + if (m_readers == 0 && m_writers == 0) { + m_currentEvent = std::make_shared(); + ++m_writers; + return fb2k::callOnRelease([this] { + PFC_INSYNC(m_guard); + PFC_ASSERT(m_currentEvent); + PFC_ASSERT(m_readers == 0 && m_writers > 0); + if (--m_writers == 0) { + m_currentEvent->set_state(true); m_currentEvent = nullptr; + } + }); + } + PFC_ASSERT(m_currentEvent); + waitFor = m_currentEvent; + } + a.waitForEvent(*waitFor); + } + } + + }; +} diff --git a/sdk/foobar2000/helpers/readers.cpp b/sdk/foobar2000/helpers/readers.cpp index 2c819db..543d28b 100644 --- a/sdk/foobar2000/helpers/readers.cpp +++ b/sdk/foobar2000/helpers/readers.cpp @@ -1,9 +1,12 @@ #include "StdAfx.h" #include "readers.h" +#include "readers_lite.h" #include "fullFileBuffer.h" #include "fileReadAhead.h" #include #include +#include +#include t_size reader_membuffer_base::read(void * p_buffer, t_size p_bytes, abort_callback & p_abort) { p_abort.check_e(); @@ -43,7 +46,7 @@ file::ptr fullFileBuffer::open(const char * path, abort_callback & abort, file:: r->init(f, abort); f = r; } - catch (std::bad_alloc) {} + catch (std::bad_alloc const &) {} return f; } @@ -57,7 +60,7 @@ file::ptr fullFileBuffer::open(const char * path, abort_callback & abort, file:: #include -#include "rethrow.h" +#include #include #include @@ -72,9 +75,10 @@ namespace { pfc::array_t m_buffer; size_t m_bufferBegin, m_bufferEnd; - pfc::event m_canRead, m_canWrite; + pfc::event m_canRead; + pfc::event_std m_canWrite; pfc::mutex m_guard; - ThreadUtils::CRethrow m_error; + std::exception_ptr m_error; t_filesize m_seekto; abort_callback_impl m_abort; bool m_remote; @@ -84,8 +88,8 @@ namespace { std::list m_dynamicInfo; }; typedef std::shared_ptr readAheadInstanceRef; - static const t_filesize seek_reopen = (filesize_invalid-1); - class fileReadAhead : public file_readonly_t< service_multi_inherit > { + static constexpr t_filesize seek_reopen = (filesize_invalid-1); + class fileReadAhead : public file_readonly_t< service_multi_inherit< service_multi_inherit, stream_receive > > { service_ptr m_metadata; public: readAheadInstanceRef m_instance; @@ -132,11 +136,17 @@ namespace { worker(*i); } ); } - t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) override { + t_size receive(void* p_buffer, t_size p_bytes, abort_callback& p_abort) override { + return read_internal(p_buffer, p_bytes, p_abort, true); + } + t_size read(void* p_buffer, t_size p_bytes, abort_callback& p_abort) override { + return read_internal(p_buffer, p_bytes, p_abort, false); + } + t_size read_internal(void * p_buffer,t_size p_bytes,abort_callback & p_abort, bool bReceive) { auto & i = * m_instance; size_t done = 0; bool initial = true; - while( done < p_bytes ) { + while( bReceive ? done == 0 : done < p_bytes ) { if ( !initial ) { // Do not invoke waiting with common case read with lots of data in the buffer pfc::event::g_twoEventWait( i.m_canRead.get_handle(), p_abort.get_abort_event(), -1); @@ -145,7 +155,7 @@ namespace { pfc::mutexScope guard ( i.m_guard ); size_t got = i.m_bufferEnd - i.m_bufferBegin; if (got == 0) { - i.m_error.rethrow(); + if (i.m_error) std::rethrow_exception(i.m_error); if ( initial && ! i.m_atEOF ) { initial = false; continue; // proceed to wait for more data } @@ -163,7 +173,7 @@ namespace { got -= delta; m_position += delta; - if (!i.m_error.didFail() && !i.m_atEOF) { + if (!i.m_error && !i.m_atEOF) { if ( got == 0 ) i.m_canRead.set_state( false ); const bool wakeUpNow = got < i.m_wakeUpThreschold; // Only set the event when *crossing* the boundary @@ -261,7 +271,7 @@ namespace { void seekInternal( t_filesize p_position ) { auto & i = * m_instance; insync( i.m_guard ); - i.m_error.rethrow(); + if (i.m_error) std::rethrow_exception(i.m_error); i.m_bufferBegin = i.m_bufferEnd = 0; i.m_canWrite.set_state(true); i.m_seekto = p_position; @@ -271,11 +281,10 @@ namespace { m_position = ( p_position == seek_reopen ) ? 0 : p_position; } static void worker( readAheadInstance_t & i ) { - ThreadUtils::CRethrow err; - err.exec( [&i] { + try { bool atEOF = false; uint8_t* bufptr = i.m_buffer.get_ptr(); - const size_t readAtOnceLimit = i.m_remote ? 256 : 4*1024; + const size_t readAtOnceLimit = i.m_remote ? 64*1024 : 256 * 1024; for ( ;; ) { i.m_canWrite.wait_for(-1); size_t readHowMuch = 0, readOffset = 0; @@ -314,7 +323,7 @@ namespace { dynInfoEntry_t dynInfo; if ( readHowMuch > 0 ) { - readHowMuch = i.m_file->read( bufptr + readOffset, readHowMuch, i.m_abort ); + readHowMuch = i.m_file->receive( bufptr + readOffset, readHowMuch, i.m_abort ); if ( readHowMuch == 0 ) atEOF = true; @@ -353,11 +362,9 @@ namespace { } } - } ); - - if ( err.didFail( ) ) { - pfc::mutexScope guard( i.m_guard ); - i.m_error = err; + } catch (...) { + pfc::mutexScope guard(i.m_guard); + i.m_error = std::current_exception(); i.m_canRead.set_state(true); } } @@ -383,3 +390,205 @@ file::ptr fileCreateReadAhead(file::ptr chain, size_t readAheadBytes, abort_call file_v2::ptr temp = std::move(obj); return std::move(temp); } + + + +namespace { + class CFileWithMemBlock : public reader_membuffer_base { + public: + CFileWithMemBlock(fb2k::memBlockRef mem, t_filestats const& stats, const char* contentType, bool remote) { + m_mem = mem; + m_stats = stats; + m_stats.m_size = mem->size(); + if (contentType != nullptr) m_contentType = contentType; + m_remote = remote; + } + const void* get_buffer() { + return m_mem->get_ptr(); + } + t_size get_buffer_size() { + return m_mem->get_size(); + } + t_filestats get_stats(abort_callback& p_abort) { + p_abort.check(); + return m_stats; + } + bool get_content_type(pfc::string_base& out) { + if (m_contentType.is_empty()) return false; + out = m_contentType; + return true; + } + bool is_remote() { + return m_remote; + } + private: + bool m_remote; + fb2k::memBlockRef m_mem; + pfc::string8 m_contentType; + t_filestats m_stats; + }; +} + +file::ptr createFileWithMemBlock(fb2k::memBlock::ptr mem, t_filestats stats, const char* contentType, bool remote) { + return new service_impl_t< CFileWithMemBlock >(mem, stats, contentType, remote); +} + +file::ptr createFileLimited(file::ptr base, t_filesize offset, t_filesize size, abort_callback& abort) { + return reader_limited::g_create(base, offset, size, abort); +} + +file::ptr createFileBigMemMirror(file::ptr source, abort_callback& abort) { + if (source->is_in_memory()) return source; + auto r = fb2k::service_new(); + r->init(source, abort); + return r; +} + +file::ptr createFileMemMirror(file::ptr source, abort_callback& abort) { + file::ptr ret; + if (!reader_membuffer_mirror::g_create(ret, source, abort)) ret = source; + return ret; +} + +namespace { + class file_memMirrorAsync : public file_readonly_t< file_v2 > { + struct shared_t { + abort_callback_impl m_abort; + size_t m_size; + pfc::mutex m_sync; + file::ptr m_file; + + pfc::bigmem m_data; + size_t m_dataAvailable = 0; + + size_t m_triggerOffset = SIZE_MAX; + pfc::event m_trigger; + }; + shared_t m_shared; + + static void worker(shared_t& s) { + + constexpr size_t tempSize = 256 * 1024; + auto temp = std::make_unique(tempSize); + + while (s.m_dataAvailable < s.m_size) { + size_t got = s.m_file->receive(temp.get(), tempSize, s.m_abort); + if (got == 0) break; + + PFC_INSYNC(s.m_sync); + s.m_data.write(temp.get(), got, s.m_dataAvailable); + + auto before = s.m_dataAvailable; + s.m_dataAvailable += got; + if (before < s.m_triggerOffset && s.m_dataAvailable >= s.m_triggerOffset) { + s.m_trigger.set_state(true); + } + } + } + t_filestats2 m_stats; + bool m_is_remote = false; + pfc::string8 m_contentType; + service_ptr m_metadata; + size_t m_position = 0; + fb2k::thread m_thread; + public: + ~file_memMirrorAsync() { + m_shared.m_abort.set(); + m_thread.waitTillDone(); + } + void open(file::ptr chain, completion_notify::ptr onDone, abort_callback& a) { + if ( chain->get_position(a) > 0 ) chain->reopen(a); + m_stats = chain->get_stats2_(stats2_all, a); + if (m_stats.m_size > SIZE_MAX) throw pfc::exception_overflow(); + m_is_remote = chain->is_remote(); + m_contentType = chain->get_content_type(); + m_metadata = chain->get_metadata_(a); + m_shared.m_size = (size_t)m_stats.m_size; + m_shared.m_file = chain; + m_shared.m_data.resize(m_shared.m_size); + auto work = [this, onDone] { + try { + worker(this->m_shared); + } catch (...) {} + this->m_shared.m_file.release(); + if (onDone.is_valid()) onDone->on_completion(this->m_shared.m_size == this->m_shared.m_dataAvailable ? 1 : 0); + }; + m_thread.startHere(work); + } + + + service_ptr get_metadata(abort_callback& a) override { + a.check(); + return m_metadata; + } + + t_filestats2 get_stats2(uint32_t, abort_callback& a) override { + a.check(); + return m_stats; + } + + t_filesize get_position(abort_callback& p_abort) override { + p_abort.check(); + return m_position; + } + + void seek(t_filesize p_position, abort_callback& p_abort) override { + if (p_position > get_size(p_abort)) throw exception_io_seek_out_of_range(); + m_position = (size_t)p_position; + } + + bool can_seek() override { return true; } + + bool get_content_type(pfc::string_base& p_out) override { + bool rv = m_contentType.length() > 0; + if (rv) p_out = m_contentType; + return rv; + } + + void reopen(abort_callback& p_abort) override { seek(0, p_abort); } + + bool is_remote() override { return m_is_remote; } + + t_size read(void* p_buffer, t_size p_bytes, abort_callback& p_abort) override { + auto limit = get_size(p_abort); + auto left = limit - m_position; + if (p_bytes > left) p_bytes = (size_t)left; + + const auto upper = m_position + p_bytes; + + auto& shared = m_shared; + + { + PFC_INSYNC(shared.m_sync); + if (shared.m_dataAvailable >= upper) { + shared.m_data.read(p_buffer, p_bytes, m_position); + m_position += p_bytes; + return p_bytes; + } + shared.m_trigger.set_state(false); + shared.m_triggerOffset = upper; + } + p_abort.waitForEvent(shared.m_trigger); + + PFC_INSYNC(shared.m_sync); + PFC_ASSERT(shared.m_dataAvailable >= upper); + shared.m_data.read(p_buffer, p_bytes, m_position); + m_position += p_bytes; + return p_bytes; + } + t_filesize skip(t_filesize p_bytes, abort_callback& p_abort) override { + auto total = get_size(p_abort); + PFC_ASSERT(total >= m_position ); + auto left = total - m_position; + if (p_bytes > left) p_bytes = left; + m_position += (size_t)p_bytes; + return p_bytes; + } + }; +} + +file::ptr createFileMemMirrorAsync(file::ptr source, completion_notify::ptr onDone, abort_callback & a) { + auto ret = fb2k::service_new(); + ret->open(source, onDone, a); + return ret; +} diff --git a/sdk/foobar2000/helpers/readers.h b/sdk/foobar2000/helpers/readers.h index 246d00f..61f0ea0 100644 --- a/sdk/foobar2000/helpers/readers.h +++ b/sdk/foobar2000/helpers/readers.h @@ -161,12 +161,12 @@ class reader_limited : public file_readonly_t { if (positionWas == filesize_invalid || positionWas > position) { r->reopen(abort); try { r->skip_object(position, abort); } - catch (exception_io_data) { throw exception_io_seek_out_of_range(); } + catch (exception_io_data const &) { throw exception_io_seek_out_of_range(); } } else { t_filesize skipMe = position - positionWas; if (skipMe > 0) { try { r->skip_object(skipMe, abort); } - catch (exception_io_data) { throw exception_io_seek_out_of_range(); } + catch (exception_io_data const &) { throw exception_io_seek_out_of_range(); } } } } diff --git a/sdk/foobar2000/helpers/readers_lite.h b/sdk/foobar2000/helpers/readers_lite.h new file mode 100644 index 0000000..2db93e0 --- /dev/null +++ b/sdk/foobar2000/helpers/readers_lite.h @@ -0,0 +1,7 @@ +#pragma once + +file::ptr createFileWithMemBlock(fb2k::memBlock::ptr mem, t_filestats stats = filestats_invalid, const char* contentType = nullptr, bool remote = false); +file::ptr createFileLimited(file::ptr base, t_filesize offset, t_filesize size, abort_callback& abort); +file::ptr createFileBigMemMirror(file::ptr source, abort_callback& abort); +file::ptr createFileMemMirror(file::ptr source, abort_callback& abort); +file::ptr createFileMemMirrorAsync(file::ptr source, completion_notify::ptr optionalDoneReading, abort_callback & a); diff --git a/sdk/foobar2000/helpers/rethrow.h b/sdk/foobar2000/helpers/rethrow.h index 81a09bb..dde4124 100644 --- a/sdk/foobar2000/helpers/rethrow.h +++ b/sdk/foobar2000/helpers/rethrow.h @@ -2,14 +2,16 @@ #include +#include + namespace ThreadUtils { class CRethrow { private: - std::function m_rethrow; + std::exception_ptr m_exception; public: bool exec( std::function f ) throw(); void rethrow() const; - bool didFail() const { return !! m_rethrow; } - void clear() { m_rethrow = nullptr; } + bool didFail() const { return !!m_exception; } + void clear() { m_exception = nullptr; } }; } diff --git a/sdk/foobar2000/helpers/tag_write_callback_impl.h b/sdk/foobar2000/helpers/tag_write_callback_impl.h index b0723ea..58d143f 100644 --- a/sdk/foobar2000/helpers/tag_write_callback_impl.h +++ b/sdk/foobar2000/helpers/tag_write_callback_impl.h @@ -23,7 +23,7 @@ class tag_write_callback_impl : public tag_write_callback { service_ptr_t l_tempfile; try { openTempFile(l_tempfile, p_abort); - } catch(exception_io) {return false;} + } catch(exception_io const &) {return false;} p_out = m_tempfile = l_tempfile; return true; } @@ -38,7 +38,7 @@ class tag_write_callback_impl : public tag_write_callback { if (p_owner.is_valid()) { try { file::g_copy_creation_time(p_owner, m_tempfile, p_abort); - } catch (exception_io) {} + } catch (exception_io const &) {} } m_tempfile.release(); diff --git a/sdk/foobar2000/helpers/text_file_loader_v2.h b/sdk/foobar2000/helpers/text_file_loader_v2.h index a4ecb68..fe2e861 100644 --- a/sdk/foobar2000/helpers/text_file_loader_v2.h +++ b/sdk/foobar2000/helpers/text_file_loader_v2.h @@ -8,7 +8,7 @@ class text_file_loader_v2 { void load(file::ptr f, abort_callback & abort); - std::vector< const char * > m_lines; + std::vector< char * > m_lines; pfc::string8 m_data; }; \ No newline at end of file diff --git a/sdk/foobar2000/helpers/ui_element_helpers.cpp b/sdk/foobar2000/helpers/ui_element_helpers.cpp index 895e7f7..934e2a4 100644 --- a/sdk/foobar2000/helpers/ui_element_helpers.cpp +++ b/sdk/foobar2000/helpers/ui_element_helpers.cpp @@ -44,7 +44,7 @@ namespace ui_element_helpers { if (!find(ptr,cfg->get_guid())) return NULL; try { return ptr->enumerate_children(cfg); - } catch(exception_io_data) { + } catch(exception_io_data const &) { return NULL; } } @@ -120,7 +120,7 @@ void ui_element_helpers::ui_element_edit_tools::standard_edit_context_menu(LPARA } CMenu menu; - WIN32_OP( menu.CreatePopupMenu() ); + WIN32_OP_D( menu.CreatePopupMenu() ); const GUID sourceItemGuid = p_item->get_guid(); const bool sourceItemEmpty = !!(sourceItemGuid == pfc::guid_null); @@ -304,7 +304,7 @@ bool ui_element_helpers::recurse_for_elem_config(ui_element_config::ptr root, ui ui_element_children_enumerator::ptr children; try { children = elem->enumerate_children(root); - } catch(exception_io_data) {return false;} + } catch(exception_io_data const &) {return false;} if (children.is_empty()) return false; const t_size childrenTotal = children->get_count(); for(t_size walk = 0; walk < childrenTotal; ++walk) { @@ -367,7 +367,7 @@ void ui_element_instance_standard_context_menu(service_ptr_tedit_mode_context_menu_test(pt, fromKeyboard)) { const unsigned idBase = 1; CMenu menu; - WIN32_OP(menu.CreatePopupMenu()); + WIN32_OP_D(menu.CreatePopupMenu()); p_elem->edit_mode_context_menu_build(pt, fromKeyboard, menu, idBase); int cmd; diff --git a/sdk/foobar2000/helpers/win32_dialog.cpp b/sdk/foobar2000/helpers/win32_dialog.cpp index 2516388..ab811f3 100644 --- a/sdk/foobar2000/helpers/win32_dialog.cpp +++ b/sdk/foobar2000/helpers/win32_dialog.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "StdAfx.h" #ifdef FOOBAR2000_DESKTOP_WINDOWS diff --git a/sdk/foobar2000/helpers/win32_misc.cpp b/sdk/foobar2000/helpers/win32_misc.cpp index d2d24cf..e36170b 100644 --- a/sdk/foobar2000/helpers/win32_misc.cpp +++ b/sdk/foobar2000/helpers/win32_misc.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "StdAfx.h" #include "win32_misc.h" #ifdef FOOBAR2000_MOBILE_WINDOWS diff --git a/sdk/foobar2000/helpers/window_placement_helper.cpp b/sdk/foobar2000/helpers/window_placement_helper.cpp index 4559020..f516e5a 100644 --- a/sdk/foobar2000/helpers/window_placement_helper.cpp +++ b/sdk/foobar2000/helpers/window_placement_helper.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "StdAfx.h" #ifdef FOOBAR2000_DESKTOP_WINDOWS @@ -41,7 +41,7 @@ static bool test_rect(const RECT * rc) { } -bool cfg_window_placement_common::read_from_window(HWND window) +bool cfg_window_placement::read_from_window(HWND window) { WINDOWPLACEMENT wp = {}; if (g_is_enabled()) { @@ -63,35 +63,41 @@ bool cfg_window_placement_common::read_from_window(HWND window) }*/ } - set(wp); + try { set(wp); } catch(...) {} // this tends to be called often / we really couldn't care less about this failing return wp.length == sizeof(wp); } -bool cfg_window_placement_common::apply_to_window(HWND window, bool allowHidden) { +bool applyWindowPlacement(HWND window, WINDOWPLACEMENT const& data, bool allowHidden) { bool ret = false; - if (g_is_enabled()) + if (data.length == sizeof(data) && test_rect(&data.rcNormalPosition)) { - auto data = get(); - if (data.length == sizeof(data) && test_rect(&data.rcNormalPosition)) - { - if (allowHidden || data.showCmd != SW_HIDE) { - if (data.showCmd == SW_HIDE && (data.flags & WPF_RESTORETOMAXIMIZED)) { - // Special case of hidden-from-maximized - auto fix = data; - fix.showCmd = SW_SHOWMINIMIZED; - if (SetWindowPlacement(window, &fix)) { - ShowWindow(window, SW_HIDE); - ret = true; - } - } else { - if (SetWindowPlacement(window, &data)) { - ret = true; - } + if (allowHidden || data.showCmd != SW_HIDE) { + if (data.showCmd == SW_HIDE && (data.flags & WPF_RESTORETOMAXIMIZED)) { + // Special case of hidden-from-maximized + auto fix = data; + fix.showCmd = SW_SHOWMINIMIZED; + if (SetWindowPlacement(window, &fix)) { + ShowWindow(window, SW_HIDE); + ret = true; + } + } else { + if (SetWindowPlacement(window, &data)) { + ret = true; } } } } + return ret; +} + +bool cfg_window_placement::apply_to_window(HWND window, bool allowHidden) { + bool ret = false; + if (g_is_enabled()) + { + auto data = get(); + ret = applyWindowPlacement(window, data, allowHidden); + } return ret; } @@ -117,8 +123,7 @@ static BOOL SetWindowSize(HWND p_wnd,unsigned p_x,unsigned p_y) return FALSE; } -bool cfg_window_size::on_window_creation(HWND p_wnd) -{ +bool cfg_window_size::apply_to_window(HWND p_wnd) { bool ret = false; if (g_is_enabled()) @@ -127,11 +132,14 @@ bool cfg_window_size::on_window_creation(HWND p_wnd) if (v.cx > 0 && v.cy > 0) { if (SetWindowSize(p_wnd, v.cx, v.cy)) ret = true; } - - } + } return ret; } +bool cfg_window_size::on_window_creation(HWND p_wnd) +{ + return apply_to_window(p_wnd); +} void cfg_window_size::on_window_destruction(HWND p_wnd) { diff --git a/sdk/foobar2000/helpers/window_placement_helper.h b/sdk/foobar2000/helpers/window_placement_helper.h index 6f8dd2b..20f0369 100644 --- a/sdk/foobar2000/helpers/window_placement_helper.h +++ b/sdk/foobar2000/helpers/window_placement_helper.h @@ -1,39 +1,45 @@ #pragma once +// DEPRECATED, NOT DPI SAFE +// use cfgDialogPosition & cfgWindowSize2 instead #ifdef FOOBAR2000_DESKTOP_WINDOWS #include "../SDK/cfg_var.h" -class cfg_window_placement_common : public cfg_struct_t { +//! Window position management helpers +//! Usage: create a static instance, like with any cfg_var; access it on creation/reposition/destruction of your window. +class cfg_window_placement : public cfg_struct_t { public: - cfg_window_placement_common(const GUID& guid) : cfg_struct_t(guid) {} + cfg_window_placement(const GUID& guid) : cfg_struct_t(guid) {} + //! Read and save position data from HWND. bool read_from_window(HWND window); + //! Apply saved position data to HWND. bool apply_to_window(HWND window, bool allowHidden); -}; -class cfg_window_placement : public cfg_window_placement_common -{ -public: + // OLD methods tracking only destroy/create. + // Use of read_from_window() / apply_to_window() instead is preferred, so changes can be saved immediately. bool on_window_creation(HWND window, bool allowHidden = false);//returns true if window position has been changed, false if not void on_window_creation_silent(HWND window); void on_window_destruction(HWND window); - cfg_window_placement(const GUID& guid) : cfg_window_placement_common(guid) {} -}; - -class cfg_window_placement_v2 : public cfg_window_placement_common { -public: - cfg_window_placement_v2(const GUID& guid) : cfg_window_placement_common(guid) {} - // All already in cfg_window_placement_common }; +// At one point there was a separate cfg_window_placement_v2 without legacy methods +typedef cfg_window_placement cfg_window_placement_v2; - +//! Window size tracker \n +//! Usage: create a static instance, like with any cfg_var; access it on creation/reposition/destruction of your window. class cfg_window_size : public cfg_struct_t { public: + cfg_window_size(const GUID& id) : cfg_struct_t(id) {} + //! Read and save size data from HWND. + bool read_from_window(HWND window); + //! Apply saved size data to HWND. + bool apply_to_window(HWND); + + // OLD methods tracking only destroy/create. + // Use of read_from_window() / apply_to_window() instead is preferred, so changes can be saved immediately. bool on_window_creation(HWND window);//returns true if window position has been changed, false if not void on_window_destruction(HWND window); - bool read_from_window(HWND window); - cfg_window_size(const GUID& id) : cfg_struct_t(id) {} }; #endif // FOOBAR2000_DESKTOP_WINDOWS diff --git a/sdk/foobar2000/helpers/writer_wav.cpp b/sdk/foobar2000/helpers/writer_wav.cpp index c1ee01f..766c0db 100644 --- a/sdk/foobar2000/helpers/writer_wav.cpp +++ b/sdk/foobar2000/helpers/writer_wav.cpp @@ -14,6 +14,7 @@ static const GUID guid_RIFF = pfc::GUID_from_text("66666972-912E-11CF-A5D6-28DB0 static const GUID guid_WAVE = pfc::GUID_from_text("65766177-ACF3-11D3-8CD1-00C04F8EDB8A"); static const GUID guid_FMT = pfc::GUID_from_text("20746D66-ACF3-11D3-8CD1-00C04F8EDB8A"); static const GUID guid_DATA = pfc::GUID_from_text("61746164-ACF3-11D3-8CD1-00C04F8EDB8A"); +static const GUID guid_JUNK = pfc::GUID_from_text("6b6E756A-ACF3-11D3-8CD1-00C04f8EDB8A"); struct RIFF_chunk_desc { GUID m_guid; @@ -25,6 +26,7 @@ static const RIFF_chunk_desc RIFF_chunks[] = { {guid_WAVE, "WAVE"}, {guid_FMT , "fmt "}, {guid_DATA, "data"}, + {guid_JUNK, "JUNK"}, }; bool wavWriterSetup_t::needWFXE() const { @@ -56,17 +58,13 @@ void wavWriterSetup_t::initialize3(const audio_chunk::spec_t & spec, unsigned bp m_float = bFloat; m_dither = bDither; m_wave64 = bWave64; + + m_rf64_explicit = false; + m_rf64_implicit = false; } void wavWriterSetup_t::initialize2(const audio_chunk & p_chunk, unsigned p_bps, unsigned p_bpsValid, bool p_float, bool p_dither, bool p_wave64) { - m_bps = p_bps; - m_bpsValid = p_bpsValid; - m_samplerate = p_chunk.get_srate(); - m_channels = p_chunk.get_channels(); - m_channel_mask = p_chunk.get_channel_config(); - m_float = p_float; - m_dither = p_dither; - m_wave64 = p_wave64; + initialize3(p_chunk.get_spec(), p_bps, p_bpsValid, p_float, p_dither, p_wave64); } void wavWriterSetup_t::initialize(const audio_chunk & p_chunk, unsigned p_bps, bool p_float, bool p_dither, bool p_wave64) @@ -102,9 +100,9 @@ void CWavWriter::writeID(const GUID & id, abort_callback & abort) { if (is64()) { m_file->write_object_t(id, abort); } else { - for(t_size walk = 0; walk < PFC_TABSIZE(RIFF_chunks); ++walk) { - if (id == RIFF_chunks[walk].m_guid) { - m_file->write(RIFF_chunks[walk].m_name, 4, abort); return; + for( auto & walk : RIFF_chunks) { + if (id == walk.m_guid) { + m_file->write(walk.m_name, 4, abort); return; } } uBugCheck(); @@ -113,11 +111,11 @@ void CWavWriter::writeID(const GUID & id, abort_callback & abort) { void CWavWriter::writeSize(t_uint64 size, abort_callback & abort) { if (is64()) { - if (size != ~0) size += 24; + if (size != UINT64_MAX) size += 24; m_file->write_lendian_t(size, abort); } else { t_uint32 clipped; - if (size > 0xFFFFFFFF) clipped = 0xFFFFFFFF; + if (size > UINT32_MAX) clipped = UINT32_MAX; else clipped = (t_uint32) size; m_file->write_lendian_t(clipped, abort); } @@ -166,23 +164,40 @@ void CWavWriter::open(service_ptr_t p_file, const wavWriterSetup_t & p_set m_wfxe = m_setup.needWFXE(); - writeID(guid_RIFF, p_abort); + if (m_setup.m_wave64) { + m_file->write_object_t(guid_RIFF, p_abort); + } else if (m_setup.m_rf64_explicit) { + m_file->write("RF64", 4, p_abort); + } else { + m_file->write("RIFF", 4, p_abort); + } + m_offset_fix1 = m_file->get_position(p_abort); - writeSize(~0, p_abort); + writeSize(UINT64_MAX, p_abort); writeID(guid_WAVE, p_abort); - + + if (!is64() && m_file->can_seek() && (m_setup.m_rf64_explicit || m_setup.m_rf64_implicit)) { + // write JUNK placeholder for DS64 + m_ds64_at = m_file->get_position(p_abort); + static const uint8_t dummy[28] = {}; // riffsize64, datasize64, samplecount64, tablecount32 + writeID(guid_JUNK, p_abort); + writeSize(sizeof(dummy), p_abort); + m_file->write_object(dummy, sizeof(dummy), p_abort); + } + + writeID(guid_FMT, p_abort); if (m_wfxe) { writeSize(sizeof(WAVEFORMATEXTENSIBLE),p_abort); - WAVEFORMATEXTENSIBLE wfxe; + WAVEFORMATEXTENSIBLE wfxe = {}; m_setup.setup_wfxe(wfxe); m_file->write_object(&wfxe,sizeof(wfxe),p_abort); } else { writeSize(sizeof(PCMWAVEFORMAT),p_abort); - WAVEFORMATEX wfx; + WAVEFORMATEX wfx = {}; m_setup.setup_wfx(wfx); m_file->write_object(&wfx,/* blah */ sizeof(PCMWAVEFORMAT),p_abort); } @@ -193,7 +208,6 @@ void CWavWriter::open(service_ptr_t p_file, const wavWriterSetup_t & p_set writeSize(UINT64_MAX, p_abort); m_offset_fix1_delta = m_file->get_position(p_abort) - chunkOverhead(); - m_bytes_written = 0; if (!m_setup.m_float) @@ -236,9 +250,30 @@ void CWavWriter::finalize(abort_callback & p_abort) if (m_file->can_seek()) { m_file->seek(m_offset_fix1,p_abort); - writeSize(m_bytes_written + alignG + m_offset_fix1_delta, p_abort); + const uint64_t riffSize = m_bytes_written + alignG + m_offset_fix1_delta; + writeSize(riffSize, p_abort); m_file->seek(m_offset_fix2,p_abort); writeSize(m_bytes_written, p_abort); + + if (!is64() && (m_setup.m_rf64_explicit || (m_setup.m_rf64_implicit && m_bytes_written > UINT32_MAX))) { + if (!m_setup.m_rf64_explicit) { // turn RIFF into RF64? + m_file->seek(0, p_abort); + m_file->write("RF64", 4, p_abort); + } + m_file->seek(m_ds64_at, p_abort); + m_file->write("ds64", 4, p_abort); + writeSize(28, p_abort); + + // RIFF size, data size, sample count + m_file->write_lendian_t(riffSize, p_abort); + m_file->write_lendian_t(m_bytes_written, p_abort); + + unsigned sampleBytes = (m_setup.m_bps + 7) / 8 * m_setup.m_channels; + uint64_t samples = m_bytes_written / sampleBytes; + m_file->write_lendian_t(samples, p_abort); + uint32_t tableCount = 0; + m_file->write_lendian_t(tableCount, p_abort); + } } m_file.release(); } @@ -262,7 +297,7 @@ audio_chunk::spec_t CWavWriter::get_spec() const { namespace { class fileWav : public foobar2000_io::file { public: - size_t read( void * buffer, size_t bytes, abort_callback & aborter ) { + size_t read( void * buffer, size_t bytes, abort_callback & aborter ) override { aborter.check(); uint8_t * out = (uint8_t*) buffer; size_t ret = 0; @@ -281,10 +316,9 @@ class fileWav : public foobar2000_io::file { } return ret; } - void write( const void * buffer, size_t bytes, abort_callback & aborter ) { + void write( const void * buffer, size_t bytes, abort_callback & aborter ) override { throw exception_io_denied(); } - // old fb2k SDK workaround fileWav( std::vector const & header, file::ptr data) { m_data = data; m_position = 0; @@ -295,34 +329,25 @@ class fileWav : public foobar2000_io::file { m_position = 0; m_header = std::move(header); } - t_filesize get_size(abort_callback & p_abort) { + t_filesize get_size(abort_callback & p_abort) override { t_filesize s = m_data->get_size( p_abort ); if (s != filesize_invalid) s += m_header.size(); return s; } - t_filesize get_position(abort_callback & p_abort) { + t_filesize get_position(abort_callback & p_abort) override { return m_position; } - void resize(t_filesize p_size,abort_callback & p_abort) { + void resize(t_filesize p_size,abort_callback & p_abort) override { throw exception_io_denied(); } - void seek(t_filesize p_position,abort_callback & p_abort) { + void seek(t_filesize p_position,abort_callback & p_abort) override { if (p_position > get_size(p_abort)) throw exception_io_seek_out_of_range(); m_position = p_position; } - bool can_seek() { - return true; - } - bool get_content_type(pfc::string_base & p_out) { return false; } - void reopen(abort_callback & p_abort) { seek(0, p_abort); } - bool is_remote() { - return m_data->is_remote(); - } - t_filestats get_stats(abort_callback & p_abort) { - t_filestats s = m_data->get_stats( p_abort ); - if (s.m_size != filesize_invalid) s.m_size += m_header.size(); - return s; - } + bool can_seek() override {return true; } + bool get_content_type(pfc::string_base & p_out) override { return false; } + void reopen(abort_callback & p_abort) override { seek(0, p_abort); } + bool is_remote() override { return m_data->is_remote(); } private: std::vector m_header; t_filesize m_position; diff --git a/sdk/foobar2000/helpers/writer_wav.h b/sdk/foobar2000/helpers/writer_wav.h index 6907dc8..bc42aa3 100644 --- a/sdk/foobar2000/helpers/writer_wav.h +++ b/sdk/foobar2000/helpers/writer_wav.h @@ -10,6 +10,7 @@ struct wavWriterSetup_t { unsigned m_bps,m_bpsValid,m_samplerate,m_channels,m_channel_mask; bool m_float,m_dither, m_wave64; + bool m_rf64_implicit, m_rf64_explicit; void initialize(const audio_chunk & p_chunk,unsigned p_bps,bool p_float,bool p_dither, bool p_wave64 = false); @@ -41,13 +42,13 @@ class CWavWriter void writeSize(t_uint64 size, abort_callback & abort); bool is64() const {return m_setup.m_wave64;} t_uint32 chunkOverhead() const {return is64() ? 24 : 8;} - t_uint32 idOverhead() const {return is64() ? 16 : 4;} void writeID(const GUID & id, abort_callback & abort); service_ptr_t m_file; service_ptr_t m_postprocessor; wavWriterSetup_t m_setup; - bool m_wfxe; - t_uint64 m_offset_fix1,m_offset_fix2,m_offset_fix1_delta,m_bytes_written; + bool m_wfxe = false; + t_uint64 m_offset_fix1 = 0,m_offset_fix2 = 0,m_offset_fix1_delta = 0,m_bytes_written = 0; + uint64_t m_ds64_at = 0; mem_block_container_aligned_incremental_impl<16> m_postprocessor_output; }; diff --git a/sdk/foobar2000/shared/Utility.cpp b/sdk/foobar2000/shared/Utility.cpp new file mode 100644 index 0000000..14999f8 --- /dev/null +++ b/sdk/foobar2000/shared/Utility.cpp @@ -0,0 +1,131 @@ +#include "shared.h" + +#include + +static std::once_flag g_infinitWaitInit; +static HANDLE g_infinitWaitEvent = NULL; + +HANDLE SHARED_EXPORT GetInfiniteWaitEvent() { + std::call_once(g_infinitWaitInit, [] { + g_infinitWaitEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + } ); + return g_infinitWaitEvent; +} + +struct createFileRequest { + TCHAR * lpFileName; + DWORD dwDesiredAccess; + DWORD dwShareMode; + LPSECURITY_ATTRIBUTES lpSecurityAttributes; + DWORD dwCreationDisposition; + DWORD dwFlagsAndAttributes; + + HANDLE hResultHandle; + + DWORD dwErrorCode; + + volatile LONG refCounter; + + void Delete() { + free(lpFileName); + if (hResultHandle != INVALID_HANDLE_VALUE) CloseHandle(hResultHandle); + delete this; + } + + void Release() { + if (InterlockedDecrement(&refCounter) == 0) { + Delete(); + } + } +}; + +static unsigned CALLBACK createFileThread(void* p) { + createFileRequest * req = reinterpret_cast(p); + + SetLastError(0); + req->hResultHandle = CreateFile(req->lpFileName, req->dwDesiredAccess, req->dwShareMode, req->lpSecurityAttributes, req->dwCreationDisposition, req->dwFlagsAndAttributes, NULL); + req->dwErrorCode = GetLastError(); + + req->Release(); + return 0; +} + + +HANDLE SHARED_EXPORT CreateFileAbortable( __in LPCWSTR lpFileName, + __in DWORD dwDesiredAccess, + __in DWORD dwShareMode, + __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, + __in DWORD dwCreationDisposition, + __in DWORD dwFlagsAndAttributes, + __in_opt HANDLE hAborter + ) { + if (hAborter == NULL || hAborter == GetInfiniteWaitEvent()) { + return CreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, NULL); + } + switch(WaitForSingleObject(hAborter, 0)) { + case WAIT_TIMEOUT: + break; + case WAIT_OBJECT_0: + SetLastError(ERROR_OPERATION_ABORTED); + return INVALID_HANDLE_VALUE; + default: + return INVALID_HANDLE_VALUE; + } + + createFileRequest * req = new createFileRequest(); + req->lpFileName = _tcsdup(lpFileName); + req->dwDesiredAccess = dwDesiredAccess; + req->dwShareMode = dwShareMode; + req->lpSecurityAttributes = lpSecurityAttributes; + req->dwCreationDisposition = dwCreationDisposition; + req->dwFlagsAndAttributes = dwFlagsAndAttributes; + req->hResultHandle = INVALID_HANDLE_VALUE; + req->dwErrorCode = 0; + req->refCounter = 2; + + HANDLE hThread = (HANDLE) _beginthreadex(NULL, 0, createFileThread, req, CREATE_SUSPENDED, NULL); + if (hThread == NULL) { + req->Delete(); + return INVALID_HANDLE_VALUE; + } + SetThreadPriority(hThread, GetThreadPriority(GetCurrentThread())); + ResumeThread(hThread); + + DWORD waitStatus; + { + HANDLE waits[2] = {hThread, hAborter}; + waitStatus = WaitForMultipleObjects(2, waits, FALSE, INFINITE); + } + HANDLE hRetVal = INVALID_HANDLE_VALUE; + DWORD dwErrorCode = 0; + switch(waitStatus) { + case WAIT_OBJECT_0: // thread completed + hRetVal = req->hResultHandle; + req->hResultHandle = INVALID_HANDLE_VALUE; + dwErrorCode = req->dwErrorCode; + break; + case WAIT_OBJECT_0 + 1: // aborted + dwErrorCode = ERROR_OPERATION_ABORTED; + CancelSynchronousIo(hThread); + break; + default: // unexpected, use last-error code from WFMO + dwErrorCode = GetLastError(); + CancelSynchronousIo(hThread); + break; + } + req->Release(); + CloseHandle(hThread); + SetLastError(dwErrorCode); + return hRetVal; +} + +namespace pfc { + BOOL winFormatSystemErrorMessageImpl(pfc::string_base & p_out, DWORD p_code); + BOOL winFormatSystemErrorMessageHook(pfc::string_base & p_out, DWORD p_code) { + return winFormatSystemErrorMessageImpl(p_out, p_code); + } + + void crashHook() { + uBugCheck(); + } +} diff --git a/sdk/foobar2000/shared/audio_math.cpp b/sdk/foobar2000/shared/audio_math.cpp new file mode 100644 index 0000000..a9ad99a --- /dev/null +++ b/sdk/foobar2000/shared/audio_math.cpp @@ -0,0 +1,137 @@ +#include "shared.h" + +//#define AUDIO_MATH_NOASM + +// NOTE: SSE4.1 int16 ops code determined MUCH SLOWER than SSE2 on Sandy Bridge era Xeon and therefore disabled for now +// #define SUPPORT_SSE41 + +#ifdef SUPPORT_SSE41 +static const bool g_have_sse41 = pfc::query_cpu_feature_set(pfc::CPU_HAVE_SSE41); + +inline static void convert_from_int16_noopt(const t_int16 * p_source,t_size p_count,audio_sample * p_output,float p_scale) +{ + t_size num = p_count; + for(;num;num--) + *(p_output++) = (audio_sample)*(p_source++) * p_scale; +} + +__declspec(naked) static void __fastcall convert_from_int16_sse41_8word(const t_int16 * p_source,t_size p_count,audio_sample * p_output,float p_scale) { + __asm { + // ecx = source, edx = count, [esp + 4] = output, [esp + 8] = scale + movss xmm7, [esp + 8] + test edx, edx + mov eax, [esp + 4] + pshufd xmm7, xmm7, 0 + jz loopend +loopbegin: + PMOVSXWD xmm0, mmword ptr [ecx] + PMOVSXWD xmm1, mmword ptr [ecx+8] + CVTDQ2PS xmm0, xmm0 + CVTDQ2PS xmm1, xmm1 + add ecx, 16 + mulps xmm0, xmm7 + mulps xmm1, xmm7 + dec edx + movups [eax], xmm0 + movups [eax + 16], xmm1 + lea eax, [eax + 32] + jnz loopbegin +loopend: + ret 8 + } +} + + +__declspec(naked) static void __fastcall convert_from_int16_sse41_8word_aligned(const t_int16 * p_source,t_size p_count,audio_sample * p_output,float p_scale) { + __asm { + // ecx = source, edx = count, [esp + 4] = output, [esp + 8] = scale + movss xmm7, [esp + 8] + test edx, edx + mov eax, [esp + 4] + pshufd xmm7, xmm7, 0 + jz loopend +loopbegin: + PMOVSXWD xmm0, mmword ptr [ecx] + PMOVSXWD xmm1, mmword ptr [ecx+8] + CVTDQ2PS xmm0, xmm0 + CVTDQ2PS xmm1, xmm1 + add ecx, 16 + mulps xmm0, xmm7 + mulps xmm1, xmm7 + dec edx + movaps [eax], xmm0 + movaps [eax + 16], xmm1 + lea eax, [eax + 32] + jnz loopbegin +loopend: + ret 8 + } +} +#endif + + + +#ifdef audio_math +#undef audio_math +#endif +namespace audio_math { + + void SHARED_EXPORT scale(const audio_sample * p_source,t_size p_count,audio_sample * p_output,audio_sample p_scale) + { + ::pfc::audio_math::scale(p_source, p_count, p_output, p_scale); + } + + void SHARED_EXPORT convert_to_int16(const audio_sample * p_source,t_size p_count,t_int16 * p_output,audio_sample p_scale) + { + ::pfc::audio_math::convert_to_int16(p_source, p_count, p_output, p_scale); + } + + audio_sample SHARED_EXPORT convert_to_int16_calculate_peak(const audio_sample * p_source,t_size p_count,t_int16 * p_output,audio_sample p_scale) + { + convert_to_int16(p_source,p_count,p_output,p_scale); + return p_scale * calculate_peak(p_source,p_count); + } + + void SHARED_EXPORT convert_from_int16(const t_int16 * p_source,t_size p_count,audio_sample * p_output,audio_sample p_scale) + { +#ifdef SUPPORT_SSE41 + if (g_have_sse41) { + audio_sample scale = (audio_sample)(p_scale / (double)0x8000); + convert_from_int16_sse41_8word(p_source, p_count >> 3, p_output, scale); + convert_from_int16_noopt(p_source + (p_count & ~7), p_count & 7, p_output + (p_count & ~7), scale); + return; + } +#endif + ::pfc::audio_math::convert_from_int16(p_source, p_count, p_output, p_scale); + } + + void SHARED_EXPORT convert_to_int32(const audio_sample * p_source,t_size p_count,t_int32 * p_output,audio_sample p_scale) + { + return ::pfc::audio_math::convert_to_int32(p_source, p_count, p_output, p_scale); + } + + audio_sample SHARED_EXPORT convert_to_int32_calculate_peak(const audio_sample * p_source,t_size p_count,t_int32 * p_output,audio_sample p_scale) + { + convert_to_int32(p_source,p_count,p_output,p_scale); + return p_scale * calculate_peak(p_source,p_count); + } + + void SHARED_EXPORT convert_from_int32(const t_int32 * p_source,t_size p_count,audio_sample * p_output,audio_sample p_scale) + { + ::pfc::audio_math::convert_from_int32(p_source, p_count, p_output, p_scale); + } + + + audio_sample SHARED_EXPORT calculate_peak(const audio_sample * p_source,t_size p_count) { + return ::pfc::audio_math::calculate_peak(p_source, p_count); + } + + void SHARED_EXPORT kill_denormal(audio_sample * p_buffer,t_size p_count) { + ::pfc::audio_math::remove_denormals(p_buffer, p_count); + } + + void SHARED_EXPORT add_offset(audio_sample * p_buffer,audio_sample p_delta,t_size p_count) { + ::pfc::audio_math::add_offset(p_buffer, p_delta, p_count); + } + +} diff --git a/sdk/foobar2000/shared/crash_info.cpp b/sdk/foobar2000/shared/crash_info.cpp new file mode 100644 index 0000000..70b1ae9 --- /dev/null +++ b/sdk/foobar2000/shared/crash_info.cpp @@ -0,0 +1,652 @@ +#include "shared.h" +#include +#include + +#if SIZE_MAX == UINT32_MAX +#define STACKSPEC "%08X" +#define PTRSPEC "%08Xh" +#define OFFSETSPEC "%Xh" +#elif SIZE_MAX == UINT64_MAX +#define STACKSPEC "%016llX" +#define PTRSPEC "%016llXh" +#define OFFSETSPEC "%llXh" +#else +#error WTF? +#endif + + +static volatile bool g_didSuppress = false; +static critical_section g_panicHandlersSync; +static std::forward_list g_panicHandlers; + +static void callPanicHandlers() { + insync(g_panicHandlersSync); + for( auto i = g_panicHandlers.begin(); i != g_panicHandlers.end(); ++ i ) { + try { + (*i)->onPanic(); + } catch(...) {} + } +} + +void SHARED_EXPORT uAddPanicHandler(fb2k::panicHandler* p) { + insync(g_panicHandlersSync); + g_panicHandlers.push_front(p); +} + +void SHARED_EXPORT uRemovePanicHandler(fb2k::panicHandler* p) { + insync(g_panicHandlersSync); + g_panicHandlers.remove(p); +} + + +enum { EXCEPTION_BUG_CHECK = 0xaa67913c }; + +#if FB2K_SUPPORT_CRASH_LOGS + +static const unsigned char utf8_header[3] = {0xEF,0xBB,0xBF}; + +static __declspec(thread) char g_thread_call_stack[1024]; +static __declspec(thread) t_size g_thread_call_stack_length; + +static critical_section g_lastEventsSync; +static pfc::chain_list_v2_t g_lastEvents; +static constexpr t_size KLastEventCount = 200; + +static pfc::string8 version_string; + +static pfc::array_t DumpPathBuffer; + +static long crash_no = 0; + +// Debug timer: GetTickCount() diff since app startup. +// Intentionally kept as dumb as possible (originally meant to format system time nicely). +// Do not want to do expensive preformat of every event that in >99% of scenarios isn't logged. +// Cannot do formatting & system calls in crash handler. +// So we just write amount of MS since app startup. +static const uint64_t debugTimerInit = GetTickCount64(); +static pfc::format_int_t queryDebugTimer() { return pfc::format_int( GetTickCount64() - debugTimerInit ); } + +static pfc::string8 g_components; + +static void WriteFileString_internal(HANDLE hFile,const char * ptr,t_size len) +{ + DWORD bah; + WriteFile(hFile,ptr,(DWORD)len,&bah,0); +} + +static HANDLE create_failure_log() +{ + bool rv = false; + if (DumpPathBuffer.get_size() == 0) return INVALID_HANDLE_VALUE; + + TCHAR * path = DumpPathBuffer.get_ptr(); + + t_size lenWalk = _tcslen(path); + + if (lenWalk == 0 || path[lenWalk-1] != '\\') {path[lenWalk++] = '\\';} + + _tcscpy(path + lenWalk, _T("crash reports")); + lenWalk += _tcslen(path + lenWalk); + + SetLastError(NO_ERROR); + if (!CreateDirectory(path, NULL)) { + if (GetLastError() != ERROR_ALREADY_EXISTS) return INVALID_HANDLE_VALUE; + } + path[lenWalk++] = '\\'; + + TCHAR * fn_out = path + lenWalk; + HANDLE hFile = INVALID_HANDLE_VALUE; + unsigned attempts = 0; + for(;;) { + wsprintf(fn_out,TEXT("failure_%08u.txt"),++attempts); + hFile = CreateFile(path,GENERIC_WRITE,0,0,CREATE_NEW,0,0); + if (hFile!=INVALID_HANDLE_VALUE) break; + if (attempts > 1000) break; + } + if (hFile!=INVALID_HANDLE_VALUE) WriteFileString_internal(hFile, (const char*)utf8_header, sizeof(utf8_header)); + return hFile; +} + + +static void WriteFileString(HANDLE hFile,const char * str) +{ + const char * ptr = str; + for(;;) + { + const char * start = ptr; + ptr = strchr(ptr,'\n'); + if (ptr) + { + if (ptr>start) { + t_size len = ptr-start; + if (ptr[-1] == '\r') --len; + WriteFileString_internal(hFile,start,len); + } + WriteFileString_internal(hFile,"\r\n",2); + ptr++; + } + else + { + WriteFileString_internal(hFile,start,strlen(start)); + break; + } + } +} + +static void WriteEvent(HANDLE hFile,const char * str) { + bool haveText = false; + const char * ptr = str; + bool isLineBreak = false; + while(*ptr) { + const char * base = ptr; + while(*ptr && *ptr != '\r' && *ptr != '\n') ++ptr; + if (ptr > base) { + if (isLineBreak) WriteFileString_internal(hFile,"\r\n",2); + WriteFileString_internal(hFile,base,ptr-base); + isLineBreak = false; haveText = true; + } + for(;;) { + if (*ptr == '\n') isLineBreak = haveText; + else if (*ptr != '\r') break; + ++ptr; + } + } + if (haveText) WriteFileString_internal(hFile,"\r\n",2); +} + +static bool read_int(t_size src, t_size* out) +{ + __try + { + *out = *(t_size*)src; + return true; + } __except (1) { return false; } +} + +static bool hexdump8(char * out,size_t address,const char * msg,int from,int to) +{ + unsigned max = (to-from)*16; + if (IsBadReadPtr((const void*)(address+(from*16)),max)) return false; + out += sprintf(out,"\n%s (" PTRSPEC "):",msg,address); + unsigned n; + const unsigned char * src = (const unsigned char*)(address)+(from*16); + + for(n=0;n(rptr); + t_size walk = 0; + for (; walk < 255; ++walk) { + char c = ptr[walk]; + if (c == 0) break; + if (c < 0x20) c = ' '; + temp[walk] = c; + } + temp[walk] = 0; + return true; + } __except (1) { return false; } +} + +static void DumpCPPExceptionData(HANDLE hFile, const ULONG_PTR* params, char* temp) { + t_size strPtr; + if (read_int(params[1] + sizeof(void*), &strPtr)) { + if (SalvageString(strPtr, temp)) { + WriteFileString(hFile, "Message: "); + WriteFileString(hFile, temp); + WriteFileString(hFile, "\n"); + } + } +} + +static void writeFailureTxt(LPEXCEPTION_POINTERS param, HANDLE hFile, DWORD lastError) { + char temp[2048]; + { + t_size address = (t_size)param->ExceptionRecord->ExceptionAddress; + sprintf(temp, "Illegal operation:\nCode: %08Xh, flags: %08Xh, address: " PTRSPEC "\n", param->ExceptionRecord->ExceptionCode, param->ExceptionRecord->ExceptionFlags, address); + WriteFileString(hFile, temp); + + if (param->ExceptionRecord->ExceptionCode == EXCEPTION_BUG_CHECK) { + WriteFileString(hFile, "Bug check\n"); + } else if (param->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && param->ExceptionRecord->NumberParameters >= 2) { + sprintf(temp, "Access violation, operation: %s, address: " PTRSPEC "\n", param->ExceptionRecord->ExceptionInformation[0] ? "write" : "read", param->ExceptionRecord->ExceptionInformation[1]); + WriteFileString(hFile, temp); + } else if (param->ExceptionRecord->NumberParameters > 0) { + WriteFileString(hFile, "Additional parameters:"); + for (DWORD walk = 0; walk < param->ExceptionRecord->NumberParameters; ++walk) { + sprintf(temp, " " PTRSPEC, param->ExceptionRecord->ExceptionInformation[walk]); + WriteFileString(hFile, temp); + } + WriteFileString(hFile, "\n"); + } + + if (param->ExceptionRecord->ExceptionCode == 0xE06D7363 && param->ExceptionRecord->NumberParameters >= 3) { //C++ exception + DumpCPPExceptionData(hFile, param->ExceptionRecord->ExceptionInformation, temp); + } + + if (lastError) { + sprintf(temp, "Last win32 error: %u\n", lastError); + WriteFileString(hFile, temp); + } + + if (g_thread_call_stack[0] != 0) { + WriteFileString(hFile, "\nCall path:\n"); + WriteFileString(hFile, g_thread_call_stack); + WriteFileString(hFile, "\n"); + } else { + WriteFileString(hFile, "\nCall path not available.\n"); + } + + + if (hexdump8(temp, address, "Code bytes", -4, +4)) WriteFileString(hFile, temp); +#ifdef _M_IX86 + if (hexdump_stack(temp, param->ContextRecord->Esp, "Stack", -2, +18)) WriteFileString(hFile, temp); + sprintf(temp, "\nRegisters:\nEAX: %08X, EBX: %08X, ECX: %08X, EDX: %08X\nESI: %08X, EDI: %08X, EBP: %08X, ESP: %08X\n", param->ContextRecord->Eax, param->ContextRecord->Ebx, param->ContextRecord->Ecx, param->ContextRecord->Edx, param->ContextRecord->Esi, param->ContextRecord->Edi, param->ContextRecord->Ebp, param->ContextRecord->Esp); + WriteFileString(hFile, temp); +#endif + +#ifdef _M_X64 + if (hexdump_stack(temp, param->ContextRecord->Rsp, "Stack", -2, +18)) WriteFileString(hFile, temp); + sprintf(temp, "\nRegisters:\nRAX: %016llX, RBX: %016llX, RCX: %016llX, RDX: %016llX\nRSI: %016llX, RDI: %016llX, RBP: %016llX, RSP: %016llX\n", param->ContextRecord->Rax, param->ContextRecord->Rbx, param->ContextRecord->Rcx, param->ContextRecord->Rdx, param->ContextRecord->Rsi, param->ContextRecord->Rdi, param->ContextRecord->Rbp, param->ContextRecord->Rsp); + WriteFileString(hFile, temp); +#endif + +#ifdef _M_ARM64 + if (hexdump_stack(temp, param->ContextRecord->Sp, "Stack", -2, +18)) WriteFileString(hFile, temp); +#endif + + WriteFileString(hFile, "\nTimestamp:\n"); + WriteFileString(hFile, queryDebugTimer() ); + WriteFileString(hFile, "ms\n"); + + { + const HANDLE hProcess = GetCurrentProcess(); + if (SymInitialize(hProcess, NULL, TRUE)) + { + { + IMAGEHLP_MODULE mod = {}; + mod.SizeOfStruct = sizeof(mod); + if (!IsBadCodePtr((FARPROC)address) && SymGetModuleInfo(hProcess, address, &mod)) + { + sprintf(temp, "\nCrash location:\nModule: %s\nOffset: " OFFSETSPEC "\n", mod.ModuleName, address - mod.BaseOfImage); + WriteFileString(hFile, temp); + } else + { + sprintf(temp, "\nUnable to identify crash location!\n"); + WriteFileString(hFile, temp); + } + } + + { + union + { + char buffer[128]; + IMAGEHLP_SYMBOL symbol; + }; + memset(buffer, 0, sizeof(buffer)); + symbol.SizeOfStruct = sizeof(symbol); + symbol.MaxNameLength = (DWORD)(buffer + sizeof(buffer) - symbol.Name); + DWORD_PTR offset = 0; + if (SymGetSymFromAddr(hProcess, address, &offset, &symbol)) + { + buffer[PFC_TABSIZE(buffer) - 1] = 0; + if (symbol.Name[0]) + { + sprintf(temp, "Symbol: \"%s\" (+" OFFSETSPEC ")\n", symbol.Name, offset); + WriteFileString(hFile, temp); + } + } + } + + WriteFileString(hFile, "\nLoaded modules:\n"); + SymEnumerateModules(hProcess, EnumModulesCallback, hFile); + +#ifdef _M_IX86 + call_stack_parse(param->ContextRecord->Esp, hFile, temp, hProcess); +#endif + +#ifdef _M_X64 + call_stack_parse(param->ContextRecord->Rsp, hFile, temp, hProcess); +#endif + + SymCleanup(hProcess); + } else { + WriteFileString(hFile, "\nFailed to get module/symbol info.\n"); + } + } + + WriteFileString(hFile, "\nEnvironment:\n"); + WriteFileString(hFile, version_string); + + WriteFileString(hFile, "\n"); + + if (!g_components.is_empty()) { + WriteFileString(hFile, "\nComponents:\n"); + WriteFileString(hFile, g_components); + } + + { + insync(g_lastEventsSync); + if (g_lastEvents.get_count() > 0) { + WriteFileString(hFile, "\nRecent events:\n"); + for( auto & walk : g_lastEvents) WriteEvent(hFile, walk); + } + } + } +} + +static bool GrabOSVersion(char * out) { + OSVERSIONINFO ver = {}; ver.dwOSVersionInfoSize = sizeof(ver); + if (!GetVersionEx(&ver)) return false; + *out = 0; + char temp[16]; + strcat(out,"Windows "); + _itoa(ver.dwMajorVersion,temp,10); strcat(out,temp); + strcat(out,"."); + _itoa(ver.dwMinorVersion,temp,10); strcat(out,temp); + return true; +} + +static void OnLogFileWritten() { + TCHAR exePath[MAX_PATH + 1] = {}; + TCHAR params[1024]; + GetModuleFileName(NULL, exePath, MAX_PATH); + exePath[MAX_PATH] = 0; + //unsafe... + wsprintf(params, _T("/crashed \"%s\""), DumpPathBuffer.get_ptr()); + ShellExecute(NULL, NULL, exePath, params, NULL, SW_SHOW); +} + +BOOL WriteMiniDumpHelper(HANDLE, LPEXCEPTION_POINTERS); //minidump.cpp + + +void SHARED_EXPORT uDumpCrashInfo(LPEXCEPTION_POINTERS param) +{ + if (g_didSuppress) return; + const DWORD lastError = GetLastError(); + if (InterlockedIncrement(&crash_no) > 1) {Sleep(10000);return;} + HANDLE hFile = create_failure_log(); + if (hFile == INVALID_HANDLE_VALUE) return; + + { + _tcscpy(_tcsrchr(DumpPathBuffer.get_ptr(), '.'), _T(".dmp")); + HANDLE hDump = CreateFile(DumpPathBuffer.get_ptr(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + if (hDump != INVALID_HANDLE_VALUE) { + const BOOL written = WriteMiniDumpHelper(hDump, param); + CloseHandle(hDump); + if (!written) { //don't bother proceeding if we don't have a valid minidump + DeleteFile(DumpPathBuffer.get_ptr()); + CloseHandle(hFile); + _tcscpy(_tcsrchr(DumpPathBuffer.get_ptr(), '.'), _T(".txt")); + DeleteFile(DumpPathBuffer.get_ptr()); + return; + } + } + _tcscpy(_tcsrchr(DumpPathBuffer.get_ptr(), '.'), _T(".txt")); + } + + writeFailureTxt(param, hFile, lastError); + + CloseHandle(hFile); + + OnLogFileWritten(); +} + +//No longer used. +size_t SHARED_EXPORT uPrintCrashInfo(LPEXCEPTION_POINTERS param,const char * extra_info,char * outbase) { + *outbase = 0; + return 0; +} + + +LONG SHARED_EXPORT uExceptFilterProc(LPEXCEPTION_POINTERS param) { + if (g_didSuppress) return UnhandledExceptionFilter(param); + callPanicHandlers(); + if (IsDebuggerPresent()) { + return UnhandledExceptionFilter(param); + } else { + if ( param->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW ) { + pfc::thread2 trd; + trd.startHere( [param] { + uDumpCrashInfo(param); + } ); + trd.waitTillDone(); + } else { + uDumpCrashInfo(param); + } + TerminateProcess(GetCurrentProcess(), 0); + return 0;// never reached + } +} + +void SHARED_EXPORT uPrintCrashInfo_Init(const char * name)//called only by exe on startup +{ + version_string = pfc::format( "App: ", name, "\nArch: ", pfc::cpuArch()); + + SetUnhandledExceptionFilter(uExceptFilterProc); +} +void SHARED_EXPORT uPrintCrashInfo_Suppress() { + g_didSuppress = true; +} + +void SHARED_EXPORT uPrintCrashInfo_AddEnvironmentInfo(const char * p_info) { + version_string << "\n" << p_info; +} + +void SHARED_EXPORT uPrintCrashInfo_SetComponentList(const char * p_info) { + g_components = p_info; +} + +void SHARED_EXPORT uPrintCrashInfo_SetDumpPath(const char * p_path) { + pfc::stringcvt::string_os_from_utf8 temp(p_path); + DumpPathBuffer.set_size(temp.length() + 256); + _tcscpy(DumpPathBuffer.get_ptr(), temp.get_ptr()); +} + +static HANDLE hLogFile = INVALID_HANDLE_VALUE; + +static void logEvent(const char* message) { + if ( hLogFile != INVALID_HANDLE_VALUE ) { + DWORD wrote = 0; + WriteFile(hLogFile, message, (DWORD) strlen(message), &wrote, NULL ); + WriteFile(hLogFile, "\r\n", 2, &wrote, NULL); + } +} + +void SHARED_EXPORT uPrintCrashInfo_StartLogging(const char * path) { + insync(g_lastEventsSync); + PFC_ASSERT(hLogFile == INVALID_HANDLE_VALUE); + hLogFile = CreateFile( pfc::stringcvt::string_wide_from_utf8(path), GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); + PFC_ASSERT(hLogFile != INVALID_HANDLE_VALUE); + + for( auto & walk : g_lastEvents ) { + logEvent( walk.c_str() ); + } +} + +void SHARED_EXPORT uPrintCrashInfo_OnEvent(const char * message, t_size length) { + + pfc::string8 msg = pfc::format("[", queryDebugTimer(), "ms] "); + msg.add_string( message, length ); + uOutputDebugString(msg + "\n"); + + insync(g_lastEventsSync); + logEvent(msg); + while(g_lastEvents.get_count() >= KLastEventCount) g_lastEvents.remove(g_lastEvents.first()); + g_lastEvents.insert_last( std::move(msg) ); +} + +static void callstack_add(const char * param) +{ + enum { MAX = PFC_TABSIZE(g_thread_call_stack) - 1} ; + t_size len = strlen(param); + if (g_thread_call_stack_length + len > MAX) len = MAX - g_thread_call_stack_length; + if (len>0) + { + memcpy(g_thread_call_stack+g_thread_call_stack_length,param,len); + g_thread_call_stack_length += len; + g_thread_call_stack[g_thread_call_stack_length]=0; + } +} + +uCallStackTracker::uCallStackTracker(const char * name) +{ + param = g_thread_call_stack_length; + if (g_thread_call_stack_length>0) callstack_add("=>"); + callstack_add(name); +} + +uCallStackTracker::~uCallStackTracker() +{ + g_thread_call_stack_length = param; + g_thread_call_stack[param]=0; + +} + +extern "C" {LPCSTR SHARED_EXPORT uGetCallStackPath() {return g_thread_call_stack;} } + +#ifdef _DEBUG +extern "C" { + void SHARED_EXPORT fb2kDebugSelfTest() { + auto ptr = SetUnhandledExceptionFilter(NULL); + PFC_ASSERT( ptr == uExceptFilterProc ); + SetUnhandledExceptionFilter(ptr); + } +} +#endif + +#else + +void SHARED_EXPORT uDumpCrashInfo(LPEXCEPTION_POINTERS param) {} +void SHARED_EXPORT uPrintCrashInfo_OnEvent(const char * message, t_size length) {} +LONG SHARED_EXPORT uExceptFilterProc(LPEXCEPTION_POINTERS param) { + return UnhandledExceptionFilter(param); +} +uCallStackTracker::uCallStackTracker(const char * name) {} +uCallStackTracker::~uCallStackTracker() {} +extern "C" { + LPCSTR SHARED_EXPORT uGetCallStackPath() { return ""; } + void SHARED_EXPORT fb2kDebugSelfTest() {} +} +#endif + +PFC_NORETURN void SHARED_EXPORT uBugCheck() { + fb2k_instacrash_scope(RaiseException(EXCEPTION_BUG_CHECK, EXCEPTION_NONCONTINUABLE, 0, NULL); ); +} diff --git a/sdk/foobar2000/shared/fb2kdebug.h b/sdk/foobar2000/shared/fb2kdebug.h index 76e8a60..552b2cb 100644 --- a/sdk/foobar2000/shared/fb2kdebug.h +++ b/sdk/foobar2000/shared/fb2kdebug.h @@ -1,5 +1,7 @@ #pragma once +#include + class uDebugLog_ : public pfc::string_formatter { public: ~uDebugLog_() {*this << "\n"; uOutputDebugString(get_ptr());} @@ -37,7 +39,7 @@ extern "C" #endif #ifdef _WIN32 - static inline void uAddDebugEvent(const char * msg) {uPrintCrashInfo_OnEvent(msg, strlen(msg));} + static inline void uAddDebugEvent(const char * msg) {uPrintCrashInfo_OnEvent(msg, SIZE_MAX);} #else static inline void uAddDebugEvent( const char * msg ) { uOutputDebugString(pfc::format(msg, "\n")); } #endif @@ -77,8 +79,8 @@ inline int uExceptFilterProc_inline(LPEXCEPTION_POINTERS param) { #define FB2K_DYNAMIC_ASSERT( X ) { if (!(X)) uBugCheck(); } -#define __except_instacrash __except(uExceptFilterProc(GetExceptionInformation())) #if FB2K_SUPPORT_CRASH_LOGS +#define __except_instacrash __except(uExceptFilterProc(GetExceptionInformation())) #define fb2k_instacrash_scope(X) __try { X; } __except_instacrash {} #else #define fb2k_instacrash_scope(X) {X;} @@ -160,11 +162,19 @@ namespace fb2k { uAddDebugEvent(msg); uBugCheck(); } + inline void crashOnException(std::function const & f, const char* context = nullptr) { + (void)context; + fb2k_instacrash_scope(f()); + } #else void crashWithMessage [[noreturn]] (const char*); + void crashOnException(std::function, const char* context = nullptr); #endif + } +#define FB2K_CrashOnException( ... ) ::fb2k::crashWithMessage(__VA_ARGS__) + // implement me #define FB2K_TRACE_ENABLED 0 diff --git a/sdk/foobar2000/shared/filedialogs.cpp b/sdk/foobar2000/shared/filedialogs.cpp new file mode 100644 index 0000000..5a3f12a --- /dev/null +++ b/sdk/foobar2000/shared/filedialogs.cpp @@ -0,0 +1,317 @@ +#include "shared.h" +#include "filedialogs.h" +#include + +#define dTEXT(X) pfc::stringcvt::string_os_from_utf8(X) + +static UINT_PTR CALLBACK uGetOpenFileName_Hook(HWND wnd,UINT msg,WPARAM wp,LPARAM lp) +{ + switch(msg) { + case WM_INITDIALOG: + { + OPENFILENAME * ofn = reinterpret_cast(lp); + reinterpret_cast(ofn->lCustData)->initialize(FindOwningPopup(wnd)); + } + return 0; + default: + return 0; + } +} + +static void ImportExtMask(pfc::array_t & out, const char * in) { + { + pfc::stringcvt::string_os_from_utf8 temp(in); + out.set_size(temp.length()+2); + out.fill_null(); + pfc::memcpy_t(out.get_ptr(),temp.get_ptr(),temp.length()); + } + + for(t_size walk = 0; walk < out.get_size(); ++walk) { + if (out[walk] == '|') out[walk] = 0; + } +} + +BOOL Vista_GetOpenFileName(HWND parent,const char * p_ext_mask,unsigned def_ext_mask,const char * p_def_ext,const char * p_title,const char * p_directory,pfc::string_base & p_filename,BOOL b_save); +BOOL Vista_GetOpenFileNameMulti(HWND parent,const char * p_ext_mask,unsigned def_ext_mask,const char * p_def_ext,const char * p_title,const char * p_directory,pfc::ptrholder_t & out); +BOOL Vista_BrowseForFolder(HWND parent, const char * p_title, pfc::string_base & path); +puGetOpenFileNameMultiResult Vista_BrowseForFolderEx(HWND parent,const char * title, const char * initPath); + +static bool UseVistaDialogs() { +#if FB2K_TARGET_MICROSOFT_STORE || _WIN32_WINNT >= 0x600 + return true; +#else + return GetWindowsVersionCode() >= 0x600; +#endif +} + +BOOL SHARED_EXPORT uGetOpenFileName(HWND parent,const char * p_ext_mask,unsigned def_ext_mask,const char * p_def_ext,const char * p_title,const char * p_directory,pfc::string_base & p_filename,BOOL b_save) { + TRACK_CALL_TEXT("uGetOpenFileName"); + try { + if (UseVistaDialogs()) return Vista_GetOpenFileName(parent, p_ext_mask, def_ext_mask, p_def_ext, p_title, p_directory, p_filename, b_save); + } catch(pfc::exception_not_implemented const &) {} + + modal_dialog_scope scope; + + pfc::array_t ext_mask; + ImportExtMask(ext_mask,p_ext_mask); + + TCHAR buffer[4096]; + + pfc::stringToBuffer(buffer,pfc::stringcvt::string_os_from_utf8(p_filename)); + + pfc::stringcvt::string_os_from_utf8 def_ext(p_def_ext ? p_def_ext : ""),title(p_title ? p_title : ""), + directory(p_directory ? p_directory : ""); + + OPENFILENAME ofn = {}; + + ofn.lStructSize=sizeof(ofn); + ofn.hwndOwner = parent; + ofn.lpstrFilter = ext_mask.get_ptr(); + ofn.nFilterIndex = def_ext_mask + 1; + ofn.lpstrFile = buffer; + ofn.lpstrInitialDir = directory; + ofn.nMaxFile = _countof(buffer); + ofn.Flags = b_save ? OFN_NOCHANGEDIR|OFN_EXPLORER|OFN_HIDEREADONLY|OFN_PATHMUSTEXIST|OFN_OVERWRITEPROMPT|OFN_ENABLEHOOK|OFN_ENABLESIZING : OFN_NOCHANGEDIR|OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY|OFN_PATHMUSTEXIST|OFN_ENABLEHOOK|OFN_ENABLESIZING; + ofn.lpstrDefExt = *(const TCHAR*)def_ext ? (const TCHAR*)def_ext : 0; + ofn.lpstrTitle = *(const TCHAR*)title ? (const TCHAR*)title : 0; + ofn.lCustData = reinterpret_cast(&scope); + ofn.lpfnHook = uGetOpenFileName_Hook; + if (b_save ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn)) + { + buffer[_countof(buffer)-1]=0; + + { + t_size ptr = _tcslen(buffer); + while(ptr>0 && buffer[ptr-1]==' ') buffer[--ptr] = 0; + } + + p_filename = pfc::stringcvt::string_utf8_from_os(buffer,_countof(buffer)); + return TRUE; + } + else return FALSE; +} + + +puGetOpenFileNameMultiResult SHARED_EXPORT uGetOpenFileNameMulti(HWND parent,const char * p_ext_mask,unsigned def_ext_mask,const char * p_def_ext,const char * p_title,const char * p_directory) { + TRACK_CALL_TEXT("uGetOpenFileNameMulti"); + try { + if (UseVistaDialogs()) { + pfc::ptrholder_t result; + if (!Vista_GetOpenFileNameMulti(parent,p_ext_mask,def_ext_mask,p_def_ext,p_title,p_directory,result)) return NULL; + return result.detach(); + } + } catch(pfc::exception_not_implemented const &) {} + + modal_dialog_scope scope; + + pfc::array_t ext_mask; + ImportExtMask(ext_mask,p_ext_mask); + + TCHAR buffer[0x4000]; + buffer[0]=0; + + pfc::stringcvt::string_os_from_utf8 def_ext(p_def_ext ? p_def_ext : ""),title(p_title ? p_title : ""), + directory(p_directory ? p_directory : ""); + + OPENFILENAME ofn = {}; + + ofn.lStructSize=sizeof(ofn); + ofn.hwndOwner = parent; + ofn.lpstrFilter = ext_mask.get_ptr(); + ofn.nFilterIndex = def_ext_mask + 1; + ofn.lpstrFile = buffer; + ofn.lpstrInitialDir = directory; + ofn.nMaxFile = _countof(buffer); + ofn.Flags = OFN_NOCHANGEDIR|OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY|OFN_PATHMUSTEXIST|OFN_ALLOWMULTISELECT|OFN_ENABLEHOOK|OFN_ENABLESIZING; + ofn.lpstrDefExt = *(const TCHAR*)def_ext ? (const TCHAR*)def_ext : 0; + ofn.lpstrTitle = *(const TCHAR*)title ? (const TCHAR*)title : 0; + ofn.lCustData = reinterpret_cast(&scope); + ofn.lpfnHook = uGetOpenFileName_Hook; + if (GetOpenFileName(&ofn)) + { + buffer[_countof(buffer)-1]=0; + buffer[_countof(buffer)-2]=0; + + pfc::ptrholder_t result = new uGetOpenFileNameMultiResult_impl; + + TCHAR * p=buffer; + while(*p) p++; + p++; + if (!*p) + { + { + t_size ptr = _tcslen(buffer); + while(ptr>0 && buffer[ptr-1]==' ') buffer[--ptr] = 0; + } + + result->AddItem(pfc::stringcvt::string_utf8_from_os(buffer)); + } + else + { + pfc::string_formatter s = (const char*) pfc::stringcvt::string_utf8_from_os(buffer,_countof(buffer)); + t_size ofs = s.length(); + if (ofs>0 && s[ofs-1]!='\\') {s.add_char('\\');ofs++;} + while(*p) + { + s.truncate(ofs); + s += pfc::stringcvt::string_utf8_from_os(p); + s.skip_trailing_char(' '); + result->AddItem(s); + while(*p) p++; + p++; + } + } + return result.detach(); + } + else return 0; +} + + + + + +struct browse_for_dir_struct +{ + const TCHAR * m_initval; + const TCHAR * m_tofind; + + modal_dialog_scope m_scope; +}; + +static bool file_exists(const TCHAR * p_path) +{ + DWORD val = GetFileAttributes(p_path); + if (val == (-1) || (val & FILE_ATTRIBUTE_DIRECTORY)) return false; + return true; +} + +static void browse_proc_check_okbutton(HWND wnd,const browse_for_dir_struct * p_struct,const TCHAR * p_path) +{ + TCHAR temp[MAX_PATH+1]; + pfc::stringToBuffer(temp, p_path); + + t_size len = _tcslen(temp); + if (len < MAX_PATH && len > 0) + { + if (temp[len-1] != '\\') + temp[len++] = '\\'; + } + t_size idx = 0; + while(p_struct->m_tofind[idx] && idx+len < MAX_PATH) + { + temp[len+idx] = p_struct->m_tofind[idx]; + idx++; + } + temp[len+idx] = 0; + + SendMessage(wnd,BFFM_ENABLEOK,0,!!file_exists(temp)); + +} + +static int _stdcall browse_proc(HWND wnd,UINT msg,LPARAM lp,LPARAM dat) +{ + browse_for_dir_struct * p_struct = reinterpret_cast(dat); + switch(msg) + { + case BFFM_INITIALIZED: + p_struct->m_scope.initialize(wnd); + SendMessage(wnd,BFFM_SETSELECTION,1,(LPARAM)p_struct->m_initval); + if (p_struct->m_tofind) browse_proc_check_okbutton(wnd,p_struct,p_struct->m_initval); + break; + case BFFM_SELCHANGED: + if (p_struct->m_tofind) + { + if (lp != 0) + { + TCHAR temp[MAX_PATH+1]; + if (SHGetPathFromIDList(reinterpret_cast(lp),temp)) + { + temp[MAX_PATH] = 0; + browse_proc_check_okbutton(wnd,p_struct,temp); + } + else + SendMessage(wnd,BFFM_ENABLEOK,0,FALSE); + } + else SendMessage(wnd,BFFM_ENABLEOK,0,FALSE); + } + break; + } + return 0; +} + +static BOOL BrowseForFolderHelper(HWND p_parent,const TCHAR * p_title,TCHAR (&p_out)[MAX_PATH],const TCHAR * p_file_to_find) +{ + pfc::com_ptr_t mallocptr; + + if (FAILED(SHGetMalloc(mallocptr.receive_ptr()))) return FALSE; + if (mallocptr.is_empty()) return FALSE; + + browse_for_dir_struct data; + data.m_initval = p_out; + data.m_tofind = p_file_to_find; + + + BROWSEINFO bi= + { + p_parent, + 0, + 0, + p_title, + BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE | BIF_EDITBOX, + browse_proc, + reinterpret_cast(&data), + 0 + }; + + LPITEMIDLIST li = SHBrowseForFolder(&bi); + if (li == NULL) return FALSE; + BOOL state = SHGetPathFromIDList(li,p_out); + mallocptr->Free(li); + return state; +} + +BOOL SHARED_EXPORT uBrowseForFolder(HWND parent,const char * p_title,pfc::string_base & out) { + TRACK_CALL_TEXT("uBrowseForFolder"); + try { + if (UseVistaDialogs()) { + return Vista_BrowseForFolder(parent,p_title,out); + } + } catch(pfc::exception_not_implemented const &) {} + + TCHAR temp[MAX_PATH]; + pfc::stringToBuffer(temp,dTEXT(out)); + BOOL rv = BrowseForFolderHelper(parent,dTEXT(p_title),temp,0); + if (rv) { + out = pfc::stringcvt::string_utf8_from_os(temp,_countof(temp)); + } + return rv; +} + +BOOL SHARED_EXPORT uBrowseForFolderWithFile(HWND parent,const char * title,pfc::string_base & out,const char * p_file_to_find) +{ + TRACK_CALL_TEXT("uBrowseForFolderWithFile"); + TCHAR temp[MAX_PATH]; + pfc::stringToBuffer(temp,dTEXT(out)); + BOOL rv = BrowseForFolderHelper(parent,dTEXT(title),temp,dTEXT(p_file_to_find)); + if (rv) { + out = pfc::stringcvt::string_utf8_from_os(temp,_countof(temp)); + } + return rv; +} + + +puGetOpenFileNameMultiResult SHARED_EXPORT uBrowseForFolderEx(HWND parent,const char * title, const char * initPath) { + TRACK_CALL_TEXT("uBrowseForFolderEx"); + try { + if (UseVistaDialogs()) { + return Vista_BrowseForFolderEx(parent,title, initPath); + } + } catch(pfc::exception_not_implemented const &) {} + + pfc::string8 temp; + if (initPath) temp = initPath; + if (!uBrowseForFolder(parent, title, temp)) return NULL; + pfc::ptrholder_t result = new uGetOpenFileNameMultiResult_impl; + result->AddItem(temp); + return result.detach(); +} diff --git a/sdk/foobar2000/shared/filedialogs_vista.cpp b/sdk/foobar2000/shared/filedialogs_vista.cpp new file mode 100644 index 0000000..4110eb3 --- /dev/null +++ b/sdk/foobar2000/shared/filedialogs_vista.cpp @@ -0,0 +1,497 @@ +#include "shared.h" +#include "filedialogs.h" +#include +#include +#include +#include +#include + +#define dTEXT(X) pfc::stringcvt::string_os_from_utf8(X) + +class FilterSpec { +public: + void clear() { + m_types.set_size(0); m_strings.remove_all(); + } + void Sanity() { + if ( GetCount() > 200 ) SetAllFiles(); + } + void SetAllFiles() { + FromString( "All files|*.*" ); + } + void FromString(const char * in) { + clear(); + if (in == NULL) return; + pfc::chain_list_v2_t types; + + for(t_size inWalk = 0; ; ) { + + t_size base1 = inWalk; + t_size delta1 = ScanForSeparator(in+base1); + t_size base2 = base1 + delta1; + if (in[base2] == 0) break; + ++base2; + t_size delta2 = ScanForSeparator(in+base2); + if (delta1 > 0 && delta2 > 0) { + COMDLG_FILTERSPEC spec; + spec.pszName = MakeString(in+base1,delta1); + spec.pszSpec = MakeString(in+base2,delta2); + types.add_item(spec); + } + inWalk = base2 + delta2; + if (in[inWalk] == 0) break; + ++inWalk; + } + + pfc::list_to_array(m_types,types); + } + + t_size GetCount() const {return m_types.get_count();} + const COMDLG_FILTERSPEC * GetPtr() const {return m_types.get_ptr();} +private: + static t_size ScanForSeparator(const char * in) { + for(t_size walk = 0; ; ++walk) { + if (in[walk] == 0 || in[walk] == '|') return walk; + } + } + WCHAR * MakeString(const char * in, t_size inLen) { + t_size len = pfc::stringcvt::estimate_utf8_to_wide(in,inLen); + WCHAR* str = AllocString(len); + pfc::stringcvt::convert_utf8_to_wide(str,len,in,inLen); + return str; + } + WCHAR * AllocString(t_size size) { + auto iter = m_strings.insert_last(); + iter->set_size(size); + return iter->get_ptr(); + } + pfc::chain_list_v2_t > m_strings; + pfc::array_t m_types; +}; + +static HRESULT AddOptionsHelper(pfc::com_ptr_t dlg, DWORD opts) { + DWORD options; + HRESULT state; + if (FAILED(state = dlg->GetOptions(&options))) return state; + options |= opts; + if (FAILED(state = dlg->SetOptions( options ))) return state; + return S_OK; +} + +namespace { + + // SPECIAL + // Deal with slow or nonworking net shares, do not lockup the calling thread in such cases, just split away + // Particularly relevant to net shares referred by raw IP, these get us stuck for a long time + class PDNArg_t : public pfc::refcounted_object_root { + public: + CoTaskMemObject m_idList; + pfc::string8 m_path; + HRESULT m_result; + }; + + static unsigned CALLBACK PDNProc(void * arg) { + pfc::refcounted_object_ptr_t ptr; ptr.attach( reinterpret_cast( arg ) ); + CoInitialize(0); + + SFGAOF dummy = {}; + ptr->m_result = SHParseDisplayName(dTEXT(ptr->m_path),NULL,&ptr->m_idList.m_ptr,0,&dummy); + + CoUninitialize(); + + return 0; + } +} + +static HRESULT SetFolderHelper(pfc::com_ptr_t dlg, const char * folderPath) { + CoTaskMemObject idList; + + // Do SHParseDisplayName() off-thread as it is known to lock up on bad net share references + pfc::refcounted_object_ptr_t ptr = new PDNArg_t(); + ptr->m_path = folderPath; + ptr->m_result = E_FAIL; + HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, PDNProc, reinterpret_cast(ptr._duplicate_ptr()), 0, NULL); + DWORD status = WaitForSingleObject( hThread, 3000 ); + CloseHandle(hThread); + if (status != WAIT_OBJECT_0) return E_FAIL; + if (FAILED(ptr->m_result)) return ptr->m_result; + + pfc::com_ptr_t item; + HRESULT state; + if (FAILED(state = SHCreateShellItem(NULL,NULL,ptr->m_idList.m_ptr,item.receive_ptr()))) return state; + return dlg->SetFolder(item.get_ptr()); +} + +namespace { + class _EH { + public: + void operator<<(HRESULT hr) { + if (FAILED(hr)) throw exception_com(hr); + } + }; + static _EH EH; +}; + +BOOL Vista_GetOpenFileName(HWND parent,const char * p_ext_mask,unsigned def_ext_mask,const char * p_def_ext,const char * p_title,const char * p_directory,pfc::string_base & p_filename,BOOL b_save) { + modal_dialog_scope modalScope(parent); + + pfc::com_ptr_t dlg; + + if (b_save) { + if (FAILED(CoCreateInstance(__uuidof(FileSaveDialog), NULL, CLSCTX_ALL, IID_IFileSaveDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented(); + } else { + if (FAILED(CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, IID_IFileOpenDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented(); + } + + { + FilterSpec spec; spec.FromString(p_ext_mask); + spec.Sanity(); + if (FAILED(dlg->SetFileTypes((UINT)spec.GetCount(),spec.GetPtr()))) return FALSE; + if (def_ext_mask < spec.GetCount()) { + if (FAILED(dlg->SetFileTypeIndex(def_ext_mask + 1))) return FALSE; + } + } + if (p_def_ext != NULL) { + if (FAILED(dlg->SetDefaultExtension(dTEXT(p_def_ext)))) return FALSE; + } + if (p_title != NULL) { + if (FAILED(dlg->SetTitle(dTEXT(p_title)))) return FALSE; + } + + if (!p_filename.is_empty()) { + pfc::string path(p_filename); + pfc::string fn = pfc::io::path::getFileName(path); + pfc::string parent = pfc::io::path::getParent(path); + if (!parent.isEmpty()) SetFolderHelper(dlg,parent.ptr()); + dlg->SetFileName(dTEXT(fn.ptr())); + } else if (p_directory != NULL) { + SetFolderHelper(dlg,p_directory); + } + + if (FAILED(AddOptionsHelper(dlg, FOS_FORCEFILESYSTEM))) return FALSE; + + if (FAILED( dlg->Show(parent) ) ) return FALSE; + + { + pfc::com_ptr_t result; + if (FAILED(dlg->GetResult(result.receive_ptr()))) return FALSE; + + CoTaskMemObject nameBuf; + if (FAILED(result->GetDisplayName(SIGDN_FILESYSPATH,&nameBuf.m_ptr))) return FALSE; + + p_filename = pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ); + } + return TRUE; +} + +BOOL Vista_GetOpenFileNameMulti(HWND parent,const char * p_ext_mask,unsigned def_ext_mask,const char * p_def_ext,const char * p_title,const char * p_directory,pfc::ptrholder_t & out) { + modal_dialog_scope modalScope(parent); + + pfc::com_ptr_t dlg; + if (FAILED(CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, IID_IFileOpenDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented(); + + { + FilterSpec spec; spec.FromString(p_ext_mask); + spec.Sanity(); + if (FAILED(dlg->SetFileTypes((UINT)spec.GetCount(),spec.GetPtr()))) return FALSE; + if (def_ext_mask < spec.GetCount()) { + if (FAILED(dlg->SetFileTypeIndex(def_ext_mask + 1))) return FALSE; + } + } + if (p_def_ext != NULL) { + if (FAILED(dlg->SetDefaultExtension(dTEXT(p_def_ext)))) return FALSE; + } + if (p_title != NULL) { + if (FAILED(dlg->SetTitle(dTEXT(p_title)))) return FALSE; + } + + if (p_directory != NULL) { + SetFolderHelper(dlg,p_directory); + } + + if (FAILED(AddOptionsHelper(dlg, FOS_ALLOWMULTISELECT | FOS_FORCEFILESYSTEM))) return FALSE; + + if (FAILED( dlg->Show(parent) ) ) return FALSE; + + { + pfc::ptrholder_t result = new uGetOpenFileNameMultiResult_impl; + pfc::com_ptr_t results; + if (FAILED(dlg->GetResults(results.receive_ptr()))) return FALSE; + DWORD total; + if (FAILED(results->GetCount(&total))) return FALSE; + for(DWORD itemWalk = 0; itemWalk < total; ++itemWalk) { + pfc::com_ptr_t item; + if (SUCCEEDED(results->GetItemAt(itemWalk,item.receive_ptr()))) { + CoTaskMemObject nameBuf; + if (FAILED(item->GetDisplayName(SIGDN_FILESYSPATH,&nameBuf.m_ptr))) return FALSE; + + result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) ); + + } + } + + if (result->get_count() == 0) return FALSE; + + out = result.detach(); + } + + return TRUE; +} +#if 0 +namespace { + class CFileDialogEvents_LocateFile : public IFileDialogEvents { + public: + CFileDialogEvents_LocateFile(pfc::stringp tofind) : m_tofind(tofind) {} + HRESULT STDMETHODCALLTYPE OnFileOk(IFileDialog *pfd) {return S_OK;} + + HRESULT STDMETHODCALLTYPE OnFolderChanging( IFileDialog *pfd, IShellItem *psiFolder) {return S_OK;} + + HRESULT STDMETHODCALLTYPE OnFolderChange( IFileDialog *pfd ) { + //pfd->GetFolder(); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE OnSelectionChange( IFileDialog *pfd ) {return S_OK;} + + HRESULT STDMETHODCALLTYPE OnShareViolation( IFileDialog *pfd, IShellItem *psi, FDE_SHAREVIOLATION_RESPONSE *pResponse) { return S_OK; } + + HRESULT STDMETHODCALLTYPE OnTypeChange( IFileDialog *pfd ) {return S_OK; } + + HRESULT STDMETHODCALLTYPE OnOverwrite( IFileDialog *pfd, IShellItem *psi, FDE_OVERWRITE_RESPONSE *pResponse) {return S_OK; } + + private: + const pfc::string m_tofind; + }; + +} +#endif +BOOL Vista_BrowseForFolder(HWND parent, const char * p_title, pfc::string_base & path) { + modal_dialog_scope modalScope(parent); + pfc::com_ptr_t dlg; + if (FAILED(CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, IID_IFileOpenDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented(); + + if (p_title != NULL) { + if (FAILED(dlg->SetTitle(dTEXT(p_title)))) return FALSE; + } + + if (FAILED(AddOptionsHelper(dlg, FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM))) return FALSE; + + if (!path.is_empty()) { + SetFolderHelper(dlg,path); + } + + if (FAILED( dlg->Show(parent) ) ) return FALSE; + + { + pfc::com_ptr_t result; + if (FAILED(dlg->GetResult(result.receive_ptr()))) return FALSE; + + CoTaskMemObject nameBuf; + if (FAILED(result->GetDisplayName(SIGDN_FILESYSPATH,&nameBuf.m_ptr))) return FALSE; + + path = pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ); + } + return TRUE; +} + + +__inline HRESULT mySHLoadLibraryFromItem( + __in IShellItem *psiLibrary, + __in DWORD grfMode, + __in REFIID riid, + __deref_out void **ppv +) +{ + *ppv = NULL; + IShellLibrary *plib; + + HRESULT hr = CoCreateInstance( + CLSID_ShellLibrary, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&plib)); + + if (SUCCEEDED(hr)) + { + hr = plib->LoadLibraryFromItem (psiLibrary, grfMode); + if (SUCCEEDED(hr)) + { + hr = plib->QueryInterface (riid, ppv); + } + plib->Release(); + } + return hr; +} + +// +// from shobjidl.h +// +__inline HRESULT mySHLoadLibraryFromKnownFolder( + __in REFKNOWNFOLDERID kfidLibrary, + __in DWORD grfMode, + __in REFIID riid, + __deref_out void **ppv) +{ + *ppv = NULL; + IShellLibrary *plib; + HRESULT hr = CoCreateInstance( + CLSID_ShellLibrary, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&plib)); + if (SUCCEEDED(hr)) + { + hr = plib->LoadLibraryFromKnownFolder(kfidLibrary, grfMode); + if (SUCCEEDED(hr)) + { + hr = plib->QueryInterface(riid, ppv); + } + plib->Release(); + } + return hr; +} + +puGetOpenFileNameMultiResult Vista_BrowseForFolderEx(HWND parent,const char * p_title, const char * initPath) { + try { + modal_dialog_scope modalScope(parent); + pfc::com_ptr_t dlg; + if (FAILED(CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, IID_IFileOpenDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented(); + + if (p_title != NULL) { + EH << dlg->SetTitle(dTEXT(p_title)); + } + + EH << AddOptionsHelper(dlg, FOS_ALLOWMULTISELECT | FOS_PICKFOLDERS); + + if (initPath && *initPath) { + SetFolderHelper(dlg,initPath); + } + + EH << dlg->Show(parent); + + pfc::ptrholder_t result = new uGetOpenFileNameMultiResult_impl; + pfc::com_ptr_t results; + EH << dlg->GetResults(results.receive_ptr()); + DWORD total; + EH << results->GetCount(&total); + CoTaskMemObject nameBuf; + for(DWORD itemWalk = 0; itemWalk < total; ++itemWalk) { + pfc::com_ptr_t item; + EH << results->GetItemAt(itemWalk,item.receive_ptr()); + if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH,nameBuf.Receive()))) { + result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) ); + } else { + pfc::com_ptr_t library; + if (SUCCEEDED(mySHLoadLibraryFromItem(item.get_ptr(), STGM_READ, IID_IShellLibrary, (void**)library.receive_ptr()))) { + pfc::com_ptr_t subFolders; + EH << library->GetFolders(LFF_FORCEFILESYSTEM, IID_IShellItemArray, (void**)subFolders.receive_ptr()); + DWORD subTotal; + EH << subFolders->GetCount(&subTotal); + for(DWORD subWalk = 0; subWalk < subTotal; ++subWalk) { + pfc::com_ptr_t subItem; + EH << subFolders->GetItemAt(subWalk,subItem.receive_ptr()); + EH << subItem->GetDisplayName(SIGDN_FILESYSPATH,nameBuf.Receive()); + result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) ); + } + } + } + } + if (result->GetCount() == 0) return NULL; + return result.detach(); + } catch(exception_com const &) { + return NULL; + } +} + +static bool GetLegacyKnownFolder(int & out, REFKNOWNFOLDERID id) { + if (id == FOLDERID_Music) { + out = CSIDL_MYMUSIC; + return true; + } else if (id == FOLDERID_Pictures) { + out = CSIDL_MYPICTURES; + return true; + } else if (id == FOLDERID_Videos) { + out = CSIDL_MYVIDEO; + return true; + } else if (id == FOLDERID_Documents) { + out = CSIDL_MYDOCUMENTS; + return true; + } else if (id == FOLDERID_Desktop) { + out = CSIDL_DESKTOP; + return true; + } else { + return false; + } +} + +puGetOpenFileNameMultiResult SHARED_EXPORT uEvalKnownFolder(REFKNOWNFOLDERID id) { + try { + pfc::com_ptr_t library; + EH << mySHLoadLibraryFromKnownFolder(id, STGM_READ, IID_IShellLibrary, (void**)library.receive_ptr()); + + pfc::com_ptr_t subFolders; + EH << library->GetFolders(LFF_FORCEFILESYSTEM, IID_IShellItemArray, (void**)subFolders.receive_ptr()); + + pfc::ptrholder_t result = new uGetOpenFileNameMultiResult_impl; + + DWORD subTotal; + EH << subFolders->GetCount(&subTotal); + CoTaskMemObject nameBuf; + for(DWORD subWalk = 0; subWalk < subTotal; ++subWalk) { + pfc::com_ptr_t subItem; + EH << subFolders->GetItemAt(subWalk,subItem.receive_ptr()); + EH << subItem->GetDisplayName(SIGDN_FILESYSPATH,nameBuf.Receive()); + result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) ); + } + if (result->get_count() == 0) return NULL; + return result.detach(); + } catch(exception_com const &) { + //failed + } + + + try { + CComPtr mgr; CComPtr folder; CoTaskMemObject path; + EH << CoCreateInstance(__uuidof(KnownFolderManager), nullptr, CLSCTX_ALL, IID_IKnownFolderManager, (void**)&mgr.p); + EH << mgr->GetFolder(id, &folder.p); + EH << folder->GetPath(0, &path.m_ptr); + + pfc::ptrholder_t result = new uGetOpenFileNameMultiResult_impl; + result->AddItem(pfc::stringcvt::string_utf8_from_os(path.m_ptr)); + return result.detach(); + } catch (exception_com const &) { + + } + + //FALLBACK + + + + { + int legacyID; + if (GetLegacyKnownFolder(legacyID, id)) { + try { + TCHAR path[MAX_PATH+16] = {}; + EH << SHGetFolderPath(NULL, legacyID, NULL, SHGFP_TYPE_CURRENT, path); + path[_countof(path)-1] = 0; + pfc::ptrholder_t result = new uGetOpenFileNameMultiResult_impl; + result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( path ) ); + return result.detach(); + } catch(exception_com const &) { + //failed; + } + } + } +#if 0 // Vista code path - uninteresting, XP shit still needs to be supported, SHGetKnownFolderPath() does not exist on XP + try { + pfc::ptrholder_t result = new uGetOpenFileNameMultiResult_impl; + CoTaskMemObject nameBuf; + EH << SHGetKnownFolderPath(id, 0, NULL, nameBuf.Receive()); + result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) ); + return result.detach(); + } catch(exception_com const &) { + //failed + } +#endif + return NULL; //failure +} diff --git a/sdk/foobar2000/shared/font_description.cpp b/sdk/foobar2000/shared/font_description.cpp new file mode 100644 index 0000000..440930c --- /dev/null +++ b/sdk/foobar2000/shared/font_description.cpp @@ -0,0 +1,126 @@ +#include "shared.h" + + +static unsigned query_dpi() +{ + unsigned ret; + HDC dc = GetDC(0); + ret = GetDeviceCaps(dc,LOGPIXELSY); + ReleaseDC(0,dc); + return ret; +} + + +#if 0 +struct t_font_description +{ + enum {m_facename_length = LF_FACESIZE*2}; + + t_uint32 m_height; + t_uint32 m_weight; + t_uint8 m_italic; + t_uint8 m_charset; + char m_facename[m_facename_length]; +} +#endif + +static void make_logfont(LOGFONT & p_logfont,const t_font_description & p_desc) +{ + p_logfont.lfHeight = - MulDiv(p_desc.m_height, query_dpi(), t_font_description::m_height_dpi); + p_logfont.lfWidth = 0; + p_logfont.lfEscapement = 0; + p_logfont.lfOrientation = 0; + p_logfont.lfWeight = p_desc.m_weight; + p_logfont.lfItalic = p_desc.m_italic; + p_logfont.lfUnderline = 0; + p_logfont.lfStrikeOut = 0; + p_logfont.lfCharSet = p_desc.m_charset; + p_logfont.lfOutPrecision = 3; + p_logfont.lfClipPrecision = 2; + p_logfont.lfQuality = 1; + p_logfont.lfPitchAndFamily = 34; + pfc::stringToBuffer(p_logfont.lfFaceName,pfc::stringcvt::string_os_from_utf8(p_desc.m_facename,tabsize(p_desc.m_facename))); +} + +static void make_description(t_font_description & p_desc,const LOGFONT & p_logfont) +{ + p_desc.m_height = MulDiv(pfc::abs_t(p_logfont.lfHeight), t_font_description::m_height_dpi, query_dpi()); + p_desc.m_weight = p_logfont.lfWeight; + p_desc.m_italic = p_logfont.lfItalic; + p_desc.m_charset = p_logfont.lfCharSet; + pfc::stringToBuffer(p_desc.m_facename,pfc::stringcvt::string_utf8_from_os(p_logfont.lfFaceName,tabsize(p_logfont.lfFaceName))); +} + +HFONT SHARED_EXPORT t_font_description::create() const +{ + LOGFONT temp; + make_logfont(temp,*this); + return CreateFontIndirect(&temp); +} + +static UINT_PTR CALLBACK choose_font_hook(HWND wnd,UINT msg,WPARAM wp,LPARAM lp) +{ + switch(msg) + { + case WM_INITDIALOG: + { + CHOOSEFONT * cf = reinterpret_cast(lp); + reinterpret_cast(cf->lCustData)->initialize(FindOwningPopup(wnd)); + } + return 0; + default: + return 0; + } +} + +bool SHARED_EXPORT t_font_description::popup_dialog(HWND p_parent) +{ + modal_dialog_scope scope; + + LOGFONT logfont; + make_logfont(logfont,*this); + + CHOOSEFONT cf = {}; + cf.lStructSize = sizeof(cf); + cf.hwndOwner = p_parent; + cf.lpLogFont = &logfont; + cf.Flags = CF_SCREENFONTS|CF_FORCEFONTEXIST|CF_INITTOLOGFONTSTRUCT|CF_ENABLEHOOK; + cf.nFontType = SCREEN_FONTTYPE; + cf.lCustData = reinterpret_cast(&scope); + cf.lpfnHook = choose_font_hook; + if (ChooseFont(&cf)) + { + make_description(*this,logfont); + return true; + } + else return false; +} + + +void SHARED_EXPORT t_font_description::from_font(HFONT p_font) +{ + LOGFONT logfont; + PFC_ASSERT_SUCCESS( GetObject((HGDIOBJ) p_font, sizeof(logfont), &logfont) != 0 ); + make_description(*this,logfont); +} + +t_font_description SHARED_EXPORT t_font_description::g_from_font(HFONT p_font) +{ + t_font_description temp; + temp.from_font(p_font); + return temp; +} + + +t_font_description SHARED_EXPORT t_font_description::g_from_logfont(LOGFONT const & lf) { + t_font_description ret; make_description(ret, lf); return ret; +} + +t_font_description SHARED_EXPORT t_font_description::g_from_system(int id) { + LOGFONT lf; + if (FAILED( GetThemeSysFont(NULL, id, &lf) ) ) { + PFC_ASSERT(!"Should not get here!"); + return g_from_font( (HFONT) GetStockObject(DEFAULT_GUI_FONT) ); + } + return g_from_logfont(lf); +} diff --git a/sdk/foobar2000/shared/minidump.cpp b/sdk/foobar2000/shared/minidump.cpp new file mode 100644 index 0000000..eec6d14 --- /dev/null +++ b/sdk/foobar2000/shared/minidump.cpp @@ -0,0 +1,251 @@ +#include "shared.h" +#include + +#ifdef _M_ARM64EC +typedef ARM64EC_NT_CONTEXT myCONTEXT; +#else +typedef CONTEXT myCONTEXT; +#endif +struct DumpState { + int state; + myCONTEXT*context; +}; + +__declspec(noinline) static bool safeRead(volatile const void* addr, size_t& dest) +{ + __try { + dest = *(const volatile size_t*)addr; + return true; + } + __except (1) { + return false; + } +} + +__declspec(noinline) static bool safeTestReadAccess(volatile const void* addr) +{ + size_t dummy; + return safeRead(addr, dummy); +} + +#if defined(_M_ARM64) || defined(_M_ARM64EC) + +BOOL WINAPI MiniDumpCallback(PVOID CallbackParam, + const PMINIDUMP_CALLBACK_INPUT CallbackInput, + PMINIDUMP_CALLBACK_OUTPUT CallbackOutput) +{ + static const unsigned STACK_SEARCH_SIZE = 0x400; + static const unsigned MEM_BLOCK_SIZE = 0x400; + + if (CallbackInput->CallbackType == MemoryCallback) { + // Called to get user defined blocks of memory to write until + // callback returns FALSE or CallbackOutput->MemorySize == 0. + + DumpState* ds = (DumpState*)CallbackParam; + switch (ds->state) { + // Save memory referenced by registers. + case 0: CallbackOutput->MemoryBase = ds->context->X0; ds->state++; break; + case 1: CallbackOutput->MemoryBase = ds->context->X1; ds->state++; break; + case 2: CallbackOutput->MemoryBase = ds->context->X2; ds->state++; break; + case 3: CallbackOutput->MemoryBase = ds->context->X3; ds->state++; break; + case 4: CallbackOutput->MemoryBase = ds->context->X4; ds->state++; break; + case 5: CallbackOutput->MemoryBase = ds->context->X5; ds->state++; break; + case 6: CallbackOutput->MemoryBase = ds->context->X6; ds->state++; break; + case 7: CallbackOutput->MemoryBase = ds->context->X7; ds->state++; break; + case 8: CallbackOutput->MemoryBase = ds->context->X8; ds->state++; break; + case 9: CallbackOutput->MemoryBase = ds->context->X9; ds->state++; break; + case 10: CallbackOutput->MemoryBase = ds->context->X10; ds->state++; break; + case 11: CallbackOutput->MemoryBase = ds->context->X11; ds->state++; break; + case 12: CallbackOutput->MemoryBase = ds->context->X12; ds->state++; break; +#ifndef _M_ARM64EC + case 13: CallbackOutput->MemoryBase = ds->context->X13; ds->state++; break; + case 14: CallbackOutput->MemoryBase = ds->context->X14; ds->state++; break; + case 15: CallbackOutput->MemoryBase = ds->context->X15; ds->state++; break; + case 16: CallbackOutput->MemoryBase = ds->context->X16; ds->state++; break; + case 17: CallbackOutput->MemoryBase = ds->context->X17; ds->state++; break; + case 18: CallbackOutput->MemoryBase = ds->context->X18; ds->state++; break; + case 19: CallbackOutput->MemoryBase = ds->context->X19; ds->state++; break; + case 20: CallbackOutput->MemoryBase = ds->context->X20; ds->state++; break; + case 21: CallbackOutput->MemoryBase = ds->context->X21; ds->state++; break; + case 22: CallbackOutput->MemoryBase = ds->context->X22; ds->state++; break; + case 23: CallbackOutput->MemoryBase = ds->context->X23; ds->state++; break; + case 24: CallbackOutput->MemoryBase = ds->context->X24; ds->state++; break; + case 25: CallbackOutput->MemoryBase = ds->context->X25; ds->state++; break; + case 26: CallbackOutput->MemoryBase = ds->context->X26; ds->state++; break; + case 27: CallbackOutput->MemoryBase = ds->context->X27; ds->state++; break; + case 28: CallbackOutput->MemoryBase = ds->context->X28; ds->state++; break; +#endif + + // Save memory referenced by values in stack. + default: + if (ds->state < 0x1000) + ds->state = 0x1000; + + size_t addr; + do { + if (ds->state > 0x1000 + STACK_SEARCH_SIZE) + return FALSE; + + if (!safeRead((void*)((ds->context->Sp & ~7) + ds->state - 0x1000), addr)) + return FALSE; + + ds->state += 4; + } while (addr < 0x1000 || !safeTestReadAccess((void*)addr)); + + CallbackOutput->MemoryBase = addr; + break; + } + + if (CallbackOutput->MemoryBase >= MEM_BLOCK_SIZE / 2) + CallbackOutput->MemoryBase -= MEM_BLOCK_SIZE / 2; + CallbackOutput->MemorySize = MEM_BLOCK_SIZE; + + // No need to perform additional checks here, the minidump engine + // safely clips the addresses to valid memory pages only. + // Also seems to apply for overlapped areas etc. + } + + return TRUE; +} + +#elif defined(_M_IX86) + +BOOL WINAPI MiniDumpCallback(PVOID CallbackParam, + const PMINIDUMP_CALLBACK_INPUT CallbackInput, + PMINIDUMP_CALLBACK_OUTPUT CallbackOutput) +{ + static const unsigned STACK_SEARCH_SIZE = 0x400; + static const unsigned MEM_BLOCK_SIZE = 0x400; + + if (CallbackInput->CallbackType == MemoryCallback) { + // Called to get user defined blocks of memory to write until + // callback returns FALSE or CallbackOutput->MemorySize == 0. + + DumpState* ds = (DumpState*)CallbackParam; + switch (ds->state) { + // Save memory referenced by registers. + case 0: CallbackOutput->MemoryBase = ds->context->Eax; ds->state++; break; + case 1: CallbackOutput->MemoryBase = ds->context->Ebx; ds->state++; break; + case 2: CallbackOutput->MemoryBase = ds->context->Ecx; ds->state++; break; + case 3: CallbackOutput->MemoryBase = ds->context->Edx; ds->state++; break; + case 4: CallbackOutput->MemoryBase = ds->context->Esi; ds->state++; break; + case 5: CallbackOutput->MemoryBase = ds->context->Edi; ds->state++; break; + case 6: CallbackOutput->MemoryBase = ds->context->Ebp; ds->state++; break; + case 7: CallbackOutput->MemoryBase = ds->context->Esp; ds->state++; break; + case 8: CallbackOutput->MemoryBase = ds->context->Eip; ds->state++; break; + + // Save memory referenced by values in stack. + default: + if (ds->state < 0x1000) + ds->state = 0x1000; + + size_t addr; + do { + if (ds->state > 0x1000 + STACK_SEARCH_SIZE) + return FALSE; + + if (!safeRead((void*)((ds->context->Esp & ~3) + ds->state - 0x1000), addr)) + return FALSE; + + ds->state += 4; + } while (addr < 0x1000 || !safeTestReadAccess((void*)addr)); + + CallbackOutput->MemoryBase = addr; + break; + } + + if (CallbackOutput->MemoryBase >= MEM_BLOCK_SIZE / 2) + CallbackOutput->MemoryBase -= MEM_BLOCK_SIZE / 2; + CallbackOutput->MemorySize = MEM_BLOCK_SIZE; + + // No need to perform additional checks here, the minidump engine + // safely clips the addresses to valid memory pages only. + // Also seems to apply for overlapped areas etc. + } + + return TRUE; +} + +#elif defined(_M_X64) + +BOOL WINAPI MiniDumpCallback(PVOID CallbackParam, + const PMINIDUMP_CALLBACK_INPUT CallbackInput, + PMINIDUMP_CALLBACK_OUTPUT CallbackOutput) +{ + static const unsigned STACK_SEARCH_SIZE = 0x400; + static const unsigned MEM_BLOCK_SIZE = 0x400; + + if (CallbackInput->CallbackType == MemoryCallback) { + // Called to get user defined blocks of memory to write until + // callback returns FALSE or CallbackOutput->MemorySize == 0. + + DumpState* ds = (DumpState*)CallbackParam; + switch (ds->state) { + // Save memory referenced by registers. + case 0: CallbackOutput->MemoryBase = ds->context->Rax; ds->state++; break; + case 1: CallbackOutput->MemoryBase = ds->context->Rbx; ds->state++; break; + case 2: CallbackOutput->MemoryBase = ds->context->Rcx; ds->state++; break; + case 3: CallbackOutput->MemoryBase = ds->context->Rdx; ds->state++; break; + case 4: CallbackOutput->MemoryBase = ds->context->Rsi; ds->state++; break; + case 5: CallbackOutput->MemoryBase = ds->context->Rdi; ds->state++; break; + case 6: CallbackOutput->MemoryBase = ds->context->Rsp; ds->state++; break; + case 7: CallbackOutput->MemoryBase = ds->context->Rbp; ds->state++; break; + case 8: CallbackOutput->MemoryBase = ds->context->R8; ds->state++; break; + case 9: CallbackOutput->MemoryBase = ds->context->R9; ds->state++; break; + case 10: CallbackOutput->MemoryBase = ds->context->R10; ds->state++; break; + case 11: CallbackOutput->MemoryBase = ds->context->R11; ds->state++; break; + case 12: CallbackOutput->MemoryBase = ds->context->R12; ds->state++; break; + case 13: CallbackOutput->MemoryBase = ds->context->R13; ds->state++; break; + case 14: CallbackOutput->MemoryBase = ds->context->R14; ds->state++; break; + case 15: CallbackOutput->MemoryBase = ds->context->R15; ds->state++; break; + + // Save memory referenced by values in stack. + default: + if (ds->state < 0x1000) + ds->state = 0x1000; + + size_t addr; + do { + if (ds->state > 0x1000 + STACK_SEARCH_SIZE) + return FALSE; + + if (!safeRead((void*)((ds->context->Rsp & ~7) + ds->state - 0x1000), addr)) + return FALSE; + + ds->state += 4; + } while (addr < 0x1000 || !safeTestReadAccess((void*)addr)); + + CallbackOutput->MemoryBase = addr; + break; + } + + if (CallbackOutput->MemoryBase >= MEM_BLOCK_SIZE / 2) + CallbackOutput->MemoryBase -= MEM_BLOCK_SIZE / 2; + CallbackOutput->MemorySize = MEM_BLOCK_SIZE; + + // No need to perform additional checks here, the minidump engine + // safely clips the addresses to valid memory pages only. + // Also seems to apply for overlapped areas etc. + } + + return TRUE; +} + +#endif // _M_X64 + + + + +BOOL WriteMiniDumpHelper(HANDLE hDump, LPEXCEPTION_POINTERS param) { + MINIDUMP_EXCEPTION_INFORMATION exception = {}; + exception.ThreadId = GetCurrentThreadId(); + exception.ExceptionPointers = param; + exception.ClientPointers = FALSE; + DumpState ds; + ds.state = 0; + ds.context = reinterpret_cast(param->ContextRecord); + MINIDUMP_CALLBACK_INFORMATION mci; + mci.CallbackRoutine = &MiniDumpCallback; + mci.CallbackParam = (void*)&ds; + return MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDump, (MINIDUMP_TYPE)(MiniDumpWithUnloadedModules), &exception, NULL, &mci); +} diff --git a/sdk/foobar2000/shared/modal_dialog.cpp b/sdk/foobar2000/shared/modal_dialog.cpp new file mode 100644 index 0000000..8478579 --- /dev/null +++ b/sdk/foobar2000/shared/modal_dialog.cpp @@ -0,0 +1,66 @@ +#include "shared.h" + +static DWORD g_main_thread = GetCurrentThreadId(); + +static t_modal_dialog_entry g_status = {0,false}; + +static bool TestMainThread() +{ + if (GetCurrentThreadId() == g_main_thread) return true; + OutputDebugString(TEXT("This function can be called only from main thread.\n")); + return false; +} + +HWND SHARED_EXPORT FindOwningPopup(HWND p_wnd) +{ + return pfc::findOwningPopup(p_wnd); +} + +void SHARED_EXPORT PokeWindow(HWND p_wnd) +{ + p_wnd = FindOwningPopup(p_wnd); + if (IsWindowEnabled(p_wnd)) + { +// SetForegroundWindow(p_wnd); + SetActiveWindow(p_wnd); + FlashWindow(p_wnd,FALSE); + } + else + { + HWND child = GetWindow(p_wnd,GW_ENABLEDPOPUP); + if (child != 0) + { +// SetForegroundWindow(child); + SetActiveWindow(child); + FlashWindow(child,FALSE); + } + } +} + +extern "C" { + void SHARED_EXPORT ModalDialog_Switch(t_modal_dialog_entry & p_entry) + { + if (TestMainThread()) + pfc::swap_t(p_entry,g_status); + } + + void SHARED_EXPORT ModalDialog_PokeExisting() + { + if (TestMainThread()) + { + if (g_status.m_in_use && g_status.m_wnd_to_poke != 0) + { + PokeWindow(g_status.m_wnd_to_poke); + MessageBeep(0); + } + } + } + + bool SHARED_EXPORT ModalDialog_CanCreateNew() + { + if (TestMainThread()) + return !g_status.m_in_use; + else + return false; + } +} \ No newline at end of file diff --git a/sdk/foobar2000/shared/shared-ARM64.lib b/sdk/foobar2000/shared/shared-ARM64.lib deleted file mode 100644 index 754a40a..0000000 Binary files a/sdk/foobar2000/shared/shared-ARM64.lib and /dev/null differ diff --git a/sdk/foobar2000/shared/shared-ARM64EC.lib b/sdk/foobar2000/shared/shared-ARM64EC.lib index 77043dc..ea9bbe9 100644 Binary files a/sdk/foobar2000/shared/shared-ARM64EC.lib and b/sdk/foobar2000/shared/shared-ARM64EC.lib differ diff --git a/sdk/foobar2000/shared/shared-Win32.lib b/sdk/foobar2000/shared/shared-Win32.lib index c5cbee1..da0ccb0 100644 Binary files a/sdk/foobar2000/shared/shared-Win32.lib and b/sdk/foobar2000/shared/shared-Win32.lib differ diff --git a/sdk/foobar2000/shared/shared-apple.mm b/sdk/foobar2000/shared/shared-apple.mm new file mode 100644 index 0000000..f2aed14 --- /dev/null +++ b/sdk/foobar2000/shared/shared-apple.mm @@ -0,0 +1,66 @@ +#include "shared.h" +#include "shared-apple.h" + +#import + +bool uSetClipboardString(const char * str) { + @autoreleasepool { + @try { + NSPasteboard * pb = [NSPasteboard generalPasteboard]; + [pb clearContents]; + [pb setString: [NSString stringWithUTF8String: str] forType:NSPasteboardTypeString]; + return true; + } @catch (NSException *) { + + } + } + return false; +} + +bool uGetClipboardString(pfc::string_base & out) { + bool rv = false; + @autoreleasepool { + NSPasteboard * pb = [NSPasteboard generalPasteboard]; + NSString * str = [pb stringForType: NSPasteboardTypeString]; + if ( str != nil ) { + out = str.UTF8String; + rv = true; + } + } + return rv; +} + +static void wrapNoExcept(std::function f) noexcept {f();} + +void fb2k::crashOnException(std::function f, const char * context) { +#if 0 + auto fail = [context] ( const char * msg ) { + if (context) { + fb2k::crashWithMessage(pfc::format(context, ": ", msg)); + } else { + fb2k::crashWithMessage(msg); + } + }; + try { + @autoreleasepool { + @try { + f(); + } @catch(NSException * e) { + auto header = pfc::format("NSException: ", e.name.UTF8String, " reason: ", e.reason.UTF8String ); + uAddDebugEvent( header ); + uAddDebugEvent("Stack:"); + for(NSString * str in e.callStackSymbols ) { + uAddDebugEvent(str.UTF8String); + } + fail(header); + } + } + } catch(std::exception const & e) { + fail(pfc::format("C++ exception: ", e.what())); + } catch(...) { + fail("Invalid exception"); + } +#else + wrapNoExcept(f); +#endif +} diff --git a/sdk/foobar2000/shared/shared-nix.cpp b/sdk/foobar2000/shared/shared-nix.cpp new file mode 100644 index 0000000..d87f918 --- /dev/null +++ b/sdk/foobar2000/shared/shared-nix.cpp @@ -0,0 +1,212 @@ +#include "shared.h" +#include +#include +#include + +// foobar2000 SDK project method... bah +namespace foobar2000_io { + PFC_NORETURN void nix_io_op_fail(); +} +using namespace foobar2000_io; + +namespace { + class uFindFileImpl : public uFindFile { + public: + uFindFileImpl ( DIR * dir ) : m_dir(dir) {} + bool FindNext() override { + m_entry = readdir(m_dir); + return m_entry != NULL; + } + const char * GetFileName() override { + return m_entry->d_name; + } + bool IsDirectory() override { + return m_entry->d_type == DT_DIR; + } + ~uFindFileImpl() { + closedir(m_dir); + } + private: + dirent * m_entry; + + DIR * m_dir; + }; + + class uFindFileFiltered : public uFindFile { + public: + uFindFileFiltered( DIR * dir, const char * wc ) : m_impl(dir), m_wc(wc) {} + bool FindNext() override { + for( ;; ) { + if (!m_impl.FindNext()) return false; + if ( testWC() ) return true; + } + } + const char * GetFileName() override { + return m_impl.GetFileName(); + } + bool IsDirectory() override { + return m_impl.IsDirectory(); + } + bool testWC() { + return wildcard_helper::test(GetFileName(), m_wc); + } + + uFindFileImpl m_impl; + const pfc::string8 m_wc; + }; +} + +puFindFile uFindFirstFile(const char * path) { + + if ( wildcard_helper::has_wildcards( path ) ) { + size_t idx = pfc::scan_filename(path); + + try { + DIR * dir = opendir( pfc::string8(path, idx) ); + if (dir == NULL) nix_io_op_fail(); + try { + uFindFile * ff; + try { + ff = new uFindFileFiltered(dir, path + idx); + } catch(...) { closedir( dir ); throw; } + if (ff->FindNext()) return ff; + delete ff; + return NULL; + } catch(...) { closedir( dir ); throw; } + } catch(...) {return NULL;} + + } + + try { + DIR * dir = opendir( path ); + if (dir == NULL) nix_io_op_fail(); + try { + uFindFile * ff; + try { + ff = new uFindFileImpl(dir); + } catch(...) { closedir( dir ); throw; } + if (ff->FindNext()) return ff; + delete ff; + return NULL; + } catch(...) { closedir( dir ); throw; } + } catch(...) {return NULL;} +} + +pfc::string8 uStringPrintf(const char * fmt, ...) { + pfc::string8 ret; + va_list list; + va_start(list,fmt); + uPrintfV(ret,fmt,list); + va_end(list); + return ret; +} + +void uPrintfV(pfc::string_base & out,const char * fmt,va_list arglist) { + pfc::string_printf_here_va(out, fmt, arglist); +} + +void uPrintf(pfc::string_base & out,const char * fmt,...) { + va_list list;va_start(list,fmt);uPrintfV(out,fmt,list);va_end(list); +} + + +static int makeInfiniteWaitEvent() { + int fdPipe[2]; + pfc::createPipe(fdPipe); + return fdPipe[0]; // leak the other end +} + +int GetInfiniteWaitEvent() { + static int obj = makeInfiniteWaitEvent(); + return obj; +} + + +// DUMMY +void SHARED_EXPORT uAddPanicHandler(fb2k::panicHandler*) { + +} +void SHARED_EXPORT uRemovePanicHandler(fb2k::panicHandler*) { + +} + +void SHARED_EXPORT uOutputDebugString(const char * msg) { + // UGLY: underlying APIs want whole lines, calling code feeds lines terminated with \n or \r\n because Windows + pfc::string8 temp ( msg ); + if ( temp.endsWith('\n') ) temp.truncate( temp.length() - 1) ; + if ( temp.endsWith('\r') ) temp.truncate( temp.length() - 1) ; + pfc::outputDebugLine(temp); +} +namespace pfc { PFC_NORETURN void crashImpl(); } +PFC_NORETURN void SHARED_EXPORT uBugCheck() { + pfc::crashImpl(); +} + +int SHARED_EXPORT uStringCompare(const char * elem1, const char * elem2) { + return pfc::naturalSortCompareI(elem1, elem2); +} + +void fb2kDebugSelfTest() { + +} + +bool uGetTempPath(pfc::string_base & out) { + auto var = getenv("TMPDIR"); + if ( var == nullptr ) uBugCheck(); + out = var; + return true; +} +bool uGetTempFileName(const char * path_name,const char * prefix,unsigned unique,pfc::string_base & out) { +#if 0 // sample use + pfc::string8 temp_path, temp_file; + uGetTempPath(temp_path); + uGetTempFileName(temp_path, "img", 0, temp_file); +#endif + pfc::string8 ret; + if ( path_name == nullptr ) uGetTempPath( ret ); + else ret = path_name; + + pfc::chain_list_v2_t segments; + if ( prefix ) segments += prefix; + if ( unique ) segments += pfc::format(unique); + segments += pfc::print_guid(pfc::createGUID()); + + pfc::string8 fn; + for( auto & seg : segments ) { + if (seg.length() == 0) continue; + if ( fn.length() > 0 ) fn += "-"; + fn += seg; + } + + ret.add_filename( fn ); + out = ret; + return true; +} + +pfc::string8 uGetTempFileName() { + pfc::string8 ret; + uGetTempFileName(nullptr, nullptr, 0, ret); + return ret; +} + + +void fb2k::crashWithMessage [[noreturn]] ( const char * msg_ ) { + uAddDebugEvent( msg_ ); + pfc::crashWithMessageOnStack(msg_); +} + +bool uSetCurrentDirectory(const char * path) { + return chdir(path) == 0; +} +bool uGetCurrentDirectory(pfc::string_base & out) { + pfc::array_t work; + work.resize( PATH_MAX ); + for(;;) { + errno = 0; + if ( getcwd(work.get_ptr(), work.size()) != nullptr ) { + out = work.get_ptr(); return true; + } + if ( errno != ENAMETOOLONG ) return false; + work.resize( work.size() * 2 ); + } +} diff --git a/sdk/foobar2000/shared/shared-nix.h b/sdk/foobar2000/shared/shared-nix.h index a6a80ad..3596c41 100644 --- a/sdk/foobar2000/shared/shared-nix.h +++ b/sdk/foobar2000/shared/shared-nix.h @@ -23,3 +23,6 @@ bool uGetTempPath(pfc::string_base & out); bool uGetTempFileName(const char * path_name,const char * prefix,unsigned unique,pfc::string_base & out); pfc::string8 uGetTempFileName(); + +bool uSetCurrentDirectory(const char * path); +bool uGetCurrentDirectory(pfc::string_base & out); diff --git a/sdk/foobar2000/shared/shared-x64.lib b/sdk/foobar2000/shared/shared-x64.lib index 26309ad..ce643a8 100644 Binary files a/sdk/foobar2000/shared/shared-x64.lib and b/sdk/foobar2000/shared/shared-x64.lib differ diff --git a/sdk/foobar2000/shared/shared.h b/sdk/foobar2000/shared/shared.h index aac1911..c7d7b90 100644 --- a/sdk/foobar2000/shared/shared.h +++ b/sdk/foobar2000/shared/shared.h @@ -89,8 +89,10 @@ BOOL SHARED_EXPORT uGetClipboardString(pfc::string_base & out); BOOL SHARED_EXPORT uSetClipboardRawData(UINT format,const void * ptr,t_size size);//does not empty the clipboard BOOL SHARED_EXPORT uGetClassName(HWND wnd,pfc::string_base & out); t_size SHARED_EXPORT uCharLength(const char * src); +#ifdef DragQueryFile // don't declare if relevant Windows #include has been omitted, breaks on HDROP BOOL SHARED_EXPORT uDragQueryFile(HDROP hDrop,UINT idx,pfc::string_base & out); UINT SHARED_EXPORT uDragQueryFileCount(HDROP hDrop); +#endif BOOL SHARED_EXPORT uGetTextExtentPoint32(HDC dc,const char * text,UINT cb,LPSIZE size);//note, cb is number of bytes, not actual unicode characters in the string (read: plain strlen() will do) BOOL SHARED_EXPORT uExtTextOut(HDC dc,int x,int y,UINT flags,const RECT * rect,const char * text,UINT cb,const int * lpdx); BOOL SHARED_EXPORT uTextOutColors(HDC dc,const char * src,UINT len,int x,int y,const RECT * clip,BOOL is_selected,DWORD default_color); @@ -229,7 +231,7 @@ int SHARED_EXPORT uTabCtrl_SetItem(HWND wnd,t_size idx,const uTCITEM * item); int SHARED_EXPORT uGetKeyNameText(LONG lparam,pfc::string_base & out); -void SHARED_EXPORT uFixAmpersandChars(const char * src,pfc::string_base & out);//for notification area icon +void SHARED_EXPORT uFixAmpersandChars(const char * src,pfc::string_base & out);//for system tray icon void SHARED_EXPORT uFixAmpersandChars_v2(const char * src,pfc::string_base & out);//for other controls #if FB2K_SUPPORT_CRASH_LOGS @@ -278,6 +280,7 @@ UINT SHARED_EXPORT uGetMenuItemType(HMENU menu,UINT position); HMODULE SHARED_EXPORT LoadSystemLibrary(const TCHAR * name); void SHARED_EXPORT uPrintCrashInfo_OnEvent(const char * message, t_size length); +void SHARED_EXPORT uPrintCrashInfo_StartLogging(const char * path); }//extern "C" @@ -565,7 +568,7 @@ typedef HICON hicon_t; typedef HMENU hmenu_t; typedef HFONT hfont_t; #else -typedef void* hwnd_t; +typedef void* hwnd_t; // Mac: bridged NSObject, context specific (NSWindow, NSView, NSViewController) typedef void* hicon_t; typedef void* hmenu_t; typedef void* hfont_t; diff --git a/sdk/foobar2000/shared/shared.vcxproj b/sdk/foobar2000/shared/shared.vcxproj new file mode 100644 index 0000000..72a5887 --- /dev/null +++ b/sdk/foobar2000/shared/shared.vcxproj @@ -0,0 +1,404 @@ + + + + + Debug + ARM64 + + + Debug + ARM64EC + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM64 + + + Release + ARM64EC + + + Release + Win32 + + + Release + x64 + + + + {054C606B-17BF-4540-AC4E-A9A7243628B8} + shared + Win32Proj + 10.0 + + + + DynamicLibrary + Unicode + true + v142 + + + DynamicLibrary + Unicode + true + v142 + + + DynamicLibrary + Unicode + true + v143 + + + DynamicLibrary + Unicode + true + v143 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v142 + + + DynamicLibrary + Unicode + v143 + + + DynamicLibrary + Unicode + v143 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + true + true + true + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + false + false + false + + + + Disabled + EnableFastChecks + Use + shared.h + Level3 + ProgramDatabase + MultiThreadedDebugDLL + 4715 + Fast + _CRT_SECURE_NO_WARNINGS;SHARED_EXPORTS;_WINDLL;%(PreprocessorDefinitions) + true + stdcpp20 + + + comctl32.lib;imagehlp.lib;uxtheme.lib;dbghelp.lib;%(AdditionalDependencies) + true + $(OutDir)shared.pdb + Windows + + + $(OutDir)shared.lib + MachineX86 + + + + + Disabled + EnableFastChecks + Use + shared.h + Level3 + ProgramDatabase + MultiThreadedDebugDLL + 4715 + Fast + _CRT_SECURE_NO_WARNINGS;SHARED_EXPORTS;_WINDLL;%(PreprocessorDefinitions) + true + stdcpp20 + + + comctl32.lib;imagehlp.lib;uxtheme.lib;dbghelp.lib;%(AdditionalDependencies) + true + $(OutDir)shared.pdb + Windows + + + $(OutDir)shared.lib + + + + + Disabled + EnableFastChecks + Use + shared.h + Level3 + ProgramDatabase + MultiThreadedDebugDLL + 4715 + Fast + _CRT_SECURE_NO_WARNINGS;SHARED_EXPORTS;_WINDLL;%(PreprocessorDefinitions) + true + stdcpp20 + + + comctl32.lib;imagehlp.lib;uxtheme.lib;dbghelp.lib;%(AdditionalDependencies) + true + $(OutDir)shared.pdb + Windows + + + $(OutDir)shared.lib + + + + + Disabled + EnableFastChecks + Use + shared.h + Level3 + ProgramDatabase + MultiThreadedDebugDLL + 4715 + Fast + _CRT_SECURE_NO_WARNINGS;SHARED_EXPORTS;_WINDLL;%(PreprocessorDefinitions) + true + stdcpp20 + + + comctl32.lib;imagehlp.lib;uxtheme.lib;dbghelp.lib;%(AdditionalDependencies) + true + $(OutDir)shared.pdb + Windows + + + $(OutDir)shared.lib + + + + + Full + true + false + Fast + false + Use + shared.h + Level3 + ProgramDatabase + MultiThreadedDLL + /d2notypeopt %(AdditionalOptions) + 4715 + true + _CRT_SECURE_NO_WARNINGS;SHARED_EXPORTS;_WINDLL;NDEBUG;%(PreprocessorDefinitions) + true + stdcpp20 + + + comctl32.lib;imagehlp.lib;uxtheme.lib;dbghelp.lib;%(AdditionalDependencies) + true + Windows + true + true + + + $(OutDir)shared.lib + MachineX86 + + + copy "$(OutDir)shared.lib" "$(ProjectDir)shared-$(Platform).lib" + + + + + Full + true + false + Fast + false + Use + shared.h + Level3 + ProgramDatabase + MultiThreadedDLL + /d2notypeopt %(AdditionalOptions) + 4715 + true + _CRT_SECURE_NO_WARNINGS;SHARED_EXPORTS;_WINDLL;NDEBUG;%(PreprocessorDefinitions) + true + stdcpp20 + + + comctl32.lib;imagehlp.lib;uxtheme.lib;dbghelp.lib;%(AdditionalDependencies) + true + Windows + true + true + + + $(OutDir)shared.lib + + + copy "$(OutDir)shared.lib" "$(ProjectDir)shared-$(Platform).lib" + + + + + Full + true + false + Fast + false + Use + shared.h + Level3 + ProgramDatabase + MultiThreadedDLL + /d2notypeopt %(AdditionalOptions) + 4715 + true + _CRT_SECURE_NO_WARNINGS;SHARED_EXPORTS;_WINDLL;NDEBUG;%(PreprocessorDefinitions) + true + stdcpp20 + + + comctl32.lib;imagehlp.lib;uxtheme.lib;dbghelp.lib;%(AdditionalDependencies) + true + Windows + true + true + + + $(OutDir)shared.lib + + + copy "$(OutDir)shared.lib" "$(ProjectDir)shared-$(Platform).lib" + + + + + Full + true + false + Fast + false + Use + shared.h + Level3 + ProgramDatabase + MultiThreadedDLL + /d2notypeopt %(AdditionalOptions) + 4715 + true + _CRT_SECURE_NO_WARNINGS;SHARED_EXPORTS;_WINDLL;NDEBUG;%(PreprocessorDefinitions) + true + stdcpp20 + + + comctl32.lib;imagehlp.lib;uxtheme.lib;dbghelp.lib;%(AdditionalDependencies) + true + Windows + true + true + + + $(OutDir)shared.lib + + + copy "$(OutDir)shared.lib" "$(ProjectDir)shared-$(Platform).lib" + + + + + + + + + + + + Create + Create + Create + Create + Create + Create + Create + Create + + + + + + + + + + + + + + + + + {ebfffb4e-261d-44d3-b89c-957b31a0bf9c} + + + + + + \ No newline at end of file diff --git a/sdk/foobar2000/shared/shared.vcxproj.filters b/sdk/foobar2000/shared/shared.vcxproj.filters new file mode 100644 index 0000000..0bad1ee --- /dev/null +++ b/sdk/foobar2000/shared/shared.vcxproj.filters @@ -0,0 +1,75 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/sdk/foobar2000/shared/shared.xcodeproj/project.pbxproj b/sdk/foobar2000/shared/shared.xcodeproj/project.pbxproj new file mode 100644 index 0000000..02b8ee3 --- /dev/null +++ b/sdk/foobar2000/shared/shared.xcodeproj/project.pbxproj @@ -0,0 +1,352 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 0F75F3A72A6B1A8C00A45078 /* stdafx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3932A6B1A8C00A45078 /* stdafx.cpp */; }; + 0F75F3AB2A6B1A8C00A45078 /* shared-apple.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F39A2A6B1A8C00A45078 /* shared-apple.mm */; }; + 0F75F3AC2A6B1A8C00A45078 /* utf8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F39B2A6B1A8C00A45078 /* utf8.cpp */; }; + 0F75F3AE2A6B1A8C00A45078 /* shared-nix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F39E2A6B1A8C00A45078 /* shared-nix.cpp */; }; + 0F75F3B02A6B1A8C00A45078 /* audio_math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F75F3A02A6B1A8C00A45078 /* audio_math.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 0F75F3772A6B1A3900A45078 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0F75F3792A6B1A3900A45078 /* libshared.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libshared.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 0F75F38C2A6B1A8C00A45078 /* modal_dialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = modal_dialog.cpp; sourceTree = ""; }; + 0F75F38D2A6B1A8C00A45078 /* minidump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump.cpp; sourceTree = ""; }; + 0F75F38E2A6B1A8C00A45078 /* font_description.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = font_description.cpp; sourceTree = ""; }; + 0F75F38F2A6B1A8C00A45078 /* filedialogs_vista.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filedialogs_vista.cpp; sourceTree = ""; }; + 0F75F3902A6B1A8C00A45078 /* audio_math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audio_math.h; sourceTree = ""; }; + 0F75F3912A6B1A8C00A45078 /* text_drawing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = text_drawing.cpp; sourceTree = ""; }; + 0F75F3922A6B1A8C00A45078 /* filedialogs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filedialogs.h; sourceTree = ""; }; + 0F75F3932A6B1A8C00A45078 /* stdafx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stdafx.cpp; sourceTree = ""; }; + 0F75F3942A6B1A8C00A45078 /* crash_info.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = crash_info.cpp; sourceTree = ""; }; + 0F75F3952A6B1A8C00A45078 /* fb2kdebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fb2kdebug.h; sourceTree = ""; }; + 0F75F3962A6B1A8C00A45078 /* systray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = systray.cpp; sourceTree = ""; }; + 0F75F3972A6B1A8C00A45078 /* win32_misc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = win32_misc.h; sourceTree = ""; }; + 0F75F3982A6B1A8C00A45078 /* shared-nix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "shared-nix.h"; sourceTree = ""; }; + 0F75F3992A6B1A8C00A45078 /* filedialogs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filedialogs.cpp; sourceTree = ""; }; + 0F75F39A2A6B1A8C00A45078 /* shared-apple.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "shared-apple.mm"; sourceTree = ""; }; + 0F75F39B2A6B1A8C00A45078 /* utf8.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = utf8.cpp; sourceTree = ""; }; + 0F75F39C2A6B1A8C00A45078 /* utf8api.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = utf8api.cpp; sourceTree = ""; }; + 0F75F39D2A6B1A8C00A45078 /* shared.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shared.h; sourceTree = ""; }; + 0F75F39E2A6B1A8C00A45078 /* shared-nix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "shared-nix.cpp"; sourceTree = ""; }; + 0F75F39F2A6B1A8C00A45078 /* Utility.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Utility.cpp; sourceTree = ""; }; + 0F75F3A02A6B1A8C00A45078 /* audio_math.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audio_math.cpp; sourceTree = ""; }; + 0F75F3A12A6B1A8C00A45078 /* shared-apple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "shared-apple.h"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0F75F3762A6B1A3900A45078 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0F75F3702A6B1A3900A45078 = { + isa = PBXGroup; + children = ( + 0F75F38B2A6B1A7D00A45078 /* Source */, + 0F75F37A2A6B1A3900A45078 /* Products */, + ); + sourceTree = ""; + }; + 0F75F37A2A6B1A3900A45078 /* Products */ = { + isa = PBXGroup; + children = ( + 0F75F3792A6B1A3900A45078 /* libshared.a */, + ); + name = Products; + sourceTree = ""; + }; + 0F75F38B2A6B1A7D00A45078 /* Source */ = { + isa = PBXGroup; + children = ( + 0F75F3A02A6B1A8C00A45078 /* audio_math.cpp */, + 0F75F3902A6B1A8C00A45078 /* audio_math.h */, + 0F75F3942A6B1A8C00A45078 /* crash_info.cpp */, + 0F75F3952A6B1A8C00A45078 /* fb2kdebug.h */, + 0F75F38F2A6B1A8C00A45078 /* filedialogs_vista.cpp */, + 0F75F3992A6B1A8C00A45078 /* filedialogs.cpp */, + 0F75F3922A6B1A8C00A45078 /* filedialogs.h */, + 0F75F38E2A6B1A8C00A45078 /* font_description.cpp */, + 0F75F38D2A6B1A8C00A45078 /* minidump.cpp */, + 0F75F38C2A6B1A8C00A45078 /* modal_dialog.cpp */, + 0F75F3A12A6B1A8C00A45078 /* shared-apple.h */, + 0F75F39A2A6B1A8C00A45078 /* shared-apple.mm */, + 0F75F39E2A6B1A8C00A45078 /* shared-nix.cpp */, + 0F75F3982A6B1A8C00A45078 /* shared-nix.h */, + 0F75F39D2A6B1A8C00A45078 /* shared.h */, + 0F75F3932A6B1A8C00A45078 /* stdafx.cpp */, + 0F75F3962A6B1A8C00A45078 /* systray.cpp */, + 0F75F3912A6B1A8C00A45078 /* text_drawing.cpp */, + 0F75F39B2A6B1A8C00A45078 /* utf8.cpp */, + 0F75F39C2A6B1A8C00A45078 /* utf8api.cpp */, + 0F75F39F2A6B1A8C00A45078 /* Utility.cpp */, + 0F75F3972A6B1A8C00A45078 /* win32_misc.h */, + ); + name = Source; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 0F75F3782A6B1A3900A45078 /* shared */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0F75F3822A6B1A3900A45078 /* Build configuration list for PBXNativeTarget "shared" */; + buildPhases = ( + 0F75F3752A6B1A3900A45078 /* Sources */, + 0F75F3762A6B1A3900A45078 /* Frameworks */, + 0F75F3772A6B1A3900A45078 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = shared; + productName = shared; + productReference = 0F75F3792A6B1A3900A45078 /* libshared.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0F75F3712A6B1A3900A45078 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1250; + TargetAttributes = { + 0F75F3782A6B1A3900A45078 = { + CreatedOnToolsVersion = 12.5.1; + }; + }; + }; + buildConfigurationList = 0F75F3742A6B1A3900A45078 /* Build configuration list for PBXProject "shared" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 0F75F3702A6B1A3900A45078; + productRefGroup = 0F75F37A2A6B1A3900A45078 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0F75F3782A6B1A3900A45078 /* shared */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 0F75F3752A6B1A3900A45078 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0F75F3B02A6B1A8C00A45078 /* audio_math.cpp in Sources */, + 0F75F3AE2A6B1A8C00A45078 /* shared-nix.cpp in Sources */, + 0F75F3AB2A6B1A8C00A45078 /* shared-apple.mm in Sources */, + 0F75F3A72A6B1A8C00A45078 /* stdafx.cpp in Sources */, + 0F75F3AC2A6B1A8C00A45078 /* utf8.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 0F75F3802A6B1A3900A45078 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = shared.h; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + ../.., + .., + ); + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 0F75F3812A6B1A3900A45078 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = shared.h; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + ../.., + .., + ); + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 0F75F3832A6B1A3900A45078 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 4S876G9VCD; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 0F75F3842A6B1A3900A45078 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 4S876G9VCD; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0F75F3742A6B1A3900A45078 /* Build configuration list for PBXProject "shared" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0F75F3802A6B1A3900A45078 /* Debug */, + 0F75F3812A6B1A3900A45078 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0F75F3822A6B1A3900A45078 /* Build configuration list for PBXNativeTarget "shared" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0F75F3832A6B1A3900A45078 /* Debug */, + 0F75F3842A6B1A3900A45078 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0F75F3712A6B1A3900A45078 /* Project object */; +} diff --git a/sdk/foobar2000/shared/stdafx.cpp b/sdk/foobar2000/shared/stdafx.cpp new file mode 100644 index 0000000..a036ba6 --- /dev/null +++ b/sdk/foobar2000/shared/stdafx.cpp @@ -0,0 +1 @@ +#include "shared.h" \ No newline at end of file diff --git a/sdk/foobar2000/shared/systray.cpp b/sdk/foobar2000/shared/systray.cpp new file mode 100644 index 0000000..3c5e047 --- /dev/null +++ b/sdk/foobar2000/shared/systray.cpp @@ -0,0 +1,108 @@ +#include "shared.h" +#include + +void SHARED_EXPORT uFixAmpersandChars(const char * src,pfc::string_base & out) +{ + out.reset(); + while(*src) + { + if (*src=='&') + { + out.add_string("&&&"); + src++; + while(*src=='&') + { + out.add_string("&&"); + src++; + } + } + else out.add_byte(*(src++)); + } +} + +void SHARED_EXPORT uFixAmpersandChars_v2(const char * src,pfc::string_base & out) +{ + out.reset(); + while(*src) + { + if (*src=='&') + { + out.add_string("&&"); + src++; + } + else out.add_byte(*(src++)); + } +} + +static BOOL run_action(DWORD action,NOTIFYICONDATA * data) +{ + if (Shell_NotifyIcon(action,data)) return TRUE; + if (action==NIM_MODIFY) + { + if (Shell_NotifyIcon(NIM_ADD,data)) return TRUE; + } + return FALSE; +} + +extern "C" +{ + + BOOL SHARED_EXPORT uShellNotifyIcon(DWORD action,HWND wnd,UINT id,UINT callbackmsg,HICON icon,const char * tip) + { + NOTIFYICONDATA nid = {}; + nid.cbSize = sizeof(nid); + nid.hWnd = wnd; + nid.uID = id; + nid.uFlags = 0; + if (callbackmsg) + { + nid.uFlags |= NIF_MESSAGE; + nid.uCallbackMessage = callbackmsg; + } + if (icon) + { + nid.uFlags |= NIF_ICON; + nid.hIcon = icon; + } + if (tip) + { + nid.uFlags |= NIF_TIP; + pfc::stringToBuffer(nid.szTip,pfc::stringcvt::string_os_from_utf8(tip)); + } + + return run_action(action,&nid); + } + + BOOL SHARED_EXPORT uShellNotifyIconEx(DWORD action,HWND wnd,UINT id,UINT callbackmsg,HICON icon,const char * tip,const char * balloon_title,const char * balloon_msg) + { + NOTIFYICONDATA nid = {}; + nid.cbSize = sizeof(nid); + nid.hWnd = wnd; + nid.uID = id; + if (callbackmsg) + { + nid.uFlags |= NIF_MESSAGE; + nid.uCallbackMessage = callbackmsg; + } + if (icon) + { + nid.uFlags |= NIF_ICON; + nid.hIcon = icon; + } + if (tip) + { + nid.uFlags |= NIF_TIP; + pfc::stringToBuffer(nid.szTip,pfc::stringcvt::string_os_from_utf8(tip)); + } + + nid.dwInfoFlags = NIIF_INFO | NIIF_NOSOUND; + //if (balloon_title || balloon_msg) + { + nid.uFlags |= NIF_INFO; + if (balloon_title) pfc::stringToBuffer(nid.szInfoTitle,pfc::stringcvt::string_os_from_utf8(balloon_title)); + if (balloon_msg) pfc::stringToBuffer(nid.szInfo,pfc::stringcvt::string_os_from_utf8(balloon_msg)); + } + return run_action(action,&nid); + } + +}//extern "C" diff --git a/sdk/foobar2000/shared/text_drawing.cpp b/sdk/foobar2000/shared/text_drawing.cpp new file mode 100644 index 0000000..a728614 --- /dev/null +++ b/sdk/foobar2000/shared/text_drawing.cpp @@ -0,0 +1,230 @@ +#include "shared.h" + + +static bool is_rect_null(const RECT * r) +{ + return r->right <= r->left || r->bottom <= r->top; +} + +UINT SHARED_EXPORT uGetTextHeight(HDC dc) +{ + TEXTMETRIC tm; + POINT pt[2]; + GetTextMetrics(dc,&tm); + pt[0].x = 0; + pt[0].y = tm.tmHeight; + pt[1].x = 0; + pt[1].y = 0; + LPtoDP(dc,pt,2); + + int ret = pt[0].y - pt[1].y; + return ret > 1 ? (unsigned)ret : 1; +} + +static int get_text_width(HDC dc,const TCHAR * src,int len) +{ + if (len<=0) return 0; + else + { + SIZE goatse; + GetTextExtentPoint32(dc,src,len,&goatse); + return goatse.cx; + } +} + +//GetTextExtentPoint32 wrapper, removes color marks +static int get_text_width_color(HDC dc,const TCHAR * src,int len) +{ + int ptr = 0; + int start = 0; + int rv = 0; + if (len<0) len = (int) _tcslen(src); + while(ptrright<=pos_x || clip->bottom<=pos_y) return TRUE; + } + SetTextAlign(dc,TA_LEFT); + SetBkMode(dc,TRANSPARENT); + SetTextColor(dc,selected ? 0xFFFFFF - default_color : default_color); + + int title_ptr = 0; + int textout_start = 0; + int position = pos_x;//item.left+BORDER; + + for(;;) + { + if (title_ptr>=len || src[title_ptr]==3) + { + if (title_ptr>textout_start) + { + int width = get_text_width(dc,src+textout_start,title_ptr-textout_start); + ExtTextOut(dc,position,pos_y,clip ? ETO_CLIPPED : 0,clip,src+textout_start,title_ptr-textout_start,0); + position += width; + textout_start = title_ptr; + } + if (title_ptr>=len) break; + } + if (src[title_ptr]==3) + { + DWORD new_color; + DWORD new_inverted; + bool have_inverted = false; + + if (src[title_ptr+1]==3) {new_color=default_color;title_ptr+=2;} + else + { + title_ptr++; + new_color = _tcstoul(src+title_ptr,0,16); + while(title_ptrtop + (item->bottom-item->top - (int)uGetTextHeight(dc)) / 2; + + int n_tabs = 0; + int total_width = 0; + { + int start = 0; + int n; + for(n=0;nright - item->left; + if (!columns) tab_total -= total_width; + int ptr = display_len; + int tab_ptr = 0; + int written = 0; + int clip_x = item->right; + do + { + int ptr_end = ptr; + while(ptr>0 && display[ptr-1]!='\t') ptr--; + const TCHAR * t_string = display + ptr; + int t_length = ptr_end - ptr; + if (t_length>0) + { + int t_width = get_text_width_color(dc,t_string,t_length) + border*2; + + int pos_x; + int pos_x_right; + + if (!columns) + { + pos_x_right = item->right - MulDiv(tab_ptr,tab_total,n_tabs) - written; + } + else + { + if (tab_ptr==0) pos_x_right = item->right; + else pos_x_right = item->right - MulDiv(tab_ptr,tab_total,n_tabs) + t_width; + } + + if (ptr==0) + { + pos_x = item->left; + } + else + { + pos_x = pos_x_right - t_width ; + if (pos_xleft) pos_x = item->left; + } + + RECT t_clip = clip; + + if (t_clip.right > clip_x) t_clip.right = clip_x; + + text_out_colors(dc,t_string,t_length,pos_x+border,pos_y,&t_clip,selected,default_color); + + if (clip_x>pos_x) clip_x = pos_x; + + written += t_width; + } + + if (ptr>0) + { + ptr--;//tab char + tab_ptr++; + } + } + while(ptr>0); + + return TRUE; +} + +extern "C" { + +BOOL SHARED_EXPORT uTextOutColors(HDC dc,const char * p_text,UINT len,int x,int y,const RECT * clip,BOOL is_selected,DWORD default_color) +{ + try { + pfc::stringcvt::string_os_from_utf8 temp(p_text); + return text_out_colors(dc,temp,pfc::downcast_guarded(temp.length()),x,y,clip,!!is_selected,default_color); + } catch(...) {return FALSE;} +} + +BOOL SHARED_EXPORT uTextOutColorsTabbed(HDC dc,const char * p_text,UINT len,const RECT * item,int border,const RECT * clip,BOOL selected,DWORD default_color,BOOL use_columns) +{ + try { + pfc::stringcvt::string_os_from_utf8 temp(p_text); + return text_out_colors_tab(dc,temp,pfc::downcast_guarded(temp.length()),item,border,clip,!!selected,default_color,!!use_columns); + } catch(...) {return FALSE;} +} + +} \ No newline at end of file diff --git a/sdk/foobar2000/shared/utf8.cpp b/sdk/foobar2000/shared/utf8.cpp new file mode 100644 index 0000000..18f8793 --- /dev/null +++ b/sdk/foobar2000/shared/utf8.cpp @@ -0,0 +1,226 @@ +#include "shared.h" + +#include + +using namespace pfc; + +extern "C" { + +#if 0 +inline static unsigned q_tolower(unsigned c) +{ + if (c>='A' && c<='Z') c += 'a' - 'A'; + return c; +} +#else +static const t_uint8 ascii_tolower_table[128] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F}; +#define q_tolower(c) ascii_tolower_table[(unsigned)c] +#endif + + +unsigned SHARED_EXPORT uCharLower(unsigned param) +{ + return pfc::charLower(param); +} + +unsigned SHARED_EXPORT uCharUpper(unsigned param) +{ + return pfc::charUpper(param); +} + +static inline int compare_wchar(unsigned c1,unsigned c2) throw() +{ + if (c1==c2) return 0; + c1 = pfc::charLower(c1); + c2 = pfc::charLower(c2); + if (c1c2) return 1; + else return 0; +} + + +int SHARED_EXPORT stricmp_utf8(const char * p1,const char * p2) throw() +{ + for(;;) + { + if (*p1>=0 && *p2>=0)//signed char + { + unsigned c1 = q_tolower(*p1), c2 = q_tolower(*p2); + if (c1c2) return 1; + else if (c1 == 0) return 0; + else + { + p1++; + p2++; + } + } + else + { + unsigned w1,w2; t_size d1,d2; + d1 = utf8_decode_char(p1,w1); + d2 = utf8_decode_char(p2,w2); + if (d1 == 0 && d2 == 0) return 0; + else if (d1==0) return -1; + else if (d2==0) return 1; + int rv = compare_wchar(w1,w2); + if (rv) return rv; + p1 += d1; + p2 += d2; + } + } +} + +int SHARED_EXPORT stricmp_utf8_stringtoblock(const char * p1,const char * p2,t_size p2_bytes) throw() +{ + return stricmp_utf8_ex(p1,-1,p2,p2_bytes); +} + +int SHARED_EXPORT stricmp_utf8_partial(const char * p1,const char * p2,t_size num) throw() +{ + for(;num;) + { + unsigned w1,w2; t_size d1,d2; + d1 = utf8_decode_char(p1,w1); + d2 = utf8_decode_char(p2,w2); + if (w2==0 || d2==0) return 0; + int rv = compare_wchar(w1,w2); + if (rv) return rv; + p1 += d1; + p2 += d2; + num--; + } + return 0; +} + +int SHARED_EXPORT stricmp_utf8_max(const char * p1,const char * p2,t_size p1_bytes) throw() +{ + return stricmp_utf8_ex(p1,p1_bytes,p2,-1); +} + +namespace { + typedef bool (*t_replace_test)(const char * src,const char * test,t_size len); + + static bool replace_test_i(const char * src,const char * test,t_size len) + { + return stricmp_utf8_max(src,test,len)==0; + } + + static bool replace_test(const char * src,const char * test,t_size len) + { + t_size ptr; + bool rv = true; + for(ptr=0;ptr0) + { + t_size ptr = 0; + while(ptr+len1<=len) + { + if (testfunc(src+ptr,s1,len1)) + { + count++; + out.add_string(s2,len2); + ptr += len1; + } + else out.add_byte(src[ptr++]); + } + if (ptr0); + assert(c2>0); + char s1[8],s2[8]; + t_size len1,len2; + len1 = utf8_encode_char(c1,s1); + len2 = utf8_encode_char(c2,s2); + return uReplaceString(out,src,src_len,s1,len1,s2,len2,casesens); +} + + +void SHARED_EXPORT uAddStringLower(string_base & out,const char * src,t_size len) +{ + while(len && *src) + { + unsigned c; t_size d; + d = utf8_decode_char(src,c,len); + if (d==0 || d>len) break; + out.add_char(uCharLower(c)); + src+=d; + len-=d; + } +} + +void SHARED_EXPORT uAddStringUpper(string_base & out,const char * src,t_size len) +{ + while(len && *src) + { + unsigned c; t_size d; + d = utf8_decode_char(src,c,len); + if (d==0 || d>len) break; + out.add_char(uCharUpper(c)); + src+=d; + len-=d; + } +} + +int SHARED_EXPORT stricmp_utf8_ex(const char * p1,t_size p1_bytes,const char * p2,t_size p2_bytes) throw() +{ + p1_bytes = strlen_max(p1,p1_bytes); + p2_bytes = strlen_max(p2,p2_bytes); + for(;;) + { + if (p1_bytes == 0 && p2_bytes == 0) return 0; + else if (p1_bytes == 0) return -1; + else if (p2_bytes == 0) return 1; + else if (*p1>0 && *p2>0)//signed char + { + unsigned c1 = q_tolower(*p1), c2 = q_tolower(*p2); + if (c1c2) return 1; + else + { + p1++; + p2++; + p1_bytes--; + p2_bytes--; + } + } + else + { + unsigned w1,w2; + auto d1 = utf8_decode_char(p1,w1,p1_bytes); + auto d2 = utf8_decode_char(p2,w2,p2_bytes); + if (d1==0) return -1; + if (d2==0) return 1; + int rv = compare_wchar(w1,w2); + if (rv) return rv; + p1 += d1; + p2 += d2; + p1_bytes -= d1; + p2_bytes -= d2; + } + } +} + +} diff --git a/sdk/foobar2000/shared/utf8api.cpp b/sdk/foobar2000/shared/utf8api.cpp new file mode 100644 index 0000000..2471b87 --- /dev/null +++ b/sdk/foobar2000/shared/utf8api.cpp @@ -0,0 +1,1186 @@ +#include "shared.h" + + +#include + +#ifndef BIF_NEWDIALOGSTYLE +#define BIF_NEWDIALOGSTYLE 0x0040 +#endif + +using namespace pfc; + +class param_os_from_utf8 +{ + bool m_is_null; + WORD m_low_word; + stringcvt::string_os_from_utf8 m_cvt; +public: + param_os_from_utf8(const char * p) : + m_is_null(p==NULL), + m_low_word( ((t_size)p & ~0xFFFF) == 0 ? (WORD)((t_size)p & 0xFFFF) : 0), + m_cvt( p != NULL && ((t_size)p & ~0xFFFF) != 0 ? p : "") + {} + operator const TCHAR *() + { + return get_ptr(); + } + const TCHAR * get_ptr() + { + return m_low_word ? (const TCHAR*)(t_size)m_low_word : m_is_null ? 0 : m_cvt.get_ptr(); + } + +}; + + + +extern "C" { + +LRESULT SHARED_EXPORT uSendMessageText(HWND wnd,UINT msg,WPARAM wp,const char * p_text) +{ + if (p_text == NULL) + return SendMessage(wnd,msg,wp,0); + else { + stringcvt::string_os_from_utf8 temp; + temp.convert(p_text); + return SendMessage(wnd,msg,wp,(LPARAM)temp.get_ptr()); + } +} + +LRESULT SHARED_EXPORT uSendDlgItemMessageText(HWND wnd,UINT id,UINT msg,WPARAM wp,const char * text) +{ + return uSendMessageText(uGetDlgItem(wnd,id),msg,wp,text);//SendDlgItemMessage(wnd,id,msg,wp,(long)(const TCHAR*)string_os_from_utf8(text)); +} + +BOOL SHARED_EXPORT uGetWindowText(HWND wnd,string_base & out) +{ + PFC_ASSERT( wnd != NULL ); + int len = GetWindowTextLength(wnd); + if (len>0) + { + len++; + pfc::array_t temp; + temp.set_size(len); + temp[0]=0; + if (GetWindowText(wnd,temp.get_ptr(),len)>0) + { + out = stringcvt::string_utf8_from_os(temp.get_ptr(),len); + return TRUE; + } + else return FALSE; + } + else + { + out.reset(); + return TRUE; + } +} + +BOOL SHARED_EXPORT uSetWindowTextEx(HWND wnd,const char * p_text,size_t p_text_length) +{ + return SetWindowText(wnd,stringcvt::string_os_from_utf8(p_text, p_text_length)); +} + + +BOOL SHARED_EXPORT uGetDlgItemText(HWND wnd,UINT id,string_base & out) +{ + return uGetWindowText(GetDlgItem(wnd,id),out); +} + +BOOL SHARED_EXPORT uSetDlgItemTextEx(HWND wnd,UINT id,const char * p_text,size_t p_text_length) +{ + return SetDlgItemText(wnd,id,stringcvt::string_os_from_utf8(p_text,p_text_length)); +} + +int SHARED_EXPORT uMessageBox(HWND wnd,const char * text,const char * caption,UINT type) +{ + modal_dialog_scope scope(wnd); + return MessageBox(wnd,param_os_from_utf8(text),param_os_from_utf8(caption),type); +} + +void SHARED_EXPORT uOutputDebugString(const char * msg) {OutputDebugString(stringcvt::string_os_from_utf8(msg));} + +BOOL SHARED_EXPORT uAppendMenu(HMENU menu,UINT flags,UINT_PTR id,const char * content) +{ + return AppendMenu(menu,flags,id,param_os_from_utf8(content)); +} + +BOOL SHARED_EXPORT uInsertMenu(HMENU menu,UINT position,UINT flags,UINT_PTR id,const char * content) +{ + return InsertMenu(menu,position,flags,id,param_os_from_utf8(content)); +} + +int SHARED_EXPORT uCharCompare(t_uint32 p_char1,t_uint32 p_char2) { +#ifdef UNICODE + wchar_t temp1[4],temp2[4]; + temp1[utf16_encode_char(p_char1,temp1)]=0; + temp2[utf16_encode_char(p_char2,temp2)]=0; + return lstrcmpiW(temp1,temp2); +#else + wchar_t temp1[4],temp2[4]; + char ctemp1[20],ctemp2[20]; + temp1[utf16_encode_char(p_char1,temp1)]=0; + temp2[utf16_encode_char(p_char2,temp2)]=0; + WideCharToMultiByte(CP_ACP,0,temp1,-1,ctemp1,_countof(ctemp1),0,0); + WideCharToMultiByte(CP_ACP,0,temp2,-1,ctemp2,_countof(ctemp2),0,0); + return lstrcmpiA(ctemp1,ctemp2); +#endif +} + +int SHARED_EXPORT uStringCompare(const char * elem1, const char * elem2) { + for(;;) { + unsigned c1,c2; t_size l1,l2; + l1 = utf8_decode_char(elem1,c1); + l2 = utf8_decode_char(elem2,c2); + if (l1==0 && l2==0) return 0; + if (c1!=c2) { + int test = uCharCompare(c1,c2); + if (test) return test; + } + elem1 += l1; + elem2 += l2; + } +} + +int SHARED_EXPORT uStringCompare_ConvertNumbers(const char * elem1,const char * elem2) { + for(;;) { + if (pfc::char_is_numeric(*elem1) && pfc::char_is_numeric(*elem2)) { + t_size delta1 = 1, delta2 = 1; + while(pfc::char_is_numeric(elem1[delta1])) delta1++; + while(pfc::char_is_numeric(elem2[delta2])) delta2++; + int test = pfc::compare_t(pfc::atoui64_ex(elem1,delta1),pfc::atoui64_ex(elem2,delta2)); + if (test != 0) return test; + elem1 += delta1; + elem2 += delta2; + } else { + unsigned c1,c2; t_size l1,l2; + l1 = utf8_decode_char(elem1,c1); + l2 = utf8_decode_char(elem2,c2); + if (l1==0 && l2==0) return 0; + if (c1!=c2) { + int test = uCharCompare(c1,c2); + if (test) return test; + } + elem1 += l1; + elem2 += l2; + } + } +} + +HINSTANCE SHARED_EXPORT uLoadLibrary(const char * name) +{ + return LoadLibrary(param_os_from_utf8(name)); +} + +HANDLE SHARED_EXPORT uCreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset,BOOL bInitialState, const char * lpName) +{ + return CreateEvent(lpEventAttributes,bManualReset,bInitialState, param_os_from_utf8(lpName)); +} + +DWORD SHARED_EXPORT uGetModuleFileName(HMODULE hMod,string_base & out) +{ + try { + pfc::array_t buffer; buffer.set_size(256); + for(;;) { + DWORD ret = GetModuleFileName(hMod,buffer.get_ptr(), (DWORD)buffer.get_size()); + if (ret == 0) return 0; + if (ret < buffer.get_size()) break; + buffer.set_size(buffer.get_size() * 2); + } + out = stringcvt::string_utf8_from_os(buffer.get_ptr(),buffer.get_size()); + return (DWORD) out.length(); + } catch(...) { + return 0; + } +} + +BOOL SHARED_EXPORT uSetClipboardRawData(UINT format,const void * ptr,t_size size) { + try { + HANDLE buffer = GlobalAlloc(GMEM_DDESHARE,size); + if (buffer == NULL) throw std::bad_alloc(); + try { + CGlobalLockScope lock(buffer); + PFC_ASSERT(lock.GetSize() == size); + memcpy(lock.GetPtr(),ptr,size); + } catch(...) { + GlobalFree(buffer); throw; + } + + if (SetClipboardData(format,buffer) == NULL) throw pfc::exception_bug_check(); + return TRUE; + } catch(...) { + return FALSE; + } +} +BOOL SHARED_EXPORT uSetClipboardString(const char * ptr) +{ + try { + CClipboardOpenScope scope; + if (!scope.Open(NULL)) return FALSE; + EmptyClipboard(); + stringcvt::string_os_from_utf8 temp(ptr); + return uSetClipboardRawData( + #ifdef UNICODE + CF_UNICODETEXT + #else + CF_TEXT + #endif + ,temp.get_ptr(), (temp.length() + 1) * sizeof(TCHAR)); + } catch(...) { + return FALSE; + } +} + +BOOL SHARED_EXPORT uGetClipboardString(pfc::string_base & p_out) { + try { + CClipboardOpenScope scope; + if (!scope.Open(NULL)) return FALSE; + HANDLE data = GetClipboardData( + #ifdef UNICODE + CF_UNICODETEXT + #else + CF_TEXT + #endif + ); + if (data == NULL) return FALSE; + + CGlobalLockScope lock(data); + p_out = pfc::stringcvt::string_utf8_from_os( (const TCHAR*) lock.GetPtr(), lock.GetSize() / sizeof(TCHAR) ); + return TRUE; + } catch(...) { + return FALSE; + } +} + + +BOOL SHARED_EXPORT uGetClassName(HWND wnd,string_base & out) +{ + TCHAR temp[512]; + temp[0]=0; + if (GetClassName(wnd,temp,_countof(temp))>0) + { + out = stringcvt::string_utf8_from_os(temp,_countof(temp)); + return TRUE; + } + else return FALSE; +} + +t_size SHARED_EXPORT uCharLength(const char * src) {return utf8_char_len(src);} + +BOOL SHARED_EXPORT uDragQueryFile(HDROP hDrop,UINT idx,string_base & out) +{ + UINT len = DragQueryFile(hDrop,idx,0,0); + if (len>0 && len!=(UINT)(~0)) + { + len++; + array_t temp; + temp.set_size(len); + temp[0] =0 ; + if (DragQueryFile(hDrop,idx,temp.get_ptr(),len)>0) + { + out = stringcvt::string_utf8_from_os(temp.get_ptr(),len); + return TRUE; + } + } + return FALSE; +} + +UINT SHARED_EXPORT uDragQueryFileCount(HDROP hDrop) +{ + return DragQueryFile(hDrop,-1,0,0); +} + + + +BOOL SHARED_EXPORT uGetTextExtentPoint32(HDC dc,const char * text,UINT cb,LPSIZE size) +{ + stringcvt::string_os_from_utf8 temp(text,cb); + return GetTextExtentPoint32(dc,temp,pfc::downcast_guarded(temp.length()),size); +} + +BOOL SHARED_EXPORT uExtTextOut(HDC dc,int x,int y,UINT flags,const RECT * rect,const char * text,UINT cb,const int * lpdx) +{ + stringcvt::string_os_from_utf8 temp(text,cb); + return ExtTextOut(dc,x,y,flags,rect,temp,pfc::downcast_guarded(_tcslen(temp)),lpdx); +} + +static UINT_PTR CALLBACK choose_color_hook(HWND wnd,UINT msg,WPARAM wp,LPARAM lp) +{ + switch(msg) + { + case WM_INITDIALOG: + { + CHOOSECOLOR * cc = reinterpret_cast(lp); + reinterpret_cast(cc->lCustData)->initialize(FindOwningPopup(wnd)); + } + return 0; + default: + return 0; + } +} + +BOOL SHARED_EXPORT uChooseColor(DWORD * p_color,HWND parent,DWORD * p_custom_colors) +{ + modal_dialog_scope scope; + + CHOOSECOLOR cc = {}; + cc.lStructSize = sizeof(cc); + cc.hwndOwner = parent; + cc.rgbResult = *p_color; + cc.lpCustColors = p_custom_colors; + cc.Flags = CC_ANYCOLOR|CC_FULLOPEN|CC_RGBINIT|CC_ENABLEHOOK; + cc.lpfnHook = choose_color_hook; + cc.lCustData = reinterpret_cast(&scope); + BOOL rv = ChooseColor(&cc); + if (rv) + { + *p_color = cc.rgbResult; + return TRUE; + } + else return FALSE; +} + +HCURSOR SHARED_EXPORT uLoadCursor(HINSTANCE hIns,const char * name) +{ + return LoadCursor(hIns,param_os_from_utf8(name)); +} + +HICON SHARED_EXPORT uLoadIcon(HINSTANCE hIns,const char * name) +{ + return LoadIcon(hIns,param_os_from_utf8(name)); +} + +HMENU SHARED_EXPORT uLoadMenu(HINSTANCE hIns,const char * name) +{ + return LoadMenu(hIns,param_os_from_utf8(name)); +} + + + +BOOL SHARED_EXPORT uGetEnvironmentVariable(const char * name,string_base & out) +{ + stringcvt::string_os_from_utf8 name_t(name); + DWORD size = GetEnvironmentVariable(name_t,0,0); + if (size>0) + { + size++; + array_t temp; + temp.set_size(size); + temp[0]=0; + if (GetEnvironmentVariable(name_t,temp.get_ptr(),size)>0) + { + out = stringcvt::string_utf8_from_os(temp.get_ptr(),size); + return TRUE; + } + } + return FALSE; +} + +HMODULE SHARED_EXPORT uGetModuleHandle(const char * name) +{ + return GetModuleHandle(param_os_from_utf8(name)); +} + +UINT SHARED_EXPORT uRegisterWindowMessage(const char * name) +{ + return RegisterWindowMessage(stringcvt::string_os_from_utf8(name)); +} + +BOOL SHARED_EXPORT uMoveFile(const char * src,const char * dst) +{ + return MoveFile(stringcvt::string_os_from_utf8(src),stringcvt::string_os_from_utf8(dst)); +} + +BOOL SHARED_EXPORT uDeleteFile(const char * fn) +{ + return DeleteFile(stringcvt::string_os_from_utf8(fn)); +} + +DWORD SHARED_EXPORT uGetFileAttributes(const char * fn) +{ + PFC_ASSERT( ! pfc::string_has_prefix_i( fn, "file://" ) ); + return GetFileAttributes(stringcvt::string_os_from_utf8(fn)); +} + +BOOL SHARED_EXPORT uRemoveDirectory(const char * fn) +{ + return RemoveDirectory(stringcvt::string_os_from_utf8(fn)); +} + +HANDLE SHARED_EXPORT uCreateFile(const char * fn,DWORD access,DWORD share,LPSECURITY_ATTRIBUTES blah,DWORD creat,DWORD flags,HANDLE tmpl) +{ + return CreateFile(stringcvt::string_os_from_utf8(fn),access,share,blah,creat,flags,tmpl); +} + +BOOL SHARED_EXPORT uCreateDirectory(const char * fn,LPSECURITY_ATTRIBUTES blah) +{ + return CreateDirectory(stringcvt::string_os_from_utf8(fn),blah); +} + +HANDLE SHARED_EXPORT uCreateMutex(LPSECURITY_ATTRIBUTES blah,BOOL bInitialOwner,const char * name) +{ + return name ? CreateMutex(blah,bInitialOwner,stringcvt::string_os_from_utf8(name)) : CreateMutex(blah,bInitialOwner,0); +} + +BOOL SHARED_EXPORT uGetFullPathName(const char * name,string_base & out) +{ + stringcvt::string_os_from_utf8 name_os(name); + unsigned len = GetFullPathName(name_os,0,0,0); + if (len==0) return FALSE; + array_t temp; + temp.set_size(len+1); + TCHAR * blah; + if (GetFullPathName(name_os,len+1,temp.get_ptr(),&blah)==0) return FALSE; + out = stringcvt::string_utf8_from_os(temp.get_ptr(),len); + return TRUE; +} + +BOOL SHARED_EXPORT uGetLongPathName(const char * name,string_base & out) +{ + TCHAR temp[4096]; + temp[0]=0; + BOOL state = GetLongPathName(stringcvt::string_os_from_utf8(name),temp,_countof(temp)); + if (state) out = stringcvt::string_utf8_from_os(temp,_countof(temp)); + return state; +} + +void SHARED_EXPORT uGetCommandLine(string_base & out) +{ + out = stringcvt::string_utf8_from_os(GetCommandLine()); +} + +BOOL SHARED_EXPORT uGetTempPath(string_base & out) +{ + TCHAR temp[MAX_PATH+1]; + temp[0]=0; + if (GetTempPath(_countof(temp),temp)) + { + out = stringcvt::string_utf8_from_os(temp,_countof(temp)); + return TRUE; + } + return FALSE; + +} +BOOL SHARED_EXPORT uGetTempFileName(const char * path_name,const char * prefix,UINT unique,string_base & out) +{ + if (path_name==0 || prefix==0) return FALSE; + TCHAR temp[MAX_PATH+1]; + temp[0]=0; + if (GetTempFileName(stringcvt::string_os_from_utf8(path_name),stringcvt::string_os_from_utf8(prefix),unique,temp)) + { + out = stringcvt::string_utf8_from_os(temp,_countof(temp)); + return TRUE; + } + return FALSE; +} + +class uFindFile_i : public uFindFile +{ + string8 fn; + WIN32_FIND_DATA fd; + HANDLE hFF; +public: + uFindFile_i() {hFF = INVALID_HANDLE_VALUE;} + bool FindFirst(const char * path) + { + hFF = FindFirstFile(stringcvt::string_os_from_utf8(path),&fd); + if (hFF==INVALID_HANDLE_VALUE) return false; + fn = stringcvt::string_utf8_from_os(fd.cFileName,_countof(fd.cFileName)); + return true; + } + virtual BOOL FindNext() + { + if (hFF==INVALID_HANDLE_VALUE) return FALSE; + BOOL rv = FindNextFile(hFF,&fd); + if (rv) fn = stringcvt::string_utf8_from_os(fd.cFileName,_countof(fd.cFileName)); + return rv; + } + + virtual const char * GetFileName() + { + return fn; + } + + virtual t_uint64 GetFileSize() + { + union + { + t_uint64 val64; + struct + { + DWORD lo,hi; + }; + } ret; + + ret.hi = fd.nFileSizeHigh; + ret.lo = fd.nFileSizeLow; + return ret.val64; + + } + virtual DWORD GetAttributes() + { + return fd.dwFileAttributes; + } + + virtual FILETIME GetCreationTime() + { + return fd.ftCreationTime; + } + virtual FILETIME GetLastAccessTime() + { + return fd.ftLastAccessTime; + } + virtual FILETIME GetLastWriteTime() + { + return fd.ftLastWriteTime; + } + virtual ~uFindFile_i() + { + if (hFF!=INVALID_HANDLE_VALUE) FindClose(hFF); + } +}; + +puFindFile SHARED_EXPORT uFindFirstFile(const char * path) +{ + pfc::ptrholder_t ptr = new uFindFile_i; + if (!ptr->FindFirst(path)) { + ptr.release(); + return NULL; + } else { + return ptr.detach(); + } +} + +HINSTANCE SHARED_EXPORT uShellExecute(HWND wnd,const char * oper,const char * file,const char * params,const char * dir,int cmd) +{ + modal_dialog_scope modal; // IDIOCY - ShellExecute may spawn a modal dialog + if (wnd) modal.initialize(wnd); + return ShellExecute(wnd,param_os_from_utf8(oper),param_os_from_utf8(file),param_os_from_utf8(params),param_os_from_utf8(dir),cmd); +} + +HWND SHARED_EXPORT uCreateStatusWindow(LONG style,const char * text,HWND parent,UINT id) +{ + return CreateStatusWindow(style,param_os_from_utf8(text),parent,id); +} + +HWND SHARED_EXPORT uCreateWindowEx(DWORD dwExStyle,const char * lpClassName,const char * lpWindowName,DWORD dwStyle,int x,int y,int nWidth,int nHeight,HWND hWndParent,HMENU hMenu,HINSTANCE hInstance,LPVOID lpParam) +{ + return CreateWindowEx(dwExStyle,param_os_from_utf8(lpClassName),param_os_from_utf8(lpWindowName),dwStyle,x,y,nWidth,nHeight,hWndParent,hMenu,hInstance,lpParam); +} + +HANDLE SHARED_EXPORT uLoadImage(HINSTANCE hIns,const char * name,UINT type,int x,int y,UINT flags) +{ + return LoadImage(hIns,param_os_from_utf8(name),type,x,y,flags); +} + +BOOL SHARED_EXPORT uGetSystemDirectory(string_base & out) +{ + UINT len = GetSystemDirectory(0,0); + if (len==0) len = MAX_PATH; + len++; + array_t temp; + temp.set_size(len); + if (GetSystemDirectory(temp.get_ptr(),len)==0) return FALSE; + out = stringcvt::string_utf8_from_os(temp.get_ptr(),len); + return TRUE; +} + +BOOL SHARED_EXPORT uGetWindowsDirectory(string_base & out) +{ + UINT len = GetWindowsDirectory(0,0); + if (len==0) len = MAX_PATH; + len++; + array_t temp; + temp.set_size(len); + if (GetWindowsDirectory(temp.get_ptr(),len)==0) return FALSE; + out = stringcvt::string_utf8_from_os(temp.get_ptr(),len); + return TRUE; +} + +BOOL SHARED_EXPORT uSetCurrentDirectory(const char * path) +{ + return SetCurrentDirectory(stringcvt::string_os_from_utf8(path)); +} + +BOOL SHARED_EXPORT uGetCurrentDirectory(string_base & out) +{ + UINT len = GetCurrentDirectory(0,0); + if (len==0) len = MAX_PATH; + len++; + array_t temp; + temp.set_size(len); + if (GetCurrentDirectory(len,temp.get_ptr())==0) return FALSE; + out = stringcvt::string_utf8_from_os(temp.get_ptr(),len); + return TRUE; +} + +BOOL SHARED_EXPORT uExpandEnvironmentStrings(const char * src,string_base & out) +{ + stringcvt::string_os_from_utf8 src_os(src); + UINT len = ExpandEnvironmentStrings(src_os,0,0); + if (len==0) len = 256; + len++; + array_t temp; + temp.set_size(len); + if (ExpandEnvironmentStrings(src_os,temp.get_ptr(),len)==0) return FALSE; + out = stringcvt::string_utf8_from_os(temp.get_ptr(),len); + return TRUE; +} + +BOOL SHARED_EXPORT uGetUserName(string_base & out) +{ + TCHAR temp[UNLEN+1]; + DWORD len = _countof(temp); + if (GetUserName(temp,&len)) + { + out = stringcvt::string_utf8_from_os(temp,_countof(temp)); + return TRUE; + } + else return FALSE; +} + +BOOL SHARED_EXPORT uGetShortPathName(const char * src,string_base & out) +{ + stringcvt::string_os_from_utf8 src_os(src); + UINT len = GetShortPathName(src_os,0,0); + if (len==0) len = MAX_PATH; + len++; + array_t temp; temp.set_size(len); + if (GetShortPathName(src_os,temp.get_ptr(),len)) + { + out = stringcvt::string_utf8_from_os(temp.get_ptr(),len); + return TRUE; + } + else return FALSE; +} + + +#ifdef UNICODE +#define DDE_CODEPAGE CP_WINUNICODE +#else +#define DDE_CODEPAGE CP_WINANSI +#endif + + +HSZ SHARED_EXPORT uDdeCreateStringHandle(DWORD ins,const char * src) +{ + return DdeCreateStringHandle(ins,stringcvt::string_os_from_utf8(src),DDE_CODEPAGE); +} + +BOOL SHARED_EXPORT uDdeQueryString(DWORD ins,HSZ hsz,string_base & out) +{ + array_t temp; + UINT len = DdeQueryString(ins,hsz,0,0,DDE_CODEPAGE); + if (len==0) len = MAX_PATH; + len++; + temp.set_size(len); + if (DdeQueryString(ins,hsz,temp.get_ptr(),len,DDE_CODEPAGE)) + { + out = stringcvt::string_utf8_from_os(temp.get_ptr(),len); + return TRUE; + } + else return FALSE; +} + +UINT SHARED_EXPORT uDdeInitialize(LPDWORD pidInst,PFNCALLBACK pfnCallback,DWORD afCmd,DWORD ulRes) +{ + return DdeInitialize(pidInst,pfnCallback,afCmd,ulRes); +} + +BOOL SHARED_EXPORT uDdeAccessData_Text(HDDEDATA data,string_base & out) +{ + const TCHAR * ptr = (const TCHAR*) DdeAccessData(data,0); + if (ptr) + { + out = stringcvt::string_utf8_from_os(ptr); + return TRUE; + } + else return FALSE; +} + +uSortString_t SHARED_EXPORT uSortStringCreate(const char * src) { + t_size lenEst = pfc::stringcvt::estimate_utf8_to_wide(src,SIZE_MAX); + TCHAR * ret = pfc::__raw_malloc_t(lenEst); + pfc::stringcvt::convert_utf8_to_wide(ret,lenEst,src,SIZE_MAX); + return reinterpret_cast( ret ); +} + +int SHARED_EXPORT uSortStringCompareEx(uSortString_t string1, uSortString_t string2,uint32_t flags) { + return CompareString(LOCALE_USER_DEFAULT,flags,reinterpret_cast(string1),-1,reinterpret_cast(string2),-1); +} + +int SHARED_EXPORT uSortStringCompare(uSortString_t string1, uSortString_t string2) { + return lstrcmpi(reinterpret_cast(string1),reinterpret_cast(string2)); +} + +void SHARED_EXPORT uSortStringFree(uSortString_t string) { + pfc::__raw_free_t(reinterpret_cast(string)); +} + +HTREEITEM SHARED_EXPORT uTreeView_InsertItem(HWND wnd,const uTVINSERTSTRUCT * param) +{ + stringcvt::string_os_from_utf8 temp; + temp.convert(param->item.pszText); + + + TVINSERTSTRUCT l_param = {}; + l_param.hParent = param->hParent; + l_param.hInsertAfter = param->hInsertAfter; + l_param.item.mask = param->item.mask; + l_param.item.hItem = param->item.hItem; + l_param.item.state = param->item.state; + l_param.item.stateMask = param->item.stateMask; + l_param.item.pszText = const_cast(temp.get_ptr()); + l_param.item.cchTextMax = 0; + l_param.item.iImage = param->item.iImage; + l_param.item.iSelectedImage = param->item.iImage; + l_param.item.cChildren = param->item.cChildren; + l_param.item.lParam = param->item.lParam; + if (param->item.mask & TVIF_INTEGRAL) + { + l_param.itemex.iIntegral = param->itemex.iIntegral; + } + + return (HTREEITEM) uSendMessage(wnd,TVM_INSERTITEM,0,(LPARAM)&l_param); +} + +UINT SHARED_EXPORT uGetFontHeight(HFONT font) +{ + UINT ret; + HDC dc = CreateCompatibleDC(0); + SelectObject(dc,font); + ret = uGetTextHeight(dc); + DeleteDC(dc); + return ret; +} + + +HIMAGELIST SHARED_EXPORT uImageList_LoadImage(HINSTANCE hi, const char * lpbmp, int cx, int cGrow, COLORREF crMask, UINT uType, UINT uFlags) +{ + return ImageList_LoadImage(hi,param_os_from_utf8(lpbmp),cx,cGrow,crMask,uType,uFlags); +} + +int SHARED_EXPORT uTabCtrl_InsertItem(HWND wnd,t_size idx,const uTCITEM * item) +{ + param_os_from_utf8 text((item->mask & TCIF_TEXT) ? item->pszText : 0); + TCITEM l_item; + assert(sizeof(l_item)==sizeof(*item));//meh lazy + memcpy(&l_item,item,sizeof(l_item)); + l_item.pszText = const_cast(text.get_ptr()); + l_item.cchTextMax = 0; + return TabCtrl_InsertItem(wnd,idx,&l_item); +} + +int SHARED_EXPORT uTabCtrl_SetItem(HWND wnd,t_size idx,const uTCITEM * item) +{ + param_os_from_utf8 text((item->mask & TCIF_TEXT) ? item->pszText : 0); + TCITEM l_item; + PFC_STATIC_ASSERT(sizeof(l_item)==sizeof(*item));//meh lazy + memcpy(&l_item,item,sizeof(l_item)); + l_item.pszText = const_cast(text.get_ptr()); + l_item.cchTextMax = 0; + return TabCtrl_SetItem(wnd,idx,&l_item); +} + +int SHARED_EXPORT uGetKeyNameText(LONG lparam,string_base & out) +{ + TCHAR temp[256]; + temp[0]=0; + if (!GetKeyNameText(lparam,temp,_countof(temp))) return 0; + out = stringcvt::string_utf8_from_os(temp,_countof(temp)); + return 1; +} + +HANDLE SHARED_EXPORT uCreateFileMapping(HANDLE hFile,LPSECURITY_ATTRIBUTES lpFileMappingAttributes,DWORD flProtect,DWORD dwMaximumSizeHigh,DWORD dwMaximumSizeLow,const char * lpName) +{ + return CreateFileMapping(hFile,lpFileMappingAttributes,flProtect,dwMaximumSizeHigh,dwMaximumSizeLow,param_os_from_utf8(lpName)); +} + +BOOL SHARED_EXPORT uListBox_GetText(HWND listbox,UINT index,string_base & out) +{ + t_size len = uSendMessage(listbox,LB_GETTEXTLEN,index,0); + if (len==LB_ERR || len>16*1024*1024) return FALSE; + if (len==0) {out.reset();return TRUE;} + + array_t temp; temp.set_size(len+1); + pfc::memset_t(temp,(TCHAR)0); + len = uSendMessage(listbox,LB_GETTEXT,index,(LPARAM)temp.get_ptr()); + if (len==LB_ERR) return false; + out = stringcvt::string_utf8_from_os(temp.get_ptr()); + return TRUE; +} +/* +void SHARED_EXPORT uPrintf(string_base & out,const char * fmt,...) +{ + va_list list; + va_start(list,fmt); + uPrintfV(out,fmt,list); + va_end(list); +} +*/ +void SHARED_EXPORT uPrintfV(string_base & out,const char * fmt,va_list arglist) +{ + pfc::string_printf_here_va(out, fmt, arglist); +} + +int SHARED_EXPORT uCompareString(DWORD flags,const char * str1,size_t len1,const char * str2,size_t len2) +{ + return CompareString(LOCALE_USER_DEFAULT,flags,stringcvt::string_os_from_utf8(str1,len1),-1,stringcvt::string_os_from_utf8(str2,len2),-1); +} + +class uResource_i : public uResource +{ + unsigned size; + const void * ptr; +public: + inline uResource_i(const void * p_ptr,unsigned p_size) : ptr(p_ptr), size(p_size) + { + } + virtual const void * GetPointer() + { + return ptr; + } + virtual unsigned GetSize() + { + return size; + } + virtual ~uResource_i() + { + } +}; + +puResource SHARED_EXPORT uLoadResource(HMODULE hMod,const char * name,const char * type,WORD wLang) +{ + HRSRC res = uFindResource(hMod,name,type,wLang); + if (res==0) return 0; + HGLOBAL hglob = LoadResource(hMod,res); + if (hglob) + { + void * ptr = LockResource(hglob); + if (ptr) + { + return new uResource_i(ptr,SizeofResource(hMod,res)); + } + else return 0; + } + else return 0; +} + +puResource SHARED_EXPORT LoadResourceEx(HMODULE hMod,const TCHAR * name,const TCHAR * type,WORD wLang) +{ + HRSRC res = wLang ? FindResourceEx(hMod,type,name,wLang) : FindResource(hMod,name,type); + if (res==0) return 0; + HGLOBAL hglob = LoadResource(hMod,res); + if (hglob) + { + void * ptr = LockResource(hglob); + if (ptr) + { + return new uResource_i(ptr,SizeofResource(hMod,res)); + } + else return 0; + } + else return 0; +} + +HRSRC SHARED_EXPORT uFindResource(HMODULE hMod,const char * name,const char * type,WORD wLang) +{ + return wLang ? FindResourceEx(hMod,param_os_from_utf8(type),param_os_from_utf8(name),wLang) : FindResource(hMod,param_os_from_utf8(name),param_os_from_utf8(type)); +} + +BOOL SHARED_EXPORT uLoadString(HINSTANCE ins,UINT id,string_base & out) +{ + BOOL rv = FALSE; + uResource * res = uLoadResource(ins,uMAKEINTRESOURCE(id),(const char*)(RT_STRING)); + if (res) + { + unsigned size = res->GetSize(); + const WCHAR * ptr = (const WCHAR*)res->GetPointer(); + if (size>=4) + { + unsigned len = *(const WORD*)(ptr+1); + if (len * 2 + 4 <= size) + { + out = stringcvt::string_utf8_from_wide(ptr+2,len); + } + } + + delete res; + rv = TRUE; + } + return rv; +} + +BOOL SHARED_EXPORT uGetMenuString(HMENU menu,UINT id,string_base & out,UINT flag) +{ + unsigned len = GetMenuString(menu,id,0,0,flag); + if (len==0) + { + out.reset(); + return FALSE; + } + array_t temp; + temp.set_size(len+1); + if (GetMenuString(menu,id,temp.get_ptr(),len+1,flag)==0) { + out.reset(); + return FALSE; + } + out = stringcvt::string_utf8_from_os(temp.get_ptr()); + return TRUE; +} + +BOOL SHARED_EXPORT uModifyMenu(HMENU menu,UINT id,UINT flags,UINT newitem,const char * data) +{ + return ModifyMenu(menu,id,flags,newitem,param_os_from_utf8(data)); +} + +UINT SHARED_EXPORT uGetMenuItemType(HMENU menu,UINT position) +{ + MENUITEMINFO info = {}; + info.cbSize = sizeof(info); + info.fMask = MIIM_TYPE; + if (!GetMenuItemInfo(menu,position,TRUE,&info)) + return 0; + return info.fType; +} + +static inline bool i_is_path_separator(unsigned c) +{ + return c=='\\' || c=='/' || c=='|' || c==':'; +} + +int SHARED_EXPORT uSortPathCompare(HANDLE string1,HANDLE string2) +{ + const TCHAR * s1 = reinterpret_cast(string1); + const TCHAR * s2 = reinterpret_cast(string2); + const TCHAR * p1, * p2; + + while (*s1 || *s2) + { + if (*s1 == *s2) + { + s1++; + s2++; + continue; + } + + p1 = s1; while (*p1 && !i_is_path_separator(*p1)) p1++; + p2 = s2; while (*p2 && !i_is_path_separator(*p2)) p2++; + + if ((!*p1 && !*p2) || (*p1 && *p2)) + { + int test = CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE | NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH, s1, pfc::downcast_guarded(p1 - s1), s2, pfc::downcast_guarded(p2 - s2)); + if (test && test != 2) return test - 2; + if (!*p1) return 0; + } + else + { + if (*p1) return -1; + else return 1; + } + + s1 = p1 + 1; + s2 = p2 + 1; + } + + return 0; +} + +UINT SHARED_EXPORT uRegisterClipboardFormat(const char * name) +{ + return RegisterClipboardFormat(stringcvt::string_os_from_utf8(name)); +} + +BOOL SHARED_EXPORT uGetClipboardFormatName(UINT format,string_base & out) +{ + TCHAR temp[1024]; + if (!GetClipboardFormatName(format,temp,_countof(temp))) return FALSE; + out = stringcvt::string_utf8_from_os(temp,_countof(temp)); + return TRUE; +} + +}//extern "C" + +BOOL SHARED_EXPORT uSearchPath(const char * path, const char * filename, const char * extension, string_base & p_out) +{ + enum {temp_size = 1024}; + param_os_from_utf8 path_os(path), filename_os(filename), extension_os(extension); + array_t temp; temp.set_size(temp_size); + LPTSTR dummy; + unsigned len; + + len = SearchPath(path_os,filename_os,extension_os,temp_size,temp.get_ptr(),&dummy); + if (len == 0) return FALSE; + if (len >= temp_size) + { + unsigned len2; + temp.set_size(len + 1); + len2 = SearchPath(path_os,filename_os,extension_os,len+1,temp.get_ptr(),&dummy); + if (len2 == 0 || len2 > len) return FALSE; + len = len2; + } + + p_out = stringcvt::string_utf8_from_os(temp.get_ptr(),len); + + return TRUE; + +} + +static bool is_ascii_alpha(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static char ascii_upper(char c) +{ + if (c >= 'a' && c <= 'z') c += 'A' - 'a'; + return c; +} + +static BOOL uFixPathCaps_Internal(const char * path,string_base & p_out, bool bQuick) { + pfc::string8_fastalloc temp, prependbuffer; + if (path[0] == '\\' && path[1] == '\\') + { + unsigned index = 2; + while(path[index] != '\\') + { + if (path[index] == 0) return FALSE; + index++; + } + index++; + if (path[index] == '\\' || path[index] == 0) return FALSE; + while(path[index] != '\\') + { + if (path[index] == 0) { + // \\host\share + uStringLower(p_out,path); + return TRUE; + } + index++; + } + index++; + if (path[index] == '\\') return FALSE; + uAddStringLower(temp,path,index); + path += index; + } + else if (is_ascii_alpha(path[0]) && path[1] == ':' && path[2] == '\\') + { + temp.add_char(ascii_upper(path[0])); + temp.add_string(":\\"); + path += 3; + } + else return FALSE; + + for(;;) + { + t_size truncat = temp.length(); + t_size delta = 0; + while(path[delta]!=0 && path[delta]!='\\') delta++; + if (delta == 0) break; + temp.add_string_nc(path,delta); + + bool found = false; + if (!bQuick) { +#ifdef UNICODE + pfc::winPrefixPath( prependbuffer, temp ); + pfc::ptrholder_t ff = uFindFirstFile(prependbuffer); +#else + pfc::ptrholder_t ff = uFindFirstFile(temp); +#endif + if (ff.is_valid()) { + do { + const char * fn = ff->GetFileName(); + if (!stricmp_utf8_ex(path,delta,fn,strlen(fn))) + { + found = true; + temp.truncate(truncat); + temp.add_string(fn); + break; + } + } while(ff->FindNext()); + } + } + if (!found) + { + temp.add_string(path + delta); + break; + } + path += delta; + if (*path == 0) break; + path ++; + temp.add_char('\\'); + } + + + p_out = temp; + + return TRUE; +} +/*BOOL SHARED_EXPORT uFixPathCapsQuick(const char * path,string_base & p_out) { + return uFixPathCaps_Internal(path, p_out, true); +}*/ +BOOL SHARED_EXPORT uFixPathCaps(const char * path,string_base & p_out) { + return uFixPathCaps_Internal(path, p_out, false); +} + +LPARAM SHARED_EXPORT uTreeView_GetUserData(HWND p_tree,HTREEITEM p_item) +{ + TVITEM item = {}; + item.mask = TVIF_PARAM; + item.hItem = p_item; + if (uSendMessage(p_tree,TVM_GETITEM,0,(LPARAM)&item)) + return item.lParam; + return 0; +} + +bool SHARED_EXPORT uTreeView_GetText(HWND p_tree,HTREEITEM p_item,string_base & p_out) +{ + TCHAR temp[1024];//changeme ? + TVITEM item = {}; + item.mask = TVIF_TEXT; + item.hItem = p_item; + item.pszText = temp; + item.cchTextMax = _countof(temp); + if (uSendMessage(p_tree,TVM_GETITEM,0,(LPARAM)&item)) + { + p_out = stringcvt::string_utf8_from_os(temp,_countof(temp)); + return true; + } + else return false; +} + +BOOL SHARED_EXPORT uSetWindowText(HWND wnd,const char * p_text) +{ + PFC_ASSERT( wnd != NULL ); + return SetWindowText(wnd,stringcvt::string_os_from_utf8(p_text)); +} + +BOOL SHARED_EXPORT uSetDlgItemText(HWND wnd,UINT id,const char * p_text) +{ + PFC_ASSERT( wnd != NULL ); + return SetDlgItemText(wnd, id, stringcvt::string_os_from_utf8(p_text)); +} + +BOOL SHARED_EXPORT uFileExists(const char * fn) +{ + DWORD attrib = uGetFileAttributes(fn); + if (attrib == 0xFFFFFFFF || (attrib & FILE_ATTRIBUTE_DIRECTORY)) return FALSE; + return TRUE; +} + +BOOL SHARED_EXPORT uFormatSystemErrorMessage(string_base & p_out,DWORD p_code) { + return pfc::winFormatSystemErrorMessage(p_out, p_code); +} + +HMODULE SHARED_EXPORT LoadSystemLibrary(const TCHAR * name) { + pfc::array_t buffer; buffer.set_size( MAX_PATH + _tcslen(name) + 2 ); + TCHAR * bufptr = buffer.get_ptr(); + if (GetSystemDirectory(bufptr, MAX_PATH) == 0) return NULL; + bufptr[MAX_PATH] = 0; + + size_t idx = _tcslen(bufptr); + if (idx > 0 && bufptr[idx-1] != '\\') bufptr[idx++] = '\\'; + + pfc::strcpy_t(bufptr+idx, name); + + return LoadLibrary(bufptr); +} \ No newline at end of file diff --git a/sdk/libPPUI/CDialogResizeHelper.h b/sdk/libPPUI/CDialogResizeHelper.h index 91932e2..1fe80d0 100644 --- a/sdk/libPPUI/CDialogResizeHelper.h +++ b/sdk/libPPUI/CDialogResizeHelper.h @@ -3,6 +3,17 @@ #include "CDialogResizeHelperCompat.h" #include +// ========================================================== +// CDialogResizeHelper +// ========================================================== +// Usage: +// Put CDialogResizeHelper member in your dialog class +// Initialize with controls sizing info table, array of CDialogResizeHelper::Param +// Put CHAIN_MSG_MAP_MEMBER(m_resizer) before your dialog message handlers +// CDialogResizeHelper will do its own message handling without marking messages as handled, that is, your handlers of the same messages will be allowed to run. +// CRect minMaxRange specifies allowed size min (left&top) and max (right&bottom) values, in DLU not pixels. +// ========================================================== + class CDialogResizeHelper : public CMessageMap { public: diff --git a/sdk/libPPUI/CEditWithButtons.cpp b/sdk/libPPUI/CEditWithButtons.cpp index bd6d728..d119e5c 100644 --- a/sdk/libPPUI/CEditWithButtons.cpp +++ b/sdk/libPPUI/CEditWithButtons.cpp @@ -171,7 +171,26 @@ void CEditWithButtons::Layout(CSize size, CFontHandle fontSetMe) { } unsigned CEditWithButtons::MeasureButton(Button_t const & button) { + if (m_fixedWidthAuto && m_fixedWidth == 0) { + CWindowDC dc(*this); + SelectObjectScope fontScope(dc, GetFont()); + SIZE sz = {}; + WIN32_OP_D( dc.GetTextExtent(L"#", 1, &sz) ); + m_fixedWidth = MulDiv(sz.cx, 3, 2); + } if (m_fixedWidth != 0) return m_fixedWidth; return button.wnd.Measure(); } + +void CEditWithButtons::OnSetFont(CFontHandle font, BOOL bRedraw) { + (void)bRedraw; + + if ( m_fixedWidthAuto ) m_fixedWidth = 0; // require re-calculation + + DefWindowProc(); + CRect rc; + if (GetClientRect(&rc)) { + Layout(rc.Size(), font); + } +} diff --git a/sdk/libPPUI/CEditWithButtons.h b/sdk/libPPUI/CEditWithButtons.h index c2df276..f5156f9 100644 --- a/sdk/libPPUI/CEditWithButtons.h +++ b/sdk/libPPUI/CEditWithButtons.h @@ -17,6 +17,7 @@ class CEditWithButtons : public CEditPPHooks { static constexpr LPARAM MSG_CHECKCONDITIONS_MAGIC2 = 0x180c2f35; BEGIN_MSG_MAP_EX(CEditWithButtons) + MSG_WM_CREATE(OnCreate) MSG_WM_SETFONT(OnSetFont) MSG_WM_WINDOWPOSCHANGED(OnPosChanged) MSG_WM_CTLCOLORBTN(OnColorBtn) @@ -36,6 +37,7 @@ class CEditWithButtons : public CEditPPHooks { BOOL SubclassWindow( HWND wnd ) { if (!CEditPPHooks::SubclassWindow(wnd)) return FALSE; + m_initialParent = GetParent(); this->ModifyStyle(0, WS_CLIPCHILDREN); RefreshButtons(); return TRUE; @@ -47,17 +49,20 @@ class CEditWithButtons : public CEditPPHooks { void AddClearButton( const wchar_t * clearVal = L"", bool bHandleEsc = false); void AddButton( const wchar_t * str, handler_t handler, condition_t condition = nullptr, const wchar_t * drawAlternateText = nullptr ); - static unsigned DefaultFixedWidth() {return GetSystemMetrics(SM_CXVSCROLL) * 3 / 4;} - void SetFixedWidth(unsigned fw = DefaultFixedWidth() ) { - m_fixedWidth = fw; + void SetFixedWidth(unsigned fw) { + m_fixedWidth = fw; m_fixedWidthAuto = false; + RefreshButtons(); + } + void SetFixedWidth() { + m_fixedWidth = 0; m_fixedWidthAuto = true; RefreshButtons(); } CRect RectOfButton( const wchar_t * text ); void Invalidate() { __super::Invalidate(); - for( auto i = m_buttons.begin(); i != m_buttons.end(); ++i ) { - if (i->wnd != NULL) i->wnd.Invalidate(); + for( auto & i : m_buttons ) { + if (i.wnd != NULL) i.wnd.Invalidate(); } } void SetShellFolderAutoComplete() { @@ -75,6 +80,11 @@ class CEditWithButtons : public CEditPPHooks { } void RefreshConditions(const wchar_t * newText = nullptr); private: + int OnCreate(LPCREATESTRUCT lpCreateStruct) { + m_initialParent = GetParent(); + SetMsgHandled(FALSE); + return 0; + } LRESULT OnCheckConditions( UINT msg, WPARAM wp, LPARAM lp ) { if ( msg == MSG_CHECKCONDITIONS && wp == MSG_CHECKCONDITIONS_MAGIC1 && lp == MSG_CHECKCONDITIONS_MAGIC2 ) { this->RefreshConditions(); @@ -105,9 +115,9 @@ class CEditWithButtons : public CEditPPHooks { return 0; } void OnEnable(BOOL bEnable) { - for( auto i = m_buttons.begin(); i != m_buttons.end(); ++ i ) { - if ( i->wnd != NULL ) { - i->wnd.EnableWindow( bEnable ); + for( auto & i : m_buttons ) { + if ( i.wnd != NULL ) { + i.wnd.EnableWindow( bEnable ); } } SetMsgHandled(FALSE); @@ -168,14 +178,8 @@ class CEditWithButtons : public CEditPPHooks { bool visible; condition_t condition; }; - void OnSetFont(CFontHandle font, BOOL bRedraw) { - (void)bRedraw; - CRect rc; - if (GetClientRect(&rc)) { - Layout(rc.Size(), font); - } - SetMsgHandled(FALSE); - } + + void OnSetFont(CFontHandle font, BOOL bRedraw); void RefreshButtons() { if ( m_hWnd != NULL && m_buttons.size() > 0 ) { @@ -192,7 +196,11 @@ class CEditWithButtons : public CEditPPHooks { return (GetKeyState(VK_SHIFT) & 0x8000) ? true : false; } CWindow FindDialog() { - return GetParent(); + // Return a window that we can send WM_NEXTDLGCTL to + // PROBLEM: There is no clear way of obtaining one - something in our GetParent() hierarchy is usually a dialog, but we don't know which one + // Assume our initial parent window to be the right thing to talk to + PFC_ASSERT(m_initialParent != NULL); + return m_initialParent; } void TabFocusThis(HWND wnd) { FindDialog().PostMessage(WM_NEXTDLGCTL, (WPARAM) wnd, TRUE ); @@ -208,6 +216,8 @@ class CEditWithButtons : public CEditPPHooks { unsigned MeasureButton(Button_t const & button ); unsigned m_fixedWidth = 0; + bool m_fixedWidthAuto = false; std::list< Button_t > m_buttons; bool m_hasAutoComplete = false; + CWindow m_initialParent; }; diff --git a/sdk/libPPUI/CListAccessible.cpp b/sdk/libPPUI/CListAccessible.cpp index 17bf374..01f221d 100644 --- a/sdk/libPPUI/CListAccessible.cpp +++ b/sdk/libPPUI/CListAccessible.cpp @@ -70,7 +70,7 @@ HRESULT IEnumVARIANT_selection::Clone(IEnumVARIANT **ppEnum) IEnumVARIANT * var; try { var = new IEnumVARIANT_selection(m_data.get_ptr(), m_data.get_size(), m_pos); - } catch(std::bad_alloc) {return E_OUTOFMEMORY;} + } catch(std::bad_alloc const &) {return E_OUTOFMEMORY;} var->AddRef(); *ppEnum = var; @@ -146,8 +146,8 @@ void CListAccessible::AccCleanup() { } LRESULT CListAccessible::AccGetObject(WPARAM wp,LPARAM lp) { - const WPARAM dwFlags = wp; - const LPARAM dwObjId = lp; + const auto dwFlags = (DWORD) wp; + const auto dwObjId = (DWORD) lp; if (dwObjId == OBJID_CLIENT) { @@ -475,7 +475,7 @@ HRESULT IAccessible_CListControl::get_accSelection(VARIANT *pvarChildren) pvarChildren->vt = VT_UNKNOWN; pvarChildren->punkVal = ptr; } - } catch(std::bad_alloc) { + } catch(std::bad_alloc const &) { return E_OUTOFMEMORY; } return S_OK; diff --git a/sdk/libPPUI/CListAccessible.h b/sdk/libPPUI/CListAccessible.h index b121d72..0b16e21 100644 --- a/sdk/libPPUI/CListAccessible.h +++ b/sdk/libPPUI/CListAccessible.h @@ -100,7 +100,7 @@ template class CListControlAccImpl : public TBaseClass, pro } size_t AccItemHitTest(CPoint const & pt) const { size_t item; - if (!this->ItemFromPoint(pt,item)) return ~0; + if (!this->ItemFromPoint(pt,item)) return SIZE_MAX; return item; } @@ -194,7 +194,7 @@ template class CListControlAccImpl : public TBaseClass, pro CWindow AccGetOtherChildWnd(size_t index) const {return index == 0 ? CWindow(this->GetHeaderCtrl()) : CWindow(NULL) ;} virtual DWORD AccGetOtherRole(size_t index) {return index > 0 ? ROLE_SYSTEM_GROUPING : ROLE_SYSTEM_WINDOW;}//FIXME? - virtual bool AccGetOtherDescription(size_t index, pfc::string_base & out) const {return false;}//FIXME?? + virtual bool AccGetOtherDescription(size_t index, pfc::string_base& out) const { (void)index; (void)out; return false; }//FIXME?? size_t AccGetOtherCount() const {return 1 + this->GetItemCount();} void AccGetOtherName(size_t index, pfc::string_base & out) const override { @@ -236,8 +236,8 @@ template class CListControlAccImpl : public TBaseClass, pro return SIZE_MAX; } bool AccIsOtherFocusable(size_t index) const {return index > 0;} - bool AccGetOtherDefaultAction(size_t index, pfc::string_base & out) const {return false;} - bool AccExecuteOtherDefaultAction(size_t index) {return false;} + bool AccGetOtherDefaultAction(size_t index, pfc::string_base& out) const { (void)index; (void)out; return false; } + bool AccExecuteOtherDefaultAction(size_t index) { (void)index; return false; } bool AccGetOtherRect(size_t index, CRect & out) const { if (index == 0) { CRect rc, client; diff --git a/sdk/libPPUI/CListControl-Subst.cpp b/sdk/libPPUI/CListControl-Subst.cpp index 0c313ef..391f635 100644 --- a/sdk/libPPUI/CListControl-Subst.cpp +++ b/sdk/libPPUI/CListControl-Subst.cpp @@ -62,9 +62,18 @@ namespace { MESSAGE_HANDLER_EX(LVM_ENABLEGROUPVIEW, OnEnableGroupView) MESSAGE_HANDLER_EX(LVM_SCROLL, OnScroll) MESSAGE_HANDLER_EX(LVM_REDRAWITEMS, OnRedrawItems) + MSG_WM_KEYDOWN(OnKeyDown) + MSG_WM_SYSKEYDOWN(OnKeyDown) CHAIN_MSG_MAP(CListControlComplete) END_MSG_MAP() + void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { + NMLVKEYDOWN arg = {}; + arg.hdr = this->setupHdr(LVN_KEYDOWN); + arg.wVKey = nChar; + sendNotify(&arg); + SetMsgHandled(FALSE); + } LRESULT OnCreate(LPCREATESTRUCT) { SetMsgHandled(FALSE); // Adopt style flags of the original control to keep various ATL checks happy @@ -861,7 +870,7 @@ namespace { auto pItem = reinterpret_cast(lp); size_t item = (size_t)pItem->iItem; const size_t total = GetItemCount(); - if (item > total) return FALSE; + if (item >= total) return FALSE; size_t subItem = (size_t)pItem->iSubItem; if (subItem > 1024) return FALSE; // quick sanity auto& rec = m_content[item]; @@ -1471,7 +1480,7 @@ namespace { } bool GetSubItemText(size_t item, size_t subItem, pfc::string_base& out) const override { - PFC_ASSERT(subItem == 0); + PFC_ASSERT(subItem == 0); (void)subItem; PFC_ASSERT(item < m_content.size()); out = m_content[item].text; return true; diff --git a/sdk/libPPUI/CListControl.cpp b/sdk/libPPUI/CListControl.cpp index 76850b7..7306530 100644 --- a/sdk/libPPUI/CListControl.cpp +++ b/sdk/libPPUI/CListControl.cpp @@ -8,6 +8,12 @@ #define PrepLayoutCache_Debug 0 #define Scroll_Debug 0 +#if Scroll_Debug +#define Scroll_Debug_Print(...) PFC_DEBUG_PRINT_FORCED(__VA_ARGS__) +#else +#define Scroll_Debug_Print(...) +#endif + CListControlUserOptions * CListControlUserOptions::instance = nullptr; CRect CListControlImpl::GetClientRectHook() const { @@ -101,9 +107,7 @@ void CListControlImpl::RefreshSlider(bool p_vertical) { } } -#if Scroll_Debug - pfc::debugLog() << "RefreshSlider vertical=" << p_vertical << ", nPage=" << si.nPage << ", nMin=" << si.nMin << ", nMax=" << si.nMax << ", nPos=" << si.nPos; -#endif + Scroll_Debug_Print("RefreshSlider vertical=", p_vertical, ", nPage=", si.nPage, ", nMin=", si.nMin, ", nMax=", si.nMax, ", nPos=", si.nPos); SetScrollInfo(p_vertical ? SB_VERT : SB_HORZ, &si); } @@ -299,9 +303,8 @@ LRESULT CListControlImpl::OnVScroll(UINT,WPARAM p_wp,LPARAM,BOOL&) { thumb = pfc::rint32(p * bottom); int target = HandleScroll(LOWORD(p_wp), m_viewOrigin.y, visible, GetItemHeight(), bottom, thumb); -#if Scroll_Debug - pfc::debugLog() << "OnVScroll thumb=" << thumb << ", target=" << target << ", bottom=" << bottom << ", visible=" << visible << ", p=" << p; -#endif + Scroll_Debug_Print("OnVScroll thumb=", thumb, ", target=", target, ", bottom=", bottom, ", visible=", visible, ", p=", p); + MoveViewOrigin(CPoint(m_viewOrigin.x, target)); return 0; @@ -313,8 +316,13 @@ LRESULT CListControlImpl::OnVScroll(UINT,WPARAM p_wp,LPARAM,BOOL&) { // As a workaround, we use GetScrollInfo() value for vscroll (good) // and workaround Logitech bug by using WPARAM position with hscroll (practically impossible to overflow) LRESULT CListControlImpl::OnHScroll(UINT,WPARAM p_wp,LPARAM,BOOL&) { - int thumb = HIWORD(p_wp); // GetScrollThumbPos(SB_HORZ); - int target = HandleScroll(LOWORD(p_wp),m_viewOrigin.x,GetVisibleRectAbs().Width(),GetItemHeight() /*fixme*/,GetViewAreaRectAbs().right,thumb); + int thumb = HIWORD(p_wp); + const auto fullWidth = GetViewAreaWidth(); + if (fullWidth > INT16_MAX) { // Possible overflow or near-overflow? Drop Logitech stupidity mitigation + thumb = GetScrollThumbPos(SB_HORZ); + } + int target = HandleScroll(LOWORD(p_wp), m_viewOrigin.x, GetVisibleRectAbs().Width(), GetItemHeight() /*fixme*/, fullWidth, thumb); + Scroll_Debug_Print("OnHScroll thumb=", thumb, ", target=", target); MoveViewOrigin(CPoint(target,m_viewOrigin.y)); return 0; } @@ -518,7 +526,7 @@ void CListControlImpl::MinGroupHeight2ChangedForGroup(groupID_t groupID, bool re void CListControlImpl::UpdateGroupOverlayByID(groupID_t groupID, int xFrom, int xTo) { t_size base, count; if (GetItemRangeAbs(GetVisibleRectAbs(), base, count)) { - bool on = false; // Have to walk whole range - there may be multiple groups with the same ID because fuck you + bool on = false; // Have to walk whole range - there may be multiple groups with the same ID for (size_t walk = 0; walk < count; ++walk) { bool test = (groupID == GetItemGroup(base + walk)); if (test && !on) { @@ -574,40 +582,44 @@ bool CListControlImpl::PrepLayoutCache(CPoint& ptOrigin, size_t indexLo, size_t #if PrepLayoutCache_Debug PFC_DEBUGLOG << "PrepLayoutCache entry"; PFC_DEBUGLOG << "PrepLayoutCache: count=" << count << " knownGroups=" << this->m_groupHeaders.size(); - PFC_DEBUGLOG << "PrepLayoutCache: indexLo=" << indexLo << " indexHi=" << indexHi; + PFC_DEBUGLOG << "PrepLayoutCache: indexLo=" << pfc::format_index(indexLo) << " indexHi=" << pfc::format_index(indexHi); #endif const int clientHeight = pfc::max_t(this->GetClientRectHook().Height(), 100); // Always walk 2*clientHeight, with area above and below int yMax = -1, yBase = 0; size_t baseItem = 0, endItem = SIZE_MAX; - if (indexLo == SIZE_MAX) { - yBase = pfc::max_t(ptOrigin.y - clientHeight / 2, 0); - yMax = yBase + clientHeight * 2; - baseItem = pfc::min_t(this->IndexFromPointAbs(yBase), count - 1); - } else { - auto itemHeight = GetItemHeight(); - size_t extraItems = (size_t)(clientHeight / itemHeight); + + if (!m_greedyGroupLayout) { + if (indexLo == SIZE_MAX) { + yBase = pfc::max_t(ptOrigin.y - clientHeight / 2, 0); + yMax = yBase + clientHeight * 2; + baseItem = pfc::min_t(this->IndexFromPointAbs(yBase), count - 1); + } else { + auto itemHeight = GetItemHeight(); + size_t extraItems = (size_t)(clientHeight / itemHeight); #if PrepLayoutCache_Debug - PFC_DEBUGLOG << "PrepLayoutCache: clientHeight=" << clientHeight << " itemHeight=" << itemHeight << " extraItems=" << extraItems; + PFC_DEBUGLOG << "PrepLayoutCache: clientHeight=" << clientHeight << " itemHeight=" << itemHeight << " extraItems=" << extraItems; #endif - if (indexLo < extraItems) baseItem = 0; - else baseItem = indexLo - extraItems; + if (indexLo < extraItems) baseItem = 0; + else baseItem = indexLo - extraItems; - if (indexHi == SIZE_MAX) { - endItem = baseItem + extraItems; - } else { - endItem = indexHi + extraItems; - } - if (endItem > count) endItem = count; + if (indexHi == SIZE_MAX) { + endItem = baseItem + extraItems; + } else { + endItem = indexHi + extraItems; + } + if (endItem > count) endItem = count; #if PrepLayoutCache_Debug - PFC_DEBUGLOG << "PrepLayoutCache: baseItem=" << baseItem << " endItem=" << endItem; + PFC_DEBUGLOG << "PrepLayoutCache: baseItem=" << baseItem << " endItem=" << endItem; #endif + } } + size_t item = baseItem; { const auto group = this->GetItemGroup(baseItem); @@ -634,7 +646,7 @@ bool CListControlImpl::PrepLayoutCache(CPoint& ptOrigin, size_t indexLo, size_t PFC_DEBUGLOG << "PrepLayoutCache: baseItem=" << baseItem; #endif - size_t anchorIdx = this->IndexFromPointAbs(ptOrigin.y); + size_t anchorIdx = m_greedyGroupLayout ? SIZE_MAX : this->IndexFromPointAbs(ptOrigin.y); int anchorDelta = 0; bool anchorIsFirstInGroup = IsItemFirstInGroupCached(anchorIdx); if (anchorIdx != SIZE_MAX) { @@ -642,7 +654,7 @@ bool CListControlImpl::PrepLayoutCache(CPoint& ptOrigin, size_t indexLo, size_t } #if PrepLayoutCache_Debug - PFC_DEBUGLOG << "PrepLayoutCache: anchorIdx=" << anchorIdx << " anchorDelta=" << anchorDelta << " anchorIsFirstInGroup=" << anchorIsFirstInGroup; + PFC_DEBUGLOG << "PrepLayoutCache: anchorIdx=" << pfc::format_index(anchorIdx) << " anchorDelta=" << anchorDelta << " anchorIsFirstInGroup=" << anchorIsFirstInGroup; #endif bool bChanged = false; @@ -730,9 +742,7 @@ bool CListControlImpl::PrepLayoutCache(CPoint& ptOrigin, size_t indexLo, size_t int CListControlImpl::GetViewAreaHeight() const { auto ret = GetItemOffsetAbs(GetItemCount()); -#if Scroll_Debug - PFC_DEBUGLOG << "GetViewAreaHeight: " << ret; -#endif + Scroll_Debug_Print("GetViewAreaHeight: " , ret); return ret; } @@ -827,7 +837,7 @@ static void AddUpdateRect(HRGN p_rgn,CRect const & p_rect) { } void CListControlImpl::OnItemsReordered( const size_t * order, size_t count ) { - PFC_ASSERT( count == GetItemCount() ); + PFC_ASSERT(count == GetItemCount()); (void)count; ReloadItems( pfc::bit_array_order_changed(order) ); } void CListControlImpl::UpdateItems(const pfc::bit_array & p_mask) { diff --git a/sdk/libPPUI/CListControl.h b/sdk/libPPUI/CListControl.h index 7bffbef..2b8b6f8 100644 --- a/sdk/libPPUI/CListControl.h +++ b/sdk/libPPUI/CListControl.h @@ -282,7 +282,7 @@ class CListControlImpl : public CWindowImpl m_varItemHeights; std::set m_groupHeaders; - + size_t FindGroupBaseCached(size_t itemFor) const; size_t FindGroupBase(size_t itemFor) const; size_t FindGroupBase(size_t itemFor, groupID_t group) const; protected: + // Grouped layout mode toggle + // Default mode is greedy, probes whole list layout in advance (no glitches when scrolling) + // In special conditions when probing is expensive, greedy mode should be turned off + bool m_greedyGroupLayout = true; + pfc::map_t m_themeCache; CTheme & themeFor( const char * what ); CTheme & theme() { return themeFor("LISTVIEW");} diff --git a/sdk/libPPUI/CListControlHeaderImpl.cpp b/sdk/libPPUI/CListControlHeaderImpl.cpp index fb44858..b8a57ef 100644 --- a/sdk/libPPUI/CListControlHeaderImpl.cpp +++ b/sdk/libPPUI/CListControlHeaderImpl.cpp @@ -8,9 +8,15 @@ #include "DarkMode.h" #include -enum { - lineBelowHeaderCY = 1 -}; +static constexpr int lineBelowHeaderCY = 1; + +// Prevent erratic behavior of header control +static constexpr uint32_t columnWidthSanityLimit = 10000; + +static uint32_t columWidthToPixels(uint32_t user) { + PFC_ASSERT(user <= CListControlHeaderImpl::columnWidthMax); + return user < columnWidthSanityLimit ? user : columnWidthSanityLimit; +} static bool testDrawLineBelowHeader() { // Win10 @@ -404,18 +410,19 @@ void CListControlHeaderImpl::GetColumnText(size_t which, pfc::string_base & out) } } -void CListControlHeaderImpl::ResizeColumn(t_size index, t_uint32 widthPixels, bool updateView) { +void CListControlHeaderImpl::ResizeColumn(t_size index, t_uint32 userWidth, bool updateView) { PFC_ASSERT( IsHeaderEnabled() ); PFC_ASSERT( index < m_colRuntime.size() ); auto& rt = m_colRuntime[index]; - rt.m_userWidth = widthPixels; + rt.m_userWidth = userWidth; if (rt.autoWidth()) { this->ProcessAutoWidth(); } else { + auto widthPixels = columWidthToPixels(userWidth); rt.m_widthPixels = widthPixels; HDITEM item = {}; item.mask = HDI_WIDTH; - item.cxy = rt.autoWidth() ? 0 : widthPixels; + item.cxy = widthPixels; { pfc::vartoggle_t scope(m_ownColumnsChange, true); m_header.SetItem((int)index, &item); } RecalcItemWidth(); if (updateView) OnColumnsChanged(); @@ -500,15 +507,19 @@ void CListControlHeaderImpl::AddColumnF( const char * label, float widthF, DWORD } AddColumn( label, w, fmtFlags, update ); } -void CListControlHeaderImpl::AddColumn(const char * label, uint32_t width, DWORD fmtFlags,bool update) { +void CListControlHeaderImpl::AddColumn(const char * label, uint32_t userWidth, DWORD fmtFlags,bool update) { + // userWidth is either UINT32_MAX or desired width in pixels PFC_ASSERT(IsWindow()); if (! IsHeaderEnabled( ) ) InitializeHeaderCtrl(); + uint32_t widthPixels = 0; + pfc::stringcvt::string_os_from_utf8 labelOS(label); HDITEM item = {}; item.mask = HDI_TEXT | HDI_FORMAT; - if ( width != UINT32_MAX ) { - item.cxy = width; + if ( userWidth <= columnWidthMax ) { + widthPixels = columWidthToPixels(userWidth); + item.cxy = widthPixels; item.mask |= HDI_WIDTH; } @@ -518,11 +529,9 @@ void CListControlHeaderImpl::AddColumn(const char * label, uint32_t width, DWORD WIN32_OP_D( (iColumn = m_header.InsertItem(m_header.GetItemCount(),&item) ) >= 0 ); colRuntime_t rt; rt.m_text = label; - rt.m_userWidth = width; - if ( width <= columnWidthMax ) { - m_itemWidth += width; - rt.m_widthPixels = width; - } + rt.m_userWidth = userWidth; + m_itemWidth += widthPixels; + rt.m_widthPixels = widthPixels; m_colRuntime.push_back( std::move(rt) ); if (update) OnColumnsChanged(); @@ -750,8 +759,8 @@ t_uint32 CListControlHeaderImpl::GetOptimalSubItemWidth(t_size item, t_size subI } t_uint32 CListControlHeaderImpl::GetOptimalWidth_Cache::GetStringTempWidth() { - if (m_stringTemp.replace_string_ex(m_stringTempUnfuckAmpersands, "&", "&&") > 0) { - m_convertTemp.convert(m_stringTempUnfuckAmpersands); + if (m_stringTemp.replace_string_ex(m_stringTempFixAmpersands, "&", "&&") > 0) { + m_convertTemp.convert(m_stringTempFixAmpersands); } else { m_convertTemp.convert(m_stringTemp); } @@ -821,6 +830,9 @@ void CListControlHeaderImpl::AutoColumnWidths(const pfc::bit_array & mask, bool } } + // Enforce limit + for (auto& walk : widths) walk = columWidthToPixels(walk); + if (expandLast) { uint32_t usedWidth = 0; size_t lastCol = SIZE_MAX; pfc::array_t order; order.set_size(columnCount); diff --git a/sdk/libPPUI/CListControlHeaderImpl.h b/sdk/libPPUI/CListControlHeaderImpl.h index ad7abb2..aebcb65 100644 --- a/sdk/libPPUI/CListControlHeaderImpl.h +++ b/sdk/libPPUI/CListControlHeaderImpl.h @@ -89,7 +89,7 @@ class CListControlHeaderImpl : public CListControlFontOps { struct GetOptimalWidth_Cache { //! For temporary use. - pfc::string8_fastalloc m_stringTemp, m_stringTempUnfuckAmpersands; + pfc::string8_fastalloc m_stringTemp, m_stringTempFixAmpersands; //! For temporary use. pfc::stringcvt::string_wide_from_utf8_t m_convertTemp; //! Our DC for measuring text. Correct font pre-selected. @@ -136,8 +136,8 @@ class CListControlHeaderImpl : public CListControlFontOps { virtual void SetCellCheckState(size_t item, size_t subItem, bool value); virtual bool ToggleSelectedItemsHook(const pfc::bit_array & mask) override; - virtual bool RenderCellImageTest(size_t item, size_t subItem) const { return false; } - virtual void RenderCellImage(size_t item, size_t subItem, CDCHandle, const CRect&) const {} + virtual bool RenderCellImageTest(size_t item, size_t subItem) const { (void)item; (void)subItem; return false; } + virtual void RenderCellImage(size_t item, size_t subItem, CDCHandle, const CRect&) const { (void)item; (void)subItem; } t_uint32 GetOptimalColumnWidth(t_size which, GetOptimalWidth_Cache & cache) const; t_uint32 GetOptimalSubItemWidthSimple(t_size item, t_size subItem) const; diff --git a/sdk/libPPUI/CListControlOwnerData.h b/sdk/libPPUI/CListControlOwnerData.h index 8f9f9d1..04488ad 100644 --- a/sdk/libPPUI/CListControlOwnerData.h +++ b/sdk/libPPUI/CListControlOwnerData.h @@ -29,7 +29,8 @@ class IListControlOwnerDataSource { lineCount = 1; return listGetSubItemText( ctx, item, subItem); } virtual void listSetEditField(ctx_t ctx, size_t item, size_t subItem, const char * val) {} - virtual uint32_t listGetEditFlags(ctx_t ctx, size_t item, size_t subItem) {return 0;} + // Returns InPlaceEdit::KFlag* + virtual uint32_t listGetEditFlags(ctx_t ctx, size_t item, size_t subItem) {return 0;} typedef InPlaceEdit::CTableEditHelperV2::autoComplete_t autoComplete_t; typedef InPlaceEdit::CTableEditHelperV2::combo_t combo_t; virtual autoComplete_t listGetAutoComplete(ctx_t, size_t item, size_t subItem) {return autoComplete_t();} diff --git a/sdk/libPPUI/CListControlSimple.h b/sdk/libPPUI/CListControlSimple.h index c8c3438..0382ab1 100644 --- a/sdk/libPPUI/CListControlSimple.h +++ b/sdk/libPPUI/CListControlSimple.h @@ -3,7 +3,7 @@ // ================================================================================ // CListControlSimple // Simplified CListControl interface; a ready-to-use class that can be instantiated -// without subclassing. +// without subclassing or setting callback objects. // Use when you don't need advanced features such as buttons or editing. // Maintains its own data. // ================================================================================ @@ -14,6 +14,7 @@ #include #include #include +#include class CListControlSimple : public CListControlReadOnly { @@ -21,8 +22,9 @@ class CListControlSimple : public CListControlReadOnly { // Events std::function onReordered; // if not set, list reordering is disabled std::function onRemoved; // if not set, list item removal is disabled - std::function onItemAction; - std::function onSelChange; + std::function onItemAction; // optional, handle item double click or enter key + std::function onSelChange; // optional, handle selectionchange + std::function onColumnHeaderClick; // optional, handle column header click, if not set sorting will happen size_t GetItemCount() const override { return m_lines.size(); @@ -57,9 +59,7 @@ class CListControlSimple : public CListControlReadOnly { void RequestReorder( const size_t * order, size_t count) override { if ( onReordered == nullptr ) return; - pfc::reorder_t( m_lines, order, count ); - this->OnItemsReordered( order, count ); - onReordered(); + _Reorder(order, count); } void RequestRemoveSelection() override { if (onRemoved == nullptr) return; @@ -125,15 +125,52 @@ class CListControlSimple : public CListControlReadOnly { this->OnItemsInserted( insertAt, count, false ); return insertAt; } + void SortBy(size_t column, bool descending) { + std::vector order; order.resize(m_lines.size()); + for (size_t walk = 0; walk < order.size(); ++walk) order[walk] = walk; + auto pred = [column, descending](const line_t& l1, const line_t& l2) { + int ret = pfc::winNaturalSortCompare(l1.at(column), l2.at(column)); + if (!descending) ret = -ret; + return ret > 0; + }; + auto pred_order = [&](size_t i1, size_t i2) { + return pred(m_lines[i1], m_lines[i2]); + }; + std::sort(order.begin(), order.end(), pred_order); + this->_Reorder(order.data(), order.size()); + this->SetSortIndicator(column, descending); + } + void SortBy(size_t column) { + HDITEM item = { HDI_FORMAT }; + if (this->GetHeaderCtrl().GetItem((int)column, &item)) { + bool bDescending = (item.fmt & HDF_SORTDOWN) != 0; + this->SortBy(column, bDescending); + } + } protected: void OnSelectionChanged(pfc::bit_array const & affected, pfc::bit_array const & status) override { __super::OnSelectionChanged(affected, status); if ( onSelChange ) onSelChange(); } + void OnColumnHeaderClick(t_size index) { + __super::OnColumnHeaderClick(index); + if (onColumnHeaderClick) onColumnHeaderClick(index); + else this->SortBy(index); + } + void _Reorder(const size_t* order, size_t count) { + pfc::reorder_t(m_lines, order, count); + this->OnItemsReordered(order, count); + if (onReordered) onReordered(); + } private: struct line_t { std::map text; size_t user = 0; + const char* at(size_t i) const { + auto iter = text.find(i); + if (iter == text.end()) return ""; + return iter->second.c_str(); + } }; std::vector m_lines; }; diff --git a/sdk/libPPUI/CListControlTruncationTooltipImpl.cpp b/sdk/libPPUI/CListControlTruncationTooltipImpl.cpp index de824ef..493e174 100644 --- a/sdk/libPPUI/CListControlTruncationTooltipImpl.cpp +++ b/sdk/libPPUI/CListControlTruncationTooltipImpl.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "CListControl.h" #include "PaintUtils.h" +#include "DarkMode.h" LRESULT CListControlTruncationTooltipImpl::OnTTShow(int,LPNMHDR,BOOL&) { SetTimer(KTooltipTimer,KTooltipTimerDelay); @@ -206,4 +207,13 @@ void CListControlTruncationTooltipImpl::InitTooltip() { m_toolinfo.lpszText = LPSTR_TEXTCALLBACK; m_toolinfo.hinst = GetThisModuleHandle(); WIN32_OP_D( m_tooltip.AddTool(&m_toolinfo) ); + + if ( GetDarkMode() ) DarkMode::ApplyDarkThemeCtrl(m_tooltip, true ); +} + +void CListControlTruncationTooltipImpl::RefreshDarkMode() { + __super::RefreshDarkMode(); + if (m_tooltip) { + DarkMode::ApplyDarkThemeCtrl(m_tooltip, GetDarkMode() ); + } } diff --git a/sdk/libPPUI/CListControlTruncationTooltipImpl.h b/sdk/libPPUI/CListControlTruncationTooltipImpl.h index e53463c..005b42c 100644 --- a/sdk/libPPUI/CListControlTruncationTooltipImpl.h +++ b/sdk/libPPUI/CListControlTruncationTooltipImpl.h @@ -19,6 +19,7 @@ class CListControlTruncationTooltipImpl : public CListControlHeaderImpl { void OnViewOriginChange(CPoint p_delta) override {TParent::OnViewOriginChange(p_delta);TooltipRemove();} void TooltipRemove(); + virtual void RefreshDarkMode(); protected: virtual bool GetTooltipData( CPoint ptAbs, pfc::string_base & text, CRect & rc, CFontHandle & font) const; private: diff --git a/sdk/libPPUI/CListControlWithSelection.cpp b/sdk/libPPUI/CListControlWithSelection.cpp index 3d6a996..a0a1376 100644 --- a/sdk/libPPUI/CListControlWithSelection.cpp +++ b/sdk/libPPUI/CListControlWithSelection.cpp @@ -304,9 +304,14 @@ LRESULT CListControlWithSelectionBase::OnRButtonUp(UINT,WPARAM,LPARAM,BOOL& bHan return 0; } +bool CListControlWithSelectionBase::ShouldBeginDrag(CPoint ptRef, CPoint ptNow) const { + auto threshold = PP::queryDragThresholdForDPI(this->GetDPI()); + return abs(ptNow.x - ptRef.x) > threshold.cx || abs(ptNow.y - ptRef.y) > threshold.cy; +} + LRESULT CListControlWithSelectionBase::OnMouseMove(UINT,WPARAM,LPARAM p_lp,BOOL&) { if (m_prepareDragDropMode) { - if (CPoint(p_lp) != m_prepareDragDropOrigin) { + if (ShouldBeginDrag(m_prepareDragDropOrigin, CPoint(p_lp))) { AbortPrepareDragDropMode(); if (!m_ownDDActive) { pfc::vartoggle_t ownDD(m_ownDDActive,true); @@ -526,7 +531,7 @@ static HRGN FrameRectRgn(const CRect & rect) { void CListControlWithSelectionBase::HandleDragSel(const CPoint & p_pt) { const CPoint pt = PointClientToAbs(p_pt); - if (pt != m_selectDragCurrentAbs) { + if (m_selectDragMoved || ShouldBeginDrag(m_selectDragCurrentAbs, pt)) { if (!this->AllowRangeSelect()) { // simplified @@ -1021,7 +1026,7 @@ void CListControlWithSelectionImpl::SelHandleRemoval(const pfc::bit_array & mask void CListControlWithSelectionImpl::SelHandleInsertion(pfc::bit_array const& mask, size_t oldCount, size_t newCount, bool select) { PFC_ASSERT(newCount == GetItemCount()); - PFC_ASSERT(oldCount <= newCount); + PFC_ASSERT(oldCount <= newCount); (void)oldCount; // To behave sanely in single-select mode, we'd have to alter selection of other items from here // Let caller worry and outright deny select requests in modes other than multisel @@ -1109,14 +1114,31 @@ bool CListControlWithSelectionBase::GetFocusRectAbs(CRect & p_rect) { return false; } +bool CListControlWithSelectionBase::GetContextMenuPoint2(CPoint& ptInOut) { + CPoint ptInvalid(-1,-1); + if (ptInOut == ptInvalid) { + ptInOut = GetContextMenuPointDefault(); + return ptInOut != ptInvalid; + } else { + CRect rc = this->GetClientRectHook(); + WIN32_OP_D( ClientToScreen(rc) ); + return !!rc.PtInRect(ptInOut); + } +} + +CPoint CListControlWithSelectionBase::GetContextMenuPointDefault() { + CRect rect; + if (!GetFocusRectAbs(rect)) return CPoint(-1,-1); + EnsureVisibleRectAbs(rect); + CPoint pt = rect.CenterPoint() - GetViewOffset(); + ClientToScreen(&pt); + return pt; +} + CPoint CListControlWithSelectionBase::GetContextMenuPoint(CPoint ptGot) { CPoint pt; if (ptGot.x == -1 && ptGot.y == -1) { - CRect rect; - if (!GetFocusRectAbs(rect)) return 0; - EnsureVisibleRectAbs(rect); - pt = rect.CenterPoint() - GetViewOffset(); - ClientToScreen(&pt); + pt = GetContextMenuPointDefault(); } else { pt = ptGot; } @@ -1126,11 +1148,7 @@ CPoint CListControlWithSelectionBase::GetContextMenuPoint(CPoint ptGot) { CPoint CListControlWithSelectionBase::GetContextMenuPoint(LPARAM lp) { CPoint pt; if (lp == -1) { - CRect rect; - if (!GetFocusRectAbs(rect)) return 0; - EnsureVisibleRectAbs(rect); - pt = rect.CenterPoint() - GetViewOffset(); - ClientToScreen(&pt); + pt = GetContextMenuPointDefault(); } else { pt = lp; } @@ -1444,7 +1462,7 @@ void CListControlWithSelectionBase::RunDragDrop(const CPoint & p_origin, bool p_ } pfc::com_ptr_t source = new CDropSourceImpl(); - source->wndOrigin = *this; + source->wndOrigin = m_hWnd; source->allowDragOutside = true; source->allowReorder = (flags & dragDrop_reorder) != 0; @@ -1474,7 +1492,7 @@ bool CListControlWithSelectionBase::RunReorderDragDrop(CPoint ptOrigin, CPoint & pfc::com_ptr_t source = new CDropSourceImpl(); pfc::com_ptr_t target = new CDropTargetImpl(); - source->wndOrigin = *this; + source->wndOrigin = m_hWnd; source->allowDragOutside = false; source->allowReorder = true; @@ -1528,6 +1546,7 @@ int CListControlWithSelectionBase::OnCreatePassThru(LPCREATESTRUCT) { return dda->dwEFfect; }; target->HookDrop = [this, flags] ( IDataObject * obj, CPoint pt ) { + this->ToggleDDScroll(false); this->ClearDropMark(); if ( this->m_ownDDActive ) { // Do not generate OnDrop for reorderings @@ -1537,9 +1556,11 @@ int CListControlWithSelectionBase::OnCreatePassThru(LPCREATESTRUCT) { }; target->HookLeave = [this] { this->ClearDropMark(); + this->ToggleDDScroll(false); }; target->Track = [this, dda](CPoint pt) { + this->ToggleDDScroll(true); if ( dda->showDropMark ) { WIN32_OP_D(this->ScreenToClient(&pt)); size_t idx = this->InsertIndexFromPoint(pt); diff --git a/sdk/libPPUI/CListControlWithSelection.h b/sdk/libPPUI/CListControlWithSelection.h index 7341229..9008fe7 100644 --- a/sdk/libPPUI/CListControlWithSelection.h +++ b/sdk/libPPUI/CListControlWithSelection.h @@ -124,6 +124,11 @@ class CListControlWithSelectionBase : public CListControl { //! Input & output in screen coordinates, per WM_CONTEXTMENU conventions. CPoint GetContextMenuPoint(LPARAM lp); CPoint GetContextMenuPoint(CPoint ptGot); + //! Import context menu point coordinates: turn (-1,-1) to something that makes sense; \n + //! Returns false if clicked point was outside client area so WM_CONTEXTMENU should be left unhandled. + bool GetContextMenuPoint2(CPoint & ptInOut); + //! Returns center-of-focused-item point for context menu, in screen coordinates. + CPoint GetContextMenuPointDefault(); protected: void ToggleDDScroll(bool p_state); @@ -215,6 +220,7 @@ class CListControlWithSelectionBase : public CListControl { bool m_prepareDragDropMode = false, m_prepareDragDropModeRightClick = false; bool m_noEnsureVisible = false; CPoint m_prepareDragDropOrigin; + bool ShouldBeginDrag(CPoint ptRef, CPoint ptNow) const; bool m_ownDDActive = false; bool m_drawThemeText = false; diff --git a/sdk/libPPUI/CListControl_EditImpl.h b/sdk/libPPUI/CListControl_EditImpl.h index f8dd4a9..0ea475b 100644 --- a/sdk/libPPUI/CListControl_EditImpl.h +++ b/sdk/libPPUI/CListControl_EditImpl.h @@ -66,6 +66,7 @@ template class CListControl_EditImpl : } private: LRESULT OnCtlColor(UINT,WPARAM wp,LPARAM lp,BOOL&) { + (void)lp; CDCHandle dc((HDC)wp); const COLORREF bkgnd = this->GetSysColorHook(CListControlImpl::colorBackground); dc.SetTextColor(this->GetSysColorHook(CListControlImpl::colorText)); diff --git a/sdk/libPPUI/Controls.cpp b/sdk/libPPUI/Controls.cpp index 68ce658..ffdcaeb 100644 --- a/sdk/libPPUI/Controls.cpp +++ b/sdk/libPPUI/Controls.cpp @@ -2,6 +2,7 @@ #include #include "Controls.h" #include "PaintUtils.h" +#include "HyperLinkCtrl.h" void CStaticSeparator::OnPaint(CDCHandle) { PaintUtils::PaintSeparatorControl(*this); @@ -11,6 +12,7 @@ void CSeparator::OnPaint(CDCHandle dc) { PaintUtils::PaintSeparatorControl(*this); } +#if 0 // BROKEN WITH DARK MODE, DO NOT USE CStaticMainInstruction::CStaticMainInstruction() { SetThemePart(TEXT_MAININSTRUCTION); } @@ -48,3 +50,33 @@ void CStaticThemed::OnPaint(CDCHandle) { PFC_ASSERT(SUCCEEDED(retval)); } } +#endif + + +#include "DarkMode-CHyperLink.h" +#include "windowLifetime.h" + +void PP::createHyperLink(HWND wndReplaceMe) { + auto obj = PP::subclassThisWindow(wndReplaceMe); + obj->SetHyperLinkExtendedStyle(HLINK_NOTIFYBUTTON); +} + +namespace { + class CHyperLinkLambda : public DarkMode::CHyperLinkImpl { + public: + std::function f; + bool Navigate() { + f(); + return true; + } + }; +} +void PP::createHyperLink(HWND wndReplaceMe, std::function handler) { + auto obj = PP::subclassThisWindow(wndReplaceMe); + obj->f = handler; +} + +void PP::createHyperLink(HWND wndReplaceMe, const wchar_t* openURL) { + auto obj = PP::subclassThisWindow(wndReplaceMe); + obj->SetHyperLink(openURL); +} diff --git a/sdk/libPPUI/Controls.h b/sdk/libPPUI/Controls.h index 374c488..b1ecf03 100644 --- a/sdk/libPPUI/Controls.h +++ b/sdk/libPPUI/Controls.h @@ -46,7 +46,9 @@ class CTextControl : public CWindowRegisteredT { CFontHandle m_font; }; - +// CStaticThemed BROKEN WITH DARK MODE, DO NOT USE +// CStaticMainInstruction = use 1.5x scaled font for non subclassed static instead +#if 0 // Static control subclass with override for theme part used for rendering class CStaticThemed : public CWindowImpl { public: @@ -78,7 +80,7 @@ class CStaticMainInstruction : public CStaticThemed { public: CStaticMainInstruction(); }; - +#endif class CSeparator : public CTextControl { diff --git a/sdk/libPPUI/DarkMode-CHyperLink.h b/sdk/libPPUI/DarkMode-CHyperLink.h new file mode 100644 index 0000000..66f8635 --- /dev/null +++ b/sdk/libPPUI/DarkMode-CHyperLink.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include "DarkMode.h" + +namespace DarkMode { + static constexpr COLORREF colorHyperLink = 0xCC6600; // taken from screenshot of syslink + + template class CHyperLinkImpl : public ::CHyperLinkImpl { + public: + BEGIN_MSG_MAP_EX(CDarkHyperLinkImpl) + MESSAGE_HANDLER_EX(DarkMode::msgSetDarkMode(), OnSetDarkMode) + CHAIN_MSG_MAP(::CHyperLinkImpl) + END_MSG_MAP() + private: + LRESULT OnSetDarkMode(UINT, WPARAM wp, LPARAM) { + const bool bDark = (wp != 0); + if ( m_clrLinkBackup == CLR_INVALID ) m_clrLinkBackup = this->m_clrLink; + + if (bDark != m_isDark) { + m_isDark = bDark; + this->m_clrLink = bDark ? colorHyperLink : m_clrLinkBackup; + this->Invalidate(); + } + + return 1; + } + COLORREF m_clrLinkBackup = CLR_INVALID; + bool m_isDark = false; + }; + + + class CHyperLink : public CHyperLinkImpl { + public: + DECLARE_WND_CLASS(_T("WTL_DarkHyperLink")) + }; + +} diff --git a/sdk/libPPUI/DarkMode.cpp b/sdk/libPPUI/DarkMode.cpp index 962c98d..deb2e3c 100644 --- a/sdk/libPPUI/DarkMode.cpp +++ b/sdk/libPPUI/DarkMode.cpp @@ -15,6 +15,15 @@ // Allow scary undocumented ordinal-dll-export functions? #define DARKMODE_ALLOW_HAX 1 +#define DARKMODE_DEBUG 0 + +#if DARKMODE_DEBUG +#define DARKMODE_DEBUG_PRINT(...) PFC_DEBUG_PRINT("DarkMode: ", __VA_ARGS__) +#else +#define DARKMODE_DEBUG_PRINT(...) +#endif + + #include #pragma comment(lib, "dwmapi.lib") @@ -56,6 +65,9 @@ Set text/bk colors explicitly Text color: 0xdedede Background: 0x191919 +Label-editing: +Pass WM_CTLCOLOR* to parent, shim TVM_EDITLABEL to pass theme to the editbox (not really necessary tho) + == Rebar == Can be beaten into working to some extent with a combination of: @@ -97,9 +109,10 @@ Full custom draw SetWindowTheme(wnd, L"", L""); works but not 100% pretty, disabled text ugly in particular Full custom draw preferred -== Group box +== Group box === SetWindowTheme(wnd, L"", L""); works but not 100% pretty, disabled text ugly in particular -Full custom draw preferred +Full custom draw preferred (we don't do this). +Avoid disabling groupboxes / use something else. ==== NOTES ==== AllowDarkModeForWindow() needs SetPreferredAppMode() to take effect, hence we implicitly call it @@ -113,7 +126,6 @@ But the latter doesn't require undocumented function calls and doesn't infect al */ namespace { - // 1903 18362 enum class PreferredAppMode { Default, @@ -163,7 +175,7 @@ namespace { }; #if DARKMODE_ALLOW_HAX using fnAllowDarkModeForWindow = bool (WINAPI*)(HWND hWnd, bool allow); // ordinal 133 - using fnSetPreferredAppMode = PreferredAppMode(WINAPI*)(PreferredAppMode appMode); // ordinal 135, in 1903 + using fnSetPreferredAppMode = PreferredAppMode(WINAPI*)(PreferredAppMode appMode); // ordinal 135, since 1809 using fnFlushMenuThemes = void (WINAPI*)(); // ordinal 136 fnAllowDarkModeForWindow _AllowDarkModeForWindow = nullptr; fnSetPreferredAppMode _SetPreferredAppMode = nullptr; @@ -173,7 +185,7 @@ namespace { void InitImports() { if (ImportsInited) return; - if (IsWindows10OrGreater()) { + if (DarkMode::IsSupportedSystem()) { HMODULE hUxtheme = LoadLibraryEx(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); if (hUxtheme) { _AllowDarkModeForWindow = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(133))); @@ -189,9 +201,14 @@ namespace { namespace DarkMode { + UINT msgSetDarkMode() { + // No need to threadguard this, should be main thread only, not much harm even if it's not + static UINT val = 0; + if (val == 0) val = RegisterWindowMessage(L"libPPUI:msgSetDarkMode"); + return val; + } bool IsSupportedSystem() { - static bool ret = IsWindows10OrGreater(); - return ret; + return Win10BuildNumber() >= 17763 && !IsWine(); // require at least Win10 1809 / Server 2019 } bool IsWindows11() { return Win10BuildNumber() >= 22000; @@ -253,9 +270,18 @@ namespace DarkMode { } } + void ApplyDarkThemeCtrl2(HWND ctrl, bool bDark, const wchar_t* ThemeID_light, const wchar_t * ThemeID_dark) { + if (ctrl == NULL) return; + AllowDarkModeForWindow(ctrl, bDark); + if (bDark && IsSupportedSystem()) { + ::SetWindowTheme(ctrl, ThemeID_dark, NULL); + } else { + ::SetWindowTheme(ctrl, ThemeID_light, NULL); + } + } + void ApplyDarkThemeCtrl(HWND ctrl, bool bDark, const wchar_t* ThemeID) { - // Both ways work - // DarkMode_Theme approach doesn't require evil undocumented MS API calls though + if ( ctrl == NULL ) return; AllowDarkModeForWindow(ctrl, bDark); if (bDark && IsSupportedSystem()) { std::wstring temp = L"DarkMode_"; temp += ThemeID; @@ -362,17 +388,13 @@ namespace DarkMode { return false; } - static bool IsHighContrastImpl() - { + bool IsHighContrast() { HIGHCONTRASTW highContrast = { sizeof(highContrast) }; if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(highContrast), &highContrast, FALSE)) - return highContrast.dwFlags & HCF_HIGHCONTRASTON; + return (highContrast.dwFlags & HCF_HIGHCONTRASTON) != 0; return false; } - bool IsHighContrast() { - static bool v = IsHighContrastImpl(); - return v; - } + static void DrawTab(CTabCtrl& tabs, CDCHandle dc, int iTab, bool selected, bool focused, const RECT * rcPaint) { (void)focused; PFC_ASSERT((tabs.GetStyle() & TCS_VERTICAL) == 0); @@ -494,7 +516,7 @@ namespace DarkMode { class CToolbarHook { bool m_dark = false; const bool m_explorerTheme; - CWindow m_wnd; + CToolBarCtrl m_wnd; public: CToolbarHook(HWND wnd, bool initial, bool bExplorerTheme) : m_wnd(wnd), m_explorerTheme(bExplorerTheme) { SetDark(initial); @@ -511,6 +533,8 @@ namespace DarkMode { if (m_explorerTheme) ::SetWindowTheme(m_wnd, L"Explorer", NULL); } m_wnd.Invalidate(); + + ApplyDarkThemeCtrl(m_wnd.GetToolTips(), v); } ~CToolbarHook() { if (m_dark) lstDark_clear(m_wnd); @@ -564,8 +588,62 @@ namespace DarkMode { void CTabsHook::SetDark(bool v) { m_dark = v; if (m_hWnd != NULL) Invalidate(); + + ApplyDarkThemeCtrl(GetToolTips(), v); } + class CTreeViewHook : public CWindowImpl { + bool m_dark; + public: + CTreeViewHook(bool v) : m_dark(v) {} + + BEGIN_MSG_MAP_EX(CTreeViewHook) + MESSAGE_RANGE_HANDLER_EX(WM_CTLCOLORMSGBOX, WM_CTLCOLORSTATIC, OnCtlColor) + MESSAGE_HANDLER_EX(msgSetDarkMode(), OnSetDarkMode) + MESSAGE_HANDLER_EX(TVM_EDITLABEL, OnEditLabel) + END_MSG_MAP() + + LRESULT OnCtlColor(UINT uMsg, WPARAM wParam, LPARAM lParam) { + return GetParent().SendMessage(uMsg, wParam, lParam); + } + LRESULT OnEditLabel(UINT, WPARAM, LPARAM) { + LRESULT ret = DefWindowProc(); + if (ret != 0) { + HWND edit = (HWND) ret; + PFC_ASSERT( ::IsWindow(edit) ); + ApplyDarkThemeCtrl( edit, m_dark ); + } + return ret; + } + void SetDark(bool v) { + if (m_dark == v) return; + m_dark = v; + ApplyDark(); + } + void ApplyDark() { + ApplyDarkThemeCtrl(m_hWnd, m_dark); + COLORREF bk = m_dark ? GetSysColor(COLOR_WINDOW) : (COLORREF)(-1); + COLORREF tx = m_dark ? GetSysColor(COLOR_WINDOWTEXT) : (COLORREF)(-1); + this->SetTextColor(tx); this->SetLineColor(tx); + this->SetBkColor(bk); + + ApplyDarkThemeCtrl(GetToolTips(), m_dark); + } + + void SubclassWindow(HWND wnd) { + WIN32_OP_D( __super::SubclassWindow(wnd) ); + this->ApplyDark(); + } + + LRESULT OnSetDarkMode(UINT, WPARAM wp, LPARAM) { + switch (wp) { + case 0: SetDark(false); break; + case 1: SetDark(true); break; + } + return 1; + } + }; + class CDialogHook : public CWindowImpl { bool m_enabled; public: @@ -828,6 +906,9 @@ namespace DarkMode { } void Paint(CDCHandle dc) { + CRect rcClient; WIN32_OP_D(GetClientRect(rcClient)); + dc.FillSolidRect(rcClient, GetSysColor(COLOR_BTNFACE)); // Wine seems to not call our WM_ERASEBKGND handler, fill the background here too + dc.SelectFont(GetFont()); dc.SetBkMode(TRANSPARENT); dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT)); @@ -864,7 +945,8 @@ namespace DarkMode { ds.hDC = dc; ds.rcItem = rcPart; - GetParent().SendMessage(WM_DRAWITEM, (WPARAM)m_hWnd, (LPARAM)&ds); + DCStateScope scope(dc); + GetParent().SendMessage(WM_DRAWITEM, GetDlgCtrlID(), (LPARAM)&ds); } else { CRect rcText = rcPart; int defMargin = rcText.Height() / 4; @@ -875,7 +957,6 @@ namespace DarkMode { } if (GetStyle() & SBARS_SIZEGRIP) { - CRect rcClient; WIN32_OP_D(GetClientRect(rcClient)); CSize size; auto theme = OpenThemeData(*this, L"status"); PFC_ASSERT(theme != NULL); @@ -921,10 +1002,15 @@ namespace DarkMode { MSG_WM_PRINTCLIENT(OnPaint) MSG_WM_ERASEBKGND(OnEraseBkgnd) MSG_WM_UPDATEUISTATE(OnUpdateUIState) + + // Note that checkbox implementation likes to paint on its own in response to events + // instead of invalidating and handling WM_PAINT + // We have to specifically trigger WM_PAINT to override their rendering with ours MESSAGE_HANDLER_EX(WM_SETFOCUS, OnMsgRedraw) MESSAGE_HANDLER_EX(WM_KILLFOCUS, OnMsgRedraw) MESSAGE_HANDLER_EX(WM_ENABLE, OnMsgRedraw) MESSAGE_HANDLER_EX(WM_SETTEXT, OnMsgRedraw) + MESSAGE_HANDLER_EX(msgSetDarkMode(), OnSetDarkMode) END_MSG_MAP() @@ -937,12 +1023,27 @@ namespace DarkMode { } LRESULT OnMsgRedraw(UINT, WPARAM, LPARAM) { - Invalidate(); SetMsgHandled(FALSE); return 0; + if ( m_dark ) { + // PROBLEM: + // Can't invalidate prior to their handling of the message + // Causes bugs with specific chains of events - EnableWindow() followed immediately SetWindowText() + LRESULT ret = DefWindowProc(); + Invalidate(); + return ret; + } + SetMsgHandled(FALSE); return 0; } void OnUpdateUIState(WORD nAction, WORD nState) { (void)nAction; - if (nState & (UISF_HIDEACCEL | UISF_HIDEFOCUS)) Invalidate(); + if ( m_dark && (nState & (UISF_HIDEACCEL | UISF_HIDEFOCUS)) != 0) { + // PROBLEM: + // Can't invalidate prior to their handling of the message + // Causes bugs with specific chains of events - EnableWindow() followed immediately SetWindowText() + DefWindowProc(); + Invalidate(); + return; + } SetMsgHandled(FALSE); } void PaintHandler(CDCHandle dc) { @@ -957,7 +1058,8 @@ namespace DarkMode { GetParent().SendMessage(WM_CTLCOLORBTN, (WPARAM)dc.m_hDC, (LPARAM)m_hWnd); if (bDisabled) dc.SetTextColor(DarkMode::GetSysColor(COLOR_GRAYTEXT)); // override WM_CTLCOLORBTN - const DWORD btnType = GetStyle() & BS_TYPEMASK; + const DWORD btnStyle = GetStyle(); + const DWORD btnType = btnStyle & BS_TYPEMASK; const bool bRadio = (btnType == BS_RADIOBUTTON || btnType == BS_AUTORADIOBUTTON); const int part = bRadio ? BP_RADIOBUTTON : BP_CHECKBOX; @@ -965,7 +1067,8 @@ namespace DarkMode { const DWORD uiState = (DWORD)SendMessage(WM_QUERYUISTATE); - const bool bPressed = (ctrlState & BST_CHECKED) != 0; + const bool bChecked = (ctrlState & BST_CHECKED) != 0; + const bool bMixed = (ctrlState & BST_INDETERMINATE) != 0; const bool bHot = (ctrlState & BST_HOT) != 0; const bool bFocus = (ctrlState & BST_FOCUS) != 0 && (uiState & UISF_HIDEFOCUS) == 0; @@ -977,11 +1080,17 @@ namespace DarkMode { if (theme != NULL && IsThemePartDefined(theme, part, 0)) { int state = 0; if (bDisabled) { - state = bPressed ? CBS_CHECKEDDISABLED : CBS_UNCHECKEDDISABLED; + if ( bChecked ) state = CBS_CHECKEDDISABLED; + else if ( bMixed ) state = CBS_MIXEDDISABLED; + else state = CBS_UNCHECKEDDISABLED; } else if (bHot) { - state = bPressed ? CBS_CHECKEDHOT : CBS_UNCHECKEDHOT; + if ( bChecked ) state = CBS_CHECKEDHOT; + else if ( bMixed ) state = CBS_MIXEDHOT; + else state = CBS_UNCHECKEDNORMAL; } else { - state = bPressed ? CBS_CHECKEDNORMAL : CBS_UNCHECKEDNORMAL; + if ( bChecked ) state = CBS_CHECKEDNORMAL; + else if ( bMixed ) state = CBS_MIXEDNORMAL; + else state = CBS_UNCHECKEDNORMAL; } CSize size; @@ -1001,7 +1110,8 @@ namespace DarkMode { if (theme != NULL) CloseThemeData(theme); if (!bDrawn) { int stateEx = bRadio ? DFCS_BUTTONRADIO : DFCS_BUTTONCHECK; - if (bPressed) stateEx |= DFCS_CHECKED; + if (bChecked) stateEx |= DFCS_CHECKED; + // FIX ME bMixed ? if (bDisabled) stateEx |= DFCS_INACTIVE; else if (bHot) stateEx |= DFCS_HOT; @@ -1020,7 +1130,12 @@ namespace DarkMode { if (!text.IsEmpty()) { CRect rcText = rcClient; rcText.left += margin; - UINT dtFlags = DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE; + UINT dtFlags = DT_VCENTER; + if (btnStyle & BS_MULTILINE) { + dtFlags |= DT_WORDBREAK; + } else { + dtFlags |= DT_END_ELLIPSIS | DT_SINGLELINE; + } dc.DrawText(text, text.GetLength(), rcText, dtFlags); if (bFocus) { dc.DrawText(text, text.GetLength(), rcText, DT_CALCRECT | dtFlags); @@ -1449,6 +1564,50 @@ namespace DarkMode { dc.DrawText(L"˅", 1, layout.lower, DT_SINGLELINE | DT_VCENTER | DT_CENTER | DT_NOPREFIX); } }; + + class CNCFrameHook : public CWindowImpl { + public: + CNCFrameHook(bool dark) : m_dark(dark) {} + + BEGIN_MSG_MAP_EX(CNCFrameHook) + MESSAGE_HANDLER_EX(msgSetDarkMode(), OnSetDarkMode) + MSG_WM_NCPAINT(OnNCPaint) + END_MSG_MAP() + + void SetDark(bool v) { + if (v != m_dark) { + m_dark = v; ApplyDark(); + } + } + BOOL SubclassWindow(HWND wnd) { + auto rv = __super::SubclassWindow(wnd); + if (rv) { + ApplyDark(); + } + return rv; + } + private: + void OnNCPaint(HRGN rgn) { + if (m_dark) { + NCPaintDarkFrame(m_hWnd, rgn); + return; + } + SetMsgHandled(FALSE); + } + void ApplyDark() { + ApplyDarkThemeCtrl(m_hWnd, m_dark); + Invalidate(); + } + LRESULT OnSetDarkMode(UINT, WPARAM wp, LPARAM) { + switch (wp) { + case 0: SetDark(false); break; + case 1: SetDark(true); break; + } + return 1; + } + + bool m_dark; + }; } void CHooks::AddPopup(HWND wnd) { @@ -1480,17 +1639,36 @@ namespace DarkMode { AddCtrlMsg(wnd); } void CHooks::AddComboBox(HWND wnd) { - addOp([wnd, this] { SetWindowTheme(wnd, m_dark ? L"DarkMode_CFD" : L"Explorer", NULL);}); + { + CComboBox combo = wnd; + COMBOBOXINFO info = {sizeof(info)}; + WIN32_OP_D( combo.GetComboBoxInfo(&info) ); + if (info.hwndList != NULL) { + AddListBox( info.hwndList ); + } + } + + addOp([wnd, this] { + SetWindowTheme(wnd, m_dark ? L"DarkMode_CFD" : L"Explorer", NULL); + }); } void CHooks::AddComboBoxEx(HWND wnd) { this->AddControls(wnd); // recurse to add the combo box } - void CHooks::AddEditBox(HWND wnd) { AddGeneric(wnd); } + void CHooks::AddEditBox(HWND wnd) { +#if 0 // Experimental + auto hook = new ImplementOnFinalMessage(m_dark); + hook->SubclassWindow( wnd ); + AddCtrlMsg( wnd ); +#else + AddGeneric(wnd); +#endif + } void CHooks::AddButton(HWND wnd) { CButton btn(wnd); auto style = btn.GetButtonStyle(); auto type = style & BS_TYPEMASK; - if ((type == BS_CHECKBOX || type == BS_AUTOCHECKBOX || type == BS_RADIOBUTTON || type == BS_AUTORADIOBUTTON) && (style & BS_PUSHLIKE) == 0) { + if ((type == BS_CHECKBOX || type == BS_AUTOCHECKBOX || type == BS_RADIOBUTTON || type == BS_AUTORADIOBUTTON || type == BS_3STATE || type == BS_AUTO3STATE) && (style & BS_PUSHLIKE) == 0) { // MS checkbox implementation is terminally retarded and won't draw text in correct color // Subclass it and draw our own content // Other button types seem OK @@ -1557,18 +1735,12 @@ namespace DarkMode { } void CHooks::AddTreeView(HWND wnd) { - this->addOp([wnd, this] { - CTreeViewCtrl tv(wnd); - ApplyDarkThemeCtrl(tv, m_dark); - COLORREF bk = m_dark ? GetSysColor(COLOR_WINDOW) : (COLORREF)(-1); - COLORREF tx = m_dark ? GetSysColor(COLOR_WINDOWTEXT) : (COLORREF)(-1); - tv.SetTextColor(tx); tv.SetLineColor(tx); - tv.SetBkColor(bk); - }); + auto hook = new ImplementOnFinalMessage(m_dark); + hook->SubclassWindow(wnd); + this->AddCtrlMsg(wnd); } void CHooks::AddListBox(HWND wnd) { - // Not needed! Yay! - // Handling WM_CTLCOLOR* is enough + this->AddGeneric( wnd ); #if 0 auto subst = CListControl_ReplaceListBox(wnd); if (subst) AddPPListControl(subst); @@ -1671,16 +1843,89 @@ namespace DarkMode { m_cleanup.clear(); } - UINT msgSetDarkMode() { - // No need to threadguard this, should be main thread only, not much harm even if it's not - static UINT val = 0; - if (val == 0) val = RegisterWindowMessage(L"libPPUI:msgSetDarkMode"); - return val; - } - void CHooks::AddApp() { addOp([this] { SetAppDarkMode(this->m_dark); }); } + + void NCPaintDarkFrame(HWND wnd_, HRGN rgn_) { + // rgn is in SCREEN COORDINATES, possibly (HRGN)1 to indicate no clipping / whole nonclient area redraw + // we're working with SCREEN COORDINATES until actual DC painting + CWindow wnd = wnd_; + + CRect rcWindow, rcClient; + WIN32_OP_D( wnd.GetWindowRect(rcWindow) ); + WIN32_OP_D( wnd.GetClientRect(rcClient) ); + WIN32_OP_D( wnd.ClientToScreen( rcClient ) ); // transform all to same coordinate system + + CRgn rgnClip; + WIN32_OP_D( rgnClip.CreateRectRgnIndirect(rcWindow) != NULL ); + if (rgn_ != NULL && rgn_ != (HRGN)1) { + // we have a valid HRGN from caller? + if (rgnClip.CombineRgn(rgn_, RGN_AND) == NULLREGION) return; // nothing to draw, exit early + } + + { + // Have scroll bars? Have DefWindowProc() them then exclude from our rgnClip. + SCROLLBARINFO si = { sizeof(si) }; + if (::GetScrollBarInfo(wnd, OBJID_VSCROLL, &si) && (si.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0 && si.rcScrollBar.left < si.rcScrollBar.right) { + CRect rc = si.rcScrollBar; + // rcClient.right = rc.right; + CRgn rgn; WIN32_OP_D( rgn.CreateRectRgnIndirect(rc) ); + int status = SIMPLEREGION; + if (rgnClip) { + status = rgn.CombineRgn(rgn, rgnClip, RGN_AND); + } + if (status != NULLREGION) { + DefWindowProc(wnd, WM_NCPAINT, (WPARAM)rgn.m_hRgn, 0); + rgnClip.CombineRgn(rgn, RGN_DIFF); // exclude from further drawing + } + } + if (::GetScrollBarInfo(wnd, OBJID_HSCROLL, &si) && (si.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0 && si.rcScrollBar.top < si.rcScrollBar.bottom) { + CRect rc = si.rcScrollBar; + // rcClient.bottom = rc.bottom; + CRgn rgn; WIN32_OP_D(rgn.CreateRectRgnIndirect(rc)); + int status = SIMPLEREGION; + if (rgnClip) { + status = rgn.CombineRgn(rgn, rgnClip, RGN_AND); + } + if (status != NULLREGION) { + DefWindowProc(wnd, WM_NCPAINT, (WPARAM)rgn.m_hRgn, 0); + rgnClip.CombineRgn(rgn, RGN_DIFF); // exclude from further drawing + } + } + } + + const auto colorLight = DarkMode::GetSysColor(COLOR_BTNHIGHLIGHT); + const auto colorDark = DarkMode::GetSysColor(COLOR_BTNSHADOW); + + CWindowDC dc( wnd ); + if (dc.IsNull()) { + PFC_ASSERT(!"???"); + return; + } + + + // Window DC has (0,0) in upper-left corner of our window (not screen, not client) + // Turn rcWindow to (0,0), (winWidth, winHeight) + CPoint origin = rcWindow.TopLeft(); + rcWindow.OffsetRect(-origin); + rcClient.OffsetRect(-origin); + + if (!rgnClip.IsNull()) { + // rgnClip is still in screen coordinates, fix this here + rgnClip.OffsetRgn(-origin); + dc.SelectClipRgn(rgnClip); + } + + // bottom + dc.FillSolidRect(CRect(rcClient.left, rcClient.bottom, rcWindow.right, rcWindow.bottom), colorLight); + // right + dc.FillSolidRect(CRect(rcClient.right, rcWindow.top, rcWindow.right, rcClient.bottom), colorLight); + // top + dc.FillSolidRect(CRect(rcWindow.left, rcWindow.top, rcWindow.right, rcClient.top), colorDark); + // left + dc.FillSolidRect(CRect(rcWindow.left, rcClient.top, rcClient.left, rcWindow.bottom), colorDark); + } } diff --git a/sdk/libPPUI/DarkMode.h b/sdk/libPPUI/DarkMode.h index 1145487..a6a50d3 100644 --- a/sdk/libPPUI/DarkMode.h +++ b/sdk/libPPUI/DarkMode.h @@ -3,12 +3,17 @@ #include namespace DarkMode { + // Is dark mode supported on this system or not? bool IsSupportedSystem(); + // Is system in dark mode or not? bool QueryUserOption(); + // Darken menus etc app-wide void SetAppDarkMode(bool bDark); + // Darken window title bar void UpdateTitleBar(HWND wnd, bool bDark ); void ApplyDarkThemeCtrl(HWND ctrl, bool bDark, const wchar_t * ThemeID = L"Explorer"); + void ApplyDarkThemeCtrl2(HWND ctrl, bool bDark, const wchar_t* ThemeID_light = L"Explorer", const wchar_t* ThemeID_dark = L"DarkMode_Explorer"); void AllowDarkModeForWindow(HWND wnd, bool bDark); // One-shot version of darkening function for editboxes @@ -31,6 +36,9 @@ namespace DarkMode { // Can be used with NOTIFY_CODE_HANDLER() directly LRESULT OnCustomDraw(int, NMHDR*, BOOL & bHandled); + // Handle WM_NCPAINT drawing dark frame + void NCPaintDarkFrame(HWND ctrl, HRGN rgn); + bool IsHighContrast(); // msgSetDarkMode diff --git a/sdk/libPPUI/EditBoxFix.cpp b/sdk/libPPUI/EditBoxFix.cpp new file mode 100644 index 0000000..b894b71 --- /dev/null +++ b/sdk/libPPUI/EditBoxFix.cpp @@ -0,0 +1,19 @@ +#include "stdafx.h" +#include "EditBoxFixes.h" +#include "wtl-pp.h" +#include "windowLifetime.h" + +namespace PP { + void editBoxFix(HWND wndEdit) { + PFC_ASSERT( IsWindow(wndEdit) ); + PP::subclassThisWindow(wndEdit); + } + void comboBoxFix(HWND wndCombo) { + PFC_ASSERT( IsWindow(wndCombo) ); + CComboBox combo = wndCombo; + COMBOBOXINFO info = { sizeof(info) }; + if (combo.GetComboBoxInfo(&info)) { + if ( info.hwndItem != NULL ) editBoxFix( info.hwndItem ); + } + } +} \ No newline at end of file diff --git a/sdk/libPPUI/EditBoxFixes.h b/sdk/libPPUI/EditBoxFixes.h new file mode 100644 index 0000000..e333743 --- /dev/null +++ b/sdk/libPPUI/EditBoxFixes.h @@ -0,0 +1,7 @@ +#pragma once + +namespace PP { + // One-line methods to inject our edit box shims: Ctrl+A, Ctrl+Backspace, etc + void editBoxFix(HWND wndEdit); + void comboBoxFix(HWND wndCombo); +} \ No newline at end of file diff --git a/sdk/libPPUI/GDIUtils.h b/sdk/libPPUI/GDIUtils.h index 6a53f35..8da18f2 100644 --- a/sdk/libPPUI/GDIUtils.h +++ b/sdk/libPPUI/GDIUtils.h @@ -107,7 +107,7 @@ class CBackBuffer : public CDC { void Attach(HBITMAP bmp) { CSize size; bool state = CBitmapHandle(bmp).GetSize(size); - ATLASSERT(state); + ATLASSERT(state); (void)state; Attach(bmp, size); } BOOL Allocate(CSize size, HDC dcCompatible = NULL) { diff --git a/sdk/libPPUI/HyperLinkCtrl.h b/sdk/libPPUI/HyperLinkCtrl.h new file mode 100644 index 0000000..383c1ac --- /dev/null +++ b/sdk/libPPUI/HyperLinkCtrl.h @@ -0,0 +1,8 @@ +#pragma once +#include +namespace PP { + // One-line method to turn static control to hyperlink firing WM_NOTIFY + void createHyperLink(HWND wndReplaceMe); + void createHyperLink(HWND wndReplaceMe, std::function handler); + void createHyperLink(HWND wndReplaceMe, const wchar_t * openURL); +} \ No newline at end of file diff --git a/sdk/libPPUI/IDataObjectUtils.cpp b/sdk/libPPUI/IDataObjectUtils.cpp index bfd673f..261cce9 100644 --- a/sdk/libPPUI/IDataObjectUtils.cpp +++ b/sdk/libPPUI/IDataObjectUtils.cpp @@ -50,9 +50,9 @@ HRESULT IDataObjectUtils::DataBlockToSTGMEDIUM(const void * blockPtr, t_size blo } return DV_E_TYMED; } - } catch(pfc::exception_not_implemented) { + } catch(pfc::exception_not_implemented const &) { return E_NOTIMPL; - } catch(std::bad_alloc) { + } catch(std::bad_alloc const &) { return E_OUTOFMEMORY; } catch(...) { return E_UNEXPECTED; diff --git a/sdk/libPPUI/ImageEncoder.cpp b/sdk/libPPUI/ImageEncoder.cpp index 1bfc955..9244f6a 100644 --- a/sdk/libPPUI/ImageEncoder.cpp +++ b/sdk/libPPUI/ImageEncoder.cpp @@ -40,9 +40,9 @@ int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) void ConvertImage(const TCHAR * in, const TCHAR * out, const TCHAR * format, ULONG quality) { GdiplusScope scope; - std::unique_ptr< Bitmap > image ( new Bitmap(in) ); - EH << image->GetLastStatus(); - SaveImage(&*image, out, format, quality); + Bitmap image (in); + EH << image.GetLastStatus(); + SaveImage(&image, out, format, quality); } static void add16clip(uint16_t& v, int d) { diff --git a/sdk/libPPUI/PaintUtils.cpp b/sdk/libPPUI/PaintUtils.cpp index 913cf72..421fd16 100644 --- a/sdk/libPPUI/PaintUtils.cpp +++ b/sdk/libPPUI/PaintUtils.cpp @@ -169,15 +169,22 @@ namespace PaintUtils { } } void DrawTrack2(HDC p_dc, const CRect& rcTrack, const CRect& rcUpdate, COLORREF clrHighlight, COLORREF clrShadow) { - CMemoryDC dc(p_dc, rcUpdate); CRect rc(*rcTrack); - - WIN32_OP_D(dc.BitBlt(rcUpdate.left, rcUpdate.top, rcUpdate.Width(), rcUpdate.Height(), p_dc, rcUpdate.left, rcUpdate.top, SRCCOPY)); - +#if 1 + CDCHandle dc(p_dc); + SelectObjectScope scope(dc, GetStockObject(DC_PEN)); + dc.SetDCPenColor(clrHighlight); + dc.MoveTo(rc.left, rc.bottom); + dc.LineTo(rc.right, rc.bottom); + dc.LineTo(rc.right, rc.top); + dc.SetDCPenColor(clrShadow); + dc.LineTo(rc.left, rc.top); + dc.LineTo(rc.left, rc.bottom); +#else try { Gdiplus::Point points[] = { Gdiplus::Point(rc.left, rc.bottom), Gdiplus::Point(rc.right, rc.bottom), Gdiplus::Point(rc.right, rc.top), Gdiplus::Point(rc.left, rc.top)}; GdiplusErrorHandler eh; - Gdiplus::Graphics graphics(dc); + Gdiplus::Graphics graphics(p_dc); eh << graphics.GetLastStatus(); Gdiplus::Color c; c.SetFromCOLORREF(clrHighlight); @@ -191,19 +198,18 @@ namespace PaintUtils { eh << graphics.DrawLine(&penSH, points[3], points[0]); } catch (std::exception const& e) { (void)e; + PFC_ASSERT(!"???"); // console::print(e.what()); } +#endif } void DrawTrackVolume2(HDC p_dc, const CRect& rcTrack, const CRect& rcUpdate, COLORREF clrHighlight, COLORREF clrShadow) { - CMemoryDC dc(p_dc, rcUpdate); CRect rc(rcTrack); - WIN32_OP_D(dc.BitBlt(rcUpdate.left, rcUpdate.top, rcUpdate.Width(), rcUpdate.Height(), p_dc, rcUpdate.left, rcUpdate.top, SRCCOPY)); - try { Gdiplus::Point points[] = { Gdiplus::Point(rc.left, rc.bottom), Gdiplus::Point(rc.right, rc.bottom), Gdiplus::Point(rc.right, rc.top) }; GdiplusErrorHandler eh; - Gdiplus::Graphics graphics(dc); + Gdiplus::Graphics graphics(p_dc); eh << graphics.GetLastStatus(); Gdiplus::Color c; c.SetFromCOLORREF(clrHighlight); @@ -218,6 +224,7 @@ namespace PaintUtils { eh << graphics.DrawLine(&penSH, points[2], points[0] + Gdiplus::Point(0, -1)); } catch (std::exception const& e) { (void)e; + PFC_ASSERT(!"???"); // console::print(e.what()); } } @@ -241,6 +248,7 @@ namespace PaintUtils { eh << graphics.DrawLine(&pen, points[0], points[1]); } catch(std::exception const & e) { (void) e; + PFC_ASSERT(!"???"); // console::print(e.what()); } } diff --git a/sdk/libPPUI/TreeMultiSel.h b/sdk/libPPUI/TreeMultiSel.h index 7268b75..1fd3617 100644 --- a/sdk/libPPUI/TreeMultiSel.h +++ b/sdk/libPPUI/TreeMultiSel.h @@ -159,9 +159,9 @@ class CTreeMultiSel : public CMessageMap { } else if (m_selection.size() > 0) { CTreeViewCtrl tree(hdr->hwndFrom); CRgn rgn; rgn.CreateRectRgn(0,0,0,0); - for(auto walk = m_selection.begin(); walk != m_selection.end(); ++walk) { + for(auto walk : m_selection) { CRect rc; - if (tree.GetItemRect(*walk, rc, TRUE)) { + if (tree.GetItemRect(walk, rc, TRUE)) { CRgn temp; temp.CreateRectRgnIndirect(rc); rgn.CombineRgn(temp, RGN_OR); } @@ -198,6 +198,29 @@ class CTreeMultiSel : public CMessageMap { SetMsgHandled(FALSE); return 0; } + + void FixFocusItem(CTreeViewCtrl tree, HTREEITEM item) { + if (this->IsItemSelected(item) || tree.GetSelectedItem() != item) return; + + auto scope = pfc::autoToggle(m_ownSelChange, true); + + for(;;) { + if (item == TVI_ROOT || item == NULL || this->IsItemSelected(item)) { + tree.SelectItem(item); return; + } + for (auto walk = tree.GetPrevSiblingItem(item); walk != NULL; walk = tree.GetPrevSiblingItem(walk)) { + if (this->IsItemSelected(walk)) { + tree.SelectItem(walk); return; + } + } + for (auto walk = tree.GetNextSiblingItem(item); walk != NULL; walk = tree.GetNextSiblingItem(walk)) { + if (this->IsItemSelected(walk)) { + tree.SelectItem(walk); return; + } + } + item = tree.GetParentItem(item); + } + } BOOL HandleClick(CTreeViewCtrl tree, CPoint pt) { UINT htFlags = 0; @@ -205,6 +228,7 @@ class CTreeMultiSel : public CMessageMap { if (item != NULL && (htFlags & TVHT_ONITEM) != 0) { if (IsKeyPressed(VK_CONTROL)) { SelectToggleItem(tree, item); + FixFocusItem(tree, item); return TRUE; } else if (item == tree.GetSelectedItem() && !IsItemSelected(item)) { SelectToggleItem(tree, item); @@ -252,7 +276,7 @@ class CTreeMultiSel : public CMessageMap { } selection_t newSel = GrabRange(tree, m_selStart, item ); - ApplySelection(tree, newSel); + ApplySelection(tree, std::move(newSel)); } static selection_t GrabRange(CTreeViewCtrl tree, HTREEITEM item1, HTREEITEM item2) { selection_t range1, range2; @@ -292,6 +316,7 @@ class CTreeMultiSel : public CMessageMap { NMTVCUSTOMDRAW* info = (NMTVCUSTOMDRAW*)hdr; switch (info->nmcd.dwDrawStage) { case CDDS_ITEMPREPAINT: + // NOTE: This doesn't work all the way. Unflagging CDIS_FOCUS isn't respected, causing weird behaviors when using ctrl+cursors or unselecting items. if (this->IsItemSelected((HTREEITEM)info->nmcd.dwItemSpec)) { info->nmcd.uItemState |= CDIS_SELECTED; } else { @@ -311,7 +336,7 @@ class CTreeMultiSel : public CMessageMap { DeselectAll(tree); SelectItem(tree, item); } - void ApplySelection(CTreeViewCtrl tree, selection_t const & newSel) { + void ApplySelection(CTreeViewCtrl tree, selection_t && newSel) { CRgn updateRgn; bool changed = false; if (newSel.size() != m_selection.size() && newSel.size() + m_selection.size() > 100) { @@ -319,21 +344,21 @@ class CTreeMultiSel : public CMessageMap { changed = true; } else { WIN32_OP_D(updateRgn.CreateRectRgn(0, 0, 0, 0) != NULL); - for (auto walk = m_selection.begin(); walk != m_selection.end(); ++walk) { - if (newSel.count(*walk) == 0) { + for (auto walk : m_selection) { + if (newSel.count(walk) == 0) { changed = true; CRect rc; - if (tree.GetItemRect(*walk, rc, TRUE)) { + if (tree.GetItemRect(walk, rc, TRUE)) { CRgn temp; WIN32_OP_D(temp.CreateRectRgnIndirect(rc)); WIN32_OP_D(updateRgn.CombineRgn(temp, RGN_OR) != ERROR); } } } - for (auto walk = newSel.begin(); walk != newSel.end(); ++walk) { - if (m_selection.count(*walk) == 0) { + for (auto walk : newSel) { + if (m_selection.count(walk) == 0) { changed = true; CRect rc; - if (tree.GetItemRect(*walk, rc, TRUE)) { + if (tree.GetItemRect(walk, rc, TRUE)) { CRgn temp; WIN32_OP_D(temp.CreateRectRgnIndirect(rc)); WIN32_OP_D(updateRgn.CombineRgn(temp, RGN_OR) != ERROR); } @@ -341,7 +366,7 @@ class CTreeMultiSel : public CMessageMap { } } if (changed) { - m_selection = newSel; + m_selection = std::move(newSel); tree.RedrawWindow(NULL, updateRgn); SendOnSelChanged(tree); } @@ -363,9 +388,9 @@ class CTreeMultiSel : public CMessageMap { CRgn updateRgn; if (m_selection.size() <= 100) { WIN32_OP_D(updateRgn.CreateRectRgn(0, 0, 0, 0) != NULL); - for (auto walk = m_selection.begin(); walk != m_selection.end(); ++walk) { + for (auto walk : m_selection) { CRect rc; - if (tree.GetItemRect(*walk, rc, TRUE)) { + if (tree.GetItemRect(walk, rc, TRUE)) { CRgn temp; WIN32_OP_D(temp.CreateRectRgnIndirect(rc)); WIN32_OP_D(updateRgn.CombineRgn(temp, RGN_OR) != ERROR); } diff --git a/sdk/libPPUI/gdiplus_helpers.cpp b/sdk/libPPUI/gdiplus_helpers.cpp index 43541f3..4130e96 100644 --- a/sdk/libPPUI/gdiplus_helpers.cpp +++ b/sdk/libPPUI/gdiplus_helpers.cpp @@ -32,22 +32,22 @@ HBITMAP GdiplusLoadBitmap(UINT id, const TCHAR* resType, CSize size) { auto stream = WinLoadResourceAsStream(GetThisModuleHandle(), MAKEINTRESOURCE(id), resType); GdiplusErrorHandler EH; - std::unique_ptr source ( new Image(stream) ); - EH << source->GetLastStatus(); + Image source ( stream ); + EH << source.GetLastStatus(); - std::unique_ptr resized ( new Bitmap(size.cx, size.cy, PixelFormat32bppARGB) ); - EH << resized->GetLastStatus(); + Bitmap resized (size.cx, size.cy, PixelFormat32bppARGB); + EH << resized.GetLastStatus(); { - std::unique_ptr target ( new Graphics(resized.get()) ); - EH << target->GetLastStatus(); - EH << target->SetInterpolationMode(InterpolationModeHighQuality); - EH << target->Clear(Color(0, 0, 0, 0)); - EH << target->DrawImage(source.get(), Rect(0, 0, size.cx, size.cy)); + Graphics target(&resized); + EH << target.GetLastStatus(); + EH << target.SetInterpolationMode(InterpolationModeHighQuality); + EH << target.Clear(Color(0, 0, 0, 0)); + EH << target.DrawImage(&source, Rect(0, 0, size.cx, size.cy)); } HBITMAP bmp = NULL; - EH << resized->GetHBITMAP(Gdiplus::Color::White, &bmp); + EH << resized.GetHBITMAP(Gdiplus::Color::White, &bmp); return bmp; } catch (...) { PFC_ASSERT(!"Should not get here"); @@ -73,11 +73,11 @@ std::unique_ptr< Gdiplus::Bitmap > GdiplusResizeImage(Gdiplus::Image* source, CS GdiplusErrorHandler EH; std::unique_ptr resized ( new Bitmap(size.cx, size.cy, pf) ); EH << resized->GetLastStatus(); - std::unique_ptr target ( new Graphics(resized.get()) ); - EH << target->GetLastStatus(); - EH << target->SetInterpolationMode(InterpolationModeHighQuality); - EH << target->Clear(Color(0, 0, 0, 0)); - EH << target->DrawImage(source, Rect(0, 0, size.cx, size.cy)); + Graphics target (resized.get() ); + EH << target.GetLastStatus(); + EH << target.SetInterpolationMode(InterpolationModeHighQuality); + EH << target.Clear(Color(0, 0, 0, 0)); + EH << target.DrawImage(source, Rect(0, 0, size.cx, size.cy)); return resized; } @@ -108,26 +108,26 @@ HICON GdiplusLoadIconEx(UINT id, const TCHAR* resType, GdiplusIconArg_t const& a auto stream = WinLoadResourceAsStream(GetThisModuleHandle(), MAKEINTRESOURCE(id), resType); GdiplusErrorHandler EH; - pfc::ptrholder_t source = new Image(stream); - EH << source->GetLastStatus(); - pfc::ptrholder_t resized = new Bitmap(arg.size.cx, arg.size.cy, PixelFormat32bppARGB); - EH << resized->GetLastStatus(); + Bitmap resized(arg.size.cx, arg.size.cy, PixelFormat32bppARGB); + EH << resized.GetLastStatus(); { - pfc::ptrholder_t target = new Graphics(resized.get_ptr()); - EH << target->GetLastStatus(); - EH << target->SetInterpolationMode(InterpolationModeHighQuality); - EH << target->Clear(Color(0, 0, 0, 0)); - EH << target->DrawImage(source.get_ptr(), Rect(0, 0, arg.size.cx, arg.size.cy)); + Image source(stream); + EH << source.GetLastStatus(); + + Graphics target(&resized); + EH << target.GetLastStatus(); + EH << target.SetInterpolationMode(InterpolationModeHighQuality); + EH << target.Clear(Color(0, 0, 0, 0)); + EH << target.DrawImage(&source, Rect(0, 0, arg.size.cx, arg.size.cy)); } - source = nullptr; - if (arg.transform) arg.transform(resized.get_ptr()); + if (arg.transform) arg.transform(&resized); HICON icon = NULL; - EH << resized->GetHICON(&icon); + EH << resized.GetHICON(&icon); return icon; } catch (...) { PFC_ASSERT(!"Should not get here"); diff --git a/sdk/libPPUI/libPPUI-license.txt b/sdk/libPPUI/libPPUI-license.txt index 3f05fcc..dd237fe 100644 --- a/sdk/libPPUI/libPPUI-license.txt +++ b/sdk/libPPUI/libPPUI-license.txt @@ -1,4 +1,4 @@ -Copyright (C) 2002-2022 Peter Pawlowski +Copyright (C) 2002-2024 Peter Pawlowski This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/sdk/libPPUI/libPPUI.vcxproj b/sdk/libPPUI/libPPUI.vcxproj index 4aedc64..0c0024d 100644 --- a/sdk/libPPUI/libPPUI.vcxproj +++ b/sdk/libPPUI/libPPUI.vcxproj @@ -39,25 +39,26 @@ {7729EB82-4069-4414-964B-AD399091A03F} Win32Proj libPPUI + 10.0 StaticLibrary true - v143 + v142 Unicode StaticLibrary false - v143 + v142 true Unicode StaticLibrary true - v143 + v142 Unicode @@ -75,7 +76,7 @@ StaticLibrary false - v143 + v142 true Unicode @@ -161,6 +162,7 @@ Fast stdcpp17 true + MultiThreadedDebugDLL Windows @@ -179,6 +181,7 @@ stdcpp17 true ProgramDatabase + MultiThreadedDebugDLL Windows @@ -196,6 +199,7 @@ .. stdcpp17 true + MultiThreadedDebugDLL Windows @@ -234,7 +238,7 @@ Fast true /d2notypeopt %(AdditionalOptions) - NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) + NDEBUG;%(PreprocessorDefinitions) stdcpp17 true @@ -258,7 +262,7 @@ false .. /d2notypeopt %(AdditionalOptions) - NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) + NDEBUG;%(PreprocessorDefinitions) stdcpp17 true @@ -282,7 +286,7 @@ false .. /d2notypeopt %(AdditionalOptions) - NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) + NDEBUG;%(PreprocessorDefinitions) stdcpp17 true @@ -306,7 +310,7 @@ false .. /d2notypeopt %(AdditionalOptions) - NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) + NDEBUG;%(PreprocessorDefinitions) stdcpp17 true @@ -352,14 +356,17 @@ + + + @@ -397,6 +404,7 @@ + @@ -419,12 +427,12 @@ + - - + \ No newline at end of file diff --git a/sdk/libPPUI/libPPUI.vcxproj.filters b/sdk/libPPUI/libPPUI.vcxproj.filters index eba3ed5..3128d60 100644 --- a/sdk/libPPUI/libPPUI.vcxproj.filters +++ b/sdk/libPPUI/libPPUI.vcxproj.filters @@ -194,6 +194,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + @@ -283,6 +292,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/sdk/libPPUI/stdafx.h b/sdk/libPPUI/stdafx.h index c124a1f..0af3d4b 100644 --- a/sdk/libPPUI/stdafx.h +++ b/sdk/libPPUI/stdafx.h @@ -19,3 +19,8 @@ #include #include + + +#ifndef _UNICODE +#error seriously? +#endif \ No newline at end of file diff --git a/sdk/libPPUI/win32_utility.cpp b/sdk/libPPUI/win32_utility.cpp index 9f99e83..c54645b 100644 --- a/sdk/libPPUI/win32_utility.cpp +++ b/sdk/libPPUI/win32_utility.cpp @@ -173,6 +173,17 @@ void InjectParentEraseHandler(HWND wnd) { void InjectParentCtlColorHandler(HWND wnd) { WIN32_OP_D(SetWindowSubclass(wnd, CtlColorProc, 0, 0)); } +static LRESULT CALLBACK BounceNextDlgCtlProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { + if (uMsg == WM_NEXTDLGCTL) { + return ::SendMessage((HWND)dwRefData, uMsg, wParam, lParam); + } + return DefSubclassProc(hWnd, uMsg, wParam, lParam); +} + +void BounceNextDlgCtl(HWND wnd, HWND wndTo) { + ::SetWindowSubclass(wnd, BounceNextDlgCtlProc, 0, (DWORD_PTR)wndTo); +} + pfc::string8 EscapeTooltipText(const char * src) { @@ -237,7 +248,7 @@ static void GetOSVersionStringAppend(pfc::string_base & out) { if (FetchWineInfoAppend(out)) return; OSVERSIONINFO ver = {}; ver.dwOSVersionInfoSize = sizeof(ver); - WIN32_OP(GetVersionEx(&ver)); + WIN32_OP_D(GetVersionEx(&ver)); SYSTEM_INFO info = {}; GetNativeSystemInfo(&info); @@ -348,3 +359,32 @@ void PP::hookWindowMessages(HWND wnd, CMessageMap* target, DWORD targetID, std:: void PP::hookWindowMessages(HWND wnd, messageHook_t h) { PP::subclassThisWindow< CWindowHook_Proc >(wnd, h); } + +namespace PP { + static LONG regReadHelper(HKEY root, const wchar_t* path, const wchar_t* value, LONG def) { + wchar_t buf[64] = {}; + DWORD cb = (DWORD)((std::size(buf) - 1) * sizeof(buf[0])); + if (RegGetValue(root, path, value, RRF_RT_REG_SZ, NULL, buf, &cb) == 0) { + return _wtol(buf); + } + return def; + } + + static SIZE querySystemDragThreshold() { + constexpr DWORD def = 1; + static constexpr wchar_t path[] = L"Control Panel\\Desktop"; + return { + (LONG)regReadHelper(HKEY_CURRENT_USER, path, L"DragWidth", def), + (LONG)regReadHelper(HKEY_CURRENT_USER, path, L"DragHeight", def) + }; + } + SIZE queryDragThresholdForDPI(SIZE dpi) { + PFC_ASSERT(dpi.cx > 0 && dpi.cy > 0); + static SIZE sys = {}; + if ( sys.cx == 0 || sys.cy == 0 ) sys = querySystemDragThreshold(); + return { MulDiv(sys.cx, dpi.cx, 96), MulDiv(sys.cy, dpi.cy, 96) }; + } + SIZE queryDragThreshold(HWND wndFor) { + return queryDragThresholdForDPI(QueryScreenDPIEx(wndFor)); + } +} diff --git a/sdk/libPPUI/win32_utility.h b/sdk/libPPUI/win32_utility.h index f03bfdb..2a2b088 100644 --- a/sdk/libPPUI/win32_utility.h +++ b/sdk/libPPUI/win32_utility.h @@ -9,6 +9,12 @@ unsigned QueryScreenDPI_Y(HWND wnd = NULL); SIZE QueryScreenDPIEx(HWND wnd = NULL); SIZE QueryContextDPI(HDC dc); +namespace PP { + // Returns drag threshold, in actual pixels (DPI-corrected), for the specified window + SIZE queryDragThreshold(HWND wndFor); + SIZE queryDragThresholdForDPI(SIZE knownDPI); +} + void HeaderControl_SetSortIndicator(HWND header, int column, bool isUp); HINSTANCE GetThisModuleHandle(); @@ -46,7 +52,7 @@ void SetDefaultMenuItem(HMENU p_menu, unsigned p_id); void GetOSVersionString(pfc::string_base & out); WORD GetOSVersionCode(); bool IsWine(); -DWORD Win10BuildNumber(); +DWORD Win10BuildNumber(); // See https://en.wikipedia.org/wiki/Windows_10_version_history for build number reference void EnumChildWindows(HWND, std::function); // Recursive void EnumChildWindowsHere(HWND, std::function); // Non-recursive \ No newline at end of file diff --git a/sdk/libPPUI/windowLifetime.h b/sdk/libPPUI/windowLifetime.h index 6a7e9bc..a49be03 100644 --- a/sdk/libPPUI/windowLifetime.h +++ b/sdk/libPPUI/windowLifetime.h @@ -1,5 +1,6 @@ #pragma once #include "ImplementOnFinalMessage.h" +#include "win32_op.h" #include namespace PP { @@ -15,8 +16,29 @@ namespace PP { //! OnFinalMessage() is automatically overridden to delete the window subclass object. template obj_t* subclassThisWindow(HWND wnd, arg_t && ... arg) { + PFC_ASSERT( wnd != NULL ); auto ret = newWindowObj(std::forward(arg) ...); WIN32_OP_D(ret->SubclassWindow(wnd)); return ret; } + + //! Creates a new window object, of ctrl_t typem with automatic lifetime management, + //! and replaces an existing control in a dialog. + template + ctrl_t * replaceDialogCtrl(CWindow wndDialog, UINT replaceControlID) { + CWindow wndReplace = wndDialog.GetDlgItem(replaceControlID); + ATLASSERT(wndReplace != NULL); + CRect rc; + CWindow wndPrev = wndDialog.GetNextDlgTabItem(wndReplace, TRUE); + WIN32_OP_D(wndReplace.GetWindowRect(&rc)); + WIN32_OP_D(wndDialog.ScreenToClient(rc)); + CString text; + wndReplace.GetWindowText(text); + WIN32_OP_D(wndReplace.DestroyWindow()); + auto ctrl = newWindowObj(); + WIN32_OP_D(ctrl->Create(wndDialog, &rc, text, 0, 0, replaceControlID)); + if (wndPrev != NULL) ctrl->SetWindowPos(wndPrev, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); + ctrl->SetFont(wndDialog.GetFont()); + return ctrl; + } } \ No newline at end of file diff --git a/sdk/libPPUI/wtl-pp.cpp b/sdk/libPPUI/wtl-pp.cpp new file mode 100644 index 0000000..1b0baf2 --- /dev/null +++ b/sdk/libPPUI/wtl-pp.cpp @@ -0,0 +1,47 @@ +#include "stdafx.h" +#include "wtl-pp.h" + +void CEditPPHooks::DeleteLastWord( CEdit wnd, bool bForward ) { + if ( wnd.GetWindowLong(GWL_STYLE) & ES_READONLY ) return; + CString buffer; + if ( wnd.GetWindowText(buffer) <= 0 ) return; + const int len = buffer.GetLength(); + int selStart = len, selEnd = len; + wnd.GetSel(selStart, selEnd); + if ( selStart < 0 || selStart > len ) selStart = len; // sanity + if ( selEnd < selStart ) selEnd = selStart; // sanity + int work = selStart; + if ( work == selEnd ) { + // Only do our stuff if there is nothing yet selected. Otherwise first delete selection. + if (bForward) { + // go forward (ctrl+del) + if (work < len && isSpecial(buffer[work])) { // linebreaks etc? + do ++ work; while( work < len && isSpecial(buffer[work])); + } else { + // delete apparent spacing + while ( work < len && isWordDelimiter(buffer[work])) ++ work; + // delete apparent word + while ( work < len && (!isWordDelimiter(buffer[work]) && !isSpecial(buffer[work]))) ++ work; + } + + if ( selEnd < work ) { + wnd.SetSel(selEnd, work, TRUE ); + wnd.ReplaceSel( TEXT(""), TRUE ); + } + } else { + // go backward (ctrl+backspace) + if ( work > 0 && isSpecial(buffer[work-1])) { // linebreaks etc? + do --work; while( work > 0 && isSpecial(buffer[work-1])); + } else { + // delete apparent spacing + while( work > 0 && isWordDelimiter(buffer[work-1]) ) --work; + // delete apparent word + while( work > 0 && (!isWordDelimiter(buffer[work-1]) && !isSpecial(buffer[work-1]))) --work; + } + if ( selEnd > work ) { + wnd.SetSel(work, selEnd, TRUE ); + wnd.ReplaceSel( TEXT(""), TRUE ); + } + } + } +} diff --git a/sdk/libPPUI/wtl-pp.h b/sdk/libPPUI/wtl-pp.h index 16c577a..6d4fdbc 100644 --- a/sdk/libPPUI/wtl-pp.h +++ b/sdk/libPPUI/wtl-pp.h @@ -4,7 +4,7 @@ #include #include -#define ATLASSERT_SUCCESS(X) {auto RetVal = (X); ATLASSERT( RetVal ); } +#define ATLASSERT_SUCCESS(X) {auto RetVal = (X); ATLASSERT( RetVal ); (void) RetVal; } #ifdef SubclassWindow // mitigate windowsx.h clash #undef SubclassWindow @@ -73,6 +73,9 @@ LRESULT RelayEraseBkgnd(HWND p_from, HWND p_to, HDC p_dc); void InjectParentEraseHandler(HWND); void InjectEraseHandler(HWND, HWND sendTo); void InjectParentCtlColorHandler(HWND); +void BounceNextDlgCtl(HWND wnd, HWND wndTo); + + #define MSG_WM_ERASEBKGND_PARENT() \ if (uMsg == WM_ERASEBKGND) { \ @@ -127,14 +130,14 @@ void InjectParentCtlColorHandler(HWND); } - +// Obsolete, use CImageListManaged instead class CImageListContainer : public CImageList { public: CImageListContainer() {} ~CImageListContainer() {Destroy();} -private: - const CImageListContainer & operator=(const CImageListContainer&); - CImageListContainer(const CImageListContainer&); + + void operator=(const CImageListContainer&) = delete; + CImageListContainer(const CImageListContainer&) = delete; }; @@ -174,7 +177,7 @@ class CCheckBox : public CButton { class CEditPPHooks : public CWindowImpl { public: - bool HandleCtrlA = true, NoEscSteal = false, NoEnterSteal = false; + bool HandleCtrlA = true, NoEscSteal = false, NoEnterSteal = false, WantAllKeys = false; std::function onEnterKey; std::function onEscKey; @@ -194,35 +197,13 @@ class CEditPPHooks : public CWindowImpl { END_MSG_MAP() - static void DeleteLastWord( CEdit wnd ) { - if ( wnd.GetWindowLong(GWL_STYLE) & ES_READONLY ) return; - int len = wnd.GetWindowTextLength(); - if ( len <= 0 ) return; - TCHAR * buffer = new TCHAR [ len + 1 ]; - if ( wnd.GetWindowText( buffer, len + 1 ) <= 0 ) { - delete[] buffer; - return; - } - buffer[len] = 0; - int selStart = len, selEnd = len; - wnd.GetSel(selStart, selEnd); - if ( selStart < 0 || selStart > len ) selStart = len; // sanity - if ( selEnd < selStart ) selEnd = selStart; // sanity - int work = selStart; - if ( work == selEnd ) { - // Only do our stuff if there is nothing yet selected. Otherwise first delete selection. - while( work > 0 && isWordDelimiter(buffer[work-1]) ) --work; - while( work > 0 && !isWordDelimiter(buffer[work-1] ) ) --work; - } - delete[] buffer; - if ( selEnd > work ) { - wnd.SetSel(work, selEnd, TRUE ); - wnd.ReplaceSel( TEXT(""), TRUE ); - } - } + static void DeleteLastWord( CEdit wnd, bool bForward = false ); private: - static bool isWordDelimiter( TCHAR c ) { - return (unsigned) c <= ' ' || c == ',' || c == '.' || c == ';' || c == ':'; + static bool isSpecial( wchar_t c ) { + return (unsigned) c < ' '; + } + static bool isWordDelimiter( wchar_t c ) { + return c == ' ' || c == ',' || c == '.' || c == ';' || c == ':'; } void OnChar(UINT nChar, UINT, UINT nFlags) { if (m_suppressChar != 0) { @@ -251,6 +232,12 @@ class CEditPPHooks : public CWindowImpl { DeleteLastWord( *this ) ; return; } } + if ( nChar == VK_DELETE ) { + if (GetHotkeyModifierFlags() == MOD_CONTROL) { + m_suppressScanCode = nFlags & 0xFF; + DeleteLastWord( *this, true ) ; return; + } + } if ( nChar == VK_RETURN && onEnterKey ) { m_suppressChar = nChar; onEnterKey(); return; @@ -263,6 +250,7 @@ class CEditPPHooks : public CWindowImpl { SetMsgHandled(FALSE); } UINT OnEditGetDlgCode(LPMSG lpMsg) { + if (WantAllKeys) return DLGC_WANTALLKEYS; if (lpMsg == NULL) { SetMsgHandled(FALSE); return 0; } else { @@ -430,23 +418,9 @@ class CWindowRegisteredT : public TBaseClass, public CMessageMap { class CSRWlock { public: - CSRWlock() : theLock() { -#if _WIN32_WINNT < 0x600 - auto dll = GetModuleHandle(_T("kernel32")); - Bind(AcquireSRWLockExclusive, dll, "AcquireSRWLockExclusive"); - Bind(AcquireSRWLockShared, dll, "AcquireSRWLockShared"); - Bind(ReleaseSRWLockExclusive, dll, "ReleaseSRWLockExclusive"); - Bind(ReleaseSRWLockShared, dll, "ReleaseSRWLockShared"); -#endif - } + CSRWlock() { } - bool HaveAPI() { -#if _WIN32_WINNT < 0x600 - return AcquireSRWLockExclusive != NULL; -#else - return true; -#endif - } + static bool HaveAPI() { return true; } void EnterShared() { AcquireSRWLockShared( & theLock ); @@ -465,51 +439,5 @@ class CSRWlock { CSRWlock(const CSRWlock&) = delete; void operator=(const CSRWlock&) = delete; - SRWLOCK theLock; -#if _WIN32_WINNT < 0x600 - template static void Bind(func_t & func, HMODULE dll, const char * name) { - func = reinterpret_cast(GetProcAddress( dll, name ) ); - } - - VOID (WINAPI * AcquireSRWLockExclusive)(PSRWLOCK SRWLock); - VOID (WINAPI * AcquireSRWLockShared)(PSRWLOCK SRWLock); - VOID (WINAPI * ReleaseSRWLockExclusive)(PSRWLOCK SRWLock); - VOID (WINAPI * ReleaseSRWLockShared)(PSRWLOCK SRWLock); -#endif -}; - -#if _WIN32_WINNT < 0x600 -class CSRWorCS { -public: - CSRWorCS() : cs() { - if (!srw.HaveAPI()) InitializeCriticalSection(&cs); - } - ~CSRWorCS() { - if (!srw.HaveAPI()) DeleteCriticalSection(& cs ); - } - void EnterShared() { - if (srw.HaveAPI()) srw.EnterShared(); - else EnterCriticalSection(&cs); - } - void EnterExclusive() { - if (srw.HaveAPI()) srw.EnterExclusive(); - else EnterCriticalSection(&cs); - } - void LeaveShared() { - if (srw.HaveAPI()) srw.LeaveShared(); - else LeaveCriticalSection(&cs); - } - void LeaveExclusive() { - if (srw.HaveAPI()) srw.LeaveExclusive(); - else LeaveCriticalSection(&cs); - } -private: - CSRWorCS(const CSRWorCS&) = delete; - void operator=(const CSRWorCS&) = delete; - - CSRWlock srw; - CRITICAL_SECTION cs; + SRWLOCK theLock = {}; }; -#else -typedef CSRWlock CSRWorCS; -#endif diff --git a/sdk/pfc/CFObject.h b/sdk/pfc/CFObject.h new file mode 100644 index 0000000..7e30c53 --- /dev/null +++ b/sdk/pfc/CFObject.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +namespace pfc { + template + class CFObject { + public: + typedef CFObject self_t; + type_t p = NULL; + + ~CFObject() { + if ( p ) CFRelease(p); + } + + void Retain(type_t arg) { + if ( p ) CFRelease(p); + p = arg; + if ( p ) CFRetain(p); + } + + void Attach(type_t arg) { + if ( p ) CFRelease(p); + p = arg; + } + + void operator=( self_t const & arg ) { + if ( p ) CFRelease(p); + p = arg.p; + if ( p ) CFRetain(p); + } + CFObject() {} + CFObject( self_t const & arg ) { + p = arg.p; + if ( p ) CFRetain(p); + } + + CFObject(self_t && arg ) { + p = arg.p; arg.p = NULL; + } + void operator=(self_t && arg) { + if ( p ) CFRelease(p); + p = arg.p; arg.p = NULL; + } + + operator bool() const { return p != NULL; } + operator type_t() const { return p;} + + + void reset() { + if ( p ) CFRelease(p); + p = NULL; + } + + void operator=(nullptr_t) { + reset(); + } + }; +} diff --git a/sdk/pfc/SmartStrStr-table.h b/sdk/pfc/SmartStrStr-table.h index 971af88..446da28 100644 --- a/sdk/pfc/SmartStrStr-table.h +++ b/sdk/pfc/SmartStrStr-table.h @@ -275,6 +275,7 @@ static constexpr mapping_t SmartStrStrTable[] = { {8216,39}, {8217,39}, {8218,44}, + {8219,39}, {8220,34}, {8221,34}, {8222,34}, diff --git a/sdk/pfc/SmartStrStr-twoCharMappings.h b/sdk/pfc/SmartStrStr-twoCharMappings.h index 042d43b..a950636 100644 --- a/sdk/pfc/SmartStrStr-twoCharMappings.h +++ b/sdk/pfc/SmartStrStr-twoCharMappings.h @@ -5,21 +5,38 @@ static constexpr struct { const char* to; } twoCharMappings[] = { {0x00C6, "AE"}, + {0x00E6, "ae"}, + {0x00DF, "ss"}, + +#if 0 + // umlauts + // the problem with these is that changing them to two-letter represenatations prevents search by non-umlaut vowel from working. + {0x00C4, "AE"}, + {0x00E4, "ae"}, + {0x00D6, "OE"}, + {0x00F6, "oe"}, + {0x00DC, "UE"}, + {0x00FC, "ue"}, +#endif + +#if 0 + // Incomplete list, hence disabled. Nobody uses these. {0x01E2, "AE"}, {0x01FC, "AE"}, - {0x00E6, "ae"}, {0x01E3, "ae"}, {0x01FD, "ae"}, {0x0152, "OE"}, {0x0153, "oe"}, {0x0276, "oe"}, + {0x01C3, "dz"}, {0x01C4, "DZ"}, - {0x01F1, "DZ"}, + {0x01C5, "Dz"}, {0x01C6, "dz"}, + {0x01F1, "DZ"}, + {0x01F2, "Dz"}, {0x01F3, "dz"}, {0x02A3, "dz"}, {0x02A5, "dz"}, - {0x00DF, "ss"}, {0x01C7, "LJ"}, {0x01C8, "Lj"}, {0x01C9, "lj"}, @@ -28,13 +45,5 @@ static constexpr struct { {0x01CC, "nj"}, {0x0132, "IJ"}, {0x0133, "ij"}, - -#if 0 // umlauts - {0x00C4, "AE"}, - {0x00E4, "ae"}, - {0x00D6, "OE"}, - {0x00F6, "oe"}, - {0x00DC, "UE"}, - {0x00FC, "ue"}, #endif }; diff --git a/sdk/pfc/SmartStrStr.cpp b/sdk/pfc/SmartStrStr.cpp index b5b0bbd..25437ca 100644 --- a/sdk/pfc/SmartStrStr.cpp +++ b/sdk/pfc/SmartStrStr.cpp @@ -1,4 +1,4 @@ -#include "pfc-lite.h" +#include "pfc-lite.h" #include "string-conv-lite.h" #include "string_conv.h" @@ -7,6 +7,50 @@ #include "SmartStrStr-table.h" #include "SmartStrStr-twoCharMappings.h" +bool SmartStrStr::isWordChar(unsigned c) { + // FIX ME map Unicode ranges somehow + return c >= 128 || pfc::char_is_ascii_alphanumeric((char)c); +} + +bool SmartStrStr::isWordChar(const char* ptr) { + unsigned c; + size_t d = pfc::utf8_decode_char(ptr, c); + if (d == 0) return false; // bad UTF-8 + return isWordChar(c); +} + +bool SmartStrStr::isValidWord(const char* ptr) { + if (*ptr == 0) return false; + do { + unsigned c; + size_t d = pfc::utf8_decode_char(ptr, c); + if (d == 0) return false; // bad UTF-8 + if (!isWordChar(c)) return false; + ptr += d; + } while (*ptr != 0); + return true; +} + +void SmartStrStr::findWords(const char* str, std::function cb) { + size_t base = 0, walk = 0; + for (;; ) { + unsigned c = 0; + size_t d = pfc::utf8_decode_char(str + walk, c); + if (d == 0) break; + + if (!SmartStrStr::isWordChar(c)) { + if (walk > base) { + cb(pfc::string_part(str + base, walk - base)); + } + base = walk + d; + } + walk += d; + } + if (walk > base) { + cb(pfc::string_part(str + base, walk - base)); + } +} + SmartStrStr::SmartStrStr() { std::map > substitutions, substitutionsReverse; std::map downconvert; @@ -50,132 +94,121 @@ SmartStrStr::SmartStrStr() { InitTwoCharMappings(); } -#ifdef _WIN32 -static_assert(sizeof(wchar_t) == sizeof(char16_t)); -const wchar_t * SmartStrStr::strStrEndW(const wchar_t * pString, const wchar_t * pSubString, size_t * outFoundAt) const { - return reinterpret_cast(strStrEnd16(reinterpret_cast(pString), reinterpret_cast(pSubString), outFoundAt)); +// == TEMPLATES == +template const char_t * SmartStrStr::matchHere_(const char_t * pString, const char_t * pUserString) const { + auto walkData = pString; + auto walkUser = pUserString; + for (;; ) { + if (*walkUser == 0) return walkData; + + uint32_t cData, cUser; + size_t dData = pfc::uni_decode_char(walkData, cData); + size_t dUser = pfc::uni_decode_char(walkUser, cUser); + if (dData == 0 || dUser == 0) return nullptr; + + if (cData != cUser) { + bool gotMulti = false; + { + const char * cDataSubst = m_twoCharMappings.query(cData); + if (cDataSubst != nullptr) { + PFC_ASSERT(strlen(cDataSubst) == 2); + if (matchOneChar(cUser, (uint32_t)cDataSubst[0])) { + auto walkUser2 = walkUser + dUser; + uint32_t cUser2; + auto dUser2 = pfc::uni_decode_char(walkUser2, cUser2); + if (matchOneChar(cUser2, (uint32_t)cDataSubst[1])) { + gotMulti = true; + dUser += dUser2; + } + } + } + } + if (!gotMulti) { + if (!matchOneChar(cUser, cData)) return nullptr; + } + } + + walkData += dData; + walkUser += dUser; + } } -const wchar_t * SmartStrStr::matchHereW(const wchar_t * pString, const wchar_t * pUserString) const { - return reinterpret_cast(matchHere16(reinterpret_cast(pString), reinterpret_cast(pUserString))); +template bool SmartStrStr::equals_( const char_t * pString, const char_t * pUserString) const { + auto p = this->matchHere_(pString, pUserString); + if ( p == nullptr ) return false; + return *p == 0; } -#endif -const char16_t * SmartStrStr::matchHere16(const char16_t * pString, const char16_t * pUserString) const { - auto walkData = pString; - auto walkUser = pUserString; - for (;; ) { - if (*walkUser == 0) return walkData; - - uint32_t cData, cUser; - size_t dData = pfc::utf16_decode_char(walkData, &cData); - size_t dUser = pfc::utf16_decode_char(walkUser, &cUser); - if (dData == 0 || dUser == 0) return nullptr; - - if (cData != cUser) { - bool gotMulti = false; - { - const char * cDataSubst = m_twoCharMappings.query(cData); - if (cDataSubst != nullptr) { - PFC_ASSERT(strlen(cDataSubst) == 2); - if (matchOneChar(cUser, (uint32_t)cDataSubst[0])) { - auto walkUser2 = walkUser + dUser; - uint32_t cUser2; - auto dUser2 = pfc::utf16_decode_char(walkUser2, &cUser2); - if (matchOneChar(cUser2, (uint32_t)cDataSubst[1])) { - gotMulti = true; - dUser += dUser2; - } - } - } - } - if (!gotMulti) { - if (!matchOneChar(cUser, cData)) return nullptr; - } - } - walkData += dData; - walkUser += dUser; - } +template const char_t * SmartStrStr::strStrEnd_(const char_t * pString, const char_t * pSubString, size_t * outFoundAt) const { + size_t walk = 0; + for (;; ) { + if (pString[walk] == 0) return nullptr; + auto end = matchHere_(pString + walk, pSubString); + if (end != nullptr) { + if (outFoundAt != nullptr) * outFoundAt = walk; + return end; + } + + size_t delta = pfc::uni_char_length(pString + walk); + if (delta == 0) return nullptr; + walk += delta; + } +} +// == END TEMPLATES == + +const char16_t * SmartStrStr::matchHere16(const char16_t * pString, const char16_t * pUserString) const { + return this->matchHere_(pString, pUserString); +} +const char * SmartStrStr::matchHere(const char * pString, const char * pUserString) const { + return this->matchHere_(pString, pUserString); +} +const wchar_t * SmartStrStr::matchHereW(const wchar_t * pString, const wchar_t * pUserString) const { + return this->matchHere_(pString, pUserString); } bool SmartStrStr::equals(const char * pString, const char * pUserString) const { - auto p = matchHere(pString, pUserString); - if ( p == nullptr ) return false; - return *p == 0; + return equals_(pString, pUserString); } bool SmartStrStr::equals16(const char16_t* pString, const char16_t* pUserString) const { - auto p = matchHere16(pString, pUserString); - if ( p == nullptr ) return false; - return *p == 0; + return equals_(pString, pUserString); +} +bool SmartStrStr::equalsW( const wchar_t * pString, const wchar_t * pUserString) const { + return equals_(pString, pUserString); +} +const char * SmartStrStr::strStrEnd(const char * pString, const char * pSubString, size_t * outFoundAt) const { + return strStrEnd_(pString, pSubString, outFoundAt); } -const char * SmartStrStr::matchHere(const char * pString, const char * pUserString) const { - const char * walkData = pString; - const char * walkUser = pUserString; - for (;; ) { - if (*walkUser == 0) return walkData; - - uint32_t cData, cUser; - size_t dData = pfc::utf8_decode_char(walkData, cData); - size_t dUser = pfc::utf8_decode_char(walkUser, cUser); - if (dData == 0 || dUser == 0) return nullptr; - - if (cData != cUser) { - bool gotMulti = false; - { - const char* cDataSubst = m_twoCharMappings.query(cData); - if (cDataSubst != nullptr) { - PFC_ASSERT(strlen(cDataSubst) == 2); - if (matchOneChar(cUser, (uint32_t)cDataSubst[0])) { - auto walkUser2 = walkUser + dUser; - uint32_t cUser2; - auto dUser2 = pfc::utf8_decode_char(walkUser2, cUser2); - if (matchOneChar(cUser2, (uint32_t)cDataSubst[1])) { - gotMulti = true; - dUser += dUser2; - } - } - } - } - if (!gotMulti) { - if (!matchOneChar(cUser, cData)) return nullptr; - } - } +const char16_t * SmartStrStr::strStrEnd16(const char16_t * pString, const char16_t * pSubString, size_t * outFoundAt) const { + return strStrEnd_(pString, pSubString, outFoundAt); +} - walkData += dData; - walkUser += dUser; - } +const wchar_t * SmartStrStr::strStrEndW(const wchar_t * pString, const wchar_t * pSubString, size_t * outFoundAt) const { + return strStrEnd_(pString, pSubString, outFoundAt); } -const char * SmartStrStr::strStrEnd(const char * pString, const char * pSubString, size_t * outFoundAt) const { - size_t walk = 0; - for (;; ) { - if (pString[walk] == 0) return nullptr; - auto end = matchHere(pString+walk, pSubString); - if (end != nullptr) { - if ( outFoundAt != nullptr ) * outFoundAt = walk; - return end; +static bool wordBeginsHere(const char* base, size_t offset) { + if (offset == 0) return true; + for (size_t len = 1; len <= offset && len <= 6; --len) { + unsigned c; + if (pfc::utf8_decode_char(base + offset - len, c) == len) { + return !SmartStrStr::isWordChar(c); } - - size_t delta = pfc::utf8_char_len( pString + walk ); - if ( delta == 0 ) return nullptr; - walk += delta; } + return false; } -const char16_t * SmartStrStr::strStrEnd16(const char16_t * pString, const char16_t * pSubString, size_t * outFoundAt) const { +const char* SmartStrStr::strStrEndWord(const char* pString, const char* pSubString, size_t* outFoundAt) const { size_t walk = 0; - for (;; ) { - if (pString[walk] == 0) return nullptr; - auto end = matchHere16(pString + walk, pSubString); - if (end != nullptr) { - if (outFoundAt != nullptr) * outFoundAt = walk; + for (;;) { + size_t foundAt = 0; + auto end = strStrEnd(pString + walk, pSubString, &foundAt); + if (end == nullptr) return nullptr; + foundAt += walk; + if (!isWordChar(end) && wordBeginsHere(pString, foundAt)) { + if (outFoundAt) *outFoundAt = foundAt; return end; } - - uint32_t dontcare; - size_t delta = pfc::utf16_decode_char(pString + walk, & dontcare); - if (delta == 0) return nullptr; - walk += delta; + walk = end - pString; } } @@ -191,13 +224,17 @@ pfc::string8 SmartStrStr::transformStr(const char* str) const { } void SmartStrStr::transformStrHere(pfc::string8& out, const char* in) const { - out.prealloc(strlen(in)); + transformStrHere(out, in, strlen(in)); +} + +void SmartStrStr::transformStrHere(pfc::string8& out, const char* in, size_t inLen) const { + out.prealloc(inLen); out.clear(); - for (;; ) { + for (size_t walk = 0; walk < inLen; ) { unsigned c; - size_t d = pfc::utf8_decode_char(in, c); - if (d == 0) break; - in += d; + size_t d = pfc::utf8_decode_char(in + walk, c); + if (d == 0 || walk+d>inLen) break; + walk += d; const char* alt = m_twoCharMappings.query(c); if (alt != nullptr) { out << alt; continue; @@ -293,15 +330,16 @@ bool SmartStrStr::testSubString_prefix_subst(const char* str, const char* sub, u } bool SmartStrStr::testSubstring(const char* str, const char* sub) const { #if 1 + // optimized version for UTF-8 unsigned prefix; - const size_t skip = pfc::utf8_decode_char(sub, prefix); + const size_t skip = pfc::uni_decode_char(sub, prefix); if ( skip == 0 ) return false; sub += skip; if (testSubString_prefix_subst(str, sub, prefix)) return true; unsigned prefix2; - const size_t skip2 = pfc::utf8_decode_char(sub, prefix2); + const size_t skip2 = pfc::uni_decode_char(sub, prefix2); if (skip2 > 0 && prefix < 0x10000 && prefix2 < 0x10000) { sub += skip2; auto alt = m_twoCharMappingsReverse.query(prefix | (prefix2 << 16)); @@ -318,6 +356,9 @@ bool SmartStrStr::testSubstring(const char* str, const char* sub) const { bool SmartStrStr::testSubstring16(const char16_t* str, const char16_t* sub) const { return this->strStrEnd16(str, sub) != nullptr; } +bool SmartStrStr::testSubstringW( const wchar_t * str, const wchar_t * sub ) const { + return this->strStrEndW(str, sub) != nullptr; +} SmartStrStr& SmartStrStr::global() { static SmartStrStr g; @@ -355,28 +396,46 @@ void SmartStrFilter::init(const char* ptr, size_t len) { bool SmartStrFilter::test_disregardCounts(const char* src) const { - if (m_items.size() == 0) return false; + if (m_items.empty()) return false; - auto& dc = SmartStrStr::global(); + for (auto& walk : m_items) { + if (!dc->strStrEnd(src, walk.first.c_str())) return false; + } + return true; +} + +bool SmartStrFilter::testWords(const char* src) const { + if (m_items.empty()) return false; for (auto& walk : m_items) { - if (!dc.strStrEnd(src, walk.first.c_str())) return false; + const auto count = walk.second; + const auto& str = walk.first; + const auto* strWalk = src; + for (size_t i = 0; i < count; ++i) { + auto next = dc->strStrEndWord(strWalk, str.c_str()); + if (next == nullptr) return false; + strWalk = next; + } } return true; } bool SmartStrFilter::test(const char* src) const { - if (m_items.size() == 0) return false; - - auto& dc = SmartStrStr::global(); + if (m_items.empty()) return false; + // Use the faster routine first, it can't be used to count occurances but nobody really knows about this feature + for (auto& walk : m_items) { + if (!dc->testSubstring(src, walk.first.c_str())) return false; + } + // Have any items where specific number of occurances is wanted? for (auto & walk : m_items) { - const t_size count = walk.second; - const std::string& str = walk.first; - const char* strWalk = src; - for (t_size walk = 0; walk < count; ++walk) { - const char* next = dc.strStrEnd(strWalk, str.c_str()); + const auto count = walk.second; + if (count == 1) continue; + const auto& str = walk.first; + const auto* strWalk = src; + for (size_t i = 0; i < count; ++i) { + auto next = dc->strStrEnd(strWalk, str.c_str()); if (next == nullptr) return false; strWalk = next; } diff --git a/sdk/pfc/SmartStrStr.h b/sdk/pfc/SmartStrStr.h index 6a1fc19..0cdc131 100644 --- a/sdk/pfc/SmartStrStr.h +++ b/sdk/pfc/SmartStrStr.h @@ -9,8 +9,6 @@ //! Implementation of string matching for search purposes, such as media library search or typefind in list views. \n //! Inspired by Unicode asymetic search, but not strictly implementing the Unicode asymetric search specifications. \n -//! Bootstraps its character mapping data from various Win32 API methods, requires no externally provided character mapping data. \n -//! Windows-only code. \n //! \n //! Keeping a global instance of it is recommended, due to one time init overhead. \n //! Thread safety: safe to call concurrently once constructed. @@ -19,25 +17,31 @@ class SmartStrStr { public: SmartStrStr(); + static bool isWordChar(unsigned c); + static bool isWordChar(const char* ptr); + static bool isValidWord(const char*); + static void findWords(const char*, std::function); + //! Returns ptr to the end of the string if positive (for continuing search), nullptr if negative. const char * strStrEnd(const char * pString, const char * pSubString, size_t * outFoundAt = nullptr) const; const char16_t * strStrEnd16(const char16_t * pString, const char16_t * pSubString, size_t * outFoundAt = nullptr) const; + const wchar_t * strStrEndW(const wchar_t * pString, const wchar_t * pSubString, size_t * outFoundAt = nullptr) const; + + const char* strStrEndWord(const char* pString, const char* pSubString, size_t* outFoundAt = nullptr) const; bool testSubstring( const char * str, const char * sub ) const; bool testSubstring16( const char16_t * str, const char16_t * sub ) const; -#ifdef _WIN32 - const wchar_t * strStrEndW(const wchar_t * pString, const wchar_t * pSubString, size_t * outFoundAt = nullptr) const; -#endif - //! Returns ptr to the end of the string if positive (for continuing search), nullptr if negative. + bool testSubstringW( const wchar_t * str, const wchar_t * sub ) const; + + //! Returns ptr to the end of the string if positive (for continuing search), nullptr if negative. const char * matchHere(const char * pString, const char * pUserString) const; const char16_t * matchHere16(const char16_t * pString, const char16_t * pUserString) const; -#ifdef _WIN32 const wchar_t * matchHereW( const wchar_t * pString, const wchar_t * pUserString) const; -#endif //! String-equals tool, compares strings rather than searching for occurance bool equals( const char * pString, const char * pUserString) const; bool equals16( const char16_t * pString, const char16_t * pUserString) const; + bool equalsW( const wchar_t * pString, const wchar_t * pUserString) const; //! One-char match. Doesn't use twoCharMappings, use only if you have to operate on char by char basis rather than call the other methods. bool matchOneChar(uint32_t cInput, uint32_t cData) const; @@ -46,7 +50,12 @@ class SmartStrStr { pfc::string8 transformStr(const char * str) const; void transformStrHere(pfc::string8& out, const char* in) const; + void transformStrHere(pfc::string8& out, const char* in, size_t inLen) const; private: + template const char_t * strStrEnd_(const char_t * pString, const char_t * pSubString, size_t * outFoundAt = nullptr) const; + template const char_t * matchHere_(const char_t * pString, const char_t * pUserString) const; + template bool equals_( const char_t * pString, const char_t * pUserString) const; + bool testSubString_prefix(const char* str, const char* sub, const char * prefix, size_t prefixLen) const; bool testSubString_prefix(const char* str, const char* sub, uint32_t c) const; bool testSubString_prefix_subst(const char* str, const char* sub, uint32_t c) const; @@ -67,17 +76,28 @@ class SmartStrStr { class SmartStrFilter { - typedef std::map t_stringlist; - t_stringlist m_items; - public: + typedef std::map t_stringlist; SmartStrFilter() { } + SmartStrFilter(t_stringlist const& arg) : m_items(arg) {} + SmartStrFilter(t_stringlist&& arg) : m_items(std::move(arg)) {} SmartStrFilter(const char* p) { init(p, strlen(p)); } SmartStrFilter(const char* p, size_t l) { init(p, l); } static bool is_spacing(char c) { return c == ' ' || c == 10 || c == 13 || c == '\t'; } void init(const char* ptr, size_t len); + void init( const char * ptr ) { init(ptr, strlen(ptr)); } bool test(const char* src) const; + bool testWords(const char* src) const; bool test_disregardCounts(const char* src) const; + + const t_stringlist& items() const { return m_items; } + operator bool() const { return !m_items.empty(); } + bool empty() const { return m_items.empty(); } + + SmartStrStr & _SmartStrStr() const { return *dc; } +private: + t_stringlist m_items; + SmartStrStr * dc = &SmartStrStr::global(); }; diff --git a/sdk/pfc/alloc.h b/sdk/pfc/alloc.h index 53ba626..6881f41 100644 --- a/sdk/pfc/alloc.h +++ b/sdk/pfc/alloc.h @@ -125,7 +125,7 @@ namespace pfc { ~alloc_simple() {delete[] m_data;} - void move_from(t_self & other) { + void move_from(t_self & other) noexcept { delete[] m_data; m_data = replace_null_t(other.m_data); m_size = replace_null_t(other.m_size); @@ -182,7 +182,7 @@ namespace pfc { void resize_content(t_size p_size) { - if (traits_t::needs_constructor || traits_t::needs_destructor) { + if constexpr (traits_t::needs_constructor || traits_t::needs_destructor) { if (p_size > m_size) {//expand do { __unsafe__in_place_constructor_t(m_buffer[m_size]); @@ -201,7 +201,7 @@ namespace pfc { PFC_ASSERT( m_size <= m_size_total ); PFC_ASSERT( m_size <= p_size ); if (m_size_total != p_size) { - if (pfc::traits_t::realloc_safe) { + if constexpr (pfc::traits_t::realloc_safe) { m_buffer = pfc::__raw_realloc_t(m_buffer,p_size); m_size_total = p_size; } else if (__raw_realloc_inplace_t(m_buffer,p_size)) { @@ -267,7 +267,7 @@ namespace pfc { void resize_content(t_size p_size) { - if (traits_t::needs_constructor || traits_t::needs_destructor) { + if constexpr (traits_t::needs_constructor || traits_t::needs_destructor) { if (p_size > m_size) {//expand do { __unsafe__in_place_constructor_t(m_buffer[m_size]); @@ -284,7 +284,7 @@ namespace pfc { void resize_storage(t_size p_size) { PFC_ASSERT( m_size <= p_size ); - if (pfc::traits_t::realloc_safe) { + if constexpr (pfc::traits_t::realloc_safe) { m_buffer = pfc::__raw_realloc_t(m_buffer,p_size); //m_size_total = p_size; } else if (__raw_realloc_inplace_t(m_buffer,p_size)) { @@ -437,7 +437,7 @@ namespace pfc { } ~alloc() { - if (pfc::traits_t::needs_destructor) set_size(0); + if constexpr (pfc::traits_t::needs_destructor) set_size(0); } t_size get_size() const {return m_size;} diff --git a/sdk/pfc/array.h b/sdk/pfc/array.h index cc00c74..cbbae8c 100644 --- a/sdk/pfc/array.h +++ b/sdk/pfc/array.h @@ -159,7 +159,7 @@ namespace pfc { try { set_size(walk); return; - } catch(std::bad_alloc) { + } catch(std::bad_alloc const &) { if (walk <= minSize) throw; // go on } diff --git a/sdk/pfc/audio_math.cpp b/sdk/pfc/audio_math.cpp index 1a731d2..7ed88ce 100644 --- a/sdk/pfc/audio_math.cpp +++ b/sdk/pfc/audio_math.cpp @@ -1,11 +1,14 @@ #include "pfc-lite.h" #include "audio_sample.h" #include "primitives.h" +#include "cpuid.h" #if (defined(_M_IX86_FP) && _M_IX86_FP >= 2) || (defined(_M_X64) && !defined(_M_ARM64EC)) || defined(__x86_64__) || defined(__SSE2__) #define AUDIO_MATH_SSE #include +#include // _mm_shuffle_epi8 +#include // _mm_blend_epi16 #ifndef _mm_loadu_si32 #define _mm_loadu_si32(p) _mm_cvtsi32_si128(*(unsigned int const*)(p)) @@ -15,22 +18,51 @@ #endif #ifdef __AVX__ -#define haveAVX true #define allowAVX 1 +#define haveAVX 1 #elif PFC_HAVE_CPUID -#include "cpuid.h" -static const bool haveAVX = pfc::query_cpu_feature_set(pfc::CPU_HAVE_AVX); #define allowAVX 1 +static const bool haveAVX = pfc::query_cpu_feature_set(pfc::CPU_HAVE_AVX); #else -#define haveAVX false #define allowAVX 0 +#define haveAVX 0 +#endif + +#ifdef __SSE4_1__ +#define haveSSE41 true +#elif PFC_HAVE_CPUID +static const bool haveSSE41 = pfc::query_cpu_feature_set(pfc::CPU_HAVE_SSE41); +#else +#define haveSSE41 false #endif +#if allowAVX +#include // _mm256_set1_pd +#endif + +#endif // end SSE + +#if defined( __aarch64__ ) || defined( _M_ARM64) || defined( _M_ARM64EC ) +#define AUDIO_MATH_ARM64 #endif -#if defined( __aarch64__ ) || defined( __ARM_NEON__ ) || defined( _M_ARM64) || defined( _M_ARM64EC ) +#if defined( AUDIO_MATH_ARM64 ) || defined( __ARM_NEON__ ) #define AUDIO_MATH_NEON #include + +// No vcvtnq_s32_f32 on ARM32, use vcvtq_s32_f32, close enough +#ifdef AUDIO_MATH_ARM64 +#define vcvtnq_s32_f32_wrap vcvtnq_s32_f32 +#else +#define vcvtnq_s32_f32_wrap vcvtq_s32_f32 +#endif + +#endif + + +#if defined( AUDIO_MATH_ARM64 ) && !defined( __ANDROID__ ) +// Don't do Neon float64 on Android, crashes clang from NDK 25 +#define AUDIO_MATH_NEON_FLOAT64 #endif template inline static float_t noopt_calculate_peak(const float_t *p_src, t_size p_num) @@ -95,9 +127,9 @@ inline static void noopt_convert(const in_t* in, out_t* out, size_t count) { for (size_t walk = 0; walk < count; ++walk) out[walk] = (out_t)in[walk]; } -#if defined(AUDIO_MATH_NEON) +#ifdef AUDIO_MATH_NEON -#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) +#ifdef AUDIO_MATH_ARM64 #define _vmaxvq_f32_wrap vmaxvq_f32 #else inline float _vmaxvq_f32_wrap( float32x4_t arg ) { @@ -152,8 +184,8 @@ inline static void neon_convert_to_int32(const float * __restrict p_source,t_siz float32x4_t f32lo = vld1q_f32( p_source ); float32x4_t f32hi = vld1q_f32( p_source + 4 ); - int32x4_t lo = vcvtq_s32_f32( vmulq_n_f32(f32lo, p_scale) ); - int32x4_t hi = vcvtq_s32_f32( vmulq_n_f32(f32hi, p_scale) ); + int32x4_t lo = vcvtnq_s32_f32_wrap( vmulq_n_f32(f32lo, p_scale) ); + int32x4_t hi = vcvtnq_s32_f32_wrap( vmulq_n_f32(f32hi, p_scale) ); vst1q_s32(p_output, lo); vst1q_s32(p_output+4, hi); @@ -195,8 +227,8 @@ inline static void neon_convert_to_int16(const float* __restrict p_source,t_size float32x4_t f32lo = vld1q_f32( p_source ); float32x4_t f32hi = vld1q_f32( p_source + 4); - int32x4_t lo = vcvtq_s32_f32( vmulq_n_f32(f32lo, p_scale) ); - int32x4_t hi = vcvtq_s32_f32( vmulq_n_f32(f32hi, p_scale) ); + int32x4_t lo = vcvtnq_s32_f32_wrap( vmulq_n_f32(f32lo, p_scale) ); + int32x4_t hi = vcvtnq_s32_f32_wrap( vmulq_n_f32(f32hi, p_scale) ); vst1q_s16(&p_output[0], vcombine_s16( vqmovn_s32( lo ), vqmovn_s32( hi ) ) ); @@ -229,7 +261,7 @@ inline static void neon_convert_from_int16(const t_int16 * __restrict p_source,t noopt_convert_from_int16( p_source, rem, p_output, p_scale ); } - +#ifdef AUDIO_MATH_NEON_FLOAT64 inline static void neon_convert_to_int16(const double* __restrict p_source, t_size p_count, int16_t* __restrict p_output, double p_scale) { size_t num = p_count / 4; @@ -241,8 +273,8 @@ inline static void neon_convert_to_int16(const double* __restrict p_source, t_si f64lo = vmulq_n_f64(f64lo, p_scale); f64hi = vmulq_n_f64(f64hi, p_scale); - int64x2_t lo64 = vcvtq_s64_f64(f64lo); - int64x2_t hi64 = vcvtq_s64_f64(f64hi); + int64x2_t lo64 = vcvtnq_s64_f64(f64lo); + int64x2_t hi64 = vcvtnq_s64_f64(f64hi); int32x4_t v32 = vcombine_s32(vqmovn_s64(lo64), vqmovn_s64(hi64)); @@ -255,8 +287,8 @@ inline static void neon_convert_to_int16(const double* __restrict p_source, t_si } noopt_convert_to_16bit(p_source, rem, p_output, p_scale); - } + inline static void neon_convert_from_int16(const t_int16* __restrict p_source, t_size p_count, double* __restrict p_output, double p_scale) { size_t num = p_count / 4; @@ -280,7 +312,9 @@ inline static void neon_convert_from_int16(const t_int16* __restrict p_source, t noopt_convert_from_int16(p_source, rem, p_output, p_scale); } -#endif +#endif // AUDIO_MATH_NEON_FLOAT64 + +#endif // AUDIO_MATH_NEON #if defined(AUDIO_MATH_SSE) @@ -354,7 +388,7 @@ inline void convert_to_32bit_sse2(const double* p_src, size_t numTotal, t_int32* auto i1 = _mm_cvtpd_epi32(v1), i2 = _mm_cvtpd_epi32(v2); - _mm_storeu_si128((__m128i*) p_dst, _mm_unpacklo_epi64(i1, i2)); p_dst += 4;; + _mm_storeu_si128((__m128i*) p_dst, _mm_unpacklo_epi64(i1, i2)); p_dst += 4; } for (; rem; --rem) { @@ -657,9 +691,6 @@ inline void convert_from_int16_avx(const t_int16* p_source, t_size p_count, doub { __m256d muld = _mm256_set1_pd(p_scale); - __m128i nulls = _mm_setzero_si128(); - __m128i delta1 = _mm_set1_epi16((int16_t)0x8000); - __m128i delta2 = _mm_set1_epi32((int32_t)0x8000); for (t_size loop = p_count >> 3; loop; --loop) { auto source = _mm_loadu_si128((__m128i*)p_source); @@ -776,7 +807,7 @@ namespace pfc { { convert_to_16bit_sse2(p_source, p_count, p_output, scale); } -#elif defined( AUDIO_MATH_NEON ) +#elif defined( AUDIO_MATH_NEON_FLOAT64 ) neon_convert_to_int16(p_source, p_count, p_output, scale); #else noopt_convert_to_16bit(p_source, p_count, p_output, scale); @@ -807,7 +838,7 @@ namespace pfc { { convert_from_int16_sse2(p_source, p_count, p_output, scale); } -#elif defined( AUDIO_MATH_NEON ) +#elif defined( AUDIO_MATH_NEON_FLOAT64 ) neon_convert_from_int16(p_source, p_count, p_output, scale); #else noopt_convert_from_int16(p_source, p_count, p_output, scale); @@ -933,4 +964,223 @@ namespace pfc { noopt_scale(in, count, out, scale); } + + typedef char store24_t; + static store24_t* store24(store24_t* out, int32_t in) { + *(out++) = ((store24_t*)&in)[0]; + *(out++) = ((store24_t*)&in)[1]; + *(out++) = ((store24_t*)&in)[2]; + return out; + } + static store24_t* store24p(store24_t* out, int32_t in) { + *(int32_t*)out = in; + return out + 3; + } + + static constexpr int32_t INT24_MAX = 0x7FFFFF, INT24_MIN = -0x800000; + + template void convert_to_int24_noopt(float_t const* in, size_t count, void* out, float_t scale) { + if (count == 0) return; + --count; + auto ptr = reinterpret_cast(out); + constexpr float_t lo = INT24_MIN, hi = INT24_MAX; + while (count) { + auto vf = *in++ * scale; + if (vf < lo) vf = lo; + else if (vf > hi) vf = hi; + ptr = store24p(ptr, audio_math::rint32(vf)); + --count; + } + + auto vf = *in * scale; + if (vf < lo) vf = lo; + else if (vf > hi) vf = hi; + store24(ptr, audio_math::rint32(vf)); + } +#ifdef AUDIO_MATH_SSE +#if allowAVX + static void f64_to_i24_avx(double const* in, size_t n, uint8_t* out, double scale) { + const __m128i pi0 = _mm_set_epi8(-128, -128, -128, -128, 14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0); + const __m128i pi1 = _mm_set_epi8(4, 2, 1, 0, -128, -128, -128, -128, 14, 13, 12, 10, 9, 8, 6, 5); + const __m128i pi2 = _mm_set_epi8(9, 8, 6, 5, 4, 2, 1, 0, -128, -128, -128, -128, 14, 13, 12, 10); + const __m128i pi3 = _mm_set_epi8(14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0, -128, -128, -128, -128); + const auto mul = _mm256_set1_pd(scale); + + // PROBLEM: if we want to handle wildly out-of-bounds values, we can't do int clipping! + // float clipping is sadly considerably slower than int clipping + const auto lo = _mm256_set1_pd(INT24_MIN); + const auto hi = _mm256_set1_pd(INT24_MAX); + + while (n >= 4 * 4) { + auto f0 = _mm256_mul_pd(_mm256_loadu_pd(in + 0), mul); + auto f1 = _mm256_mul_pd(_mm256_loadu_pd(in + 4), mul); + auto f2 = _mm256_mul_pd(_mm256_loadu_pd(in + 8), mul); + auto f3 = _mm256_mul_pd(_mm256_loadu_pd(in + 12), mul); + f0 = _mm256_max_pd(_mm256_min_pd(f0, hi), lo); + f1 = _mm256_max_pd(_mm256_min_pd(f1, hi), lo); + f2 = _mm256_max_pd(_mm256_min_pd(f2, hi), lo); + f3 = _mm256_max_pd(_mm256_min_pd(f3, hi), lo); + __m128i w0 = _mm256_cvtpd_epi32(f0); + __m128i w1 = _mm256_cvtpd_epi32(f1); + __m128i w2 = _mm256_cvtpd_epi32(f2); + __m128i w3 = _mm256_cvtpd_epi32(f3); + + // _mm_shuffle_epi8 : SSSE3 + w0 = _mm_shuffle_epi8(w0, pi0); + w1 = _mm_shuffle_epi8(w1, pi1); + w2 = _mm_shuffle_epi8(w2, pi2); + w3 = _mm_shuffle_epi8(w3, pi3); + + // _mm_blend_epi16 : SSE4.1 + __m128i u0 = _mm_blend_epi16(w0, w1, 0xC0); + __m128i u1 = _mm_blend_epi16(w1, w2, 0xF0); + __m128i u2 = _mm_blend_epi16(w2, w3, 0xFC); + + _mm_storeu_si128((__m128i*)(out + 0), u0); + _mm_storeu_si128((__m128i*)(out + 16), u1); + _mm_storeu_si128((__m128i*)(out + 32), u2); + + in += 4 * 4; + out += 16 * 3; + n -= 4 * 4; + } + + convert_to_int24_noopt(in, n, out, scale); + } +#endif // allowAVX + static void f64_to_i24_sse41(double const* in, size_t n, uint8_t* out, double scale) { + const __m128i pi0 = _mm_set_epi8(-128, -128, -128, -128, 14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0); + const __m128i pi1 = _mm_set_epi8(4, 2, 1, 0, -128, -128, -128, -128, 14, 13, 12, 10, 9, 8, 6, 5); + const __m128i pi2 = _mm_set_epi8(9, 8, 6, 5, 4, 2, 1, 0, -128, -128, -128, -128, 14, 13, 12, 10); + const __m128i pi3 = _mm_set_epi8(14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0, -128, -128, -128, -128); + const auto mul = _mm_set1_pd(scale); + + // PROBLEM: if we want to handle wildly out-of-bounds values, we can't do int clipping! + // float clipping is sadly considerably slower than int clipping + const auto lo = _mm_set1_pd(INT24_MIN); + const auto hi = _mm_set1_pd(INT24_MAX); + + while (n >= 4 * 4) { + auto f0 = _mm_mul_pd(_mm_loadu_pd(in + 0), mul); + auto f1 = _mm_mul_pd(_mm_loadu_pd(in + 2), mul); + auto f2 = _mm_mul_pd(_mm_loadu_pd(in + 4), mul); + auto f3 = _mm_mul_pd(_mm_loadu_pd(in + 6), mul); + auto f4 = _mm_mul_pd(_mm_loadu_pd(in + 8), mul); + auto f5 = _mm_mul_pd(_mm_loadu_pd(in + 10), mul); + auto f6 = _mm_mul_pd(_mm_loadu_pd(in + 12), mul); + auto f7 = _mm_mul_pd(_mm_loadu_pd(in + 14), mul); + f0 = _mm_max_pd(_mm_min_pd(f0, hi), lo); + f1 = _mm_max_pd(_mm_min_pd(f1, hi), lo); + f2 = _mm_max_pd(_mm_min_pd(f2, hi), lo); + f3 = _mm_max_pd(_mm_min_pd(f3, hi), lo); + f4 = _mm_max_pd(_mm_min_pd(f4, hi), lo); + f5 = _mm_max_pd(_mm_min_pd(f5, hi), lo); + f6 = _mm_max_pd(_mm_min_pd(f6, hi), lo); + f7 = _mm_max_pd(_mm_min_pd(f7, hi), lo); + + + + __m128i w0 = _mm_unpacklo_epi64(_mm_cvtpd_epi32(f0), _mm_cvtpd_epi32(f1)); + __m128i w1 = _mm_unpacklo_epi64(_mm_cvtpd_epi32(f2), _mm_cvtpd_epi32(f3)); + __m128i w2 = _mm_unpacklo_epi64(_mm_cvtpd_epi32(f4), _mm_cvtpd_epi32(f5)); + __m128i w3 = _mm_unpacklo_epi64(_mm_cvtpd_epi32(f6), _mm_cvtpd_epi32(f7)); + + // _mm_shuffle_epi8 : SSSE3 + w0 = _mm_shuffle_epi8(w0, pi0); + w1 = _mm_shuffle_epi8(w1, pi1); + w2 = _mm_shuffle_epi8(w2, pi2); + w3 = _mm_shuffle_epi8(w3, pi3); + + // _mm_blend_epi16 : SSE4.1 + __m128i u0 = _mm_blend_epi16(w0, w1, 0xC0); + __m128i u1 = _mm_blend_epi16(w1, w2, 0xF0); + __m128i u2 = _mm_blend_epi16(w2, w3, 0xFC); + + _mm_storeu_si128((__m128i*)(out + 0), u0); + _mm_storeu_si128((__m128i*)(out + 16), u1); + _mm_storeu_si128((__m128i*)(out + 32), u2); + + in += 4 * 4; + out += 16 * 3; + n -= 4 * 4; + } + + convert_to_int24_noopt(in, n, out, scale); + } + static void f32_to_i24_sse41(float const* in, size_t n, uint8_t* out, float scale) { + const __m128i pi0 = _mm_set_epi8(-128, -128, -128, -128, 14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0); + const __m128i pi1 = _mm_set_epi8(4, 2, 1, 0, -128, -128, -128, -128, 14, 13, 12, 10, 9, 8, 6, 5); + const __m128i pi2 = _mm_set_epi8(9, 8, 6, 5, 4, 2, 1, 0, -128, -128, -128, -128, 14, 13, 12, 10); + const __m128i pi3 = _mm_set_epi8(14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0, -128, -128, -128, -128); + const __m128 mul = _mm_set1_ps(scale); + + // PROBLEM: if we want to handle wildly out-of-bounds values, we can't do int clipping! + // float clipping is sadly considerably slower than int clipping + const auto lo = _mm_set1_ps(INT24_MIN); + const auto hi = _mm_set1_ps(INT24_MAX); + + while (n >= 4 * 4) { + auto f0 = _mm_mul_ps(_mm_loadu_ps(in + 0), mul); + auto f1 = _mm_mul_ps(_mm_loadu_ps(in + 4), mul); + auto f2 = _mm_mul_ps(_mm_loadu_ps(in + 8), mul); + auto f3 = _mm_mul_ps(_mm_loadu_ps(in + 12), mul); + f0 = _mm_min_ps(_mm_max_ps(f0, lo), hi); + f1 = _mm_min_ps(_mm_max_ps(f1, lo), hi); + f2 = _mm_min_ps(_mm_max_ps(f2, lo), hi); + f3 = _mm_min_ps(_mm_max_ps(f3, lo), hi); + __m128i w0 = _mm_cvtps_epi32(f0); + __m128i w1 = _mm_cvtps_epi32(f1); + __m128i w2 = _mm_cvtps_epi32(f2); + __m128i w3 = _mm_cvtps_epi32(f3); + + // _mm_shuffle_epi8 : SSSE3 + w0 = _mm_shuffle_epi8(w0, pi0); + w1 = _mm_shuffle_epi8(w1, pi1); + w2 = _mm_shuffle_epi8(w2, pi2); + w3 = _mm_shuffle_epi8(w3, pi3); + + // _mm_blend_epi16 : SSE4.1 + __m128i u0 = _mm_blend_epi16(w0, w1, 0xC0); + __m128i u1 = _mm_blend_epi16(w1, w2, 0xF0); + __m128i u2 = _mm_blend_epi16(w2, w3, 0xFC); + + _mm_storeu_si128((__m128i*)(out + 0), u0); + _mm_storeu_si128((__m128i*)(out + 16), u1); + _mm_storeu_si128((__m128i*)(out + 32), u2); + + in += 4 * 4; + out += 16 * 3; + n -= 4 * 4; + } + + convert_to_int24_noopt(in, n, out, scale); + } + +#endif // AUDIO_MATH_SSE + + void audio_math::convert_to_int24(const float* in, size_t count, void* out, float scale) { + scale *= 0x800000; + +#ifdef AUDIO_MATH_SSE + if (haveSSE41) { + f32_to_i24_sse41(in, count, (uint8_t*)out, scale); return; + } +#endif + convert_to_int24_noopt(in, count, out, scale); + } + void audio_math::convert_to_int24(const double* in, size_t count, void* out, double scale) { + scale *= 0x800000; +#ifdef AUDIO_MATH_SSE +#if allowAVX + if (haveAVX) { + f64_to_i24_avx(in, count, (uint8_t*)out, scale); return; + } +#endif // allowAVX + if (haveSSE41) { + f64_to_i24_sse41(in, count, (uint8_t*)out, scale); return; + } +#endif // AUDIO_MATH_SSE + convert_to_int24_noopt(in, count, out, scale); + } + } diff --git a/sdk/pfc/audio_sample.cpp b/sdk/pfc/audio_sample.cpp index 36172e1..198bf6a 100644 --- a/sdk/pfc/audio_sample.cpp +++ b/sdk/pfc/audio_sample.cpp @@ -4,26 +4,26 @@ #include "byte_order.h" namespace pfc { - float audio_math::decodeFloat24ptr(const void * sourcePtr) { + float audio_math::decodeFloat24ptr(const void* sourcePtr) { PFC_STATIC_ASSERT(pfc::byte_order_is_little_endian); union { uint8_t bytes[4]; float v; } u; - const uint8_t * s = reinterpret_cast(sourcePtr); + const uint8_t* s = reinterpret_cast(sourcePtr); u.bytes[0] = 0; u.bytes[1] = s[0]; u.bytes[2] = s[1]; u.bytes[3] = s[2]; return u.v; } - float audio_math::decodeFloat24ptrbs(const void * sourcePtr) { + float audio_math::decodeFloat24ptrbs(const void* sourcePtr) { PFC_STATIC_ASSERT(pfc::byte_order_is_little_endian); union { uint8_t bytes[4]; float v; } u; - const uint8_t * s = reinterpret_cast(sourcePtr); + const uint8_t* s = reinterpret_cast(sourcePtr); u.bytes[0] = 0; u.bytes[1] = s[2]; u.bytes[2] = s[1]; @@ -36,36 +36,36 @@ namespace pfc { const unsigned widthBits = 16; typedef uint16_t source_t; - /* typedef uint64_t out_t; typedef double retval_t; - enum { - outExponent = 11, - outFraction = 52, - outExponentShift = (1 << (outExponent-1))-1 - };*/ + /* typedef uint64_t out_t; typedef double retval_t; + enum { + outExponent = 11, + outFraction = 52, + outExponentShift = (1 << (outExponent-1))-1 + };*/ typedef uint32_t out_t; typedef float retval_t; - enum { - outExponent = 8, - outFraction = 23, - outExponentShift = (1 << (outExponent-1))-1 + enum { + outExponent = 8, + outFraction = 23, + outExponentShift = (1 << (outExponent - 1)) - 1 }; const unsigned exponentBits = widthBits - fractionBits - 1; // 1 bit sign | exponent | fraction - source_t fraction = source & (((source_t)1 << fractionBits)-1); + source_t fraction = source & (((source_t)1 << fractionBits) - 1); source >>= fractionBits; - int exponent = (int)( source & (((source_t)1 << exponentBits)-1) ) - (int)((1 << (exponentBits-1))-1); + int exponent = (int)(source & (((source_t)1 << exponentBits) - 1)) - (int)((1 << (exponentBits - 1)) - 1); source >>= exponentBits; - if (outExponent + outExponentShift <= 0) return 0; + if constexpr (outExponent + outExponentShift <= 0) return 0; - out_t output = (out_t)( source&1 ); + out_t output = (out_t)(source & 1); output <<= outExponent; - output |= (unsigned) (exponent + outExponentShift) & ( (1<> -shift); - else output |= (out_t) (fraction << shift); + int shift = (int)outFraction - (int)fractionBits; + if (shift < 0) output |= (out_t)(fraction >> -shift); + else output |= (out_t)(fraction << shift); return *(retval_t*)&output / pfc::audio_math::float16scale; } diff --git a/sdk/pfc/audio_sample.h b/sdk/pfc/audio_sample.h index 5f4a5ae..586b780 100644 --- a/sdk/pfc/audio_sample.h +++ b/sdk/pfc/audio_sample.h @@ -21,6 +21,8 @@ namespace pfc { static void convert_from_int16(const int16_t* p_source, size_t p_count, double * p_output, double p_scale); static void convert_from_int32(const int32_t* p_source, size_t p_count, float* p_output, float p_scale); static void convert_from_int32(const int32_t* p_source, size_t p_count, double* p_output, double p_scale); + static void convert_to_int24(const float* in, size_t count, void* out, float scale); + static void convert_to_int24(const double* in, size_t count, void* out, double scale); static float calculate_peak(const float * p_source, size_t p_count); static double calculate_peak(const double * p_source, size_t p_count); @@ -44,10 +46,10 @@ namespace pfc { static void convert(const double* in, double* out, size_t count); static void convert(const double* in, double* out, size_t count, double scale); - inline static int64_t rint64(float val) { return (int64_t)llroundf(val); } - inline static int32_t rint32(float val) { return (int32_t)lroundf(val); } - inline static int64_t rint64(double val) { return (int64_t)llround(val); } - inline static int32_t rint32(double val) { return (int32_t)lround(val); } + inline static int64_t rint64(float val) { return (int64_t)llrint(val); } + inline static int32_t rint32(float val) { return (int32_t)lrint(val); } + inline static int64_t rint64(double val) { return (int64_t)llrint(val); } + inline static int32_t rint32(double val) { return (int32_t)lrint(val); } static inline uint64_t time_to_samples(double p_time, uint32_t p_sample_rate) { return (uint64_t)pfc::rint64((double)p_sample_rate * p_time); @@ -61,13 +63,13 @@ namespace pfc { static inline double gain_to_scale(double p_gain) { return pow(10.0, p_gain / 20.0); } static inline double scale_to_gain(double scale) { return 20.0*log10(scale); } + static unsigned bitrate_kbps( uint64_t fileSize, double duration ); + static constexpr float float16scale = 65536.f; - static float decodeFloat24ptr(const void * sourcePtr); - static float decodeFloat24ptrbs(const void * sourcePtr); + static float decodeFloat24ptr(const void* sourcePtr); + static float decodeFloat24ptrbs(const void* sourcePtr); static float decodeFloat16(uint16_t source); - - static unsigned bitrate_kbps( uint64_t fileSize, double duration ); }; // class audio_math } // namespace pfc diff --git a/sdk/pfc/avltree.h b/sdk/pfc/avltree.h index 6287b09..0b7d942 100644 --- a/sdk/pfc/avltree.h +++ b/sdk/pfc/avltree.h @@ -9,15 +9,15 @@ namespace pfc { public: typedef _list_node t_node; typedef _avltree_node t_self; - template _avltree_node(t_param const& param) : t_node(param), m_left(), m_right(), m_depth() {} + template _avltree_node(t_param const& param) : t_node(param) {} typedef refcounted_object_ptr_t t_ptr; typedef t_self* t_rawptr; - t_ptr m_left, m_right; - t_rawptr m_parent; + t_ptr m_left, m_right; // smart ptr, no init + t_rawptr m_parent = nullptr; - t_size m_depth; + t_size m_depth = 0; void link_left(t_self* ptr) throw() { m_left = ptr; @@ -457,6 +457,7 @@ namespace pfc { _unlink_recur(m_root); m_root.release(); } + void clear() throw() { remove_all(); } bool remove(const_iterator const& iter) { PFC_ASSERT(iter.is_valid()); @@ -471,9 +472,8 @@ namespace pfc { return ret; } - t_size get_count() const throw() { - return calc_count(m_root.get_ptr()); - } + t_size get_count() const throw() { return calc_count(m_root.get_ptr()); } + size_t size() const throw() { return get_count(); } template void enumerate(t_callback && p_callback) const { diff --git a/sdk/pfc/bigmem.cpp b/sdk/pfc/bigmem.cpp index d7e79ae..a9ebc17 100644 --- a/sdk/pfc/bigmem.cpp +++ b/sdk/pfc/bigmem.cpp @@ -23,7 +23,7 @@ namespace pfc { m_data.set_size(0); m_size = 0; } - void bigmem::read(void* ptrOut, size_t bytes, size_t offset) { + void bigmem::read(void* ptrOut, size_t bytes, size_t offset) const { PFC_ASSERT(offset + bytes <= size()); uint8_t* outWalk = (uint8_t*)ptrOut; while (bytes > 0) { diff --git a/sdk/pfc/bigmem.h b/sdk/pfc/bigmem.h index 58d9481..ff2392b 100644 --- a/sdk/pfc/bigmem.h +++ b/sdk/pfc/bigmem.h @@ -12,7 +12,7 @@ namespace pfc { void resize(size_t newSize); size_t size() const {return m_size;} void clear(); - void read(void * ptrOut, size_t bytes, size_t offset); + void read(void * ptrOut, size_t bytes, size_t offset) const; void write(const void * ptrIn, size_t bytes, size_t offset); uint8_t * _slicePtr(size_t which); size_t _sliceCount(); diff --git a/sdk/pfc/bsearch_inline.h b/sdk/pfc/bsearch_inline.h index 92afed5..0d332d4 100644 --- a/sdk/pfc/bsearch_inline.h +++ b/sdk/pfc/bsearch_inline.h @@ -5,7 +5,7 @@ namespace pfc { //deprecated template -inline bool bsearch_inline_t(t_size p_count, const t_callback & p_callback,t_size & p_result) +inline bool bsearch_inline_t(t_size p_count, t_callback && p_callback,t_size & p_result) { t_size max = p_count; t_size min = 0; @@ -49,7 +49,7 @@ inline bool bsearch_simple_inline_t(t_buffer&& p_buffer, t_size p_count, t_value } template -inline bool bsearch_simple_inline_t(t_buffer && p_buffer,t_size p_count,t_value const & p_value,t_size & p_result) +inline bool bsearch_simple_inline_t(t_buffer && p_buffer,t_size p_count,t_value && p_value,t_size & p_result) { t_size max = p_count; t_size min = 0; diff --git a/sdk/pfc/byte_order.h b/sdk/pfc/byte_order.h index 5c22d33..a9d2178 100644 --- a/sdk/pfc/byte_order.h +++ b/sdk/pfc/byte_order.h @@ -124,13 +124,13 @@ namespace byte_order { #if PFC_BYTE_ORDER_IS_BIG_ENDIAN//big endian template inline void order_native_to_le_t(T& param) {param = pfc::byteswap_t(param);} - template inline void order_native_to_be_t(T& param) {} + template inline void order_native_to_be_t(T& param) { (void)param; } template inline void order_le_to_native_t(T& param) {param = pfc::byteswap_t(param);} - template inline void order_be_to_native_t(T& param) {} + template inline void order_be_to_native_t(T& param) { (void)param; } #else//little endian - template inline void order_native_to_le_t(T& param) {} + template inline void order_native_to_le_t(T& param) { (void)param; } template inline void order_native_to_be_t(T& param) {param = pfc::byteswap_t(param);} - template inline void order_le_to_native_t(T& param) {} + template inline void order_le_to_native_t(T& param) { (void)param; } template inline void order_be_to_native_t(T& param) {param = pfc::byteswap_t(param);} #endif }; diff --git a/sdk/pfc/charDownConvert.cpp b/sdk/pfc/charDownConvert.cpp index a49f18d..3b3c89c 100644 --- a/sdk/pfc/charDownConvert.cpp +++ b/sdk/pfc/charDownConvert.cpp @@ -81,44 +81,49 @@ namespace pfc { size_t CharDownConvert::numMappings() { return std::size(g_mappings); } CharDownConvert::CharDownConvert() { + std::map charConvertMap; + for (auto& l : g_mappings) { - m_charConvertMap[(uint32_t)l.from] = (uint32_t)l.to; + charConvertMap[(uint32_t)l.from] = (uint32_t)l.to; + } + for (auto& line : twoCharMappings) { + charConvertMap[line.from] = line.to; } - g_charConvertMapInit_AddBullshitExceptions(); + + m_charConvertMap.initialize(std::move(charConvertMap)); } void CharDownConvert::TransformCharCachedAppend(t_uint32 c, pfc::string_base& out) { - auto subst = m_charConvertMap.find(c); - if (subst != m_charConvertMap.end()) { - out << subst->second.ptr(); + auto subst = m_charConvertMap.query_ptr(c); + if (subst != nullptr) { + out << subst->ptr(); } else { out.add_char(c); } } + void CharDownConvert::TransformStringHere(pfc::string_base& out, const char* src, size_t len) { + out.reset(); this->TransformStringAppend(out, src, len); + } - void CharDownConvert::TransformStringAppend(pfc::string_base& out, const char* src) { - for (;;) { - char c = *src; + void CharDownConvert::TransformStringAppend(pfc::string_base& out, const char* src, size_t len) { + size_t walk = 0; + while(walk < len) { + char c = src[walk]; if (c > 0) { out.add_byte(pfc::ascii_tolower_lookup(c)); - ++src; + ++walk; } else if (c == 0) { break; } else { - unsigned c; t_size d; - d = pfc::utf8_decode_char(src, c); + unsigned wc; t_size d; + d = pfc::utf8_decode_char(src + walk, wc, len - walk); if (d == 0) break; - TransformCharCachedAppend(c, out); - src += d; + TransformCharCachedAppend(wc, out); + walk += d; } } } - void CharDownConvert::g_charConvertMapInit_AddBullshitExceptions() { - for (auto& line : twoCharMappings) { - m_charConvertMap[line.from] = line.to; - } - } CharDownConvert& CharDownConvert::instance() { static CharDownConvert obj; return obj; } diff --git a/sdk/pfc/charDownConvert.h b/sdk/pfc/charDownConvert.h index 1cf825d..33a87a2 100644 --- a/sdk/pfc/charDownConvert.h +++ b/sdk/pfc/charDownConvert.h @@ -1,10 +1,8 @@ #pragma once #include "string_base.h" -#include - -// This converts to ASCII *and* lowercases for matching -// Legacy feature, do not use in new code +#include "fixed_map.h" +// This converts to ASCII *and* lowercases for simplified search matching namespace pfc { class CharStorage { public: @@ -27,7 +25,7 @@ namespace pfc { #endif } - char m_data[16]; + char m_data[16] = {}; }; class CharDownConvert { @@ -35,7 +33,8 @@ namespace pfc { CharDownConvert(); void TransformCharCachedAppend(t_uint32 c, pfc::string_base& out); - void TransformStringAppend(pfc::string_base& out, const char* src); + void TransformStringAppend(pfc::string_base& out, const char* src, size_t len = SIZE_MAX); + void TransformStringHere(pfc::string_base& out, const char* src, size_t len = SIZE_MAX); string8 TransformString(const char* src) { pfc::string8 ret; TransformStringAppend(ret, src); return ret; } void TransformString(pfc::string_base& out, const char* src) { out.reset(); TransformStringAppend(out, src); @@ -50,10 +49,6 @@ namespace pfc { static size_t numMappings(); private: - - void g_charConvertMapInit_AddBullshitExceptions(); - private: - std::map m_charConvertMap; + fixed_map m_charConvertMap; }; } - diff --git a/sdk/pfc/cpuid.cpp b/sdk/pfc/cpuid.cpp index 19d0eae..9fff5c5 100644 --- a/sdk/pfc/cpuid.cpp +++ b/sdk/pfc/cpuid.cpp @@ -64,16 +64,8 @@ namespace pfc { namespace pfc { const char* cpuArch() { -#ifdef _M_ARM64EC - return "ARM64EC"; -#elif defined(_M_X64) || defined(__x86_64__) - return "x64"; -#elif defined(_M_IX86) || defined(__i386__) - return "x86"; -#elif defined(_M_ARM64) || defined(__aarch64__) - return "ARM64"; -#elif defined(_M_ARM) || defined(__arm__) - return "ARM"; +#ifdef PFC_CPU_ARCH + return PFC_CPU_ARCH; #else return "Unknown"; #endif diff --git a/sdk/pfc/cpuid.h b/sdk/pfc/cpuid.h index 1c7bd8f..4757cbd 100644 --- a/sdk/pfc/cpuid.h +++ b/sdk/pfc/cpuid.h @@ -24,4 +24,16 @@ namespace pfc { namespace pfc { const char* cpuArch(); -} \ No newline at end of file +} + +#ifdef _M_ARM64EC +#define PFC_CPU_ARCH "ARM64EC" +#elif defined(_M_X64) || defined(__x86_64__) +#define PFC_CPU_ARCH "x64" +#elif defined(_M_IX86) || defined(__i386__) +#define PFC_CPU_ARCH "x86" +#elif defined(_M_ARM64) || defined(__aarch64__) +#define PFC_CPU_ARCH "ARM64" +#elif defined(_M_ARM) || defined(__arm__) +#define PFC_CPU_ARCH "ARM" +#endif diff --git a/sdk/pfc/debug.h b/sdk/pfc/debug.h index 6d01467..4a5512b 100644 --- a/sdk/pfc/debug.h +++ b/sdk/pfc/debug.h @@ -7,11 +7,16 @@ #endif namespace pfc { + void debugBreak(); [[noreturn]] void crash(); [[noreturn]] void crashWithMessageOnStack( const char * msg ); void outputDebugLine(const char * msg); - // Debug logger service. +#ifdef __APPLE__ + [[noreturn]] void appleThrowException( const char * name, const char * reason ); +#endif + + // Debug logger service. // It is up to the caller to ensure thread safety. You want to create debugLineReceiver instances on app startup and never destroy them. class debugLineReceiver { public: @@ -42,3 +47,11 @@ namespace pfc { #define PFC_SET_THREAD_DESCRIPTION(X) { ::pfc::setCurrentThreadDescription(X); } #define PFC_SET_THREAD_DESCRIPTION_SUPPORTED + +#define PFC_DEBUG_PRINT_FORCED(...) ::pfc::outputDebugLine(pfc::format(__VA_ARGS__)) + +#if PFC_DEBUG +#define PFC_DEBUG_PRINT(...) PFC_DEBUG_PRINT_FORCED(pfc::format(__VA_ARGS__)) +#else +#define PFC_DEBUG_PRINT(...) +#endif diff --git a/sdk/pfc/event_std.h b/sdk/pfc/event_std.h new file mode 100644 index 0000000..99bf77c --- /dev/null +++ b/sdk/pfc/event_std.h @@ -0,0 +1,49 @@ +#pragma once + +// Alternate lightweight implementation of pfc::event, with similar method sigantures but no support for multi-event-wait-for-any. +// It's a safe drop-in replacement for the regular event - code trying to use unsupported methods will fail to compile rather than behave incorrectly. + +// Rationale: +// Mac/Linux multi-wait-capable pipe-backed event is relatively expensive, in terms of CPU, memory and file descriptors opened. +// On Windows, event_std still outperforms regular win32 event but the difference is mostly insignificant in real life use cases. + +#include +#include +namespace pfc { + class event_std { + public: + void set_state(bool v = true) { + std::scoped_lock lock(m_mutex); + if (m_state != v) { + m_state = v; + if (v) m_condition.notify_all(); + } + } + bool is_set() const { return m_state; } + void wait() { + std::unique_lock lock(m_mutex); + m_condition.wait(lock, [this] { return this->m_state; }); + } + bool wait_for(double timeout) { + if (timeout < 0) { wait(); return true; } + std::unique_lock lock(m_mutex); + return m_condition.wait_for(lock, std::chrono::duration(timeout), [this] { return this->m_state; }); + } + void wait_and_clear() { + std::unique_lock lock(m_mutex); + m_condition.wait(lock, [this] { return this->m_state; }); + m_state = false; + } + bool wait_for_and_clear(double timeout) { + if ( timeout < 0 ) {wait_and_clear(); return true;} + std::unique_lock lock(m_mutex); + bool rv = m_condition.wait_for(lock, std::chrono::duration(timeout), [this] { return this->m_state; }); + if ( rv ) m_state = false; + return rv; + } + private: + volatile bool m_state = false; + std::condition_variable m_condition; + std::mutex m_mutex; + }; +} diff --git a/sdk/pfc/filehandle.cpp b/sdk/pfc/filehandle.cpp index f4cbc79..57359e2 100644 --- a/sdk/pfc/filehandle.cpp +++ b/sdk/pfc/filehandle.cpp @@ -6,7 +6,7 @@ #endif namespace pfc { -void fileHandleClose( fileHandle_t h ) { +void fileHandleClose( fileHandle_t h ) noexcept { if (h == fileHandleInvalid) return; #ifdef _WIN32 CloseHandle( h ); @@ -26,7 +26,7 @@ fileHandle_t fileHandleDup( fileHandle_t h ) { #endif } -void fileHandle::close() { +void fileHandle::close() noexcept { fileHandleClose( h ); clear(); } diff --git a/sdk/pfc/filehandle.h b/sdk/pfc/filehandle.h index b11a25d..c19f087 100644 --- a/sdk/pfc/filehandle.h +++ b/sdk/pfc/filehandle.h @@ -6,26 +6,26 @@ namespace pfc { const fileHandle_t fileHandleInvalid = INVALID_HANDLE_VALUE; #else typedef int fileHandle_t; - const fileHandle_t fileHandleInvalid = -1; + constexpr fileHandle_t fileHandleInvalid = -1; #endif - void fileHandleClose( fileHandle_t h ); + void fileHandleClose( fileHandle_t h ) noexcept; fileHandle_t fileHandleDup( fileHandle_t h ); class fileHandle { public: fileHandle( fileHandle_t val ) : h(val) {} fileHandle() : h ( fileHandleInvalid ) {} - ~fileHandle() { close(); } - fileHandle( fileHandle && other ) { h = other.h; other.clear(); } - void operator=( fileHandle && other ) { close(); h = other.h; other.clear(); } + ~fileHandle() noexcept { close(); } + fileHandle( fileHandle && other ) noexcept { h = other.h; other.clear(); } + void operator=( fileHandle && other ) noexcept { close(); h = other.h; other.clear(); } void operator=( fileHandle_t other ) { close(); h = other; } - void close(); - void clear() { h = fileHandleInvalid; } - bool isValid() { return h != fileHandleInvalid; } + void close() noexcept; + void clear() noexcept { h = fileHandleInvalid; } + bool isValid() noexcept { return h != fileHandleInvalid; } fileHandle_t h; private: - fileHandle( const fileHandle & ); - void operator=( const fileHandle & ); + fileHandle( const fileHandle & ) = delete; + void operator=( const fileHandle & ) = delete; }; } diff --git a/sdk/pfc/filetimetools.cpp b/sdk/pfc/filetimetools.cpp new file mode 100644 index 0000000..95195b6 --- /dev/null +++ b/sdk/pfc/filetimetools.cpp @@ -0,0 +1,331 @@ +#include "pfc-lite.h" + +#include "filetimetools.h" + +#include "timers.h" + +#include + +namespace { + class exception_time_error {}; +} + +using namespace pfc; + +#ifndef _WIN32 +namespace { + typedef uint16_t WORD; + + typedef struct _SYSTEMTIME { + WORD wYear; + WORD wMonth; + WORD wDayOfWeek; + WORD wDay; + WORD wHour; + WORD wMinute; + WORD wSecond; + WORD wMilliseconds; + } SYSTEMTIME, * PSYSTEMTIME, * LPSYSTEMTIME; + +} +static void SystemTimeToNix(const SYSTEMTIME& st, struct tm& Time) { + memset(&Time, 0, sizeof(Time)); + Time.tm_sec = st.wSecond; + Time.tm_min = st.wMinute; + Time.tm_hour = st.wHour; + Time.tm_mday = st.wDay; + Time.tm_mon = st.wMonth - 1; + Time.tm_year = st.wYear - 1900; +} + +static t_filetimestamp ExportSystemTime(const SYSTEMTIME& st) { + struct tm Time; + SystemTimeToNix(st, Time); + return pfc::fileTimeUtoW(mktime(&Time)); +} + +static t_filetimestamp ExportSystemTimeLocal(const SYSTEMTIME& st) { + struct tm Time, Local; + SystemTimeToNix(st, Time); + time_t t = mktime(&Time); + localtime_r(&t, &Local); + return pfc::fileTimeUtoW(mktime(&Local)); +} +static void SystemTimeFromNix(SYSTEMTIME& st, struct tm const& Time, t_filetimestamp origTS) { + memset(&st, 0, sizeof(st)); + st.wSecond = Time.tm_sec; + st.wMinute = Time.tm_min; + st.wHour = Time.tm_hour; + st.wDay = Time.tm_mday; + st.wDayOfWeek = Time.tm_wday; + st.wMonth = Time.tm_mon + 1; + st.wYear = Time.tm_year + 1900; + st.wMilliseconds = (origTS % filetimestamp_1second_increment) / (filetimestamp_1second_increment/1000); +} + +static bool MakeSystemTime(SYSTEMTIME& st, t_filetimestamp ts) { + time_t t = (time_t)pfc::fileTimeWtoU(ts); + struct tm Time; + if (gmtime_r(&t, &Time) == NULL) return false; + SystemTimeFromNix(st, Time, ts); + return true; +} + +static bool MakeSystemTimeLocal(SYSTEMTIME& st, t_filetimestamp ts) { + time_t t = (time_t)pfc::fileTimeWtoU(ts); + struct tm Time; + if (localtime_r(&t, &Time) == NULL) return false; + SystemTimeFromNix(st, Time, ts); + return true; +} + +#else +static t_filetimestamp ExportSystemTime(const SYSTEMTIME& st) { + t_filetimestamp base; + if (!SystemTimeToFileTime(&st, (FILETIME*)&base)) throw exception_time_error(); + return base; +} +static t_filetimestamp ExportSystemTimeLocal(const SYSTEMTIME& st) { +#ifdef FOOBAR2000_DESKTOP_WINDOWS + t_filetimestamp base, out; + if (!SystemTimeToFileTime(&st, (FILETIME*)&base)) throw exception_time_error(); + if (!LocalFileTimeToFileTime((const FILETIME*)&base, (FILETIME*)&out)) throw exception_time_error(); + return out; +#else + SYSTEMTIME UTC; + if (!TzSpecificLocalTimeToSystemTime(NULL, &st, &UTC)) throw exception_time_error(); + return ExportSystemTime(UTC); +#endif +} +static bool MakeSystemTime(SYSTEMTIME& st, t_filetimestamp ts) { + if (ts == filetimestamp_invalid) return false; + return !!FileTimeToSystemTime((const FILETIME*)&ts, &st); + +} +static bool MakeSystemTimeLocal(SYSTEMTIME& st, t_filetimestamp ts) { + if (ts == filetimestamp_invalid) return false; +#ifdef FOOBAR2000_DESKTOP_WINDOWS + FILETIME ft; + if (FileTimeToLocalFileTime((FILETIME*)&ts, &ft)) { + if (FileTimeToSystemTime(&ft, &st)) { + return true; + } + } + return false; +#else + SYSTEMTIME UTC; + if (FileTimeToSystemTime((FILETIME*)&ts, &UTC)) { + if (SystemTimeToTzSpecificLocalTime(NULL, &UTC, &st)) return true; + } + return false; +#endif +} +#endif // _WIN32 + +static bool is_spacing(char c) { return c == ' ' || c == 10 || c == 13 || c == '\t'; } + +static unsigned ParseDateElem(const char* ptr, t_size len) { + unsigned ret = 0; + for (t_size walk = 0; walk < len; ++walk) { + const char c = ptr[walk]; + if (c < '0' || c > '9') throw exception_time_error(); + ret = ret * 10 + (unsigned)(c - '0'); + } + return ret; +} + +static bool st_sanity(SYSTEMTIME const& st) { + return st.wYear >= 1601 && st.wMonth >= 1 && st.wMonth <= 12 && st.wDay >= 1 && st.wDay <= 31 && st.wHour < 24 && st.wMinute < 60 && st.wSecond < 60 && st.wMilliseconds < 1000; +} +static t_filetimestamp filetimestamp_from_string_internal(const char* date, bool local) { + // Accepted format + // YYYY-MM-DD HH:MM:SS + try { + SYSTEMTIME st = {}; + st.wDay = 1; st.wMonth = 1; + + unsigned walk = 0; + auto worker = [&](unsigned n) { + auto ret = ParseDateElem(date + walk, n); + walk += n; + if (ret > UINT16_MAX) throw exception_time_error(); + return (WORD)ret;; + }; + + auto skip = [&](char c) { + if (date[walk] == c) ++walk; + }; + + auto skipSpacing = [&] { + while (is_spacing(date[walk])) ++walk; + }; + skipSpacing(); + st.wYear = worker(4); + skip('-'); + st.wMonth = worker(2); + skip('-'); + st.wDay = worker(2); + skipSpacing(); + st.wHour = worker(2); + skip(':'); + st.wMinute = worker(2); + skip(':'); + st.wSecond = worker(2); + if (date[walk] == '.') { + double v = pfc::string_to_float(date + walk); + st.wMilliseconds = (WORD)floor(v * 1000.f); // don't ever round up, don't want to handle ms of 1000 + } + + if (!st_sanity(st)) throw exception_time_error(); + + if (local) { + return ExportSystemTimeLocal(st); + } else { + return ExportSystemTime(st); + } + } catch (exception_time_error const &) { + return filetimestamp_invalid; + } +} + +namespace pfc { + t_filetimestamp filetimestamp_from_string(const char* date) { + return filetimestamp_from_string_internal(date, true); + } + + t_filetimestamp filetimestamp_from_string_utc(const char* date) { + return filetimestamp_from_string_internal(date, false); + } + + static constexpr char g_invalidMsg[] = ""; + + pfc::string_formatter format_filetimestamp(t_filetimestamp p_timestamp) { + try { + SYSTEMTIME st; + if (MakeSystemTimeLocal(st, p_timestamp)) { + pfc::string_formatter buffer; + buffer + << pfc::format_uint(st.wYear, 4) << "-" << pfc::format_uint(st.wMonth, 2) << "-" << pfc::format_uint(st.wDay, 2) << " " + << pfc::format_uint(st.wHour, 2) << ":" << pfc::format_uint(st.wMinute, 2) << ":" << pfc::format_uint(st.wSecond, 2); + return buffer; + } + } catch (...) {} + return g_invalidMsg; + } + + pfc::string_formatter format_filetimestamp_ms(t_filetimestamp p_timestamp) { + try { + SYSTEMTIME st; + if (MakeSystemTimeLocal(st, p_timestamp)) { + pfc::string_formatter buffer; + buffer + << pfc::format_uint(st.wYear, 4) << "-" << pfc::format_uint(st.wMonth, 2) << "-" << pfc::format_uint(st.wDay, 2) << " " + << pfc::format_uint(st.wHour, 2) << ":" << pfc::format_uint(st.wMinute, 2) << ":" << pfc::format_uint(st.wSecond, 2) << "." << pfc::format_uint(st.wMilliseconds, 3); + return buffer; + } + } catch (...) {} + return g_invalidMsg; + } + + pfc::string_formatter format_filetimestamp_utc(t_filetimestamp p_timestamp) { + try { + SYSTEMTIME st; + if (MakeSystemTime(st, p_timestamp)) { + pfc::string_formatter buffer; + buffer + << pfc::format_uint(st.wYear, 4) << "-" << pfc::format_uint(st.wMonth, 2) << "-" << pfc::format_uint(st.wDay, 2) << " " + << pfc::format_uint(st.wHour, 2) << ":" << pfc::format_uint(st.wMinute, 2) << ":" << pfc::format_uint(st.wSecond, 2); + return buffer; + } + } catch (...) {} + return g_invalidMsg; + } + +} // namespace foobar2000_io + +namespace { + struct dateISO_t { + unsigned Y, M, D; + unsigned h, m, s; + double sfrac; + int tzdelta; + }; + + dateISO_t read_ISO_8601(const char* dateISO) { + dateISO_t ret = {}; + // 2022-01-26T13:44:51.200000Z + // 2010-02-19T14:54:23.031+08:00 + // 2022-01-27T11:01:49+00:00 + // 2022-01-27T11:01:49Z + // 20220127T110149Z + + unsigned walk = 0; + auto worker = [&](unsigned n) { + auto ret = ParseDateElem(dateISO + walk, n); + walk += n; + return ret; + }; + auto skip = [&](char c) { + if (dateISO[walk] == c) ++walk; + }; + auto expect = [&](char c) { + if (dateISO[walk] != c) throw exception_time_error(); + ++walk; + }; + ret.Y = worker(4); + skip('-'); + ret.M = worker(2); + skip('-'); + ret.D = worker(2); + expect('T'); + ret.h = worker(2); + skip(':'); + ret.m = worker(2); + skip(':'); + ret.s = worker(2); + if (dateISO[walk] == '.') { + unsigned base = walk; + ++walk; + while (pfc::char_is_numeric(dateISO[walk])) ++walk; + ret.sfrac = pfc::string_to_float(dateISO + base, walk - base); + } + if (dateISO[walk] == '+' || dateISO[walk] == '-') { + bool neg = dateISO[walk] == '-'; + ++walk; + unsigned tz_h = worker(2); + if (tz_h >= 24) throw exception_time_error(); + skip(':'); + unsigned tz_m = worker(2); + if (tz_m >= 60) throw exception_time_error(); + tz_m += tz_h * 60; + ret.tzdelta = neg ? (int)tz_m : -(int)tz_m; // reversed! it's a timezone offset, have to *add* it if timezone has a minus + } + return ret; + } +} + +t_filetimestamp pfc::filetimestamp_from_string_ISO_8601(const char* dateISO) { + try { + auto elems = read_ISO_8601(dateISO); + + SYSTEMTIME st = {}; + st.wDay = 1; st.wMonth = 1; + st.wYear = (WORD)elems.Y; + st.wMonth = (WORD)elems.M; + st.wDay = (WORD)elems.D; + st.wHour = (WORD)elems.h; + st.wMinute = (WORD)elems.m; + st.wSecond = (WORD)elems.s; + st.wMilliseconds = (WORD)floor(elems.sfrac * 1000.f); + + if (!st_sanity(st)) throw exception_time_error(); + + auto ret = ExportSystemTime(st); + + ret += filetimestamp_1second_increment * elems.tzdelta * 60; + + return ret; + } catch (...) { + return filetimestamp_invalid; + } +} diff --git a/sdk/pfc/filetimetools.h b/sdk/pfc/filetimetools.h new file mode 100644 index 0000000..c7af5aa --- /dev/null +++ b/sdk/pfc/filetimetools.h @@ -0,0 +1,20 @@ +#pragma once + +namespace pfc { + typedef uint64_t t_filetimestamp; + static constexpr t_filetimestamp filetimestamp_invalid = 0; + static constexpr t_filetimestamp filetimestamp_1second_increment = 10000000; + + t_filetimestamp filetimestamp_from_string(const char * date); + t_filetimestamp filetimestamp_from_string_utc(const char* date); + // From ISO 8601 time + t_filetimestamp filetimestamp_from_string_ISO_8601(const char* date); + + //! Warning: this formats according to system timezone settings, created strings should be used for display only, never for storage. + pfc::string_formatter format_filetimestamp(t_filetimestamp p_timestamp); + //! UTC timestamp + pfc::string_formatter format_filetimestamp_utc(t_filetimestamp p_timestamp); + //! Local timestamp with milliseconds + pfc::string_formatter format_filetimestamp_ms(t_filetimestamp p_timestamp); + +} diff --git a/sdk/pfc/iterators.h b/sdk/pfc/iterators.h index 6bc83b5..00bb424 100644 --- a/sdk/pfc/iterators.h +++ b/sdk/pfc/iterators.h @@ -53,6 +53,9 @@ namespace pfc { bool operator==(const t_self & other) const throw() {return this->m_content == other.m_content;} bool operator!=(const t_self & other) const throw() {return this->m_content != other.m_content;} + + // Returns pointer to referenced item - null if iterator isn't valid + const t_item* get() const noexcept { return this->m_content.is_valid() ? &this->m_content->m_content : nullptr; } protected: t_nodeptr m_content; }; @@ -82,6 +85,9 @@ namespace pfc { bool operator==(const t_self & other) const throw() {return this->m_content == other.m_content;} bool operator!=(const t_self & other) const throw() {return this->m_content != other.m_content;} + + // Returns pointer to referenced item - null if iterator isn't valid + t_item* get() const noexcept { return this->m_content.is_valid() ? &this->m_content->m_content : nullptr; } }; template class forward_iterator { diff --git a/sdk/pfc/list.h b/sdk/pfc/list.h index b3e94c0..fb45b29 100644 --- a/sdk/pfc/list.h +++ b/sdk/pfc/list.h @@ -87,11 +87,11 @@ private: typedef list_base_const_t t_self; t_size n,max = get_count(); for(n=0;n - inline bool have_item(const t_search & p_item) const {return find_item(p_item)!=~0;} + inline bool have_item(const t_search & p_item) const {return find_item(p_item)!=SIZE_MAX;} template @@ -151,7 +151,7 @@ class list_single_ref_t : public list_base_const_t public: list_single_ref_t(const T & p_item,t_size p_count = 1) : m_item(p_item), m_count(p_count) {} t_size get_count() const {return m_count;} - void get_item_ex(T& p_out,t_size n) const {PFC_ASSERT(n { inline t_size insert_item(const T & item,t_size base) {return insert_items(list_single_ref_t(item),base);} t_size insert_items_repeat(const T & item,t_size num,t_size base) {return insert_items(list_single_ref_t(item,num),base);} - inline t_size add_items_repeat(T item,t_size num) {return insert_items_repeat(item,num,~0);} + inline t_size add_items_repeat(T item,t_size num) {return insert_items_repeat(item,num,SIZE_MAX);} t_size insert_items_fromptr(const T* source,t_size num,t_size base) {return insert_items(list_const_ptr_t(source,num),base);} - inline t_size add_items_fromptr(const T* source,t_size num) {return insert_items_fromptr(source,num,~0);} + inline t_size add_items_fromptr(const T* source,t_size num) {return insert_items_fromptr(source,num,SIZE_MAX);} inline t_size add_items(const list_base_const_t & items) {return insert_items(items,SIZE_MAX);} inline t_size add_item(const T& item) {return insert_item(item,SIZE_MAX);} @@ -394,11 +394,11 @@ class list_impl_t : public list_base_t T remove_by_idx(t_size idx) { - T ret = m_buffer[idx]; + T ret = std::move(m_buffer[idx]); t_size n; t_size max = m_buffer.get_size(); for(n=idx+1;n } template - void add_items(const t_in & in) {insert_items(in, ~0);} + void add_items(const t_in & in) {insert_items(in, SIZE_MAX);} void get_items_mask(list_impl_t & out,const bit_array & mask) { @@ -636,11 +636,11 @@ class list_impl_t : public list_base_t t_size n,max = get_size(); for(n=0;n - inline bool have_item(const t_search & p_item) const {return this->template find_item(p_item)!=~0;} + inline bool have_item(const t_search & p_item) const {return this->template find_item(p_item)!=SIZE_MAX;} template t_self & operator=(t_in const & source) {remove_all(); add_items(source); return *this;} template t_self & operator+=(t_in const & p_source) {add_item(p_source); return *this;} diff --git a/sdk/pfc/map.h b/sdk/pfc/map.h index acbf3ed..546b597 100644 --- a/sdk/pfc/map.h +++ b/sdk/pfc/map.h @@ -40,6 +40,9 @@ namespace pfc { return m_data.have_item(t_search_query<_t_key>(p_key)); } + template + bool contains(key_t const& arg) const { return have_item(arg); } + template bool query(const _t_key & p_key,_t_value & p_value) const { const t_storage * storage = m_data.find_ptr(t_search_query<_t_key>(p_key)); @@ -135,8 +138,10 @@ namespace pfc { t_size get_count() const throw() {return m_data.get_count();} + size_t size() const throw() { return get_count(); } void remove_all() throw() {m_data.remove_all();} + void clear() throw() { remove_all(); } template void overwrite(const t_source & p_source) { diff --git a/sdk/pfc/nix-objects.cpp b/sdk/pfc/nix-objects.cpp index b3012a0..01072f8 100644 --- a/sdk/pfc/nix-objects.cpp +++ b/sdk/pfc/nix-objects.cpp @@ -125,8 +125,7 @@ namespace pfc { pfc::array_t< pollfd > v; v.set_size_discard( count ); size_t walk = 0; - for( auto i = total.m_fds.begin(); i != total.m_fds.end(); ++ i ) { - const int fd = *i; + for( auto fd : total.m_fds) { auto & f = v[walk++]; f.fd = fd; f.events = (Reads[fd] ? POLLIN : 0) | (Writes[fd] ? POLLOUT : 0); @@ -166,19 +165,47 @@ namespace pfc { if (f.revents & POLLOUT) Writes += f.fd; if (f.revents & POLLERR) Errors += f.fd; } + PFC_ASSERT( !Reads.m_fds.empty() || !Writes.m_fds.empty() || !Errors.m_fds.empty() ); } return status; } - bool fdCanRead( int fd ) { - return fdWaitRead( fd, 0 ); + inline bool fdCanRead_select( int fdRead ) { + PFC_ASSERT( fdRead < FD_SETSIZE ); + timeval tv = {}; + fd_set set; + FD_ZERO(&set); + FD_SET(fdRead, &set); + + return select(fdRead + 1, &set, nullptr, nullptr, &tv) > 0; + } + inline bool fdCanRead_poll(int fdRead) { + pollfd arg = {fdRead, POLLIN }; + poll(&arg, 1, 0); + return (arg.revents & POLLIN) != 0; } + + bool fdCanRead( int fdRead ) { + if ( fdRead < 0 ) { + PFC_ASSERT( !"???" ); + return false; + } + #ifdef __APPLE__ + // BROKEN extremely inefficient implementation of poll() on Apple systems, avoid if possible + if ( fdRead < FD_SETSIZE ) { + return fdCanRead_select( fdRead ); + } + #endif + return fdCanRead_poll(fdRead); + } + bool fdCanWrite( int fd ) { return fdWaitWrite( fd, 0 ); } bool fdWaitRead( int fd, double timeOutSeconds ) { + if ( timeOutSeconds == 0 ) return fdCanRead( fd ); fdSelect sel; sel.Reads += fd; return sel.Select( timeOutSeconds ) > 0; } @@ -187,10 +214,11 @@ namespace pfc { return sel.Select( timeOutSeconds ) > 0; } - nix_event::nix_event() { + nix_event::nix_event(bool state) { createPipe( m_fd ); setNonBlocking( m_fd[0] ); setNonBlocking( m_fd[1] ); + if ( state ) set_state(true); } nix_event::~nix_event() { close( m_fd[0] ); @@ -216,6 +244,9 @@ namespace pfc { bool nix_event::wait_for( double p_timeout_seconds ) { return fdWaitRead( m_fd[0], p_timeout_seconds ); } + bool nix_event::is_set() { + return fdCanRead(m_fd[0]); + } bool nix_event::g_wait_for( int p_event, double p_timeout_seconds ) { return fdWaitRead( p_event, p_timeout_seconds ); } @@ -299,14 +330,16 @@ namespace pfc { #endif } + static int openDevRand() { + int ret = open("/dev/urandom", O_RDONLY); + if ( ret < 0 ) throw exception_nix(); + return ret; + } void nixGetRandomData( void * outPtr, size_t outBytes ) { try { - fileHandle randomData; - randomData = open("/dev/urandom", O_RDONLY); - if (randomData.h < 0) throw exception_nix(); + static fileHandle randomData = openDevRand(); if (read(randomData.h, outPtr, outBytes) != outBytes) throw exception_nix(); - } - catch (std::exception const & e) { + } catch (std::exception const & e) { throw std::runtime_error("getRandomData failure"); } } diff --git a/sdk/pfc/nix-objects.h b/sdk/pfc/nix-objects.h index d7bc8e6..d7d8bb7 100644 --- a/sdk/pfc/nix-objects.h +++ b/sdk/pfc/nix-objects.h @@ -77,13 +77,15 @@ namespace pfc { class nix_event { public: - nix_event(); + nix_event(bool state = false); ~nix_event(); void set_state( bool state ); - bool is_set( ) {return wait_for(0); } + bool is_set( ); + void wait_and_clear() { wait(); set_state(false); } + void wait() { wait_for(-1); } bool wait_for( double p_timeout_seconds ); static bool g_wait_for( int p_event, double p_timeout_seconds ); diff --git a/sdk/pfc/obj-c.mm b/sdk/pfc/obj-c.mm index a1ca504..e3dc734 100644 --- a/sdk/pfc/obj-c.mm +++ b/sdk/pfc/obj-c.mm @@ -16,6 +16,7 @@ #endif #include "pfc.h" +#include "sortstring.h" namespace pfc { @@ -31,21 +32,21 @@ bool isShiftKeyPressed() { #if TARGET_OS_MAC && !TARGET_OS_IPHONE - return ( [NSEvent modifierFlags] & NSShiftKeyMask ) != 0; + return ( [NSEvent modifierFlags] & NSEventModifierFlagShift ) != 0; #else return false; #endif } bool isCtrlKeyPressed() { #if TARGET_OS_MAC && !TARGET_OS_IPHONE - return ( [NSEvent modifierFlags] & NSControlKeyMask ) != 0; + return ( [NSEvent modifierFlags] & NSEventModifierFlagControl ) != 0; #else return false; #endif } bool isAltKeyPressed() { #if TARGET_OS_MAC && !TARGET_OS_IPHONE - return ( [NSEvent modifierFlags] & NSAlternateKeyMask ) != 0; + return ( [NSEvent modifierFlags] & NSEventModifierFlagOption ) != 0; #else return false; #endif @@ -104,5 +105,39 @@ void appleSetThreadDescription( const char * str ) { return ret; } } + + int appleNaturalSortCompare(const char* s1, const char* s2) { + @autoreleasepool { + NSString * str1 = [NSString stringWithUTF8String: s1]; + NSString * str2 = [NSString stringWithUTF8String: s2]; + return (int) [str1 localizedCompare: str2]; + } + } + int appleNaturalSortCompareI(const char* s1, const char* s2) { + @autoreleasepool { + NSString * str1 = [NSString stringWithUTF8String: s1]; + NSString * str2 = [NSString stringWithUTF8String: s2]; + return (int) [str1 localizedCaseInsensitiveCompare: str2]; + } + } + [[noreturn]] void appleThrowException( const char * name, const char * reason ) { + @autoreleasepool { + @throw [NSException exceptionWithName: [NSString stringWithUTF8String: name] reason:[NSString stringWithUTF8String: reason] userInfo:nil]; + } + } + +#ifndef PFC_SORTSTRING_GENERIC + sortString_t makeSortString(const char* str) { + sortString_t ret; + ret.Attach( CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8) ); + return ret; + } + int sortStringCompare(sortString_t const& s1, sortString_t const& s2) { + return (int) CFStringCompare(s1.p, s2.p, kCFCompareLocalized | kCFCompareNumerically ); + } + int sortStringCompareI(sortString_t const& s1, sortString_t const& s2) { + return (int) CFStringCompare(s1.p, s2.p, kCFCompareLocalized | kCFCompareNumerically | kCFCompareCaseInsensitive ); + } +#endif } #endif diff --git a/sdk/pfc/order_helper.h b/sdk/pfc/order_helper.h index 373c686..93aae8f 100644 --- a/sdk/pfc/order_helper.h +++ b/sdk/pfc/order_helper.h @@ -14,6 +14,8 @@ namespace pfc { void create_move_item_permutation( size_t * p_output, size_t p_count, size_t from, size_t to ); bool create_drop_permutation(size_t * out, size_t itemCount, pfc::bit_array const & maskSelected, size_t insertMark ); + + bool is_identity(size_t const* order, size_t count); } class order_helper diff --git a/sdk/pfc/other.cpp b/sdk/pfc/other.cpp index aa6fec3..a241949 100644 --- a/sdk/pfc/other.cpp +++ b/sdk/pfc/other.cpp @@ -152,6 +152,12 @@ namespace pfc { return false; } + bool is_identity(size_t const* order, size_t count) { + for (size_t walk = 0; walk < count; ++walk) { + if (order[walk] != walk) return false; + } + return true; + } } void order_helper::g_swap(t_size * data,t_size ptr1,t_size ptr2) @@ -238,11 +244,19 @@ void pfc::outputDebugLine(const char * msg) { #endif } +void pfc::debugBreak() { +#ifdef _WIN32 + __debugbreak(); +#else + raise(SIGTRAP); +#endif +} + #if PFC_DEBUG #ifdef _WIN32 void pfc::myassert_win32(const wchar_t * _Message, const wchar_t *_File, unsigned _Line) { - if (IsDebuggerPresent()) pfc::crash(); + if (IsDebuggerPresent()) debugBreak(); PFC_DEBUGLOG << "PFC_ASSERT failure: " << _Message; PFC_DEBUGLOG << "PFC_ASSERT location: " << _File << " : " << _Line; _wassert(_Message,_File,_Line); @@ -252,7 +266,7 @@ void pfc::myassert_win32(const wchar_t * _Message, const wchar_t *_File, unsigne void pfc::myassert(const char * _Message, const char *_File, unsigned _Line) { PFC_DEBUGLOG << "Assert failure: \"" << _Message << "\" in: " << _File << " line " << _Line; - crash(); + debugBreak(); } #endif @@ -371,7 +385,7 @@ namespace pfc { void * ptr; #ifdef _MSC_VER ptr = _aligned_malloc(s, alignBytes); - throw std::bad_alloc(); + if (ptr == nullptr) throw std::bad_alloc(); #else #ifdef __ANDROID__ if ((ptr = memalign( alignBytes, s )) == NULL) throw std::bad_alloc(); diff --git a/sdk/pfc/pathUtils.cpp b/sdk/pfc/pathUtils.cpp index 7e9e829..2fbe2a8 100644 --- a/sdk/pfc/pathUtils.cpp +++ b/sdk/pfc/pathUtils.cpp @@ -13,26 +13,26 @@ namespace pfc { namespace io { namespace path { string getFileName(string path) { t_size split = path.lastIndexOfAnyChar(KPathSeparators); - if (split == ~0) return path; + if (split == SIZE_MAX) return path; else return path.subString(split+1); } string getFileNameWithoutExtension(string path) { string fn = getFileName(path); t_size split = fn.lastIndexOf('.'); - if (split == ~0) return fn; + if (split == SIZE_MAX) return fn; else return fn.subString(0,split); } string getFileExtension(string path) { string fn = getFileName(path); t_size split = fn.lastIndexOf('.'); - if (split == ~0) return ""; + if (split == SIZE_MAX) return ""; else return fn.subString(split); } string getDirectory(string filePath) {return getParent(filePath);} string getParent(string filePath) { t_size split = filePath.lastIndexOfAnyChar(KPathSeparators); - if (split == ~0) return ""; + if (split == SIZE_MAX) return ""; #ifdef _WINDOWS if (split > 0 && getIllegalNameChars().contains(filePath[split-1])) { if (split + 1 < filePath.length()) return filePath.subString(0,split+1); @@ -80,23 +80,23 @@ const char * charReplaceDefault(char c) { const char * charReplaceModern(char c) { switch (c) { case '*': - return u8"∗"; + return reinterpret_cast( u8"∗" ); case '\"': - return u8"''"; + return reinterpret_cast( u8"''" ); case ':': - return u8"∶"; + return reinterpret_cast( u8"∶" ); case '/': - return u8"\u2215"; + return reinterpret_cast( u8"\u2215" ); case '\\': - return u8"⧵"; + return reinterpret_cast( u8"⧵" ); case '?': - return u8"?"; + return reinterpret_cast( u8"?" ); case '<': - return u8"˂"; + return reinterpret_cast( u8"˂" ); case '>': - return u8"˃"; + return reinterpret_cast( u8"˃" ); case '|': - return u8"∣"; + return reinterpret_cast( u8"∣" ); default: return "_"; } @@ -183,8 +183,14 @@ string getIllegalNameChars(bool allowWC) { static const char * const specialIllegalNames[] = { "con", "aux", "lst", "prn", "nul", "eof", "inp", "out" }; +#endif // _WINDOWS + +#ifdef _WIN32 +static constexpr unsigned maxPathComponent = 255; +#else +static constexpr unsigned maxPathComponent = NAME_MAX; +#endif -enum { maxPathComponent = 255 }; static size_t safeTruncat( const char * str, size_t maxLen ) { size_t i = 0; size_t ret = 0; @@ -226,7 +232,6 @@ static string truncatePathComponent( string name, bool preserveExt ) { size_t truncat = safeTruncat( name.c_str(), maxPathComponent ); return name.subString(0, truncat); } -#endif // _WINDOWS static string trailingSanity(string name, bool preserveExt, const char * lstIllegal) { @@ -278,9 +283,9 @@ string validateFileName(string name, bool allowWC, bool preserveExt, charReplace name = trailingSanity(name, preserveExt, lstIllegal); } -#ifdef _WINDOWS name = truncatePathComponent(name, preserveExt); +#ifdef _WINDOWS for( auto p : specialIllegalNames ) { if (pfc::stringEqualsI_ascii( name.c_str(), p ) ) { name += "-"; diff --git a/sdk/pfc/pfc-license.txt b/sdk/pfc/pfc-license.txt index 3f05fcc..dd237fe 100644 --- a/sdk/pfc/pfc-license.txt +++ b/sdk/pfc/pfc-license.txt @@ -1,4 +1,4 @@ -Copyright (C) 2002-2022 Peter Pawlowski +Copyright (C) 2002-2024 Peter Pawlowski This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/sdk/pfc/pfc.vcxproj b/sdk/pfc/pfc.vcxproj index e8541b2..a03c81c 100644 --- a/sdk/pfc/pfc.vcxproj +++ b/sdk/pfc/pfc.vcxproj @@ -75,19 +75,19 @@ StaticLibrary true - v143 + v142 Unicode StaticLibrary true - v143 + v142 Unicode StaticLibrary true - v143 + v142 Unicode @@ -105,7 +105,7 @@ StaticLibrary true - v143 + v142 Unicode @@ -122,17 +122,17 @@ StaticLibrary - v143 + v142 Unicode StaticLibrary - v143 + v142 Unicode StaticLibrary - v143 + v142 Unicode @@ -147,7 +147,7 @@ StaticLibrary - v143 + v142 Unicode @@ -429,6 +429,7 @@ stdcpp17 true true + MultiThreadedDLL NDEBUG;%(PreprocessorDefinitions) @@ -665,7 +666,9 @@ + + @@ -701,6 +704,8 @@ + + @@ -772,6 +777,7 @@ + Disabled Disabled diff --git a/sdk/pfc/pfc.vcxproj.filters b/sdk/pfc/pfc.vcxproj.filters index 9ad3218..4a94674 100644 --- a/sdk/pfc/pfc.vcxproj.filters +++ b/sdk/pfc/pfc.vcxproj.filters @@ -97,6 +97,9 @@ Source Files + + Source Files + @@ -348,6 +351,18 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + diff --git a/sdk/pfc/pfc.xcodeproj/project.pbxproj b/sdk/pfc/pfc.xcodeproj/project.pbxproj new file mode 100644 index 0000000..8af4f46 --- /dev/null +++ b/sdk/pfc/pfc.xcodeproj/project.pbxproj @@ -0,0 +1,903 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 0F0794D427C90AA4006BAD7F /* fixed_map.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F0794CF27C90AA4006BAD7F /* fixed_map.h */; }; + 0F0794D527C90AA4006BAD7F /* charDownConvert.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F0794D027C90AA4006BAD7F /* charDownConvert.h */; }; + 0F0794D627C90AA4006BAD7F /* SmartStrStr-table.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F0794D127C90AA4006BAD7F /* SmartStrStr-table.h */; }; + 0F0794D727C90AA4006BAD7F /* charDownConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F0794D227C90AA4006BAD7F /* charDownConvert.cpp */; }; + 0F0794D827C90AA4006BAD7F /* SmartStrStr-twoCharMappings.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F0794D327C90AA4006BAD7F /* SmartStrStr-twoCharMappings.h */; }; + 0F0794D927C90AA8006BAD7F /* charDownConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F0794D227C90AA4006BAD7F /* charDownConvert.cpp */; }; + 0F14904D242E44C300D0BD81 /* notifyList.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F14904C242E44C300D0BD81 /* notifyList.h */; }; + 0F14904F242E44ED00D0BD81 /* autoref.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F14904E242E44ED00D0BD81 /* autoref.h */; }; + 0F149051242E454C00D0BD81 /* weakRef.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F149050242E454C00D0BD81 /* weakRef.h */; }; + 0F22012C2B0CCEA60074C1FC /* sortstring.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2201292B0CCEA60074C1FC /* sortstring.h */; }; + 0F22012D2B0CCEA60074C1FC /* string_simple.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F22012A2B0CCEA60074C1FC /* string_simple.h */; }; + 0F22012E2B0CCEA60074C1FC /* sort2.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F22012B2B0CCEA60074C1FC /* sort2.h */; }; + 0F2201322B0CCF3D0074C1FC /* CFObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2201312B0CCF3D0074C1FC /* CFObject.h */; }; + 0F2F4BF7250BFF660014812D /* pfc-fb2k-hooks.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2F4BEF250BFF660014812D /* pfc-fb2k-hooks.h */; }; + 0F2F4BF8250BFF660014812D /* killswitch.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2F4BF0250BFF660014812D /* killswitch.h */; }; + 0F2F4BF9250BFF660014812D /* pfc-fb2k-hooks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F2F4BF1250BFF660014812D /* pfc-fb2k-hooks.cpp */; }; + 0F2F4BFA250BFF660014812D /* pfc-fb2k-hooks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F2F4BF1250BFF660014812D /* pfc-fb2k-hooks.cpp */; }; + 0F2F4BFB250BFF660014812D /* stdsort.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2F4BF2250BFF660014812D /* stdsort.h */; }; + 0F2F4BFC250BFF660014812D /* suppress_fb2k_hooks.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2F4BF3250BFF660014812D /* suppress_fb2k_hooks.h */; }; + 0F2F4BFD250BFF660014812D /* platform-objects.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2F4BF4250BFF660014812D /* platform-objects.h */; }; + 0F2F4BFE250BFF660014812D /* pocket_char_ops.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2F4BF5250BFF660014812D /* pocket_char_ops.h */; }; + 0F2F4BFF250BFF660014812D /* cmd_thread.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2F4BF6250BFF660014812D /* cmd_thread.h */; }; + 0F54079726C2964500A118C8 /* splitString2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FB717E126C295370040D7FE /* splitString2.cpp */; }; + 0F643510253A250600D6335A /* string-conv-lite.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F64350D253A250600D6335A /* string-conv-lite.h */; }; + 0F643511253A250600D6335A /* pp-winapi.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F64350E253A250600D6335A /* pp-winapi.h */; }; + 0F643512253A250600D6335A /* string-conv-lite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F64350F253A250600D6335A /* string-conv-lite.cpp */; }; + 0F643513253A250600D6335A /* string-conv-lite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F64350F253A250600D6335A /* string-conv-lite.cpp */; }; + 0F65005525122FD5001B03BA /* string-compare.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F65005125122FD5001B03BA /* string-compare.cpp */; }; + 0F65005625122FD5001B03BA /* string-compare.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F65005125122FD5001B03BA /* string-compare.cpp */; }; + 0F65005725122FD5001B03BA /* string-compare.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F65005225122FD5001B03BA /* string-compare.h */; }; + 0F65005825122FD5001B03BA /* string-part.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F65005325122FD5001B03BA /* string-part.h */; }; + 0F65005925122FD5001B03BA /* string-lite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F65005425122FD5001B03BA /* string-lite.cpp */; }; + 0F65005A25122FD5001B03BA /* string-lite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F65005425122FD5001B03BA /* string-lite.cpp */; }; + 0F7A1B6A2A692C88004F89FB /* filetimetools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F7A1B682A692C88004F89FB /* filetimetools.cpp */; }; + 0F7A1B6B2A692C88004F89FB /* filetimetools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F7A1B682A692C88004F89FB /* filetimetools.cpp */; }; + 0F7A1B6C2A692C88004F89FB /* filetimetools.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F7A1B692A692C88004F89FB /* filetimetools.h */; }; + 0F7EDDAA27FAFDA5000996AA /* unicode-normalize.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F7EDDA827FAFDA5000996AA /* unicode-normalize.cpp */; }; + 0F7EDDAB27FAFDA5000996AA /* unicode-normalize.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F7EDDA827FAFDA5000996AA /* unicode-normalize.cpp */; }; + 0F7EDDAC27FAFDA5000996AA /* unicode-normalize.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F7EDDA927FAFDA5000996AA /* unicode-normalize.h */; }; + 0FAC031727C8EC6500BA9E97 /* SmartStrStr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FAC031527C8EC6500BA9E97 /* SmartStrStr.cpp */; }; + 0FAC031827C8EC6500BA9E97 /* SmartStrStr.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FAC031627C8EC6500BA9E97 /* SmartStrStr.h */; }; + 0FAC031927C8EC6A00BA9E97 /* SmartStrStr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FAC031527C8EC6500BA9E97 /* SmartStrStr.cpp */; }; + 0FB717E226C295370040D7FE /* splitString2.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB717E026C295370040D7FE /* splitString2.h */; }; + 0FB717E326C295370040D7FE /* splitString2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FB717E126C295370040D7FE /* splitString2.cpp */; }; + 0FFFAEAE23C9C9580023328B /* crashWithMessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FFFAEAD23C9C9580023328B /* crashWithMessage.cpp */; }; + 0FFFAEAF23C9DB640023328B /* crashWithMessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FFFAEAD23C9C9580023328B /* crashWithMessage.cpp */; }; + B10D405B19ADFADB004D2596 /* audio_math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1D8A0CD198FB83D00A23435 /* audio_math.cpp */; }; + B10D405C19ADFADB004D2596 /* audio_sample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1D8A0CE198FB83D00A23435 /* audio_sample.cpp */; }; + B10D405D19ADFADB004D2596 /* base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35C6198A702E00EF7043 /* base64.cpp */; }; + B10D405E19ADFADB004D2596 /* bit_array.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35CB198A702E00EF7043 /* bit_array.cpp */; }; + B10D405F19ADFADB004D2596 /* bsearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35CE198A702E00EF7043 /* bsearch.cpp */; }; + B10D406019ADFADB004D2596 /* cpuid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35D3198A702E00EF7043 /* cpuid.cpp */; }; + B10D406119ADFADB004D2596 /* filehandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1C37D7E19922EF500EE6ABC /* filehandle.cpp */; }; + B10D406219ADFADB004D2596 /* guid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35D6198A702E00EF7043 /* guid.cpp */; }; + B10D406319ADFADB004D2596 /* nix-objects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35DF198A702E00EF7043 /* nix-objects.cpp */; }; + B10D406419ADFADB004D2596 /* other.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35E3198A702E00EF7043 /* other.cpp */; }; + B10D406519ADFADB004D2596 /* pathUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35E5198A702E00EF7043 /* pathUtils.cpp */; }; + B10D406619ADFADB004D2596 /* printf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35EA198A702E00EF7043 /* printf.cpp */; }; + B10D406719ADFADB004D2596 /* selftest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35F0198A702E00EF7043 /* selftest.cpp */; }; + B10D406819ADFADB004D2596 /* sort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35F1198A702E00EF7043 /* sort.cpp */; }; + B10D406919ADFADB004D2596 /* stdafx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35F3198A702E00EF7043 /* stdafx.cpp */; }; + B10D406A19ADFADB004D2596 /* string_base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35F7198A702E00EF7043 /* string_base.cpp */; }; + B10D406B19ADFADB004D2596 /* string_conv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35F4198A702E00EF7043 /* string_conv.cpp */; }; + B10D406D19ADFADB004D2596 /* synchro_nix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35FD198A702E00EF7043 /* synchro_nix.cpp */; }; + B10D406E19ADFADB004D2596 /* threads.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD3600198A702E00EF7043 /* threads.cpp */; }; + B10D406F19ADFADB004D2596 /* timers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35EB198A702E00EF7043 /* timers.cpp */; }; + B10D407019ADFADB004D2596 /* utf8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD3603198A702E00EF7043 /* utf8.cpp */; }; + B10D407119ADFADB004D2596 /* wildcard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1B502D3198FF75000525EAF /* wildcard.cpp */; }; + B10D407219ADFADB004D2596 /* win-objects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD3604198A702E00EF7043 /* win-objects.cpp */; }; + B10D407319ADFE52004D2596 /* obj-c.mm in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35E1198A702E00EF7043 /* obj-c.mm */; }; + B125C8AD1F46D9A900ADB97B /* once.h in Headers */ = {isa = PBXBuildFile; fileRef = B125C8AC1F46D9A900ADB97B /* once.h */; }; + B12CBBC81BD4D96A00952805 /* bigmem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B12CBBC61BD4D96A00952805 /* bigmem.cpp */; }; + B12CBBC91BD4D96A00952805 /* bigmem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B12CBBC61BD4D96A00952805 /* bigmem.cpp */; }; + B12CBBCA1BD4D96A00952805 /* bigmem.h in Headers */ = {isa = PBXBuildFile; fileRef = B12CBBC71BD4D96A00952805 /* bigmem.h */; }; + B16695F719ACC12A0001728F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B16695F619ACC12A0001728F /* Foundation.framework */; }; + B17EB26E1E85358D0057E2A4 /* pool.h in Headers */ = {isa = PBXBuildFile; fileRef = B17EB26B1E85358D0057E2A4 /* pool.h */; }; + B17EB26F1E85358D0057E2A4 /* splitString.h in Headers */ = {isa = PBXBuildFile; fileRef = B17EB26C1E85358D0057E2A4 /* splitString.h */; }; + B17EB2701E85358D0057E2A4 /* wait_queue.h in Headers */ = {isa = PBXBuildFile; fileRef = B17EB26D1E85358D0057E2A4 /* wait_queue.h */; }; + B1B502D5198FF75000525EAF /* wildcard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1B502D3198FF75000525EAF /* wildcard.cpp */; }; + B1B502D6198FF75000525EAF /* wildcard.h in Headers */ = {isa = PBXBuildFile; fileRef = B1B502D4198FF75000525EAF /* wildcard.h */; }; + B1C37D7F19922EF500EE6ABC /* filehandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1C37D7E19922EF500EE6ABC /* filehandle.cpp */; }; + B1CF88B71BD6657F00F42F87 /* fpu.h in Headers */ = {isa = PBXBuildFile; fileRef = B1CF88B41BD6657F00F42F87 /* fpu.h */; }; + B1CF88B81BD6657F00F42F87 /* mem_block.h in Headers */ = {isa = PBXBuildFile; fileRef = B1CF88B51BD6657F00F42F87 /* mem_block.h */; }; + B1CF88B91BD6657F00F42F87 /* string-lite.h in Headers */ = {isa = PBXBuildFile; fileRef = B1CF88B61BD6657F00F42F87 /* string-lite.h */; }; + B1D8A0D0198FB83D00A23435 /* audio_math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1D8A0CD198FB83D00A23435 /* audio_math.cpp */; }; + B1D8A0D1198FB83D00A23435 /* audio_sample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1D8A0CE198FB83D00A23435 /* audio_sample.cpp */; }; + B1D8A0D2198FB83D00A23435 /* audio_sample.h in Headers */ = {isa = PBXBuildFile; fileRef = B1D8A0CF198FB83D00A23435 /* audio_sample.h */; }; + B1DD3606198A702E00EF7043 /* alloc.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35C3198A702E00EF7043 /* alloc.h */; }; + B1DD3607198A702E00EF7043 /* array.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35C4198A702E00EF7043 /* array.h */; }; + B1DD3608198A702E00EF7043 /* avltree.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35C5198A702E00EF7043 /* avltree.h */; }; + B1DD3609198A702E00EF7043 /* base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35C6198A702E00EF7043 /* base64.cpp */; }; + B1DD360A198A702E00EF7043 /* base64.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35C7198A702E00EF7043 /* base64.h */; }; + B1DD360B198A702E00EF7043 /* binary_search.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35C8198A702E00EF7043 /* binary_search.h */; }; + B1DD360C198A702E00EF7043 /* bit_array_impl_part2.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35C9198A702E00EF7043 /* bit_array_impl_part2.h */; }; + B1DD360D198A702E00EF7043 /* bit_array_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35CA198A702E00EF7043 /* bit_array_impl.h */; }; + B1DD360E198A702E00EF7043 /* bit_array.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35CB198A702E00EF7043 /* bit_array.cpp */; }; + B1DD360F198A702E00EF7043 /* bit_array.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35CC198A702E00EF7043 /* bit_array.h */; }; + B1DD3610198A702E00EF7043 /* bsearch_inline.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35CD198A702E00EF7043 /* bsearch_inline.h */; }; + B1DD3611198A702E00EF7043 /* bsearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35CE198A702E00EF7043 /* bsearch.cpp */; }; + B1DD3612198A702E00EF7043 /* bsearch.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35CF198A702E00EF7043 /* bsearch.h */; }; + B1DD3613198A702E00EF7043 /* byte_order.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35D0198A702E00EF7043 /* byte_order.h */; }; + B1DD3614198A702E00EF7043 /* chain_list_v2.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35D1198A702E00EF7043 /* chain_list_v2.h */; }; + B1DD3615198A702E00EF7043 /* com_ptr_t.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35D2198A702E00EF7043 /* com_ptr_t.h */; }; + B1DD3616198A702E00EF7043 /* cpuid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35D3198A702E00EF7043 /* cpuid.cpp */; }; + B1DD3617198A702E00EF7043 /* cpuid.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35D4198A702E00EF7043 /* cpuid.h */; }; + B1DD3618198A702E00EF7043 /* event.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35D5198A702E00EF7043 /* event.h */; }; + B1DD3619198A702E00EF7043 /* guid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35D6198A702E00EF7043 /* guid.cpp */; }; + B1DD361A198A702E00EF7043 /* guid.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35D7198A702E00EF7043 /* guid.h */; }; + B1DD361C198A702E00EF7043 /* int_types.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35D9198A702E00EF7043 /* int_types.h */; }; + B1DD361D198A702E00EF7043 /* iterators.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35DA198A702E00EF7043 /* iterators.h */; }; + B1DD361E198A702E00EF7043 /* list.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35DB198A702E00EF7043 /* list.h */; }; + B1DD361F198A702E00EF7043 /* map.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35DC198A702E00EF7043 /* map.h */; }; + B1DD3621198A702E00EF7043 /* memalign.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35DE198A702E00EF7043 /* memalign.h */; }; + B1DD3622198A702E00EF7043 /* nix-objects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35DF198A702E00EF7043 /* nix-objects.cpp */; }; + B1DD3623198A702E00EF7043 /* nix-objects.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35E0198A702E00EF7043 /* nix-objects.h */; }; + B1DD3624198A702E00EF7043 /* obj-c.mm in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35E1198A702E00EF7043 /* obj-c.mm */; }; + B1DD3625198A702E00EF7043 /* order_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35E2198A702E00EF7043 /* order_helper.h */; }; + B1DD3626198A702E00EF7043 /* other.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35E3198A702E00EF7043 /* other.cpp */; }; + B1DD3627198A702E00EF7043 /* other.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35E4198A702E00EF7043 /* other.h */; }; + B1DD3628198A702E00EF7043 /* pathUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35E5198A702E00EF7043 /* pathUtils.cpp */; }; + B1DD3629198A702E00EF7043 /* pathUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35E6198A702E00EF7043 /* pathUtils.h */; }; + B1DD362A198A702E00EF7043 /* pfc.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35E7198A702E00EF7043 /* pfc.h */; }; + B1DD362B198A702E00EF7043 /* primitives_part2.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35E8198A702E00EF7043 /* primitives_part2.h */; }; + B1DD362C198A702E00EF7043 /* primitives.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35E9198A702E00EF7043 /* primitives.h */; }; + B1DD362D198A702E00EF7043 /* printf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35EA198A702E00EF7043 /* printf.cpp */; }; + B1DD362E198A702E00EF7043 /* timers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35EB198A702E00EF7043 /* timers.cpp */; }; + B1DD362F198A702E00EF7043 /* timers.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35EC198A702E00EF7043 /* timers.h */; }; + B1DD3630198A702E00EF7043 /* ptr_list.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35ED198A702E00EF7043 /* ptr_list.h */; }; + B1DD3631198A702E00EF7043 /* rcptr.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35EE198A702E00EF7043 /* rcptr.h */; }; + B1DD3632198A702E00EF7043 /* ref_counter.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35EF198A702E00EF7043 /* ref_counter.h */; }; + B1DD3633198A702E00EF7043 /* selftest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35F0198A702E00EF7043 /* selftest.cpp */; }; + B1DD3634198A702E00EF7043 /* sort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35F1198A702E00EF7043 /* sort.cpp */; }; + B1DD3635198A702E00EF7043 /* sort.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35F2198A702E00EF7043 /* sort.h */; }; + B1DD3636198A702E00EF7043 /* stdafx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35F3198A702E00EF7043 /* stdafx.cpp */; }; + B1DD3637198A702E00EF7043 /* string_conv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35F4198A702E00EF7043 /* string_conv.cpp */; }; + B1DD3638198A702E00EF7043 /* string_conv.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35F5198A702E00EF7043 /* string_conv.h */; }; + B1DD3639198A702E00EF7043 /* string_list.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35F6198A702E00EF7043 /* string_list.h */; }; + B1DD363A198A702E00EF7043 /* string_base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35F7198A702E00EF7043 /* string_base.cpp */; }; + B1DD363B198A702E00EF7043 /* string_base.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35F8198A702E00EF7043 /* string_base.h */; }; + B1DD363F198A702E00EF7043 /* syncd_storage.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35FC198A702E00EF7043 /* syncd_storage.h */; }; + B1DD3640198A702E00EF7043 /* synchro_nix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD35FD198A702E00EF7043 /* synchro_nix.cpp */; }; + B1DD3641198A702E00EF7043 /* synchro_nix.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35FE198A702E00EF7043 /* synchro_nix.h */; }; + B1DD3642198A702E00EF7043 /* synchro_win.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD35FF198A702E00EF7043 /* synchro_win.h */; }; + B1DD3643198A702E00EF7043 /* threads.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD3600198A702E00EF7043 /* threads.cpp */; }; + B1DD3644198A702E00EF7043 /* threads.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD3601198A702E00EF7043 /* threads.h */; }; + B1DD3645198A702E00EF7043 /* traits.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD3602198A702E00EF7043 /* traits.h */; }; + B1DD3646198A702E00EF7043 /* utf8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD3603198A702E00EF7043 /* utf8.cpp */; }; + B1DD3647198A702E00EF7043 /* win-objects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1DD3604198A702E00EF7043 /* win-objects.cpp */; }; + B1DD3648198A702E00EF7043 /* win-objects.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DD3605198A702E00EF7043 /* win-objects.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + B16695F219ACC12A0001728F /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0F0794CF27C90AA4006BAD7F /* fixed_map.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fixed_map.h; sourceTree = ""; }; + 0F0794D027C90AA4006BAD7F /* charDownConvert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = charDownConvert.h; sourceTree = ""; }; + 0F0794D127C90AA4006BAD7F /* SmartStrStr-table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SmartStrStr-table.h"; sourceTree = ""; }; + 0F0794D227C90AA4006BAD7F /* charDownConvert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = charDownConvert.cpp; sourceTree = ""; }; + 0F0794D327C90AA4006BAD7F /* SmartStrStr-twoCharMappings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SmartStrStr-twoCharMappings.h"; sourceTree = ""; }; + 0F14904C242E44C300D0BD81 /* notifyList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = notifyList.h; sourceTree = ""; }; + 0F14904E242E44ED00D0BD81 /* autoref.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = autoref.h; sourceTree = ""; }; + 0F149050242E454C00D0BD81 /* weakRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = weakRef.h; sourceTree = ""; }; + 0F17B3DA2B5E8F0E00FC86C1 /* event_std.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = event_std.h; sourceTree = ""; }; + 0F2201292B0CCEA60074C1FC /* sortstring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sortstring.h; sourceTree = ""; }; + 0F22012A2B0CCEA60074C1FC /* string_simple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_simple.h; sourceTree = ""; }; + 0F22012B2B0CCEA60074C1FC /* sort2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sort2.h; sourceTree = ""; }; + 0F2201312B0CCF3D0074C1FC /* CFObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFObject.h; sourceTree = ""; }; + 0F2F4BEF250BFF660014812D /* pfc-fb2k-hooks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "pfc-fb2k-hooks.h"; sourceTree = ""; }; + 0F2F4BF0250BFF660014812D /* killswitch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = killswitch.h; sourceTree = ""; }; + 0F2F4BF1250BFF660014812D /* pfc-fb2k-hooks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "pfc-fb2k-hooks.cpp"; sourceTree = ""; }; + 0F2F4BF2250BFF660014812D /* stdsort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdsort.h; sourceTree = ""; }; + 0F2F4BF3250BFF660014812D /* suppress_fb2k_hooks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = suppress_fb2k_hooks.h; sourceTree = ""; }; + 0F2F4BF4250BFF660014812D /* platform-objects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "platform-objects.h"; sourceTree = ""; }; + 0F2F4BF5250BFF660014812D /* pocket_char_ops.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pocket_char_ops.h; sourceTree = ""; }; + 0F2F4BF6250BFF660014812D /* cmd_thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_thread.h; sourceTree = ""; }; + 0F64350D253A250600D6335A /* string-conv-lite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "string-conv-lite.h"; sourceTree = ""; }; + 0F64350E253A250600D6335A /* pp-winapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "pp-winapi.h"; sourceTree = ""; }; + 0F64350F253A250600D6335A /* string-conv-lite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "string-conv-lite.cpp"; sourceTree = ""; }; + 0F65005125122FD5001B03BA /* string-compare.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "string-compare.cpp"; sourceTree = ""; }; + 0F65005225122FD5001B03BA /* string-compare.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "string-compare.h"; sourceTree = ""; }; + 0F65005325122FD5001B03BA /* string-part.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "string-part.h"; sourceTree = ""; }; + 0F65005425122FD5001B03BA /* string-lite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "string-lite.cpp"; sourceTree = ""; }; + 0F7A1B682A692C88004F89FB /* filetimetools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filetimetools.cpp; sourceTree = ""; }; + 0F7A1B692A692C88004F89FB /* filetimetools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filetimetools.h; sourceTree = ""; }; + 0F7EDDA827FAFDA5000996AA /* unicode-normalize.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "unicode-normalize.cpp"; sourceTree = ""; }; + 0F7EDDA927FAFDA5000996AA /* unicode-normalize.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "unicode-normalize.h"; sourceTree = ""; }; + 0FAC031527C8EC6500BA9E97 /* SmartStrStr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SmartStrStr.cpp; sourceTree = ""; }; + 0FAC031627C8EC6500BA9E97 /* SmartStrStr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SmartStrStr.h; sourceTree = ""; }; + 0FB717E026C295370040D7FE /* splitString2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = splitString2.h; sourceTree = ""; }; + 0FB717E126C295370040D7FE /* splitString2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = splitString2.cpp; sourceTree = ""; }; + 0FFFAEAD23C9C9580023328B /* crashWithMessage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = crashWithMessage.cpp; sourceTree = ""; }; + B125C8AC1F46D9A900ADB97B /* once.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = once.h; sourceTree = ""; }; + B12CBBB61BD3F08800952805 /* pfc-lite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "pfc-lite.h"; sourceTree = ""; }; + B12CBBB71BD3F0D100952805 /* string-interface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "string-interface.h"; sourceTree = ""; }; + B12CBBB91BD4A01400952805 /* debug.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = debug.h; sourceTree = ""; }; + B12CBBC61BD4D96A00952805 /* bigmem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bigmem.cpp; sourceTree = ""; }; + B12CBBC71BD4D96A00952805 /* bigmem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bigmem.h; sourceTree = ""; }; + B12CBBD01BD4DD4600952805 /* ptrholder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ptrholder.h; sourceTree = ""; }; + B12CBBD11BD4DE8100952805 /* synchro.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = synchro.h; sourceTree = ""; }; + B16695F419ACC12A0001728F /* libpfc-iOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libpfc-iOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + B16695F619ACC12A0001728F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + B166960419ACC12A0001728F /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + B166960719ACC12A0001728F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + B17EB26B1E85358D0057E2A4 /* pool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pool.h; sourceTree = ""; }; + B17EB26C1E85358D0057E2A4 /* splitString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = splitString.h; sourceTree = ""; }; + B17EB26D1E85358D0057E2A4 /* wait_queue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wait_queue.h; sourceTree = ""; }; + B1B502D3198FF75000525EAF /* wildcard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wildcard.cpp; sourceTree = ""; }; + B1B502D4198FF75000525EAF /* wildcard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wildcard.h; sourceTree = ""; }; + B1C37D7D19922EEB00EE6ABC /* filehandle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = filehandle.h; sourceTree = ""; }; + B1C37D7E19922EF500EE6ABC /* filehandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filehandle.cpp; sourceTree = ""; }; + B1CF88B41BD6657F00F42F87 /* fpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fpu.h; sourceTree = ""; }; + B1CF88B51BD6657F00F42F87 /* mem_block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mem_block.h; sourceTree = ""; }; + B1CF88B61BD6657F00F42F87 /* string-lite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "string-lite.h"; sourceTree = ""; }; + B1D8A0CD198FB83D00A23435 /* audio_math.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audio_math.cpp; sourceTree = ""; }; + B1D8A0CE198FB83D00A23435 /* audio_sample.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audio_sample.cpp; sourceTree = ""; }; + B1D8A0CF198FB83D00A23435 /* audio_sample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audio_sample.h; sourceTree = ""; }; + B1DD35AF198A6FAA00EF7043 /* libpfc-Mac.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libpfc-Mac.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + B1DD35C3198A702E00EF7043 /* alloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = alloc.h; sourceTree = ""; }; + B1DD35C4198A702E00EF7043 /* array.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = array.h; sourceTree = ""; }; + B1DD35C5198A702E00EF7043 /* avltree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = avltree.h; sourceTree = ""; }; + B1DD35C6198A702E00EF7043 /* base64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base64.cpp; sourceTree = ""; }; + B1DD35C7198A702E00EF7043 /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = ""; }; + B1DD35C8198A702E00EF7043 /* binary_search.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = binary_search.h; sourceTree = ""; }; + B1DD35C9198A702E00EF7043 /* bit_array_impl_part2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bit_array_impl_part2.h; sourceTree = ""; }; + B1DD35CA198A702E00EF7043 /* bit_array_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bit_array_impl.h; sourceTree = ""; }; + B1DD35CB198A702E00EF7043 /* bit_array.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bit_array.cpp; sourceTree = ""; }; + B1DD35CC198A702E00EF7043 /* bit_array.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bit_array.h; sourceTree = ""; }; + B1DD35CD198A702E00EF7043 /* bsearch_inline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bsearch_inline.h; sourceTree = ""; }; + B1DD35CE198A702E00EF7043 /* bsearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bsearch.cpp; sourceTree = ""; }; + B1DD35CF198A702E00EF7043 /* bsearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bsearch.h; sourceTree = ""; }; + B1DD35D0198A702E00EF7043 /* byte_order.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = byte_order.h; sourceTree = ""; }; + B1DD35D1198A702E00EF7043 /* chain_list_v2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = chain_list_v2.h; sourceTree = ""; }; + B1DD35D2198A702E00EF7043 /* com_ptr_t.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = com_ptr_t.h; sourceTree = ""; }; + B1DD35D3198A702E00EF7043 /* cpuid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cpuid.cpp; sourceTree = ""; }; + B1DD35D4198A702E00EF7043 /* cpuid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpuid.h; sourceTree = ""; }; + B1DD35D5198A702E00EF7043 /* event.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = event.h; sourceTree = ""; }; + B1DD35D6198A702E00EF7043 /* guid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = guid.cpp; sourceTree = ""; }; + B1DD35D7198A702E00EF7043 /* guid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = guid.h; sourceTree = ""; }; + B1DD35D9198A702E00EF7043 /* int_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = int_types.h; sourceTree = ""; }; + B1DD35DA198A702E00EF7043 /* iterators.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iterators.h; sourceTree = ""; }; + B1DD35DB198A702E00EF7043 /* list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = list.h; sourceTree = ""; }; + B1DD35DC198A702E00EF7043 /* map.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = map.h; sourceTree = ""; }; + B1DD35DE198A702E00EF7043 /* memalign.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memalign.h; sourceTree = ""; }; + B1DD35DF198A702E00EF7043 /* nix-objects.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "nix-objects.cpp"; sourceTree = ""; }; + B1DD35E0198A702E00EF7043 /* nix-objects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "nix-objects.h"; sourceTree = ""; }; + B1DD35E1198A702E00EF7043 /* obj-c.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "obj-c.mm"; sourceTree = ""; }; + B1DD35E2198A702E00EF7043 /* order_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = order_helper.h; sourceTree = ""; }; + B1DD35E3198A702E00EF7043 /* other.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = other.cpp; sourceTree = ""; }; + B1DD35E4198A702E00EF7043 /* other.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = other.h; sourceTree = ""; }; + B1DD35E5198A702E00EF7043 /* pathUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pathUtils.cpp; sourceTree = ""; }; + B1DD35E6198A702E00EF7043 /* pathUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pathUtils.h; sourceTree = ""; }; + B1DD35E7198A702E00EF7043 /* pfc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pfc.h; sourceTree = ""; }; + B1DD35E8198A702E00EF7043 /* primitives_part2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = primitives_part2.h; sourceTree = ""; }; + B1DD35E9198A702E00EF7043 /* primitives.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = primitives.h; sourceTree = ""; }; + B1DD35EA198A702E00EF7043 /* printf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = printf.cpp; sourceTree = ""; }; + B1DD35EB198A702E00EF7043 /* timers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = timers.cpp; sourceTree = ""; }; + B1DD35EC198A702E00EF7043 /* timers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = timers.h; sourceTree = ""; }; + B1DD35ED198A702E00EF7043 /* ptr_list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ptr_list.h; sourceTree = ""; }; + B1DD35EE198A702E00EF7043 /* rcptr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rcptr.h; sourceTree = ""; }; + B1DD35EF198A702E00EF7043 /* ref_counter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ref_counter.h; sourceTree = ""; }; + B1DD35F0198A702E00EF7043 /* selftest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = selftest.cpp; sourceTree = ""; }; + B1DD35F1198A702E00EF7043 /* sort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sort.cpp; sourceTree = ""; }; + B1DD35F2198A702E00EF7043 /* sort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sort.h; sourceTree = ""; }; + B1DD35F3198A702E00EF7043 /* stdafx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stdafx.cpp; sourceTree = ""; }; + B1DD35F4198A702E00EF7043 /* string_conv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_conv.cpp; sourceTree = ""; }; + B1DD35F5198A702E00EF7043 /* string_conv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_conv.h; sourceTree = ""; }; + B1DD35F6198A702E00EF7043 /* string_list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_list.h; sourceTree = ""; }; + B1DD35F7198A702E00EF7043 /* string_base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_base.cpp; sourceTree = ""; }; + B1DD35F8198A702E00EF7043 /* string_base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_base.h; sourceTree = ""; }; + B1DD35FC198A702E00EF7043 /* syncd_storage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = syncd_storage.h; sourceTree = ""; }; + B1DD35FD198A702E00EF7043 /* synchro_nix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = synchro_nix.cpp; sourceTree = ""; }; + B1DD35FE198A702E00EF7043 /* synchro_nix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = synchro_nix.h; sourceTree = ""; }; + B1DD35FF198A702E00EF7043 /* synchro_win.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = synchro_win.h; sourceTree = ""; }; + B1DD3600198A702E00EF7043 /* threads.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = threads.cpp; sourceTree = ""; }; + B1DD3601198A702E00EF7043 /* threads.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = threads.h; sourceTree = ""; }; + B1DD3602198A702E00EF7043 /* traits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = traits.h; sourceTree = ""; }; + B1DD3603198A702E00EF7043 /* utf8.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = utf8.cpp; sourceTree = ""; }; + B1DD3604198A702E00EF7043 /* win-objects.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "win-objects.cpp"; sourceTree = ""; }; + B1DD3605198A702E00EF7043 /* win-objects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "win-objects.h"; sourceTree = ""; }; + B1EFBAC51B90658600C2CE84 /* lockless.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = lockless.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B16695F119ACC12A0001728F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B16695F719ACC12A0001728F /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B1DD35AC198A6FAA00EF7043 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B16695F519ACC12A0001728F /* Frameworks */ = { + isa = PBXGroup; + children = ( + B16695F619ACC12A0001728F /* Foundation.framework */, + B166960419ACC12A0001728F /* XCTest.framework */, + B166960719ACC12A0001728F /* UIKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + B1DD35A6198A6FAA00EF7043 = { + isa = PBXGroup; + children = ( + B1DD35C2198A701100EF7043 /* Source */, + B16695F519ACC12A0001728F /* Frameworks */, + B1DD35B0198A6FAA00EF7043 /* Products */, + ); + sourceTree = ""; + }; + B1DD35B0198A6FAA00EF7043 /* Products */ = { + isa = PBXGroup; + children = ( + B1DD35AF198A6FAA00EF7043 /* libpfc-Mac.a */, + B16695F419ACC12A0001728F /* libpfc-iOS.a */, + ); + name = Products; + sourceTree = ""; + }; + B1DD35C2198A701100EF7043 /* Source */ = { + isa = PBXGroup; + children = ( + 0F17B3DA2B5E8F0E00FC86C1 /* event_std.h */, + 0F2201312B0CCF3D0074C1FC /* CFObject.h */, + 0F22012B2B0CCEA60074C1FC /* sort2.h */, + 0F2201292B0CCEA60074C1FC /* sortstring.h */, + 0F22012A2B0CCEA60074C1FC /* string_simple.h */, + 0F7A1B682A692C88004F89FB /* filetimetools.cpp */, + 0F7A1B692A692C88004F89FB /* filetimetools.h */, + 0F0794D227C90AA4006BAD7F /* charDownConvert.cpp */, + 0F0794D027C90AA4006BAD7F /* charDownConvert.h */, + 0F0794CF27C90AA4006BAD7F /* fixed_map.h */, + 0F0794D127C90AA4006BAD7F /* SmartStrStr-table.h */, + 0F0794D327C90AA4006BAD7F /* SmartStrStr-twoCharMappings.h */, + 0FAC031527C8EC6500BA9E97 /* SmartStrStr.cpp */, + 0FAC031627C8EC6500BA9E97 /* SmartStrStr.h */, + B1DD35C3198A702E00EF7043 /* alloc.h */, + B1DD35C4198A702E00EF7043 /* array.h */, + B1D8A0CD198FB83D00A23435 /* audio_math.cpp */, + B1D8A0CE198FB83D00A23435 /* audio_sample.cpp */, + B1D8A0CF198FB83D00A23435 /* audio_sample.h */, + 0F14904E242E44ED00D0BD81 /* autoref.h */, + B1DD35C5198A702E00EF7043 /* avltree.h */, + B1DD35C6198A702E00EF7043 /* base64.cpp */, + B1DD35C7198A702E00EF7043 /* base64.h */, + B12CBBC61BD4D96A00952805 /* bigmem.cpp */, + B12CBBC71BD4D96A00952805 /* bigmem.h */, + B1DD35C8198A702E00EF7043 /* binary_search.h */, + B1DD35C9198A702E00EF7043 /* bit_array_impl_part2.h */, + B1DD35CA198A702E00EF7043 /* bit_array_impl.h */, + B1DD35CB198A702E00EF7043 /* bit_array.cpp */, + B1DD35CC198A702E00EF7043 /* bit_array.h */, + B1DD35CD198A702E00EF7043 /* bsearch_inline.h */, + B1DD35CE198A702E00EF7043 /* bsearch.cpp */, + B1DD35CF198A702E00EF7043 /* bsearch.h */, + B1DD35D0198A702E00EF7043 /* byte_order.h */, + B1DD35D1198A702E00EF7043 /* chain_list_v2.h */, + 0F2F4BF6250BFF660014812D /* cmd_thread.h */, + B1DD35D2198A702E00EF7043 /* com_ptr_t.h */, + B1DD35D3198A702E00EF7043 /* cpuid.cpp */, + B1DD35D4198A702E00EF7043 /* cpuid.h */, + 0FFFAEAD23C9C9580023328B /* crashWithMessage.cpp */, + B12CBBB91BD4A01400952805 /* debug.h */, + B1DD35D5198A702E00EF7043 /* event.h */, + B1C37D7E19922EF500EE6ABC /* filehandle.cpp */, + B1C37D7D19922EEB00EE6ABC /* filehandle.h */, + B1CF88B41BD6657F00F42F87 /* fpu.h */, + B1DD35D6198A702E00EF7043 /* guid.cpp */, + B1DD35D7198A702E00EF7043 /* guid.h */, + B1DD35D9198A702E00EF7043 /* int_types.h */, + B1DD35DA198A702E00EF7043 /* iterators.h */, + 0F2F4BF0250BFF660014812D /* killswitch.h */, + B1DD35DB198A702E00EF7043 /* list.h */, + B1EFBAC51B90658600C2CE84 /* lockless.h */, + B1DD35DC198A702E00EF7043 /* map.h */, + B1CF88B51BD6657F00F42F87 /* mem_block.h */, + B1DD35DE198A702E00EF7043 /* memalign.h */, + B1DD35DF198A702E00EF7043 /* nix-objects.cpp */, + B1DD35E0198A702E00EF7043 /* nix-objects.h */, + 0F14904C242E44C300D0BD81 /* notifyList.h */, + B1DD35E1198A702E00EF7043 /* obj-c.mm */, + B125C8AC1F46D9A900ADB97B /* once.h */, + B1DD35E2198A702E00EF7043 /* order_helper.h */, + B1DD35E3198A702E00EF7043 /* other.cpp */, + B1DD35E4198A702E00EF7043 /* other.h */, + B1DD35E5198A702E00EF7043 /* pathUtils.cpp */, + B1DD35E6198A702E00EF7043 /* pathUtils.h */, + 0F2F4BF1250BFF660014812D /* pfc-fb2k-hooks.cpp */, + 0F2F4BEF250BFF660014812D /* pfc-fb2k-hooks.h */, + B12CBBB61BD3F08800952805 /* pfc-lite.h */, + B1DD35E7198A702E00EF7043 /* pfc.h */, + 0F2F4BF4250BFF660014812D /* platform-objects.h */, + 0F2F4BF5250BFF660014812D /* pocket_char_ops.h */, + B17EB26B1E85358D0057E2A4 /* pool.h */, + 0F64350E253A250600D6335A /* pp-winapi.h */, + B1DD35E8198A702E00EF7043 /* primitives_part2.h */, + B1DD35E9198A702E00EF7043 /* primitives.h */, + B1DD35EA198A702E00EF7043 /* printf.cpp */, + B1DD35ED198A702E00EF7043 /* ptr_list.h */, + B12CBBD01BD4DD4600952805 /* ptrholder.h */, + B1DD35EE198A702E00EF7043 /* rcptr.h */, + B1DD35EF198A702E00EF7043 /* ref_counter.h */, + B1DD35F0198A702E00EF7043 /* selftest.cpp */, + B1DD35F1198A702E00EF7043 /* sort.cpp */, + B1DD35F2198A702E00EF7043 /* sort.h */, + B17EB26C1E85358D0057E2A4 /* splitString.h */, + 0FB717E126C295370040D7FE /* splitString2.cpp */, + 0FB717E026C295370040D7FE /* splitString2.h */, + B1DD35F3198A702E00EF7043 /* stdafx.cpp */, + 0F2F4BF2250BFF660014812D /* stdsort.h */, + B1DD35F7198A702E00EF7043 /* string_base.cpp */, + B1DD35F8198A702E00EF7043 /* string_base.h */, + B1DD35F4198A702E00EF7043 /* string_conv.cpp */, + B1DD35F5198A702E00EF7043 /* string_conv.h */, + B1DD35F6198A702E00EF7043 /* string_list.h */, + 0F65005125122FD5001B03BA /* string-compare.cpp */, + 0F65005225122FD5001B03BA /* string-compare.h */, + 0F64350F253A250600D6335A /* string-conv-lite.cpp */, + 0F64350D253A250600D6335A /* string-conv-lite.h */, + B12CBBB71BD3F0D100952805 /* string-interface.h */, + 0F65005425122FD5001B03BA /* string-lite.cpp */, + B1CF88B61BD6657F00F42F87 /* string-lite.h */, + 0F65005325122FD5001B03BA /* string-part.h */, + 0F2F4BF3250BFF660014812D /* suppress_fb2k_hooks.h */, + B1DD35FC198A702E00EF7043 /* syncd_storage.h */, + B1DD35FD198A702E00EF7043 /* synchro_nix.cpp */, + B1DD35FE198A702E00EF7043 /* synchro_nix.h */, + B1DD35FF198A702E00EF7043 /* synchro_win.h */, + B12CBBD11BD4DE8100952805 /* synchro.h */, + B1DD3600198A702E00EF7043 /* threads.cpp */, + B1DD3601198A702E00EF7043 /* threads.h */, + B1DD35EB198A702E00EF7043 /* timers.cpp */, + B1DD35EC198A702E00EF7043 /* timers.h */, + B1DD3602198A702E00EF7043 /* traits.h */, + B1DD3603198A702E00EF7043 /* utf8.cpp */, + B17EB26D1E85358D0057E2A4 /* wait_queue.h */, + 0F149050242E454C00D0BD81 /* weakRef.h */, + B1B502D3198FF75000525EAF /* wildcard.cpp */, + B1B502D4198FF75000525EAF /* wildcard.h */, + B1DD3604198A702E00EF7043 /* win-objects.cpp */, + B1DD3605198A702E00EF7043 /* win-objects.h */, + 0F7EDDA827FAFDA5000996AA /* unicode-normalize.cpp */, + 0F7EDDA927FAFDA5000996AA /* unicode-normalize.h */, + ); + name = Source; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + B1DD35AD198A6FAA00EF7043 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + B1DD361E198A702E00EF7043 /* list.h in Headers */, + 0F2F4BF7250BFF660014812D /* pfc-fb2k-hooks.h in Headers */, + B12CBBCA1BD4D96A00952805 /* bigmem.h in Headers */, + B1DD3629198A702E00EF7043 /* pathUtils.h in Headers */, + B1DD3615198A702E00EF7043 /* com_ptr_t.h in Headers */, + B1DD360D198A702E00EF7043 /* bit_array_impl.h in Headers */, + 0F7EDDAC27FAFDA5000996AA /* unicode-normalize.h in Headers */, + B1DD3617198A702E00EF7043 /* cpuid.h in Headers */, + 0F643510253A250600D6335A /* string-conv-lite.h in Headers */, + B1DD3635198A702E00EF7043 /* sort.h in Headers */, + B1DD3648198A702E00EF7043 /* win-objects.h in Headers */, + B1DD3607198A702E00EF7043 /* array.h in Headers */, + 0F2201322B0CCF3D0074C1FC /* CFObject.h in Headers */, + B1DD360B198A702E00EF7043 /* binary_search.h in Headers */, + 0F22012D2B0CCEA60074C1FC /* string_simple.h in Headers */, + B1DD3621198A702E00EF7043 /* memalign.h in Headers */, + B1B502D6198FF75000525EAF /* wildcard.h in Headers */, + B1DD361A198A702E00EF7043 /* guid.h in Headers */, + B1DD362F198A702E00EF7043 /* timers.h in Headers */, + 0F0794D827C90AA4006BAD7F /* SmartStrStr-twoCharMappings.h in Headers */, + B1DD3638198A702E00EF7043 /* string_conv.h in Headers */, + B1DD3610198A702E00EF7043 /* bsearch_inline.h in Headers */, + 0F65005725122FD5001B03BA /* string-compare.h in Headers */, + 0F14904F242E44ED00D0BD81 /* autoref.h in Headers */, + 0F0794D527C90AA4006BAD7F /* charDownConvert.h in Headers */, + 0FAC031827C8EC6500BA9E97 /* SmartStrStr.h in Headers */, + B1DD3641198A702E00EF7043 /* synchro_nix.h in Headers */, + B1CF88B91BD6657F00F42F87 /* string-lite.h in Headers */, + B1DD360C198A702E00EF7043 /* bit_array_impl_part2.h in Headers */, + B1DD3645198A702E00EF7043 /* traits.h in Headers */, + 0F2F4BFB250BFF660014812D /* stdsort.h in Headers */, + B1DD361F198A702E00EF7043 /* map.h in Headers */, + 0F2F4BF8250BFF660014812D /* killswitch.h in Headers */, + B1DD3612198A702E00EF7043 /* bsearch.h in Headers */, + 0F2F4BFE250BFF660014812D /* pocket_char_ops.h in Headers */, + 0F149051242E454C00D0BD81 /* weakRef.h in Headers */, + B1CF88B81BD6657F00F42F87 /* mem_block.h in Headers */, + B1DD3614198A702E00EF7043 /* chain_list_v2.h in Headers */, + 0F0794D627C90AA4006BAD7F /* SmartStrStr-table.h in Headers */, + B1DD3627198A702E00EF7043 /* other.h in Headers */, + 0F2F4BFC250BFF660014812D /* suppress_fb2k_hooks.h in Headers */, + B17EB26E1E85358D0057E2A4 /* pool.h in Headers */, + 0F22012E2B0CCEA60074C1FC /* sort2.h in Headers */, + B17EB26F1E85358D0057E2A4 /* splitString.h in Headers */, + 0F2F4BFF250BFF660014812D /* cmd_thread.h in Headers */, + B1DD362B198A702E00EF7043 /* primitives_part2.h in Headers */, + 0F2F4BFD250BFF660014812D /* platform-objects.h in Headers */, + B1DD3632198A702E00EF7043 /* ref_counter.h in Headers */, + 0F22012C2B0CCEA60074C1FC /* sortstring.h in Headers */, + B1DD3630198A702E00EF7043 /* ptr_list.h in Headers */, + B17EB2701E85358D0057E2A4 /* wait_queue.h in Headers */, + B1DD362C198A702E00EF7043 /* primitives.h in Headers */, + B1DD3639198A702E00EF7043 /* string_list.h in Headers */, + B1DD361C198A702E00EF7043 /* int_types.h in Headers */, + B1DD3618198A702E00EF7043 /* event.h in Headers */, + 0F7A1B6C2A692C88004F89FB /* filetimetools.h in Headers */, + B1DD3608198A702E00EF7043 /* avltree.h in Headers */, + B1DD3606198A702E00EF7043 /* alloc.h in Headers */, + 0F14904D242E44C300D0BD81 /* notifyList.h in Headers */, + B1DD3644198A702E00EF7043 /* threads.h in Headers */, + 0F65005825122FD5001B03BA /* string-part.h in Headers */, + B1DD3623198A702E00EF7043 /* nix-objects.h in Headers */, + B1DD363F198A702E00EF7043 /* syncd_storage.h in Headers */, + B1DD362A198A702E00EF7043 /* pfc.h in Headers */, + B1DD3613198A702E00EF7043 /* byte_order.h in Headers */, + B1DD3631198A702E00EF7043 /* rcptr.h in Headers */, + 0F0794D427C90AA4006BAD7F /* fixed_map.h in Headers */, + B1DD3642198A702E00EF7043 /* synchro_win.h in Headers */, + 0F643511253A250600D6335A /* pp-winapi.h in Headers */, + B1DD360F198A702E00EF7043 /* bit_array.h in Headers */, + B1DD360A198A702E00EF7043 /* base64.h in Headers */, + B1D8A0D2198FB83D00A23435 /* audio_sample.h in Headers */, + B125C8AD1F46D9A900ADB97B /* once.h in Headers */, + B1DD363B198A702E00EF7043 /* string_base.h in Headers */, + B1DD361D198A702E00EF7043 /* iterators.h in Headers */, + 0FB717E226C295370040D7FE /* splitString2.h in Headers */, + B1DD3625198A702E00EF7043 /* order_helper.h in Headers */, + B1CF88B71BD6657F00F42F87 /* fpu.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + B16695F319ACC12A0001728F /* pfc-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = B166961819ACC12A0001728F /* Build configuration list for PBXNativeTarget "pfc-iOS" */; + buildPhases = ( + B16695F019ACC12A0001728F /* Sources */, + B16695F119ACC12A0001728F /* Frameworks */, + B16695F219ACC12A0001728F /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "pfc-iOS"; + productName = "pfc-iOS"; + productReference = B16695F419ACC12A0001728F /* libpfc-iOS.a */; + productType = "com.apple.product-type.library.static"; + }; + B1DD35AE198A6FAA00EF7043 /* pfc-Mac */ = { + isa = PBXNativeTarget; + buildConfigurationList = B1DD35B3198A6FAA00EF7043 /* Build configuration list for PBXNativeTarget "pfc-Mac" */; + buildPhases = ( + B1DD35AB198A6FAA00EF7043 /* Sources */, + B1DD35AC198A6FAA00EF7043 /* Frameworks */, + B1DD35AD198A6FAA00EF7043 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "pfc-Mac"; + productName = pfc; + productReference = B1DD35AF198A6FAA00EF7043 /* libpfc-Mac.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B1DD35A7198A6FAA00EF7043 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1320; + ORGANIZATIONNAME = "___FULLUSERNAME___"; + }; + buildConfigurationList = B1DD35AA198A6FAA00EF7043 /* Build configuration list for PBXProject "pfc" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = B1DD35A6198A6FAA00EF7043; + productRefGroup = B1DD35B0198A6FAA00EF7043 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B1DD35AE198A6FAA00EF7043 /* pfc-Mac */, + B16695F319ACC12A0001728F /* pfc-iOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + B16695F019ACC12A0001728F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B10D405C19ADFADB004D2596 /* audio_sample.cpp in Sources */, + B10D406A19ADFADB004D2596 /* string_base.cpp in Sources */, + 0F643513253A250600D6335A /* string-conv-lite.cpp in Sources */, + B10D405B19ADFADB004D2596 /* audio_math.cpp in Sources */, + B10D406E19ADFADB004D2596 /* threads.cpp in Sources */, + B10D406619ADFADB004D2596 /* printf.cpp in Sources */, + 0F54079726C2964500A118C8 /* splitString2.cpp in Sources */, + 0FFFAEAF23C9DB640023328B /* crashWithMessage.cpp in Sources */, + 0F65005A25122FD5001B03BA /* string-lite.cpp in Sources */, + B10D406419ADFADB004D2596 /* other.cpp in Sources */, + B10D406519ADFADB004D2596 /* pathUtils.cpp in Sources */, + B10D406319ADFADB004D2596 /* nix-objects.cpp in Sources */, + B10D406219ADFADB004D2596 /* guid.cpp in Sources */, + B10D405F19ADFADB004D2596 /* bsearch.cpp in Sources */, + B10D406719ADFADB004D2596 /* selftest.cpp in Sources */, + 0F7A1B6B2A692C88004F89FB /* filetimetools.cpp in Sources */, + B10D406919ADFADB004D2596 /* stdafx.cpp in Sources */, + B10D406819ADFADB004D2596 /* sort.cpp in Sources */, + 0F65005625122FD5001B03BA /* string-compare.cpp in Sources */, + B12CBBC91BD4D96A00952805 /* bigmem.cpp in Sources */, + B10D406D19ADFADB004D2596 /* synchro_nix.cpp in Sources */, + B10D406019ADFADB004D2596 /* cpuid.cpp in Sources */, + 0F7EDDAB27FAFDA5000996AA /* unicode-normalize.cpp in Sources */, + B10D405D19ADFADB004D2596 /* base64.cpp in Sources */, + B10D406B19ADFADB004D2596 /* string_conv.cpp in Sources */, + 0F0794D927C90AA8006BAD7F /* charDownConvert.cpp in Sources */, + B10D407019ADFADB004D2596 /* utf8.cpp in Sources */, + B10D407319ADFE52004D2596 /* obj-c.mm in Sources */, + 0FAC031927C8EC6A00BA9E97 /* SmartStrStr.cpp in Sources */, + B10D407119ADFADB004D2596 /* wildcard.cpp in Sources */, + 0F2F4BFA250BFF660014812D /* pfc-fb2k-hooks.cpp in Sources */, + B10D405E19ADFADB004D2596 /* bit_array.cpp in Sources */, + B10D407219ADFADB004D2596 /* win-objects.cpp in Sources */, + B10D406119ADFADB004D2596 /* filehandle.cpp in Sources */, + B10D406F19ADFADB004D2596 /* timers.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B1DD35AB198A6FAA00EF7043 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B1DD3647198A702E00EF7043 /* win-objects.cpp in Sources */, + B1DD3611198A702E00EF7043 /* bsearch.cpp in Sources */, + 0F643512253A250600D6335A /* string-conv-lite.cpp in Sources */, + B1DD3609198A702E00EF7043 /* base64.cpp in Sources */, + B1DD363A198A702E00EF7043 /* string_base.cpp in Sources */, + B1DD362D198A702E00EF7043 /* printf.cpp in Sources */, + 0FB717E326C295370040D7FE /* splitString2.cpp in Sources */, + 0FFFAEAE23C9C9580023328B /* crashWithMessage.cpp in Sources */, + 0F65005925122FD5001B03BA /* string-lite.cpp in Sources */, + B1D8A0D1198FB83D00A23435 /* audio_sample.cpp in Sources */, + B1DD3637198A702E00EF7043 /* string_conv.cpp in Sources */, + B1C37D7F19922EF500EE6ABC /* filehandle.cpp in Sources */, + B1DD3643198A702E00EF7043 /* threads.cpp in Sources */, + B1DD3624198A702E00EF7043 /* obj-c.mm in Sources */, + B1D8A0D0198FB83D00A23435 /* audio_math.cpp in Sources */, + 0F7A1B6A2A692C88004F89FB /* filetimetools.cpp in Sources */, + B1DD3628198A702E00EF7043 /* pathUtils.cpp in Sources */, + B1DD3646198A702E00EF7043 /* utf8.cpp in Sources */, + 0F65005525122FD5001B03BA /* string-compare.cpp in Sources */, + B1DD3636198A702E00EF7043 /* stdafx.cpp in Sources */, + B12CBBC81BD4D96A00952805 /* bigmem.cpp in Sources */, + B1DD362E198A702E00EF7043 /* timers.cpp in Sources */, + 0F7EDDAA27FAFDA5000996AA /* unicode-normalize.cpp in Sources */, + B1DD3622198A702E00EF7043 /* nix-objects.cpp in Sources */, + B1DD3640198A702E00EF7043 /* synchro_nix.cpp in Sources */, + 0F0794D727C90AA4006BAD7F /* charDownConvert.cpp in Sources */, + B1DD3633198A702E00EF7043 /* selftest.cpp in Sources */, + B1DD3626198A702E00EF7043 /* other.cpp in Sources */, + 0FAC031727C8EC6500BA9E97 /* SmartStrStr.cpp in Sources */, + B1DD360E198A702E00EF7043 /* bit_array.cpp in Sources */, + B1DD3619198A702E00EF7043 /* guid.cpp in Sources */, + 0F2F4BF9250BFF660014812D /* pfc-fb2k-hooks.cpp in Sources */, + B1DD3634198A702E00EF7043 /* sort.cpp in Sources */, + B1DD3616198A702E00EF7043 /* cpuid.cpp in Sources */, + B1B502D5198FF75000525EAF /* wildcard.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + B166961419ACC12A0001728F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DSTROOT = /tmp/pfc_iOS.dst; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + }; + name = Debug; + }; + B166961519ACC12A0001728F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DSTROOT = /tmp/pfc_iOS.dst; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + B1DD35B1198A6FAA00EF7043 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "pfc-lite.h"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MACOSX_DEPLOYMENT_TARGET = 11.0; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + B1DD35B2198A6FAA00EF7043 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "pfc-lite.h"; + GCC_PREPROCESSOR_DEFINITIONS = "NDEBUG=1"; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MACOSX_DEPLOYMENT_TARGET = 11.0; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Release; + }; + B1DD35B4198A6FAA00EF7043 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + B1DD35B5198A6FAA00EF7043 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B166961819ACC12A0001728F /* Build configuration list for PBXNativeTarget "pfc-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B166961419ACC12A0001728F /* Debug */, + B166961519ACC12A0001728F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B1DD35AA198A6FAA00EF7043 /* Build configuration list for PBXProject "pfc" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B1DD35B1198A6FAA00EF7043 /* Debug */, + B1DD35B2198A6FAA00EF7043 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B1DD35B3198A6FAA00EF7043 /* Build configuration list for PBXNativeTarget "pfc-Mac" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B1DD35B4198A6FAA00EF7043 /* Debug */, + B1DD35B5198A6FAA00EF7043 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B1DD35A7198A6FAA00EF7043 /* Project object */; +} diff --git a/sdk/pfc/pocket_char_ops.h b/sdk/pfc/pocket_char_ops.h index b877ae9..d6f491c 100644 --- a/sdk/pfc/pocket_char_ops.h +++ b/sdk/pfc/pocket_char_ops.h @@ -138,22 +138,27 @@ size_t utf8_encode_char(unsigned wide, char * target) noexcept target[5] = 0x80 | (wide & 0x3F); wide = wide >> 6; wide |= 0x4000000; + [[fallthrough]]; case 5: target[4] = 0x80 | (wide & 0x3F); wide = wide >> 6; wide |= 0x200000; + [[fallthrough]]; case 4: target[3] = 0x80 | (wide & 0x3F); wide = wide >> 6; wide |= 0x10000; + [[fallthrough]]; case 3: target[2] = 0x80 | (wide & 0x3F); wide = wide >> 6; wide |= 0x800; + [[fallthrough]]; case 2: target[1] = 0x80 | (wide & 0x3F); wide = wide >> 6; wide |= 0xC0; + [[fallthrough]]; case 1: target[0] = wide & 0xFF; } diff --git a/sdk/pfc/primitives.h b/sdk/pfc/primitives.h index 71a13f9..6cddf1b 100644 --- a/sdk/pfc/primitives.h +++ b/sdk/pfc/primitives.h @@ -117,11 +117,11 @@ namespace pfc { } template void __unsafe__in_place_destructor_t(t_type & p_item) throw() { - if (traits_t::needs_destructor) try{ p_item.~t_type(); } catch(...) {} + if constexpr (traits_t::needs_destructor) try{ p_item.~t_type(); } catch(...) {} } template void __unsafe__in_place_constructor_t(t_type & p_item) { - if (traits_t::needs_constructor) { + if constexpr (traits_t::needs_constructor) { t_type * ret = new(&p_item) t_type; PFC_ASSERT(ret == &p_item); (void) ret; // suppress warning @@ -129,14 +129,14 @@ namespace pfc { } template void __unsafe__in_place_destructor_array_t(t_type * p_items, t_size p_count) throw() { - if (traits_t::needs_destructor) { + if constexpr (traits_t::needs_destructor) { t_type * walk = p_items; for(t_size n=p_count;n;--n) __unsafe__in_place_destructor_t(*(walk++)); } } template t_type * __unsafe__in_place_constructor_array_t(t_type * p_items,t_size p_count) { - if (traits_t::needs_constructor) { + if constexpr (traits_t::needs_constructor) { t_size walkptr = 0; try { for(walkptr=0;walkptr void __unsafe__in_place_constructor_copy_t(t_type & p_item,const t_copy & p_copyfrom) { - if (traits_t::needs_constructor) { + if constexpr (traits_t::needs_constructor) { t_type * ret = new(&p_item) t_type(p_copyfrom); PFC_ASSERT(ret == &p_item); (void) ret; // suppress warning @@ -191,7 +191,7 @@ namespace pfc { template t_ret * safe_ptr_cast(t_param * p_param) { - if (pfc::is_same_type::value) return p_param; + if constexpr (pfc::is_same_type::value) return p_param; else { if (p_param == NULL) return NULL; else return p_param; @@ -282,7 +282,7 @@ namespace pfc { template inline void __unsafe__swap_raw_t(void * p_object1, void * p_object2) { - if (p_size % sizeof(t_size) == 0) { + if constexpr (p_size % sizeof(t_size) == 0) { swap_multi_t(reinterpret_cast(p_object1),reinterpret_cast(p_object2)); } else { swap_multi_t(reinterpret_cast(p_object1),reinterpret_cast(p_object2)); @@ -291,7 +291,7 @@ namespace pfc { template inline void swap_t(T & p_item1, T & p_item2) { - if (traits_t::realloc_safe) { + if constexpr (traits_t::realloc_safe) { __unsafe__swap_raw_t( reinterpret_cast( &p_item1 ), reinterpret_cast( &p_item2 ) ); } else { T temp( std::move(p_item2) ); @@ -469,22 +469,12 @@ namespace pfc { } } - - - template - inline t_size append_t(t_array & p_array,const T & p_item) - { - t_size old_count = p_array.get_size(); - p_array.set_size(old_count + 1); - p_array[old_count] = p_item; - return old_count; - } template inline t_size append_t(t_array & p_array, T && p_item) { t_size old_count = p_array.get_size(); p_array.set_size(old_count + 1); - p_array[old_count] = std::move(p_item); + p_array[old_count] = std::forward(p_item); return old_count; } @@ -723,20 +713,22 @@ namespace pfc { t_int32 rint32(double p_val); t_int64 rint64(double p_val); - - + //! Returns amount of items left. template inline size_t remove_if_t( array_t & arr, pred_t pred ) { const size_t inCount = arr.size(); size_t walk = 0; - - for( walk = 0; walk < inCount; ++ walk ) { + for (;; ) { + if ( walk == inCount ) return inCount; if ( pred(arr[walk]) ) break; + ++ walk; } size_t total = walk; + ++ walk; // already know that at walk is pred() positive + for( ; walk < inCount; ++ walk ) { if ( !pred(arr[walk] ) ) { move_t(arr[total++], arr[walk]); @@ -747,8 +739,9 @@ namespace pfc { return total; } + //! Returns amount of items left. template - inline t_size remove_mask_t(t_array & p_array,const bit_array & p_mask)//returns amount of items left + inline t_size remove_mask_t(t_array & p_array,const bit_array & p_mask) { t_size n,count = p_array.size(), total = 0; @@ -882,7 +875,7 @@ namespace pfc { template incrementScope autoIncrement(obj_t& v) { return incrementScope(v); } - inline unsigned countBits32(uint32_t i) { + constexpr inline unsigned countBits32(uint32_t i) { const uint32_t mask = 0x11111111; uint32_t acc = i & mask; acc += (i >> 1) & mask; diff --git a/sdk/pfc/selftest.cpp b/sdk/pfc/selftest.cpp index c2a7a7d..c7d4754 100644 --- a/sdk/pfc/selftest.cpp +++ b/sdk/pfc/selftest.cpp @@ -7,7 +7,7 @@ namespace { class foo {}; } -inline pfc::string_base & operator<<(pfc::string_base & p_fmt,foo p_source) {p_fmt.add_string_("FOO"); return p_fmt;} +inline pfc::string_base& operator<<(pfc::string_base& p_fmt, foo p_source) { (void)p_source; p_fmt.add_string_("FOO"); return p_fmt; } namespace { using namespace pfc; diff --git a/sdk/pfc/sort.cpp b/sdk/pfc/sort.cpp index 53299e6..56da0bd 100644 --- a/sdk/pfc/sort.cpp +++ b/sdk/pfc/sort.cpp @@ -1,5 +1,6 @@ #include "pfc-lite.h" #include "sort.h" +#include "sort2.h" #include "bit_array_impl.h" #include "ref_counter.h" @@ -267,5 +268,15 @@ void sort_stable(sort_callback & p_callback,t_size p_count) sort(cb,p_count); } + + +permutation_t make_identitiy(size_t count) { + permutation_t ret; ret.set_size_discard(count); + for (size_t walk = 0; walk < count; ++walk) { + ret[walk] = walk; + } + return ret; +} + } diff --git a/sdk/pfc/sort2.h b/sdk/pfc/sort2.h new file mode 100644 index 0000000..0ad1eb9 --- /dev/null +++ b/sdk/pfc/sort2.h @@ -0,0 +1,34 @@ +#pragma once + +#include "sort.h" + +// 2023 additions + + +namespace pfc { + + typedef array_t permutation_t; + + permutation_t make_identitiy(size_t); + + template + permutation_t sort_get_permutation(container_t const& data, compare_t compare) { + const size_t count = std::size(data); + auto ret = make_identitiy( count ); + if ( count > 0 ) sort_get_permutation_t(data, compare, count, ret.get_ptr() ); + return ret; + } + template + permutation_t sort_stable_get_permutation(container_t const& data, compare_t compare) { + const size_t count = std::size(data); + auto ret = make_identitiy( count ); + if ( count > 0 ) sort_stable_get_permutation_t(data, compare, count, ret.get_ptr() ); + return ret; + } + + template + void reorder(container_t& data, permutation_t const& order) { + PFC_ASSERT( std::size(data) == std::size(order) ); + reorder_t( data, order.get_ptr(), order.get_size() ); + } +} \ No newline at end of file diff --git a/sdk/pfc/sortstring.h b/sdk/pfc/sortstring.h new file mode 100644 index 0000000..83a939a --- /dev/null +++ b/sdk/pfc/sortstring.h @@ -0,0 +1,30 @@ +#pragma once + +#ifdef _WIN32 +#include // std::unique_ptr<> +#endif +#ifdef __APPLE__ +#include "CFObject.h" +#endif + +namespace pfc { +#ifdef _WIN32 + typedef std::unique_ptr sortString_t; + sortString_t makeSortString(const char* str); + sortString_t makeSortString(const wchar_t* str); + int sortStringCompare(sortString_t const& s1, sortString_t const& s2); + int sortStringCompareI(sortString_t const& s1, sortString_t const& s2); +#elif defined(__APPLE__) + typedef CFObject sortString_t; + sortString_t makeSortString(const char* str); + int sortStringCompare(sortString_t const& s1, sortString_t const& s2); + int sortStringCompareI(sortString_t const& s1, sortString_t const& s2); +#else +#define PFC_SORTSTRING_GENERIC + typedef pfc::string8 sortString_t; + inline sortString_t makeSortString(const char* str) { return str; } + inline sortString_t makeSortString(pfc::string8&& str) { return std::move(str); } + int sortStringCompare(const char* str1, const char* str2); + int sortStringCompareI(const char* str1, const char* str2); +#endif +} diff --git a/sdk/pfc/splitString.h b/sdk/pfc/splitString.h index c28f394..259f37e 100644 --- a/sdk/pfc/splitString.h +++ b/sdk/pfc/splitString.h @@ -57,7 +57,7 @@ namespace pfc { public: _splitStringSimple_check(char c) : m_char(c) {} t_size operator()(const char* str, t_size len) const { - PFC_ASSERT(len > 0); + PFC_ASSERT(len > 0); (void)len; if (*str == m_char) return 1; else return 0; } diff --git a/sdk/pfc/splitString2.cpp b/sdk/pfc/splitString2.cpp index 495f30b..f4c0957 100644 --- a/sdk/pfc/splitString2.cpp +++ b/sdk/pfc/splitString2.cpp @@ -7,7 +7,7 @@ namespace { class counter_t { public: size_t count = 0; - inline void operator+=(pfc::string_part_ref const& p) { ++count; } + inline void operator+=(pfc::string_part_ref const& p) { (void)p; ++count; } }; template class wrapper_t { diff --git a/sdk/pfc/string-compare.cpp b/sdk/pfc/string-compare.cpp index 0b3c0e5..453f253 100644 --- a/sdk/pfc/string-compare.cpp +++ b/sdk/pfc/string-compare.cpp @@ -3,8 +3,25 @@ #include "string-compare.h" #include "string_base.h" #include "debug.h" +#include "bsearch_inline.h" +#include "sortstring.h" namespace pfc { + unsigned charToANSI(unsigned GotChar, unsigned fallback) { + if (GotChar < 128) return GotChar; + + static constexpr uint16_t from[] = {L'\u00C0', L'\u00C1', L'\u00C2', L'\u00C3', L'\u00C4', L'\u00C5', L'\u00C7', L'\u00C8', L'\u00C9', L'\u00CA', L'\u00CB', L'\u00CC', L'\u00CD', L'\u00CE', L'\u00CF', L'\u00D1', L'\u00D2', L'\u00D3', L'\u00D4', L'\u00D5', L'\u00D6', L'\u00D8', L'\u00D9', L'\u00DA', L'\u00DB', L'\u00DC', L'\u00DD', L'\u00E0', L'\u00E1', L'\u00E2', L'\u00E3', L'\u00E4', L'\u00E5', L'\u00E7', L'\u00E8', L'\u00E9', L'\u00EA', L'\u00EB', L'\u00EC', L'\u00ED', L'\u00EE', L'\u00EF', L'\u00F0', L'\u00F1', L'\u00F2', L'\u00F3', L'\u00F4', L'\u00F5', L'\u00F6', L'\u00F8', L'\u00F9', L'\u00FA', L'\u00FB', L'\u00FC', L'\u00FD', L'\u0100', L'\u0101', L'\u0102', L'\u0103', L'\u0104', L'\u0105', L'\u0106', L'\u0107', L'\u0108', L'\u0109', L'\u010A', L'\u010B', L'\u010C', L'\u010D', L'\u010E', L'\u010F', L'\u0110', L'\u0111', L'\u0112', L'\u0113', L'\u0114', L'\u0115', L'\u0116', L'\u0117', L'\u0118', L'\u0119', L'\u011A', L'\u011B', L'\u011C', L'\u011D', L'\u011E', L'\u011F', L'\u0120', L'\u0121', L'\u0122', L'\u0123', L'\u0128', L'\u0129', L'\u012A', L'\u012B', L'\u012C', L'\u012D', L'\u012E', L'\u012F', L'\u0130', L'\u0131', L'\u0134', L'\u0135', L'\u0136', L'\u0137', L'\u0139', L'\u013A', L'\u013B', L'\u013C', L'\u013D', L'\u013E', L'\u013F', L'\u0140', L'\u0141', L'\u0142', L'\u0143', L'\u0144', L'\u0145', L'\u0146', L'\u0147', L'\u0148', L'\u0149', L'\u014A', L'\u014B', L'\u014C', L'\u014D', L'\u014E', L'\u014F', L'\u0150', L'\u0151', L'\u0154', L'\u0155', L'\u0156', L'\u0157', L'\u0158', L'\u0159', L'\u015A', L'\u015B', L'\u015C', L'\u015D', L'\u015E', L'\u015F', L'\u0160', L'\u0161', L'\u0162', L'\u0163', L'\u0164', L'\u0165', L'\u0166', L'\u0167', L'\u0168', L'\u0169', L'\u016A', L'\u016B', L'\u016C', L'\u016D', L'\u016E', L'\u016F', L'\u0170', L'\u0171', L'\u0172', L'\u0173', L'\u0174', L'\u0175', L'\u0176', L'\u0177', L'\u0178', L'\u0179', L'\u017A', L'\u017B', L'\u017C', L'\u017D', L'\u017E'}; + static constexpr uint16_t to[] = {L'\u0041', L'\u0041', L'\u0041', L'\u0041', L'\u0041', L'\u0041', L'\u0043', L'\u0045', L'\u0045', L'\u0045', L'\u0045', L'\u0049', L'\u0049', L'\u0049', L'\u0049', L'\u004E', L'\u004F', L'\u004F', L'\u004F', L'\u004F', L'\u004F', L'\u004F', L'\u0055', L'\u0055', L'\u0055', L'\u0055', L'\u0059', L'\u0061', L'\u0061', L'\u0061', L'\u0061', L'\u0061', L'\u0061', L'\u0063', L'\u0065', L'\u0065', L'\u0065', L'\u0065', L'\u0069', L'\u0069', L'\u0069', L'\u0069', L'\u006F', L'\u006E', L'\u006F', L'\u006F', L'\u006F', L'\u006F', L'\u006F', L'\u006F', L'\u0075', L'\u0075', L'\u0075', L'\u0075', L'\u0079', L'\u0041', L'\u0061', L'\u0041', L'\u0061', L'\u0041', L'\u0061', L'\u0043', L'\u0063', L'\u0043', L'\u0063', L'\u0043', L'\u0063', L'\u0043', L'\u0063', L'\u0044', L'\u0064', L'\u0044', L'\u0064', L'\u0045', L'\u0065', L'\u0045', L'\u0065', L'\u0045', L'\u0065', L'\u0045', L'\u0065', L'\u0045', L'\u0065', L'\u0047', L'\u0067', L'\u0047', L'\u0067', L'\u0047', L'\u0067', L'\u0047', L'\u0067', L'\u0049', L'\u0069', L'\u0049', L'\u0069', L'\u0049', L'\u0069', L'\u0049', L'\u0069', L'\u0049', L'\u0069', L'\u004A', L'\u006A', L'\u004B', L'\u006B', L'\u004C', L'\u006C', L'\u004C', L'\u006C', L'\u004C', L'\u006C', L'\u004C', L'\u006C', L'\u004C', L'\u006C', L'\u004E', L'\u006E', L'\u004E', L'\u006E', L'\u004E', L'\u006E', L'\u006E', L'\u004E', L'\u006E', L'\u004F', L'\u006F', L'\u004F', L'\u006F', L'\u004F', L'\u006F', L'\u0052', L'\u0072', L'\u0052', L'\u0072', L'\u0052', L'\u0072', L'\u0053', L'\u0073', L'\u0053', L'\u0073', L'\u0053', L'\u0073', L'\u0053', L'\u0073', L'\u0054', L'\u0074', L'\u0054', L'\u0074', L'\u0054', L'\u0074', L'\u0055', L'\u0075', L'\u0055', L'\u0075', L'\u0055', L'\u0075', L'\u0055', L'\u0075', L'\u0055', L'\u0075', L'\u0055', L'\u0075', L'\u0057', L'\u0077', L'\u0059', L'\u0079', L'\u0059', L'\u005A', L'\u007A', L'\u005A', L'\u007A', L'\u005A', L'\u007A'}; + static_assert(std::size(from) == std::size(to)); + + size_t idx; + if (bsearch_simple_inline_t(from, std::size(from), GotChar, idx)) { + return to[idx]; + } + + return fallback; + } + int stricmp_ascii_partial(const char* str, const char* substr) throw() { size_t walk = 0; for (;;) { @@ -19,6 +36,19 @@ namespace pfc { } } + bool stringEqualsI_ascii_ex(const char* s1, size_t len1, const char* s2, size_t len2) throw() { + t_size walk1 = 0, walk2 = 0; + for (;;) { + char c1 = (walk1 < len1) ? s1[walk1] : 0; + char c2 = (walk2 < len2) ? s2[walk2] : 0; + c1 = ascii_tolower(c1); c2 = ascii_tolower(c2); + if (c1 != c2) return false; + if (c1 == 0) return true; + walk1++; + walk2++; + } + } + int stricmp_ascii_ex(const char* const s1, t_size const len1, const char* const s2, t_size const len2) throw() { t_size walk1 = 0, walk2 = 0; for (;;) { @@ -31,7 +61,6 @@ namespace pfc { walk1++; walk2++; } - } int wstricmp_ascii(const wchar_t* s1, const wchar_t* s2) throw() { @@ -87,31 +116,38 @@ namespace pfc { while (char_is_numeric(s2[l2])) ++l2; size_t l = max_t(l1, l2); - for (size_t w = 0; w < l; ++w) { - char digit1, digit2; - - t_ssize off; - - off = w + l1 - l; - if (off >= 0) { - digit1 = s1[w - l + l1]; - } else { - digit1 = 0; + for (int pass = 0; pass < 2; ++pass) { + const char filler = pass ? 'z' : '0'; + for (size_t w = 0; w < l; ++w) { + char digit1 = filler, digit2 = filler; + + t_ssize off; + + off = w + l1 - l; + if (off >= 0) { + digit1 = s1[w - l + l1]; + } + off = w + l2 - l; + if (off >= 0) { + digit2 = s2[w - l + l2]; + } + if (digit1 < digit2) return -1; + if (digit1 > digit2) return 1; } - off = w + l2 - l; - if (off >= 0) { - digit2 = s2[w - l + l2]; - } else { - digit2 = 0; - } - if (digit1 < digit2) return -1; - if (digit1 > digit2) return 1; } - s1 += l1; s2 += l2; continue; } + unsigned alt1 = charToANSI(c1, c1), alt2 = charToANSI(c2, c2); + if (alt1 != c1 || alt2 != c2) { + if (insensitive) { + alt1 = charLower(alt1); + alt2 = charLower(alt2); + } + if (alt1 < alt2) return -1; + if (alt1 > alt2) return 1; + } if (insensitive) { c1 = charLower(c1); @@ -134,9 +170,34 @@ namespace pfc { int naturalSortCompareI(const char* s1, const char* s2) throw() { return naturalSortCompareInternal(s1, s2, true); } - +#ifdef _WIN32 + int winNaturalSortCompare(const char* s1, const char* s2); + int winNaturalSortCompareI(const char* s1, const char* s2); +#endif +#ifdef __APPLE__ + int appleNaturalSortCompare(const char* s1, const char* s2); + int appleNaturalSortCompareI(const char* s1, const char* s2); +#endif + int sysNaturalSortCompare(const char* s1, const char* s2) { +#ifdef _WIN32 + return winNaturalSortCompare(s1, s2); +#elif defined(__APPLE__) + return appleNaturalSortCompare(s1, s2); +#else + return naturalSortCompare(s1, s2); +#endif + } + int sysNaturalSortCompareI(const char* s1, const char* s2) { +#ifdef _WIN32 + return winNaturalSortCompareI(s1, s2); +#elif defined(__APPLE__) + return appleNaturalSortCompareI(s1, s2); +#else + return naturalSortCompareI(s1, s2); +#endif + } const char* _stringComparatorCommon::myStringToPtr(string_part_ref) { - pfc::crash(); return nullptr; + pfc::crash(); } int stringCompareCaseInsensitiveEx(string_part_ref s1, string_part_ref s2) { @@ -172,5 +233,12 @@ namespace pfc { s1 += d1; s2 += d2; } } +#ifdef PFC_SORTSTRING_GENERIC + int sortStringCompare(const char* str1, const char* str2) { + return naturalSortCompare(str1, str2); + } + int sortStringCompareI(const char* str1, const char* str2) { + return naturalSortCompareI(str1, str2); + } +#endif } - diff --git a/sdk/pfc/string-compare.h b/sdk/pfc/string-compare.h index 551a108..d7d2f0b 100644 --- a/sdk/pfc/string-compare.h +++ b/sdk/pfc/string-compare.h @@ -6,14 +6,22 @@ namespace pfc { int wstricmp_ascii(const wchar_t* s1, const wchar_t* s2) throw(); int stricmp_ascii(const char* s1, const char* s2) throw(); int stricmp_ascii_ex(const char* s1, t_size len1, const char* s2, t_size len2) throw(); + + // Platform-independant lowlevel natural sort implementation int naturalSortCompare(const char* s1, const char* s2) throw(); int naturalSortCompareI(const char* s1, const char* s2) throw(); + // System-specialized natural sort compare, better ordering of Unicode text, specialized for Apple and MS platforms + // Falls back to naturalSortCompare/naturalSortCompareI where not available + int sysNaturalSortCompare(const char* s1, const char* s2); + int sysNaturalSortCompareI(const char* s1, const char* s2); + int strcmp_ex(const char* p1, t_size n1, const char* p2, t_size n2) throw(); int strcmp_nc(const char* p1, size_t n1, const char* p2, size_t n2) throw(); bool stringEqualsI_utf8(const char* p1, const char* p2) throw(); bool stringEqualsI_ascii(const char* p1, const char* p2) throw(); + bool stringEqualsI_ascii_ex(const char* p1, size_t l1, const char* p2, size_t l2) throw(); int stringCompareCaseInsensitive(const char* s1, const char* s2); int stringCompareCaseInsensitiveEx(string_part_ref s1, string_part_ref s2); @@ -48,7 +56,7 @@ namespace pfc { public: template static int compare(T1 const& v1, T2 const& v2) { - if (is_same_type::value || is_same_type::value) { + if constexpr (is_same_type::value || is_same_type::value) { return stringCompareCaseInsensitiveEx(stringToRef(v1), stringToRef(v2)); } else { return stringCompareCaseInsensitive(myStringToPtr(v1), myStringToPtr(v2)); @@ -59,7 +67,7 @@ namespace pfc { public: template static int compare(T1 const& v1, T2 const& v2) { - if (is_same_type::value || is_same_type::value) { + if constexpr (is_same_type::value || is_same_type::value) { return compare_ex(stringToRef(v1), stringToRef(v2)); } else { return stricmp_ascii(myStringToPtr(v1), myStringToPtr(v2)); diff --git a/sdk/pfc/string-interface.h b/sdk/pfc/string-interface.h index abbc43d..6ce7d54 100644 --- a/sdk/pfc/string-interface.h +++ b/sdk/pfc/string-interface.h @@ -76,8 +76,13 @@ namespace pfc { void truncate_last_char(); void truncate_number_suffix(); - bool truncate_eol(t_size start = 0); - bool fix_eol(const char* append = " (...)", t_size start = 0); + //! Truncates string at first encountered end-of-line mark, starting search from startBytes position, in bytes.. + bool truncate_eol(t_size startBytes = 0); + //! Truncates string at first encountered end-of-line mark, starting search from startBytes position, in bytes. + //! Adds append value if string was altered. + bool fix_eol(const char* append = " (...)", t_size startBytes = 0); + //! Limits string length to the specified value in actual characters. + //! That is, multi-byte UTF-8 characters will be counted as one and never broken apart. bool limit_length(t_size length_in_chars, const char* append = " (...)"); void truncate_filename() { truncate(scan_filename()); } diff --git a/sdk/pfc/string-lite.cpp b/sdk/pfc/string-lite.cpp index 8aecc62..6294e59 100644 --- a/sdk/pfc/string-lite.cpp +++ b/sdk/pfc/string-lite.cpp @@ -259,7 +259,7 @@ namespace pfc { string s(_s); const t_size len = length(); const char* content = ptr(); - for (t_size walk = 0; walk < len; ++walk) { + for (t_size walk = base; walk < len; ++walk) { if (s.contains(content[walk])) return walk; } return SIZE_MAX; @@ -267,7 +267,7 @@ namespace pfc { t_size stringLite::lastIndexOfAnyChar(stringp _s, t_size base) const { string s(_s); const char* content = ptr(); - for (t_size _walk = length(); _walk > 0; --_walk) { + for (t_size _walk = min_t(base, length()); _walk > 0; --_walk) { const t_size walk = _walk - 1; if (s.contains(content[walk])) return walk; } diff --git a/sdk/pfc/string-lite.h b/sdk/pfc/string-lite.h index 5f698e0..974afb9 100644 --- a/sdk/pfc/string-lite.h +++ b/sdk/pfc/string-lite.h @@ -11,8 +11,10 @@ namespace pfc { class stringLite : public string_base { public: - class tagNoShrink {}; + struct tagNoShrink {}; + struct tagPrealloc { size_t amount; }; stringLite(tagNoShrink) { this->setNoShrink(); } + stringLite(tagPrealloc const& tag) { this->prealloc(tag.amount); } stringLite() {} stringLite( const stringLite & other ) { copy(other); } stringLite( stringLite && other ) noexcept { move(other); } @@ -124,7 +126,8 @@ namespace pfc { char firstChar() const; char lastChar() const; - bool isEmpty() const { return length() == 0; } + bool isEmpty() const { return empty(); } + bool empty() const { return length() == 0;} stringLite replace(stringp strOld, stringp strNew) const; stringLite trim(char c) const; diff --git a/sdk/pfc/string_base.cpp b/sdk/pfc/string_base.cpp index 15005b1..1d26622 100644 --- a/sdk/pfc/string_base.cpp +++ b/sdk/pfc/string_base.cpp @@ -161,6 +161,19 @@ string8 string_filename(const char * fn) return ret; } +const char * extract_ext_v2( const char * filenameDotExt ) { + auto split = strrchr(filenameDotExt, '.'); + return split ? split+1 : ""; +} + +string8 remove_ext_v2( const char * filenameDotExt ) { + auto split = strrchr(filenameDotExt, '.'); + string8 ret; + if ( split ) ret.set_string_nc( filenameDotExt, split-filenameDotExt ); + else ret = filenameDotExt; + return ret; +} + const char * filename_ext_v2( const char * fn, char slash ) { if ( slash == 0 ) { slash = pfc::io::path::getDefaultSeparator(); @@ -337,9 +350,11 @@ static double pfc_string_to_float_internal(const char * src) noexcept return (double) val * exp_int(10, div); } +double string_to_float(const char * src) noexcept { + return pfc_string_to_float_internal(src); +} double string_to_float(const char * src,t_size max) noexcept { - //old function wants an oldstyle nullterminated string, and i don't currently care enough to rewrite it as it works appropriately otherwise - char blargh[128]; + char blargh[128]; if (max > 127) max = 127; t_size walk; for(walk = 0; walk < max && src[walk]; walk++) blargh[walk] = src[walk]; @@ -500,7 +515,7 @@ string8 format_float(double p_val,unsigned p_width,unsigned p_prec) char format_hex_char(unsigned p_val) { PFC_ASSERT(p_val < 16); - return (p_val < 10) ? p_val + '0' : p_val - 10 + 'A'; + return (p_val < 10) ? (char)p_val + '0' : (char)p_val - 10 + 'A'; } format_int_t format_hex(t_uint64 p_val,unsigned p_width) @@ -531,7 +546,7 @@ format_int_t format_hex(t_uint64 p_val,unsigned p_width) char format_hex_char_lowercase(unsigned p_val) { PFC_ASSERT(p_val < 16); - return (p_val < 10) ? p_val + '0' : p_val - 10 + 'a'; + return (p_val < 10) ? (char)p_val + '0' : (char)p_val - 10 + 'a'; } format_int_t format_hex_lowercase(t_uint64 p_val,unsigned p_width) @@ -902,6 +917,15 @@ string8 format_time_ex(double p_seconds,unsigned p_extra) { return ret; } +void stringToUpperHere(string_base& p_out, const char* p_source, t_size p_sourceLen) { + p_out.clear(); + stringToUpperAppend(p_out, p_source, p_sourceLen); +} +void stringToLowerHere(string_base& p_out, const char* p_source, t_size p_sourceLen) { + p_out.clear(); + stringToLowerAppend(p_out, p_source, p_sourceLen); +} + void stringToUpperAppend(string_base & out, const char * src, t_size len) { while(len && *src) { unsigned c; t_size d; @@ -951,6 +975,29 @@ string8 format_file_size_short(uint64_t size, uint64_t * outUsedScale) { return ret; } +pfc::string8 format_index(size_t idx) { + return idx == SIZE_MAX ? "" : pfc::format_uint(idx); +} + +pfc::string8 format_permutation(const size_t* arg, size_t n) { + pfc::string_formatter ret; + for( size_t walk = 0; walk < n; ++ walk ) { + if (arg[walk] != walk) { + if ( !ret.is_empty() ) ret << ", "; + ret << arg[walk] << "->" << walk; + } + } + return ret; +} +pfc::string8 format_mask(pfc::bit_array const& mask, size_t n) { + pfc::string_formatter ret; + mask.for_each(true, 0, n, [&] (size_t idx) { + if (!ret.is_empty() ) ret << ", "; + ret << idx; + }); + return ret; +} + bool string_base::truncate_eol(t_size start) { const char * ptr = get_ptr() + start; @@ -1096,7 +1143,7 @@ uint32_t charLower(uint32_t param) uint32_t charUpper(uint32_t param) { if (param<128) { - if (param>='a' && param<='z') param += 'A' - 'a'; + if (param>='a' && param<='z') param -= (uint32_t)( 'a' - 'A' ); return param; } #ifdef PFC_WINDOWS_DESKTOP_APP @@ -1320,4 +1367,37 @@ void string_base::fix_dir_separator(char c) { return ret; } + pfc::string8 recover_invalid_utf8(const char* in, const char* subst) { + pfc::string8 ret; ret.prealloc(strlen(in)); + for (;;) { + char c = *in; + if (c == 0) break; + if (c < ' ') { + ret += subst; + } else { + ret.add_byte(c); + } + ++in; + } + return ret; + } + static bool is_spacing(char c) { + switch (c) { + case ' ': case '\n': case '\r': case '\t': return true; + default: return false; + } + } + pfc::string8 string_trim_spacing(const char* in) { + const char* temp_ptr = in; + while (is_spacing(*temp_ptr)) temp_ptr++; + const char* temp_start = temp_ptr; + const char* temp_end = temp_ptr; + while (*temp_ptr) + { + if (!is_spacing(*temp_ptr)) temp_end = temp_ptr + 1; + temp_ptr++; + } + + return string_part_ref { temp_start, (size_t)(temp_end - temp_start) }; + } } //namespace pfc diff --git a/sdk/pfc/string_base.h b/sdk/pfc/string_base.h index df3148e..12dfa4d 100644 --- a/sdk/pfc/string_base.h +++ b/sdk/pfc/string_base.h @@ -16,7 +16,6 @@ namespace pfc { bool is_lower_ascii(const char * param); bool is_multiline(const char * p_string,t_size p_len = SIZE_MAX); bool has_path_bad_chars(const char * param); - void recover_invalid_utf8(const char * src,char * out,unsigned replace);//out must be enough to hold strlen(char) + 1, or appropiately bigger if replace needs multiple chars void convert_to_lower_ascii(const char * src,t_size max,char * out,char replace = '?');//out should be at least strlen(src)+1 long template inline char_t ascii_tolower(char_t c) {if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; return c;} @@ -107,7 +106,23 @@ namespace pfc { t_size wide_decode_char(const wchar_t * p_source,unsigned * p_out,t_size p_source_length = SIZE_MAX) noexcept; t_size wide_encode_char(unsigned c,wchar_t * out) noexcept; - + size_t uni_char_length(const char *); + size_t uni_char_length(const char16_t *); + size_t uni_char_length(const wchar_t *); + + size_t uni_decode_char(const char16_t * p_source, unsigned & p_out, size_t p_source_length = SIZE_MAX) noexcept; + size_t uni_decode_char(const char * p_source, unsigned & p_out, size_t p_source_length = SIZE_MAX) noexcept; + size_t uni_decode_char(const wchar_t * p_source, unsigned & p_out, size_t p_source_length = SIZE_MAX) noexcept; + + size_t uni_encode_char(unsigned c, char* out) noexcept; + size_t uni_encode_char(unsigned c, char16_t* out) noexcept; + size_t uni_encode_char(unsigned c, wchar_t* out) noexcept; + +#ifdef __cpp_char8_t + inline size_t uni_char_length(const char8_t* arg) { return uni_char_length(reinterpret_cast(arg)); } + inline size_t uni_decode_char(const char8_t* p_source, unsigned& p_out, size_t p_source_length = SIZE_MAX) noexcept { return uni_decode_char(reinterpret_cast(p_source), p_out, p_source_length); } + inline size_t uni_encode_char(unsigned c, char8_t* out) noexcept { return uni_encode_char(c, reinterpret_cast(out)); } +#endif t_size strstr_ex(const char * p_string,t_size p_string_len,const char * p_substring,t_size p_substring_len) noexcept; @@ -215,6 +230,8 @@ namespace pfc { string8 string_filename_ext(const char * fn); const char * filename_ext_v2 ( const char * fn, char slash = 0 ); + string8 remove_ext_v2( const char * fileNameDotExt ); // Just removes extension, assumes argument to hold just filename.ext, not whole path + const char * extract_ext_v2( const char * fileNameDotExt ); // Just extracts extension, assumes argument to hold just filename.ext, not whole path size_t find_extension_offset(const char * src); string8 string_extension(const char * src); @@ -222,7 +239,8 @@ namespace pfc { string8 string_directory(const char * p_path); void float_to_string(char * out,t_size out_max,double val,unsigned precision,bool force_sign = false);//doesnt add E+X etc, has internal range limits, useful for storing float numbers as strings without having to bother with international coma/dot settings BS - double string_to_float(const char * src,t_size len = SIZE_MAX) noexcept; + double string_to_float(const char * src,t_size len) noexcept; + double string_to_float(const char * src) noexcept; string8 format_float(double p_val,unsigned p_width = 0,unsigned p_prec = 7); @@ -258,6 +276,9 @@ namespace pfc { string8 format_file_size_short(uint64_t size, uint64_t * outScaleUsed = nullptr); + string8 format_index(size_t idx); + string8 format_permutation(const size_t* arg, size_t n); + string8 format_mask(bit_array const& mask, size_t n); } inline pfc::string_base & operator<<(pfc::string_base & p_fmt,const char * p_source) {p_fmt.add_string_(p_source); return p_fmt;} @@ -310,8 +331,10 @@ namespace pfc { namespace pfc { - void stringToUpperAppend(string_base & p_out, const char * p_source, t_size p_sourceLen); - void stringToLowerAppend(string_base & p_out, const char * p_source, t_size p_sourceLen); + void stringToUpperAppend(string_base & p_out, const char * p_source, t_size p_sourceLen = SIZE_MAX); + void stringToLowerAppend(string_base & p_out, const char * p_source, t_size p_sourceLen = SIZE_MAX); + void stringToUpperHere(string_base& p_out, const char* p_source, t_size p_sourceLen = SIZE_MAX); + void stringToLowerHere(string_base& p_out, const char* p_source, t_size p_sourceLen = SIZE_MAX); t_uint32 charLower(t_uint32 param); t_uint32 charUpper(t_uint32 param); char ascii_tolower_lookup(char c); @@ -407,4 +430,9 @@ namespace pfc { } pfc::string8 prefixLines(const char* str, const char* prefix, const char * setEOL = "\n"); + + + pfc::string8 recover_invalid_utf8(const char* in, const char* subst = "_"); + + pfc::string8 string_trim_spacing(const char* in); } diff --git a/sdk/pfc/string_conv.cpp b/sdk/pfc/string_conv.cpp index 5edd891..ff9bece 100644 --- a/sdk/pfc/string_conv.cpp +++ b/sdk/pfc/string_conv.cpp @@ -178,7 +178,7 @@ namespace pfc { t_size convert_utf8_to_wide_unchecked(wchar_t * p_out,const char * p_in) { t_size inptr = 0; - string_writer_t writer(p_out,~0); + string_writer_t writer(p_out,SIZE_MAX); while(!writer.is_overrun()) { unsigned newchar = 0; diff --git a/sdk/pfc/string_conv.h b/sdk/pfc/string_conv.h index a45e7e1..e3f12e3 100644 --- a/sdk/pfc/string_conv.h +++ b/sdk/pfc/string_conv.h @@ -72,14 +72,14 @@ namespace pfc { template<> inline const char * null_string_t() {return "";} template<> inline const wchar_t * null_string_t() {return L"";} - template t_size strlen_t(const t_char * p_string,t_size p_string_size = ~0) { + template t_size strlen_t(const t_char * p_string,t_size p_string_size = SIZE_MAX) { for(t_size n=0;n bool string_is_empty_t(const t_char * p_string,t_size p_string_size = ~0) { + template bool string_is_empty_t(const t_char * p_string,t_size p_string_size = SIZE_MAX) { if (p_string_size == 0) return true; return p_string[0] == 0; } @@ -101,9 +101,9 @@ namespace pfc { class string_utf8_from_wide_t { public: string_utf8_from_wide_t() {} - string_utf8_from_wide_t(const wchar_t * p_source,t_size p_source_size = ~0) {convert(p_source,p_source_size);} + string_utf8_from_wide_t(const wchar_t * p_source,t_size p_source_size = SIZE_MAX) {convert(p_source,p_source_size);} - void convert(const wchar_t * p_source,t_size p_source_size = ~0) { + void convert(const wchar_t * p_source,t_size p_source_size = SIZE_MAX) { t_size size = estimate_wide_to_utf8(p_source,p_source_size); m_buffer.set_size(size); convert_wide_to_utf8( m_buffer.get_ptr_var(),size,p_source,p_source_size); @@ -173,9 +173,9 @@ namespace pfc { class string_wide_from_win1252_t { public: string_wide_from_win1252_t() {} - string_wide_from_win1252_t(const char * p_source,t_size p_source_size = ~0) {convert(p_source,p_source_size);} + string_wide_from_win1252_t(const char * p_source,t_size p_source_size = SIZE_MAX) {convert(p_source,p_source_size);} - void convert(const char * p_source,t_size p_source_size = ~0) { + void convert(const char * p_source,t_size p_source_size = SIZE_MAX) { t_size size = estimate_win1252_to_wide(p_source,p_source_size); m_buffer.set_size(size); convert_win1252_to_wide(m_buffer.get_ptr_var(),size,p_source,p_source_size); @@ -196,9 +196,9 @@ namespace pfc { class string_win1252_from_wide_t { public: string_win1252_from_wide_t() {} - string_win1252_from_wide_t(const wchar_t * p_source,t_size p_source_size = ~0) {convert(p_source,p_source_size);} + string_win1252_from_wide_t(const wchar_t * p_source,t_size p_source_size = SIZE_MAX) {convert(p_source,p_source_size);} - void convert(const wchar_t * p_source,t_size p_source_size = ~0) { + void convert(const wchar_t * p_source,t_size p_source_size = SIZE_MAX) { t_size size = estimate_wide_to_win1252(p_source,p_source_size); m_buffer.set_size(size); convert_wide_to_win1252(m_buffer.get_ptr_var(),size,p_source,p_source_size); @@ -219,9 +219,9 @@ namespace pfc { class string_utf8_from_win1252_t { public: string_utf8_from_win1252_t() {} - string_utf8_from_win1252_t(const char * p_source,t_size p_source_size = ~0) {convert(p_source,p_source_size);} + string_utf8_from_win1252_t(const char * p_source,t_size p_source_size = SIZE_MAX) {convert(p_source,p_source_size);} - void convert(const char * p_source,t_size p_source_size = ~0) { + void convert(const char * p_source,t_size p_source_size = SIZE_MAX) { t_size size = estimate_win1252_to_utf8(p_source,p_source_size); m_buffer.set_size(size); convert_win1252_to_utf8(m_buffer.get_ptr_var(),size,p_source,p_source_size); @@ -242,9 +242,9 @@ namespace pfc { class string_win1252_from_utf8_t { public: string_win1252_from_utf8_t() {} - string_win1252_from_utf8_t(const char * p_source,t_size p_source_size = ~0) {convert(p_source,p_source_size);} + string_win1252_from_utf8_t(const char * p_source,t_size p_source_size = SIZE_MAX) {convert(p_source,p_source_size);} - void convert(const char * p_source,t_size p_source_size = ~0) { + void convert(const char * p_source,t_size p_source_size = SIZE_MAX) { t_size size = estimate_utf8_to_win1252(p_source,p_source_size); m_buffer.set_size(size); convert_utf8_to_win1252(m_buffer.get_ptr_var(),size,p_source,p_source_size); @@ -263,9 +263,9 @@ namespace pfc { class string_ascii_from_utf8 { public: string_ascii_from_utf8() {} - string_ascii_from_utf8( const char * p_source, t_size p_source_size = ~0) { convert(p_source, p_source_size); } + string_ascii_from_utf8( const char * p_source, t_size p_source_size = SIZE_MAX) { convert(p_source, p_source_size); } - void convert( const char * p_source, t_size p_source_size = ~0) { + void convert( const char * p_source, t_size p_source_size = SIZE_MAX) { t_size size = estimate_utf8_to_ascii(p_source, p_source_size); m_buffer.set_size(size); convert_utf8_to_ascii(m_buffer.get_ptr_var(), size, p_source, p_source_size); @@ -283,9 +283,9 @@ namespace pfc { class string_utf8_from_utf16 { public: string_utf8_from_utf16() {} - string_utf8_from_utf16( const char16_t * p_source, size_t p_source_size = ~0) {convert(p_source, p_source_size);} + string_utf8_from_utf16( const char16_t * p_source, size_t p_source_size = SIZE_MAX) {convert(p_source, p_source_size);} - void convert( const char16_t * p_source, size_t p_source_size = ~0) { + void convert( const char16_t * p_source, size_t p_source_size = SIZE_MAX) { size_t size = estimate_utf16_to_utf8(p_source, p_source_size); m_buffer.set_size(size); convert_utf16_to_utf8(m_buffer.get_ptr_var(), size, p_source, p_source_size ); @@ -387,9 +387,9 @@ namespace pfc { public: string_wide_from_codepage_t() {} string_wide_from_codepage_t(const string_wide_from_codepage_t & p_source) : m_buffer(p_source.m_buffer) {} - string_wide_from_codepage_t(unsigned p_codepage,const char * p_source,t_size p_source_size = ~0) {convert(p_codepage,p_source,p_source_size);} + string_wide_from_codepage_t(unsigned p_codepage,const char * p_source,t_size p_source_size = SIZE_MAX) {convert(p_codepage,p_source,p_source_size);} - void convert(unsigned p_codepage,const char * p_source,t_size p_source_size = ~0) { + void convert(unsigned p_codepage,const char * p_source,t_size p_source_size = SIZE_MAX) { t_size size = estimate_codepage_to_wide(p_codepage,p_source,p_source_size); m_buffer.set_size(size); convert_codepage_to_wide(p_codepage, m_buffer.get_ptr_var(),size,p_source,p_source_size); @@ -411,9 +411,9 @@ namespace pfc { public: string_codepage_from_wide_t() {} string_codepage_from_wide_t(const string_codepage_from_wide_t & p_source) : m_buffer(p_source.m_buffer) {} - string_codepage_from_wide_t(unsigned p_codepage,const wchar_t * p_source,t_size p_source_size = ~0) {convert(p_codepage,p_source,p_source_size);} + string_codepage_from_wide_t(unsigned p_codepage,const wchar_t * p_source,t_size p_source_size = SIZE_MAX) {convert(p_codepage,p_source,p_source_size);} - void convert(unsigned p_codepage,const wchar_t * p_source,t_size p_source_size = ~0) { + void convert(unsigned p_codepage,const wchar_t * p_source,t_size p_source_size = SIZE_MAX) { t_size size = estimate_wide_to_codepage(p_codepage,p_source,p_source_size); m_buffer.set_size(size); convert_wide_to_codepage(p_codepage, m_buffer.get_ptr_var(),size,p_source,p_source_size); @@ -433,7 +433,7 @@ namespace pfc { public: string_codepage_from_utf8() {} string_codepage_from_utf8(const string_codepage_from_utf8 & p_source) : m_buffer(p_source.m_buffer) {} - string_codepage_from_utf8(unsigned p_codepage,const char * p_source,t_size p_source_size = ~0) {convert(p_codepage,p_source,p_source_size);} + string_codepage_from_utf8(unsigned p_codepage,const char * p_source,t_size p_source_size = SIZE_MAX) {convert(p_codepage,p_source,p_source_size);} void convert(unsigned p_codepage,const char * p_source,t_size p_source_size = SIZE_MAX) { string_wide_from_utf8 temp; @@ -456,9 +456,9 @@ namespace pfc { public: string_utf8_from_codepage() {} string_utf8_from_codepage(const string_utf8_from_codepage & p_source) : m_buffer(p_source.m_buffer) {} - string_utf8_from_codepage(unsigned p_codepage,const char * p_source,t_size p_source_size = ~0) {convert(p_codepage,p_source,p_source_size);} + string_utf8_from_codepage(unsigned p_codepage,const char * p_source,t_size p_source_size = SIZE_MAX) {convert(p_codepage,p_source,p_source_size);} - void convert(unsigned p_codepage,const char * p_source,t_size p_source_size = ~0) { + void convert(unsigned p_codepage,const char * p_source,t_size p_source_size = SIZE_MAX) { string_wide_from_codepage temp; temp.convert(p_codepage,p_source,p_source_size); t_size size = estimate_wide_to_utf8(temp,SIZE_MAX); @@ -480,13 +480,13 @@ namespace pfc { public: string_utf8_from_ansi() {} string_utf8_from_ansi(const string_utf8_from_ansi & p_source) : m_buffer(p_source.m_buffer) {} - string_utf8_from_ansi(const char * p_source,t_size p_source_size = ~0) : m_buffer(codepage_system,p_source,p_source_size) {} + string_utf8_from_ansi(const char * p_source,t_size p_source_size = SIZE_MAX) : m_buffer(codepage_system,p_source,p_source_size) {} operator const char * () const {return get_ptr();} const char * get_ptr() const {return m_buffer.get_ptr();} const char * toString() const {return get_ptr();} bool is_empty() const {return string_is_empty_t(get_ptr());} t_size length() const {return strlen_t(get_ptr());} - void convert(const char * p_source,t_size p_source_size = ~0) {m_buffer.convert(codepage_system,p_source,p_source_size);} + void convert(const char * p_source,t_size p_source_size = SIZE_MAX) {m_buffer.convert(codepage_system,p_source,p_source_size);} private: string_utf8_from_codepage m_buffer; @@ -496,13 +496,13 @@ namespace pfc { public: string_ansi_from_utf8() {} string_ansi_from_utf8(const string_ansi_from_utf8 & p_source) : m_buffer(p_source.m_buffer) {} - string_ansi_from_utf8(const char * p_source,t_size p_source_size = ~0) : m_buffer(codepage_system,p_source,p_source_size) {} + string_ansi_from_utf8(const char * p_source,t_size p_source_size = SIZE_MAX) : m_buffer(codepage_system,p_source,p_source_size) {} operator const char * () const {return get_ptr();} const char * get_ptr() const {return m_buffer.get_ptr();} bool is_empty() const {return string_is_empty_t(get_ptr());} t_size length() const {return strlen_t(get_ptr());} - void convert(const char * p_source,t_size p_source_size = ~0) {m_buffer.convert(codepage_system,p_source,p_source_size);} + void convert(const char * p_source,t_size p_source_size = SIZE_MAX) {m_buffer.convert(codepage_system,p_source,p_source_size);} private: string_codepage_from_utf8 m_buffer; @@ -512,13 +512,13 @@ namespace pfc { public: string_wide_from_ansi() {} string_wide_from_ansi(const string_wide_from_ansi & p_source) : m_buffer(p_source.m_buffer) {} - string_wide_from_ansi(const char * p_source,t_size p_source_size = ~0) : m_buffer(codepage_system,p_source,p_source_size) {} + string_wide_from_ansi(const char * p_source,t_size p_source_size = SIZE_MAX) : m_buffer(codepage_system,p_source,p_source_size) {} operator const wchar_t * () const {return get_ptr();} const wchar_t * get_ptr() const {return m_buffer.get_ptr();} bool is_empty() const {return string_is_empty_t(get_ptr());} t_size length() const {return strlen_t(get_ptr());} - void convert(const char * p_source,t_size p_source_size = ~0) {m_buffer.convert(codepage_system,p_source,p_source_size);} + void convert(const char * p_source,t_size p_source_size = SIZE_MAX) {m_buffer.convert(codepage_system,p_source,p_source_size);} private: string_wide_from_codepage m_buffer; @@ -528,13 +528,13 @@ namespace pfc { public: string_ansi_from_wide() {} string_ansi_from_wide(const string_ansi_from_wide & p_source) : m_buffer(p_source.m_buffer) {} - string_ansi_from_wide(const wchar_t * p_source,t_size p_source_size = ~0) : m_buffer(codepage_system,p_source,p_source_size) {} + string_ansi_from_wide(const wchar_t * p_source,t_size p_source_size = SIZE_MAX) : m_buffer(codepage_system,p_source,p_source_size) {} operator const char * () const {return get_ptr();} const char * get_ptr() const {return m_buffer.get_ptr();} bool is_empty() const {return string_is_empty_t(get_ptr());} t_size length() const {return strlen_t(get_ptr());} - void convert(const wchar_t * p_source,t_size p_source_size = ~0) {m_buffer.convert(codepage_system,p_source,p_source_size);} + void convert(const wchar_t * p_source,t_size p_source_size = SIZE_MAX) {m_buffer.convert(codepage_system,p_source,p_source_size);} private: string_codepage_from_wide m_buffer; @@ -552,14 +552,14 @@ namespace pfc { class string_utf8_from_os_ex { public: - template string_utf8_from_os_ex(const t_source * source, t_size sourceLen = ~0) { + template string_utf8_from_os_ex(const t_source * source, t_size sourceLen = SIZE_MAX) { convert(source,sourceLen); } - void convert(const char * source, t_size sourceLen = ~0) { + void convert(const char * source, t_size sourceLen = SIZE_MAX) { m_buffer = string_utf8_from_ansi(source,sourceLen); } - void convert(const wchar_t * source, t_size sourceLen = ~0) { + void convert(const wchar_t * source, t_size sourceLen = SIZE_MAX) { m_buffer = string_utf8_from_wide(source,sourceLen); } diff --git a/sdk/pfc/string_list.h b/sdk/pfc/string_list.h index 75d047e..d7fe975 100644 --- a/sdk/pfc/string_list.h +++ b/sdk/pfc/string_list.h @@ -26,6 +26,8 @@ namespace pfc { template string_list_impl & operator+=(const t_what & p_source) {pfc::append_t(m_data, p_source); return *this;} void set_item(size_t idx, const char* str) { m_data[idx] = str; } + + void remove_mask(bit_array const& mask) { pfc::remove_mask_t(m_data, mask); } private: template void _append(const t_what & p_source) { const t_size toadd = p_source.get_size(), base = m_data.get_size(); diff --git a/sdk/pfc/syncd_storage.h b/sdk/pfc/syncd_storage.h index d38c72e..9b82a02 100644 --- a/sdk/pfc/syncd_storage.h +++ b/sdk/pfc/syncd_storage.h @@ -12,9 +12,9 @@ class syncd_storage { template syncd_storage(const t_source & p_source) : m_object(p_source) {} template - void set(t_source const & p_in) { + void set(t_source && p_in) { inWriteSync(m_sync); - m_object = p_in; + m_object = std::forward( p_in ); } template void get(t_destination & p_out) const { @@ -26,7 +26,7 @@ class syncd_storage { return m_object; } template - const t_self & operator=(t_source const & p_source) {set(p_source); return *this;} + const t_self & operator=(t_source && p_source) {set(std::forward(p_source)); return *this;} private: mutable ::pfc::readWriteLock m_sync; t_object m_object; @@ -47,13 +47,14 @@ class syncd_storage_flagged { m_changed_flag = p_flag; } template - void set(t_source const & p_in) { + void set(t_source && p_in) { inWriteSync(m_sync); - m_object = p_in; + m_object = std::forward(p_in); m_changed_flag = true; } bool has_changed() const { - inReadSync(m_sync); + // No point in locking here + // inReadSync(m_sync); return m_changed_flag; } t_object peek() const {inReadSync(m_sync); return m_object;} @@ -86,7 +87,19 @@ class syncd_storage_flagged { m_changed_flag = false; } template - const t_self & operator=(t_source const & p_source) {set(p_source); return *this;} + const t_self & operator=(t_source && p_source) {set(std::forward(p_source)); return *this;} + + template + bool compare_and_set(arg_t&& arg) { + inWriteSync(m_sync); + bool ret = false; + if (arg != m_object) { + m_object = std::forward(arg); + m_changed_flag = true; + ret = true; + } + return ret; + } private: mutable volatile bool m_changed_flag; mutable ::pfc::readWriteLock m_sync; diff --git a/sdk/pfc/synchro.h b/sdk/pfc/synchro.h index 4d50f7f..671122e 100644 --- a/sdk/pfc/synchro.h +++ b/sdk/pfc/synchro.h @@ -11,14 +11,14 @@ namespace pfc { class dummyLock { public: - void enterRead() {} - void enterWrite() {} - void leaveRead() {} - void leaveWrite() {} - void enter() {} - void leave() {} - void lock() {} - void unlock() {} + void enterRead() noexcept {} + void enterWrite() noexcept {} + void leaveRead() noexcept {} + void leaveWrite() noexcept {} + void enter() noexcept {} + void leave() noexcept {} + void lock() noexcept {} + void unlock() noexcept {} }; template @@ -26,9 +26,9 @@ namespace pfc { private: typedef mutexScope_ self_t; public: - mutexScope_( mutex_t * m ) throw() : m_mutex(m) { m_mutex->enter(); } - mutexScope_( mutex_t & m ) throw() : m_mutex(&m) { m_mutex->enter(); } - ~mutexScope_( ) throw() {m_mutex->leave();} + mutexScope_( mutex_t * m ) noexcept : m_mutex(m) { m_mutex->enter(); } + mutexScope_( mutex_t & m ) noexcept : m_mutex(&m) { m_mutex->enter(); } + ~mutexScope_( ) noexcept {m_mutex->leave();} private: void operator=( const self_t & ) = delete; mutexScope_(const self_t & ) = delete; @@ -40,21 +40,21 @@ namespace pfc { template class _readWriteLock_scope_read { public: - _readWriteLock_scope_read( lock_t & lock ) : m_lock( lock ) { m_lock.enterRead(); } - ~_readWriteLock_scope_read() {m_lock.leaveRead();} + _readWriteLock_scope_read( lock_t & lock ) noexcept : m_lock( lock ) { m_lock.enterRead(); } + ~_readWriteLock_scope_read() noexcept {m_lock.leaveRead();} private: - _readWriteLock_scope_read( const _readWriteLock_scope_read &); - void operator=( const _readWriteLock_scope_read &); + _readWriteLock_scope_read( const _readWriteLock_scope_read &) = delete; + void operator=( const _readWriteLock_scope_read &) = delete; lock_t & m_lock; }; template class _readWriteLock_scope_write { public: - _readWriteLock_scope_write( lock_t & lock ) : m_lock( lock ) { m_lock.enterWrite(); } - ~_readWriteLock_scope_write() {m_lock.leaveWrite();} + _readWriteLock_scope_write( lock_t & lock ) noexcept : m_lock( lock ) { m_lock.enterWrite(); } + ~_readWriteLock_scope_write() noexcept {m_lock.leaveWrite();} private: - _readWriteLock_scope_write( const _readWriteLock_scope_write &); - void operator=( const _readWriteLock_scope_write &); + _readWriteLock_scope_write( const _readWriteLock_scope_write &) = delete; + void operator=( const _readWriteLock_scope_write &) = delete; lock_t & m_lock; }; } diff --git a/sdk/pfc/synchro_nix.h b/sdk/pfc/synchro_nix.h index ead9420..36d18ad 100644 --- a/sdk/pfc/synchro_nix.h +++ b/sdk/pfc/synchro_nix.h @@ -20,12 +20,12 @@ namespace pfc { class mutexBase { public: - void lock() throw() {pthread_mutex_lock(&obj);} - void unlock() throw() {pthread_mutex_unlock(&obj);} + void lock() noexcept {pthread_mutex_lock(&obj);} + void unlock() noexcept {pthread_mutex_unlock(&obj);} - void enter() throw() {lock();} - void leave() throw() {unlock();} - bool tryEnter() throw() {return pthread_mutex_trylock(&obj) == 0; } + void enter() noexcept {lock();} + void leave() noexcept {unlock();} + bool tryEnter() noexcept {return pthread_mutex_trylock(&obj) == 0; } void create( const pthread_mutexattr_t * attr ); void create( const mutexAttr & ); @@ -88,10 +88,10 @@ namespace pfc { void create( const readWriteLockAttr & ); void destroy() {pthread_rwlock_destroy( & obj ); } - void enterRead() {pthread_rwlock_rdlock( &obj ); } - void enterWrite() {pthread_rwlock_wrlock( &obj ); } - void leaveRead() {pthread_rwlock_unlock( &obj ); } - void leaveWrite() {pthread_rwlock_unlock( &obj ); } + void enterRead() noexcept {pthread_rwlock_rdlock( &obj ); } + void enterWrite() noexcept {pthread_rwlock_wrlock( &obj ); } + void leaveRead() noexcept {pthread_rwlock_unlock( &obj ); } + void leaveWrite() noexcept {pthread_rwlock_unlock( &obj ); } protected: readWriteLockBase() {} ~readWriteLockBase() {} diff --git a/sdk/pfc/synchro_win.h b/sdk/pfc/synchro_win.h index c52a193..ca554d4 100644 --- a/sdk/pfc/synchro_win.h +++ b/sdk/pfc/synchro_win.h @@ -4,18 +4,18 @@ class _critical_section_base { protected: CRITICAL_SECTION sec; public: - _critical_section_base() {} - inline void enter() throw() {EnterCriticalSection(&sec);} - inline void leave() throw() {LeaveCriticalSection(&sec);} - inline void create() throw() { + _critical_section_base() = default; + inline void enter() noexcept {EnterCriticalSection(&sec);} + inline void leave() noexcept {LeaveCriticalSection(&sec);} + inline void create() noexcept { #ifdef PFC_WINDOWS_DESKTOP_APP InitializeCriticalSection(&sec); #else InitializeCriticalSectionEx(&sec,0,0); #endif } - inline void destroy() throw() {DeleteCriticalSection(&sec);} - inline bool tryEnter() throw() { return !!TryEnterCriticalSection(&sec); } + inline void destroy() noexcept {DeleteCriticalSection(&sec);} + inline bool tryEnter() noexcept { return !!TryEnterCriticalSection(&sec); } private: _critical_section_base(const _critical_section_base&) = delete; void operator=(const _critical_section_base&) = delete; @@ -24,17 +24,17 @@ class _critical_section_base { // Static-lifetime critical section, no cleanup - valid until process termination class critical_section_static : public _critical_section_base { public: - critical_section_static() {create();} + critical_section_static() noexcept {create();} #if !PFC_LEAK_STATIC_OBJECTS - ~critical_section_static() {destroy();} + ~critical_section_static() noexcept {destroy();} #endif }; // Regular critical section, intended for any lifetime scopes class critical_section : public _critical_section_base { public: - critical_section() {create();} - ~critical_section() {destroy();} + critical_section() noexcept {create();} + ~critical_section() noexcept {destroy();} }; namespace pfc { @@ -43,19 +43,18 @@ namespace pfc { // Warning, non-recursion proof class readWriteLock { public: - readWriteLock() : theLock() { - } + readWriteLock() = default; - void enterRead() { + void enterRead() noexcept { AcquireSRWLockShared( & theLock ); } - void enterWrite() { + void enterWrite() noexcept { AcquireSRWLockExclusive( & theLock ); } - void leaveRead() { + void leaveRead() noexcept { ReleaseSRWLockShared( & theLock ); } - void leaveWrite() { + void leaveWrite() noexcept { ReleaseSRWLockExclusive( &theLock ); } @@ -63,7 +62,7 @@ class readWriteLock { readWriteLock(const readWriteLock&) = delete; void operator=(const readWriteLock&) = delete; - SRWLOCK theLock; + SRWLOCK theLock = SRWLOCK_INIT; }; typedef ::_critical_section_base mutexBase_t; diff --git a/sdk/pfc/threadSafeObj.h b/sdk/pfc/threadSafeObj.h new file mode 100644 index 0000000..dd96c21 --- /dev/null +++ b/sdk/pfc/threadSafeObj.h @@ -0,0 +1,39 @@ +#pragma once +namespace pfc { + + template + struct threadSafeObjCommon { + typedef mutex_t_ mutex_t; + typedef obj_t_ obj_t; + template threadSafeObjCommon( arg_t && ... arg ) : m_obj(std::forward(arg) ... ) {} + mutex_t m_mutex; + obj_t m_obj; + }; + + template + class threadSafeObjLock { + typedef threadSafeObjLock self_t; + common_t & m_common; + public: + typename common_t::obj_t & operator*() { return m_common.m_obj; } + typename common_t::obj_t * operator->() { return & m_common.m_obj; } + + threadSafeObjLock( common_t & arg ) : m_common(arg) { m_common.m_mutex.lock(); } + ~threadSafeObjLock( ) { m_common.m_mutex.unlock(); } + threadSafeObjLock( const self_t & ) = delete; + void operator=( const self_t & ) = delete; + }; + + template + class threadSafeObj { + typedef threadSafeObjCommon common_t; + typedef threadSafeObjLock lock_t; + common_t m_common; + public: + template + threadSafeObj( arg_t && ... arg ) : m_common( std::forward(arg) ... ) {} + + lock_t get() { return lock_t(m_common); } + }; + +} diff --git a/sdk/pfc/threads.cpp b/sdk/pfc/threads.cpp index ab23dce..1e0efc9 100644 --- a/sdk/pfc/threads.cpp +++ b/sdk/pfc/threads.cpp @@ -11,7 +11,7 @@ #include "threads.h" #include "debug.h" -#include "ptrholder.h" +#include #include @@ -180,9 +180,9 @@ namespace pfc { } void splitThread(thread::arg_t const & arg, std::function f) { - ptrholder_t< std::function > arg2 ( new std::function(f) ); + auto arg2 = std::make_unique >(f); #ifdef _WIN32 - HANDLE h = MyBeginThread(winSplitThreadProc, arg2.get_ptr(), NULL, arg.winThreadPriority ); + HANDLE h = MyBeginThread(winSplitThreadProc, arg2.get(), NULL, arg.winThreadPriority ); CloseHandle(h); #else #ifdef __APPLE__ @@ -194,11 +194,11 @@ namespace pfc { attr.apply( arg ); - if (pthread_create(&thread, &attr.a, nixSplitThreadProc, arg2.get_ptr()) != 0) thread::couldNotCreateThread(); + if (pthread_create(&thread, &attr.a, nixSplitThreadProc, arg2.get()) != 0) thread::couldNotCreateThread(); pthread_detach(thread); #endif - arg2.detach(); + arg2.release(); } #ifndef __APPLE__ // Stub for non Apple diff --git a/sdk/pfc/threads.h b/sdk/pfc/threads.h index dd3ecce..5a5b8aa 100644 --- a/sdk/pfc/threads.h +++ b/sdk/pfc/threads.h @@ -59,7 +59,11 @@ namespace pfc { thread(); ~thread() {PFC_ASSERT(!isActive()); waitTillDone();} void start( arg_t const & arg = argCurrentThread() ); + //! Valid thread object (created and not joined)? bool isActive() const; + //! Joins the thread: blocks until complete, releases resources. \n + //! After waitTillDone() returns, isActive() becomes false. \n + //! No-op if thread not started. void waitTillDone() {close();} #ifdef _WIN32 void winStart(int priority, DWORD * outThreadID); diff --git a/sdk/pfc/timers.cpp b/sdk/pfc/timers.cpp index 428bf6b..4875751 100644 --- a/sdk/pfc/timers.cpp +++ b/sdk/pfc/timers.cpp @@ -112,19 +112,12 @@ profiler_static::~profiler_static() uint64_t ret; GetSystemTimeAsFileTime((FILETIME*)&ret); return ret; -#else - -#if defined( __APPLE__ ) && defined(TIME_UTC) - if (__builtin_available(iOS 13.0, macOS 10.15, *)) { - struct timespec ts; - timespec_get(&ts, TIME_UTC); - return fileTimeUtoW(ts); - } -#endif - - // Generic inaccurate method - return fileTimeUtoW(time(NULL)); -#endif +#else // not _WIN32 + timespec ts = {}; + auto status = clock_gettime( CLOCK_REALTIME, &ts); + PFC_ASSERT( status == 0 ); (void) status; + return fileTimeUtoW(ts); +#endif // _WIN32 or not } } diff --git a/sdk/pfc/timers.h b/sdk/pfc/timers.h index 643e8d7..d0b50ea 100644 --- a/sdk/pfc/timers.h +++ b/sdk/pfc/timers.h @@ -70,19 +70,24 @@ class hires_timer { } private: double _query(t_uint64 p_val) const { - return (double)( p_val - m_start ) / (double) g_query_freq(); + return (double)( p_val - m_start ) * m_mul; } static t_uint64 g_query() { LARGE_INTEGER val; if (!QueryPerformanceCounter(&val)) throw pfc::exception_not_implemented(); return val.QuadPart; } + static double init_mul() { + return 1.0 / (double)g_query_freq(); + } static t_uint64 g_query_freq() { LARGE_INTEGER val; if (!QueryPerformanceFrequency(&val)) throw pfc::exception_not_implemented(); + PFC_ASSERT(val.QuadPart > 0); return val.QuadPart; } t_uint64 m_start; + double m_mul = init_mul(); }; class lores_timer { @@ -116,6 +121,34 @@ class lores_timer { } t_uint64 m_start = 0; }; + +class media_timer { + typedef DWORD val_t; + static val_t _now() { return timeGetTime(); } +public: + void start() { + _start(_now()); + } + double query() const { + return _query(_now()); + } + double query_reset() { + auto now = _now(); + double ret = _query(now); + _start(now); + return ret; + } + pfc::string8 queryString(unsigned precision = 3) const { + return pfc::format_time_ex(query(), precision).get_ptr(); + } + static media_timer create_and_start() { + media_timer t; t.start(); return t; + } +private: + void _start(val_t t) { m_start = t; } + double _query(val_t t) const { return (t - m_start) / 1000.0; } + val_t m_start = 0; +}; } #else // not _WIN32 @@ -137,6 +170,7 @@ class hires_timer { }; typedef hires_timer lores_timer; +typedef hires_timer media_timer; } diff --git a/sdk/pfc/utf8.cpp b/sdk/pfc/utf8.cpp index 27e189d..9efb2c0 100644 --- a/sdk/pfc/utf8.cpp +++ b/sdk/pfc/utf8.cpp @@ -18,7 +18,7 @@ namespace pfc { t_size wide_decode_char(const wchar_t * p_source,unsigned * p_out,t_size p_source_length) throw() { PFC_STATIC_ASSERT( sizeof( wchar_t ) == sizeof( char16_t ) || sizeof( wchar_t ) == sizeof( unsigned ) ); - if (sizeof( wchar_t ) == sizeof( char16_t ) ) { + if constexpr (sizeof( wchar_t ) == sizeof( char16_t ) ) { return utf16_decode_char( reinterpret_cast< const char16_t *>(p_source), p_out, p_source_length ); } else { if (p_source_length == 0) { * p_out = 0; return 0; } @@ -28,7 +28,7 @@ namespace pfc { } t_size wide_encode_char(unsigned c,wchar_t * out) throw() { PFC_STATIC_ASSERT( sizeof( wchar_t ) == sizeof( char16_t ) || sizeof( wchar_t ) == sizeof( unsigned ) ); - if (sizeof( wchar_t ) == sizeof( char16_t ) ) { + if constexpr (sizeof( wchar_t ) == sizeof( char16_t ) ) { return utf16_encode_char( c, reinterpret_cast< char16_t * >(out) ); } else { * out = (wchar_t) c; @@ -36,6 +36,58 @@ namespace pfc { } } + size_t uni_decode_char(const char16_t * p_source, unsigned & p_out, size_t p_source_length) noexcept { + return utf16_decode_char(p_source, &p_out, p_source_length); + } + size_t uni_decode_char(const char * p_source, unsigned & p_out, size_t p_source_length) noexcept { + return utf8_decode_char(p_source, p_out, p_source_length); + } + size_t uni_decode_char(const wchar_t * p_source, unsigned & p_out, size_t p_source_length) noexcept { + if constexpr ( sizeof(wchar_t) == sizeof(char16_t)) { + return utf16_decode_char( reinterpret_cast(p_source), &p_out, p_source_length); + } else { + if (p_source_length > 0) { + unsigned c = (unsigned)*p_source; + if (c != 0) { + p_out = c; return 1; + } + } + p_out = 0; return 0; + } + } + + size_t uni_char_length(const char * arg) { + return utf8_char_len(arg); + } + size_t uni_char_length(const char16_t * arg) { + unsigned dontcare; + return utf16_decode_char(arg, &dontcare); + } + size_t uni_char_length(const wchar_t * arg) { + if constexpr ( sizeof(wchar_t) == sizeof(char16_t) ) { + unsigned dontcare; + return utf16_decode_char(reinterpret_cast(arg), &dontcare); + } else { + return *arg == 0 ? 0 : 1; + } + } + + size_t uni_encode_char(unsigned c, char* out) noexcept { + PFC_ASSERT(c != 0); + return utf8_encode_char(c, out); + } + size_t uni_encode_char(unsigned c, char16_t* out) noexcept { + PFC_ASSERT(c != 0); + return utf16_encode_char(c, out); + } + size_t uni_encode_char(unsigned c, wchar_t* out) noexcept { + PFC_ASSERT(c != 0); + if constexpr ( sizeof(wchar_t) == sizeof(char16_t)) { + return utf16_encode_char(c, reinterpret_cast(out)); + } else { + *out = (wchar_t)c; return 1; + } + } bool is_lower_ascii(const char * param) diff --git a/sdk/pfc/wait_queue.h b/sdk/pfc/wait_queue.h index f4c557f..a7a98ce 100644 --- a/sdk/pfc/wait_queue.h +++ b/sdk/pfc/wait_queue.h @@ -98,7 +98,7 @@ namespace pfc { return m_canWrite.get_handle(); } - waitQueue2() : m_eof() { + waitQueue2() { m_canWrite.set_state(true); } @@ -133,6 +133,38 @@ namespace pfc { } } + typedef std::function receive_peek_t; + // Block until there's something to return + return multiple objects at once. + // Use peek function (optional) to stop reading / leave remaining items for the next call to pick up. + std::list receive(pfc::eventHandle_t hAbort, receive_peek_t peek = nullptr , bool* didAbort = nullptr) { + if (didAbort != nullptr) *didAbort = false; + std::list ret; + for (bool retry = false; ; retry = true ) { + // try without wait first, only place where this is really used can poll abort before/after without system calls + if (retry && pfc::event::g_twoEventWait(hAbort, m_canRead.get_handle(), -1) == 1) { + if (didAbort != nullptr) *didAbort = true; + break; + } + mutexScope guard(m_mutex); + auto i = m_list.begin(); + if (i == m_list.end()) { + if (m_eof) break; + continue; + } + bool bDidGet = false; + do { + if (peek && !peek(*i)) break; + auto n = i; ++n; + ret.splice(ret.end(), m_list, i); + i = std::move(n); + bDidGet = true; + } while (i != m_list.end()); + if ( bDidGet ) didGet(); + break; + } + return ret; + } + bool get(obj_t & out, pfc::eventHandle_t hAbort, bool * didAbort = nullptr) { if (didAbort != nullptr) * didAbort = false; for (;; ) { @@ -162,8 +194,9 @@ namespace pfc { } private: void didGet() { + // mutex assumed locked if (m_eof) return; - if (m_list.size() == 0) { + if (m_list.empty()) { m_canRead.set_state(false); m_canWrite.set_state(true); } else { @@ -171,9 +204,10 @@ namespace pfc { } } void refreshCanWrite() { + // mutex assumed locked m_canWrite.set_state( !m_eof && canWriteCheck(m_list)); } - bool m_eof; + bool m_eof = false; std::list m_list; mutex m_mutex; event m_canRead, m_canWrite; diff --git a/sdk/pfc/win-objects.cpp b/sdk/pfc/win-objects.cpp index eb50c39..7d67be4 100644 --- a/sdk/pfc/win-objects.cpp +++ b/sdk/pfc/win-objects.cpp @@ -11,6 +11,12 @@ #include "pfc-fb2k-hooks.h" +#include "sortstring.h" + +// StrCmpLogicalW() +#include +#pragma comment(lib, "Shlwapi.lib") + namespace pfc { BOOL winFormatSystemErrorMessageImpl(pfc::string_base & p_out,DWORD p_code) { @@ -421,6 +427,43 @@ namespace pfc { } #endif } + pfc::string8 format_window(HWND wnd) { + pfc::string_formatter ret; + ret << "0x" << format_hex( (size_t)wnd ); + auto title = getWindowText(wnd); + if (title.length() > 0) { + ret << " [" << title << "]"; + } + return ret; + } + +#define _(X) {X, #X} + struct winStyle_t { + DWORD v; const char * n; + }; + static const winStyle_t winStyles[] = { + _(WS_POPUP), _(WS_CHILD), _(WS_MINIMIZE), _(WS_VISIBLE), + _(WS_DISABLED), _(WS_CLIPSIBLINGS), _(WS_CLIPCHILDREN), _(WS_MAXIMIZE), + _(WS_BORDER), _(WS_DLGFRAME), _(WS_VSCROLL), _(WS_HSCROLL), + _(WS_SYSMENU), _(WS_THICKFRAME), _(WS_GROUP), _(WS_TABSTOP), + _(WS_MINIMIZEBOX), _(WS_MAXIMIZEBOX) + }; + + pfc::string8 format_windowStyle(DWORD style) { + pfc::string_formatter ret; + ret << "0x" << format_hex( style, 8 ); + if (style != 0) { + pfc::string_formatter label; + for (auto& s : winStyles) if (style & s.v) { + if ( label.length() > 0 ) label << "|"; + label << s.n; + } + if (label.length() > 0) { + ret << " [" << label << "]"; + } + } + return ret; + } } #else @@ -478,6 +521,43 @@ namespace pfc { pfc::string8 unicodeNormalizeC(const char* str) { return winUnicodeNormalize(str, NormalizationC); } + int winNaturalSortCompare(const char* s1, const char* s2) { + int ret = winNaturalSortCompareI(s1, s2); + if (ret == 0) ret = strcmp(s1, s2); + return ret; + } + int winNaturalSortCompare(const wchar_t* s1, const wchar_t* s2) { + int ret = winNaturalSortCompareI(s1, s2); + if (ret == 0) ret = wcscmp(s1, s2); + return ret; + } + int winNaturalSortCompareI(const char* s1, const char* s2) { + return winNaturalSortCompareI(wideFromUTF8(s1), wideFromUTF8(s2)); + } + int winNaturalSortCompareI(const wchar_t* s1, const wchar_t* s2) { + return StrCmpLogicalW(s1, s2); + } + +#ifndef PFC_SORTSTRING_GENERIC + sortString_t makeSortString(const char* in) { + auto out = std::make_unique(pfc::stringcvt::estimate_utf8_to_wide(in)); + pfc::stringcvt::convert_utf8_to_wide_unchecked(out.get(), in); + return out; + } + sortString_t makeSortString(const wchar_t* in) { + size_t l = wcslen(in) + 1; + auto out = std::make_unique(l+1); + memcpy(out.get(), in, sizeof(wchar_t) * l); + return out; + } + int sortStringCompare(sortString_t const& s1, sortString_t const& s2) { + return winNaturalSortCompare(s1.get(), s2.get()); + } + int sortStringCompareI(sortString_t const& s1, sortString_t const& s2) { + return winNaturalSortCompareI(s1.get(), s2.get()); + } +#endif } + #endif // _WIN32 diff --git a/sdk/pfc/win-objects.h b/sdk/pfc/win-objects.h index 3ca3aa5..63d89cc 100644 --- a/sdk/pfc/win-objects.h +++ b/sdk/pfc/win-objects.h @@ -138,7 +138,9 @@ class win32_event { //! Returns true when signaled, false on timeout bool wait_for(double p_timeout_seconds) {return g_wait_for(get(),p_timeout_seconds);} - + void wait() { wait_for(-1); } + void wait_and_clear() { wait(); set_state(false); } + static DWORD g_calculate_wait_time(double p_seconds); //! Returns true when signaled, false on timeout @@ -168,7 +170,7 @@ namespace pfc { class event : public win32_event { public: - event() { create(true, false); } + event(bool initial = false) { create(true, initial); } HANDLE get_handle() const { return win32_event::get(); } }; @@ -333,5 +335,14 @@ namespace pfc { #ifdef PFC_WINDOWS_DESKTOP_APP void winSetThreadDescription(HANDLE hThread, const wchar_t * desc); + + pfc::string8 format_window(HWND wnd); + pfc::string8 format_windowStyle(DWORD); #endif // PFC_WINDOWS_DESKTOP_APP + + int winNaturalSortCompare(const char* s1, const char* s2); + int winNaturalSortCompare(const wchar_t* s1, const wchar_t* s2); + int winNaturalSortCompareI(const char* s1, const char* s2); + int winNaturalSortCompareI(const wchar_t* s1, const wchar_t* s2); + } diff --git a/sdk/sdk-license.txt b/sdk/sdk-license.txt index eac453f..2708c41 100644 --- a/sdk/sdk-license.txt +++ b/sdk/sdk-license.txt @@ -1,5 +1,5 @@ -foobar2000 1.6 SDK -Copyright (c) 2001-2021, Peter Pawlowski +foobar2000 2.x SDK +Copyright (c) 2002-2024, Peter Pawlowski All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/sdk/sdk-readme.html b/sdk/sdk-readme.html index 69b897c..da8f635 100644 --- a/sdk/sdk-readme.html +++ b/sdk/sdk-readme.html @@ -8,12 +8,12 @@

-foobar2000 SDK, version 2022-11-16 +foobar2000 SDK, version 2024-12-02

Documentation:
-SDK release notes
-foobar2000 Development Overview +SDK Change Log
+foobar2000 Development Overview