21 Commits

Author SHA1 Message Date
81e8eaad0f Merge branch 'feature-cleanup' 2022-08-31 12:53:29 -07:00
84d206a87b Merge branch 'master' of http://git.dormedas.com/dormedas/town-of-us-updater 2022-08-31 12:52:52 -07:00
e59c25af25 Cleaned up dead code comments
Locating the among us folder and launching the app are one in the same, need GUI work to allow it to update after location

More and changed log messages
2022-08-31 12:51:56 -07:00
cf75410860 Clippy fixes 2022-08-29 23:30:29 -07:00
35828e4b5b Clippy fixes 2022-08-29 22:59:53 -07:00
bac49bc766 Fix clippy compile error 2022-08-29 22:59:19 -07:00
9564c70a33 Remove comments, simplify 2022-08-29 19:17:47 -07:00
c9e9cbc66d Merge branch 'master' into feature-cleanup 2022-08-29 18:43:53 -07:00
88f8ca7031 Bump to 2.0.0 2022-08-29 18:43:40 -07:00
721e08cfc6 Fix warning 2022-08-29 18:43:22 -07:00
d6e3a7133b Move AmongUsVersion to its own file. Concatenate and reduce imports. 2022-08-29 18:41:03 -07:00
9e90a18c6a Update 'README.md' 2022-08-29 14:31:59 -07:00
9132afe917 Add .gitignore 2022-08-29 11:32:40 -07:00
ebad7cb893 Fix blocking reqwest crash 2022-08-29 11:31:21 -07:00
b4c22cdc48 Migrated to druid gui 2022-08-21 10:50:48 -07:00
54b2b8b117 Updated log message 2022-08-16 08:55:26 -07:00
91579d333e Fully implemented tokio runtime. Added build script. Executable has an icon. 2022-08-16 08:06:06 -07:00
0b940aea70 Updated version check logic. Still un-implemented 2022-07-26 22:12:30 -07:00
86d1e176d4 More improved messaging, added folder detection caching 2022-07-26 20:37:00 -07:00
b6221643e2 Improve messaging around the app 2022-07-26 19:43:50 -07:00
3150019b76 Bumped version to 1.0.0, started work on update infrastructure 2022-07-20 09:26:22 -07:00
9 changed files with 423 additions and 221 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
/target /target
Cargo.lock

View File

@@ -1,7 +1,8 @@
[package] [package]
name = "town-of-us-updater" name = "town-of-us-updater"
version = "0.1.0" version = "2.0.0"
edition = "2021" edition = "2021"
build = "src/build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -12,3 +13,9 @@ fs_extra = "1.2.0"
dirs = "4.0.0" dirs = "4.0.0"
reqwest = {version = "0.11.11", features = ["blocking"]} reqwest = {version = "0.11.11", features = ["blocking"]}
iui = "0.3.0" iui = "0.3.0"
serde_json = "1.0"
tokio = {version="1", features=["full"]}
druid = "0.7.0"
[build-dependencies]
embed-resource = "1.6"

View File

@@ -1,2 +1,20 @@
# town-of-us-updater # Town of Us Updater
A tool to automatically install the **Town of Us R** mod for **Among Us**.
## Features
- Caches old builds to allow you to continue playing the mod in case of a breaking Among Us update
- GUI to select which build to play
- Auto-detection of Among Us install directory
# Contributing
Help. I have no standards. Just look at the code.
## How to Build
- [Install Rust](https://www.rust-lang.org/learn/get-started)
- Download repository
- Navigate to repository
- `cargo build`

BIN
assets/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

BIN
assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

1
assets/main.rc Normal file
View File

@@ -0,0 +1 @@
1 ICON "icon.ico"

39
src/among_us_version.rs Normal file
View File

@@ -0,0 +1,39 @@
use std::fmt;
#[derive(Ord, PartialOrd, Eq, PartialEq)]
pub struct AmongUsVersion {
year: i32,
month: i32,
day: i32,
}
impl fmt::Display for AmongUsVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}.{}", self.year, self.month, self.day)
}
}
impl From<&str> for AmongUsVersion {
fn from(s: &str) -> AmongUsVersion {
// Ignore a prepending "v"
let tmp_str = s.replace("v", "");
let v: Vec<&str> = tmp_str.split('.').collect();
AmongUsVersion {
year: v[0].parse().unwrap(),
month: v[1].parse().unwrap(),
day: v[2].parse().unwrap(),
}
}
}
impl From<(i32, i32, i32)> for AmongUsVersion {
fn from(s: (i32, i32, i32)) -> AmongUsVersion {
AmongUsVersion {
year: s.0,
month: s.1,
day: s.2,
}
}
}

