package com.technofovea.hllib; import com.sun.jna.FunctionMapper; import com.sun.jna.NativeLibrary; import com.technofovea.hllib.methods.FullLibrary; import com.sun.jna.Library; import com.sun.jna.Native; import com.technofovea.hllib.enums.HlOption; import com.technofovea.hllib.methods.ManagedLibrary; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class provides access to the singleton libary interface with the HlLib * DLL. It is possible to either request the raw interface, or the safer proxied * "managed" interface. These are not separate instances, just different levels * of safety for accessing the singleton. * * @author Darien Hager */ public class HlLib { public static final String LIBRARY_NAME = "hllib"; public static final String ARCH_PROPERTY = "sun.arch.data.model"; public static final String ARCH_32 = "32"; public static final String ARCH_64 = "64"; public static final String ARCH_32_SUFFIX = "_32"; public static final String ARCH_64_SUFFIX = "_64"; private static final Logger logger = LoggerFactory.getLogger(HlLib.class); public static final String LIBRARY_VERSION = "2.4.0"; static ManagedLibrary instance = null; static FullLibrary rawInstance; static InvocationManager handler; static void setup() { FunctionMapper func_mapper = new FunctionMapper() { public String getFunctionName(NativeLibrary lib, Method target) { // Add "hl" and uppercase the first letter. String original = target.getName(); StringBuilder temp = new StringBuilder(original); if (original.startsWith("wad")) { temp.replace(0, 3, "WAD"); } else if (original.startsWith("ncf")) { temp.replace(0, 3, "NCF"); } // Capitalize first letter if not already done temp.replace(0, 1, temp.substring(0, 1).toUpperCase()); temp.insert(0, "hl"); // Prefix with "hl" return temp.toString(); } }; logger.debug("Setting JNA type and function mappers"); Map<String, Object> options = new HashMap<String, Object>(); options.put(Library.OPTION_TYPE_MAPPER, new HlTypeMapper()); options.put(Library.OPTION_FUNCTION_MAPPER, func_mapper); logger.debug("Detecting architecture from property {}", ARCH_PROPERTY); String arch = System.getProperty(ARCH_PROPERTY); final String libName; if (ARCH_32.equals(arch)) { libName = LIBRARY_NAME + ARCH_32_SUFFIX; } else if (ARCH_64.equals(arch)) { libName = LIBRARY_NAME + ARCH_64_SUFFIX; } else { logger.warn("Unable to determine architecture (32-bit vs 64-bit) for HlLib. Defaulting to 32bit."); libName = LIBRARY_NAME + ARCH_32_SUFFIX; } logger.info("Loading native library: {}", libName); rawInstance = (FullLibrary) Native.loadLibrary(libName, FullLibrary.class, options); logger.debug("Calling library initialization function"); rawInstance.initialize(); logger.debug("Creating dynamic proxy for library access"); handler = new InvocationManager(rawInstance); ManagedLibrary proxy = (ManagedLibrary) Proxy.newProxyInstance( FullLibrary.class.getClassLoader(), new Class[]{ManagedLibrary.class}, handler); instance = proxy; String version = instance.getString(HlOption.VERSION); if (!LIBRARY_VERSION.equalsIgnoreCase(version)) { logger.warn("Version of HlLib, '{}',did not match the expected version, '{}'", version, LIBRARY_VERSION); } } @Override protected void finalize() throws Throwable { if (rawInstance != null) { rawInstance.shutdown(); } } /** * Creates or retrieves the singleton which manages the overhead of packages * and startup/shutdown code. * * @return HlLibrary proxy/implementation */ public static ManagedLibrary getLibrary() { synchronized (HlLib.class) { if (instance == null) { setup(); } return instance; } } /** * Use with caution! This provides the raw experience, the mostly-unmanaged * library with no proxying, startup/shutdown code, automatic package * selection, etc. You can crash your JVM with ease. Do not use the raw * and managed libraries at the same time, or chaos may ensue. * * It is recommended you only use this if you have access to the source * files to debug possible errors. * * Note that the "initialize" library call will already have been made. * @todo make public again if needed * @return HlLibrary proxy/implementation */ private static FullLibrary getRawLibrary() { synchronized (HlLib.class) { if (rawInstance == null) { setup(); } return rawInstance; } } }