use serde::{Deserialize, Serialize};
use crate::image::{Color, Image, SharedImage};
use crate::music::{Music, SharedMusic};
use crate::pyxel::Pyxel;
use crate::sound::{
SharedSound, Sound, SoundEffect, SoundNote, SoundSpeed, SoundTone, SoundVolume,
};
use crate::tilemap::{ImageSource, ImageTileCoord, SharedTilemap, Tilemap};
use crate::utils::{compress_vec2, expand_vec2, trim_empty_vecs};
#[derive(Clone, Serialize, Deserialize)]
struct ImageData {
width: u32,
height: u32,
data: Vec<Vec<Color>>,
}
impl ImageData {
fn from_image(image: SharedImage) -> Self {
let image = image.lock();
let width = image.width();
let height = image.height();
let data: Vec<Vec<_>> = image
.canvas
.data
.chunks(width as usize)
.map(<[Color]>::to_vec)
.collect();
let data = compress_vec2(&data);
Self {
width,
height,
data,
}
}
fn to_image(&self) -> SharedImage {
let data = expand_vec2(&self.data, self.height as usize, self.width as usize);
let image = Image::new(self.width, self.height);
{
let mut image = image.lock();
image.canvas.data = data.into_iter().flatten().collect();
}
image
}
}
#[derive(Clone, Serialize, Deserialize)]
struct TilemapData {
width: u32,
height: u32,
imgsrc: u32,
data: Vec<Vec<ImageTileCoord>>,
}
impl TilemapData {
fn from_tilemap(tilemap: SharedTilemap) -> Self {
let tilemap = tilemap.lock();
let width = tilemap.width();
let height = tilemap.height();
let imgsrc = match tilemap.imgsrc {
ImageSource::Index(value) => value,
ImageSource::Image(_) => 0,
};
let data: Vec<_> = tilemap
.canvas
.data
.iter()
.flat_map(|(tx, ty)| [*tx, *ty].to_vec())
.collect();
let data: Vec<Vec<_>> = data
.chunks((width * 2) as usize)
.map(<[ImageTileCoord]>::to_vec)
.collect();
let data = compress_vec2(&data);
Self {
width,
height,
imgsrc,
data,
}
}
fn to_tilemap(&self) -> SharedTilemap {
let data = expand_vec2(&self.data, self.height as usize, (self.width * 2) as usize);
let tilemap = Tilemap::new(self.width, self.height, ImageSource::Index(self.imgsrc));
{
let mut tilemap = tilemap.lock();
let data: Vec<_> = data.clone().into_iter().flatten().collect();
tilemap.canvas.data = data.chunks(2).map(|chunk| (chunk[0], chunk[1])).collect();
}
tilemap
}
}
#[derive(Clone, Serialize, Deserialize)]
struct SoundData {
notes: Vec<SoundNote>,
tones: Vec<SoundTone>,
volumes: Vec<SoundVolume>,
effects: Vec<SoundEffect>,
speed: SoundSpeed,
}
impl SoundData {
fn from_sound(sound: SharedSound) -> Self {
let sound = sound.lock();
Self {
notes: sound.notes.clone(),
tones: sound.tones.clone(),
volumes: sound.volumes.clone(),
effects: sound.effects.clone(),
speed: sound.speed,
}
}
fn to_sound(&self) -> SharedSound {
let sound = Sound::new();
{
let mut sound = sound.lock();
sound.notes.clone_from(&self.notes);
sound.tones.clone_from(&self.tones);
sound.volumes.clone_from(&self.volumes);
sound.effects.clone_from(&self.effects);
sound.speed = self.speed;
}
sound
}
}
#[derive(Clone, Serialize, Deserialize)]
struct MusicData {
seqs: Vec<Vec<u32>>,
}
impl MusicData {
fn from_music(music: SharedMusic) -> Self {
let music = music.lock();
let seqs: Vec<_> = music.seqs.iter().map(|seq| seq.lock().clone()).collect();
let seqs = trim_empty_vecs(&seqs);
Self { seqs }
}
fn to_music(&self) -> SharedMusic {
let seqs = trim_empty_vecs(&self.seqs);
let music = Music::new();
{
let mut music = music.lock();
music.seqs = seqs
.iter()
.map(|seq| new_shared_type!(seq.clone()))
.collect();
}
music
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct ResourceData {
pub format_version: u32,
images: Vec<ImageData>,
tilemaps: Vec<TilemapData>,
sounds: Vec<SoundData>,
musics: Vec<MusicData>,
}
impl ResourceData {
pub fn from_toml(toml_text: &str) -> Self {
toml::from_str(toml_text).unwrap()
}
pub fn from_runtime(pyxel: &Pyxel) -> Self {
let mut resource_data = ResourceData {
format_version: 1, images: Vec::new(),
tilemaps: Vec::new(),
sounds: Vec::new(),
musics: Vec::new(),
};
for image in &*pyxel.images.lock() {
resource_data
.images
.push(ImageData::from_image(image.clone()));
}
for tilemap in &*pyxel.tilemaps.lock() {
resource_data
.tilemaps
.push(TilemapData::from_tilemap(tilemap.clone()));
}
for sound in &*pyxel.sounds.lock() {
resource_data
.sounds
.push(SoundData::from_sound(sound.clone()));
}
for music in &*pyxel.musics.lock() {
resource_data
.musics
.push(MusicData::from_music(music.clone()));
}
resource_data
}
pub fn to_runtime(
&self,
pyxel: &Pyxel,
exclude_images: bool,
exclude_tilemaps: bool,
exclude_sounds: bool,
exclude_musics: bool,
) {
if !exclude_images && !self.images.is_empty() {
let mut images = Vec::new();
for image_data in &self.images {
images.push(image_data.to_image());
}
*pyxel.images.lock() = images;
}
if !exclude_tilemaps && !self.tilemaps.is_empty() {
let mut tilemaps = Vec::new();
for tilemap_data in &self.tilemaps {
tilemaps.push(tilemap_data.to_tilemap());
}
*pyxel.tilemaps.lock() = tilemaps;
}
if !exclude_sounds && !self.sounds.is_empty() {
let mut sounds = Vec::new();
for sound_data in &self.sounds {
sounds.push(sound_data.to_sound());
}
*pyxel.sounds.lock() = sounds;
}
if !exclude_musics && !self.musics.is_empty() {
let mut musics = Vec::new();
for music_data in &self.musics {
musics.push(music_data.to_music());
}
*pyxel.musics.lock() = musics;
}
}
pub fn to_toml(
&self,
exclude_images: bool,
exclude_tilemaps: bool,
exclude_sounds: bool,
exclude_musics: bool,
) -> String {
let mut resource_data = (*self).clone();
if exclude_images {
resource_data.images.clear();
}
if exclude_tilemaps {
resource_data.tilemaps.clear();
}
if exclude_sounds {
resource_data.sounds.clear();
}
if exclude_musics {
resource_data.musics.clear();
}
toml::to_string(&resource_data).unwrap()
}
}