<commit_before>use super::cpu::*;
use super::memory::Memory;
use super::registers::{Registers, Z_FLAG_MASK, N_FLAG_MASK, H_FLAG_MASK,
    C_FLAG_MASK};

//
// --- Helper functions ---
//

// Swap the byte's nibbles, reset the NHC flags and set the Z flag in f.
fn swap(f: &mut u8, val: &mut u8) {
    *f = 0;
    let x1 = (*val & 0x0F) << 4;
    let x2 = (*val >> 4) & 0x0F;
    *val = x1 | x2;
    if *val == 0 {
        *f |= Z_FLAG_MASK;
    }
}

// The CB-prefixed opcodes are implemented in this crate for better clarity in
// the code. Notations used :
// - (X) means the value stored in memory at the X address
#[allow(non_snake_case)]
impl<M> Cpu<M> where M: Memory {

    //
    // --- SWAP ---
    //

    // SWAP_r_b : swap register X's nibbles, reset NHC flags and set Z flag
    pub fn SWAP_r_b(&mut self) -> CycleType { swap(&mut self.regs.f, &mut self.regs.b); 2 }
    pub fn SWAP_r_c(&mut self) -> CycleType { swap(&mut self.regs.f, &mut self.regs.c); 2 }
    pub fn SWAP_r_d(&mut self) -> CycleType { swap(&mut self.regs.f, &mut self.regs.d); 2 }
    pub fn SWAP_r_e(&mut self) -> CycleType { swap(&mut self.regs.f, &mut self.regs.e); 2 }
    pub fn SWAP_r_h(&mut self) -> CycleType { swap(&mut self.regs.f, &mut self.regs.h); 2 }
    pub fn SWAP_r_l(&mut self) -> CycleType { swap(&mut self.regs.f, &mut self.regs.l); 2 }
    pub fn SWAP_r_a(&mut self) -> CycleType { swap(&mut self.regs.f, &mut self.regs.a); 2 }
    // SWAP_HLm : same but for (HL)
    pub fn SWAP_HLm(&mut self) -> CycleType {
        let address = (self.regs.h as u16) << 8 + self.regs.l as u16;
        let mut temp_byte = self.mem.read_byte(address);
        swap(&mut self.regs.f, &mut temp_byte);
        self.mem.write_byte(address, temp_byte);
        4
    }
}
<commit_msg>CB-SWAP instructions now implemented with a macro<commit_after>use super::cpu::*;
use super::memory::Memory;
use super::registers::{Registers, Z_FLAG_MASK, N_FLAG_MASK, H_FLAG_MASK,
    C_FLAG_MASK};

//
// --- Helper macros ---
//

// Swap the byte's nibbles, reset the NHC flags and set the Z flag in f.
macro_rules! swap {
    ($s: ident, $x: expr) => ({
        $s.regs.f = 0x0;
        let x1 = ($x & 0x0F) << 4;
        let x2 = ($x >> 4) & 0x0F;
        $x = x1 | x2;
        if $x == 0 {
            $s.regs.f |= Z_FLAG_MASK;
        }
    })
}

// The CB-prefixed opcodes are implemented in this crate for better clarity in
// the code. Notations used :
// - (X) means the value stored in memory at the X address
#[allow(non_snake_case)]
impl<M> Cpu<M> where M: Memory {

    //
    // --- SWAP ---
    //

    // SWAP_r_b : swap register X's nibbles, reset NHC flags and set Z flag
    pub fn SWAP_r_b(&mut self) -> CycleType { swap!(self, self.regs.b); 2 }
    pub fn SWAP_r_c(&mut self) -> CycleType { swap!(self, self.regs.c); 2 }
    pub fn SWAP_r_d(&mut self) -> CycleType { swap!(self, self.regs.d); 2 }
    pub fn SWAP_r_e(&mut self) -> CycleType { swap!(self, self.regs.e); 2 }
    pub fn SWAP_r_h(&mut self) -> CycleType { swap!(self, self.regs.h); 2 }
    pub fn SWAP_r_l(&mut self) -> CycleType { swap!(self, self.regs.l); 2 }
    pub fn SWAP_r_a(&mut self) -> CycleType { swap!(self, self.regs.a); 2 }
    // SWAP_HLm : same but for (HL)
    pub fn SWAP_HLm(&mut self) -> CycleType {
        let address = (self.regs.h as u16) << 8 + self.regs.l as u16;
        let mut temp_byte = self.mem.read_byte(address);
        swap!(self, temp_byte);
        self.mem.write_byte(address, temp_byte);
        4
    }
}
<|endoftext|>


<commit_before>use alga::general::SupersetOf;
use nalgebra::{Point3, Scalar, Unit};
use num::{Bounded, One, Zero}; // TODO: `use ::std::num::{One, Zero};`.
use std::ops::Range;

use clamp::{Clamped, ClampedRange};
use math::{self, FRay3, FPoint3, FromSpace, FScalar, FVector3, LowerBound, Mask, UPoint3,
           UpperBound, UScalar, UVector3};

/// Defines the bounds for `LogWidth` values.
pub struct LogWidthRange;

impl ClampedRange<u8> for LogWidthRange {
    fn max_value() -> u8 {
        31
    }

    fn min_value() -> u8 {
        4
    }
}

/// Logarithmic width. Most uses of the term "width" refer to `LogWidth`.
///
/// This is the base-2 logarithmic width of a space in a tree, such that a width
/// `x` exponentiates as `2ˣ`. Exponentiation is performed as a simple bit
/// shift.
///
/// Being logarithmic, if a cube has a width `x`, then its immediate
/// subdivisions have a width `x - 1` and its parent a width `x + 1`.
///
/// `LogWidth` is clamped to the open range `[4, 31]`.
pub type LogWidth = Clamped<u8, LogWidthRange>;

impl LogWidth {
    /// Exponentiates a `LogWidth` into a `UScalar`.
    pub fn exp(&self) -> UScalar {
        UScalar::one() << self.to_inner()
    }
}

/// An axis in the tree space.
///
/// Defines and orders the axes in the tree space.
#[derive(Clone, Copy)]
pub enum Axis {
    X = 0,
    Y = 1,
    Z = 2,
}

impl Axis {
    /// Gets a `Range` over axes as `usize`s.
    pub fn range() -> Range<usize> {
        (Axis::X as usize)..(Axis::Z as usize + 1)
    }

    pub fn to_vector(&self) -> FVector3 {
        match *self {
            Axis::X => FVector3::x(),
            Axis::Y => FVector3::y(),
            Axis::Z => FVector3::z(),
        }
    }
}

impl From<usize> for Axis {
    fn from(index: usize) -> Self {
        match index {
            0 => Axis::X,
            1 => Axis::Y,
            2 => Axis::Z,
            _ => panic!(), // TODO: Use `TryFrom`.
        }
    }
}

/// A direction along an `Axis`.
pub enum Direction {
    Positive,
    Negative,
}

/// An orientation in the tree space.
///
/// This is akin to choosing a face of a cube in a tree and orienting that face
/// based on the axis and direction in which it deforms. For example, `Left` is
/// a face along the `X` axis that deforms in the `Positive` direction.
pub enum Orientation {
    Left,
    Right,
    Top,
    Bottom,
    Front,
    Back,
}

impl Orientation {
    /// Gets the `Axis` associated with the `Orientation`.
    pub fn axis(&self) -> Axis {
        match *self {
            Orientation::Left | Orientation::Right => Axis::X,
            Orientation::Top | Orientation::Bottom => Axis::Y,
            Orientation::Front | Orientation::Back => Axis::Z,
        }
    }

    /// Gets the `Direction` associated with the `Orientation`.
    pub fn direction(&self) -> Direction {
        match *self {
            Orientation::Left | Orientation::Bottom | Orientation::Back => Direction::Positive,
            Orientation::Right | Orientation::Top | Orientation::Front => Direction::Negative,
        }
    }
}

/// A shape or primitive that can test for intersection with another shape or
/// primitive.
pub trait Intersects<T> {
    /// Tests for intersection.
    fn intersects(&self, other: &T) -> bool;
}

/// Details of intersection with an `FRay3`.
pub struct RayIntersection {
    /// The minimum time of impact.
    pub distance: FScalar,
    /// The minimum point of intersection.
    pub point: FPoint3,
    /// The normal at the point of intersection.
    pub normal: Unit<FVector3>,
}

impl RayIntersection {
    /// Creates a new `RayIntersection`. Normalizes `normal`.
    fn new(distance: FScalar, point: FPoint3, normal: FVector3) -> Self {
        RayIntersection {
            distance: distance,
            point: point,
            normal: Unit::new_normalize(normal),
        }
    }
}

/// Shape or primitive that can test for intersection with an `FRay3`, omitting
/// some details about the intersection.
pub trait PartialRayCast {
    /// Determines if an `FRay3` intersects the shape or primitive. Returns the
    /// minimum and maximum time of impact (distance), respectively.
    fn partial_ray_intersection(&self, ray: &FRay3) -> Option<(FScalar, FScalar)>;
}

/// Shape or primitive that can test for intersection with an `FRay3`.
pub trait RayCast: PartialRayCast {
    /// Determines if an `FRay3` intersects the shape or primitive and returns
    /// details about the intersection.
    fn ray_intersection(&self, ray: &FRay3) -> Option<RayIntersection>;
}

impl<T> Intersects<FRay3> for T
    where T: PartialRayCast
{
    fn intersects(&self, ray: &FRay3) -> bool {
        self.partial_ray_intersection(ray).is_some()
    }
}

/// Axis-aligned bounding box.
///
/// `AABB`s are represented as an origin and extent.
pub struct AABB {
    pub origin: UPoint3,
    pub extent: UVector3,
}

impl AABB {
    /// Constructs a new `AABB` at the given point in space with the given
    /// extent.
    pub fn new(origin: UPoint3, extent: UVector3) -> Self {
        AABB {
            origin: origin,
            extent: extent,
        }
    }

    /// Constructs the union of two `AABB`s.
    ///
    /// The union is the cuboid formed from the lower and upper bounds of the
    /// `AABB`s.
    pub fn union(&self, other: &Self) -> Self {
        let start = self.origin.lower_bound(&other.origin);
        let end = self.endpoint().upper_bound(&other.endpoint());
        AABB::new(start, end - start)
    }

    /// Gets the midpoint (center) of the `AABB`.
    pub fn midpoint(&self) -> UPoint3 {
        self.origin + (self.extent / 2)
    }

    /// Gets the absolute endpoint of the `AABB`.
    pub fn endpoint(&self) -> UPoint3 {
        self.origin + self.extent
    }

    /// Gets the normal at a point along or near the surface of the `AABB`.
    fn normal(&self, point: &FPoint3) -> FVector3 {
        let point = point - FPoint3::from_space(self.midpoint());
        let mut min_distance = FScalar::max_value();
        let mut normal = FVector3::zero();
        for axis in Axis::range() {
            let distance = (self.extent[axis] as FScalar - point[axis].abs()).abs();
            if distance < min_distance {
                min_distance = distance;
                normal = Axis::from(axis).to_vector() * point[axis].signum();
            }
        }
        normal
    }
}

impl Intersects<AABB> for AABB {
    /// Determines if two `AABB`s intersect.
    fn intersects(&self, aabb: &AABB) -> bool {
        for axis in Axis::range() {
            if (self.origin[axis] + self.extent[axis]) < aabb.origin[axis] {
                return false;
            }
            if self.origin[axis] > (aabb.origin[axis] + aabb.extent[axis]) {
                return false;
            }
        }
        true
    }
}

impl<T> Intersects<Point3<T>> for AABB
    where T: PartialOrd + Scalar + SupersetOf<UScalar>
{
    /// Determines if a point intersects an `AABB`.
    fn intersects(&self, point: &Point3<T>) -> bool {
        use nalgebra::convert;

        for axis in Axis::range() {
            if convert::<UScalar, T>(self.origin[axis] + self.extent[axis]) < point[axis] {
                return false;
            }
            if convert::<UScalar, T>(self.origin[axis]) > point[axis] {
                return false;
            }
        }
        true
    }
}

