Skip to content

Commit

Permalink
fix: Recursively download every atom of data from titlestorage
Browse files Browse the repository at this point in the history
  • Loading branch information
tuxuser committed Jan 14, 2025
1 parent 109961d commit 6468f1e
Showing 1 changed file with 58 additions and 18 deletions.
76 changes: 58 additions & 18 deletions examples/src/bin/auth_titlehub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::collections::HashMap;
use std::io::Write;
use std::path::PathBuf;

use log::{info, warn, debug, trace};
use log::{info, debug, trace};
use async_trait::async_trait;
use reqwest::Url;
use serde::Deserialize;
Expand Down Expand Up @@ -97,16 +97,30 @@ impl AuthPromptCallback for HttpCallbackHandler {
}
}

pub fn assemble_filepath(root_path: &PathBuf, path: &str) -> PathBuf {
let modified_path = path
// Replace separator with platform-specific separator
.replace("/", std::path::MAIN_SEPARATOR_STR)
// Strip ,savedgame suffix
.replace(",savedgame", "")
.replace("X", ".")
.replace("E", "-");
pub fn assemble_filepath(root_path: &PathBuf, atom_type: &str, path: &str) -> PathBuf {
let modified_path = {
let tmp = path
// Replace separator with platform-specific separator
.replace("/", std::path::MAIN_SEPARATOR_STR)
// Strip ,savedgame suffix
.replace(",savedgame", "")
.replace("X", ".")
.replace("E", "-");

if tmp.starts_with(std::path::MAIN_SEPARATOR_STR) {
// Remove leading path seperator
tmp[1..].to_string()
}
else {
tmp
}
};

let mut new_path = root_path.to_path_buf();
new_path.push(atom_type);
new_path.push(modified_path);

root_path.join(modified_path)
new_path
}

#[tokio::main]
Expand Down Expand Up @@ -155,8 +169,8 @@ async fn main() -> Result<(), Error> {

let client = reqwest::Client::new();

let pfn = "Microsoft.ProjectSpark-Dakota_8wekyb3d8bbwe";
let scid = "d3d00100-7976-472f-a3f7-bc1760d19e14";
let pfn = "Microsoft.ArthurProduct_8wekyb3d8bbwe";
let scid = "05c20100-6e60-45d5-878a-4903149e11ae";

let mut target_dir = PathBuf::new();
target_dir.push(&pfn);
Expand Down Expand Up @@ -190,8 +204,6 @@ async fn main() -> Result<(), Error> {
for blob in metadata.blobs {
info!("- Fetching {} ({} bytes)", &blob.file_name, blob.size);

let filepath = assemble_filepath(&target_dir, &blob.file_name);

let atoms = client
.get(format!("https://titlestorage.xboxlive.com/connectedstorage/users/xuid({xuid})/scids/{scid}/{}", blob.file_name))
.header("x-xbl-contract-version", "107")
Expand All @@ -213,8 +225,9 @@ async fn main() -> Result<(), Error> {
trace!("{atoms:?}");

debug!("* Found {} atoms", atoms.atoms.len());
if let Some(atom_guid) = atoms.atoms.get("Data") {
debug!("Fetching atom {atom_guid}");
for (atom_type, atom_guid) in atoms.atoms.iter() {
let filepath = assemble_filepath(&target_dir, atom_type, &blob.file_name);
debug!("Fetching atom {atom_guid} (Type: {atom_type})");
let filedata = client
.get(format!("https://titlestorage.xboxlive.com/connectedstorage/users/xuid({xuid})/scids/{scid}/{atom_guid}"))
.header("x-xbl-contract-version", "107")
Expand All @@ -241,8 +254,6 @@ async fn main() -> Result<(), Error> {

let mut filehandle = std::fs::File::create(filepath)?;
filehandle.write_all(&filedata)?;
} else {
warn!("No atom with 'Data' found for blob {}", blob.file_name);
}
}
Ok(())
Expand All @@ -252,8 +263,37 @@ async fn main() -> Result<(), Error> {
#[cfg(test)]
mod tests
{
use std::str::FromStr;

use super::*;

//#[cfg(target_os="windows")]
#[cfg(not(target_os = "windows"))]
#[test]
fn test_assemble_unix() {
assert_eq!(
"/root/filesystem/Data/save-container.bin",
assemble_filepath(&PathBuf::from_str("/root/filesystem").unwrap(), "Data", "/saveEcontainerXbin,savedgame").as_os_str()
);
assert_eq!(
"/root/filesystem/Data/save-container.bin",
assemble_filepath(&PathBuf::from_str("/root/filesystem").unwrap(), "Data", "saveEcontainerXbin,savedgame").as_os_str()
);
}

#[cfg(target_os = "windows")]
#[test]
fn test_assemble_filepath_windows() {
assert_eq!(
"C:\\some_dir\\Data\\save-container.bin",
assemble_filepath(&PathBuf::from_str("C:\\some_dir\\").unwrap(), "Data", "/saveEcontainerXbin,savedgame").as_os_str()
);
assert_eq!(
"C:\\some_dir\\Data\\save-container.bin",
assemble_filepath(&PathBuf::from_str("C:\\some_dir\\").unwrap(), "Data", "saveEcontainerXbin,savedgame").as_os_str()
);
}

#[test]
fn deserialize_blob_response() {
let data = r#"{"blobs":[{"fileName":"save_container,savedgame","displayName":"Save Data","etag":"\"0x8DCA185D3F40E2A\"","clientFileTime":"2024-07-11T08:45:22.5700000Z","size":6745}],"pagingInfo":{"totalItems":1,"continuationToken":null}}"#;
Expand Down

0 comments on commit 6468f1e

Please sign in to comment.