Ejemplo n.º 1
0
void C4SDefinitions::SetModules(const char *szList, const char *szRelativeToPath, const char *szRelativeToPath2)
	{
	int32_t cnt;

	// Empty list: local only
	if (!SModuleCount(szList)) 
		{ 
		LocalOnly=TRUE; 
		for (cnt=0; cnt<C4S_MaxDefinitions; cnt++) Definition[cnt][0]=0;
		return;
		}

	// Set list
	LocalOnly=FALSE;
	for (cnt=0; cnt<C4S_MaxDefinitions; cnt++) 
		{
		SGetModule(szList,cnt,Definition[cnt],_MAX_PATH);
		// Make relative path
		if (szRelativeToPath && *szRelativeToPath)
			if (SEqualNoCase(Definition[cnt],szRelativeToPath,SLen(szRelativeToPath)))
				SCopy(Definition[cnt]+SLen(szRelativeToPath),Definition[cnt]);
		if (szRelativeToPath2 && *szRelativeToPath2)
			if (SEqualNoCase(Definition[cnt],szRelativeToPath2,SLen(szRelativeToPath2)))
				SCopy(Definition[cnt]+SLen(szRelativeToPath2),Definition[cnt]);
		}

	}
Ejemplo n.º 2
0
void C4Network2IRCClient::OnMessage(bool fNotice, const char *szSender, const char *szTarget, const char *szText)
	{

	// Find channel, if not private.
	C4Network2IRCChannel *pChan = NULL;
	if(!SEqualNoCase(szTarget, Nick.getData()))
		pChan = getChannel(szTarget);

	// CTCP tagged data?
	const char X_DELIM = '\001';
	if(szText[0] == X_DELIM)
		{
		// Process messages (it's very rarely more than one, but the spec allows it)
		const char *pMsg = szText + 1;
		while(*pMsg)
			{
			// Find end
			const char *pEnd = strchr(pMsg, X_DELIM);
			if(!pEnd) pEnd = pMsg + SLen(pMsg);
			// Copy CTCP query/reply, get tag
			StdStrBuf CTCP; CTCP.Copy(pMsg, pEnd - pMsg);
			StdStrBuf Tag; Tag.CopyUntil(CTCP.getData(), ' ');
			const char *szData = SSearch(CTCP.getData(), " ");
			StdStrBuf Sender; Sender.CopyUntil(szSender, '!');
			// Process
			if(SEqualNoCase(Tag.getData(), "ACTION"))
				PushMessage(MSG_Action, szSender, szTarget, szData ? szData : "");
			if(SEqualNoCase(Tag.getData(), "FINGER") && !fNotice)
				{
				StdStrBuf Answer;
				if(Config.Registered())
					{
					Answer = Config.GetRegistrationData("Cuid");
					}
				else
					{
					Answer = LoadResStr("IDS_PRC_UNREGUSER");
					}
				Send("NOTICE", FormatString("%s :%cFINGER %s%c", 
					Sender.getData(), X_DELIM, 
					Answer.getData(),
					X_DELIM).getData());
				}
			if(SEqualNoCase(Tag.getData(), "VERSION") && !fNotice)
				Send("NOTICE", FormatString("%s :%cVERSION " C4ENGINECAPTION ":" C4VERSION ":" C4_OS "%c", 
					Sender.getData(), X_DELIM, X_DELIM).getData());
			if(SEqualNoCase(Tag.getData(), "PING") && !fNotice)
				Send("NOTICE", FormatString("%s :%cPING %s%c", 
					Sender.getData(), X_DELIM, szData, X_DELIM).getData());
			// Get next message
			pMsg = pEnd;
			if(*pMsg == X_DELIM) pMsg++;
			}
		}

	// Standard message (not CTCP tagged): Push
	else
		PushMessage(fNotice ? MSG_Notice : MSG_Message, szSender, szTarget, szText);

	}