impl PartialRayCast for AABB {
    /// Determines if an `FRay3` intersects an `AABB`. Returns the minimum and
    /// maximum times of impact as a tuple, respectively.
    fn partial_ray_intersection(&self, ray: &FRay3) -> Option<(FScalar, FScalar)> {
        let mut min = FVector3::zero();
        let mut max = FVector3::zero();
        for axis in Axis::range() {
            let lower = self.origin[axis] as FScalar;
            let upper = lower + self.extent[axis] as FScalar;
            let origin = ray.origin[axis];
            let direction = ray.direction[axis];

            let (low, high) = math::min_max((lower - origin) / direction,
                                            (upper - origin) / direction);
            min[axis] = low;
            max[axis] = high;
        }

        let tmin = math::partial_max(math::partial_max(min.x, min.y), min.z);
        let tmax = math::partial_min(math::partial_min(max.x, max.y), max.z);
        if tmax < 0.0 || tmin > tmax {
            None
        }
        else {
            Some((tmin, tmax))
        }
    }
}

impl RayCast for AABB {
    /// Determines if an `FRay3` intersects an `AABB`. Returns detailed
    /// `RayIntersection` data.
    fn ray_intersection(&self, ray: &FRay3) -> Option<RayIntersection> {
        self.partial_ray_intersection(ray).map(|(distance, _)| {
            let point = ray.origin + (*ray.direction * distance);
            RayIntersection::new(distance, point, self.normal(&point))
        })
    }
}

/// A cubic spatial partition. `Partition`s are represented as an origin and a
/// width.
///
/// `Partition`s are associated with every `Cube` in a tree.
#[derive(Clone, Copy)]
pub struct Partition {
    origin: UPoint3,
    width: LogWidth,
}

impl Partition {
    /// Constructs a new `Partition` at the given point in space with the given
    /// width.
    pub fn at_point(point: &UPoint3, width: LogWidth) -> Self {
        Partition {
            origin: point.mask(!UScalar::zero() << width.to_inner()),
            width: width,
        }
    }

    /// Constructs the sub-`Partition` at the given index. Returns `None` if
    /// there is no such `Partition`, because the minimum width has been
    /// exceeded.
    ///
    /// # Panics
    ///
    /// Panics if `index` is not within the range [0, 8).
    pub fn at_index(&self, index: usize) -> Option<Self> {
        if self.is_min_width() {
            None
        }
        else {
            let width = self.width - 1;
            Some(Partition {
                origin: self.origin + vector_at_index(index, width),
                width: width,
            })
        }
    }

    /// Gets the origin of the `Partition`.
    pub fn origin(&self) -> &UPoint3 {
        &self.origin
    }

    /// Gets the width of the `Partition`.
    pub fn width(&self) -> LogWidth {
        self.width
    }

    /// Gets the midpoint of the `Partition`.
    pub fn midpoint(&self) -> UPoint3 {
        let m = (self.width - 1).exp();
        self.origin + UVector3::new(m, m, m)
    }

    pub fn extent(&self) -> UVector3 {
        (UVector3::new(1, 1, 1) * self.width.exp()) - UVector3::new(1, 1, 1)
    }

    /// Gets the `AABB` of the `Partition`.
    pub fn aabb(&self) -> AABB {
        AABB::new(self.origin, self.extent())
    }

    /// Returns `true` if the `Partition` has the minimum possible width.
    pub fn is_min_width(&self) -> bool {
        self.width == LogWidth::min_value()
    }
}

/// A spatial (cubic) element in a tree.
pub trait Spatial {
    /// Gets the `Partition` of the `Spatial`.
    fn partition(&self) -> &Partition;

    /// Gets the depth of the `Spatial` in the tree.
    fn depth(&self) -> u8;

    /// Gets the `AABB` of the `Spatial`.
    fn aabb(&self) -> AABB {
        self.partition().aabb()
    }
}

/// Gets the subdivision index in a tree for a given point at the given width.
#[cfg_attr(rustfmt, rustfmt_skip)]
pub fn index_at_point(point: &UPoint3, width: LogWidth) -> usize {
    let width = width.to_inner();
    ((((point.x >> width) & UScalar::one()) << 0) |
     (((point.y >> width) & UScalar::one()) << 1) |
     (((point.z >> width) & UScalar::one()) << 2)) as usize
}

/// Gets a vector to the origin of a subdivision in a tree at a given index and
/// width.
pub fn vector_at_index(index: usize, width: LogWidth) -> UVector3 {
    assert!(index < 8);
    let index = index as UScalar;
    let width = width.exp();
    UVector3::new(((index >> 0) & UScalar::one()) * width,
                  ((index >> 1) & UScalar::one()) * width,
                  ((index >> 2) & UScalar::one()) * width)
}

#[cfg(test)]
mod tests {
    use super::*;
}
<commit_msg>Establish a reference width for cubes.<commit_after>use alga::general::SupersetOf;
use nalgebra::{Point3, Scalar, Unit};
use num::{Bounded, One, Zero}; // TODO: `use ::std::num::{One, Zero};`.
use std::ops::Range;

use clamp::{Clamped, ClampedRange};
use math::{self, FRay3, FPoint3, FromSpace, FScalar, FVector3, LowerBound, Mask, UPoint3,
           UpperBound, UScalar, UVector3};

/// Defines the bounds for `LogWidth` values.
pub struct LogWidthRange;

impl ClampedRange<u8> for LogWidthRange {
    fn max_value() -> u8 {
        31
    }

    fn min_value() -> u8 {
        4
    }
}

/// Logarithmic width. Most uses of the term "width" refer to `LogWidth`.
///
/// This is the base-2 logarithmic width of a space in a tree, such that a width
/// `x` exponentiates as `2ˣ`. Exponentiation is performed as a simple bit
/// shift.
///
/// Being logarithmic, if a cube has a width `x`, then its immediate
/// subdivisions have a width `x - 1` and its parent a width `x + 1`.
///
/// `LogWidth` is clamped to the open range `[4, 31]`.
pub type LogWidth = Clamped<u8, LogWidthRange>;

impl LogWidth {
    /// Reference width of `Cube`s.
    pub fn unit() -> Self {
        LogWidth::new(8)
    }

    /// Exponentiates a `LogWidth` into a `UScalar`.
    pub fn exp(&self) -> UScalar {
        UScalar::one() << self.to_inner()
    }
}

/// An axis in the tree space.
///
/// Defines and orders the axes in the tree space.
#[derive(Clone, Copy)]
pub enum Axis {
    X = 0,
    Y = 1,
    Z = 2,
}

impl Axis {
    /// Gets a `Range` over axes as `usize`s.
    pub fn range() -> Range<usize> {
        (Axis::X as usize)..(Axis::Z as usize + 1)
    }

    pub fn to_vector(&self) -> FVector3 {
        match *self {
            Axis::X => FVector3::x(),
            Axis::Y => FVector3::y(),
            Axis::Z => FVector3::z(),
        }
    }
}

impl From<usize> for Axis {
    fn from(index: usize) -> Self {
        match index {
            0 => Axis::X,
            1 => Axis::Y,
            2 => Axis::Z,
            _ => panic!(), // TODO: Use `TryFrom`.
        }
    }
}

/// A direction along an `Axis`.
pub enum Direction {
    Positive,
    Negative,
}

/// An orientation in the tree space.
///
/// This is akin to choosing a face of a cube in a tree and orienting that face
/// based on the axis and direction in which it deforms. For example, `Left` is
/// a face along the `X` axis that deforms in the `Positive` direction.
pub enum Orientation {
    Left,
    Right,
    Top,
    Bottom,
    Front,
    Back,
}

impl Orientation {
    /// Gets the `Axis` associated with the `Orientation`.
    pub fn axis(&self) -> Axis {
        match *self {
            Orientation::Left | Orientation::Right => Axis::X,
            Orientation::Top | Orientation::Bottom => Axis::Y,
            Orientation::Front | Orientation::Back => Axis::Z,
        }
    }

    /// Gets the `Direction` associated with the `Orientation`.
    pub fn direction(&self) -> Direction {
        match *self {
            Orientation::Left | Orientation::Bottom | Orientation::Back => Direction::Positive,
            Orientation::Right | Orientation::Top | Orientation::Front => Direction::Negative,
        }
    }
}

/// A shape or primitive that can test for intersection with another shape or
/// primitive.
pub trait Intersects<T> {
    /// Tests for intersection.
    fn intersects(&self, other: &T) -> bool;
}

/// Details of intersection with an `FRay3`.
pub struct RayIntersection {
    /// The minimum time of impact.
    pub distance: FScalar,
    /// The minimum point of intersection.
    pub point: FPoint3,
    /// The normal at the point of intersection.
    pub normal: Unit<FVector3>,
}

impl RayIntersection {
    /// Creates a new `RayIntersection`. Normalizes `normal`.
    fn new(distance: FScalar, point: FPoint3, normal: FVector3) -> Self {
        RayIntersection {
            distance: distance,
            point: point,
            normal: Unit::new_normalize(normal),
        }
    }
}

/// Shape or primitive that can test for intersection with an `FRay3`, omitting
/// some details about the intersection.
pub trait PartialRayCast {
    /// Determines if an `FRay3` intersects the shape or primitive. Returns the
    /// minimum and maximum time of impact (distance), respectively.
    fn partial_ray_intersection(&self, ray: &FRay3) -> Option<(FScalar, FScalar)>;
}

/// Shape or primitive that can test for intersection with an `FRay3`.
pub trait RayCast: PartialRayCast {
    /// Determines if an `FRay3` intersects the shape or primitive and returns
    /// details about the intersection.
    fn ray_intersection(&self, ray: &FRay3) -> Option<RayIntersection>;
}

impl<T> Intersects<FRay3> for T
    where T: PartialRayCast
{
    fn intersects(&self, ray: &FRay3) -> bool {
        self.partial_ray_intersection(ray).is_some()
    }
}

/// Axis-aligned bounding box.
///
/// `AABB`s are represented as an origin and extent.
pub struct AABB {
    pub origin: UPoint3,
    pub extent: UVector3,
}

impl AABB {
    /// Constructs a new `AABB` at the given point in space with the given
    /// extent.
    pub fn new(origin: UPoint3, extent: UVector3) -> Self {
        AABB {
            origin: origin,
            extent: extent,
        }
    }

    /// Constructs the union of two `AABB`s.
    ///
    /// The union is the cuboid formed from the lower and upper bounds of the
    /// `AABB`s.
    pub fn union(&self, other: &Self) -> Self {
        let start = self.origin.lower_bound(&other.origin);
        let end = self.endpoint().upper_bound(&other.endpoint());
        AABB::new(start, end - start)
    }

    /// Gets the midpoint (center) of the `AABB`.
    pub fn midpoint(&self) -> UPoint3 {
        self.origin + (self.extent / 2)
    }

    /// Gets the absolute endpoint of the `AABB`.
    pub fn endpoint(&self) -> UPoint3 {
        self.origin + self.extent
    }

    /// Gets the normal at a point along or near the surface of the `AABB`.
    fn normal(&self, point: &FPoint3) -> FVector3 {
        let point = point - FPoint3::from_space(self.midpoint());
        let mut min_distance = FScalar::max_value();
        let mut normal = FVector3::zero();
        for axis in Axis::range() {
            let distance = (self.extent[axis] as FScalar - point[axis].abs()).abs();
            if distance < min_distance {
                min_distance = distance;
                normal = Axis::from(axis).to_vector() * point[axis].signum();
            }
        }
        normal
    }
}

