<commit_before>use rand;
use rand::Rng;
use sprite::*;
use util::{coin_flip, random_range};

#[derive(Copy, Clone)]
pub enum Appearance {
    NoItem = 0x00,
    Crowbar = 0x01,
    VolcanicShard = 0x02,
    Taser = 0x03,
    JellyGun = 0x04,

    // Fruits group (shuffled)
    Lumimelon = 0x05,
    Glowfruit = 0x06,
    Shineapple = 0x07,

    // Pills group (shuffled)
    RoundPill = 0x08,
    TinyPill = 0x09,
    DiamondPill = 0x0a,
    OblongPill = 0x0b,
    SoftPill = 0x0c,
    HexagonalPill = 0x0d,
    WidePill = 0x0e,
    TranslucentPill = 0x0f,

    ThickSweater = 0x10,
    BallisticVest = 0x11,
    DragonScaleMail = 0x12,
    TitaniumNecklace = 0x13,
    RustyNecklace = 0x14,
    CrimsonNecklace = 0x15,
    GlowingNecklace = 0x16,
    UnholyNecklace = 0x17,
    Wand = 0x18,
    Manual = 0x19,
    Guidebook = 0x1a,

    // Devices group (shuffled)
    RedDevice = 0x1b,
    YellowDevice = 0x1c,
    BlueDevice = 0x1d,

    Palantir = 0x1e,
    GoldenPendant = 0x1f,
}

impl Appearance {
    pub fn from_byte(byte: u8) -> Appearance {
        use self::Appearance::*;
        match byte {
            0x01 => Crowbar,
            0x02 => VolcanicShard,
            0x03 => Taser,
            0x04 => JellyGun,
            0x05 => Lumimelon,
            0x06 => Glowfruit,
            0x07 => Shineapple,
            0x08 => RoundPill,
            0x09 => TinyPill,
            0x0a => DiamondPill,
            0x0b => OblongPill,
            0x0c => SoftPill,
            0x0d => HexagonalPill,
            0x0e => WidePill,
            0x0f => TranslucentPill,
            0x10 => ThickSweater,
            0x11 => BallisticVest,
            0x12 => DragonScaleMail,
            0x13 => TitaniumNecklace,
            0x14 => RustyNecklace,
            0x15 => CrimsonNecklace,
            0x16 => GlowingNecklace,
            0x17 => UnholyNecklace,
            0x18 => Wand,
            0x19 => Manual,
            0x1a => Guidebook,
            0x1b => RedDevice,
            0x1c => YellowDevice,
            0x1d => BlueDevice,
            0x1e => Palantir,
            0x1f => GoldenPendant,
            _ => NoItem,
        }
    }
}

struct AppearanceInfo {
    name: &'static str,
    sprite: Sprite,
}

const WEAPON_CHAR: char = '/';
const FOOD_CHAR: char = '%';
const PILL_CHAR: char = '*';
const ARMOR_CHAR: char = '[';
const NECKLACE_CHAR: char = '"';
const DEVICE_CHAR: char = '&';
const ARTIFACT_CHAR: char = '$';

const APPEARANCE_INFOS: [AppearanceInfo; 32] = [
    AppearanceInfo {name: "(no item)",         sprite: Sprite {character: '?',           color: GLITCH}},
    AppearanceInfo {name: "crowbar",           sprite: Sprite {character: WEAPON_CHAR,   color: TEAL}},
    AppearanceInfo {name: "volcanic shard",    sprite: Sprite {character: WEAPON_CHAR,   color: MAROON}},
    AppearanceInfo {name: "taser",             sprite: Sprite {character: WEAPON_CHAR,   color: AQUA}},
    AppearanceInfo {name: "jelly gun",         sprite: Sprite {character: WEAPON_CHAR,   color: LIME}},
    AppearanceInfo {name: "lumimelon",         sprite: Sprite {character: FOOD_CHAR,     color: LIME}},
    AppearanceInfo {name: "glowfruit",         sprite: Sprite {character: FOOD_CHAR,     color: PINK}},
    AppearanceInfo {name: "shineapple",        sprite: Sprite {character: FOOD_CHAR,     color: YELLOW}},
    AppearanceInfo {name: "round pill",        sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "tiny pill",         sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "diamond pill",      sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "oblong pill",       sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "soft pill",         sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "hexagonal pill",    sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "wide pill",         sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "translucent pill",  sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "thick sweater",     sprite: Sprite {character: ARMOR_CHAR,    color: PURPLE}},
    AppearanceInfo {name: "ballistic vest",    sprite: Sprite {character: ARMOR_CHAR,    color: TEAL}},
    AppearanceInfo {name: "dragon scale mail", sprite: Sprite {character: ARMOR_CHAR,    color: GREEN}},
    AppearanceInfo {name: "titanium necklace", sprite: Sprite {character: NECKLACE_CHAR, color: GRAY}},
    AppearanceInfo {name: "rusty necklace",    sprite: Sprite {character: NECKLACE_CHAR, color: BROWN}},
    AppearanceInfo {name: "crimson necklace",  sprite: Sprite {character: NECKLACE_CHAR, color: MAROON}},
    AppearanceInfo {name: "glowing necklace",  sprite: Sprite {character: NECKLACE_CHAR, color: YELLOW}},
    AppearanceInfo {name: "unholy necklace",   sprite: Sprite {character: NECKLACE_CHAR, color: DARK}},
    AppearanceInfo {name: "wand",              sprite: Sprite {character: DEVICE_CHAR,   color: NAVY}},
    AppearanceInfo {name: "manual",            sprite: Sprite {character: DEVICE_CHAR,   color: TEAL}},
    AppearanceInfo {name: "guidebook",         sprite: Sprite {character: DEVICE_CHAR,   color: TEAL}},
    AppearanceInfo {name: "red device",        sprite: Sprite {character: DEVICE_CHAR,   color: RED}},
    AppearanceInfo {name: "yellow device",     sprite: Sprite {character: DEVICE_CHAR,   color: YELLOW}},
    AppearanceInfo {name: "blue device",       sprite: Sprite {character: DEVICE_CHAR,   color: BLUE}},
    AppearanceInfo {name: "palantir",          sprite: Sprite {character: ARTIFACT_CHAR, color: SAPPHIRE}},
    AppearanceInfo {name: "golden pendant",    sprite: Sprite {character: ARTIFACT_CHAR, color: GOLD}},
];

impl Appearance {
    // Unidentified item names.
    pub fn name(self) -> &'static str {
        APPEARANCE_INFOS[self as usize].name
    }

    pub fn sprite(self) -> Sprite {
        APPEARANCE_INFOS[self as usize].sprite
    }
}

#[derive(Copy, Clone, Eq, PartialEq)]
pub enum Kind {
    Crowbar,
    VolcanicShard,
    Taser,
    JellyGun,
    FullHPFruit,
    FullTPFruit,
    CancellationFruit,
    ChargePill,
    XPUpPill,
    HastePill,
    IdentifyPill,
    XPDownPill,
    PoisonPill,
    ProtectPill,
    TormentPill,
    ThickSweater,
    BallisticVest,
    DragonScaleMail,
    TitaniumNecklace,
    RustyNecklace,
    CrimsonNecklace,
    GlowingNecklace,
    UnholyNecklace,
    WandOfDeath,
    Manual,
    Guidebook,
    Corruptor,
    Offsetter,
    Copier,
    Palantir,
    GoldenPendant,
}

// A map from Appearances (0x00 through 0x1f) to Kinds.
// NoItem is mapped to None; other items are mapped to Some<Kind>.
pub type AppearanceMap = [Option<Kind>; 0x20];

// Make a random appearance map by shuffling the unidentified fruits, pills, and devices.
pub fn random_appearance_map() -> AppearanceMap {
    use item::Kind::*;

    // Shuffle the kinds that will correspond to randomized item descriptions.
    let mut fruits: [Kind; 3] = [
        FullHPFruit, FullTPFruit, CancellationFruit
    ];
    rand::thread_rng().shuffle(&mut fruits);

    let mut pills: [Kind; 8] = [
        ChargePill, XPUpPill, HastePill, IdentifyPill,
        XPDownPill, PoisonPill, ProtectPill, TormentPill
    ];
    rand::thread_rng().shuffle(&mut pills);

    let mut devices: [Kind; 3] = [
        Corruptor, Offsetter, Copier
    ];
    rand::thread_rng().shuffle(&mut devices);

    // Build a map.
    [
        None,
        Some(Crowbar),
        Some(VolcanicShard),
        Some(Taser),
        Some(JellyGun),
        Some(fruits[0]),
        Some(fruits[1]),
        Some(fruits[2]),
        Some(pills[0]),
        Some(pills[1]),
        Some(pills[2]),
        Some(pills[3]),
        Some(pills[4]),
        Some(pills[5]),
        Some(pills[6]),
        Some(pills[7]),
        Some(ThickSweater),
        Some(BallisticVest),
        Some(DragonScaleMail),
        Some(TitaniumNecklace),
        Some(RustyNecklace),
        Some(CrimsonNecklace),
        Some(GlowingNecklace),
        Some(UnholyNecklace),
        Some(WandOfDeath),
        Some(devices[0]),
        Some(devices[1]),
        Some(devices[2]),
        Some(Offsetter),
        Some(Copier),
        Some(Palantir),
        Some(GoldenPendant),
    ]
}

pub struct Item {
    // 5 high bits
    appearance: Appearance,

    // 3 low bits
    enchanted: bool,
    equipped: bool,
    cursed: bool,
}

impl Item {
/*
    // An inventory byte with its five high bits cleared represents an
    // empty inventory slot; `from_byte` returns None for such bytes.
    pub fn from_byte(byte: u8, appearance_map: &AppearanceMap) -> Option<Item> {
        appearance_map[byte as usize >> 3].map(|kind| Item {
            kind: kind,
            enchanted: byte & 0b100 > 0,
            equipped: byte & 0b010 > 0,
            cursed: byte & 0b001 > 0,
        })
    }

    pub fn to_byte(&self, appearance_map: &AppearanceMap) -> u8 {
        for n in 0..19 {
            if appearance_map[n] == Some(self.kind) {
                return (n << 3) as u8
                    + (if self.enchanted {0b100} else {0})
                    + (if self.equipped {0b010} else {0})
                    + (if self.cursed {0b001} else {0});
            }
        }
        panic!("Invalid AppearanceMap!");
    }
*/

    pub fn spawn() -> Item {
        Item {
            appearance: Appearance::from_byte(random_range(0x00..0x20)),
            enchanted: coin_flip(),
            equipped: coin_flip(),
            cursed: coin_flip(),
        }
    }

    pub fn sprite(&self) -> Sprite {
        self.appearance.sprite()
    }

    pub fn name(&self) -> &'static str {
        self.appearance.name()
    }
}
<commit_msg>remove NoItem<commit_after>use rand;
use rand::Rng;
use sprite::*;
use util::{coin_flip, random_range};

#[derive(Copy, Clone)]
pub enum Appearance {
    Crowbar = 0x01,
    VolcanicShard = 0x02,
    Taser = 0x03,
    JellyGun = 0x04,

    // Fruits group (shuffled)
    Lumimelon = 0x05,
    Glowfruit = 0x06,
    Shineapple = 0x07,

    // Pills group (shuffled)
    RoundPill = 0x08,
    TinyPill = 0x09,
    DiamondPill = 0x0a,
    OblongPill = 0x0b,
    SoftPill = 0x0c,
    HexagonalPill = 0x0d,
    WidePill = 0x0e,
    TranslucentPill = 0x0f,

