<commit_before>//! # VkApp
//! Presentation of application to easily realizing authorization process.
//!
//! # Examples
//! ```ignore
//! let app = VkApp::new(3252132, "v5.34".into(), Some("http://example.com/vk-redirect.html".into()));
//! let url = app.auth_client_uri("audio, friends".into());
//! //... And then redirect user on the url and so...
//! ```

extern crate hyper;

use std::io::prelude::*;

use api::CallError;
use user::VkUser;
use server::VkServer;
use fake_browser::{authorization_client_uri, fake_browser};

/// Reflect VK app needs
#[derive(Debug)]
pub struct VkApp {
    app_id: u64,
    version: String,
    redirect: String
}

impl VkApp {

    /// Constructor of new app.
    pub fn new(app_id: u64, version: String, mut redirect: Option<String>) -> VkApp {
        if redirect.is_none() {
            redirect = Some("https://oauth.vk.com/blank.html".into());
        }
        VkApp { app_id: app_id, version: version, redirect: redirect.unwrap() }
    }

    /// Return URL for user(client, Standalone) authorization. [Documentation
    /// overview](https://vk.com/dev/auth_mobile)
    pub fn auth_client_uri(&self, scope: String) -> String {
        authorization_client_uri(self.app_id, scope, self.version.clone(), self.redirect.clone())
    }

    /// Implement client authorization without using real user browser.
    /// __Warning:__ Use the method only for test or with care about user privacy
    /// and vk.com privacy policy. Use it on your own risk 'cause there's no guarantee
    /// that the way will work always success.
    pub fn client(&self, login: String, pass: String, scope: String ) -> Result<VkUser, CallError> {
        match fake_browser(login, pass, self.auth_client_uri(scope)) {
            Ok((token, expires, id)) => Ok(VkUser::new(id, token, expires)),
            Err(e) => Err(e)
        }
    }
    /// Implement Server authorization for 'secure.*' methods of VK.com API.
    pub fn server(&self, secret: String) -> Result<VkServer, CallError> {
        use ::rustc_serialize::json::Json;
        use self::hyper::client::Client;
        use self::hyper::client::response::Response;

        let url = format!("https://oauth.vk.com/access_token?client_id={}&client_secret={}&v={}&grant_type=client_credentials", self.app_id, secret, self.version);

        let client = Client::new();

        let mut res: Response;

        match client.get(&url).send() {
            Ok(r) => {
                res = r
            },
            Err(e) => {
                return Err(CallError::new("Server request error.".into(),
                Some(Box::new(e))))

            }
        };
        let mut answer = String::new();

        match res.read_to_string(&mut answer) {
            Ok(_) => {
                match answer.parse::<Json>() {
                    Ok(object) => {
                        match object.find("access_token") {
                            Some(json) => {
                                if json.is_string() {
                                    Ok(VkServer::new(
                                            self.app_id,
                                            json.as_string().unwrap().to_string(),
                                            secret))
                                }
                                else {
                                    Err(CallError::new("Error parse json.".into(), None))
                                }
                            }
                            None => {
                                Err(CallError::new("Error access_token field".into(), None))
                            }                                }
                    }
                    Err(e) => {
                        Err(CallError::new(
                                "Error parse body of response to json object".into(),
                                Some(Box::new(e))))
                    }
                }
            }
            Err(e) => {
                Err(CallError::new(
                        "Error reading server reponse body.".into(),
                        Some(Box::new(e))))
            }
        }
    }
}
<commit_msg>Implemented support Authorization Code Flow way.<commit_after>//! # VkApp
//! Presentation of application to easily realizing authorization process.
//!
//! # Examples
//! ```ignore
//! let app = VkApp::new(3252132, "v5.34".into(), Some("http://example.com/vk-redirect.html".into()));
//! let url = app.auth_client_uri("audio, friends".into());
//! //... And then redirect user on the url and so...
//! ```

extern crate hyper;

use std::io::prelude::*;
use ::rustc_serialize::json::Json;

use api::CallError;
use user::VkUser;
use server::VkServer;
use fake_browser::{authorization_client_uri, fake_browser};

/// Reflect VK app needs
#[derive(Debug)]
pub struct VkApp {
    app_id: u64,
    version: String,
    redirect: String
}

impl VkApp {

    /// Constructor of new app.
    pub fn new(app_id: u64, version: String, mut redirect: Option<String>) -> VkApp {
        if redirect.is_none() {
            redirect = Some("https://oauth.vk.com/blank.html".into());
        }
        VkApp { app_id: app_id, version: version, redirect: redirect.unwrap() }
    }

    /// Return URL for user(client, Standalone) authorization. [Documentation
    /// overview](https://vk.com/dev/auth_mobile)
    pub fn auth_client_uri(&self, scope: String) -> String {
        authorization_client_uri(self.app_id, scope, self.version.clone(), self.redirect.clone())
    }

    /// Build URL to redirect a user in Authorization Code Flow OAuth
    /// authrization case.
    pub fn auth_site_uri(&self, scope: String, state: String) -> String {
        format!("https://oauth.vk.com/authorize?client_id={id}&scope={scope}&redirect_uri={redirect}&response_type=code&v={v}&state={state}", id=self.app_id, scope=scope, redirect=self.redirect, v=self.version,state=state)
    }

    /// Implement client authorization without using real user browser.
    /// __Warning:__ Use the method only for test or with care about user privacy
    /// and vk.com privacy policy. Use it on your own risk 'cause there's no guarantee
    /// that the way will work always success.
    pub fn client(&self, login: String, pass: String, scope: String ) -> Result<VkUser, CallError> {
        match fake_browser(login, pass, self.auth_client_uri(scope)) {
            Ok((token, expires, id)) => Ok(VkUser::new(id, token, expires)),
            Err(e) => Err(e)
        }
    }
    /// Implement Server authorization for 'secure.*' methods of VK.com API.
    pub fn server(&self, secret: String) -> Result<VkServer, CallError> {
        let url = format!("https://oauth.vk.com/access_token?client_id={}&client_secret={}&v={}&grant_type=client_credentials", self.app_id, secret, self.version);
        match VkApp::fetch_json(url) {
            Ok(object) => {
                match object.find("access_token") {
                    Some(json) => {
                        if json.is_string() {
                            Ok(VkServer::new(
                                    self.app_id,
                                    json.as_string().unwrap().to_string(),
                                    secret))
                        }
                        else {
                            Err(CallError::new("Error parse json.".into(), None))
                        }
                    }
                    None => {
                        Err(CallError::new("Error access_token field".into(), None))
                    }
                }
            }
            Err(e) => {
                Err(CallError::new("Cannot fetch JSON.".into(), Some(Box::new(e))))
            }
        }
    }
    /// Function that build new VkUser instance using use code and secret of the app.
    /// (Authorization Code Flow)
    pub fn site(&self, secret: String, code: String) -> Result<VkUser,CallError> {

        let url = format!("https://oauth.vk.com/access_token?client_id={id}&client_secret={secret}&code={code}&redirect_uri={redirect}", id=self.app_id, secret=secret,code=code, redirect=self.redirect);

        match VkApp::fetch_json(url) {
            Ok(object) => {
                if object["access_token"].is_string() && object["expires_in"].is_u64() && object["user_id"].is_u64() {
                   Ok(VkUser::new(
                       object["user_id"].as_u64().unwrap(),
                       object["access_token"].as_string().unwrap().into(),
                       object["expires_in"].as_u64().unwrap()))
                }
                else {
                    Err(CallError::new("access_token, expires_in or user_id field is missing in answered object.".into(), None))
                }
            }
            Err(e) => {
                Err(CallError::new("Cannot fetch JSON.".into(), Some(Box::new(e))))
            }
        }

    }

    fn fetch_json(url: String) -> Result<Json, CallError> {
        use self::hyper::client::Client;
        use self::hyper::client::response::Response;

        let client = Client::new();

        let mut res: Response;

        match client.get(&url).send() {
            Ok(r) => res = r,
            Err(e) => return Err(CallError::new("Can't make a request to get JSON.".into(), Some(Box::new(e))))
        };

        let mut answer = String::new();

        match res.read_to_string(&mut answer) {
            Ok(_) => {
                match answer.parse::<Json>() {
                    Ok(object) => {
                        Ok(object)
                    }
                    Err(e) => {
                        Err(CallError::new(
                                "Cannot parse answer to JSON object.".into(),
                                Some(Box::new(e))))
                    }
                }
            }
            Err(e) => {
                Err(CallError::new(
                        "Cannot read server answer.".into(),
                        Some(Box::new(e))))
            }
        }

    }

}
<|endoftext|>


<commit_before>use super::smp::Smp;
use super::dsp::dsp::Dsp;
use super::timer::Timer;
use super::ring_buffer::RingBuffer;
use super::spc::spc::{Spc, RAM_LEN, IPL_ROM_LEN};

static DEFAULT_IPL_ROM: [u8; IPL_ROM_LEN] = [
    0xcd, 0xef, 0xbd, 0xe8, 0x00, 0xc6, 0x1d, 0xd0,
    0xfc, 0x8f, 0xaa, 0xf4, 0x8f, 0xbb, 0xf5, 0x78,
    0xcc, 0xf4, 0xd0, 0xfb, 0x2f, 0x19, 0xeb, 0xf4,
    0xd0, 0xfc, 0x7e, 0xf4, 0xd0, 0x0b, 0xe4, 0xf5,
    0xcb, 0xf4, 0xd7, 0x00, 0xfc, 0xd0, 0xf3, 0xab,
    0x01, 0x10, 0xef, 0x7e, 0xf4, 0x10, 0xeb, 0xba,
    0xf6, 0xda, 0x00, 0xba, 0xf4, 0xc4, 0xf4, 0xdd,
    0x5d, 0xd0, 0xdb, 0x1f, 0x00, 0x00, 0xc0, 0xff];

const SAMPLE_RATE: usize = 32000;
pub const BUFFER_LEN: usize = SAMPLE_RATE * 2;

pub struct Apu {
    ram: Box<[u8; RAM_LEN]>,
    ipl_rom: Box<[u8; IPL_ROM_LEN]>,

    smp: Option<Box<Smp>>,
    dsp: Option<Box<Dsp>>,

    timers: [Timer; 3],

    left_output_buffer: Box<[i16; BUFFER_LEN]>,
    right_output_buffer: Box<[i16; BUFFER_LEN]>,
    overflow_buffer: Box<RingBuffer>,

    is_ipl_rom_enabled: bool,
    dsp_reg_address: u8
}

impl Apu {
    pub fn new() -> Box<Apu> {
        let mut ret = Box::new(Apu {
            ram: Box::new([0; RAM_LEN]),
            ipl_rom: Box::new([0; IPL_ROM_LEN]),

            smp: None,
            dsp: None,

            timers: [Timer::new(256), Timer::new(256), Timer::new(32)],

            left_output_buffer: Box::new([0; BUFFER_LEN]),
            right_output_buffer: Box::new([0; BUFFER_LEN]),
            overflow_buffer: Box::new(RingBuffer::new()),

            is_ipl_rom_enabled: true,
            dsp_reg_address: 0
        });
        let ret_ptr = &mut *ret as *mut _;
        ret.smp = Some(Box::new(Smp::new(ret_ptr)));
        ret.dsp = Some(Dsp::new(ret_ptr));
        ret.reset();
        ret
    }

