package NET.worlds.network; import NET.worlds.console.Gamma; import NET.worlds.console.Main; import NET.worlds.console.MainCallback; import NET.worlds.console.MainTerminalCallback; import NET.worlds.core.IniFile; import NET.worlds.core.Std; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OptionalDataException; import java.io.Serializable; import java.util.Collection; import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; public class Cache implements MainCallback, MainTerminalCallback, Serializable { private static final long serialVersionUID = -7557149391688293661L; private static final long CACHE_VERSION = 0L; private static String CACHE_DIR = Gamma.earlyURLUnalias("home:cachedir/").replace('/', '\\'); static final int CACHE_MIN_CHANGE = IniFile.gamma().getIniInt("NetCacheMinChange", 50); static final long CACHE_MAX_DELAY = IniFile.gamma().getIniInt("NetCacheMaxDelay", 5) * 1000L * 60L; private Date lastCacheSave = new Date(); static transient Cache cache = initLoad(); private transient Hashtable table = new Hashtable(); private transient CacheEntry terminator = new CacheEntry(); transient long totalBytes; transient int hasChanged; private int nextAvailable = 1; public static void ClearCustomAvatars() { Collection c = cache.table.values(); Iterator it = c.iterator(); Vector tmp = new Vector(); while (it.hasNext()) { CacheEntry ce = it.next(); if (ce != null && ce.url.toString().contains("custom")) { tmp.add(ce); } } for (int i = 0; i < tmp.size(); i++) { removeEntry(tmp.get(i)); } } static Cache initLoad() { System.out.println("Initializing cache..."); Cache c = new Cache(); FileInputStream in = null; ObjectInputStream s = null; try { System.out.println("Restoring cache..."); File fi = new File(CACHE_DIR + "cache.index"); if (!fi.exists()) { fi = new File(CACHE_DIR + "cache.index.new"); if (!fi.exists()) { fi = new File(CACHE_DIR + "cache.index.old"); } } in = new FileInputStream(fi); s = new ObjectInputStream(in); long version = s.readLong(); if (version != 0L) { throw new Exception("Wrong version of cache.index"); } c = (Cache)s.readObject(); c.terminator = new CacheEntry(); c.table = new Hashtable(); Object obj; while ((obj = s.readObject()) != null && obj instanceof CacheEntry) { if (((CacheEntry)obj).url != null && ((CacheEntry)obj).localName != null && ((CacheEntry)obj).state >= 7) { c.add((CacheEntry)obj); } } s.close(); s = null; in.close(); in = null; System.out.println("Marking cache as closed..."); } catch (OptionalDataException var22) { } catch (Exception var23) { System.out.println(var23); System.out.println("Flushing cache index."); File findex = new File(CACHE_DIR + "cache.index"); findex.mkdirs(); findex.delete(); } finally { if (s != null) { try { s.close(); } catch (Exception var21) { } ObjectInputStream var26 = null; } if (in != null) { try { in.close(); } catch (Exception var20) { } FileInputStream var25 = null; } } String[] names = new File(CACHE_DIR).list(); Hashtable files = new Hashtable(); if (names != null) { for (int i = 0; i < names.length; i++) { files.put((CACHE_DIR + names[i]).toUpperCase(), ""); } files.remove((CACHE_DIR + "cache.index").toUpperCase()); } c.totalBytes = 0L; c.hasChanged = 0; CacheEntry p = c.terminator.next; while (p != c.terminator) { CacheEntry e = p; p = p.next; String name = e.localName.toUpperCase(); boolean isFile = files.get(name) != null; files.remove(name); c.totalBytes = c.totalBytes + e.bytes; if (!e.done() || !isFile) { c.remove(e); if (isFile) { new File(name).delete(); } } } Enumeration e = files.keys(); while (e.hasMoreElements()) { new File(e.nextElement()).delete(); } File f = new File("./avatars.zip"); if (f.exists()) { cache = c; InjectZipFile(f, "rel:avatar:"); } c.lastCacheSave = new Date(); Main.register(c); return c; } public static CacheFile getFile(URL url, boolean forceRecheck) { return cache.getAFile(url, forceRecheck); } public static CacheFile getFile(URL url) { return cache.getAFile(url, false); } public static CacheFile getFile(String url) { return cache.getAFile(URL.make(url), false); } public static CacheEntry getEntry(URL url) { return cache.get(url); } public static void removeEntry(CacheEntry ce) { cache.remove(ce); } public static void InjectZipFile(File zipFile, String urlPrefix) { long timeStamp = zipFile.lastModified(); try { ZipFile zf = new ZipFile(zipFile); System.out.println("Adding " + zf.size() + " entries from " + zipFile); Enumeration e = zf.entries(); while (e.hasMoreElements()) { ZipEntry ze = (ZipEntry)e.nextElement(); InputStream is = zf.getInputStream(ze); URL remoteName = URL.make(urlPrefix + ze.getName()); CacheEntry ce = cache.get(remoteName); if (ce == null) { String localName = assignLocalName(remoteName); FileOutputStream fos = new FileOutputStream(localName); byte[] buffer = new byte[4096]; while (true) { try { int bytesRead = is.read(buffer); if (bytesRead == -1) { break; } fos.write(buffer, 0, bytesRead); } catch (IOException var14) { break; } } fos.close(); ce = new CacheEntry(); ce.localName = new String(localName); ce.url = remoteName; ce.state = 4; ce.remoteTime = timeStamp; ce.checkTime = new Date(); cache.add(ce); } is.close(); } zf.close(); } catch (Exception var15) { System.out.println("Error processing cache zip file: " + var15); } } private synchronized CacheFile getAFile(URL url, boolean forceRecheck) { CacheEntry e = null; if (url.isRemote()) { e = cache.get(url); if (e == null) { e = new CacheEntry(url); cache.add(e); } else if (forceRecheck) { e.forceRecheck(); } } return new CacheFile(url, e); } private Cache() { } @Override public void mainCallback() { } public synchronized void resyncIndex() { if (this.hasChanged >= CACHE_MIN_CHANGE || this.lastCacheSave.before(new Date(new Date().getTime() - CACHE_MAX_DELAY))) { this.saveIndex(); System.gc(); } } public void saveIndex() { try { System.out.println("Marking cache as open..."); File fin = new File(CACHE_DIR + "cache.index.new"); FileOutputStream o = new FileOutputStream(fin); ObjectOutputStream s = new ObjectOutputStream(o); s.writeLong(0L); s.writeObject(this); for (CacheEntry e = this.terminator.next; e != this.terminator; e = e.next) { if (e.url != null && e.localName != null) { s.writeObject(e); } } s.writeInt(0); s.flush(); s.close(); o.close(); this.lastCacheSave = new Date(); this.hasChanged = 0; System.out.println("Marking cache as closed..."); File fi = new File(CACHE_DIR + "cache.index"); File fio = new File(CACHE_DIR + "cache.index.old"); fi.renameTo(fio); fin.renameTo(fi); fio.delete(); } catch (Exception var6) { System.out.println("Error writing cache index: " + var6); } } @Override public void terminalCallback() { Main.unregister(this); this.saveIndex(); } static String assignLocalName(URL url) { String remoteName = url.unalias(); String ext = ".temp"; int lastDot = remoteName.lastIndexOf(46); if (lastDot > remoteName.lastIndexOf(47) && remoteName.indexOf("?", lastDot) < 0 && remoteName.indexOf("#", lastDot) < 0) { ext = remoteName.substring(lastDot); } return url.endsWith(".gr2") ? CACHE_DIR + url.getBase() : CACHE_DIR + Integer.toString(cache.nextAvailable++, 36) + ext; } public synchronized void add(CacheEntry e) { this.table.put(e.url, e); CacheEntry p = this.terminator.prev; p.next = e; e.prev = p; e.next = this.terminator; this.terminator.prev = e; this.hasChanged++; } public int numEntries() { return this.table.size(); } public synchronized CacheEntry get(URL url) { CacheEntry e = this.table.get(url); if (e != null) { this.markUsed(e); } return e; } private void markUsed(CacheEntry e) { if (e != null && e.next != this.terminator) { e.prev.next = e.next; e.next.prev = e.prev; this.terminator.prev.next = e; e.prev = this.terminator.prev; e.next = this.terminator; this.terminator.prev = e; } } public void remove(CacheEntry e) { this.totalBytes = this.totalBytes - e.bytes; e.prev.next = e.next; e.next.prev = e.prev; e.prev = null; e.next = null; this.table.remove(e.url); this.hasChanged++; new File(e.localName.toUpperCase()).delete(); } private CacheEntry freeLRU() { for (CacheEntry e = this.terminator.next; e != this.terminator; e = e.next) { if (!e.inUse()) { this.remove(e); return e; } } return null; } public synchronized void makeSpaceFor(int incomingSize) { while (cache.numEntries() > CacheEntry.CACHE_MAX_ENTRIES && this.freeLRU() != null) { } while ((this.totalBytes + 2L * incomingSize) / 1024L > Std.GetDiskFreeSpace() && Std.GetDiskFreeSpace() > -1L) { CacheEntry freed = this.freeLRU(); if (freed == null) { break; } } } }