Commits

spencercw committed 92e6925

Add initial basic support for Android.

Comments (0)

Files changed (67)

 cmake_minimum_required(VERSION 2.8)
+
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" CACHE PATH "Directory for all binary output.")
+set(LIBRARY_OUTPUT_PATH_ROOT "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" CACHE PATH "Directory for Android libraries.")
+set(LIBRARY_OUTPUT_PATH "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" CACHE PATH "Directory for Android libraries.")
+
 project(gb_emulator)
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" CACHE PATH "Directory for all binary output.")
+
+# Configure build options
+if(DEFINED ANDROID)
+	set(GB_BUILD_DEBUGGER OFF)
+	set(GB_USE_GLES2 ON)
+	set(GB_ANDROID_GDBSERVER_PATH "" CACHE PATH "Path to gdbserver executable.")
+else()
+	set(GB_BUILD_DEBUGGER OFF CACHE BOOL "Whether to build the debugger")
+	set(GB_USE_GLES2 OFF CACHE BOOL "Whether to use OpenGL ES 2.0 instead of standard OpenGL")
+endif()
 
 add_subdirectory(gb_emulator)
-add_subdirectory(gb_emulator_main)
 add_subdirectory(gb_net)
 add_subdirectory(third_party)
+
+if(DEFINED ANDROID)
+	add_subdirectory(android)
+else()
+	add_subdirectory(gb_emulator_main)
+endif()

android/CMakeLists.txt

+add_subdirectory(jni)

android/java/.idea/.name

+gb_emulator

android/java/.idea/ant.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="AntConfiguration">
+    <defaultAnt bundledAnt="true" />
+  </component>
+</project>
+

android/java/.idea/codeStyleSettings.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectCodeStyleSettingsManager">
+    <option name="PER_PROJECT_SETTINGS">
+      <value>
+        <XML>
+          <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
+        </XML>
+      </value>
+    </option>
+  </component>
+</project>
+

android/java/.idea/compiler.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <option name="DEFAULT_COMPILER" value="Javac" />
+    <excludeFromCompile>
+      <directory url="file://$PROJECT_DIR$/gen" includeSubdirectories="true" />
+    </excludeFromCompile>
+    <resourceExtensions />
+    <wildcardResourcePatterns>
+      <entry name="!?*.java" />
+      <entry name="!?*.form" />
+      <entry name="!?*.class" />
+      <entry name="!?*.groovy" />
+      <entry name="!?*.scala" />
+      <entry name="!?*.flex" />
+      <entry name="!?*.kt" />
+      <entry name="!?*.clj" />
+    </wildcardResourcePatterns>
+    <annotationProcessing>
+      <profile default="true" name="Default" enabled="false">
+        <processorPath useClasspath="true" />
+      </profile>
+    </annotationProcessing>
+  </component>
+</project>
+

android/java/.idea/copyright/profiles_settings.xml

+<component name="CopyrightManager">
+  <settings default="">
+    <module2copyright />
+  </settings>
+</component>

android/java/.idea/encodings.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
+</project>
+

android/java/.idea/misc.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="EntryPointsManager">
+    <entry_points version="2.0" />
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="Android 4.2 Platform" project-jdk-type="Android SDK">
+    <output url="file://$PROJECT_DIR$/out" />
+  </component>
+</project>
+

android/java/.idea/modules.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/gb_emulator.iml" filepath="$PROJECT_DIR$/gb_emulator.iml" />
+    </modules>
+  </component>
+</project>
+

android/java/.idea/scopes/scope_settings.xml

+<component name="DependencyValidationManager">
+  <state>
+    <option name="SKIP_IMPORT_STATEMENTS" value="false" />
+  </state>
+</component>

android/java/.idea/uiDesigner.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Palette2">
+    <group name="Swing">
+      <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+      </item>
+      <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
+        <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+        <initial-values>
+          <property name="text" value="Button" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="RadioButton" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="CheckBox" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="Label" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+          <preferred-size width="-1" height="20" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+      </item>
+    </group>
+  </component>
+</project>
+

android/java/.idea/vcs.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="" />
+  </component>
+</project>
+

android/java/AndroidManifest.xml

+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="uk.co.cwspencer.gb_emulator"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk
+        android:minSdkVersion="14"
+        android:targetSdkVersion="17" />
+
+    <uses-feature
+        android:glEsVersion="0x00020000"
+        android:required="true" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme"
+        android:debuggable="true" >
+        <activity
+            android:name="uk.co.cwspencer.gb_emulator.MainActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>

android/java/gb_emulator.iml

+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="android" name="Android">
+      <configuration>
+        <option name="GEN_FOLDER_RELATIVE_PATH_APT" value="/gen" />
+        <option name="GEN_FOLDER_RELATIVE_PATH_AIDL" value="/gen" />
+        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/AndroidManifest.xml" />
+        <option name="RES_FOLDER_RELATIVE_PATH" value="/res" />
+        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/assets" />
+        <option name="LIBS_FOLDER_RELATIVE_PATH" value="/libs" />
+        <option name="USE_CUSTOM_APK_RESOURCE_FOLDER" value="false" />
+        <option name="CUSTOM_APK_RESOURCE_FOLDER" value="" />
+        <option name="USE_CUSTOM_COMPILER_MANIFEST" value="false" />
+        <option name="CUSTOM_COMPILER_MANIFEST" value="" />
+        <option name="APK_PATH" value="" />
+        <option name="LIBRARY_PROJECT" value="false" />
+        <option name="RUN_PROCESS_RESOURCES_MAVEN_TASK" value="true" />
+        <option name="GENERATE_UNSIGNED_APK" value="false" />
+        <option name="CUSTOM_DEBUG_KEYSTORE_PATH" value="" />
+        <option name="PACK_TEST_CODE" value="false" />
+        <option name="RUN_PROGUARD" value="false" />
+        <option name="PROGUARD_CFG_PATH" value="/proguard-project.txt" />
+        <resOverlayFolders>
+          <path>/res-overlay</path>
+        </resOverlayFolders>
+        <includeSystemProguardFile>true</includeSystemProguardFile>
+        <includeAssetsFromLibraries>false</includeAssetsFromLibraries>
+        <additionalNativeLibs />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" />
+    </content>
+    <content url="file://$MODULE_DIR$/../jni" />
+    <content url="file://$MODULE_DIR$/../../gb_debugger_msvs" />
+    <content url="file://$MODULE_DIR$/../../gb_emulator" />
+    <content url="file://$MODULE_DIR$/../../gb_emulator_main" />
+    <content url="file://$MODULE_DIR$/../../gb_net" />
+    <content url="file://$MODULE_DIR$/../../tests" />
+    <content url="file://$MODULE_DIR$/../../third_party" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
+

