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