76cba56a1c
This btw points out another circular dependancy between the texture and the JQuad - a texture owns a bunch of JQuads, yet the renderer uses JQuads and always assumes that the texture is valid. We're going to need to add more defensiveness to JGE to protect against this. Other changes in this check-in: WResourceManager doesn't derive from JResourceManager anymore. It actually didn't require anything from the base, so I killed the dependency. Also cleaned up the notion of a WTrackedQuad in the WCachedResource - it didn't need a separate class, just a better container. I've build this & tested against PSP, win, linux, QT (linux). I haven't tried against iOS and QT Win, or Maemo. If these other platforms are broken, I apologize in advance! - I'm hoping it should be fairly simple to put them back into play.
1456 lines
38 KiB
C++
1456 lines
38 KiB
C++
#include "PrecompiledHeader.h"
|
|
|
|
#include <JFileSystem.h>
|
|
|
|
#include "utils.h"
|
|
#include "GameOptions.h"
|
|
#include "WResourceManager.h"
|
|
#include "StyleManager.h"
|
|
|
|
#if defined (WIN32)
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#include "WFont.h"
|
|
#include <JLogger.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");
|
|
}
|
|
|
|
WResourceManager* WResourceManager::sInstance = NULL;
|
|
|
|
int WResourceManager::RetrieveError()
|
|
{
|
|
return lastError;
|
|
}
|
|
|
|
bool WResourceManager::RemoveOldest()
|
|
{
|
|
if (sampleWCache.RemoveOldest()) return true;
|
|
if (textureWCache.RemoveOldest()) return true;
|
|
if (psiWCache.RemoveOldest()) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//WResourceManager
|
|
void WResourceManager::DebugRender()
|
|
{
|
|
JRenderer* renderer = JRenderer::GetInstance();
|
|
WFont * font = WResourceManager::Instance()->GetWFont(Fonts::MAIN_FONT);
|
|
font->SetColor(ARGB(255,255,255,255));
|
|
|
|
if (!font || !renderer) return;
|
|
|
|
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);
|
|
|
|
#if defined (WIN32) || defined (LINUX) || defined (IOS)
|
|
#else
|
|
int maxLinear = ramAvailableLineareMax();
|
|
int ram = ramAvailable();
|
|
sprintf(buf, "Ram : linear max: %i - total : %i\n", maxLinear, ram);
|
|
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 WResourceManager::Size()
|
|
{
|
|
unsigned long res = 0;
|
|
res += textureWCache.totalSize;
|
|
res += sampleWCache.totalSize;
|
|
res += psiWCache.totalSize;
|
|
return res;
|
|
}
|
|
|
|
unsigned long WResourceManager::SizeCached()
|
|
{
|
|
unsigned long res = 0;
|
|
res += textureWCache.cacheSize;
|
|
res += sampleWCache.cacheSize;
|
|
res += psiWCache.cacheSize;
|
|
return res;
|
|
}
|
|
|
|
unsigned long WResourceManager::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 WResourceManager::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 WResourceManager::CountCached()
|
|
{
|
|
unsigned int count = 0;
|
|
count += textureWCache.cacheItems;
|
|
count += sampleWCache.cacheItems;
|
|
count += psiWCache.cacheItems;
|
|
return count;
|
|
}
|
|
unsigned int WResourceManager::CountManaged()
|
|
{
|
|
unsigned int count = 0;
|
|
count += textureWCache.managed.size();
|
|
count += sampleWCache.managed.size();
|
|
count += psiWCache.managed.size();
|
|
return count;
|
|
}
|
|
|
|
unsigned int WResourceManager::nowTime()
|
|
{
|
|
if (lastTime > MAX_CACHE_TIME) FlattenTimes();
|
|
|
|
return ++lastTime;
|
|
}
|
|
|
|
void WResourceManager::FlattenTimes()
|
|
{
|
|
unsigned int t;
|
|
lastTime = sampleWCache.Flatten();
|
|
|
|
t = textureWCache.Flatten();
|
|
if (t > lastTime) lastTime = t;
|
|
|
|
t = psiWCache.Flatten();
|
|
if (t > lastTime) lastTime = t;
|
|
}
|
|
|
|
WResourceManager::WResourceManager()
|
|
{
|
|
DebugTrace("Init WResourceManager : " << 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;
|
|
}
|
|
|
|
WResourceManager::~WResourceManager()
|
|
{
|
|
LOG("==Destroying WResourceManager==");
|
|
RemoveWFonts();
|
|
|
|
LOG("==Successfully Destroyed WResourceManager==");
|
|
}
|
|
|
|
JQuadPtr WResourceManager::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;
|
|
|
|
string filename = setlist[card->setId];
|
|
filename += "/";
|
|
string filename1 = filename + card->getImageName();
|
|
int id = card->getMTGId();
|
|
|
|
//Aliases.
|
|
if (style == RETRIEVE_THUMB)
|
|
{
|
|
submode = submode | TEXTURE_SUB_THUMB;
|
|
style = RETRIEVE_NORMAL;
|
|
}
|
|
|
|
//Hack to allow either ID or card name as a filename for a given card.
|
|
// When missing the first attempt (for example [id].jpg), the cache assigns a "404" to the card's image cache,
|
|
// Preventing us to try a second time with [name].jpg.
|
|
//To bypass this, we first check if the card was ever marked as "null". If not, it means it's the first time we're looking for it
|
|
// In that case, we "unmiss" it after trying the [id].jpg, in order to give a chance to the [name.jpg]
|
|
bool canUnmiss = false;
|
|
{
|
|
JQuadPtr tempQuad = RetrieveQuad(filename1, 0, 0, 0, 0, "", RETRIEVE_EXISTING, submode | TEXTURE_SUB_5551, id);
|
|
lastError = textureWCache.mError;
|
|
if (!tempQuad && lastError != CACHE_ERROR_404)
|
|
{
|
|
canUnmiss = true;
|
|
}
|
|
}
|
|
JQuadPtr jq = RetrieveQuad(filename1, 0, 0, 0, 0, "", style, submode | TEXTURE_SUB_5551, id);
|
|
if (!jq)
|
|
{
|
|
if (canUnmiss)
|
|
{
|
|
int mId = id;
|
|
//To differentiate between cached thumbnails and the real thing.
|
|
if (submode & TEXTURE_SUB_THUMB)
|
|
{
|
|
if (mId < 0)
|
|
mId -= THUMBNAILS_OFFSET;
|
|
else
|
|
mId += THUMBNAILS_OFFSET;
|
|
}
|
|
textureWCache.RemoveMiss(mId);
|
|
}
|
|
filename1 = filename + card->data->getName() + ".jpg";
|
|
jq = RetrieveQuad(filename1, 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 WResourceManager::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 WResourceManager::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 WResourceManager::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 WResourceManager::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 WResourceManager::RetrieveTempQuad(const string& filename, int submode)
|
|
{
|
|
return RetrieveQuad(filename, 0, 0, 0, 0, "temporary", RETRIEVE_NORMAL, submode);
|
|
}
|
|
|
|
JQuadPtr WResourceManager::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 = NULL;
|
|
if (style == RETRIEVE_MANAGE || style == RETRIEVE_EXISTING)
|
|
jtex = textureWCache.Retrieve(id, filename, style, submode);
|
|
else
|
|
jtex = textureWCache.Retrieve(id, filename, RETRIEVE_NORMAL, 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 WResourceManager::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 WResourceManager::Unmiss(string filename)
|
|
{
|
|
map<int, WCachedTexture*>::iterator it;
|
|
int id = textureWCache.makeID(0, filename, CACHE_NORMAL);
|
|
textureWCache.RemoveMiss(id);
|
|
}
|
|
|
|
void WResourceManager::ClearUnlocked()
|
|
{
|
|
textureWCache.ClearUnlocked();
|
|
sampleWCache.ClearUnlocked();
|
|
psiWCache.ClearUnlocked();
|
|
}
|
|
|
|
void WResourceManager::Release(JSample * sample)
|
|
{
|
|
if (!sample) return;
|
|
|
|
sampleWCache.Release(sample);
|
|
}
|
|
|
|
JTexture * WResourceManager::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 WResourceManager::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* WResourceManager::GetTexture(const string &textureName)
|
|
{
|
|
JTexture * jtex = RetrieveTexture(textureName, RETRIEVE_RESOURCE);
|
|
return jtex;
|
|
}
|
|
|
|
JTexture* WResourceManager::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 * WResourceManager::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 * WResourceManager::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 WResourceManager::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, true)) return buf;
|
|
}
|
|
}
|
|
|
|
if (theme != "" && theme != "Default")
|
|
{
|
|
sprintf(buf, "themes/%s/%s", theme.c_str(), filename.c_str());
|
|
if (fileOK(buf, true)) 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, true)) return graphdir;
|
|
|
|
//Failure. Check sets.
|
|
sprintf(buf, "sets/%s", filename.c_str());
|
|
if (fileOK(buf, true)) return buf;
|
|
|
|
//Failure. Check raw faile.
|
|
sprintf(buf, "%s", filename.c_str());
|
|
if (fileOK(buf, true)) return buf;
|
|
|
|
//Complete abject failure. Probably a crash...
|
|
return graphdir;
|
|
}
|
|
|
|
string WResourceManager::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, true)) return buf;
|
|
}
|
|
else
|
|
{
|
|
sprintf(buf, "player/%s", filename.c_str());
|
|
if (fileOK(buf, true)) 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, true)) 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, true)) return buf;
|
|
|
|
//Failure. Check graphics
|
|
sprintf(buf, "graphics/%s", filename.c_str());
|
|
if (fileOK(buf, true)) return buf;
|
|
|
|
//Failure. Check raw faile.
|
|
sprintf(buf, "%s", filename.c_str());
|
|
if (fileOK(buf, true)) return buf;
|
|
|
|
//Complete abject failure. Probably a crash...
|
|
return "";
|
|
}
|
|
|
|
string WResourceManager::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, JGE_GET_RES("themes/%s/sets/%s/%s.zip").c_str(), 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, true)) 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, JGE_GET_RES("sets/%s/%s.zip").c_str(), 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, true)) return defdir;
|
|
|
|
//Complete failure.
|
|
return "";
|
|
}
|
|
|
|
string WResourceManager::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, true)) 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, true)) return defdir;
|
|
|
|
//Failure. Check raw faile.
|
|
sprintf(defdir, "%s", filename.c_str());
|
|
if (fileOK(defdir, true)) return defdir;
|
|
|
|
//Complete abject failure. Probably a crash...
|
|
return "";
|
|
}
|
|
|
|
string WResourceManager::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, true)) 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, true)) return defdir;
|
|
|
|
//Complete abject failure. Probably a crash...
|
|
return "";
|
|
}
|
|
|
|
int WResourceManager::dirOK(const string& dirname)
|
|
{
|
|
char fname[512];
|
|
|
|
#if defined (WIN32)
|
|
sprintf(fname,JGE_GET_RES(dirname).c_str());
|
|
|
|
struct _stat statBuffer;
|
|
return (_stat(fname, &statBuffer) >= 0 && // make sure it exists
|
|
statBuffer.st_mode & S_IFDIR); // and it's not a file
|
|
#else
|
|
sprintf(fname, "%s", JGE_GET_RES(dirname).c_str());
|
|
struct stat st;
|
|
if (stat(fname, &st) == 0) return 1;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int WResourceManager::fileOK(const string& filename, bool relative)
|
|
{
|
|
wagic::ifstream * fp = NULL;
|
|
if (relative)
|
|
{
|
|
fp = NEW wagic::ifstream(JGE_GET_RES(filename).c_str());
|
|
}
|
|
else
|
|
fp = NEW wagic::ifstream(filename.c_str());
|
|
|
|
int result = 0;
|
|
if (fp)
|
|
{
|
|
if (*fp) result = 1;
|
|
fp->close();
|
|
delete fp;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void WResourceManager::InitFonts(const std::string& inLang)
|
|
{
|
|
unsigned int idOffset = 0;
|
|
|
|
if (inLang.compare("cn") == 0)
|
|
{
|
|
mFontFileExtension = kExtension_gbk;
|
|
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;
|
|
}
|
|
|
|
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;
|
|
LoadWFont("simon", 11, Fonts::MAIN_FONT + idOffset);
|
|
GetWFont(Fonts::MAIN_FONT)->SetTracking(-1);
|
|
LoadWFont("f3", 16, Fonts::MENU_FONT + idOffset);
|
|
LoadWFont("magic", 16, Fonts::MAGIC_FONT + idOffset);
|
|
LoadWFont("smallface", 7, Fonts::SMALLFACE_FONT + idOffset);
|
|
}
|
|
|
|
int WResourceManager::ReloadWFonts()
|
|
{
|
|
RemoveWFonts();
|
|
|
|
string lang = options[Options::LANG].str;
|
|
std::transform(lang.begin(), lang.end(), lang.begin(), ::tolower);
|
|
|
|
InitFonts(lang);
|
|
|
|
return 1;
|
|
}
|
|
|
|
WFont* WResourceManager::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* WResourceManager::GetWFont(int id)
|
|
{
|
|
WFont* font = NULL;
|
|
FontMap::iterator iter = mWFontMap.find(id);
|
|
if (iter != mWFontMap.end())
|
|
{
|
|
font = iter->second;
|
|
}
|
|
return font;
|
|
}
|
|
|
|
void WResourceManager::RemoveWFonts()
|
|
{
|
|
for (FontMap::iterator font = mWFontMap.begin(); font != mWFontMap.end(); ++font)
|
|
{
|
|
delete font->second;
|
|
}
|
|
mWFontMap.clear();
|
|
}
|
|
|
|
void WResourceManager::ResetCacheLimits()
|
|
{
|
|
#if defined WIN32 || defined LINUX || defined (IOS)
|
|
#ifdef FORCE_LOW_CACHE_MEMORY
|
|
textureWCache.Resize(kConstrainedCacheLimit, MAX_CACHE_OBJECTS);
|
|
#else
|
|
textureWCache.Resize(HUGE_CACHE_LIMIT,MAX_CACHE_OBJECTS);
|
|
#endif
|
|
#else
|
|
unsigned int ram = ramAvailable();
|
|
unsigned int myNewSize = ram - OPERATIONAL_SIZE + textureWCache.totalSize;
|
|
if (myNewSize < TEXTURES_CACHE_MINSIZE)
|
|
{
|
|
fprintf(stderr, "Error, Not enough RAM for Cache: %i - total Ram: %i\n", myNewSize, ram);
|
|
}
|
|
textureWCache.Resize(myNewSize, MAX_CACHE_OBJECTS);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
JMusic * WResourceManager::ssLoadMusic(const char *fileName)
|
|
{
|
|
string file = musicFile(fileName);
|
|
if (!file.size()) return NULL;
|
|
return JSoundSystem::GetInstance()->LoadMusic(file.c_str());
|
|
}
|
|
|
|
void WResourceManager::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())
|
|
{
|
|
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;
|
|
|
|
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)
|
|
{
|
|
if (submode & CACHE_EXISTING)
|
|
{ //Should never get this far.
|
|
mError = CACHE_ERROR_NOT_CACHED;
|
|
return NULL;
|
|
}
|
|
|
|
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)
|
|
{
|
|
SAFE_DELETE(item);
|
|
return NULL;
|
|
}
|
|
//Probably not enough memory: cleanup and try again
|
|
Cleanup();
|
|
mError = CACHE_ERROR_NONE;
|
|
if (!item->Attempt(filename, submode, mError) || !item->isGood())
|
|
{
|
|
SAFE_DELETE(item);
|
|
mError = CACHE_ERROR_BAD;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//Success! Enforce cache limits, then return.
|
|
mError = CACHE_ERROR_NONE;
|
|
item->lock();
|
|
Cleanup();
|
|
item->unlock();
|
|
return item;
|
|
}
|
|
|
|
template<class cacheItem, class cacheActual>
|
|
cacheItem* WCache<cacheItem, cacheActual>::Retrieve(int id, const string& filename, int style, int submode)
|
|
{
|
|
//Check cache.
|
|
cacheItem * tc = NULL;
|
|
mError = CACHE_ERROR_NONE; //Reset error status.
|
|
|
|
if (style == RETRIEVE_EXISTING || style == RETRIEVE_RESOURCE)
|
|
tc = Get(id, filename, style, submode | CACHE_EXISTING);
|
|
else
|
|
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;
|
|
}
|
|
}
|
|
|
|
//To differentiate between cached thumbnails and the real thing.
|
|
if (submode & TEXTURE_SUB_THUMB)
|
|
{
|
|
if (mId < 0)
|
|
mId -= THUMBNAILS_OFFSET;
|
|
else
|
|
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.
|
|
}
|
|
|
|
//Failed to find managed resource and won't create one. Record a miss.
|
|
if (style == RETRIEVE_RESOURCE)
|
|
{
|
|
managed[lookup] = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
//Not managed, so look in cache.
|
|
if (style != RETRIEVE_MANAGE)
|
|
{
|
|
it = cache.find(lookup);
|
|
//Well, we've found something...
|
|
if (it != cache.end())
|
|
{
|
|
if (!it->second) mError = CACHE_ERROR_404;
|
|
return it->second; //A hit, or maybe a miss.
|
|
}
|
|
}
|
|
|
|
//Didn't exist in cache.
|
|
if (submode & CACHE_EXISTING)
|
|
{
|
|
mError = CACHE_ERROR_NOT_CACHED;
|
|
return NULL;
|
|
}
|
|
|
|
//Space in cache, make new texture
|
|
cacheItem * item = AttemptNew(filename, submode);
|
|
|
|
if (style == RETRIEVE_MANAGE)
|
|
{
|
|
if (mError == CACHE_ERROR_404 || item) managed[lookup] = item; //Record a hit or miss.
|
|
if (item)
|
|
{
|
|
item->deadbolt(); //Make permanent.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mError == CACHE_ERROR_404 || item) cache[lookup] = item;
|
|
}
|
|
|
|
if (!item) 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;
|
|
}
|
|
|
|
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()
|
|
{
|
|
while (cacheItems < cache.size() && cache.size() - cacheItems > MAX_CACHE_MISSES)
|
|
{
|
|
RemoveMiss();
|
|
}
|
|
|
|
while (cacheItems > MAX_CACHE_OBJECTS || cacheItems > maxCached || cacheSize > maxCacheSize
|
|
#if defined WIN32 || defined LINUX || defined (IOS)
|
|
#else
|
|
|| ramAvailableLineareMax() < MIN_LINEAR_RAM
|
|
#endif
|
|
)
|
|
{
|
|
if (!RemoveOldest())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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--;
|
|
|
|
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;
|
|
}
|