impl Intersects<AABB> for AABB {
    /// Determines if two `AABB`s intersect.
    fn intersects(&self, aabb: &AABB) -> bool {
        for axis in Axis::range() {
            if (self.origin[axis] + self.extent[axis]) < aabb.origin[axis] {
                return false;
            }
            if self.origin[axis] > (aabb.origin[axis] + aabb.extent[axis]) {
                return false;
            }
        }
        true
    }
}

impl<T> Intersects<Point3<T>> for AABB
    where T: PartialOrd + Scalar + SupersetOf<UScalar>
{
    /// Determines if a point intersects an `AABB`.
    fn intersects(&self, point: &Point3<T>) -> bool {
        use nalgebra::convert;

        for axis in Axis::range() {
            if convert::<UScalar, T>(self.origin[axis] + self.extent[axis]) < point[axis] {
                return false;
            }
            if convert::<UScalar, T>(self.origin[axis]) > point[axis] {
                return false;
            }
        }
        true
    }
}

impl PartialRayCast for AABB {
    /// Determines if an `FRay3` intersects an `AABB`. Returns the minimum and
    /// maximum times of impact as a tuple, respectively.
    fn partial_ray_intersection(&self, ray: &FRay3) -> Option<(FScalar, FScalar)> {
        let mut min = FVector3::zero();
        let mut max = FVector3::zero();
        for axis in Axis::range() {
            let lower = self.origin[axis] as FScalar;
            let upper = lower + self.extent[axis] as FScalar;
            let origin = ray.origin[axis];
            let direction = ray.direction[axis];

            let (low, high) = math::min_max((lower - origin) / direction,
                                            (upper - origin) / direction);
            min[axis] = low;
            max[axis] = high;
        }

        let tmin = math::partial_max(math::partial_max(min.x, min.y), min.z);
        let tmax = math::partial_min(math::partial_min(max.x, max.y), max.z);
        if tmax < 0.0 || tmin > tmax {
            None
        }
        else {
            Some((tmin, tmax))
        }
    }
}

impl RayCast for AABB {
    /// Determines if an `FRay3` intersects an `AABB`. Returns detailed
    /// `RayIntersection` data.
    fn ray_intersection(&self, ray: &FRay3) -> Option<RayIntersection> {
        self.partial_ray_intersection(ray).map(|(distance, _)| {
            let point = ray.origin + (*ray.direction * distance);
            RayIntersection::new(distance, point, self.normal(&point))
        })
    }
}

/// A cubic spatial partition. `Partition`s are represented as an origin and a
/// width.
///
/// `Partition`s are associated with every `Cube` in a tree.
#[derive(Clone, Copy)]
pub struct Partition {
    origin: UPoint3,
    width: LogWidth,
}

impl Partition {
    /// Constructs a new `Partition` at the given point in space with the given
    /// width.
    pub fn at_point(point: &UPoint3, width: LogWidth) -> Self {
        Partition {
            origin: point.mask(!UScalar::zero() << width.to_inner()),
            width: width,
        }
    }

    /// Constructs the sub-`Partition` at the given index. Returns `None` if
    /// there is no such `Partition`, because the minimum width has been
    /// exceeded.
    ///
    /// # Panics
    ///
    /// Panics if `index` is not within the range [0, 8).
    pub fn at_index(&self, index: usize) -> Option<Self> {
        if self.is_min_width() {
            None
        }
        else {
            let width = self.width - 1;
            Some(Partition {
                origin: self.origin + vector_at_index(index, width),
                width: width,
            })
        }
    }

    /// Gets the origin of the `Partition`.
    pub fn origin(&self) -> &UPoint3 {
        &self.origin
    }

    /// Gets the width of the `Partition`.
    pub fn width(&self) -> LogWidth {
        self.width
    }

    /// Gets the midpoint of the `Partition`.
    pub fn midpoint(&self) -> UPoint3 {
        let m = (self.width - 1).exp();
        self.origin + UVector3::new(m, m, m)
    }

    pub fn extent(&self) -> UVector3 {
        (UVector3::new(1, 1, 1) * self.width.exp()) - UVector3::new(1, 1, 1)
    }

    /// Gets the `AABB` of the `Partition`.
    pub fn aabb(&self) -> AABB {
        AABB::new(self.origin, self.extent())
    }

    /// Returns `true` if the `Partition` has the minimum possible width.
    pub fn is_min_width(&self) -> bool {
        self.width == LogWidth::min_value()
    }
}

/// A spatial (cubic) element in a tree.
pub trait Spatial {
    /// Gets the `Partition` of the `Spatial`.
    fn partition(&self) -> &Partition;

    /// Gets the depth of the `Spatial` in the tree.
    fn depth(&self) -> u8;

    /// Gets the `AABB` of the `Spatial`.
    fn aabb(&self) -> AABB {
        self.partition().aabb()
    }
}

/// Gets the subdivision index in a tree for a given point at the given width.
#[cfg_attr(rustfmt, rustfmt_skip)]
pub fn index_at_point(point: &UPoint3, width: LogWidth) -> usize {
    let width = width.to_inner();
    ((((point.x >> width) & UScalar::one()) << 0) |
     (((point.y >> width) & UScalar::one()) << 1) |
     (((point.z >> width) & UScalar::one()) << 2)) as usize
}

/// Gets a vector to the origin of a subdivision in a tree at a given index and
/// width.
pub fn vector_at_index(index: usize, width: LogWidth) -> UVector3 {
    assert!(index < 8);
    let index = index as UScalar;
    let width = width.exp();
    UVector3::new(((index >> 0) & UScalar::one()) * width,
                  ((index >> 1) & UScalar::one()) * width,
                  ((index >> 2) & UScalar::one()) * width)
}

#[cfg(test)]
mod tests {
    use super::*;
}
<|endoftext|>


<commit_before>extern crate nalgebra;
extern crate num;

use num::{One, Zero}; // TODO: `use ::std::num::{One, Zero};`.
use std::ops;

use math::{Clamp, Mask, UPoint3, UScalar, UVector3};

pub type LogWidth = u8; // TODO: https://github.com/rust-lang/rfcs/issues/671

pub const MAX_WIDTH: LogWidth = 31;
pub const MIN_WIDTH: LogWidth = 4;

impl Clamp<LogWidth> for LogWidth {
    fn clamp(&self, min: LogWidth, max: LogWidth) -> Self {
        nalgebra::clamp(*self, min, max)
    }
}

pub enum Axis {
    X = 0,
    Y = 1,
    Z = 2,
}

impl Axis {
    pub fn range() -> ops::Range<usize> {
        (Axis::X as usize)..(Axis::Z as usize + 1)
    }
}

impl From<usize> for Axis {
    fn from(index: usize) -> Self {
        match index {
            0 => Axis::X,
            1 => Axis::Y,
            2 => Axis::Z,
            _ => panic!() // TODO: Use `TryFrom`.
        }
    }
}

pub enum Direction {
    Positive,
    Negative,
}

pub enum Orientation {
    Left,
    Right,
    Top,
    Bottom,
    Front,
    Back,
}

impl Orientation {
    pub fn axis(&self) -> Axis {
        match *self {
            Orientation::Left | Orientation::Right => Axis::X,
            Orientation::Top | Orientation::Bottom => Axis::Y,
            Orientation::Front | Orientation::Back => Axis::Z,
        }
    }

    pub fn direction(&self) -> Direction {
        match *self {
            Orientation::Left | Orientation::Top | Orientation::Front => Direction::Positive,
            Orientation::Right | Orientation::Bottom | Orientation::Back => Direction::Negative,
        }
    }
}

pub struct AABB {
    pub origin: UPoint3,
    pub extent: UVector3,
}

impl AABB {
    pub fn new(origin: UPoint3, extent: UVector3) -> Self {
        AABB {
            origin: origin,
            extent: extent,
        }
    }

    pub fn intersects(&self, other: &Self) -> bool {
        for axis in Axis::range() {
            if (self.origin[axis] + self.extent[axis]) < other.origin[axis] {
                return false;
            }
            if self.origin[axis] > (other.origin[axis] + other.extent[axis]) {
                return false;
            }
        }
        true
    }
}

/// A cubic spatial partition in the `UScalar` space. `Partition`s are
/// represented as an origin and a width.
#[derive(Clone, Copy)]
pub struct Partition {
    origin: UPoint3,
    width: LogWidth,
}

impl Partition {
    /// Constructs a `Partition` at the given point in space with the given
    /// width. The width is clamped to [`MIN_WIDTH`, `MAX_WIDTH`].
    pub fn at_point(point: &UPoint3, width: LogWidth) -> Self {
        let width = width.clamp(MIN_WIDTH, MAX_WIDTH);
        Partition {
            origin: point.mask(!UScalar::zero() << width),
            width: width,
        }
    }

    /// Constructs the sub-`Partition` at the given index. Returns `None` if
    /// there is no such `Partition`, because the minimum width has been
    /// exceeded.
    ///
    /// # Panics
    ///
    /// Panics if `index` is not within the range [0, 8).
    pub fn at_index(&self, index: usize) -> Option<Self> {
        if self.is_min_width() {
            None
        }
        else {
            let width = self.width - 1;
            Some(Partition {
                origin: self.origin + vector_at_index(index, width),
                width: width,
            })
        }
    }

    /// Gets the origin of the `Partition`.
    pub fn origin(&self) -> &UPoint3 {
        &self.origin
    }

    /// Gets the width of the `Partition`.
    pub fn width(&self) -> LogWidth {
        self.width
    }

    /// Gets the midpoint of the `Partition`.
    pub fn midpoint(&self) -> UPoint3 {
        let m = exp(self.width - 1);
        self.origin + UVector3::new(m, m, m)
    }

    pub fn extent(&self) -> UVector3 {
        (UVector3::one() * exp(self.width)) - UVector3::one()
    }

    pub fn aabb(&self) -> AABB {
        AABB::new(self.origin, self.extent())
    }

    /// Returns `true` if the `Partition` has the minimum possible width.
    pub fn is_min_width(&self) -> bool {
        self.width == MIN_WIDTH
    }
}

pub trait Spatial {
    fn partition(&self) -> &Partition;

    fn depth(&self) -> u8;

    fn aabb(&self) -> AABB {
        self.partition().aabb()
    }
}

pub fn exp(width: LogWidth) -> UScalar {
    UScalar::one() << width
}

#[cfg_attr(rustfmt, rustfmt_skip)]
pub fn index_at_point(point: &UPoint3, width: LogWidth) -> usize {
    ((((point.x >> width) & UScalar::one()) << 0) |
     (((point.y >> width) & UScalar::one()) << 1) |
     (((point.z >> width) & UScalar::one()) << 2)) as usize
}

pub fn vector_at_index(index: usize, width: LogWidth) -> UVector3 {
    assert!(index < 8);
    let index = index as UScalar;
    let width = exp(width);
    UVector3::new(((index >> 0) & UScalar::one()) * width,
                  ((index >> 1) & UScalar::one()) * width,
                  ((index >> 2) & UScalar::one()) * width)
}

#[cfg(test)]
mod tests {
    use super::*;
}
<commit_msg>Auto-derive `Clone` and `Copy` for `Axis`.<commit_after>extern crate nalgebra;
extern crate num;

use num::{One, Zero}; // TODO: `use ::std::num::{One, Zero};`.
use std::ops;

use math::{Clamp, Mask, UPoint3, UScalar, UVector3};

pub type LogWidth = u8; // TODO: https://github.com/rust-lang/rfcs/issues/671

pub const MAX_WIDTH: LogWidth = 31;
pub const MIN_WIDTH: LogWidth = 4;

impl Clamp<LogWidth> for LogWidth {
    fn clamp(&self, min: LogWidth, max: LogWidth) -> Self {
        nalgebra::clamp(*self, min, max)
    }
}

#[derive(Clone, Copy)]
pub enum Axis {
    X = 0,
    Y = 1,
    Z = 2,
}

impl Axis {
    pub fn range() -> ops::Range<usize> {
        (Axis::X as usize)..(Axis::Z as usize + 1)
    }
}

