/* ** 2012 Februar 24 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. */ package info.ata4.bsplib.app; import info.ata4.log.LogUtils; import java.io.InputStream; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; /** * Source engine application database handler. * * @author Nico Bergemann <barracuda415 at yahoo.de> */ public class SourceAppDB { private static final Logger L = LogUtils.getLogger(); private static SourceAppDB instance; private List<SourceApp> appList = new ArrayList<>(); private Map<Integer, SourceApp> appMap = new HashMap<>(); private float score; public static SourceAppDB getInstance() { if (instance == null) { instance = new SourceAppDB(); } return instance; } private SourceAppDB() { SAXParserFactory spf = SAXParserFactory.newInstance(); try (InputStream is = getClass().getResourceAsStream("appdb.xml")) { SAXParser sp = spf.newSAXParser(); SourceAppHandler handler = new SourceAppHandler(); sp.parse(is, handler); appList = handler.getAppList(); // generate ID map for validation and faster access for (SourceApp app : appList) { Integer appID = app.getAppID(); // warn if we have more than one app for an ID if (appMap.containsKey(appID)) { L.log(Level.WARNING, "Duplicate App ID {0} for \"{1}\" and \"{2}\"", new Object[]{appID, appMap.get(appID), app}); } appMap.put(appID, app); } } catch (Exception ex) { L.log(Level.SEVERE, "Can't load Source application database", ex); } } /** * Returns the source app for a Steam AppID. * * @param appID Steam AppID * @return the app identifier for the ID or SourceApp.UNKNOWN if it couldn't * be found. */ public SourceApp fromID(int appID) { if (appMap == null || !appMap.containsKey(appID)) { return SourceApp.UNKNOWN; } else { return appMap.get(appID); } } /** * Tries to find the AppID though a heuristical search with the given * parameters. * * @param bspName BSP file name * @param bspVersion BSP file version * @param classNames Complete set of entity class names * @return */ public SourceApp find(String bspName, int bspVersion, Set<String> classNames) { SourceApp candidate = SourceApp.UNKNOWN; score = 0; if (appList == null) { return candidate; } for (SourceApp app : appList) { // skip candidates with wrong version if (app.canCheckVersion() && !app.checkVersion(bspVersion)) { continue; } L.log(Level.FINER, "Testing {0}", app.getName()); float scoreNew = 0; // check entity class names if (app.canCheckEntities()) { scoreNew += app.checkEntities(classNames); } // check BSP file name pattern if (app.canCheckName()) { scoreNew += app.checkName(bspName); } if (scoreNew != 0 && scoreNew > score) { L.log(Level.FINER, "New candidate {0} with a score of {1}", new Object[]{app.getName(), scoreNew}); candidate = app; score = scoreNew; } } return candidate; } /** * Returns the total heuristic score for the last call of * {@link #find(java.lang.String, int, java.util.Set)}. * * @return total score */ public float getScore() { return score; } /** * Returns the list of all Source apps from the database. * * @return list of Source apps */ public List<SourceApp> getAppList() { return Collections.unmodifiableList(appList); } }