android/java/res/drawable-hdpi/ic_launcher.png

Added
New image

android/java/res/drawable-mdpi/ic_launcher.png

Added
New image

android/java/res/drawable-xhdpi/ic_launcher.png

Added
New image

android/java/res/menu/activity_main.xml

+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <item
+        android:id="@+id/menu_settings"
+        android:orderInCategory="100"
+        android:showAsAction="never"
+        android:title="@string/menu_settings"/>
+
+</menu>

android/java/res/values-v11/styles.xml

+<resources>
+
+    <!--
+        Base application theme for API 11+. This theme completely replaces
+        AppBaseTheme from res/values/styles.xml on API 11+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+        <!-- API 11 theme customizations can go here. -->
+    </style>
+
+</resources>

android/java/res/values-v14/styles.xml

+<resources>
+
+    <!--
+        Base application theme for API 14+. This theme completely replaces
+        AppBaseTheme from BOTH res/values/styles.xml and
+        res/values-v11/styles.xml on API 14+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- API 14 theme customizations can go here. -->
+    </style>
+
+</resources>

android/java/res/values/strings.xml

+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <string name="app_name">Game Boy Emulator</string>
+    <string name="menu_settings">Settings</string>
+
+</resources>

android/java/res/values/styles.xml

+<resources>
+
+    <!--
+        Base application theme, dependent on API level. This theme is replaced
+        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Light">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
+    </style>
+
+    <!-- Application theme. -->
+    <style name="AppTheme" parent="AppBaseTheme">
+        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+    </style>
+
+</resources>

android/java/src/uk/co/cwspencer/gb_emulator/GbJNI.java

+package uk.co.cwspencer.gb_emulator;
+
+import android.content.res.AssetManager;
+
+/**
+ * JNI wrapper for the Gb class.
+ */
+public class GbJNI
+{
+	static
+	{
+		// Dependencies have to be loaded explicitly in reverse order
+		System.loadLibrary("hqx");
+		System.loadLibrary("gb_net");
+		System.loadLibrary("gb_emulator");
+		System.loadLibrary("gb_emulator_jni");
+	}
+
+	private long nativePtr;
+
+	/**
+	 * Constructor; loads the BIOS.
+	 * @param assetManager The Android asset manager.
+	 */
+	public GbJNI(AssetManager assetManager)
+	{
+		nativePtr = init(assetManager);
+	}
+	private native long init(Object assetManager);
+
+	/**
+	 * Frees resources allocated by the class.
+	 */
+	public void deinit()
+	{
+		deinit(nativePtr);
+	}
+	private native void deinit(long nativePtr);
+
+	/**
+	 * Frees resources allocated by the class.
+	 */
+	@Override
+	protected void finalize() throws Throwable
+	{
+        super.finalize();
+		deinit(nativePtr);
+	}
+
+	/**
+	 * Runs the emulator.
+	 * This doesn't return until the emulator exited or there is an error. Use doFrame() instead if
+	 * you need polling behaviour.
+	 */
+	public void run()
+	{
+		run(nativePtr);
+	}
+	private native void run(long nativePtr);
+
+	/**
+	 * Performs one frame's worth of emulation.
+	 * This can be used instead of run() if you need polling behaviour. Note this this function will
+	 * execute one frame's worth of CPU cycles, but may not actually render anything if the game has
+	 * turned the display off.
+	 */
+	public void doFrame()
+	{
+		doFrame(nativePtr);
+	}
+	private native void doFrame(long nativePtr);
+
+	/**
+	 * Resets the emulator without loading a ROM.
+	 */
+	public void reset()
+	{
+		reset(nativePtr);
+	}
+	private native void reset(long nativePtr);
+
+	/**
+	 * Resets the emulator and loads the given ROM.
+	 * @param rom Path to the ROM to load.
+	 */
+	public void reset(String rom)
+	{
+		reset(nativePtr, rom);
+	}
+	private native void reset(long nativePtr, String rom);
+
+	/**
+	 * Saves the current state of the emulator.
+	 * The file is saved with the same name as the ROM file with the extension replaced with ".sav".
+	 */
+	public void save()
+	{
+		save(nativePtr);
+	}
+	private native void save(long nativePtr);
+
+	/**
+	 * Loads the emulator state from file.
+	 * This is to be used in conjunction with save().
+	 */
+	public void load()
+	{
+		load(nativePtr);
+	}
+	private native void load(long nativePtr);
+
+	/**
+	 * Saves the battery-backed RAM to a file.
+	 * This is what games use to save data. The data is automatically reloaded when the emulator
+	 * starts if the file is present.
+	 */
+	public void saveRam()
+	{
+		saveRam(nativePtr);
+	}
+	private native void saveRam(long nativePtr);
+}

android/java/src/uk/co/cwspencer/gb_emulator/MainActivity.java

+package uk.co.cwspencer.gb_emulator;
+
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.app.Activity;
+import android.view.Menu;
+
+public class MainActivity extends Activity
+{
+	// The emulator context
+	private GbJNI m_gb = null;
+
+	// The OpenGL surface
+	private GLSurfaceView m_glView;
+
+	@Override
+	protected void onCreate(Bundle savedInstanceState)
+	{
+		super.onCreate(savedInstanceState);
+
+		// Create the OpenGL surface
+		m_glView = new TestGLSurface(this, getAssets());
+		setContentView(m_glView);
+	}
+}

android/java/src/uk/co/cwspencer/gb_emulator/TestGLRenderer.java

+package uk.co.cwspencer.gb_emulator;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.content.res.AssetManager;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView.Renderer;
+
+public class TestGLRenderer implements Renderer
+{
+	private AssetManager m_assetManager;
+	private GbJNI m_gb;
+
+	TestGLRenderer(AssetManager assetManager)
+	{
+		m_assetManager = assetManager;
+	}
+
+	@Override
+	public void onSurfaceCreated(GL10 unused, EGLConfig config)
+	{
+		m_gb = new GbJNI(m_assetManager);
+	}
+
+	@Override
+	public void onSurfaceChanged(GL10 unused, int width, int height)
+	{
+		// Adjust the viewport
+		GLES20.glViewport(0, 0, width, height);
+	}
+
+	@Override
+	public void onDrawFrame(GL10 unused)
+	{
+		m_gb.doFrame();
+	}
+}