impl From<usize> for Axis {
    fn from(index: usize) -> Self {
        match index {
            0 => Axis::X,
            1 => Axis::Y,
            2 => Axis::Z,
            _ => panic!() // TODO: Use `TryFrom`.
        }
    }
}

pub enum Direction {
    Positive,
    Negative,
}

pub enum Orientation {
    Left,
    Right,
    Top,
    Bottom,
    Front,
    Back,
}

impl Orientation {
    pub fn axis(&self) -> Axis {
        match *self {
            Orientation::Left | Orientation::Right => Axis::X,
            Orientation::Top | Orientation::Bottom => Axis::Y,
            Orientation::Front | Orientation::Back => Axis::Z,
        }
    }

    pub fn direction(&self) -> Direction {
        match *self {
            Orientation::Left | Orientation::Top | Orientation::Front => Direction::Positive,
            Orientation::Right | Orientation::Bottom | Orientation::Back => Direction::Negative,
        }
    }
}

pub struct AABB {
    pub origin: UPoint3,
    pub extent: UVector3,
}

impl AABB {
    pub fn new(origin: UPoint3, extent: UVector3) -> Self {
        AABB {
            origin: origin,
            extent: extent,
        }
    }

    pub fn intersects(&self, other: &Self) -> bool {
        for axis in Axis::range() {
            if (self.origin[axis] + self.extent[axis]) < other.origin[axis] {
                return false;
            }
            if self.origin[axis] > (other.origin[axis] + other.extent[axis]) {
                return false;
            }
        }
        true
    }
}

/// A cubic spatial partition in the `UScalar` space. `Partition`s are
/// represented as an origin and a width.
#[derive(Clone, Copy)]
pub struct Partition {
    origin: UPoint3,
    width: LogWidth,
}

impl Partition {
    /// Constructs a `Partition` at the given point in space with the given
    /// width. The width is clamped to [`MIN_WIDTH`, `MAX_WIDTH`].
    pub fn at_point(point: &UPoint3, width: LogWidth) -> Self {
        let width = width.clamp(MIN_WIDTH, MAX_WIDTH);
        Partition {
            origin: point.mask(!UScalar::zero() << width),
            width: width,
        }
    }

    /// Constructs the sub-`Partition` at the given index. Returns `None` if
    /// there is no such `Partition`, because the minimum width has been
    /// exceeded.
    ///
    /// # Panics
    ///
    /// Panics if `index` is not within the range [0, 8).
    pub fn at_index(&self, index: usize) -> Option<Self> {
        if self.is_min_width() {
            None
        }
        else {
            let width = self.width - 1;
            Some(Partition {
                origin: self.origin + vector_at_index(index, width),
                width: width,
            })
        }
    }

    /// Gets the origin of the `Partition`.
    pub fn origin(&self) -> &UPoint3 {
        &self.origin
    }

    /// Gets the width of the `Partition`.
    pub fn width(&self) -> LogWidth {
        self.width
    }

    /// Gets the midpoint of the `Partition`.
    pub fn midpoint(&self) -> UPoint3 {
        let m = exp(self.width - 1);
        self.origin + UVector3::new(m, m, m)
    }

    pub fn extent(&self) -> UVector3 {
        (UVector3::one() * exp(self.width)) - UVector3::one()
    }

    pub fn aabb(&self) -> AABB {
        AABB::new(self.origin, self.extent())
    }

    /// Returns `true` if the `Partition` has the minimum possible width.
    pub fn is_min_width(&self) -> bool {
        self.width == MIN_WIDTH
    }
}

pub trait Spatial {
    fn partition(&self) -> &Partition;

    fn depth(&self) -> u8;

    fn aabb(&self) -> AABB {
        self.partition().aabb()
    }
}

pub fn exp(width: LogWidth) -> UScalar {
    UScalar::one() << width
}

#[cfg_attr(rustfmt, rustfmt_skip)]
pub fn index_at_point(point: &UPoint3, width: LogWidth) -> usize {
    ((((point.x >> width) & UScalar::one()) << 0) |
     (((point.y >> width) & UScalar::one()) << 1) |
     (((point.z >> width) & UScalar::one()) << 2)) as usize
}

pub fn vector_at_index(index: usize, width: LogWidth) -> UVector3 {
    assert!(index < 8);
    let index = index as UScalar;
    let width = exp(width);
    UVector3::new(((index >> 0) & UScalar::one()) * width,
                  ((index >> 1) & UScalar::one()) * width,
                  ((index >> 2) & UScalar::one()) * width)
}

#[cfg(test)]
mod tests {
    use super::*;
}
<|endoftext|>


<commit_before>use std::convert::TryFrom;
use std::io::Read;
use std::path;

#[rustfmt::skip]
use ::image;

use crate::context::{Context, DebugId};
use crate::error::GameError;
use crate::error::GameResult;
use crate::filesystem;
use crate::graphics;
use crate::graphics::shader::*;
use crate::graphics::*;

/// Generic in-GPU-memory image data available to be drawn on the screen.
/// You probably just want to look at the `Image` type.
#[derive(Clone, PartialEq)]
pub struct ImageGeneric<B>
where
    B: BackendSpec,
{
    pub(crate) texture: gfx::handle::RawShaderResourceView<B::Resources>,
    pub(crate) texture_handle: gfx::handle::RawTexture<B::Resources>,
    pub(crate) sampler_info: gfx::texture::SamplerInfo,
    pub(crate) blend_mode: Option<BlendMode>,
    pub(crate) width: u16,
    pub(crate) height: u16,

    pub(crate) debug_id: DebugId,
}

impl<B> ImageGeneric<B>
where
    B: BackendSpec,
{
    /// A helper function that just takes a factory directly so we can make an image
    /// without needing the full context object, so we can create an Image while still
    /// creating the GraphicsContext.
    pub(crate) fn make_raw(
        factory: &mut <B as BackendSpec>::Factory,
        sampler_info: &texture::SamplerInfo,
        width: u16,
        height: u16,
        rgba: &[u8],
        color_format: gfx::format::Format,
        debug_id: DebugId,
    ) -> GameResult<Self> {
        if width == 0 || height == 0 {
            let msg = format!(
                "Tried to create a texture of size {}x{}, each dimension must
                be >0",
                width, height
            );
            return Err(GameError::ResourceLoadError(msg));
        }
        // Check for overflow, which might happen on 32-bit systems.
        // Textures can be max u16*u16, pixels, but then have 4 bytes per pixel.
        let uwidth = usize::from(width);
        let uheight = usize::from(height);
        let expected_bytes = uwidth
            .checked_mul(uheight)
            .and_then(|size| size.checked_mul(4))
            .ok_or_else(|| {
                let msg = format!(
                    "Integer overflow in Image::make_raw, image size: {} {}",
                    uwidth, uheight
                );
                GameError::ResourceLoadError(msg)
            })?;
        if expected_bytes != rgba.len() {
            let msg = format!(
                "Tried to create a texture of size {}x{}, but gave {} bytes of data (expected {})",
                width,
                height,
                rgba.len(),
                expected_bytes
            );
            return Err(GameError::ResourceLoadError(msg));
        }

        let kind = gfx::texture::Kind::D2(width, height, gfx::texture::AaMode::Single);
        use gfx::memory::Bind;
        let gfx::format::Format(surface_format, channel_type) = color_format;
        let texinfo = gfx::texture::Info {
            kind,
            levels: 1,
            format: surface_format,
            bind: Bind::SHADER_RESOURCE
                | Bind::RENDER_TARGET
                | Bind::TRANSFER_SRC
                | Bind::TRANSFER_DST,
            usage: gfx::memory::Usage::Dynamic,
        };
        let raw_tex = factory.create_texture_raw(
            texinfo,
            Some(channel_type),
            Some((&[&rgba], gfx::texture::Mipmap::Provided)),
        )?;
        let resource_desc = gfx::texture::ResourceDesc {
            channel: channel_type,
            layer: None,
            min: 0,
            max: raw_tex.get_info().levels - 1,
            swizzle: gfx::format::Swizzle::new(),
        };
        let raw_view = factory.view_texture_as_shader_resource_raw(&raw_tex, resource_desc)?;
        Ok(Self {
            texture: raw_view,
            texture_handle: raw_tex,
            sampler_info: *sampler_info,
            blend_mode: None,
            width,
            height,
            debug_id,
        })
    }

    /// A helper function to get the raw gfx texture handle
    pub fn get_raw_texture_handle(&self) -> gfx::handle::RawTexture<B::Resources> {
        self.texture_handle.clone()
    }

    /// A helper function to get the raw gfx texture view
    pub fn get_raw_texture_view(&self) -> gfx::handle::RawShaderResourceView<B::Resources> {
        self.texture.clone()
    }
}

/// In-GPU-memory image data available to be drawn on the screen,
/// using the OpenGL backend.
///
/// Under the hood this is just an `Arc`'ed texture handle and
/// some metadata, so cloning it is fairly cheap; it doesn't
/// make another copy of the underlying image data.
pub type Image = ImageGeneric<GlBackendSpec>;

/// The supported formats for saving an image.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ImageFormat {
    /// .png image format (defaults to RGBA with 8-bit channels.)
    Png,
}

