service.h

Go to the documentation of this file.
00001 #ifndef _foobar2000_sdk_service_h_included_
00002 #define _foobar2000_sdk_service_h_included_
00003 
00004 typedef const void* service_class_ref;
00005 
00006 PFC_DECLARE_EXCEPTION(exception_service_not_found,pfc::exception,"Service not found");
00007 PFC_DECLARE_EXCEPTION(exception_service_extension_not_found,pfc::exception,"Service extension not found");
00008 PFC_DECLARE_EXCEPTION(exception_service_duplicated,pfc::exception,"Service duplicated");
00009 
00010 #ifdef _MSC_VER
00011 #define FOOGUIDDECL __declspec(selectany)
00012 #else
00013 #define FOOGUIDDECL
00014 #endif
00015 
00016 
00017 #define DECLARE_GUID(NAME,A,S,D,F,G,H,J,K,L,Z,X) FOOGUIDDECL const GUID NAME = {A,S,D,{F,G,H,J,K,L,Z,X}};
00018 #define DECLARE_CLASS_GUID(NAME,A,S,D,F,G,H,J,K,L,Z,X) FOOGUIDDECL const GUID NAME::class_guid = {A,S,D,{F,G,H,J,K,L,Z,X}};
00019 
00021 template<typename T> class service_obscure_refcounting : public T {
00022 private:
00023         int service_add_ref() throw();
00024         int service_release() throw();
00025 };
00026 
00028 template<typename T> static inline service_obscure_refcounting<T>* service_obscure_refcounting_cast(T * p_source) throw() {return static_cast<service_obscure_refcounting<T>*>(p_source);}
00029 
00030 //Must be templated instead of taking service_base* because of multiple inheritance issues.
00031 template<typename T> static void service_release_safe(T * p_ptr) throw() {
00032         if (p_ptr != NULL) PFC_ASSERT_NO_EXCEPTION( p_ptr->service_release() );
00033 }
00034 
00035 //Must be templated instead of taking service_base* because of multiple inheritance issues.
00036 template<typename T> static void service_add_ref_safe(T * p_ptr) throw() {
00037         if (p_ptr != NULL) PFC_ASSERT_NO_EXCEPTION( p_ptr->service_add_ref() );
00038 }
00039 
00040 class service_base;
00041 
00043 template<typename T>
00044 class service_ptr_t {
00045 private:
00046         typedef service_ptr_t<T> t_self;
00047 public:
00048         inline service_ptr_t() throw() : m_ptr(NULL) {}
00049         inline service_ptr_t(T* p_ptr) throw() : m_ptr(NULL) {copy(p_ptr);}
00050         inline service_ptr_t(const t_self & p_source) throw() : m_ptr(NULL) {copy(p_source);}
00051 
00052         template<typename t_source>
00053         inline service_ptr_t(t_source * p_ptr) throw() : m_ptr(NULL) {copy(p_ptr);}
00054 
00055         template<typename t_source>
00056         inline service_ptr_t(const service_ptr_t<t_source> & p_source) throw() : m_ptr(NULL) {copy(p_source);}
00057 
00058         inline ~service_ptr_t() throw() {service_release_safe(m_ptr);}
00059         
00060         template<typename t_source>
00061         void copy(t_source * p_ptr) throw() {
00062                 service_add_ref_safe(p_ptr);
00063                 service_release_safe(m_ptr);
00064                 m_ptr = pfc::safe_ptr_cast<T>(p_ptr);
00065                 
00066         }
00067 
00068         template<typename t_source>
00069         inline void copy(const service_ptr_t<t_source> & p_source) throw() {copy(p_source.get_ptr());}
00070 
00071 
00072         inline const t_self & operator=(const t_self & p_source) throw() {copy(p_source); return *this;}
00073         inline const t_self & operator=(T * p_ptr) throw() {copy(p_ptr); return *this;}
00074 
00075         template<typename t_source> inline t_self & operator=(const service_ptr_t<t_source> & p_source) throw() {copy(p_source); return *this;}
00076         template<typename t_source> inline t_self & operator=(t_source * p_ptr) throw() {copy(p_ptr); return *this;}
00077         
00078         inline void release() throw() {
00079                 service_release_safe(m_ptr);
00080                 m_ptr = NULL;
00081         }
00082 
00083 
00084         inline service_obscure_refcounting<T>* operator->() const throw() {PFC_ASSERT(m_ptr != NULL);return service_obscure_refcounting_cast(m_ptr);}
00085 
00086         inline T* get_ptr() const throw() {return m_ptr;}
00087         
00088         inline bool is_valid() const throw() {return m_ptr != NULL;}
00089         inline bool is_empty() const throw() {return m_ptr == NULL;}
00090 
00091         inline bool operator==(const t_self & p_item) const throw() {return m_ptr == p_item.get_ptr();}
00092         inline bool operator!=(const t_self & p_item) const throw() {return m_ptr != p_item.get_ptr();}
00093         inline bool operator>(const t_self & p_item) const throw() {return m_ptr > p_item.get_ptr();}
00094         inline bool operator<(const t_self & p_item) const throw() {return m_ptr < p_item.get_ptr();}
00095 
00096         template<typename t_other>
00097         inline t_self & operator<<(service_ptr_t<t_other> & p_source) throw() {attach(p_source.detach());return *this;}
00098         template<typename t_other>
00099         inline t_self & operator>>(service_ptr_t<t_other> & p_dest) throw() {p_dest.attach(detach());return *this;}
00100 
00101 
00102         inline T* __unsafe_duplicate() const throw() {//should not be used ! temporary !
00103                 service_add_ref_safe(m_ptr);
00104                 return m_ptr;
00105         }
00106 
00107         inline T* detach() throw() {
00108                 return pfc::replace_null_t(m_ptr);
00109         }
00110 
00111         template<typename t_source>
00112         inline void attach(t_source * p_ptr) throw() {
00113                 service_release_safe(m_ptr);
00114                 m_ptr = pfc::safe_ptr_cast<T>(p_ptr);
00115         }
00116 
00117         T & operator*() const throw() {return *m_ptr;}
00118 
00119         service_ptr_t<service_base> & _as_base_ptr() {
00120                 PFC_ASSERT( _as_base_ptr_check() );
00121                 return *reinterpret_cast<service_ptr_t<service_base>*>(this);
00122         }
00123         static bool _as_base_ptr_check() {
00124                 return static_cast<service_base*>((T*)NULL) == reinterpret_cast<service_base*>((T*)NULL);
00125         }
00126 private:
00127         T* m_ptr;
00128 };
00129 
00130 namespace pfc {
00131         template<typename T>
00132         class traits_t<service_ptr_t<T> > : public traits_default {
00133         public:
00134                 enum { realloc_safe = true, constructor_may_fail = false};
00135         };
00136 }
00137 
00138 
00139 template<typename T, template<typename> class t_alloc = pfc::alloc_fast>
00140 class service_list_t : public pfc::list_t<service_ptr_t<T>, t_alloc >
00141 {
00142 };
00143 
00149 #define FB2K_MAKE_SERVICE_INTERFACE(THISCLASS,PARENTCLASS) \
00150         public: \
00151                 typedef THISCLASS t_interface;  \
00152                 typedef PARENTCLASS t_interface_parent; \
00153                         \
00154                 static const GUID class_guid;   \
00155                         \
00156                 virtual bool service_query(service_ptr_t<service_base> & p_out,const GUID & p_guid) {   \
00157                         if (p_guid == class_guid) {p_out = this; return true;}  \
00158                         else return PARENTCLASS::service_query(p_out,p_guid);   \
00159                 }       \
00160                 typedef service_ptr_t<t_interface> ptr; \
00161         protected:      \
00162                 THISCLASS() {}  \
00163                 ~THISCLASS() {} \
00164         private:        \
00165                 const THISCLASS & operator=(const THISCLASS &) {throw pfc::exception_not_implemented();}        \
00166                 THISCLASS(const THISCLASS &) {throw pfc::exception_not_implemented();}  \
00167         private:        \
00168                 void __private__service_declaration_selftest() {        \
00169                         pfc::assert_same_type<PARENTCLASS,PARENTCLASS::t_interface>(); /*parentclass must be an interface*/     \
00170                         __validate_service_class_helper<THISCLASS>(); /*service_base must be reachable by walking t_interface_parent*/  \
00171                         pfc::safe_cast<service_base*>(this); /*this class must derive from service_base, directly or indirectly, and be implictly castable to it*/ \
00172                 }
00173 
00178 #define FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(THISCLASS)       \
00179         public: \
00180                 typedef THISCLASS t_interface_entrypoint;       \
00181         FB2K_MAKE_SERVICE_INTERFACE(THISCLASS,service_base)
00182                 
00183         
00184 #define FB2K_DECLARE_SERVICE_BEGIN(THISCLASS,BASECLASS) \
00185         class NOVTABLE THISCLASS : public BASECLASS     {       \
00186                 FB2K_MAKE_SERVICE_INTERFACE(THISCLASS,BASECLASS);       \
00187         public:
00188 
00189 #define FB2K_DECLARE_SERVICE_END() \
00190         };
00191 
00192 #define FB2K_DECLARE_SERVICE_ENTRYPOINT_BEGIN(THISCLASS) \
00193         class NOVTABLE THISCLASS : public service_base {        \
00194                 FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(THISCLASS)       \
00195         public:
00196                 
00197 
00200 class NOVTABLE service_base
00201 {
00202 public: 
00205         virtual int service_release() throw() = 0;
00208         virtual int service_add_ref() throw() = 0;
00211         virtual bool service_query(service_ptr_t<service_base> & p_out,const GUID & p_guid) {return false;}
00212 
00216         template<class T>
00217         bool service_query_t(service_ptr_t<T> & p_out)
00218         {
00219                 pfc::assert_same_type<T,T::t_interface>();
00220                 return service_query( *reinterpret_cast<service_ptr_t<service_base>*>(&p_out),T::class_guid);
00221         }
00222 
00223         typedef service_base t_interface;
00224         
00225 protected:
00226         service_base() {}
00227         ~service_base() {}
00228 private:
00229         service_base(const service_base&) {throw pfc::exception_not_implemented();}
00230         const service_base & operator=(const service_base&) {throw pfc::exception_not_implemented();}
00231 };
00232 
00233 typedef service_ptr_t<service_base> service_ptr;
00234 
00235 template<typename T>
00236 static void __validate_service_class_helper() {
00237         __validate_service_class_helper<T::t_interface_parent>();
00238 }
00239 
00240 template<>
00241 static void __validate_service_class_helper<service_base>() {}
00242 
00243 
00244 #include "service_impl.h"
00245 
00246 class NOVTABLE service_factory_base {
00247 protected:
00248         inline service_factory_base(const GUID & p_guid) : m_guid(p_guid) {PFC_ASSERT(!core_api::are_services_available());__internal__next=__internal__list;__internal__list=this;}
00249         inline ~service_factory_base() {PFC_ASSERT(!core_api::are_services_available());}
00250 public:
00251         inline const GUID & get_class_guid() const {return m_guid;}
00252 
00253         static service_class_ref enum_find_class(const GUID & p_guid);
00254         static bool enum_create(service_ptr_t<service_base> & p_out,service_class_ref p_class,t_size p_index);
00255         static t_size enum_get_count(service_class_ref p_class);
00256 
00257         inline static bool is_service_present(const GUID & g) {return enum_get_count(enum_find_class(g))>0;}
00258 
00260         virtual void instance_create(service_ptr_t<service_base> & p_out) = 0;
00261 
00263         static service_factory_base *__internal__list;
00265         service_factory_base * __internal__next;
00266 private:
00267         const GUID & m_guid;
00268 };
00269 
00270 
00271 template<typename B>
00272 class service_factory_base_t : public service_factory_base {
00273 public:
00274         service_factory_base_t() : service_factory_base(B::class_guid) {
00275                 pfc::assert_same_type<B,B::t_interface_entrypoint>();
00276         }
00277 
00278 };
00279 
00280 
00281 template<typename T> static void _validate_service_ptr(service_ptr_t<T> const & ptr) {
00282         PFC_ASSERT( ptr.is_valid() );
00283         service_ptr_t<T> test;
00284         PFC_ASSERT( ptr->service_query_t(test) );
00285 }
00286 
00287 #ifdef _DEBUG
00288 #define FB2K_ASSERT_VALID_SERVICE_PTR(ptr) _validate_service_ptr(ptr)
00289 #else
00290 #define FB2K_ASSERT_VALID_SERVICE_PTR(ptr)
00291 #endif
00292 
00293 template<class T> static bool service_enum_create_t(service_ptr_t<T> & p_out,t_size p_index) {
00294         pfc::assert_same_type<T,T::t_interface_entrypoint>();
00295         service_ptr_t<service_base> ptr;
00296         if (service_factory_base::enum_create(ptr,service_factory_base::enum_find_class(T::class_guid),p_index)) {
00297                 p_out = static_cast<T*>(ptr.get_ptr());
00298                 return true;
00299         } else {
00300                 p_out.release();
00301                 return false;
00302         }
00303 }
00304 
00305 template<typename T> static service_class_ref _service_find_class() {
00306         pfc::assert_same_type<T,T::t_interface_entrypoint>();
00307         return service_factory_base::enum_find_class(T::class_guid);
00308 }
00309 
00310 template<typename what>
00311 static bool _service_instantiate_helper(service_ptr_t<what> & out, service_class_ref servClass, t_size index) {
00312         /*if (out._as_base_ptr_check()) {
00313                 const bool state = service_factory_base::enum_create(out._as_base_ptr(), servClass, index);
00314                 if (state) { FB2K_ASSERT_VALID_SERVICE_PTR(out); }
00315                 return state;
00316         } else */{
00317                 service_ptr temp;
00318                 const bool state = service_factory_base::enum_create(temp, servClass, index);
00319                 if (state) {
00320                         out.attach( static_cast<what*>( temp.detach() ) );
00321                         FB2K_ASSERT_VALID_SERVICE_PTR( out );
00322                 }
00323                 return state;
00324         }
00325 }
00326 
00327 template<typename T> class service_class_helper_t {
00328 public:
00329         service_class_helper_t() : m_class(service_factory_base::enum_find_class(T::class_guid)) {
00330                 pfc::assert_same_type<T,T::t_interface_entrypoint>();
00331         }
00332         t_size get_count() const {
00333                 return service_factory_base::enum_get_count(m_class);
00334         }
00335 
00336         bool create(service_ptr_t<T> & p_out,t_size p_index) const {
00337                 return _service_instantiate_helper(p_out, m_class, p_index);
00338         }
00339 
00340         service_ptr_t<T> create(t_size p_index) const {
00341                 service_ptr_t<T> temp;
00342                 if (!create(temp,p_index)) throw pfc::exception_bug_check_v2();
00343                 return temp;
00344         }
00345         service_class_ref get_class() const {return m_class;}
00346 private:
00347         service_class_ref m_class;
00348 };
00349 
00350 void _standard_api_create_internal(service_ptr & out, const GUID & classID);
00351 
00352 template<typename T> static void standard_api_create_t(service_ptr_t<T> & p_out) {
00353         if (pfc::is_same_type<T,T::t_interface_entrypoint>::value) {
00354                 _standard_api_create_internal(p_out._as_base_ptr(), T::class_guid);
00355                 FB2K_ASSERT_VALID_SERVICE_PTR(p_out);
00356         } else {
00357                 service_ptr_t<T::t_interface_entrypoint> temp;
00358                 standard_api_create_t(temp);
00359                 if (!temp->service_query_t(p_out)) throw exception_service_extension_not_found();
00360         }
00361 }
00362 
00363 template<typename T> static service_ptr_t<T> standard_api_create_t() {
00364         service_ptr_t<T> temp;
00365         standard_api_create_t(temp);
00366         return temp;
00367 }
00368 
00369 template<typename T>
00370 static bool static_api_test_t() {
00371         typedef T::t_interface_entrypoint EP;
00372         service_class_helper_t<EP> helper;
00373         if (helper.get_count() != 1) return false;
00374         if (!pfc::is_same_type<T,EP>::value) {
00375                 service_ptr_t<T> t;
00376                 if (!helper.create(0)->service_query_t(t)) return false;
00377         }
00378         return true;
00379 }
00380 
00381 #define FB2K_API_AVAILABLE(API) static_api_test_t<API>()
00382 
00387 template<typename t_interface>
00388 class static_api_ptr_t {
00389 public:
00390         static_api_ptr_t() {
00391                 standard_api_create_t(m_ptr);
00392         }
00393         service_obscure_refcounting<t_interface>* operator->() const {return service_obscure_refcounting_cast(m_ptr.get_ptr());}
00394         t_interface* get_ptr() const {return m_ptr.get_ptr();}
00395 private:
00396         service_ptr_t<t_interface> m_ptr;
00397 };
00398 
00400 template<typename T> class service_instance_array_t {
00401 public:
00402         typedef service_ptr_t<T> t_ptr;
00403         service_instance_array_t() {
00404                 service_class_helper_t<T> helper;
00405                 const t_size count = helper.get_count();
00406                 m_data.set_size(count);
00407                 for(t_size n=0;n<count;n++) m_data[n] = helper.create(n);
00408         }
00409 
00410         t_size get_size() const {return m_data.get_size();}
00411         const t_ptr & operator[](t_size p_index) const {return m_data[p_index];}
00412 
00413         //nonconst version to allow sorting/bsearching; do not abuse
00414         t_ptr & operator[](t_size p_index) {return m_data[p_index];}
00415 private:
00416         pfc::array_t<t_ptr> m_data;
00417 };
00418 
00419 template<typename t_interface>
00420 class service_enum_t {
00421 public:
00422         service_enum_t() : m_index(0) {
00423                 pfc::assert_same_type<t_interface,typename t_interface::t_interface_entrypoint>();
00424         }
00425         void reset() {m_index = 0;}
00426 
00427         template<typename t_query>
00428         bool first(service_ptr_t<t_query> & p_out) {
00429                 reset();
00430                 return next(p_out);
00431         }
00432         
00433         template<typename t_query>
00434         bool next(service_ptr_t<t_query> & p_out) {
00435                 pfc::assert_same_type<typename t_query::t_interface_entrypoint,t_interface>();
00436                 if (pfc::is_same_type<t_query,t_interface>::value) {
00437                         return __next(reinterpret_cast<service_ptr_t<t_interface>&>(p_out));
00438                 } else {
00439                         service_ptr_t<t_interface> temp;
00440                         while(__next(temp)) {
00441                                 if (temp->service_query_t(p_out)) return true;
00442                         }
00443                         return false;
00444                 }
00445         }
00446 
00447 private:
00448         bool __next(service_ptr_t<t_interface> & p_out) {
00449                 return m_helper.create(p_out,m_index++);
00450         }
00451         unsigned m_index;
00452         service_class_helper_t<t_interface> m_helper;
00453 };
00454 
00455 template<typename T>
00456 class service_factory_t : public service_factory_base_t<typename T::t_interface_entrypoint> {
00457 public:
00458         void instance_create(service_ptr_t<service_base> & p_out) {
00459                 p_out = pfc::safe_cast<service_base*>(pfc::safe_cast<typename T::t_interface_entrypoint*>(pfc::safe_cast<T*>(  new service_impl_t<T>  )));
00460         }
00461 };
00462 
00463 template<typename T>
00464 class service_factory_single_t : public service_factory_base_t<typename T::t_interface_entrypoint> {
00465         service_impl_single_t<T> g_instance;
00466 public:
00467         TEMPLATE_CONSTRUCTOR_FORWARD_FLOOD(service_factory_single_t,g_instance)
00468 
00469         void instance_create(service_ptr_t<service_base> & p_out) {
00470                 p_out = pfc::safe_cast<service_base*>(pfc::safe_cast<typename T::t_interface_entrypoint*>(pfc::safe_cast<T*>(&g_instance)));
00471         }
00472 
00473         inline T& get_static_instance() {return g_instance;}
00474 };
00475 
00476 template<typename T>
00477 class service_factory_single_ref_t : public service_factory_base_t<typename T::t_interface_entrypoint>
00478 {
00479 private:
00480         T & instance;
00481 public:
00482         service_factory_single_ref_t(T& param) : instance(param) {}
00483 
00484         void instance_create(service_ptr_t<service_base> & p_out) {
00485                 p_out = pfc::safe_cast<service_base*>(pfc::safe_cast<typename T::t_interface_entrypoint*>(pfc::safe_cast<T*>(&instance)));
00486         }
00487 
00488         inline T& get_static_instance() {return instance;}
00489 };
00490 
00491 
00492 template<typename T>
00493 class service_factory_single_transparent_t : public service_factory_base_t<typename T::t_interface_entrypoint>, public service_impl_single_t<T>
00494 {       
00495 public:
00496         TEMPLATE_CONSTRUCTOR_FORWARD_FLOOD(service_factory_single_transparent_t,service_impl_single_t<T>)
00497 
00498         void instance_create(service_ptr_t<service_base> & p_out) {
00499                 p_out = pfc::safe_cast<service_base*>(pfc::safe_cast<typename T::t_interface_entrypoint*>(pfc::safe_cast<T*>(this)));
00500         }
00501 
00502         inline T& get_static_instance() {return *(T*)this;}
00503 };
00504 
00505 
00506 
00507 
00508 
00509 
00510 
00511 
00512 
00513 
00514 
00515 
00516 
00517 
00518 
00519 template<typename what>
00520 static bool service_by_guid_fallback(service_ptr_t<what> & out, const GUID & id) {
00521         service_enum_t<what> e;
00522         service_ptr_t<what> ptr;
00523         while(e.next(ptr)) {
00524                 if (ptr->get_guid() == id) {out = ptr; return true;}
00525         }
00526         return false;
00527 }
00528 
00529 template<typename what>
00530 class service_by_guid_data {
00531 public:
00532         service_by_guid_data() : m_servClass(), m_inited() {}
00533 
00534         bool ready() const {return m_inited;}
00535 
00536         void initialize() {
00537                 if (m_inited) return;
00538                 pfc::assert_same_type< what, typename what::t_interface_entrypoint >();
00539                 m_servClass = service_factory_base::enum_find_class(what::class_guid);
00540                 const t_size servCount = service_factory_base::enum_get_count(m_servClass);
00541                 for(t_size walk = 0; walk < servCount; ++walk) {
00542                         service_ptr_t<what> temp;
00543                         if (_service_instantiate_helper(temp, m_servClass, walk)) {
00544                                 m_order.set(temp->get_guid(), walk);
00545                         }
00546                 }
00547                 m_inited = true;
00548         }
00549 
00550         bool create(service_ptr_t<what> & out, const GUID & id) const {
00551                 PFC_ASSERT(m_inited);
00552                 t_size index;
00553                 if (!m_order.query(id,index)) return false;
00554                 return _service_instantiate_helper(out, m_servClass, index);
00555         }
00556         service_ptr_t<what> create(const GUID & id) const {
00557                 service_ptr_t<what> temp; if (!crete(temp,id)) throw exception_service_not_found(); return temp;
00558         }
00559 
00560 private:
00561         volatile bool m_inited;
00562         pfc::map_t<GUID,t_size> m_order;
00563         service_class_ref m_servClass;
00564 };
00565 
00566 template<typename what>
00567 class _service_by_guid_data_container {
00568 public:
00569         static service_by_guid_data<what> data;
00570 };
00571 template<typename what> service_by_guid_data<what> _service_by_guid_data_container<what>::data;
00572 
00573 
00574 template<typename what>
00575 static void service_by_guid_init() {
00576         service_by_guid_data<what> & data = _service_by_guid_data_container<what>::data;
00577         data.initialize();
00578 }
00579 template<typename what>
00580 static bool service_by_guid(service_ptr_t<what> & out, const GUID & id) {
00581         pfc::assert_same_type< what, typename what::t_interface_entrypoint >();
00582         service_by_guid_data<what> & data = _service_by_guid_data_container<what>::data;
00583         if (data.ready()) {
00584                 //fall-thru
00585         } else if (core_api::is_main_thread()) {
00586                 data.initialize();
00587         } else {
00588 #ifdef _DEBUG
00589                 uDebugLog() << "Warning: service_by_guid() used in non-main thread without initialization, using fallback";
00590 #endif
00591                 return service_by_guid_fallback(out,id);
00592         }
00593         return data.create(out,id);
00594 }
00595 template<typename what>
00596 static service_ptr_t<what> service_by_guid(const GUID & id) {
00597         service_ptr_t<what> temp;
00598         if (!service_by_guid(temp,id)) throw exception_service_not_found();
00599         return temp;
00600 }
00601 
00602 #define FB2K_FOR_EACH_SERVICE(type, call) {service_enum_t<type> e; service_ptr_t<type> ptr; while(e.next(ptr)) {ptr->call;} }
00603 
00604 #endif //_foobar2000_sdk_service_h_included_

Generated on Thu Aug 28 21:15:34 2008 for foobar2000 SDK by  doxygen 1.5.5