android/java/src/uk/co/cwspencer/gb_emulator/TestGLSurface.java

+package uk.co.cwspencer.gb_emulator;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.graphics.PixelFormat;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+
+public class TestGLSurface extends GLSurfaceView
+{
+	public TestGLSurface(Context context, AssetManager assetManager)
+	{
+		super(context);
+		init(assetManager);
+	}
+
+	public TestGLSurface(Context context, AttributeSet attrs, AssetManager assetManager)
+	{
+		super(context, attrs);
+		init(assetManager);
+	}
+
+	private void init(AssetManager assetManager)
+	{
+		// Initialise EGL
+		getHolder().setFormat(PixelFormat.TRANSLUCENT);
+		setEGLContextFactory(new ContextFactory());
+		setEGLConfigChooser(new ConfigChooser());
+		setRenderer(new TestGLRenderer(assetManager));
+		setRenderMode(RENDERMODE_CONTINUOUSLY);
+	}
+
+	private static class ContextFactory implements GLSurfaceView.EGLContextFactory
+	{
+		private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+
+		@Override
+		public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig)
+		{
+			int[] attributes = {
+				EGL_CONTEXT_CLIENT_VERSION, 2,
+				EGL10.EGL_NONE };
+			EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT,
+				attributes);
+			return context;
+		}
+
+		@Override
+		public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context)
+		{
+			egl.eglDestroyContext(display, context);
+		}
+	}
+
+	private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser
+	{
+		private static int EGL_OPENGL_ES2_BIT = 4;
+		private static int[] attributes = {
+			EGL10.EGL_RED_SIZE, 8,
+			EGL10.EGL_GREEN_SIZE, 8,
+			EGL10.EGL_BLUE_SIZE, 8,
+			EGL10.EGL_ALPHA_SIZE, 8,
+			EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+			EGL10.EGL_NONE };
+
+		@Override
+		public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display)
+		{
+			int[] configCount = new int[1];
+			EGLConfig[] configs = new EGLConfig[1];
+			if (!egl.eglChooseConfig(display, attributes, configs, 1, configCount))
+			{
+				throw new IllegalArgumentException("No suitable EGL configuration available");
+			}
+			return configs[0];
+		}
+	}
+}

android/jni/CMakeLists.txt

+find_package(Boost REQUIRED)
+
+include_directories("${PROJECT_SOURCE_DIR}/gb_emulator/include")
+include_directories(${Boost_INCLUDE_DIRS})
+
+link_directories(${Boost_LIBRARY_DIRS})
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -ggdb3")
+
+add_library(gb_emulator_jni SHARED
+	gb_android_asset_manager.h
+	gb_android_asset_manager.cpp
+	gb_jni.h
+	gb_jni.cpp)
+target_link_libraries(gb_emulator_jni
+	android
+	gb_emulator)
+
+# Generate Android.mk
+set(GB_ANDROID_PATH "${PROJECT_SOURCE_DIR}/android/java")
+file(WRITE "${GB_ANDROID_PATH}/jni/Android.mk" "APP_ABI := ${ANDROID_NDK_ABI_NAME}\n")
+
+# Copy the gdbserver executable
+if(NOT "${GB_ANDROID_GDBSERVER_PATH}" STREQUAL "")
+	file(COPY "${GB_ANDROID_GDBSERVER_PATH}" DESTINATION "${GB_ANDROID_PATH}/libs/${ANDROID_NDK_ABI_NAME}/")
+endif()
+
+# Copy libraries to the obj and libs directories
+file(MAKE_DIRECTORY "${GB_ANDROID_PATH}/obj/local/${ANDROID_NDK_ABI_NAME}/")
+file(GLOB SHARED_LIBS "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/*.so")
+foreach(SHARED_LIB ${SHARED_LIBS})
+	add_custom_command(TARGET gb_emulator_jni POST_BUILD
+		COMMAND ${CMAKE_COMMAND} -E copy "${SHARED_LIB}"
+			"${GB_ANDROID_PATH}/obj/local/${ANDROID_NDK_ABI_NAME}/"
+		COMMAND ${CMAKE_COMMAND} -E copy "${SHARED_LIB}"
+			"${GB_ANDROID_PATH}/libs/${ANDROID_NDK_ABI_NAME}/")
+endforeach()
+
+# Strip symbols in the libs directory
+add_custom_command(TARGET gb_emulator_jni POST_BUILD COMMAND
+	${CMAKE_STRIP} "${GB_ANDROID_PATH}/libs/${ANDROID_NDK_ABI_NAME}/*.so")

android/jni/gb_android_asset_manager.cpp