    pub fn reset(&mut self) {
        // TODO: Randomize ram
        // TODO: Is there a better way to do this?
        for i in 0..IPL_ROM_LEN {
            self.ipl_rom[i] = DEFAULT_IPL_ROM[i];
        }

        if let Some(ref mut smp) = self.smp {
            smp.reset();
        }
        if let Some(ref mut dsp) = self.dsp {
            dsp.reset();
        }
        for timer in self.timers.iter_mut() {
            timer.reset();
        }

        self.is_ipl_rom_enabled = true;
        self.dsp_reg_address = 0;
    }

    pub fn render(&mut self, left_buffer: &mut [i16], right_buffer: &mut [i16], num_samples: i32) {
        if let Some(ref mut smp) = self.smp {
            if let Some(ref mut dsp) = self.dsp {
                while self.overflow_buffer.get_sample_count() < num_samples {
                    dsp.set_output_buffers(&mut *self.left_output_buffer as *mut _, &mut *self.right_output_buffer as *mut _);
                    smp.run(num_samples * 64);
                    dsp.flush();
                    self.overflow_buffer.write(&mut *self.left_output_buffer, &mut *self.right_output_buffer, dsp.output_index);
                }
            }

            self.overflow_buffer.read(left_buffer, right_buffer, num_samples);
        }
    }

    pub fn cpu_cycles_callback(&mut self, num_cycles: i32) {
        if let Some(ref mut dsp) = self.dsp {
            dsp.cycles_callback(num_cycles);
        }
        for timer in self.timers.iter_mut() {
            timer.cpu_cycles_callback(num_cycles);
        }
    }

    pub fn read_u8(&mut self, address: u32) -> u8 {
        let address = address & 0xffff;
        if address >= 0xf0 && address < 0x0100 {
            match address {
                0xf0 | 0xf1 => 0,

                0xf2 => self.dsp_reg_address,
                0xf3 => if let Some(ref mut dsp) = self.dsp { dsp.get_register(self.dsp_reg_address) } else { 0 },

                0xfa ... 0xfc => 0,

                0xfd => self.timers[0].read_counter(),
                0xfe => self.timers[1].read_counter(),
                0xff => self.timers[2].read_counter(),

                _ => 0
            }
        } else if address >= 0xffc0 && self.is_ipl_rom_enabled {
            self.ipl_rom[(address - 0xffc0) as usize]
        } else {
            self.ram[address as usize]
        }
    }

    pub fn write_u8(&mut self, address: u32, value: u8) {
        let address = address & 0xffff;
        if address >= 0x00f0 && address < 0x0100 {
            match address {
                0xf0 => { self.set_test_reg(value); },
                0xf1 => { self.set_control_reg(value); },
                0xf2 => { self.dsp_reg_address = value; },
                0xf3 => { if let Some(ref mut dsp) = self.dsp { dsp.set_register(self.dsp_reg_address, value); } },

                0xf4 ... 0xf9 => { self.ram[address as usize] = value; },

                0xfa => { self.timers[0].set_target(value); },
                0xfb => { self.timers[1].set_target(value); },
                0xfc => { self.timers[2].set_target(value); },

                _ => () // Do nothing
            }
        } else {
            self.ram[address as usize] = value;
        }
    }

    pub fn set_state(&mut self, spc: &Spc) {
        self.reset();

        for i in 0..RAM_LEN {
            self.ram[i] = spc.ram[i];
        }
        for i in 0..IPL_ROM_LEN {
            self.ipl_rom[i] = spc.ipl_rom[i];
        }

        if let Some(ref mut smp) = self.smp {
            smp.reg_pc = spc.pc;
            smp.reg_a = spc.a;
            smp.reg_x = spc.x;
            smp.reg_y = spc.y;
            smp.set_psw(spc.psw);
            smp.reg_sp = spc.sp;
        }

        if let Some(ref mut dsp) = self.dsp {
            dsp.set_state(spc);
        }

        for i in 0..3 {
            self.timers[i].set_target(self.ram[0xfa + i]);
        }
        let control_reg = self.ram[0xf1];
        self.set_control_reg(control_reg);

        self.dsp_reg_address = self.ram[0xf2];
    }

    pub fn clear_echo_buffer(&mut self) {
        if let Some(ref mut dsp) = self.dsp {
            let length = dsp.calculate_echo_length();
            let mut end_addr = dsp.get_echo_start_address() as i32 + length;
            if end_addr > RAM_LEN as i32 {
                end_addr = RAM_LEN as i32;
            }
            for i in dsp.get_echo_start_address() as i32..end_addr {
                self.ram[i as usize] = 0xff;
            }
        }
    }

    fn set_test_reg(&self, value: u8) {
        // TODO
        let _ = value;
        panic!("Test reg not yet implemented");
    }

    fn set_control_reg(&mut self, value: u8) {
        self.is_ipl_rom_enabled = (value & 0x80) != 0;
        if (value & 0x20) != 0 {
            self.write_u8(0xf6, 0x00);
            self.write_u8(0xf7, 0x00);
        }
        if (value & 0x10) != 0 {
            self.write_u8(0xf4, 0x00);
            self.write_u8(0xf5, 0x00);
        }
        self.timers[0].set_start_stop_bit((value & 0x01) != 0);
        self.timers[1].set_start_stop_bit((value & 0x02) != 0);
        self.timers[2].set_start_stop_bit((value & 0x04) != 0);
    }
}
<commit_msg>Refactored "if let" stuff out of Apu<commit_after>use super::smp::Smp;
use super::dsp::dsp::Dsp;
use super::timer::Timer;
use super::ring_buffer::RingBuffer;
use super::spc::spc::{Spc, RAM_LEN, IPL_ROM_LEN};

static DEFAULT_IPL_ROM: [u8; IPL_ROM_LEN] = [
    0xcd, 0xef, 0xbd, 0xe8, 0x00, 0xc6, 0x1d, 0xd0,
    0xfc, 0x8f, 0xaa, 0xf4, 0x8f, 0xbb, 0xf5, 0x78,
    0xcc, 0xf4, 0xd0, 0xfb, 0x2f, 0x19, 0xeb, 0xf4,
    0xd0, 0xfc, 0x7e, 0xf4, 0xd0, 0x0b, 0xe4, 0xf5,
    0xcb, 0xf4, 0xd7, 0x00, 0xfc, 0xd0, 0xf3, 0xab,
    0x01, 0x10, 0xef, 0x7e, 0xf4, 0x10, 0xeb, 0xba,
    0xf6, 0xda, 0x00, 0xba, 0xf4, 0xc4, 0xf4, 0xdd,
    0x5d, 0xd0, 0xdb, 0x1f, 0x00, 0x00, 0xc0, 0xff];

const SAMPLE_RATE: usize = 32000;
pub const BUFFER_LEN: usize = SAMPLE_RATE * 2;

pub struct Apu {
    ram: Box<[u8; RAM_LEN]>,
    ipl_rom: Box<[u8; IPL_ROM_LEN]>,

    smp: Option<Box<Smp>>,
    dsp: Option<Box<Dsp>>,

    timers: [Timer; 3],

    left_output_buffer: Box<[i16; BUFFER_LEN]>,
    right_output_buffer: Box<[i16; BUFFER_LEN]>,
    overflow_buffer: Box<RingBuffer>,

    is_ipl_rom_enabled: bool,
    dsp_reg_address: u8
}

impl Apu {
    pub fn new() -> Box<Apu> {
        let mut ret = Box::new(Apu {
            ram: Box::new([0; RAM_LEN]),
            ipl_rom: Box::new([0; IPL_ROM_LEN]),

            smp: None,
            dsp: None,

            timers: [Timer::new(256), Timer::new(256), Timer::new(32)],

            left_output_buffer: Box::new([0; BUFFER_LEN]),
            right_output_buffer: Box::new([0; BUFFER_LEN]),
            overflow_buffer: Box::new(RingBuffer::new()),

            is_ipl_rom_enabled: true,
            dsp_reg_address: 0
        });
        let ret_ptr = &mut *ret as *mut _;
        ret.smp = Some(Box::new(Smp::new(ret_ptr)));
        ret.dsp = Some(Dsp::new(ret_ptr));
        ret.reset();
        ret
    }

    pub fn reset(&mut self) {
        // TODO: Randomize ram
        // TODO: Is there a better way to do this?
        for i in 0..IPL_ROM_LEN {
            self.ipl_rom[i] = DEFAULT_IPL_ROM[i];
        }

        self.smp.as_mut().unwrap().reset();
        self.dsp.as_mut().unwrap().reset();
        for timer in self.timers.iter_mut() {
            timer.reset();
        }

        self.is_ipl_rom_enabled = true;
        self.dsp_reg_address = 0;
    }

    pub fn render(&mut self, left_buffer: &mut [i16], right_buffer: &mut [i16], num_samples: i32) {
        let smp = self.smp.as_mut().unwrap();
        let dsp = self.dsp.as_mut().unwrap();
        while self.overflow_buffer.get_sample_count() < num_samples {
            dsp.set_output_buffers(&mut *self.left_output_buffer as *mut _, &mut *self.right_output_buffer as *mut _);
            smp.run(num_samples * 64);
            dsp.flush();
            self.overflow_buffer.write(&mut *self.left_output_buffer, &mut *self.right_output_buffer, dsp.output_index);
        }

        self.overflow_buffer.read(left_buffer, right_buffer, num_samples);
    }

    pub fn cpu_cycles_callback(&mut self, num_cycles: i32) {
        self.dsp.as_mut().unwrap().cycles_callback(num_cycles);
        for timer in self.timers.iter_mut() {
            timer.cpu_cycles_callback(num_cycles);
        }
    }

    pub fn read_u8(&mut self, address: u32) -> u8 {
        let address = address & 0xffff;
        if address >= 0xf0 && address < 0x0100 {
            match address {
                0xf0 | 0xf1 => 0,

                0xf2 => self.dsp_reg_address,
                0xf3 => self.dsp.as_mut().unwrap().get_register(self.dsp_reg_address),

                0xfa ... 0xfc => 0,

                0xfd => self.timers[0].read_counter(),
                0xfe => self.timers[1].read_counter(),
                0xff => self.timers[2].read_counter(),

                _ => 0
            }
        } else if address >= 0xffc0 && self.is_ipl_rom_enabled {
            self.ipl_rom[(address - 0xffc0) as usize]
        } else {
            self.ram[address as usize]
        }
    }

    pub fn write_u8(&mut self, address: u32, value: u8) {
        let address = address & 0xffff;
        if address >= 0x00f0 && address < 0x0100 {
            match address {
                0xf0 => { self.set_test_reg(value); },
                0xf1 => { self.set_control_reg(value); },
                0xf2 => { self.dsp_reg_address = value; },
                0xf3 => { self.dsp.as_mut().unwrap().set_register(self.dsp_reg_address, value); },

                0xf4 ... 0xf9 => { self.ram[address as usize] = value; },

                0xfa => { self.timers[0].set_target(value); },
                0xfb => { self.timers[1].set_target(value); },
                0xfc => { self.timers[2].set_target(value); },

                _ => () // Do nothing
            }
        } else {
            self.ram[address as usize] = value;
        }
    }

    pub fn set_state(&mut self, spc: &Spc) {
        self.reset();

        for i in 0..RAM_LEN {
            self.ram[i] = spc.ram[i];
        }
        for i in 0..IPL_ROM_LEN {
            self.ipl_rom[i] = spc.ipl_rom[i];
        }

        {
            let smp = self.smp.as_mut().unwrap();
            smp.reg_pc = spc.pc;
            smp.reg_a = spc.a;
            smp.reg_x = spc.x;
            smp.reg_y = spc.y;
            smp.set_psw(spc.psw);
            smp.reg_sp = spc.sp;
        }

        self.dsp.as_mut().unwrap().set_state(spc);

        for i in 0..3 {
            self.timers[i].set_target(self.ram[0xfa + i]);
        }
        let control_reg = self.ram[0xf1];
        self.set_control_reg(control_reg);

        self.dsp_reg_address = self.ram[0xf2];
    }

