openclonk/src/script/C4ValueMap.cpp

466 lines
9.5 KiB
C++

/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
*/
#include "C4Include.h"
#include "script/C4ValueMap.h"
#include "script/C4Value.h"
// *** C4ValueMapData ***
C4ValueMapData::C4ValueMapData() = default;
C4ValueMapData::C4ValueMapData(const C4ValueMapData &DataToCopy)
{
SetNameList(DataToCopy.pNames);
if (pNames) for (int32_t i = 0; i < pNames->iSize; i++)
pData[i].Set(DataToCopy.pData[i]);
}
C4ValueMapData &C4ValueMapData::operator = (const C4ValueMapData &DataToCopy)
{
SetNameList(DataToCopy.pNames);
if (pNames) for (int32_t i = 0; i < pNames->iSize; i++)
pData[i].Set(DataToCopy.pData[i]);
return *this;
}
bool C4ValueMapData::operator == (const C4ValueMapData &Data) const
{
if (pNames != Data.pNames) return false;
if (pNames)
for (int i = 0; i < pNames->iSize; i++)
if (pData[i] != Data.pData[i])
return false;
return true;
}
C4ValueMapData::~C4ValueMapData()
{
Reset();
}
void C4ValueMapData::Reset()
{
// unreg from name list (if using one)
if (pNames) UnRegister();
pNames = nullptr;
// free data
delete[] pData;
pData = nullptr;
}
void C4ValueMapData::ResetContent()
{
if (pNames)
// Realloc list (will destroy all data)
ReAllocList();
else
{
delete[] pData;
pData = nullptr;
}
}
void C4ValueMapData::SetNameList(C4ValueMapNames *pnNames)
{
if (pNames == pnNames) return;
if (pNames)
{
// save name array from old name list
char **pOldNames = pNames->pNames;
int32_t iOldSize = pNames->iSize;
// unreg from old name list
// (manually, because Data::UnRegister() would destroy content)
C4ValueMapNames *pNames = this->pNames;
pNames->UnRegister(this);
// register at new name list
pnNames->Register(this);
// call OnNameListChanged to copy data and realloc data array
OnNameListChanged(const_cast<const char **>(pOldNames), iOldSize);
// delete old names list, if it is temporary
if (bTempNameList)
delete pNames;
bTempNameList = false;
// ok
}
else
{
// simply register...
Register(pnNames);
}
}
void C4ValueMapData::Register(C4ValueMapNames *pnNames)
{
// UnReg from old?
if (pNames) UnRegister();
if (pnNames) pnNames->Register(this);
pNames = pnNames;
// alloc data array
ReAllocList();
}
void C4ValueMapData::UnRegister()
{
if (!pNames) return;
// safe pointer
C4ValueMapNames *pNames = this->pNames;
// unreg
pNames->UnRegister(this);
// delete name list (if it is temporary)
if (bTempNameList)
delete pNames;
bTempNameList = false;
// delete data array
delete[] pData;
pData = nullptr;
}
C4ValueMapNames *C4ValueMapData::CreateTempNameList()
{
// create new list
C4ValueMapNames *pTempNames = new C4ValueMapNames();
// register (this func will unreg if necessary, too)
Register(pTempNames);
// error?
if (pNames != pTempNames)
{
delete pTempNames;
return nullptr;
}
// set flag
bTempNameList = true;
return pTempNames;
}
void C4ValueMapData::ReAllocList()
{
if (!pNames)
{
Reset();
return;
}
// delete old array
delete[] pData;
// create new one
pData = new C4Value [pNames->iSize] ();
// ok...
}
void C4ValueMapData::OnNameListChanged(const char **pOldNames, int32_t iOldSize)
{
if (!pNames)
{
Reset();
return;
}
// this function does not use ReAllocList because the old values
// have to be hold.
// save pointer on old data
C4Value *pOldData = pData;
// create new data list
pData = new C4Value [pNames->iSize] ();
// (try to) copy data
int32_t i, j;
for (i = 0; i < iOldSize; i++)
{
if (i < pNames->iSize && SEqual(pNames->pNames[i], pOldNames[i]))
{
pData[i] = pOldData[i];
}
else for (j = 0; j < pNames->iSize; j++)
{
if (SEqual(pNames->pNames[j], pOldNames[i]))
{
pData[j] = pOldData[i];
break;
}
}
}
// delete old data array
delete[] pOldData;
}
C4Value *C4ValueMapData::GetItem(int32_t iNr)
{
assert(pNames);
assert(iNr < pNames->iSize);
assert(iNr >= 0);
// the list is nothing without name list...
if (!pNames) return nullptr;
if (iNr >= pNames->iSize) return nullptr;
return &pData[iNr];
}
C4Value *C4ValueMapData::GetItem(const char *strName)
{
assert(pNames);
if (!pNames) return nullptr;
int32_t iNr = pNames->GetItemNr(strName);
assert(iNr != -1);
if (iNr == -1) return nullptr;
return &pData[iNr];
}
int32_t C4ValueMapData::GetAnzItems()
{
if (!pNames) return 0;
return pNames->iSize;
}
void C4ValueMapData::Denumerate(C4ValueNumbers * numbers)
{
if (!pNames) return;
for (int32_t i = 0; i < pNames->iSize; i++)
pData[i].Denumerate(numbers);
}
void C4ValueMapData::CompileFunc(StdCompiler *pComp, C4ValueNumbers * numbers)
{
bool deserializing = pComp->isDeserializer();
C4ValueMapNames *pOldNames = pNames;
if (deserializing) Reset();
// Compile item count
int32_t iValueCnt;
if (!deserializing) iValueCnt = pNames ? pNames->iSize : 0;
pComp->Value(mkDefaultAdapt(iValueCnt, 0));
// nuthing 2do for no items
if (!iValueCnt) return;
// Separator (';')
pComp->Separator(StdCompiler::SEP_SEP2);
// Data
char **ppNames = !deserializing ? pNames->pNames : new char * [iValueCnt];
if (deserializing) for (int32_t i = 0; i < iValueCnt; i++) ppNames[i] = nullptr;
C4Value *pValues = !deserializing ? pData : new C4Value [iValueCnt];
// Compile
try
{
for (int32_t i = 0; i < iValueCnt; i++)
{
// Separate
if (i) pComp->Separator();
// Name
StdStrBuf Name;
if (!deserializing) Name.Ref(ppNames[i]);
pComp->Value(mkParAdapt(Name, StdCompiler::RCT_Idtf));
if (deserializing) ppNames[i] = Name.GrabPointer();
// Separator ('=')
pComp->Separator(StdCompiler::SEP_SET);
// Value
pComp->Value(mkParAdapt(pValues[i], numbers));
}
}
catch (...)
{
// make sure no mem is leaked on compiler error in name list
if (deserializing)
{
for (int32_t i = 0; i < iValueCnt; i++) if (ppNames[i]) free(ppNames[i]);
delete [] ppNames;
delete [] pValues;
}
throw;
}
// Set
if (deserializing)
{
// Set
CreateTempNameList();
pNames->SetNameArray(const_cast<const char **>(ppNames), iValueCnt);
for (int32_t i = 0; i < iValueCnt; i++) free(ppNames[i]);
delete [] ppNames; delete [] pData;
pData = pValues;
// Assign old name list
if (pOldNames) SetNameList(pOldNames);
}
}
// *** C4ValueMapNames ***
C4ValueMapNames::C4ValueMapNames() = default;
C4ValueMapNames::C4ValueMapNames(const C4ValueMapNames& NamesToCopy)
{
ChangeNameList(const_cast<const char **>(NamesToCopy.pNames), NamesToCopy.iSize);
}
C4ValueMapNames& C4ValueMapNames::operator = (const C4ValueMapNames &NamesToCopy)
{
ChangeNameList(const_cast<const char **>(NamesToCopy.pNames), NamesToCopy.iSize);
return *this;
}
C4ValueMapNames::~C4ValueMapNames()
{
Reset();
}
void C4ValueMapNames::Reset()
{
// unreg all data lists
while (pFirst) UnRegister(pFirst);
// free name list
for (int32_t i = 0; i < iSize; i++)
delete[] pNames[i];
delete[] pNames;
pNames = nullptr;
iSize = 0;
}
void C4ValueMapNames::Register(C4ValueMapData *pData)
{
// add to begin of list
pData->pNext = pFirst;
pFirst = pData;
// set name list
pData->pNames = this;
}
void C4ValueMapNames::UnRegister(C4ValueMapData *pData)
{
// find in list
C4ValueMapData *pAktData = pFirst, *pLastData = nullptr;
while (pAktData && pAktData != pData)
{
pLastData = pAktData;
pAktData = pAktData->pNext;
}
if (!pAktData)
// isn't registred here...
return;
// unreg
if (pLastData)
pLastData->pNext = pData->pNext;
else
pFirst = pData->pNext;
pData->pNext = nullptr;
pData->pNames = nullptr;
}
void C4ValueMapNames::ChangeNameList(const char **pnNames, int32_t nSize)
{
// safe old name list
char **pOldNames = pNames;
int32_t iOldSize = iSize;
// create new lists
pNames = new char *[nSize];
// copy names
int32_t i;
for (i = 0; i < nSize; i++)
{
pNames[i] = new char [SLen(pnNames[i]) + 1];
SCopy(pnNames[i], pNames[i], SLen(pnNames[i]) + 1);
}
// set new size
iSize = nSize;
// call OnNameListChanged list for all "child" lists
C4ValueMapData *pAktData = pFirst;
while (pAktData)
{
pAktData->OnNameListChanged(const_cast<const char **>(pOldNames), iOldSize);
pAktData = pAktData->pNext;
}
// delete old list
for (i = 0; i < iOldSize; i++)
delete[] pOldNames[i];
delete[] pOldNames;
// ok.
}
void C4ValueMapNames::SetNameArray(const char **pnNames, int32_t nSize)
{
// simply pass it through...
ChangeNameList(pnNames, nSize);
}
int32_t C4ValueMapNames::AddName(const char *pnName)
{
// name already existing?
int32_t iNr;
if ((iNr=GetItemNr(pnName)) != -1)
return iNr;
// create new dummy lists
const char **pDummyNames = new const char *[iSize + 1];
// copy all data from old list
// (danger! if ChangeNameList would now delete them before
// creating the new list, this would cause cruel errors...)
int32_t i;
for (i = 0; i < iSize; i++)
{
pDummyNames[i] = pNames[i];
}
pDummyNames[i] = pnName;
// change list
ChangeNameList(pDummyNames, iSize + 1);
// delete dummy arrays
delete[] pDummyNames;
// return index to new element (simply last element)
return iSize-1;
}
int32_t C4ValueMapNames::GetItemNr(const char *strName) const
{
for (int32_t i = 0; i < iSize; i++)
if (SEqual(pNames[i], strName))
return i;
return -1;
}