+/*  Copyright © 2013 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "gb_android_asset_manager.h"
+
+#include <stdexcept>
+
+#include <android/asset_manager.h>
+#include <android/asset_manager_jni.h>
+
+using std::istream;
+using std::runtime_error;
+using std::string;
+using std::unique_ptr;
+
+class GbAndroidAsset:
+	public GbAsset
+{
+public:
+	GbAndroidAsset(AAssetManager *androidAssetManager, const string &path)
+	{
+		aAsset_ = AAssetManager_open(androidAssetManager, path.c_str(), AASSET_MODE_UNKNOWN);
+		if (!aAsset_)
+		{
+			throw runtime_error("failed to open asset " + path);
+		}
+	}
+
+	~GbAndroidAsset()
+	{
+		AAsset_close(aAsset_);
+	}
+
+	size_t size() const
+	{
+		return AAsset_getLength(aAsset_);
+	}
+
+	string readAll()
+	{
+		const void *data = AAsset_getBuffer(aAsset_);
+		if (!data)
+		{
+			throw runtime_error("failed to read from asset");
+		}
+		return string(static_cast<const char *>(data), size());
+	}
+
+private:
+	// The android asset
+	AAsset *aAsset_;
+
+	// Disabled operations
+	GbAndroidAsset(const GbAndroidAsset &);
+	GbAndroidAsset & operator=(const GbAndroidAsset &);
+};
+
+GbAndroidAssetManager::GbAndroidAssetManager(JNIEnv *env, jobject jAssetManager)
+{
+	androidAssetManager_ = AAssetManager_fromJava(env, jAssetManager);
+	if (!androidAssetManager_)
+	{
+		throw runtime_error("failed to get Android asset manager");
+	}
+}
+
+GbAndroidAssetManager::~GbAndroidAssetManager()
+{
+}
+
+unique_ptr<GbAsset> GbAndroidAssetManager::getAsset(const string &path)
+{
+	return unique_ptr<GbAsset>(new GbAndroidAsset(androidAssetManager_, path));
+}

android/jni/gb_android_asset_manager.h

+/*  Copyright © 2013 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GB_ANDROID_ASSET_MANAGER_H_771FBFC0_623E_11E2_A919_0002A5D5C51B
+#define GB_ANDROID_ASSET_MANAGER_H_771FBFC0_623E_11E2_A919_0002A5D5C51B
+
+#include <android/asset_manager.h>
+
+#include <jni.h>
+
+#include <gb_emulator/gb_asset_manager.h>
+
+//! Asset manager which hooks into the Android asset manager system.
+class GbAndroidAssetManager:
+	public GbAssetManager
+{
+public:
+	//! Constructor; hooks into the given Android asset manager.
+	GbAndroidAssetManager(JNIEnv *env, jobject assetManager);
+
+	//! Destructor.
+	~GbAndroidAssetManager();
+
+	//! Opens the given asset.
+	/**
+	 * \param path Relative path to the asset from the root of the assets store. This should only
+	 * contain ASCII characters. The '/' character may be used to separate subdirectories.
+	 * \return The asset.
+	 * \throws std::runtime_error Thrown if the asset could not be opened.
+	 */
+	std::unique_ptr<GbAsset> getAsset(const std::string &path);
+
+private:
+	// The Android asset manager
+	AAssetManager *androidAssetManager_;
+
+	// Disabled operations
+	GbAndroidAssetManager(const GbAndroidAssetManager &);
+	GbAndroidAssetManager & operator=(const GbAndroidAssetManager &);
+};
+
+#endif

android/jni/gb_jni.cpp

