diff --git a/engine/physics/src/physics.rs b/engine/physics/src/physics.rs
index 3fd234022..0036a0441 100644
--- a/engine/physics/src/physics.rs
+++ b/engine/physics/src/physics.rs
@@ -982,10 +982,7 @@ impl PhysicsContext<'_>{
 impl PhysicsData{
 	/// use with caution, this is the only non-instruction way to mess with physics
 	pub fn generate_models(&mut self,map:&map::CompleteMap){
-		let mut modes=map.modes.clone();
-		for mode in &mut modes.modes{
-			mode.denormalize_data();
-		}
+		let mut modes=map.modes.clone().denormalize();
 		let mut used_contact_attributes=Vec::new();
 		let mut used_intersect_attributes=Vec::new();
 
diff --git a/lib/bsp_loader/src/bsp.rs b/lib/bsp_loader/src/bsp.rs
index 0cbf32b5c..afbd2ff6e 100644
--- a/lib/bsp_loader/src/bsp.rs
+++ b/lib/bsp_loader/src/bsp.rs
@@ -6,6 +6,7 @@ use strafesnet_common::{map,model,integer,gameplay_attributes};
 use strafesnet_deferred_loader::deferred_loader::{MeshDeferredLoader,RenderConfigDeferredLoader};
 use strafesnet_deferred_loader::mesh::Meshes;
 use strafesnet_deferred_loader::texture::{RenderConfigs,Texture};
+use strafesnet_common::gameplay_modes::{NormalizedMode,NormalizedModes,Mode,Stage};
 
 use crate::valve_transform;
 
@@ -268,15 +269,15 @@ pub fn convert<'a>(
 			color:glam::Vec4::W,
 		});
 
-		let first_stage=strafesnet_common::gameplay_modes::Stage::empty(model_id);
-		let main_mode=strafesnet_common::gameplay_modes::Mode::new(
+		let first_stage=Stage::empty(model_id);
+		let main_mode=Mode::new(
 			strafesnet_common::gameplay_style::StyleModifiers::source_bhop(),
 			model_id,
 			std::collections::HashMap::new(),
 			vec![first_stage],
 			std::collections::HashMap::new(),
 		);
-		modes_list.push(main_mode);
+		modes_list.push(NormalizedMode::new(main_mode));
 	}
 
 	PartialMap1{
@@ -284,7 +285,7 @@ pub fn convert<'a>(
 		world_meshes,
 		prop_models,
 		world_models,
-		modes:strafesnet_common::gameplay_modes::Modes::new(modes_list),
+		modes:NormalizedModes::new(modes_list),
 	}
 }
 