Ejemplo n.º 3
0
int32_t C4TextureMap::GetIndex(const char *szMaterial, const char *szTexture, BOOL fAddIfNotExist, const char *szErrorIfFailed)
  {
  BYTE byIndex;
  // Find existing
	for (byIndex = 1; byIndex < C4M_MaxTexIndex; byIndex++)
		if (!Entry[byIndex].isNull())
			if (SEqualNoCase(Entry[byIndex].GetMaterialName(), szMaterial))
				if (!szTexture || SEqualNoCase(Entry[byIndex].GetTextureName(), szTexture))
					return byIndex;
  // Add new entry
  if (fAddIfNotExist)
    for (byIndex=1; byIndex<C4M_MaxTexIndex; byIndex++)
      if (Entry[byIndex].isNull())
        {
        if (AddEntry(byIndex, szMaterial, szTexture))
					{
					fEntriesAdded=true;
					return byIndex;
					}
#ifdef C4ENGINE
				if (szErrorIfFailed) DebugLogF("Error getting MatTex %s-%s for %s from TextureMap: Init failed.", szMaterial, szTexture, szErrorIfFailed);
#endif
        return 0;
        }
  // Else, fail
#ifdef C4ENGINE
	if (szErrorIfFailed) DebugLogF("Error getting MatTex %s-%s for %s from TextureMap: %s.", szMaterial, szTexture, szErrorIfFailed, fAddIfNotExist ? "Map is full!" : "Entry not found.");
#endif
  return 0;  
  }