impl Image {
    /// Load a new image from the file at the given path. The documentation for the
    /// [`filesystem`](../filesystem/index.html) module explains how the path must be specified.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Self> {
        let mut buf = Vec::new();
        let mut reader = context.filesystem.open(path)?;
        let _ = reader.read_to_end(&mut buf)?;
        Self::from_bytes(context, &buf)
    }

    /// Creates a new `Image` from the given buffer, which should contain an image encoded
    /// in a supported image file format.
    pub fn from_bytes(context: &mut Context, bytes: &[u8]) -> GameResult<Self> {
        let img = image::load_from_memory(bytes)?.to_rgba8();
        let (width, height) = img.dimensions();
        let better_width = u16::try_from(width)
            .map_err(|_| GameError::ResourceLoadError(String::from("Image width > u16::MAX")))?;
        let better_height = u16::try_from(height)
            .map_err(|_| GameError::ResourceLoadError(String::from("Image height > u16::MAX")))?;
        Self::from_rgba8(context, better_width, better_height, &img.into_raw())
    }

    /// Creates a new `Image` from the given buffer of `u8` RGBA values.
    ///
    /// The pixel layout is row-major.  That is,
    /// the first 4 `u8` values make the top-left pixel in the `Image`, the
    /// next 4 make the next pixel in the same row, and so on to the end of
    /// the row.  The next `width * 4` values make up the second row, and so
    /// on.
    pub fn from_rgba8(
        context: &mut Context,
        width: u16,
        height: u16,
        rgba: &[u8],
    ) -> GameResult<Self> {
        let debug_id = DebugId::get(context);
        let color_format = context.gfx_context.color_format();
        Self::make_raw(
            &mut *context.gfx_context.factory,
            &context.gfx_context.default_sampler_info,
            width,
            height,
            rgba,
            color_format,
            debug_id,
        )
    }

    /// Dumps the `Image`'s data to a `Vec` of `u8` RGBA values.
    pub fn to_rgba8(&self, ctx: &mut Context) -> GameResult<Vec<u8>> {
        use gfx::memory::Typed;
        use gfx::traits::FactoryExt;

        let gfx = &mut ctx.gfx_context;
        let w = self.width;
        let h = self.height;

        // Note: In the GFX example, the download buffer is created ahead of time
        // and updated on screen resize events. This may be preferable, but then
        // the buffer also needs to be updated when we switch to/from a canvas.
        // Unsure of the performance impact of creating this as it is needed.
        // Probably okay for now though, since this probably won't be a super
        // common operation.
        let dl_buffer = gfx
            .factory
            .create_download_buffer::<u8>(usize::from(w) * usize::from(h) * 4)?;

        let factory = &mut *gfx.factory;
        let mut local_encoder = GlBackendSpec::encoder(factory);

        local_encoder.copy_texture_to_buffer_raw(
            &self.texture_handle,
            None,
            gfx::texture::RawImageInfo {
                xoffset: 0,
                yoffset: 0,
                zoffset: 0,
                width: w,
                height: h,
                depth: 0,
                format: gfx.color_format(),
                mipmap: 0,
            },
            dl_buffer.raw(),
            0,
        )?;
        local_encoder.flush(&mut *gfx.device);

        let reader = gfx.factory.read_mapping(&dl_buffer)?.to_vec();
        Ok(reader.to_vec())
    }

    /// Encode the `Image` to the given file format and
    /// write it out to the given path.
    ///
    /// See the [`filesystem`](../filesystem/index.html) module docs for where exactly
    /// the file will end up.
    pub fn encode<P: AsRef<path::Path>>(
        &self,
        ctx: &mut Context,
        format: ImageFormat,
        path: P,
    ) -> GameResult {
        use std::io;
        let data = self.to_rgba8(ctx)?;
        let f = filesystem::create(ctx, path)?;
        let writer = &mut io::BufWriter::new(f);
        let color_format = image::ColorType::Rgba8;
        match format {
            ImageFormat::Png => image::png::PngEncoder::new(writer)
                .encode(
                    &data,
                    u32::from(self.width),
                    u32::from(self.height),
                    color_format,
                )
                .map_err(Into::into),
        }
    }

    /// A little helper function that creates a new `Image` that is just
    /// a solid square of the given size and color.  Mainly useful for
    /// debugging.
    pub fn solid(context: &mut Context, size: u16, color: Color) -> GameResult<Self> {
        let (r, g, b, a) = color.into();
        let pixel_array: [u8; 4] = [r, g, b, a];
        let size_squared = usize::from(size) * usize::from(size);
        let mut buffer = Vec::with_capacity(size_squared);
        for _i in 0..size_squared {
            buffer.extend(&pixel_array[..]);
        }
        Image::from_rgba8(context, size, size, &buffer)
    }

    /// Return the width of the image.
    pub fn width(&self) -> u16 {
        self.width
    }

    /// Return the height of the image.
    pub fn height(&self) -> u16 {
        self.height
    }

    /// Get the filter mode for the image.
    pub fn filter(&self) -> FilterMode {
        self.sampler_info.filter.into()
    }

    /// Set the filter mode for the image.
    pub fn set_filter(&mut self, mode: FilterMode) {
        self.sampler_info.filter = mode.into();
    }

    /// Returns the dimensions of the image.
    pub fn dimensions(&self) -> Rect {
        Rect::new(0.0, 0.0, f32::from(self.width()), f32::from(self.height()))
    }

    /// Gets the `Image`'s `WrapMode` along the X and Y axes.
    pub fn wrap(&self) -> (WrapMode, WrapMode) {
        (self.sampler_info.wrap_mode.0, self.sampler_info.wrap_mode.1)
    }

    /// Sets the `Image`'s `WrapMode` along the X and Y axes.
    pub fn set_wrap(&mut self, wrap_x: WrapMode, wrap_y: WrapMode) {
        self.sampler_info.wrap_mode.0 = wrap_x;
        self.sampler_info.wrap_mode.1 = wrap_y;
    }
}

impl fmt::Debug for Image {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "<Image: {}x{}, {:p}, texture address {:p}, sampler: {:?}>",
            self.width(),
            self.height(),
            self,
            &self.texture,
            &self.sampler_info
        )
    }
}

impl Drawable for Image {
    fn draw(&self, ctx: &mut Context, param: DrawParam) -> GameResult {
        self.debug_id.assert(ctx);

        let src_width = param.src.w;
        let src_height = param.src.h;
        // We have to mess with the scale to make everything
        // be its-unit-size-in-pixels.
        let scale_x = src_width * f32::from(self.width);
        let scale_y = src_height * f32::from(self.height);
        let new_param = match param.trans {
            Transform::Values { scale, .. } => param.scale(mint::Vector2 {
                x: scale.x * scale_x,
                y: scale.y * scale_y,
            }),
            Transform::Matrix(m) => param.transform(
                Matrix4::from(m) * Matrix4::from_scale(glam::vec3(scale_x, scale_y, 1.0)),
            ),
        };

        draw_image_raw(self, ctx, new_param)
    }

    fn dimensions(&self, _: &mut Context) -> Option<graphics::Rect> {
        Some(self.dimensions())
    }

    fn set_blend_mode(&mut self, mode: Option<BlendMode>) {
        self.blend_mode = mode;
    }

    fn blend_mode(&self) -> Option<BlendMode> {
        self.blend_mode
    }
}

pub(crate) fn draw_image_raw(image: &Image, ctx: &mut Context, param: DrawParam) -> GameResult {
    let gfx = &mut ctx.gfx_context;

    gfx.update_instance_properties(param)?;
    let sampler = gfx
        .samplers
        .get_or_insert(image.sampler_info, gfx.factory.as_mut());
    gfx.data.vbuf = gfx.quad_vertex_buffer.clone();
    let typed_thingy = gfx
        .backend_spec
        .raw_to_typed_shader_resource(image.texture.clone());
    gfx.data.tex = (typed_thingy, sampler);
    let previous_mode: Option<BlendMode> = if let Some(mode) = image.blend_mode {
        let current_mode = gfx.blend_mode();
        if current_mode != mode {
            gfx.set_blend_mode(mode)?;
            Some(current_mode)
        } else {
            None
        }
    } else {
        None
    };

    gfx.draw(None)?;
    if let Some(mode) = previous_mode {
        gfx.set_blend_mode(mode)?;
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ContextBuilder;
    #[test]
    fn test_invalid_image_size() {
        let (ctx, _) = &mut ContextBuilder::new("unittest", "unittest").build().unwrap();
        let _i = assert!(Image::from_rgba8(ctx, 0, 0, &[]).is_err());
        let _i = assert!(Image::from_rgba8(ctx, 3432, 432, &[]).is_err());
        let _i = Image::from_rgba8(ctx, 2, 2, &[99; 16]).unwrap();
    }
}
<commit_msg>No need to call .into_raw()<commit_after>use std::convert::TryFrom;
use std::io::Read;
use std::path;

#[rustfmt::skip]
use ::image;

use crate::context::{Context, DebugId};
use crate::error::GameError;
use crate::error::GameResult;
use crate::filesystem;
use crate::graphics;
use crate::graphics::shader::*;
use crate::graphics::*;

/// Generic in-GPU-memory image data available to be drawn on the screen.
/// You probably just want to look at the `Image` type.
#[derive(Clone, PartialEq)]
pub struct ImageGeneric<B>
where
    B: BackendSpec,
{
    pub(crate) texture: gfx::handle::RawShaderResourceView<B::Resources>,
    pub(crate) texture_handle: gfx::handle::RawTexture<B::Resources>,
    pub(crate) sampler_info: gfx::texture::SamplerInfo,
    pub(crate) blend_mode: Option<BlendMode>,
    pub(crate) width: u16,
    pub(crate) height: u16,

    pub(crate) debug_id: DebugId,
}

impl<B> ImageGeneric<B>
where
    B: BackendSpec,
{
    /// A helper function that just takes a factory directly so we can make an image
    /// without needing the full context object, so we can create an Image while still
    /// creating the GraphicsContext.
    pub(crate) fn make_raw(
        factory: &mut <B as BackendSpec>::Factory,
        sampler_info: &texture::SamplerInfo,
        width: u16,
        height: u16,
        rgba: &[u8],
        color_format: gfx::format::Format,
        debug_id: DebugId,
    ) -> GameResult<Self> {
        if width == 0 || height == 0 {
            let msg = format!(
                "Tried to create a texture of size {}x{}, each dimension must
                be >0",
                width, height
            );
            return Err(GameError::ResourceLoadError(msg));
        }
        // Check for overflow, which might happen on 32-bit systems.
        // Textures can be max u16*u16, pixels, but then have 4 bytes per pixel.
        let uwidth = usize::from(width);
        let uheight = usize::from(height);
        let expected_bytes = uwidth
            .checked_mul(uheight)
            .and_then(|size| size.checked_mul(4))
            .ok_or_else(|| {
                let msg = format!(
                    "Integer overflow in Image::make_raw, image size: {} {}",
                    uwidth, uheight
                );
                GameError::ResourceLoadError(msg)
            })?;
        if expected_bytes != rgba.len() {
            let msg = format!(
                "Tried to create a texture of size {}x{}, but gave {} bytes of data (expected {})",
                width,
                height,
                rgba.len(),
                expected_bytes
            );
            return Err(GameError::ResourceLoadError(msg));
        }

        let kind = gfx::texture::Kind::D2(width, height, gfx::texture::AaMode::Single);
        use gfx::memory::Bind;
        let gfx::format::Format(surface_format, channel_type) = color_format;
        let texinfo = gfx::texture::Info {
            kind,
            levels: 1,
            format: surface_format,
            bind: Bind::SHADER_RESOURCE
                | Bind::RENDER_TARGET
                | Bind::TRANSFER_SRC
                | Bind::TRANSFER_DST,
            usage: gfx::memory::Usage::Dynamic,
        };
        let raw_tex = factory.create_texture_raw(
            texinfo,
            Some(channel_type),
            Some((&[&rgba], gfx::texture::Mipmap::Provided)),
        )?;
        let resource_desc = gfx::texture::ResourceDesc {
            channel: channel_type,
            layer: None,
            min: 0,
            max: raw_tex.get_info().levels - 1,
            swizzle: gfx::format::Swizzle::new(),
        };
        let raw_view = factory.view_texture_as_shader_resource_raw(&raw_tex, resource_desc)?;
        Ok(Self {
            texture: raw_view,
            texture_handle: raw_tex,
            sampler_info: *sampler_info,
            blend_mode: None,
            width,
            height,
            debug_id,
        })
    }

    /// A helper function to get the raw gfx texture handle
    pub fn get_raw_texture_handle(&self) -> gfx::handle::RawTexture<B::Resources> {
        self.texture_handle.clone()
    }

    /// A helper function to get the raw gfx texture view
    pub fn get_raw_texture_view(&self) -> gfx::handle::RawShaderResourceView<B::Resources> {
        self.texture.clone()
    }
}

/// In-GPU-memory image data available to be drawn on the screen,
/// using the OpenGL backend.
///
/// Under the hood this is just an `Arc`'ed texture handle and
/// some metadata, so cloning it is fairly cheap; it doesn't
/// make another copy of the underlying image data.
pub type Image = ImageGeneric<GlBackendSpec>;

/// The supported formats for saving an image.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ImageFormat {
    /// .png image format (defaults to RGBA with 8-bit channels.)
    Png,
}

