1084 lines
27 KiB
C
1084 lines
27 KiB
C
|
/*****************************************************************************
|
||
|
*
|
||
|
* This program is free software ; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* 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
|
||
|
*
|
||
|
* $Id: node.c 585 2006-01-16 09:48:55Z picard $
|
||
|
*
|
||
|
* The Core Pocket Media Player
|
||
|
* Copyright (c) 2004-2005 Gabor Kovacs
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
#include "common.h"
|
||
|
|
||
|
#define MODULE_PATH FOURCC('M','P','A','T')
|
||
|
#define PLUGIN_KEEPALIVE 3*60
|
||
|
|
||
|
typedef struct nodeclass
|
||
|
{
|
||
|
nodedef Def;
|
||
|
bool_t Registered;
|
||
|
int ModuleNo;
|
||
|
struct nodeclass* Parent;
|
||
|
|
||
|
} nodeclass;
|
||
|
|
||
|
typedef struct nodemodule
|
||
|
{
|
||
|
int Id;
|
||
|
int ObjectCount;
|
||
|
bool_t Tmp;
|
||
|
int64_t Date;
|
||
|
int KeepAlive;
|
||
|
void* Module;
|
||
|
void* Db;
|
||
|
void* Func;
|
||
|
uint8_t* Min;
|
||
|
uint8_t* Max;
|
||
|
|
||
|
} nodemodule;
|
||
|
|
||
|
static NOINLINE void FreeModule(nodemodule* p)
|
||
|
{
|
||
|
NodeFreeModule(p->Module,p->Db);
|
||
|
p->Module = NULL;
|
||
|
p->Db = NULL;
|
||
|
p->Func = NULL;
|
||
|
p->Min = NULL;
|
||
|
}
|
||
|
|
||
|
void NodeAddModule(const tchar_t* Path,int Id,int64_t Date,bool_t Load,bool_t Tmp)
|
||
|
{
|
||
|
context* p = Context();
|
||
|
LockEnter(p->NodeLock);
|
||
|
if (ArrayAppend(&p->NodeModule,NULL,sizeof(nodemodule),256))
|
||
|
{
|
||
|
int No = ARRAYCOUNT(p->NodeModule,nodemodule)-1;
|
||
|
nodemodule* Module = ARRAYBEGIN(p->NodeModule,nodemodule)+No;
|
||
|
memset(Module,0,sizeof(nodemodule));
|
||
|
Module->Id = Id;
|
||
|
Module->Date = Date;
|
||
|
Module->Tmp = Tmp;
|
||
|
|
||
|
StringAdd(1,MODULE_PATH,No,Path);
|
||
|
if (Load)
|
||
|
{
|
||
|
p->LoadModuleNo = No;
|
||
|
Module->Module = NodeLoadModule(Path,&Module->Id,&Module->Func,&Module->Db);
|
||
|
p->LoadModuleNo = 0;
|
||
|
#ifdef PLUGINCLEANUP
|
||
|
FreeModule(Module);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
LockLeave(p->NodeLock);
|
||
|
}
|
||
|
|
||
|
static NOINLINE bool_t TmpModule(context* p,int No)
|
||
|
{
|
||
|
return ARRAYBEGIN(p->NodeModule,nodemodule)[No].Tmp;
|
||
|
}
|
||
|
|
||
|
static NOINLINE int FindModule(context* p,const tchar_t* Path,int Id)
|
||
|
{
|
||
|
// important to find from the begining for Palm OS
|
||
|
// so the exising plugins are found first
|
||
|
|
||
|
int No,Count;
|
||
|
int Result = -1;
|
||
|
|
||
|
LockEnter(p->NodeLock);
|
||
|
Count = ARRAYCOUNT(p->NodeModule,nodemodule);
|
||
|
if (!Path) Path = T("");
|
||
|
for (No=0;No<Count;++No)
|
||
|
{
|
||
|
bool_t SameId = ARRAYBEGIN(p->NodeModule,nodemodule)[No].Id == Id;
|
||
|
bool_t SameName = tcsicmp(Path,LangStrDef(MODULE_PATH,No))==0;
|
||
|
|
||
|
if (SameId && Id!=0) // same Id means same module
|
||
|
SameName = 1;
|
||
|
if (SameName && !Id)
|
||
|
SameId = 1;
|
||
|
|
||
|
if (SameId && SameName)
|
||
|
{
|
||
|
Result = No;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
LockLeave(p->NodeLock);
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
int NodeGetClassCount()
|
||
|
{
|
||
|
return ARRAYCOUNT(Context()->NodeClass,nodeclass*);
|
||
|
}
|
||
|
|
||
|
const nodedef* NodeGetClass(int No,int* Module)
|
||
|
{
|
||
|
nodeclass* i = ARRAYBEGIN(Context()->NodeClass,nodeclass*)[No];
|
||
|
if (Module)
|
||
|
*Module = i->ModuleNo;
|
||
|
return &i->Def;
|
||
|
}
|
||
|
|
||
|
int NodeGetModuleCount()
|
||
|
{
|
||
|
return ARRAYCOUNT(Context()->NodeModule,nodemodule);
|
||
|
}
|
||
|
|
||
|
const tchar_t* NodeGetModule(int No,int* Id,int64_t* Date)
|
||
|
{
|
||
|
*Id = ARRAYBEGIN(Context()->NodeModule,nodemodule)[No].Id;
|
||
|
*Date = ARRAYBEGIN(Context()->NodeModule,nodemodule)[No].Date;
|
||
|
return LangStrDef(MODULE_PATH,No);
|
||
|
}
|
||
|
|
||
|
bool_t NodeFindModule(const tchar_t* Path,int Id)
|
||
|
{
|
||
|
return FindModule(Context(),Path,Id)>0;
|
||
|
}
|
||
|
|
||
|
static int CmpClass(const nodeclass* const* a, const nodeclass* const* b)
|
||
|
{
|
||
|
int AClass = (*a)->Def.Class;
|
||
|
int BClass = (*b)->Def.Class;
|
||
|
if (AClass > BClass) return 1;
|
||
|
if (AClass < BClass) return -1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int CmpClassPri(const nodeclass* const* a, const nodeclass* const* b)
|
||
|
{
|
||
|
int APriority = (*a)->Def.Priority;
|
||
|
int BPriority = (*b)->Def.Priority;
|
||
|
if (APriority > BPriority) return -1;
|
||
|
if (APriority < BPriority) return 1;
|
||
|
return CmpClass(a,b);
|
||
|
}
|
||
|
|
||
|
static NOINLINE nodeclass* FindClass(context* p,int Class)
|
||
|
{
|
||
|
int Pos;
|
||
|
bool_t Found;
|
||
|
nodeclass Item;
|
||
|
nodeclass* Ptr = &Item;
|
||
|
|
||
|
LockEnter(p->NodeLock);
|
||
|
Item.Def.Class = Class;
|
||
|
Pos = ArrayFind(&p->NodeClass,ARRAYCOUNT(p->NodeClass,nodeclass*),
|
||
|
sizeof(nodeclass*),&Ptr,(arraycmp)CmpClass,&Found);
|
||
|
if (Found)
|
||
|
Ptr = ARRAYBEGIN(p->NodeClass,nodeclass*)[Pos];
|
||
|
else
|
||
|
Ptr = NULL;
|
||
|
LockLeave(p->NodeLock);
|
||
|
return Ptr;
|
||
|
}
|
||
|
|
||
|
static int CmpNodePri(const node* const* a,const node* const* b)
|
||
|
{
|
||
|
context* p = Context();
|
||
|
const nodeclass* ca = FindClass(p,(*a)->Class);
|
||
|
const nodeclass* cb = FindClass(p,(*b)->Class);
|
||
|
return CmpClassPri(&ca,&cb);
|
||
|
}
|
||
|
|
||
|
static NOINLINE int ReleaseModules(context* p,bool_t Force)
|
||
|
{
|
||
|
int n=0;
|
||
|
int Time = Force ? MAX_INT:GetTimeTick();
|
||
|
nodemodule* j;
|
||
|
LockEnter(p->NodeLock);
|
||
|
for (j=ARRAYBEGIN(p->NodeModule,nodemodule);j!=ARRAYEND(p->NodeModule,nodemodule);++j)
|
||
|
if (j->ObjectCount==0 && j->Module && j->KeepAlive < Time)
|
||
|
{
|
||
|
//DEBUG_MSG1(-1,T("FreeModule %s"),LangStrDef(MODULE_PATH,j-ARRAYBEGIN(p->NodeModule,nodemodule)));
|
||
|
FreeModule(j);
|
||
|
++n;
|
||
|
}
|
||
|
LockLeave(p->NodeLock);
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
static NOINLINE void UnlockModule(context* p,int No)
|
||
|
{
|
||
|
nodemodule* Module = ARRAYBEGIN(p->NodeModule,nodemodule)+No;
|
||
|
if (--Module->ObjectCount==0 && Module->Module)
|
||
|
{
|
||
|
#ifdef PLUGINCLEANUP
|
||
|
Module->KeepAlive = GetTimeTick() + GetTimeFreq()*PLUGIN_KEEPALIVE;
|
||
|
ReleaseModules(p,0);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static NOINLINE void CallDelete(context* p,node* Node,nodeclass* Class0,bool_t Global)
|
||
|
{
|
||
|
nodeclass* Class;
|
||
|
|
||
|
for (Class=Class0;Class;Class=Class->Parent)
|
||
|
if (Class->Def.Delete)
|
||
|
Class->Def.Delete(Node);
|
||
|
|
||
|
if (!Global)
|
||
|
for (Class=Class0;Class;Class=Class->Parent)
|
||
|
UnlockModule(p,Class->ModuleNo);
|
||
|
}
|
||
|
|
||
|
static NOINLINE nodemodule* LoadModule(context* p,int No)
|
||
|
{
|
||
|
nodemodule* Module = ARRAYBEGIN(p->NodeModule,nodemodule)+No;
|
||
|
if (!Module->Module && No>0 && p->LoadModuleNo!=No)
|
||
|
{
|
||
|
//DEBUG_MSG1(-1,T("LoadModule %s"),LangStrDef(MODULE_PATH,No));
|
||
|
p->LoadModuleNo = No;
|
||
|
Module->Module = NodeLoadModule(LangStrDef(MODULE_PATH,No),&Module->Id,&Module->Func,&Module->Db);
|
||
|
p->LoadModuleNo = 0;
|
||
|
if (!Module->Module)
|
||
|
return NULL;
|
||
|
}
|
||
|
return Module;
|
||
|
}
|
||
|
|
||
|
static int CallCreate(context* p,node* Node,nodeclass* Class,bool_t Global)
|
||
|
{
|
||
|
if (Class)
|
||
|
{
|
||
|
nodemodule* Module = LoadModule(p,Class->ModuleNo);
|
||
|
if (!Module)
|
||
|
return ERR_NOT_SUPPORTED;
|
||
|
|
||
|
if (Class->Def.ParentClass && !Class->Parent)
|
||
|
return ERR_NOT_SUPPORTED;
|
||
|
|
||
|
if (CallCreate(p,Node,Class->Parent,Global)!=ERR_NONE)
|
||
|
return ERR_NOT_SUPPORTED;
|
||
|
|
||
|
if (!Class->Registered || (Class->Def.Create && Class->Def.Create(Node) != ERR_NONE))
|
||
|
{
|
||
|
CallDelete(p,Node,Class->Parent,Global);
|
||
|
return ERR_NOT_SUPPORTED;
|
||
|
}
|
||
|
|
||
|
if (!Global)
|
||
|
Module->ObjectCount++;
|
||
|
}
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
static void Delete(context* p,node** i)
|
||
|
{
|
||
|
node* Node = *i;
|
||
|
nodeclass* Item = FindClass(p,Node->Class);
|
||
|
*i = NULL;
|
||
|
if (Item)
|
||
|
CallDelete(p,Node,Item,(Item->Def.Flags & CF_GLOBAL)!=0);
|
||
|
free(Node);
|
||
|
}
|
||
|
|
||
|
static void Register(context* p,const nodedef* Def,bool_t Registered,int ModuleNo)
|
||
|
{
|
||
|
nodeclass* Class;
|
||
|
|
||
|
LockEnter(p->NodeLock);
|
||
|
|
||
|
Class = FindClass(p,Def->Class);
|
||
|
if (!Class)
|
||
|
{
|
||
|
nodeclass **c;
|
||
|
|
||
|
Class = (nodeclass*) malloc(sizeof(nodeclass));
|
||
|
if (!Class)
|
||
|
{
|
||
|
LockLeave(p->NodeLock);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Class->Def.Class = Def->Class;
|
||
|
Class->Def.ParentClass = 0;
|
||
|
Class->Registered = 0;
|
||
|
Class->ModuleNo = 0;
|
||
|
Class->Def.Flags = 0;
|
||
|
Class->Def.Priority = -1;
|
||
|
if (!ArrayAdd(&p->NodeClass,ARRAYCOUNT(p->NodeClass,nodeclass*),
|
||
|
sizeof(nodeclass*),&Class,(arraycmp)CmpClass,128))
|
||
|
{
|
||
|
free(Class);
|
||
|
LockLeave(p->NodeLock);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// fix possible child classes
|
||
|
for (c=ARRAYBEGIN(p->NodeClass,nodeclass*);c!=ARRAYEND(p->NodeClass,nodeclass*);++c)
|
||
|
if ((*c)->Def.ParentClass == Def->Class)
|
||
|
(*c)->Parent = Class;
|
||
|
}
|
||
|
else
|
||
|
if (!Registered)
|
||
|
{
|
||
|
LockLeave(p->NodeLock);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (Registered)
|
||
|
{
|
||
|
if (Class->Def.Priority > Def->Priority && Class->ModuleNo != ModuleNo)
|
||
|
{
|
||
|
LockLeave(p->NodeLock);
|
||
|
return; // already exits with higher priority in another module
|
||
|
}
|
||
|
|
||
|
#ifndef REGISTRY_GLOBAL
|
||
|
if (ModuleNo>0 && (Class->ModuleNo != ModuleNo || Class->Def.ParentClass != Def->ParentClass || Class->Def.Flags != Def->Flags))
|
||
|
{
|
||
|
// save settings to registry for new classes
|
||
|
NodeRegSaveString(Def->Class,NODE_NAME);
|
||
|
NodeRegSaveString(Def->Class,NODE_CONTENTTYPE);
|
||
|
NodeRegSaveString(Def->Class,NODE_EXTS);
|
||
|
NodeRegSaveString(Def->Class,NODE_PROBE);
|
||
|
NodeRegSaveValue(Def->Class,NODE_MODULE_PATH,LangStrDef(MODULE_PATH,ModuleNo),MAXPATH,TYPE_STRING);
|
||
|
NodeRegSaveValue(Def->Class,NODE_PARENT,&Def->ParentClass,sizeof(int),TYPE_INT);
|
||
|
NodeRegSaveValue(Def->Class,NODE_FLAGS,&Def->Flags,sizeof(int),TYPE_INT);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
if (Class->Def.Priority != Def->Priority)
|
||
|
{
|
||
|
ArrayRemove(&p->NodeClassPri,ARRAYCOUNT(p->NodeClassPri,nodeclass*),
|
||
|
sizeof(nodeclass*),&Class,(arraycmp)CmpClassPri);
|
||
|
Class->Def.Priority = Def->Priority;
|
||
|
ArrayAdd(&p->NodeClassPri,ARRAYCOUNT(p->NodeClassPri,nodeclass*),
|
||
|
sizeof(nodeclass*),&Class,(arraycmp)CmpClassPri,128);
|
||
|
|
||
|
#ifndef REGISTRY_GLOBAL
|
||
|
if (ModuleNo>0 && Registered)
|
||
|
NodeRegSaveValue(Def->Class,NODE_PRIORITY,Def->Priority!=PRI_DEFAULT?&Def->Priority:NULL,sizeof(int),TYPE_INT);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
Class->Def = *Def;
|
||
|
Class->ModuleNo = ModuleNo;
|
||
|
Class->Parent = FindClass(p,Def->ParentClass);
|
||
|
Class->Registered = Registered;
|
||
|
|
||
|
LockLeave(p->NodeLock);
|
||
|
}
|
||
|
|
||
|
void NodeLoadClass(const nodedef* Def,const tchar_t* Module,int ModuleId)
|
||
|
{
|
||
|
context* p = Context();
|
||
|
int ModuleNo = FindModule(p,Module,ModuleId);
|
||
|
if (ModuleNo>=0)
|
||
|
Register(p,Def,0,ModuleNo);
|
||
|
}
|
||
|
|
||
|
void NodeUnRegisterClass(int ClassId)
|
||
|
{
|
||
|
context* p = Context();
|
||
|
nodeclass* Class = FindClass(p,ClassId);
|
||
|
if (Class && Class->Registered)
|
||
|
{
|
||
|
node **i;
|
||
|
|
||
|
LockEnter(p->NodeLock);
|
||
|
|
||
|
Class->Registered = 0;
|
||
|
|
||
|
// delete all objects
|
||
|
for (i=ARRAYBEGIN(p->Node,node*);i!=ARRAYEND(p->Node,node*);++i)
|
||
|
if (*i && NodeIsClass((*i)->Class,ClassId))
|
||
|
Delete(p,i);
|
||
|
|
||
|
Class->Def.Create = NULL;
|
||
|
Class->Def.Delete = NULL;
|
||
|
|
||
|
LockLeave(p->NodeLock);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static node* NodeCreateFromClass(context* p, nodeclass* Class)
|
||
|
{
|
||
|
node **Empty = NULL;
|
||
|
node **i;
|
||
|
nodemodule* Module;
|
||
|
node* Node;
|
||
|
int Size;
|
||
|
nodeclass *j;
|
||
|
|
||
|
if (!Class || (Class->Def.Flags & CF_ABSTRACT))
|
||
|
return NULL;
|
||
|
|
||
|
Size = 0;
|
||
|
for (j=Class;j;j=j->Parent)
|
||
|
if (Size < (j->Def.Flags & CF_SIZE))
|
||
|
Size = (j->Def.Flags & CF_SIZE);
|
||
|
if (!Size)
|
||
|
return NULL;
|
||
|
|
||
|
LockEnter(p->NodeLock);
|
||
|
|
||
|
if ((Class->Def.Flags & CF_GLOBAL) &&
|
||
|
(Module = LoadModule(p,Class->ModuleNo))!=NULL &&
|
||
|
(Node = NodeEnumObject(NULL,Class->Def.Class))!=NULL)
|
||
|
{
|
||
|
++Module->ObjectCount;
|
||
|
LockLeave(p->NodeLock);
|
||
|
return Node;
|
||
|
}
|
||
|
|
||
|
for (i=ARRAYBEGIN(p->Node,node*);i!=ARRAYEND(p->Node,node*);++i)
|
||
|
if (!*i)
|
||
|
{
|
||
|
Empty = i;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!Empty)
|
||
|
{
|
||
|
if (!ArrayAppend(&p->Node,NULL,sizeof(node**),256))
|
||
|
{
|
||
|
LockLeave(p->NodeLock);
|
||
|
return NULL;
|
||
|
}
|
||
|
Empty = ARRAYEND(p->Node,node*)-1;
|
||
|
}
|
||
|
|
||
|
*Empty = Node = (node*) malloc(Size);
|
||
|
if (Node)
|
||
|
{
|
||
|
memset(Node,0,Size);
|
||
|
Node->Class = Class->Def.Class;
|
||
|
if (CallCreate(p,Node,Class,(Class->Def.Flags & CF_GLOBAL)!=0) != ERR_NONE)
|
||
|
{
|
||
|
for (i=ARRAYBEGIN(p->Node,node*);i!=ARRAYEND(p->Node,node*);++i)
|
||
|
if (*i == Node)
|
||
|
*i = NULL;
|
||
|
free(Node);
|
||
|
Node = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#ifndef REGISTRY_GLOBAL
|
||
|
if (Class->Def.Flags & CF_GLOBAL)
|
||
|
NodeRegLoad(Node);
|
||
|
#endif
|
||
|
Node->Set(Node,NODE_SETTINGSCHANGED,NULL,0); // should be after NodeRegLoad
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LockLeave(p->NodeLock);
|
||
|
return Node;
|
||
|
}
|
||
|
|
||
|
void NodeRegisterClass(const nodedef* Def)
|
||
|
{
|
||
|
context* p = Context();
|
||
|
if (Def->Flags & CF_GLOBAL)
|
||
|
{
|
||
|
//create global object with fake class and don't register if fails
|
||
|
nodeclass Class;
|
||
|
memset(&Class,0,sizeof(Class));
|
||
|
Class.Def = *Def;
|
||
|
Class.ModuleNo = p->LoadModuleNo;
|
||
|
Class.Parent = FindClass(p,Def->ParentClass);
|
||
|
Class.Registered = 1;
|
||
|
if (!Class.Parent || !NodeCreateFromClass(p,&Class))
|
||
|
return;
|
||
|
}
|
||
|
Register(p,Def,1,p->LoadModuleNo);
|
||
|
}
|
||
|
|
||
|
node* NodeCreate(int ClassId)
|
||
|
{
|
||
|
context* p = Context();
|
||
|
return NodeCreateFromClass(p,FindClass(p,ClassId));
|
||
|
}
|
||
|
|
||
|
void NodeDelete(node* Node)
|
||
|
{
|
||
|
if (Node)
|
||
|
{
|
||
|
nodeclass* Class;
|
||
|
context* p = Context();
|
||
|
|
||
|
LockEnter(p->NodeLock);
|
||
|
Class = FindClass(p,Node->Class);
|
||
|
if (Class && (Class->Def.Flags & CF_GLOBAL))
|
||
|
UnlockModule(p,Class->ModuleNo);
|
||
|
else
|
||
|
{
|
||
|
node **i;
|
||
|
for (i=ARRAYBEGIN(p->Node,node*);i!=ARRAYEND(p->Node,node*);++i)
|
||
|
if (*i == Node)
|
||
|
{
|
||
|
Delete(p,i);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
LockLeave(p->NodeLock);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const nodedef* NodeClassDef(int Class)
|
||
|
{
|
||
|
nodeclass* i = FindClass(Context(),Class);
|
||
|
return i?&i->Def:NULL;
|
||
|
}
|
||
|
|
||
|
static NOINLINE bool_t GetNode(context* p,int n,node** Node)
|
||
|
{
|
||
|
node** i;
|
||
|
bool_t Result = 0;
|
||
|
LockEnter(p->NodeLock);
|
||
|
i = ARRAYBEGIN(p->Node,node*)+n;
|
||
|
if (i<ARRAYEND(p->Node,node*))
|
||
|
{
|
||
|
*Node = *i;
|
||
|
Result = 1;
|
||
|
}
|
||
|
LockLeave(p->NodeLock);
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
bool_t NodeHibernate()
|
||
|
{
|
||
|
int Mode;
|
||
|
int No;
|
||
|
node* Node;
|
||
|
context* p = Context();
|
||
|
bool_t Changed = 0;
|
||
|
|
||
|
if (p && !p->InHibernate)
|
||
|
{
|
||
|
p->InHibernate = 1;
|
||
|
|
||
|
#ifdef PLUGINCLEANUP
|
||
|
if (ReleaseModules(p,1))
|
||
|
Changed = 1;
|
||
|
#endif
|
||
|
|
||
|
for (Mode=0;!Changed && Mode<HIBERNATEMODES;++Mode)
|
||
|
for (No=0;GetNode(p,No,&Node);++No)
|
||
|
if (Node && Node->Set && Node->Set(Node,NODE_HIBERNATE,&Mode,sizeof(int))==ERR_NONE)
|
||
|
Changed = 1;
|
||
|
|
||
|
if (!Changed && ReleaseModules(p,1))
|
||
|
Changed = 1;
|
||
|
|
||
|
p->InHibernate = 0;
|
||
|
}
|
||
|
return Changed;
|
||
|
}
|
||
|
|
||
|
int NodeBroadcast(int Msg,const void* Data,int Size)
|
||
|
{
|
||
|
int No;
|
||
|
context* p = Context();
|
||
|
node* Node;
|
||
|
|
||
|
for (No=0;GetNode(p,No,&Node);++No)
|
||
|
if (Node && Node->Set)
|
||
|
Node->Set(Node,Msg,Data,Size);
|
||
|
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
int NodeSettingsChanged()
|
||
|
{
|
||
|
return NodeBroadcast(NODE_SETTINGSCHANGED,NULL,0);
|
||
|
}
|
||
|
|
||
|
node* NodeEnumObject(array* List, int Class)
|
||
|
{
|
||
|
context* p = Context();
|
||
|
node **i;
|
||
|
|
||
|
if (List)
|
||
|
memset(List,0,sizeof(array));
|
||
|
|
||
|
LockEnter(p->NodeLock);
|
||
|
|
||
|
for (i=ARRAYBEGIN(p->Node,node*);i!=ARRAYEND(p->Node,node*);++i)
|
||
|
if (*i && NodeIsClass((*i)->Class,Class))
|
||
|
{
|
||
|
if (!List)
|
||
|
{
|
||
|
node* Node = *i;
|
||
|
LockLeave(p->NodeLock);
|
||
|
return Node;
|
||
|
}
|
||
|
ArrayAppend(List,i,sizeof(node**),64);
|
||
|
}
|
||
|
|
||
|
LockLeave(p->NodeLock);
|
||
|
|
||
|
if (List)
|
||
|
ArraySort(List,ARRAYCOUNT(*List,node*),sizeof(node*),(arraycmp)CmpNodePri);
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
bool_t NodeIsClass(int ClassId, int PartOfClass)
|
||
|
{
|
||
|
nodeclass* Item;
|
||
|
for (Item = FindClass(Context(),ClassId);Item;Item=Item->Parent)
|
||
|
if (Item->Def.Class == PartOfClass)
|
||
|
return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void DumpPtr(stream* File, tchar_t* s, node* Ptr, int No)
|
||
|
{
|
||
|
if (Ptr)
|
||
|
{
|
||
|
tchar_t v[32];
|
||
|
if (No)
|
||
|
stprintf_s(v,TSIZEOF(v),T(":%d"),No);
|
||
|
else
|
||
|
v[0] = 0;
|
||
|
StreamPrintf(File,T("%s%s%s (0x%08x)\n"),s,LangStrDef(Ptr->Class,0),v,(int)Ptr);
|
||
|
}
|
||
|
else
|
||
|
StreamPrintf(File,T("%sNULL\n"),s);
|
||
|
}
|
||
|
|
||
|
static void DumpFormat(stream* File, tchar_t* s,const packetformat* Format)
|
||
|
{
|
||
|
tchar_t v[8];
|
||
|
switch (Format->Type)
|
||
|
{
|
||
|
case PACKET_VIDEO:
|
||
|
StreamPrintf(File,T("%svideo"),s);
|
||
|
if (Format->Format.Video.Pixel.Flags & PF_FOURCC)
|
||
|
{
|
||
|
FourCCToString(v,TSIZEOF(v),Format->Format.Video.Pixel.FourCC);
|
||
|
StreamPrintf(File,T(" fourcc=%s"),v);
|
||
|
}
|
||
|
else
|
||
|
StreamPrintf(File,T(" bitcount=%d rmask=%04x gmask=%04x bmask=%04x"),Format->Format.Video.Pixel.BitCount,
|
||
|
Format->Format.Video.Pixel.BitMask[0],Format->Format.Video.Pixel.BitMask[1],Format->Format.Video.Pixel.BitMask[2]);
|
||
|
if (Format->Format.Video.Width) StreamPrintf(File,T(" width=%d"),Format->Format.Video.Width);
|
||
|
if (Format->Format.Video.Height) StreamPrintf(File,T(" height=%d"),Format->Format.Video.Height);
|
||
|
if (Format->Format.Video.Pitch) StreamPrintf(File,T(" pitch=%d"),Format->Format.Video.Pitch);
|
||
|
if (Format->Format.Video.Direction) StreamPrintf(File,T(" dir=%d"),Format->Format.Video.Direction);
|
||
|
if (Format->ByteRate) StreamPrintf(File,T(" bitrate=%d"),Format->ByteRate*8);
|
||
|
StreamPrintf(File,T("\n"));
|
||
|
break;
|
||
|
case PACKET_AUDIO:
|
||
|
StreamPrintf(File,T("%saudio"),s);
|
||
|
if (Format->Format.Audio.Format) StreamPrintf(File,T(" fmt=%04x"),Format->Format.Audio.Format);
|
||
|
if (Format->Format.Audio.SampleRate) StreamPrintf(File,T(" rate=%d"),Format->Format.Audio.SampleRate);
|
||
|
if (Format->Format.Audio.Channels) StreamPrintf(File,T(" ch=%d"),Format->Format.Audio.Channels);
|
||
|
if (Format->Format.Audio.Bits) StreamPrintf(File,T(" bits=%d"),Format->Format.Audio.Bits);
|
||
|
if (Format->ByteRate) StreamPrintf(File,T(" bitrate=%d"),Format->ByteRate*8);
|
||
|
StreamPrintf(File,T("\n"));
|
||
|
break;
|
||
|
case PACKET_SUBTITLE:
|
||
|
FourCCToString(v,TSIZEOF(v),Format->Format.Subtitle.FourCC);
|
||
|
StreamPrintf(File,T("%ssubtitle fourcc=%s\n"),s,v);
|
||
|
break;
|
||
|
case PACKET_NONE:
|
||
|
StreamPrintf(File,T("%sempty packet format\n"),s);
|
||
|
break;
|
||
|
default:
|
||
|
StreamPrintf(File,T("%sunknown packet format (%d)\n"),s,Format->Type);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void NodeDump(struct stream* File)
|
||
|
{
|
||
|
context* p = Context();
|
||
|
node **i;
|
||
|
nodemodule* j;
|
||
|
nodeclass** k;
|
||
|
int No;
|
||
|
|
||
|
for (No=0,j=ARRAYBEGIN(p->NodeModule,nodemodule);j!=ARRAYEND(p->NodeModule,nodemodule);++j,++No)
|
||
|
TRY_BEGIN
|
||
|
{
|
||
|
int ClassCount = 0;
|
||
|
for (k=ARRAYBEGIN(p->NodeClass,nodeclass*);k!=ARRAYEND(p->NodeClass,nodeclass*);++k)
|
||
|
if ((*k)->ModuleNo == No)
|
||
|
++ClassCount;
|
||
|
|
||
|
if (j->Func && !j->Min)
|
||
|
CodeFindPages(j->Func,&j->Min,&j->Max,NULL);
|
||
|
StreamPrintf(File,T("%s %08x-%08x obj:%d class:%d\n"),LangStrDef(MODULE_PATH,No),j->Min,j->Max,j->ObjectCount,ClassCount);
|
||
|
}
|
||
|
TRY_END
|
||
|
|
||
|
StreamPrintf(File,T("\n\n"));
|
||
|
|
||
|
//no NodeLock, becase may be stayed locked when dumping after crash
|
||
|
for (i=ARRAYBEGIN(p->Node,node*);i!=ARRAYEND(p->Node,node*);++i)
|
||
|
if (*i)
|
||
|
TRY_BEGIN
|
||
|
{
|
||
|
tchar_t Flags[64];
|
||
|
tchar_t s[256];
|
||
|
tchar_t Buffer[512];
|
||
|
const tchar_t* Name;
|
||
|
rect Rect;
|
||
|
packetformat Format;
|
||
|
pin Pin;
|
||
|
rgb RGB;
|
||
|
point Point;
|
||
|
bool_t Bool;
|
||
|
tick_t Tick;
|
||
|
fraction Fraction;
|
||
|
node* Ptr;
|
||
|
int Int,No;
|
||
|
node* p = *i;
|
||
|
datadef DataDef;
|
||
|
|
||
|
StreamPrintf(File,T("%s (0x%08x)\n"),LangStrDef(p->Class,0),(int)p);
|
||
|
|
||
|
for (No=0;NodeEnum(p,No,&DataDef)==ERR_NONE;++No)
|
||
|
{
|
||
|
TRY_BEGIN
|
||
|
{
|
||
|
|
||
|
Name = LangStrDef(DataDef.Class,DataDef.No);
|
||
|
if (!Name[0]) Name = DataDef.Name ? DataDef.Name : T("");
|
||
|
|
||
|
Flags[0] = 0;
|
||
|
if (DataDef.Flags & DF_INPUT)
|
||
|
tcscat_s(Flags,TSIZEOF(Flags),T(":IN"));
|
||
|
if (DataDef.Flags & DF_OUTPUT)
|
||
|
tcscat_s(Flags,TSIZEOF(Flags),T(":OUT"));
|
||
|
|
||
|
stprintf_s(s,TSIZEOF(s),T(" %s(%d)%s="),Name,DataDef.No,Flags);
|
||
|
|
||
|
switch (DataDef.Type)
|
||
|
{
|
||
|
case TYPE_BOOL:
|
||
|
if (p->Get(p,DataDef.No,&Bool,sizeof(bool_t))==ERR_NONE)
|
||
|
{
|
||
|
BoolToString(Buffer,TSIZEOF(Buffer),Bool);
|
||
|
StreamPrintf(File,T("%s%s\n"),s,Buffer);
|
||
|
}
|
||
|
break;
|
||
|
case TYPE_TICK:
|
||
|
if (p->Get(p,DataDef.No,&Tick,sizeof(tick_t))==ERR_NONE)
|
||
|
{
|
||
|
TickToString(Buffer,TSIZEOF(Buffer),Tick,Tick<TICKSPERSEC,1,0);
|
||
|
StreamPrintf(File,T("%s%s\n"),s,Buffer);
|
||
|
}
|
||
|
break;
|
||
|
case TYPE_INT:
|
||
|
if (p->Get(p,DataDef.No,&Int,sizeof(int))==ERR_NONE)
|
||
|
{
|
||
|
IntToString(Buffer,TSIZEOF(Buffer),Int,(DataDef.Flags & DF_HEX)!=0);
|
||
|
StreamPrintf(File,T("%s%s\n"),s,Buffer);
|
||
|
}
|
||
|
break;
|
||
|
case TYPE_HOTKEY:
|
||
|
if (p->Get(p,DataDef.No,&Int,sizeof(int))==ERR_NONE)
|
||
|
{
|
||
|
HotKeyToString(Buffer,TSIZEOF(Buffer),Int);
|
||
|
StreamPrintf(File,T("%s%s\n"),s,Buffer);
|
||
|
}
|
||
|
break;
|
||
|
case TYPE_FRACTION:
|
||
|
if (p->Get(p,DataDef.No,&Fraction,sizeof(fraction))==ERR_NONE)
|
||
|
{
|
||
|
FractionToString(Buffer,TSIZEOF(Buffer),&Fraction,1,2);
|
||
|
StreamPrintf(File,T("%s%s\n"),s,Buffer);
|
||
|
}
|
||
|
break;
|
||
|
case TYPE_STRING:
|
||
|
if (p->Get(p,DataDef.No,Buffer,sizeof(Buffer))==ERR_NONE)
|
||
|
StreamPrintf(File,T("%s%s\n"),s,Buffer);
|
||
|
break;
|
||
|
case TYPE_RECT:
|
||
|
if (p->Get(p,DataDef.No,&Rect,sizeof(rect))==ERR_NONE)
|
||
|
StreamPrintf(File,T("%s%d:%d:%d:%d\n"),s,Rect.x,Rect.y,Rect.Width,Rect.Height);
|
||
|
break;
|
||
|
case TYPE_POINT:
|
||
|
if (p->Get(p,DataDef.No,&Point,sizeof(point))==ERR_NONE)
|
||
|
StreamPrintf(File,T("%s%d:%d\n"),s,Point.x,Point.y);
|
||
|
break;
|
||
|
case TYPE_RGB:
|
||
|
if (p->Get(p,DataDef.No,&RGB.v,sizeof(RGB.v))==ERR_NONE)
|
||
|
StreamPrintf(File,T("%s0x%082%082%082\n"),s,RGB.c.r,RGB.c.b,RGB.c.b);
|
||
|
break;
|
||
|
case TYPE_COMMENT:
|
||
|
if (p->Get(p,DataDef.No,&Pin,sizeof(Pin))==ERR_NONE)
|
||
|
DumpPtr(File,s,Pin.Node,Pin.No);
|
||
|
break;
|
||
|
case TYPE_PACKET:
|
||
|
if (p->Get(p,DataDef.No|PIN_FORMAT,&Format,sizeof(Format))==ERR_NONE)
|
||
|
DumpFormat(File,s,&Format);
|
||
|
if (p->Get(p,DataDef.No,&Pin,sizeof(Pin))==ERR_NONE)
|
||
|
DumpPtr(File,s,Pin.Node,Pin.No);
|
||
|
break;
|
||
|
case TYPE_NODE:
|
||
|
if (p->Get(p,DataDef.No,&Ptr,sizeof(Ptr)) == ERR_NONE)
|
||
|
DumpPtr(File,s,Ptr,0);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
TRY_END
|
||
|
}
|
||
|
}
|
||
|
TRY_END
|
||
|
|
||
|
StreamPrintf(File,T("\n\n"));
|
||
|
|
||
|
for (k=ARRAYBEGIN(p->NodeClass,nodeclass*);k!=ARRAYEND(p->NodeClass,nodeclass*);++k)
|
||
|
TRY_BEGIN
|
||
|
{
|
||
|
tchar_t s[16],s2[16];
|
||
|
int Class = (*k)->Def.Class;
|
||
|
FourCCToString(s,TSIZEOF(s),Class);
|
||
|
FourCCToString(s2,TSIZEOF(s2),(*k)->Def.ParentClass);
|
||
|
StreamPrintf(File,T("%s %s p:%d r:%d "),s,s2,(*k)->Parent!=NULL,(*k)->Registered);
|
||
|
StreamPrintf(File,T("c:%s e:%s p:%s m:%s\n"),
|
||
|
LangStrDef(Class,NODE_CONTENTTYPE),
|
||
|
LangStrDef(Class,NODE_EXTS),
|
||
|
LangStrDef(Class,NODE_PROBE),
|
||
|
LangStrDef(MODULE_PATH,(*k)->ModuleNo));
|
||
|
}
|
||
|
TRY_END
|
||
|
}
|
||
|
|
||
|
int NodeEnumTable(int* No, datadef* Out, const datatable* p)
|
||
|
{
|
||
|
int Count;
|
||
|
for (Count=0;p[Count].Type>=0;++Count);
|
||
|
|
||
|
if (*No < Count)
|
||
|
{
|
||
|
Out->Class = p[Count].No;
|
||
|
p += *No;
|
||
|
Out->No = p->No;
|
||
|
Out->Type = p->Type;
|
||
|
Out->Flags = p->Flags;
|
||
|
Out->Format1 = p->Format1;
|
||
|
Out->Format2 = p->Format2;
|
||
|
|
||
|
switch (Out->Type)
|
||
|
{
|
||
|
case TYPE_BOOL: Out->Size = sizeof(bool_t); break;
|
||
|
case TYPE_INT: Out->Size = sizeof(int); break;
|
||
|
case TYPE_HOTKEY: Out->Size = sizeof(int); break;
|
||
|
case TYPE_FRACTION: Out->Size = sizeof(fraction); break;
|
||
|
case TYPE_STRING: Out->Size = MAXDATA; break;
|
||
|
case TYPE_RECT: Out->Size = sizeof(rect); break;
|
||
|
case TYPE_POINT: Out->Size = sizeof(point); break;
|
||
|
case TYPE_RGB: Out->Size = sizeof(rgbval_t); break;
|
||
|
case TYPE_BINARY: Out->Size = Out->Format1; break;
|
||
|
case TYPE_NODE: Out->Size = sizeof(node*); break;
|
||
|
case TYPE_NOTIFY: Out->Size = sizeof(notify); break;
|
||
|
case TYPE_COMMENT:
|
||
|
case TYPE_PACKET: Out->Size = sizeof(pin); break;
|
||
|
case TYPE_TICK: Out->Size = sizeof(tick_t); break;
|
||
|
case TYPE_BLITFX: Out->Size = sizeof(blitfx); break;
|
||
|
default:
|
||
|
Out->Size = 0;
|
||
|
break;
|
||
|
}
|
||
|
Out->Name = LangStr(Out->Class,Out->No);
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
*No -= Count;
|
||
|
return ERR_INVALID_PARAM;
|
||
|
}
|
||
|
|
||
|
bool_t NodeDataDef(node* p, int No, datadef* Out)
|
||
|
{
|
||
|
int i;
|
||
|
for (i=0;NodeEnum(p,i,Out)==ERR_NONE;++i)
|
||
|
if (Out->No == No)
|
||
|
return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int NodeEnum(node* p,int No,datadef* Param)
|
||
|
{
|
||
|
if (!p) return ERR_INVALID_PARAM;
|
||
|
return p->Enum(p,&No,Param);
|
||
|
}
|
||
|
|
||
|
int NodeEnumClass(array* List, int Class)
|
||
|
{
|
||
|
return NodeEnumClassEx(List,Class,NULL,NULL,NULL,0);
|
||
|
}
|
||
|
|
||
|
int NodeEnumClassEx(array* List, int Class, const tchar_t* ContentType, const tchar_t* URL, const void* Data, int Length)
|
||
|
{
|
||
|
context* p = Context();
|
||
|
nodeclass* v;
|
||
|
nodeclass **i;
|
||
|
|
||
|
if (ContentType && !ContentType[0])
|
||
|
ContentType = NULL;
|
||
|
|
||
|
if (URL && !URL[0])
|
||
|
URL = NULL;
|
||
|
|
||
|
if (List)
|
||
|
memset(List,0,sizeof(array));
|
||
|
|
||
|
LockEnter(p->NodeLock);
|
||
|
for (i=ARRAYBEGIN(p->NodeClassPri,nodeclass*);i!=ARRAYEND(p->NodeClassPri,nodeclass*);++i)
|
||
|
if (!((*i)->Def.Flags & CF_ABSTRACT) && !TmpModule(p,(*i)->ModuleNo)) // skip abstract
|
||
|
for (v=*i;v;v=v->Parent)
|
||
|
if (v->Def.Class == Class)
|
||
|
{
|
||
|
int Id = (*i)->Def.Class;
|
||
|
|
||
|
if ((ContentType || URL || Data) &&
|
||
|
(!ContentType || !CheckContentType(ContentType,LangStrDef(Id,NODE_CONTENTTYPE))) &&
|
||
|
(!URL || !CheckExts(URL,LangStrDef(Id,NODE_EXTS))) &&
|
||
|
(!Data || !DataProbe(Data,Length,LangStrDef(Id,NODE_PROBE))))
|
||
|
continue;
|
||
|
|
||
|
if (!List)
|
||
|
{
|
||
|
LockLeave(p->NodeLock);
|
||
|
return Id;
|
||
|
}
|
||
|
ArrayAppend(List,&Id,sizeof(int),64);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
LockLeave(p->NodeLock);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int Enum(void* This,int* EnumNo,datadef* Out) { return ERR_INVALID_PARAM; }
|
||
|
static int Get(void* This,int No,void* Data,int Size) { return ERR_INVALID_PARAM; }
|
||
|
static int Set(void* This,int No,const void* Data,int Size) { return ERR_INVALID_PARAM; }
|
||
|
|
||
|
static int Create(node* p)
|
||
|
{
|
||
|
p->Enum = Enum;
|
||
|
p->Get = Get;
|
||
|
p->Set = Set;
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
static const nodedef Node =
|
||
|
{
|
||
|
CF_ABSTRACT,
|
||
|
NODE_CLASS,
|
||
|
0,
|
||
|
PRI_DEFAULT,
|
||
|
(nodecreate)Create
|
||
|
};
|
||
|
|
||
|
void Node_Init()
|
||
|
{
|
||
|
context* p = Context();
|
||
|
ArrayAlloc(&p->NodeClass,sizeof(nodeclass*)*100,128);
|
||
|
ArrayAlloc(&p->NodeClassPri,sizeof(nodeclass*)*100,128);
|
||
|
p->NodeLock = LockCreate();
|
||
|
|
||
|
NodeAddModule(T("common"),0,0,0,0);
|
||
|
if (!ARRAYEMPTY(p->NodeModule))
|
||
|
*(void(**)())&ARRAYBEGIN(p->NodeModule,nodemodule)->Func = Node_Init;
|
||
|
NodeRegisterClass(&Node);
|
||
|
}
|
||
|
|
||
|
void Node_Done()
|
||
|
{
|
||
|
context* p = Context();
|
||
|
nodeclass **i;
|
||
|
NodeUnRegisterClass(NODE_CLASS);
|
||
|
#ifndef NDEBUG
|
||
|
{
|
||
|
nodemodule* j;
|
||
|
for (j=ARRAYBEGIN(p->NodeModule,nodemodule);j!=ARRAYEND(p->NodeModule,nodemodule);++j)
|
||
|
{
|
||
|
if (j->ObjectCount!=0)
|
||
|
DebugMessage(T("module problem %s"),LangStr(MODULE_PATH,j-ARRAYBEGIN(p->NodeModule,nodemodule)));
|
||
|
assert(j->ObjectCount==0);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
ReleaseModules(p,1);
|
||
|
for (i=ARRAYBEGIN(p->NodeClass,nodeclass*);i!=ARRAYEND(p->NodeClass,nodeclass*);++i)
|
||
|
free(*i);
|
||
|
ArrayClear(&p->Node);
|
||
|
ArrayClear(&p->NodeClass);
|
||
|
ArrayClear(&p->NodeClassPri);
|
||
|
ArrayClear(&p->NodeModule);
|
||
|
LockDelete(p->NodeLock);
|
||
|
p->NodeLock = NULL;
|
||
|
}
|
||
|
|
||
|
bool_t NodeLocatePtr(void* Ptr, tchar_t* OutName, int OutLen, int* OutBase)
|
||
|
{
|
||
|
if (Ptr)
|
||
|
{
|
||
|
context* p = Context();
|
||
|
if (p)
|
||
|
{
|
||
|
int No;
|
||
|
uint8_t* v = (uint8_t*)Ptr;
|
||
|
nodemodule *i;
|
||
|
// no locking (dump after crash)
|
||
|
for (No=0,i=ARRAYBEGIN(p->NodeModule,nodemodule);i!=ARRAYEND(p->NodeModule,nodemodule);++i,++No)
|
||
|
{
|
||
|
if (i->Func)
|
||
|
{
|
||
|
if (!i->Min)
|
||
|
CodeFindPages(i->Func,&i->Min,&i->Max,NULL);
|
||
|
|
||
|
if (i->Min <= v && i->Max > v)
|
||
|
{
|
||
|
tcscpy_s(OutName,OutLen,LangStrDef(MODULE_PATH,No));
|
||
|
*OutBase = v - i->Min;
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#ifndef REGISTRY_GLOBAL
|
||
|
|
||
|
bool_t NodeRegLoadString(int Class, int Id)
|
||
|
{
|
||
|
uint8_t Buffer[MAXDATA];
|
||
|
if (NodeRegLoadValue(Class,Id,Buffer,sizeof(Buffer),TYPE_STRING))
|
||
|
{
|
||
|
StringAdd(1,Class,Id,(tchar_t*)Buffer);
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void NodeRegSaveString(int Class, int Id)
|
||
|
{
|
||
|
const tchar_t* s = LangStrDef(Class,Id);
|
||
|
int Len = tcslen(s);
|
||
|
if (Len)
|
||
|
++Len;
|
||
|
else
|
||
|
s = NULL;
|
||
|
NodeRegSaveValue(Class,Id,s,Len*sizeof(tchar_t),TYPE_STRING);
|
||
|
}
|
||
|
|
||
|
#endif
|