Ejemplo n.º 4
0
bool C4ScriptHost::ReloadScript(const char *szPath, const char *szLanguage)
{
	// this?
	if (SEqualNoCase(szPath, ComponentHost.GetFilePath()) || (stringTable && SEqualNoCase(szPath, stringTable->GetFilePath())))
	{
		// try reload
		char szParentPath[_MAX_PATH + 1]; C4Group ParentGrp;
		if (GetParentPath(szPath, szParentPath))
			if (ParentGrp.Open(szParentPath))
				if (Load(ParentGrp, NULL, szLanguage, stringTable))
					return true;
	}
	return false;
}
Ejemplo n.º 5
0
bool C4Record::AddFile(const char *szLocalFilename, const char *szAddAs,
                       bool fDelete) {
  if (!fRecording) return false;

  // Streaming?
  if (fStreaming) {
    // Special stripping for streaming
    StdCopyStrBuf szFile(szLocalFilename);
    if (SEqualNoCase(GetExtension(szAddAs), "c4p")) {
      // Create a copy
      MakeTempFilename(&szFile);
      if (!CopyItem(szLocalFilename, szFile.getData())) return false;
      // Strip it
      if (!C4Player::Strip(szFile.getData(), true)) return false;
    }

    // Add to stream
    if (!StreamFile(szFile.getData(), szAddAs)) return false;

    // Remove temporary file
    if (szFile != szLocalFilename) EraseItem(szFile.getData());
  }

  // Add to record group
  if (fDelete) {
    if (!RecordGrp.Move(szLocalFilename, szAddAs)) return false;
  } else {
    if (!RecordGrp.Add(szLocalFilename, szAddAs)) return false;
  }

  return true;
}
Ejemplo n.º 6
0
bool C4PlayerInfoListAttributeConflictResolver::IsAttributeConflict(const C4PlayerInfo *pInfo1, const C4PlayerInfo *pInfo2, C4PlayerInfo::AttributeLevel eLevel)
{
	// check for conflict of colors and names
	if (eAttr == C4PlayerInfo::PLRATT_Color)
	{
		uint32_t dwClr1 = pInfo1->GetColor(), dwClr2 = 0;
		switch (eLevel)
		{
		case C4PlayerInfo::PLRAL_Current: dwClr2 = pInfo2->GetColor(); break;
		case C4PlayerInfo::PLRAL_Original: dwClr2 = pInfo2->GetOriginalColor(); break;
		case C4PlayerInfo::PLRAL_Alternate: dwClr2 = pInfo2->GetAlternateColor(); break;
		}
		return IsColorConflict(dwClr1, dwClr2);
	}
	else if (eAttr == C4PlayerInfo::PLRATT_Name)
	{
		const char *szName1 = pInfo1->GetName(), *szName2 = "";
		switch (eLevel)
		{
		case C4PlayerInfo::PLRAL_Current: szName2 = pInfo2->GetName(); break;
		case C4PlayerInfo::PLRAL_Original: szName2 = pInfo2->GetOriginalName(); break;
		default: return SEqualNoCase(szName1, szName2);
		}
	}
	return false;
}
Ejemplo n.º 7
0
bool C4DefGraphics::LoadSkeleton(C4Group &hGroup, const char* szFileName, StdMeshSkeletonLoader& loader)
{
	char* buf = NULL;
	size_t size;

	try
	{
		if (!hGroup.LoadEntry(szFileName, &buf, &size, 1)) return false;

		StdCopyStrBuf filename = StdCopyStrBuf();
		StdMeshSkeletonLoader::MakeFullSkeletonPath(filename, hGroup.GetName(), szFileName);

		if (SEqualNoCase(GetExtension(szFileName), "xml"))
		{
			loader.LoadSkeletonXml(filename, buf, size);
		}
		else
		{
			loader.LoadSkeletonBinary(filename, buf, size);
		}

		delete[] buf;
	}
	catch (const std::runtime_error& ex)
	{
		DebugLogF("Failed to load skeleton in definition %s: %s", hGroup.GetName(), ex.what());
		delete[] buf;
		return false;
	}

	return true;
}
Ejemplo n.º 8
0
bool C4DefGraphics::LoadMesh(C4Group &hGroup, const char* szFileName, StdMeshSkeletonLoader& loader)
{
	char* buf = NULL;
	size_t size;

	try
	{
		if(!hGroup.LoadEntry(szFileName, &buf, &size, 1)) return false;

		if (SEqualNoCase(GetExtension(szFileName), "xml"))
		{
			Mesh = StdMeshLoader::LoadMeshXml(buf, size, ::MeshMaterialManager, loader, hGroup.GetName());
		}
		else
		{
			Mesh = StdMeshLoader::LoadMeshBinary(buf, size, ::MeshMaterialManager, loader, hGroup.GetName());
		}
		delete[] buf;

		Mesh->SetLabel(pDef->id.ToString());

		// order submeshes
		Mesh->PostInit();
	}
	catch (const std::runtime_error& ex)
	{
		DebugLogF("Failed to load mesh in definition %s: %s", hGroup.GetName(), ex.what());
		delete[] buf;
		return false;
	}

	Type = TYPE_Mesh;
	return true;
}
Ejemplo n.º 9
0
C4SoundEffect *C4SoundSystem::GetEffect(const char *szSndName) {
  C4SoundEffect *pSfx;
  char szName[C4MaxSoundName + 4 + 1];
  int32_t iNumber;
  // Evaluate sound name
  SCopy(szSndName, szName, C4MaxSoundName);
  // Default extension
  DefaultExtension(szName, "wav");
  // Convert old style '*' wildcard to correct '?' wildcard
  // For sound effects, '*' is supposed to match single digits only
  SReplaceChar(szName, '*', '?');
  // Sound with a wildcard: determine number of available matches
  if (SCharCount('?', szName)) {
    // Search global sound file
    if (!(iNumber = SoundFile.EntryCount(szName)))
      // Search scenario local files
      if (!(iNumber = Game.ScenarioFile.EntryCount(szName)))
        // Search bank loaded sounds
        if (!(iNumber = EffectInBank(szName)))
          // None found: failure
          return NULL;
    // Insert index to name
    iNumber = BoundBy(1 + SafeRandom(iNumber), 1, 9);
    SReplaceChar(szName, '?', '0' + iNumber);
  }
  // Find requested sound effect in bank
  for (pSfx = FirstSound; pSfx; pSfx = pSfx->Next)
    if (SEqualNoCase(szName, pSfx->Name)) break;
  // Sound not in bank, try add
  if (!pSfx)
    if (!(pSfx = AddEffect(szName))) return NULL;
  return pSfx;
}
Ejemplo n.º 10
0
void C4StartupMainDlg::UpdateParticipants() {
  // First validate all participants (files must exist)
  StdStrBuf strPlayers, strPlayer;
  strPlayer.SetLength(1024 + 1);
  strPlayers.Copy(Config.General.Participants);
  *Config.General.Participants = 0;
  for (int i = 0; SCopySegment(strPlayers.getData(), i, strPlayer.getMData(),
                               ';', 1024, true);
       i++) {
    const char *szPlayer = strPlayer.getData();
    if (!szPlayer || !*szPlayer) continue;
    if (!FileExists(szPlayer)) continue;
    if (!SEqualNoCase(GetExtension(szPlayer), "c4p"))
      continue;  // additional sanity check to clear strange exe-path-only
                 // entries in player list?
    SAddModule(Config.General.Participants, szPlayer);
  }
  // Draw selected players - we are currently displaying the players stored in
  // Config.General.Participants.
  // Existence of the player files is not validated and player filenames are
  // displayed directly
  // (names are not loaded from the player core).
  strPlayers.Format(LoadResStr("IDS_DESC_PLRS"));
  if (!Config.General.Participants[0])
    strPlayers.Append(LoadResStr("IDS_DLG_NOPLAYERSSELECTED"));
  else
    for (int i = 0; SCopySegment(Config.General.Participants, i,
                                 strPlayer.getMData(), ';', 1024, true);
         i++) {
      if (i > 0) strPlayers.Append(", ");
      strPlayers.Append(C4Language::IconvClonk(
                            GetFilenameOnly(strPlayer.getData())).getData());
    }
  pParticipantsLbl->SetText(strPlayers.getData());
}
Ejemplo n.º 11
0
C4Network2IRCChannel *C4Network2IRCClient::getChannel(const char *szName) const 
	{
	for(C4Network2IRCChannel *pChan = pChannels; pChan; pChan = pChan->Next)
		if(SEqualNoCase(pChan->getName(), szName))
			return pChan;
	return NULL;
	}
