diff options
Diffstat (limited to 'src/libs/cdp/cdpapi.c')
-rw-r--r-- | src/libs/cdp/cdpapi.c | 864 |
1 files changed, 864 insertions, 0 deletions
diff --git a/src/libs/cdp/cdpapi.c b/src/libs/cdp/cdpapi.c new file mode 100644 index 0000000..e50e39d --- /dev/null +++ b/src/libs/cdp/cdpapi.c @@ -0,0 +1,864 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/* + * CDP API definitions + * the API is used by both the engine and modules + */ + +#include "cdp.h" +#include "port.h" +#include "cdpint.h" +#include "uqmversion.h" + +#define MAX_REG_ITFS 255 +#define MAX_REG_EVENTS 1023 + +static cdp_Error cdp_api_error = CDPERR_NONE; + +static uint32 cdp_Host_GetApiVersion (void); +static uint32 cdp_Host_GetVersion (void); +static cdp_Error cdp_Host_GetApiError (void); +static cdp_Itf* cdp_Host_GetItf (const char* name); +static bool cdp_Host_GetItfs (cdp_ItfDef* defs); +static cdp_ItfReg* cdp_Host_RegisterItf (const char* name, + cdp_ApiVersion ver_from, cdp_ApiVersion ver_to, + cdp_Itf*, cdp_Module*); +static void cdp_Host_UnregisterItf (cdp_ItfReg*); +static bool cdp_Host_RegisterItfs (cdp_ItfDef* defs, cdp_Module*); +static void cdp_Host_UnregisterItfs (cdp_ItfDef* defs); +static cdp_Event cdp_Host_GetEvent (const char* name); +static bool cdp_Host_GetEvents (cdp_EventDef* defs); +static cdp_EventReg* cdp_Host_RegisterEvent (const char* name, cdp_Module*); +static void cdp_Host_UnregisterEvent (cdp_EventReg*); +static bool cdp_Host_RegisterEvents (cdp_EventDef* defs, cdp_Module*); +static void cdp_Host_UnregisterEvents (cdp_EventDef* defs); +static bool cdp_Host_SubscribeEvent (cdp_Event, cdp_EventProc, cdp_Module*); +static void cdp_Host_UnsubscribeEvent (cdp_Event, cdp_EventProc); +static bool cdp_Host_SubscribeEvents (cdp_EventDef* defs, cdp_Module*); +static void cdp_Host_UnsubscribeEvents (cdp_EventDef* defs); +static cdp_EventResult cdp_Host_FireEvent (cdp_EventReg*, uint32, void*); + +// Interfaces +cdp_Itf_HostVtbl_v1 cdp_host_itf_v1 = +{ + cdp_Host_GetApiVersion, + cdp_Host_GetVersion, + cdp_Host_GetApiError, + cdp_Host_GetItf, + cdp_Host_GetItfs, + cdp_Host_RegisterItf, + cdp_Host_UnregisterItf, + cdp_Host_RegisterItfs, + cdp_Host_UnregisterItfs, + cdp_Host_GetEvent, + cdp_Host_GetEvents, + cdp_Host_RegisterEvent, + cdp_Host_UnregisterEvent, + cdp_Host_RegisterEvents, + cdp_Host_UnregisterEvents, + cdp_Host_SubscribeEvent, + cdp_Host_UnsubscribeEvent, + cdp_Host_SubscribeEvents, + cdp_Host_UnsubscribeEvents, + cdp_Host_FireEvent, +}; + +cdp_Itf_MemoryVtbl_v1 cdp_memory_itf_v1 = +{ + HMalloc, + HFree, + HCalloc, + HRealloc, +}; + +cdp_Itf_IoVtbl_v1 cdp_io_itf_v1 = +{ + uio_fopen, + uio_fclose, + uio_fread, + uio_fwrite, + uio_fseek, + uio_ftell, + uio_fflush, + uio_feof, + uio_ferror, +}; + +cdp_Itf_SoundVtbl_v1 cdp_sound_itf_v1 = +{ + SoundDecoder_Register, + SoundDecoder_Unregister, + SoundDecoder_Lookup, +}; + +cdp_Itf_VideoVtbl_v1 cdp_video_itf_v1 = +{ + VideoDecoder_Register, + VideoDecoder_Unregister, + VideoDecoder_Lookup, +}; + +// the actual interface registration struct/handle +struct cdp_ItfReg +{ + bool builtin; + bool used; + const char* name; + cdp_ApiVersion ver_from; + cdp_ApiVersion ver_to; + cdp_Itf* itfvtbl; + cdp_Module* module; +}; + +#define CDP_DECLARE_ITF(kind,vf,vt,vtbl) \ + {true, true, CDPITF_KIND_##kind, \ + CDPAPI_VERSION_##vf, CDPAPI_VERSION_##vt, vtbl, NULL} + +// Built-in interfaces + space for loadable +cdp_ItfReg cdp_itfs[MAX_REG_ITFS + 1] = +{ + CDP_DECLARE_ITF (HOST, 1, 1, &cdp_host_itf_v1), + CDP_DECLARE_ITF (MEMORY, 1, 1, &cdp_memory_itf_v1), + CDP_DECLARE_ITF (IO, 1, 1, &cdp_io_itf_v1), + CDP_DECLARE_ITF (SOUND, 1, 1, &cdp_sound_itf_v1), + CDP_DECLARE_ITF (VIDEO, 1, 1, &cdp_video_itf_v1), + // TODO: put newly defined built-in interfaces here + + {false, false, "", 0, 0, NULL} // term +}; + +// event bind descriptor +typedef struct +{ + cdp_EventProc proc; + cdp_Module* module; + +} cdp_EventBind; + +#define EVENT_BIND_GROW 16 + +// the actual event registration struct/handle +struct cdp_EventReg +{ + bool builtin; + bool used; + const char* name; + cdp_EventBind* binds; + uint32 bindslots; + cdp_Module* module; +}; + +#define CDP_DECLARE_EVENT(name) \ + {true, true, "UQM." #name, NULL, 0, NULL} + +// Built-in events + space for loadable +// a cdp_Event handle is an index into this array +cdp_EventReg cdp_evts[MAX_REG_EVENTS + 1] = +{ + // sample - no real events defined yet + CDP_DECLARE_EVENT (PlanetSide.TouchDown), + CDP_DECLARE_EVENT (PlanetSide.LiftOff), + // TODO: put newly defined built-in events here + + {false, false, "", NULL, 0, NULL} // term +}; + +cdp_Error +cdp_GetApiError (void) +{ + cdp_Error ret = cdp_api_error; + cdp_api_error = CDPERR_NONE; + return ret; +} + +bool +cdp_InitApi (void) +{ + int i; + cdp_Module* kernel; + + // preprocess built-in itfs + kernel = cdp_LoadModule (NULL); + + for (i = 0; cdp_itfs[i].builtin; ++i) + { + cdp_itfs[i].module = kernel; + } + // clear the rest + //memset (cdp_itfs + i, 0, + // sizeof (cdp_itfs) - sizeof (cdp_ItfReg) * i); + + for (i = 0; cdp_evts[i].builtin; ++i) + { + cdp_evts[i].module = kernel; + cdp_evts[i].bindslots = 0; + cdp_evts[i].binds = NULL; + } + + return true; +} + +void +cdp_UninitApi (void) +{ + cdp_ItfReg* itf; + + // unregister custom interfaces + for (itf = cdp_itfs; itf->used; ++itf) + { + if (!itf->builtin) + { + itf->used = false; + if (itf->name) + HFree (itf->name); + itf->name = NULL; + itf->itfvtbl = NULL; + itf->module = NULL; + } + } +} + +static uint32 +cdp_Host_GetApiVersion (void) +{ + return CDPAPI_VERSION; +} + +static uint32 +cdp_Host_GetVersion (void) +{ + return (UQM_MAJOR_VERSION << 20) | (UQM_MINOR_VERSION << 15) | + UQM_PATCH_VERSION; +} + +static cdp_Error +cdp_Host_GetApiError (void) +{ + return cdp_GetApiError (); +} + +static char* +cdp_MakeContextName (const char* ctx, const char* name) +{ + int namelen; + char* id_name; + + namelen = strlen(ctx) + strlen(name) + 2; + id_name = HMalloc (namelen); + strcpy(id_name, ctx); + strcat(id_name, "."); + strcat(id_name, name); + + return id_name; +} + +/*********************************************************** + * Interface system * + ***********************************************************/ + +cdp_ItfReg* +cdp_GetInterfaceReg (const char* name, cdp_ApiVersion api_ver) +{ + cdp_ItfReg* itf; + + for (itf = cdp_itfs; itf->used && + (!itf->name || strcasecmp(itf->name, name) != 0 || + api_ver < itf->ver_from || api_ver > itf->ver_to); + itf++) + ; + if (!itf->name) + { + cdp_api_error = CDPERR_NO_ITF; + return NULL; + } + + return itf; +} + +cdp_Itf* +cdp_GetInterface (const char* name, cdp_ApiVersion api_ver) +{ + cdp_ItfReg* reg; + + reg = cdp_GetInterfaceReg (name, api_ver); + return reg ? reg->itfvtbl : NULL; +} + +static cdp_Itf* +cdp_Host_GetItf (const char* name) +{ + return cdp_GetInterface (name, CDPAPI_VERSION_1); +} + +static bool +cdp_Host_GetItfs (cdp_ItfDef* defs) +{ + cdp_ItfDef* def; + cdp_ItfReg* reg; + int errors = 0; + + for (def = defs; def->name; ++def) + { + // registration handle is not returned + def->reg = NULL; + + reg = cdp_GetInterfaceReg (def->name, CDPAPI_VERSION_1); + if (reg) + { + def->itf = reg->itfvtbl; + def->name = reg->name; // set to cannonical name + def->ver_from = reg->ver_from; + def->ver_to = reg->ver_to; + def->module = reg->module; + } + else + { + def->itf = NULL; + def->module = NULL; + def->ver_from = 0; + def->ver_to = 0; + ++errors; + } + } + + return !errors; +} + +static cdp_ItfReg* +cdp_Host_RegisterItf (const char* name, cdp_ApiVersion ver_from, + cdp_ApiVersion ver_to, cdp_Itf* itfvtbl, + cdp_Module* owner) +{ + cdp_ItfReg* itfreg; + cdp_ItfReg* newslot = NULL; + char* id_name; + const char* ctx; + + if (!owner) + { + fprintf (stderr, "cdp_Host_RegisterItf(): " + "No owner info supplied\n"); + //return NULL; + } + if (!name || !*name || !itfvtbl) + { + fprintf (stderr, "cdp_Host_RegisterItf(): " + "Null or invalid interface (from %s)\n", + cdp_GetModuleName (owner, true)); + return NULL; + } + ctx = cdp_GetModuleContext (owner, false); + if (!ctx) + { + fprintf (stderr, "cdp_Host_RegisterItf(): " + "Null or invalid context (from %s)\n", + cdp_GetModuleName (owner, true)); + return NULL; + } + + // TODO: review version policy (below) + // enforce version policy and do not allow obsolete interfaces + // POLICY: all modules MUST be aware of recent API changes and will not + // be allowed to expose interfaces that support and/or utilize obsoleted + // API versions + if (ver_from < CDPAPI_VERSION_MIN) + ver_from = CDPAPI_VERSION_MIN; + if (ver_to < CDPAPI_VERSION_MIN) + { + fprintf (stderr, "cdp_Host_RegisterItf(): " + "Obsolete interface %s (from %s)\n", + name, cdp_GetModuleName (owner, true)); + return NULL; + } + + id_name = cdp_MakeContextName (ctx, name); + + // check if interface already registered + for (itfreg = cdp_itfs; itfreg->used && + (!itfreg->name || strcasecmp(itfreg->name, id_name) != 0 || + ver_from < itfreg->ver_from || ver_to > itfreg->ver_to); + ++itfreg) + { + // and pick up an empty slot (where available) + if (!newslot && !itfreg->name) + newslot = itfreg; + } + + if (itfreg >= cdp_itfs + MAX_REG_ITFS) + { + fprintf (stderr, "cdp_Host_RegisterItf(): " + "Interfaces limit reached\n"); + HFree (id_name); + return NULL; + } + else if (itfreg->name) + { + fprintf (stderr, "cdp_Host_RegisterItf(): " + "Interface %s already registered for these versions, " + "%s denied\n", + name, cdp_GetModuleName (owner, true)); + HFree (id_name); + return NULL; + } + + if (!newslot) + { + newslot = itfreg; + newslot->used = true; + // make next one a term + itfreg[1].builtin = false; + itfreg[1].used = false; + itfreg[1].name = NULL; + itfreg[1].itfvtbl = NULL; + } + + newslot->name = id_name; + newslot->ver_from = ver_from; + newslot->ver_to = ver_to; + newslot->itfvtbl = itfvtbl; + newslot->module = owner; + + return newslot; +} + +static void +cdp_Host_UnregisterItf (cdp_ItfReg* itfreg) +{ + if (itfreg < cdp_itfs || itfreg >= cdp_itfs + MAX_REG_ITFS || + !itfreg->name || !itfreg->itfvtbl) + { + fprintf (stderr, "cdp_Host_UnregisterItf(): " + "Invalid or expired interface passed\n"); + return; + } + + if (!itfreg->builtin) + { + HFree (itfreg->name); + } + itfreg->module = NULL; + itfreg->name = NULL; + itfreg->itfvtbl = NULL; +} + +static bool +cdp_Host_RegisterItfs (cdp_ItfDef* defs, cdp_Module* owner) +{ + cdp_ItfDef* def; + int errors = 0; + + for (def = defs; def->name; ++def) + { + def->reg = cdp_Host_RegisterItf (def->name, def->ver_from, + def->ver_to, def->itf, owner); + if (def->reg) + { + def->module = owner; + } + else + { + def->module = NULL; + ++errors; + } + } + + return !errors; +} + +static void +cdp_Host_UnregisterItfs (cdp_ItfDef* defs) +{ + cdp_ItfDef* def; + + for (def = defs; def->name; ++def) + { + if (def->reg) + cdp_Host_UnregisterItf (def->reg); + } +} + +/*********************************************************** + * Event system * + ***********************************************************/ + +cdp_EventReg* +cdp_GetEventReg (const char* name) +{ + cdp_EventReg* evt; + + for (evt = cdp_evts; evt->used && + (!evt->name || strcasecmp(evt->name, name) != 0); + evt++) + ; + if (!evt->name) + { + cdp_api_error = CDPERR_NO_EVENT; + return NULL; + } + + return evt; +} + +// hopefully inlinable +static cdp_Event +cdp_EventFromReg (cdp_EventReg* reg) +{ + return (reg - cdp_evts) / sizeof (cdp_EventReg); +} + +// hopefully inlinable +static cdp_EventReg* +cdp_RegFromEvent (cdp_Event event) +{ + return cdp_evts + event; +} + +cdp_Event +cdp_GetEvent (const char* name) +{ + cdp_EventReg* reg; + + reg = cdp_GetEventReg (name); + return reg ? cdp_EventFromReg (reg) : CDP_EVENT_INVALID; +} + +static cdp_EventBind* +cdp_AllocEventBinds (cdp_EventBind* binds, uint32 ccur, uint32 cnew) +{ + cdp_EventBind* newbinds; + uint32 newsize; + + newsize = cnew * sizeof (cdp_EventBind); + if (binds) + newbinds = HRealloc (binds, newsize); + else + newbinds = HMalloc (newsize); + + if (cnew > ccur) + memset (newbinds + ccur, 0, + (cnew - ccur) * sizeof (cdp_EventBind)); + + return newbinds; +} + +static cdp_Event +cdp_Host_GetEvent (const char* name) +{ + return cdp_GetEvent (name); +} + +static bool +cdp_Host_GetEvents (cdp_EventDef* defs) +{ + cdp_EventDef* def; + cdp_EventReg* reg; + int errors = 0; + + for (def = defs; def->name; ++def) + { + // registration handle is not returned + def->reg = NULL; + + reg = cdp_GetEventReg (def->name); + if (reg) + { + def->event = cdp_EventFromReg(reg); + def->name = reg->name; // set to cannonical name + def->module = reg->module; + } + else + { + def->event = CDP_EVENT_INVALID; + def->module = NULL; + ++errors; + } + } + + return !errors; +} + +static cdp_EventReg* +cdp_Host_RegisterEvent (const char* name, cdp_Module* owner) +{ + cdp_EventReg* evtreg; + cdp_EventReg* newslot = NULL; + char* id_name; + const char* ctx; + + if (!owner) + { + fprintf (stderr, "cdp_Host_RegisterEvent(): " + "No owner info supplied\n"); + //return NULL; + } + if (!name || !*name) + { + fprintf (stderr, "cdp_Host_RegisterEvent(): " + "Null or invalid event (from %s)\n", + cdp_GetModuleName (owner, true)); + return NULL; + } + ctx = cdp_GetModuleContext (owner, false); + if (!ctx) + { + fprintf (stderr, "cdp_Host_RegisterEvent(): " + "Null or invalid context (from %s)\n", + cdp_GetModuleName (owner, true)); + return NULL; + } + + id_name = cdp_MakeContextName (ctx, name); + + // check if event already registered + for (evtreg = cdp_evts; evtreg->used && + (!evtreg->name || strcasecmp(evtreg->name, id_name) != 0); + ++evtreg) + { + // and pick up an empty slot (where available) + if (!newslot && !evtreg->name) + newslot = evtreg; + } + + if (evtreg >= cdp_evts + MAX_REG_EVENTS) + { + fprintf (stderr, "cdp_Host_RegisterEvent(): " + "Event limit reached\n"); + HFree (id_name); + return NULL; + } + else if (evtreg->name) + { + fprintf (stderr, "cdp_Host_RegisterEvent(): " + "Event %s already registered, " + "%s denied\n", + name, cdp_GetModuleName (owner, true)); + HFree (id_name); + return NULL; + } + + if (!newslot) + { + newslot = evtreg; + newslot->used = true; + // make next one a term + evtreg[1].builtin = false; + evtreg[1].used = false; + evtreg[1].name = NULL; + } + + newslot->name = id_name; + newslot->module = owner; + newslot->binds = NULL; + newslot->bindslots = 0; + + return newslot; +} + +static void +cdp_Host_UnregisterEvent (cdp_EventReg* evtreg) +{ + if (evtreg < cdp_evts || evtreg >= cdp_evts + MAX_REG_EVENTS || + !evtreg->name) + { + fprintf (stderr, "cdp_Host_UnregisterEvent(): " + "Invalid or expired event passed\n"); + return; + } + + if (!evtreg->builtin) + { + HFree (evtreg->name); + } + evtreg->module = NULL; + evtreg->name = NULL; + if (evtreg->binds) + HFree (evtreg->binds); + evtreg->binds = NULL; + evtreg->bindslots = 0; +} + +static bool +cdp_Host_RegisterEvents (cdp_EventDef* defs, cdp_Module* owner) +{ + cdp_EventDef* def; + int errors = 0; + + for (def = defs; def->name; ++def) + { + def->reg = cdp_Host_RegisterEvent (def->name, owner); + if (def->reg) + { + def->module = owner; + } + else + { + def->module = NULL; + ++errors; + } + } + + return !errors; +} + +static void +cdp_Host_UnregisterEvents (cdp_EventDef* defs) +{ + cdp_EventDef* def; + + for (def = defs; def->name; ++def) + { + if (def->reg) + cdp_Host_UnregisterEvent (def->reg); + } +} + +static bool +cdp_Host_SubscribeEvent (cdp_Event event, cdp_EventProc proc, cdp_Module* module) +{ + cdp_EventReg* reg = cdp_RegFromEvent (event); + cdp_EventBind* bind = NULL; + uint32 i; + + if (reg < cdp_evts || reg >= cdp_evts + MAX_REG_EVENTS || + !reg->name) + { + fprintf (stderr, "cdp_Host_SubscribeEvent(): " + "Invalid or expired event passed\n"); + return false; + } + + if (reg->binds) + { + // check for duplicate or find a new slot + for (i = 0, bind = reg->binds; i < reg->bindslots && + (!bind->proc || bind->proc != proc); + ++i, ++bind) + ; + if (i >= reg->bindslots) + { // full - add more slots + reg->binds = cdp_AllocEventBinds (reg->binds, + reg->bindslots, reg->bindslots + EVENT_BIND_GROW); + bind = reg->binds + reg->bindslots; + reg->bindslots += EVENT_BIND_GROW; + } + else if (bind->proc == proc) + { // already bound + return true; + } + } + else + { + reg->binds = cdp_AllocEventBinds (NULL, 0, EVENT_BIND_GROW); + reg->bindslots = EVENT_BIND_GROW; + bind = reg->binds; + } + + bind->proc = proc; + bind->module = module; + + return true; +} + +static void +cdp_Host_UnsubscribeEvent (cdp_Event event, cdp_EventProc proc) +{ + cdp_EventReg* reg = cdp_RegFromEvent (event); + cdp_EventBind* bind = NULL; + uint32 i; + + if (reg < cdp_evts || reg >= cdp_evts + MAX_REG_EVENTS || + !reg->name) + { // event either expired or invalid + return; + } + + if (!reg->binds || !reg->bindslots) + return; // hmm, no bindings + + // check for duplicate or find a new slot + for (i = 0, bind = reg->binds; i < reg->bindslots && + bind->proc != proc; + ++i, ++bind) + ; + if (i >= reg->bindslots) + return; // binding not found + + bind->proc = NULL; + bind->module = NULL; +} + +static bool +cdp_Host_SubscribeEvents (cdp_EventDef* defs, cdp_Module* module) +{ + cdp_EventDef* def; + int errors = 0; + + for (def = defs; def->name; ++def) + { + if (def->event != CDP_EVENT_INVALID && def->proc) + if (!cdp_Host_SubscribeEvent (def->event, def->proc, module)) + ++errors; + } + return !errors; +} + +static void +cdp_Host_UnsubscribeEvents (cdp_EventDef* defs) +{ + cdp_EventDef* def; + + for (def = defs; def->name; ++def) + { + if (def->event != CDP_EVENT_INVALID && def->proc) + cdp_Host_UnsubscribeEvent (def->event, def->proc); + } +} + +static cdp_EventResult +cdp_Host_FireEvent (cdp_EventReg* evtreg, uint32 iparam, void* pparam) +{ + bool bHandled = false; + cdp_EventResult ret = 0; + cdp_Event event; + cdp_EventBind* bind; + uint32 i; + + if (evtreg < cdp_evts || evtreg >= cdp_evts + MAX_REG_EVENTS || + !evtreg->name) + { +#ifdef DEBUG + fprintf (stderr, "cdp_Host_FireEvent(): Invalid event\n"); +#endif + return 0; + } + + if (!evtreg->binds) + return 0; // no subscribers + + event = cdp_EventFromReg (evtreg); + + // call event procs in opposite order of binding + for (i = evtreg->bindslots, bind = evtreg->binds + i - 1; + !bHandled && i > 0; + --i, --bind) + { + if (bind->proc) + ret = bind->proc (event, iparam, pparam, &bHandled); + } + return ret; +} |