5
src/build.rs Normal file
View File

@@ -0,0 +1,5 @@
extern crate embed_resource;
fn main() {
embed_resource::compile("assets/main.rc");
}

View File

@@ -1,48 +1,43 @@
use std::path::{Path, PathBuf}; // Modules
use std::str; mod among_us_version;
use std::*;
// Uses
use among_us_version::*;
use std::{cell::RefCell, fs, io, path::Path, path::PathBuf, process, rc::Rc, str};
use fs_extra::{copy_items, dir}; use fs_extra::{copy_items, dir};
use iui::controls::{Button, Group, Label, VerticalBox};
use iui::prelude::*;
use regex::Regex; use regex::Regex;
static AMONG_US_APPID: &'static str = "945360"; // GUI stuff
use druid::{
commands, widget::*, AppDelegate, AppLauncher, Application, Data, DelegateCtx, Env,
FileDialogOptions, Handled, Target, WidgetExt, WindowDesc,
};
struct AmongUsVersion { struct Delegate;
year: i32,
month: i32, #[derive(Data, Clone)]
day: i32, struct AppData {
pub among_us_path: Rc<RefCell<String>>,
pub installs_path: String,
} }
impl fmt::Display for AmongUsVersion { static AMONG_US_APPID: &str = "945360";
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}.{}", self.year, self.month, self.day)
}
}
impl From<&str> for AmongUsVersion {
fn from(s: &str) -> AmongUsVersion {
let v: Vec<&str> = s.split(".").collect();
AmongUsVersion {
year: i32::from_str_radix(v[0], 10).unwrap(),
month: i32::from_str_radix(v[1], 10).unwrap(),
day: i32::from_str_radix(v[2], 10).unwrap(),
}
}
}
fn attempt_run_among_us(install_path: &Path) { fn attempt_run_among_us(install_path: &Path) {
let executable_path: path::PathBuf = [install_path.to_str().unwrap(), "Among Us.exe"] let executable_path: PathBuf = [install_path.to_str().unwrap(), "Among Us.exe"]
.iter() .iter()
.collect(); .collect();
std::process::Command::new(executable_path.to_str().unwrap()) process::Command::new(executable_path.to_str().unwrap())
.spawn() .spawn()
.unwrap(); .unwrap();
} }
fn unmod_among_us_folder(folder_path: &Path) { fn unmod_among_us_folder(folder_path: &Path) {
println!("Unmodding Among Us...");
let doorstop_path: PathBuf = [folder_path.to_str().unwrap(), "doorstop_config.ini"] let doorstop_path: PathBuf = [folder_path.to_str().unwrap(), "doorstop_config.ini"]
.iter() .iter()
.collect(); .collect();
@@ -52,6 +47,7 @@ fn unmod_among_us_folder(folder_path: &Path) {
let winhttp_path: PathBuf = [folder_path.to_str().unwrap(), "winhttp.dll"] let winhttp_path: PathBuf = [folder_path.to_str().unwrap(), "winhttp.dll"]
.iter() .iter()
.collect(); .collect();
let bepinex_path: PathBuf = [folder_path.to_str().unwrap(), "BepInEx"].iter().collect(); let bepinex_path: PathBuf = [folder_path.to_str().unwrap(), "BepInEx"].iter().collect();
let mono_path: PathBuf = [folder_path.to_str().unwrap(), "mono"].iter().collect(); let mono_path: PathBuf = [folder_path.to_str().unwrap(), "mono"].iter().collect();
fs::remove_file(doorstop_path).unwrap_or_default(); fs::remove_file(doorstop_path).unwrap_or_default();
@@ -61,7 +57,91 @@ fn unmod_among_us_folder(folder_path: &Path) {
fs::remove_dir_all(mono_path).unwrap_or_default(); fs::remove_dir_all(mono_path).unwrap_or_default();
} }
fn main() { async fn get_latest_updater_version() -> Result<(String, String), reqwest::Error> {
let version = env!("CARGO_PKG_VERSION");
let mut cur_vers = version.split('.');
let major_ver = cur_vers.next().unwrap().parse::<i32>().unwrap();
let minor_ver = cur_vers.next().unwrap().parse::<i32>().unwrap();
let patch_ver = cur_vers.next().unwrap().parse::<i32>().unwrap();
let body =
reqwest::get("https://git.dormedas.com/api/v1/repos/dormedas/town-of-us-updater/releases");
let root: serde_json::Value = serde_json::from_str(&body.await?.text().await?).unwrap();
for i in root.as_array().unwrap() {
let tag_name = i["tag_name"].as_str().unwrap();
if let Some(trimmed_str) = tag_name.strip_prefix('v') {
let mut vers = trimmed_str.split('.');
let tag_major_ver = vers.next().unwrap().parse::<i32>().unwrap();
let tag_minor_ver = vers.next().unwrap().parse::<i32>().unwrap();
let tag_patch_ver = vers.next().unwrap().parse::<i32>().unwrap();
if tag_major_ver > major_ver
|| (tag_major_ver == major_ver && tag_minor_ver > minor_ver)
|| (tag_major_ver == major_ver
&& tag_minor_ver == minor_ver
&& tag_patch_ver > patch_ver)
{
println!("New version of the updater detected!");
}
println!("{}", trimmed_str);
} else {
let mut vers = tag_name.split('.');
let tag_major_ver = vers.next().unwrap().parse::<i32>().unwrap();
let tag_minor_ver = vers.next().unwrap().parse::<i32>().unwrap();
let tag_patch_ver = vers.next().unwrap().parse::<i32>().unwrap();
if tag_major_ver > major_ver
|| (tag_major_ver == major_ver && tag_minor_ver > minor_ver)
|| (tag_major_ver == major_ver
&& tag_minor_ver == minor_ver
&& tag_patch_ver > patch_ver)
{
println!("New version of the updater detected!");
}
println!("{}", i["tag_name"]);
}
}
Ok((String::from("no"), String::from("yes")))
}
impl AppDelegate<AppData> for Delegate {
fn command(
&mut self,
_ctx: &mut DelegateCtx,
_target: Target,
cmd: &druid::Command,
data: &mut AppData,
_env: &Env,
) -> Handled {
if let Some(file_info) = cmd.get(commands::OPEN_FILE) {
println!("{:?}", file_info);
let among_us_folder = file_info.path();
let mut buf = among_us_folder.to_path_buf();
buf.pop();
let mut borrow = data.among_us_path.borrow_mut();
*borrow = String::from(buf.to_str().unwrap());
// Pop the selected file off the end of the path
// among_us_folder.pop();
println!("{}", buf.to_str().unwrap());
println!("Handled!");
Application::global().quit();
return Handled::Yes;
}
Handled::No
}
}
#[tokio::main]
async fn main() {
let version = env!("CARGO_PKG_VERSION");
let title_string: String = format!("Town of Us Updater - {}", version);
println!("Updater Version: {}", version);
get_latest_updater_version().await.unwrap();
// CREATE PROGRAM DIRECTORY // CREATE PROGRAM DIRECTORY
let mut data_path = dirs::data_dir().unwrap(); let mut data_path = dirs::data_dir().unwrap();
data_path.push("town-of-us-updater"); data_path.push("town-of-us-updater");
@@ -71,149 +151,232 @@ fn main() {
installs_path.push("installs"); installs_path.push("installs");
fs::create_dir(installs_path.clone()).unwrap_or(()); fs::create_dir(installs_path.clone()).unwrap_or(());
let ui = UI::init().expect("UI failed to init"); let app_data: AppData = AppData {
among_us_path: Rc::new(RefCell::new(String::from(""))),
installs_path: String::from(""),
};
let mut win = Window::new(&ui, "Town of Us Updater", 600, 400, WindowType::NoMenubar); let mut root_column: druid::widget::Flex<AppData> = druid::widget::Flex::column();
let mut vbox = VerticalBox::new(&ui); // DETERMINE AMONG US VERSION
vbox.set_padded(&ui, true); let mut among_us_folder = PathBuf::new();
let mut button = Button::new(&ui, "Button"); let mut existing_file_path = data_path.clone();
button.on_clicked(&ui, { existing_file_path.push("existing_among_us_dir.txt");
let ui = ui.clone(); let existing_found_folder = fs::read_to_string(existing_file_path.clone());
move |btn| {
let folder_opt = detect_among_us_folder();
if folder_opt.is_some() {
btn.set_text(&ui, "Amogus");
ui.quit();
}
}
});
let folder_opt = detect_among_us_folder(); if let Ok(existing_folder) = existing_found_folder {
let mut among_us_folder = path::PathBuf::new(); among_us_folder.push(existing_folder);
if folder_opt.is_some() {
among_us_folder.push(folder_opt.unwrap());
} else { } else {
win.modal_msg(&ui, "Find Among Us", "On the following Open File dialog, navigate to your Among Us folder and select Among Us.exe"); let folder_opt = detect_among_us_folder();
let open_window = win.open_file(&ui); if folder_opt.is_some() {
// println!("{}", open_window.unwrap().to_str().unwrap()); among_us_folder.push(folder_opt.unwrap());
among_us_folder = open_window.unwrap().clone(); } else {
among_us_folder.pop(); let open_button = Button::new("Locate Among Us").fix_height(45.0).on_click(
move |ctx, _: &mut AppData, _| {
ctx.submit_command(
druid::commands::SHOW_OPEN_PANEL.with(FileDialogOptions::new()),
);
},
);
// If we can't find Among Us, add the locate button.
// TODO: make this button then update the UI assuming it's valid
root_column.add_flex_child(open_button, 1.0);
// Pop the selected file off the end of the path
// among_us_folder.pop();
}
if among_us_folder.exists() {
fs::write(existing_file_path, among_us_folder.to_str().unwrap()).unwrap();
}
} }
println!("Among Us Folder: {}", among_us_folder.to_str().unwrap()); if let Some(among_us_folder_str) = among_us_folder.to_str() {
if among_us_folder_str.len() > 0 {
let among_us_version = println!("Among Us Folder: {}", among_us_folder_str);
determine_among_us_version(String::from(among_us_folder.to_str().unwrap())).unwrap(); } else {
let ver_url: (String, String, bool) = println!("Among Us Folder not automatically determined");
determine_town_of_us_url(among_us_version.clone()).unwrap();
let version_smash = format!("{}-{}", among_us_version.clone(), ver_url.0.clone());
let new_installed_path: path::PathBuf =
[installs_path.to_str().unwrap(), version_smash.as_str()]
.iter()
.collect();
if !path::Path::exists(&new_installed_path) {
copy_folder_to_target(
among_us_folder.to_str().unwrap(),
installs_path.to_str().unwrap(),
);
let among_us_path: path::PathBuf = [installs_path.to_str().unwrap(), "Among Us\\"]
.iter()
.collect();
if !among_us_path.to_str().unwrap().contains("Among Us") {
process::exit(0);
} }
}
// Un-mod whatever we found if it's modded if let Some(among_us_version) =
unmod_among_us_folder(&among_us_path); determine_among_us_version(String::from(among_us_folder.to_str().unwrap()))
{
println!( println!("AmongUsVersion: {}", among_us_version);
"Rename {} to {}", let ver_url: (String, String, bool) =
among_us_path.to_str().unwrap(), determine_town_of_us_url(among_us_version.to_string().clone())
new_installed_path.to_str().unwrap() .await
);
let mut perms = fs::metadata(among_us_path.clone()).unwrap().permissions();
perms.set_readonly(false);
fs::set_permissions(among_us_path.clone(), perms).unwrap();
fs::rename(among_us_path, new_installed_path.clone()).unwrap();
let mut download_path = data_path.clone();
let downloaded_filename = ver_url.1.rsplit("/").nth(0).unwrap();
download_path.push(downloaded_filename.clone());
if !path::Path::exists(&download_path) {
println!("{:?}", download_path);
println!("Downloading Town of Us [{}]", ver_url.1.clone());
let zip = reqwest::blocking::get(ver_url.1.clone())
.unwrap()
.bytes()
.unwrap(); .unwrap();
fs::write(download_path.clone(), zip).unwrap();
}
let opened_zip = fs::File::open(download_path).unwrap(); let version_smash = format!("{}-{}", among_us_version, ver_url.0.clone());
println!("Extracting..."); let new_installed_path: PathBuf = [installs_path.to_str().unwrap(), version_smash.as_str()]
let mut archive = zip::ZipArchive::new(opened_zip).unwrap(); .iter()
archive.extract(data_path.clone()).unwrap(); .collect();
let mut root_folder_path = String::new(); if !Path::exists(&new_installed_path) {
for i in archive.file_names() { println!("Copying Among Us to cache location...");
root_folder_path = String::from(i.split("/").nth(0).unwrap()); copy_folder_to_target(
break; among_us_folder.to_str().unwrap(),
} installs_path.to_str().unwrap(),
let extracted_path: path::PathBuf = );
[data_path.to_str().unwrap(), root_folder_path.as_str(), "."]
let among_us_path: PathBuf = [installs_path.to_str().unwrap(), "Among Us\\"]
.iter() .iter()
.collect(); .collect();
println!("{}", extracted_path.to_str().unwrap());
copy_folder_contents_to_target(
extracted_path.to_str().unwrap(),
new_installed_path.to_str().unwrap(),
);
} else {
println!("Modded install already found");
}
// Find existing installs, make buttons for them if !among_us_path.to_str().unwrap().contains("Among Us") {
let install_iter = fs::read_dir(installs_path.clone()); process::exit(0);
}
match install_iter { // Un-mod whatever we found if it's modded
Ok(iter) => { unmod_among_us_folder(&among_us_path);
println!(
"Renaming {} to {}",
among_us_path.to_str().unwrap(),
new_installed_path.to_str().unwrap()
);
let mut perms = fs::metadata(among_us_path.clone()).unwrap().permissions();
perms.set_readonly(false);
fs::set_permissions(among_us_path.clone(), perms).unwrap();
fs::rename(among_us_path, new_installed_path.clone()).unwrap();
let mut download_path = data_path.clone();
let downloaded_filename = ver_url.1.rsplit('/').next().unwrap();
download_path.push(downloaded_filename.clone());
if !Path::exists(&download_path) {
// println!("{:?}", download_path);
println!("Downloading Town of Us... [{}]", ver_url.1.clone());
let zip = reqwest::get(ver_url.1.clone())
.await
.unwrap()
.bytes()
.await
.unwrap();
fs::write(download_path.clone(), zip).unwrap();
}
let opened_zip = fs::File::open(download_path).unwrap();
println!("Extracting mod zip file...");
let mut archive = zip::ZipArchive::new(opened_zip).unwrap();
archive.extract(data_path.clone()).unwrap();
let mut root_folder_path = String::new();
for i in archive.file_names() {
root_folder_path = String::from(i.split('/').next().unwrap());
break;
}
let extracted_path: PathBuf =
[data_path.to_str().unwrap(), root_folder_path.as_str(), "."]
.iter()
.collect();
println!("{}", extracted_path.to_str().unwrap());
copy_folder_contents_to_target(
extracted_path.to_str().unwrap(),
new_installed_path.to_str().unwrap(),
);
} else {
println!("Modded install already found");
}
// Find existing installs, make buttons for them
let install_iter = fs::read_dir(installs_path.clone());
if let Ok(iter) = install_iter {
let mut collection: Vec<Result<fs::DirEntry, io::Error>> = iter.collect(); let mut collection: Vec<Result<fs::DirEntry, io::Error>> = iter.collect();
collection.reverse(); collection.reverse();
// Iterate first to find labels:
let mut flexbox_array: Vec<druid::widget::Flex<AppData>> = Vec::new();
let mut auv_array: Vec<String> = Vec::new();
for i in &collection {
let existing_ver_smash = i.as_ref().unwrap().file_name();
let mut ver_smash_split = existing_ver_smash.to_str().unwrap().split('-');
let auv = ver_smash_split.next().unwrap();
if !auv_array.contains(&auv.to_string()) {
let label_text = format!("Among Us {}", auv);
let flex: druid::widget::Flex<AppData> = druid::widget::Flex::column()
.with_flex_child(
druid::widget::Label::new(label_text.as_str()).with_text_size(24.0),
1.0,
)
.with_default_spacer();
flexbox_array.push(flex);
auv_array.push(auv.to_string());
}
}
println!("Installs list:");
for i in collection { for i in collection {
// for i in iter {
let existing_ver_smash = i.unwrap().file_name(); let existing_ver_smash = i.unwrap().file_name();
let mut button = Button::new(&ui, existing_ver_smash.clone().to_str().unwrap()); let mut ver_smash_split = existing_ver_smash.to_str().unwrap().split('-');
button.on_clicked(&ui, { let auv = ver_smash_split.next().unwrap();
let ui = ui.clone(); let button_string: String =
let installs_path = installs_path.clone(); format!("Town of Us {}", ver_smash_split.next().unwrap());
let existing_ver_smash = existing_ver_smash.clone();
move |btn| { for (index, j) in auv_array.iter().enumerate() {
let mut new_path = installs_path.clone(); if j == auv {
new_path.push(existing_ver_smash.clone()); let mut clone = installs_path.clone();
println!("{}", new_path.clone().to_str().unwrap()); clone.push(existing_ver_smash.clone());
attempt_run_among_us(&new_path);
btn.set_text(&ui, "Launching..."); let mut button_row: Flex<AppData> = Flex::row();
let fybutton = druid::widget::Button::new(button_string.as_str())
.fix_height(45.0)
.center()
.on_click(move |_, _: &mut AppData, _| {
attempt_run_among_us(&clone);
});
button_row.add_flex_child(fybutton, 1.0);
// TODO: Uncomment
// let delete_button = druid::widget::Button::new("Delete")
// .fix_height(45.0)
// .on_click(move |_, _: &mut AppData, _| {});
// button_row.add_flex_child(delete_button, 1.0);
flexbox_array
.get_mut(index)
.unwrap()
.add_flex_child(button_row, 1.0);
println!("- {}", existing_ver_smash.clone().to_str().unwrap());
} }
}); }
vbox.append(&ui, button, LayoutStrategy::Stretchy); }
println!("{}", existing_ver_smash.clone().to_str().unwrap());
for i in flexbox_array {
root_column.add_flex_child(i, 1.0);
} }
} }
_ => (),
} }
win.set_child(&ui, vbox); // println!("Checking for updater updates...");
win.show(&ui); // let vals: (String, String) = version_check_thread_handle.join().unwrap();
ui.main();
// TODO: Auto launch latest sanctioned if the user has a setting like that
// Auto launch latest experimental as well
println!("Launching main window...");
let main_window = WindowDesc::new(|| root_column)
.title(title_string)
.window_size((400.0, 400.0));
let app_launcher = AppLauncher::with_window(main_window);
let _external_handler = app_launcher.get_external_handle();
app_launcher.launch(app_data).unwrap();
// let mut install_folder_path = installs_path.clone(); // let mut install_folder_path = installs_path.clone();
// install_folder_path.push(version_smash); // install_folder_path.push(version_smash);
// fs::create_dir(install_folder_path.clone()).unwrap_or(()); // fs::create_dir(install_folder_path.clone()).unwrap_or(());
@@ -225,46 +388,30 @@ fn main() {
// .iter() // .iter()
// .collect(); // .collect();
// std::process::Command::new(executable_path.to_str().unwrap()) // process::Command::new(executable_path.to_str().unwrap())
// .spawn() // .spawn()
// .unwrap(); // .unwrap();
} }
// Returns (Version, URL) // Returns (Version, URL)
fn determine_town_of_us_url(among_us_version: String) -> Option<(String, String, bool)> { async fn determine_town_of_us_url(among_us_version: String) -> Option<(String, String, bool)> {
let markdown = reqwest::blocking::get( let markdown =
"https://raw.githubusercontent.com/eDonnes124/Town-Of-Us-R/master/README.md", reqwest::get("https://raw.githubusercontent.com/eDonnes124/Town-Of-Us-R/master/README.md")
) .await
.unwrap() .unwrap()
.text() .text()
.unwrap(); .await
.unwrap();
let mut line = markdown.find(&among_us_version); let mut line = markdown.find(&among_us_version);
let mut line_offset = 0; let mut line_offset = 0;
let mut official_compatibility = false; let mut official_compatibility = false;
if line.is_some() { if line.is_some() {
println!("Found official version!"); println!("Found sanctioned version!");
official_compatibility = true; official_compatibility = true;
} else { } else {
println!("Official version cannot be determined, installing experimental latest..."); println!("Sanctioned version cannot be determined, installing experimental latest...");
line = markdown.find("[Download]"); line = markdown.find("[Download]");
line_offset = 15; line_offset = 15;
// println!("At this point, there are two options:");
// println!(" [1] Revert Among Us to public-previous in Steam THEN select this option.");
// println!(" [2] Attempt to use the latest version of Town of Us anyway");
// let mut input = String::new();
// io::stdin().read_line(&mut input).unwrap();
// match input.trim() {
// "1" => {
// println!("Re-attempting...");
// return None;
// }
// "2" => {
// println!("Continuing...");
// line = markdown.find("[Download]");
// line_offset = 15;
// }
// _ => return None,
// };
} }
// 100% scientific "-15" here to get the correct version since we find to [Download] above which is after the version number for that line // 100% scientific "-15" here to get the correct version since we find to [Download] above which is after the version number for that line
let splits = markdown.split_at(line.unwrap() - line_offset); let splits = markdown.split_at(line.unwrap() - line_offset);
@@ -273,7 +420,7 @@ fn determine_town_of_us_url(among_us_version: String) -> Option<(String, String,
let captures = url_regex.captures(splits.1).unwrap(); let captures = url_regex.captures(splits.1).unwrap();
let capture = captures.get(captures.len() - 1).unwrap(); let capture = captures.get(captures.len() - 1).unwrap();
let url = splits.1.get(capture.start()..capture.end()).unwrap(); let url = splits.1.get(capture.start()..capture.end()).unwrap();
println!("Official URL is: {}", url); println!("Mod URL is: {}", url);
let ver_regex = Regex::new(r#"\| (v\d\.\d\.\d) \|"#).unwrap(); let ver_regex = Regex::new(r#"\| (v\d\.\d\.\d) \|"#).unwrap();
let ver_captures = ver_regex.captures(splits.1).unwrap(); let ver_captures = ver_regex.captures(splits.1).unwrap();
let ver_capture = ver_captures.get(ver_captures.len() - 1).unwrap(); let ver_capture = ver_captures.get(ver_captures.len() - 1).unwrap();
@@ -281,66 +428,49 @@ fn determine_town_of_us_url(among_us_version: String) -> Option<(String, String,
.1 .1
.get(ver_capture.start()..ver_capture.end()) .get(ver_capture.start()..ver_capture.end())
.unwrap(); .unwrap();
println!("Matching version is: {}", ver); println!("Installing Town of Us version: {}", ver);
Some((String::from(ver), String::from(url), official_compatibility)) Some((String::from(ver), String::from(url), official_compatibility))
} }
fn determine_among_us_version(folder_root: String) -> Option<String> { fn determine_among_us_version(folder_root: String) -> Option<AmongUsVersion> {
let asset_file = format!("{}\\Among Us_Data\\globalgamemanagers", folder_root); let asset_file = format!("{}\\Among Us_Data\\globalgamemanagers", folder_root);
const TARGET_BYTES_LEN: usize = 16; const TARGET_BYTES_LEN: usize = 16;
let target_bytes: [u8; TARGET_BYTES_LEN] = [3, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0]; let target_bytes: [u8; TARGET_BYTES_LEN] = [3, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0];
let file_bytes = fs::read(asset_file).unwrap(); if let Ok(file_bytes) = fs::read(asset_file) {
//println!("{:?}", file_bytes); let mut bytes_str = String::new();
let mut target_head = 0;
let mut file_index = 0;
for i in &file_bytes {
if target_head == TARGET_BYTES_LEN {
break;
}
let mut bytes_str = String::new(); if *i == target_bytes[target_head] {
let mut target_head = 0; target_head += 1;
let mut file_index = 0; } else {
for i in &file_bytes { target_head = 0;
if target_head == TARGET_BYTES_LEN { }
break; file_index += 1;
bytes_str += &format!("{i:x}");
} }
if *i == target_bytes[target_head] { let offset: usize = usize::from(file_bytes[file_index]);
target_head += 1; file_index += 4;
} else {
target_head = 0; Some(AmongUsVersion::from(
} str::from_utf8(file_bytes.get(file_index..file_index + offset).unwrap()).unwrap(),
file_index += 1; ))
bytes_str += &format!("{i:x}"); } else {
None
} }
let offset: usize = usize::from(file_bytes[file_index]); //println!("{:?}", file_bytes);
file_index += 4;
// let mut offset = 10;
// if file_bytes[file_index + 9] == 0 {
// offset -= 1;
// }
// if file_bytes[file_index + 8] == 0 {
// offset -= 1;
// }
println!(
"|{}|",
str::from_utf8(file_bytes.get(file_index..file_index + offset).unwrap()).unwrap()
);
let ver = AmongUsVersion::from(
str::from_utf8(file_bytes.get(file_index..file_index + offset).unwrap()).unwrap(),
);
println!("AmongUsVersion: {}", ver);
Some(String::from(
str::from_utf8(file_bytes.get(file_index..file_index + offset).unwrap()).unwrap(),
))
//println!("{}", bytes_str);
//None
} }
fn copy_folder_to_target<T: AsRef<path::Path>>(source: T, dest: T) { fn copy_folder_to_target<T: AsRef<Path>>(source: T, dest: T) {
let mut copy_opts = dir::CopyOptions::new(); let mut copy_opts = dir::CopyOptions::new();
copy_opts.overwrite = true; copy_opts.overwrite = true;
copy_opts.skip_exist = true; copy_opts.skip_exist = true;
@@ -350,7 +480,7 @@ fn copy_folder_to_target<T: AsRef<path::Path>>(source: T, dest: T) {
copy_items(&from_paths, dest, &copy_opts).unwrap(); copy_items(&from_paths, dest, &copy_opts).unwrap();
} }
fn copy_folder_contents_to_target<T: AsRef<path::Path>>(source: T, dest: T) { fn copy_folder_contents_to_target<T: AsRef<Path>>(source: T, dest: T) {
let mut copy_opts = dir::CopyOptions::new(); let mut copy_opts = dir::CopyOptions::new();
copy_opts.overwrite = true; copy_opts.overwrite = true;
copy_opts.skip_exist = true; copy_opts.skip_exist = true;
@@ -358,6 +488,7 @@ fn copy_folder_contents_to_target<T: AsRef<path::Path>>(source: T, dest: T) {
copy_opts.content_only = true; copy_opts.content_only = true;
fs_extra::dir::copy(source, dest, &copy_opts).unwrap(); fs_extra::dir::copy(source, dest, &copy_opts).unwrap();
} }
fn detect_among_us_folder() -> Option<String> { fn detect_among_us_folder() -> Option<String> {
let library_folder = let library_folder =
fs::read_to_string("C:\\Program Files (x86)\\Steam\\steamapps\\libraryfolders.vdf"); fs::read_to_string("C:\\Program Files (x86)\\Steam\\steamapps\\libraryfolders.vdf");