Ejemplo n.º 12
0
C4LanguageInfo* C4Language::FindInfo(const char *strCode)
{
	for (C4LanguageInfo *pInfo = Infos; pInfo; pInfo = pInfo->Next)
		if (SEqualNoCase(pInfo->Code, strCode, 2))
			return pInfo;
	return NULL;
}
Ejemplo n.º 13
0
bool C4DefGraphics::LoadSkeleton(C4Group &hGroup, const char* szFileName, StdMeshSkeletonLoader& loader)
{
	char* buf = NULL;
	size_t size;

	try
	{
		if (!hGroup.LoadEntry(szFileName, &buf, &size, 1)) return false;

		// delete skeleton from the map for reloading, or else if you delete or rename
		// a skeleton file in the folder the old skeleton will still exist in the map
		loader.RemoveSkeleton(hGroup.GetName(), szFileName);

		if (SEqualNoCase(GetExtension(szFileName), "xml"))
		{
			loader.LoadSkeletonXml(hGroup.GetName(), szFileName, buf, size);
		}
		else
		{
			loader.LoadSkeletonBinary(hGroup.GetName(), szFileName, buf, size);
		}

		delete[] buf;
	}
	catch (const std::runtime_error& ex)
	{
		DebugLogF("Failed to load skeleton in definition %s: %s", hGroup.GetName(), ex.what());
		delete[] buf;
		return false;
	}

	return true;
}
Ejemplo n.º 14
0
void C4StartupMainDlg::UpdateParticipants()
{
	// First validate all participants (files must exist)
	std::string strPlayers(Config.General.Participants);
	std::vector<char> strPlayer(1025);
	*Config.General.Participants=0;
	for (int i = 0; SCopySegment(strPlayers.c_str(), i, &strPlayer[0], ';', strPlayer.size() - 1, true); i++)
	{
		const char *szPlayer = &strPlayer[0];
		std::string strPlayerFile(Config.General.UserDataPath);
		strPlayerFile.append(szPlayer);
		if (!szPlayer || !*szPlayer) continue;
		if (!FileExists(strPlayerFile.c_str())) continue;
		if (!SEqualNoCase(GetExtension(szPlayer), "ocp")) continue; // additional sanity check to clear strange exe-path-only entries in player list?
		SAddModule(Config.General.Participants, szPlayer);
	}
	// Draw selected players - we are currently displaying the players stored in Config.General.Participants.
	// Existence of the player files is not validated and player filenames are displayed directly
	// (names are not loaded from the player core).
	strPlayers = LoadResStr("IDS_DESC_PLRS");
	if (!Config.General.Participants[0])
		strPlayers.append(LoadResStr("IDS_DLG_NOPLAYERSSELECTED"));
	else
		for (int i = 0; SCopySegment(Config.General.Participants, i, &strPlayer[0], ';', 1024, true); i++)
		{
			if (i > 0) strPlayers.append(", ");
			strPlayers.append(GetFilenameOnly(&strPlayer[0]));
		}
	pParticipantsLbl->SetText(strPlayers.c_str());
}
Ejemplo n.º 15
0
void C4MusicSystem::Load(const char *szFile)
{
	// safety
	if (!szFile || !*szFile) return;
	C4MusicFile *NewSong=NULL;
#if AUDIO_TK == AUDIO_TK_OPENAL
	// openal: Only ogg supported
	const char *szExt = GetExtension(szFile);
	if (SEqualNoCase(szExt, "ogg")) NewSong = new C4MusicFileOgg;
#elif AUDIO_TK == AUDIO_TK_SDL_MIXER
	if (GetMusicFileTypeByExtension(GetExtension(szFile)) == MUSICTYPE_UNKNOWN) return;
	NewSong = new C4MusicFileSDL;
#endif
	// unrecognized type/mod not initialized?
	if (!NewSong) return;
	// init music file
	NewSong->Init(szFile);
	// add song to list (push back)
	C4MusicFile *pCurr = Songs;
	while (pCurr && pCurr->pNext) pCurr = pCurr->pNext;
	if (pCurr) pCurr->pNext = NewSong; else Songs = NewSong;
	NewSong->pNext = NULL;
	// count songs
	SongCount++;
	playlist_valid = false;
}
Ejemplo n.º 16
0
C4Texture * C4TextureMap::GetTexture(const char *szTexture)
  {
  C4Texture *pTexture;
  for (pTexture=FirstTexture; pTexture; pTexture=pTexture->Next)
    if (SEqualNoCase(pTexture->Name,szTexture))
      return pTexture;
  return NULL;
  }