    pub fn clear_echo_buffer(&mut self) {
        let dsp = self.dsp.as_mut().unwrap();
        let length = dsp.calculate_echo_length();
        let mut end_addr = dsp.get_echo_start_address() as i32 + length;
        if end_addr > RAM_LEN as i32 {
            end_addr = RAM_LEN as i32;
        }
        for i in dsp.get_echo_start_address() as i32..end_addr {
            self.ram[i as usize] = 0xff;
        }
    }

    fn set_test_reg(&self, value: u8) {
        // TODO
        let _ = value;
        panic!("Test reg not yet implemented");
    }

    fn set_control_reg(&mut self, value: u8) {
        self.is_ipl_rom_enabled = (value & 0x80) != 0;
        if (value & 0x20) != 0 {
            self.write_u8(0xf6, 0x00);
            self.write_u8(0xf7, 0x00);
        }
        if (value & 0x10) != 0 {
            self.write_u8(0xf4, 0x00);
            self.write_u8(0xf5, 0x00);
        }
        self.timers[0].set_start_stop_bit((value & 0x01) != 0);
        self.timers[1].set_start_stop_bit((value & 0x02) != 0);
        self.timers[2].set_start_stop_bit((value & 0x04) != 0);
    }
}
<|endoftext|>


<commit_before>//! Art objects represent both tiles and static graphics.

use mul_reader::MulReader;
use std::io::{IoResult, MemReader, IoError, OtherIoError};
use color::{Color, Color16, Color32};
use utils::DataBuffer;

//NOTE: apparently, when looking up statics by ID, they're offset by 0x4000.

/*impl Tile for MapTile {
    fn with_transparency(&self, transparency_color: pixel) -> ~[pixel] {
        let mut image: ~[pixel] = ~[];
        let mut data_source = byte_helpers::Buffer::new(self.raw_image.clone());

        for uint::range(0, 44) |i| {

            let slice_size: uint = if (i >= 22) {(44 - i) * 2} else {(i + 1) * 2};
            image.grow((22 - (slice_size / 2)), &transparency_color);
            let slice_data = data_source.read_items(slice_size);
            image.push_all(slice_data);
            image.grow((22 - (slice_size / 2)), &transparency_color);
        };
        image
    }
}

impl Tile for StaticTile {
    fn with_transparency(&self, transparency_color: pixel) -> ~[pixel] {
        let mut image: ~[pixel] = ~[];

        for self.raw_image_rows.iter().advance |row| {
            let mut current_width = 0;
            for row.iter().advance |run_pair| {
                image.grow(run_pair.offset as uint, &transparency_color);
                image.push_all(run_pair.run);
                current_width += run_pair.offset as uint + run_pair.run.len();
                assert!(current_width <= self.width as uint)
            }
            if current_width < self.width as uint {
                image.grow((self.width as uint) - current_width, &transparency_color)
            }
        }
        image
    }
}
*/

pub trait Art {
    /**
     * Convert to a 32bit array
     *
     * Returns (width, height, colors)
     */
    fn to_32bit(&self) -> (u32, u32, Vec<Color32>);
}

pub const TILE_SIZE: u32 = 2048;
pub const STATIC_OFFSET: u32 = 0x4000;

pub struct RunPair {
    pub offset: u16,
    pub run: Vec<Color16>
}

pub type StaticRow = Vec<RunPair>;

pub struct Tile {
    pub header: u32,
    pub image_data: [Color16, ..1022]
}

impl Art for Tile {
    fn to_32bit(&self) -> (u32, u32, Vec<Color32>) {
        let mut image: Vec<Color32> = vec![];

        let mut reader = DataBuffer::new(&self.image_data);

        for i in range(0, 44) {

            let slice_size = if i >= 22 {
                (44 - i) * 2
            } else {
                (i + 1) * 2
            };

            image.grow((22 - (slice_size / 2)), 0);
            for _pixel_idx in range(0, slice_size) {
                let (r, g, b, a) = reader.read().to_rgba();
                image.push(Color::from_rgba(r, g, b, a));
            }
            image.grow((22 - (slice_size / 2)), 0);
        };
        (44, 44, image)
    }
}

pub struct Static {
    pub size: u16,
    pub trigger: u16,
    pub width: u16,
    pub height: u16,
    pub rows: Vec<StaticRow>
}

pub enum TileOrStatic {
    Tile(Tile),
    Static(Static)
}

pub struct ArtReader {
    mul_reader: MulReader
}

impl ArtReader {
    pub fn new(index_path: &Path, mul_path: &Path) -> IoResult<ArtReader> {
        let mul_reader = try!(MulReader::new(index_path, mul_path));
        Ok(ArtReader {
            mul_reader: mul_reader
        })
    }

    pub fn read(&mut self, id: u32) -> IoResult<TileOrStatic> {
        let raw = try!(self.mul_reader.read(id));
        let mut reader = MemReader::new(raw.data);
        if id >= STATIC_OFFSET {
            //It's a static, so deal with accordingly
            //let size = try!(reader.read_le_u16());
            //let trigger = try!(reader.read_le_u16());
            panic!("Not yet implemented");
        } else {
            //It's a map tile
            if raw.length != TILE_SIZE {
                Err(IoError{
                    kind: OtherIoError,
                    desc: "Invalid tile size",
                    detail: Some(format!("Got tile size of {}, expected {}", raw.length, TILE_SIZE))
                })
            } else {
                let header = try!(reader.read_le_u32());
                let mut body = [0, ..1022];
                for idx in range(0, 1022) {
                    body[idx] = try!(reader.read_le_u16());
                }
                Ok(TileOrStatic::Tile(Tile {
                    header: header, image_data: body
                }))
            }
        }
    }

    pub fn read_tile(&mut self, id: u32) -> IoResult<Tile> {
        match self.read(id) {
            Ok(TileOrStatic::Tile(tile)) => Ok(tile),
            Ok(_) => Err(IoError {
                kind: OtherIoError,
                desc: "Index out of bounts",
                detail: None
            }),
            Err(e) => Err(e)
        }
    }
}

/*
impl TileReader {

    pub fn read_tile(&self, id: uint) -> option::Option<MapTile> {
        match self.mul_reader.read(id) {
            option::Some(record) => {
                if (record.data.len() != expected_tile_size) {
                    io::println(fmt!("%u BAD SIZE AT %u VERSUS %u", id, record.data.len(), expected_tile_size));
                    return option::None;
                }

                let mut data_source = byte_helpers::Buffer::new(record.data);
                let record_header = byte_helpers::bytes_to_le_uint(data_source.read_items(4));
                let raw_image: ~[pixel] = byte_helpers::u8vec_to_u16vec(data_source.read_items(1012 * 2));

                option::Some(MapTile{
                    header: record_header as u32,
                    raw_image: raw_image
                })
            },
            option::None => option::None
        }
    }

    pub fn read_static(&self, id: uint) -> option::Option<StaticTile> {
        match self.mul_reader.read(id) {
            option::Some(record) => {
                let mut data_source = byte_helpers::Buffer::new(record.data);
                let data_size: u16 = byte_helpers::bytes_to_le_uint(data_source.read_items(2)) as u16; //Might not be size :P
                let trigger: u16 = byte_helpers::bytes_to_le_uint(data_source.read_items(2)) as u16;
                let width: u16 = byte_helpers::bytes_to_le_uint(data_source.read_items(2)) as u16;
                let height: u16 = byte_helpers::bytes_to_le_uint(data_source.read_items(2)) as u16;

                if (width == 0 || height >= 1024 || height == 0 || height >= 1024) {
                    error!("Bad image dimensions found at %u", id);
                    return option::None;
                }

                //Read the offset table
                let mut offset_table: ~[u16] = ~[];
                for uint::range(0, height as uint) |_index| {
                    let offset = byte_helpers::bytes_to_le_uint(data_source.read_items(2)) as u16;
                    offset_table.push(offset);
                }

                let data_start_pos = data_source.pos;
                let mut rows = ~[];

                for offset_table.iter().advance |offset| {
                    data_source.seek(data_start_pos as uint + (*offset as uint * 2));
                    let mut current_row_width: uint = 0;
                    let mut row = ~[];

                    loop {
                        let x_offset = byte_helpers::bytes_to_le_uint(data_source.read_items(2)) as u16;
                        let run_length = byte_helpers::bytes_to_le_uint(data_source.read_items(2)) as u16;

                        if (x_offset + run_length == 0) {
                            break;
                        } else {
                            row.push(RunPair{
                                offset: x_offset,
                                run: byte_helpers::u8vec_to_u16vec(data_source.read_items((run_length as uint) * 2))
                            });
                            current_row_width += x_offset as uint + run_length as uint;
                            assert!(current_row_width <= width as uint);
                        }
                    }
                    rows.push(row);
                }

                option::Some(StaticTile {
                    data_size: data_size,
                    trigger: trigger,
                    width: width,
                    height: height,
                    raw_image_rows: rows
                })
            },
            option::None => option::None
        }
    }
}

pub fn TileReader(index_path: &path::Path, mul_path: &path::Path) -> result::Result<TileReader, ~str> {
    match mul_reader::MulReader::new(index_path, mul_path) {
        result::Err(message) => result::Err(message),
        result::Ok(mul_reader) => {
            result::Ok(TileReader{
                mul_reader: mul_reader
            })
        }
    }
}

/*pub fn load_tiles(root_path: ~str) -> (~[(uint, MapTile)], ~[(uint, StaticTile)]) { //TODO: Find a better return type for this
    match mul_reader::reader(root_path, ~"artidx.mul", ~"art.mul") {
        result::Err(message) => {
            io::println(fmt!("Error reading art tiles - %s", message));
            fail
        },
        result::Ok(reader) => {
            let mut map_tiles: ~[(uint, MapTile)] = ~[];
            let mut static_tiles: ~[(uint, StaticTile)] = ~[];

            let mut index:uint = 0;
            while (reader.eof() != true) {
                let item: option::Option<mul_reader::MulRecord> = reader.read_bytes();
                if option::is_some(&item) {
                    let to_innerped: mul_reader::MulRecord = option::to_inner(item);
                    //let record_header = byte_helpers::bytes_to_le_uint(vec::slice(to_innerped.data, 0, 3));

                    if (index < 0x4000) {
                        let maybe_map_tile: option::Option<MapTile> = parse_map_tile(to_innerped);
                        if option::is_some(&maybe_map_tile) {
                            let tuple = (index, option::to_inner(maybe_map_tile));
                            map_tiles.push(tuple);
                        }
                    } else if (index < 0x8000){
                        let maybe_static_tile: option::Option<StaticTile> = parse_static_tile(to_innerped);
                        if option::is_some(&maybe_static_tile) {
                            let tuple = (index, option::to_inner(maybe_static_tile));
                            static_tiles.push(tuple);
                        }
                    }
                }
                index += 1;
            }

            (map_tiles, static_tiles)
        }
    }
}

fn parse_static_tile(record: mul_reader::MulRecord) -> option::Option<StaticTile> {

    let data_source = byte_helpers::ByteBuffer(copy record.data);

    let data_size: u16 = data_source.read_le_uint(2) as u16; //Might not be size :P
    let trigger: u16 = data_source.read_le_uint(2) as u16;
    let width: u16 = data_source.read_le_uint(2) as u16;
    let height: u16 = data_source.read_le_uint(2) as u16;

    if (width == 0 || height >= 1024 || height == 0 || height >= 1024) {
        io::println("Bad image dimensions found");
        return option::None;
    }

    let mut image: ~[u16] = ~[];

    //Read the offset table
    let mut offset_table: ~[u16] = ~[];
    for uint::range(0, height as uint) |_index| {
        let offset = data_source.read_le_uint(2) as u16;
        offset_table.push(offset);
    }

    let data_start_pos = data_source.pos;

    for offset_table.each |offset| {
        data_source.seek(data_start_pos as uint + (*offset as uint * 2));
        let mut current_row_width: uint = 0;

        loop {
            let x_offset = data_source.read_le_uint(2) as u16;
            let run_length = data_source.read_le_uint(2) as u16;

            if (x_offset + run_length == 0) {
                image.grow(width as uint - current_row_width, &transparent);
                break;
            } else {
                let run = byte_helpers::u8vec_to_u16vec(data_source.read_bytes((run_length as uint) * 2));
                image.grow(x_offset as uint, &transparent);
                image.push_all(run);
                current_row_width += x_offset as uint + run_length as uint;
                fail_unless!(current_row_width <= width as uint);
            }
        }
    }

    return option::Some({
        data_size: data_size,
        trigger: trigger,
        width: width,
        height: height,
        image: image
    });
}*/