+/*  Copyright © 2013 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "gb_jni.h"
+
+#include <stdint.h>
+
+#include <gb_emulator/gb.h>
+#include <gb_emulator/gb_config.h>
+
+#include "gb_android_asset_manager.h"
+
+jlong Java_uk_co_cwspencer_gb_1emulator_GbJNI_init(JNIEnv *env, jobject, jobject jAssetManager)
+{
+	GbConfig config;
+	config.assetManager = new GbAndroidAssetManager(env, jAssetManager);
+
+	Gb *gb = new Gb(config);
+	return reinterpret_cast<jlong>(gb);
+}
+
+void Java_uk_co_cwspencer_gb_1emulator_GbJNI_deinit(JNIEnv *, jobject, jlong context)
+{
+	// TODO: Delete asset manager
+	Gb *gb = reinterpret_cast<Gb *>(context);
+	delete gb;
+}
+
+void Java_uk_co_cwspencer_gb_1emulator_GbJNI_doFrame(JNIEnv *, jobject, jlong context)
+{
+	Gb *gb = reinterpret_cast<Gb *>(context);
+	gb->doFrame();
+}

android/jni/gb_jni.h

+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class uk_co_cwspencer_gb_emulator_GbJNI */
+
+#ifndef _Included_uk_co_cwspencer_gb_emulator_GbJNI
+#define _Included_uk_co_cwspencer_gb_emulator_GbJNI
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     uk_co_cwspencer_gb_emulator_GbJNI
+ * Method:    init
+ * Signature: (Ljava/lang/Object;)J
+ */
+JNIEXPORT jlong JNICALL Java_uk_co_cwspencer_gb_1emulator_GbJNI_init
+  (JNIEnv *, jobject, jobject);
+
+/*
+ * Class:     uk_co_cwspencer_gb_emulator_GbJNI
+ * Method:    deinit
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_uk_co_cwspencer_gb_1emulator_GbJNI_deinit
+  (JNIEnv *, jobject, jlong);
+
+/*
+ * Class:     uk_co_cwspencer_gb_emulator_GbJNI
+ * Method:    run
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_uk_co_cwspencer_gb_1emulator_GbJNI_run
+  (JNIEnv *, jobject, jlong);
+
+/*
+ * Class:     uk_co_cwspencer_gb_emulator_GbJNI
+ * Method:    doFrame
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_uk_co_cwspencer_gb_1emulator_GbJNI_doFrame
+  (JNIEnv *, jobject, jlong);
+
+/*
+ * Class:     uk_co_cwspencer_gb_emulator_GbJNI
+ * Method:    reset
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_uk_co_cwspencer_gb_1emulator_GbJNI_reset__J
+  (JNIEnv *, jobject, jlong);
+
+/*
+ * Class:     uk_co_cwspencer_gb_emulator_GbJNI
+ * Method:    reset
+ * Signature: (JLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_uk_co_cwspencer_gb_1emulator_GbJNI_reset__JLjava_lang_String_2
+  (JNIEnv *, jobject, jlong, jstring);
+
+/*
+ * Class:     uk_co_cwspencer_gb_emulator_GbJNI
+ * Method:    save
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_uk_co_cwspencer_gb_1emulator_GbJNI_save
+  (JNIEnv *, jobject, jlong);
+
+/*
+ * Class:     uk_co_cwspencer_gb_emulator_GbJNI
+ * Method:    load
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_uk_co_cwspencer_gb_1emulator_GbJNI_load
+  (JNIEnv *, jobject, jlong);
+
+/*
+ * Class:     uk_co_cwspencer_gb_emulator_GbJNI
+ * Method:    saveRam
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_uk_co_cwspencer_gb_1emulator_GbJNI_saveRam
+  (JNIEnv *, jobject, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

gb_emulator/CMakeLists.txt

 set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
-set(GB_USE_GLES2 OFF CACHE BOOL "Whether to use OpenGL ES 2.0 instead of standard OpenGL")
 
+# Locate dependencies
 find_package(Boost REQUIRED)
-find_package(Cg REQUIRED)
-find_package(DirectX REQUIRED)
+if(NOT DEFINED ANDROID)
+	find_package(Cg REQUIRED)
+	find_package(DirectX REQUIRED)
 
-if(NOT ${GB_USE_GLES2} STREQUAL ON)
-	find_package(OpenGL REQUIRED)
+	if(NOT ${GB_USE_GLES2} STREQUAL ON)
+		find_package(OpenGL REQUIRED)
+	endif()
 endif()
 
+# Configure include directories
 include_directories(".")
 include_directories("include")
 include_directories("${PROJECT_SOURCE_DIR}/gb_net/include")
 include_directories("${PROJECT_SOURCE_DIR}/third_party/hqx/include")
-include_directories("${PROJECT_SOURCE_DIR}/third_party/libsndfile/include")
 include_directories("${PROJECT_SOURCE_DIR}/third_party/protobuf/src")
-include_directories("${PROJECT_SOURCE_DIR}/third_party/sdl/include")
 include_directories(${Boost_INCLUDE_DIRS})
-include_directories(${CG_INCLUDE_PATH})
-include_directories(${DirectX_INCLUDE_DIR})
+if(NOT DEFINED ANDROID)
+	include_directories(${CG_INCLUDE_PATH})
+	include_directories(${DirectX_INCLUDE_DIR})
 
-if(${GB_USE_GLES2})
-	include_directories("${PROJECT_SOURCE_DIR}/third_party/angle/include")
-else()
-	include_directories("${PROJECT_SOURCE_DIR}/third_party/glew/include")
-	include_directories(${OPENGL_INCLUDE_DIR})
+	if(${GB_USE_GLES2})
+		include_directories("${PROJECT_SOURCE_DIR}/third_party/angle/include")
+	else()
+		include_directories("${PROJECT_SOURCE_DIR}/third_party/glew/include")
+		include_directories(${OPENGL_INCLUDE_DIR})
+	endif()
 endif()
 
 link_directories(${Boost_LIBRARY_DIRS})
 
+# Set macros
+add_definitions(-DBOOST_FILESYSTEM_VERSION=3)
 add_definitions(-D_SCL_SECURE_NO_WARNINGS)
 add_definitions(-DUNICODE -D_UNICODE)
 add_definitions(-DGB_EMULATOR_EXPORTS)
 add_definitions(-DWIN32_LEAN_AND_MEAN)
 add_definitions(-D_WIN32_WINNT=0x0600)
-
 if(${GB_USE_GLES2})
 	add_definitions(-DGB_USE_GLES2)
 else()
 	add_definitions(-DGLEW_STATIC)
 endif()
+if(${GB_BUILD_DEBUGGER})
+	add_definitions(-DGB_BUILD_DEBUGGER)
+endif()
+# We can't use the default filesystem asset manager on Android
+if(DEFINED ANDROID)
+	add_definitions(-DGB_NO_DEFAULT_ASSET_MANAGER)
+endif()
 
-# Set warning level
+# Set compiler flags
 if(MSVC)
 	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /WX")
 elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
-	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
+	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -std=c++0x -O3 -ggdb3")
 endif()
 
 # Disable warnings on specific files
 	shaders/gles2/fragment_shader.glsl
 	shaders/gles2/vertex_shader.glsl)
 
+# Configure source files
+# Platform-specific files
+if(WIN32)
+	set(GB_PLATFORM_SOURCES
+		include/gb_emulator/gb_sound_wasapi.h
+		include/gb_emulator/gb_sound_wasapi_renderer.h
+		include/gb_emulator/gb_video_d3d11.h
+		src/gb_sound_wasapi.cpp
+		src/gb_sound_wasapi_renderer.cpp
+		src/gb_video_d3d11.cpp
+		shaders/pixel_shader.cg
+		shaders/vertex_shader.cg)
+endif()
+
+if(NOT DEFINED ANDROID)
+	set(GB_PLATFORM_SOURCES
+		${GB_PLATFORM_SOURCES}
+		include/gb_emulator/gb_filesystem_asset_manager.h
+		src/gb_filesystem_asset_manager.cpp)
+endif()
+
+# Debugger files, if enabled
+if(${GB_BUILD_DEBUGGER})
+	set(GB_DEBUGGER_SOURCES
+		include/gb_emulator/cdb_file.h
+		include/gb_emulator/gb_debugger.h
+		include/gb_emulator/gb_debugger_module.h
+		include/gb_emulator/gb_disassembler.h
+		src/cdb_file.cpp
+		src/gb_debugger.cpp
+		src/gb_disassembler.cpp)
+endif()
+
+# OpenGL ES files, if enabled
 if(${GB_USE_GLES2})
 	set(GB_GL_SOURCES
 		include/gb_emulator/gb_video_opengl_es2.h
-		src/gb_video_opengl_es2.cpp)
+		src/gb_video_opengl_es2.cpp
+		shaders/gles2/fragment_shader.glsl
+		shaders/gles2/vertex_shader.glsl)
 else()
 	set(GB_GL_SOURCES
 		include/gb_emulator/gb_video_opengl.h
 endif()
 
 add_library(gb_emulator SHARED
-	include/gb_emulator/cdb_file.h
 	include/gb_emulator/constants.h
 	include/gb_emulator/defs.h
 	include/gb_emulator/gb.h
+	include/gb_emulator/gb_asset_manager.h
 	include/gb_emulator/gb_comms_serial.h
 	include/gb_emulator/gb_config.h
 	include/gb_emulator/gb_cpu.h
-	include/gb_emulator/gb_debugger.h
-	include/gb_emulator/gb_debugger_module.h
-	include/gb_emulator/gb_disassembler.h
 	include/gb_emulator/gb_input.h
 	include/gb_emulator/gb_memory.h
 	include/gb_emulator/gb_sound.h
-	include/gb_emulator/gb_sound_wasapi.h
-	include/gb_emulator/gb_sound_wasapi_renderer.h
+	include/gb_emulator/gb_sound_dummy.h
 	include/gb_emulator/gb_timers.h
 	include/gb_emulator/gb_video.h
-	include/gb_emulator/gb_video_d3d11.h
 
 	gb.pb.cc
-	src/cdb_file.cpp
 	src/gb.cpp
 	src/gb_comms_serial.cpp
 	src/gb_cpu.cpp
-	src/gb_debugger.cpp
-	src/gb_disassembler.cpp
 	src/gb_input.cpp
 	src/gb_memory.cpp
 	src/gb_sound.cpp
-	src/gb_sound_wasapi.cpp
-	src/gb_sound_wasapi_renderer.cpp
+	src/gb_sound_dummy.cpp
 	src/gb_timers.cpp
 	src/gb_video.cpp
-	src/gb_video_d3d11.cpp
-	${GB_GL_SOURCES}
 
-	shaders/pixel_shader.cg
-	shaders/vertex_shader.cg
-	shaders/gles2/fragment_shader.glsl
-	shaders/gles2/vertex_shader.glsl)
+	${GB_PLATFORM_SOURCES}
+	${GB_DEBUGGER_SOURCES}
+	${GB_GL_SOURCES})
 
 target_link_libraries(gb_emulator
 	gb_net
 	hqx
-	libEGL
-	libGLESv2
 	libprotobuf
-	${CG_LIBRARY}
-	${CG_D3D11_LIBRARY}
-	${DirectX_XINPUT_LIBRARY})
+	${Boost_LIBRARIES})
 
-if(NOT ${GB_USE_GLES2})
+if(NOT DEFINED ANDROID)
+	target_link_libraries(gb_emulator
+		${CG_LIBRARY}
+		${CG_D3D11_LIBRARY}
+		${DirectX_XINPUT_LIBRARY})
+endif()
+
+if(${GB_USE_GLES2})
+	if(DEFINED ANDROID)
+		target_link_libraries(gb_emulator
+			GLESv2)
+	else()
+		target_link_libraries(gb_emulator
+			libEGL
+			libGLESv2)
+	endif()
+else()
 	target_link_libraries(gb_emulator
 		glew
 		${CG_GL_LIBRARY}

gb_emulator/include/gb_emulator/defs.h

 #ifndef DEFS_H_4E1047E0_FD6C_11E0_9C2D_0002A5D5C51B
 #define DEFS_H_4E1047E0_FD6C_11E0_9C2D_0002A5D5C51B
 
-#ifdef GB_EMULATOR_EXPORTS
-#define GB_EMULATOR_API __declspec(dllexport)
+#ifdef _WIN32
+
+# ifdef GB_EMULATOR_EXPORTS
+#  define GB_EMULATOR_API __declspec(dllexport)
+# else
+#  define GB_EMULATOR_API __declspec(dllimport)
+# endif
+
+# ifdef GB_DEBUGGER_EXPORTS
+#  define GB_DEBUGGER_API __declspec(dllexport)
+# else
+#  define GB_DEBUGGER_API __declspec(dllimport)
+# endif
+
 #else
-#define GB_EMULATOR_API __declspec(dllimport)
-#endif
 
-#ifdef GB_DEBUGGER_EXPORTS
-#define GB_DEBUGGER_API __declspec(dllexport)
-#else
-#define GB_DEBUGGER_API __declspec(dllimport)
+# define GB_EMULATOR_API
+# define GB_DEBUGGER_API
+
 #endif
 
 #endif

gb_emulator/include/gb_emulator/gb.h

 
 #include <gb_emulator/defs.h>
 #include <gb_emulator/gb_cpu.h>
-#include <gb_emulator/gb_debugger.h>
-#include <gb_emulator/gb_debugger_module.h>
 #include <gb_emulator/gb_timers.h>
 
+#ifdef GB_BUILD_DEBUGGER
+# include <gb_emulator/gb_debugger.h>
+# include <gb_emulator/gb_debugger_module.h>
+#endif
+
+class GbAssetManager;
 class GbCommsSerial;
 class GbConfig;
 class GbDebugger;
 class GbInput;
 class GbMemoryImpl;
+class GbSound;
 class GbSoundWasapi;
+class GbVideo;
 class GbVideoD3D11;
+class GbVideoOpenGL;
+class GbVideoOpenGLES2;
 
 //! Main class for the GameBoy emulator.
 /**
 	friend class GbSoundWasapi;
 	friend class GbVideo;
 	friend class GbVideoD3D11;
+	friend class GbVideoOpenGL;
+	friend class GbVideoOpenGLES2;
 	friend class GbDebugger;
 
 public:
 
 	//! Runs the emulator.
 	/**
-	 * This doesn't return until the emulator exited or there is an error.
+	 * This doesn't return until the emulator exited or there is an error. Use doFrame() instead if
+	 * you need polling behaviour.
 	 */
 	void run();
 
