diff --git a/TO_MERGE/agm/Optics/agm_optics_pip.p3d b/TO_MERGE/agm/Optics/agm_optics_pip.p3d
new file mode 100644
index 0000000000..d331ce4acd
Binary files /dev/null and b/TO_MERGE/agm/Optics/agm_optics_pip.p3d differ
diff --git a/TO_MERGE/agm/Optics/clientInit.sqf b/TO_MERGE/agm/Optics/clientInit.sqf
new file mode 100644
index 0000000000..7dca46fdc4
--- /dev/null
+++ b/TO_MERGE/agm/Optics/clientInit.sqf
@@ -0,0 +1,25 @@
+// TMR: Optics initialization and functions
+// (C) 2013 Ryan Schultz. See LICENSE.
+
+// Request a resource layer from the game engine.
+AGM_Optics_scopeRSC = ["AGM_Optics_Scope"] call BIS_fnc_rscLayer;
+
+// Set global variables
+AGM_Optics_inScope = false; // Is the scope up?
+AGM_Optics_currentOptic = ""; // What optic is attached right now?
+
+0 = 0 spawn {
+  waituntil {!isNull (findDisplay 46)};
+
+  [] call AGM_Optics_fnc_initScope;
+
+  // PiP technique by BadBenson
+  AGM_Optics_Camera = "camera" camCreate (positioncameratoworld [0,0,0]);
+  AGM_Optics_Camera camSetFov 0.7;
+  AGM_Optics_Camera camSetTarget player;
+  AGM_Optics_Camera camCommit 1;
+  "agm_optics_rendertarget0" setPiPEffect [2, 1.0, 1.0, 1.0, 0.0, [0.0, 1.0, 0.0, 0.25], [1.0, 0.0, 1.0, 1.0], [0.199, 0.587, 0.114, 0.0]];
+  AGM_Optics_Camera cameraEffect ["INTERNAL", "BACK","agm_optics_rendertarget0"];
+
+  waitUntil {[] call AGM_Optics_fnc_mainLoop; False};
+};
diff --git a/TO_MERGE/agm/Optics/config.cpp b/TO_MERGE/agm/Optics/config.cpp
new file mode 100644
index 0000000000..429c57cbe2
--- /dev/null
+++ b/TO_MERGE/agm/Optics/config.cpp
@@ -0,0 +1,454 @@
+class CfgPatches {
+  class AGM_Optics {
+    units[] = {};
+    weapons[] = {};
+    requiredVersion = 0.60;
+    requiredAddons[] = {AGM_Core};
+    version = 0.1;
+    author[] = {"Taosenai"};
+    authorUrl = "http://www.ryanschultz.org/tmr/";
+  };
+};
+
+class CfgFunctions {
+  class AGM_Optics {
+    class AGM_Optics {
+      file = "AGM_Optics\functions";
+      class firedEH;
+      class hideScope;
+      class initScope;
+      class mainLoop;
+    };
+  };
+};
+
+class Extended_PostInit_EventHandlers {
+  class AGM_Optics {
+    clientInit = "call compile preProcessFileLineNumbers '\AGM_Optics\clientInit.sqf'";
+  };
+};
+
+class Extended_FiredBIS_EventHandlers {
+  class CAManBase {
+    class AGM_Optics {
+      clientFiredBIS = "_this call AGM_Optics_fnc_firedEH;";
+    };
+  };
+};
+
+class CfgOpticsEffect {
+  class AGM_OpticsRadBlur1 {
+    type = "radialblur";
+    params[] = {0.015, 0, 0.14, 0.2};
+    priority = 950;
+  };
+};
+
+
+class CfgWeapons {
+  class ItemCore;
+  class InventoryItem_Base_F;
+  class InventoryMuzzleItem_Base_F;
+  class InventoryOpticsItem_Base_F;
+
+  class optic_Hamr : ItemCore {
+    displayName = "HAMR 4x";
+    descriptionShort = "High Accuracy Multi-Range Optic<br />Magnification: 4x<br />Reticle: CM-RW 6.5mm";
+    scope = 2;
+    weaponInfoType = "AGM_RscWeapon";
+
+    AGM_Optics_enhanced = 1;
+    AGM_Optics_reticle = "\AGM_Optics\data\hamr\hamr-reticle65_ca.paa";
+    AGM_Optics_reticleIllum = "\AGM_Optics\data\hamr\hamr-reticle65Illum_ca.paa";
+    AGM_Optics_body = "\AGM_Optics\data\hamr\hamr-body_ca.paa";
+    AGM_Optics_bodyNight = "\AGM_Optics\data\hamr\hamr-bodyNight_ca.paa";
+
+    model = "\A3\weapons_f\acc\acco_hamr_F";
+
+    class ItemInfo : InventoryOpticsItem_Base_F {
+      mass = 4;
+      optics = 1;
+      optictype = 1;
+      rmbhint = "HAMR";
+      modeloptics = "\AGM_Optics\agm_optics_pip.p3d";
+
+      class OpticsModes {
+        class Hamr2Collimator {
+          AGM_Optics_enhanced = 0;
+          opticsID = 1;
+          useModelOptics = 0;
+          opticsppeffects[] = {};
+          opticsFlare = 0;
+          opticsDisablePeripherialVision = 0;
+          opticsZoomMin = 0.375;
+          opticsZoomMax = 1;
+          opticsZoomInit = 0.75;
+          memoryPointCamera = "eye";
+          visionMode[] = {};
+          distanceZoomMin = 300;
+          distanceZoomMax = 300;
+        };
+
+        class Hamr2Scope {
+          cameradir = "";
+          distanceZoomMin = 300;
+          distanceZoomMax = 300;
+          memorypointcamera = "opticView";
+          opticsdisableperipherialvision = 0;
+          opticsdisplayname = "IHAMR";
+          opticsflare = 1;
+          opticsid = 2;
+          opticsppeffects[] = {"OpticsCHAbera2", "OpticsBlur1", "AGM_OpticsRadBlur1"};
+          opticszoominit = 0.0872664626;
+          opticszoommax = 0.0872664626;
+          opticszoommin = 0.0872664626;
+          discretefov[] = {0.0872664626};
+          discreteinitindex = 0;
+          usemodeloptics = 1;
+          modeloptics = "\AGM_Optics\agm_optics_pip.p3d";
+          visionmode[] = {"Normal", "NVG"};
+        };
+      };
+    };
+  };
+
+  class optic_Arco : ItemCore {
+    descriptionshort = "Advanced Rifle Combat Optic<br />Magnification: 4x<br />Reticle: SpecterDR 6.5mm";
+    displayname = "ARCO 4x";
+    picture = "\A3\weapons_F\Data\UI\gear_acco_Arco_CA.paa";
+    scope = 2;
+    weaponInfoType = "AGM_RscWeapon";
+
+    model = "\A3\weapons_f\acc\acco_Arco_F";
+
+    AGM_Optics_enhanced = 1;
+    AGM_Optics_reticle = "\AGM_Optics\data\arco\arco-reticle65_ca.paa";
+    AGM_Optics_reticleIllum = "\AGM_Optics\data\arco\arco-reticle65Illum_ca.paa";
+    AGM_Optics_body = "\AGM_Optics\data\arco\arco-body_ca.paa";
+    AGM_Optics_bodyNight = "\AGM_Optics\data\arco\arco-bodyNight_ca.paa";
+
+    class ItemInfo: InventoryOpticsItem_Base_F {
+      mass = 4;
+      optics = 1;
+      optictype = 1;
+      rmbhint = "ARCO";
+
+      class OpticsModes {
+        class ARCO2collimator {
+          AGM_Optics_enhanced = 0;
+          cameradir = "";
+          distancezoommax = 300;
+          distancezoommin = 300;
+          memorypointcamera = "eye";
+          opticsdisableperipherialvision = 0;
+          opticsdisplayname = "CQB";
+          opticsflare = 0;
+          opticsid = 1;
+          opticsppeffects[] = {};
+          opticszoominit = 0.75;
+          opticszoommax = 1.1;
+          opticszoommin = 0.375;
+          usemodeloptics = 0;
+          visionmode[] = {};
+        };
+        class ARCO2scope: ARCO2collimator {
+          cameradir = "";
+          distanceZoomMin = 300;
+          distanceZoomMax = 300;
+          memorypointcamera = "opticView";
+          opticsdisableperipherialvision = 0;
+          opticsdisplayname = "ARCO";
+          opticsflare = 1;
+          opticsid = 2;
+          opticsppeffects[] = {"OpticsCHAbera2", "OpticsBlur1", "AGM_OpticsRadBlur1"};
+          opticszoominit = 0.0872664626; // 0.0872664626 rad = 5 degrees
+          opticszoommax = 0.0872664626; // SpecterDR 4x is 6 degrees
+          opticszoommin = 0.0872664626; // Scope graphic in game covers 1 degree
+          discretefov[] = {0.0872664626};
+          discreteinitindex = 0;
+          usemodeloptics = 1;
+          modeloptics = "\AGM_Optics\data\AGM_Optics_reticle90.p3d";
+          visionmode[] = {"Normal"};
+        };
+      };
+    };
+  };
+
+  class optic_MRCO : ItemCore {
+    displayName = "MRCO 1x/4x";
+    descriptionShort = "Medium Range Combat Optic<br />Magnification: 1x/4x<br />Reticle: Pitbull Gen II 5.56mm";
+    scope = 2;
+    weaponInfoType = "AGM_RscWeapon";
+
+    AGM_Optics_enhanced = 1;
+    AGM_Optics_reticle = "\AGM_Optics\data\mrco\mrco-reticle556_ca.paa";
+    AGM_Optics_reticleIllum = "\AGM_Optics\data\mrco\mrco-reticle556Illum_ca.paa";
+    AGM_Optics_body = "\AGM_Optics\data\mrco\mrco-body_ca.paa";
+    AGM_Optics_bodyNight = "\AGM_Optics\data\mrco\mrco-bodyNight_ca.paa";
+
+    class ItemInfo : InventoryOpticsItem_Base_F {
+      opticType = 1;
+      mass = 4;
+      optics = 1;
+      modelOptics = "\A3\Weapons_f_beta\acc\reticle_MRCO_F";
+
+      class OpticsModes {
+        class MRCOcq {
+          AGM_Optics_enhanced = 0;
+          opticsID = 1;
+          useModelOptics = 0;
+          opticsPPEffects[] = {};
+          opticsFlare = 0;
+          opticsDisablePeripherialVision = 0;
+          opticsZoomMin = 0.375;
+          opticsZoomMax = 1;
+          opticsZoomInit = 0.75;
+          memoryPointCamera = "eye";
+          visionMode[] = {};
+          distanceZoomMin = 100;
+          distanceZoomMax = 100;
+        };
+
+        class MRCOscope {
+          cameradir = "";
+          distanceZoomMin = 300;
+          distanceZoomMax = 300;
+          memorypointcamera = "eye";
+          opticsdisableperipherialvision = 0;
+          opticsdisplayname = "MRCO";
+          opticsflare = 1;
+          opticsid = 2;
+          opticsppeffects[] = {"OpticsCHAbera2", "OpticsBlur2", "AGM_OpticsRadBlur1"};
+          opticszoominit = 0.0872664626;
+          opticszoommax = 0.0872664626;
+          opticszoommin = 0.0872664626;
+          discretefov[] = {0.0872664626};
+          discreteinitindex = 0;
+          usemodeloptics = 1;
+          modeloptics = "\AGM_Optics\data\AGM_Optics_reticle90.p3d";
+          visionmode[] = {"Normal"};
+        };
+      };
+    };
+  };
+
+  class optic_Nightstalker : ItemCore {
+    class ItemInfo: InventoryOpticsItem_Base_F {
+      class OpticsModes {
+        class NCTALKEP {};
+        class Iron : NCTALKEP {
+          opticsppeffects[] = {}; // Fix Arma 3 bug
+        };
+      };
+    };
+  };
+
+  class optic_SOS: ItemCore {
+    class ItemInfo: InventoryOpticsItem_Base_F {
+      modelOptics = "\AGM_Optics\agm_optics_pip.p3d";
+      class OpticsModes {
+        class Snip {
+          visionMode[] = {"Normal","TI","NVG"};
+          thermalMode[] = {5,6};
+          opticsPPEffects[] = {"OpticsCHAbera1","radblur"};
+          modelOptics[] = {"\AGM_Optics\agm_optics_pip.p3d","\AGM_Optics\agm_optics_pip.p3d"};
+        };
+      };
+
+    };
+  };
+
+  class optic_DMS : ItemCore {
+    class ItemInfo: InventoryOpticsItem_Base_F {
+      class OpticsModes {
+        class Snip {};
+        class Iron : Snip {
+          opticsppeffects[] = {}; // Fix Arma 3 bug
+        };
+      };
+    };
+  };
+
+  class optic_LRPS : ItemCore {
+    descriptionshort = "Nightforce NXS Riflescope<br />Magnification: 5.5-22x";
+    displayname = "NXS 5.5-22x";
+    weaponinfotype = "AGM_RscWeapon";
+
+    AGM_Optics_enhanced = 1;
+    AGM_Optics_reticle = "\AGM_Optics\data\sos\sos-reticleMLR_ca.paa";
+    AGM_Optics_reticleIllum = "\AGM_Optics\data\sos\sos-reticleMLRIllum_ca.paa";
+    AGM_Optics_body = "\AGM_Optics\data\sos\sos-body_ca.paa";
+    AGM_Optics_bodyNight = "\AGM_Optics\data\sos\sos-bodyNight_ca.paa";
+
+    class ItemInfo: InventoryOpticsItem_Base_F {
+      modeloptics = "\AGM_Optics\data\AGM_Optics_reticle90.p3d";
+      weaponinfotype = "RscWeaponRangeZeroingFOV";
+      opticType = 2; // Sniper optics
+
+      class OpticsModes {
+        // Based on Nightforce NXS 5.5-22 scope
+        class Snip {
+          cameradir = "";
+          discretedistance[] = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2100, 2200, 2300};
+          discretedistanceinitindex = 0;
+          discreteinitindex = 0;
+          distancezoommax = 2300;
+          distancezoommin = 100;
+          memorypointcamera = "opticView";
+          modeloptics = "\AGM_Optics\data\AGM_Optics_reticle90.p3d";
+          opticsdisableperipherialvision = 1;
+          opticsdisplayname = "SOS";
+          opticsflare = 1;
+          opticsid = 1;
+          opticsppeffects[] = {"OpticsCHAbera1", "OpticsBlur1", "AGM_OpticsRadBlur1"};
+          // How to determine opticszoom
+          // First do the basic math based on the listed FOV of the scope to
+          // get a baseline FOV
+          // 0.1 meter at 100 meters = 1 mrad
+          //
+          // 5.5x FOV -- 5.3 m at 100 m = 53 mrad
+          // = 0.053 rad = 3.037 deg FOV
+
+          // 22x FOV -- 1.4 m at 100m = 14 mrad
+          // = 0.014 rad = 0.802 deg
+
+          // The FOV you give the engine is based on a rather larger scope outline, so we
+          // have to do this extra work ourselves.
+
+          // At 1680x1050
+          // The width of a TMR optic viewfield is 864px
+          // The engine viewport width (which is what the below FOV is based on) is 980
+          // (864/980) = (FOV to give engine / true FOV of optic)
+          // 864/980 * 0.053 = 0.04673
+          // 864/980 * 0.014 = 0.01234
+
+          // Measured experimentally, these values seem quite right.
+          // Certainly they're close enough after you account for pixel density, etc.
+
+          opticszoominit = 0.01234;
+          opticszoommax = 0.04673;
+          opticszoommin = 0.01234;
+          discretefov[] = {};
+          usemodeloptics = 1;
+          visionmode[] = {"Normal"};
+        };
+      };
+    };
+  };
+
+  class optic_Yorris : ItemCore {
+    descriptionshort = "Burris FastFire II Red Dot Sight<br />Magnification: 1x";
+    displayname = "FastFire II";
+  };
+
+  class optic_MRD : ItemCore {
+    descriptionshort = "Eotech MRDS Red Dot Sight<br />Magnification: 1x";
+    displayname = "MRDS";
+  };
+
+  class optic_Holosight : ItemCore {
+    descriptionshort = "Eotech XPS3 Holographic Sight<br />Magnification: 1x";
+    displayname = "XPS3 Holo";
+  };
+};
+
+class RscOpticsText;
+class RscOpticsValue;
+class RscInGameUI {
+  class RscUnitInfo;
+  class RscWeaponZeroing;
+  class AGM_RscWeapon : RscWeaponZeroing {
+    idd = -1;
+    controls[] = {"CA_Zeroing", "CA_FOVMode"};
+
+    onLoad ="with uiNameSpace do { AGM_OpticsIGUI = _this select 0 }";
+
+    class CA_FOVMode : RscOpticsValue {
+      idc = 154;
+      style = 2;
+      colorText[] = {0, 0, 0, 0};
+      x = 0;
+      y = 0;
+      w = 0;
+      h = 0;
+    };
+  };
+};
+
+class RscTitles {
+  class AGM_Optics_Scope {
+    idd = -1;
+    onLoad = "with uiNameSpace do { AGM_Optics_Scope = _this select 0 };";
+    onUnload = "";
+    movingEnable = 1;
+    duration = 10000;
+    controls[] = {"Reticle", "ReticleNight", "BodyNight", "Body"};
+
+    class Reticle {
+      colorBackground[] = {0,0,0, 0};
+      colorText[] = {1,1,1, 1};
+      fade = 0;
+      font = "PuristaMedium";
+      h = SafeZoneH;
+      idc = 1;
+      lineSpacing = 1.0;
+      movingEnable = 1;
+      size = 0;
+      sizeEx = 1;
+      style = 48;
+      text = "";
+      type = 0;
+      w = SafeZoneWAbs / ((getResolution select 0) / (getResolution select 1));
+      x = (SafeZoneXAbs + SafeZoneWAbs/2 - (SafeZoneWAbs / ((getResolution select 0) / (getResolution select 1)))/2);
+      y = SafeZoneY;
+    };
+
+    class ReticleNight : Reticle {
+      idc = 2;
+      text = "";
+    };
+
+    class Body : Reticle {
+      idc = 6;
+      text = "";
+      x = (SafeZoneXAbs + SafeZoneWAbs/2 - (SafeZoneWAbs / ((getResolution select 0) / (getResolution select 1))));
+      y = SafeZoneY - (SafeZoneH/2);
+      w = SafeZoneWAbs / ((getResolution select 0) / (getResolution select 1)) * 2;
+      h = SafeZoneH * 2;
+    };
+
+    class BodyNight : Body {
+      idc = 5;
+      text = "";
+    };
+  };
+};
+
+class PreloadTextures {
+  class CfgWeapons {
+    class optic_hamr {
+      AGM_Optics_body= "*";
+      AGM_Optics_bodyNight = "*";
+      AGM_Optics_reticle = "*";
+      AGM_Optics_reticleIllum = "*";
+    };
+    class optic_arco {
+      AGM_Optics_body= "*";
+      AGM_Optics_bodyNight = "*";
+      AGM_Optics_reticle = "*";
+      AGM_Optics_reticleIllum = "*";
+    };
+    class optic_mrco {
+      AGM_Optics_body= "*";
+      AGM_Optics_bodyNight = "*";
+      AGM_Optics_reticle = "*";
+      AGM_Optics_reticleIllum = "*";
+    };
+    class optic_LRPS {
+      AGM_Optics_body= "*";
+      AGM_Optics_bodyNight = "*";
+      AGM_Optics_reticle = "*";
+      AGM_Optics_reticleIllum = "*";
+    };
+  };
+};
diff --git a/TO_MERGE/agm/Optics/data/arco/arco-bodyNight_ca.paa b/TO_MERGE/agm/Optics/data/arco/arco-bodyNight_ca.paa
new file mode 100644
index 0000000000..9b186b56f4
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/arco/arco-bodyNight_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/arco/arco-body_ca.paa b/TO_MERGE/agm/Optics/data/arco/arco-body_ca.paa
new file mode 100644
index 0000000000..193b14a92c
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/arco/arco-body_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/arco/arco-reticle65Illum_ca.paa b/TO_MERGE/agm/Optics/data/arco/arco-reticle65Illum_ca.paa
new file mode 100644
index 0000000000..d193051e81
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/arco/arco-reticle65Illum_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/arco/arco-reticle65_ca.paa b/TO_MERGE/agm/Optics/data/arco/arco-reticle65_ca.paa
new file mode 100644
index 0000000000..671417fd4b
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/arco/arco-reticle65_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/black.rvmat b/TO_MERGE/agm/Optics/data/black.rvmat
new file mode 100644
index 0000000000..431d76689f
--- /dev/null
+++ b/TO_MERGE/agm/Optics/data/black.rvmat
@@ -0,0 +1,8 @@
+ambient[]={0,0,0,0.89999998};
+diffuse[]={0,0,0,0.89999998};
+forcedDiffuse[]={0,0,0,1};
+emmisive[]={0,0,0,1};
+specular[]={0,0,0,0};
+specularPower=1;
+PixelShaderID="Normal";
+VertexShaderID="Basic";
diff --git a/TO_MERGE/agm/Optics/data/em.rvmat b/TO_MERGE/agm/Optics/data/em.rvmat
new file mode 100644
index 0000000000..a491df4b9a
--- /dev/null
+++ b/TO_MERGE/agm/Optics/data/em.rvmat
@@ -0,0 +1,20 @@
+ambient[]={1,1,1,1};
+diffuse[]={1,1,1,1};
+forcedDiffuse[]={0,0,0,0};
+emmisive[]={1,1,1,1};
+specular[]={1,0.99956858,1,1};
+specularPower=1;
+PixelShaderID="Normal";
+VertexShaderID="Basic";
+class Stage1
+{
+	texture="#(argb,8,8,3)color(0.5,0.5,0.5,0.5,DT)";
+	uvSource="tex";
+	class uvTransform
+	{
+		aside[]={1,0,0};
+		up[]={0,1,0};
+		dir[]={0,0,0};
+		pos[]={0,0,0};
+	};
+};
diff --git a/TO_MERGE/agm/Optics/data/hamr/hamr-bodyNight_ca.paa b/TO_MERGE/agm/Optics/data/hamr/hamr-bodyNight_ca.paa
new file mode 100644
index 0000000000..422b13f7f1
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/hamr/hamr-bodyNight_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/hamr/hamr-body_ca.paa b/TO_MERGE/agm/Optics/data/hamr/hamr-body_ca.paa
new file mode 100644
index 0000000000..6b55916c5f
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/hamr/hamr-body_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/hamr/hamr-reticle65Illum_ca.paa b/TO_MERGE/agm/Optics/data/hamr/hamr-reticle65Illum_ca.paa
new file mode 100644
index 0000000000..7af73234fc
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/hamr/hamr-reticle65Illum_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/hamr/hamr-reticle65_ca.paa b/TO_MERGE/agm/Optics/data/hamr/hamr-reticle65_ca.paa
new file mode 100644
index 0000000000..2cbf329c89
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/hamr/hamr-reticle65_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/mrco/mrco-bodyNight_ca.paa b/TO_MERGE/agm/Optics/data/mrco/mrco-bodyNight_ca.paa
new file mode 100644
index 0000000000..381fdd3e97
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/mrco/mrco-bodyNight_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/mrco/mrco-body_ca.paa b/TO_MERGE/agm/Optics/data/mrco/mrco-body_ca.paa
new file mode 100644
index 0000000000..bfcaa9828f
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/mrco/mrco-body_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/mrco/mrco-reticle556Illum_ca.paa b/TO_MERGE/agm/Optics/data/mrco/mrco-reticle556Illum_ca.paa
new file mode 100644
index 0000000000..5bf836d593
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/mrco/mrco-reticle556Illum_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/mrco/mrco-reticle556_ca.paa b/TO_MERGE/agm/Optics/data/mrco/mrco-reticle556_ca.paa
new file mode 100644
index 0000000000..e7b8599f83
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/mrco/mrco-reticle556_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/scopeblack-100_ca.paa b/TO_MERGE/agm/Optics/data/scopeblack-100_ca.paa
new file mode 100644
index 0000000000..d0232dc0cc
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/scopeblack-100_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/scopeblack-70_ca.paa b/TO_MERGE/agm/Optics/data/scopeblack-70_ca.paa
new file mode 100644
index 0000000000..62b06d7f84
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/scopeblack-70_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/scopeblack-80_ca.paa b/TO_MERGE/agm/Optics/data/scopeblack-80_ca.paa
new file mode 100644
index 0000000000..f74e3e41f1
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/scopeblack-80_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/scopeblack-90_ca.paa b/TO_MERGE/agm/Optics/data/scopeblack-90_ca.paa
new file mode 100644
index 0000000000..2240dcc5fe
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/scopeblack-90_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/sos/sos-bodyNight_ca.paa b/TO_MERGE/agm/Optics/data/sos/sos-bodyNight_ca.paa
new file mode 100644
index 0000000000..22f008b17a
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/sos/sos-bodyNight_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/sos/sos-body_ca.paa b/TO_MERGE/agm/Optics/data/sos/sos-body_ca.paa
new file mode 100644
index 0000000000..37c6d47d63
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/sos/sos-body_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/sos/sos-reticleMLRIllum_ca.paa b/TO_MERGE/agm/Optics/data/sos/sos-reticleMLRIllum_ca.paa
new file mode 100644
index 0000000000..65d0afb2e2
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/sos/sos-reticleMLRIllum_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/sos/sos-reticleMLR_ca.paa b/TO_MERGE/agm/Optics/data/sos/sos-reticleMLR_ca.paa
new file mode 100644
index 0000000000..48bc7f4e00
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/sos/sos-reticleMLR_ca.paa differ
diff --git a/TO_MERGE/agm/Optics/data/tmr_optics_reticle100.p3d b/TO_MERGE/agm/Optics/data/tmr_optics_reticle100.p3d
new file mode 100644
index 0000000000..365c9d5555
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/tmr_optics_reticle100.p3d differ
diff --git a/TO_MERGE/agm/Optics/data/tmr_optics_reticle70.p3d b/TO_MERGE/agm/Optics/data/tmr_optics_reticle70.p3d
new file mode 100644
index 0000000000..cbb0181a17
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/tmr_optics_reticle70.p3d differ
diff --git a/TO_MERGE/agm/Optics/data/tmr_optics_reticle80.p3d b/TO_MERGE/agm/Optics/data/tmr_optics_reticle80.p3d
new file mode 100644
index 0000000000..cbb0181a17
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/tmr_optics_reticle80.p3d differ
diff --git a/TO_MERGE/agm/Optics/data/tmr_optics_reticle90.p3d b/TO_MERGE/agm/Optics/data/tmr_optics_reticle90.p3d
new file mode 100644
index 0000000000..c2f1ae5ceb
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/tmr_optics_reticle90.p3d differ
diff --git a/TO_MERGE/agm/Optics/data/tmr_reticle_clear.p3d b/TO_MERGE/agm/Optics/data/tmr_reticle_clear.p3d
new file mode 100644
index 0000000000..89a29d7d8c
Binary files /dev/null and b/TO_MERGE/agm/Optics/data/tmr_reticle_clear.p3d differ
diff --git a/TO_MERGE/agm/Optics/functions/fn_firedEH.sqf b/TO_MERGE/agm/Optics/functions/fn_firedEH.sqf
new file mode 100644
index 0000000000..1030fceefd
--- /dev/null
+++ b/TO_MERGE/agm/Optics/functions/fn_firedEH.sqf
@@ -0,0 +1,149 @@
+/*
+ * Original Author: Taosenai
+ * Adapted By: KoffeinFlummi
+ *
+ * Animates the scope when firing.
+ *
+ * Arguments:
+ * 0: Unit
+ * 1: Weapon
+ * 2: Muzzle
+ * 3: Mode
+ * 4: Ammo
+ * 5: Magazine
+ * 6: Projectile
+ *
+ * Return Value:
+ * None
+ */
+
+if (_this select 0 != AGM_player) exitwith {}; // Sanity check
+
+0 = _this spawn {
+  disableSerialization;
+
+  _weaponType = _this select 1;
+  _config = configFile >> "CfgWeapons" >> _weaponType;
+  _scope = uiNameSpace getVariable "AGM_Optics_Scope";
+
+  // @todo
+  _recoilMulti = getNumber (_config >> "tmr_smallarms_recoil_shakeMultiplier"); // Will be 0 if undefined
+
+  if (_recoilMulti == 0) then {
+    _recoilMulti = 1;
+  };
+  if (_recoilMulti > 2.6) then {
+    _recoilMulti = 2.6; // Don't get too high
+  };
+
+  // Reduce the reticle movement as the player drops into lower, supported stances.
+  _detectStance = (player selectionPosition "Neck" select 2);
+  if (_detectStance < 1.3) then {
+    _recoilMulti = _recoilMulti - 0.10;
+  };
+  if (_detectStance < 0.7) then {
+    _recoilMulti = _recoilMulti - 0.20;
+  };
+
+  // Reduce reticle movement if the player is rested (tmr_autorest).
+  if (player getVariable ["tmr_autorest_rested", false]) then {
+    _recoilMulti = _recoilMulti - 0.20;
+  };
+
+  // Reduce reticle movement if the player is deployed (tmr_autorest).
+  if (player getVariable ["tmr_autorest_deployed", false]) then {
+    _recoilMulti = _recoilMulti - 0.30;
+  };
+  _recoilMulti = 1;
+  // @endtodo
+
+
+  // Constants which determine how the scope recoils
+  _recoilScope = 0.03 * _recoilMulti + random 0.0015;
+  _recoilRing = 0.03 * _recoilMulti + random 0.0015;
+
+  _randomScopeShiftX = 0.005 * _recoilMulti - random 0.011;
+
+  _randomReticleShiftX = 0.0036 * _recoilMulti + random 0.0045; // Always tend up and right;
+  _randomReticleShiftY = -0.0046 * _recoilMulti - random 0.0055;
+
+  /////////
+  // Center everything
+
+  // getResolution select 4 should return the aspect ratio, but it's totally wrong
+  // for triple head displays. We'll compute it manually.
+  _aspectRatio = (getResolution select 0) / (getResolution select 1);
+
+  _reticleX = (SafeZoneXAbs + SafeZoneWAbs/2 - (SafeZoneWAbs / _aspectRatio)/2);
+  _reticleY = SafeZoneY;
+  _reticleW = SafeZoneWAbs / _aspectRatio;
+  _reticleH = SafeZoneH;
+
+  // Reticle
+  (_scope displayCtrl 1) ctrlSetPosition [_reticleX, _reticleY, _reticleW, _reticleH];
+  // Reticle night (illum)
+  (_scope displayCtrl 2) ctrlSetPosition [_reticleX, _reticleY, _reticleW, _reticleH];
+
+  _bodyX = (SafeZoneXAbs + SafeZoneWAbs/2 - (SafeZoneWAbs / _aspectRatio));
+  _bodyY = SafeZoneY - (SafeZoneH/2);
+  _bodyW = SafeZoneWAbs / _aspectRatio * 2;
+  _bodyH = SafeZoneH * 2;
+
+  // Body night
+  (_scope displayCtrl 5) ctrlSetPosition [_bodyX, _bodyY, _bodyW, _bodyH];
+  // Body
+  (_scope displayCtrl 6) ctrlSetPosition [_bodyX, _bodyY, _bodyW, _bodyH];
+
+  _centerDelay = 0.01;
+  (_scope displayCtrl 1) ctrlCommit _centerDelay;
+  (_scope displayCtrl 2) ctrlCommit _centerDelay;
+  (_scope displayCtrl 5) ctrlCommit _centerDelay;
+  (_scope displayCtrl 6) ctrlCommit _centerDelay;
+
+  /////////
+  // Create and commit recoil effect
+
+  // Move reticle
+
+  (_scope displayCtrl 1) ctrlSetPosition [_reticleX - (_recoilScope/2) + _randomReticleShiftX, _reticleY - (_recoilScope/2) + _randomReticleShiftY, _reticleW + _recoilScope, _reticleH + _recoilScope];
+  (_scope displayCtrl 2) ctrlSetPosition [_reticleX - (_recoilScope/2) + _randomReticleShiftX, _reticleY - (_recoilScope/2) + _randomReticleShiftY, _reticleW + _recoilScope, _reticleH + _recoilScope];
+
+  // Move body
+
+  (_scope displayCtrl 5) ctrlSetPosition [_bodyX - (_recoilScope/2) + _randomScopeShiftX, _bodyY - (_recoilScope/2), _bodyW + _recoilScope, _bodyH + _recoilScope];
+  (_scope displayCtrl 6) ctrlSetPosition [_bodyX - (_recoilScope/2) + _randomScopeShiftX, _bodyY - (_recoilScope/2), _bodyW + _recoilScope, _bodyH + _recoilScope];
+
+  _recoilDelay = 0.036;
+  _fa = false;
+  _cwm = currentWeaponMode player;
+  if (_cwm == "FullAuto" || _cwm == "manual" || _cwm == "Burst") then {
+    _recoilDelay =  getNumber (_config >> _cwm >> "reloadTime")/2.2;
+    _fa = true;
+  };
+  (_scope displayCtrl 1) ctrlCommit _recoilDelay;
+  (_scope displayCtrl 2) ctrlCommit _recoilDelay;
+  (_scope displayCtrl 5) ctrlCommit _recoilDelay;
+  (_scope displayCtrl 6) ctrlCommit _recoilDelay;
+
+  //////////////
+
+  waituntil {sleep 0.01; ctrlCommitted (_scope displayCtrl 6)};
+
+  //////////////
+
+  //////
+  // Bring them all back
+  (_scope displayCtrl 1) ctrlSetPosition [_reticleX, _reticleY, _reticleW, _reticleH];
+  (_scope displayCtrl 2) ctrlSetPosition [_reticleX, _reticleY, _reticleW, _reticleH];
+  (_scope displayCtrl 5) ctrlSetPosition [_bodyX, _bodyY, _bodyW, _bodyH];
+  (_scope displayCtrl 6) ctrlSetPosition [_bodyX, _bodyY, _bodyW, _bodyH];
+
+  _recenterDelay = 0.09;
+  if (_fa) then {
+    _recenterDelay =  getNumber (_config >> _cwm >> "reloadTime")/2.2;
+  };
+  (_scope displayCtrl 1) ctrlCommit _recenterDelay;
+  (_scope displayCtrl 2) ctrlCommit _recenterDelay;
+  (_scope displayCtrl 5) ctrlCommit _recenterDelay;
+  (_scope displayCtrl 6) ctrlCommit _recenterDelay;
+};
diff --git a/TO_MERGE/agm/Optics/functions/fn_hideScope.sqf b/TO_MERGE/agm/Optics/functions/fn_hideScope.sqf
new file mode 100644
index 0000000000..8b048f1bd7
--- /dev/null
+++ b/TO_MERGE/agm/Optics/functions/fn_hideScope.sqf
@@ -0,0 +1,19 @@
+/*
+ * Original Author: Taosenai
+ * Adapted By: KoffeinFlummi
+ *
+ * Hides the scope.
+ *
+ */
+
+private ["_scope"];
+
+((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 1) ctrlSetTextColor [1,1,1,0];
+((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 2) ctrlSetTextColor [1,1,1,0];
+((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 5) ctrlSetTextColor [1,1,1,0];
+((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 6) ctrlSetTextColor [1,1,1,0];
+
+((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 1) ctrlCommit 0;
+((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 2) ctrlCommit 0;
+((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 5) ctrlCommit 0;
+((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 6) ctrlCommit 0;
diff --git a/TO_MERGE/agm/Optics/functions/fn_initScope.sqf b/TO_MERGE/agm/Optics/functions/fn_initScope.sqf
new file mode 100644
index 0000000000..f2e078ebbe
--- /dev/null
+++ b/TO_MERGE/agm/Optics/functions/fn_initScope.sqf
@@ -0,0 +1,25 @@
+/*
+ * Original Author: Taosenai
+ * Adapted By: KoffeinFlummi
+ *
+ * Initializes the scope resources.
+ *
+ */
+
+private ["_display"];
+
+// Make sure we only cutRsc when the resource isn't already available
+if (isNil {uiNameSpace getVariable "AGM_Optics_Scope"} or {isNull (uiNameSpace getVariable "AGM_Optics_Scope")}) exitWith {
+  AGM_Optics_scopeRSC cutRsc ["AGM_Optics_Scope","PLAIN",0];
+  ((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 1) ctrlSetTextColor [1,1,1,0];
+  ((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 2) ctrlSetTextColor [1,1,1,0];
+  ((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 5) ctrlSetTextColor [1,1,1,0];
+  ((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 6) ctrlSetTextColor [1,1,1,0];
+
+  ((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 1) ctrlCommit 0;
+  ((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 2) ctrlCommit 0;
+  ((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 5) ctrlCommit 0;
+  ((uiNameSpace getVariable "AGM_Optics_Scope") displayCtrl 6) ctrlCommit 0;
+  True
+};
+False
diff --git a/TO_MERGE/agm/Optics/functions/fn_mainLoop.sqf b/TO_MERGE/agm/Optics/functions/fn_mainLoop.sqf
new file mode 100644
index 0000000000..e73b56db37
--- /dev/null
+++ b/TO_MERGE/agm/Optics/functions/fn_mainLoop.sqf
@@ -0,0 +1,94 @@
+/*
+ * Original Author: Taosenai
+ * Adapted By: KoffeinFlummi
+ *
+ * Monitors the RscInGameUI and displays the overlays when needed.
+ *
+ * Arguments:
+ * None
+ *
+ * Return Value:
+ * None
+ */
+
+if !(cameraOn == AGM_player && {alive AGM_player} && {!visibleMap} && {ctrlShown ((uinamespace getVariable "AGM_OpticsIGUI") displayCtrl 154)}) exitWith {
+  // Failed the state check, hide the scope if it's up
+  if (AGM_Optics_inScope) then {
+    // Hide the scope
+    AGM_Optics_inScope = false;
+    AGM_Optics_inScope_FOV = ([] call cba_fnc_getFOV) select 0;
+
+    [] call AGM_Optics_fnc_hideScope;
+  };
+};
+
+AGM_Optics_Camera setposATL (positioncameratoworld [0,0,0.4]);
+AGM_Optics_Camera camPrepareTarget (positioncameratoworld [0,0,50]);
+AGM_Optics_Camera camCommitPrepared 0;
+
+if (cameraView == "Gunner") then {
+  AGM_Optics_Camera camsetFOV 0.7;
+  AGM_Optics_Camera camcommit 0;
+} else {
+  AGM_Optics_Camera camsetFOV 0.01;
+  AGM_Optics_Camera camcommit 0;
+};
+
+private ["_optic", "_scope"];
+
+disableSerialization;
+
+// Get the name of the attached optic
+_optic = (primaryWeaponItems AGM_player) select 2;
+_scope = uiNameSpace getVariable "AGM_Optics_Scope";
+
+// Init the scope (if needed)
+[] call AGM_Optics_fnc_initScope;
+
+// Check if the optic has changed since we last drew it
+_doUpdateAllLayers = false;
+if (AGM_Optics_currentOptic != _optic) then {
+  AGM_Optics_currentOptic = _optic;
+  _doUpdateAllLayers = true;
+};
+
+// Check if Splendid Camera, unit switch, etc. has blanked out our displays for no good reason (grrr)
+if (ctrlText (_scope displayCtrl 1) == "") then {
+  _doUpdateAllLayers = true;
+};
+
+// Draw the correct layers (don't show them)
+if (_doUpdateAllLayers) then {
+  (_scope displayCtrl 1) ctrlSetText getText (configFile >> "CfgWeapons" >> _optic >> "AGM_Optics_reticle");
+  (_scope displayCtrl 2) ctrlSetText getText (configFile >> "CfgWeapons" >> _optic >> "AGM_Optics_reticleIllum");
+  (_scope displayCtrl 5) ctrlSetText getText (configFile >> "CfgWeapons" >> _optic >> "AGM_Optics_bodyNight");
+  (_scope displayCtrl 6) ctrlSetText getText (configFile >> "CfgWeapons" >> _optic >> "AGM_Optics_body");
+};
+
+// Stop processing if already in the scope view and FOV hasn't changed
+if (AGM_Optics_inScope) exitwith {};
+
+// Mark that we're in enhanced scope view
+AGM_Optics_inScope = true;
+
+// Calculate lighting
+_lighting = sunOrMoon; // 1 is day, 0 is night
+
+_nightOpacity = 1;
+_dayOpacity = (0 max moonIntensity * (1 - (0 max overcast)))/5;
+
+if (_lighting == 1) then {
+  _nightOpacity = 0;
+  _dayOpacity = 1;
+};
+
+// Apply lighting and make layers visible
+(_scope displayCtrl 1) ctrlSetTextColor [1,1,1,1];
+(_scope displayCtrl 2) ctrlSetTextColor [1,1,1,_nightOpacity];
+(_scope displayCtrl 5) ctrlSetTextColor [1,1,1,_nightOpacity];
+(_scope displayCtrl 6) ctrlSetTextColor [1,1,1,_dayOpacity];
+
+(_scope displayCtrl 1) ctrlCommit 0;
+(_scope displayCtrl 2) ctrlCommit 0;
+(_scope displayCtrl 5) ctrlCommit 0;
+(_scope displayCtrl 6) ctrlCommit 0;
diff --git a/addons/common/functions/fnc_progressBar.sqf b/addons/common/functions/fnc_progressBar.sqf
index 15fbab1e24..69b91237e6 100644
--- a/addons/common/functions/fnc_progressBar.sqf
+++ b/addons/common/functions/fnc_progressBar.sqf
@@ -1,24 +1,24 @@
 /*
-* Author: commy2, Glowbal, PabstMirror
-*
-* Draw progress bar and execute given function if succesful.
-* Finish/Failure/Conditional are all passed [_args, _elapsedTime, _totalTime, _errorCode]
-*
-* Argument:
-* 0: NUMBER - Total Time (in game "time" seconds)
-* 1: ARRAY - Arguments, passed to condition, fail and finish
-* 2: CODE or STRING - On Finish: Code called or STRING raised as event.
-* 3: CODE or STRING - On Failure: Code called or STRING raised as event.
-* 4: STRING - (Optional) Localized Title
-* 5: CODE - (Optional) Code to check each frame
-* 6: ARRAY - (Optional) Exceptions for checking EGVAR(common,canInteractWith)
-*
-* Return value:
-* Nothing
-*
-* Example:
-* [5, [], {Hint "Finished!"}, {hint "Failure!"}, "My Title"] call ace_common_fnc_progressBar
-*/
+ * Author: commy2, Glowbal, PabstMirror
+ *
+ * Draw progress bar and execute given function if succesful.
+ * Finish/Failure/Conditional are all passed [_args, _elapsedTime, _totalTime, _errorCode]
+ *
+ * Argument:
+ * 0: NUMBER - Total Time (in game "time" seconds)
+ * 1: ARRAY - Arguments, passed to condition, fail and finish
+ * 2: CODE or STRING - On Finish: Code called or STRING raised as event.
+ * 3: CODE or STRING - On Failure: Code called or STRING raised as event.
+ * 4: STRING - (Optional) Localized Title
+ * 5: CODE - (Optional) Code to check each frame
+ * 6: ARRAY - (Optional) Exceptions for checking EFUNC(common,canInteractWith)
+ *
+ * Return value:
+ * Nothing
+ *
+ * Example:
+ * [5, [], {Hint "Finished!"}, {hint "Failure!"}, "My Title"] call ace_common_fnc_progressBar
+ */
 
 #include "script_component.hpp"
 
@@ -36,65 +36,69 @@ createDialog QGVAR(ProgressBar_Dialog);
 (uiNamespace getVariable QGVAR(ctrlProgressBarTitle)) ctrlSetText _localizedTitle;
 
 if (GVAR(SettingProgressBarLocation) == 1) then {
-  private "_ctrlPos";
-  _ctrlPos =  [1 * (((safezoneW / safezoneH) min 1.2) / 40) + (safezoneX + (safezoneW - ((safezoneW / safezoneH) min 1.2))/2), 29 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25) + (safezoneY + (safezoneH - (((safezoneW / safezoneH) min 1.2) / 1.2))/2), 38 * (((safezoneW / safezoneH) min 1.2) / 40), 0.8 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)];
-  (uiNamespace getVariable QGVAR(ctrlProgressBar)) ctrlSetPosition _ctrlPos;
-  (uiNamespace getVariable QGVAR(ctrlProgressBarTitle)) ctrlSetPosition _ctrlPos;
-  (uiNamespace getVariable QGVAR(ctrlProgressBar)) ctrlCommit 0;
-  (uiNamespace getVariable QGVAR(ctrlProgressBarTitle)) ctrlCommit 0;
+    private "_ctrlPos";
+    _ctrlPos =  [1 * (((safezoneW / safezoneH) min 1.2) / 40) + (safezoneX + (safezoneW - ((safezoneW / safezoneH) min 1.2))/2), 29 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25) + (safezoneY + (safezoneH - (((safezoneW / safezoneH) min 1.2) / 1.2))/2), 38 * (((safezoneW / safezoneH) min 1.2) / 40), 0.8 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)];
+    (uiNamespace getVariable QGVAR(ctrlProgressBar)) ctrlSetPosition _ctrlPos;
+    (uiNamespace getVariable QGVAR(ctrlProgressBarTitle)) ctrlSetPosition _ctrlPos;
+    (uiNamespace getVariable QGVAR(ctrlProgressBar)) ctrlCommit 0;
+    (uiNamespace getVariable QGVAR(ctrlProgressBarTitle)) ctrlCommit 0;
 };
 
 
 _perFrameFunction = {
-  PARAMS_2(_parameters,_pfhID);
-  EXPLODE_8_PVT(_parameters,_args,_onFinish,_onFail,_condition,_player,_startTime,_totalTime,_exceptions);
-  private ["_elapsedTime", "_errorCode"];
+    PARAMS_2(_parameters,_pfhID);
+    EXPLODE_8_PVT(_parameters,_args,_onFinish,_onFail,_condition,_player,_startTime,_totalTime,_exceptions);
+    private ["_elapsedTime", "_errorCode"];
 
-  _elapsedTime = time - _startTime;
-  _errorCode = -1;
+    _elapsedTime = time - _startTime;
+    _errorCode = -1;
 
-  if (isNull (uiNamespace getVariable [QGVAR(ctrlProgressBar), controlNull])) then {
-    _errorCode = 1;
-  } else {
-    if (ACE_player != _player) then {
-      _errorCode = 2;
+    if (isNull (uiNamespace getVariable [QGVAR(ctrlProgressBar), controlNull])) then {
+        _errorCode = 1;
     } else {
-      if (!([_args, _elapsedTime, _totalTime, _errorCode] call _condition)) then {
-        _errorCode = 3;
-      } else {
-        if (!([_player, objNull, _exceptions] call EGVAR(common,canInteractWith))) then {
-          _errorCode = 4;
+        if (ACE_player != _player) then {
+            _errorCode = 2;
         } else {
-          if (_elapsedTime >= _totalTime) then {
-            _errorCode = 0;
-          };
+            if (!([_args, _elapsedTime, _totalTime, _errorCode] call _condition)) then {
+                _errorCode = 3;
+            } else {
+                if (!([_player, objNull, _exceptions] call EFUNC(common,canInteractWith))) then {
+                    _errorCode = 4;
+                } else {
+                    if (_elapsedTime >= _totalTime) then {
+                        _errorCode = 0;
+                    };
+                };
+            };
         };
-      };
     };
-  };
 
-  if (_errorCode != -1) then {
-    //Error or Success, close dialog and remove PFEH
-    closeDialog 0;
-    [_pfhID] call CBA_fnc_removePerFrameHandler;
+    if (_errorCode != -1) then {
+        //Error or Success, close dialog and remove PFEH
 
-    if (_errorCode == 0) then {
-      if ((typeName _onFinish) == (typeName "")) then {
-        [_onFinish, [_args, _elapsedTime, _totalTime, _errorCode]] call FUNC(localEvent);
-      } else {
-        [_args, _elapsedTime, _totalTime, _errorCode] call _onFinish;
-      };
+        //Only close dialog if it's the progressBar:
+        if (!isNull (uiNamespace getVariable [QGVAR(ctrlProgressBar), controlNull])) then {
+            closeDialog 0;
+        };
+        [_pfhID] call CBA_fnc_removePerFrameHandler;
+
+        if (_errorCode == 0) then {
+            if ((typeName _onFinish) == (typeName "")) then {
+                [_onFinish, [_args, _elapsedTime, _totalTime, _errorCode]] call FUNC(localEvent);
+            } else {
+                [_args, _elapsedTime, _totalTime, _errorCode] call _onFinish;
+            };
+        } else {
+            if ((typeName _onFail) == (typeName "")) then {
+                [_onFail, [_args, _elapsedTime, _totalTime, _errorCode]] call FUNC(localEvent);
+            } else {
+                [_args, _elapsedTime, _totalTime, _errorCode] call _onFail;
+            };
+        };
     } else {
-      if ((typeName _onFail) == (typeName "")) then {
-        [_onFail, [_args, _elapsedTime, _totalTime, _errorCode]] call FUNC(localEvent);
-      } else {
-        [_args, _elapsedTime, _totalTime, _errorCode] call _onFail;
-      };
+        //Update Progress Bar (ratio of elepased:total)
+        (uiNamespace getVariable QGVAR(ctrlProgressBar)) progressSetPosition (_elapsedTime / _totalTime);
     };
-  } else {
-    //Update Progress Bar (ratio of elepased:total)
-    (uiNamespace getVariable QGVAR(ctrlProgressBar)) progressSetPosition (_elapsedTime / _totalTime);
-  };
 };
 
 [_perFrameFunction, 0, [_args, _onFinish, _onFail, _condition, _player, time, _totalTime, _exceptions]] call CBA_fnc_addPerFrameHandler;
diff --git a/tools/ace_build_tool/pabstFrankensteinBuilder.py b/tools/ace_build_tool/pabstFrankensteinBuilder.py
new file mode 100644
index 0000000000..eb35f1c7e5
--- /dev/null
+++ b/tools/ace_build_tool/pabstFrankensteinBuilder.py
@@ -0,0 +1,776 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+# make.py
+# An Arma 3 addon build system
+
+###############################################################################
+
+# The MIT License (MIT)
+
+# Copyright (c) 2013-2014 Ryan Schultz 
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+###############################################################################
+
+__version__ = "0.3dev"
+
+import sys
+
+if sys.version_info[0] == 2:
+	print("Python 3 is required.")
+	sys.exit(1)
+
+import os
+import os.path
+import shutil
+import platform
+import glob
+import subprocess
+import hashlib
+import configparser
+import json
+import traceback
+
+if sys.platform == "win32":
+	import winreg
+
+###############################################################################
+# http://akiscode.com/articles/sha-1directoryhash.shtml
+# Copyright (c) 2009 Stephen Akiki
+# MIT License (Means you can do whatever you want with this)
+#  See http://www.opensource.org/licenses/mit-license.php
+# Error Codes:
+#   -1 -> Directory does not exist
+#   -2 -> General error (see stack traceback)
+def  get_directory_hash(directory):
+	directory_hash = hashlib.sha1()
+	if not os.path.exists (directory):
+		return -1
+	
+	try:
+		for root, dirs, files in os.walk(directory):
+			for names in files:
+				path = os.path.join(root, names)
+				try:
+					f = open(path, 'rb')
+				except:
+					# You can't open the file for some reason
+					f.close()
+					continue
+
+				while 1:
+					# Read file in as little chunks
+					buf = f.read(4096)
+					if not buf: break
+					new = hashlib.sha1(buf)
+					directory_hash.update(new.digest())
+				f.close()
+
+	except:
+		# Print the stack traceback
+		traceback.print_exc()
+		return -2
+
+	return directory_hash.hexdigest()
+
+# Copyright (c) André Burgaud
+# http://www.burgaud.com/bring-colors-to-the-windows-console-with-python/
+if sys.platform == "win32":
+	from ctypes import windll, Structure, c_short, c_ushort, byref
+
+	SHORT = c_short
+	WORD = c_ushort
+
+	class COORD(Structure):
+	  """struct in wincon.h."""
+	  _fields_ = [
+		("X", SHORT),
+		("Y", SHORT)]
+
+	class SMALL_RECT(Structure):
+	  """struct in wincon.h."""
+	  _fields_ = [
+		("Left", SHORT),
+		("Top", SHORT),
+		("Right", SHORT),
+		("Bottom", SHORT)]
+
+	class CONSOLE_SCREEN_BUFFER_INFO(Structure):
+	  """struct in wincon.h."""
+	  _fields_ = [
+		("dwSize", COORD),
+		("dwCursorPosition", COORD),
+		("wAttributes", WORD),
+		("srWindow", SMALL_RECT),
+		("dwMaximumWindowSize", COORD)]
+
+	# winbase.h
+	STD_INPUT_HANDLE = -10
+	STD_OUTPUT_HANDLE = -11
+	STD_ERROR_HANDLE = -12
+
+	# wincon.h
+	FOREGROUND_BLACK     = 0x0000
+	FOREGROUND_BLUE      = 0x0001
+	FOREGROUND_GREEN     = 0x0002
+	FOREGROUND_CYAN      = 0x0003
+	FOREGROUND_RED       = 0x0004
+	FOREGROUND_MAGENTA   = 0x0005
+	FOREGROUND_YELLOW    = 0x0006
+	FOREGROUND_GREY      = 0x0007
+	FOREGROUND_INTENSITY = 0x0008 # foreground color is intensified.
+
+	BACKGROUND_BLACK     = 0x0000
+	BACKGROUND_BLUE      = 0x0010
+	BACKGROUND_GREEN     = 0x0020
+	BACKGROUND_CYAN      = 0x0030
+	BACKGROUND_RED       = 0x0040
+	BACKGROUND_MAGENTA   = 0x0050
+	BACKGROUND_YELLOW    = 0x0060
+	BACKGROUND_GREY      = 0x0070
+	BACKGROUND_INTENSITY = 0x0080 # background color is intensified.
+
+	stdout_handle = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
+	SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute
+	GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo
+
+	def get_text_attr():
+	  """Returns the character attributes (colors) of the console screen
+	  buffer."""
+	  csbi = CONSOLE_SCREEN_BUFFER_INFO()
+	  GetConsoleScreenBufferInfo(stdout_handle, byref(csbi))
+	  return csbi.wAttributes
+
+	def set_text_attr(color):
+	  """Sets the character attributes (colors) of the console screen
+	  buffer. Color is a combination of foreground and background color,
+	  foreground and background intensity."""
+	  SetConsoleTextAttribute(stdout_handle, color)
+###############################################################################
+
+def find_bi_tools(work_drive):
+	"""Find BI tools."""
+
+	reg = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER)
+	try:
+		k = winreg.OpenKey(reg, r"Software\bohemia interactive\arma 3 tools")
+		arma3tools_path = winreg.QueryValueEx(k, "path")[0]
+		winreg.CloseKey(k)
+	except:
+		raise Exception("BadTools","Arma 3 Tools are not installed correctly or the P: drive needs to be created.")
+
+	addonbuilder_path = os.path.join(arma3tools_path, "AddonBuilder", "AddonBuilder.exe")
+	dssignfile_path = os.path.join(arma3tools_path, "DSSignFile", "DSSignFile.exe")
+	dscreatekey_path = os.path.join(arma3tools_path, "DSSignFile", "DSCreateKey.exe")
+
+	if os.path.isfile(addonbuilder_path) and os.path.isfile(dssignfile_path) and os.path.isfile(dscreatekey_path):
+		return [addonbuilder_path, dssignfile_path, dscreatekey_path]
+	else:
+		raise Exception("BadTools","Arma 3 Tools are not installed correctly or the P: drive needs to be created.")
+
+def find_depbo_tools():
+	"""Use registry entries to find DePBO-based tools."""
+
+	reg = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER)
+	try:
+		k = winreg.OpenKey(reg, r"Software\Mikero\pboProject")
+		try:
+			pboproject_path = winreg.QueryValueEx(k, "exe")[0]
+			winreg.CloseKey(k)
+			print("Found pboproject.")
+		except:
+			print_error("ERROR: Could not find pboProject.")
+
+		k = winreg.OpenKey(reg, r"Software\Mikero\rapify")
+		try:
+			rapify_path = winreg.QueryValueEx(k, "exe")[0]
+			winreg.CloseKey(k)
+			print("Found rapify.")
+		except:
+			print_error("Could not find rapify.")
+
+		k = winreg.OpenKey(reg, r"Software\Mikero\MakePbo")
+		try:
+			makepbo_path = winreg.QueryValueEx(k, "exe")[0]
+			winreg.CloseKey(k)
+			print("Found makepbo.")
+		except:
+			print_error("Could not find makepbo.")
+	except:
+		raise Exception("BadDePBO", "DePBO tools not installed correctly")
+
+	#Strip any quotations from the path due to a MikeRo tool bug which leaves a trailing space in some of its registry paths.
+	return [pboproject_path.strip('"'),rapify_path.strip('"'),makepbo_path.strip('"')]
+
+def color(color):
+	"""Set the color. Works on Win32 and normal terminals."""
+	if sys.platform == "win32":
+		if color == "green":
+			set_text_attr(FOREGROUND_GREEN | get_text_attr() & 0x0070 | FOREGROUND_INTENSITY)
+		elif color == "red":
+			set_text_attr(FOREGROUND_RED | get_text_attr() & 0x0070 | FOREGROUND_INTENSITY)
+		elif color == "blue":
+			set_text_attr(FOREGROUND_BLUE | get_text_attr() & 0x0070 | FOREGROUND_INTENSITY)
+		elif color == "reset":
+			set_text_attr(FOREGROUND_GREY | get_text_attr() & 0x0070)
+		elif color == "grey":
+			set_text_attr(FOREGROUND_GREY | get_text_attr() & 0x0070)
+	else :
+		if color == "green":
+			sys.stdout.write('\033[92m')
+		elif color == "red":
+			sys.stdout.write('\033[91m')
+		elif color == "blue":
+			sys.stdout.write('\033[94m')
+		elif color == "reset":
+			sys.stdout.write('\033[0m')
+
+def print_error(msg):
+	color("red")
+	print ("ERROR: " + msg)
+	color("reset")
+
+def print_green(msg):
+	color("green")
+	print(msg)
+	color("reset")
+
+def print_blue(msg):
+	color("blue")
+	print(msg)
+	color("reset")
+	
+def print_yellow(msg):
+	color("yellow")
+	print(msg)
+	color("reset")
+
+###############################################################################
+
+def main(argv):
+	"""Build an Arma addon suite in a directory from rules in a make.cfg file."""
+	print_blue(("\nmake.py for Arma, v" + __version__))
+
+	if sys.platform != "win32":
+		print_error("Non-Windows platform (Cygwin?). Please re-run from cmd.")
+		sys.exit(1)
+
+	reg = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER)
+	try:
+		k = winreg.OpenKey(reg, r"Software\bohemia interactive\arma 3 tools")
+		arma3tools_path = winreg.QueryValueEx(k, "path")[0]
+		winreg.CloseKey(k)
+	except:
+		raise Exception("BadTools","Arma 3 Tools are not installed correctly or the P: drive needs to be created.")
+	
+	# Default behaviors
+	test = False # Copy to Arma 3 directory?
+	arg_modules = False # Only build modules on command line?
+	make_release = False # Make zip file from the release?
+	release_version = 0 # Version of release
+	use_pboproject = True # Default to pboProject build tool
+	make_target = "DEFAULT" # Which section in make.cfg to use for the build
+	new_key = False # Make a new key and use it to sign?
+	quiet = False # Suppress output from build tool?
+
+	# Parse arguments
+	if "help" in argv or "-h" in argv or "--help" in argv:
+		print ("""
+make.py [help] [test] [force] [key <name>] [target <name>] [release <version>]
+        [module name] [module name] [...]
+
+test -- Copy result to Arma 3.
+release <version> -- Make archive with <version>.
+force -- Ignore cache and build all.
+target <name> -- Use rules in make.cfg under heading [<name>] rather than 
+   default [Make]
+key <name> -- Use key in working directory with <name> to sign. If it does not 
+   exist, create key.
+quiet -- Suppress command line output from build tool.
+
+If module names are specified, only those modules will be built.
+
+Examples:
+   make.py force test 
+      Build all modules (ignoring cache) and copy the mod folder to the Arma 3 
+      directory.
+   make.py mymodule_gun
+      Only build the module named 'mymodule_gun'.
+   make.py force key MyNewKey release 1.0
+      Build all modules (ignoring cache), sign them with NewKey, and pack them 
+      into a zip file for release with version 1.0.
+
+
+If a file called $NOBIN$ is found in the module directory, that module will not be binarized.
+
+See the make.cfg file for additional build options.
+""")
+		sys.exit(0)
+
+	if "force" in argv:
+		argv.remove("force")
+		force_build = True
+	else:
+		force_build = False
+
+	if "test" in argv:
+		test = True
+		argv.remove("test")
+
+	if "release" in argv:
+		make_release = True
+		release_version = argv[argv.index("release") + 1]
+		argv.remove(release_version)
+		argv.remove("release")
+
+	if "target" in argv:
+		make_target = argv[argv.index("target") + 1]
+		argv.remove("target")
+		argv.remove(make_target)
+		force_build = True
+
+	if "key" in argv:
+		new_key = True
+		key_name = argv[argv.index("key") + 1]
+		argv.remove("key")
+		argv.remove(key_name)
+
+	if "quiet" in argv:
+		quiet = True
+		argv.remove("quiet")
+
+	# Get the directory the make script is in.
+	make_root = os.path.dirname(os.path.realpath(__file__))
+	make_root_parent = os.path.abspath(os.path.join(os.getcwd(), os.pardir))
+	os.chdir(make_root)
+
+	cfg = configparser.ConfigParser();
+	try:
+		cfg.read(os.path.join(make_root, "make.cfg"))
+
+		# Project name (with @ symbol)
+		project = cfg.get(make_target, "project", fallback="@"+os.path.basename(os.getcwd()))
+
+		# Private key path
+		key = cfg.get(make_target, "key", fallback=None)
+
+		# Project prefix (folder path)
+		prefix = cfg.get(make_target, "prefix", fallback="")
+
+		# Should we autodetect modules on a complete build?
+		module_autodetect = cfg.getboolean(make_target, "module_autodetect", fallback=True)
+
+		# Manual list of modules to build for a complete build
+		modules = cfg.get(make_target, "modules", fallback=None)
+		# Parse it out
+		if modules:
+			modules = [x.strip() for x in modules.split(',')]
+		else:
+			modules = []
+		
+		# List of directories to ignore when detecting
+		ignore = [x.strip() for x in cfg.get(make_target, "ignore",  fallback="release").split(',')]
+
+		# BI Tools work drive on Windows
+		work_drive = cfg.get(make_target, "work_drive",  fallback="P:\\")
+
+		# Which build tool should we use?
+		build_tool = "pboproject"
+
+		# Release/build directory, relative to script dir
+		release_dir = cfg.get(make_target, "release_dir", fallback="release")
+
+		# Project PBO file prefix (files are renamed to prefix_name.pbo)
+		pbo_name_prefix = cfg.get(make_target, "pbo_name_prefix", fallback=None)
+		
+		# Project module Root 
+		module_root_parent = os.path.abspath(os.path.join(os.path.join(work_drive, prefix), os.pardir))
+		module_root = cfg.get(make_target, "module_root", fallback=os.path.join(make_root_parent, "addons"))
+		print_green ("module_root: " + module_root)
+		if (os.path.isdir(module_root)):
+			os.chdir(module_root)
+		else:
+			print_error ("Directory " + module_root + " does not exist.")
+			sys.exit()
+
+	except:
+		raise
+		print_error("Could not parse make.cfg.")
+		sys.exit(1)
+
+		
+		
+	# See if we have been given specific modules to build from command line.
+	if len(argv) > 1 and not make_release:
+		arg_modules = True
+		modules = argv[1:]
+
+	# Find the tools we need.
+	try:
+		tools = find_bi_tools(work_drive)
+		addonbuilder = tools[0]
+		dssignfile = tools[1]
+		dscreatekey = tools[2]
+
+	except:
+		print_error("Arma 3 Tools are not installed correctly or the P: drive has not been created.")
+		sys.exit(1)
+
+	if build_tool == "pboproject":
+		try:
+			depbo_tools = find_depbo_tools()
+			pboproject = depbo_tools[0]
+			rapifyTool = depbo_tools[1]
+			makepboTool = depbo_tools[2]
+		except:
+			raise
+			print_error("Could not find dePBO tools. Download the needed tools from: https://dev.withsix.com/projects/mikero-pbodll/files")
+			sys.exit(1)
+
+	# Try to open and deserialize build cache file.
+	try:
+		cache = {}
+		with open(os.path.join(make_root, "make.cache"), 'r') as f:
+			cache_raw = f.read()
+
+		cache = json.loads(cache_raw)
+
+	except:
+		print ("No cache found.")
+		cache = {}
+
+	# Get list of subdirs in make root.
+	dirs = next(os.walk(module_root))[1]
+
+	# Autodetect what directories to build.
+	if module_autodetect and not arg_modules:
+		modules = []
+		for path in dirs:
+			# Any dir that has a config.cpp in its root is an addon to build.
+			config_path = os.path.join(path, 'config.cpp')
+			if os.path.isfile(config_path) and not path in ignore:
+				modules.append(path)
+
+	# Make the key specified from command line if necessary.
+	if new_key:
+		if not os.path.isfile(os.path.join(module_root, key_name + ".biprivatekey")):
+			print_green("\nRequested key does not exist.")
+			ret = subprocess.call([dscreatekey, key_name]) # Created in make_root
+			if ret == 0:
+				print_blue("Created: " + os.path.join(module_root, key_name + ".biprivatekey"))
+			else:
+				print_error("Failed to create key!")
+
+			try:
+				print_blue("Copying public key to release directory.")
+
+				try:
+					os.makedirs(os.path.join(module_root, release_dir, "Keys"))
+				except:
+					pass
+
+				shutil.copyfile(os.path.join(module_root, key_name + ".bikey"), os.path.join(module_root, release_dir, "Keys", key_name + ".bikey"))
+
+			except:
+				raise
+				print_error("Could not copy key to release directory.")
+
+		else:
+			print_green("\nNOTE: Using key " + os.path.join(module_root, key_name + ".biprivatekey"))
+
+		key = os.path.join(module_root, key_name + ".biprivatekey")
+
+
+	# For each module, prep files and then build.
+	for module in modules:
+		print_green("\nMaking " + module + "-"*max(1, (60-len(module))))
+
+		# Cache check
+		if module in cache:
+			old_sha = cache[module]
+		else:
+			old_sha = ""
+
+		# Hash the module
+		new_sha = get_directory_hash(os.path.join(module_root, module))
+
+		# Check if it needs rebuilt
+		# print ("Hash:", new_sha)
+		if old_sha == new_sha:
+			if not force_build:
+				print("Module has not changed.")
+				# Skip everything else
+				continue
+
+		# Only do this if the project isn't stored directly on the work drive.
+		# Split the path at the drive name and see if they are on the same drive (usually P:)
+		if os.path.splitdrive(module_root)[0] != os.path.splitdrive(work_drive)[0]:
+			try:
+				# Remove old work drive version (ignore errors)
+				shutil.rmtree(os.path.join(work_drive, prefix, module), True)
+
+				# Copy module to the work drive
+				shutil.copytree(module, os.path.join(work_drive, prefix, module))
+
+			except:
+				raise
+				print_error("ERROR: Could not copy module to work drive. Does the module exist?")
+				input("Press Enter to continue...")
+				print("Resuming build...")
+				continue
+		else:
+			print("WARNING: Module is stored on work drive (" + work_drive + ").")
+
+		try:
+			# Remove the old pbo, key, and log
+			old = os.path.join(module_root, release_dir, project, "Addons", module) + "*"
+			files = glob.glob(old)
+			for f in files:
+				os.remove(f)
+
+			if pbo_name_prefix:
+				old = os.path.join(module_root, release_dir, project, "Addons", pbo_name_prefix+module) + "*"
+				files = glob.glob(old)
+				for f in files:
+					os.remove(f)
+		except:
+			raise
+			print_error("ERROR: Could not copy module to work drive. Does the module exist?")
+			input("Press Enter to continue...")
+			print("Resuming build...")
+			continue
+
+		# Build the module into a pbo
+		print_blue("Building: " + os.path.join(work_drive, prefix, module))
+		print_blue("Destination: " + os.path.join(module_root, release_dir, project, "Addons"))
+		
+		# Make destination folder (if needed)
+		try:
+			os.makedirs(os.path.join(module_root, release_dir, project, "Addons"))
+		except:
+			pass
+
+		# Run build tool
+		build_successful = False
+		if build_tool == "pboproject":
+			try:
+				#PABST: Convert config (run the macro'd config.cpp through CfgConvert twice to produce a de-macro'd cpp that pboProject can read without fucking up:
+				os.chdir("P:\\CfgConvert")
+				shutil.copyfile(os.path.join(work_drive, prefix, module, "config.cpp"), os.path.join(work_drive, prefix, module, "config.backup"))
+				print_green("\Pabst (double converting):" + "cfgConvertGUI.exe " + os.path.join(work_drive, prefix, module, "config.cpp"))
+				ret = subprocess.call(["cfgConvertGUI.exe", os.path.join(work_drive, prefix, module, "config.cpp")])
+				ret = subprocess.call(["cfgConvertGUI.exe", os.path.join(work_drive, prefix, module, "config.bin")])
+				        
+				# Call pboProject
+				os.chdir("P:\\")
+				
+				if os.path.isfile(os.path.join(work_drive, prefix, module, "$NOBIN$")):
+					print_green("$NOBIN$ Found. Proceeding with non-binarizing!")
+					cmd = [makepboTool, "-P","-A","-L","-N","-G", os.path.join(work_drive, prefix, module),os.path.join(module_root, release_dir, project,"Addons")]
+									
+				else:
+					cmd = [pboproject, "-P", os.path.join(work_drive, prefix, module), "+Engine=Arma3", "-S","+Noisy", "+X", "+Clean", "+Mod="+os.path.join(module_root, release_dir, project), "-Key"]
+
+				color("grey")
+				if quiet:
+					devnull = open(os.devnull, 'w')
+					ret = subprocess.call(cmd, stdout=devnull)
+					devnull.close()
+				else:
+					ret = subprocess.call(cmd)
+				color("reset")
+
+				if ret == 0:
+					print_green("pboProject return code == " + str(ret))
+					# Prettyprefix rename the PBO if requested.
+					if pbo_name_prefix:
+						try:
+							os.rename(os.path.join(module_root, release_dir, project, "Addons", module+".pbo"), os.path.join(module_root, release_dir, project, "Addons", pbo_name_prefix+module+".pbo"))
+						except:
+							raise
+							print_error("Could not rename built PBO with prefix.")
+					# Sign result
+					if key:
+						print("Signing with " + key + ".")
+						if pbo_name_prefix:
+							ret = subprocess.call([dssignfile, key, os.path.join(module_root, release_dir, project, "Addons", pbo_name_prefix + module + ".pbo")])
+						else:
+							ret = subprocess.call([dssignfile, key, os.path.join(module_root, release_dir, project, "Addons", module + ".pbo")])
+						
+						if ret == 0:
+							build_successful = True
+					else:
+						build_successful = True
+						
+				if not build_successful:
+					print_error("pboProject return code == " + str(ret))
+					print_error("Module not successfully built/signed.")
+					#input("Press Enter to continue...")
+					print ("Resuming build...")
+					continue
+
+				#PABST: cleanup config BS (you could comment this out to see the "de-macroed" cpp
+				print_green("\Pabst (restoring): " + os.path.join(work_drive, prefix, module, "config.cpp"))
+				os.remove(os.path.join(work_drive, prefix, module, "config.cpp"))
+				os.remove(os.path.join(work_drive, prefix, module, "config.bin"))
+				os.rename(os.path.join(work_drive, prefix, module, "config.backup"), os.path.join(work_drive, prefix, module, "config.cpp"))
+ 
+				# Back to the root	
+				os.chdir(module_root)
+
+			except:
+				raise
+				print_error("Could not run Addon Builder.")
+				input("Press Enter to continue...")
+				print ("Resuming build...")
+				continue
+
+		elif build_tool== "addonbuilder":
+			# Detect $NOBIN$ and do not binarize if found.
+			if os.path.isfile(os.path.join(work_drive, prefix, module, "$NOBIN$")):
+				do_binarize = False
+				print("$NOBIN$ file found in module, packing only.")
+			else:
+				do_binarize = True
+			try:
+				# Call AddonBuilder
+				os.chdir("P:\\")
+
+				cmd = [addonbuilder, os.path.join(work_drive, prefix, module), os.path.join(make_root, release_dir, project, "Addons"), "-clear", "-project="+work_drive]
+				if not do_binarize:
+					cmd.append("-packonly")
+
+				if quiet:
+					previousDirectory = os.getcwd()
+					os.chdir(arma3tools_path)
+					devnull = open(os.devnull, 'w')
+					ret = subprocess.call(cmd, stdout=devnull)
+					devnull.close()
+					os.chdir(previousDirectory)
+				else:
+					previousDirectory = os.getcwd()
+					os.chdir(arma3tools_path)
+					print_error("Current directory - " + os.getcwd())
+					ret = subprocess.call(cmd)
+					os.chdir(previousDirectory)
+					print_error("Current directory - " + os.getcwd())
+				color("reset")
+				print_green("completed")
+				# Prettyprefix rename the PBO if requested.
+				if pbo_name_prefix:
+					try:
+						os.rename(os.path.join(make_root, release_dir, project, "Addons", module+".pbo"), os.path.join(make_root, release_dir, project, "Addons", pbo_name_prefix+module+".pbo"))
+					except:
+						raise
+						print_error("Could not rename built PBO with prefix.")
+				
+				if ret == 0:
+					# Sign result
+					if key:
+						print("Signing with " + key + ".")
+						if pbo_name_prefix:
+							ret = subprocess.call([dssignfile, key, os.path.join(make_root, release_dir, project, "Addons", pbo_name_prefix + module + ".pbo")])
+						else:
+							ret = subprocess.call([dssignfile, key, os.path.join(make_root, release_dir, project, "Addons", module + ".pbo")])
+						
+						if ret == 0:
+							build_successful = True
+					else:
+						build_successful = True
+
+				if not build_successful:
+					print_error("Module not successfully built.")
+
+				# Back to the root	
+				os.chdir(make_root)
+
+			except:
+				raise
+				print_error("Could not run Addon Builder.")
+				input("Press Enter to continue...")
+				print ("Resuming build...")
+				continue
+			
+		else:
+			print_error("Unknown build_tool " + build_tool + "!")
+
+		# Update the hash for a successfully built module
+		if build_successful:
+			cache[module] = new_sha
+
+	# Done building all modules!
+
+	# Write out the cache state
+	cache_out = json.dumps(cache)
+	with open(os.path.join(make_root, "make.cache"), 'w') as f:
+		f.write(cache_out)
+
+	# Delete the pboproject temp files if building a release.
+	if make_release and build_tool == "pboproject":
+		try:
+			shutil.rmtree(os.path.join(module_root, release_dir, project, "temp"), True)
+		except:
+			print_error("ERROR: Could not delete pboProject temp files.")
+
+	print_green("\nDone.")
+
+	# Make release
+	if make_release:
+		print_blue("\nMaking release: " + project + "-" + release_version + ".zip")
+		
+		try:
+			# Delete all log files
+			for root, dirs, files in os.walk(os.path.join(module_root, release_dir, project, "Addons")):
+				for currentFile in files:
+					if currentFile.lower().endswith("log"):
+						os.remove(os.path.join(root, currentFile))
+
+			# Create a zip with the contents of release/ in it
+			shutil.make_archive(project + "-" + release_version, "zip", os.path.join(module_root, release_dir))
+		except:
+			raise
+			print_error("Could not make release.")
+
+	# Copy to Arma 3 folder for testing
+	if test:
+		print_blue("\nCopying to Arma 3.")
+
+		if sys.platform == "win32":
+			reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
+			try:
+				k = winreg.OpenKey(reg, r"SOFTWARE\Wow6432Node\Bohemia Interactive\Arma 3")
+				a3_path = winreg.EnumValue(k, 1)[1]
+				winreg.CloseKey(k)
+			except:
+				print_error("Could not find Arma 3's directory in the registry.")
+		else:
+			a3_path = cygwin_a3path
+
+		if os.path.exists(a3_path):
+			try:
+				shutil.rmtree(os.path.join(a3_path, project), True)
+				shutil.copytree(os.path.join(module_root, release_dir, project), os.path.join(a3_path, project))
+			except:
+				print_error("Could not copy files. Is Arma 3 running?")
+
+if __name__ == "__main__":
+	main(sys.argv)
+input("Press Enter to continue...")
\ No newline at end of file