src/main.rs

changeset 0
79c63b4440ee
child 2
474f76259cf0
equal deleted inserted replaced
-1:000000000000 0:79c63b4440ee
1 use std::env;
2 use std::convert::TryFrom;
3 use regex::Regex;
4
5 const REGEX_STEAMID2: &str = r"^STEAM_([0-5]):([01]):(\d+$)";
6 const REGEX_STEAMID3: &str = r"^\[(.):([01]):(\d+)\]$";
7
8 /* Valve SteamID Format:
9 * A SteamID is just a packed 64-bit unsigned integer!
10 *
11 * It consists of five parts, from least to most significant bit:
12 * 1. Authentication Server - 1 bit (1)
13 * 2. Account Number - 31 bits (32)
14 * 3. Instance - 20 bits (52)
15 * 4. Account Type - 4 bits (56)
16 * 5. Universe - 8 bits (64)
17 *
18 * This can be visualized like so:
19 * 1. _______________________________________________________________X
20 * 2. ________________________________XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_
21 * 3. ____________XXXXXXXXXXXXXXXXXXXX________________________________
22 * 4. ________XXXX____________________________________________________
23 * 5. XXXXXXXX________________________________________________________
24 *
25 * There are multiple ways to express a SteamID, some are lossy.
26 * A. steamID64 - (1)+(2)+(3)+(4)+(5)
27 * B. steamID2 - STEAM_(5):(1):(2)
28 * C. steamID3 - [(4):(5):(1)+(2)]
29 */
30
31 /*
32 * Instance
33 * The Instance field nominally holds what 'instance' the steamID is, however
34 * when specifying a chatroom, the last 8 bits define the "type" of chatroom.
35 * This can be visualized like so:
36 * ____________ZZZZZZZZXXXXXXXXXXXX
37 */
38
39 fn account_type_to_char(account_type: u8, instance: Option<u32>) -> char {
40 match account_type {
41 0 => 'I', // Invalid
42 1 => 'U', // Individual (Profiles)
43 2 => 'M', // Multiseat
44 3 => 'G', // GameServer
45 4 => 'A', // AnonGameServer
46 5 => 'P', // Pending
47 6 => 'C', // ContentServer
48 7 => 'g', // Clan (Groups)
49 8 => match instance.unwrap_or(0) >> 12 & 255 { // EChatSteamIDInstanceFlags (Shifted 12 right)
50 1 => 'T', // MatchMaking Lobby
51 2 => 'L', // Lobby
52 4 => 'c', // Clan Chat
53 _ => 'c', // Clan Chat (Default)
54 }
55 //9 => '',// ConsoleUser
56 10 => 'a', // AnonUser
57 _ => 'I', // Invalid (Default)
58 }
59 }
60
61 #[derive(Debug)]
62 enum SteamIDAccountType {
63 Invalid,
64 Individual,
65 Multiseat,
66 GameServer,
67 AnonGameServer,
68 Pending,
69 ContentServer,
70 Clan,
71 Chat,
72 ConsoleUser,
73 AnonUser,
74 }
75
76 impl SteamIDAccountType {
77 fn to_char(self, instance: Option<u32>) -> char {
78 match self {
79 SteamIDAccountType::Invalid => 'I',
80 SteamIDAccountType::Individual => 'U',
81 SteamIDAccountType::Multiseat => 'M',
82 SteamIDAccountType::GameServer => 'G',
83 SteamIDAccountType::AnonGameServer => 'A',
84 SteamIDAccountType::Pending => 'P',
85 SteamIDAccountType::ContentServer => 'C',
86 SteamIDAccountType::Clan => 'g',
87 SteamIDAccountType::Chat =>
88 match instance.unwrap_or(0) >> 12 & 255 { // EChatSteamIDInstanceFlags (Shifted 12 right)
89 1 => 'T', // MatchMaking Lobby
90 2 => 'L', // Lobby
91 4 => 'c', // Clan Chat
92 _ => 'c', // Clan Chat (Default)
93 }
94 SteamIDAccountType::ConsoleUser => 'I',
95 SteamIDAccountType::AnonUser => 'a',
96 }
97 }
98 fn to_int(self) -> u32 {
99 match self {
100 SteamIDAccountType::Invalid => 0,
101 SteamIDAccountType::Individual => 1,
102 SteamIDAccountType::Multiseat => 2,
103 SteamIDAccountType::GameServer => 3,
104 SteamIDAccountType::AnonGameServer => 4,
105 SteamIDAccountType::Pending => 5,
106 SteamIDAccountType::ContentServer => 6,
107 SteamIDAccountType::Clan => 7,
108 SteamIDAccountType::Chat => 8,
109 SteamIDAccountType::ConsoleUser => 9,
110 SteamIDAccountType::AnonUser => 10,
111 }
112 }
113 fn from_char(account_type: char) -> SteamIDAccountType {
114 match account_type {
115 'I' => SteamIDAccountType::Invalid,
116 'U' => SteamIDAccountType::Individual,
117 'M' => SteamIDAccountType::Multiseat,
118 'G' => SteamIDAccountType::GameServer,
119 'A' => SteamIDAccountType::AnonGameServer,
120 'P' => SteamIDAccountType::Pending,
121 'C' => SteamIDAccountType::ContentServer,
122 'g' => SteamIDAccountType::Clan,
123 'c' => SteamIDAccountType::Chat,
124 'T' => SteamIDAccountType::Chat,
125 'L' => SteamIDAccountType::Chat,
126 'a' => SteamIDAccountType::AnonUser,
127 _ => SteamIDAccountType::Invalid,
128 }
129 }
130 fn from_int(account_type: u8) -> SteamIDAccountType {
131 match account_type {
132 0 => SteamIDAccountType::Invalid,
133 1 => SteamIDAccountType::Individual,
134 2 => SteamIDAccountType::Multiseat,
135 3 => SteamIDAccountType::GameServer,
136 4 => SteamIDAccountType::AnonGameServer,
137 5 => SteamIDAccountType::Pending,
138 6 => SteamIDAccountType::ContentServer,
139 7 => SteamIDAccountType::Clan,
140 8 => SteamIDAccountType::Chat,
141 9 => SteamIDAccountType::ConsoleUser,
142 10 => SteamIDAccountType::AnonUser,
143 _ => SteamIDAccountType::Invalid,
144 }
145 }
146 }
147
148 #[derive(Debug, PartialEq)]
149 enum SteamIDType{
150 SteamID64,
151 SteamID2,
152 SteamID3,
153 }
154
155 fn string_to_steamid_type(steamid: &str) -> Result<SteamIDType, &str> {
156 if steamid.parse::<u64>().is_ok() {
157 return Ok(SteamIDType::SteamID64)
158 }
159 let steamid2 = Regex::new(REGEX_STEAMID2).unwrap();
160 if steamid2.is_match(steamid) {
161 return Ok(SteamIDType::SteamID2)
162 }
163
164 let steamid3 = Regex::new(REGEX_STEAMID3).unwrap();
165 if steamid3.is_match(steamid) {
166 return Ok(SteamIDType::SteamID3)
167 }
168 Err("Unable to parse to any SteamID Format.")
169 }
170
171 fn steamid2_to_steamid64( steamid2: &str) -> u64 {
172 let regex = Regex::new(REGEX_STEAMID2).unwrap();
173 let captures = regex.captures(steamid2).unwrap();
174
175 let universe = captures.get(1).unwrap().as_str().parse::<u64>().unwrap_or(1);
176 let auth_server = captures.get(2).unwrap().as_str().parse::<u64>().unwrap();
177 let account_id = captures.get(3).unwrap().as_str().parse::<u64>().unwrap();
178
179 let steam64 = (universe << 56) | auth_server | (account_id << 1) | 76561197960265728;
180 steam64
181 }
182
183 fn steamid3_to_steamid64( steamid3: &str) -> u64 {
184 let regex = Regex::new(REGEX_STEAMID3).unwrap();
185 let captures = regex.captures(steamid3).unwrap();
186
187 let account_type = captures.get(1).unwrap().as_str().parse::<char>().unwrap_or('I');
188 let account_type = u64::from(SteamIDAccountType::to_int(SteamIDAccountType::from_char(account_type)));
189
190 let universe = captures.get(2).unwrap().as_str().parse::<u64>().unwrap_or(1);
191 let account_id = captures.get(3).unwrap().as_str().parse::<u64>().unwrap();
192
193 let steam64 = (account_type << 52) | (universe << 56) | account_id ;
194 steam64
195 }
196
197 fn string_to_steamid64( input: &str) -> Result<u64, &str> {
198 let steam_type = string_to_steamid_type(input);
199 match steam_type {
200 Ok(steam_type) => {
201 match steam_type {
202 SteamIDType::SteamID64 => return Ok(input.parse::<u64>().unwrap()),
203 SteamIDType::SteamID2 => return Ok(steamid2_to_steamid64(input)),
204 SteamIDType::SteamID3 => return Ok(steamid3_to_steamid64(input)),
205 }
206 }
207 Err(steam_type) => return Err(steam_type)
208 }
209 }
210
211 struct SteamID {
212 account_id: u32,
213 account_instance: u32,
214 account_type: u8,
215 account_universe: u8,
216 }
217
218 impl SteamID {
219 fn new() -> SteamID {
220 SteamID {
221 account_id: 0,
222 account_instance: 1,
223 account_type: 1,
224 account_universe: 1,
225 }
226 }
227 fn set_steamid64(&mut self, steamid_64: u64) {
228 self.account_id = u32::try_from(steamid_64 & 4294967295).unwrap_or(1);
229 self.account_instance = u32::try_from(steamid_64 >> 32 & 1048575).unwrap_or(1);
230 self.account_type = u8::try_from(steamid_64 >> 52 & 15).unwrap_or(1);
231 self.account_universe = u8::try_from(steamid_64 >> 56 & 15).unwrap_or(1);
232 }
233 fn get_steamid64(&self) -> u64 {
234 u64::from(self.account_id) |
235 u64::from(self.account_instance) << 32 |
236 u64::from(self.account_type) << 52 |
237 u64::from(self.account_universe) << 56
238 }
239 fn get_steamid2(&self) -> String {
240 let authserver: u32 = self.account_id & 1; // Ideally we'd cast this to a bool and convert that to a 0 or 1 later.
241 let accountid: u32 = (self.account_id >> 1) & 2147483647;
242 format!("STEAM_{}:{}:{}", self.account_universe, authserver, accountid)
243 }
244 fn get_steamid3(&self) -> String {
245 let type_char: char = account_type_to_char(self.account_type, Some(self.account_instance));
246 format!("[{}:{}:{}]", type_char, self.account_universe, self.account_id)
247 }
248 }
249
250
251
252 fn main() {
253 // Gather our CLI arguments
254 let args: Vec<String> = env::args().collect();
255
256 //println!("Args: {:?}", args);
257 //println!("Len: {}", args.len());
258
259 // Dumb check, make sure they even tried providing a SteamID
260 if args.len() < 2 {
261 println!("No IDs provided!");
262 std::process::exit(-1);
263 }
264
265 // Process all of our passed strings
266 for i in 1..args.len() {
267 let input = &args[i];
268 let steam_type = string_to_steamid_type(input);
269 match steam_type {
270 Ok(steam_type) => {
271 println!("Interpreting as {:?}", steam_type );
272 match steam_type {
273 SteamIDType::SteamID64 => {
274 let mut steamid_object = SteamID::new();
275 steamid_object.set_steamid64(input.parse::<u64>().expect("SteamID64 Not a Number!"));
276 println!("steamID64:\t{}", steamid_object.get_steamid64());
277 println!("steamID: \t{}", steamid_object.get_steamid2());
278 println!("steamID3: \t{}", steamid_object.get_steamid3());
279 }
280 SteamIDType::SteamID2 => {
281 let mut steamid_object = SteamID::new();
282 steamid_object.set_steamid64(steamid2_to_steamid64(input));
283 println!("steamID64:\t{}", steamid_object.get_steamid64());
284 println!("steamID: \t{}", steamid_object.get_steamid2());
285 println!("steamID3: \t{}", steamid_object.get_steamid3());
286 }
287 SteamIDType::SteamID3 => {
288 let mut steamid_object = SteamID::new();
289 steamid_object.set_steamid64(steamid3_to_steamid64(input));
290 println!("steamID64:\t{}", steamid_object.get_steamid64());
291 println!("steamID: \t{}", steamid_object.get_steamid2());
292 println!("steamID3: \t{}", steamid_object.get_steamid3());
293 }
294 }
295 }
296 Err(_steam_type) => {
297 println!("Unable to interpret {}", input);
298 }
299 }
300 println!("");
301 }
302
303 // println!("Generating Alias SteamID64s...");
304 // thread::sleep(time::Duration::from_secs(1));
305 // for n in 1..1048575 {
306 // let instance: u64 = n << 32;
307 // let newsteam64 = (steamid64 & 18442240478377148415) | instance;
308 // println!("http://steamcommunity.com/profiles/{}", newsteam64);
309 // }
310 }

mercurial