+	//! Performs one frame's worth of emulation.
+	/**
+	 * This can be used instead of run() if you need polling behaviour. Note this this function will
+	 * execute one frame's worth of CPU cycles, but may not actually render anything if the game has
+	 * turned the display off.
+	 */
+	void doFrame();
+
 	//! Resets the emulator.
 	/**
 	 * Path to the new ROM to load; may be empty, in which case no ROM is loaded.
 	// I/O service used by various sub-systems
 	boost::asio::io_service ioService_;
 
+#ifdef GB_BUILD_DEBUGGER
 #ifdef _WIN32
 	// Loaded plugin modules. The values in this vector are not actually used; they are only saved
 	// so they are automatically freed when the class is destructed. Note it is important that these
 	// are freed after the values in debuggers_.
 	std::vector<boost::shared_ptr<HINSTANCE__> > debuggerModules_;
 #endif
+
 	// Loaded debuggers
 	std::vector<boost::shared_ptr<GbDebuggerModule> > debuggers_;
+#endif
 
 	// Emulator state
 	std::basic_string<uint8_t> bios_;
 	// sure any messages are delivered to the game in time to ensure no data is lost
 	bool inSync_;
 
+	// Countdowns for polling the various subsystems
+	int videoCycles_;
+	int timerCycles_;
+	double soundCycles_;
+	int frameCycles_;
+
 	boost::scoped_ptr<GbCpu> cpu_;
 	boost::scoped_ptr<GbSound> sound_;
 	boost::scoped_ptr<GbVideo> video_;
 	boost::scoped_ptr<GbCommsSerial> serial_;
 	int serialCycles_;
 	GbTimers timers_;
+#ifdef GB_BUILD_DEBUGGER
 	GbDebugger debugger_;
+#endif
 	bool gbc_;
 
+	// The asset manager
+	std::unique_ptr<GbAssetManager> defaultAssetManager_;
+	GbAssetManager *assetManager_;
+
 	// Filenames
-	boost::filesystem::path installDir_;
-	boost::filesystem::path biosFilename_;
 	boost::filesystem::path romFilename_;
 	boost::filesystem::path saveFilename_;
 	boost::filesystem::path saveRamFilename_;
 	// Loads the battery-backed RAM from the saved file, if available
 	void loadRam();
 
+#ifdef GB_BUILD_DEBUGGER
 	// Loads any debugger plugins
 	void loadDebuggers();
+#endif
 
 	// Callback which is called at ~57Hz using the sound clock. This simply increments frames_ to
 	// indicate another frame's worth of CPU cycles should be executed.

gb_emulator/include/gb_emulator/gb_asset_manager.h

+/*  Copyright © 2013 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GB_ASSET_MANAGER_H_375F1040_6232_11E2_875B_0002A5D5C51B
+#define GB_ASSET_MANAGER_H_375F1040_6232_11E2_875B_0002A5D5C51B
+
+#include <iostream>
+#include <memory>
+#include <stdexcept>
+#include <string>
+
+//! Abstract class representing an asset distributed with the application.
+class GbAsset
+{
+public:
+	//! Returns the size of the asset.
+	virtual size_t size() const = 0;
+
+	//! Reads all data from the asset.
+	/**
+	 * \throws std::runtime_error Thrown if the asset could not be read.
+	 */
+	virtual std::string readAll() = 0;
+};
+
+//! Abstract class for accessing assets distributed with the application.
+class GbAssetManager
+{
+public:
+	//! Opens the given asset.
+	/**
+	 * \param path Relative path to the asset from the root of the assets store. This should only
+	 * contain ASCII characters. The '/' character may be used to separate subdirectories.
+	 * \return The asset.
+	 * \throws std::runtime_error Thrown if the asset could not be opened.
+	 */
+	virtual std::unique_ptr<GbAsset> getAsset(const std::string &path) = 0;
+
+	//! Convenience function to read all data from the given asset.
+	/**
+	 * \throws std::runtime_error Thrown if the asset could not be opened or read.
+	 */
+	std::string readAsset(const std::string &path)
+	{
+		std::unique_ptr<GbAsset> asset = getAsset(path);
+		return asset->readAll();
+	}
+};
+
+#endif