impl Image {
    /// Load a new image from the file at the given path. The documentation for the
    /// [`filesystem`](../filesystem/index.html) module explains how the path must be specified.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Self> {
        let mut buf = Vec::new();
        let mut reader = context.filesystem.open(path)?;
        let _ = reader.read_to_end(&mut buf)?;
        Self::from_bytes(context, &buf)
    }

    /// Creates a new `Image` from the given buffer, which should contain an image encoded
    /// in a supported image file format.
    pub fn from_bytes(context: &mut Context, bytes: &[u8]) -> GameResult<Self> {
        let img = image::load_from_memory(bytes)?.to_rgba8();
        let (width, height) = img.dimensions();
        let better_width = u16::try_from(width)
            .map_err(|_| GameError::ResourceLoadError(String::from("Image width > u16::MAX")))?;
        let better_height = u16::try_from(height)
            .map_err(|_| GameError::ResourceLoadError(String::from("Image height > u16::MAX")))?;
        Self::from_rgba8(context, better_width, better_height, &img)
    }

    /// Creates a new `Image` from the given buffer of `u8` RGBA values.
    ///
    /// The pixel layout is row-major.  That is,
    /// the first 4 `u8` values make the top-left pixel in the `Image`, the
    /// next 4 make the next pixel in the same row, and so on to the end of
    /// the row.  The next `width * 4` values make up the second row, and so
    /// on.
    pub fn from_rgba8(
        context: &mut Context,
        width: u16,
        height: u16,
        rgba: &[u8],
    ) -> GameResult<Self> {
        let debug_id = DebugId::get(context);
        let color_format = context.gfx_context.color_format();
        Self::make_raw(
            &mut *context.gfx_context.factory,
            &context.gfx_context.default_sampler_info,
            width,
            height,
            rgba,
            color_format,
            debug_id,
        )
    }

    /// Dumps the `Image`'s data to a `Vec` of `u8` RGBA values.
    pub fn to_rgba8(&self, ctx: &mut Context) -> GameResult<Vec<u8>> {
        use gfx::memory::Typed;
        use gfx::traits::FactoryExt;

        let gfx = &mut ctx.gfx_context;
        let w = self.width;
        let h = self.height;

        // Note: In the GFX example, the download buffer is created ahead of time
        // and updated on screen resize events. This may be preferable, but then
        // the buffer also needs to be updated when we switch to/from a canvas.
        // Unsure of the performance impact of creating this as it is needed.
        // Probably okay for now though, since this probably won't be a super
        // common operation.
        let dl_buffer = gfx
            .factory
            .create_download_buffer::<u8>(usize::from(w) * usize::from(h) * 4)?;

        let factory = &mut *gfx.factory;
        let mut local_encoder = GlBackendSpec::encoder(factory);

        local_encoder.copy_texture_to_buffer_raw(
            &self.texture_handle,
            None,
            gfx::texture::RawImageInfo {
                xoffset: 0,
                yoffset: 0,
                zoffset: 0,
                width: w,
                height: h,
                depth: 0,
                format: gfx.color_format(),
                mipmap: 0,
            },
            dl_buffer.raw(),
            0,
        )?;
        local_encoder.flush(&mut *gfx.device);

        let reader = gfx.factory.read_mapping(&dl_buffer)?.to_vec();
        Ok(reader.to_vec())
    }

    /// Encode the `Image` to the given file format and
    /// write it out to the given path.
    ///
    /// See the [`filesystem`](../filesystem/index.html) module docs for where exactly
    /// the file will end up.
    pub fn encode<P: AsRef<path::Path>>(
        &self,
        ctx: &mut Context,
        format: ImageFormat,
        path: P,
    ) -> GameResult {
        use std::io;
        let data = self.to_rgba8(ctx)?;
        let f = filesystem::create(ctx, path)?;
        let writer = &mut io::BufWriter::new(f);
        let color_format = image::ColorType::Rgba8;
        match format {
            ImageFormat::Png => image::png::PngEncoder::new(writer)
                .encode(
                    &data,
                    u32::from(self.width),
                    u32::from(self.height),
                    color_format,
                )
                .map_err(Into::into),
        }
    }

    /// A little helper function that creates a new `Image` that is just
    /// a solid square of the given size and color.  Mainly useful for
    /// debugging.
    pub fn solid(context: &mut Context, size: u16, color: Color) -> GameResult<Self> {
        let (r, g, b, a) = color.into();
        let pixel_array: [u8; 4] = [r, g, b, a];
        let size_squared = usize::from(size) * usize::from(size);
        let mut buffer = Vec::with_capacity(size_squared);
        for _i in 0..size_squared {
            buffer.extend(&pixel_array[..]);
        }
        Image::from_rgba8(context, size, size, &buffer)
    }

    /// Return the width of the image.
    pub fn width(&self) -> u16 {
        self.width
    }

    /// Return the height of the image.
    pub fn height(&self) -> u16 {
        self.height
    }

    /// Get the filter mode for the image.
    pub fn filter(&self) -> FilterMode {
        self.sampler_info.filter.into()
    }

    /// Set the filter mode for the image.
    pub fn set_filter(&mut self, mode: FilterMode) {
        self.sampler_info.filter = mode.into();
    }

    /// Returns the dimensions of the image.
    pub fn dimensions(&self) -> Rect {
        Rect::new(0.0, 0.0, f32::from(self.width()), f32::from(self.height()))
    }

    /// Gets the `Image`'s `WrapMode` along the X and Y axes.
    pub fn wrap(&self) -> (WrapMode, WrapMode) {
        (self.sampler_info.wrap_mode.0, self.sampler_info.wrap_mode.1)
    }

    /// Sets the `Image`'s `WrapMode` along the X and Y axes.
    pub fn set_wrap(&mut self, wrap_x: WrapMode, wrap_y: WrapMode) {
        self.sampler_info.wrap_mode.0 = wrap_x;
        self.sampler_info.wrap_mode.1 = wrap_y;
    }
}

impl fmt::Debug for Image {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "<Image: {}x{}, {:p}, texture address {:p}, sampler: {:?}>",
            self.width(),
            self.height(),
            self,
            &self.texture,
            &self.sampler_info
        )
    }
}

impl Drawable for Image {
    fn draw(&self, ctx: &mut Context, param: DrawParam) -> GameResult {
        self.debug_id.assert(ctx);

        let src_width = param.src.w;
        let src_height = param.src.h;
        // We have to mess with the scale to make everything
        // be its-unit-size-in-pixels.
        let scale_x = src_width * f32::from(self.width);
        let scale_y = src_height * f32::from(self.height);
        let new_param = match param.trans {
            Transform::Values { scale, .. } => param.scale(mint::Vector2 {
                x: scale.x * scale_x,
                y: scale.y * scale_y,
            }),
            Transform::Matrix(m) => param.transform(
                Matrix4::from(m) * Matrix4::from_scale(glam::vec3(scale_x, scale_y, 1.0)),
            ),
        };

        draw_image_raw(self, ctx, new_param)
    }

    fn dimensions(&self, _: &mut Context) -> Option<graphics::Rect> {
        Some(self.dimensions())
    }

    fn set_blend_mode(&mut self, mode: Option<BlendMode>) {
        self.blend_mode = mode;
    }

    fn blend_mode(&self) -> Option<BlendMode> {
        self.blend_mode
    }
}

pub(crate) fn draw_image_raw(image: &Image, ctx: &mut Context, param: DrawParam) -> GameResult {
    let gfx = &mut ctx.gfx_context;

    gfx.update_instance_properties(param)?;
    let sampler = gfx
        .samplers
        .get_or_insert(image.sampler_info, gfx.factory.as_mut());
    gfx.data.vbuf = gfx.quad_vertex_buffer.clone();
    let typed_thingy = gfx
        .backend_spec
        .raw_to_typed_shader_resource(image.texture.clone());
    gfx.data.tex = (typed_thingy, sampler);
    let previous_mode: Option<BlendMode> = if let Some(mode) = image.blend_mode {
        let current_mode = gfx.blend_mode();
        if current_mode != mode {
            gfx.set_blend_mode(mode)?;
            Some(current_mode)
        } else {
            None
        }
    } else {
        None
    };

    gfx.draw(None)?;
    if let Some(mode) = previous_mode {
        gfx.set_blend_mode(mode)?;
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ContextBuilder;
    #[test]
    fn test_invalid_image_size() {
        let (ctx, _) = &mut ContextBuilder::new("unittest", "unittest").build().unwrap();
        let _i = assert!(Image::from_rgba8(ctx, 0, 0, &[]).is_err());
        let _i = assert!(Image::from_rgba8(ctx, 3432, 432, &[]).is_err());
        let _i = Image::from_rgba8(ctx, 2, 2, &[99; 16]).unwrap();
    }
}
<|endoftext|>


<commit_before>//! Provides a portable interface to the filesystem.
//!
//! This module provides access to files in specific places:
//!
//! * The `resources/` subdirectory in the same directory as the
//! program executable,
//! * The `resources.zip` file in the same
//! directory as the program executable,
//! * The root folder of the  game's "save" directory which is in a
//! platform-dependent location,
//! such as `~/.local/share/<gameid>/` on Linux.  The `gameid`
//! is the the string passed to
//! `Context::load_from_conf()`; some platforms such as Windows also
//! incorporate the `author` string into the path.
//!
//! Files will be looked for in these locations in order, and the first one
//! found used.  That allows game assets to be easily distributed as an archive
//! file, but locally overridden for testing or modding simply by putting
//! altered copies of them in the game's `resources/` directory.
//!
//! Note that the file lookups WILL follow symlinks!  It is
//! more for convenience than absolute security, so don't treat it as
//! being secure.
//!
//! If you build `ggez` with the `cargo-resource-root` feature flag, it will
//! also look for a `resources/` subdirectory in the same directory as your
//! `Cargo.toml`, which can be very convenient for development.

// BUGGO: TODO: Also make it print out the searched directories when it
// can't find a file!

use std::env;
use std::fmt;
use std::io;
use std::path;

use app_dirs::*;

use GameError;
use GameResult;
use conf;
use vfs::{self, VFS};

pub use vfs::OpenOptions;

const CONFIG_NAME: &'static str = "/conf.toml";

/// A structure that contains the filesystem state and cache.
#[derive(Debug)]
pub struct Filesystem {
    vfs: vfs::OverlayFS,
    resources_path: path::PathBuf,
    zip_path: path::PathBuf,
    // user_config_path: path::PathBuf,
    user_data_path: path::PathBuf,
}

/// Represents a file, either in the filesystem, or in the resources zip file,
/// or whatever.
pub enum File {
    VfsFile(Box<vfs::VFile>),
}

impl fmt::Debug for File {
    // TODO: Make this more useful.
    // But we can't seem to get a filename out of a file,
    // soooooo.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            File::VfsFile(ref _file) => write!(f, "VfsFile"),
        }
    }
}

impl io::Read for File {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            File::VfsFile(ref mut f) => f.read(buf),
        }
    }
}

impl io::Write for File {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match *self {
            File::VfsFile(ref mut f) => f.write(buf),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match *self {
            File::VfsFile(ref mut f) => f.flush(),
        }
    }
}

impl Filesystem {
    /// Create a new Filesystem instance, using the given `id` and (on
    /// some platforms) the `author` as a portion of the user
    /// directory path.  This function is called automatically by
    /// ggez, the end user should never need to call it.
    pub fn new(id: &'static str, author: &'static str) -> GameResult<Filesystem> {
        let app_info = AppInfo {
            name: id,
            author: author,
        };
        let mut root_path = env::current_exe()?;

        // Ditch the filename (if any)
        if root_path.file_name().is_some() {
            root_path.pop();
        }

        // Set up VFS to merge resource path, root path, and zip path.
        let mut overlay = vfs::OverlayFS::new();

        let mut resources_path;
        let mut resources_zip_path;
        let user_data_path;
        // let user_config_path;
        // <game exe root>/resources/
        {
            resources_path = root_path.clone();
            resources_path.push("resources");
            let physfs = vfs::PhysicalFS::new(&resources_path, true);
            overlay.push_back(Box::new(physfs));
        }

        // <root>/resources.zip
        {
            resources_zip_path = root_path.clone();
            resources_zip_path.push("resources.zip");
            if resources_zip_path.exists() {
                let zipfs = vfs::ZipFS::new(&resources_zip_path)?;
                overlay.push_back(Box::new(zipfs));
            }
        }

        // Per-user data dir,
        // ~/.local/share/whatever/
        {
            user_data_path = app_root(AppDataType::UserData, &app_info)?;
            let physfs = vfs::PhysicalFS::new(&user_data_path, true);
            overlay.push_back(Box::new(physfs));
        }

        // // Writeable local dir, ~/.config/whatever/
        // // Save game dir is read-write
        // {
        //     user_config_path = app_root(AppDataType::UserConfig, &app_info)?;
        //     let physfs = vfs::PhysicalFS::new(&user_config_path, false);
        //     overlay.push_back(Box::new(physfs));
        // }

        // Cargo manifest dir!
        #[cfg(feature = "cargo-resource-root")]
        {
            if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
                let mut path = path::PathBuf::from(manifest_dir);
                path.push("resources");
                let physfs = vfs::PhysicalFS::new(&path, false);
                overlay.push_back(Box::new(physfs));
            }
        }

