Initial

Tue, 08 Dec 2020 23:44:49 -0700

author
Glitchvid <Glitchvid@glitchvid.com>
date
Tue, 08 Dec 2020 23:44:49 -0700
changeset 0
79c63b4440ee
child 1
ad88152b3c76

Initial

Cargo.lock file | annotate | diff | comparison | revisions
Cargo.toml file | annotate | diff | comparison | revisions
src/main.rs file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Cargo.lock	Tue Dec 08 23:44:49 2020 -0700
@@ -0,0 +1,56 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "aho-corasick"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "memchr"
+version = "2.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
+
+[[package]]
+name = "regex"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+ "thread_local",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
+
+[[package]]
+name = "steamid"
+version = "0.1.0"
+dependencies = [
+ "regex",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
+dependencies = [
+ "lazy_static",
+]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Cargo.toml	Tue Dec 08 23:44:49 2020 -0700
@@ -0,0 +1,10 @@
+[package]
+name = "steamid"
+version = "0.1.0"
+authors = ["glitchvid"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+regex = "1"
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main.rs	Tue Dec 08 23:44:49 2020 -0700
@@ -0,0 +1,310 @@
+use std::env;
+use std::convert::TryFrom;
+use regex::Regex;
+
+const REGEX_STEAMID2: &str = r"^STEAM_([0-5]):([01]):(\d+$)";
+const REGEX_STEAMID3: &str = r"^\[(.):([01]):(\d+)\]$";
+
+/* Valve SteamID Format:
+ *  A SteamID is just a packed 64-bit unsigned integer!
+ * 
+ * It consists of five parts, from least to most significant bit:
+ *  1. Authentication Server    - 1 bit     (1)
+ *  2. Account Number           - 31 bits   (32)
+ *  3. Instance                 - 20 bits   (52)
+ *  4. Account Type             - 4 bits    (56)
+ *  5. Universe                 - 8 bits    (64)
+ * 
+ * This can be visualized like so:
+ *  1. _______________________________________________________________X
+ *  2. ________________________________XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_
+ *  3. ____________XXXXXXXXXXXXXXXXXXXX________________________________
+ *  4. ________XXXX____________________________________________________
+ *  5. XXXXXXXX________________________________________________________
+ * 
+ * There are multiple ways to express a SteamID, some are lossy.
+ *  A. steamID64        - (1)+(2)+(3)+(4)+(5)
+ *  B. steamID2         - STEAM_(5):(1):(2)
+ *  C. steamID3         - [(4):(5):(1)+(2)]
+*/
+
+/*
+ * Instance
+ *  The Instance field nominally holds what 'instance' the steamID is, however
+ *  when specifying a chatroom, the last 8 bits define the "type" of chatroom.
+ * This can be visualized like so:
+ *  ____________ZZZZZZZZXXXXXXXXXXXX
+*/
+
+fn account_type_to_char(account_type: u8, instance: Option<u32>) -> char {
+    match account_type {
+        0   => 'I', // Invalid
+        1   => 'U', // Individual (Profiles)
+        2   => 'M', // Multiseat
+        3   => 'G', // GameServer
+        4   => 'A', // AnonGameServer
+        5   => 'P', // Pending
+        6   => 'C', // ContentServer
+        7   => 'g', // Clan (Groups)
+        8   =>  match instance.unwrap_or(0) >> 12 & 255 { // EChatSteamIDInstanceFlags (Shifted 12 right)
+                    1   => 'T', // MatchMaking Lobby
+                    2   => 'L', // Lobby
+                    4   => 'c', // Clan Chat
+                    _   => 'c', // Clan Chat (Default)
+                }
+        //9   => '',// ConsoleUser
+        10  => 'a', // AnonUser
+        _   => 'I', // Invalid (Default)
+    }
+}
+
+#[derive(Debug)]
+enum SteamIDAccountType {
+    Invalid,
+    Individual,
+    Multiseat,
+    GameServer,
+    AnonGameServer,
+    Pending,
+    ContentServer,
+    Clan,
+    Chat,
+    ConsoleUser,
+    AnonUser,
+}
+
+impl SteamIDAccountType {
+    fn to_char(self, instance: Option<u32>) -> char {
+        match self {
+            SteamIDAccountType::Invalid         => 'I',
+            SteamIDAccountType::Individual      => 'U',
+            SteamIDAccountType::Multiseat       => 'M',
+            SteamIDAccountType::GameServer      => 'G',
+            SteamIDAccountType::AnonGameServer  => 'A',
+            SteamIDAccountType::Pending         => 'P',
+            SteamIDAccountType::ContentServer   => 'C',
+            SteamIDAccountType::Clan            => 'g',
+            SteamIDAccountType::Chat            =>
+                    match instance.unwrap_or(0) >> 12 & 255 { // EChatSteamIDInstanceFlags (Shifted 12 right)
+                        1   => 'T', // MatchMaking Lobby
+                        2   => 'L', // Lobby
+                        4   => 'c', // Clan Chat
+                        _   => 'c', // Clan Chat (Default)
+                    }
+            SteamIDAccountType::ConsoleUser     => 'I',
+            SteamIDAccountType::AnonUser        => 'a',
+        }
+    }
+    fn to_int(self) -> u32 {
+        match self {
+            SteamIDAccountType::Invalid         => 0,
+            SteamIDAccountType::Individual      => 1,
+            SteamIDAccountType::Multiseat       => 2,
+            SteamIDAccountType::GameServer      => 3,
+            SteamIDAccountType::AnonGameServer  => 4,
+            SteamIDAccountType::Pending         => 5,
+            SteamIDAccountType::ContentServer   => 6,
+            SteamIDAccountType::Clan            => 7,
+            SteamIDAccountType::Chat            => 8,
+            SteamIDAccountType::ConsoleUser     => 9,
+            SteamIDAccountType::AnonUser        => 10,
+        }
+    }
+    fn from_char(account_type: char) -> SteamIDAccountType {
+        match account_type {
+            'I'   => SteamIDAccountType::Invalid,
+            'U'   => SteamIDAccountType::Individual,
+            'M'   => SteamIDAccountType::Multiseat,
+            'G'   => SteamIDAccountType::GameServer,
+            'A'   => SteamIDAccountType::AnonGameServer,
+            'P'   => SteamIDAccountType::Pending,
+            'C'   => SteamIDAccountType::ContentServer,
+            'g'   => SteamIDAccountType::Clan,
+            'c'   => SteamIDAccountType::Chat,
+            'T'   => SteamIDAccountType::Chat,
+            'L'   => SteamIDAccountType::Chat,
+            'a'   => SteamIDAccountType::AnonUser,
+            _     => SteamIDAccountType::Invalid,
+        }
+    }
+    fn from_int(account_type: u8) -> SteamIDAccountType {
+        match account_type {
+            0   => SteamIDAccountType::Invalid,
+            1   => SteamIDAccountType::Individual,
+            2   => SteamIDAccountType::Multiseat,
+            3   => SteamIDAccountType::GameServer,
+            4   => SteamIDAccountType::AnonGameServer,
+            5   => SteamIDAccountType::Pending,
+            6   => SteamIDAccountType::ContentServer,
+            7   => SteamIDAccountType::Clan,
+            8   => SteamIDAccountType::Chat,
+            9   => SteamIDAccountType::ConsoleUser,
+            10  => SteamIDAccountType::AnonUser,
+            _   => SteamIDAccountType::Invalid,
+        }
+    }
+}
+
+#[derive(Debug, PartialEq)]
+enum SteamIDType{
+    SteamID64,
+    SteamID2,
+    SteamID3,
+}
+
+fn string_to_steamid_type(steamid: &str) -> Result<SteamIDType, &str> {
+    if steamid.parse::<u64>().is_ok() {
+        return Ok(SteamIDType::SteamID64)
+    }
+    let steamid2 = Regex::new(REGEX_STEAMID2).unwrap();
+    if steamid2.is_match(steamid) {
+        return Ok(SteamIDType::SteamID2)
+    }
+    
+    let steamid3 = Regex::new(REGEX_STEAMID3).unwrap();
+    if steamid3.is_match(steamid) {
+        return Ok(SteamIDType::SteamID3)
+    }
+    Err("Unable to parse to any SteamID Format.")
+}
+
+fn steamid2_to_steamid64( steamid2: &str) -> u64 {
+    let regex = Regex::new(REGEX_STEAMID2).unwrap();
+    let captures = regex.captures(steamid2).unwrap();
+
+    let universe = captures.get(1).unwrap().as_str().parse::<u64>().unwrap_or(1);
+    let auth_server = captures.get(2).unwrap().as_str().parse::<u64>().unwrap();
+    let account_id = captures.get(3).unwrap().as_str().parse::<u64>().unwrap();
+
+    let steam64 = (universe << 56) | auth_server | (account_id << 1) | 76561197960265728;
+    steam64
+}
+
+fn steamid3_to_steamid64( steamid3: &str) -> u64 {
+    let regex = Regex::new(REGEX_STEAMID3).unwrap();
+    let captures = regex.captures(steamid3).unwrap();
+
+    let account_type = captures.get(1).unwrap().as_str().parse::<char>().unwrap_or('I');
+    let account_type = u64::from(SteamIDAccountType::to_int(SteamIDAccountType::from_char(account_type)));
+
+    let universe = captures.get(2).unwrap().as_str().parse::<u64>().unwrap_or(1);
+    let account_id = captures.get(3).unwrap().as_str().parse::<u64>().unwrap();
+
+    let steam64 = (account_type << 52) | (universe << 56) | account_id ;
+    steam64
+}
+
+fn string_to_steamid64( input: &str) -> Result<u64, &str> {
+    let steam_type = string_to_steamid_type(input);
+    match steam_type {
+        Ok(steam_type) => {
+            match steam_type {
+                SteamIDType::SteamID64 => return Ok(input.parse::<u64>().unwrap()),
+                SteamIDType::SteamID2 => return Ok(steamid2_to_steamid64(input)),
+                SteamIDType::SteamID3 => return Ok(steamid3_to_steamid64(input)),
+            }
+        }
+        Err(steam_type) => return Err(steam_type)
+    }
+}
+
+struct SteamID {
+    account_id: u32,
+    account_instance: u32,
+    account_type: u8,
+    account_universe: u8,
+}
+
+impl SteamID {
+    fn new() -> SteamID {
+        SteamID {
+            account_id: 0,
+            account_instance:  1,
+            account_type: 1,
+            account_universe: 1,
+        }
+    }
+    fn set_steamid64(&mut self, steamid_64: u64) {
+        self.account_id = u32::try_from(steamid_64 & 4294967295).unwrap_or(1);
+        self.account_instance =  u32::try_from(steamid_64 >> 32 & 1048575).unwrap_or(1);
+        self.account_type = u8::try_from(steamid_64 >> 52 & 15).unwrap_or(1);
+        self.account_universe = u8::try_from(steamid_64 >> 56 & 15).unwrap_or(1);
+    }
+    fn get_steamid64(&self) -> u64 {
+        u64::from(self.account_id) |
+        u64::from(self.account_instance) << 32 |
+        u64::from(self.account_type) << 52 |
+        u64::from(self.account_universe)  << 56
+    }
+    fn get_steamid2(&self) -> String {
+        let authserver: u32 = self.account_id & 1; // Ideally we'd cast this to a bool and convert that to a 0 or 1 later.
+        let accountid: u32 = (self.account_id >> 1) & 2147483647;
+        format!("STEAM_{}:{}:{}", self.account_universe, authserver, accountid)
+    }
+    fn get_steamid3(&self) -> String {
+        let type_char: char = account_type_to_char(self.account_type, Some(self.account_instance));
+        format!("[{}:{}:{}]", type_char, self.account_universe, self.account_id)
+    }
+}
+
+
+
+fn main() {
+    // Gather our CLI arguments
+    let args: Vec<String> = env::args().collect();
+
+    //println!("Args: {:?}", args);
+    //println!("Len: {}", args.len());
+   
+    // Dumb check, make sure they even tried providing a SteamID
+    if args.len() < 2 {
+        println!("No IDs provided!");
+        std::process::exit(-1);
+    }
+
+    // Process all of our passed strings
+    for i in 1..args.len() {
+        let input = &args[i];
+        let steam_type = string_to_steamid_type(input);
+        match steam_type {
+            Ok(steam_type) => {
+                println!("Interpreting as {:?}", steam_type );
+                match steam_type {
+                    SteamIDType::SteamID64 => {
+                        let mut steamid_object = SteamID::new();
+                        steamid_object.set_steamid64(input.parse::<u64>().expect("SteamID64 Not a Number!"));
+                        println!("steamID64:\t{}", steamid_object.get_steamid64());
+                        println!("steamID:  \t{}", steamid_object.get_steamid2());
+                        println!("steamID3: \t{}", steamid_object.get_steamid3());
+                    }
+                    SteamIDType::SteamID2 => {
+                        let mut steamid_object = SteamID::new();
+                        steamid_object.set_steamid64(steamid2_to_steamid64(input));
+                        println!("steamID64:\t{}", steamid_object.get_steamid64());
+                        println!("steamID:  \t{}", steamid_object.get_steamid2());
+                        println!("steamID3: \t{}", steamid_object.get_steamid3());
+                    }
+                    SteamIDType::SteamID3 => {
+                        let mut steamid_object = SteamID::new();
+                        steamid_object.set_steamid64(steamid3_to_steamid64(input));
+                        println!("steamID64:\t{}", steamid_object.get_steamid64());
+                        println!("steamID:  \t{}", steamid_object.get_steamid2());
+                        println!("steamID3: \t{}", steamid_object.get_steamid3());
+                    }
+                }
+            }
+            Err(_steam_type) => {
+                println!("Unable to interpret {}", input);
+            }
+        }
+        println!("");
+    }
+
+    // println!("Generating Alias SteamID64s...");
+    // thread::sleep(time::Duration::from_secs(1));
+    // for n in 1..1048575 {
+    //     let instance: u64 = n << 32;
+    //     let newsteam64 = (steamid64 & 18442240478377148415) | instance;
+    //     println!("http://steamcommunity.com/profiles/{}", newsteam64);
+    // }
+}

mercurial