gb_emulator/include/gb_emulator/gb_config.h

 
 #include <boost/filesystem/path.hpp>
 
+#include <gb_emulator/gb_asset_manager.h>
+
+//! Class containing configuration options to control the behaviour of the emulator.
 class GbConfig
 {
 public:
 	//! The rendering method to use.
 	Renderer renderer;
 
+	//! The asset manager to retrieve assets distributed with the application from.
+	/**
+	 * If null the default asset manager will be used which retrieves assets from the installation
+	 * directory. This must be valid for the lifetime of the Gb instance.
+	 */
+	GbAssetManager *assetManager;
+
 	//! The ROM file to execute.
 	/**
 	 * This may be empty.
 	videoDriver(VIDEO_DEFAULT),
 	audioDriver(AUDIO_DEFAULT),
 	renderer(RENDER_DEFAULT),
+	assetManager(nullptr),
 	serialPort(0)
 	{
 	}

gb_emulator/include/gb_emulator/gb_cpu.h

 	//! Handles CPU interrupts.
 	void handleInterrupts();
 
+#ifdef GB_BUILD_DEBUGGER
 	//! Attaches the given debugger to the CPU emulator.
 	void attachDebugger(GbDebugger *debugger)
 	{
 		debugger_ = debugger;
 	}
+#endif
 
 	//! Saves the current state of the CPU emulator into the given message.
 	void save(GbCpuData &data) const;
 	// Memory manager
 	GbMemory &mem_;
 
+#ifdef GB_BUILD_DEBUGGER
 	// Debugger, if attached
 	GbDebugger *debugger_;
+#endif
 
 	// Disabled operations
 	GbCpu(const GbCpu &);

gb_emulator/include/gb_emulator/gb_filesystem_asset_manager.h

+/*  Copyright © 2013 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GB_FILESYSTEM_ASSET_MANAGER_H_D3245AA0_6235_11E2_892B_0002A5D5C51B
+#define GB_FILESYSTEM_ASSET_MANAGER_H_D3245AA0_6235_11E2_892B_0002A5D5C51B
+
+#include <boost/filesystem/path.hpp>
+
+#include <gb_emulator/gb_asset_manager.h>
+
+//! Asset manager implementation which retrieves assets from the filesystem.
+class GbFilesystemAssetManager:
+	public GbAssetManager
+{
+public:
+	//! Default constructor.
+	GbFilesystemAssetManager();
+
+	//! Destructor.
+	~GbFilesystemAssetManager();
+
+	//! Opens the given asset.
+	/**
+	 * \param path Relative path to the asset from the root of the assets store. This should only
+	 * contain ASCII characters. The '/' character may be used to separate subdirectories.
+	 * \return The asset.
+	 * \throws std::runtime_error Thrown if the asset could not be opened.
+	 */
+	std::unique_ptr<GbAsset> getAsset(const std::string &path);
+
+private:
+	// Path to the assets directory
+	boost::filesystem::path assetsDir_;
+
+	// Disabled operations
+	GbFilesystemAssetManager(const GbFilesystemAssetManager &);
+	GbFilesystemAssetManager & operator=(const GbFilesystemAssetManager &);
+};
+
+#endif

gb_emulator/include/gb_emulator/gb_input.h

 
 #include <stdint.h>
 
-#include <boost/thread/thread.hpp>
+#ifdef _WIN32
+# include <boost/thread/thread.hpp>
 
-#include <Windows.h>
-#include <XInput.h>
+# include <Windows.h>
+# include <XInput.h>
+#endif
 
 class Gb;
 
 	uint8_t joy_;
 	uint8_t joyAxis_;
 
+#ifdef _WIN32
 	XINPUT_STATE xInputState_;
 	boost::mutex xInputMutex_;
 
 	// Background input thread
 	boost::thread inputThread_;
 	void run();
+#endif
 
 	// Disabled operations
 	GbInput(const GbInput &);

gb_emulator/include/gb_emulator/gb_sound_dummy.h

+/*  Copyright © 2013 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GB_SOUND_DUMMY_H_820F8640_5CF3_11E2_8611_0002A5D5C51B
+#define GB_SOUND_DUMMY_H_820F8640_5CF3_11E2_8611_0002A5D5C51B
+
+#include <gb_emulator/gb_sound.h>
+
+//! Dummy sound driver.
+class GbSoundDummy: public GbSound
+{
+public:
+	//! Constructor.
+	GbSoundDummy(Gb &gb);
+
+	//! Destructor.
+	~GbSoundDummy();
+
+	//! Indicates whether the driver is available.
+	/**
+	 * This implementation always returns true.
+	 */
+	bool isAvailable()
+	{
+		return true;
+	}
+
+	//! Opens the sound device and prepares for playback.
+	void initialise();
+
+	//! Checks for and handles any events then returns.
+	void pollEvents();
+
+	//! Sets the callback function to be called at the given frequency based on the audio clock.
+	void setTimerCallback(double frequency, boost::function<void ()> callback);
+
+private:
+	// Sound timer callback
+	boost::function<void ()> timerCallback_;
+
+	// Disabled operations
+	GbSoundDummy(const GbSoundDummy &);
+	GbSoundDummy & operator=(const GbSoundDummy &);
+};
+
+#endif