@@ -294,7 +295,7 @@ pub struct PartialMap1{
 	prop_models:Vec<model::Model>,
 	world_meshes:Vec<model::Mesh>,
 	world_models:Vec<model::Model>,
-	modes:strafesnet_common::gameplay_modes::Modes,
+	modes:NormalizedModes,
 }
 impl PartialMap1{
 	pub fn add_prop_meshes<'a>(
@@ -317,7 +318,7 @@ pub struct PartialMap2{
 	prop_models:Vec<model::Model>,
 	world_meshes:Vec<model::Mesh>,
 	world_models:Vec<model::Model>,
-	modes:strafesnet_common::gameplay_modes::Modes,
+	modes:NormalizedModes,
 }
 impl PartialMap2{
 	pub fn add_render_configs_and_textures(
diff --git a/lib/common/src/gameplay_modes.rs b/lib/common/src/gameplay_modes.rs
index 90735be00..42ee25043 100644
--- a/lib/common/src/gameplay_modes.rs
+++ b/lib/common/src/gameplay_modes.rs
@@ -214,19 +214,32 @@ impl Mode{
 	pub fn get_element(&self,model_id:ModelId)->Option<&StageElement>{
 		self.elements.get(&model_id)
 	}
+}
+
+#[derive(Clone)]
+pub struct NormalizedMode(Mode);
+impl NormalizedMode{
+	pub fn new(mode:Mode)->Self{
+		Self(mode)
+	}
+	pub fn into_inner(self)->Mode{
+		let Self(mode)=self;
+		mode
+	}
 	//TODO: put this in the SNF
-	pub fn denormalize_data(&mut self){
+	pub fn denormalize(self)->Mode{
+		let NormalizedMode(mut mode)=self;
 		//expand and index normalized data
-		self.zones.insert(self.start,Zone::Start);
-		for (stage_id,stage) in self.stages.iter().enumerate(){
-			self.elements.insert(stage.spawn,StageElement{
+		mode.zones.insert(mode.start,Zone::Start);
+		for (stage_id,stage) in mode.stages.iter().enumerate(){
+			mode.elements.insert(stage.spawn,StageElement{
 				stage_id:StageId(stage_id as u32),
 				force:false,
 				behaviour:StageElementBehaviour::SpawnAt,
 				jump_limit:None,
 			});
 			for (_,&model) in &stage.ordered_checkpoints{
-				self.elements.insert(model,StageElement{
+				mode.elements.insert(model,StageElement{
 					stage_id:StageId(stage_id as u32),
 					force:false,
 					behaviour:StageElementBehaviour::Checkpoint,
@@ -234,7 +247,7 @@ impl Mode{
 				});
 			}
 			for &model in &stage.unordered_checkpoints{
-				self.elements.insert(model,StageElement{
+				mode.elements.insert(model,StageElement{
 					stage_id:StageId(stage_id as u32),
 					force:false,
 					behaviour:StageElementBehaviour::Checkpoint,
@@ -242,12 +255,13 @@ impl Mode{
 				});
 			}
 		}
+		mode
 	}
 }
 
 #[derive(Default,Clone)]
 pub struct Modes{
-	pub modes:Vec<Mode>,
+	modes:Vec<Mode>,
 }
 impl Modes{
 	pub const fn new(modes:Vec<Mode>)->Self{
@@ -266,6 +280,31 @@ impl Modes{
 	}
 }
 
+#[derive(Clone)]
+pub struct NormalizedModes{
+	modes:Vec<NormalizedMode>,
+}
+impl NormalizedModes{
+	pub fn new(modes:Vec<NormalizedMode>)->Self{
+		Self{modes}
+	}
+	pub fn len(&self)->usize{
+		self.modes.len()
+	}
+	pub fn denormalize(self)->Modes{
+		Modes{
+			modes:self.modes.into_iter().map(NormalizedMode::denormalize).collect(),
+		}
+	}
+}
+impl IntoIterator for NormalizedModes{
+	type Item=<Vec<NormalizedMode> as IntoIterator>::Item;
+	type IntoIter=<Vec<NormalizedMode> as IntoIterator>::IntoIter;
+	fn into_iter(self)->Self::IntoIter{
+		self.modes.into_iter()
+	}
+}
+
 
 #[derive(Default)]
 pub struct StageUpdate{
@@ -352,7 +391,7 @@ pub struct ModesBuilder{
 	stage_updates:Vec<(ModeId,StageId,StageUpdate)>,
 }
 impl ModesBuilder{
-	pub fn build(mut self)->Modes{
+	pub fn build_normalized(mut self)->NormalizedModes{
 		//collect modes and stages into contiguous arrays
 		let mut unique_modes:Vec<(ModeId,Mode)>
 		=self.modes.into_iter().collect();
@@ -412,7 +451,7 @@ impl ModesBuilder{
 				}
 			}
 		}
-		Modes::new(modes.into_iter().map(|mode_builder|mode_builder.mode).collect())
+		NormalizedModes::new(modes.into_iter().map(|mode_builder|NormalizedMode(mode_builder.mode)).collect())
 	}
 	pub fn insert_mode(&mut self,mode_id:ModeId,mode:Mode){
 		assert!(self.modes.insert(mode_id,mode).is_none(),"Cannot replace existing mode");
diff --git a/lib/common/src/map.rs b/lib/common/src/map.rs
index 3c4020c5e..6279ef2e3 100644
--- a/lib/common/src/map.rs
+++ b/lib/common/src/map.rs
@@ -4,7 +4,7 @@ use crate::gameplay_attributes;
 //this is a temporary struct to try to get the code running again
 //TODO: use snf::map::Region to update the data in physics and graphics instead of this
 pub struct CompleteMap{
-	pub modes:gameplay_modes::Modes,
+	pub modes:gameplay_modes::NormalizedModes,
 	pub attributes:Vec<gameplay_attributes::CollisionAttributes>,
 	pub meshes:Vec<model::Mesh>,
 	pub models:Vec<model::Model>,
diff --git a/lib/rbx_loader/src/rbx.rs b/lib/rbx_loader/src/rbx.rs
index 208394d0e..fbfc58b10 100644
--- a/lib/rbx_loader/src/rbx.rs
+++ b/lib/rbx_loader/src/rbx.rs
@@ -4,7 +4,7 @@ use crate::primitives;
 use strafesnet_common::aabb::Aabb;
 use strafesnet_common::map;
 use strafesnet_common::model;
-use strafesnet_common::gameplay_modes::{Mode,ModeId,ModeUpdate,Modes,ModesBuilder,Stage,StageElement,StageElementBehaviour,StageId,Zone};
+use strafesnet_common::gameplay_modes::{NormalizedModes,Mode,ModeId,ModeUpdate,ModesBuilder,Stage,StageElement,StageElementBehaviour,StageId,Zone};
 use strafesnet_common::gameplay_style;
 use strafesnet_common::gameplay_attributes as attr;
 use strafesnet_common::integer::{self,vec3,Planar64,Planar64Vec3,Planar64Mat3,Planar64Affine3};
@@ -922,7 +922,7 @@ impl PartialMap1<'_>{
 	PartialMap2{
 		meshes:self.primitive_meshes,
 		models,
-		modes:modes_builder.build(),
+		modes:modes_builder.build_normalized(),
 		attributes:unique_attributes,
 	}
 	}
@@ -931,7 +931,7 @@ impl PartialMap1<'_>{
 pub struct PartialMap2{
 	meshes:Vec<model::Mesh>,
 	models:Vec<model::Model>,
-	modes:Modes,
+	modes:NormalizedModes,
 	attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
 }
 impl PartialMap2{
diff --git a/lib/snf/src/map.rs b/lib/snf/src/map.rs
index 5661364b0..1f476c088 100644
--- a/lib/snf/src/map.rs
+++ b/lib/snf/src/map.rs
@@ -138,7 +138,7 @@ struct MapHeader{
 	//#[br(count=num_resources_external)]
 	//external_resources:Vec<ResourceExternalHeader>,
 	#[br(count=num_modes)]
-	modes:Vec<newtypes::gameplay_modes::Mode>,
+	modes:Vec<newtypes::gameplay_modes::NormalizedMode>,
 	#[br(count=num_attributes)]
 	attributes:Vec<newtypes::gameplay_attributes::CollisionAttributes>,
 	#[br(count=num_render_configs)]
@@ -181,7 +181,7 @@ fn read_texture<R:BinReaderExt>(file:&mut crate::file::File<R>,block_id:BlockId)
 pub struct StreamableMap<R:BinReaderExt>{
 	file:crate::file::File<R>,
 	//this includes every platform... move the unconstrained datas to their appropriate data block?
-	modes:gameplay_modes::Modes,
+	modes:gameplay_modes::NormalizedModes,
 	//this is every possible attribute... need some sort of streaming system
 	attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
 	//this is every possible render configuration... shaders and such... need streaming
@@ -223,7 +223,7 @@ impl<R:BinReaderExt> StreamableMap<R>{
 		}
 		Ok(Self{
 			file,
-			modes:strafesnet_common::gameplay_modes::Modes::new(modes),
+			modes:strafesnet_common::gameplay_modes::NormalizedModes::new(modes),
 			attributes,
 			render_configs,
 			bvh:strafesnet_common::bvh::generate_bvh(bvh),
@@ -430,13 +430,13 @@ pub fn write_map<W:BinWriterExt>(mut writer:W,map:strafesnet_common::map::Comple
 		num_spacial_blocks:spacial_blocks.len() as u32,
 		num_resource_blocks:resource_blocks.len() as u32,
 		//num_resources_external:0,
-		num_modes:map.modes.modes.len() as u32,
+		num_modes:map.modes.len() as u32,
 		num_attributes:map.attributes.len() as u32,
 		num_render_configs:map.render_configs.len() as u32,
 		spacial_blocks,
 		resource_blocks,
 		//external_resources:Vec::new(),
-		modes:map.modes.modes.into_iter().map(Into::into).collect(),
+		modes:map.modes.into_iter().map(Into::into).collect(),
 		attributes:map.attributes.into_iter().map(Into::into).collect(),
 		render_configs:map.render_configs.into_iter().map(Into::into).collect(),
 	};
diff --git a/lib/snf/src/newtypes/gameplay_modes.rs b/lib/snf/src/newtypes/gameplay_modes.rs
index 4363208c7..d84249623 100644
--- a/lib/snf/src/newtypes/gameplay_modes.rs
+++ b/lib/snf/src/newtypes/gameplay_modes.rs
@@ -157,7 +157,7 @@ pub struct ModeHeader{
 }
 #[binrw::binrw]
 #[brw(little)]
-pub struct Mode{
+pub struct NormalizedMode{
 	pub header:ModeHeader,
 	pub style:super::gameplay_style::StyleModifiers,
 	pub start:u32,
@@ -179,10 +179,10 @@ impl std::fmt::Display for ModeError{
 	}
 }
 impl std::error::Error for ModeError{}
-impl TryInto<strafesnet_common::gameplay_modes::Mode> for Mode{
+impl TryInto<strafesnet_common::gameplay_modes::NormalizedMode> for NormalizedMode{
 	type Error=ModeError;
-	fn try_into(self)->Result<strafesnet_common::gameplay_modes::Mode,Self::Error>{
-		Ok(strafesnet_common::gameplay_modes::Mode::new(
+	fn try_into(self)->Result<strafesnet_common::gameplay_modes::NormalizedMode,Self::Error>{
+		Ok(strafesnet_common::gameplay_modes::NormalizedMode::new(strafesnet_common::gameplay_modes::Mode::new(
 			self.style.try_into().map_err(ModeError::StyleModifier)?,
 			strafesnet_common::model::ModelId::new(self.start),
 			self.zones.into_iter().map(|(model_id,zone)|
@@ -192,12 +192,12 @@ impl TryInto<strafesnet_common::gameplay_modes::Mode> for Mode{
 			self.elements.into_iter().map(|(model_id,stage_element)|
 				Ok((strafesnet_common::model::ModelId::new(model_id),stage_element.try_into()?))
 			).collect::<Result<_,_>>().map_err(ModeError::StageElement)?,
-		))
+		)))
 	}
 }
-impl From<strafesnet_common::gameplay_modes::Mode> for Mode{
-	fn from(value:strafesnet_common::gameplay_modes::Mode)->Self{
-		let (style,start,zones,stages,elements)=value.into_inner();
+impl From<strafesnet_common::gameplay_modes::NormalizedMode> for NormalizedMode{
+	fn from(value:strafesnet_common::gameplay_modes::NormalizedMode)->Self{
+		let (style,start,zones,stages,elements)=value.into_inner().into_inner();
 		Self{
 			header:ModeHeader{
 				zones:zones.len() as u32,