    ThickSweater = 0x10,
    BallisticVest = 0x11,
    DragonScaleMail = 0x12,
    TitaniumNecklace = 0x13,
    RustyNecklace = 0x14,
    CrimsonNecklace = 0x15,
    GlowingNecklace = 0x16,
    UnholyNecklace = 0x17,
    Wand = 0x18,
    Manual = 0x19,
    Guidebook = 0x1a,

    // Devices group (shuffled)
    RedDevice = 0x1b,
    YellowDevice = 0x1c,
    BlueDevice = 0x1d,

    Palantir = 0x1e,
    GoldenPendant = 0x1f,
}

impl Appearance {
    pub fn from_byte(byte: u8) -> Appearance {
        use self::Appearance::*;
        match byte {
            0x01 => Crowbar,
            0x02 => VolcanicShard,
            0x03 => Taser,
            0x04 => JellyGun,
            0x05 => Lumimelon,
            0x06 => Glowfruit,
            0x07 => Shineapple,
            0x08 => RoundPill,
            0x09 => TinyPill,
            0x0a => DiamondPill,
            0x0b => OblongPill,
            0x0c => SoftPill,
            0x0d => HexagonalPill,
            0x0e => WidePill,
            0x0f => TranslucentPill,
            0x10 => ThickSweater,
            0x11 => BallisticVest,
            0x12 => DragonScaleMail,
            0x13 => TitaniumNecklace,
            0x14 => RustyNecklace,
            0x15 => CrimsonNecklace,
            0x16 => GlowingNecklace,
            0x17 => UnholyNecklace,
            0x18 => Wand,
            0x19 => Manual,
            0x1a => Guidebook,
            0x1b => RedDevice,
            0x1c => YellowDevice,
            0x1d => BlueDevice,
            0x1e => Palantir,
            0x1f => GoldenPendant,
            _    => panic!("Item::Appearance::from_byte - invalid argument")
        }
    }
}

struct AppearanceInfo {
    name: &'static str,
    sprite: Sprite,
}

const WEAPON_CHAR: char = '/';
const FOOD_CHAR: char = '%';
const PILL_CHAR: char = '*';
const ARMOR_CHAR: char = '[';
const NECKLACE_CHAR: char = '"';
const DEVICE_CHAR: char = '&';
const ARTIFACT_CHAR: char = '$';

const APPEARANCE_INFOS: [AppearanceInfo; 31] = [
    AppearanceInfo {name: "crowbar",           sprite: Sprite {character: WEAPON_CHAR,   color: TEAL}},
    AppearanceInfo {name: "volcanic shard",    sprite: Sprite {character: WEAPON_CHAR,   color: MAROON}},
    AppearanceInfo {name: "taser",             sprite: Sprite {character: WEAPON_CHAR,   color: AQUA}},
    AppearanceInfo {name: "jelly gun",         sprite: Sprite {character: WEAPON_CHAR,   color: LIME}},
    AppearanceInfo {name: "lumimelon",         sprite: Sprite {character: FOOD_CHAR,     color: LIME}},
    AppearanceInfo {name: "glowfruit",         sprite: Sprite {character: FOOD_CHAR,     color: PINK}},
    AppearanceInfo {name: "shineapple",        sprite: Sprite {character: FOOD_CHAR,     color: YELLOW}},
    AppearanceInfo {name: "round pill",        sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "tiny pill",         sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "diamond pill",      sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "oblong pill",       sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "soft pill",         sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "hexagonal pill",    sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "wide pill",         sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "translucent pill",  sprite: Sprite {character: PILL_CHAR,     color: WHITE}},
    AppearanceInfo {name: "thick sweater",     sprite: Sprite {character: ARMOR_CHAR,    color: PURPLE}},
    AppearanceInfo {name: "ballistic vest",    sprite: Sprite {character: ARMOR_CHAR,    color: TEAL}},
    AppearanceInfo {name: "dragon scale mail", sprite: Sprite {character: ARMOR_CHAR,    color: GREEN}},
    AppearanceInfo {name: "titanium necklace", sprite: Sprite {character: NECKLACE_CHAR, color: GRAY}},
    AppearanceInfo {name: "rusty necklace",    sprite: Sprite {character: NECKLACE_CHAR, color: BROWN}},
    AppearanceInfo {name: "crimson necklace",  sprite: Sprite {character: NECKLACE_CHAR, color: MAROON}},
    AppearanceInfo {name: "glowing necklace",  sprite: Sprite {character: NECKLACE_CHAR, color: YELLOW}},
    AppearanceInfo {name: "unholy necklace",   sprite: Sprite {character: NECKLACE_CHAR, color: DARK}},
    AppearanceInfo {name: "wand",              sprite: Sprite {character: DEVICE_CHAR,   color: NAVY}},
    AppearanceInfo {name: "manual",            sprite: Sprite {character: DEVICE_CHAR,   color: TEAL}},
    AppearanceInfo {name: "guidebook",         sprite: Sprite {character: DEVICE_CHAR,   color: TEAL}},
    AppearanceInfo {name: "red device",        sprite: Sprite {character: DEVICE_CHAR,   color: RED}},
    AppearanceInfo {name: "yellow device",     sprite: Sprite {character: DEVICE_CHAR,   color: YELLOW}},
    AppearanceInfo {name: "blue device",       sprite: Sprite {character: DEVICE_CHAR,   color: BLUE}},
    AppearanceInfo {name: "palantir",          sprite: Sprite {character: ARTIFACT_CHAR, color: SAPPHIRE}},
    AppearanceInfo {name: "golden pendant",    sprite: Sprite {character: ARTIFACT_CHAR, color: GOLD}},
];

impl Appearance {
    // Unidentified item names.
    pub fn name(self) -> &'static str {
        APPEARANCE_INFOS[self as usize - 1].name
    }

    pub fn sprite(self) -> Sprite {
        APPEARANCE_INFOS[self as usize - 1].sprite
    }
}

#[derive(Copy, Clone, Eq, PartialEq)]
pub enum Kind {
    Crowbar,
    VolcanicShard,
    Taser,
    JellyGun,
    FullHPFruit,
    FullTPFruit,
    CancellationFruit,
    ChargePill,
    XPUpPill,
    HastePill,
    IdentifyPill,
    XPDownPill,
    PoisonPill,
    ProtectPill,
    TormentPill,
    ThickSweater,
    BallisticVest,
    DragonScaleMail,
    TitaniumNecklace,
    RustyNecklace,
    CrimsonNecklace,
    GlowingNecklace,
    UnholyNecklace,
    WandOfDeath,
    Manual,
    Guidebook,
    Corruptor,
    Offsetter,
    Copier,
    Palantir,
    GoldenPendant,
}

// A map from Appearances (0x00 through 0x1f) to Kinds.
// 0x00 is mapped to None; items are mapped to Some<Kind>.
pub type AppearanceMap = [Option<Kind>; 0x20];

// Make a random appearance map by shuffling the unidentified fruits, pills, and devices.
pub fn random_appearance_map() -> AppearanceMap {
    use item::Kind::*;

    // Shuffle the kinds that will correspond to randomized item descriptions.
    let mut fruits: [Kind; 3] = [
        FullHPFruit, FullTPFruit, CancellationFruit
    ];
    rand::thread_rng().shuffle(&mut fruits);

    let mut pills: [Kind; 8] = [
        ChargePill, XPUpPill, HastePill, IdentifyPill,
        XPDownPill, PoisonPill, ProtectPill, TormentPill
    ];
    rand::thread_rng().shuffle(&mut pills);

    let mut devices: [Kind; 3] = [
        Corruptor, Offsetter, Copier
    ];
    rand::thread_rng().shuffle(&mut devices);

    // Build a map.
    [
        None,
        Some(Crowbar),
        Some(VolcanicShard),
        Some(Taser),
        Some(JellyGun),
        Some(fruits[0]),
        Some(fruits[1]),
        Some(fruits[2]),
        Some(pills[0]),
        Some(pills[1]),
        Some(pills[2]),
        Some(pills[3]),
        Some(pills[4]),
        Some(pills[5]),
        Some(pills[6]),
        Some(pills[7]),
        Some(ThickSweater),
        Some(BallisticVest),
        Some(DragonScaleMail),
        Some(TitaniumNecklace),
        Some(RustyNecklace),
        Some(CrimsonNecklace),
        Some(GlowingNecklace),
        Some(UnholyNecklace),
        Some(WandOfDeath),
        Some(devices[0]),
        Some(devices[1]),
        Some(devices[2]),
        Some(Offsetter),
        Some(Copier),
        Some(Palantir),
        Some(GoldenPendant),
    ]
}

pub struct Item {
    // 5 high bits
    appearance: Appearance,

    // 3 low bits
    enchanted: bool,
    equipped: bool,
    cursed: bool,
}

impl Item {
/*
    // An inventory byte with its five high bits cleared represents an
    // empty inventory slot; `from_byte` returns None for such bytes.
    pub fn from_byte(byte: u8, appearance_map: &AppearanceMap) -> Option<Item> {
        appearance_map[byte as usize >> 3].map(|kind| Item {
            kind: kind,
            enchanted: byte & 0b100 > 0,
            equipped: byte & 0b010 > 0,
            cursed: byte & 0b001 > 0,
        })
    }

    pub fn to_byte(&self, appearance_map: &AppearanceMap) -> u8 {
        for n in 0..19 {
            if appearance_map[n] == Some(self.kind) {
                return (n << 3) as u8
                    + (if self.enchanted {0b100} else {0})
                    + (if self.equipped {0b010} else {0})
                    + (if self.cursed {0b001} else {0});
            }
        }
        panic!("Invalid AppearanceMap!");
    }
*/

    pub fn spawn() -> Item {
        Item {
            appearance: Appearance::from_byte(random_range(0x01..0x20)),
            enchanted: coin_flip(),
            equipped: coin_flip(),
            cursed: coin_flip(),
        }
    }

    pub fn sprite(&self) -> Sprite {
        self.appearance.sprite()
    }

