use casc_storage::types::CascConfig;
use casc_storage::{CascStorage, EKey, ProgressiveConfig, SizeHint};
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use tracing::{debug, info, warn};
struct TextureManager {
storage: Arc<tokio::sync::Mutex<CascStorage>>,
textures: HashMap<EKey, Arc<casc_storage::progressive::ProgressiveFile>>,
config: ProgressiveConfig,
}
impl TextureManager {
fn new(storage: CascStorage) -> Self {
let config = ProgressiveConfig {
chunk_size: 128 * 1024, max_prefetch_chunks: 2, chunk_timeout: std::time::Duration::from_secs(10),
use_predictive_prefetch: true,
min_progressive_size: 512 * 1024, };
let mut storage = storage;
storage.init_progressive_loading(config.clone());
Self {
storage: Arc::new(tokio::sync::Mutex::new(storage)),
textures: HashMap::new(),
config,
}
}
async fn load_texture_header(
&mut self,
ekey: &EKey,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
info!("Loading texture header for {}", ekey);
if let Some(progressive_file) = self.textures.get(ekey) {
debug!("Reusing existing texture stream");
return Ok(progressive_file.read(0, 148).await?);
}
let storage = self.storage.lock().await;
let size_hint = SizeHint::Unknown;
if !size_hint.should_use_progressive(&self.config) {
debug!("Texture is small, using regular read");
let data = storage.read(ekey)?;
return Ok(data[..148.min(data.len())].to_vec());
}
let progressive_file = storage.read_progressive(ekey, size_hint).await?;
let header = progressive_file.read(0, 148).await?;
drop(storage); self.textures.insert(*ekey, progressive_file);
Ok(header)
}
async fn load_texture_mipmap(
&mut self,
ekey: &EKey,
mipmap_offset: u64,
mipmap_size: usize,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
info!(
"Loading mipmap for {} (offset: {}, size: {})",
ekey, mipmap_offset, mipmap_size
);
if !self.textures.contains_key(ekey) {
let storage = self.storage.lock().await;
let size_hint = SizeHint::Unknown;
let progressive_file = storage.read_progressive(ekey, size_hint).await?;
drop(storage);
self.textures.insert(*ekey, progressive_file);
}
let progressive_file = self.textures.get(ekey).unwrap();
Ok(progressive_file.read(mipmap_offset, mipmap_size).await?)
}
async fn unload_texture(&mut self, ekey: &EKey) {
if self.textures.remove(ekey).is_some() {
info!("Unloaded texture {}", ekey);
}
}
async fn get_statistics(&self) -> HashMap<EKey, casc_storage::progressive::LoadingStats> {
let mut stats = HashMap::new();
for (ekey, file) in &self.textures {
stats.insert(*ekey, file.get_stats().await);
}
stats
}
}
async fn simulate_texture_loading() -> Result<(), Box<dyn std::error::Error>> {
let data_path =
Path::new("/home/danielsreichenbach/Downloads/wow/1.13.2.31650.windows-win64/Data");
if !data_path.exists() {
warn!("Data path does not exist: {:?}", data_path);
return Ok(());
}
info!("Initializing texture manager");
let config = CascConfig {
data_path: data_path.to_path_buf(),
cache_size_mb: 100,
max_archive_size: 1024 * 1024 * 1024,
use_memory_mapping: true,
read_only: false,
};
let storage = CascStorage::new(config)?;
let mut texture_manager = TextureManager::new(storage);
let storage = texture_manager.storage.lock().await;
let files: Vec<EKey> = storage.get_all_ekeys().into_iter().take(5).collect();
drop(storage);
if files.is_empty() {
warn!("No files found to simulate texture loading");
return Ok(());
}
info!("Simulating texture streaming for {} files", files.len());
for ekey in &files {
match texture_manager.load_texture_header(ekey).await {
Ok(header) => {
info!("Loaded texture header: {} bytes", header.len());
}
Err(e) => {
warn!("Failed to load texture header: {}", e);
}
}
}
if let Some(ekey) = files.first() {
info!("\nSimulating mipmap loading for texture {}", ekey);
for level in 0..3 {
let offset = 148 + (level * 1024); let size = 1024;
match texture_manager
.load_texture_mipmap(ekey, offset, size)
.await
{
Ok(data) => {
info!("Loaded mipmap level {}: {} bytes", level, data.len());
}
Err(e) => {
warn!("Failed to load mipmap level {}: {}", level, e);
}
}
}
}
info!("\n--- Texture Streaming Statistics ---");
let stats = texture_manager.get_statistics().await;
let total_memory: u64 = stats.values().map(|s| s.bytes_loaded).sum();
let total_chunks: usize = stats.values().map(|s| s.chunks_loaded).sum();
info!("Total textures loaded: {}", stats.len());
info!("Total memory used: {} MB", total_memory / (1024 * 1024));
info!("Total chunks loaded: {}", total_chunks);
for (ekey, stat) in stats {
let cache_ratio =
stat.cache_hits as f64 / (stat.cache_hits + stat.cache_misses).max(1) as f64;
info!(
" Texture {}: {} KB loaded, {:.1}% cache hit rate",
ekey,
stat.bytes_loaded / 1024,
cache_ratio * 100.0
);
}
info!("\nUnloading textures...");
for ekey in &files {
texture_manager.unload_texture(ekey).await;
}
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
simulate_texture_loading().await?;
info!("Texture streaming demo completed");
Ok(())
}