Index: code/mission/missioncampaign.cpp
===================================================================
--- code/mission/missioncampaign.cpp	(revision 10778)
+++ code/mission/missioncampaign.cpp	(working copy)
@@ -928,10 +928,25 @@
 		return 0;
 
 	Campaign.current_mission = Campaign.prev_mission;
+	Campaign.prev_mission = -1;
 	Campaign.next_mission = Campaign.current_mission;
 	Campaign.num_missions_completed--;
 	Campaign.missions[Campaign.next_mission].completed = 0;
 
+	if (Campaign.num_variables > 0) {
+		vm_free( Campaign.variables );
+	}
+
+	Campaign.num_variables = Campaign.redalert_num_variables;
+
+	// copy backed up variables over
+	if (Campaign.redalert_num_variables > 0) {
+		Assert( Campaign.redalert_variables );
+
+		Campaign.variables = (sexp_variable *) vm_malloc(Campaign.num_variables * sizeof(sexp_variable));
+		memcpy(Campaign.variables, Campaign.redalert_variables, Campaign.redalert_num_variables * sizeof(sexp_variable));
+	}
+
 	Pilot.save_savefile();
 
 	// reset the player stats to be the stats from this level
@@ -983,10 +998,10 @@
 /**
  * Store mission's goals and events in Campaign struct
  */
-void mission_campaign_store_goals_and_events_and_variables()
+void mission_campaign_store_goals_and_events()
 {
 	char *name;
-	int cur, i, j;
+	int cur, i;
 	cmission *mission;
 
 	cur = Campaign.current_mission;
@@ -1058,7 +1073,16 @@
 		} else
 			Int3();
 	}
+}
 