        let fs = Filesystem {
            vfs: overlay,
            resources_path: resources_path,
            zip_path: resources_zip_path,
            // user_config_path: user_config_path,
            user_data_path: user_data_path,
        };

        Ok(fs)
    }

    /// Opens the given path and returns the resulting `File`
    /// in read-only mode.
    pub fn open<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<File> {
        self.vfs.open(path.as_ref()).map(|f| File::VfsFile(f))
    }

    /// Opens a file in the user directory with the given `filesystem::OpenOptions`.
    /// Note that even if you open a file read-only, it can only access
    /// files in the user directory.
    pub fn open_options<P: AsRef<path::Path>>(
        &mut self,
        path: P,
        options: &OpenOptions,
    ) -> GameResult<File> {
        self.vfs
            .open_options(path.as_ref(), options)
            .map(|f| File::VfsFile(f))
            .map_err(|e| {
                GameError::ResourceLoadError(format!(
                    "Tried to open {:?} but got error: {:?}",
                    path.as_ref(),
                    e
                ))
            })
    }

    /// Creates a new file in the user directory and opens it
    /// to be written to, truncating it if it already exists.
    pub fn create<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<File> {
        self.vfs.create(path.as_ref()).map(|f| File::VfsFile(f))
    }

    /// Create an empty directory in the user dir
    /// with the given name.  Any parents to that directory
    /// that do not exist will be created.
    pub fn create_dir<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<()> {
        self.vfs.mkdir(path.as_ref())
    }

    /// Deletes the specified file in the user dir.
    pub fn delete<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<()> {
        self.vfs.rm(path.as_ref())
    }

    /// Deletes the specified directory in the user dir,
    /// and all its contents!
    pub fn delete_dir<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<()> {
        self.vfs.rmrf(path.as_ref())
    }

    /// Check whether a file or directory exists.
    pub fn exists<P: AsRef<path::Path>>(&self, path: P) -> bool {
        self.vfs.exists(path.as_ref())
    }

    /// Check whether a path points at a file.
    pub fn is_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
        self.vfs
            .metadata(path.as_ref())
            .map(|m| m.is_file())
            .unwrap_or(false)
    }

    /// Check whether a path points at a directory.
    pub fn is_dir<P: AsRef<path::Path>>(&self, path: P) -> bool {
        self.vfs
            .metadata(path.as_ref())
            .map(|m| m.is_dir())
            .unwrap_or(false)
    }

    /// Return the full path to the user data directory
    pub fn get_user_data_dir(&self) -> &path::Path {
        &self.user_data_path
    }

    /// Returns the full path to the resource directory
    /// (even if it doesn't exist)
    pub fn get_resources_dir(&self) -> &path::Path {
        &self.resources_path
    }

    /// Returns a list of all files and directories in the resource directory,
    /// in no particular order.
    ///
    /// Lists the base directory if an empty path is given.
    pub fn read_dir<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<Vec<path::PathBuf>> {
        // TODO: This should return an iterator, and be called iter()
        let itr = self.vfs
            .read_dir(path.as_ref())?
            .map(|fname| fname.unwrap())
            .collect();
        Ok(itr)
    }

    /// Prints the contents of all data directories.
    /// Useful for debugging.
    pub fn print_all(&mut self) -> GameResult<()> {
        for vfs in self.vfs.roots() {
            println!("Source {:?}", vfs);
            for itm in vfs.read_dir(path::Path::new("/"))? {
                println!("  {:?}", itm);
            }
        }
        Ok(())
    }

    /// Looks for a file named "conf.toml" in the resources directory
    /// loads it if it finds it.
    /// If it can't read it for some reason, returns an error.
    pub fn read_config(&mut self) -> GameResult<conf::Conf> {
        let conf_path = path::Path::new(CONFIG_NAME);
        if self.is_file(conf_path) {
            let mut file = self.open(conf_path)?;
            let c = conf::Conf::from_toml_file(&mut file)?;
            Ok(c)
        } else {
            Err(GameError::ConfigError(
                String::from("Config file not found"),
            ))
        }
    }

    /// Takes a `conf::Conf` object and saves it to the user directory,
    /// overwriting any file already there.
    pub fn write_config(&mut self, conf: &conf::Conf) -> GameResult<()> {
        let conf_path = path::Path::new(CONFIG_NAME);
        let mut file = self.create(conf_path)?;
        conf.to_toml_file(&mut file)?;
        if self.is_file(conf_path) {
            Ok(())
        } else {
            Err(GameError::ConfigError(format!(
                "Failed to write config file at {}",
                conf_path.to_string_lossy()
            )))
        }
    }
}

#[cfg(test)]
mod tests {
    use error::*;
    use filesystem::*;
    use std::path;
    use std::io::{Read, Write};
    use conf;

    fn get_dummy_fs_for_tests() -> Filesystem {
        let mut path = path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        path.push("resources");
        let physfs = vfs::PhysicalFS::new(&path, false);
        let mut ofs = vfs::OverlayFS::new();
        ofs.push_front(Box::new(physfs));
        Filesystem {
            vfs: ofs,

            resources_path: "".into(),
            zip_path: "".into(),
            // user_config_path: "".into(),
            user_data_path: "".into(),
        }

    }

    #[test]
    fn test_file_exists() {
        let f = get_dummy_fs_for_tests();

        let tile_file = path::Path::new("/tile.png");
        assert!(f.exists(tile_file));
        assert!(f.is_file(tile_file));

        let tile_file = path::Path::new("/oglebog.png");
        assert!(!f.exists(tile_file));
        assert!(!f.is_file(tile_file));
        assert!(!f.is_dir(tile_file));

    }

    #[test]
    fn test_read_dir() {
        let mut f = get_dummy_fs_for_tests();

        let dir_contents_size = f.read_dir("/").unwrap().len();
        assert!(dir_contents_size > 0);
    }

    #[test]
    fn test_create_delete_file() {
        let mut fs = get_dummy_fs_for_tests();
        let test_file = path::Path::new("/testfile.txt");
        let bytes = "test".as_bytes();

        {
            let mut file = fs.create(test_file).unwrap();
            file.write(bytes).unwrap();
        }
        {
            let mut buffer = Vec::new();
            let mut file = fs.open(test_file).unwrap();
            file.read_to_end(&mut buffer).unwrap();
            assert_eq!(bytes, buffer.as_slice());
        }

        fs.delete(test_file).unwrap();
    }

    #[test]
    fn test_file_not_found() {
        let mut fs = get_dummy_fs_for_tests();
        {
            let rel_file = "testfile.txt";
            match fs.open(rel_file) {
                Err(GameError::ResourceNotFound(_, _)) => (),
                Err(e) => panic!("Invalid error for opening file with relative path: {:?}", e),
                Ok(f) => panic!("Should have gotten an error but instead got {:?}!", f),
            }
        }

        {
            // This absolute path should work on Windows too since we
            // completely remove filesystem roots.
            match fs.open("/ooglebooglebarg.txt") {
                Err(GameError::ResourceNotFound(_, _)) => (),
                Err(e) => panic!("Invalid error for opening nonexistent file: {}", e),
                Ok(f) => panic!("Should have gotten an error but instead got {:?}", f),
            }
        }
    }

    #[test]
    fn test_write_config() {
        let mut f = get_dummy_fs_for_tests();
        let conf = conf::Conf::new();
        // The config file should end up in
        // the resources directory with this
        match f.write_config(&conf) {
            Ok(_) => (),
            Err(e) => panic!("{:?}", e),
        }
        // Remove the config file!
        f.delete(CONFIG_NAME).unwrap();
    }
}
<commit_msg>Uncommented the writable user vfs dir.  :/<commit_after>//! Provides a portable interface to the filesystem.
//!
//! This module provides access to files in specific places:
//!
//! * The `resources/` subdirectory in the same directory as the
//! program executable,
//! * The `resources.zip` file in the same
//! directory as the program executable,
//! * The root folder of the  game's "save" directory which is in a
//! platform-dependent location,
//! such as `~/.local/share/<gameid>/` on Linux.  The `gameid`
//! is the the string passed to
//! `Context::load_from_conf()`; some platforms such as Windows also
//! incorporate the `author` string into the path.
//!
//! Files will be looked for in these locations in order, and the first one
//! found used.  That allows game assets to be easily distributed as an archive
//! file, but locally overridden for testing or modding simply by putting
//! altered copies of them in the game's `resources/` directory.
//!
//! Note that the file lookups WILL follow symlinks!  It is
//! more for convenience than absolute security, so don't treat it as
//! being secure.
//!
//! If you build `ggez` with the `cargo-resource-root` feature flag, it will
//! also look for a `resources/` subdirectory in the same directory as your
//! `Cargo.toml`, which can be very convenient for development.

// BUGGO: TODO: Also make it print out the searched directories when it
// can't find a file!

use std::env;
use std::fmt;
use std::io;
use std::path;

use app_dirs::*;

use GameError;
use GameResult;
use conf;
use vfs::{self, VFS};

pub use vfs::OpenOptions;

const CONFIG_NAME: &'static str = "/conf.toml";

/// A structure that contains the filesystem state and cache.
#[derive(Debug)]
pub struct Filesystem {
    vfs: vfs::OverlayFS,
    resources_path: path::PathBuf,
    zip_path: path::PathBuf,
    // user_config_path: path::PathBuf,
    user_data_path: path::PathBuf,
}

/// Represents a file, either in the filesystem, or in the resources zip file,
/// or whatever.
pub enum File {
    VfsFile(Box<vfs::VFile>),
}

impl fmt::Debug for File {
    // TODO: Make this more useful.
    // But we can't seem to get a filename out of a file,
    // soooooo.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            File::VfsFile(ref _file) => write!(f, "VfsFile"),
        }
    }
}

impl io::Read for File {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            File::VfsFile(ref mut f) => f.read(buf),
        }
    }
}

impl io::Write for File {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match *self {
            File::VfsFile(ref mut f) => f.write(buf),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match *self {
            File::VfsFile(ref mut f) => f.flush(),
        }
    }
}

impl Filesystem {
    /// Create a new Filesystem instance, using the given `id` and (on
    /// some platforms) the `author` as a portion of the user
    /// directory path.  This function is called automatically by
    /// ggez, the end user should never need to call it.
    pub fn new(id: &'static str, author: &'static str) -> GameResult<Filesystem> {
        let app_info = AppInfo {
            name: id,
            author: author,
        };
        let mut root_path = env::current_exe()?;

        // Ditch the filename (if any)
        if root_path.file_name().is_some() {
            root_path.pop();
        }

        // Set up VFS to merge resource path, root path, and zip path.
        let mut overlay = vfs::OverlayFS::new();

        let mut resources_path;
        let mut resources_zip_path;
        let user_data_path;
        // let user_config_path;
        // <game exe root>/resources/
        {
            resources_path = root_path.clone();
            resources_path.push("resources");
            let physfs = vfs::PhysicalFS::new(&resources_path, true);
            overlay.push_back(Box::new(physfs));
        }

        // <root>/resources.zip
        {
            resources_zip_path = root_path.clone();
            resources_zip_path.push("resources.zip");
            if resources_zip_path.exists() {
                let zipfs = vfs::ZipFS::new(&resources_zip_path)?;
                overlay.push_back(Box::new(zipfs));
            }
        }

        // Per-user data dir,
        // ~/.local/share/whatever/
        {
            user_data_path = app_root(AppDataType::UserData, &app_info)?;
            let physfs = vfs::PhysicalFS::new(&user_data_path, true);
            overlay.push_back(Box::new(physfs));
        }

        // Writeable local dir, ~/.config/whatever/
        // Save game dir is read-write
        {
            user_config_path = app_root(AppDataType::UserConfig, &app_info)?;
            let physfs = vfs::PhysicalFS::new(&user_config_path, false);
            overlay.push_back(Box::new(physfs));
        }

        // Cargo manifest dir!
        #[cfg(feature = "cargo-resource-root")]
        {
            if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
                let mut path = path::PathBuf::from(manifest_dir);
                path.push("resources");
                let physfs = vfs::PhysicalFS::new(&path, true);
                overlay.push_back(Box::new(physfs));
            }
        }

