diff foosdk/sdk/foobar2000/SDK/titleformat.h @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/foosdk/sdk/foobar2000/SDK/titleformat.h	Mon Jan 05 02:15:46 2026 -0500
@@ -0,0 +1,265 @@
+#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 = 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:
+	titleformat_text_out() {}
+	~titleformat_text_out() {}
+};
+
+//! This class allows custom processing of title formatting output, aware of whole substrings being passed, etc.
+class NOVTABLE titleformat_text_filter {
+public:
+	virtual void write(const GUID & p_inputtype,pfc::string_receiver & p_out,const char * p_data,t_size p_data_length) = 0;
+protected:
+	titleformat_text_filter() {}
+	~titleformat_text_filter() {}
+};
+
+class NOVTABLE titleformat_hook_function_params
+{
+public:
+	virtual t_size get_param_count() = 0;
+	virtual void get_param(t_size index,const char * & p_string,t_size & p_string_len) = 0;//warning: not a null-terminated string
+	
+	//helper
+	t_size get_param_uint(t_size index);
+};
+
+class NOVTABLE titleformat_hook
+{
+public:
+	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;
+};
+
+//! \since 2.0
+class NOVTABLE titleformat_object_v2 : public titleformat_object {
+	FB2K_MAKE_SERVICE_INTERFACE(titleformat_object_v2, titleformat_object);
+public:
+	//! Walk from idx=0 up until returns null.
+	virtual const char* enum_used_fields(size_t idx) = 0;
+	//! Walk from idx=0 up until returns null.
+	virtual const char* enum_used_functions(size_t idx) = 0;
+	//! Returns true if evaluation of this only references fields such as file path, which can be evaluated without accessing actual metadb data.
+	virtual bool requires_metadb_info() = 0;
+};
+
+//! Standard service for instantiating titleformat_object. Implemented by the core; do not reimplement.
+//! To instantiate, use titleformat_compiler::get().
+class NOVTABLE titleformat_compiler : public service_base {
+	FB2K_MAKE_SERVICE_COREAPI(titleformat_compiler);
+public:
+	//! Returns false in case of a compilation error.
+	virtual bool compile(titleformat_object::ptr & p_out,const char * p_spec) = 0;
+	//! Helper;
+	void run(titleformat_hook * p_source,pfc::string_base & p_out,const char * p_spec);
+	//! Should never fail, falls back to %filename% in case of failure.
+	void compile_safe(titleformat_object::ptr & p_out,const char * p_spec);
+
+	//! Falls back to p_fallback in case of failure.
+	void compile_safe_ex(titleformat_object::ptr & p_out,const char * p_spec,const char * p_fallback = "<ERROR>");
+
+	//! Crashes when script can't be compiled. For use with hardcoded scripts only.
+	void compile_force(titleformat_object::ptr & p_out,const char * p_spec) {if (!compile(p_out,p_spec)) uBugCheck();}
+
+	titleformat_object::ptr compile(const char* spec);
+	titleformat_object::ptr compile_force(const char* spec);
+	titleformat_object::ptr compile_fallback(const char* spec, const char* fallback);
+
+
+	static void remove_color_marks(const char * src,pfc::string_base & out);//helper
+	static void remove_forbidden_chars(titleformat_text_out * p_out,const GUID & p_inputtype,const char * p_source,t_size p_source_len,const char * p_forbidden_chars);
+	static void remove_forbidden_chars_string_append(pfc::string_receiver & p_out,const char * p_source,t_size p_source_len,const char * p_forbidden_chars);
+	static void remove_forbidden_chars_string(pfc::string_base & p_out,const char * p_source,t_size p_source_len,const char * p_forbidden_chars);
+};
+
+// \since 2.0
+class NOVTABLE titleformat_compiler_v2 : public titleformat_compiler {
+	FB2K_MAKE_SERVICE_COREAPI_EXTENSION(titleformat_compiler_v2, titleformat_compiler);
+public:
+	virtual titleformat_object::ptr concat(pfc::list_base_const_t<titleformat_object::ptr> const&) = 0;
+};
+
+
+class titleformat_object_wrapper {
+public:
+	titleformat_object_wrapper(const char * p_script) {
+		titleformat_compiler::get()->compile_force(m_script,p_script);
+	}
+
+	operator const service_ptr_t<titleformat_object> &() const {return m_script;}
+	
+private:
+	service_ptr_t<titleformat_object> m_script;
+};
+
+
+//helpers
+
+
+class titleformat_text_out_impl_filter_chars : public titleformat_text_out
+{
+public:
+	inline titleformat_text_out_impl_filter_chars(titleformat_text_out * p_chain,const char * p_restricted_chars)
+		: m_chain(p_chain), m_restricted_chars(p_restricted_chars) {}
+	void write(const GUID & p_inputtype,const char * p_data,t_size p_data_length);
+private:
+	titleformat_text_out * m_chain;
+	const char * m_restricted_chars;
+};
+
+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 &,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;
+};
+
+class titleformat_common_methods : public service_base {
+public:
+	virtual bool process_field(const file_info & p_info,const playable_location & p_location,titleformat_text_out * p_out,const char * p_name,t_size p_name_length,bool & p_found_flag) = 0;
+	virtual bool process_function(const file_info & p_info,const playable_location & p_location,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;
+	virtual bool remap_meta(const file_info & p_info,t_size & p_index, const char * p_name, t_size p_name_length) = 0;
+	
+	FB2K_MAKE_SERVICE_COREAPI(titleformat_common_methods);
+};
+
+class titleformat_hook_impl_file_info : public titleformat_hook
+{
+public:
+	titleformat_hook_impl_file_info(const playable_location & p_location,const file_info * p_info) : m_info(p_info), m_location(p_location) {}//caller must ensure that referenced file_info object is alive as long as the titleformat_hook_impl_file_info instance
+	bool process_field(titleformat_text_out * p_out,const char * p_name,t_size p_name_length,bool & p_found_flag);
+	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);
+protected:
+	bool remap_meta(t_size & p_index, const char * p_name, t_size p_name_length) {return m_api->remap_meta(*m_info,p_index,p_name,p_name_length);}
+	const file_info * m_info;
+private:
+	const playable_location & m_location;
+	const titleformat_common_methods::ptr m_api = titleformat_common_methods::get();
+};
+
+class titleformat_hook_impl_splitter : public titleformat_hook {
+public:
+	inline titleformat_hook_impl_splitter(titleformat_hook * p_hook1,titleformat_hook * p_hook2) : m_hook1(p_hook1), m_hook2(p_hook2) {}
+	bool process_field(titleformat_text_out * p_out,const char * p_name,t_size p_name_length,bool & p_found_flag);
+	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);
+private:
+	titleformat_hook * m_hook1, * m_hook2;
+};
+
+class titleformat_text_filter_impl_reserved_chars : public titleformat_text_filter {
+public:
+	titleformat_text_filter_impl_reserved_chars(const char * p_reserved_chars) : m_reserved_chars(p_reserved_chars) {}
+	void write(const GUID & p_inputtype,pfc::string_receiver & p_out,const char * p_data,t_size p_data_length) override;
+private:
+	const char * m_reserved_chars;
+};
+
+class titleformat_text_filter_impl_filename_chars : public titleformat_text_filter {
+public:
+	void write(const GUID & p_inputType,pfc::string_receiver & p_out,const char * p_data,t_size p_dataLength) override;
+};
+
+class titleformat_text_filter_nontext_chars : public titleformat_text_filter {
+public:
+	inline static bool isReserved(char c) { return c >= 0 && c < 0x20; }
+	void write(const GUID & p_inputtype,pfc::string_receiver & p_out,const char * p_data,t_size p_data_length) override;
+};
+
+
+
+
+
+
+
+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) override {
+		if (
+			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",SIZE_MAX) == 0
+			) {
+			p_out->write_int(titleformat_inputtypes::unknown,m_total);
+			p_found_flag = true; return true;			
+		} else {
+			p_found_flag = false; 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;
+};
+
+class string_formatter_tf : public pfc::string_base {
+	[[noreturn]] static void verboten() { FB2K_BugCheck(); }
+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 override {
+		verboten();
+	}
+	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 *,t_size) override {
+		verboten();
+	}
+	void truncate(t_size) override {
+		verboten();
+	}
+	t_size get_length() const override {
+		verboten();
+	}
+	char * lock_buffer(t_size) override {
+		verboten();
+	}
+	void unlock_buffer() override {
+		verboten();
+	}
+
+private:
+	titleformat_text_out * const m_out;
+	const GUID m_inputType;
+};
+
+
+class titleformat_object_cache {
+public:
+	titleformat_object_cache(const char * pattern) : m_pattern(pattern) {}
+	operator titleformat_object::ptr() {
+		PFC_ASSERT(core_api::assert_main_thread());
+		if (m_obj.is_empty()) {
+			titleformat_compiler::get()->compile_force(m_obj, m_pattern);
+		}
+		return m_obj;
+	}
+private:
+	const char * const m_pattern;
+	titleformat_object::ptr m_obj;
+};
+
+
+class titleformat_patterns {
+public:
+    static const char * patternAlbumSplit();
+    static const char * patternSortTracks();
+};