diff --git a/Cargo.lock b/Cargo.lock index 304d907..28314e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -199,14 +199,14 @@ checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "cssparser" -version = "0.34.0" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c66d1cd8ed61bf80b38432613a7a2f09401ab8d0501110655f8b341484a3e3" +checksum = "5b3df4f93e5fbbe73ec01ec8d3f68bba73107993a5b1e7519273c32db9b0d5be" dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf", + "phf 0.11.2", "smallvec", ] @@ -397,9 +397,9 @@ dependencies = [ [[package]] name = "ego-tree" -version = "0.9.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c6ba7d4eec39eaa9ab24d44a0e73a7949a1095a8b3f3abb11eddf27dbb56a53" +checksum = "12a0bb14ac04a9fcf170d0bbbef949b44cc492f4452bd20c095636956f653642" [[package]] name = "either" @@ -526,6 +526,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -578,9 +587,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "html5ever" -version = "0.29.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e15626aaf9c351bc696217cbe29cb9b5e86c43f8a46b5e2f5c6c5cf7cb904ce" +checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" dependencies = [ "log", "mac", @@ -812,13 +821,13 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "markup5ever" -version = "0.14.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c88c6129bd24319e62a0359cb6b958fa7e8be6e19bb1663bc396b90883aca5" +checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" dependencies = [ "log", - "phf", - "phf_codegen", + "phf 0.11.2", + "phf_codegen 0.11.2", "string_cache", "string_cache_codegen", "tendril", @@ -993,6 +1002,15 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + [[package]] name = "phf" version = "0.11.2" @@ -1003,6 +1021,16 @@ dependencies = [ "phf_shared 0.11.2", ] +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + [[package]] name = "phf_codegen" version = "0.11.2" @@ -1328,15 +1356,16 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scraper" -version = "0.21.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0e749d29b2064585327af5038a5a8eb73aeebad4a3472e83531a436563f7208" +checksum = "b90460b31bfe1fc07be8262e42c665ad97118d4585869de9345a84d501a9eaf0" dependencies = [ "ahash", "cssparser", "ego-tree", + "getopts", "html5ever", - "precomputed-hash", + "once_cell", "selectors", "tendril", ] @@ -1366,9 +1395,9 @@ dependencies = [ [[package]] name = "selectors" -version = "0.26.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd568a4c9bb598e291a08244a5c1f5a8a6650bee243b5b0f8dbb3d9cc1d87fe8" +checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06" dependencies = [ "bitflags", "cssparser", @@ -1376,8 +1405,8 @@ dependencies = [ "fxhash", "log", "new_debug_unreachable", - "phf", - "phf_codegen", + "phf 0.10.1", + "phf_codegen 0.10.0", "precomputed-hash", "servo_arc", "smallvec", @@ -1429,9 +1458,9 @@ dependencies = [ [[package]] name = "servo_arc" -version = "0.4.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae65c4249478a2647db249fb43e23cec56a2c8974a427e7bd8cb5a1d0964921a" +checksum = "d036d71a959e00c77a63538b90a6c2390969f9772b096ea837205c6bd0491a44" dependencies = [ "stable_deref_trait", ] @@ -1525,9 +1554,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.85" +version = "2.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" dependencies = [ "proc-macro2", "quote", @@ -1599,18 +1628,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.65" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.65" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 6d491fd..4d0d589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,8 @@ cxx-qt-build = { git = "https://github.com/KDAB/cxx-qt", branch = "main", featur [dependencies] # Used to scrape the Lodestone HTML pages -scraper = { version ="0.21", default-features = false } +# NOTE: Do not upgrade to 0.21, as it crashes in WebAssembly for some reason. Report this upstream. +scraper = { version ="0.20" } # Used to serialize the JSON data we export serde = { version = "1.0", features = ["derive"], default-features = false } diff --git a/dalamud/Auracite/EndStep.cs b/dalamud/Auracite/EndStep.cs index a90675e..1a187e1 100644 --- a/dalamud/Auracite/EndStep.cs +++ b/dalamud/Auracite/EndStep.cs @@ -44,10 +44,17 @@ public class EndStep : IStep [Route(HttpVerbs.Get, "/package")] public void GetPackage() { + Response.Headers.Set(HttpHeaderNames.AccessControlAllowOrigin, "*"); Response.ContentType = MimeType.Json; using var writer = HttpContext.OpenResponseText(Encoding.UTF8, true); writer.Write(JsonConvert.SerializeObject(Plugin.package)); - + } + + // TODO: Make this a POST request? + // This is needed since we don't know when the CORS handshake really stops. This really shouldn't be needed though. + [Route(HttpVerbs.Get, "/stop")] + public void Stop() + { _endStep.End(); } } @@ -61,7 +68,6 @@ public class EndStep : IStep _server = new WebServer(o => o .WithUrlPrefix("http://localhost:42072/") .WithMode(HttpListenerMode.EmbedIO)) - .WithCors("http://localhost:42072/") .WithWebApi("/", m => m.WithController(() => new Controller(this))); _server.RunAsync(); } diff --git a/index.html b/index.html index 5c98806..627e15c 100644 --- a/index.html +++ b/index.html @@ -10,7 +10,7 @@ function archive() { init().then(() => { - archive_character_base64(document.getElementById("name").value, false).then((uri) => { + archive_character_base64(document.getElementById("name").value, document.getElementById("scales").checked).then((uri) => { // Download character archive window.location.replace(uri); document.getElementById("statusMessage").innerText = "Archive complete!"; diff --git a/src/downloader.rs b/src/downloader.rs index 7b71ddb..4ae0e15 100644 --- a/src/downloader.rs +++ b/src/downloader.rs @@ -1,9 +1,14 @@ use reqwest::Url; pub async fn download(url: &Url) -> Result, reqwest::Error> { - let client = reqwest::Client::builder() - .no_proxy() // This fixes localhost connections... for some reason (https://github.com/seanmonstar/reqwest/issues/913) - .build()?; + let mut client = reqwest::Client::builder(); + + #[cfg(not(target_family = "wasm"))] + { + client = client.no_proxy(); // This fixes localhost connections... for some reason (https://github.com/seanmonstar/reqwest/issues/913) + } + + let client = client.build()?; let body = client.get(url.to_string()) .send() diff --git a/src/lib.rs b/src/lib.rs index 11c6f1b..df30a5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,8 +21,18 @@ use wasm_bindgen::prelude::wasm_bindgen; #[cfg(target_family = "wasm")] use wasm_bindgen::JsValue; +/// The main Lodestone domain const LODESTONE_HOST: &str = "https://na.finalfantasyxiv.com"; +/// The Lodestone proxy used in WebAssembly builds. Needed for CORS and cookie injection. +const LODESTONE_TUNNEL_HOST: &str = "https://lodestone-tunnel.ryne.moe"; + +/// The image domain. +const IMAGE_HOST: &str = "img2.finalfantasyxiv.com"; + +/// The image proxy used in WebAssembly builds. Needed for CORS. +const IMAGE_TUNNEL_HOST: &str = "img-tunnel.ryne.moe"; + #[derive(Default, Deserialize, Clone)] struct Package { playtime: String, @@ -66,13 +76,20 @@ impl From for JsValue { ArchiveError::CharacterNotFound => { JsValue::from_str(&"character_not_found".to_string()) } ArchiveError::ParsingError => { JsValue::from_str(&"parsing_error".to_string())} ArchiveError::UnknownError => { JsValue::from_str(&"unknown_error".to_string()) } + ArchiveError::CouldNotConnectToDalamud => { JsValue::from_str(&"could_not_connect_to_dalamud".to_string()) } } } } /// Archives the character named `character_name` and gives a ZIP file as bytes that can be written to disk. pub async fn archive_character(character_name: &str, use_dalamud: bool) -> Result, ArchiveError> { - let search_url = Url::parse_with_params(&format!("{LODESTONE_HOST}/lodestone/character?"), &[("q", character_name)]).map_err(|_| ArchiveError::UnknownError)?; + let lodestone_host = if cfg!(target_family = "wasm") { + LODESTONE_TUNNEL_HOST + } else { + LODESTONE_HOST + }; + + let search_url = Url::parse_with_params(&format!("{lodestone_host}/lodestone/character?"), &[("q", character_name)]).map_err(|_| ArchiveError::UnknownError)?; let search_page = download(&search_url) .await .map_err(|_| ArchiveError::DownloadFailed(search_url.to_string()))?; @@ -83,7 +100,7 @@ pub async fn archive_character(character_name: &str, use_dalamud: bool) -> Resul return Err(ArchiveError::CharacterNotFound); } - let char_page_url = Url::parse(&format!("{LODESTONE_HOST}{}", href)).map_err(|_| ArchiveError::UnknownError)?; + let char_page_url = Url::parse(&format!("{lodestone_host}{}", href)).map_err(|_| ArchiveError::UnknownError)?; let char_page = download(&char_page_url) .await .map_err(|_| ArchiveError::DownloadFailed(char_page_url.to_string()))?; @@ -92,13 +109,17 @@ pub async fn archive_character(character_name: &str, use_dalamud: bool) -> Resul let mut char_data = parser::parse_lodestone(&char_page); // 2 MiB, for one JSON and two images - let mut buf = vec![0; 2097152]; - let mut zip = ZipWriter::new(std::io::Cursor::new(&mut buf[..])); + let mut buf = Vec::new(); + let mut zip = ZipWriter::new(std::io::Cursor::new(&mut buf)); let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored); if !char_data.portrait_url.is_empty() { - let portrait_url = char_data.portrait_url.replace("img2.finalfantasyxiv.com", "img-tunnel.ryne.moe"); + let portrait_url = if cfg!(target_family = "wasm") { + &char_data.portrait_url.replace(IMAGE_HOST, IMAGE_TUNNEL_HOST) + } else { + &char_data.portrait_url + }; let portrait_url = Url::parse(&portrait_url).map_err(|_| ArchiveError::UnknownError)?; let portrait = download(&portrait_url) @@ -109,7 +130,11 @@ pub async fn archive_character(character_name: &str, use_dalamud: bool) -> Resul zip.write_all(&*portrait)?; } if !char_data.face_url.is_empty() { - let face_url = char_data.face_url.replace("img2.finalfantasyxiv.com", "img-tunnel.ryne.moe"); + let face_url = if cfg!(target_family = "wasm") { + &char_data.face_url.replace(IMAGE_HOST, IMAGE_TUNNEL_HOST) + } else { + &char_data.face_url + }; let face_url = Url::parse(&face_url).map_err(|_| ArchiveError::UnknownError)?; let face = download(&face_url) @@ -137,6 +162,10 @@ pub async fn archive_character(character_name: &str, use_dalamud: bool) -> Resul char_data.is_novice = package.is_novice; char_data.is_returner = package.is_returner; char_data.player_commendations = package.player_commendations; // TODO: fetch from the lodestone? + + // Stop the HTTP server + let stop_url = Url::parse(&"http://localhost:42072/stop").map_err(|_| ArchiveError::UnknownError)?; + download(&stop_url).await; } zip.start_file("character.json", options)?;