gb_emulator/include/gb_emulator/gb_video_d3d11.h

 
 #include <stdint.h>
 
-#include <boost/filesystem/path.hpp>
 #include <boost/shared_ptr.hpp>
 
 #include <atlbase.h>
 	 * decoding will be offloaded onto the graphics hardware which results in a big decrease in CPU
 	 * usage, but the result of that is that the fancy image scaling effects are disabled, so
 	 * software rendering should be used unless performance is an issue.
-	 * \param installDir The installation directory.
 	 */
-	GbVideoD3D11(Gb &gb, GbConfig::Renderer renderer, const boost::filesystem::path &installDir);
+	GbVideoD3D11(Gb &gb, GbConfig::Renderer renderer);
 
 	//! Destructor.
 	~GbVideoD3D11();
 	// The rendering method
 	GbConfig::Renderer renderer_;
 
-	// The installation directory
-	boost::filesystem::path installDir_;
-
 	// Indicates which pixel shader is bound. There are separate shaders for GBC and DMG mode
 	// because they have different colour formats. This is only used when hardware rendering is
 	// used.

gb_emulator/include/gb_emulator/gb_video_opengl.h

 #include <memory>
 #include <vector>
 
-#include <boost/filesystem/path.hpp>
-
 #include <Windows.h>
 #include <GL/glew.h>
 #include <GL/GL.h>
 
+#include <gb_emulator/gb_asset_manager.h>
 #include <gb_emulator/gb_video.h>
 
 struct _CGcontext;
 	//! Constructor.
 	/**
 	 * \param gb The emulator context.
-	 * \param installDir The installation directory.
 	 */
-	GbVideoOpenGL(Gb &gb, const boost::filesystem::path &installDir);
+	GbVideoOpenGL(Gb &gb);
 
 	//! Destructor.
 	~GbVideoOpenGL();
 	std::unique_ptr<_CGprogram, void (*)(_CGprogram *)> vertexShader_;
 	std::unique_ptr<_CGprogram, void (*)(_CGprogram *)> fragmentShader_;
 
-	// The installation directory
-	boost::filesystem::path installDir_;
-
 	// RGB pixel buffers
 	std::unique_ptr<uint32_t[]> unscaledPixelBuffer_;
 	std::unique_ptr<uint32_t[]> textureBuffer_;

gb_emulator/include/gb_emulator/gb_video_opengl_es2.h

 #include <memory>
 #include <vector>
 
-#include <boost/filesystem/path.hpp>
-
 #include <EGL/egl.h>
 #include <GLES2/gl2.h>
 
+#include <gb_emulator/gb_asset_manager.h>
 #include <gb_emulator/gb_video.h>
 
 //! OpenGL video driver.
 	//! Constructor.
 	/**
 	 * \param gb The emulator context.
-	 * \param installDir The installation directory.
 	 */
-	GbVideoOpenGLES2(Gb &gb, const boost::filesystem::path &installDir);
+	GbVideoOpenGLES2(Gb &gb);
 
 	//! Destructor.
 	~GbVideoOpenGLES2();
 	//! Initialises OpenGL and opens the display.
 	void initialise();
 
+#ifdef _WIN32
 	//! Returns the window handle.
 	HWND window() const
 	{
 		return window_;
 	}
+#endif
 
 private:
 	// Dimensions of the texture to which the video is rendered
 	static const unsigned TEXTURE_WIDTH = 1024;
 	static const unsigned TEXTURE_HEIGHT = 1024;
 
+#ifdef _WIN32
 	// The window we are drawing into
 	HWND window_;
 	ATOM windowClass_;
 	EGLDisplay display_;
 	EGLSurface surface_;
 	EGLContext eglContext_;
+#endif
 
 	GLuint vertexShader_;
 	GLuint fragmentShader_;
 	std::vector<std::string> extensions_;
 	GLint pixelFormat_;
 
-	// The installation directory
-	boost::filesystem::path installDir_;
-
 	// RGB pixel buffers
 	std::unique_ptr<uint32_t[]> unscaledPixelBuffer_;
 	std::unique_ptr<uint32_t[]> scaledPixelBuffer_;
 	// Initialisation functions
 	void initialiseGL(GLsizei width, GLsizei height);
 	void initialiseShaders();
-	GLuint compileShader(GLenum type, const boost::filesystem::path &source);
+	GLuint compileShader(GLenum type, const std::string &sourcePath);
 
+#ifdef _WIN32
 	// Window message procedure
 	static LRESULT CALLBACK initialWindowProc(HWND window, unsigned message, WPARAM wParam,
 		LPARAM lParam);
 	static LRESULT CALLBACK windowProc(HWND window, unsigned message, WPARAM wParam, LPARAM lParam);
 	LRESULT doWindowProc(HWND window, unsigned message, WPARAM wParam, LPARAM lParam);
+#endif
 
 	// Disabled operations
 	GbVideoOpenGLES2(const GbVideoOpenGLES2 &);

gb_emulator/src/cdb_file.cpp

-/*  Copyright  2011 Chris Spencer <spencercw@gmail.com>
+/*  Copyright © 2011 Chris Spencer <spencercw@gmail.com>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by

gb_emulator/src/gb.cpp

 #include <gb_emulator/gb.h>
 #include <gb_emulator/gb_comms_serial.h>
 #include <gb_emulator/gb_config.h>
+#include <gb_emulator/gb_filesystem_asset_manager.h>
 #include <gb_emulator/gb_input.h>
 #include <gb_emulator/gb_memory.h>
-#include <gb_emulator/gb_sound_wasapi.h>
-#include <gb_emulator/gb_video_d3d11.h>
+#include <gb_emulator/gb_sound_dummy.h>
 
 #ifdef GB_USE_GLES2
 # include <gb_emulator/gb_video_opengl_es2.h>