        let fs = Filesystem {
            vfs: overlay,
            resources_path: resources_path,
            zip_path: resources_zip_path,
            // user_config_path: user_config_path,
            user_data_path: user_data_path,
        };

        Ok(fs)
    }

    /// Opens the given path and returns the resulting `File`
    /// in read-only mode.
    pub fn open<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<File> {
        self.vfs.open(path.as_ref()).map(|f| File::VfsFile(f))
    }

    /// Opens a file in the user directory with the given `filesystem::OpenOptions`.
    /// Note that even if you open a file read-only, it can only access
    /// files in the user directory.
    pub fn open_options<P: AsRef<path::Path>>(
        &mut self,
        path: P,
        options: &OpenOptions,
    ) -> GameResult<File> {
        self.vfs
            .open_options(path.as_ref(), options)
            .map(|f| File::VfsFile(f))
            .map_err(|e| {
                GameError::ResourceLoadError(format!(
                    "Tried to open {:?} but got error: {:?}",
                    path.as_ref(),
                    e
                ))
            })
    }

    /// Creates a new file in the user directory and opens it
    /// to be written to, truncating it if it already exists.
    pub fn create<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<File> {
        self.vfs.create(path.as_ref()).map(|f| File::VfsFile(f))
    }

    /// Create an empty directory in the user dir
    /// with the given name.  Any parents to that directory
    /// that do not exist will be created.
    pub fn create_dir<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<()> {
        self.vfs.mkdir(path.as_ref())
    }

    /// Deletes the specified file in the user dir.
    pub fn delete<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<()> {
        self.vfs.rm(path.as_ref())
    }

    /// Deletes the specified directory in the user dir,
    /// and all its contents!
    pub fn delete_dir<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<()> {
        self.vfs.rmrf(path.as_ref())
    }

    /// Check whether a file or directory exists.
    pub fn exists<P: AsRef<path::Path>>(&self, path: P) -> bool {
        self.vfs.exists(path.as_ref())
    }

    /// Check whether a path points at a file.
    pub fn is_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
        self.vfs
            .metadata(path.as_ref())
            .map(|m| m.is_file())
            .unwrap_or(false)
    }

    /// Check whether a path points at a directory.
    pub fn is_dir<P: AsRef<path::Path>>(&self, path: P) -> bool {
        self.vfs
            .metadata(path.as_ref())
            .map(|m| m.is_dir())
            .unwrap_or(false)
    }

    /// Return the full path to the user data directory
    pub fn get_user_data_dir(&self) -> &path::Path {
        &self.user_data_path
    }

    /// Returns the full path to the resource directory
    /// (even if it doesn't exist)
    pub fn get_resources_dir(&self) -> &path::Path {
        &self.resources_path
    }

    /// Returns a list of all files and directories in the resource directory,
    /// in no particular order.
    ///
    /// Lists the base directory if an empty path is given.
    pub fn read_dir<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<Vec<path::PathBuf>> {
        // TODO: This should return an iterator, and be called iter()
        let itr = self.vfs
            .read_dir(path.as_ref())?
            .map(|fname| fname.unwrap())
            .collect();
        Ok(itr)
    }

    /// Prints the contents of all data directories.
    /// Useful for debugging.
    pub fn print_all(&mut self) -> GameResult<()> {
        for vfs in self.vfs.roots() {
            println!("Source {:?}", vfs);
            for itm in vfs.read_dir(path::Path::new("/"))? {
                println!("  {:?}", itm);
            }
        }
        Ok(())
    }

    /// Looks for a file named "conf.toml" in the resources directory
    /// loads it if it finds it.
    /// If it can't read it for some reason, returns an error.
    pub fn read_config(&mut self) -> GameResult<conf::Conf> {
        let conf_path = path::Path::new(CONFIG_NAME);
        if self.is_file(conf_path) {
            let mut file = self.open(conf_path)?;
            let c = conf::Conf::from_toml_file(&mut file)?;
            Ok(c)
        } else {
            Err(GameError::ConfigError(
                String::from("Config file not found"),
            ))
        }
    }

    /// Takes a `conf::Conf` object and saves it to the user directory,
    /// overwriting any file already there.
    pub fn write_config(&mut self, conf: &conf::Conf) -> GameResult<()> {
        let conf_path = path::Path::new(CONFIG_NAME);
        let mut file = self.create(conf_path)?;
        conf.to_toml_file(&mut file)?;
        if self.is_file(conf_path) {
            Ok(())
        } else {
            Err(GameError::ConfigError(format!(
                "Failed to write config file at {}",
                conf_path.to_string_lossy()
            )))
        }
    }
}

#[cfg(test)]
mod tests {
    use error::*;
    use filesystem::*;
    use std::path;
    use std::io::{Read, Write};
    use conf;

    fn get_dummy_fs_for_tests() -> Filesystem {
        let mut path = path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        path.push("resources");
        let physfs = vfs::PhysicalFS::new(&path, false);
        let mut ofs = vfs::OverlayFS::new();
        ofs.push_front(Box::new(physfs));
        Filesystem {
            vfs: ofs,

            resources_path: "".into(),
            zip_path: "".into(),
            // user_config_path: "".into(),
            user_data_path: "".into(),
        }

    }

    #[test]
    fn test_file_exists() {
        let f = get_dummy_fs_for_tests();

        let tile_file = path::Path::new("/tile.png");
        assert!(f.exists(tile_file));
        assert!(f.is_file(tile_file));

        let tile_file = path::Path::new("/oglebog.png");
        assert!(!f.exists(tile_file));
        assert!(!f.is_file(tile_file));
        assert!(!f.is_dir(tile_file));

    }

    #[test]
    fn test_read_dir() {
        let mut f = get_dummy_fs_for_tests();

        let dir_contents_size = f.read_dir("/").unwrap().len();
        assert!(dir_contents_size > 0);
    }

    #[test]
    fn test_create_delete_file() {
        let mut fs = get_dummy_fs_for_tests();
        let test_file = path::Path::new("/testfile.txt");
        let bytes = "test".as_bytes();

        {
            let mut file = fs.create(test_file).unwrap();
            file.write(bytes).unwrap();
        }
        {
            let mut buffer = Vec::new();
            let mut file = fs.open(test_file).unwrap();
            file.read_to_end(&mut buffer).unwrap();
            assert_eq!(bytes, buffer.as_slice());
        }

        fs.delete(test_file).unwrap();
    }

    #[test]
    fn test_file_not_found() {
        let mut fs = get_dummy_fs_for_tests();
        {
            let rel_file = "testfile.txt";
            match fs.open(rel_file) {
                Err(GameError::ResourceNotFound(_, _)) => (),
                Err(e) => panic!("Invalid error for opening file with relative path: {:?}", e),
                Ok(f) => panic!("Should have gotten an error but instead got {:?}!", f),
            }
        }

        {
            // This absolute path should work on Windows too since we
            // completely remove filesystem roots.
            match fs.open("/ooglebooglebarg.txt") {
                Err(GameError::ResourceNotFound(_, _)) => (),
                Err(e) => panic!("Invalid error for opening nonexistent file: {}", e),
                Ok(f) => panic!("Should have gotten an error but instead got {:?}", f),
            }
        }
    }

    #[test]
    fn test_write_config() {
        let mut f = get_dummy_fs_for_tests();
        let conf = conf::Conf::new();
        // The config file should end up in
        // the resources directory with this
        match f.write_config(&conf) {
            Ok(_) => (),
            Err(e) => panic!("{:?}", e),
        }
        // Remove the config file!
        f.delete(CONFIG_NAME).unwrap();
    }
}
<|endoftext|>


<commit_before>use std::io;

use futures::stream::Stream;
use futures::{Future, Task, Poll};
use futures_io::{Ready, IoFuture};

use event_loop::{IoSource, LoopHandle};

/// A concrete implementation of a stream of readiness notifications for I/O
/// objects that originates from an event loop.
///
/// Created by the `ReadinessStream::new` method, each `ReadinessStream` is
/// associated with a specific event loop and source of events that will be
/// registered with an event loop.
///
/// Currently readiness streams have "edge" semantics. That is, if a stream
/// receives a readable notification it will not receive another readable
/// notification until all bytes have been read from the stream.
///
/// Note that the precise semantics of when notifications are received will
/// likely be configurable in the future.
pub struct ReadinessStream {
    io_token: usize,
    loop_handle: LoopHandle,
    source: IoSource,
}

impl ReadinessStream {
    /// Creates a new readiness stream associated with the provided
    /// `loop_handle` and for the given `source`.
    ///
    /// This method returns a future which will resolve to the readiness stream
    /// when it's ready.
    pub fn new(loop_handle: LoopHandle, source: IoSource)
               -> Box<IoFuture<ReadinessStream>> {
        loop_handle.add_source(source.clone()).map(|token| {
            ReadinessStream {
                io_token: token,
                source: source,
                loop_handle: loop_handle,
            }
        }).boxed()
    }
}

impl Stream for ReadinessStream {
    type Item = Ready;
    type Error = io::Error;

    fn poll(&mut self, _task: &mut Task) -> Poll<Option<Ready>, io::Error> {
        match self.source.take_readiness() {
            None => Poll::NotReady,
            Some(r) => Poll::Ok(Some(r)),
        }
    }

    fn schedule(&mut self, task: &mut Task) {
        self.loop_handle.schedule(self.io_token, task)
    }
}

impl Drop for ReadinessStream {
    fn drop(&mut self) {
        self.loop_handle.drop_source(self.io_token)
    }
}
<commit_msg>Remove the executable bit on a file<commit_after>use std::io;

use futures::stream::Stream;
use futures::{Future, Task, Poll};
use futures_io::{Ready, IoFuture};

use event_loop::{IoSource, LoopHandle};

/// A concrete implementation of a stream of readiness notifications for I/O
/// objects that originates from an event loop.
///
/// Created by the `ReadinessStream::new` method, each `ReadinessStream` is
/// associated with a specific event loop and source of events that will be
/// registered with an event loop.
///
/// Currently readiness streams have "edge" semantics. That is, if a stream
/// receives a readable notification it will not receive another readable
/// notification until all bytes have been read from the stream.
///
/// Note that the precise semantics of when notifications are received will
/// likely be configurable in the future.
pub struct ReadinessStream {
    io_token: usize,
    loop_handle: LoopHandle,
    source: IoSource,
}

impl ReadinessStream {
    /// Creates a new readiness stream associated with the provided
    /// `loop_handle` and for the given `source`.
    ///
    /// This method returns a future which will resolve to the readiness stream
    /// when it's ready.
    pub fn new(loop_handle: LoopHandle, source: IoSource)
               -> Box<IoFuture<ReadinessStream>> {
        loop_handle.add_source(source.clone()).map(|token| {
            ReadinessStream {
                io_token: token,
                source: source,
                loop_handle: loop_handle,
            }
        }).boxed()
    }
}

impl Stream for ReadinessStream {
    type Item = Ready;
    type Error = io::Error;

    fn poll(&mut self, _task: &mut Task) -> Poll<Option<Ready>, io::Error> {
        match self.source.take_readiness() {
            None => Poll::NotReady,
            Some(r) => Poll::Ok(Some(r)),
        }
    }

    fn schedule(&mut self, task: &mut Task) {
        self.loop_handle.schedule(self.io_token, task)
    }
}

impl Drop for ReadinessStream {
    fn drop(&mut self) {
        self.loop_handle.drop_source(self.io_token)
    }
}
<|endoftext|>