aboutsummaryrefslogtreecommitdiff
path: root/Src/timer
diff options
context:
space:
mode:
Diffstat (limited to 'Src/timer')
-rw-r--r--Src/timer/Factory.cpp69
-rw-r--r--Src/timer/Factory.h24
-rw-r--r--Src/timer/ScriptFactory.cpp68
-rw-r--r--Src/timer/ScriptFactory.h24
-rw-r--r--Src/timer/ScriptObjectService.cpp29
-rw-r--r--Src/timer/ScriptObjectService.h11
-rw-r--r--Src/timer/api.h17
-rw-r--r--Src/timer/main.cpp92
-rw-r--r--Src/timer/resource.h14
-rw-r--r--Src/timer/stimer.cpp175
-rw-r--r--Src/timer/stimer.h76
-rw-r--r--Src/timer/timer.rc76
-rw-r--r--Src/timer/timer.sln40
-rw-r--r--Src/timer/timer.vcxproj282
-rw-r--r--Src/timer/timer.vcxproj.filters95
-rw-r--r--Src/timer/timerapi.cpp32
-rw-r--r--Src/timer/timerapi.h21
-rw-r--r--Src/timer/timermul.cpp370
-rw-r--r--Src/timer/timermul.h145
-rw-r--r--Src/timer/tmultiplex.cpp128
-rw-r--r--Src/timer/tmultiplex.h45
-rw-r--r--Src/timer/version.rc239
22 files changed, 1872 insertions, 0 deletions
diff --git a/Src/timer/Factory.cpp b/Src/timer/Factory.cpp
new file mode 100644
index 00000000..104f844f
--- /dev/null
+++ b/Src/timer/Factory.cpp
@@ -0,0 +1,69 @@
+#include "Factory.h"
+#include "api.h"
+#include "timerapi.h"
+
+TimerApi *timer_svc = 0;
+static const char serviceName[] = "Timer Service";
+
+void Factory::Stop()
+{
+ delete timer_svc;
+ timer_svc=0;
+}
+FOURCC Factory::GetServiceType()
+{
+ return WaSvc::UNIQUE;
+}
+
+const char *Factory::GetServiceName()
+{
+ return serviceName;
+}
+
+GUID Factory::GetGUID()
+{
+ return timerApiServiceGuid;
+}
+
+void *Factory::GetInterface(int global_lock)
+{
+ if (!timer_svc)
+ timer_svc = new TimerApi;
+// if (global_lock)
+// WASABI_API_SVC->service_lock(this, (void *)ifc);
+ return timer_svc;
+}
+
+int Factory::SupportNonLockingInterface()
+{
+ return 1;
+}
+
+int Factory::ReleaseInterface(void *ifc)
+{
+ //WASABI_API_SVC->service_unlock(ifc);
+ return 1;
+}
+
+const char *Factory::GetTestString()
+{
+ return 0;
+}
+
+int Factory::ServiceNotify(int msg, int param1, int param2)
+{
+ return 1;
+}
+
+#define CBCLASS Factory
+START_DISPATCH;
+CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType)
+CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName)
+CB(WASERVICEFACTORY_GETGUID, GetGUID)
+CB(WASERVICEFACTORY_GETINTERFACE, GetInterface)
+CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface)
+CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface)
+CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString)
+CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify)
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/timer/Factory.h b/Src/timer/Factory.h
new file mode 100644
index 00000000..c7b0c144
--- /dev/null
+++ b/Src/timer/Factory.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "api.h"
+#include <api/service/waservicefactory.h>
+#include <api/service/services.h>
+
+class Factory : public waServiceFactory
+{
+public:
+ FOURCC GetServiceType();
+ const char *GetServiceName();
+ GUID GetGUID();
+ void *GetInterface(int global_lock);
+ int SupportNonLockingInterface();
+ int ReleaseInterface(void *ifc);
+ const char *GetTestString();
+ int ServiceNotify(int msg, int param1, int param2);
+ void Stop();
+
+protected:
+ RECVS_DISPATCH;
+};
+
+
diff --git a/Src/timer/ScriptFactory.cpp b/Src/timer/ScriptFactory.cpp
new file mode 100644
index 00000000..75b6f558
--- /dev/null
+++ b/Src/timer/ScriptFactory.cpp
@@ -0,0 +1,68 @@
+#include "ScriptFactory.h"
+#include "api.h"
+#include "ScriptObjectService.h"
+
+static ScriptObjectService svc;
+static const char serviceName[] = "Timer Maki Object";
+
+// GUID of our service factory, not the GUID of the maki object
+// {538A1D71-74B0-4fbf-877A-241D10A937F3}
+static const GUID timer_maki_guid =
+{ 0x538a1d71, 0x74b0, 0x4fbf, { 0x87, 0x7a, 0x24, 0x1d, 0x10, 0xa9, 0x37, 0xf3 } };
+
+
+FOURCC ScriptFactory::GetServiceType()
+{
+ return svc.getServiceType();
+}
+
+const char *ScriptFactory::GetServiceName()
+{
+ return serviceName;
+}
+
+GUID ScriptFactory::GetGUID()
+{
+ return timer_maki_guid;
+}
+
+void *ScriptFactory::GetInterface(int global_lock)
+{
+// if (global_lock)
+// WASABI_API_SVC->service_lock(this, (void *)ifc);
+ return &svc;
+}
+
+int ScriptFactory::SupportNonLockingInterface()
+{
+ return 1;
+}
+
+int ScriptFactory::ReleaseInterface(void *ifc)
+{
+ //WASABI_API_SVC->service_unlock(ifc);
+ return 1;
+}
+
+const char *ScriptFactory::GetTestString()
+{
+ return 0;
+}
+
+int ScriptFactory::ServiceNotify(int msg, int param1, int param2)
+{
+ return 1;
+}
+
+#define CBCLASS ScriptFactory
+START_DISPATCH;
+CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType)
+CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName)
+CB(WASERVICEFACTORY_GETGUID, GetGUID)
+CB(WASERVICEFACTORY_GETINTERFACE, GetInterface)
+CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface)
+CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface)
+CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString)
+CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify)
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/timer/ScriptFactory.h b/Src/timer/ScriptFactory.h
new file mode 100644
index 00000000..4e516d01
--- /dev/null
+++ b/Src/timer/ScriptFactory.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "api.h"
+#include <api/service/waservicefactory.h>
+#include <api/service/services.h>
+
+class ScriptFactory : public waServiceFactory
+{
+public:
+ FOURCC GetServiceType();
+ const char *GetServiceName();
+ GUID GetGUID();
+ void *GetInterface(int global_lock);
+ int SupportNonLockingInterface();
+ int ReleaseInterface(void *ifc);
+ const char *GetTestString();
+ int ServiceNotify(int msg, int param1, int param2);
+ void Stop();
+
+protected:
+ RECVS_DISPATCH;
+};
+
+
diff --git a/Src/timer/ScriptObjectService.cpp b/Src/timer/ScriptObjectService.cpp
new file mode 100644
index 00000000..6ea7cbb7
--- /dev/null
+++ b/Src/timer/ScriptObjectService.cpp
@@ -0,0 +1,29 @@
+#include "ScriptObjectService.h"
+#include <api/script/objects/rootobjcontroller.h>
+#include "STimer.h"
+
+ScriptObjectController *script_root=0;
+extern TimerScriptController _timerController;
+
+
+ScriptObjectController *ScriptObjectService::getController(int n)
+{
+ if (n == 0)
+ return &_timerController;
+ return 0;
+}
+
+
+void ScriptObjectService::onRegisterClasses(ScriptObjectController *rootController)
+{
+ script_root = rootController;
+}
+
+
+
+#define CBCLASS ScriptObjectService
+START_DISPATCH;
+ CB(GETCONTROLLER, getController);
+ VCB(ONREGISTER, onRegisterClasses);
+END_DISPATCH;
+#undef CBCLASS
diff --git a/Src/timer/ScriptObjectService.h b/Src/timer/ScriptObjectService.h
new file mode 100644
index 00000000..872657ac
--- /dev/null
+++ b/Src/timer/ScriptObjectService.h
@@ -0,0 +1,11 @@
+#pragma once
+#include <api/service/svcs/svc_scriptobj.h>
+
+class ScriptObjectService : public svc_scriptObject
+{
+public:
+ ScriptObjectController *getController(int n);
+ void onRegisterClasses(ScriptObjectController *rootController);
+protected:
+ RECVS_DISPATCH;
+};
diff --git a/Src/timer/api.h b/Src/timer/api.h
new file mode 100644
index 00000000..f7ab3d6d
--- /dev/null
+++ b/Src/timer/api.h
@@ -0,0 +1,17 @@
+#ifndef NULLSOFT_API_TIMER_H
+#define NULLSOFT_API_TIMER_H
+
+#include <api/config/api_config.h>
+#define WASABI_API_CONFIG configApi
+
+#include <api/syscb/api_syscb.h>
+#define WASABI_API_SYSCB sysCallbackApi
+
+#include <api/script/api_maki.h>
+#define WASABI_API_MAKI makiApi
+
+#include "timerapi.h"
+extern TimerApi *timer_svc;
+#define WASABI_API_TIMER timer_svc
+
+#endif // !NULLSOFT_API_TIMER_H
diff --git a/Src/timer/main.cpp b/Src/timer/main.cpp
new file mode 100644
index 00000000..dd8f2b1f
--- /dev/null
+++ b/Src/timer/main.cpp
@@ -0,0 +1,92 @@
+#include "api.h"
+#include "../nu/ServiceWatcher.h"
+#include "../Agave/Component/ifc_wa5component.h"
+#include <api/service/waServiceFactory.h>
+#include "Factory.h"
+#include "ScriptFactory.h"
+
+Factory factory;
+static ScriptFactory scriptFactory;
+api_config *WASABI_API_CONFIG = 0;
+api_syscb *WASABI_API_SYSCB = 0;
+api_service *WASABI_API_SVC = 0;
+api_maki *WASABI_API_MAKI = 0;
+
+static ServiceWatcher serviceWatcher;
+
+class TimerComponent : public ifc_wa5component
+{
+public:
+ void RegisterServices(api_service *service);
+ int RegisterServicesSafeModeOk();
+ void DeregisterServices(api_service *service);
+protected:
+ RECVS_DISPATCH;
+};
+
+
+template <class api_t>
+api_t *GetService(GUID serviceGUID)
+{
+ waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(serviceGUID);
+ if (sf)
+ return (api_t *)sf->getInterface();
+ else
+ return 0;
+}
+
+inline void ReleaseService(GUID serviceGUID, void *service)
+{
+ if (service)
+ {
+ waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(serviceGUID);
+ if (sf)
+ sf->releaseInterface(service);
+ }
+}
+
+void TimerComponent::RegisterServices(api_service *service)
+{
+ WASABI_API_SVC = service;
+ WASABI_API_SYSCB = GetService<api_syscb>(syscbApiServiceGuid);
+ serviceWatcher.WatchWith(WASABI_API_SVC);
+ serviceWatcher.WatchFor(&WASABI_API_CONFIG, configApiServiceGuid);
+ serviceWatcher.WatchFor(&WASABI_API_MAKI, makiApiServiceGuid);
+ service->service_register(&factory);
+ service->service_register(&scriptFactory);
+
+ // register for service callbacks in case any of these don't exist yet
+ WASABI_API_SYSCB->syscb_registerCallback(&serviceWatcher);
+}
+
+int TimerComponent::RegisterServicesSafeModeOk()
+{
+ return 1;
+}
+
+void TimerComponent::DeregisterServices(api_service *service)
+{
+ service->service_deregister(&factory);
+ service->service_deregister(&scriptFactory);
+ serviceWatcher.StopWatching();
+ serviceWatcher.Clear();
+
+ ReleaseService(configApiServiceGuid, WASABI_API_CONFIG);
+ ReleaseService(syscbApiServiceGuid, WASABI_API_SYSCB);
+ ReleaseService(makiApiServiceGuid, WASABI_API_MAKI);
+ factory.Stop();
+}
+
+TimerComponent timerComponent;
+extern "C" __declspec(dllexport) ifc_wa5component *GetWinamp5SystemComponent()
+{
+ return &timerComponent;
+}
+
+#define CBCLASS TimerComponent
+START_DISPATCH;
+VCB(API_WA5COMPONENT_REGISTERSERVICES, RegisterServices)
+CB(15, RegisterServicesSafeModeOk)
+VCB(API_WA5COMPONENT_DEREEGISTERSERVICES, DeregisterServices)
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/timer/resource.h b/Src/timer/resource.h
new file mode 100644
index 00000000..71f1a668
--- /dev/null
+++ b/Src/timer/resource.h
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by timer.rc
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/timer/stimer.cpp b/Src/timer/stimer.cpp
new file mode 100644
index 00000000..88a99472
--- /dev/null
+++ b/Src/timer/stimer.cpp
@@ -0,0 +1,175 @@
+#include "api.h"
+#include "stimer.h"
+#include <api/script/objcontroller.h>
+#include <api/script/objects/rootobj.h>
+#include "Factory.h"
+extern Factory factory;
+
+STimer::STimer() {
+ getScriptObject()->vcpu_setInterface(timerGuid, (void *)static_cast<STimer *>(this));
+ getScriptObject()->vcpu_setClassName(L"Timer");
+ getScriptObject()->vcpu_setController(timerController);
+ delay = 1000;
+ started = 0;
+}
+
+STimer::~STimer() {
+}
+
+void STimer::setDelay(int d) {
+ delay = d;
+ if (started)
+ timerclient_setTimer(STIMER_ID, getDelay());
+}
+
+int STimer::getDelay(void) {
+ return delay;
+}
+
+void STimer::start(void) {
+ if (started) { stop(); }
+ timerclient_setTimer(STIMER_ID, getDelay());
+ started = 1;
+}
+
+void STimer::stop(void) {
+ if (!started) return;
+ timerclient_killTimer(STIMER_ID);
+ started = 0;
+}
+
+void STimer::onTimer(void)
+{
+ if (started)
+ {
+ script_onTimer(SCRIPT_CALL, getScriptObject());
+ }
+}
+
+void STimer::timerclient_timerCallback(int id)
+{
+ if (id == STIMER_ID)
+ onTimer();
+}
+
+int STimer::isRunning() {
+ return started;
+}
+
+extern ScriptObjectController *script_root;
+TimerScriptController _timerController;
+TimerScriptController *timerController=&_timerController;
+
+// -- Functions table -------------------------------------
+function_descriptor_struct TimerScriptController::exportedFunction[] = {
+ {L"setDelay", 1, (void*)STimer::script_setDelay },
+ {L"getDelay", 0, (void*)STimer::script_getDelay },
+ {L"start", 0, (void*)STimer::script_start },
+ {L"stop", 0, (void*)STimer::script_stop },
+ {L"onTimer", 0, (void*)STimer::script_onTimer },
+ {L"isRunning",0, (void*)STimer::script_isRunning },
+ {L"getSkipped",0, (void*)STimer::script_getSkipped },
+};
+// --------------------------------------------------------
+
+const wchar_t *TimerScriptController::getClassName() {
+ return L"Timer";
+}
+
+const wchar_t *TimerScriptController::getAncestorClassName() {
+ return L"Object";
+}
+
+ScriptObjectController *TimerScriptController::getAncestorController() {
+ return script_root;
+}
+
+ScriptObject *TimerScriptController::instantiate() {
+ if (!WASABI_API_TIMER)
+ {
+ WASABI_API_TIMER = (TimerApi *)factory.GetInterface(0);
+ }
+ STimer *s = new STimer;
+ ASSERT(s != NULL);
+ return s->getScriptObject();
+}
+
+void TimerScriptController::destroy(ScriptObject *o) {
+ STimer *s = static_cast<STimer *>(o->vcpu_getInterface(timerGuid));
+ ASSERT(s != NULL);
+ delete s;
+}
+
+void *TimerScriptController::encapsulate(ScriptObject *o) {
+ return NULL; // no encapsulation for timer yet
+}
+
+void TimerScriptController::deencapsulate(void *o) {
+}
+
+int TimerScriptController::getNumFunctions() {
+ return sizeof(exportedFunction) / sizeof(function_descriptor_struct);
+}
+
+const function_descriptor_struct *TimerScriptController::getExportedFunctions() {
+ return exportedFunction;
+}
+
+GUID TimerScriptController::getClassGuid() {
+ return timerGuid;
+}
+
+scriptVar STimer::script_onTimer(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) {
+ SCRIPT_FUNCTION_INIT;
+ PROCESS_HOOKS0(o, timerController);
+ SCRIPT_FUNCTION_CHECKABORTEVENT;
+ SCRIPT_EXEC_EVENT0(o);
+}
+
+static bool isNumeric(int t)
+{
+ return (t == SCRIPT_INT || t == SCRIPT_BOOLEAN || t == SCRIPT_FLOAT || t == SCRIPT_DOUBLE);
+}
+
+scriptVar STimer::script_setDelay(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar d) {
+ SCRIPT_FUNCTION_INIT;
+ ASSERT(isNumeric(d.type));
+ STimer *t = static_cast<STimer *>(o->vcpu_getInterface(timerGuid));
+ if (t) t->setDelay(d.data.idata);
+ RETURN_SCRIPT_VOID;
+}
+
+scriptVar STimer::script_getDelay(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) {
+ SCRIPT_FUNCTION_INIT;
+ STimer *t = static_cast<STimer *>(o->vcpu_getInterface(timerGuid));
+ if (t) return MAKE_SCRIPT_INT(t->getDelay());
+ RETURN_SCRIPT_ZERO;
+}
+
+scriptVar STimer::script_start(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) {
+ SCRIPT_FUNCTION_INIT;
+ STimer *t = static_cast<STimer *>(o->vcpu_getInterface(timerGuid));
+ if (t) t->start();
+ RETURN_SCRIPT_VOID;
+}
+
+scriptVar STimer::script_stop(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) {
+ SCRIPT_FUNCTION_INIT;
+ STimer *t = static_cast<STimer *>(o->vcpu_getInterface(timerGuid));
+ if (t) t->stop();
+ RETURN_SCRIPT_VOID;
+}
+
+scriptVar STimer::script_isRunning(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) {
+ SCRIPT_FUNCTION_INIT;
+ STimer *t = static_cast<STimer *>(o->vcpu_getInterface(timerGuid));
+ if (t) return MAKE_SCRIPT_BOOLEAN(t->isRunning());
+ RETURN_SCRIPT_ZERO;
+}
+
+scriptVar STimer::script_getSkipped(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) {
+ SCRIPT_FUNCTION_INIT;
+ STimer *t = static_cast<STimer *>(o->vcpu_getInterface(timerGuid));
+ if (t) return MAKE_SCRIPT_INT(t->timerclient_getSkipped());
+ RETURN_SCRIPT_ZERO;
+}
diff --git a/Src/timer/stimer.h b/Src/timer/stimer.h
new file mode 100644
index 00000000..0e91c3f3
--- /dev/null
+++ b/Src/timer/stimer.h
@@ -0,0 +1,76 @@
+//PORTABLE
+#ifndef _STIMER_H
+#define _STIMER_H
+
+#include <api/script/objects/rootobject.h>
+#include <api/script/objects/rootobj.h>
+
+#include <api/timer/timerclient.h>
+
+#define STIMER_PARENT RootObjectInstance
+
+// {5D0C5BB6-7DE1-4b1f-A70F-8D1659941941}
+static const GUID timerGuid =
+{ 0x5d0c5bb6, 0x7de1, 0x4b1f, { 0xa7, 0xf, 0x8d, 0x16, 0x59, 0x94, 0x19, 0x41 } };
+
+class TimerScriptController : public ScriptObjectControllerI {
+ public:
+
+ virtual const wchar_t *getClassName();
+ virtual const wchar_t *getAncestorClassName();
+ virtual ScriptObjectController *getAncestorController();
+ virtual int getNumFunctions();
+ virtual const function_descriptor_struct *getExportedFunctions();
+ virtual GUID getClassGuid();
+ virtual ScriptObject *instantiate();
+ virtual void destroy(ScriptObject *o);
+ virtual void *encapsulate(ScriptObject *o);
+ virtual void deencapsulate(void *o);
+
+ private:
+
+ static function_descriptor_struct exportedFunction[];
+
+};
+
+extern TimerScriptController *timerController;
+
+
+#ifndef _NOSTUDIO
+
+#define STIMER_ID 2481
+
+class STimer : public STIMER_PARENT, public TimerClientDI {
+public:
+ STimer();
+ virtual ~STimer();
+
+ void start(void);
+ void stop(void);
+ int getDelay(void);
+ void setDelay(int d);
+ void onTimer(void);
+ int isRunning();
+
+ void timerclient_timerCallback(int id);
+
+private:
+ int delay;
+ int started;
+
+#else
+class STimer : public STIMER_SCRIPTPARENT {
+#endif
+
+public:
+
+ static scriptVar script_onTimer(SCRIPT_FUNCTION_PARAMS, ScriptObject *o);
+ static scriptVar script_setDelay(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar d);
+ static scriptVar script_getDelay(SCRIPT_FUNCTION_PARAMS, ScriptObject *o);
+ static scriptVar script_start(SCRIPT_FUNCTION_PARAMS, ScriptObject *o);
+ static scriptVar script_stop(SCRIPT_FUNCTION_PARAMS, ScriptObject *o);
+ static scriptVar script_isRunning(SCRIPT_FUNCTION_PARAMS, ScriptObject *o);
+ static scriptVar script_getSkipped(SCRIPT_FUNCTION_PARAMS, ScriptObject *o);
+};
+
+#endif
diff --git a/Src/timer/timer.rc b/Src/timer/timer.rc
new file mode 100644
index 00000000..fcff7711
--- /dev/null
+++ b/Src/timer/timer.rc
@@ -0,0 +1,76 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#include ""version.rc2""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/timer/timer.sln b/Src/timer/timer.sln
new file mode 100644
index 00000000..21e4adb2
--- /dev/null
+++ b/Src/timer/timer.sln
@@ -0,0 +1,40 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29424.173
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "timer", "timer.vcxproj", "{C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bfc", "..\Wasabi\bfc\bfc.vcxproj", "{D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Debug|Win32.ActiveCfg = Debug|Win32
+ {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Debug|Win32.Build.0 = Debug|Win32
+ {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Debug|x64.ActiveCfg = Debug|x64
+ {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Debug|x64.Build.0 = Debug|x64
+ {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Release|Win32.ActiveCfg = Release|Win32
+ {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Release|Win32.Build.0 = Release|Win32
+ {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Release|x64.ActiveCfg = Release|x64
+ {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Release|x64.Build.0 = Release|x64
+ {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|Win32.Build.0 = Debug|Win32
+ {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|x64.ActiveCfg = Debug|x64
+ {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|x64.Build.0 = Debug|x64
+ {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|Win32.ActiveCfg = Release|Win32
+ {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|Win32.Build.0 = Release|Win32
+ {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|x64.ActiveCfg = Release|x64
+ {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {5A4ED350-80C8-4501-87C2-26BBC67EAD28}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/timer/timer.vcxproj b/Src/timer/timer.vcxproj
new file mode 100644
index 00000000..8a128f7b
--- /dev/null
+++ b/Src/timer/timer.vcxproj
@@ -0,0 +1,282 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}</ProjectGuid>
+ <RootNamespace>timer</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetExt>.w5s</TargetExt>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetExt>.w5s</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetExt>.w5s</TargetExt>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetExt>.w5s</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnabled>false</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;TIMER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(ProjectDir)x86_Debug\$(ProjectName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;TIMER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(ProjectDir)x64_Debug\$(ProjectName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;TIMER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(ProjectDir)x86_Release\$(ProjectName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;TIMER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(ProjectDir)x64_Release\$(ProjectName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Wasabi\bfc\bfc.vcxproj">
+ <Project>{d0ec862e-dddd-4f4f-934f-b75dc9062dc1}</Project>
+ <CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
+ <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
+ </ProjectReference>
+ <ProjectReference Include="..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\nu\ServiceWatcher.cpp" />
+ <ClCompile Include="..\Wasabi\api\script\objcontroller.cpp" />
+ <ClCompile Include="..\Wasabi\api\script\objects\rootobj.cpp" />
+ <ClCompile Include="..\Wasabi\api\script\objects\rootobjcbx.cpp" />
+ <ClCompile Include="..\Wasabi\api\script\scriptobji.cpp" />
+ <ClCompile Include="..\Wasabi\api\script\scriptobjx.cpp" />
+ <ClCompile Include="..\Wasabi\api\timer\timerclient.cpp" />
+ <ClCompile Include="Factory.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="ScriptFactory.cpp" />
+ <ClCompile Include="ScriptObjectService.cpp" />
+ <ClCompile Include="stimer.cpp" />
+ <ClCompile Include="timerapi.cpp" />
+ <ClCompile Include="timermul.cpp" />
+ <ClCompile Include="tmultiplex.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api.h" />
+ <ClInclude Include="Factory.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="ScriptFactory.h" />
+ <ClInclude Include="ScriptObjectService.h" />
+ <ClInclude Include="stimer.h" />
+ <ClInclude Include="timerapi.h" />
+ <ClInclude Include="timermul.h" />
+ <ClInclude Include="tmultiplex.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="timer.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/timer/timer.vcxproj.filters b/Src/timer/timer.vcxproj.filters
new file mode 100644
index 00000000..6f91d699
--- /dev/null
+++ b/Src/timer/timer.vcxproj.filters
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="Factory.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Wasabi\api\script\objcontroller.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Wasabi\api\script\objects\rootobj.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Wasabi\api\script\objects\rootobjcbx.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ScriptFactory.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ScriptObjectService.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Wasabi\api\script\scriptobji.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Wasabi\api\script\scriptobjx.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nu\ServiceWatcher.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="stimer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="timerapi.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Wasabi\api\timer\timerclient.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="timermul.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="tmultiplex.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Factory.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ScriptFactory.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ScriptObjectService.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="stimer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="timerapi.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="timermul.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="tmultiplex.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{dc3ff9be-ed5b-4440-a237-bd67ac1d61ba}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{196e3323-cdec-4d80-91b1-c5896e275321}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{691078fc-f387-4cdb-aa1b-f1d6a961cf0d}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="timer.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/timer/timerapi.cpp b/Src/timer/timerapi.cpp
new file mode 100644
index 00000000..d0ed1f87
--- /dev/null
+++ b/Src/timer/timerapi.cpp
@@ -0,0 +1,32 @@
+#include "api.h"
+#include "timerapi.h"
+#include "tmultiplex.h"
+
+//timer_api *timerApi = NULL;
+
+TimerApi::TimerApi()
+{
+}
+
+TimerApi::~TimerApi()
+{
+ multiplex.shutdown();
+}
+
+TimerToken TimerApi::timer_add(TimerClient *client, intptr_t id, int ms)
+{
+ multiplex.add(client, id, ms);
+ return id;
+}
+
+void TimerApi::timer_remove(TimerClient *client, TimerToken id)
+{
+ multiplex.remove(client, id);
+}
+
+#define CBCLASS TimerApi
+START_DISPATCH;
+ CB(TIMER_API_ADD, timer_add);
+ VCB(TIMER_API_REMOVE, timer_remove);
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/timer/timerapi.h b/Src/timer/timerapi.h
new file mode 100644
index 00000000..65a4640d
--- /dev/null
+++ b/Src/timer/timerapi.h
@@ -0,0 +1,21 @@
+#ifndef __TIMER_SVC_H
+#define __TIMER_SVC_H
+
+#include <api/timer/api_timer.h>
+#include "tmultiplex.h"
+
+class TimerApi : public timer_api
+{
+public:
+ TimerApi();
+ ~TimerApi();
+ virtual TimerToken timer_add(TimerClient *client, intptr_t id, int ms);
+ virtual void timer_remove(TimerClient *client, TimerToken token = -1);
+
+protected:
+ MainTimerMultiplexer multiplex;
+ RECVS_DISPATCH;
+};
+
+
+#endif
diff --git a/Src/timer/timermul.cpp b/Src/timer/timermul.cpp
new file mode 100644
index 00000000..6e7213a9
--- /dev/null
+++ b/Src/timer/timermul.cpp
@@ -0,0 +1,370 @@
+#include <bfc/platform/platform.h>
+#include "timermul.h"
+
+#include <api.h>
+#include <api/config/items/attribs.h>
+#include <api/config/items/cfgitem.h>
+
+ // {9149C445-3C30-4e04-8433-5A518ED0FDDE}
+ const GUID uioptions_guid =
+ { 0x9149c445, 0x3c30, 0x4e04, { 0x84, 0x33, 0x5a, 0x51, 0x8e, 0xd0, 0xfd, 0xde } };
+
+PtrListQuickSorted<MultiplexerServer, MultiplexerServerComparatorTID> servers_tid;
+PtrListQuickSorted<MultiplexerServer, MultiplexerServerComparatorTID> servers_mux;
+
+TimerMultiplexer::TimerMultiplexer() {
+ timerset = 0;
+ nslices = 0;
+ resolution = -1;
+ check_resolution = true;
+ client = NULL;
+ curslice = 0;
+ running_timer = NULL;
+ uioptions = NULL;
+ justexited = 0;
+ firstevent = 1;
+ resetTimer(50); // initial, is changed for config value on first event
+}
+
+TimerMultiplexer::~TimerMultiplexer() {
+ doShutdown();
+}
+
+void TimerMultiplexer::setClient(TimerMultiplexerClient *_client) {
+ client = _client;
+}
+
+void TimerMultiplexer::addTimer(int ms, void *data) {
+ //if (ms < 0) { DebugString("Timer with negative delay set, ignored coz the time machine service isn't ready yet\n"); }
+ MultiplexedTimer *t = new MultiplexedTimer(ms, data);
+ if (ms >= MAX_TIMER_DELAY) {
+ lptimers.addItem(t);
+ t->nexttick = Wasabi::Std::getTickCount() + t->ms;
+ } else {
+ timers.addItem(t);
+ if (nslices > 0)
+ distribute(t);
+ }
+}
+
+void TimerMultiplexer::removeTimer(void *data) {
+
+ if (running_timer && running_timer->data == data)
+ running_timer = NULL;
+
+ int i;
+ for (i=0;i<timers.getNumItems();i++) {
+ MultiplexedTimer *t = timers.enumItem(i);
+ if (t->data == data) {
+ removeFromWheel(t);
+ timers.removeByPos(i);
+ delete t;
+ return;
+ }
+ }
+ for (i=0;i<lptimers.getNumItems();i++) {
+ MultiplexedTimer *t = lptimers.enumItem(i);
+ if (t->data == data) {
+ removeFromLowPrecision(t);
+ delete t;
+ return;
+ }
+ }
+}
+
+void TimerMultiplexer::setResolution(int ms) {
+ resolution = ms;
+}
+
+void TimerMultiplexer::shutdown() {
+ doShutdown();
+}
+
+void TimerMultiplexer::doShutdown() {
+ timers.deleteAll();
+ wheel.deleteAll();
+ lptimers.deleteAll();
+ if (timerset) {
+ MultiplexerServer *s = servers_mux.findItem((const wchar_t *)this);
+ if (s) {
+#ifdef WIN32
+ KillTimer(NULL, s->getId());
+#elif defined(LINUX)
+
+#endif
+ }
+ timerset = 0;
+ }
+}
+
+void TimerMultiplexer::checkResolution(DWORD now) {
+ if (check_resolution == true)
+ {
+ if (WASABI_API_CONFIG)
+ {
+ if (uioptions == NULL)
+ {
+ uioptions = WASABI_API_CONFIG->config_getCfgItemByGuid(uioptions_guid);
+ if (uioptions)
+ {
+ ifc_dependent *ui_change = uioptions->getDependencyPtr();
+ ui_change->dependent_regViewer(this, 1);
+ }
+ }
+ check_resolution = uioptions?false:true;
+ int nresolution = uioptions ? _intVal(uioptions, L"Multiplexed timers resolution") : DEF_RES;
+
+ nresolution = MAX(10, MIN(MAX_TIMER_DELAY/LOW_RES_DIV, nresolution));
+ if (nresolution != resolution) {
+ resetTimer(nresolution);
+ resolution = nresolution;
+ resetWheel();
+ }
+ }
+ }
+}
+
+VOID CALLBACK timerMultiplexerServerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) {
+ MultiplexerServer *s = servers_tid.findItem((const wchar_t *)&idEvent);
+ if (s) s->getMultiplexer()->onServerTimer();
+}
+
+void TimerMultiplexer::resetTimer(int newresolution) {
+ if (timerset) {
+ MultiplexerServer *s = servers_mux.findItem((const wchar_t *)this);
+ if (s)
+ KillTimer(NULL, s->getId());
+ }
+
+ // linux port implements settimer
+ UINT_PTR id = SetTimer(NULL, 0, newresolution, timerMultiplexerServerProc);
+ MultiplexerServer *s = servers_mux.findItem((const wchar_t *)this);
+ if (!s) {
+ s = new MultiplexerServer(this, (UINT)id);
+ servers_mux.addItem(s);
+ servers_tid.addItem(s);
+ } else {
+ s->setId(id);
+ servers_tid.sort();
+ }
+ timerset = 1;
+}
+
+PtrList<MultiplexedTimer> *TimerMultiplexer::getSlice(int n) {
+ ASSERT(nslices > 0);
+ return wheel.enumItem(n % nslices);
+}
+
+void TimerMultiplexer::resetWheel() {
+
+ wheel.deleteAll();
+
+ nslices = MAX_TIMER_DELAY / resolution;
+
+ for (int i=0;i<nslices;i++)
+ wheel.addItem(new PtrList< MultiplexedTimer >);
+
+ curslice = 0;
+ distributeAll();
+}
+
+void TimerMultiplexer::distributeAll() {
+ for (int i=0;i<timers.getNumItems();i++) {
+ distribute(timers.enumItem(i));
+ }
+}
+
+void TimerMultiplexer::distribute(MultiplexedTimer *t) {
+ ASSERT(t != NULL);
+
+ int delay = t->ms;
+
+ int slice = delay / resolution + curslice;
+ PtrList<MultiplexedTimer> *l = getSlice(slice);
+
+ ASSERT(l != NULL);
+
+ l->addItem(t);
+}
+
+void TimerMultiplexer::onServerTimer() {
+
+ justexited = 0;
+
+ DWORD now = Wasabi::Std::getTickCount();
+
+ checkResolution(now);
+
+ runCurSlice(now);
+
+ if ((curslice % (nslices/LOW_RES_DIV)) == 0) { // execute low precision timers every MAX_TIMER_DELAY/LOW_RES_DIV
+ runLowPrecisionTimers(now);
+ }
+
+ if (!justexited) {
+ curslice++;
+ curslice %= nslices;
+ }
+
+ justexited = 1;
+
+ if (firstevent) {
+ firstevent = 0;
+ checkResolution(Wasabi::Std::getTickCount());
+ }
+}
+
+void TimerMultiplexer::runCurSlice(DWORD now) {
+ //DebugString("Running slice %d\n", curslice);
+ PtrList<MultiplexedTimer> *slice = getSlice(curslice);
+ ASSERT(slice != NULL);
+
+ // mark them clean
+ int i;
+ for (i=0;i<slice->getNumItems();i++)
+ slice->enumItem(i)->flag = 0;
+
+ // run events
+ int n;
+ do {
+ n = 0;
+ for (i=0;i<slice->getNumItems();i++) {
+ MultiplexedTimer *t = slice->enumItem(i);
+ if (t == NULL) break; // do not remove this line even if you think it's useless
+ // t might have been removed by a previous runTimer in this slice, so see if it's still here and if not, ignore
+ if (!timers.haveItem(t)) { slice->removeItem(t); i--; continue; }
+ if (t->flag == 1) continue;
+ t->flag = 1;
+ int lastdelay = MAX(0, (int)(now - t->lastmscount));
+ DWORD last = t->lastmscount;
+ if (last == 0) last = now;
+ t->lastmscount = now;
+ t->lastdelay = lastdelay;
+ running_timer = t;
+ runTimer(now, last, t, slice, i);
+// -----------------------------------------------------------------------
+// WARNING
+//
+// below this line, you can no longer assume that t is pointing at valid
+// memory, because runTimer can eventually call removeTimer
+// -----------------------------------------------------------------------
+ n++;
+ }
+ } while (n > 0);
+}
+
+void TimerMultiplexer::runTimer(DWORD now, DWORD last, MultiplexedTimer *t, PtrList<MultiplexedTimer> *slice, int pos) {
+
+ int nextslice = curslice + t->ms / resolution;
+ int spent = now - last;
+ int lost = spent - t->ms;
+
+ if (lost > 0) {
+ t->lost += (float)lost / (float)t->ms;
+ }
+
+ PtrList<MultiplexedTimer> *next = getSlice(nextslice);
+ ASSERT(next != NULL);
+
+ if (slice == next) {
+ nextslice++;
+ next = getSlice(nextslice);
+ }
+
+ slice->removeByPos(pos);
+ next->addItem(t);
+
+ int skip = (int)t->lost;
+ t->lost -= (int)t->lost;
+ if (client) {
+ client->onMultiplexedTimer(t->data, skip, t->lastdelay);
+// -----------------------------------------------------------------------
+// WARNING
+//
+// below this line, you can no longer assume that t is pointing at valid
+// memory, because onMultiplexedTimer can eventually call removeTimer
+// -----------------------------------------------------------------------
+ }
+}
+
+void TimerMultiplexer::removeFromWheel(MultiplexedTimer *t) {
+ for (int i=0;i<nslices;i++) {
+ PtrList<MultiplexedTimer> *slice = getSlice(i);
+ for (int j=0;j<slice->getNumItems();j++) {
+ if (slice->enumItem(j) == t) {
+ slice->removeByPos(j);
+ j--;
+ }
+ }
+ }
+}
+
+void TimerMultiplexer::removeFromLowPrecision(MultiplexedTimer *t) {
+ for (int i=0;i<lptimers.getNumItems();i++) {
+ if (lptimers.enumItem(i) == t) {
+ lptimers.removeByPos(i);
+ i--;
+ }
+ }
+}
+
+void TimerMultiplexer::runLowPrecisionTimers(DWORD now) {
+ int restart;
+ do {
+ restart = 0;
+ for (int i=0;i<lptimers.getNumItems();i++) {
+ MultiplexedTimer *t = lptimers.enumItem(i);
+ if (t->nexttick < now) {
+ if (client) {
+ running_timer = t;
+ t->lost += (now - t->nexttick) / t->ms;
+ int skip = (int)t->lost;
+ t->lost -= skip; // remove integer part
+ DWORD last = t->lastmscount;
+ t->lastdelay = now-last;
+ t->lastmscount = now;
+ t->nexttick = t->nexttick+(t->ms)*(skip+1);
+ client->onMultiplexedTimer(t->data, skip, t->lastdelay);
+// -----------------------------------------------------------------------
+// WARNING
+//
+// below this line, you can no longer assume that t is pointing at valid
+// memory, because onMultiplexedTimer can eventually call removeTimer
+// -----------------------------------------------------------------------
+ }
+ if (running_timer == NULL) { // onMultiplexedTimer called removeTimer
+ restart =1;
+ break;
+ }
+ }
+ }
+ } while (restart);
+}
+
+
+int TimerMultiplexer::getNumTimers() {
+ return timers.getNumItems();
+}
+
+int TimerMultiplexer::getNumTimersLP() {
+ return lptimers.getNumItems();
+}
+
+int TimerMultiplexer::dependentViewer_callback(ifc_dependent *item, const GUID *classguid, int cb, intptr_t param1, intptr_t param2 , void *ptr, size_t ptrlen)
+{
+ if (param1 == CfgItem::Event_ATTRIBUTE_CHANGED)
+ {
+ check_resolution=true;
+ }
+ else if (param1 == CfgItem::Event_ATTRIBUTE_REMOVED)
+ {
+ uioptions=0;
+ }
+ return 1;
+}
+
+#define CBCLASS TimerMultiplexer
+START_DISPATCH;
+CB(DEPENDENTVIEWER_CALLBACK, dependentViewer_callback)
+END_DISPATCH;
+#undef CBCLASS
diff --git a/Src/timer/timermul.h b/Src/timer/timermul.h
new file mode 100644
index 00000000..67dcafb2
--- /dev/null
+++ b/Src/timer/timermul.h
@@ -0,0 +1,145 @@
+#ifndef __TIMER_MULTIPLEXER_H
+#define __TIMER_MULTIPLEXER_H
+
+#include <bfc/common.h>
+#include <bfc/ptrlist.h>
+#include <api/dependency/api_dependentviewer.h>
+
+// FG> not too sure how to get a callback for attribute change, if anyone wants to change it be my guest ;)
+#define RESOLUTION_CHECK_DELAY 1000 // check for resolution changes every second
+
+// if uioptions CfgItem not found, use this value for resolution
+#define DEF_RES 20
+
+// below MAX_TIMER_DELAY, timer are multiplexed using a 'wheel' algorithm (mem used = MAX_TIMER_DELAY/resolution * sizeof(PtrList) + ntimers*sizeof(MultiplexedTimer), but fast (no lookup) )
+// above MAX_TIMER_DELAY, resolution drops to MAX_TIMER_DELAY/LOW_RES_DIV and uses ntimers*sizeof(MultiplexedTimer) bytes
+#define MAX_TIMER_DELAY 1000 // keep this dividable by LOW_RES_DIV please
+#define LOW_RES_DIV 4
+
+class CfgItem;
+class api_config;
+
+class TimerMultiplexerClient {
+ public:
+ virtual void onMultiplexedTimer(void *data, int skip, int mssincelast)=0;
+};
+
+class MultiplexedTimer {
+ public:
+ MultiplexedTimer(int _ms, void *_data) : ms(_ms), data(_data) {
+ nexttick=0;
+ flag=0;
+ lost = 0;
+ lastmscount=0;
+ lastdelay=0;
+ }
+ virtual ~MultiplexedTimer() { }
+
+ int ms;
+ void *data;
+ DWORD nexttick; // only used by low precision timers
+ int flag; // only used by hi precision timers
+ float lost; // only used by hi precision timers
+ DWORD lastmscount;
+ int lastdelay;
+};
+
+class TimerMultiplexer : public ifc_dependentviewer
+{
+ public:
+
+ TimerMultiplexer();
+ virtual ~TimerMultiplexer();
+
+ virtual void setClient(TimerMultiplexerClient *client);
+
+ virtual void onServerTimer();
+
+ virtual void addTimer(int ms, void *data);
+ virtual void removeTimer(void *data);
+ virtual void setResolution(int ms);
+
+ virtual void shutdown();
+ virtual int getNumTimers();
+ virtual int getNumTimersLP();
+
+ private:
+
+ void checkResolution(DWORD now);
+ void resetTimer(int newresolution);
+ void resetWheel();
+ void distributeAll();
+ void distribute(MultiplexedTimer *t);
+ void runCurSlice(DWORD now);
+ void runTimer(DWORD now, DWORD last, MultiplexedTimer *t, PtrList<MultiplexedTimer> *slice, int pos);
+ void removeFromWheel(MultiplexedTimer *t);
+ void runLowPrecisionTimers(DWORD now);
+ void removeFromLowPrecision(MultiplexedTimer *t);
+ void doShutdown();
+ PtrList<MultiplexedTimer> *getSlice(int n);
+
+ TimerMultiplexerClient *client;
+ int resolution;
+ bool check_resolution;
+ int timerset;
+
+ int curslice;
+ int nslices;
+ int justexited;
+ int firstevent;
+
+ PtrList< PtrList< MultiplexedTimer > > wheel;
+ PtrList< MultiplexedTimer > timers;
+ PtrList< MultiplexedTimer > lptimers;
+ MultiplexedTimer *running_timer;
+ int dependentViewer_callback(ifc_dependent *item, const GUID *classguid, int cb, intptr_t param1 = 0, intptr_t param2 = 0, void *ptr = NULL, size_t ptrlen = 0);
+
+ CfgItem *uioptions;
+ RECVS_DISPATCH;
+};
+
+class MultiplexerServer {
+public:
+ MultiplexerServer(TimerMultiplexer *mux, UINT tid) : m_mux(mux), m_tid(tid) {}
+ virtual ~MultiplexerServer() {}
+ TimerMultiplexer *getMultiplexer() { return m_mux; }
+ UINT_PTR getId() { return m_tid; }
+ void setId(UINT_PTR id) { m_tid = id; }
+private:
+ TimerMultiplexer *m_mux;
+ UINT_PTR m_tid;
+};
+
+class MultiplexerServerComparatorTID {
+public:
+ // comparator for sorting
+ static int compareItem(MultiplexerServer *p1, MultiplexerServer* p2) {
+ if (p1->getId() < p2->getId()) return -1;
+ if (p1->getId() > p2->getId()) return 1;
+ return 0;
+ }
+ // comparator for searching
+ static int compareAttrib(const wchar_t *attrib, MultiplexerServer *item) {
+ if (*((UINT *)attrib) < item->getId()) return -1;
+ if (*((UINT *)attrib) < item->getId()) return 1;
+ return 0;
+ }
+};
+
+class MultiplexerServerComparatorMux{
+public:
+ // comparator for sorting
+ static int compareItem(MultiplexerServer *p1, MultiplexerServer* p2) {
+ if (p1->getMultiplexer() < p2->getMultiplexer()) return -1;
+ if (p1->getMultiplexer() > p2->getMultiplexer()) return 1;
+ return 0;
+ }
+ // comparator for searching
+ static int compareAttrib(const wchar_t *attrib, MultiplexerServer *item) {
+ if ((TimerMultiplexer *)attrib < item->getMultiplexer()) return -1;
+ if ((TimerMultiplexer *)attrib < item->getMultiplexer()) return 1;
+ return 0;
+ }
+};
+
+#endif
diff --git a/Src/timer/tmultiplex.cpp b/Src/timer/tmultiplex.cpp
new file mode 100644
index 00000000..e28b79d0
--- /dev/null
+++ b/Src/timer/tmultiplex.cpp
@@ -0,0 +1,128 @@
+#include "tmultiplex.h"
+#include <api/timer/timerclient.h>
+#include <assert.h>
+VirtualTimer::VirtualTimer(TimerClient *_client, intptr_t _id, api_dependent *depend) :
+ client(_client), id(_id), dep(depend)
+{
+ name = client->timerclient_getName();
+ mclient = client->timerclient_getMasterClient();
+}
+
+MainTimerMultiplexer::MainTimerMultiplexer() {
+ setClient(this);
+}
+
+MainTimerMultiplexer::~MainTimerMultiplexer() {
+/*
+ foreach(timerclients)
+ VirtualTimer *vt = timerclients.getfor();
+ //DebugString("TIMER MULTIPLEXER WARNING: TimerClient %X (%s) was not deregistered\n", vt->client, vt->name.getValue());
+ endfor;
+*/
+// NOTE: if you get a crash here, someone probably had a timer event outstanding
+// or didn't call down in timerclient_timerCallback()
+
+// Also this is guaranteed to happen if one of your timerclient objects (ie: a wnd) was not deleted
+// eventho your DLL has been unloaded. the watched pointer will remain watched instead of unregistering
+// itself from its viewers. DependentViewerI (one of our direct ancestors) will try to dereference that pointer in
+// order to signal it that a viewer was detached, and it will crash.
+
+ timerclients.deleteAll();
+}
+
+void MainTimerMultiplexer::add(TimerClient *client, intptr_t id, int ms) {
+ remove(client, id);
+ api_dependent *d = client->timerclient_getDependencyPtr();
+ assert(d != NULL);
+ VirtualTimer *t = new VirtualTimer(client, id, d);
+ timerclients.addItem(t);
+ viewer_addViewItem(d);
+ if (t->mclient) {
+ d = t->mclient->timerclient_getDependencyPtr();
+ ASSERT(d != NULL);
+ viewer_addViewItem(d);
+ }
+ addTimer(ms, static_cast<void *>(t));
+}
+
+void MainTimerMultiplexer::remove(TimerClient *client, intptr_t id) {
+ while (masters.haveItem(client)) masters.removeItem(client);
+ for (int i=0;i<timerclients.getNumItems();i++) {
+ VirtualTimer *t = timerclients.enumItem(i);
+ masters.removeItem(t->mclient);//BU store mclient on VirtualTimer now
+ if (t->client == client && (t->id == id || id == -1)) {
+ viewer_delViewItem(t->dep);
+ timerclients.removeByPos(i);
+ removeTimer(static_cast<void *>(t));
+ delete t;
+ i--;
+ }
+ }
+}
+
+int MainTimerMultiplexer::isValidTimerClientPtr(TimerClient *tc, api_dependent *dep) {
+// try {
+ __try {
+ api_dependent *d = tc->timerclient_getDependencyPtr();
+ if (d != dep) return 0;
+ //} catch (...) {
+ } __except (1) {
+ return 0;
+ }
+ return 1;
+}
+
+void MainTimerMultiplexer::onMultiplexedTimer(void *data, int skip, int mssincelasttimer) {
+ assert(data != NULL);
+ VirtualTimer *t = static_cast<VirtualTimer *>(data);
+ if (!isValidTimerClientPtr(t->client, t->dep)) {
+ //DebugString("TIMER MULTIPLEXER WARNING: TimerClient %X (%s) is no longer valid! (%d)\n", t->client, t->name.getValue(), t->id);
+ remove(t->client, -1);
+ t->client = 0;
+ } else {
+ TimerClient *mc = t->client->timerclient_getMasterClient();
+ if (mc) masters.addItem(mc);
+ t->client->timerclient_setSkipped(skip);
+ t->client->timerclient_setTimerDelay(mssincelasttimer);
+ t->client->timerclient_timerCallback(t->id);
+ // -----------------------------------------------------------------------
+ // WARNING
+ //
+ // below this line, you can no longer assume that t is pointing at valid
+ // memory, because timerCallback can eventually call remove
+ // -----------------------------------------------------------------------
+ }
+}
+
+void MainTimerMultiplexer::onServerTimer() {
+ TimerMultiplexer::onServerTimer();
+ TimerClient *last = NULL;
+ for (int i=0;i<masters.getNumItems();i++) {
+ TimerClient *t = masters.enumItem(i);
+ if (t == last) continue;
+ t->timerclient_onMasterClientMultiplex();
+ last = t;
+ }
+ masters.removeAll();
+}
+
+int MainTimerMultiplexer::haveClient(TimerClient *client) {
+ for (int i=0;i<timerclients.getNumItems();i++)
+ {
+ VirtualTimer *vt = timerclients.enumItem(i);
+ if (vt && vt->client == client)
+ return 1;
+ }
+ return 0;
+}
+
+int MainTimerMultiplexer::viewer_onItemDeleted(api_dependent *item) {
+ for (int i=0;i<timerclients.getNumItems();i++) {
+ VirtualTimer *vt = timerclients.enumItem(i);
+ if (vt->dep == item) {
+ remove(vt->client, -1);
+ return 1;
+ }
+ }
+ return 1;
+}
diff --git a/Src/timer/tmultiplex.h b/Src/timer/tmultiplex.h
new file mode 100644
index 00000000..3ed426ba
--- /dev/null
+++ b/Src/timer/tmultiplex.h
@@ -0,0 +1,45 @@
+#ifndef __MAIN_TIMERMULTIPLEXER_H
+#define __MAIN_TIMERMULTIPLEXER_H
+
+#include "timermul.h"
+#include <bfc/depend.h>
+#include <bfc/string/StringW.h>
+#include <api/timer/timerclient.h>
+
+class VirtualTimer
+{
+ public:
+ VirtualTimer(TimerClient *_client, intptr_t _id, api_dependent *depend);
+ virtual ~VirtualTimer() { }
+
+ TimerClient *client, *mclient;
+ api_dependent *dep;
+ StringW name;
+ intptr_t id;
+};
+
+class MainTimerMultiplexer : public TimerMultiplexer, public TimerMultiplexerClient, public DependentViewerI {
+ public:
+
+ MainTimerMultiplexer();
+ virtual ~MainTimerMultiplexer();
+
+ virtual void add(TimerClient *client, intptr_t id, int ms);
+ virtual void remove(TimerClient *client, intptr_t id);
+
+ virtual void onMultiplexedTimer(void *data, int skip, int mssincelast);
+ virtual void onServerTimer();
+
+ virtual int viewer_onItemDeleted(api_dependent *item);
+
+ private:
+
+ int isValidTimerClientPtr(TimerClient *tc, api_dependent *dep);
+
+ int haveClient(TimerClient *client);
+ PtrList<VirtualTimer> timerclients;
+ PtrListQuickSortedByPtrVal<TimerClient> masters;
+
+};
+
+#endif
diff --git a/Src/timer/version.rc2 b/Src/timer/version.rc2
new file mode 100644
index 00000000..69e70a60
--- /dev/null
+++ b/Src/timer/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION WINAMP_PRODUCTVER
+ PRODUCTVERSION WINAMP_PRODUCTVER
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Winamp SA"
+ VALUE "FileDescription", "Winamp 5.x System Component"
+ VALUE "FileVersion", STR_WINAMP_PRODUCTVER
+ VALUE "InternalName", "timer.w5s"
+ VALUE "LegalCopyright", "Copyright © 2005-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "timer.w5s"
+ VALUE "ProductName", "Winamp Timer API & Scripting Support Service"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END