Apply dos2unix to rust sources
authorunc0rr
Tue, 04 Jun 2019 22:34:42 +0200
changeset 15141 febccab419b1
parent 15140 901751d3cd80
child 15142 1a43b570cbe4
Apply dos2unix to rust sources
rust/hedgewars-server/src/core.rs
rust/hedgewars-server/src/core/indexslab.rs
rust/hedgewars-server/src/handlers/checker.rs
rust/hedgewars-server/src/server/io.rs
rust/hwphysics/src/collision.rs
rust/hwphysics/src/common.rs
rust/hwphysics/src/grid.rs
rust/hwphysics/src/physics.rs
rust/lib-hedgewars-engine/src/render.rs
rust/lib-hedgewars-engine/src/render/atlas.rs
rust/lib-hedgewars-engine/src/render/camera.rs
rust/lib-hedgewars-engine/src/render/gear.rs
rust/mapgen/src/theme.rs
--- a/rust/hedgewars-server/src/core.rs	Tue Jun 04 23:19:18 2019 +0300
+++ b/rust/hedgewars-server/src/core.rs	Tue Jun 04 22:34:42 2019 +0200
@@ -1,5 +1,5 @@
-pub mod client;
-pub mod indexslab;
-pub mod room;
-pub mod server;
-pub mod types;
+pub mod client;
+pub mod indexslab;
+pub mod room;
+pub mod server;
+pub mod types;
--- a/rust/hedgewars-server/src/core/indexslab.rs	Tue Jun 04 23:19:18 2019 +0300
+++ b/rust/hedgewars-server/src/core/indexslab.rs	Tue Jun 04 22:34:42 2019 +0200
@@ -1,71 +1,71 @@
-use std::{
-    iter,
-    mem::replace,
-    ops::{Index, IndexMut},
-};
-
-pub struct IndexSlab<T> {
-    data: Vec<Option<T>>,
-}
-
-impl<T> IndexSlab<T> {
-    pub fn new() -> Self {
-        Self { data: Vec::new() }
-    }
-
-    pub fn with_capacity(capacity: usize) -> Self {
-        Self {
-            data: Vec::with_capacity(capacity),
-        }
-    }
-
-    pub fn insert(&mut self, index: usize, value: T) {
-        if index >= self.data.len() {
-            self.data.reserve(index - self.data.len() + 1);
-            self.data.extend((self.data.len()..index).map(|_| None));
-            self.data.push(Some(value))
-        } else {
-            self.data[index] = Some(value);
-        }
-    }
-
-    pub fn contains(&self, index: usize) -> bool {
-        self.data.get(index).and_then(|x| x.as_ref()).is_some()
-    }
-
-    pub fn remove(&mut self, index: usize) -> Option<T> {
-        if let Some(x) = self.data.get_mut(index) {
-            replace(x, None)
-        } else {
-            None
-        }
-    }
-
-    pub fn iter(&self) -> impl Iterator<Item = (usize, &T)> {
-        self.data
-            .iter()
-            .enumerate()
-            .filter_map(|(index, opt)| opt.as_ref().and_then(|x| Some((index, x))))
-    }
-
-    pub fn iter_mut(&mut self) -> impl Iterator<Item = (usize, &mut T)> {
-        self.data
-            .iter_mut()
-            .enumerate()
-            .filter_map(|(index, opt)| opt.as_mut().and_then(|x| Some((index, x))))
-    }
-}
-
-impl<T> Index<usize> for IndexSlab<T> {
-    type Output = T;
-
-    fn index(&self, index: usize) -> &Self::Output {
-        self.data[index].as_ref().unwrap()
-    }
-}
-
-impl<T> IndexMut<usize> for IndexSlab<T> {
-    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
-        self.data[index].as_mut().unwrap()
-    }
-}
+use std::{
+    iter,
+    mem::replace,
+    ops::{Index, IndexMut},
+};
+
+pub struct IndexSlab<T> {
+    data: Vec<Option<T>>,
+}
+
+impl<T> IndexSlab<T> {
+    pub fn new() -> Self {
+        Self { data: Vec::new() }
+    }
+
+    pub fn with_capacity(capacity: usize) -> Self {
+        Self {
+            data: Vec::with_capacity(capacity),
+        }
+    }
+
+    pub fn insert(&mut self, index: usize, value: T) {
+        if index >= self.data.len() {
+            self.data.reserve(index - self.data.len() + 1);
+            self.data.extend((self.data.len()..index).map(|_| None));
+            self.data.push(Some(value))
+        } else {
+            self.data[index] = Some(value);
+        }
+    }
+
+    pub fn contains(&self, index: usize) -> bool {
+        self.data.get(index).and_then(|x| x.as_ref()).is_some()
+    }
+
+    pub fn remove(&mut self, index: usize) -> Option<T> {
+        if let Some(x) = self.data.get_mut(index) {
+            replace(x, None)
+        } else {
+            None
+        }
+    }
+
+    pub fn iter(&self) -> impl Iterator<Item = (usize, &T)> {
+        self.data
+            .iter()
+            .enumerate()
+            .filter_map(|(index, opt)| opt.as_ref().and_then(|x| Some((index, x))))
+    }
+
+    pub fn iter_mut(&mut self) -> impl Iterator<Item = (usize, &mut T)> {
+        self.data
+            .iter_mut()
+            .enumerate()
+            .filter_map(|(index, opt)| opt.as_mut().and_then(|x| Some((index, x))))
+    }
+}
+
+impl<T> Index<usize> for IndexSlab<T> {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        self.data[index].as_ref().unwrap()
+    }
+}
+
+impl<T> IndexMut<usize> for IndexSlab<T> {
+    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+        self.data[index].as_mut().unwrap()
+    }
+}
--- a/rust/hedgewars-server/src/handlers/checker.rs	Tue Jun 04 23:19:18 2019 +0300
+++ b/rust/hedgewars-server/src/handlers/checker.rs	Tue Jun 04 22:34:42 2019 +0200
@@ -1,13 +1,13 @@
-use log::*;
-use mio;
-
-use crate::{
-    core::{server::HwServer, types::ClientId},
-    protocol::messages::HwProtocolMessage,
-};
-
-pub fn handle(_server: &mut HwServer, _client_id: ClientId, message: HwProtocolMessage) {
-    match message {
-        _ => warn!("Unknown command"),
-    }
-}
+use log::*;
+use mio;
+
+use crate::{
+    core::{server::HwServer, types::ClientId},
+    protocol::messages::HwProtocolMessage,
+};
+
+pub fn handle(_server: &mut HwServer, _client_id: ClientId, message: HwProtocolMessage) {
+    match message {
+        _ => warn!("Unknown command"),
+    }
+}
--- a/rust/hedgewars-server/src/server/io.rs	Tue Jun 04 23:19:18 2019 +0300
+++ b/rust/hedgewars-server/src/server/io.rs	Tue Jun 04 22:34:42 2019 +0200
@@ -1,162 +1,162 @@
-use std::{
-    fs::{File, OpenOptions},
-    io::{Error, ErrorKind, Read, Result, Write},
-    sync::mpsc,
-    thread,
-};
-
-use crate::{
-    handlers::{IoResult, IoTask},
-    server::database::Database,
-};
-use log::*;
-use mio::{Evented, Poll, PollOpt};
-use mio_extras::channel;
-
-pub type RequestId = u32;
-
-pub struct IoThread {
-    core_tx: mpsc::Sender<(RequestId, IoTask)>,
-    core_rx: channel::Receiver<(RequestId, IoResult)>,
-}
-
-impl IoThread {
-    pub fn new() -> Self {
-        let (core_tx, io_rx) = mpsc::channel();
-        let (io_tx, core_rx) = channel::channel();
-
-        let mut db = Database::new();
-        db.connect("localhost");
-
-        thread::spawn(move || {
-            while let Ok((request_id, task)) = io_rx.recv() {
-                let response = match task {
-                    IoTask::CheckRegistered { nick } => match db.is_registered(&nick) {
-                        Ok(is_registered) => IoResult::AccountRegistered(is_registered),
-                        Err(e) => {
-                            warn!("Unable to check account's existence: {}", e);
-                            IoResult::AccountRegistered(false)
-                        }
-                    },
-
-                    IoTask::GetAccount {
-                        nick,
-                        protocol,
-                        password_hash,
-                        client_salt,
-                        server_salt,
-                    } => {
-                        match db.get_account(
-                            &nick,
-                            protocol,
-                            &password_hash,
-                            &client_salt,
-                            &server_salt,
-                        ) {
-                            Ok(account) => IoResult::Account(account),
-                            Err(e) => {
-                                warn!("Unable to get account data: {}", e);
-                                IoResult::Account(None)
-                            }
-                        }
-                    }
-
-                    IoTask::GetReplay { id } => {
-                        let result = match db.get_replay_name(id) {
-                            Ok(Some(filename)) => {
-                                let filename = format!(
-                                    "checked/{}",
-                                    if filename.starts_with("replays/") {
-                                        &filename[8..]
-                                    } else {
-                                        &filename
-                                    }
-                                );
-                                match load_file(&filename) {
-                                    Ok(contents) => Some(unimplemented!()),
-                                    Err(e) => {
-                                        warn!(
-                                            "Error while writing the room config file \"{}\": {}",
-                                            filename, e
-                                        );
-                                        None
-                                    }
-                                }
-                            }
-                            Ok(None) => None,
-                            Err(e) => {
-                                warn!("Unable to get replay name: {}", e);
-                                None
-                            }
-                        };
-                        IoResult::Replay(result)
-                    }
-
-                    IoTask::SaveRoom {
-                        room_id,
-                        filename,
-                        contents,
-                    } => {
-                        let result = match save_file(&filename, &contents) {
-                            Ok(()) => true,
-                            Err(e) => {
-                                warn!(
-                                    "Error while writing the room config file \"{}\": {}",
-                                    filename, e
-                                );
-                                false
-                            }
-                        };
-                        IoResult::SaveRoom(room_id, result)
-                    }
-
-                    IoTask::LoadRoom { room_id, filename } => {
-                        let result = match load_file(&filename) {
-                            Ok(contents) => Some(contents),
-                            Err(e) => {
-                                warn!(
-                                    "Error while writing the room config file \"{}\": {}",
-                                    filename, e
-                                );
-                                None
-                            }
-                        };
-                        IoResult::LoadRoom(room_id, result)
-                    }
-                };
-                io_tx.send((request_id, response));
-            }
-        });
-
-        Self { core_rx, core_tx }
-    }
-
-    pub fn send(&self, request_id: RequestId, task: IoTask) {
-        self.core_tx.send((request_id, task)).unwrap();
-    }
-
-    pub fn try_recv(&self) -> Option<(RequestId, IoResult)> {
-        match self.core_rx.try_recv() {
-            Ok(result) => Some(result),
-            Err(mpsc::TryRecvError::Empty) => None,
-            Err(mpsc::TryRecvError::Disconnected) => unreachable!(),
-        }
-    }
-
-    pub fn register_rx(&self, poll: &mio::Poll, token: mio::Token) -> Result<()> {
-        self.core_rx
-            .register(poll, token, mio::Ready::readable(), PollOpt::edge())
-    }
-}
-
-fn save_file(filename: &str, contents: &str) -> Result<()> {
-    let mut writer = OpenOptions::new().create(true).write(true).open(filename)?;
-    writer.write_all(contents.as_bytes())
-}
-
-fn load_file(filename: &str) -> Result<String> {
-    let mut reader = File::open(filename)?;
-    let mut result = String::new();
-    reader.read_to_string(&mut result)?;
-    Ok(result)
-}
+use std::{
+    fs::{File, OpenOptions},
+    io::{Error, ErrorKind, Read, Result, Write},
+    sync::mpsc,
+    thread,
+};
+
+use crate::{
+    handlers::{IoResult, IoTask},
+    server::database::Database,
+};
+use log::*;
+use mio::{Evented, Poll, PollOpt};
+use mio_extras::channel;
+
+pub type RequestId = u32;
+
+pub struct IoThread {
+    core_tx: mpsc::Sender<(RequestId, IoTask)>,
+    core_rx: channel::Receiver<(RequestId, IoResult)>,
+}
+
+impl IoThread {
+    pub fn new() -> Self {
+        let (core_tx, io_rx) = mpsc::channel();
+        let (io_tx, core_rx) = channel::channel();
+
+        let mut db = Database::new();
+        db.connect("localhost");
+
+        thread::spawn(move || {
+            while let Ok((request_id, task)) = io_rx.recv() {
+                let response = match task {
+                    IoTask::CheckRegistered { nick } => match db.is_registered(&nick) {
+                        Ok(is_registered) => IoResult::AccountRegistered(is_registered),
+                        Err(e) => {
+                            warn!("Unable to check account's existence: {}", e);
+                            IoResult::AccountRegistered(false)
+                        }
+                    },
+
+                    IoTask::GetAccount {
+                        nick,
+                        protocol,
+                        password_hash,
+                        client_salt,
+                        server_salt,
+                    } => {
+                        match db.get_account(
+                            &nick,
+                            protocol,
+                            &password_hash,
+                            &client_salt,
+                            &server_salt,
+                        ) {
+                            Ok(account) => IoResult::Account(account),
+                            Err(e) => {
+                                warn!("Unable to get account data: {}", e);
+                                IoResult::Account(None)
+                            }
+                        }
+                    }
+
+                    IoTask::GetReplay { id } => {
+                        let result = match db.get_replay_name(id) {
+                            Ok(Some(filename)) => {
+                                let filename = format!(
+                                    "checked/{}",
+                                    if filename.starts_with("replays/") {
+                                        &filename[8..]
+                                    } else {
+                                        &filename
+                                    }
+                                );
+                                match load_file(&filename) {
+                                    Ok(contents) => Some(unimplemented!()),
+                                    Err(e) => {
+                                        warn!(
+                                            "Error while writing the room config file \"{}\": {}",
+                                            filename, e
+                                        );
+                                        None
+                                    }
+                                }
+                            }
+                            Ok(None) => None,
+                            Err(e) => {
+                                warn!("Unable to get replay name: {}", e);
+                                None
+                            }
+                        };
+                        IoResult::Replay(result)
+                    }
+
+                    IoTask::SaveRoom {
+                        room_id,
+                        filename,
+                        contents,
+                    } => {
+                        let result = match save_file(&filename, &contents) {
+                            Ok(()) => true,
+                            Err(e) => {
+                                warn!(
+                                    "Error while writing the room config file \"{}\": {}",
+                                    filename, e
+                                );
+                                false
+                            }
+                        };
+                        IoResult::SaveRoom(room_id, result)
+                    }
+
+                    IoTask::LoadRoom { room_id, filename } => {
+                        let result = match load_file(&filename) {
+                            Ok(contents) => Some(contents),
+                            Err(e) => {
+                                warn!(
+                                    "Error while writing the room config file \"{}\": {}",
+                                    filename, e
+                                );
+                                None
+                            }
+                        };
+                        IoResult::LoadRoom(room_id, result)
+                    }
+                };
+                io_tx.send((request_id, response));
+            }
+        });
+
+        Self { core_rx, core_tx }
+    }
+
+    pub fn send(&self, request_id: RequestId, task: IoTask) {
+        self.core_tx.send((request_id, task)).unwrap();
+    }
+
+    pub fn try_recv(&self) -> Option<(RequestId, IoResult)> {
+        match self.core_rx.try_recv() {
+            Ok(result) => Some(result),
+            Err(mpsc::TryRecvError::Empty) => None,
+            Err(mpsc::TryRecvError::Disconnected) => unreachable!(),
+        }
+    }
+
+    pub fn register_rx(&self, poll: &mio::Poll, token: mio::Token) -> Result<()> {
+        self.core_rx
+            .register(poll, token, mio::Ready::readable(), PollOpt::edge())
+    }
+}
+
+fn save_file(filename: &str, contents: &str) -> Result<()> {
+    let mut writer = OpenOptions::new().create(true).write(true).open(filename)?;
+    writer.write_all(contents.as_bytes())
+}
+
+fn load_file(filename: &str) -> Result<String> {
+    let mut reader = File::open(filename)?;
+    let mut result = String::new();
+    reader.read_to_string(&mut result)?;
+    Ok(result)
+}
--- a/rust/hwphysics/src/collision.rs	Tue Jun 04 23:19:18 2019 +0300
+++ b/rust/hwphysics/src/collision.rs	Tue Jun 04 22:34:42 2019 +0200
@@ -1,129 +1,129 @@
-use std::ops::RangeInclusive;
-
-use crate::{
-    common::{GearData, GearDataProcessor, GearId},
-    grid::Grid,
-    physics::PhysicsData,
-};
-
-use fpnum::*;
-use integral_geometry::{GridIndex, Point, Size};
-use land2d::Land2D;
-
-pub fn fppoint_round(point: &FPPoint) -> Point {
-    Point::new(point.x().round() as i32, point.y().round() as i32)
-}
-
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
-pub struct CircleBounds {
-    pub center: FPPoint,
-    pub radius: FPNum,
-}
-
-impl CircleBounds {
-    pub fn intersects(&self, other: &CircleBounds) -> bool {
-        (other.center - self.center).is_in_range(self.radius + other.radius)
-    }
-
-    pub fn rows(&self) -> impl Iterator<Item = (usize, RangeInclusive<usize>)> {
-        let radius = self.radius.abs_round() as usize;
-        let center = Point::from_fppoint(&self.center);
-        (center.y as usize - radius..=center.y as usize + radius)
-            .map(move |row| (row, center.x as usize - radius..=center.x as usize + radius))
-    }
-}
-
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
-pub struct CollisionData {
-    pub bounds: CircleBounds,
-}
-
-impl GearData for CollisionData {}
-
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
-pub struct ContactData {
-    pub elasticity: FPNum,
-    pub friction: FPNum,
-}
-
-impl GearData for ContactData {}
-
-struct EnabledCollisionsCollection {
-    gear_ids: Vec<GearId>,
-    collisions: Vec<CollisionData>,
-}
-
-impl EnabledCollisionsCollection {
-    fn new() -> Self {
-        Self {
-            gear_ids: Vec::new(),
-            collisions: Vec::new(),
-        }
-    }
-
-    fn push(&mut self, gear_id: GearId, collision: CollisionData) {
-        self.gear_ids.push(gear_id);
-        self.collisions.push(collision);
-    }
-
-    fn iter(&self) -> impl Iterator<Item = (GearId, &CollisionData)> {
-        self.gear_ids.iter().cloned().zip(self.collisions.iter())
-    }
-}
-
-pub struct CollisionProcessor {
-    grid: Grid,
-    enabled_collisions: EnabledCollisionsCollection,
-
-    detected_collisions: DetectedCollisions,
-}
-
-pub struct DetectedCollisions {
-    pub pairs: Vec<(GearId, GearId)>,
-    pub positions: Vec<Point>,
-}
-
-impl DetectedCollisions {
-    pub fn new(capacity: usize) -> Self {
-        Self {
-            pairs: Vec::with_capacity(capacity),
-            positions: Vec::with_capacity(capacity),
-        }
-    }
-
-    pub fn push(&mut self, contact_gear_id1: GearId, contact_gear_id2: GearId, position: &FPPoint) {
-        self.pairs.push((contact_gear_id1, contact_gear_id2));
-        self.positions.push(fppoint_round(&position));
-    }
-}
-
-impl CollisionProcessor {
-    pub fn new(size: Size) -> Self {
-        Self {
-            grid: Grid::new(size),
-            enabled_collisions: EnabledCollisionsCollection::new(),
-            detected_collisions: DetectedCollisions::new(0),
-        }
-    }
-
-    pub fn process(&mut self, land: &Land2D<u32>, updates: &crate::physics::PositionUpdates) {
-        self.grid.check_collisions(&mut self.detected_collisions);
-
-        for (gear_id, collision) in self.enabled_collisions.iter() {
-            if collision
-                .bounds
-                .rows()
-                .any(|(y, r)| (&land[y][r]).iter().any(|v| *v != 0))
-            {
-                self.detected_collisions
-                    .push(gear_id, 0, &collision.bounds.center)
-            }
-        }
-    }
-}
-
-impl GearDataProcessor<CollisionData> for CollisionProcessor {
-    fn add(&mut self, gear_id: GearId, gear_data: CollisionData) {
-        self.grid.insert_static(gear_id, &gear_data.bounds);
-    }
-}
+use std::ops::RangeInclusive;
+
+use crate::{
+    common::{GearData, GearDataProcessor, GearId},
+    grid::Grid,
+    physics::PhysicsData,
+};
+
+use fpnum::*;
+use integral_geometry::{GridIndex, Point, Size};
+use land2d::Land2D;
+
+pub fn fppoint_round(point: &FPPoint) -> Point {
+    Point::new(point.x().round() as i32, point.y().round() as i32)
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct CircleBounds {
+    pub center: FPPoint,
+    pub radius: FPNum,
+}
+
+impl CircleBounds {
+    pub fn intersects(&self, other: &CircleBounds) -> bool {
+        (other.center - self.center).is_in_range(self.radius + other.radius)
+    }
+
+    pub fn rows(&self) -> impl Iterator<Item = (usize, RangeInclusive<usize>)> {
+        let radius = self.radius.abs_round() as usize;
+        let center = Point::from_fppoint(&self.center);
+        (center.y as usize - radius..=center.y as usize + radius)
+            .map(move |row| (row, center.x as usize - radius..=center.x as usize + radius))
+    }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct CollisionData {
+    pub bounds: CircleBounds,
+}
+
+impl GearData for CollisionData {}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ContactData {
+    pub elasticity: FPNum,
+    pub friction: FPNum,
+}
+
+impl GearData for ContactData {}
+
+struct EnabledCollisionsCollection {
+    gear_ids: Vec<GearId>,
+    collisions: Vec<CollisionData>,
+}
+
+impl EnabledCollisionsCollection {
+    fn new() -> Self {
+        Self {
+            gear_ids: Vec::new(),
+            collisions: Vec::new(),
+        }
+    }
+
+    fn push(&mut self, gear_id: GearId, collision: CollisionData) {
+        self.gear_ids.push(gear_id);
+        self.collisions.push(collision);
+    }
+
+    fn iter(&self) -> impl Iterator<Item = (GearId, &CollisionData)> {
+        self.gear_ids.iter().cloned().zip(self.collisions.iter())
+    }
+}
+
+pub struct CollisionProcessor {
+    grid: Grid,
+    enabled_collisions: EnabledCollisionsCollection,
+
+    detected_collisions: DetectedCollisions,
+}
+
+pub struct DetectedCollisions {
+    pub pairs: Vec<(GearId, GearId)>,
+    pub positions: Vec<Point>,
+}
+
+impl DetectedCollisions {
+    pub fn new(capacity: usize) -> Self {
+        Self {
+            pairs: Vec::with_capacity(capacity),
+            positions: Vec::with_capacity(capacity),
+        }
+    }
+
+    pub fn push(&mut self, contact_gear_id1: GearId, contact_gear_id2: GearId, position: &FPPoint) {
+        self.pairs.push((contact_gear_id1, contact_gear_id2));
+        self.positions.push(fppoint_round(&position));
+    }
+}
+
+impl CollisionProcessor {
+    pub fn new(size: Size) -> Self {
+        Self {
+            grid: Grid::new(size),
+            enabled_collisions: EnabledCollisionsCollection::new(),
+            detected_collisions: DetectedCollisions::new(0),
+        }
+    }
+
+    pub fn process(&mut self, land: &Land2D<u32>, updates: &crate::physics::PositionUpdates) {
+        self.grid.check_collisions(&mut self.detected_collisions);
+
+        for (gear_id, collision) in self.enabled_collisions.iter() {
+            if collision
+                .bounds
+                .rows()
+                .any(|(y, r)| (&land[y][r]).iter().any(|v| *v != 0))
+            {
+                self.detected_collisions
+                    .push(gear_id, 0, &collision.bounds.center)
+            }
+        }
+    }
+}
+
+impl GearDataProcessor<CollisionData> for CollisionProcessor {
+    fn add(&mut self, gear_id: GearId, gear_data: CollisionData) {
+        self.grid.insert_static(gear_id, &gear_data.bounds);
+    }
+}
--- a/rust/hwphysics/src/common.rs	Tue Jun 04 23:19:18 2019 +0300
+++ b/rust/hwphysics/src/common.rs	Tue Jun 04 22:34:42 2019 +0200
@@ -1,10 +1,10 @@
-pub type GearId = u16;
-pub trait GearData {}
-
-pub trait GearDataProcessor<T: GearData> {
-    fn add(&mut self, gear_id: GearId, gear_data: T);
-}
-
-pub trait GearDataAggregator<T: GearData> {
-    fn find_processor(&mut self) -> &mut GearDataProcessor<T>;
-}
+pub type GearId = u16;
+pub trait GearData {}
+
+pub trait GearDataProcessor<T: GearData> {
+    fn add(&mut self, gear_id: GearId, gear_data: T);
+}
+
+pub trait GearDataAggregator<T: GearData> {
+    fn find_processor(&mut self) -> &mut GearDataProcessor<T>;
+}
--- a/rust/hwphysics/src/grid.rs	Tue Jun 04 23:19:18 2019 +0300
+++ b/rust/hwphysics/src/grid.rs	Tue Jun 04 22:34:42 2019 +0200
@@ -1,83 +1,83 @@
-use crate::{
-    collision::{fppoint_round, CircleBounds, DetectedCollisions},
-    common::GearId,
-};
-
-use fpnum::FPPoint;
-use integral_geometry::{GridIndex, Point, Size};
-
-struct GridBin {
-    refs: Vec<GearId>,
-    static_entries: Vec<CircleBounds>,
-    dynamic_entries: Vec<CircleBounds>,
-}
-
-impl GridBin {
-    fn new() -> Self {
-        Self {
-            refs: vec![],
-            static_entries: vec![],
-            dynamic_entries: vec![],
-        }
-    }
-}
-
-const GRID_BIN_SIZE: usize = 128;
-
-pub struct Grid {
-    bins: Vec<GridBin>,
-    space_size: Size,
-    bins_count: Size,
-    index: GridIndex,
-}
-
-impl Grid {
-    pub fn new(size: Size) -> Self {
-        assert!(size.is_power_of_two());
-        let bins_count = Size::new(size.width / GRID_BIN_SIZE, size.height / GRID_BIN_SIZE);
-
-        Self {
-            bins: (0..bins_count.area()).map(|_| GridBin::new()).collect(),
-            space_size: size,
-            bins_count,
-            index: Size::square(GRID_BIN_SIZE).to_grid_index(),
-        }
-    }
-
-    fn bin_index(&self, position: &FPPoint) -> Point {
-        self.index.map(fppoint_round(position))
-    }
-
-    fn lookup_bin(&mut self, position: &FPPoint) -> &mut GridBin {
-        let index = self.bin_index(position);
-        &mut self.bins[index.x as usize * self.bins_count.width + index.y as usize]
-    }
-
-    pub fn insert_static(&mut self, gear_id: GearId, bounds: &CircleBounds) {
-        self.lookup_bin(&bounds.center).static_entries.push(*bounds)
-    }
-
-    pub fn insert_dynamic(&mut self, gear_id: GearId, bounds: &CircleBounds) {
-        self.lookup_bin(&bounds.center)
-            .dynamic_entries
-            .push(*bounds)
-    }
-
-    pub fn check_collisions(&self, collisions: &mut DetectedCollisions) {
-        for bin in &self.bins {
-            for bounds in &bin.dynamic_entries {
-                for other in &bin.dynamic_entries {
-                    if bounds.intersects(other) && bounds != other {
-                        collisions.push(0, 0, &bounds.center)
-                    }
-                }
-
-                for other in &bin.static_entries {
-                    if bounds.intersects(other) {
-                        collisions.push(0, 0, &bounds.center)
-                    }
-                }
-            }
-        }
-    }
-}
+use crate::{
+    collision::{fppoint_round, CircleBounds, DetectedCollisions},
+    common::GearId,
+};
+
+use fpnum::FPPoint;
+use integral_geometry::{GridIndex, Point, Size};
+
+struct GridBin {
+    refs: Vec<GearId>,
+    static_entries: Vec<CircleBounds>,
+    dynamic_entries: Vec<CircleBounds>,
+}
+
+impl GridBin {
+    fn new() -> Self {
+        Self {
+            refs: vec![],
+            static_entries: vec![],
+            dynamic_entries: vec![],
+        }
+    }
+}
+
+const GRID_BIN_SIZE: usize = 128;
+
+pub struct Grid {
+    bins: Vec<GridBin>,
+    space_size: Size,
+    bins_count: Size,
+    index: GridIndex,
+}
+
+impl Grid {
+    pub fn new(size: Size) -> Self {
+        assert!(size.is_power_of_two());
+        let bins_count = Size::new(size.width / GRID_BIN_SIZE, size.height / GRID_BIN_SIZE);
+
+        Self {
+            bins: (0..bins_count.area()).map(|_| GridBin::new()).collect(),
+            space_size: size,
+            bins_count,
+            index: Size::square(GRID_BIN_SIZE).to_grid_index(),
+        }
+    }
+
+    fn bin_index(&self, position: &FPPoint) -> Point {
+        self.index.map(fppoint_round(position))
+    }
+
+    fn lookup_bin(&mut self, position: &FPPoint) -> &mut GridBin {
+        let index = self.bin_index(position);
+        &mut self.bins[index.x as usize * self.bins_count.width + index.y as usize]
+    }
+
+    pub fn insert_static(&mut self, gear_id: GearId, bounds: &CircleBounds) {
+        self.lookup_bin(&bounds.center).static_entries.push(*bounds)
+    }
+
+    pub fn insert_dynamic(&mut self, gear_id: GearId, bounds: &CircleBounds) {
+        self.lookup_bin(&bounds.center)
+            .dynamic_entries
+            .push(*bounds)
+    }
+
+    pub fn check_collisions(&self, collisions: &mut DetectedCollisions) {
+        for bin in &self.bins {
+            for bounds in &bin.dynamic_entries {
+                for other in &bin.dynamic_entries {
+                    if bounds.intersects(other) && bounds != other {
+                        collisions.push(0, 0, &bounds.center)
+                    }
+                }
+
+                for other in &bin.static_entries {
+                    if bounds.intersects(other) {
+                        collisions.push(0, 0, &bounds.center)
+                    }
+                }
+            }
+        }
+    }
+}
--- a/rust/hwphysics/src/physics.rs	Tue Jun 04 23:19:18 2019 +0300
+++ b/rust/hwphysics/src/physics.rs	Tue Jun 04 22:34:42 2019 +0200
@@ -1,137 +1,137 @@
-use crate::common::{GearData, GearDataProcessor, GearId};
-use fpnum::*;
-use integral_geometry::{GridIndex, Point, Size};
-
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
-pub struct PhysicsData {
-    pub position: FPPoint,
-    pub velocity: FPPoint,
-}
-
-impl GearData for PhysicsData {}
-
-impl PhysicsData {
-    pub fn new(position: FPPoint, velocity: FPPoint) -> Self {
-        Self { position, velocity }
-    }
-}
-
-pub struct DynamicPhysicsCollection {
-    gear_ids: Vec<GearId>,
-    positions: Vec<FPPoint>,
-    velocities: Vec<FPPoint>,
-}
-
-impl DynamicPhysicsCollection {
-    fn new() -> Self {
-        Self {
-            gear_ids: Vec::new(),
-            positions: Vec::new(),
-            velocities: Vec::new(),
-        }
-    }
-
-    fn len(&self) -> usize {
-        self.gear_ids.len()
-    }
-
-    fn push(&mut self, id: GearId, physics: PhysicsData) {
-        self.gear_ids.push(id);
-        self.positions.push(physics.position);
-        self.velocities.push(physics.velocity);
-    }
-
-    fn iter_pos_update(&mut self) -> impl Iterator<Item = (GearId, (&mut FPPoint, &FPPoint))> {
-        self.gear_ids
-            .iter()
-            .cloned()
-            .zip(self.positions.iter_mut().zip(self.velocities.iter()))
-    }
-}
-
-pub struct StaticPhysicsCollection {
-    gear_ids: Vec<GearId>,
-    positions: Vec<FPPoint>,
-}
-
-impl StaticPhysicsCollection {
-    fn new() -> Self {
-        Self {
-            gear_ids: Vec::new(),
-            positions: Vec::new(),
-        }
-    }
-
-    fn push(&mut self, gear_id: GearId, physics: PhysicsData) {
-        self.gear_ids.push(gear_id);
-        self.positions.push(physics.position);
-    }
-}
-
-pub struct PhysicsProcessor {
-    dynamic_physics: DynamicPhysicsCollection,
-    static_physics: StaticPhysicsCollection,
-
-    physics_cleanup: Vec<GearId>,
-    position_updates: PositionUpdates,
-}
-
-pub struct PositionUpdates {
-    pub gear_ids: Vec<GearId>,
-    pub positions: Vec<FPPoint>,
-}
-
-impl PositionUpdates {
-    pub fn new(capacity: usize) -> Self {
-        Self {
-            gear_ids: Vec::with_capacity(capacity),
-            positions: Vec::with_capacity(capacity),
-        }
-    }
-
-    pub fn push(&mut self, gear_id: GearId, position: &FPPoint) {
-        self.gear_ids.push(gear_id);
-        self.positions.push(*position);
-    }
-}
-
-impl PhysicsProcessor {
-    pub fn new() -> Self {
-        PhysicsProcessor {
-            dynamic_physics: DynamicPhysicsCollection::new(),
-            static_physics: StaticPhysicsCollection::new(),
-            physics_cleanup: Vec::new(),
-            position_updates: PositionUpdates::new(0),
-        }
-    }
-
-    pub fn process(&mut self, time_step: FPNum) -> &PositionUpdates {
-        for (gear_id, (pos, vel)) in self.dynamic_physics.iter_pos_update() {
-            *pos += *vel * time_step;
-            if !vel.is_zero() {
-                self.position_updates.push(gear_id, pos)
-            } else {
-                self.physics_cleanup.push(gear_id)
-            }
-        }
-        &self.position_updates
-    }
-
-    pub fn push(&mut self, gear_id: GearId, physics_data: PhysicsData) {
-        if physics_data.velocity.is_zero() {
-            self.static_physics.push(gear_id, physics_data);
-        } else {
-            self.dynamic_physics.push(gear_id, physics_data);
-        }
-    }
-}
-
-impl GearDataProcessor<PhysicsData> for PhysicsProcessor {
-    fn add(&mut self, gear_id: GearId, gear_data: PhysicsData) {
-        if gear_data.velocity.is_zero() {
-            self.static_physics.push(gear_id, gear_data);
-        } else {
-            self.dynamic_physics.push(gear_id, gear_data);
-        }
-    }
-}
+use crate::common::{GearData, GearDataProcessor, GearId};
+use fpnum::*;
+use integral_geometry::{GridIndex, Point, Size};
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct PhysicsData {
+    pub position: FPPoint,
+    pub velocity: FPPoint,
+}
+
+impl GearData for PhysicsData {}
+
+impl PhysicsData {
+    pub fn new(position: FPPoint, velocity: FPPoint) -> Self {
+        Self { position, velocity }
+    }
+}
+
+pub struct DynamicPhysicsCollection {
+    gear_ids: Vec<GearId>,
+    positions: Vec<FPPoint>,
+    velocities: Vec<FPPoint>,
+}
+
+impl DynamicPhysicsCollection {
+    fn new() -> Self {
+        Self {
+            gear_ids: Vec::new(),
+            positions: Vec::new(),
+            velocities: Vec::new(),
+        }
+    }
+
+    fn len(&self) -> usize {
+        self.gear_ids.len()
+    }
+
+    fn push(&mut self, id: GearId, physics: PhysicsData) {
+        self.gear_ids.push(id);
+        self.positions.push(physics.position);
+        self.velocities.push(physics.velocity);
+    }
+
+    fn iter_pos_update(&mut self) -> impl Iterator<Item = (GearId, (&mut FPPoint, &FPPoint))> {
+        self.gear_ids
+            .iter()
+            .cloned()
+            .zip(self.positions.iter_mut().zip(self.velocities.iter()))
+    }
+}
+
+pub struct StaticPhysicsCollection {
+    gear_ids: Vec<GearId>,
+    positions: Vec<FPPoint>,
+}
+
+impl StaticPhysicsCollection {
+    fn new() -> Self {
+        Self {
+            gear_ids: Vec::new(),
+            positions: Vec::new(),
+        }
+    }
+
+    fn push(&mut self, gear_id: GearId, physics: PhysicsData) {
+        self.gear_ids.push(gear_id);
+        self.positions.push(physics.position);
+    }
+}
+
+pub struct PhysicsProcessor {
+    dynamic_physics: DynamicPhysicsCollection,
+    static_physics: StaticPhysicsCollection,
+
+    physics_cleanup: Vec<GearId>,
+    position_updates: PositionUpdates,
+}
+
+pub struct PositionUpdates {
+    pub gear_ids: Vec<GearId>,
+    pub positions: Vec<FPPoint>,
+}
+
+impl PositionUpdates {
+    pub fn new(capacity: usize) -> Self {
+        Self {
+            gear_ids: Vec::with_capacity(capacity),
+            positions: Vec::with_capacity(capacity),
+        }
+    }
+
+    pub fn push(&mut self, gear_id: GearId, position: &FPPoint) {
+        self.gear_ids.push(gear_id);
+        self.positions.push(*position);
+    }
+}
+
+impl PhysicsProcessor {
+    pub fn new() -> Self {
+        PhysicsProcessor {
+            dynamic_physics: DynamicPhysicsCollection::new(),
+            static_physics: StaticPhysicsCollection::new(),
+            physics_cleanup: Vec::new(),
+            position_updates: PositionUpdates::new(0),
+        }
+    }
+
+    pub fn process(&mut self, time_step: FPNum) -> &PositionUpdates {
+        for (gear_id, (pos, vel)) in self.dynamic_physics.iter_pos_update() {
+            *pos += *vel * time_step;
+            if !vel.is_zero() {
+                self.position_updates.push(gear_id, pos)
+            } else {
+                self.physics_cleanup.push(gear_id)
+            }
+        }
+        &self.position_updates
+    }
+
+    pub fn push(&mut self, gear_id: GearId, physics_data: PhysicsData) {
+        if physics_data.velocity.is_zero() {
+            self.static_physics.push(gear_id, physics_data);
+        } else {
+            self.dynamic_physics.push(gear_id, physics_data);
+        }
+    }
+}
+
+impl GearDataProcessor<PhysicsData> for PhysicsProcessor {
+    fn add(&mut self, gear_id: GearId, gear_data: PhysicsData) {
+        if gear_data.velocity.is_zero() {
+            self.static_physics.push(gear_id, gear_data);
+        } else {
+            self.dynamic_physics.push(gear_id, gear_data);
+        }
+    }
+}
--- a/rust/lib-hedgewars-engine/src/render.rs	Tue Jun 04 23:19:18 2019 +0300
+++ b/rust/lib-hedgewars-engine/src/render.rs	Tue Jun 04 22:34:42 2019 +0200
@@ -1,9 +1,9 @@
-pub mod atlas;
-pub mod camera;
-mod gear;
-mod gl;
-mod map;
-
-use self::gl::*;
-pub use self::map::*;
-pub use gear::*;
+pub mod atlas;
+pub mod camera;
+mod gear;
+mod gl;
+mod map;
+
+use self::gl::*;
+pub use self::map::*;
+pub use gear::*;
--- a/rust/lib-hedgewars-engine/src/render/atlas.rs	Tue Jun 04 23:19:18 2019 +0300
+++ b/rust/lib-hedgewars-engine/src/render/atlas.rs	Tue Jun 04 22:34:42 2019 +0200
@@ -1,398 +1,398 @@
-use integral_geometry::{Rect, Size};
-use std::cmp::{max, min, Ordering};
-
-#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
-struct Fit {
-    short_side: u32,
-    long_side: u32,
-}
-
-impl Fit {
-    fn new() -> Self {
-        Self {
-            short_side: u32::max_value(),
-            long_side: u32::max_value(),
-        }
-    }
-
-    fn measure(container: Size, size: Size) -> Option<Self> {
-        if container.contains(size) {
-            let x_leftover = container.width - size.width;
-            let y_leftover = container.height - size.height;
-            Some(Self {
-                short_side: min(x_leftover, y_leftover) as u32,
-                long_side: max(x_leftover, y_leftover) as u32,
-            })
-        } else {
-            None
-        }
-    }
-}
-
-#[derive(PartialEq, Eq)]
-pub struct UsedSpace {
-    used_area: usize,
-    total_area: usize,
-}
-
-impl UsedSpace {
-    const fn new(used_area: usize, total_area: usize) -> Self {
-        Self {
-            used_area,
-            total_area,
-        }
-    }
-
-    const fn used(&self) -> usize {
-        self.used_area
-    }
-
-    const fn total(&self) -> usize {
-        self.total_area
-    }
-}
-
-impl std::fmt::Debug for UsedSpace {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
-        write!(
-            f,
-            "{:.2}%",
-            self.used() as f32 / self.total() as f32 / 100.0
-        )?;
-        Ok(())
-    }
-}
-
-pub struct Atlas {
-    size: Size,
-    free_rects: Vec<Rect>,
-    used_rects: Vec<Rect>,
-    splits: Vec<Rect>,
-}
-
-impl Atlas {
-    pub fn new(size: Size) -> Self {
-        Self {
-            size,
-            free_rects: vec![Rect::at_origin(size)],
-            used_rects: vec![],
-            splits: vec![],
-        }
-    }
-
-    pub fn size(&self) -> Size {
-        self.size
-    }
-
-    pub fn used_space(&self) -> UsedSpace {
-        let used = self.used_rects.iter().map(|r| r.size().area()).sum();
-        UsedSpace::new(used, self.size.area())
-    }
-
-    fn find_position(&self, size: Size) -> Option<(Rect, Fit)> {
-        let mut best_rect = Rect::EMPTY;
-        let mut best_fit = Fit::new();
-
-        for rect in &self.free_rects {
-            if let Some(fit) = Fit::measure(rect.size(), size) {
-                if fit < best_fit {
-                    best_fit = fit;
-                    best_rect = Rect::from_size(rect.top_left(), size);
-                }
-            }
-
-            if let Some(fit) = Fit::measure(rect.size(), size.transpose()) {
-                if fit < best_fit {
-                    best_fit = fit;
-                    best_rect = Rect::from_size(rect.top_left(), size.transpose());
-                }
-            }
-        }
-
-        if best_rect == Rect::EMPTY {
-            None
-        } else {
-            Some((best_rect, best_fit))
-        }
-    }
-
-    fn split_insert(&mut self, rect: Rect) {
-        let mut splits = std::mem::replace(&mut self.splits, vec![]);
-        let mut buffer = [Rect::EMPTY; 4];
-
-        for i in (0..self.free_rects.len()).rev() {
-            if let Some(count) = split_rect(self.free_rects[i], rect, &mut splits, &mut buffer) {
-                self.free_rects.swap_remove(i as usize);
-                splits.extend_from_slice(&buffer[0..count]);
-            }
-        }
-
-        filter_swap_remove(&mut splits, |s| {
-            self.free_rects.iter().any(|r| r.contains_rect(s))
-        });
-        self.free_rects.extend(splits.drain(..));
-        std::mem::replace(&mut self.splits, splits);
-
-        self.used_rects.push(rect);
-    }
-
-    pub fn insert(&mut self, size: Size) -> Option<Rect> {
-        let (rect, _) = self.find_position(size)?;
-        self.split_insert(rect);
-        Some(rect)
-    }
-
-    pub fn insert_set<Iter>(&mut self, sizes: Iter) -> Vec<Rect>
-    where
-        Iter: Iterator<Item = Size>,
-    {
-        let mut sizes: Vec<_> = sizes.collect();
-        let mut result = vec![];
-
-        while let Some((index, (rect, _))) = sizes
-            .iter()
-            .enumerate()
-            .filter_map(|(i, s)| self.find_position(*s).map(|res| (i, res)))
-            .min_by_key(|(_, (_, fit))| fit.clone())
-        {
-            self.split_insert(rect);
-
-            result.push(rect);
-            sizes.swap_remove(index);
-        }
-        result
-    }
-
-    pub fn reset(&mut self) {
-        self.free_rects.clear();
-        self.used_rects.clear();
-        self.free_rects.push(Rect::at_origin(self.size));
-    }
-}
-
-pub struct AtlasCollection {
-    texture_size: Size,
-    atlases: Vec<Atlas>,
-}
-
-impl AtlasCollection {
-    pub fn new(texture_size: Size) -> Self {
-        Self {
-            texture_size,
-            atlases: vec![],
-        }
-    }
-
-    fn repack(&mut self, size: Size) -> bool {
-        for atlas in &mut self.atlases {
-            let mut temp_atlas = Atlas::new(atlas.size());
-            let sizes = atlas
-                .used_rects
-                .iter()
-                .map(|r| r.size())
-                .chain(std::iter::once(size));
-            if !temp_atlas.insert_set(sizes).is_empty() {
-                std::mem::swap(atlas, &mut temp_atlas);
-                return true;
-            }
-        }
-        false
-    }
-
-    pub fn insert_sprite(&mut self, size: Size) -> bool {
-        if !self.texture_size.contains(size) {
-            false
-        } else {
-            if let Some(rect) = self.atlases.iter_mut().find_map(|a| a.insert(size)) {
-
-            } else if !self.repack(size) {
-                let mut atlas = Atlas::new(self.texture_size);
-                atlas.insert(size);
-                self.atlases.push(atlas);
-            }
-            true
-        }
-    }
-}
-
-#[inline]
-fn filter_swap_remove<T, F>(vec: &mut Vec<T>, predicate: F)
-where
-    F: Fn(&T) -> bool,
-{
-    let mut i = 0;
-    while i < vec.len() {
-        if predicate(&vec[i]) {
-            vec.swap_remove(i);
-        } else {
-            i += 1;
-        }
-    }
-}
-
-#[inline]
-fn prune_push(
-    previous_splits: &mut Vec<Rect>,
-    buffer: &mut [Rect; 4],
-    buffer_size: &mut usize,
-    rect: Rect,
-) {
-    if !previous_splits.iter().any(|r| r.contains_rect(&rect)) {
-        filter_swap_remove(previous_splits, |s| rect.contains_rect(s));
-        buffer[*buffer_size] = rect;
-        *buffer_size += 1;
-    }
-}
-
-fn split_rect(
-    free_rect: Rect,
-    rect: Rect,
-    previous_splits: &mut Vec<Rect>,
-    buffer: &mut [Rect; 4],
-) -> Option<usize> {
-    let mut buffer_size = 0usize;
-    let split = free_rect.intersects(&rect);
-    if split {
-        if rect.left() > free_rect.left() {
-            let trim = free_rect.right() - rect.left() + 1;
-            prune_push(
-                previous_splits,
-                buffer,
-                &mut buffer_size,
-                free_rect.with_margins(0, -trim, 0, 0),
-            );
-        }
-        if rect.right() < free_rect.right() {
-            let trim = rect.right() - free_rect.left() + 1;
-            prune_push(
-                previous_splits,
-                buffer,
-                &mut buffer_size,
-                free_rect.with_margins(-trim, 0, 0, 0),
-            );
-        }
-        if rect.top() > free_rect.top() {
-            let trim = free_rect.bottom() - rect.top() + 1;
-            prune_push(
-                previous_splits,
-                buffer,
-                &mut buffer_size,
-                free_rect.with_margins(0, 0, 0, -trim),
-            );;
-        }
-        if rect.bottom() < free_rect.bottom() {
-            let trim = rect.bottom() - free_rect.top() + 1;
-            prune_push(
-                previous_splits,
-                buffer,
-                &mut buffer_size,
-                free_rect.with_margins(0, 0, -trim, 0),
-            );;
-        }
-    }
-    if split {
-        Some(buffer_size)
-    } else {
-        None
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::Atlas;
-    use integral_geometry::{Rect, Size};
-    use itertools::Itertools as _;
-    use proptest::prelude::*;
-
-    #[test]
-    fn insert() {
-        let atlas_size = Size::square(16);
-        let mut atlas = Atlas::new(atlas_size);
-
-        assert_eq!(None, atlas.insert(Size::square(20)));
-
-        let rect_size = Size::new(11, 3);
-        let rect = atlas.insert(rect_size).unwrap();
-
-        assert_eq!(rect, Rect::at_origin(rect_size));
-        assert_eq!(2, atlas.free_rects.len());
-    }
-
-    #[derive(Debug, Clone)]
-    struct TestRect(Size);
-    struct TestRectParameters(Size);
-
-    impl Default for TestRectParameters {
-        fn default() -> Self {
-            Self(Size::square(64))
-        }
-    }
-
-    impl Arbitrary for TestRect {
-        type Parameters = TestRectParameters;
-
-        fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
-            (1..=args.0.width, 1..=args.0.height)
-                .prop_map(|(w, h)| TestRect(Size::new(w, h)))
-                .boxed()
-        }
-
-        type Strategy = BoxedStrategy<TestRect>;
-    }
-
-    trait HasSize {
-        fn size(&self) -> Size;
-    }
-
-    impl HasSize for TestRect {
-        fn size(&self) -> Size {
-            self.0
-        }
-    }
-
-    impl HasSize for Rect {
-        fn size(&self) -> Size {
-            self.size()
-        }
-    }
-
-    fn sum_area<S: HasSize>(items: &[S]) -> usize {
-        items.iter().map(|s| s.size().area()).sum()
-    }
-
-    proptest! {
-        #[test]
-        fn prop_insert(rects in Vec::<TestRect>::arbitrary()) {
-            let container = Rect::at_origin(Size::square(2048));
-            let mut atlas = Atlas::new(container.size());
-            let inserted: Vec<_> = rects.iter().filter_map(|TestRect(size)| atlas.insert(*size)).collect();
-
-            let mut inserted_pairs = inserted.iter().cartesian_product(inserted.iter());
-
-            assert!(inserted.iter().all(|r| container.contains_rect(r)));
-            assert!(inserted_pairs.all(|(r1, r2)| r1 == r2 || r1 != r2 && !r1.intersects(r2)));
-
-            assert_eq!(inserted.len(), rects.len());
-            assert_eq!(sum_area(&inserted), sum_area(&rects));
-        }
-    }
-
-    proptest! {
-        #[test]
-        fn prop_insert_set(rects in Vec::<TestRect>::arbitrary()) {
-            let container = Rect::at_origin(Size::square(2048));
-            let mut atlas = Atlas::new(container.size());
-            let mut set_atlas = Atlas::new(container.size());
-
-            let inserted: Vec<_> = rects.iter().filter_map(|TestRect(size)| atlas.insert(*size)).collect();
-            let set_inserted: Vec<_> = set_atlas.insert_set(rects.iter().map(|TestRect(size)| *size));
-
-            let mut set_inserted_pairs = set_inserted.iter().cartesian_product(set_inserted.iter());
-
-            assert!(set_inserted_pairs.all(|(r1, r2)| r1 == r2 || r1 != r2 && !r1.intersects(r2)));
-            assert!(set_atlas.used_space().used() <= atlas.used_space().used());
-
-            assert_eq!(sum_area(&set_inserted), sum_area(&inserted));
-        }
-    }
-}
+use integral_geometry::{Rect, Size};
+use std::cmp::{max, min, Ordering};
+
+#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
+struct Fit {
+    short_side: u32,
+    long_side: u32,
+}
+
+impl Fit {
+    fn new() -> Self {
+        Self {
+            short_side: u32::max_value(),
+            long_side: u32::max_value(),
+        }
+    }
+
+    fn measure(container: Size, size: Size) -> Option<Self> {
+        if container.contains(size) {
+            let x_leftover = container.width - size.width;
+            let y_leftover = container.height - size.height;
+            Some(Self {
+                short_side: min(x_leftover, y_leftover) as u32,
+                long_side: max(x_leftover, y_leftover) as u32,
+            })
+        } else {
+            None
+        }
+    }
+}
+
+#[derive(PartialEq, Eq)]
+pub struct UsedSpace {
+    used_area: usize,
+    total_area: usize,
+}
+
+impl UsedSpace {
+    const fn new(used_area: usize, total_area: usize) -> Self {
+        Self {
+            used_area,
+            total_area,
+        }
+    }
+
+    const fn used(&self) -> usize {
+        self.used_area
+    }
+
+    const fn total(&self) -> usize {
+        self.total_area
+    }
+}
+
+impl std::fmt::Debug for UsedSpace {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+        write!(
+            f,
+            "{:.2}%",
+            self.used() as f32 / self.total() as f32 / 100.0
+        )?;
+        Ok(())
+    }
+}
+
+pub struct Atlas {
+    size: Size,
+    free_rects: Vec<Rect>,
+    used_rects: Vec<Rect>,
+    splits: Vec<Rect>,
+}
+
+impl Atlas {
+    pub fn new(size: Size) -> Self {
+        Self {
+            size,
+            free_rects: vec![Rect::at_origin(size)],
+            used_rects: vec![],
+            splits: vec![],
+        }
+    }
+
+    pub fn size(&self) -> Size {
+        self.size
+    }
+
+    pub fn used_space(&self) -> UsedSpace {
+        let used = self.used_rects.iter().map(|r| r.size().area()).sum();
+        UsedSpace::new(used, self.size.area())
+    }
+
+    fn find_position(&self, size: Size) -> Option<(Rect, Fit)> {
+        let mut best_rect = Rect::EMPTY;
+        let mut best_fit = Fit::new();
+
+        for rect in &self.free_rects {
+            if let Some(fit) = Fit::measure(rect.size(), size) {
+                if fit < best_fit {
+                    best_fit = fit;
+                    best_rect = Rect::from_size(rect.top_left(), size);
+                }
+            }
+
+            if let Some(fit) = Fit::measure(rect.size(), size.transpose()) {
+                if fit < best_fit {
+                    best_fit = fit;
+                    best_rect = Rect::from_size(rect.top_left(), size.transpose());
+                }
+            }
+        }
+
+        if best_rect == Rect::EMPTY {
+            None
+        } else {
+            Some((best_rect, best_fit))
+        }
+    }
+
+    fn split_insert(&mut self, rect: Rect) {
+        let mut splits = std::mem::replace(&mut self.splits, vec![]);
+        let mut buffer = [Rect::EMPTY; 4];
+
+        for i in (0..self.free_rects.len()).rev() {
+            if let Some(count) = split_rect(self.free_rects[i], rect, &mut splits, &mut buffer) {
+                self.free_rects.swap_remove(i as usize);
+                splits.extend_from_slice(&buffer[0..count]);
+            }
+        }
+
+        filter_swap_remove(&mut splits, |s| {
+            self.free_rects.iter().any(|r| r.contains_rect(s))
+        });
+        self.free_rects.extend(splits.drain(..));
+        std::mem::replace(&mut self.splits, splits);
+
+        self.used_rects.push(rect);
+    }
+
+    pub fn insert(&mut self, size: Size) -> Option<Rect> {
+        let (rect, _) = self.find_position(size)?;
+        self.split_insert(rect);
+        Some(rect)
+    }
+
+    pub fn insert_set<Iter>(&mut self, sizes: Iter) -> Vec<Rect>
+    where
+        Iter: Iterator<Item = Size>,
+    {
+        let mut sizes: Vec<_> = sizes.collect();
+        let mut result = vec![];
+
+        while let Some((index, (rect, _))) = sizes
+            .iter()
+            .enumerate()
+            .filter_map(|(i, s)| self.find_position(*s).map(|res| (i, res)))
+            .min_by_key(|(_, (_, fit))| fit.clone())
+        {
+            self.split_insert(rect);
+
+            result.push(rect);
+            sizes.swap_remove(index);
+        }
+        result
+    }
+
+    pub fn reset(&mut self) {
+        self.free_rects.clear();
+        self.used_rects.clear();
+        self.free_rects.push(Rect::at_origin(self.size));
+    }
+}
+
+pub struct AtlasCollection {
+    texture_size: Size,
+    atlases: Vec<Atlas>,
+}
+
+impl AtlasCollection {
+    pub fn new(texture_size: Size) -> Self {
+        Self {
+            texture_size,
+            atlases: vec![],
+        }
+    }
+
+    fn repack(&mut self, size: Size) -> bool {
+        for atlas in &mut self.atlases {
+            let mut temp_atlas = Atlas::new(atlas.size());
+            let sizes = atlas
+                .used_rects
+                .iter()
+                .map(|r| r.size())
+                .chain(std::iter::once(size));
+            if !temp_atlas.insert_set(sizes).is_empty() {
+                std::mem::swap(atlas, &mut temp_atlas);
+                return true;
+            }
+        }
+        false
+    }
+
+    pub fn insert_sprite(&mut self, size: Size) -> bool {
+        if !self.texture_size.contains(size) {
+            false
+        } else {
+            if let Some(rect) = self.atlases.iter_mut().find_map(|a| a.insert(size)) {
+
+            } else if !self.repack(size) {
+                let mut atlas = Atlas::new(self.texture_size);
+                atlas.insert(size);
+                self.atlases.push(atlas);
+            }
+            true
+        }
+    }
+}
+
+#[inline]
+fn filter_swap_remove<T, F>(vec: &mut Vec<T>, predicate: F)
+where
+    F: Fn(&T) -> bool,
+{
+    let mut i = 0;
+    while i < vec.len() {
+        if predicate(&vec[i]) {
+            vec.swap_remove(i);
+        } else {
+            i += 1;
+        }
+    }
+}
+
+#[inline]
+fn prune_push(
+    previous_splits: &mut Vec<Rect>,
+    buffer: &mut [Rect; 4],
+    buffer_size: &mut usize,
+    rect: Rect,
+) {
+    if !previous_splits.iter().any(|r| r.contains_rect(&rect)) {
+        filter_swap_remove(previous_splits, |s| rect.contains_rect(s));
+        buffer[*buffer_size] = rect;
+        *buffer_size += 1;
+    }
+}
+
+fn split_rect(
+    free_rect: Rect,
+    rect: Rect,
+    previous_splits: &mut Vec<Rect>,
+    buffer: &mut [Rect; 4],
+) -> Option<usize> {
+    let mut buffer_size = 0usize;
+    let split = free_rect.intersects(&rect);
+    if split {
+        if rect.left() > free_rect.left() {
+            let trim = free_rect.right() - rect.left() + 1;
+            prune_push(
+                previous_splits,
+                buffer,
+                &mut buffer_size,
+                free_rect.with_margins(0, -trim, 0, 0),
+            );
+        }
+        if rect.right() < free_rect.right() {
+            let trim = rect.right() - free_rect.left() + 1;
+            prune_push(
+                previous_splits,
+                buffer,
+                &mut buffer_size,
+                free_rect.with_margins(-trim, 0, 0, 0),
+            );
+        }
+        if rect.top() > free_rect.top() {
+            let trim = free_rect.bottom() - rect.top() + 1;
+            prune_push(
+                previous_splits,
+                buffer,
+                &mut buffer_size,
+                free_rect.with_margins(0, 0, 0, -trim),
+            );;
+        }
+        if rect.bottom() < free_rect.bottom() {
+            let trim = rect.bottom() - free_rect.top() + 1;
+            prune_push(
+                previous_splits,
+                buffer,
+                &mut buffer_size,
+                free_rect.with_margins(0, 0, -trim, 0),
+            );;
+        }
+    }
+    if split {
+        Some(buffer_size)
+    } else {
+        None
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::Atlas;
+    use integral_geometry::{Rect, Size};
+    use itertools::Itertools as _;
+    use proptest::prelude::*;
+
+    #[test]
+    fn insert() {
+        let atlas_size = Size::square(16);
+        let mut atlas = Atlas::new(atlas_size);
+
+        assert_eq!(None, atlas.insert(Size::square(20)));
+
+        let rect_size = Size::new(11, 3);
+        let rect = atlas.insert(rect_size).unwrap();
+
+        assert_eq!(rect, Rect::at_origin(rect_size));
+        assert_eq!(2, atlas.free_rects.len());
+    }
+
+    #[derive(Debug, Clone)]
+    struct TestRect(Size);
+    struct TestRectParameters(Size);
+
+    impl Default for TestRectParameters {
+        fn default() -> Self {
+            Self(Size::square(64))
+        }
+    }
+
+    impl Arbitrary for TestRect {
+        type Parameters = TestRectParameters;
+
+        fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
+            (1..=args.0.width, 1..=args.0.height)
+                .prop_map(|(w, h)| TestRect(Size::new(w, h)))
+                .boxed()
+        }
+
+        type Strategy = BoxedStrategy<TestRect>;
+    }
+
+    trait HasSize {
+        fn size(&self) -> Size;
+    }
+
+    impl HasSize for TestRect {
+        fn size(&self) -> Size {
+            self.0
+        }
+    }
+
+    impl HasSize for Rect {
+        fn size(&self) -> Size {
+            self.size()
+        }
+    }
+
+    fn sum_area<S: HasSize>(items: &[S]) -> usize {
+        items.iter().map(|s| s.size().area()).sum()
+    }
+
+    proptest! {
+        #[test]
+        fn prop_insert(rects in Vec::<TestRect>::arbitrary()) {
+            let container = Rect::at_origin(Size::square(2048));
+            let mut atlas = Atlas::new(container.size());
+            let inserted: Vec<_> = rects.iter().filter_map(|TestRect(size)| atlas.insert(*size)).collect();
+
+            let mut inserted_pairs = inserted.iter().cartesian_product(inserted.iter());
+
+            assert!(inserted.iter().all(|r| container.contains_rect(r)));
+            assert!(inserted_pairs.all(|(r1, r2)| r1 == r2 || r1 != r2 && !r1.intersects(r2)));
+
+            assert_eq!(inserted.len(), rects.len());
+            assert_eq!(sum_area(&inserted), sum_area(&rects));
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn prop_insert_set(rects in Vec::<TestRect>::arbitrary()) {
+            let container = Rect::at_origin(Size::square(2048));
+            let mut atlas = Atlas::new(container.size());
+            let mut set_atlas = Atlas::new(container.size());
+
+            let inserted: Vec<_> = rects.iter().filter_map(|TestRect(size)| atlas.insert(*size)).collect();
+            let set_inserted: Vec<_> = set_atlas.insert_set(rects.iter().map(|TestRect(size)| *size));
+
+            let mut set_inserted_pairs = set_inserted.iter().cartesian_product(set_inserted.iter());
+
+            assert!(set_inserted_pairs.all(|(r1, r2)| r1 == r2 || r1 != r2 && !r1.intersects(r2)));
+            assert!(set_atlas.used_space().used() <= atlas.used_space().used());
+
+            assert_eq!(sum_area(&set_inserted), sum_area(&inserted));
+        }
+    }
+}
--- a/rust/lib-hedgewars-engine/src/render/camera.rs	Tue Jun 04 23:19:18 2019 +0300
+++ b/rust/lib-hedgewars-engine/src/render/camera.rs	Tue Jun 04 22:34:42 2019 +0200
@@ -1,64 +1,64 @@
-use integral_geometry::{Point, Rect, Size};
-
-#[derive(Debug)]
-pub struct Camera {
-    pub position: Point,
-    pub zoom: f32,
-    size: Size,
-}
-
-impl Camera {
-    pub fn new() -> Self {
-        Self::with_size(Size::new(1024, 768))
-    }
-
-    pub fn with_size(size: Size) -> Self {
-        Self {
-            position: Point::ZERO,
-            zoom: 1.0,
-            size,
-        }
-    }
-
-    pub fn viewport(&self) -> Rect {
-        #[inline]
-        fn scale(value: usize, zoom: f32) -> i32 {
-            (value as f32 / zoom / 2.0) as i32
-        }
-        let half_width = scale(self.size.width, self.zoom);
-        let half_height = scale(self.size.height, self.zoom);
-        Rect::from_box(
-            self.position.x - half_width,
-            self.position.x + half_width,
-            self.position.y - half_height,
-            self.position.y + half_height,
-        )
-    }
-
-    pub fn projection(&self) -> [f32; 16] {
-        let viewport = self.viewport();
-        let left = viewport.left() as f32;
-        let width = viewport.width() as f32;
-        let height = viewport.height() as f32;
-        let top = viewport.top() as f32;
-
-        [
-            2f32 / width,
-            0f32,
-            0f32,
-            0f32,
-            0f32,
-            2f32 / -height,
-            0f32,
-            0f32,
-            0f32,
-            0f32,
-            0.5f32,
-            0f32,
-            -(2.0 * left + width) / width,
-            (2.0 * top + height) / height,
-            0.5f32,
-            1f32,
-        ]
-    }
-}
+use integral_geometry::{Point, Rect, Size};
+
+#[derive(Debug)]
+pub struct Camera {
+    pub position: Point,
+    pub zoom: f32,
+    size: Size,
+}
+
+impl Camera {
+    pub fn new() -> Self {
+        Self::with_size(Size::new(1024, 768))
+    }
+
+    pub fn with_size(size: Size) -> Self {
+        Self {
+            position: Point::ZERO,
+            zoom: 1.0,
+            size,
+        }
+    }
+
+    pub fn viewport(&self) -> Rect {
+        #[inline]
+        fn scale(value: usize, zoom: f32) -> i32 {
+            (value as f32 / zoom / 2.0) as i32
+        }
+        let half_width = scale(self.size.width, self.zoom);
+        let half_height = scale(self.size.height, self.zoom);
+        Rect::from_box(
+            self.position.x - half_width,
+            self.position.x + half_width,
+            self.position.y - half_height,
+            self.position.y + half_height,
+        )
+    }
+
+    pub fn projection(&self) -> [f32; 16] {
+        let viewport = self.viewport();
+        let left = viewport.left() as f32;
+        let width = viewport.width() as f32;
+        let height = viewport.height() as f32;
+        let top = viewport.top() as f32;
+
+        [
+            2f32 / width,
+            0f32,
+            0f32,
+            0f32,
+            0f32,
+            2f32 / -height,
+            0f32,
+            0f32,
+            0f32,
+            0f32,
+            0.5f32,
+            0f32,
+            -(2.0 * left + width) / width,
+            (2.0 * top + height) / height,
+            0.5f32,
+            1f32,
+        ]
+    }
+}
--- a/rust/lib-hedgewars-engine/src/render/gear.rs	Tue Jun 04 23:19:18 2019 +0300
+++ b/rust/lib-hedgewars-engine/src/render/gear.rs	Tue Jun 04 22:34:42 2019 +0200
@@ -1,15 +1,15 @@
-use super::atlas::AtlasCollection;
-use integral_geometry::Size;
-
-struct GearRenderer {
-    atlas: AtlasCollection,
-}
-
-const ATLAS_SIZE: Size = Size::square(2048);
-
-impl GearRenderer {
-    pub fn new() -> Self {
-        let atlas = AtlasCollection::new(ATLAS_SIZE);
-        Self { atlas }
-    }
-}
+use super::atlas::AtlasCollection;
+use integral_geometry::Size;
+
+struct GearRenderer {
+    atlas: AtlasCollection,
+}
+
+const ATLAS_SIZE: Size = Size::square(2048);
+
+impl GearRenderer {
+    pub fn new() -> Self {
+        let atlas = AtlasCollection::new(ATLAS_SIZE);
+        Self { atlas }
+    }
+}
--- a/rust/mapgen/src/theme.rs	Tue Jun 04 23:19:18 2019 +0300
+++ b/rust/mapgen/src/theme.rs	Tue Jun 04 22:34:42 2019 +0200
@@ -1,194 +1,194 @@
-use png::{ColorType, Decoder, DecodingError};
-use std::{
-    fs::{read_dir, File},
-    io,
-    io::BufReader,
-    path::Path,
-    slice::{from_raw_parts, from_raw_parts_mut},
-};
-
-use integral_geometry::Size;
-use vec2d::Vec2D;
-
-pub struct ThemeSprite {
-    pixels: Vec2D<u32>,
-}
-
-impl ThemeSprite {
-    #[inline]
-    pub fn size(&self) -> Size {
-        self.pixels.size()
-    }
-
-    #[inline]
-    pub fn width(&self) -> usize {
-        self.size().width
-    }
-
-    #[inline]
-    pub fn height(&self) -> usize {
-        self.size().height
-    }
-
-    #[inline]
-    pub fn rows(&self) -> impl DoubleEndedIterator<Item = &[u32]> {
-        self.pixels.rows()
-    }
-
-    #[inline]
-    pub fn get_row(&self, index: usize) -> &[u32] {
-        &self.pixels[index]
-    }
-
-    #[inline]
-    pub fn get_pixel(&self, x: usize, y: usize) -> u32 {
-        self.pixels[y][x]
-    }
-
-    pub fn to_transposed(&self) -> ThemeSprite {
-        let size = self.size().transpose();
-        let mut pixels = Vec2D::new(size, 0u32);
-        for (y, row) in self.pixels.rows().enumerate() {
-            for (x, v) in row.iter().enumerate() {
-                pixels[x][y] = *v;
-            }
-        }
-        ThemeSprite { pixels }
-    }
-
-    pub fn to_tiled(&self) -> TiledSprite {
-        let size = self.size();
-        assert!(size.is_power_of_two());
-        let tile_width_shift = size.width.trailing_zeros() as usize + 2;
-        let mut pixels = vec![0u32; size.area()];
-
-        for (y, row) in self.pixels.rows().enumerate() {
-            for (x, v) in row.iter().enumerate() {
-                pixels[get_tiled_index(x, y, tile_width_shift)] = *v;
-            }
-        }
-
-        TiledSprite {
-            tile_width_shift,
-            size,
-            pixels,
-        }
-    }
-}
-
-#[inline]
-fn get_tiled_index(x: usize, y: usize, tile_width_shift: usize) -> usize {
-    (((y >> 2) << tile_width_shift) + ((x >> 2) << 4)) + ((y & 0b11) << 2) + (x & 0b11)
-}
-
-pub struct TiledSprite {
-    tile_width_shift: usize,
-    size: Size,
-    pixels: Vec<u32>,
-}
-
-impl TiledSprite {
-    #[inline]
-    pub fn size(&self) -> Size {
-        self.size
-    }
-
-    #[inline]
-    pub fn width(&self) -> usize {
-        self.size().width
-    }
-
-    #[inline]
-    pub fn height(&self) -> usize {
-        self.size().height
-    }
-
-    #[inline]
-    pub fn get_pixel(&self, x: usize, y: usize) -> u32 {
-        self.pixels[get_tiled_index(x, y, self.tile_width_shift)]
-    }
-}
-
-pub struct Theme {
-    land_texture: Option<ThemeSprite>,
-    border_texture: Option<ThemeSprite>,
-}
-
-impl Theme {
-    pub fn land_texture(&self) -> Option<&ThemeSprite> {
-        self.land_texture.as_ref()
-    }
-
-    pub fn border_texture(&self) -> Option<&ThemeSprite> {
-        self.border_texture.as_ref()
-    }
-}
-
-#[derive(Debug)]
-pub enum ThemeLoadError {
-    File(io::Error),
-    Decoding(DecodingError),
-    Format(String),
-}
-
-impl From<io::Error> for ThemeLoadError {
-    fn from(e: io::Error) -> Self {
-        ThemeLoadError::File(e)
-    }
-}
-
-impl From<DecodingError> for ThemeLoadError {
-    fn from(e: DecodingError) -> Self {
-        ThemeLoadError::Decoding(e)
-    }
-}
-
-impl Theme {
-    pub fn new() -> Self {
-        Theme {
-            land_texture: None,
-            border_texture: None,
-        }
-    }
-
-    pub fn load(path: &Path) -> Result<Theme, ThemeLoadError> {
-        let mut theme = Self::new();
-
-        for entry in read_dir(path)? {
-            let file = entry?;
-            if file.file_name() == "LandTex.png" {
-                theme.land_texture = Some(load_sprite(&file.path())?)
-            } else if file.file_name() == "Border.png" {
-                theme.border_texture = Some(load_sprite(&file.path())?)
-            }
-        }
-
-        Ok(theme)
-    }
-}
-
-fn load_sprite(path: &Path) -> Result<ThemeSprite, ThemeLoadError> {
-    let decoder = Decoder::new(BufReader::new(File::open(path)?));
-    let (info, mut reader) = decoder.read_info()?;
-
-    if info.color_type != ColorType::RGBA {
-        return Err(ThemeLoadError::Format(format!(
-            "Unexpected format: {:?}",
-            info.color_type
-        )));
-    }
-    let size = Size::new(info.width as usize, info.height as usize);
-
-    let mut pixels: Vec2D<u32> = Vec2D::new(size, 0);
-    reader.next_frame(slice_u32_to_u8_mut(pixels.as_mut_slice()))?;
-
-    Ok(ThemeSprite { pixels })
-}
-
-pub fn slice_u32_to_u8(slice_u32: &[u32]) -> &[u8] {
-    unsafe { from_raw_parts::<u8>(slice_u32.as_ptr() as *const u8, slice_u32.len() * 4) }
-}
-
-pub fn slice_u32_to_u8_mut(slice_u32: &mut [u32]) -> &mut [u8] {
-    unsafe { from_raw_parts_mut::<u8>(slice_u32.as_mut_ptr() as *mut u8, slice_u32.len() * 4) }
-}
+use png::{ColorType, Decoder, DecodingError};
+use std::{
+    fs::{read_dir, File},
+    io,
+    io::BufReader,
+    path::Path,
+    slice::{from_raw_parts, from_raw_parts_mut},
+};
+
+use integral_geometry::Size;
+use vec2d::Vec2D;
+
+pub struct ThemeSprite {
+    pixels: Vec2D<u32>,
+}
+
+impl ThemeSprite {
+    #[inline]
+    pub fn size(&self) -> Size {
+        self.pixels.size()
+    }
+
+    #[inline]
+    pub fn width(&self) -> usize {
+        self.size().width
+    }
+
+    #[inline]
+    pub fn height(&self) -> usize {
+        self.size().height
+    }
+
+    #[inline]
+    pub fn rows(&self) -> impl DoubleEndedIterator<Item = &[u32]> {
+        self.pixels.rows()
+    }
+
+    #[inline]
+    pub fn get_row(&self, index: usize) -> &[u32] {
+        &self.pixels[index]
+    }
+
+    #[inline]
+    pub fn get_pixel(&self, x: usize, y: usize) -> u32 {
+        self.pixels[y][x]
+    }
+
+    pub fn to_transposed(&self) -> ThemeSprite {
+        let size = self.size().transpose();
+        let mut pixels = Vec2D::new(size, 0u32);
+        for (y, row) in self.pixels.rows().enumerate() {
+            for (x, v) in row.iter().enumerate() {
+                pixels[x][y] = *v;
+            }
+        }
+        ThemeSprite { pixels }
+    }
+
+    pub fn to_tiled(&self) -> TiledSprite {
+        let size = self.size();
+        assert!(size.is_power_of_two());
+        let tile_width_shift = size.width.trailing_zeros() as usize + 2;
+        let mut pixels = vec![0u32; size.area()];
+
+        for (y, row) in self.pixels.rows().enumerate() {
+            for (x, v) in row.iter().enumerate() {
+                pixels[get_tiled_index(x, y, tile_width_shift)] = *v;
+            }
+        }
+
+        TiledSprite {
+            tile_width_shift,
+            size,
+            pixels,
+        }
+    }
+}
+
+#[inline]
+fn get_tiled_index(x: usize, y: usize, tile_width_shift: usize) -> usize {
+    (((y >> 2) << tile_width_shift) + ((x >> 2) << 4)) + ((y & 0b11) << 2) + (x & 0b11)
+}
+
+pub struct TiledSprite {
+    tile_width_shift: usize,
+    size: Size,
+    pixels: Vec<u32>,
+}
+
+impl TiledSprite {
+    #[inline]
+    pub fn size(&self) -> Size {
+        self.size
+    }
+
+    #[inline]
+    pub fn width(&self) -> usize {
+        self.size().width
+    }
+
+    #[inline]
+    pub fn height(&self) -> usize {
+        self.size().height
+    }
+
+    #[inline]
+    pub fn get_pixel(&self, x: usize, y: usize) -> u32 {
+        self.pixels[get_tiled_index(x, y, self.tile_width_shift)]
+    }
+}
+
+pub struct Theme {
+    land_texture: Option<ThemeSprite>,
+    border_texture: Option<ThemeSprite>,
+}
+
+impl Theme {
+    pub fn land_texture(&self) -> Option<&ThemeSprite> {
+        self.land_texture.as_ref()
+    }
+
+    pub fn border_texture(&self) -> Option<&ThemeSprite> {
+        self.border_texture.as_ref()
+    }
+}
+
+#[derive(Debug)]
+pub enum ThemeLoadError {
+    File(io::Error),
+    Decoding(DecodingError),
+    Format(String),
+}
+
+impl From<io::Error> for ThemeLoadError {
+    fn from(e: io::Error) -> Self {
+        ThemeLoadError::File(e)
+    }
+}
+
+impl From<DecodingError> for ThemeLoadError {
+    fn from(e: DecodingError) -> Self {
+        ThemeLoadError::Decoding(e)
+    }
+}
+
+impl Theme {
+    pub fn new() -> Self {
+        Theme {
+            land_texture: None,
+            border_texture: None,
+        }
+    }
+
+    pub fn load(path: &Path) -> Result<Theme, ThemeLoadError> {
+        let mut theme = Self::new();
+
+        for entry in read_dir(path)? {
+            let file = entry?;
+            if file.file_name() == "LandTex.png" {
+                theme.land_texture = Some(load_sprite(&file.path())?)
+            } else if file.file_name() == "Border.png" {
+                theme.border_texture = Some(load_sprite(&file.path())?)
+            }
+        }
+
+        Ok(theme)
+    }
+}
+
+fn load_sprite(path: &Path) -> Result<ThemeSprite, ThemeLoadError> {
+    let decoder = Decoder::new(BufReader::new(File::open(path)?));
+    let (info, mut reader) = decoder.read_info()?;
+
+    if info.color_type != ColorType::RGBA {
+        return Err(ThemeLoadError::Format(format!(
+            "Unexpected format: {:?}",
+            info.color_type
+        )));
+    }
+    let size = Size::new(info.width as usize, info.height as usize);
+
+    let mut pixels: Vec2D<u32> = Vec2D::new(size, 0);
+    reader.next_frame(slice_u32_to_u8_mut(pixels.as_mut_slice()))?;
+
+    Ok(ThemeSprite { pixels })
+}
+
+pub fn slice_u32_to_u8(slice_u32: &[u32]) -> &[u8] {
+    unsafe { from_raw_parts::<u8>(slice_u32.as_ptr() as *const u8, slice_u32.len() * 4) }
+}
+
+pub fn slice_u32_to_u8_mut(slice_u32: &mut [u32]) -> &mut [u8] {
+    unsafe { from_raw_parts_mut::<u8>(slice_u32.as_mut_ptr() as *mut u8, slice_u32.len() * 4) }
+}