2023-08-06 08:25:04 -04:00
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
2023-09-22 16:39:56 -04:00
use std ::io ::{ BufWriter , Cursor } ;
2023-08-06 08:25:04 -04:00
2023-09-22 16:39:56 -04:00
use binrw ::{ BinRead , BinWrite } ;
2023-08-06 08:25:04 -04:00
use binrw ::binrw ;
2023-10-13 16:16:04 -04:00
use crate ::{ ByteBuffer , ByteSpan } ;
2024-04-16 21:03:26 -04:00
use crate ::common_file_operations ::{ read_bool_from , write_bool_as } ;
2023-08-06 08:25:04 -04:00
2023-07-08 15:29:00 -04:00
use crate ::race ::{ Gender , Race , Subrace } ;
fn convert_dat_race ( x : u8 ) -> 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
}
}
2023-08-02 16:27:10 -04:00
fn convert_race_dat ( race : & Race ) -> u8 {
match race {
Race ::Hyur = > 1 ,
Race ::Elezen = > 2 ,
Race ::Lalafell = > 3 ,
Race ::Miqote = > 4 ,
Race ::Roegadyn = > 5 ,
Race ::AuRa = > 6 ,
Race ::Hrothgar = > 7 ,
Race ::Viera = > 8
}
}
2023-07-08 15:29:00 -04:00
fn convert_dat_gender ( x : u8 ) -> Gender {
match x {
0 = > Gender ::Male ,
1 = > Gender ::Female ,
_ = > Gender ::Male
}
}
2023-08-02 16:27:10 -04:00
fn convert_gender_dat ( gender : & Gender ) -> u8 {
match gender {
Gender ::Male = > 0 ,
Gender ::Female = > 1 ,
}
}
2023-07-08 15:29:00 -04:00
fn convert_dat_subrace ( x : u8 ) -> 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
}
}
2023-08-02 16:27:10 -04:00
fn convert_subrace_dat ( subrace : & Subrace ) -> u8 {
match subrace {
Subrace ::Midlander = > 1 ,
Subrace ::Highlander = > 2 ,
Subrace ::Wildwood = > 3 ,
Subrace ::Duskwight = > 4 ,
Subrace ::Plainsfolk = > 5 ,
Subrace ::Dunesfolk = > 6 ,
Subrace ::Seeker = > 7 ,
Subrace ::Keeper = > 8 ,
Subrace :: SeaWolf = > 9 ,
Subrace ::Hellsguard = > 10 ,
Subrace ::Raen = > 11 ,
Subrace ::Xaela = > 12 ,
Subrace ::Hellion = > 13 ,
Subrace ::Lost = > 14 ,
Subrace ::Rava = > 15 ,
Subrace ::Veena = > 16
}
}
2023-09-22 18:13:20 -04:00
/// Represents the several options that make up a character data file (DAT) which is used by the game's character creation system to save and load presets.
2023-08-02 16:27:10 -04:00
#[ binrw ]
2023-07-08 15:29:00 -04:00
#[ br(little) ]
#[ repr(C) ]
#[ br(magic = 0x2013FF14u32) ]
#[ derive(Debug) ]
2023-09-22 18:13:20 -04:00
pub struct CharacterData { // version 4
/// The version of the character data, the only supported version right now is 4.
2023-07-08 15:29:00 -04:00
pub version : u32 ,
2023-09-22 18:13:20 -04:00
/// The checksum of the data fields.
2023-07-08 15:29:00 -04:00
#[ br(pad_after = 4) ]
pub checksum : u32 ,
2023-09-22 18:13:20 -04:00
/// The race of the character.
2023-09-22 19:17:24 -04:00
#[ br(map = convert_dat_race ) ]
#[ bw(map = convert_race_dat ) ]
2023-07-08 15:29:00 -04:00
pub race : Race ,
2023-09-22 18:13:20 -04:00
/// The gender of the character.
2023-09-22 19:17:24 -04:00
#[ br(map = convert_dat_gender ) ]
#[ bw(map = convert_gender_dat ) ]
2023-07-08 15:29:00 -04:00
pub gender : Gender ,
2023-09-22 18:13:20 -04:00
/// The age of the character. Normal = 1, Old = 3, Young = 4.
pub age : u8 ,
/// The height of the character.
2023-07-08 15:29:00 -04:00
pub height : u8 ,
2023-09-22 18:13:20 -04:00
/// The character's subrace.
2023-09-22 19:17:24 -04:00
#[ br(map = convert_dat_subrace ) ]
#[ bw(map = convert_subrace_dat ) ]
2023-07-08 15:29:00 -04:00
pub subrace : Subrace ,
2023-09-22 18:13:20 -04:00
/// The character's selected head.
2023-07-08 15:29:00 -04:00
pub head : u8 ,
2023-09-22 18:13:20 -04:00
/// The character's selected hair.
2023-07-08 15:29:00 -04:00
pub hair : u8 ,
2023-09-22 18:13:20 -04:00
/// If hair highlights are enabled for this character.
2024-04-16 21:03:26 -04:00
#[ br(map = read_bool_from::<u8>) ]
#[ bw(map = write_bool_as::<u8>) ]
2023-07-08 15:29:00 -04:00
pub enable_highlights : bool ,
2023-09-22 18:13:20 -04:00
/// The character's skin tone.
2023-07-08 15:29:00 -04:00
pub skin_tone : u8 ,
2023-09-22 18:13:20 -04:00
/// The character's right eye color.
2023-07-08 15:29:00 -04:00
pub right_eye_color : u8 ,
2023-09-22 18:13:20 -04:00
/// The character's hair color.
2023-07-08 15:29:00 -04:00
pub hair_tone : u8 ,
2023-09-22 18:13:20 -04:00
/// The color of the hair highlights.
2023-07-08 15:29:00 -04:00
pub highlights : u8 ,
2023-09-22 18:13:20 -04:00
/// The selected facial features.
2023-07-08 15:29:00 -04:00
pub facial_features : u8 ,
2023-09-22 18:13:20 -04:00
/// If the character has limbal eyes.
2023-07-08 15:29:00 -04:00
pub limbal_eyes : u8 ,
2023-09-22 18:13:20 -04:00
/// The character's selected eyebrows.
2023-07-08 15:29:00 -04:00
pub eyebrows : u8 ,
2023-09-22 18:13:20 -04:00
/// The character's left eye color.
2023-07-08 15:29:00 -04:00
pub left_eye_color : u8 ,
2023-09-22 18:13:20 -04:00
/// The character's selected eyes.
2023-07-08 15:29:00 -04:00
pub eyes : u8 ,
2023-09-22 18:13:20 -04:00
/// The character's selected nose.
2023-07-08 15:29:00 -04:00
pub nose : u8 ,
2023-09-22 18:13:20 -04:00
/// The character's selected jaw.
2023-07-08 15:29:00 -04:00
pub jaw : u8 ,
2023-09-22 18:13:20 -04:00
/// The character's selected mouth.
2023-07-08 15:29:00 -04:00
pub mouth : u8 ,
2023-09-22 18:13:20 -04:00
/// The character's selected pattern.
2023-07-08 15:29:00 -04:00
pub lips_tone_fur_pattern : u8 ,
2023-09-22 18:13:20 -04:00
/// The character's selected tail.
2023-07-08 15:29:00 -04:00
pub tail : u8 ,
2023-09-22 18:13:20 -04:00
/// The character's choice of face paint.
2023-07-08 15:29:00 -04:00
pub face_paint : u8 ,
2023-09-22 18:13:20 -04:00
/// The size of the character's bust.
2023-07-08 15:29:00 -04:00
pub bust : u8 ,
2023-09-22 18:13:20 -04:00
/// The color of the face paint.
2023-07-08 15:29:00 -04:00
pub face_paint_color : u8 ,
2023-09-22 18:13:20 -04:00
/// The character's chosen voice.
2023-07-08 15:29:00 -04:00
pub voice : u8 ,
2023-09-22 18:13:20 -04:00
/// The timestamp when the preset was created.
2023-07-08 15:29:00 -04:00
#[ br(pad_before = 1) ]
pub timestamp : [ u8 ; 4 ]
}
2023-09-22 18:13:20 -04:00
impl CharacterData {
/// Parses existing character data.
2023-10-13 16:16:04 -04:00
pub fn from_existing ( buffer : ByteSpan ) -> Option < CharacterData > {
2023-07-08 15:29:00 -04:00
let mut cursor = Cursor ::new ( buffer ) ;
2023-09-22 18:13:20 -04:00
CharacterData ::read ( & mut cursor ) . ok ( )
2023-07-08 15:29:00 -04:00
}
2023-09-22 16:39:56 -04:00
2023-09-22 18:13:20 -04:00
/// Write existing character data to a buffer.
2023-10-13 16:16:04 -04:00
pub fn write_to_buffer ( & self ) -> Option < ByteBuffer > {
let mut buffer = ByteBuffer ::new ( ) ;
2023-09-22 16:39:56 -04:00
{
let cursor = Cursor ::new ( & mut buffer ) ;
let mut writer = BufWriter ::new ( cursor ) ;
2024-04-16 21:28:33 -04:00
self . write_le ( & mut writer ) . ok ( ) ? ;
2023-09-22 16:39:56 -04:00
}
Some ( buffer )
}
2024-04-16 21:43:15 -04:00
}
#[ cfg(test) ]
mod tests {
use std ::fs ::read ;
use std ::path ::PathBuf ;
use super ::* ;
#[ test ]
fn test_invalid ( ) {
let mut d = PathBuf ::from ( env! ( " CARGO_MANIFEST_DIR " ) ) ;
d . push ( " resources/tests " ) ;
d . push ( " random " ) ;
// Feeding it invalid data should not panic
CharacterData ::from_existing ( & read ( d ) . unwrap ( ) ) ;
}
}