diff --git a/Cargo.lock b/Cargo.lock
index aa98a27..c87e690 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -142,6 +142,17 @@ version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
 
+[[package]]
+name = "binrw"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "173901312e9850391d4d7c1318c4e099fdc037d61870fca427429830efdb4e5f"
+dependencies = [
+ "array-init",
+ "binrw_derive 0.13.3",
+ "bytemuck",
+]
+
 [[package]]
 name = "binrw"
 version = "0.14.1"
@@ -149,10 +160,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7d4bca59c20d6f40c2cc0802afbe1e788b89096f61bdf7aeea6bf00f10c2909b"
 dependencies = [
  "array-init",
- "binrw_derive",
+ "binrw_derive 0.14.1",
  "bytemuck",
 ]
 
+[[package]]
+name = "binrw_derive"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb515fdd6f8d3a357c8e19b8ec59ef53880807864329b1cb1cba5c53bf76557e"
+dependencies = [
+ "either",
+ "owo-colors",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "binrw_derive"
 version = "0.14.1"
@@ -1959,7 +1983,7 @@ version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "36372fd7feb6d3c5780d2ada39d1397be9e196ddfbb23ba1d84e7a75cf790adb"
 dependencies = [
- "binrw",
+ "binrw 0.14.1",
  "lazy-regex",
 ]
 
@@ -2332,6 +2356,7 @@ dependencies = [
  "strafesnet_deferred_loader",
  "vbsp",
  "vmdl",
+ "vpk",
 ]
 
 [[package]]
@@ -2422,7 +2447,7 @@ dependencies = [
 name = "strafesnet_snf"
 version = "0.2.0"
 dependencies = [
- "binrw",
+ "binrw 0.14.1",
  "id",
  "strafesnet_common",
 ]
@@ -2704,7 +2729,7 @@ checksum = "f14a5685e0bb386aac9b9c6046a05152a46a0bc58d53afb3fbe577f1a1c2bb05"
 dependencies = [
  "ahash",
  "arrayvec",
- "binrw",
+ "binrw 0.14.1",
  "bitflags 2.8.0",
  "bv",
  "cgmath",
@@ -2755,6 +2780,17 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "vpk"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60ec10e731515f58d5494d472f027d9c6fc8500fcb790ff55751031bcad87b6b"
+dependencies = [
+ "ahash",
+ "binrw 0.13.3",
+ "thiserror 1.0.69",
+]
+
 [[package]]
 name = "walkdir"
 version = "2.5.0"
diff --git a/lib/bsp_loader/Cargo.toml b/lib/bsp_loader/Cargo.toml
index 971cc20..60b3dfe 100644
--- a/lib/bsp_loader/Cargo.toml
+++ b/lib/bsp_loader/Cargo.toml
@@ -15,3 +15,4 @@ strafesnet_common = { path = "../common", registry = "strafesnet" }
 strafesnet_deferred_loader = { path = "../deferred_loader" }
 vbsp = "0.6.0"
 vmdl = "0.2.0"
+vpk = "0.2.0"
diff --git a/lib/bsp_loader/src/lib.rs b/lib/bsp_loader/src/lib.rs
index c4e9f19..d160417 100644
--- a/lib/bsp_loader/src/lib.rs
+++ b/lib/bsp_loader/src/lib.rs
@@ -62,7 +62,7 @@ impl Bsp{
 	pub const fn new(value:vbsp::Bsp)->Self{
 		Self(value)
 	}
-	pub fn to_snf(&self,failure_mode:LoadFailureMode)->Result<strafesnet_common::map::CompleteMap,LoadError>{
+	pub fn to_snf(&self,failure_mode:LoadFailureMode,vpk_list:&[vpk::VPK])->Result<strafesnet_common::map::CompleteMap,LoadError>{
 		let mut texture_deferred_loader=RenderConfigDeferredLoader::new();
 		let mut mesh_deferred_loader=MeshDeferredLoader::new();
 
@@ -72,7 +72,7 @@ impl Bsp{
 			&mut mesh_deferred_loader,
 		);
 
-		let mut mesh_loader=loader::MeshLoader::new(self,&mut texture_deferred_loader);
+		let mut mesh_loader=loader::MeshLoader::new(self,vpk_list,&mut texture_deferred_loader);
 		let prop_meshes=mesh_deferred_loader.into_meshes(&mut mesh_loader,failure_mode).map_err(LoadError::Mesh)?;
 
 		let map_step2=map_step1.add_prop_meshes(prop_meshes);
diff --git a/lib/bsp_loader/src/loader.rs b/lib/bsp_loader/src/loader.rs
index bda2f29..dab5cbf 100644
--- a/lib/bsp_loader/src/loader.rs
+++ b/lib/bsp_loader/src/loader.rs
@@ -72,22 +72,40 @@ impl From<vbsp::BspError> for MeshError{
 	}
 }
 
-pub struct MeshLoader<'a,'b>{
+pub struct MeshLoader<'a,'b,'c>{
 	bsp:&'a Bsp,
+	vpks:&'c [vpk::VPK],
 	deferred_loader:&'b mut strafesnet_deferred_loader::deferred_loader::RenderConfigDeferredLoader<Cow<'a,str>>,
 }
-impl MeshLoader<'_,'_>{
-	pub fn new<'a,'b>(
+impl MeshLoader<'_,'_,'_>{
+	pub fn new<'a,'b,'c>(
 		bsp:&'a Bsp,
+		vpks:&'c [vpk::VPK],
 		deferred_loader:&'b mut strafesnet_deferred_loader::deferred_loader::RenderConfigDeferredLoader<Cow<'a,str>>,
-	)->MeshLoader<'a,'b>{
+	)->MeshLoader<'a,'b,'c>{
 		MeshLoader{
 			bsp,
+			vpks,
 			deferred_loader,
 		}
 	}
+	fn find(&self,path:&str)->Result<Option<Cow<[u8]>>,vbsp::BspError>{
+		// search bsp
+		if let Some(data)=self.bsp.as_ref().pack.get(path)?{
+			return Ok(Some(Cow::Owned(data)));
+		}
+
+		//search each vpk
+		for vpk in self.vpks{
+			if let Some(vpk_entry)=vpk.tree.get(path){
+				return Ok(Some(vpk_entry.get()?));
+			}
+		}
+
+		Ok(None)
+	}
 }
-impl<'a> Loader<vmdl::Model> for MeshLoader<'a,'_>{
+impl<'a> Loader<vmdl::Model> for MeshLoader<'a,'_,'_>{
 	type Error=MeshError;
 	type Index=&'a str;
 	fn load(&mut self,index:Self::Index)->Result<vmdl::Model,Self::Error>{
@@ -99,10 +117,9 @@ impl<'a> Loader<vmdl::Model> for MeshLoader<'a,'_>{
 		vvd_path.set_extension("vvd");
 		vtx_path.set_extension("dx90.vtx");
 		// TODO: search more packs, possibly using an index of multiple packs
-		let bsp=self.bsp.as_ref();
-		let mdl=bsp.pack.get(mdl_path_lower.as_str())?.ok_or(MeshError::MissingMdl)?;
-		let vtx=bsp.pack.get(vtx_path.as_os_str().to_str().unwrap())?.ok_or(MeshError::MissingVtx)?;
-		let vvd=bsp.pack.get(vvd_path.as_os_str().to_str().unwrap())?.ok_or(MeshError::MissingVvd)?;
+		let mdl=self.find(mdl_path_lower.as_str())?.ok_or(MeshError::MissingMdl)?;
+		let vtx=self.find(vtx_path.as_os_str().to_str().unwrap())?.ok_or(MeshError::MissingVtx)?;
+		let vvd=self.find(vvd_path.as_os_str().to_str().unwrap())?.ok_or(MeshError::MissingVvd)?;
 		Ok(vmdl::Model::from_parts(
 			vmdl::mdl::Mdl::read(mdl.as_ref())?,
 			vmdl::vtx::Vtx::read(vtx.as_ref())?,
@@ -111,7 +128,7 @@ impl<'a> Loader<vmdl::Model> for MeshLoader<'a,'_>{
 	}
 }
 
-impl<'a> Loader<Mesh> for MeshLoader<'a,'_>{
+impl<'a> Loader<Mesh> for MeshLoader<'a,'_,'_>{
 	type Error=MeshError;
 	type Index=&'a str;
 	fn load(&mut self,index:Self::Index)->Result<Mesh,Self::Error>{
diff --git a/strafe-client/src/file.rs b/strafe-client/src/file.rs
index 6fb2bd6..d27d70a 100644
--- a/strafe-client/src/file.rs
+++ b/strafe-client/src/file.rs
@@ -105,7 +105,7 @@ pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<LoadFormat,LoadError>{
 		},
 		#[cfg(feature="source")]
 		ReadFormat::Source(bsp)=>Ok(LoadFormat::Map(
-			bsp.to_snf(LoadFailureMode::DefaultToNone).map_err(LoadError::LoadSource)?
+			bsp.to_snf(LoadFailureMode::DefaultToNone,&[]).map_err(LoadError::LoadSource)?
 		)),
 	}
 }