1
Fork 0
mirror of https://github.com/redstrate/Auracite.git synced 2025-04-21 20:27:46 +00:00

Write actual appearance data to the .dat file

It actually loads in-game, cool! Auracite does not yet extract the voice, that
will have to be done eventually.
This commit is contained in:
Joshua Goins 2025-03-10 20:20:36 -04:00
parent e18ddec008
commit fbec3dcd11

View file

@ -5,6 +5,7 @@ pub mod parser;
use serde::Deserialize; use serde::Deserialize;
use std::io::Write; use std::io::Write;
use std::time::{SystemTime, UNIX_EPOCH};
use physis::race::{Gender, Race, Subrace}; use physis::race::{Gender, Race, Subrace};
use reqwest::Url; use reqwest::Url;
use zip::result::ZipError; use zip::result::ZipError;
@ -120,6 +121,51 @@ impl From<ArchiveError> for JsValue {
} }
} }
// FIXME: this is stupid and also just copied from physis
fn convert_dat_race(x: i32) -> Race {
match x {
1 => Race::Hyur,
2 => Race::Elezen,
3 => Race::Lalafell,
4 => Race::Miqote,
5 => Race::Roegadyn,
6 => Race::AuRa,
7 => Race::Hrothgar,
8 => Race::Viera,
_ => Race::Hyur,
}
}
fn convert_dat_gender(x: i32) -> Gender {
match x {
0 => Gender::Male,
1 => Gender::Female,
_ => Gender::Male,
}
}
fn convert_dat_subrace(x: i32) -> Subrace {
match x {
1 => Subrace::Midlander,
2 => Subrace::Highlander,
3 => Subrace::Wildwood,
4 => Subrace::Duskwight,
5 => Subrace::Plainsfolk,
6 => Subrace::Dunesfolk,
7 => Subrace::Seeker,
8 => Subrace::Keeper,
9 => Subrace::SeaWolf,
10 => Subrace::Hellsguard,
11 => Subrace::Raen,
12 => Subrace::Xaela,
13 => Subrace::Hellion,
14 => Subrace::Lost,
15 => Subrace::Rava,
16 => Subrace::Veena,
_ => Subrace::Midlander,
}
}
/// Archives the character named `character_name` and gives a ZIP file as bytes that can be written to disk. /// 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<Vec<u8>, ArchiveError> { pub async fn archive_character(character_name: &str, use_dalamud: bool) -> Result<Vec<u8>, ArchiveError> {
let lodestone_host = if cfg!(target_family = "wasm") { let lodestone_host = if cfg!(target_family = "wasm") {
@ -272,49 +318,57 @@ pub async fn archive_character(character_name: &str, use_dalamud: bool) -> Resul
zip.write_all(&*BASE64_STANDARD.decode(accent.trim_start_matches("data:image/png;base64,")).unwrap())?; zip.write_all(&*BASE64_STANDARD.decode(accent.trim_start_matches("data:image/png;base64,")).unwrap())?;
} }
// Stop the HTTP server let timestamp: u32 = SystemTime::now()
let stop_url = Url::parse(&"http://localhost:42072/stop").map_err(|_| ArchiveError::UnknownError)?; .duration_since(UNIX_EPOCH)
download(&stop_url).await; .expect("Failed to get UNIX timestamp!")
} .as_secs()
.try_into()
.unwrap();
let char_dat = physis::chardat::CharacterData { let char_dat = physis::chardat::CharacterData {
version: 0, version: 7,
customize: physis::chardat::CustomizeData { customize: physis::chardat::CustomizeData {
race: Race::Hyur, race: convert_dat_race(package.race),
gender: Gender::Male, gender: convert_dat_gender(package.gender),
age: 0, age: package.model_type as u8,
height: 0, height: package.height as u8,
subrace: Subrace::Midlander, subrace: convert_dat_subrace(package.tribe),
face: 0, face: package.face_type as u8,
hair: 0, hair: package.hair_style as u8,
enable_highlights: false, enable_highlights: package.has_highlights,
skin_tone: 0, skin_tone: package.skin_color as u8,
right_eye_color: 0, right_eye_color: package.eye_color as u8,
hair_tone: 0, hair_tone: package.hair_color as u8,
highlights: 0, highlights: package.hair_color2 as u8,
facial_features: 0, facial_features: package.face_features as u8,
facial_feature_color: 0, facial_feature_color: package.face_features_color as u8,
eyebrows: 0, eyebrows: package.eyebrows as u8,
left_eye_color: 0, left_eye_color: package.eye_color2 as u8,
eyes: 0, eyes: package.eye_shape as u8,
nose: 0, nose: package.nose_shape as u8,
jaw: 0, jaw: package.jaw_shape as u8,
mouth: 0, mouth: package.lip_style as u8,
lips_tone_fur_pattern: 0, lips_tone_fur_pattern: package.lip_color as u8,
race_feature_size: 0, race_feature_size: package.race_feature_size as u8,
race_feature_type: 0, race_feature_type: package.race_feature_type as u8,
bust: 0, bust: package.bust_size as u8,
face_paint: 0, face_paint: package.facepaint as u8,
face_paint_color: 0, face_paint_color: package.facepaint_color as u8,
voice: 0, voice: 0, // TODO: need to get from game
}, },
timestamp: 0, timestamp,
comment: "Generated by Auracite".to_string(), comment: "Generated by Auracite".to_string(),
}; };
zip.start_file("FFXIV_CHARA_01.dat", options)?; zip.start_file("FFXIV_CHARA_01.dat", options)?;
zip.write_all(&*char_dat.write_to_buffer().unwrap())?; zip.write_all(&*char_dat.write_to_buffer().unwrap())?;
// Stop the HTTP server
let stop_url = Url::parse(&"http://localhost:42072/stop").map_err(|_| ArchiveError::UnknownError)?;
// I'm intentionally ignoring the message because it doesn't matter if it fails - and it usually does
let _ = download(&stop_url).await;
}
zip.start_file("character.json", options)?; zip.start_file("character.json", options)?;
zip.write_all(serde_json::to_string(&char_data).unwrap().as_ref())?; zip.write_all(serde_json::to_string(&char_data).unwrap().as_ref())?;