Ejemplo n.º 17
0
C4KeyShiftState C4KeyCodeEx::String2KeyShift(const StdStrBuf &sName)
{
	// query map
	const C4KeyShiftMapEntry *pCheck = KeyShiftMap;
	while (pCheck->szName)
			if (SEqualNoCase(sName.getData(), pCheck->szName)) break; else ++pCheck;
	return pCheck->eShift;
}
Ejemplo n.º 18
0
int32_t C4MaterialMap::Get(const char *szMaterial)
{
	int32_t cnt;
	for (cnt=0; cnt<Num; cnt++)
		if (SEqualNoCase(szMaterial,Map[cnt].Name))
			return cnt;
	return MNone;
}
Ejemplo n.º 19
0
C4ChatControl::ChatSheet::NickItem *C4ChatControl::ChatSheet::GetNickItem(
    const char *szByNick) {
  // find by name
  for (NickItem *pNickItem = GetFirstNickItem(); pNickItem;
       pNickItem = GetNextNickItem(pNickItem))
    if (SEqualNoCase(pNickItem->GetNick(), szByNick)) return pNickItem;
  // not found
  return NULL;
}
Ejemplo n.º 20
0
bool C4TextureMap::CheckTexture(const char *szTexture) {
#ifdef C4ENGINE
  C4Texture *pTexture;
  for (pTexture = FirstTexture; pTexture; pTexture = pTexture->Next)
    if (SEqualNoCase(pTexture->Name, szTexture)) return true;
  return false;
#else
  return true;
#endif
}
Ejemplo n.º 21
0
C4DefGraphics *C4DefGraphics::Get(const char *szGrpName)
{
	// no group or empty string: base graphics
	if (!szGrpName || !szGrpName[0]) return this;
	// search additional graphics
	for (C4AdditionalDefGraphics *pGrp = pNext; pGrp; pGrp=pGrp->pNext)
		if (SEqualNoCase(pGrp->GetName(), szGrpName)) return pGrp;
	// nothing found
	return NULL;
}
Ejemplo n.º 22
0
C4Group *C4ScenarioSection::GetGroupfile(C4Group &rGrp)
	{
	// check temp filename
	if (szTempFilename) if (rGrp.Open(szTempFilename)) return &rGrp; else return NULL;
	// check filename within scenario
	if (szFilename) if (rGrp.OpenAsChild(&Game.ScenarioFile, szFilename)) return &rGrp; else return NULL;
	// unmodified main section: return main group
	if (SEqualNoCase(szName, C4ScenSect_Main)) return &Game.ScenarioFile;
	// failure
	return NULL;
	}