/*
pub fn to_bitmap(width: u32, height: u32, data: ~[u16]) -> ~[u8] { //TODO: Make this take arbitrary pixel depths
    let signature: ~[u8] = ~[0x42, 0x4D];
    let file_size: ~[u8] = byte_helpers::uint_to_le_bytes(((width * height * 2) + 14 + 40) as u64, 4);
    let reserved: ~[u8] = ~[0, 0, 0, 0];
    let data_offset: ~[u8] = byte_helpers::uint_to_le_bytes(54, 4);

    let header_size: ~[u8] = byte_helpers::uint_to_le_bytes(40, 4);
    let width_bytes: ~[u8] = byte_helpers::uint_to_le_bytes(width as u64, 4); //FIXME: should be signed?
    let height_bytes: ~[u8] = byte_helpers::uint_to_le_bytes(height as u64, 4);
    let colour_panes: ~[u8] = ~[1, 0];
    let depth: ~[u8] = ~[16, 0];
    let compression: ~[u8] = ~[0,0,0,0];
    let image_size: ~[u8] = ~[0,0,0,0];
    let horizontal_res: ~[u8] = ~[0, 0, 0, 0];
    let vertical_res: ~[u8] = ~[0, 0, 0, 0];
    let palette_count: ~[u8] = ~[0, 0, 0, 0];
    let important_colours: ~[u8] = ~[0, 0, 0, 0];

    //54 bytes so far
    //TODO: explode the image vector, iterate backwards, turn it into bytes
    let mut rows: ~[~[u8]] = ~[];
    for uint::range(0, height as uint) |i| {
        let slice = vec::slice(data, i * (width as uint), (i+1) * (width as uint));
        let mut row: ~[u8] = ~[];
        for slice.each |sliced| {
            row.push_all(byte_helpers::uint_to_le_bytes(*sliced as u64, 2));
        }
        rows.push(row);
    };
    vec::reverse(rows);
    //vec::grow(pixels, 44 * 44 * 4, 0x7f);

    return vec::concat(~[
        signature,
        file_size,
        reserved,
        data_offset,

        header_size,
        width_bytes,
        height_bytes,
        colour_panes,
        depth,
        compression,
        image_size,
        horizontal_res,
        vertical_res,
        palette_count,
        important_colours,

        vec::concat(rows)
    ]);
}*/*/
<commit_msg>Started working on pulling through statics<commit_after>//! Art objects represent both tiles and static graphics.

use mul_reader::MulReader;
use std::io::{IoResult, MemReader, IoError, OtherIoError};
use color::{Color, Color16, Color32};
use utils::DataBuffer;

/*impl Tile for StaticTile {
    fn with_transparency(&self, transparency_color: pixel) -> ~[pixel] {
        let mut image: ~[pixel] = ~[];

        for self.raw_image_rows.iter().advance |row| {
            let mut current_width = 0;
            for row.iter().advance |run_pair| {
                image.grow(run_pair.offset as uint, &transparency_color);
                image.push_all(run_pair.run);
                current_width += run_pair.offset as uint + run_pair.run.len();
                assert!(current_width <= self.width as uint)
            }
            if current_width < self.width as uint {
                image.grow((self.width as uint) - current_width, &transparency_color)
            }
        }
        image
    }
}
*/

pub trait Art {
    /**
     * Convert to a 32bit array
     *
     * Returns (width, height, colors)
     */
    fn to_32bit(&self) -> (u32, u32, Vec<Color32>);
}

pub const TILE_SIZE: u32 = 2048;
pub const STATIC_OFFSET: u32 = 0x4000;

pub struct RunPair {
    pub offset: u16,
    pub run: Vec<Color16>
}

pub type StaticRow = Vec<RunPair>;

pub struct Tile {
    pub header: u32,
    pub image_data: [Color16, ..1022]
}

impl Art for Tile {
    fn to_32bit(&self) -> (u32, u32, Vec<Color32>) {
        let mut image: Vec<Color32> = vec![];

        let mut reader = DataBuffer::new(&self.image_data);

        for i in range(0, 44) {

            let slice_size = if i >= 22 {
                (44 - i) * 2
            } else {
                (i + 1) * 2
            };

            image.grow((22 - (slice_size / 2)), 0);
            for _pixel_idx in range(0, slice_size) {
                let (r, g, b, a) = reader.read().to_rgba();
                image.push(Color::from_rgba(r, g, b, a));
            }
            image.grow((22 - (slice_size / 2)), 0);
        };
        (44, 44, image)
    }
}

pub struct Static {
    pub size: u16,
    pub trigger: u16,
    pub width: u16,
    pub height: u16,
    pub rows: Vec<StaticRow>
}

pub enum TileOrStatic {
    Tile(Tile),
    Static(Static)
}

pub struct ArtReader {
    mul_reader: MulReader
}

impl ArtReader {
    pub fn new(index_path: &Path, mul_path: &Path) -> IoResult<ArtReader> {
        let mul_reader = try!(MulReader::new(index_path, mul_path));
        Ok(ArtReader {
            mul_reader: mul_reader
        })
    }

    pub fn read(&mut self, id: u32) -> IoResult<TileOrStatic> {
        let raw = try!(self.mul_reader.read(id));
        let mut reader = MemReader::new(raw.data);
        if id >= STATIC_OFFSET {
            //It's a static, so deal with accordingly
            let size = try!(reader.read_le_u16());
            let trigger = try!(reader.read_le_u16());
            let width = try!(reader.read_le_u16());
            let height = try!(reader.read_le_u16());
            if (width == 0 || height >= 1024 || height == 0 || height >= 1024) {
                Err(IoError{
                    kind: OtherIoError,
                    desc: "Invalid image dimensions",
                    detail: Some(format!("Got invalid width and height of {}, {}", width, height))
                })
            } else {
                panic!("Not yet implemented");
            }
        } else {
            //It's a map tile
            if raw.length != TILE_SIZE {
                Err(IoError{
                    kind: OtherIoError,
                    desc: "Invalid tile size",
                    detail: Some(format!("Got tile size of {}, expected {}", raw.length, TILE_SIZE))
                })
            } else {
                let header = try!(reader.read_le_u32());
                let mut body = [0, ..1022];
                for idx in range(0, 1022) {
                    body[idx] = try!(reader.read_le_u16());
                }
                Ok(TileOrStatic::Tile(Tile {
                    header: header, image_data: body
                }))
            }
        }
    }

    pub fn read_tile(&mut self, id: u32) -> IoResult<Tile> {
        match self.read(id) {
            Ok(TileOrStatic::Tile(tile)) => Ok(tile),
            Ok(_) => Err(IoError {
                kind: OtherIoError,
                desc: "Index out of bounds",
                detail: None
            }),
            Err(e) => Err(e)
        }
    }

    pub fn read_static(&mut self, id: u32)-> IoResult<Static> {
        match self.read(id + STATIC_OFFSET) {
            Ok(TileOrStatic::Static(stat)) => Ok(stat),
            Ok(_) => Err(IoError {
                kind: OtherIoError,
                desc: "Index out of bounds",
                detail: None
            }),
            Err(e) => Err(e)
        }
    }
}

/*
impl TileReader {

    pub fn read_static(&self, id: uint) -> option::Option<StaticTile> {
        match self.mul_reader.read(id) {
            option::Some(record) => {
                let mut data_source = byte_helpers::Buffer::new(record.data);
                let data_size: u16 = byte_helpers::bytes_to_le_uint(data_source.read_items(2)) as u16; //Might not be size :P
                let trigger: u16 = byte_helpers::bytes_to_le_uint(data_source.read_items(2)) as u16;
                let width: u16 = byte_helpers::bytes_to_le_uint(data_source.read_items(2)) as u16;
                let height: u16 = byte_helpers::bytes_to_le_uint(data_source.read_items(2)) as u16;

                if (width == 0 || height >= 1024 || height == 0 || height >= 1024) {
                    error!("Bad image dimensions found at %u", id);
                    return option::None;
                }

                //Read the offset table
                let mut offset_table: ~[u16] = ~[];
                for uint::range(0, height as uint) |_index| {
                    let offset = byte_helpers::bytes_to_le_uint(data_source.read_items(2)) as u16;
                    offset_table.push(offset);
                }

                let data_start_pos = data_source.pos;
                let mut rows = ~[];

                for offset_table.iter().advance |offset| {
                    data_source.seek(data_start_pos as uint + (*offset as uint * 2));
                    let mut current_row_width: uint = 0;
                    let mut row = ~[];

                    loop {
                        let x_offset = byte_helpers::bytes_to_le_uint(data_source.read_items(2)) as u16;
                        let run_length = byte_helpers::bytes_to_le_uint(data_source.read_items(2)) as u16;

                        if (x_offset + run_length == 0) {
                            break;
                        } else {
                            row.push(RunPair{
                                offset: x_offset,
                                run: byte_helpers::u8vec_to_u16vec(data_source.read_items((run_length as uint) * 2))
                            });
                            current_row_width += x_offset as uint + run_length as uint;
                            assert!(current_row_width <= width as uint);
                        }
                    }
                    rows.push(row);
                }

                option::Some(StaticTile {
                    data_size: data_size,
                    trigger: trigger,
                    width: width,
                    height: height,
                    raw_image_rows: rows
                })
            },
            option::None => option::None
        }
    }
}

pub fn TileReader(index_path: &path::Path, mul_path: &path::Path) -> result::Result<TileReader, ~str> {
    match mul_reader::MulReader::new(index_path, mul_path) {
        result::Err(message) => result::Err(message),
        result::Ok(mul_reader) => {
            result::Ok(TileReader{
                mul_reader: mul_reader
            })
        }
    }
}

/*pub fn load_tiles(root_path: ~str) -> (~[(uint, MapTile)], ~[(uint, StaticTile)]) { //TODO: Find a better return type for this
    match mul_reader::reader(root_path, ~"artidx.mul", ~"art.mul") {
        result::Err(message) => {
            io::println(fmt!("Error reading art tiles - %s", message));
            fail
        },
        result::Ok(reader) => {
            let mut map_tiles: ~[(uint, MapTile)] = ~[];
            let mut static_tiles: ~[(uint, StaticTile)] = ~[];

            let mut index:uint = 0;
            while (reader.eof() != true) {
                let item: option::Option<mul_reader::MulRecord> = reader.read_bytes();
                if option::is_some(&item) {
                    let to_innerped: mul_reader::MulRecord = option::to_inner(item);
                    //let record_header = byte_helpers::bytes_to_le_uint(vec::slice(to_innerped.data, 0, 3));

                    if (index < 0x4000) {
                        let maybe_map_tile: option::Option<MapTile> = parse_map_tile(to_innerped);
                        if option::is_some(&maybe_map_tile) {
                            let tuple = (index, option::to_inner(maybe_map_tile));
                            map_tiles.push(tuple);
                        }
                    } else if (index < 0x8000){
                        let maybe_static_tile: option::Option<StaticTile> = parse_static_tile(to_innerped);
                        if option::is_some(&maybe_static_tile) {
                            let tuple = (index, option::to_inner(maybe_static_tile));
                            static_tiles.push(tuple);
                        }
                    }
                }
                index += 1;
            }

            (map_tiles, static_tiles)
        }
    }
}

fn parse_static_tile(record: mul_reader::MulRecord) -> option::Option<StaticTile> {

    let data_source = byte_helpers::ByteBuffer(copy record.data);

    let data_size: u16 = data_source.read_le_uint(2) as u16; //Might not be size :P
    let trigger: u16 = data_source.read_le_uint(2) as u16;
    let width: u16 = data_source.read_le_uint(2) as u16;
    let height: u16 = data_source.read_le_uint(2) as u16;

    if (width == 0 || height >= 1024 || height == 0 || height >= 1024) {
        io::println("Bad image dimensions found");
        return option::None;
    }

    let mut image: ~[u16] = ~[];

    //Read the offset table
    let mut offset_table: ~[u16] = ~[];
    for uint::range(0, height as uint) |_index| {
        let offset = data_source.read_le_uint(2) as u16;
        offset_table.push(offset);
    }

    let data_start_pos = data_source.pos;

    for offset_table.each |offset| {
        data_source.seek(data_start_pos as uint + (*offset as uint * 2));
        let mut current_row_width: uint = 0;

        loop {
            let x_offset = data_source.read_le_uint(2) as u16;
            let run_length = data_source.read_le_uint(2) as u16;

            if (x_offset + run_length == 0) {
                image.grow(width as uint - current_row_width, &transparent);
                break;
            } else {
                let run = byte_helpers::u8vec_to_u16vec(data_source.read_bytes((run_length as uint) * 2));
                image.grow(x_offset as uint, &transparent);
                image.push_all(run);
                current_row_width += x_offset as uint + run_length as uint;
                fail_unless!(current_row_width <= width as uint);
            }
        }
    }

    return option::Some({
        data_size: data_size,
        trigger: trigger,
        width: width,
        height: height,
        image: image
    });
}*/

