text: /*
   *			GPAC - Multimedia Framework C SDK
   *
   *			Authors: Jean Le Feuvre
   *			Copyright (c) Telecom ParisTech 2005-2012
   *					All rights reserved
   *
   *  This file is part of GPAC / common tools sub-project
   *
   *  GPAC is free software; you can redistribute it and/or modify
   *  it under the terms of the GNU Lesser General Public License as published by
   *  the Free Software Foundation; either version 2, or (at your option)
   *  any later version.
   *
   *  GPAC 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 Lesser General Public License for more details.
   *
   *  You should have received a copy of the GNU Lesser General Public
   *  License along with this library; see the file COPYING.  If not, write to
   *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
   *
   */
  
  #include <gpac/tools.h>
  #include <gpac/network.h>
  
  #ifdef __SYMBIAN32__
  
  #include <time.h>
  #include <sys/stat.h>
  #include <sys/time.h>
  #include <dirent.h>
  #include <unistd.h>
  #include <sys/times.h>
  #include <sys/resource.h>
  /*symbian stdlib*/
  #include <e32std.h>
  /*symbian core (for scheduler & trap cleanup)*/
  #include <e32base.h>
  /*hardware abstraction layer*/
  #include <hal.h>
  
  /*gpac module internals*/
  #include "module_wrap.h"
  #include <gpac/thread.h>
  
  
  //  Exported Functions (DLL entry point)
  #ifndef EKA2 // for EKA1 only
  GLDEF_C TInt E32Dll(TDllReason /*aReason*/)
  // Called when the DLL is loaded and unloaded. Note: have to define
  // epoccalldllentrypoints in MMP file to get this called in THUMB.
  {
  	return KErrNone;
  }
  
  #endif
  
  
  
  #define SLEEP_ABS_SELECT		1
  
  static u32 sys_start_time = 0;
  
  GF_EXPORT
  u32 gf_sys_clock()
  {
  	struct timeval now;
  	gettimeofday(&now, NULL);
  	return ( (now.tv_sec)*1000 + (now.tv_usec) / 1000) - sys_start_time;
  }
  
  
  GF_EXPORT
  void gf_sleep(u32 ms)
  {
  	TTimeIntervalMicroSeconds32 inter;
  	inter = (TInt) (1000*ms);
  #ifdef __SERIES60_3X__
  	User::AfterHighRes(inter);
  #else
  	User::After(inter);
  #endif
  
  #if 0
  	TInt error;
  	CActiveScheduler::RunIfReady(error, CActive::EPriorityIdle);
  
  	RTimer timer;
  	TRequestStatus timerStatus;
  	timer.CreateLocal();
  
  	timer.After(timerStatus,ms*1000);
  	User::WaitForRequest(timerStatus);
  #endif
  }
  
  GF_EXPORT
  void gf_delete_file(char *fileName)
  {
  	remove(fileName);
  }
  
  
  GF_EXPORT
  void gf_rand_init(Bool Reset)
  {
  	if (Reset) {
  		srand(1);
  	} else {
  		srand( (u32) time(NULL) );
  	}
  }
  
  GF_EXPORT
  u32 gf_rand()
  {
  	return rand();
  }
  
  
  #ifndef GPAC_READ_ONLY
  GF_EXPORT
  FILE *gf_temp_file_new()
  {
  	return tmpfile();
  }
  #endif
  
  
  GF_EXPORT
  void gf_utc_time_since_1970(u32 *sec, u32 *msec)
  {
  	struct timeval tv;
  	gettimeofday(&tv, NULL);
  	*sec = tv.tv_sec;
  	*msec = tv.tv_usec/1000;
  }
  
  GF_EXPORT
  void gf_get_user_name(char *buf, u32 buf_size)
  {
  	strcpy(buf, "mpeg4-user");
  
  #if 0
  	s32 len;
  	char *t;
  	strcpy(buf, "");
  	len = 1024;
  	GetUserName(buf, &len);
  	if (!len) {
  		t = getenv("USER");
  		if (t) strcpy(buf, t);
  	}
  #endif
  #if 0
  	struct passwd *pw;
  	pw = getpwuid(getuid());
  	strcpy(buf, "");
  	if (pw && pw->pw_name) strcpy(name, pw->pw_name);
  #endif
  }
  
  GF_EXPORT
  char * my_str_upr(char *str)
  {
  	u32 i;
  	for (i=0; i<strlen(str); i++) {
  		str[i] = toupper(str[i]);
  	}
  	return str;
  }
  GF_EXPORT
  char * my_str_lwr(char *str)
  {
  	u32 i;
  	for (i=0; i<strlen(str); i++) {
  		str[i] = tolower(str[i]);
  	}
  	return str;
  }
  
  /*enumerate directories*/
  GF_EXPORT
  GF_Err gf_enum_directory(const char *dir, Bool enum_directory, gf_enum_dir_item enum_dir_fct, void *cbck, const char *filter)
  {
  	unsigned char item_path[GF_MAX_PATH];
  	unsigned char path[GF_MAX_PATH], *file;
  
  	DIR *the_dir;
  	struct dirent* the_file;
  	struct stat st;
  
  	if (!dir || !enum_dir_fct) return GF_BAD_PARAM;
  
  	strcpy((char*)path, dir);
  	if (path[strlen((const char*)path)-1] != '\\') strcat((char*)path, "\\");
  
  
  	the_dir = opendir((char*)path);
  	if (the_dir == NULL) return GF_IO_ERR;
  
  	the_file = readdir(the_dir);
  	while (the_file) {
  		if (!strcmp(the_file->d_name, "..")) goto next;
  		if (the_file->d_name[0] == '.') goto next;
  
  		if (filter) {
  			char ext[30];
  			char *sep = strrchr(the_file->d_name, '.');
  			if (!sep) goto next;
  			strcpy(ext, sep+1);
  			strlwr(ext);
  			if (!strstr(filter, sep+1)) goto next;
  		}
  
  		strcpy((char*)item_path, (const char*)path);
  		strcat((char*)item_path, the_file->d_name);
  		if (stat( (const char*)item_path, &st ) != 0) goto next;
  		if (enum_directory && ( (st.st_mode & S_IFMT) != S_IFDIR)) goto next;
  		if (!enum_directory && ((st.st_mode & S_IFMT) == S_IFDIR)) goto next;
  		file = (unsigned char*)the_file->d_name;
  
  		if (enum_dir_fct(cbck, (char *)file, (char *)item_path)) {
  			break;
  		}
  
  next:
  		the_file = readdir(the_dir);
  	}
  	return GF_OK;
  }
  
  
  GF_EXPORT
  u64 gf_ftell(FILE *fp)
  {
  	return (u64) ftell(fp);
  }
  
  GF_EXPORT
  u64 gf_fseek(FILE *fp, s64 offset, s32 whence)
  {
  	return fseek(fp, (s32) offset, whence);
  }
  
  GF_EXPORT
  FILE *gf_fopen(const char *file_name, const char *mode)
  {
  	return fopen(file_name, mode);
  }
  
  
  /*symbian thread*/
  typedef RThread* TH_HANDLE;
  
  /*********************************************************************
  						OS-Specific Thread Object
  **********************************************************************/
  struct __tag_thread
  {
  
  	u32 status;
  	TH_HANDLE threadH;
  	u32 stackSize;
  	/* the thread procedure */
  	u32 (*Run)(void *param);
  	void *args;
  #ifndef GPAC_DISABLE_LOG
  	char *log_name;
  #endif
  	/* lock for signal */
  	GF_Semaphore *_signal;
  };
  
  GF_EXPORT
  GF_Thread *gf_th_new(const char *name)
  {
  	GF_Thread *tmp = (GF_Thread *) gf_malloc(sizeof(GF_Thread));
  	memset((void *)tmp, 0, sizeof(GF_Thread));
  	tmp->status = GF_THREAD_STATUS_STOP;
  #ifndef GPAC_DISABLE_LOG
  	if (name) {
  		tmp->log_name = gf_strdup(name);
  	} else {
  		char szN[20];
  		sprintf(szN, "0x%08x", (u32) tmp);
  		tmp->log_name = gf_strdup(szN);
  	}
  #endif
  
  	return tmp;
  }
  
  static void *RunThread(void *ptr)
  {
  	TInt err;
  	u32 ret = 0;
  	CTrapCleanup * cleanup = NULL;
  	GF_Thread *t = (GF_Thread *)ptr;
  
  
  	CActiveScheduler * scheduler = new CActiveScheduler();
  	if (scheduler == NULL) {
  		t->status = GF_THREAD_STATUS_DEAD;
  		gf_sema_notify(t->_signal, 1);
  		goto exit;
  	}
  #ifndef GPAC_DISABLE_LOG
  	GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Thread %s] Installing ActiveScheduler\n", t->log_name));
  #endif
  	CActiveScheduler::Install(scheduler);
  
  #ifndef GPAC_DISABLE_LOG
  	GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Thread %s] Creating cleanup trap\n", t->log_name));
  #endif
  	cleanup = CTrapCleanup::New();
  	if( cleanup == NULL ) {
  		t->status = GF_THREAD_STATUS_DEAD;
  		gf_sema_notify(t->_signal, 1);
  		delete CActiveScheduler::Current();
  		goto exit;
  	}
  #if 0
  	CActiveScheduler::Start();
  #endif
  	/* OK , signal the caller */
  	t->status = GF_THREAD_STATUS_RUN;
  	gf_sema_notify(t->_signal, 1);
  	/* Run our thread */
  #ifndef GPAC_DISABLE_LOG
  	GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Thread %s] Entering thread proc\n", t->log_name));
  #endif
  	TRAP(err, ret=t->Run(t->args) );
  	//ret = t->Run(t->args);
  
  	delete CActiveScheduler::Current();
  	delete cleanup;
  
  exit:
  #ifndef GPAC_DISABLE_LOG
  	GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Thread %s] Exit\n", t->log_name ));
  #endif
  	t->status = GF_THREAD_STATUS_DEAD;
  	t->Run = NULL;
  	t->threadH->Close();
  	t->threadH = NULL;
  	return (void *)ret;
  }
  
  GF_EXPORT
  GF_Err gf_th_run(GF_Thread *t, u32 (*Run)(void *param), void *param)
  {
  	TBuf<32>	threadName;
  
  	const TUint KThreadMinHeapSize = 0x1000;
  	const TUint KThreadMaxHeapSize = 0x10000;
  
  	if (!t || t->Run || t->_signal) return GF_BAD_PARAM;
  	t->Run = Run;
  	t->args = param;
  	t->_signal = gf_sema_new(1, 0);
  	if (! t->_signal) {
  		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Unable to create thread start-up semaphore\n"));
  		t->status = GF_THREAD_STATUS_DEAD;
  		return GF_IO_ERR;
  	}
  
  	threadName.Format(_L("GTH%d"), (u32) t);
  	t->threadH = new RThread();
  	if ( t->threadH->Create(threadName, (TThreadFunction)RunThread, KDefaultStackSize, KThreadMinHeapSize, KThreadMaxHeapSize, (void *)t, EOwnerProcess) != KErrNone) {
  		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Unable to create thread\n"));
  		t->status = GF_THREAD_STATUS_DEAD;
  		return GF_IO_ERR;
  	}
  	t->threadH->Resume();
  
  	/*wait for the child function to call us - do NOT return before, otherwise the thread status would
  	be unknown*/
  	//GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Waiting for thread to start\n"));
  	gf_sema_wait(t->_signal);
  	gf_sema_del(t->_signal);
  	t->_signal = NULL;
  	return GF_OK;
  }
  
  
  /* Stops a thread. If Destroy is not 0, thread is destroyed DANGEROUS as no cleanup */
  static void Thread_Stop(GF_Thread *t, Bool Destroy)
  {
  	if (gf_th_status(t) == GF_THREAD_STATUS_RUN) {
  		if (Destroy) {
  			t->threadH->Terminate(0);
  			t->threadH = NULL;
  		}
  		else {
  			t->threadH->Suspend();
  		}
  	}
  	t->status = GF_THREAD_STATUS_DEAD;
  }
  
  GF_EXPORT
  void gf_th_stop(GF_Thread *t)
  {
  	Thread_Stop(t, 0);
  }
  
  GF_EXPORT
  void gf_th_del(GF_Thread *t)
  {
  	Thread_Stop(t, 0);
  #ifndef GPAC_DISABLE_LOG
  	gf_free(t->log_name);
  #endif
  	gf_free(t);
  }
  
  
  GF_EXPORT
  void gf_th_set_priority(GF_Thread *t, s32 priority)
  {
  	/*FIXEME: tune priorities on symbian*/
  #if 0
  	if (priority > 200)
  		t->threadH->SetPriority(EPriorityRealTime);
  	else
  		t->threadH->SetPriority(EPriorityNormal);
  #endif
  }
  
  GF_EXPORT
  u32 gf_th_status(GF_Thread *t)
  {
  	if (!t) return 0;
  	return t->status;
  }
  
  
  GF_EXPORT
  u32 gf_th_id()
  {
  	return RThread().Id();
  }
  
  
  
  /*********************************************************************
  						OS-Specific Mutex Object
  **********************************************************************/
  struct __tag_mutex
  {
  	RMutex *hMutex;
  	/* We filter recursive calls (1 thread calling Lock several times in a row only locks
  	ONCE the mutex. Holder is the current ThreadID of the mutex holder*/
  	u32 Holder, HolderCount;
  #ifndef GPAC_DISABLE_LOG
  	char *log_name;
  #endif
  };
  
  
  GF_EXPORT
  GF_Mutex *gf_mx_new(const char *name)
  {
  	GF_Mutex *tmp = (GF_Mutex *)gf_malloc(sizeof(GF_Mutex));
  	if (!tmp) return NULL;
  	memset(tmp, 0, sizeof(GF_Mutex));
  
  	tmp->hMutex = new RMutex();
  	if( tmp->hMutex->CreateLocal() != KErrNone) {
  		gf_free(tmp);
  		return NULL;
  	}
  #ifndef GPAC_DISABLE_LOG
  	if (name) {
  		tmp->log_name = gf_strdup(name);
  	} else {
  		char szN[20];
  		sprintf(szN, "0x%08x", (u32) tmp);
  		tmp->log_name = gf_strdup(szN);
  	}
  #endif
  	return tmp;
  }
  
  GF_EXPORT
  void gf_mx_del(GF_Mutex *mx)
  {
  	mx->hMutex->Close();
  	gf_free(mx);
  }
  
  GF_EXPORT
  void gf_mx_v(GF_Mutex *mx)
  {
  	u32 caller;
  	if (!mx) return;
  	caller = gf_th_id();
  
  	/*only if we own*/
  	if (caller != mx->Holder) {
  		GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Invalid mutex release - owner PID %d - caller PID %d\n", mx->Holder, caller));
  		return;
  	}
  	if (!mx->HolderCount) {
  		GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Invalid mutex release - mutex not locked\n"));
  		return;
  	}
  	mx->HolderCount -= 1;
  
  	if (mx->HolderCount == 0) {
  		mx->Holder = 0;
  		mx->hMutex->Signal();
  	}
  }
  
  GF_EXPORT
  u32 gf_mx_p(GF_Mutex *mx)
  {
  	u32 caller;
  	if (!mx) return 0;
  	caller = gf_th_id();
  	if (caller == mx->Holder) {
  		mx->HolderCount += 1;
  		return 1;
  	}
  	mx->hMutex->Wait();
  	mx->Holder = caller;
  	mx->HolderCount = 1;
  	return 1;
  }
  
  GF_EXPORT
  Bool gf_mx_try_lock(GF_Mutex *mx)
  {
  	u32 caller;
  	if (!mx) return 0;
  	caller = gf_th_id();
  	if (caller == mx->Holder) {
  		mx->HolderCount += 1;
  		return 1;
  	}
  	/*FIXME !! WE MUST HAVE tryLock*/
  	gf_mx_p(mx);
  	return 1;
  }
  
  
  
  
  /*********************************************************************
  						OS-Specific Semaphore Object
  **********************************************************************/
  struct __tag_semaphore
  {
  	RSemaphore *hSemaphore;
  };
  
  
  GF_EXPORT
  GF_Semaphore *gf_sema_new(u32 MaxCount, u32 InitCount)
  {
  	GF_Semaphore *tmp = (GF_Semaphore *) gf_malloc(sizeof(GF_Semaphore));
  
  	if (!tmp) return NULL;
  	tmp->hSemaphore = new RSemaphore();
  	if (!tmp->hSemaphore) {
  		gf_free(tmp);
  		return NULL;
  	}
  	TBuf<32>	semaName;
  	semaName.Format(_L("GPAC_SEM%d"), (u32) tmp);
  	tmp->hSemaphore->CreateGlobal(semaName, InitCount);
  
  	return tmp;
  }
  
  GF_EXPORT
  void gf_sema_del(GF_Semaphore *sm)
  {
  	sm->hSemaphore->Close();
  	gf_free(sm);
  }
  
  GF_EXPORT
  Bool gf_sema_notify(GF_Semaphore *sm, u32 NbRelease)
  {
      if (!sm) return GF_FALSE;
  	sm->hSemaphore->Signal(NbRelease);
  	return GF_TRUE;
  }
  
  GF_EXPORT
  void gf_sema_wait(GF_Semaphore *sm)
  {
  	sm->hSemaphore->Wait();
  }
  
  GF_EXPORT
  Bool gf_sema_wait_for(GF_Semaphore *sm, u32 TimeOut)
  {
  	return 0;
  }
  
  
  
  
  
  static u32 sys_init = 0;
  GF_SystemRTInfo the_rti;
  
  GF_EXPORT
  void gf_sys_init()
  {
  	if (!sys_init) {
  		memset(&the_rti, 0, sizeof(GF_SystemRTInfo));
  		the_rti.pid = getpid();
  		sys_start_time = gf_sys_clock();
  	}
  	sys_init += 1;
  }
  
  GF_EXPORT
  void gf_sys_close()
  {
  	if (sys_init > 0) {
  		sys_init --;
  		if (sys_init) return;
  	}
  }
  
  #ifdef GPAC_MEMORY_TRACKING
  extern size_t gpac_allocated_memory;
  #endif
  
  /*CPU and Memory Usage*/
  GF_EXPORT
  Bool gf_sys_get_rti(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags)
  {
  	TInt ram, ram_free;
  	u32 now, time;
  #ifdef __SERIES60_3X__
  	TModuleMemoryInfo mi;
  #endif
  	TTimeIntervalMicroSeconds tims;
  	RProcess cur_process;
  	RThread cur_th;
  
  	now = gf_sys_clock();
  	if (!rti->sampling_instant) {
  		rti->sampling_instant = now;
  		if (cur_th.GetCpuTime(tims) != KErrNone) {
  			return 0;
  		}
  #ifdef __SERIES60_3X__
  		rti->process_cpu_time = (u32) (tims.Int64() / 1000);
  #else
  		rti->process_cpu_time = (u32) ( TInt64(tims.Int64() / 1000).GetTInt() );
  #endif
  		return 0;
  	}
  	if (rti->sampling_instant + refresh_time_ms > now) return 0;
  	rti->sampling_period_duration = now - rti->sampling_instant;
  	rti->sampling_instant = now;
  
  	if (cur_th.Process(cur_process) != KErrNone) {
  		return 0;
  	}
  #ifdef __SERIES60_3X__
  	if (cur_process.GetMemoryInfo(mi) != KErrNone) {
  		return 0;
  	}
  	rti->process_memory = mi.iCodeSize + mi.iConstDataSize + mi.iInitialisedDataSize + mi.iUninitialisedDataSize;
  #endif
  	if (cur_th.GetCpuTime(tims) != KErrNone) {
  		return 0;
  	}
  #ifdef __SERIES60_3X__
  	time = (u32) (tims.Int64() / 1000);
  #else
  	time = (u32) ( TInt64(tims.Int64() / 1000).GetTInt() );
  #endif
  	rti->process_cpu_time_diff = time - rti->process_cpu_time;
  	rti->process_cpu_time = time;
  	rti->process_cpu_usage = 100*rti->process_cpu_time_diff / rti->sampling_period_duration;
  	if (rti->process_cpu_usage > 100) rti->process_cpu_usage = 100;
  
  	HAL::Get(HALData::EMemoryRAM, ram);
  	HAL::Get(HALData::EMemoryRAMFree, ram_free);
  	rti->physical_memory = ram;
  	rti->physical_memory_avail = ram_free;
  #ifdef GPAC_MEMORY_TRACKING
  	rti->gpac_memory = gpac_allocated_memory;
  #endif
  	return 1;
  }
  
  GF_EXPORT
  Bool gf_sys_get_battery_state(Bool *onBattery, u32 *state, u32*level)
  {
  	return 1;
  }
  
  
  /*delete all interfaces loaded on object*/
  void gf_modules_free_module(ModuleInstance *inst)
  {
  	void *objinterface;
  	while (gf_list_count(inst->interfaces)) {
  		objinterface = gf_list_get(inst->interfaces, 0);
  		gf_list_rem(inst->interfaces, 0);
  		inst->destroy_func(objinterface);
  	}
  	if (inst->lib_handle) {
  		RLibrary* pLibrary = (RLibrary *) inst->lib_handle;
  		pLibrary->Close();
  	}
  	gf_list_del(inst->interfaces);
  	gf_free(inst);
  }
  
  Bool gf_modules_load_library(ModuleInstance *inst)
  {
  	const TUid KGPACModuleUid= {0x10000080};
  	char s_path[GF_MAX_PATH];
  	HBufC *path;
  	TInt e;
  
  	if (inst->lib_handle) return 1;
  
  	sprintf(s_path, "%s%c%s", inst->plugman->dir, GF_PATH_SEPARATOR, inst->szName);
  
  	path = HBufC::NewL( User::StringLength( ( TUint8* ) s_path) + 1 );
  	path->Des().Copy( TPtrC8(( TText8* ) s_path) );
  
  	RLibrary* pLibrary = new RLibrary();
  	e = pLibrary->Load(*path);
  	delete path;
  
  	if (e != KErrNone) {
  		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Cannot load library %s: %d", s_path, e));
  		delete pLibrary;
  		goto err_exit;
  	}
  	/*check UID 2 is GPAC's identifier*/
  	if (pLibrary->Type()[1] != KGPACModuleUid) {
  		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Invalid library UID %x", (u32) pLibrary->Type()[1].iUid));
  		pLibrary->Close();
  		delete pLibrary;
  		goto err_exit;
  	}
  	inst->query_func = (QueryInterfaces) pLibrary->Lookup(1);
  	inst->load_func = (LoadInterface) pLibrary->Lookup(2);
  	inst->destroy_func = (ShutdownInterface) pLibrary->Lookup(3);
  
  	if ((inst->query_func==NULL) || (inst->load_func==NULL) || (inst->destroy_func==NULL) ) {
  		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Library %s has invalid interfaces", inst->szName));
  		pLibrary->Close();
  		delete pLibrary;
  		goto err_exit;
  	}
  
  	//store library handle
  	inst->lib_handle = (void*) pLibrary;
  	//GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Module %s loaded\n", inst->szName));
  	return 1;
  
  err_exit:
  	gf_cfg_set_key(inst->plugman->cfg, "SymbianDLLs", inst->szName, "no");
  	return 0;
  }
  
  void gf_modules_unload_library(ModuleInstance *inst)
  {
  	if (!inst->lib_handle || gf_list_count(inst->interfaces)) return;
  
  	RLibrary* pLibrary = (RLibrary *) inst->lib_handle;
  	pLibrary->Close();
  	delete pLibrary;
  
  	inst->lib_handle = NULL;
  	inst->load_func = NULL;
  	inst->destroy_func = NULL;
  	inst->query_func = NULL;
  	//GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Module %s unloaded\n", inst->szName));
  }
  
  
  static Bool enum_modules(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info)
  {
  	ModuleInstance *inst;
  
  	GF_ModuleManager *pm = (GF_ModuleManager *) cbck;
  
  	if (strstr(item_name, "nposmozilla")) return 0;
  	if (strncmp(item_name, "gm_", 3)) return 0;
  	if (gf_module_is_loaded(pm, item_name) ) return 0;
  
  	/*TODO FIXME: add module check for symbian */
  
  	GF_SAFEALLOC(inst, ModuleInstance);
  	inst->interfaces = gf_list_new();
  	inst->plugman = pm;
  	strcpy((char*)inst->szName, item_name);
  	gf_list_add(pm->plug_list, inst);
  	return 0;
  }
  
  /*refresh modules - note we don't check for deleted modules but since we've open them the OS should forbid delete*/
  GF_EXPORT
  u32 gf_modules_refresh(GF_ModuleManager *pm)
  {
  	if (!pm) return 0;
  	//!! symbian 9.1 doesn't allow for DLL browsing in /sys/bin, and can only load DLLs in /sys/bin !!
  #if 0
  	gf_enum_directory((char*)pm->dir, 0, enum_modules, pm, ".dll");
  #else
  	u32 i, mod_count;
  
  	mod_count = gf_cfg_get_key_count(pm->cfg, "SymbianDLLs");
  	for (i=0; i<mod_count; i++) {
  		const char *mod = gf_cfg_get_key_name(pm->cfg, "SymbianDLLs", i);
  		if (stricmp(gf_cfg_get_key(pm->cfg, "SymbianDLLs", mod), "yes")) continue;
  		enum_modules(pm, (char*)mod, NULL);
  	}
  #endif
  	return gf_list_count(pm->plug_list);
  }
  
  #endif
  
  