Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/asset/asset_decompression.rs
9353 views
1
//! Implements loader for a Gzip compressed asset.
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
/// Possible errors that can be produced by [`GzAssetLoader`]
24
#[non_exhaustive]
25
#[derive(Debug, Error)]
26
enum GzAssetLoaderError {
27
/// An [IO](std::io) Error
28
#[error("Could not load asset: {0}")]
29
Io(#[from] std::io::Error),
30
/// An error caused when the asset path cannot be used to determine the uncompressed asset type.
31
#[error("Could not determine file path of uncompressed asset")]
32
IndeterminateFilePath,
33
/// An error caused by the internal asset loader.
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
// Now that we have decompressed the asset, let's pass it back to the
73
// context to continue loading
74
75
let mut reader = VecReader::new(bytes_uncompressed);
76
77
let uncompressed = load_context
78
.loader()
79
.with_unknown_type()
80
.immediate()
81
.with_reader(&mut reader)
82
.load(contained_path)
83
.await?;
84
85
Ok(GzAsset { uncompressed })
86
}
87
88
fn extensions(&self) -> &[&str] {
89
&["gz"]
90
}
91
}
92
93
#[derive(Component, Default)]
94
struct Compressed<T> {
95
compressed: Handle<GzAsset>,
96
_phantom: PhantomData<T>,
97
}
98
99
fn main() {
100
App::new()
101
.add_plugins(DefaultPlugins)
102
.init_asset::<GzAsset>()
103
.init_asset_loader::<GzAssetLoader>()
104
.add_systems(Startup, setup)
105
.add_systems(Update, decompress::<Sprite, Image>)
106
.run();
107
}
108
109
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
110
commands.spawn(Camera2d);
111
112
commands.spawn(Compressed::<Image> {
113
compressed: asset_server.load("data/compressed_image.png.gz"),
114
..default()
115
});
116
}
117
118
fn decompress<T: Component + From<Handle<A>>, A: Asset>(
119
mut commands: Commands,
120
asset_server: Res<AssetServer>,
121
mut compressed_assets: ResMut<Assets<GzAsset>>,
122
query: Query<(Entity, &Compressed<A>)>,
123
) {
124
for (entity, Compressed { compressed, .. }) in query.iter() {
125
let Some(GzAsset { uncompressed }) = compressed_assets.remove(compressed) else {
126
continue;
127
};
128
129
let uncompressed = uncompressed.take::<A>().unwrap();
130
131
commands
132
.entity(entity)
133
.remove::<Compressed<A>>()
134
.insert(T::from(asset_server.add(uncompressed)));
135
}
136
}
137
138