/*
pub fn to_bitmap(width: u32, height: u32, data: ~[u16]) -> ~[u8] { //TODO: Make this take arbitrary pixel depths
    let signature: ~[u8] = ~[0x42, 0x4D];
    let file_size: ~[u8] = byte_helpers::uint_to_le_bytes(((width * height * 2) + 14 + 40) as u64, 4);
    let reserved: ~[u8] = ~[0, 0, 0, 0];
    let data_offset: ~[u8] = byte_helpers::uint_to_le_bytes(54, 4);

    let header_size: ~[u8] = byte_helpers::uint_to_le_bytes(40, 4);
    let width_bytes: ~[u8] = byte_helpers::uint_to_le_bytes(width as u64, 4); //FIXME: should be signed?
    let height_bytes: ~[u8] = byte_helpers::uint_to_le_bytes(height as u64, 4);
    let colour_panes: ~[u8] = ~[1, 0];
    let depth: ~[u8] = ~[16, 0];
    let compression: ~[u8] = ~[0,0,0,0];
    let image_size: ~[u8] = ~[0,0,0,0];
    let horizontal_res: ~[u8] = ~[0, 0, 0, 0];
    let vertical_res: ~[u8] = ~[0, 0, 0, 0];
    let palette_count: ~[u8] = ~[0, 0, 0, 0];
    let important_colours: ~[u8] = ~[0, 0, 0, 0];

    //54 bytes so far
    //TODO: explode the image vector, iterate backwards, turn it into bytes
    let mut rows: ~[~[u8]] = ~[];
    for uint::range(0, height as uint) |i| {
        let slice = vec::slice(data, i * (width as uint), (i+1) * (width as uint));
        let mut row: ~[u8] = ~[];
        for slice.each |sliced| {
            row.push_all(byte_helpers::uint_to_le_bytes(*sliced as u64, 2));
        }
        rows.push(row);
    };
    vec::reverse(rows);
    //vec::grow(pixels, 44 * 44 * 4, 0x7f);

    return vec::concat(~[
        signature,
        file_size,
        reserved,
        data_offset,

        header_size,
        width_bytes,
        height_bytes,
        colour_panes,
        depth,
        compression,
        image_size,
        horizontal_res,
        vertical_res,
        palette_count,
        important_colours,

        vec::concat(rows)
    ]);
}*/*/
<|endoftext|>


<commit_before>use crate::{Event, Image, ImageError, MsfIndex, TrackType};

use std::path::Path;

use chdr::{ChdError, ChdFile};
use chdr::metadata::CdTrackInfo;

use log::debug;

use thiserror::Error;

const BYTES_PER_SECTOR: u32 =  2352 + 96;

// TODO: Can we really assume that the first track's pregap is always
// two seconds long?
const FIRST_TRACK_PREGAP: u32 = 150;

#[derive(Debug)]
struct Track {
    start_lba: u32,
    track_type: TrackType,
    track_info: CdTrackInfo,
}

#[derive(Debug, Error)]
pub enum ChdImageError {
    #[error(transparent)]
    ChdError(#[from] ChdError),
    #[error("CHD file does not seem like a CDROM image (wrong hunk size)")]
    WrongHunkSize,
    #[error("Wrong buffer size, needs to be 2352 bytes")]
    WrongBufferSize,
    #[error("Unsupported sector format: {0}")]
    UnsupportedSectorFormat(String)
}

pub struct ChdImage {
    chd: ChdFile,
    tracks: Vec<Track>,

    hunk: Vec<u8>,
    current_hunk_no: u32,
    current_lba: u32,
    // Starts counting from 0
    current_track: usize,

    sectors_per_hunk: u32,
}

impl ChdImage {
    pub fn open<P>(path: P) -> Result<ChdImage, ChdImageError>
        where P: AsRef<Path>
    {
        let mut chd = ChdFile::open(path)?;

        if chd.hunk_len() % BYTES_PER_SECTOR != 0 {
            return Err(ChdImageError::WrongHunkSize);
        }

        let mut hunk = vec![0; chd.hunk_len() as usize];
        chd.read_hunk(0, &mut hunk[..])?;

        let sectors_per_hunk = chd.hunk_len() / BYTES_PER_SECTOR;

        let mut tracks = Vec::new();
        if let Ok(chd_tracks) = chd.cd_tracks() {
            let mut current_lba = FIRST_TRACK_PREGAP;
            for chd_track in chd_tracks {
                let track_type = match chd_track.track_type.as_str() {
                    "MODE1_RAW" => TrackType::Mode1,
                    "MODE2_RAW" => TrackType::Mode2,
                    "AUDIO" => TrackType::Audio,
                    _ => return Err(ChdImageError::UnsupportedSectorFormat(chd_track.track_type)),
                };
                let start_lba = current_lba;
                current_lba += chd_track.frames;
                tracks.push(Track {
                    start_lba,
                    track_type,
                    track_info: chd_track
                });
            }
        }

        Ok(ChdImage {
            chd,
            hunk,
            current_hunk_no: 0,
            current_lba: 150,
            current_track: 0,

            sectors_per_hunk,

            tracks,
        })
    }

    fn update_current_track(&mut self, lba: u32) -> Result<(), ImageError> {
        let current_track = &self.tracks[self.current_track];
        let current_track_range_end = current_track.start_lba + current_track.track_info.frames;
        if !(current_track.start_lba..current_track_range_end).contains(&lba) {
            if let Some(index) = self.tracks.iter().position(
                |x| lba >= x.start_lba && lba < (x.start_lba + x.track_info.frames)
            ) {
                self.current_track = index;
                Ok(())
            } else {
                Err(ImageError::OutOfRange)
            }
        } else {
            Ok(())
        }
    }

    fn set_location_lba(&mut self, lba: u32) -> Result<(), ImageError> {
        self.current_lba = lba;
        // TODO: Can we really assume that the first track's pregap is always
        // two seconds long?
        if lba < FIRST_TRACK_PREGAP {
            self.current_track = 0;
            return Ok(());
        }

        self.update_current_track(lba)?;

        let lba = lba - FIRST_TRACK_PREGAP;
        debug!("set_location_lba {}", lba);
        let hunk_no = lba / self.sectors_per_hunk;
        if hunk_no > self.chd.num_hunks() {
            return Err(ImageError::OutOfRange);
        }
        if hunk_no != self.current_hunk_no {
            let res = self.chd.read_hunk(hunk_no, &mut self.hunk[..]);
            if let Err(e) = res {
                return Err(ChdImageError::from(e).into());
            }
            self.current_hunk_no = hunk_no;
        }
        Ok(())
    }
}

impl Image for ChdImage {
    fn num_tracks(&self) -> usize {
        self.tracks.len()
    }

    fn current_subchannel_q_valid(&self) -> bool {
        // TODO
        true
    }

    fn current_track(&self) -> Result<u8, ImageError> {
        Ok(self.current_track as u8 + 1)
    }

    fn current_index(&self) -> Result<u8, ImageError> {
        let current_track = &self.tracks[self.current_track];
        let track_local_lba = self.current_lba - current_track.start_lba;
        let index = if track_local_lba > current_track.track_info.pregap.unwrap_or(0) {
            1
        } else {
            0
        };
        Ok(index)
    }

    fn current_track_local_msf(&self) -> Result<MsfIndex, ImageError> {
        let current_track = &self.tracks[self.current_track];
        let index01_lba =
            current_track.start_lba + current_track.track_info.pregap.unwrap_or(150);

        if self.current_lba < index01_lba {
            // Negative MSFs are (100,0,0) - x
            let reference = 100 * 60 * 75;
            let offset = index01_lba - self.current_lba;
            Ok(MsfIndex::from_lba(reference - offset)?)
        } else {
            Ok(MsfIndex::from_lba(self.current_lba - index01_lba)?)
        }
    }

    fn current_global_msf(&self) -> Result<MsfIndex, ImageError> {
        Ok(MsfIndex::from_lba(self.current_lba)?)
    }

    fn current_track_type(&self) -> Result<TrackType, ImageError> {
        let current_track = &self.tracks[self.current_track];
        Ok(current_track.track_type)
    }

    fn first_track_type(&self) -> TrackType {
        self.tracks.first().unwrap().track_type
    }

    fn track_start(&self, track: u8) -> Result<MsfIndex, ImageError> {
        // Track 0: Special case for PlayStation, return length of whole disc
        // TODO: Make this less ugly?
        if track == 0 {
            let len = self.chd.num_hunks() * self.chd.hunk_len();
            let num_sectors = FIRST_TRACK_PREGAP + len / BYTES_PER_SECTOR;
            Ok(MsfIndex::from_lba(num_sectors)?)
        } else if track <= self.tracks.len() as u8 {
            let track = &self.tracks[track as usize - 1];
            let start_lba_index01 =
                track.start_lba + track.track_info.pregap.unwrap_or(150);
            debug!("track_start: {:?} {:?}", track, MsfIndex::from_lba(start_lba_index01));
            Ok(MsfIndex::from_lba(start_lba_index01)?)
        } else {
            Err(ImageError::OutOfRange)
        }
    }

