Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions maxima-bootstrap/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ async fn platform_launch(args: BootstrapLaunchArgs) -> Result<(), NativeError> {
None,
false,
CommandType::WaitForExitAndRun,
Some(&args.slug),
)
.await?;

Expand Down
9 changes: 5 additions & 4 deletions maxima-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ enum Mode {
ListGames,
LocateGame {
path: String,
slug: String,
},
CloudSync {
game_slug: String,
Expand Down Expand Up @@ -301,7 +302,7 @@ async fn startup() -> Result<()> {
start_game(&offer_id, game_path, game_args, login, maxima_arc.clone()).await
}
Mode::ListGames => list_games(maxima_arc.clone()).await,
Mode::LocateGame { path } => locate_game(maxima_arc.clone(), &path).await,
Mode::LocateGame { path, slug } => locate_game(maxima_arc.clone(), &path, &slug).await,
Mode::CloudSync { game_slug, write } => {
do_cloud_sync(maxima_arc.clone(), &game_slug, write).await
}
Expand Down Expand Up @@ -622,7 +623,7 @@ async fn juno_token_refresh(maxima_arc: LockedMaxima) -> Result<()> {
}

async fn read_license_file(content_id: &str) -> Result<()> {
let path = ooa::get_license_dir()?.join(format!("{}.dlf", content_id));
let path = ooa::get_license_dir(None)?.join(format!("{}.dlf", content_id));
let mut data = tokio::fs::read(path).await?;
data.drain(0..65); // Signature

Expand Down Expand Up @@ -768,10 +769,10 @@ async fn list_games(maxima_arc: LockedMaxima) -> Result<()> {
Ok(())
}

async fn locate_game(maxima_arc: LockedMaxima, path: &str) -> Result<()> {
async fn locate_game(maxima_arc: LockedMaxima, path: &str, slug: &str) -> Result<()> {
let path = PathBuf::from(path);
let manifest = manifest::read(path.join(MANIFEST_RELATIVE_PATH)).await?;
manifest.run_touchup(&path).await?;
manifest.run_touchup(&path, slug).await?;
info!("Installed!");
Ok(())
}
Expand Down
1 change: 1 addition & 0 deletions maxima-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ gethostname = "0.5.0"
thiserror = "2.0.12"
url = "2.5.2"
http = "0.2.12"
globset = "0.4"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.9", features = [
Expand Down
40 changes: 40 additions & 0 deletions maxima-lib/src/content/exclusion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::util::native::maxima_dir;
use globset::{Glob, GlobSet, GlobSetBuilder};
use log::{error, info, warn};
use std::fs::File;
use std::io::{BufRead, BufReader};

pub fn get_exclusion_list(offer_id: String) -> GlobSet {
let mut builder = GlobSetBuilder::new();

if let Ok(dir) = maxima_dir()
// Checks to make sure maxima directory exists
{
let filepath = dir.join("exclude").join(&offer_id); // Path to exclusion file
info!("Loading exclusion file from {}", filepath.display());

if let Ok(file) = File::open(&filepath)
// Opens the exclusion file, fails if not found
{
let reader = BufReader::new(file);
for line in reader.lines().flatten() {
let entry = line.trim();
if !entry.is_empty() && !entry.starts_with('#') {
if let Ok(g) = Glob::new(entry) {
builder.add(g);
} else {
warn!("Invalid glob pattern '{}' in {}", entry, filepath.display());
}
}
}
} else {
warn!("Exclusion file not found: {}", filepath.display());
}
} else {
error!("Failed to resolve maxima data directory");
}

builder
.build()
.unwrap_or_else(|_| GlobSetBuilder::new().build().unwrap()) // Returns an empty GlobSet on failure
}
19 changes: 17 additions & 2 deletions maxima-lib/src/content/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ use std::{
},
};

use crate::core::LockedMaxima;
use derive_builder::Builder;
use derive_getters::Getters;
use futures::StreamExt;
use globset::GlobSet;
use log::{debug, error, info};
use reqwest::StatusCode;
use serde::{Deserialize, Serialize};
Expand All @@ -19,6 +21,7 @@ use tokio_util::sync::CancellationToken;
use crate::{
content::{
downloader::{DownloadError, ZipDownloader},
exclusion::get_exclusion_list,
zip::{self, CompressionType, ZipError, ZipFileEntry},
ContentService,
},
Expand All @@ -38,6 +41,7 @@ pub struct QueuedGame {
offer_id: String,
build_id: String,
path: PathBuf,
slug: String,
}

#[derive(Default, Getters, Serialize, Deserialize)]
Expand Down Expand Up @@ -124,6 +128,7 @@ impl DownloadQueue {

pub struct GameDownloader {
offer_id: String,
slug: String,

downloader: Arc<ZipDownloader>,
entries: Vec<ZipFileEntry>,
Expand All @@ -149,8 +154,15 @@ impl GameDownloader {
let downloader = ZipDownloader::new(&game.offer_id, &url.url(), &game.path).await?;

let mut entries = Vec::new();

let exclusion_list = get_exclusion_list(game.offer_id.clone());

for ele in downloader.manifest().entries() {
// TODO: Filtering
if exclusion_list.is_match(&ele.name()) {
info!("Excluding file from download: {}", ele.name());
continue;
}
entries.push(ele.clone());
}

Expand All @@ -163,6 +175,7 @@ impl GameDownloader {

Ok(GameDownloader {
offer_id: game.offer_id.to_owned(),
slug: game.slug.to_owned(),

downloader: Arc::new(downloader),
entries,
Expand All @@ -178,6 +191,7 @@ impl GameDownloader {
let (downloader_arc, entries, cancel_token, completed_bytes, notify) =
self.prepare_download_vars();
let total_count = self.total_count;
let slug = self.slug.clone();
tokio::spawn(async move {
let dl = GameDownloader::start_downloads(
total_count,
Expand All @@ -186,6 +200,7 @@ impl GameDownloader {
cancel_token,
completed_bytes,
notify,
slug,
)
.await;
if let Err(err) = dl {
Expand Down Expand Up @@ -219,6 +234,7 @@ impl GameDownloader {
cancel_token: CancellationToken,
completed_bytes: Arc<AtomicUsize>,
notify: Arc<Notify>,
slug: String,
) -> Result<(), DownloaderError> {
let mut handles = Vec::with_capacity(total_count);

Expand Down Expand Up @@ -258,8 +274,7 @@ impl GameDownloader {

info!("Files downloaded, running touchup...");
let manifest = manifest::read(path.join(MANIFEST_RELATIVE_PATH)).await?;

manifest.run_touchup(path).await?;
manifest.run_touchup(path, &slug).await?;
info!("Installation finished!");

completed_bytes.fetch_add(1, Ordering::SeqCst);
Expand Down
1 change: 1 addition & 0 deletions maxima-lib/src/content/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::core::{
};

pub mod downloader;
pub mod exclusion;
pub mod manager;
pub mod zip;
pub mod zlib;
Expand Down
29 changes: 15 additions & 14 deletions maxima-lib/src/core/auth/hardware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ impl HardwareInfo {
}

#[cfg(target_os = "linux")]
pub fn new(version: u32) -> Self {
pub fn new(version: u32, slug: Option<&str>) -> Self {
use std::{fs, path::Path, process::Command};

let board_manufacturer = match fs::read_to_string("/sys/class/dmi/id/board_vendor") {
Expand All @@ -167,7 +167,7 @@ impl HardwareInfo {
};

let bios_sn = String::from("Serial number");
let os_install_date = get_root_creation_str();
let os_install_date = get_root_creation_str(slug);
let os_sn = String::from("00330-50000-00000-AAOEM");

let mut gpu_pnp_id: Option<String> = None;
Expand Down Expand Up @@ -246,7 +246,7 @@ impl HardwareInfo {
}

#[cfg(target_os = "macos")]
pub fn new(version: u32) -> Self {
pub fn new(version: u32, slug: Option<&str>) -> Self {
use std::process::Command;

use smbioslib::{
Expand All @@ -273,7 +273,7 @@ impl HardwareInfo {
bios_sn = bios.serial_number().to_string();
}

let os_install_date = get_root_creation_str();
let os_install_date = get_root_creation_str(slug);
let mut os_sn = String::from("None");
if let Some(uuid) = bios_data.and_then(|bios| bios.uuid()) {
os_sn = uuid.to_string();
Expand All @@ -282,7 +282,8 @@ impl HardwareInfo {
let mut gpu_pnp_id: Option<String> = None;
let output = Command::new("system_profiler")
.args(["SPDisplaysDataType", "-json"])
.output().unwrap();
.output()
.unwrap();
if output.status.success() {
let json = String::from_utf8_lossy(&output.stdout);
let result: SPDisplaysDataType = serde_json::from_str(&json).unwrap();
Expand All @@ -298,7 +299,10 @@ impl HardwareInfo {
}

let mut disk_sn = String::from("None");
let output = Command::new("diskutil").args(["info", "/"]).output().unwrap();
let output = Command::new("diskutil")
.args(["info", "/"])
.output()
.unwrap();
// Check if the command was successful
if output.status.success() {
// Convert the output bytes to a UTF-8 string
Expand Down Expand Up @@ -472,23 +476,20 @@ impl HardwareInfo {
}

#[cfg(unix)]
fn get_root_creation_str() -> String {
fn get_root_creation_str(slug: Option<&str>) -> String {
use crate::unix::wine::wine_prefix_dir;
use chrono::{TimeZone, Utc};
use std::{fs, os::unix::fs::MetadataExt};

let date_str = String::from("1970010100:00:00.000000000+0000");
let wine_prefix = wine_prefix_dir();
if wine_prefix.is_err() {
return date_str;
}
let wine_prefix = wine_prefix.unwrap();
let wine_prefix = match wine_prefix_dir(slug) {
Ok(prefix) => prefix,
Err(_) => return date_str,
};
let date_str = match fs::metadata(wine_prefix.join("drive_c")) {
Ok(metadata) => {
let nsec = (metadata.mtime_nsec() / 1_000_000) * 1_000_000;
// Convert Unix timestamp to a DateTime
let datetime = Utc.timestamp_nanos((metadata.mtime() * 1_000_000_000) + nsec);
// Format the DateTime
return datetime.format("%Y%m%d%H%M%S%.6f+000").to_string();
}
Err(_) => date_str,
Expand Down
2 changes: 1 addition & 1 deletion maxima-lib/src/core/auth/pc_sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub struct PCSign<'a> {

impl PCSign<'_> {
pub fn new() -> Result<Self, HardwareHashError> {
let hw_info = HardwareInfo::new(1);
let hw_info = HardwareInfo::new(1, None);

let timestamp = Utc::now();
let formatted_timestamp = timestamp.format("%Y-%m-%d %H:%M:%S:%3f");
Expand Down
14 changes: 12 additions & 2 deletions maxima-lib/src/core/background_service_nix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ pub struct WineInjectArgs {
pub path: String,
}

pub async fn wine_get_pid(launch_id: &str, name: &str) -> Result<u32, NativeError> {
pub async fn wine_get_pid(
launch_id: &str,
name: &str,
slug: Option<&str>,
) -> Result<u32, NativeError> {
debug!("Searching for wine PID for {}", name);

let launch_args = WineGetPidArgs {
Expand All @@ -43,6 +47,7 @@ pub async fn wine_get_pid(launch_id: &str, name: &str) -> Result<u32, NativeErro
None,
true,
CommandType::RunInPrefix,
slug,
)
.await?;

Expand All @@ -63,7 +68,11 @@ pub async fn wine_get_pid(launch_id: &str, name: &str) -> Result<u32, NativeErro
Ok(pid.as_str().parse()?)
}

pub async fn request_library_injection(pid: u32, path: &str) -> Result<(), NativeError> {
pub async fn request_library_injection(
pid: u32,
path: &str,
slug: Option<&str>,
) -> Result<(), NativeError> {
debug!("Injecting {}", path);

let launch_args = WineInjectArgs {
Expand All @@ -81,6 +90,7 @@ pub async fn request_library_injection(pid: u32, path: &str) -> Result<(), Nativ
None,
false,
CommandType::RunInPrefix,
slug,
)
.await?;

Expand Down
Loading