CacheGroup* BuildCacheGroup(Config& config, const string& name, bool isTerminal) {
    CacheGroup* cgp = new CacheGroup;
    CacheGroup& cg = *cgp;

    string prefix = "sys.caches." + name + ".";

    bool isPrefetcher = config.get<bool>(prefix + "isPrefetcher", false);
    if (isPrefetcher) { //build a prefetcher group
        uint32_t prefetchers = config.get<uint32_t>(prefix + "prefetchers", 1);
        cg.resize(prefetchers);
        for (vector<BaseCache*>& bg : cg) bg.resize(1);
        for (uint32_t i = 0; i < prefetchers; i++) {
            stringstream ss;
            ss << name << "-" << i;
            g_string pfName(ss.str().c_str());
            cg[i][0] = new StreamPrefetcher(pfName);
        }
        return cgp;
    }

    uint32_t size = config.get<uint32_t>(prefix + "size", 64*1024);
    uint32_t banks = config.get<uint32_t>(prefix + "banks", 1);
    uint32_t caches = config.get<uint32_t>(prefix + "caches", 1);

    uint32_t bankSize = size/banks;
    if (size % banks != 0) {
        panic("%s: banks (%d) does not divide the size (%d bytes)", name.c_str(), banks, size);
    }

    cg.resize(caches);
    for (vector<BaseCache*>& bg : cg) bg.resize(banks);

    for (uint32_t i = 0; i < caches; i++) {
        for (uint32_t j = 0; j < banks; j++) {
            stringstream ss;
            ss << name << "-" << i;
            if (banks > 1) {
                ss << "b" << j;
            }
            g_string bankName(ss.str().c_str());
            uint32_t domain = (i*banks + j)*zinfo->numDomains/(caches*banks); //(banks > 1)? nextDomain() : (i*banks + j)*zinfo->numDomains/(caches*banks);
            cg[i][j] = BuildCacheBank(config, prefix, bankName, bankSize, isTerminal, domain);
        }
    }

    return cgp;
}
void CSampleBank::load(bool async)
{
	// TODO : add async loading support !

	CSampleBankManager::TVirtualBankCont::iterator it(_SampleBankManager->m_VirtualBanks.find(_Name));
	if (it != _SampleBankManager->m_VirtualBanks.end())
	{
		// this is a virtual sample bank !
		nlinfo("Loading virtual sample bank %s", CStringMapper::unmap(_Name).c_str());
		
		const CAudioMixerUser::TBackgroundFlags &flags = _SampleBankManager->m_AudioMixer->getBackgroundFlags();
		
		for (uint i=0; i<it->second.size(); ++i)
		{
			if (flags.Flags[it->second[i].Filter])
			{
				CSampleBank *bank = _SampleBankManager->findSampleBank(it->second[i].BankName);
				if (bank)
					bank->load(async);
			}
		}
	}

	//nlinfo("Loading sample bank %s %", CStringMapper::unmap(_Name).c_str(), async?"":"Asynchronously");

	vector<string> filenames;
//	vector<string>::iterator iter;

	if (_Loaded)
	{
		nlwarning("Trying to load an already loaded bank : %s", CStringMapper::unmap(_Name).c_str ());
		return;
	}


	// Load the sample bank from the builded sample_bank file.
	string bankName(CStringMapper::unmap(_Name)+".sample_bank");
	string filename = CPath::lookup(bankName, false);
	if (filename.empty())
	{
		nlwarning("Could not find sample bank [%s]", bankName.c_str());
		return;
	}

	try
	{

		CIFile	sampleBank(filename);

		CAudioMixerUser::TSampleBankHeader sbh;
		sampleBank.serial(sbh);
		_LoadingDone = false;

		sint32 seekStart = sampleBank.getPos();


		uint8	*data = 0;
		uint	i;
		for (i=0; i<sbh.Name.size(); ++i)
		{
			IBuffer *ibuffer = _SampleBankManager->m_AudioMixer->getSoundDriver()->createBuffer();
			nlassert(ibuffer);

			TStringId	nameId = CStringMapper::map(CFile::getFilenameWithoutExtension(sbh.Name[i]));
			ibuffer->setName(nameId);

	/*		{
				sint16 *data16 = new sint16[sbh.NbSample[i]];
				IBuffer::TADPCMState	state;
				state.PreviousSample = 0;
				state.StepIndex = 0;
				uint count =0;
				for (count=0; count+1024<sbh.NbSample[i]; count+=1024)
				{
					IBuffer::decodeADPCM(data+count/2, data16+count, 1024, state);
				}
				IBuffer::decodeADPCM(data+count/2, data16+count, sbh.NbSample[i]-count, state);

				state.PreviousSample = 0;
				state.StepIndex = 0;
				sint16	*data16_2 = new sint16[sbh.NbSample[i]];
 				IBuffer::decodeADPCM(data, data16_2, sbh.NbSample[i], state);

				for (uint j=0; j<sbh.NbSample[i]; ++j)
				{
					if (data16[j] != data16_2[j])
					{
						nlwarning("Sample differ at %u", j);
					}
				}

				_SoundDriver->readRawBuffer(ibuffer, sbh.Name[i], (uint8*)data16, sbh.NbSample[i]*2, Mono16, sbh.Freq[i]);
				delete [] data16;
				delete [] data16_2;
			}
	*/

			if (_SampleBankManager->m_AudioMixer->useAPDCM())
			{
				data = (uint8*) realloc(data, sbh.SizeAdpcm[i]);
				sampleBank.seek(seekStart + sbh.OffsetAdpcm[i], CIFile::begin);
				sampleBank.serialBuffer(data, sbh.SizeAdpcm[i]);
				ibuffer->setFormat(IBuffer::FormatDviAdpcm, 1, 16, sbh.Freq[i]);
				if (!ibuffer->fill(data, sbh.SizeAdpcm[i]))
					nlwarning("AM: ibuffer->fill returned false with FormatADPCM");
			}
			else
			{
				data = (uint8*) realloc(data, sbh.SizeMono16[i]);
				sampleBank.seek(seekStart + sbh.OffsetMono16[i], CIFile::begin);
				sampleBank.serialBuffer(data, sbh.SizeMono16[i]);
				ibuffer->setFormat(IBuffer::FormatPcm, 1, 16, sbh.Freq[i]);
				if (!ibuffer->fill(data, sbh.SizeMono16[i]))
					nlwarning("AM: ibuffer->fill returned false with FormatPCM");
			}

			_ByteSize += ibuffer->getSize();

			_Samples[nameId] = ibuffer;

			// Warn the sound bank that the sample are available.
			CAudioMixerUser::getInstance()->getSoundBank()->bufferLoaded(nameId, ibuffer);
		}
		free(data);

		_SampleBankManager->m_LoadedSize += _ByteSize;
	}
	catch(Exception &e)
	{
		// loading failed !
		nlwarning("Exception %s during loading of sample bank %s", e.what(), filename.c_str());

		if (_SampleBankManager->m_AudioMixer->getPackedSheetUpdate())
		{
			nlinfo("Deleting offending sound bank, you need to restart to recreate it!");
			CFile::deleteFile(filename);
		}
	}

	_Loaded = true;
	_LoadingDone = true;



///////////////////////////////////////// OLD Version //////////////////////////////////////
/*

	std::string list = CPath::lookup(CStringMapper::unmap(_Name)+CAudioMixerUser::SampleBankListExt, false);
	if (list.empty())
	{
		nlwarning("File %s not found to load sample bank %s", (CStringMapper::unmap(_Name)+CAudioMixerUser::SampleBankListExt).c_str(), CStringMapper::unmap(_Name).c_str());
		return;
	}


	NLMISC::CIFile sampleBankList(list);
	sampleBankList.serialCont(filenames);

	for (iter = filenames.begin(); iter != filenames.end(); iter++)
	{
		IBuffer* ibuffer = NULL;
		try
		{
			ibuffer = _SoundDriver->createBuffer();
			nlassert(ibuffer);

//			std::string sampleName(CFile::getFilenameWithoutExtension(*iter));
			NLMISC::TStringId sampleName(CStringMapper::map(CFile::getFilenameWithoutExtension(*iter)));

			if (async)
			{
				ibuffer->presetName(sampleName);
				nldebug("Preloading sample [%s]", CStringMapper::unmap(sampleName).c_str());
			}
			else
			{
				std::string fullName = NLMISC::CPath::lookup(*iter, false);
				if (!fullName.empty())
				{
					NLMISC::CIFile	ifile(fullName);
					uint size = ifile.getFileSize();
					uint8 *buffer = new uint8[ifile.getFileSize()];
					ifile.serialBuffer(buffer, size);

					_SoundDriver->readWavBuffer(ibuffer, fullName, buffer, size);
					_ByteSize += ibuffer->getSize();

					delete [] buffer;
				}
			}
			_Samples[sampleName] = ibuffer ;

			// Warn the sound bank that the sample are available.
			CSoundBank::instance()->bufferLoaded(sampleName, ibuffer);
		}
		catch (ESoundDriver &e)
		{
			if (ibuffer != NULL) {
				delete ibuffer;
				ibuffer = NULL;
			}
			nlwarning("Problem with file '%s': %s", (*iter).c_str(), e.what());
		}
	}

	_Loaded = true;

	if (!async)
	{
		_LoadingDone = true;
		// compute the sample bank size.
		_LoadedSize += _ByteSize;
	}
	else
	{
		// fill the loading list.
		TSampleTable::iterator first(_Samples.begin()), last(_Samples.end());
		for (; first != last; ++first)
		{
			_LoadList.push_back(make_pair(first->second, first->first));
		}
		_SplitLoadDone = false;
		// send the first files
		for (uint i=0; i<ASYNC_LOADING_SPLIT && !_LoadList.empty(); ++i)
		{
			CAsyncFileManagerSound::getInstance().loadWavFile(_LoadList.front().first, CStringMapper::unmap(_LoadList.front().second)+".wav");
			_LoadList.pop_front();
		}
		// add a end loading event...
		CAsyncFileManagerSound::getInstance().signal(&_SplitLoadDone);
		// and register for update on the mixer
		CAudioMixerUser::instance()->registerUpdate(this);
	}
	*/
}