    fn set_location(&mut self, target: MsfIndex) -> Result<(), ImageError> {
        self.set_location_lba(target.to_lba())
    }

    fn set_location_to_track(&mut self, track: u8) -> Result<(), ImageError> {
        debug!("set_location_to_track {}", track);
        let track_start = self.track_start(track)?;
        self.set_location(track_start)?;
        Ok(())
    }

    fn advance_position(&mut self) -> Result<Option<Event>, ImageError> {
        let res = self.set_location_lba(self.current_lba + 1);
        if let Err(e) = res {
            if let ImageError::OutOfRange = e {
                Ok(Some(Event::EndOfDisc))
            } else {
                Err(e)
            }
        } else {
            Ok(None)
        }
    }

    fn copy_current_sector(&self, buf: &mut[u8]) -> Result<(), ImageError> {
        if buf.len() != 2352 {
            return Err(ChdImageError::WrongBufferSize.into())
        }
        if self.current_lba < FIRST_TRACK_PREGAP {
            buf.fill(0);
            return Ok(());
        }
        let current_file_lba = self.current_lba - FIRST_TRACK_PREGAP;
        let sector_in_hunk = current_file_lba % self.sectors_per_hunk;
        let sector_start = (sector_in_hunk * BYTES_PER_SECTOR) as usize;
        let sector = &self.hunk[sector_start..sector_start + 2352];
        buf.clone_from_slice(sector);

        if self.current_track_type().unwrap() == TrackType::Audio {
            for x in buf.chunks_exact_mut(2) {
                x.swap(0, 1);
            }
        }

        Ok(())
    }
}<commit_msg>CHD: Detect track changes<commit_after>use crate::{Event, Image, ImageError, MsfIndex, TrackType};

use std::path::Path;

use chdr::{ChdError, ChdFile};
use chdr::metadata::CdTrackInfo;

use log::debug;

use thiserror::Error;

const BYTES_PER_SECTOR: u32 =  2352 + 96;

// TODO: Can we really assume that the first track's pregap is always
// two seconds long?
const FIRST_TRACK_PREGAP: u32 = 150;

#[derive(Debug)]
struct Track {
    start_lba: u32,
    track_type: TrackType,
    track_info: CdTrackInfo,
}

#[derive(Debug, Error)]
pub enum ChdImageError {
    #[error(transparent)]
    ChdError(#[from] ChdError),
    #[error("CHD file does not seem like a CDROM image (wrong hunk size)")]
    WrongHunkSize,
    #[error("Wrong buffer size, needs to be 2352 bytes")]
    WrongBufferSize,
    #[error("Unsupported sector format: {0}")]
    UnsupportedSectorFormat(String)
}

pub struct ChdImage {
    chd: ChdFile,
    tracks: Vec<Track>,

    hunk: Vec<u8>,
    current_hunk_no: u32,
    current_lba: u32,
    // Starts counting from 0
    current_track: usize,

    sectors_per_hunk: u32,
}

impl ChdImage {
    pub fn open<P>(path: P) -> Result<ChdImage, ChdImageError>
        where P: AsRef<Path>
    {
        let mut chd = ChdFile::open(path)?;

        if chd.hunk_len() % BYTES_PER_SECTOR != 0 {
            return Err(ChdImageError::WrongHunkSize);
        }

        let mut hunk = vec![0; chd.hunk_len() as usize];
        chd.read_hunk(0, &mut hunk[..])?;

        let sectors_per_hunk = chd.hunk_len() / BYTES_PER_SECTOR;

        let mut tracks = Vec::new();
        if let Ok(chd_tracks) = chd.cd_tracks() {
            let mut current_lba = FIRST_TRACK_PREGAP;
            for chd_track in chd_tracks {
                let track_type = match chd_track.track_type.as_str() {
                    "MODE1_RAW" => TrackType::Mode1,
                    "MODE2_RAW" => TrackType::Mode2,
                    "AUDIO" => TrackType::Audio,
                    _ => return Err(ChdImageError::UnsupportedSectorFormat(chd_track.track_type)),
                };
                let start_lba = current_lba;
                current_lba += chd_track.frames;
                tracks.push(Track {
                    start_lba,
                    track_type,
                    track_info: chd_track
                });
            }
        }

        Ok(ChdImage {
            chd,
            hunk,
            current_hunk_no: 0,
            current_lba: 150,
            current_track: 0,

            sectors_per_hunk,

            tracks,
        })
    }

    fn update_current_track(&mut self, lba: u32) -> Result<(), ImageError> {
        let current_track = &self.tracks[self.current_track];
        let current_track_range_end = current_track.start_lba + current_track.track_info.frames;
        if !(current_track.start_lba..current_track_range_end).contains(&lba) {
            if let Some(index) = self.tracks.iter().position(
                |x| lba >= x.start_lba && lba < (x.start_lba + x.track_info.frames)
            ) {
                self.current_track = index;
                Ok(())
            } else {
                Err(ImageError::OutOfRange)
            }
        } else {
            Ok(())
        }
    }

    fn set_location_lba(&mut self, lba: u32) -> Result<(), ImageError> {
        self.current_lba = lba;
        // TODO: Can we really assume that the first track's pregap is always
        // two seconds long?
        if lba < FIRST_TRACK_PREGAP {
            self.current_track = 0;
            return Ok(());
        }

        self.update_current_track(lba)?;

        let lba = lba - FIRST_TRACK_PREGAP;
        debug!("set_location_lba {}", lba);
        let hunk_no = lba / self.sectors_per_hunk;
        if hunk_no > self.chd.num_hunks() {
            return Err(ImageError::OutOfRange);
        }
        if hunk_no != self.current_hunk_no {
            let res = self.chd.read_hunk(hunk_no, &mut self.hunk[..]);
            if let Err(e) = res {
                return Err(ChdImageError::from(e).into());
            }
            self.current_hunk_no = hunk_no;
        }
        Ok(())
    }
}

impl Image for ChdImage {
    fn num_tracks(&self) -> usize {
        self.tracks.len()
    }

    fn current_subchannel_q_valid(&self) -> bool {
        // TODO
        true
    }

    fn current_track(&self) -> Result<u8, ImageError> {
        Ok(self.current_track as u8 + 1)
    }

    fn current_index(&self) -> Result<u8, ImageError> {
        let current_track = &self.tracks[self.current_track];
        let track_local_lba = self.current_lba - current_track.start_lba;
        let index = if track_local_lba > current_track.track_info.pregap.unwrap_or(0) {
            1
        } else {
            0
        };
        Ok(index)
    }

    fn current_track_local_msf(&self) -> Result<MsfIndex, ImageError> {
        let current_track = &self.tracks[self.current_track];
        let index01_lba =
            current_track.start_lba + current_track.track_info.pregap.unwrap_or(150);

        if self.current_lba < index01_lba {
            // Negative MSFs are (100,0,0) - x
            let reference = 100 * 60 * 75;
            let offset = index01_lba - self.current_lba;
            Ok(MsfIndex::from_lba(reference - offset)?)
        } else {
            Ok(MsfIndex::from_lba(self.current_lba - index01_lba)?)
        }
    }

    fn current_global_msf(&self) -> Result<MsfIndex, ImageError> {
        Ok(MsfIndex::from_lba(self.current_lba)?)
    }

    fn current_track_type(&self) -> Result<TrackType, ImageError> {
        let current_track = &self.tracks[self.current_track];
        Ok(current_track.track_type)
    }

    fn first_track_type(&self) -> TrackType {
        self.tracks.first().unwrap().track_type
    }

    fn track_start(&self, track: u8) -> Result<MsfIndex, ImageError> {
        // Track 0: Special case for PlayStation, return length of whole disc
        // TODO: Make this less ugly?
        if track == 0 {
            let len = self.chd.num_hunks() * self.chd.hunk_len();
            let num_sectors = FIRST_TRACK_PREGAP + len / BYTES_PER_SECTOR;
            Ok(MsfIndex::from_lba(num_sectors)?)
        } else if track <= self.tracks.len() as u8 {
            let track = &self.tracks[track as usize - 1];
            let start_lba_index01 =
                track.start_lba + track.track_info.pregap.unwrap_or(150);
            debug!("track_start: {:?} {:?}", track, MsfIndex::from_lba(start_lba_index01));
            Ok(MsfIndex::from_lba(start_lba_index01)?)
        } else {
            Err(ImageError::OutOfRange)
        }
    }

    fn set_location(&mut self, target: MsfIndex) -> Result<(), ImageError> {
        self.set_location_lba(target.to_lba())
    }

    fn set_location_to_track(&mut self, track: u8) -> Result<(), ImageError> {
        debug!("set_location_to_track {}", track);
        let track_start = self.track_start(track)?;
        self.set_location(track_start)?;
        Ok(())
    }

    fn advance_position(&mut self) -> Result<Option<Event>, ImageError> {
        let old_track = self.current_track;
        let res = self.set_location_lba(self.current_lba + 1);
        if let Err(e) = res {
            if let ImageError::OutOfRange = e {
                Ok(Some(Event::EndOfDisc))
            } else {
                Err(e)
            }
        } else if self.current_track != old_track {
            Ok(Some(Event::TrackChange))
        } else {
            Ok(None)
        }
    }

    fn copy_current_sector(&self, buf: &mut[u8]) -> Result<(), ImageError> {
        if buf.len() != 2352 {
            return Err(ChdImageError::WrongBufferSize.into())
        }
        if self.current_lba < FIRST_TRACK_PREGAP {
            buf.fill(0);
            return Ok(());
        }
        let current_file_lba = self.current_lba - FIRST_TRACK_PREGAP;
        let sector_in_hunk = current_file_lba % self.sectors_per_hunk;
        let sector_start = (sector_in_hunk * BYTES_PER_SECTOR) as usize;
        let sector = &self.hunk[sector_start..sector_start + 2352];
        buf.clone_from_slice(sector);

        if self.current_track_type().unwrap() == TrackType::Audio {
            for x in buf.chunks_exact_mut(2) {
                x.swap(0, 1);
            }
        }

        Ok(())
    }
}<|endoftext|>


<commit_before>use command::*;
use std::{env, path::PathBuf};
use structopt::{clap::ArgGroup, StructOpt};

const APP_NAME: &str = "git-plz";
const CMD_BRANCH: &str = "BRANCH";

fn branch_arg_group<'a>() -> ArgGroup<'a> {
    ArgGroup::with_name("branch").required(true)
}

#[derive(StructOpt, Debug)]
struct PathArg {
    /// Path to execute command. Defaults to working directory.
    #[structopt(name = "PATH", parse(from_os_str))]
    value: Option<PathBuf>,
}

impl Into<PathBuf> for PathArg {
    fn into(self) -> PathBuf {
        self.value
            .unwrap_or_else(|| env::current_dir().expect("Could not get working directory"))
    }
}

#[derive(StructOpt, Debug)]
#[structopt(bin_name = APP_NAME)]
enum RunOption {
    /// Perform bulk local branch operations
    #[structopt(name = "branch", group = branch_arg_group())]
    Branch {
        #[structopt(
            short = "f",
            long = "find",
            group = "branch",
            value_name = CMD_BRANCH,
        )]
        find: Option<String>,
        #[structopt(
            short = "d",
            long = "delete",
            group = "branch",
            value_name = CMD_BRANCH
        )]
        delete: Option<String>,
        #[structopt(flatten)]
        path: PathArg,
    },
    /// Checkout branch across repos
    #[structopt(name = "checkout")]
    Checkout {
        /// Branch name
        #[structopt(name = "BRANCH")]
        branch: String,
        #[structopt(flatten)]
        path: PathArg,
    },
    /// Recursive fetch
    #[structopt(name = "fetch")]
    Fetch {
        #[structopt(flatten)]
        path: PathArg,
    },
    /// Recursive hard reset
    #[structopt(name = "reset")]
    Reset {
        #[structopt(flatten)]
        path: PathArg,
    },
    /// Recursive directory search version of git status
    #[structopt(name = "status")]
    Status {
        #[structopt(flatten)]
        path: PathArg,
    },
}