    pub fn name(&self) -> &'static str {
        self.appearance.name()
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![feature(io)]

use std::old_io::process::{Command, ProcessExit, StdioContainer};
use std::os;

fn main() {
    let out_dir = os::getenv("OUT_DIR").unwrap();
    let result = Command::new("make")
        .args(&["-f", "makefile.cargo"])
        .stdout(StdioContainer::InheritFd(1))
        .stderr(StdioContainer::InheritFd(2))
        .status()
        .unwrap();
    assert_eq!(result, ProcessExit::ExitStatus(0));
    println!("cargo:rustc-flags=-L native={}", out_dir);
}

<commit_msg>Update to modern Rust.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::process::{Command, Stdio};
use std::env;

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let jobs = format!("-j{}", env::var("NUM_JOBS").unwrap());
    let result = Command::new("make")
        .args(&["-f", "makefile.cargo", &jobs])
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .status()
        .unwrap();
    assert!(result.success());
    println!("cargo:rustc-flags=-L native={}", out_dir);
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

fn main() {
    // Pass our linker script to the top crate
    let mut ld = String::new();

    let interrupts = env::var_os("CARGO_FEATURE_INTERRUPTS").is_some();
    let static_ram = env::var_os("CARGO_FEATURE_STATIC_RAM").is_some();

    ld.push_str("
MEMORY
{
  CCRAM : ORIGIN = 0x10000000, LENGTH = 8K
  FLASH : ORIGIN = 0x08000000, LENGTH = 256K
  RAM : ORIGIN = 0x20000000, LENGTH = 40K
}

ENTRY(_reset)

SECTIONS
{
  .text ORIGIN(FLASH) :
  {
    /* Vector table */
    _VECTOR_TABLE = .;
    LONG(ORIGIN(CCRAM) + LENGTH(CCRAM));
    LONG(_reset + 1);
    KEEP(*(.rodata._EXCEPTIONS));
    _eexceptions = .;");

    if interrupts {
        ld.push_str("
    KEEP(*(.rodata._INTERRUPTS));
    _einterrupts = .;");
    }

    ld.push_str("
    /* Entry point: reset handler */
    _reset = .;
    *(.text._reset);

    *(.text.*);
    *(.rodata.*);
  } > FLASH");

    if static_ram {
        ld.push_str("
  .bss : ALIGN(4)
  {
    _sbss = .;
    *(.bss.*);
    _ebss = ALIGN(4);
  } > RAM

  .data : ALIGN(4)
  {
    _sdata = .;
    *(.data.*);
    _edata = ALIGN(4);
  } > RAM AT > FLASH

  _sidata = LOADADDR(.data);
  _heap_start = .;
  _heap_end = ORIGIN(RAM) + LENGTH(RAM);
");
    }

    ld.push_str("
  /DISCARD/ :
  {
    *(.ARM.exidx.*)
    *(.note.gnu.build-id.*)");

    if !static_ram {
        ld.push_str("
    *(.bss.*)
    *(.data.*)");
    }

    ld.push_str("
  }
}

/* HACK to make these symbols overrideable by _dependencies_ (they were
   already overridable by the top crate), we declare them as undefined
   (EXTERN) here. */
EXTERN(__aeabi_memclr4);
EXTERN(_default_exception_handler);
EXTERN(_init);

/* Exceptions */
PROVIDE(_nmi = _default_exception_handler);
PROVIDE(_hard_fault = _default_exception_handler);
PROVIDE(_memmanage_fault = _default_exception_handler);
PROVIDE(_bus_fault = _default_exception_handler);
PROVIDE(_usage_fault = _default_exception_handler);
PROVIDE(_svcall = _default_exception_handler);
PROVIDE(_pendsv = _default_exception_handler);
PROVIDE(_systick = _default_exception_handler);");

    if interrupts {

        ld.push_str("
/* Interrupts */
PROVIDE(_wwdg = _default_exception_handler);
PROVIDE(_pvd = _default_exception_handler);
PROVIDE(_tamper_stamp = _default_exception_handler);
PROVIDE(_rtc_wkup = _default_exception_handler);
PROVIDE(_flash = _default_exception_handler);
PROVIDE(_rcc = _default_exception_handler);
PROVIDE(_exti0 = _default_exception_handler);
PROVIDE(_exti1 = _default_exception_handler);
PROVIDE(_exti2_ts = _default_exception_handler);
PROVIDE(_exti3 = _default_exception_handler);
PROVIDE(_exti4 = _default_exception_handler);
PROVIDE(_dma1_channel1 = _default_exception_handler);
PROVIDE(_dma1_channel2 = _default_exception_handler);
PROVIDE(_dma1_channel3 = _default_exception_handler);
PROVIDE(_dma1_channel4 = _default_exception_handler);
PROVIDE(_dma1_channel5 = _default_exception_handler);
PROVIDE(_dma1_channel6 = _default_exception_handler);
PROVIDE(_dma1_channel7 = _default_exception_handler);
PROVIDE(_adc1_2 = _default_exception_handler);
PROVIDE(_usb_hp_can_tx = _default_exception_handler);
PROVIDE(_usb_lp_can_rx0 = _default_exception_handler);
PROVIDE(_can_rx1 = _default_exception_handler);
PROVIDE(_can_sce = _default_exception_handler);
PROVIDE(_exti9_5 = _default_exception_handler);
PROVIDE(_tim1_brk_tim15 = _default_exception_handler);
PROVIDE(_tim1_up_tim16 = _default_exception_handler);
PROVIDE(_tim1_trg_com_tim17 = _default_exception_handler);
PROVIDE(_tim1_cc = _default_exception_handler);
PROVIDE(_tim2 = _default_exception_handler);
PROVIDE(_tim3 = _default_exception_handler);
PROVIDE(_tim4 = _default_exception_handler);
PROVIDE(_i2c1_ev = _default_exception_handler);
PROVIDE(_i2c1_er = _default_exception_handler);
PROVIDE(_i2c2_ev = _default_exception_handler);
PROVIDE(_i2c2_er = _default_exception_handler);
PROVIDE(_spi1 = _default_exception_handler);
PROVIDE(_spi2 = _default_exception_handler);
PROVIDE(_usart1 = _default_exception_handler);
PROVIDE(_usart2 = _default_exception_handler);
PROVIDE(_usart3 = _default_exception_handler);
PROVIDE(_exti15_10 = _default_exception_handler);
PROVIDE(_rtc_alarm = _default_exception_handler);
PROVIDE(_usb_wake_up = _default_exception_handler);
PROVIDE(_tim8_brk = _default_exception_handler);
PROVIDE(_tim8_up = _default_exception_handler);
PROVIDE(_tim8_trg_com = _default_exception_handler);
PROVIDE(_tim8_cc = _default_exception_handler);
PROVIDE(_adc3 = _default_exception_handler);
PROVIDE(_fmc = _default_exception_handler);
PROVIDE(_spi3 = _default_exception_handler);
PROVIDE(_uart4 = _default_exception_handler);
PROVIDE(_uart5 = _default_exception_handler);
PROVIDE(_tim6_dac = _default_exception_handler);
PROVIDE(_tim7 = _default_exception_handler);
PROVIDE(_dma2_channel1 = _default_exception_handler);
PROVIDE(_dma2_channel2 = _default_exception_handler);
PROVIDE(_dma2_channel3 = _default_exception_handler);
PROVIDE(_dma2_channel4 = _default_exception_handler);
PROVIDE(_dma2_channel5 = _default_exception_handler);
PROVIDE(_adc4 = _default_exception_handler);
PROVIDE(_comp1_2_3 = _default_exception_handler);
PROVIDE(_comp4_5_6 = _default_exception_handler);
PROVIDE(_comp7 = _default_exception_handler);
PROVIDE(_i2c3_ev = _default_exception_handler);
PROVIDE(_i2c3_er = _default_exception_handler);
PROVIDE(_usb_hp = _default_exception_handler);
PROVIDE(_usb_lp = _default_exception_handler);
PROVIDE(_usb_wake_up_rmp = _default_exception_handler);
PROVIDE(_tim20_brk = _default_exception_handler);
PROVIDE(_tim20_up = _default_exception_handler);
PROVIDE(_tim20_trg_com = _default_exception_handler);
PROVIDE(_tim20_cc = _default_exception_handler);
PROVIDE(_fpu = _default_exception_handler);
PROVIDE(_spi4 = _default_exception_handler);");
    }

    ld.push_str("
ASSERT(_eexceptions - ORIGIN(FLASH) == 0x40, \"exceptions \
                 not linked where expected\");");

    if interrupts {
        ld.push_str("
ASSERT(_einterrupts - ORIGIN(FLASH) == 0x194, \
                     \"interrupts not linked where expected\");");
    }

    let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    File::create(out.join("stm32f3discovery.ld"))
        .unwrap()
        .write_all(ld.as_bytes())
        .unwrap();

    println!("cargo:rustc-link-search={}", out.display());
}
<commit_msg>KEEP the _reset symbol<commit_after>use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

fn main() {
    // Pass our linker script to the top crate
    let mut ld = String::new();

    let interrupts = env::var_os("CARGO_FEATURE_INTERRUPTS").is_some();
    let static_ram = env::var_os("CARGO_FEATURE_STATIC_RAM").is_some();

    ld.push_str("
MEMORY
{
  CCRAM : ORIGIN = 0x10000000, LENGTH = 8K
  FLASH : ORIGIN = 0x08000000, LENGTH = 256K
  RAM : ORIGIN = 0x20000000, LENGTH = 40K
}

ENTRY(_reset)

SECTIONS
{
  .text ORIGIN(FLASH) :
  {
    /* Vector table */
    _VECTOR_TABLE = .;
    LONG(ORIGIN(CCRAM) + LENGTH(CCRAM));
    LONG(_reset + 1);
    KEEP(*(.rodata._EXCEPTIONS));
    _eexceptions = .;");

    if interrupts {
        ld.push_str("
    KEEP(*(.rodata._INTERRUPTS));
    _einterrupts = .;");
    }

    ld.push_str("
    /* Entry point: reset handler */
    _reset = .;
    KEEP(*(.text._reset));

    *(.text.*);
    *(.rodata.*);
  } > FLASH");

    if static_ram {
        ld.push_str("
  .bss : ALIGN(4)
  {
    _sbss = .;
    *(.bss.*);
    _ebss = ALIGN(4);
  } > RAM

  .data : ALIGN(4)
  {
    _sdata = .;
    *(.data.*);
    _edata = ALIGN(4);
  } > RAM AT > FLASH

  _sidata = LOADADDR(.data);
  _heap_start = .;
  _heap_end = ORIGIN(RAM) + LENGTH(RAM);
");
    }

    ld.push_str("
  /DISCARD/ :
  {
    *(.ARM.exidx.*)
    *(.note.gnu.build-id.*)");

    if !static_ram {
        ld.push_str("
    *(.bss.*)
    *(.data.*)");
    }

    ld.push_str("
  }
}

/* HACK to make these symbols overrideable by _dependencies_ (they were
   already overridable by the top crate), we declare them as undefined
   (EXTERN) here. */
EXTERN(__aeabi_memclr4);
EXTERN(_default_exception_handler);
EXTERN(_init);

/* Exceptions */
PROVIDE(_nmi = _default_exception_handler);
PROVIDE(_hard_fault = _default_exception_handler);
PROVIDE(_memmanage_fault = _default_exception_handler);
PROVIDE(_bus_fault = _default_exception_handler);
PROVIDE(_usage_fault = _default_exception_handler);
PROVIDE(_svcall = _default_exception_handler);
PROVIDE(_pendsv = _default_exception_handler);
PROVIDE(_systick = _default_exception_handler);");

    if interrupts {

        ld.push_str("
/* Interrupts */
PROVIDE(_wwdg = _default_exception_handler);
PROVIDE(_pvd = _default_exception_handler);
PROVIDE(_tamper_stamp = _default_exception_handler);
PROVIDE(_rtc_wkup = _default_exception_handler);
PROVIDE(_flash = _default_exception_handler);
PROVIDE(_rcc = _default_exception_handler);
PROVIDE(_exti0 = _default_exception_handler);
PROVIDE(_exti1 = _default_exception_handler);
PROVIDE(_exti2_ts = _default_exception_handler);
PROVIDE(_exti3 = _default_exception_handler);
PROVIDE(_exti4 = _default_exception_handler);
PROVIDE(_dma1_channel1 = _default_exception_handler);
PROVIDE(_dma1_channel2 = _default_exception_handler);
PROVIDE(_dma1_channel3 = _default_exception_handler);
PROVIDE(_dma1_channel4 = _default_exception_handler);
PROVIDE(_dma1_channel5 = _default_exception_handler);
PROVIDE(_dma1_channel6 = _default_exception_handler);
PROVIDE(_dma1_channel7 = _default_exception_handler);
PROVIDE(_adc1_2 = _default_exception_handler);
PROVIDE(_usb_hp_can_tx = _default_exception_handler);
PROVIDE(_usb_lp_can_rx0 = _default_exception_handler);
PROVIDE(_can_rx1 = _default_exception_handler);
PROVIDE(_can_sce = _default_exception_handler);
PROVIDE(_exti9_5 = _default_exception_handler);
PROVIDE(_tim1_brk_tim15 = _default_exception_handler);
PROVIDE(_tim1_up_tim16 = _default_exception_handler);
PROVIDE(_tim1_trg_com_tim17 = _default_exception_handler);
PROVIDE(_tim1_cc = _default_exception_handler);
PROVIDE(_tim2 = _default_exception_handler);
PROVIDE(_tim3 = _default_exception_handler);
PROVIDE(_tim4 = _default_exception_handler);
PROVIDE(_i2c1_ev = _default_exception_handler);
PROVIDE(_i2c1_er = _default_exception_handler);
PROVIDE(_i2c2_ev = _default_exception_handler);
PROVIDE(_i2c2_er = _default_exception_handler);
PROVIDE(_spi1 = _default_exception_handler);
PROVIDE(_spi2 = _default_exception_handler);
PROVIDE(_usart1 = _default_exception_handler);
PROVIDE(_usart2 = _default_exception_handler);
PROVIDE(_usart3 = _default_exception_handler);
PROVIDE(_exti15_10 = _default_exception_handler);
PROVIDE(_rtc_alarm = _default_exception_handler);
PROVIDE(_usb_wake_up = _default_exception_handler);
PROVIDE(_tim8_brk = _default_exception_handler);
PROVIDE(_tim8_up = _default_exception_handler);
PROVIDE(_tim8_trg_com = _default_exception_handler);
PROVIDE(_tim8_cc = _default_exception_handler);
PROVIDE(_adc3 = _default_exception_handler);
PROVIDE(_fmc = _default_exception_handler);
PROVIDE(_spi3 = _default_exception_handler);
PROVIDE(_uart4 = _default_exception_handler);
PROVIDE(_uart5 = _default_exception_handler);
PROVIDE(_tim6_dac = _default_exception_handler);
PROVIDE(_tim7 = _default_exception_handler);
PROVIDE(_dma2_channel1 = _default_exception_handler);
PROVIDE(_dma2_channel2 = _default_exception_handler);
PROVIDE(_dma2_channel3 = _default_exception_handler);
PROVIDE(_dma2_channel4 = _default_exception_handler);
PROVIDE(_dma2_channel5 = _default_exception_handler);
PROVIDE(_adc4 = _default_exception_handler);
PROVIDE(_comp1_2_3 = _default_exception_handler);
PROVIDE(_comp4_5_6 = _default_exception_handler);
PROVIDE(_comp7 = _default_exception_handler);
PROVIDE(_i2c3_ev = _default_exception_handler);
PROVIDE(_i2c3_er = _default_exception_handler);
PROVIDE(_usb_hp = _default_exception_handler);
PROVIDE(_usb_lp = _default_exception_handler);
PROVIDE(_usb_wake_up_rmp = _default_exception_handler);
PROVIDE(_tim20_brk = _default_exception_handler);
PROVIDE(_tim20_up = _default_exception_handler);
PROVIDE(_tim20_trg_com = _default_exception_handler);
PROVIDE(_tim20_cc = _default_exception_handler);
PROVIDE(_fpu = _default_exception_handler);
PROVIDE(_spi4 = _default_exception_handler);");
    }

    ld.push_str("
ASSERT(_eexceptions - ORIGIN(FLASH) == 0x40, \"exceptions \
                 not linked where expected\");");

    if interrupts {
        ld.push_str("
ASSERT(_einterrupts - ORIGIN(FLASH) == 0x194, \
                     \"interrupts not linked where expected\");");
    }

    let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    File::create(out.join("stm32f3discovery.ld"))
        .unwrap()
        .write_all(ld.as_bytes())
        .unwrap();

    println!("cargo:rustc-link-search={}", out.display());
}
<|endoftext|>


<commit_before>#[cfg(any(feature = "backend_egl", feature = "backend_gl"))]
extern crate gl_generator;

#[cfg(any(feature = "backend_egl", feature = "backend_gl"))]
use gl_generator::{Api, Fallbacks, Profile, Registry};
use std::{env, fs::File, path::PathBuf};

#[cfg(any(feature = "backend_egl", feature = "backend_gl"))]
fn main() {
    let dest = PathBuf::from(&env::var("OUT_DIR").unwrap());

    println!("cargo:rerun-if-changed=build.rs");

    if env::var_os("CARGO_FEATURE_BACKEND_EGL").is_some() {
        let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap();
        Registry::new(
            Api::Egl,
            (1, 5),
            Profile::Core,
            Fallbacks::All,
            [
                "EGL_KHR_create_context",
                "EGL_EXT_create_context_robustness",
                "EGL_KHR_create_context_no_error",
                "EGL_KHR_platform_x11",
                "EGL_KHR_platform_android",
                "EGL_KHR_platform_wayland",
                "EGL_KHR_platform_gbm",
                "EGL_EXT_platform_base",
                "EGL_EXT_platform_x11",
                "EGL_MESA_platform_gbm",
                "EGL_EXT_platform_wayland",
                "EGL_EXT_platform_device",
                "EGL_KHR_image_base",
            ],
        ).write_bindings(gl_generator::GlobalGenerator, &mut file)
        .unwrap();
    }

    if env::var_os("CARGO_FEATURE_RENDERER_GL").is_some() {
        let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap();
        Registry::new(
            Api::Gles2,
            (3, 2),
            Profile::Compatibility,
            Fallbacks::None,
            ["GL_OES_EGL_image"],
        ).write_bindings(gl_generator::StructGenerator, &mut file)
        .unwrap();
    }
}

#[cfg(not(any(feature = "backend_egl", feature = "backend_gl")))]
fn main() {}<commit_msg>build: fix wrongly named feature<commit_after>#[cfg(any(feature = "backend_egl", feature = "renderer_gl"))]
extern crate gl_generator;

#[cfg(any(feature = "backend_egl", feature = "renderer_gl"))]
use gl_generator::{Api, Fallbacks, Profile, Registry};
use std::{env, fs::File, path::PathBuf};

#[cfg(any(feature = "backend_egl", feature = "backend_gl"))]
fn main() {
    let dest = PathBuf::from(&env::var("OUT_DIR").unwrap());

    println!("cargo:rerun-if-changed=build.rs");

    if env::var_os("CARGO_FEATURE_BACKEND_EGL").is_some() {
        let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap();
        Registry::new(
            Api::Egl,
            (1, 5),
            Profile::Core,
            Fallbacks::All,
            [
                "EGL_KHR_create_context",
                "EGL_EXT_create_context_robustness",
                "EGL_KHR_create_context_no_error",
                "EGL_KHR_platform_x11",
                "EGL_KHR_platform_android",
                "EGL_KHR_platform_wayland",
                "EGL_KHR_platform_gbm",
                "EGL_EXT_platform_base",
                "EGL_EXT_platform_x11",
                "EGL_MESA_platform_gbm",
                "EGL_EXT_platform_wayland",
                "EGL_EXT_platform_device",
                "EGL_KHR_image_base",
            ],
        ).write_bindings(gl_generator::GlobalGenerator, &mut file)
        .unwrap();
    }

    if env::var_os("CARGO_FEATURE_RENDERER_GL").is_some() {
        let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap();
        Registry::new(
            Api::Gles2,
            (3, 2),
            Profile::Compatibility,
            Fallbacks::None,
            ["GL_OES_EGL_image"],
        ).write_bindings(gl_generator::StructGenerator, &mut file)
        .unwrap();
    }
}

#[cfg(not(any(feature = "backend_egl", feature = "renderer_gl")))]
fn main() {}<|endoftext|>


<commit_before>use std::env;

fn main() {
    // Decide ideal limb width for arithmetic in the float parser. Refer to
    // src/lexical/math.rs for where this has an effect.
    let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
    match target_arch.as_str() {
        "aarch64" | "mips64" | "powerpc64" | "x86_64" => {
            println!("cargo:rustc-cfg=limb_width_64");
        }
        _ => {
            println!("cargo:rustc-cfg=limb_width_32");
        }
    }
}
<commit_msg>Add compiler version detection for BTreeMap api<commit_after>use std::env;
use std::process::Command;
use std::str::{self, FromStr};

fn main() {
    // Decide ideal limb width for arithmetic in the float parser. Refer to
    // src/lexical/math.rs for where this has an effect.
    let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
    match target_arch.as_str() {
        "aarch64" | "mips64" | "powerpc64" | "x86_64" => {
            println!("cargo:rustc-cfg=limb_width_64");
        }
        _ => {
            println!("cargo:rustc-cfg=limb_width_32");
        }
    }

    let minor = match rustc_minor_version() {
        Some(minor) => minor,
        None => return,
    };

    // BTreeMap::get_key_value
    // https://blog.rust-lang.org/2019/12/19/Rust-1.40.0.html#additions-to-the-standard-library
    if minor >= 40 {
        println!("cargo:rustc-cfg=btreemap_get_key_value");
    }

    // BTreeMap::remove_entry
    // https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#library-changes
    if minor >= 45 {
        println!("cargo:rustc-cfg=btreemap_remove_entry");
    }
}

fn rustc_minor_version() -> Option<u32> {
    let rustc = env::var_os("RUSTC")?;
    let output = Command::new(rustc).arg("--version").output().ok()?;
    let version = str::from_utf8(&output.stdout).ok()?;
    let mut pieces = version.split('.');
    if pieces.next() != Some("rustc 1") {
        return None;
    }
    let next = pieces.next()?;
    u32::from_str(next).ok()
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::Command;

fn main() {
    let output_dir = env::var("OUT_DIR").unwrap();
    let output_file_path = Path::new(&output_dir).join("git-hash");
    let mut output_file = File::create(&output_file_path).unwrap();

    let git_command = Command::new("git")
        .args(&["rev-parse", "HEAD"])
        .output()
        .unwrap();

    output_file
        .write_all(git_command.stdout.as_slice())
        .unwrap();
}
<commit_msg>Add a comment explaining what git-hash is for.<commit_after>use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::Command;

fn main() {
    // Write the hash of our current revision to the file
    // ${OUT_DIR}/git-hash, so that it can be tetxually included in the
    // binary using include_str!() in src/main.rs.
    let output_dir = env::var("OUT_DIR").unwrap();
    let output_file_path = Path::new(&output_dir).join("git-hash");
    let mut output_file = File::create(&output_file_path).unwrap();

    let git_command = Command::new("git")
        .args(&["rev-parse", "HEAD"])
        .output()
        .unwrap();

    output_file
        .write_all(git_command.stdout.as_slice())
        .unwrap();
}
<|endoftext|>


<commit_before>// build.rs
//
// Execute Erlang script to generate API lists and extract config.
//

use std::process::Command;
use std::env;
use std::path::Path;

fn main() {
	// FIXME: take env escript if present
	let escript = "escript";

	// emit generated files to OUT_DIR
	let out_dir = env::var("OUT_DIR").unwrap();
	let dst = Path::new(&out_dir);
	Command::new(escript).arg("gen_api.erl").arg(dst).status().unwrap();
}
<commit_msg>improve error reporting, use ESCRIPT env var if present<commit_after>// build.rs
//
// Execute Erlang script to generate API lists and extract config.
//

use std::process::Command;
use std::env;
use std::path::Path;

fn main() {
	// use environment escript if available
	let escript = env::var("ESCRIPT").unwrap_or("escript".to_string());

	// emit generated files to OUT_DIR
	let out_dir = env::var("OUT_DIR")
		.map_err(|_|"Can't read OUT_DIR env variable.")
		.unwrap();

	let dst = Path::new(&out_dir);
	match Command::new(escript).arg("gen_api.erl").arg(dst).status()
		.map_err(|_|"Failed to start gen_api.erl.  Is 'escript' available in the path?")
		.unwrap().success() {
			true  => (),
			false => panic!("gen_api.erl encountered an error.")
		}
}
<|endoftext|>


<commit_before>#![forbid(unsafe_code)]

pub mod api;
pub mod db;
pub mod importer;
pub mod indexer;
pub mod model;
pub mod opening;
pub mod util;

use std::{mem, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};

use axum::{
    extract::{Extension, Path, Query},
    handler::{get, put},
    http::StatusCode,
    AddExtensionLayer, Json, Router,
};
use futures_util::stream::Stream;
use serde::Deserialize;
use serde_with::{serde_as, DisplayFromStr};
use shakmaty::{
    fen::Fen,
    san::{San, SanPlus},
    uci::Uci,
    variant::{Variant, VariantPosition},
    zobrist::Zobrist,
    CastlingMode,
};
use tokio::sync::watch;
use clap::Parser;

use crate::{
    api::{
        Error, ExplorerGame, ExplorerGameWithUci, ExplorerMove, ExplorerResponse, LichessQuery,
        Limits, MasterQuery, NdJson, PersonalQuery, PersonalQueryFilter,
    },
    db::{Database, QueryableDatabase},
    importer::{LichessGame, LichessImporter, MasterImporter},
    indexer::{IndexerOpt, IndexerStub},
    model::{GameId, KeyBuilder, KeyPrefix, MasterGame, MasterGameWithId, PreparedMove, UserId},
    opening::{Opening, Openings},
    util::DedupStreamExt as _,
};

#[derive(Parser)]
struct Opt {
    #[clap(long = "bind", default_value = "127.0.0.1:9000")]
    bind: SocketAddr,
    #[clap(long = "db", default_value = "_db")]
    db: PathBuf,
    #[clap(flatten)]
    indexer: IndexerOpt,
}

#[tokio::main]
async fn main() {
    env_logger::Builder::from_env(
        env_logger::Env::new()
            .filter("EXPLORER_LOG")
            .write_style("EXPLORER_LOG_STYLE"),
    )
    .format_timestamp(None)
    .format_module_path(false)
    .format_target(false)
    .init();

    let opt = Opt::parse();

    let openings: &'static Openings = Box::leak(Box::new(Openings::build_table()));
    let db = Arc::new(Database::open(opt.db).expect("db"));
    let (indexer, join_handles) = IndexerStub::spawn(Arc::clone(&db), opt.indexer);
    let master_importer = MasterImporter::new(Arc::clone(&db));
    let lichess_importer = LichessImporter::new(Arc::clone(&db));

    let app = Router::new()
        .route("/admin/:prop", get(db_property))
        .route("/admin/game/:prop", get(game_property))
        .route("/admin/personal/:prop", get(personal_property))
        .route("/admin/explorer.indexing", get(num_indexing))
        .route("/import/master", put(master_import))
        .route("/import/lichess", put(lichess_import))
        .route("/master/pgn/:id", get(master_pgn))
        .route("/master", get(master))
        .route("/personal", get(personal))
        .route("/lichess", get(lichess))
        .layer(AddExtensionLayer::new(openings))
        .layer(AddExtensionLayer::new(db))
        .layer(AddExtensionLayer::new(master_importer))
        .layer(AddExtensionLayer::new(lichess_importer))
        .layer(AddExtensionLayer::new(indexer));

    #[cfg(feature = "cors")]
    let app = app.layer(tower_http::set_header::SetResponseHeaderLayer::<
        _,
        axum::body::Body,
    >::if_not_present(
        axum::http::header::ACCESS_CONTROL_ALLOW_ORIGIN,
        axum::http::HeaderValue::from_static("*"),
    ));

    axum::Server::bind(&opt.bind)
        .serve(app.into_make_service())
        .await
        .expect("bind");

    for join_handle in join_handles {
        join_handle.await.expect("indexer");
    }
}

async fn db_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .db_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn game_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .game_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn personal_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .personal_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn num_indexing(Extension(indexer): Extension<IndexerStub>) -> String {
    indexer.num_indexing().await.to_string()
}

struct PersonalStreamState {
    indexing: Option<watch::Receiver<()>>,
    key: KeyPrefix,
    db: Arc<Database>,
    filter: PersonalQueryFilter,
    limits: Limits,
    pos: VariantPosition,
    opening: Option<&'static Opening>,
    first: bool,
    done: bool,
}

fn finalize_lichess_moves(
    moves: Vec<PreparedMove>,
    pos: &VariantPosition,
    queryable: &QueryableDatabase,
) -> Vec<ExplorerMove> {
    moves
        .into_iter()
        .map(|p| ExplorerMove {
            stats: p.stats,
            san: p.uci.to_move(pos).map_or(
                SanPlus {
                    san: San::Null,
                    suffix: None,
                },
                |m| SanPlus::from_move(pos.clone(), &m),
            ),
            uci: p.uci,
            average_rating: p.average_rating,
            average_opponent_rating: p.average_opponent_rating,
            game: p.game.and_then(|id| {
                queryable
                    .get_game_info(id)
                    .expect("get game")
                    .map(|info| ExplorerGame::from_lichess(id, info))
            }),
        })
        .collect()
}

fn finalize_lichess_games(
    games: Vec<(Uci, GameId)>,
    queryable: &QueryableDatabase,
) -> Vec<ExplorerGameWithUci> {
    games
        .into_iter()
        .flat_map(|(uci, id)| {
            queryable
                .get_game_info(id)
                .expect("get game")
                .map(|info| ExplorerGameWithUci {
                    uci,
                    row: ExplorerGame::from_lichess(id, info),
                })
        })
        .collect()
}

async fn personal(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Extension(indexer): Extension<IndexerStub>,
    Query(query): Query<PersonalQuery>,
) -> Result<NdJson<impl Stream<Item = ExplorerResponse>>, Error> {
    let player = UserId::from(query.player);
    let indexing = indexer.index_player(&player).await;

    let variant = query.variant.into();

    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => VariantPosition::from_setup(variant, &Fen::from(fen), CastlingMode::Chess960)?,
        None => VariantPosition::new(variant),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;

    let key = KeyBuilder::personal(&player, query.color).with_zobrist(variant, pos.zobrist_hash());

    let state = PersonalStreamState {
        filter: query.filter,
        limits: query.limits,
        db,
        indexing,
        opening,
        key,
        pos: pos.into_inner(),
        first: true,
        done: false,
    };

    Ok(NdJson(futures_util::stream::unfold(
        state,
        |mut state| async move {
            if state.done {
                return None;
            }

            let first = mem::replace(&mut state.first, false);
            state.done = match state.indexing {
                Some(ref mut indexing) => {
                    tokio::select! {
                        _ = indexing.changed() => true,
                        _ = tokio::time::sleep(Duration::from_millis(if first { 0 } else { 1000 })) => false,
                    }
                }
                None => true,
            };

            let queryable = state.db.queryable();
            let mut filtered = queryable
                .get_personal(&state.key, state.filter.since, state.filter.until)
                .expect("get personal")
                .prepare(&state.filter);

            filtered.moves.truncate(state.limits.moves.unwrap_or(usize::MAX));
            filtered.recent_games.truncate(state.limits.recent_games);

            Some((
                ExplorerResponse {
                    total: filtered.total,
                    moves: finalize_lichess_moves(filtered.moves, &state.pos, &queryable),
                    recent_games: Some(finalize_lichess_games(filtered.recent_games, &queryable)),
                    top_games: None,
                    opening: state.opening,
                },
                state,
            ))
        },
    ).dedup_by_key(|res| res.total.total())))
}

async fn master_import(
    Json(body): Json<MasterGameWithId>,
    Extension(importer): Extension<MasterImporter>,
) -> Result<(), Error> {
    importer.import(body).await
}

#[serde_as]
#[derive(Deserialize)]
struct MasterGameId(#[serde_as(as = "DisplayFromStr")] GameId);

async fn master_pgn(
    Path(MasterGameId(id)): Path<MasterGameId>,
    Extension(db): Extension<Arc<Database>>,
) -> Result<MasterGame, StatusCode> {
    match db.queryable().get_master_game(id).expect("get master game") {
        Some(game) => Ok(game),
        None => Err(StatusCode::NOT_FOUND),
    }
}

async fn master(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<MasterQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => {
            VariantPosition::from_setup(Variant::Chess, &Fen::from(fen), CastlingMode::Chess960)?
        }
        None => VariantPosition::new(Variant::Chess),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;
    let key = KeyBuilder::master().with_zobrist(Variant::Chess, pos.zobrist_hash());
    let queryable = db.queryable();
    let mut entry = queryable
        .get_master(key, query.since, query.until)
        .expect("get master")
        .prepare();

    entry.moves.truncate(query.limits.moves.unwrap_or(12));
    entry.top_games.truncate(query.limits.top_games);

    Ok(Json(ExplorerResponse {
        total: entry.total,
        moves: entry
            .moves
            .into_iter()
            .map(|p| ExplorerMove {
                san: p.uci.to_move(&pos).map_or(
                    SanPlus {
                        san: San::Null,
                        suffix: None,
                    },
                    |m| SanPlus::from_move(pos.clone(), &m),
                ),
                uci: p.uci,
                average_rating: p.average_rating,
                average_opponent_rating: p.average_opponent_rating,
                stats: p.stats,
                game: p.game.and_then(|id| {
                    queryable
                        .get_master_game(id)
                        .expect("get master game")
                        .map(|info| ExplorerGame::from_master(id, info))
                }),
            })
            .collect(),
        top_games: Some(
            entry
                .top_games
                .into_iter()
                .flat_map(|(uci, id)| {
                    queryable
                        .get_master_game(id)
                        .expect("get master game")
                        .map(|info| ExplorerGameWithUci {
                            uci,
                            row: ExplorerGame::from_master(id, info),
                        })
                })
                .collect(),
        ),
        opening,
        recent_games: None,
    }))
}

async fn lichess_import(
    Json(body): Json<Vec<LichessGame>>,
    Extension(importer): Extension<LichessImporter>,
) -> Result<(), Error> {
    for game in body {
        importer.import(game).await?;
    }
    Ok(())
}

async fn lichess(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<LichessQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let variant = Variant::from(query.variant);
    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => VariantPosition::from_setup(variant, &Fen::from(fen), CastlingMode::Chess960)?,
        None => VariantPosition::new(variant),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;

    let key = KeyBuilder::lichess().with_zobrist(variant, pos.zobrist_hash());
    let queryable = db.queryable();
    let mut filtered = queryable
        .get_lichess(&key, query.filter.since, query.filter.until)
        .expect("get lichess")
        .prepare(&query.filter);

    filtered.moves.truncate(query.limits.moves.unwrap_or(12));
    filtered.recent_games.truncate(query.limits.recent_games);
    filtered.top_games.truncate(query.limits.recent_games);

    Ok(Json(ExplorerResponse {
        total: filtered.total,
        moves: finalize_lichess_moves(filtered.moves, pos.as_inner(), &queryable),
        recent_games: Some(finalize_lichess_games(filtered.recent_games, &queryable)),
        top_games: Some(finalize_lichess_games(filtered.top_games, &queryable)),
        opening,
    }))
}
<commit_msg>move conflicting route<commit_after>#![forbid(unsafe_code)]

pub mod api;
pub mod db;
pub mod importer;
pub mod indexer;
pub mod model;
pub mod opening;
pub mod util;

use std::{mem, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};

use axum::{
    extract::{Extension, Path, Query},
    handler::{get, put},
    http::StatusCode,
    AddExtensionLayer, Json, Router,
};
use futures_util::stream::Stream;
use serde::Deserialize;
use serde_with::{serde_as, DisplayFromStr};
use shakmaty::{
    fen::Fen,
    san::{San, SanPlus},
    uci::Uci,
    variant::{Variant, VariantPosition},
    zobrist::Zobrist,
    CastlingMode,
};
use tokio::sync::watch;
use clap::Parser;

use crate::{
    api::{
        Error, ExplorerGame, ExplorerGameWithUci, ExplorerMove, ExplorerResponse, LichessQuery,
        Limits, MasterQuery, NdJson, PersonalQuery, PersonalQueryFilter,
    },
    db::{Database, QueryableDatabase},
    importer::{LichessGame, LichessImporter, MasterImporter},
    indexer::{IndexerOpt, IndexerStub},
    model::{GameId, KeyBuilder, KeyPrefix, MasterGame, MasterGameWithId, PreparedMove, UserId},
    opening::{Opening, Openings},
    util::DedupStreamExt as _,
};

#[derive(Parser)]
struct Opt {
    #[clap(long = "bind", default_value = "127.0.0.1:9000")]
    bind: SocketAddr,
    #[clap(long = "db", default_value = "_db")]
    db: PathBuf,
    #[clap(flatten)]
    indexer: IndexerOpt,
}

#[tokio::main]
async fn main() {
    env_logger::Builder::from_env(
        env_logger::Env::new()
            .filter("EXPLORER_LOG")
            .write_style("EXPLORER_LOG_STYLE"),
    )
    .format_timestamp(None)
    .format_module_path(false)
    .format_target(false)
    .init();

    let opt = Opt::parse();

    let openings: &'static Openings = Box::leak(Box::new(Openings::build_table()));
    let db = Arc::new(Database::open(opt.db).expect("db"));
    let (indexer, join_handles) = IndexerStub::spawn(Arc::clone(&db), opt.indexer);
    let master_importer = MasterImporter::new(Arc::clone(&db));
    let lichess_importer = LichessImporter::new(Arc::clone(&db));

    let app = Router::new()
        .route("/admin/db/:prop", get(db_property))
        .route("/admin/game/:prop", get(game_property))
        .route("/admin/personal/:prop", get(personal_property))
        .route("/admin/explorer.indexing", get(num_indexing))
        .route("/import/master", put(master_import))
        .route("/import/lichess", put(lichess_import))
        .route("/master/pgn/:id", get(master_pgn))
        .route("/master", get(master))
        .route("/personal", get(personal))
        .route("/lichess", get(lichess))
        .layer(AddExtensionLayer::new(openings))
        .layer(AddExtensionLayer::new(db))
        .layer(AddExtensionLayer::new(master_importer))
        .layer(AddExtensionLayer::new(lichess_importer))
        .layer(AddExtensionLayer::new(indexer));

    #[cfg(feature = "cors")]
    let app = app.layer(tower_http::set_header::SetResponseHeaderLayer::<
        _,
        axum::body::Body,
    >::if_not_present(
        axum::http::header::ACCESS_CONTROL_ALLOW_ORIGIN,
        axum::http::HeaderValue::from_static("*"),
    ));

    axum::Server::bind(&opt.bind)
        .serve(app.into_make_service())
        .await
        .expect("bind");

    for join_handle in join_handles {
        join_handle.await.expect("indexer");
    }
}

async fn db_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .db_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn game_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .game_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn personal_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .personal_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn num_indexing(Extension(indexer): Extension<IndexerStub>) -> String {
    indexer.num_indexing().await.to_string()
}

struct PersonalStreamState {
    indexing: Option<watch::Receiver<()>>,
    key: KeyPrefix,
    db: Arc<Database>,
    filter: PersonalQueryFilter,
    limits: Limits,
    pos: VariantPosition,
    opening: Option<&'static Opening>,
    first: bool,
    done: bool,
}

fn finalize_lichess_moves(
    moves: Vec<PreparedMove>,
    pos: &VariantPosition,
    queryable: &QueryableDatabase,
) -> Vec<ExplorerMove> {
    moves
        .into_iter()
        .map(|p| ExplorerMove {
            stats: p.stats,
            san: p.uci.to_move(pos).map_or(
                SanPlus {
                    san: San::Null,
                    suffix: None,
                },
                |m| SanPlus::from_move(pos.clone(), &m),
            ),
            uci: p.uci,
            average_rating: p.average_rating,
            average_opponent_rating: p.average_opponent_rating,
            game: p.game.and_then(|id| {
                queryable
                    .get_game_info(id)
                    .expect("get game")
                    .map(|info| ExplorerGame::from_lichess(id, info))
            }),
        })
        .collect()
}

fn finalize_lichess_games(
    games: Vec<(Uci, GameId)>,
    queryable: &QueryableDatabase,
) -> Vec<ExplorerGameWithUci> {
    games
        .into_iter()
        .flat_map(|(uci, id)| {
            queryable
                .get_game_info(id)
                .expect("get game")
                .map(|info| ExplorerGameWithUci {
                    uci,
                    row: ExplorerGame::from_lichess(id, info),
                })
        })
        .collect()
}

async fn personal(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Extension(indexer): Extension<IndexerStub>,
    Query(query): Query<PersonalQuery>,
) -> Result<NdJson<impl Stream<Item = ExplorerResponse>>, Error> {
    let player = UserId::from(query.player);
    let indexing = indexer.index_player(&player).await;

    let variant = query.variant.into();

    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => VariantPosition::from_setup(variant, &Fen::from(fen), CastlingMode::Chess960)?,
        None => VariantPosition::new(variant),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;

    let key = KeyBuilder::personal(&player, query.color).with_zobrist(variant, pos.zobrist_hash());

    let state = PersonalStreamState {
        filter: query.filter,
        limits: query.limits,
        db,
        indexing,
        opening,
        key,
        pos: pos.into_inner(),
        first: true,
        done: false,
    };

    Ok(NdJson(futures_util::stream::unfold(
        state,
        |mut state| async move {
            if state.done {
                return None;
            }

            let first = mem::replace(&mut state.first, false);
            state.done = match state.indexing {
                Some(ref mut indexing) => {
                    tokio::select! {
                        _ = indexing.changed() => true,
                        _ = tokio::time::sleep(Duration::from_millis(if first { 0 } else { 1000 })) => false,
                    }
                }
                None => true,
            };

            let queryable = state.db.queryable();
            let mut filtered = queryable
                .get_personal(&state.key, state.filter.since, state.filter.until)
                .expect("get personal")
                .prepare(&state.filter);

            filtered.moves.truncate(state.limits.moves.unwrap_or(usize::MAX));
            filtered.recent_games.truncate(state.limits.recent_games);

            Some((
                ExplorerResponse {
                    total: filtered.total,
                    moves: finalize_lichess_moves(filtered.moves, &state.pos, &queryable),
                    recent_games: Some(finalize_lichess_games(filtered.recent_games, &queryable)),
                    top_games: None,
                    opening: state.opening,
                },
                state,
            ))
        },
    ).dedup_by_key(|res| res.total.total())))
}

async fn master_import(
    Json(body): Json<MasterGameWithId>,
    Extension(importer): Extension<MasterImporter>,
) -> Result<(), Error> {
    importer.import(body).await
}

#[serde_as]
#[derive(Deserialize)]
struct MasterGameId(#[serde_as(as = "DisplayFromStr")] GameId);

async fn master_pgn(
    Path(MasterGameId(id)): Path<MasterGameId>,
    Extension(db): Extension<Arc<Database>>,
) -> Result<MasterGame, StatusCode> {
    match db.queryable().get_master_game(id).expect("get master game") {
        Some(game) => Ok(game),
        None => Err(StatusCode::NOT_FOUND),
    }
}

async fn master(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<MasterQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => {
            VariantPosition::from_setup(Variant::Chess, &Fen::from(fen), CastlingMode::Chess960)?
        }
        None => VariantPosition::new(Variant::Chess),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;
    let key = KeyBuilder::master().with_zobrist(Variant::Chess, pos.zobrist_hash());
    let queryable = db.queryable();
    let mut entry = queryable
        .get_master(key, query.since, query.until)
        .expect("get master")
        .prepare();

    entry.moves.truncate(query.limits.moves.unwrap_or(12));
    entry.top_games.truncate(query.limits.top_games);

    Ok(Json(ExplorerResponse {
        total: entry.total,
        moves: entry
            .moves
            .into_iter()
            .map(|p| ExplorerMove {
                san: p.uci.to_move(&pos).map_or(
                    SanPlus {
                        san: San::Null,
                        suffix: None,
                    },
                    |m| SanPlus::from_move(pos.clone(), &m),
                ),
                uci: p.uci,
                average_rating: p.average_rating,
                average_opponent_rating: p.average_opponent_rating,
                stats: p.stats,
                game: p.game.and_then(|id| {
                    queryable
                        .get_master_game(id)
                        .expect("get master game")
                        .map(|info| ExplorerGame::from_master(id, info))
                }),
            })
            .collect(),
        top_games: Some(
            entry
                .top_games
                .into_iter()
                .flat_map(|(uci, id)| {
                    queryable
                        .get_master_game(id)
                        .expect("get master game")
                        .map(|info| ExplorerGameWithUci {
                            uci,
                            row: ExplorerGame::from_master(id, info),
                        })
                })
                .collect(),
        ),
        opening,
        recent_games: None,
    }))
}

async fn lichess_import(
    Json(body): Json<Vec<LichessGame>>,
    Extension(importer): Extension<LichessImporter>,
) -> Result<(), Error> {
    for game in body {
        importer.import(game).await?;
    }
    Ok(())
}

async fn lichess(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<LichessQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let variant = Variant::from(query.variant);
    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => VariantPosition::from_setup(variant, &Fen::from(fen), CastlingMode::Chess960)?,
        None => VariantPosition::new(variant),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;

    let key = KeyBuilder::lichess().with_zobrist(variant, pos.zobrist_hash());
    let queryable = db.queryable();
    let mut filtered = queryable
        .get_lichess(&key, query.filter.since, query.filter.until)
        .expect("get lichess")
        .prepare(&query.filter);

    filtered.moves.truncate(query.limits.moves.unwrap_or(12));
    filtered.recent_games.truncate(query.limits.recent_games);
    filtered.top_games.truncate(query.limits.recent_games);

    Ok(Json(ExplorerResponse {
        total: filtered.total,
        moves: finalize_lichess_moves(filtered.moves, pos.as_inner(), &queryable),
        recent_games: Some(finalize_lichess_games(filtered.recent_games, &queryable)),
        top_games: Some(finalize_lichess_games(filtered.top_games, &queryable)),
        opening,
    }))
}
<|endoftext|>


<commit_before>extern crate atty;
extern crate termcolor;
extern crate goblin;
extern crate metagoblin;
extern crate hexplay;
extern crate structopt;
#[macro_use]
extern crate structopt_derive;
extern crate rustc_demangle;
extern crate cpp_demangle;
extern crate scroll;
#[macro_use]
extern crate prettytable;
extern crate env_logger;

use goblin::{Hint, Object, elf, mach, archive};
use std::path::Path;
use std::fs::File;
use std::io::Read;

pub use goblin::error;
use structopt::StructOpt;

mod format;
mod format_elf;
use format_elf::Elf;
mod format_mach;
use format_mach::Mach;
mod format_archive;
use format_archive::Archive;
mod format_meta;
use format_meta::Meta;

#[derive(StructOpt, Debug, Clone)]
#[structopt(name = "bingrep", about = "bingrep - grepping through binaries since 2017")]
pub struct Opt {
    #[structopt(long = "ranges", help = "Print a high level overview of the file offset ranges in this binary")]
    ranges: bool,

    #[structopt(long = "hex", help = "Print a colored and semantically tagged hex table")]
    hex: bool,

    #[structopt(short = "d", long = "debug", help = "Print debug version of parse results")]
    debug: bool,

    #[structopt(short = "t", long = "truncate", help = "Truncate string results to X characters", default_value = "32")]
    truncate: usize,

    #[structopt(long = "color", help = "Forces coloring, even in files and pipes")]
    color: bool,

    #[structopt(short = "s", long = "search", help = "Search for string")]
    search: Option<String>,

    #[structopt(short = "D", long = "demangle", help = "Apply Rust/C++ demangling")]
    demangle: bool,

    #[structopt(help = "Binary file")]
    input: String,
}

fn run (opt: Opt) -> error::Result<()> {
    let path = Path::new(&opt.input);
    let mut fd = File::open(path)?;
    let peek = goblin::peek(&mut fd)?;
    if let Hint::Unknown(magic) = peek {
        println!("unknown magic: {:#x}", magic)
    } else {
        let bytes = { let mut v = Vec::new(); fd.read_to_end(&mut v)?; v };
        let object = Object::parse(&bytes)?;
        // we print the semantically tagged hex table
        if opt.hex || opt.ranges {
            let meta = Meta::new(&object, &bytes, opt.clone());
            if opt.hex {
                meta.print_hex()?;
            }
            else {
                meta.print_ranges()?;
            }
            return Ok(());
        }
        // otherwise we print the kind of object
        match object {
            Object::Elf(elf) => {
                if opt.debug {
                    println!("{:#?}", elf);
                } else {
                    let elf = Elf::new(elf, bytes.as_slice(), opt.clone());
                    if let Some(search) = opt.search {
                        elf.search(&search)?;
                    } else {
                        elf.print()?;
                    }
                }
            },
            Object::PE(pe) => {
                println!("pe: {:#?}", &pe);
            },
            Object::Mach(mach) => {
                match mach {
                    mach::Mach::Fat(multi) => {
                        for mach in &multi {
                            match mach {
                                Ok(binary) => {
                                    if opt.debug {
                                        println!("{:#?}", binary);
                                    } else {
                                        let mach = Mach(binary, opt.clone());
                                        mach.print()?;
                                    }
                                },
                                Err(err) => {
                                    println!("{}", err);
                                }
                            }
                        }
                    },
                    mach::Mach::Binary(binary) => {
                        if opt.debug {
                            println!("{:#?}", binary);
                        } else {
                            let mach = Mach(binary, opt.clone());
                            mach.print()?;
                        }
                    }
                }
            }
            Object::Archive(archive) => {
                if opt.debug {
                    println!("archive: {:#?}", &archive);
                } else {
                    let archive = Archive::new(archive, opt.clone());
                    archive.print()?;
                }
            },
            _ => unreachable!()
        }
    }
    Ok(())
}

pub fn main () {
    let opt = Opt::from_args();
    env_logger::init().unwrap();
    match run(opt) {
        Ok(()) => (),
        Err(err) => println!("{:#}", err)
    }
}
<commit_msg>archive: add extract flag<commit_after>extern crate atty;
extern crate termcolor;
extern crate goblin;
extern crate metagoblin;
extern crate hexplay;
extern crate structopt;
#[macro_use]
extern crate structopt_derive;
extern crate rustc_demangle;
extern crate cpp_demangle;
extern crate scroll;
#[macro_use]
extern crate prettytable;
extern crate env_logger;

use goblin::{Hint, Object, elf, mach, archive};
use std::path::Path;
use std::fs::File;
use std::io::Read;
use std::io::Write;

pub use goblin::error;
use structopt::StructOpt;

mod format;
mod format_elf;
use format_elf::Elf;
mod format_mach;
use format_mach::Mach;
mod format_archive;
use format_archive::Archive;
mod format_meta;
use format_meta::Meta;

#[derive(StructOpt, Debug, Clone)]
#[structopt(name = "bingrep", about = "bingrep - grepping through binaries since 2017")]
pub struct Opt {
    #[structopt(long = "extract", help = "Extract from an archive the object file which contains the given symbol")]
    extract: Option<String>,

    #[structopt(long = "ranges", help = "Print a high level overview of the file offset ranges in this binary")]
    ranges: bool,

    #[structopt(long = "hex", help = "Print a colored and semantically tagged hex table")]
    hex: bool,

    #[structopt(short = "d", long = "debug", help = "Print debug version of parse results")]
    debug: bool,

    #[structopt(short = "t", long = "truncate", help = "Truncate string results to X characters", default_value = "32")]
    truncate: usize,

    #[structopt(long = "color", help = "Forces coloring, even in files and pipes")]
    color: bool,

    #[structopt(short = "s", long = "search", help = "Search for string")]
    search: Option<String>,

    #[structopt(short = "D", long = "demangle", help = "Apply Rust/C++ demangling")]
    demangle: bool,

    #[structopt(help = "Binary file")]
    input: String,
}

fn run (opt: Opt) -> error::Result<()> {
    let path = Path::new(&opt.input);
    let mut fd = File::open(path)?;
    let peek = goblin::peek(&mut fd)?;
    if let Hint::Unknown(magic) = peek {
        println!("unknown magic: {:#x}", magic)
    } else {
        let bytes = { let mut v = Vec::new(); fd.read_to_end(&mut v)?; v };
        let object = Object::parse(&bytes)?;
        // we print the semantically tagged hex table
        if opt.hex || opt.ranges {
            let meta = Meta::new(&object, &bytes, opt.clone());
            if opt.hex {
                meta.print_hex()?;
            }
            else {
                meta.print_ranges()?;
            }
            return Ok(());
        }
        // otherwise we print the kind of object
        match object {
            Object::Elf(elf) => {
                if opt.debug {
                    println!("{:#?}", elf);
                } else {
                    let elf = Elf::new(elf, bytes.as_slice(), opt.clone());
                    if let Some(search) = opt.search {
                        elf.search(&search)?;
                    } else {
                        elf.print()?;
                    }
                }
            },
            Object::PE(pe) => {
                println!("pe: {:#?}", &pe);
            },
            Object::Mach(mach) => {
                match mach {
                    mach::Mach::Fat(multi) => {
                        for mach in &multi {
                            match mach {
                                Ok(binary) => {
                                    if opt.debug {
                                        println!("{:#?}", binary);
                                    } else {
                                        let mach = Mach(binary, opt.clone());
                                        mach.print()?;
                                    }
                                },
                                Err(err) => {
                                    println!("{}", err);
                                }
                            }
                        }
                    },
                    mach::Mach::Binary(binary) => {
                        if opt.debug {
                            println!("{:#?}", binary);
                        } else {
                            let mach = Mach(binary, opt.clone());
                            mach.print()?;
                        }
                    }
                }
            }
            Object::Archive(archive) => {
                if let Some(symbol) = opt.extract {
                    if let Some(member) = archive.member_of_symbol(&symbol) {
                        let bytes = archive.extract(member, &bytes)?;
                        let mut file = File::create(Path::new(member))?;
                        file.write(bytes)?;
                    } else {
                        // FIXME: return error
                        println!("No member contains {:?}", symbol);
                    }
                } else {
                    if opt.debug {
                        println!("archive: {:#?}", &archive);
                    } else {
                        let archive = Archive::new(archive, opt.clone());
                        archive.print()?;
                    }
                }
            },
            _ => unreachable!()
        }
    }
    Ok(())
}

pub fn main () {
    let opt = Opt::from_args();
    env_logger::init().unwrap();
    match run(opt) {
        Ok(()) => (),
        Err(err) => println!("{:#}", err)
    }
}
<|endoftext|>


<commit_before>#![allow(clippy::needless_range_loop)]

#[macro_use]
extern crate log;

use pixels::{wgpu::TextureFormat, Pixels, PixelsBuilder, SurfaceTexture};
use std::path::Path;
use std::time::{Duration, Instant};
use winit::{
    dpi::LogicalSize,
    event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    window::{Window, WindowBuilder},
};

mod cartridge;
mod cpu;
mod debug;
mod disasm;
mod gameboy;
mod interrupt;
mod joypad;
mod mem;
mod ram;
mod serial;
mod sound;
mod timer;
mod video;

//
// Video Output
//

struct VideoOut {
    window: Window,
    pixels: Pixels,
}

impl VideoOut {
    fn new(event_loop: &EventLoop<()>, scale: u32) -> VideoOut {
        let window_width = video::SCREEN_WIDTH as u32 * scale;
        let window_height = video::SCREEN_HEIGHT as u32 * scale;

        let size = LogicalSize::new(window_width, window_height);

        let window = WindowBuilder::new()
            .with_inner_size(size)
            .with_resizable(false)
            .build(event_loop)
            .unwrap();

        let size = window.inner_size();
        let texture = SurfaceTexture::new(size.width, size.height, &window);

        let pixels = PixelsBuilder::new(
            video::SCREEN_WIDTH as u32,
            video::SCREEN_HEIGHT as u32,
            texture,
        )
        .texture_format(TextureFormat::Bgra8UnormSrgb)
        .enable_vsync(false)
        .build()
        .unwrap();

        VideoOut { window, pixels }
    }

    fn blit(&mut self, pixels: &[u8]) {
        self.pixels.get_frame().copy_from_slice(pixels);
    }

    fn render(&mut self) {
        self.pixels.render().unwrap();
    }

    fn set_title(&mut self, title: &str) {
        self.window.set_title(title);
    }
}

fn keymap(code: VirtualKeyCode) -> Option<joypad::Button> {
    match code {
        VirtualKeyCode::Up => Some(joypad::Button::Up),
        VirtualKeyCode::Down => Some(joypad::Button::Down),
        VirtualKeyCode::Left => Some(joypad::Button::Left),
        VirtualKeyCode::Right => Some(joypad::Button::Right),
        VirtualKeyCode::Return => Some(joypad::Button::Start),
        VirtualKeyCode::RShift => Some(joypad::Button::Select),
        VirtualKeyCode::C => Some(joypad::Button::A),
        VirtualKeyCode::X => Some(joypad::Button::B),
        _ => None,
    }
}

#[derive(PartialEq)]
enum State {
    Paused,
    Running,
    WaitForSync,
    Step,
    Done,
}

fn main() {
    let args: Vec<_> = std::env::args().collect();
    if args.len() != 2 && !(args.len() == 3 && args[1] == "-d") {
        println!("Usage: {} [-d] rom.gb", args[0]);
        return;
    }

    let mut disassemble = false;
    let path = if args.len() == 2 {
        &args[1]
    } else {
        disassemble = true;
        &args[2]
    };

    let mut cart = match cartridge::Cartridge::from_path(Path::new(path)) {
        Ok(cart) => cart,
        Err(e) => panic!("I/O error: {}", e),
    };

    if disassemble {
        // Disassemble only
        let mut d = disasm::Disasm {
            mem: &mut cart,
            pc: 0,
        };
        while d.pc <= 0x7fff {
            let pc = d.pc;
            println!("${:04X}\t{}", pc, cpu::decode(&mut d));
        }
        return;
    }

    println!("Name: {}", cart.title);
    println!("Type: {}", cart.cartridge_type);

    let mut gameboy = gameboy::GameBoy::new(cart);

    let event_loop = EventLoop::new();

    let mut video_out = VideoOut::new(&event_loop, 4);
    video_out.set_title("Rustboy");

    let mut state = State::Paused;
    let mut debugger = debug::Debugger::new();

    let frame_duration =
        Duration::from_secs_f64(video::SCREEN_REFRESH_CYCLES as f64 / cpu::CYCLES_PER_SEC as f64);
    let mut frame_start = Instant::now();

    let mut last_fps_update = frame_start;
    let mut frames = 0;

    println!(
        "ft: {:?} / fps: {:.02}",
        frame_duration,
        1.0 / frame_duration.as_secs_f64()
    );

    event_loop.run(move |event, _, control_flow| {
        if let Event::RedrawRequested(_) = event {
            println!("RedrawRequested");
            video_out.render();
            return;
        }

        if let Event::WindowEvent {
            event: WindowEvent::CloseRequested,
            ..
        } = event
        {
            state = State::Done;
        }

        if let Event::WindowEvent {
            event:
                WindowEvent::KeyboardInput {
                    input:
                        KeyboardInput {
                            virtual_keycode: Some(keycode),
                            state: keystate,
                            ..
                        },
                    ..
                },
            ..
        } = event
        {
            if keystate == ElementState::Pressed && keycode == VirtualKeyCode::Escape {
                state = State::Paused;
            }

            if let Some(button) = keymap(keycode) {
                gameboy.set_button(button, keystate == ElementState::Pressed);
            }
        }

        if let Event::MainEventsCleared = event {
            if state == State::Done {
                *control_flow = ControlFlow::Exit;
                return;
            }

            // Debugger
            if state == State::Paused || state == State::Step {
                match debugger.prompt(gameboy.cpu()) {
                    debug::DebuggerCommand::Quit => state = State::Done,
                    debug::DebuggerCommand::Run => state = State::Running,
                    debug::DebuggerCommand::Step => state = State::Step,
                }
            }

            // Emulation loop
            if state == State::Running {
                let mut new_frame;
                loop {
                    new_frame = gameboy.step();

                    if debugger.should_break(gameboy.cpu()) {
                        state = State::Paused;
                        break;
                    }

                    if new_frame || state == State::Step {
                        break;
                    }
                }

                if new_frame {
                    video_out.blit(gameboy.pixels());
                    video_out.render();

                    state = State::WaitForSync;
                }
            }

            // Sync frame rate
            if state == State::WaitForSync {
                if frame_start.elapsed() >= frame_duration {
                    state = State::Running;

                    frame_start = Instant::now();

                    frames += 1;
                    if last_fps_update.elapsed() > Duration::from_secs(1) {
                        let fps = frames as f64 / last_fps_update.elapsed().as_secs_f64();
                        video_out.set_title(format!("Rustboy - {:.02} fps", fps).as_ref());
                        last_fps_update = frame_start;
                        frames = 0;
                    }
                }
            }
        }
    });
}
<commit_msg>Fixed clippy warnings<commit_after>#![allow(clippy::needless_range_loop)]

#[macro_use]
extern crate log;

use pixels::{wgpu::TextureFormat, Pixels, PixelsBuilder, SurfaceTexture};
use std::path::Path;
use std::time::{Duration, Instant};
use winit::{
    dpi::LogicalSize,
    event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    window::{Window, WindowBuilder},
};

mod cartridge;
mod cpu;
mod debug;
mod disasm;
mod gameboy;
mod interrupt;
mod joypad;
mod mem;
mod ram;
mod serial;
mod sound;
mod timer;
mod video;

//
// Video Output
//

struct VideoOut {
    window: Window,
    pixels: Pixels,
}

impl VideoOut {
    fn new(event_loop: &EventLoop<()>, scale: u32) -> VideoOut {
        let window_width = video::SCREEN_WIDTH as u32 * scale;
        let window_height = video::SCREEN_HEIGHT as u32 * scale;

        let size = LogicalSize::new(window_width, window_height);

        let window = WindowBuilder::new()
            .with_inner_size(size)
            .with_resizable(false)
            .build(event_loop)
            .unwrap();

        let size = window.inner_size();
        let texture = SurfaceTexture::new(size.width, size.height, &window);

        let pixels = PixelsBuilder::new(
            video::SCREEN_WIDTH as u32,
            video::SCREEN_HEIGHT as u32,
            texture,
        )
        .texture_format(TextureFormat::Bgra8UnormSrgb)
        .enable_vsync(false)
        .build()
        .unwrap();

        VideoOut { window, pixels }
    }

    fn blit(&mut self, pixels: &[u8]) {
        self.pixels.get_frame().copy_from_slice(pixels);
    }

    fn render(&mut self) {
        self.pixels.render().unwrap();
    }

    fn set_title(&mut self, title: &str) {
        self.window.set_title(title);
    }
}

fn keymap(code: VirtualKeyCode) -> Option<joypad::Button> {
    match code {
        VirtualKeyCode::Up => Some(joypad::Button::Up),
        VirtualKeyCode::Down => Some(joypad::Button::Down),
        VirtualKeyCode::Left => Some(joypad::Button::Left),
        VirtualKeyCode::Right => Some(joypad::Button::Right),
        VirtualKeyCode::Return => Some(joypad::Button::Start),
        VirtualKeyCode::RShift => Some(joypad::Button::Select),
        VirtualKeyCode::C => Some(joypad::Button::A),
        VirtualKeyCode::X => Some(joypad::Button::B),
        _ => None,
    }
}

#[derive(PartialEq)]
enum State {
    Paused,
    Running,
    WaitForSync,
    Step,
    Done,
}

fn main() {
    let args: Vec<_> = std::env::args().collect();
    if args.len() != 2 && !(args.len() == 3 && args[1] == "-d") {
        println!("Usage: {} [-d] rom.gb", args[0]);
        return;
    }

    let mut disassemble = false;
    let path = if args.len() == 2 {
        &args[1]
    } else {
        disassemble = true;
        &args[2]
    };

    let mut cart = match cartridge::Cartridge::from_path(Path::new(path)) {
        Ok(cart) => cart,
        Err(e) => panic!("I/O error: {}", e),
    };

    if disassemble {
        // Disassemble only
        let mut d = disasm::Disasm {
            mem: &mut cart,
            pc: 0,
        };
        while d.pc <= 0x7fff {
            let pc = d.pc;
            println!("${:04X}\t{}", pc, cpu::decode(&mut d));
        }
        return;
    }

    println!("Name: {}", cart.title);
    println!("Type: {}", cart.cartridge_type);

    let mut gameboy = gameboy::GameBoy::new(cart);

    let event_loop = EventLoop::new();

    let mut video_out = VideoOut::new(&event_loop, 4);
    video_out.set_title("Rustboy");

    let mut state = State::Paused;
    let mut debugger = debug::Debugger::new();

    let frame_duration =
        Duration::from_secs_f64(video::SCREEN_REFRESH_CYCLES as f64 / cpu::CYCLES_PER_SEC as f64);
    let mut frame_start = Instant::now();

    let mut last_fps_update = frame_start;
    let mut frames = 0;

    println!(
        "ft: {:?} / fps: {:.02}",
        frame_duration,
        1.0 / frame_duration.as_secs_f64()
    );

    event_loop.run(move |event, _, control_flow| {
        if let Event::RedrawRequested(_) = event {
            video_out.render();
            return;
        }

        if let Event::WindowEvent {
            event: WindowEvent::CloseRequested,
            ..
        } = event
        {
            state = State::Done;
        }

        if let Event::WindowEvent {
            event:
                WindowEvent::KeyboardInput {
                    input:
                        KeyboardInput {
                            virtual_keycode: Some(keycode),
                            state: keystate,
                            ..
                        },
                    ..
                },
            ..
        } = event
        {
            if keystate == ElementState::Pressed && keycode == VirtualKeyCode::Escape {
                state = State::Paused;
            }

            if let Some(button) = keymap(keycode) {
                gameboy.set_button(button, keystate == ElementState::Pressed);
            }
        }

        if let Event::MainEventsCleared = event {
            if state == State::Done {
                *control_flow = ControlFlow::Exit;
                return;
            }

            // Debugger
            if state == State::Paused || state == State::Step {
                match debugger.prompt(gameboy.cpu()) {
                    debug::DebuggerCommand::Quit => state = State::Done,
                    debug::DebuggerCommand::Run => state = State::Running,
                    debug::DebuggerCommand::Step => state = State::Step,
                }
            }

            // Emulation loop
            if state == State::Running {
                let mut new_frame;
                loop {
                    new_frame = gameboy.step();

                    if debugger.should_break(gameboy.cpu()) {
                        state = State::Paused;
                        break;
                    }

                    if new_frame || state == State::Step {
                        break;
                    }
                }

                if new_frame {
                    video_out.blit(gameboy.pixels());
                    video_out.render();

                    state = State::WaitForSync;
                }
            }

            // Sync frame rate
            if state == State::WaitForSync && frame_start.elapsed() >= frame_duration {
                state = State::Running;

                frame_start = Instant::now();

                frames += 1;

                if last_fps_update.elapsed() > Duration::from_secs(1) {
                    let fps = frames as f64 / last_fps_update.elapsed().as_secs_f64();
                    video_out.set_title(format!("Rustboy - {:.02} fps", fps).as_ref());
                    last_fps_update = frame_start;
                    frames = 0;
                }
            }
        }
    });
}
<|endoftext|>


<commit_before>mod vm;
mod command;
mod parse;
mod program;

use vm::VM;
use program::Program;
use std::io::Write;
use std::env;

fn main() {
    for argument in env::args() {
        println!("{}", argument);
    }

    let mut vm = VM::new();
    let mut program = Program::new();
    loop {
        print!("{}", if program.is_seeking {"... "} else {"bf> "});
        std::io::stdout().flush().unwrap();

        let mut line = String::new();
        let commands = match std::io::stdin().read_line(&mut line) {
            Ok(0) => break,
            Ok(_) => parse::parse(&line),
            Err(error) => panic!(error),
        };
        program.append(&commands);
        program.execute(&mut vm);
    }
}

fn repl (mut vm: VM, mut program: Program) -> () {
    loop {
        print!("{}", if program.is_seeking {"... "} else {"bf> "});
        std::io::stdout().flush().unwrap();

        let mut line = String::new();
        let commands = match std::io::stdin().read_line(&mut line) {
            Ok(0) => break,
            Ok(_) => parse::parse(&line),
            Err(error) => panic!(error),
        };
        program.append(&commands);
        program.execute(&mut vm);
    }
}
<commit_msg>Replace inline main logic with call to repl<commit_after>mod vm;
mod command;
mod parse;
mod program;

use vm::VM;
use program::Program;
use std::io::Write;
use std::env;

fn main() {
    for argument in env::args() {
        println!("{}", argument);
    }

    let vm = VM::new();
    let program = Program::new();
    repl(vm, program);
}

fn repl (mut vm: VM, mut program: Program) -> () {
    loop {
        print!("{}", if program.is_seeking {"... "} else {"bf> "});
        std::io::stdout().flush().unwrap();

        let mut line = String::new();
        let commands = match std::io::stdin().read_line(&mut line) {
            Ok(0) => break,
            Ok(_) => parse::parse(&line),
            Err(error) => panic!(error),
        };
        program.append(&commands);
        program.execute(&mut vm);
    }
}
<|endoftext|>