diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000..50910bd --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,11 @@ +install: + - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + - if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly + - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin + - rustc -V + - cargo -V + +build: false + +test_script: + - cargo test --locked diff --git a/.cargo-ok b/.cargo-ok new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4e30131 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/target +**/*.rs.bk +Cargo.lock +bin/ +pkg/ +wasm-pack.log diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7a91325 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,69 @@ +language: rust +sudo: false + +cache: cargo + +matrix: + include: + + # Builds with wasm-pack. + - rust: beta + env: RUST_BACKTRACE=1 + addons: + firefox: latest + chrome: stable + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f + script: + - cargo generate --git . --name testing + # Having a broken Cargo.toml (in that it has curlies in fields) anywhere + # in any of our parent dirs is problematic. + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - wasm-pack build + - wasm-pack test --chrome --firefox --headless + + # Builds on nightly. + - rust: nightly + env: RUST_BACKTRACE=1 + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - rustup target add wasm32-unknown-unknown + script: + - cargo generate --git . --name testing + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - cargo check + - cargo check --target wasm32-unknown-unknown + - cargo check --no-default-features + - cargo check --target wasm32-unknown-unknown --no-default-features + - cargo check --no-default-features --features console_error_panic_hook + - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook + - cargo check --no-default-features --features "console_error_panic_hook wee_alloc" + - cargo check --target wasm32-unknown-unknown --no-default-features --features "console_error_panic_hook wee_alloc" + + # Builds on beta. + - rust: beta + env: RUST_BACKTRACE=1 + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - rustup target add wasm32-unknown-unknown + script: + - cargo generate --git . --name testing + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - cargo check + - cargo check --target wasm32-unknown-unknown + - cargo check --no-default-features + - cargo check --target wasm32-unknown-unknown --no-default-features + - cargo check --no-default-features --features console_error_panic_hook + - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook + # Note: no enabling the `wee_alloc` feature here because it requires + # nightly for now. diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..447e2f6 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "libenchcrack" +description = "Code for cracking minecraft's XP seed" +version = "0.1.0" +authors = ["ImUrX "] +edition = "2018" +license = "MIT" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +default = ["console_error_panic_hook"] + +[dependencies] +wasm-bindgen = "0.2.63" +console_error_panic_hook = { version = "0.1.6", optional = true } +wee_alloc = "0.4.5" + +[dev-dependencies] +wasm-bindgen-test = "0.3.13" + +[dependencies.web-sys] +version = "0.3.44" +features = [ + "console" +] + +[profile.release] +# Tell `rustc` to optimize for small code size. +opt-level = "s" +lto = true diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ff73144 --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2018 ImUrX + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6cb9820 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# libenchcrack diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..504a924 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,78 @@ +#![no_std] + +extern crate alloc; +pub mod utils; + +use crate::utils::Rand; +use wasm_bindgen::prelude::*; +use core::ops::Range; +use alloc::vec::Vec; + +// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global +// allocator. +#[cfg(feature = "wee_alloc")] +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + +#[wasm_bindgen] +pub struct Cracker { + possible_seeds: Vec, + start_size: Range, + thread_id: usize, + threads: usize, + rng: Rand +} + +#[wasm_bindgen] +impl Cracker { + + #[wasm_bindgen(constructor)] + pub fn new(thread_id: usize, threads: usize) -> Cracker { + let size = u32::MAX / threads as u32; + let start = (i32::MIN as i64 + (size * thread_id as u32) as i64) as i32; + Cracker { + possible_seeds: Vec::with_capacity((80e6 as usize) / threads), + start_size: + start..(start as i64 + size as i64) as i32 + + if thread_id == threads - 1 { thread_id as i32 } else { 0 } , + rng: Rand::new(), + thread_id, threads + } + } + + #[wasm_bindgen] + pub fn reset(&mut self) { + self.possible_seeds.clear(); + } + + #[wasm_bindgen] + pub fn possible_seeds(&self) -> usize { + self.possible_seeds.len() + } + + #[wasm_bindgen] + pub fn first_input(&mut self, shelves: i32, slot1: i32, slot2: i32, slot3: i32, + shelves_s: i32, slot_s1: i32, slot_s2: i32, slot_s3: i32) { + for seed in self.start_size.clone() { + if self.rng.verify_seed(seed, shelves, slot1, slot2, slot3) + && self.rng.verify_seed(seed, shelves_s, slot_s1, slot_s2, slot_s3) { self.possible_seeds.push(seed); } + } + + if self.thread_id == self.threads - 1 { + if self.rng.verify_seed(i32::MAX, shelves, slot1, slot2, slot3) + && self.rng.verify_seed(i32::MAX, shelves_s, slot_s1, slot_s2, slot_s3) { self.possible_seeds.push(i32::MAX); } + } + } + + #[wasm_bindgen] + pub fn add_input(&mut self, shelves: i32, slot1: i32, slot2: i32, slot3: i32) { + let rng = &mut self.rng; + self.possible_seeds.iter().filter(|&x| { + !rng.verify_seed(*x, shelves, slot1, slot2, slot3) + }).for_each(drop); + } + + pub fn contains(&self, x: i32) -> bool { + self.possible_seeds.iter().any(|&y| x == y) + } +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..d9202f2 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,94 @@ +extern crate web_sys; +use web_sys::console; +use core::cmp; +use core::num::Wrapping; + +const MULT: i64 = 0x5DEECE66D; +const MASK: i64 = (1 << 48) - 1; + +pub fn set_panic_hook() { + // When the `console_error_panic_hook` feature is enabled, we can call the + // `set_panic_hook` function at least once during initialization, and then + // we will get better error messages if our code ever panics. + // + // For more details see + // https://github.com/rustwasm/console_error_panic_hook#readme + #[cfg(feature = "console_error_panic_hook")] + console_error_panic_hook::set_once(); +} + + +pub struct Timer<'a> { + name: &'a str, +} + +impl<'a> Timer<'a> { + pub fn new(name: &'a str) -> Timer<'a> { + console::time_with_label(name); + Timer { name } + } +} + +impl<'a> Drop for Timer<'a> { + fn drop(&mut self) { + console::time_end_with_label(self.name); + } +} + + +pub struct Rand { + pub seed: i64, +} + +impl Rand { + pub fn new() -> Rand { + Rand { seed: 0 } + } + + pub fn set_seed(&mut self, seed: i64) { + self.seed = (seed ^ MULT) & MASK; + } + + pub fn next(&mut self) -> i32 { + self.seed = (Wrapping(self.seed) * Wrapping(MULT) + Wrapping(0xB)).0 & MASK; + (self.seed as u64 >> 17) as i32 + } + + pub fn next_int(&mut self, bound: Wrapping) -> i32 { + let mut r = Wrapping(self.next()); + let m = bound - Wrapping(1); + if (bound & m) == Wrapping(0) { + r = Wrapping(((bound.0 as i64 * r.0 as i64) >> 31) as i32); + } else { + let mut u = r; + while {r = u % bound; u - r + m} < Wrapping(0) { + u = Wrapping(self.next()); + } + } + r.0 + } + + fn generic_enchantibility(&mut self, shelves: i32) -> i32 { + let first = self.next_int(Wrapping(8)); + let second = self.next_int(Wrapping(shelves + 1)); + first + 1 + (shelves >> 1) + second + } + + pub fn levels_slot1(&mut self, shelves: i32) -> i32 { + let slot1 = self.generic_enchantibility(shelves) / 3; + if slot1 < 1 { 1 } else { slot1 } + } + + pub fn levels_slot2(&mut self, shelves: i32) -> i32 { + (self.generic_enchantibility(shelves) * 2 / 3) + 1 + } + + pub fn levels_slot3(&mut self, shelves: i32) -> i32 { + cmp::max(self.generic_enchantibility(shelves), shelves * 2) + } + + pub fn verify_seed(&mut self, seed: i32, shelves: i32, slot1: i32, slot2: i32, slot3: i32) -> bool { + self.set_seed(seed as i64); + self.levels_slot1(shelves) == slot1 && self.levels_slot2(shelves) == slot2 && self.levels_slot3(shelves) == slot3 + } +} diff --git a/tests/web.rs b/tests/web.rs new file mode 100644 index 0000000..c71640b --- /dev/null +++ b/tests/web.rs @@ -0,0 +1,73 @@ +//! Test suite for the Web and headless browsers. + +#![cfg(target_arch = "wasm32")] + +extern crate libenchcrack; +extern crate wasm_bindgen_test; +use wasm_bindgen_test::*; +use libenchcrack::Cracker; +use libenchcrack::utils::{Rand, Timer}; +use std::num::Wrapping; +use std::panic; + +wasm_bindgen_test_configure!(run_in_browser); + +#[wasm_bindgen_test] +fn rng_seed() { + let mut rng = Rand::new(); + rng.set_seed(0); + assert_eq!(rng.seed, 25214903917); + rng.set_seed(150123); + assert_eq!(rng.seed, 25215020038); + rng.set_seed(-500); + assert_eq!(rng.seed, 281449761806433); +} + +#[wasm_bindgen_test] +fn rng_next() { + let mut rng = Rand::new(); + rng.set_seed(0); + assert_eq!(rng.next(), 1569741360); + rng.set_seed(150123); + assert_eq!(rng.next(), 286134746); + rng.set_seed(-500); + assert_eq!(rng.next(), 518875706); +} + +#[wasm_bindgen_test] +fn rng_next_int() { + let mut rng = Rand::new(); + rng.set_seed(0); + assert_eq!(rng.next_int(Wrapping(8)), 5); + assert_eq!(rng.next_int(Wrapping(8)), 6); + rng.set_seed(1949457528); + assert_eq!(rng.next_int(Wrapping(8)), 3); + assert_eq!(rng.next_int(Wrapping(5)), 0); + rng.set_seed(-500); + assert_eq!(rng.next_int(Wrapping(15)), 11); +} + +#[wasm_bindgen_test] +fn slots() { + let mut rng = Rand::new(); + rng.set_seed(1949457528); + assert_eq!(rng.levels_slot1(4), 2); + assert_eq!(rng.levels_slot2(4), 7); + assert_eq!(rng.levels_slot3(4), 8); + + rng.set_seed(1949457528); + assert_eq!(rng.levels_slot1(15), 6); + assert_eq!(rng.levels_slot2(15), 9); + assert_eq!(rng.levels_slot3(15), 30); +} + +#[wasm_bindgen_test] +fn cracking() { + let mut cracker = Cracker::new(0, 1); + + { + let _timer = Timer::new("first input"); + cracker.first_input(4, 2, 7, 8, 15, 6, 9, 30); + } + assert!(cracker.contains(1949457528)); +}