1484 lines
40 KiB
C++
1484 lines
40 KiB
C++
#include "PrecompiledHeader.h"
|
|
|
|
#include "WResourceManagerImpl.h"
|
|
#include "StyleManager.h"
|
|
|
|
#include "CacheEngine.h"
|
|
|
|
#if defined (WIN32)
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#include "WFont.h"
|
|
|
|
//#define FORCE_LOW_CACHE_MEMORY
|
|
const unsigned int kConstrainedCacheLimit = 8 * 1024 * 1024;
|
|
|
|
extern bool neofont;
|
|
int idCounter = OTHERS_OFFSET;
|
|
|
|
namespace
|
|
{
|
|
const std::string kExtension_png(".png");
|
|
const std::string kExtension_gbk(".gbk");
|
|
const std::string kExtension_font(".font");
|
|
|
|
// mutex meant for the cache map
|
|
boost::mutex sCacheMutex;
|
|
// mutex meant to protect against unthread-safe calls into JFileSystem, etc.
|
|
boost::mutex sLoadFunctionMutex;
|
|
}
|
|
|
|
WResourceManager* WResourceManager::sInstance = NULL;
|
|
|
|
WResourceManager* WResourceManager::Instance()
|
|
{
|
|
if (sInstance == NULL)
|
|
{
|
|
sInstance = NEW ResourceManagerImpl;
|
|
}
|
|
|
|
return sInstance;
|
|
}
|
|
|
|
void WResourceManager::Terminate()
|
|
{
|
|
if (sInstance)
|
|
SAFE_DELETE(sInstance);
|
|
}
|
|
|
|
|
|
int ResourceManagerImpl::RetrieveError()
|
|
{
|
|
return lastError;
|
|
}
|
|
|
|
bool ResourceManagerImpl::RemoveOldest()
|
|
{
|
|
if (sampleWCache.RemoveOldest()) return true;
|
|
if (textureWCache.RemoveOldest()) return true;
|
|
if (psiWCache.RemoveOldest()) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//ResourceManagerImpl
|
|
void ResourceManagerImpl::DebugRender()
|
|
{
|
|
JRenderer* renderer = JRenderer::GetInstance();
|
|
WFont * font = ResourceManagerImpl::Instance()->GetWFont(Fonts::MAIN_FONT);
|
|
if (!font || !renderer) return;
|
|
|
|
font->SetColor(ARGB(255,255,255,255));
|
|
font->SetScale(DEFAULT_MAIN_FONT_SCALE);
|
|
renderer->FillRect(0, 0, SCREEN_WIDTH, 40, ARGB(128,155,0,0));
|
|
|
|
renderer->FillRect(0, SCREEN_HEIGHT - 20, SCREEN_WIDTH, 40, ARGB(128,155,0,0));
|
|
char buf[512];
|
|
|
|
unsigned long man = 0;
|
|
unsigned int misses = 0;
|
|
|
|
if (textureWCache.cacheItems < textureWCache.cache.size()) misses = textureWCache.cache.size() - textureWCache.cacheItems;
|
|
|
|
if (textureWCache.totalSize > textureWCache.cacheSize) man = textureWCache.totalSize - textureWCache.cacheSize;
|
|
|
|
sprintf(buf, "Textures %u+%llu (of %u) items (%u misses), Pixels: %lu (of %lu) + %lu", textureWCache.cacheItems,
|
|
(long long unsigned int) textureWCache.managed.size(), textureWCache.maxCached, misses,
|
|
textureWCache.cacheSize, textureWCache.maxCacheSize, man);
|
|
font->DrawString(buf, 10, 5);
|
|
|
|
#ifdef PSP
|
|
//deliberately off - these functions aren't thread safe!
|
|
//int maxLinear = ramAvailableLineareMax();
|
|
//int ram = ramAvailable();
|
|
|
|
//sprintf(buf, "Ram : linear max: %i - total : %i sceSize : %i\n", maxLinear, ram, sceSize);
|
|
//font->DrawString(buf, 10, 20);
|
|
#endif
|
|
|
|
sprintf(buf, "Time: %u. Total Size: %lu (%lu cached, %lu managed). ", lastTime, Size(), SizeCached(), SizeManaged());
|
|
font->DrawString(buf, SCREEN_WIDTH - 10, SCREEN_HEIGHT - 15, JGETEXT_RIGHT);
|
|
|
|
#ifdef DEBUG_CACHE
|
|
if(debugMessage.size())
|
|
font->DrawString(debugMessage.c_str(), SCREEN_WIDTH-10,SCREEN_HEIGHT-25,JGETEXT_RIGHT);
|
|
|
|
#endif
|
|
}
|
|
|
|
unsigned long ResourceManagerImpl::Size()
|
|
{
|
|
unsigned long res = 0;
|
|
res += textureWCache.totalSize;
|
|
res += sampleWCache.totalSize;
|
|
res += psiWCache.totalSize;
|
|
return res;
|
|
}
|
|
|
|
unsigned long ResourceManagerImpl::SizeCached()
|
|
{
|
|
unsigned long res = 0;
|
|
res += textureWCache.cacheSize;
|
|
res += sampleWCache.cacheSize;
|
|
res += psiWCache.cacheSize;
|
|
return res;
|
|
}
|
|
|
|
unsigned long ResourceManagerImpl::SizeManaged()
|
|
{
|
|
unsigned long res = 0;
|
|
if (textureWCache.totalSize > textureWCache.cacheSize) res += textureWCache.totalSize - textureWCache.cacheSize;
|
|
|
|
if (sampleWCache.totalSize > sampleWCache.cacheSize) res += sampleWCache.totalSize - sampleWCache.cacheSize;
|
|
|
|
if (psiWCache.totalSize > psiWCache.cacheSize) res += psiWCache.totalSize - psiWCache.cacheSize;
|
|
|
|
return res;
|
|
}
|
|
|
|
unsigned int ResourceManagerImpl::Count()
|
|
{
|
|
unsigned int count = 0;
|
|
count += textureWCache.cacheItems;
|
|
count += textureWCache.managed.size();
|
|
count += sampleWCache.cacheItems;
|
|
count += sampleWCache.managed.size();
|
|
count += psiWCache.cacheItems;
|
|
count += psiWCache.managed.size();
|
|
return count;
|
|
}
|
|
|
|
unsigned int ResourceManagerImpl::CountCached()
|
|
{
|
|
unsigned int count = 0;
|
|
count += textureWCache.cacheItems;
|
|
count += sampleWCache.cacheItems;
|
|
count += psiWCache.cacheItems;
|
|
return count;
|
|
}
|
|
unsigned int ResourceManagerImpl::CountManaged()
|
|
{
|
|
unsigned int count = 0;
|
|
count += textureWCache.managed.size();
|
|
count += sampleWCache.managed.size();
|
|
count += psiWCache.managed.size();
|
|
return count;
|
|
}
|
|
|
|
unsigned int ResourceManagerImpl::nowTime()
|
|
{
|
|
if (lastTime > MAX_CACHE_TIME) FlattenTimes();
|
|
|
|
return ++lastTime;
|
|
}
|
|
|
|
void ResourceManagerImpl::FlattenTimes()
|
|
{
|
|
unsigned int t;
|
|
lastTime = sampleWCache.Flatten();
|
|
|
|
t = textureWCache.Flatten();
|
|
if (t > lastTime) lastTime = t;
|
|
|
|
t = psiWCache.Flatten();
|
|
if (t > lastTime) lastTime = t;
|
|
}
|
|
|
|
ResourceManagerImpl::ResourceManagerImpl()
|
|
{
|
|
DebugTrace("Init ResourceManagerImpl : " << this);
|
|
#ifdef DEBUG_CACHE
|
|
menuCached = 0;
|
|
#endif
|
|
|
|
psiWCache.Resize(PSI_CACHE_SIZE, 20);
|
|
sampleWCache.Resize(SAMPLES_CACHE_SIZE, MAX_CACHED_SAMPLES);
|
|
textureWCache.Resize(TEXTURES_CACHE_MINSIZE, MAX_CACHE_OBJECTS);
|
|
lastTime = 0;
|
|
lastError = CACHE_ERROR_NONE;
|
|
|
|
bThemedCards = false;
|
|
|
|
LOG("Calling CacheEngine::Create");
|
|
|
|
#ifdef PSP
|
|
CacheEngine::Create<UnthreadedCardRetriever>(textureWCache);
|
|
#else
|
|
CacheEngine::Create<ThreadedCardRetriever>(textureWCache);
|
|
#endif
|
|
}
|
|
|
|
ResourceManagerImpl::~ResourceManagerImpl()
|
|
{
|
|
LOG("==Destroying ResourceManagerImpl==");
|
|
RemoveWFonts();
|
|
|
|
CacheEngine::Terminate();
|
|
LOG("==Successfully Destroyed ResourceManagerImpl==");
|
|
}
|
|
|
|
bool ResourceManagerImpl::IsThreaded()
|
|
{
|
|
return CacheEngine::IsThreaded();
|
|
}
|
|
|
|
JQuadPtr ResourceManagerImpl::RetrieveCard(MTGCard * card, int style, int submode)
|
|
{
|
|
//Cards are never, ever resource managed, so just check cache.
|
|
if (!card || options[Options::DISABLECARDS].number) return JQuadPtr();
|
|
|
|
submode = submode | TEXTURE_SUB_CARD;
|
|
|
|
static std::ostringstream filename;
|
|
filename.str("");
|
|
filename << setlist[card->setId] << "/" << card->getImageName();
|
|
|
|
int id = card->getMTGId();
|
|
|
|
//Aliases.
|
|
if (style == RETRIEVE_THUMB)
|
|
{
|
|
submode = submode | TEXTURE_SUB_THUMB;
|
|
style = RETRIEVE_NORMAL;
|
|
}
|
|
|
|
JQuadPtr jq = RetrieveQuad(filename.str(), 0, 0, 0, 0, "", style, submode | TEXTURE_SUB_5551, id);
|
|
|
|
lastError = textureWCache.mError;
|
|
if (jq)
|
|
{
|
|
jq->SetHotSpot(static_cast<float> (jq->mTex->mWidth / 2), static_cast<float> (jq->mTex->mHeight / 2));
|
|
return jq;
|
|
}
|
|
|
|
return JQuadPtr();
|
|
}
|
|
|
|
int ResourceManagerImpl::AddQuadToManaged(const WManagedQuad& inQuad)
|
|
{
|
|
int id = mIDLookupMap.size();
|
|
mIDLookupMap.insert(make_pair(id, inQuad.resname));
|
|
mManagedQuads.insert(make_pair(inQuad.resname, inQuad));
|
|
|
|
return id;
|
|
}
|
|
|
|
int ResourceManagerImpl::CreateQuad(const string &quadName, const string &textureName, float x, float y, float width, float height)
|
|
{
|
|
if (!quadName.size() || !textureName.size()) return INVALID_ID;
|
|
|
|
if (GetQuad(quadName) != NULL)
|
|
{
|
|
assert(false);
|
|
return ALREADY_EXISTS;
|
|
}
|
|
|
|
WCachedTexture * jtex = textureWCache.Retrieve(0, textureName, RETRIEVE_MANAGE);
|
|
lastError = textureWCache.mError;
|
|
int id = INVALID_ID;
|
|
//Somehow, jtex wasn't promoted.
|
|
if (RETRIEVE_MANAGE && jtex && !jtex->isPermanent()) return id;
|
|
|
|
if (jtex)
|
|
{
|
|
JQuadPtr quad = jtex->GetQuad(x, y, width, height, quadName);
|
|
|
|
if (quad.get())
|
|
{
|
|
jtex->deadbolt();
|
|
|
|
WManagedQuad mq;
|
|
mq.resname = quadName;
|
|
mq.texture = jtex;
|
|
id = AddQuadToManaged(mq);
|
|
}
|
|
}
|
|
|
|
assert(id != INVALID_ID);
|
|
return id;
|
|
}
|
|
|
|
JQuadPtr ResourceManagerImpl::GetQuad(const string &quadName)
|
|
{
|
|
JQuadPtr result;
|
|
ManagedQuadMap::const_iterator found = mManagedQuads.find(quadName);
|
|
if (found != mManagedQuads.end())
|
|
{
|
|
result = found->second.texture->GetQuad(quadName);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
JQuadPtr ResourceManagerImpl::GetQuad(int id)
|
|
{
|
|
JQuadPtr result;
|
|
if (id < 0 || id >= (int) mManagedQuads.size()) return result;
|
|
|
|
IDLookupMap::const_iterator key = mIDLookupMap.find(id);
|
|
if (key != mIDLookupMap.end())
|
|
{
|
|
WCachedTexture* jtex = mManagedQuads[key->second].texture;
|
|
if (jtex)
|
|
{
|
|
result = jtex->GetQuad(key->second);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
JQuadPtr ResourceManagerImpl::RetrieveTempQuad(const string& filename, int submode)
|
|
{
|
|
return RetrieveQuad(filename, 0, 0, 0, 0, "temporary", RETRIEVE_NORMAL, submode);
|
|
}
|
|
|
|
JQuadPtr ResourceManagerImpl::RetrieveQuad(const string& filename, float offX, float offY, float width, float height, string resname,
|
|
int style, int submode, int id)
|
|
{
|
|
//Lookup managed resources, but only with a real resname.
|
|
if (resname.size() && (style == RETRIEVE_MANAGE || style == RETRIEVE_RESOURCE))
|
|
{
|
|
JQuadPtr quad = GetQuad(resname);
|
|
if (quad.get() || style == RETRIEVE_RESOURCE) return quad;
|
|
}
|
|
|
|
//Aliases.
|
|
if (style == RETRIEVE_THUMB)
|
|
{
|
|
submode = submode | TEXTURE_SUB_THUMB;
|
|
style = RETRIEVE_NORMAL;
|
|
}
|
|
|
|
//Resname defaults to filename.
|
|
if (!resname.size()) resname = filename;
|
|
|
|
//No quad, but we have a managed texture for this!
|
|
WCachedTexture* jtex = textureWCache.Retrieve(id, filename, style, submode);
|
|
|
|
lastError = textureWCache.mError;
|
|
|
|
//Somehow, jtex wasn't promoted.
|
|
if (style == RETRIEVE_MANAGE && jtex && !jtex->isPermanent()) return JQuadPtr();
|
|
|
|
//Make this quad, overwriting any similarly resname'd quads.
|
|
if (jtex)
|
|
{
|
|
JQuadPtr quad = jtex->GetQuad(offX, offY, width, height, resname);
|
|
|
|
if (!quad.get())
|
|
return quad;
|
|
|
|
if (style == RETRIEVE_MANAGE && resname != "")
|
|
{
|
|
WManagedQuad mq;
|
|
mq.resname = resname;
|
|
mq.texture = jtex;
|
|
AddQuadToManaged(mq);
|
|
}
|
|
|
|
if (style == RETRIEVE_LOCK)
|
|
jtex->lock();
|
|
else if (style == RETRIEVE_UNLOCK)
|
|
jtex->unlock();
|
|
else if (style == RETRIEVE_MANAGE) jtex->deadbolt();
|
|
|
|
return quad;
|
|
}
|
|
|
|
//Texture doesn't exist, so no quad.
|
|
return JQuadPtr();
|
|
}
|
|
|
|
void ResourceManagerImpl::Release(JTexture * tex)
|
|
{
|
|
if (!tex) return;
|
|
|
|
//Copied direct from WCache::Release(). This is quick and dirty.
|
|
map<int, WCachedTexture*>::iterator it;
|
|
for (it = textureWCache.cache.begin(); it != textureWCache.cache.end(); it++)
|
|
{
|
|
if (it->second && it->second->compare(tex)) break;
|
|
}
|
|
|
|
if (it == textureWCache.cache.end()) return; //Not here, can't release.
|
|
|
|
if (it->second)
|
|
{
|
|
it->second->unlock(); //Release one lock.
|
|
if (it->second->locks != WRES_UNLOCKED) //Normally we'd call isLocked, but this way ignores quads.
|
|
return; //Locked
|
|
}
|
|
|
|
textureWCache.Delete(it->second);
|
|
textureWCache.cache.erase(it);
|
|
return; //Released!
|
|
}
|
|
|
|
void ResourceManagerImpl::ClearUnlocked()
|
|
{
|
|
textureWCache.ClearUnlocked();
|
|
sampleWCache.ClearUnlocked();
|
|
psiWCache.ClearUnlocked();
|
|
}
|
|
|
|
void ResourceManagerImpl::Release(JSample * sample)
|
|
{
|
|
if (!sample) return;
|
|
|
|
sampleWCache.Release(sample);
|
|
}
|
|
|
|
JTexture * ResourceManagerImpl::RetrieveTexture(const string& filename, int style, int submode)
|
|
{
|
|
//Aliases.
|
|
if (style == RETRIEVE_THUMB)
|
|
{
|
|
submode = submode | TEXTURE_SUB_THUMB;
|
|
style = RETRIEVE_NORMAL;
|
|
}
|
|
|
|
WCachedTexture* res = textureWCache.Retrieve(0, filename, style, submode);
|
|
lastError = textureWCache.mError;
|
|
|
|
if (res)
|
|
{ //a non-null result will always be good.
|
|
JTexture * t = res->Actual();
|
|
return t;
|
|
}
|
|
#ifdef DEBUG_CACHE
|
|
else
|
|
{
|
|
switch(textureWCache.mError)
|
|
{
|
|
case CACHE_ERROR_NONE:
|
|
debugMessage = "Not in cache: ";
|
|
break;
|
|
case CACHE_ERROR_404:
|
|
debugMessage = "File not found: ";
|
|
break;
|
|
case CACHE_ERROR_BAD_ALLOC:
|
|
debugMessage = "Out of memory: ";
|
|
break;
|
|
case CACHE_ERROR_BAD:
|
|
debugMessage = "Cache bad: ";
|
|
break;
|
|
case CACHE_ERROR_NOT_MANAGED:
|
|
debugMessage = "Resource not managed: ";
|
|
break;
|
|
case CACHE_ERROR_LOST:
|
|
debugMessage = "Resource went bad, potential memory leak: ";
|
|
break;
|
|
default:
|
|
debugMessage = "Unspecified error: ";
|
|
}
|
|
debugMessage += filename;
|
|
}
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int ResourceManagerImpl::CreateTexture(const string &textureName)
|
|
{
|
|
JTexture * jtex = RetrieveTexture(textureName, RETRIEVE_MANAGE);
|
|
|
|
if (jtex) return (int) jtex->mTexId; //Because it's unsigned on windows/linux.
|
|
|
|
return INVALID_ID;
|
|
}
|
|
|
|
JTexture* ResourceManagerImpl::GetTexture(const string &textureName)
|
|
{
|
|
JTexture * jtex = RetrieveTexture(textureName, RETRIEVE_RESOURCE);
|
|
return jtex;
|
|
}
|
|
|
|
JTexture* ResourceManagerImpl::GetTexture(int id)
|
|
{
|
|
map<int, WCachedTexture*>::iterator it;
|
|
JTexture *jtex = NULL;
|
|
|
|
if (id == INVALID_ID) return NULL;
|
|
|
|
for (it = textureWCache.managed.begin(); it != textureWCache.managed.end(); it++)
|
|
{
|
|
if (it->second)
|
|
{
|
|
jtex = it->second->Actual();
|
|
if (id == (int) jtex->mTexId) return jtex;
|
|
}
|
|
}
|
|
|
|
return jtex;
|
|
}
|
|
|
|
hgeParticleSystemInfo * ResourceManagerImpl::RetrievePSI(const string& filename, JQuad * texture, int style, int submode)
|
|
{
|
|
if (!texture) return NULL;
|
|
|
|
WCachedParticles * res = psiWCache.Retrieve(0, filename, style, submode);
|
|
lastError = psiWCache.mError;
|
|
|
|
if (res) //A non-null result will always be good.
|
|
{
|
|
hgeParticleSystemInfo * i = res->Actual();
|
|
i->sprite = texture;
|
|
return i;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
JSample * ResourceManagerImpl::RetrieveSample(const string& filename, int style, int submode)
|
|
{
|
|
WCachedSample * tc = NULL;
|
|
tc = sampleWCache.Retrieve(0, filename, style, submode);
|
|
lastError = sampleWCache.mError;
|
|
|
|
//Sample exists! Get it.
|
|
if (tc && tc->isGood())
|
|
{
|
|
JSample * js = tc->Actual();
|
|
return js;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
string ResourceManagerImpl::graphicsFile(const string& filename)
|
|
{
|
|
char buf[512];
|
|
|
|
//Check the theme folder.
|
|
string theme = options[Options::ACTIVE_THEME].str;
|
|
|
|
//Check for a theme style renaming:
|
|
if (filename != "style.txt")
|
|
{
|
|
WStyle * ws = options.getStyle();
|
|
if (ws)
|
|
{
|
|
sprintf(buf, "themes/%s/%s", theme.c_str(), ws->stylized(filename).c_str());
|
|
if (fileOK(buf)) return buf;
|
|
}
|
|
}
|
|
|
|
if (theme != "" && theme != "Default")
|
|
{
|
|
sprintf(buf, "themes/%s/%s", theme.c_str(), filename.c_str());
|
|
if (fileOK(buf)) return buf;
|
|
}
|
|
/*
|
|
//FIXME Put back when we're using modes.
|
|
//Failure. Check mode graphics
|
|
string mode = options[Options::ACTIVE_MODE].str;
|
|
|
|
if(mode != "" && mode != "Default"){
|
|
sprintf(buf,"modes/%s/graphics/%s",mode.c_str(),filename.c_str());
|
|
if(fileOK(buf,true))
|
|
return buf;
|
|
}
|
|
*/
|
|
//Failure. Check graphics
|
|
char graphdir[512];
|
|
sprintf(graphdir, "graphics/%s", filename.c_str());
|
|
if (fileOK(graphdir)) return graphdir;
|
|
|
|
//Failure. Check sets.
|
|
sprintf(buf, "sets/%s", filename.c_str());
|
|
if (fileOK(buf)) return buf;
|
|
|
|
//Failure. Check raw faile.
|
|
sprintf(buf, "%s", filename.c_str());
|
|
if (fileOK(buf)) return buf;
|
|
|
|
//Complete abject failure. Probably a crash...
|
|
return graphdir;
|
|
}
|
|
|
|
string ResourceManagerImpl::avatarFile(const string& filename)
|
|
{
|
|
char buf[512];
|
|
|
|
//Check the profile folder.
|
|
string profile = options[Options::ACTIVE_PROFILE].str;
|
|
|
|
if (profile != "" && profile != "Default")
|
|
{
|
|
sprintf(buf, "profiles/%s/%s", profile.c_str(), filename.c_str());
|
|
if (fileOK(buf)) return buf;
|
|
}
|
|
else
|
|
{
|
|
sprintf(buf, "player/%s", filename.c_str());
|
|
if (fileOK(buf)) return buf;
|
|
}
|
|
|
|
//Check the theme folder.
|
|
string theme = options[Options::ACTIVE_THEME].str;
|
|
|
|
if (theme != "" && theme != "Default")
|
|
{
|
|
sprintf(buf, "themes/%s/%s", theme.c_str(), filename.c_str());
|
|
if (fileOK(buf)) return buf;
|
|
}
|
|
/*
|
|
//FIXME Put back when we're using modes.
|
|
//Failure. Check mode graphics
|
|
string mode = options[Options::ACTIVE_MODE].str;
|
|
|
|
if(mode != "" && mode != "Default"){
|
|
sprintf(buf,"modes/%s/graphics/%s",mode.c_str(),filename.c_str());
|
|
if(fileOK(buf,true))
|
|
return buf;
|
|
}
|
|
*/
|
|
|
|
//Failure. Check Baka
|
|
sprintf(buf, "ai/baka/avatars/%s", filename.c_str());
|
|
if (fileOK(buf)) return buf;
|
|
|
|
//Failure. Check graphics
|
|
sprintf(buf, "graphics/%s", filename.c_str());
|
|
if (fileOK(buf)) return buf;
|
|
|
|
//Failure. Check raw faile.
|
|
sprintf(buf, "%s", filename.c_str());
|
|
if (fileOK(buf)) return buf;
|
|
|
|
//Complete abject failure. Probably a crash...
|
|
return "";
|
|
}
|
|
|
|
string ResourceManagerImpl::cardFile(const string& filename)
|
|
{
|
|
char buf[512];
|
|
string::size_type i = 0;
|
|
string set;
|
|
JFileSystem* fs = JFileSystem::GetInstance();
|
|
|
|
//Check the theme folder.
|
|
string theme = options[Options::ACTIVE_THEME].str;
|
|
|
|
if (theme != "" && theme != "Default")
|
|
{
|
|
//Does this theme use custom cards?
|
|
if (bThemedCards)
|
|
{
|
|
//Check zipped first. Discover set name.
|
|
for (i = 0; i < filename.size(); i++)
|
|
{
|
|
if (filename[i] == '\\' || filename[i] == '/') break;
|
|
}
|
|
|
|
if (i != filename.size()) set = filename.substr(0, i);
|
|
|
|
if (set.size())
|
|
{
|
|
char zipname[512];
|
|
sprintf(zipname, "themes/%s/sets/%s/%s.zip", theme.c_str(), set.c_str(), set.c_str());
|
|
if (fs->AttachZipFile(zipname)) return filename.substr(i + 1);
|
|
}
|
|
|
|
sprintf(buf, "themes/%s/sets/%s", theme.c_str(), filename.c_str());
|
|
if (fileOK(buf)) return buf; //Themed, unzipped.
|
|
}
|
|
}
|
|
|
|
//FIXME Put back when we're using modes.
|
|
/*
|
|
//Failure. Check mode
|
|
string mode = options[Options::ACTIVE_MODE].str;
|
|
|
|
if(mode != "" && mode != "Default"){
|
|
sprintf(buf,"modes/%s/sets/%s",mode.c_str(),filename.c_str());
|
|
if(fileOK(buf,true))
|
|
return buf;
|
|
}
|
|
*/
|
|
//Failure. Assume it's in a zip file?
|
|
if (!set.size())
|
|
{ //Didn't fill "set" string, so do it now.
|
|
for (i = 0; i < filename.size(); i++)
|
|
{
|
|
if (filename[i] == '\\' || filename[i] == '/') break;
|
|
}
|
|
|
|
if (i != filename.size()) set = filename.substr(0, i);
|
|
}
|
|
|
|
if (set.size())
|
|
{
|
|
char zipname[512];
|
|
sprintf(zipname, "sets/%s/%s.zip", set.c_str(), set.c_str());
|
|
if (fs->AttachZipFile(zipname)) return filename.substr(i + 1);
|
|
}
|
|
|
|
//Failure. Check for unzipped file in sets
|
|
char defdir[512];
|
|
sprintf(defdir, "sets/%s", filename.c_str());
|
|
if (fileOK(defdir)) return defdir;
|
|
|
|
//Complete failure.
|
|
return "";
|
|
}
|
|
|
|
string ResourceManagerImpl::musicFile(const string& filename)
|
|
{
|
|
char buf[512];
|
|
|
|
//Check the theme folder.
|
|
string theme = options[Options::ACTIVE_THEME].str;
|
|
|
|
if (theme != "" && theme != "Default")
|
|
{
|
|
sprintf(buf, "themes/%s/sound/%s", theme.c_str(), filename.c_str());
|
|
if (fileOK(buf)) return buf;
|
|
}
|
|
|
|
/*
|
|
//FIXME Put back when we're using modes.
|
|
//Failure. Check mode
|
|
string mode = options[Options::ACTIVE_MODE].str;
|
|
|
|
if(mode != "" && mode != "Default"){
|
|
sprintf(buf,"modes/%s/sound/%s",mode.c_str(),filename.c_str());
|
|
if(fileOK(buf,true))
|
|
return buf;
|
|
}*/
|
|
|
|
//Failure. Check sound
|
|
char defdir[512];
|
|
sprintf(defdir, "sound/%s", filename.c_str());
|
|
if (fileOK(defdir)) return defdir;
|
|
|
|
//Failure. Check raw file.
|
|
sprintf(defdir, "%s", filename.c_str());
|
|
if (fileOK(defdir)) return defdir;
|
|
|
|
//Complete abject failure. Probably a crash...
|
|
return "";
|
|
}
|
|
|
|
string ResourceManagerImpl::sfxFile(const string& filename)
|
|
{
|
|
char buf[512];
|
|
|
|
//Check the theme folder.
|
|
string theme = options[Options::ACTIVE_THEME].str;
|
|
|
|
if (theme != "" && theme != "Default")
|
|
{
|
|
sprintf(buf, "themes/%s/sound/sfx/%s", theme.c_str(), filename.c_str());
|
|
if (fileOK(buf)) return buf;
|
|
}
|
|
|
|
/*
|
|
//FIXME: Put back when we're using modes.
|
|
//Failure. Check mode
|
|
string mode = options[Options::ACTIVE_MODE].str;
|
|
if(mode != "" && mode != "Default"){
|
|
sprintf(buf,"modes/%s/sound/sfx/%s",mode.c_str(),filename.c_str());
|
|
if(fileOK(buf,true))
|
|
return buf;
|
|
}
|
|
*/
|
|
//Failure. Check sound
|
|
char defdir[512];
|
|
sprintf(defdir, "sound/sfx/%s", filename.c_str());
|
|
if (fileOK(defdir)) return defdir;
|
|
|
|
//Complete abject failure. Probably a crash...
|
|
return "";
|
|
}
|
|
|
|
bool ResourceManagerImpl::dirOK(const string& dirname)
|
|
{
|
|
return JFileSystem::GetInstance()->DirExists(dirname);
|
|
}
|
|
|
|
bool ResourceManagerImpl::fileOK(const string& filename)
|
|
{
|
|
return JFileSystem::GetInstance()->FileExists(filename);
|
|
}
|
|
|
|
void ResourceManagerImpl::InitFonts(const std::string& inLang)
|
|
{
|
|
unsigned int idOffset = 0;
|
|
|
|
if (inLang.compare("cn") == 0)
|
|
{
|
|
mFontFileExtension = kExtension_gbk;
|
|
LoadWFont("simon", 12, Fonts::MAIN_FONT);
|
|
LoadWFont("pspf3", 16, Fonts::MENU_FONT);
|
|
LoadWFont("f3", 16, Fonts::MENU_FONT);
|
|
LoadWFont("magic", 16, Fonts::MAGIC_FONT);
|
|
LoadWFont("smallface", 12, Fonts::SMALLFACE_FONT);
|
|
|
|
idOffset = Fonts::kSingleByteFontOffset;
|
|
}
|
|
|
|
if (inLang.compare("jp") == 0)
|
|
{
|
|
mFontFileExtension = kExtension_font;
|
|
LoadWFont("simon", 12, Fonts::MAIN_FONT);
|
|
LoadWFont("f3", 16, Fonts::MENU_FONT);
|
|
LoadWFont("magic", 16, Fonts::MAGIC_FONT);
|
|
LoadWFont("smallface", 12, Fonts::SMALLFACE_FONT);
|
|
|
|
idOffset = Fonts::kSingleByteFontOffset;
|
|
}
|
|
|
|
mFontFileExtension = kExtension_png;
|
|
|
|
|
|
#if defined (PSP)
|
|
LoadWFont("pspsimon", 11, Fonts::MAIN_FONT + idOffset);
|
|
GetWFont(Fonts::MAIN_FONT)->SetTracking(-1);
|
|
LoadWFont("pspf3", 16, Fonts::MENU_FONT + idOffset);
|
|
#else
|
|
LoadWFont("simon", 11, Fonts::MAIN_FONT + idOffset);
|
|
GetWFont(Fonts::MAIN_FONT)->SetTracking(-1);
|
|
LoadWFont("f3", 16, Fonts::MENU_FONT + idOffset);
|
|
#endif
|
|
LoadWFont("magic", 16, Fonts::MAGIC_FONT + idOffset);
|
|
LoadWFont("smallface", 7, Fonts::SMALLFACE_FONT + idOffset);
|
|
}
|
|
|
|
int ResourceManagerImpl::ReloadWFonts()
|
|
{
|
|
RemoveWFonts();
|
|
|
|
string lang = options[Options::LANG].str;
|
|
std::transform(lang.begin(), lang.end(), lang.begin(), ::tolower);
|
|
|
|
InitFonts(lang);
|
|
|
|
return 1;
|
|
}
|
|
|
|
WFont* ResourceManagerImpl::LoadWFont(const string& inFontname, int inFontHeight, int inFontID)
|
|
{
|
|
WFont* font = GetWFont(inFontID);
|
|
if (font)
|
|
{
|
|
return font;
|
|
}
|
|
|
|
string mFontName = inFontname + mFontFileExtension;
|
|
string path = graphicsFile(mFontName);
|
|
|
|
if (mFontFileExtension == kExtension_font)
|
|
font = NEW WUFont(inFontID, path.c_str(), inFontHeight, true);
|
|
else if (mFontFileExtension == kExtension_gbk)
|
|
font = NEW WGBKFont(inFontID, path.c_str(), inFontHeight, true);
|
|
else
|
|
font = NEW WLBFont(inFontID, path.c_str(), inFontHeight, true);
|
|
mWFontMap[inFontID] = font;
|
|
|
|
return font;
|
|
}
|
|
|
|
WFont* ResourceManagerImpl::GetWFont(int id)
|
|
{
|
|
WFont* font = NULL;
|
|
FontMap::iterator iter = mWFontMap.find(id);
|
|
if (iter != mWFontMap.end())
|
|
{
|
|
font = iter->second;
|
|
}
|
|
return font;
|
|
}
|
|
|
|
void ResourceManagerImpl::RemoveWFonts()
|
|
{
|
|
for (FontMap::iterator font = mWFontMap.begin(); font != mWFontMap.end(); ++font)
|
|
{
|
|
delete font->second;
|
|
}
|
|
mWFontMap.clear();
|
|
}
|
|
|
|
void ResourceManagerImpl::ResetCacheLimits()
|
|
{
|
|
#if defined PSP
|
|
unsigned int ram = ramAvailable();
|
|
unsigned int myNewSize = ram - OPERATIONAL_SIZE + textureWCache.totalSize;
|
|
if (myNewSize < TEXTURES_CACHE_MINSIZE)
|
|
{
|
|
DebugTrace( "Error, Not enough RAM for Cache: " << myNewSize << " - total Ram: " << ram);
|
|
}
|
|
textureWCache.Resize(MIN(myNewSize, HUGE_CACHE_LIMIT), MAX_CACHE_OBJECTS);
|
|
#else
|
|
#ifdef FORCE_LOW_CACHE_MEMORY
|
|
textureWCache.Resize(kConstrainedCacheLimit, MAX_CACHE_OBJECTS);
|
|
#else
|
|
unsigned long cacheSize = options["cachesize"].number ? (unsigned long)(options["cachesize"].number) * 1024 * 1024 : HUGE_CACHE_LIMIT;
|
|
textureWCache.Resize(cacheSize,MAX_CACHE_OBJECTS);
|
|
#endif
|
|
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
JMusic * ResourceManagerImpl::ssLoadMusic(const char *fileName)
|
|
{
|
|
string file = musicFile(fileName);
|
|
if (!file.size()) return NULL;
|
|
return JSoundSystem::GetInstance()->LoadMusic(file.c_str());
|
|
}
|
|
|
|
void ResourceManagerImpl::Refresh()
|
|
{
|
|
//Really easy cache relinking.
|
|
ReloadWFonts();
|
|
sampleWCache.Refresh();
|
|
textureWCache.Refresh();
|
|
psiWCache.Refresh();
|
|
|
|
//Check for card images in theme.
|
|
bThemedCards = false;
|
|
if (!options[Options::ACTIVE_THEME].isDefault())
|
|
{
|
|
char buf[512];
|
|
sprintf(buf, "themes/%s/sets", options[Options::ACTIVE_THEME].str.c_str());
|
|
|
|
if (dirOK(buf)) bThemedCards = true;
|
|
}
|
|
}
|
|
|
|
//WCache
|
|
template<class cacheItem, class cacheActual>
|
|
bool WCache<cacheItem, cacheActual>::RemoveOldest()
|
|
{
|
|
typename map<int, cacheItem*>::iterator oldest = cache.end();
|
|
|
|
for (typename map<int, cacheItem*>::iterator it = cache.begin(); it != cache.end(); ++it)
|
|
{
|
|
if (it->second && !it->second->isLocked() && (oldest == cache.end() || it->second->lastTime < oldest->second->lastTime)) oldest
|
|
= it;
|
|
}
|
|
|
|
if (oldest != cache.end() && oldest->second && !oldest->second->isLocked())
|
|
{
|
|
#ifdef DEBUG_CACHE
|
|
std::ostringstream stream;
|
|
stream << "erasing from cache: " << oldest->second->mFilename << " " << oldest->first;
|
|
LOG(stream.str().c_str());
|
|
#endif
|
|
Delete(oldest->second);
|
|
cache.erase(oldest);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
template<class cacheItem, class cacheActual>
|
|
void WCache<cacheItem, cacheActual>::ClearUnlocked()
|
|
{
|
|
typename map<int, cacheItem*>::iterator it, next;
|
|
|
|
for (it = cache.begin(); it != cache.end(); it = next)
|
|
{
|
|
next = it;
|
|
next++;
|
|
|
|
if (it->second && !it->second->isLocked())
|
|
{
|
|
Delete(it->second);
|
|
cache.erase(it);
|
|
}
|
|
else if (!it->second)
|
|
{
|
|
cache.erase(it);
|
|
}
|
|
}
|
|
}
|
|
template<class cacheItem, class cacheActual>
|
|
void WCache<cacheItem, cacheActual>::Resize(unsigned long size, int items)
|
|
{
|
|
maxCacheSize = size;
|
|
DebugTrace(typeid(cacheActual).name() << " cache resized to " << size << " bytes");
|
|
|
|
#ifdef DEBUG_CACHE
|
|
std::ostringstream stream;
|
|
stream << "Max cache limit resized to " << size << ", items limit reset to " << items;
|
|
LOG(stream.str().c_str());
|
|
#endif
|
|
|
|
if (items > MAX_CACHE_OBJECTS || items < 1)
|
|
maxCached = MAX_CACHE_OBJECTS;
|
|
else
|
|
maxCached = items;
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
cacheItem* WCache<cacheItem, cacheActual>::AttemptNew(const string& filename, int submode)
|
|
{
|
|
cacheItem* item = NEW cacheItem;
|
|
if (!item)
|
|
{
|
|
mError = CACHE_ERROR_BAD_ALLOC;
|
|
return NULL;
|
|
}
|
|
|
|
mError = CACHE_ERROR_NONE;
|
|
|
|
if (!item->Attempt(filename, submode, mError) || !item->isGood())
|
|
{
|
|
//No such file. Fail
|
|
if (mError == CACHE_ERROR_404)
|
|
{
|
|
DebugTrace("AttemptNew failed to load. Deleting cache item " << ToHex(item));
|
|
SAFE_DELETE(item);
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
DebugTrace("AttemptNew failed to load (not a 404 error). Deleting cache item " << ToHex(item));
|
|
SAFE_DELETE(item);
|
|
mError = CACHE_ERROR_BAD;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//Success! Enforce cache limits, then return.
|
|
mError = CACHE_ERROR_NONE;
|
|
|
|
return item;
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
cacheItem* WCache<cacheItem, cacheActual>::Retrieve(int id, const string& filename, int style, int submode)
|
|
{
|
|
//Check cache.
|
|
mError = CACHE_ERROR_NONE; //Reset error status.
|
|
cacheItem* tc = Get(id, filename, style, submode);
|
|
|
|
//Retrieve resource only works on permanent items.
|
|
if (style == RETRIEVE_RESOURCE && tc && !tc->isPermanent())
|
|
{
|
|
mError = CACHE_ERROR_NOT_MANAGED;
|
|
return NULL;
|
|
}
|
|
|
|
//Perform lock or unlock on entry.
|
|
if (tc)
|
|
{
|
|
switch (style)
|
|
{
|
|
case RETRIEVE_LOCK:
|
|
tc->lock();
|
|
break;
|
|
case RETRIEVE_UNLOCK:
|
|
tc->unlock();
|
|
break;
|
|
case RETRIEVE_MANAGE:
|
|
if (!tc->isPermanent())
|
|
{
|
|
//Unlink the managed resource from the cache.
|
|
UnlinkCache(tc);
|
|
|
|
//Post it in managed resources.
|
|
managed[makeID(id, filename, submode)] = tc;
|
|
tc->deadbolt();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//Resource exists!
|
|
if (tc)
|
|
{
|
|
if (tc->isGood())
|
|
{
|
|
tc->hit();
|
|
return tc; //Everything fine.
|
|
}
|
|
//Something went wrong.
|
|
RemoveItem(tc);
|
|
mError = CACHE_ERROR_BAD;
|
|
}
|
|
|
|
//Record managed failure. Cache failure is recorded in Get().
|
|
if ((style == RETRIEVE_MANAGE || style == RETRIEVE_RESOURCE) && mError == CACHE_ERROR_404)
|
|
managed[makeID(id, filename, submode)] = NULL;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
int WCache<cacheItem, cacheActual>::makeID(int id, const string& filename, int submode)
|
|
{
|
|
int mId = id;
|
|
if (!mId)
|
|
{
|
|
mId = ids[filename];
|
|
if (!mId)
|
|
{
|
|
mId = idCounter++;
|
|
ids[filename] = mId;
|
|
}
|
|
}
|
|
|
|
if (submode & TEXTURE_SUB_THUMB)
|
|
mId += THUMBNAILS_OFFSET;
|
|
|
|
return mId;
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
cacheItem* WCache<cacheItem, cacheActual>::Get(int id, const string& filename, int style, int submode)
|
|
{
|
|
typename map<int, cacheItem*>::iterator it;
|
|
int lookup = makeID(id, filename, submode);
|
|
|
|
//Check for managed resources first. Always
|
|
it = managed.find(lookup);
|
|
|
|
//Something is managed.
|
|
if (it != managed.end())
|
|
{
|
|
return it->second; //A hit.
|
|
}
|
|
|
|
//Not managed, so look in cache.
|
|
if (style != RETRIEVE_MANAGE)
|
|
{
|
|
boost::mutex::scoped_lock lock(sCacheMutex);
|
|
//DebugTrace("Cache lock acquired, looking up index " << lookup);
|
|
it = cache.find(lookup);
|
|
//Well, we've found something...
|
|
if (it != cache.end())
|
|
{
|
|
if (!it->second)
|
|
{
|
|
mError = CACHE_ERROR_404;
|
|
//DebugTrace("cache hit, no item??");
|
|
//assert(false);
|
|
}
|
|
return it->second; //A hit, or maybe a miss.
|
|
}
|
|
}
|
|
|
|
// not hit in the cache, respect the RETRIEVE_EXISTING flag if present
|
|
if (style == RETRIEVE_EXISTING)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// no hit in cache, clear space before attempting to load a new one
|
|
// note: Cleanup() should ONLY be ever called on the main (UI) thread!
|
|
Cleanup();
|
|
|
|
// check if we're doing a card lookup
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// commented out for the following reason:
|
|
//cardgui updates so quick that the only reason a person would notice a cache miss is because it takes the
|
|
//engine longer to switch the card quad from kGeneric to the actual image, than it does to just let the update pass.
|
|
#if defined (PSP)
|
|
if (submode & TEXTURE_SUB_CARD)
|
|
{
|
|
// processing a cache miss, return a generic card & queue up an async read
|
|
|
|
// side note: using a string reference here to a global predefined string, as basic_string is not thread safe for allocations!
|
|
const std::string& cardPath = (submode & TEXTURE_SUB_THUMB) ? kGenericThumbCard : kGenericCard;
|
|
int genericCardId = makeID(0, cardPath, CACHE_NORMAL);
|
|
it = managed.find(genericCardId);
|
|
assert(it != managed.end());
|
|
|
|
CacheEngine::Instance()->QueueRequest(filename, submode, lookup);
|
|
return it->second;
|
|
}
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//Space in cache, make new texture
|
|
return LoadIntoCache(lookup, filename, submode, style);
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
cacheItem* WCache<cacheItem, cacheActual>::LoadIntoCache(int id, const string& filename, int submode, int style)
|
|
{
|
|
// note: my original implementation only had one lock (the cache mutex lock) - I eventually
|
|
// added this second one, as locking at the Get() call means that the main thread is blocked on doing a simple cache
|
|
// check. If you're hitting the system hard (like, paging up in the deck editor which forces 7 cards to load simultaneously),
|
|
// we'd block the UI thread for a long period at this point. So I moved the cache mutex to lock specifically only attempts to touch
|
|
// the shared cache container, and this separate lock was added to insulate us against thread safety issues in JGE. In particular,
|
|
// JFileSystem is particularly unsafe, as it assumes that we have only one zip loaded at a time... rather than add mutexes in JGE,
|
|
// I've kept it local to here.
|
|
boost::mutex::scoped_lock functionLock(sLoadFunctionMutex);
|
|
cacheItem* item = AttemptNew(filename, submode);
|
|
//assert(item);
|
|
if (style == RETRIEVE_MANAGE)
|
|
{
|
|
if (mError == CACHE_ERROR_404 || item)
|
|
{
|
|
managed[id] = item; //Record a hit or miss.
|
|
}
|
|
if (item)
|
|
{
|
|
item->deadbolt(); //Make permanent.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mError == CACHE_ERROR_404 || item)
|
|
{
|
|
boost::mutex::scoped_lock lock(sCacheMutex);
|
|
cache[id] = item;
|
|
DebugTrace("inserted item ptr " << ToHex(item) << " at index " << id);
|
|
}
|
|
}
|
|
|
|
if (item == NULL)
|
|
{
|
|
DebugTrace("Can't locate ");
|
|
if (submode & TEXTURE_SUB_THUMB)
|
|
{
|
|
DebugTrace("thumbnail ");
|
|
}
|
|
DebugTrace(filename);
|
|
|
|
return NULL; //Failure
|
|
}
|
|
|
|
//Succeeded in making a new item.
|
|
unsigned long isize = item->size();
|
|
totalSize += isize;
|
|
|
|
mError = CACHE_ERROR_NONE;
|
|
if (style != RETRIEVE_MANAGE)
|
|
{
|
|
cacheItems++;
|
|
cacheSize += isize;
|
|
}
|
|
|
|
#ifdef DEBUG_CACHE
|
|
std::ostringstream stream;
|
|
stream << "Cache insert: " << filename << " " << id << ", cacheItem count: " << cacheItems << ", cacheSize is now: " << cacheSize;
|
|
LOG(stream.str().c_str());
|
|
#endif
|
|
|
|
return item;
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
void WCache<cacheItem, cacheActual>::Refresh()
|
|
{
|
|
typename map<int, cacheItem*>::iterator it;
|
|
ClearUnlocked();
|
|
|
|
for (it = cache.begin(); it != cache.end(); it++)
|
|
{
|
|
if (it->second)
|
|
{
|
|
it->second->Refresh();
|
|
}
|
|
}
|
|
for (it = managed.begin(); it != managed.end(); it++)
|
|
{
|
|
if (it->second)
|
|
{
|
|
it->second->Refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
WCache<cacheItem, cacheActual>::WCache()
|
|
{
|
|
cacheSize = 0;
|
|
totalSize = 0;
|
|
|
|
maxCacheSize = TEXTURES_CACHE_MINSIZE;
|
|
|
|
maxCached = MAX_CACHE_OBJECTS;
|
|
cacheItems = 0;
|
|
mError = CACHE_ERROR_NONE;
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
WCache<cacheItem, cacheActual>::~WCache()
|
|
{
|
|
typename map<int, cacheItem*>::iterator it;
|
|
|
|
//Delete from cache & managed
|
|
for (it = cache.begin(); it != cache.end(); it++)
|
|
{
|
|
SAFE_DELETE(it->second);
|
|
}
|
|
|
|
for (it = managed.begin(); it != managed.end(); it++)
|
|
{
|
|
SAFE_DELETE(it->second);
|
|
}
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
bool WCache<cacheItem, cacheActual>::Cleanup()
|
|
{
|
|
bool result = true;
|
|
// this looks redundant, but the idea is, don't grab the mutex if there's no work to do
|
|
if (RequiresOldItemCleanup())
|
|
{
|
|
boost::mutex::scoped_lock lock(sCacheMutex);
|
|
while (RequiresOldItemCleanup())
|
|
{
|
|
if (!RemoveOldest())
|
|
{
|
|
result = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool WCacheSort::operator()(const WResource * l, const WResource * r)
|
|
{
|
|
if (!l || !r) return false;
|
|
return (l->lastTime < r->lastTime);
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
unsigned int WCache<cacheItem, cacheActual>::Flatten()
|
|
{
|
|
vector<cacheItem*> items;
|
|
unsigned int oldest = 0;
|
|
unsigned int lastSet = 0;
|
|
|
|
if (!cache.size()) return 0;
|
|
|
|
for (typename map<int, cacheItem*>::iterator it = cache.begin(); it != cache.end(); ++it)
|
|
{
|
|
if (!it->second) continue;
|
|
items.push_back(it->second);
|
|
}
|
|
|
|
sort(items.begin(), items.end(), WCacheSort());
|
|
|
|
for (typename vector<cacheItem*>::iterator it = items.begin(); it != items.end(); ++it)
|
|
{
|
|
assert((*it) && (*it)->lastTime > lastSet);
|
|
lastSet = (*it)->lastTime;
|
|
(*it)->lastTime = ++oldest;
|
|
}
|
|
|
|
return oldest + 1;
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
bool WCache<cacheItem, cacheActual>::RemoveMiss(int id)
|
|
{
|
|
typename map<int, cacheItem*>::iterator it = cache.end();
|
|
|
|
for (it = cache.begin(); it != cache.end(); it++)
|
|
{
|
|
if ((id == 0 || it->first == id) && it->second == NULL) break;
|
|
}
|
|
|
|
if (it != cache.end())
|
|
{
|
|
cache.erase(it);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
bool WCache<cacheItem, cacheActual>::RemoveItem(cacheItem * item, bool force)
|
|
{
|
|
typename map<int, cacheItem*>::iterator it;
|
|
|
|
if (item == NULL) return false; //Use RemoveMiss to remove cache misses, not this.
|
|
|
|
for (it = cache.begin(); it != cache.end(); it++)
|
|
{
|
|
if (it->second == item) break;
|
|
}
|
|
if (it != cache.end() && it->second && (force || !it->second->isLocked()))
|
|
{
|
|
Delete(it->second);
|
|
cache.erase(it);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
bool WCache<cacheItem, cacheActual>::UnlinkCache(cacheItem * item)
|
|
{
|
|
typename map<int, cacheItem*>::iterator it = cache.end();
|
|
|
|
if (item == NULL) return false; //Use RemoveMiss to remove cache misses, not this.
|
|
|
|
for (it = cache.begin(); it != cache.end(); it++)
|
|
{
|
|
if (it->second == item) break;
|
|
}
|
|
if (it != cache.end() && it->second)
|
|
{
|
|
it->second = NULL;
|
|
unsigned long isize = item->size();
|
|
|
|
cacheSize -= isize;
|
|
cacheItems--;
|
|
cache.erase(it);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
bool WCache<cacheItem, cacheActual>::Delete(cacheItem * item)
|
|
{
|
|
if (!item) return false;
|
|
|
|
unsigned long isize = item->size();
|
|
totalSize -= isize;
|
|
cacheSize -= isize;
|
|
#ifdef DEBUG_CACHE
|
|
if(cacheItems == 0)
|
|
DebugTrace("cacheItems out of sync.");
|
|
#endif
|
|
|
|
cacheItems--;
|
|
|
|
DebugTrace("Deleting cache item " << ToHex(item) << ", cache reduced by " << isize << " bytes");
|
|
SAFE_DELETE(item);
|
|
return true;
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
bool WCache<cacheItem, cacheActual>::Release(cacheActual* actual)
|
|
{
|
|
if (!actual) return false;
|
|
|
|
typename map<int, cacheItem*>::iterator it;
|
|
for (it = cache.begin(); it != cache.end(); it++)
|
|
{
|
|
if (it->second && it->second->compare(actual)) break;
|
|
}
|
|
|
|
if (it == cache.end()) return false; //Not here, can't release.
|
|
|
|
if (it->second)
|
|
{
|
|
it->second->unlock(); //Release one lock.
|
|
if (it->second->isLocked()) return true; //Still locked, won't delete, not technically a failure.
|
|
}
|
|
|
|
//Released!
|
|
Delete(it->second);
|
|
cache.erase(it);
|
|
return true;
|
|
}
|