diff --git a/.gitattributes b/.gitattributes
index b02df2c..7322845 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -45,3 +45,12 @@
*.zip filter=lfs diff=lfs merge=lfs -text
*.7z filter=lfs diff=lfs merge=lfs -text
*.rar filter=lfs diff=lfs merge=lfs -text
+
+# Native third-party plugin binaries
+*.dll filter=lfs diff=lfs merge=lfs -text
+*.dylib filter=lfs diff=lfs merge=lfs -text
+*.so filter=lfs diff=lfs merge=lfs -text
+*.a filter=lfs diff=lfs merge=lfs -text
+*.lib filter=lfs diff=lfs merge=lfs -text
+*.framework/** filter=lfs diff=lfs merge=lfs -text
+*.xcframework/** filter=lfs diff=lfs merge=lfs -text
diff --git a/Build/Android/project.properties b/Build/Android/project.properties
new file mode 100644
index 0000000..738e84e
--- /dev/null
+++ b/Build/Android/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
diff --git a/Build/Android/res/drawable-hdpi/icon.png b/Build/Android/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..58f3f17
--- /dev/null
+++ b/Build/Android/res/drawable-hdpi/icon.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7cdd52f5e33009dce149cb48a11be11a82eb5a2774fc338055e3d37a6e0592f3
+size 13245
diff --git a/Build/Android/res/drawable-ldpi/icon.png b/Build/Android/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..a534bd8
--- /dev/null
+++ b/Build/Android/res/drawable-ldpi/icon.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bf8c7f476c8da179feff59f9060fb0b131a22cdedcc55f3d0cd3010dc2454a15
+size 3523
diff --git a/Build/Android/res/drawable-mdpi/icon.png b/Build/Android/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..d034554
--- /dev/null
+++ b/Build/Android/res/drawable-mdpi/icon.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ed5ad319ab0061aedcc9f59846fd3ee746bef47fe294b6e1ec55c0e4cf7ac1bd
+size 6106
diff --git a/Build/Android/res/drawable-nodpi/splashscreen_portrait.png b/Build/Android/res/drawable-nodpi/splashscreen_portrait.png
new file mode 100644
index 0000000..e8fdde6
--- /dev/null
+++ b/Build/Android/res/drawable-nodpi/splashscreen_portrait.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4f69b30c9dc683f9586443f6c28059091f081f7fa205be57521d41ae1a2eae91
+size 1997085
diff --git a/Build/Android/res/drawable-nodpi/splashscreen_portrait_nodpi.png b/Build/Android/res/drawable-nodpi/splashscreen_portrait_nodpi.png
new file mode 100644
index 0000000..e8fdde6
--- /dev/null
+++ b/Build/Android/res/drawable-nodpi/splashscreen_portrait_nodpi.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4f69b30c9dc683f9586443f6c28059091f081f7fa205be57521d41ae1a2eae91
+size 1997085
diff --git a/Build/Android/res/drawable-xhdpi/icon.png b/Build/Android/res/drawable-xhdpi/icon.png
new file mode 100644
index 0000000..6825d62
--- /dev/null
+++ b/Build/Android/res/drawable-xhdpi/icon.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:79bca2555879015b83757171c5e3ff9b1a9c8d950819f4cf06141cf00bf2e0ea
+size 23139
diff --git a/Build/Android/res/drawable/downloadimageh.png b/Build/Android/res/drawable/downloadimageh.png
new file mode 100644
index 0000000..13c57cf
--- /dev/null
+++ b/Build/Android/res/drawable/downloadimageh.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b3601ecbecaa03fa4e736212b68993cd01078f05b0bf664f2ba342e28a552679
+size 22639
diff --git a/Build/Android/res/drawable/downloadimagev.png b/Build/Android/res/drawable/downloadimagev.png
new file mode 100644
index 0000000..a326e3a
--- /dev/null
+++ b/Build/Android/res/drawable/downloadimagev.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ae947737268b7d25c5111f2ce06cb0b599e1f5719c58f23a154ef183aa3a99d2
+size 22787
diff --git a/Build/Android/res/drawable/icon.png b/Build/Android/res/drawable/icon.png
new file mode 100644
index 0000000..d034554
--- /dev/null
+++ b/Build/Android/res/drawable/icon.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ed5ad319ab0061aedcc9f59846fd3ee746bef47fe294b6e1ec55c0e4cf7ac1bd
+size 6106
diff --git a/Build/Android/res/drawable/splashscreen_landscape.png b/Build/Android/res/drawable/splashscreen_landscape.png
new file mode 100644
index 0000000..4cb1ab7
--- /dev/null
+++ b/Build/Android/res/drawable/splashscreen_landscape.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b3444852dae629c71195714de34d10a7e7088c12fb703d1f0e97cc26853a66a3
+size 5224
diff --git a/Build/Android/res/drawable/splashscreen_p.xml b/Build/Android/res/drawable/splashscreen_p.xml
new file mode 100644
index 0000000..cb9c939
--- /dev/null
+++ b/Build/Android/res/drawable/splashscreen_p.xml
@@ -0,0 +1,7 @@
+
+
+
+ -
+
+
+
diff --git a/Build/Android/res/drawable/splashscreen_portrait.png b/Build/Android/res/drawable/splashscreen_portrait.png
new file mode 100644
index 0000000..e8fdde6
--- /dev/null
+++ b/Build/Android/res/drawable/splashscreen_portrait.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4f69b30c9dc683f9586443f6c28059091f081f7fa205be57521d41ae1a2eae91
+size 1997085
diff --git a/Build/Android/res/values/GooglePlayAppID.xml b/Build/Android/res/values/GooglePlayAppID.xml
new file mode 100644
index 0000000..108444c
--- /dev/null
+++ b/Build/Android/res/values/GooglePlayAppID.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/Build/Android/src/com/AIGames/FunnyGirl/AlarmReceiver.java b/Build/Android/src/com/AIGames/FunnyGirl/AlarmReceiver.java
new file mode 100644
index 0000000..35dcb64
--- /dev/null
+++ b/Build/Android/src/com/AIGames/FunnyGirl/AlarmReceiver.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.AIGames.FunnyGirl;
+
+import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+
+/**
+ * You should start your derived downloader class when this receiver gets the message
+ * from the alarm service using the provided service helper function within the
+ * DownloaderClientMarshaller. This class must be then registered in your AndroidManifest.xml
+ * file with a section like this:
+ *
+ */
+public class AlarmReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent, OBBDownloaderService.class);
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/Build/Android/src/com/AIGames/FunnyGirl/DownloaderActivity.java b/Build/Android/src/com/AIGames/FunnyGirl/DownloaderActivity.java
new file mode 100644
index 0000000..1cdd4fb
--- /dev/null
+++ b/Build/Android/src/com/AIGames/FunnyGirl/DownloaderActivity.java
@@ -0,0 +1,809 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.AIGames.FunnyGirl;
+
+import com.android.vending.expansion.zipfile.ZipResourceFile;
+import com.android.vending.expansion.zipfile.ZipResourceFile.ZipEntryRO;
+import com.google.android.vending.expansion.downloader.Constants;
+import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
+import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
+import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller;
+import com.google.android.vending.expansion.downloader.Helpers;
+import com.google.android.vending.expansion.downloader.IDownloaderClient;
+import com.google.android.vending.expansion.downloader.IDownloaderService;
+import com.google.android.vending.expansion.downloader.IStub;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Messenger;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.util.zip.CRC32;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+
+import com.epicgames.unreal.GameActivity;
+
+/**
+ * This is sample code for a project built against the downloader library. It
+ * implements the IDownloaderClient that the client marshaler will talk to as
+ * messages are delivered from the DownloaderService.
+ */
+public class DownloaderActivity extends Activity implements IDownloaderClient {
+ private static final String LOG_TAG = "LVLDownloader";
+ private ProgressBar mPB;
+
+ private TextView mStatusText;
+ private TextView mProgressFraction;
+ private TextView mProgressPercent;
+ private TextView mAverageSpeed;
+ private TextView mTimeRemaining;
+
+ private View mDashboard;
+ private View mCellMessage;
+
+ private Button mPauseButton;
+ private Button mWiFiSettingsButton;
+
+ private boolean mStatePaused;
+ private int mState;
+
+ private IDownloaderService mRemoteService;
+
+ private IStub mDownloaderClientStub;
+
+ private final CharSequence[] OBBSelectItems = { "Use Store Data", "Use Development Data" };
+
+
+ private void setState(int newState) {
+ if (mState != newState) {
+ mState = newState;
+ mStatusText.setText(Helpers.getDownloaderStringResourceIDFromState(newState));
+ }
+ }
+
+ private void setButtonPausedState(boolean paused) {
+ mStatePaused = paused;
+ int stringResourceID = paused ? R.string.text_button_resume :
+ R.string.text_button_pause;
+ mPauseButton.setText(stringResourceID);
+ }
+
+ static DownloaderActivity _download;
+
+ private Intent OutputData;
+
+ /**
+ * Go through each of the APK Expansion files defined in the structure above
+ * and determine if the files are present and match the required size. Free
+ * applications should definitely consider doing this, as this allows the
+ * application to be launched for the first time without having a network
+ * connection present. Paid applications that use LVL should probably do at
+ * least one LVL check that requires the network to be present, so this is
+ * not as necessary.
+ *
+ * @return true if they are present.
+ */
+ boolean expansionFilesDelivered() {
+
+ for (OBBData.XAPKFile xf : OBBData.xAPKS) {
+ String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsMain, xf.mFileVersion, OBBData.AppType);
+ GameActivity.Log.debug("Checking for file : " + fileName);
+ String fileForNewFile = Helpers.generateSaveFileName(this, fileName);
+ String fileForDevFile = Helpers.generateSaveFileNameDevelopment(this, fileName);
+ GameActivity.Log.debug("which is really being resolved to : " + fileForNewFile + "\n Or : " + fileForDevFile);
+ if (!Helpers.doesFileExist(this, fileName, xf.mFileSize, false) &&
+ !Helpers.doesFileExistDev(this, fileName, xf.mFileSize, false))
+ return false;
+ }
+ return true;
+ }
+
+ boolean onlySingleExpansionFileFound() {
+ for (OBBData.XAPKFile xf : OBBData.xAPKS) {
+ String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsMain, xf.mFileVersion, OBBData.AppType);
+ GameActivity.Log.debug("Checking for file : " + fileName);
+ String fileForNewFile = Helpers.generateSaveFileName(this, fileName);
+ String fileForDevFile = Helpers.generateSaveFileNameDevelopment(this, fileName);
+
+ if (Helpers.doesFileExist(this, fileName, xf.mFileSize, false) &&
+ Helpers.doesFileExistDev(this, fileName, xf.mFileSize, false))
+ return false;
+ }
+
+ return true;
+ }
+
+ File getFileDetailsCacheFile() {
+ return new File(this.getExternalFilesDir(null), "cacheFile.txt");
+ }
+
+ boolean expansionFilesUptoData() {
+
+ File cacheFile = getFileDetailsCacheFile();
+ // Read data into an array or something...
+ Map fileDetailsMap = new HashMap();
+
+ if(cacheFile.exists()) {
+ try {
+ FileReader fileCache = new FileReader(cacheFile);
+ BufferedReader bufferedFileCache = new BufferedReader(fileCache);
+ List lines = new ArrayList();
+ String line = null;
+ while ((line = bufferedFileCache.readLine()) != null) {
+ lines.add(line);
+ }
+ bufferedFileCache.close();
+
+ for(String dataLine : lines)
+ {
+ GameActivity.Log.debug("Splitting dataLine => " + dataLine);
+ String[] parts = dataLine.split(",");
+ fileDetailsMap.put(parts[0], Long.parseLong(parts[1]));
+ }
+ }
+ catch(Exception e)
+ {
+ GameActivity.Log.debug("Exception thrown during file details reading.");
+ e.printStackTrace();
+ fileDetailsMap.clear();
+ }
+ }
+
+ for (OBBData.XAPKFile xf : OBBData.xAPKS) {
+ String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsMain, xf.mFileVersion, OBBData.AppType);
+ String fileForNewFile = Helpers.generateSaveFileName(this, fileName);
+ String fileForDevFile = Helpers.generateSaveFileNameDevelopment(this, fileName);
+ // check to see if time/data on files match cached version
+ // if not return false
+ File srcFile = new File(fileForNewFile);
+ File srcDevFile = new File(fileForDevFile);
+ long lastModified = srcFile.lastModified();
+ long lastModifiedDev = srcDevFile.lastModified();
+ if(!(srcFile.exists() && fileDetailsMap.containsKey(fileName) && lastModified == fileDetailsMap.get(fileName))
+ &&
+ !(srcDevFile.exists() && fileDetailsMap.containsKey(fileName) && lastModifiedDev == fileDetailsMap.get(fileName)))
+ return false;
+ }
+ return true;
+ }
+
+ static private void RemoveOBBFile(int OBBToDelete) {
+
+ for (OBBData.XAPKFile xf : OBBData.xAPKS) {
+ String fileName = Helpers.getExpansionAPKFileName(DownloaderActivity._download, xf.mIsMain, xf.mFileVersion, OBBData.AppType);
+ switch(OBBToDelete)
+ {
+ case 0:
+ String fileForNewFile = Helpers.generateSaveFileName(DownloaderActivity._download, fileName);
+ File srcFile = new File(fileForNewFile);
+ srcFile.delete();
+ break;
+ case 1:
+ String fileForDevFile = Helpers.generateSaveFileNameDevelopment(DownloaderActivity._download, fileName);
+ File srcDevFile = new File(fileForDevFile);
+ srcDevFile.delete();
+ break;
+ }
+ }
+ }
+
+ private void ProcessOBBFiles()
+ {
+ if(GameActivity.Get().VerifyOBBOnStartUp && !expansionFilesUptoData()) {
+ validateXAPKZipFiles();
+ } else {
+ OutputData.putExtra(GameActivity.DOWNLOAD_RETURN_NAME, GameActivity.DOWNLOAD_FILES_PRESENT);
+ setResult(RESULT_OK, OutputData);
+ finish();
+ overridePendingTransition(R.anim.noaction, R.anim.noaction);
+ }
+ }
+
+ /**
+ * Calculating a moving average for the validation speed so we don't get
+ * jumpy calculations for time etc.
+ */
+ static private final float SMOOTHING_FACTOR = 0.005f;
+
+ /**
+ * Used by the async task
+ */
+ private boolean mCancelValidation;
+
+ /**
+ * Go through each of the Expansion APK files and open each as a zip file.
+ * Calculate the CRC for each file and return false if any fail to match.
+ *
+ * @return true if XAPKZipFile is successful
+ */
+ void validateXAPKZipFiles() {
+ AsyncTask