+void mission_campaign_store_variables()
+{
+	int cur, i, j;
+	cmission *mission;
+
+	cur = Campaign.current_mission;
+	mission = &Campaign.missions[cur];
+
 	// Goober5000 - handle campaign-persistent variables -------------------------------------
 	if (mission->variables != NULL) {
 		vm_free( mission->variables );
@@ -1113,9 +1137,18 @@
 		sexp_variable *n_variables = (sexp_variable *) vm_malloc(total_variables * sizeof(sexp_variable));
 		Assert( n_variables );
 
+		if (Campaign.redalert_num_variables > 0) {
+			vm_free( Campaign.redalert_variables );
+		}
+
+		Campaign.redalert_num_variables = Campaign.num_variables;
+
 		// copy existing variables over
 		if (Campaign.num_variables > 0) {
 			Assert( Campaign.variables );
+
+			Campaign.redalert_variables = (sexp_variable *) vm_malloc(Campaign.num_variables * sizeof(sexp_variable));
+			memcpy(Campaign.redalert_variables, Campaign.variables, Campaign.num_variables * sizeof(sexp_variable));
 			memcpy(n_variables, Campaign.variables, Campaign.num_variables * sizeof(sexp_variable));
 
 			variable_count = Campaign.num_variables;
@@ -1161,6 +1194,12 @@
 	// --------------------------------------------------------------------------
 }
 
+void mission_campaign_store_goals_and_events_and_variables()
+{
+	mission_campaign_store_goals_and_events();
+	mission_campaign_store_variables();
+}
+
 /**
  * Called when the player's mission is over.  It updates the internal store of goals
  * and their status then saves the state of the campaign in the campaign file.  
@@ -1350,6 +1389,11 @@
 		vm_free(Campaign.variables);
 		Campaign.variables = NULL;
 	}
+	Campaign.redalert_num_variables = 0;
+	if (Campaign.redalert_variables != NULL) {
+		vm_free(Campaign.redalert_variables);
+		Campaign.redalert_variables = NULL;
+	}
 }
 
 /**
Index: code/mission/missioncampaign.h
===================================================================
--- code/mission/missioncampaign.h	(revision 10778)
+++ code/mission/missioncampaign.h	(working copy)
@@ -126,6 +126,8 @@
 	cmission	missions[MAX_CAMPAIGN_MISSIONS];	// decription of the missions
 	int				num_variables;					// number of variables this campaign had - Goober5000
 	sexp_variable	*variables;						// malloced array of sexp_variables (of num_variables size) containing campaign-persistent variables - Goober5000
+	int				redalert_num_variables;			// These two variables hold the previous state of the above for restoration
+	sexp_variable	*redalert_variables;			// if you replay the previous mission in a Red Alert scenario. -MageKing17
 
 	campaign()
 		: desc(NULL), num_missions(0), variables(NULL)
@@ -242,6 +244,12 @@
 int mission_load_up_campaign( player *p = NULL );
 
 // stores mission goals and events in Campaign struct
+void mission_campaign_store_goals_and_events();
+
+// stores campaign-persistent variables
+void mission_campaign_store_variables();
+
+// does both of the above
 void mission_campaign_store_goals_and_events_and_variables();
 
 // evaluates next mission and possible loop mission
Index: code/missionui/missiondebrief.cpp
===================================================================
--- code/missionui/missiondebrief.cpp	(revision 10778)
+++ code/missionui/missiondebrief.cpp	(working copy)
@@ -1352,6 +1352,8 @@
 		// mission that isn't in a campaign.
 		if ( Game_mode & GM_CAMPAIGN_MODE ) {
 
+			mission_campaign_store_variables();
+
 			// check for possible mission loop
 			// check for (1) mission loop available, (2) don't have to repeat last mission
 			if(!(Game_mode & GM_MULTIPLAYER)){
@@ -2032,7 +2034,7 @@
 	if ( (Game_mode & GM_CAMPAIGN_MODE) && ( !MULTIPLAYER_CLIENT )	) {
 		// MUST store goals and events first - may be used to evaluate next mission
 		// store goals and events
-		mission_campaign_store_goals_and_events_and_variables();
+		mission_campaign_store_goals_and_events();
 
 		// evaluate next mission
 		mission_campaign_eval_next_mission();
Index: code/pilotfile/csg.cpp
===================================================================
--- code/pilotfile/csg.cpp	(revision 10778)
+++ code/pilotfile/csg.cpp	(working copy)
@@ -1029,6 +1029,29 @@
 			cfread_string_len(Campaign.variables[idx].variable_name, TOKEN_LENGTH, cfp);
 		}
 	}
+
+	if (csg_ver < 4) { // CSG files before version 4 don't have a Red Alert set of CPVs to load, so just copy the regular set.
+		Campaign.redalert_num_variables = Campaign.num_variables;
+		Campaign.redalert_variables = (sexp_variable *) vm_malloc( Campaign.redalert_num_variables * sizeof(sexp_variable) );
+		Verify( Campaign.redalert_variables != NULL);
+
+		memcpy( Campaign.redalert_variables, Campaign.variables, Campaign.num_variables * sizeof(sexp_variable));
+	} else {
+		Campaign.redalert_num_variables = cfread_int(cfp);
+
+		if (Campaign.redalert_num_variables > 0) {
+			Campaign.redalert_variables = (sexp_variable *) vm_malloc( Campaign.redalert_num_variables * sizeof(sexp_variable) );
+			Verify( Campaign.redalert_variables != NULL );
+
+			memset( Campaign.redalert_variables, 0, Campaign.redalert_num_variables * sizeof(sexp_variable) );
+
+			for (idx = 0; idx < Campaign.redalert_num_variables; idx++) {
+				Campaign.redalert_variables[idx].type = cfread_int(cfp);
+				cfread_string_len(Campaign.redalert_variables[idx].text, TOKEN_LENGTH, cfp);
+				cfread_string_len(Campaign.redalert_variables[idx].variable_name, TOKEN_LENGTH, cfp);
+			}
+		}
+	}
 }
 
 void pilotfile::csg_write_variables()
@@ -1045,6 +1068,14 @@
 		cfwrite_string_len(Campaign.variables[idx].variable_name, cfp);
 	}
 
+	cfwrite_int(Campaign.redalert_num_variables, cfp);
+
+	for (idx = 0; idx < Campaign.redalert_num_variables; idx++) {
+		cfwrite_int(Campaign.redalert_variables[idx].type, cfp);
+		cfwrite_string_len(Campaign.redalert_variables[idx].text, cfp);
+		cfwrite_string_len(Campaign.redalert_variables[idx].variable_name, cfp);
+	}
+
 	endSection();
 }
 
@@ -1256,6 +1287,12 @@
 		Campaign.variables = NULL;
 	}
 
+	if (Campaign.redalert_variables) {
+		Campaign.redalert_num_variables = 0;
+		vm_free(Campaign.redalert_variables);
+		Campaign.redalert_num_variables = NULL;
+	}
+
 	// clear out mission stuff
 	for (idx = 0; idx < MAX_CAMPAIGN_MISSIONS; idx++) {
 		missionp = &Campaign.missions[idx];
Index: code/pilotfile/pilotfile.h
===================================================================
--- code/pilotfile/pilotfile.h	(revision 10778)
+++ code/pilotfile/pilotfile.h	(working copy)
@@ -23,7 +23,8 @@
 //   1 - re-add recent missions
 //   2 - separate single/multi squad name & pic
 //   3 - remove separate detail settings for campaigns
-static const ubyte CSG_VERSION = 3;
+//   4 - add CPV rollback for Red Alert missions
+static const ubyte CSG_VERSION = 4;
 
 
 class pilotfile {