Ejemplo n.º 23
0
C4Player* C4PlayerList::GetAtClient(const char *szName, int iIndex) const
{
	int cindex=0;
	for (C4Player *pPlr=First; pPlr; pPlr=pPlr->Next)
		if (SEqualNoCase(pPlr->AtClientName,szName))
		{
			if (cindex==iIndex) return pPlr;
			cindex++;
		}
	return NULL;
}
Ejemplo n.º 24
0
bool C4ChatControl::IsServiceName(const char *szName) {
  // return true for some hardcoded list of service names
  if (!szName) return false;
  const char *szServiceNames[] = {"NickServ", "ChanServ", "MemoServ",
                                  "HelpServ", "Global",   NULL},
             *szServiceName;
  int32_t i = 0;
  while (szServiceName = szServiceNames[i++])
    if (SEqualNoCase(szName, szServiceName)) return true;
  return false;
}
Ejemplo n.º 25
0
bool C4StartupNetListEntry::IsSameRefQueryAddress(const char *szJoinaddress)
{
	// only unretrieved references
	if (!pRefClient) return false;
	// if request failed, create a duplicate anyway in case the game is opened now
	// except masterservers, which would re-search some time later anyway
	if (fError && eQueryType != NRQT_Masterserver) return false;
	// check equality of address
	// do it the simple way for now
	return SEqualNoCase(sRefClientAddress.getData(), szJoinaddress);
}
Ejemplo n.º 26
0
C4ChatControl::ChatSheet *C4ChatControl::GetSheetByTitle(
    const char *szTitle, C4ChatControl::ChatSheet::SheetType eType) {
  int32_t i = 0;
  C4GUI::Tabular::Sheet *pSheet;
  const char *szCheckTitle;
  while (pSheet = pTabChats->GetSheet(i++))
    if (szCheckTitle = pSheet->GetTitle())
      if (SEqualNoCase(szCheckTitle, szTitle)) {
        ChatSheet *pChatSheet = static_cast<ChatSheet *>(pSheet);
        if (eType == pChatSheet->GetSheetType()) return pChatSheet;
      }
  return NULL;
}
Ejemplo n.º 27
0
C4ScenarioSection::C4ScenarioSection(char *szName) {
  // copy name
  if (szName && !SEqualNoCase(szName, C4ScenSect_Main) && *szName) {
    this->szName = new char[strlen(szName) + 1];
    SCopy(szName, this->szName);
  } else
    this->szName = const_cast<char *>(C4ScenSect_Main);
  // zero fields
  szTempFilename = szFilename = 0;
  fModified = false;
  // link into main list
  pNext = Game.pScenarioSections;
  Game.pScenarioSections = this;
}
Ejemplo n.º 28
0
void SetClientPrefix(char *szFilename, const char *szClient)
{
	char szTemp[1024+1];
	// Compose prefix
	char szPrefix[1024+1];
	SCopy(szClient,szPrefix); SAppendChar('-',szPrefix);
	// Prefix already set?
	SCopy(GetFilename(szFilename),szTemp,SLen(szPrefix));
	if (SEqualNoCase(szTemp,szPrefix)) return;
	// Insert prefix
	SCopy(GetFilename(szFilename),szTemp);
	SCopy(szPrefix,GetFilename(szFilename));
	SAppend(szTemp,szFilename);
}
Ejemplo n.º 29
0
bool C4ChatInputDialog::KeyCompleteNick() {
  if (!pEdit) return false;
  char IncompleteNick[256 + 1];
  // get current word in edit
  if (!pEdit->GetCurrentWord(IncompleteNick, 256)) return false;
  if (!*IncompleteNick) return false;
  C4Player *plr = Game.Players.First;
  while (plr) {
    // Compare name and input
    if (SEqualNoCase(plr->GetName(), IncompleteNick, SLen(IncompleteNick))) {
      pEdit->InsertText(plr->GetName() + SLen(IncompleteNick), true);
      return true;
    } else
      plr = plr->Next;
  }
  // no match found
  return false;
}
Ejemplo n.º 30
0
C4Graph *C4Network2Stats::GetGraphByName(const StdStrBuf &rszName,
                                         bool &rfIsTemp) {
  // compare against default graph names
  rfIsTemp = false;
  if (SEqualNoCase(rszName.getData(), "oc")) return &statObjCount;
  if (SEqualNoCase(rszName.getData(), "fps")) return &statFPS;
  if (SEqualNoCase(rszName.getData(), "netio")) return &graphNetIO;
  if (SEqualNoCase(rszName.getData(), "pings")) return &statPings;
  if (SEqualNoCase(rszName.getData(), "control")) return &statControls;
  if (SEqualNoCase(rszName.getData(), "apm")) return &statActions;
  // no match
  return NULL;
}