pub struct MappedArgs {
    command: Box<dyn Command>,
    path: PathBuf,
}

impl MappedArgs {
    fn new(run_option: RunOption) -> Self {
        struct ArgPair(Box<dyn Command>, PathArg);

        let ArgPair(command, path) = match run_option {
            RunOption::Branch { path, delete, find } => {
                // TODO: This needs to be an enum again
                if let Some(branch) = delete {
                    ArgPair(Box::new(BranchDeleteCommand::new(branch)), path)
                } else if let Some(branch) = find {
                    ArgPair(Box::new(BranchFindCommand::new(branch)), path)
                } else {
                    panic!("Invalid branch option");
                }
            }
            RunOption::Checkout { path, branch } => {
                ArgPair(Box::new(CheckoutCommand::new(branch)), path)
            }
            RunOption::Fetch { path } => ArgPair(Box::new(FetchCommand::new()), path),
            RunOption::Reset { path } => ArgPair(Box::new(ResetCommand::new()), path),
            RunOption::Status { path } => ArgPair(Box::new(StatusCommand::new()), path),
        };

        Self {
            command,
            path: path.into(),
        }
    }

    pub fn destructure(self) -> (Box<dyn Command>, PathBuf) {
        (self.command, self.path)
    }
}

pub fn handle_args() -> MappedArgs {
    MappedArgs::new(RunOption::from_args())
}
<commit_msg>Updated structopt<commit_after>use command::*;
use std::{env, path::PathBuf};
use structopt::{clap::ArgGroup, StructOpt};

const CMD_BRANCH: &str = "branch";

#[derive(StructOpt, Debug)]
struct PathArg {
    /// Path to execute command. Defaults to working directory.
    path: Option<PathBuf>,
}

impl Into<PathBuf> for PathArg {
    fn into(self) -> PathBuf {
        self.path
            .unwrap_or_else(|| env::current_dir().expect("Could not get working directory"))
    }
}

#[derive(StructOpt, Debug)]
#[structopt(author, about)]
enum RunOption {
    /// Perform bulk local branch operations
    #[structopt(group = ArgGroup::with_name("branch").required(true))]
    Branch {
        #[structopt(
            short,
            long,
            group = CMD_BRANCH,
            value_name = CMD_BRANCH,
        )]
        find: Option<String>,
        #[structopt(
            short,
            long,
            group = CMD_BRANCH,
            value_name = CMD_BRANCH
        )]
        delete: Option<String>,
        #[structopt(flatten)]
        path: PathArg,
    },
    /// Checkout branch across repos
    Checkout {
        /// Branch name
        branch: String,
        #[structopt(flatten)]
        path: PathArg,
    },
    /// Recursive fetch
    Fetch {
        #[structopt(flatten)]
        path: PathArg,
    },
    /// Recursive hard reset
    Reset {
        #[structopt(flatten)]
        path: PathArg,
    },
    /// Recursive directory search version of git status
    Status {
        #[structopt(flatten)]
        path: PathArg,
    },
}

pub struct MappedArgs {
    command: Box<dyn Command>,
    path: PathBuf,
}

impl MappedArgs {
    fn new(run_option: RunOption) -> Self {
        struct ArgPair(Box<dyn Command>, PathArg);

        let ArgPair(command, path) = match run_option {
            RunOption::Branch { path, delete, find } => {
                // TODO: This needs to be an enum again
                if let Some(branch) = delete {
                    ArgPair(Box::new(BranchDeleteCommand::new(branch)), path)
                } else if let Some(branch) = find {
                    ArgPair(Box::new(BranchFindCommand::new(branch)), path)
                } else {
                    panic!("Invalid branch option");
                }
            }
            RunOption::Checkout { path, branch } => {
                ArgPair(Box::new(CheckoutCommand::new(branch)), path)
            }
            RunOption::Fetch { path } => ArgPair(Box::new(FetchCommand::new()), path),
            RunOption::Reset { path } => ArgPair(Box::new(ResetCommand::new()), path),
            RunOption::Status { path } => ArgPair(Box::new(StatusCommand::new()), path),
        };

        Self {
            command,
            path: path.into(),
        }
    }

    pub fn destructure(self) -> (Box<dyn Command>, PathBuf) {
        (self.command, self.path)
    }
}

pub fn handle_args() -> MappedArgs {
    MappedArgs::new(RunOption::from_args())
}
<|endoftext|>


<commit_before>//! This exposes the command line interface that the binary uses
use std::fs;
use std::io;
use std::env;
use std::process;
use std::sync::Mutex;

use clap::{App, Arg, SubCommand, ArgMatches, AppSettings};
use chrono;
use log;

use super::{Result, ResultExt, Error};
use super::sdk::{Sdk, DumpOptions};
use super::config::Config;
use super::memdb::stash::{MemDbStash, SyncOptions};
use super::api::server::{ApiServer, BindOptions};

struct SimpleLogger<W: ?Sized> {
    f: Mutex<Box<W>>,
}

impl<W: io::Write + Send + ?Sized> log::Log for SimpleLogger<W> {

    fn enabled(&self, metadata: &log::LogMetadata) -> bool {
        metadata.level() <= log::LogLevel::Info
    }

    fn log(&self, record: &log::LogRecord) {
        let mut f = self.f.lock().unwrap();
        if self.enabled(record.metadata()) {
            writeln!(f, "[{}] {} | {}{}",
                     chrono::Local::now(),
                     record.target().split(':').next().unwrap(),
                     match record.level() {
                         log::LogLevel::Error => "ERROR: ",
                         log::LogLevel::Warn => "WARNING: ",
                         _ => "",
                     },
                     record.args()).ok();
        }
    }
}

fn setup_logging(config: &Config) -> Result<()> {
    let filter = config.get_log_level_filter()?;
    if filter >= log::LogLevel::Debug {
        env::set_var("RUST_BACKTRACE", "1");
    }

    let f : Box<io::Write + Send> = match config.get_log_filename()? {
        Some(path) => Box::new(fs::File::open(path)?),
        None => Box::new(io::stdout()),
    };
    log::set_logger(|max_log_level| {
        max_log_level.set(filter);
        Box::new(SimpleLogger {
            f: Mutex::new(f),
        })
    }).unwrap();
    Ok(())
}

/// Main entry point that starts the CLI
pub fn main() {
    match execute() {
        Ok(()) => {},
        Err(err) => {
            use std::error::Error;
            println!("error: {}", err);
            let mut cause = err.cause();
            while let Some(the_cause) = cause {
                println!("  caused by: {}", the_cause);
                cause = the_cause.cause();
            }
            if let Some(backtrace) = err.backtrace() {
                debug!("  Traceback: {:?}", backtrace);
            }
            process::exit(1);
        }
    }
}

fn config_from_matches(matches: &ArgMatches) -> Result<Config> {
    let mut cfg = if let Some(config_path) = matches.value_of("config") {
        Config::load_file(config_path)?
    } else {
        Config::load_default()?
    };

    if let Some(value) = matches.value_of("log_level") {
        let filter = value.parse()
            .map_err(|_| Error::from("Invalid value for log level"))?;
        cfg.set_log_level_filter(filter);
    };

    if let Some(value) = matches.value_of("symbol_dir") {
        cfg.set_symbol_dir(value);
    }

    if let Some(value) = matches.value_of("aws_bucket_url") {
        cfg.set_aws_bucket_url(value);
    }

    if let Some(value) = matches.value_of("aws_region") {
        let region = value.parse()
            .map_err(|_| Error::from("Invalid AWS region"))?;
        cfg.set_aws_region(region);
    }

    Ok(cfg)
}

fn execute() -> Result<()> {
    let app = App::new("sentry-symbolserver")
        .about("This tool implements an Apple SDK processor and server.")
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .setting(AppSettings::ColorNever)
        .arg(Arg::with_name("config")
             .long("config")
             .value_name("FILE")
             .help("The path to the config file"))
        .arg(Arg::with_name("log_level")
             .short("l")
             .long("log-level")
             .value_name("LEVEL")
             .help("Overrides the log level from the config"))
        .arg(Arg::with_name("symbol_dir")
             .short("p")
             .long("symbol-dir")
             .value_name("PATH")
             .help("The path to the symbol directory"))
        .arg(Arg::with_name("aws_bucket_url")
             .long("aws-bucket-url")
             .value_name("URL")
             .help("The bucket URL the sync tool should pull from"))
        .arg(Arg::with_name("aws_region")
             .long("aws-region")
             .value_name("REGION")
             .help("Sets the AWS region the bucket is located in"))
        .subcommand(
            SubCommand::with_name("sync")
                .about("Updates symbols from S3"))
        .subcommand(
            SubCommand::with_name("run")
                .about("Runs the symbol server")
                .arg(Arg::with_name("disable_sync")
                     .long("disable-sync")
                     .help("Disables the background synching"))
                .arg(Arg::with_name("bind")
                     .long("bind")
                     .value_name("ADDR")
                     .help("Bind to a specific address (ip:port)"))
                .arg(Arg::with_name("bind_fd")
                     .long("bind-fd")
                     .value_name("FD")
                     .help("Bind to a specific file descriptor"))
                .arg(Arg::with_name("threads")
                     .long("threads")
                     .short("t")
                     .value_name("COUNT")
                     .help("Overrides the default listener thread count")))
        .subcommand(
            SubCommand::with_name("convert-sdk")
                .about("Converts an SDK into a memdb file")
                .arg(Arg::with_name("path")
                     .index(1)
                     .value_name("PATH")
                     .required(true)
                     .multiple(true)
                     .help("Path to the support folder"))
                .arg(Arg::with_name("compress")
                     .short("c")
                     .long("compress")
                     .help("Write compressed files instead."))
                .arg(Arg::with_name("output-path")
                     .short("o")
                     .long("output")
                     .help("Where the result should be stored")));
    let matches = app.get_matches();

    let cfg = config_from_matches(&matches)?;
    setup_logging(&cfg)?;

    if let Some(matches) = matches.subcommand_matches("convert-sdk") {
        convert_sdk_action(matches.values_of("path").unwrap().collect(),
                           matches.value_of("output-path").unwrap_or("."),
                           matches.is_present("compress"))?;
    } else if let Some(matches) = matches.subcommand_matches("run") {
        run_action(&cfg, matches)?;
    } else if let Some(_matches) = matches.subcommand_matches("sync") {
        sync_action(&cfg)?;
    }

    Ok(())
}

fn convert_sdk_action(paths: Vec<&str>, output_path: &str, compress: bool)
    -> Result<()>
{
    let dst_base = env::current_dir().unwrap().join(output_path);

    for (idx, path) in paths.iter().enumerate() {
        if idx > 0 {
            println!("");
        }
        let sdk = Sdk::new(&path)?;
        let mut dst = dst_base.join(sdk.info().memdb_filename());
        if compress {
            dst.set_extension("memdbz");
        }

        println!("SDK {} ({} {}):", sdk.info().name(),
                 sdk.info().version(), sdk.info().build().unwrap_or("UNKNONW"));

        // make sure we close the file at the end, in case we want to
        // re-open it for compressing.
        let f = fs::File::create(&dst)?;
        let options = DumpOptions {
            show_progress_bar: true,
            compress: compress,
            ..Default::default()
        };
        sdk.dump_memdb(f, options)?;
    }

    Ok(())
}

