| 1 |
|
| 2 |
|
| 3 |
use bevy::{ |
| 4 |
asset::{ |
| 5 |
io::{Reader, VecReader}, |
| 6 |
AssetLoader, ErasedLoadedAsset, LoadContext, LoadDirectError, |
| 7 |
}, |
| 8 |
prelude::*, |
| 9 |
reflect::TypePath, |
| 10 |
}; |
| 11 |
use flate2::read::GzDecoder; |
| 12 |
use std::{io::prelude::*, marker::PhantomData}; |
| 13 |
use thiserror::Error; |
| 14 |
|
| 15 |
#[derive(Asset, TypePath)] |
| 16 |
struct GzAsset { |
| 17 |
uncompressed: ErasedLoadedAsset, |
| 18 |
} |
| 19 |
|
| 20 |
#[derive(Default, TypePath)] |
| 21 |
struct GzAssetLoader; |
| 22 |
|
| 23 |
|
| 24 |
#[non_exhaustive] |
| 25 |
#[derive(Debug, Error)] |
| 26 |
enum GzAssetLoaderError { |
| 27 |
|
| 28 |
#[error("Could not load asset: {0}")] |
| 29 |
Io(#[from] std::io::Error), |
| 30 |
|
| 31 |
#[error("Could not determine file path of uncompressed asset")] |
| 32 |
IndeterminateFilePath, |
| 33 |
|
| 34 |
#[error("Could not load contained asset: {0}")] |
| 35 |
LoadDirectError(#[from] LoadDirectError), |
| 36 |
} |
| 37 |
|
| 38 |
impl AssetLoader for GzAssetLoader { |
| 39 |
type Asset = GzAsset; |
| 40 |
type Settings = (); |
| 41 |
type Error = GzAssetLoaderError; |
| 42 |
|
| 43 |
async fn load( |
| 44 |
&self, |
| 45 |
reader: &mut dyn Reader, |
| 46 |
_settings: &(), |
| 47 |
load_context: &mut LoadContext<'_>, |
| 48 |
) -> Result<Self::Asset, Self::Error> { |
| 49 |
let compressed_path = load_context.path(); |
| 50 |
let file_name = compressed_path |
| 51 |
.path() |
| 52 |
.file_name() |
| 53 |
.ok_or(GzAssetLoaderError::IndeterminateFilePath)? |
| 54 |
.to_string_lossy(); |
| 55 |
let uncompressed_file_name = file_name |
| 56 |
.strip_suffix(".gz") |
| 57 |
.ok_or(GzAssetLoaderError::IndeterminateFilePath)?; |
| 58 |
let contained_path = compressed_path |
| 59 |
.resolve_embed_str(uncompressed_file_name) |
| 60 |
.map_err(|_| GzAssetLoaderError::IndeterminateFilePath)?; |
| 61 |
|
| 62 |
let mut bytes_compressed = Vec::new(); |
| 63 |
|
| 64 |
reader.read_to_end(&mut bytes_compressed).await?; |
| 65 |
|
| 66 |
let mut decoder = GzDecoder::new(bytes_compressed.as_slice()); |
| 67 |
|
| 68 |
let mut bytes_uncompressed = Vec::new(); |
| 69 |
|
| 70 |
decoder.read_to_end(&mut bytes_uncompressed)?; |
| 71 |
|
| 72 |
|
| 73 |
|
| 74 |
|
| 75 |
let mut reader = VecReader::new(bytes_uncompressed); |
| 76 |
|
| 77 |
let uncompressed = load_context |
| 78 |
.load_builder() |
| 79 |
.load_untyped_value_from_reader(contained_path, &mut reader) |
| 80 |
.await?; |
| 81 |
|
| 82 |
Ok(GzAsset { uncompressed }) |
| 83 |
} |
| 84 |
|
| 85 |
fn extensions(&self) -> &[&str] { |
| 86 |
&["gz"] |
| 87 |
} |
| 88 |
} |
| 89 |
|
| 90 |
#[derive(Component, Default)] |
| 91 |
struct Compressed<T> { |
| 92 |
compressed: Handle<GzAsset>, |
| 93 |
_phantom: PhantomData<T>, |
| 94 |
} |
| 95 |
|
| 96 |
fn main() { |
| 97 |
App::new() |
| 98 |
.add_plugins(DefaultPlugins) |
| 99 |
.init_asset::<GzAsset>() |
| 100 |
.init_asset_loader::<GzAssetLoader>() |
| 101 |
.add_systems(Startup, setup) |
| 102 |
.add_systems(Update, decompress::<Sprite, Image>) |
| 103 |
.run(); |
| 104 |
} |
| 105 |
|
| 106 |
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { |
| 107 |
commands.spawn(Camera2d); |
| 108 |
|
| 109 |
commands.spawn(Compressed::<Image> { |
| 110 |
compressed: asset_server.load("data/compressed_image.png.gz"), |
| 111 |
..default() |
| 112 |
}); |
| 113 |
} |
| 114 |
|
| 115 |
fn decompress<T: Component + From<Handle<A>>, A: Asset>( |
| 116 |
mut commands: Commands, |
| 117 |
asset_server: Res<AssetServer>, |
| 118 |
mut compressed_assets: ResMut<Assets<GzAsset>>, |
| 119 |
query: Query<(Entity, &Compressed<A>)>, |
| 120 |
) { |
| 121 |
for (entity, Compressed { compressed, .. }) in query.iter() { |
| 122 |
let Some(GzAsset { uncompressed }) = compressed_assets.remove(compressed) else { |
| 123 |
continue; |
| 124 |
}; |
| 125 |
|
| 126 |
let uncompressed = uncompressed.take::<A>().unwrap(); |
| 127 |
|
| 128 |
commands |
| 129 |
.entity(entity) |
| 130 |
.remove::<Compressed<A>>() |
| 131 |
.insert(T::from(asset_server.add(uncompressed))); |
| 132 |
} |
| 133 |
} |