fn sync_action(config: &Config) -> Result<()> {
    let stash = MemDbStash::new(config)?;
    stash.sync(SyncOptions {
        user_facing: true,
        ..Default::default()
    })?;
    Ok(())
}

fn run_action(config: &Config, matches: &ArgMatches) -> Result<()> {
    let api_server = ApiServer::new(config)?;

    if !matches.is_present("disable_sync") {
        api_server.spawn_sync_thread()?;
    }

    let threads: usize = if let Some(threads) = matches.value_of("threads") {
        threads.parse().chain_err(|| "invalid value for threads")?
    } else {
        config.get_server_threads()?
    };

    api_server.run(threads, if let Some(addr) = matches.value_of("bind") {
        BindOptions::BindToAddr(addr)
    } else if let Some(fd) = matches.value_of("bind_fd") {
        BindOptions::BindToFd(fd.parse().chain_err(|| "invalid value for file descriptor")?)
    } else {
        BindOptions::UseConfig
    })?;

    Ok(())
}
<commit_msg>Added version flag<commit_after>//! This exposes the command line interface that the binary uses
use std::fs;
use std::io;
use std::env;
use std::process;
use std::sync::Mutex;

use clap::{App, Arg, SubCommand, ArgMatches, AppSettings};
use chrono;
use log;

use super::{Result, ResultExt, Error};
use super::sdk::{Sdk, DumpOptions};
use super::config::Config;
use super::constants::VERSION;
use super::memdb::stash::{MemDbStash, SyncOptions};
use super::api::server::{ApiServer, BindOptions};

struct SimpleLogger<W: ?Sized> {
    f: Mutex<Box<W>>,
}

impl<W: io::Write + Send + ?Sized> log::Log for SimpleLogger<W> {

    fn enabled(&self, metadata: &log::LogMetadata) -> bool {
        metadata.level() <= log::LogLevel::Info
    }

    fn log(&self, record: &log::LogRecord) {
        let mut f = self.f.lock().unwrap();
        if self.enabled(record.metadata()) {
            writeln!(f, "[{}] {} | {}{}",
                     chrono::Local::now(),
                     record.target().split(':').next().unwrap(),
                     match record.level() {
                         log::LogLevel::Error => "ERROR: ",
                         log::LogLevel::Warn => "WARNING: ",
                         _ => "",
                     },
                     record.args()).ok();
        }
    }
}

fn setup_logging(config: &Config) -> Result<()> {
    let filter = config.get_log_level_filter()?;
    if filter >= log::LogLevel::Debug {
        env::set_var("RUST_BACKTRACE", "1");
    }

    let f : Box<io::Write + Send> = match config.get_log_filename()? {
        Some(path) => Box::new(fs::File::open(path)?),
        None => Box::new(io::stdout()),
    };
    log::set_logger(|max_log_level| {
        max_log_level.set(filter);
        Box::new(SimpleLogger {
            f: Mutex::new(f),
        })
    }).unwrap();
    Ok(())
}

/// Main entry point that starts the CLI
pub fn main() {
    match execute() {
        Ok(()) => {},
        Err(err) => {
            use std::error::Error;
            println!("error: {}", err);
            let mut cause = err.cause();
            while let Some(the_cause) = cause {
                println!("  caused by: {}", the_cause);
                cause = the_cause.cause();
            }
            if let Some(backtrace) = err.backtrace() {
                debug!("  Traceback: {:?}", backtrace);
            }
            process::exit(1);
        }
    }
}

fn config_from_matches(matches: &ArgMatches) -> Result<Config> {
    let mut cfg = if let Some(config_path) = matches.value_of("config") {
        Config::load_file(config_path)?
    } else {
        Config::load_default()?
    };

    if let Some(value) = matches.value_of("log_level") {
        let filter = value.parse()
            .map_err(|_| Error::from("Invalid value for log level"))?;
        cfg.set_log_level_filter(filter);
    };

    if let Some(value) = matches.value_of("symbol_dir") {
        cfg.set_symbol_dir(value);
    }

    if let Some(value) = matches.value_of("aws_bucket_url") {
        cfg.set_aws_bucket_url(value);
    }

    if let Some(value) = matches.value_of("aws_region") {
        let region = value.parse()
            .map_err(|_| Error::from("Invalid AWS region"))?;
        cfg.set_aws_region(region);
    }

    Ok(cfg)
}

fn execute() -> Result<()> {
    let app = App::new("sentry-symbolserver")
        .version(VERSION)
        .about("This tool implements an Apple SDK processor and server.")
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .setting(AppSettings::ColorNever)
        .arg(Arg::with_name("config")
             .long("config")
             .value_name("FILE")
             .help("The path to the config file"))
        .arg(Arg::with_name("log_level")
             .short("l")
             .long("log-level")
             .value_name("LEVEL")
             .help("Overrides the log level from the config"))
        .arg(Arg::with_name("symbol_dir")
             .short("p")
             .long("symbol-dir")
             .value_name("PATH")
             .help("The path to the symbol directory"))
        .arg(Arg::with_name("aws_bucket_url")
             .long("aws-bucket-url")
             .value_name("URL")
             .help("The bucket URL the sync tool should pull from"))
        .arg(Arg::with_name("aws_region")
             .long("aws-region")
             .value_name("REGION")
             .help("Sets the AWS region the bucket is located in"))
        .subcommand(
            SubCommand::with_name("sync")
                .about("Updates symbols from S3"))
        .subcommand(
            SubCommand::with_name("run")
                .about("Runs the symbol server")
                .arg(Arg::with_name("disable_sync")
                     .long("disable-sync")
                     .help("Disables the background synching"))
                .arg(Arg::with_name("bind")
                     .long("bind")
                     .value_name("ADDR")
                     .help("Bind to a specific address (ip:port)"))
                .arg(Arg::with_name("bind_fd")
                     .long("bind-fd")
                     .value_name("FD")
                     .help("Bind to a specific file descriptor"))
                .arg(Arg::with_name("threads")
                     .long("threads")
                     .short("t")
                     .value_name("COUNT")
                     .help("Overrides the default listener thread count")))
        .subcommand(
            SubCommand::with_name("convert-sdk")
                .about("Converts an SDK into a memdb file")
                .arg(Arg::with_name("path")
                     .index(1)
                     .value_name("PATH")
                     .required(true)
                     .multiple(true)
                     .help("Path to the support folder"))
                .arg(Arg::with_name("compress")
                     .short("c")
                     .long("compress")
                     .help("Write compressed files instead."))
                .arg(Arg::with_name("output-path")
                     .short("o")
                     .long("output")
                     .help("Where the result should be stored")));
    let matches = app.get_matches();

    let cfg = config_from_matches(&matches)?;
    setup_logging(&cfg)?;

    if let Some(matches) = matches.subcommand_matches("convert-sdk") {
        convert_sdk_action(matches.values_of("path").unwrap().collect(),
                           matches.value_of("output-path").unwrap_or("."),
                           matches.is_present("compress"))?;
    } else if let Some(matches) = matches.subcommand_matches("run") {
        run_action(&cfg, matches)?;
    } else if let Some(_matches) = matches.subcommand_matches("sync") {
        sync_action(&cfg)?;
    }

    Ok(())
}

fn convert_sdk_action(paths: Vec<&str>, output_path: &str, compress: bool)
    -> Result<()>
{
    let dst_base = env::current_dir().unwrap().join(output_path);

    for (idx, path) in paths.iter().enumerate() {
        if idx > 0 {
            println!("");
        }
        let sdk = Sdk::new(&path)?;
        let mut dst = dst_base.join(sdk.info().memdb_filename());
        if compress {
            dst.set_extension("memdbz");
        }

        println!("SDK {} ({} {}):", sdk.info().name(),
                 sdk.info().version(), sdk.info().build().unwrap_or("UNKNONW"));

        // make sure we close the file at the end, in case we want to
        // re-open it for compressing.
        let f = fs::File::create(&dst)?;
        let options = DumpOptions {
            show_progress_bar: true,
            compress: compress,
            ..Default::default()
        };
        sdk.dump_memdb(f, options)?;
    }

    Ok(())
}

fn sync_action(config: &Config) -> Result<()> {
    let stash = MemDbStash::new(config)?;
    stash.sync(SyncOptions {
        user_facing: true,
        ..Default::default()
    })?;
    Ok(())
}

fn run_action(config: &Config, matches: &ArgMatches) -> Result<()> {
    let api_server = ApiServer::new(config)?;

    if !matches.is_present("disable_sync") {
        api_server.spawn_sync_thread()?;
    }

    let threads: usize = if let Some(threads) = matches.value_of("threads") {
        threads.parse().chain_err(|| "invalid value for threads")?
    } else {
        config.get_server_threads()?
    };

    api_server.run(threads, if let Some(addr) = matches.value_of("bind") {
        BindOptions::BindToAddr(addr)
    } else if let Some(fd) = matches.value_of("bind_fd") {
        BindOptions::BindToFd(fd.parse().chain_err(|| "invalid value for file descriptor")?)
    } else {
        BindOptions::UseConfig
    })?;

    Ok(())
}
<|endoftext|>


<commit_before>use std::fmt;

// Basic DOM data structures:

pub enum Node {
    Element(ElementData),
    Text(TextData),
}

pub type NodeList = Vec<Box<Node>>;

pub struct NodeData {
    pub children: Option<NodeList>,
}

pub struct ElementData {
    pub node: NodeData,
    pub local_name: String,
}

pub struct TextData {
    pub node: NodeData,
    pub data: String,
}

// Constructor functions for convenience:

impl Node {
    pub fn new_text(data: String) -> Node {
        Text(TextData {
            node: NodeData {
              children: None
            },
            data: data,
        })
    }

    pub fn new_elem(local_name: String, children: NodeList) -> Node {
        Element(ElementData {
            node: NodeData {
              children: Some(children),
            },
            local_name: local_name,
        })
    }
}

// Formatting for debug output:

impl fmt::Show for Node {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Text(ref text) => {
                write!(f, "{}", text.data)
            }
            Element(ref elem) => {
                try!(write!(f, "<{}>", elem.local_name));
                match elem.node.children {
                    Some(ref children) => {
                        for child in children.iter() {
                            try!(write!(f, "{}", *child));
                        }
                    }
                    None => {}
                }
                write!(f, "</{}>", elem.local_name)
            }
        }
    }
}
<commit_msg>Derive Show for DOM types<commit_after>//! Basic DOM data structures:

#[deriving(Show)]
pub enum Node {
    Element(ElementData),
    Text(TextData),
}

pub type NodeList = Vec<Box<Node>>;

#[deriving(Show)]
pub struct NodeData {
    pub children: Option<NodeList>,
}

#[deriving(Show)]
pub struct ElementData {
    pub node: NodeData,
    pub local_name: String,
}

#[deriving(Show)]
pub struct TextData {
    pub node: NodeData,
    pub data: String,
}

// Constructor functions for convenience:

impl Node {
    pub fn new_text(data: String) -> Node {
        Text(TextData {
            node: NodeData {
              children: None
            },
            data: data,
        })
    }

    pub fn new_elem(local_name: String, children: NodeList) -> Node {
        Element(ElementData {
            node: NodeData {
              children: Some(children),
            },
            local_name: local_name,
        })
    }
}
<|endoftext|>