mirror of
https://github.com/redstrate/Physis.git
synced 2025-04-20 03:37:47 +00:00
Calculate the correct checksum when writing character saved data
This commit is contained in:
parent
445a90a0fa
commit
f89eed5dc6
1 changed files with 163 additions and 131 deletions
282
src/chardat.rs
282
src/chardat.rs
|
@ -95,21 +95,11 @@ fn convert_subrace_dat(subrace: &Subrace) -> u8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[br(little)]
|
#[br(little)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[brw(magic = 0x2013FF14u32)]
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CharacterData {
|
pub struct CustomizeData {
|
||||||
/// The "version" of the game this was created with.
|
|
||||||
/// Always corresponds to the released expansion at the time, e.g. A Realm Reborn character will have a version of 1. A Shadowbringers character will have a version of 4.
|
|
||||||
pub version: u32,
|
|
||||||
|
|
||||||
/// The checksum of the data fields.
|
|
||||||
#[brw(pad_after = 4)]
|
|
||||||
pub checksum: u32,
|
|
||||||
|
|
||||||
/// The race of the character.
|
/// The race of the character.
|
||||||
#[br(map = convert_dat_race)]
|
#[br(map = convert_dat_race)]
|
||||||
#[bw(map = convert_race_dat)]
|
#[bw(map = convert_race_dat)]
|
||||||
|
@ -183,10 +173,10 @@ pub struct CharacterData {
|
||||||
pub lips_tone_fur_pattern: u8,
|
pub lips_tone_fur_pattern: u8,
|
||||||
|
|
||||||
/// Depending on the race, it's either the ear size, muscle size, or tail size.
|
/// Depending on the race, it's either the ear size, muscle size, or tail size.
|
||||||
pub ear_muscle_tail_size: u8,
|
pub race_feature_size: u8,
|
||||||
|
|
||||||
/// Depending on the race, it's the selected tail or ears.
|
/// Depending on the race, it's the selected tail or ears.
|
||||||
pub tail_ears_type: u8,
|
pub race_feature_type: u8,
|
||||||
|
|
||||||
/// The size of the character's bust from 0 to 100.
|
/// The size of the character's bust from 0 to 100.
|
||||||
pub bust: u8,
|
pub bust: u8,
|
||||||
|
@ -200,6 +190,26 @@ pub struct CharacterData {
|
||||||
|
|
||||||
/// The character's chosen voice.
|
/// The character's chosen voice.
|
||||||
pub voice: u8,
|
pub voice: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
#[binrw]
|
||||||
|
#[br(little)]
|
||||||
|
#[repr(C)]
|
||||||
|
#[brw(magic = 0x2013FF14u32)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CharacterData {
|
||||||
|
/// The "version" of the game this was created with.
|
||||||
|
/// Always corresponds to the released expansion at the time, e.g. A Realm Reborn character will have a version of 1. A Shadowbringers character will have a version of 4.
|
||||||
|
pub version: u32,
|
||||||
|
|
||||||
|
/// The checksum of the data fields.
|
||||||
|
// TODO: should we re-expose this checksum?
|
||||||
|
#[brw(pad_after = 4)]
|
||||||
|
#[bw(calc = self.calc_checksum())]
|
||||||
|
pub checksum: u32,
|
||||||
|
|
||||||
|
pub customize: CustomizeData,
|
||||||
|
|
||||||
/// The timestamp when the preset was created.
|
/// The timestamp when the preset was created.
|
||||||
/// This is a UTC time in seconds since the Unix epoch.
|
/// This is a UTC time in seconds since the Unix epoch.
|
||||||
|
@ -235,6 +245,32 @@ impl CharacterData {
|
||||||
|
|
||||||
Some(buffer)
|
Some(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn calc_checksum(&self) -> u32 {
|
||||||
|
let mut buffer = ByteBuffer::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let cursor = Cursor::new(&mut buffer);
|
||||||
|
let mut writer = BufWriter::new(cursor);
|
||||||
|
|
||||||
|
self.customize.write_le(&mut writer).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The checksum also considers the timestamp and the comment
|
||||||
|
buffer.push(0x00);
|
||||||
|
buffer.extend_from_slice(&self.timestamp.to_le_bytes());
|
||||||
|
|
||||||
|
let mut comment = write_string(&self.comment);
|
||||||
|
comment.resize(164, 0);
|
||||||
|
buffer.extend_from_slice(&comment);
|
||||||
|
|
||||||
|
let mut checksum: u32 = 0;
|
||||||
|
for (i, byte) in buffer.iter().enumerate() {
|
||||||
|
checksum ^= (*byte as u32) << (i % 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -267,34 +303,33 @@ mod tests {
|
||||||
let chardat = common_setup("arr.dat");
|
let chardat = common_setup("arr.dat");
|
||||||
|
|
||||||
assert_eq!(chardat.version, 1);
|
assert_eq!(chardat.version, 1);
|
||||||
assert_eq!(chardat.checksum, 747334988);
|
assert_eq!(chardat.customize.race, Race::Hyur);
|
||||||
assert_eq!(chardat.race, Race::Hyur);
|
assert_eq!(chardat.customize.gender, Gender::Male);
|
||||||
assert_eq!(chardat.gender, Gender::Male);
|
assert_eq!(chardat.customize.age, 1);
|
||||||
assert_eq!(chardat.age, 1);
|
assert_eq!(chardat.customize.height, 50);
|
||||||
assert_eq!(chardat.height, 50);
|
assert_eq!(chardat.customize.subrace, Subrace::Midlander);
|
||||||
assert_eq!(chardat.subrace, Subrace::Midlander);
|
assert_eq!(chardat.customize.face, 5);
|
||||||
assert_eq!(chardat.face, 5);
|
assert_eq!(chardat.customize.hair, 1);
|
||||||
assert_eq!(chardat.hair, 1);
|
assert_eq!(chardat.customize.enable_highlights, false);
|
||||||
assert_eq!(chardat.enable_highlights, false);
|
assert_eq!(chardat.customize.skin_tone, 2);
|
||||||
assert_eq!(chardat.skin_tone, 2);
|
assert_eq!(chardat.customize.right_eye_color, 37);
|
||||||
assert_eq!(chardat.right_eye_color, 37);
|
assert_eq!(chardat.customize.hair_tone, 53);
|
||||||
assert_eq!(chardat.hair_tone, 53);
|
assert_eq!(chardat.customize.highlights, 0);
|
||||||
assert_eq!(chardat.highlights, 0);
|
assert_eq!(chardat.customize.facial_features, 2);
|
||||||
assert_eq!(chardat.facial_features, 2);
|
assert_eq!(chardat.customize.facial_feature_color, 2);
|
||||||
assert_eq!(chardat.facial_feature_color, 2);
|
assert_eq!(chardat.customize.eyebrows, 0);
|
||||||
assert_eq!(chardat.eyebrows, 0);
|
assert_eq!(chardat.customize.left_eye_color, 37);
|
||||||
assert_eq!(chardat.left_eye_color, 37);
|
assert_eq!(chardat.customize.eyes, 0);
|
||||||
assert_eq!(chardat.eyes, 0);
|
assert_eq!(chardat.customize.nose, 0);
|
||||||
assert_eq!(chardat.nose, 0);
|
assert_eq!(chardat.customize.jaw, 0);
|
||||||
assert_eq!(chardat.jaw, 0);
|
assert_eq!(chardat.customize.mouth, 0);
|
||||||
assert_eq!(chardat.mouth, 0);
|
assert_eq!(chardat.customize.lips_tone_fur_pattern, 43);
|
||||||
assert_eq!(chardat.lips_tone_fur_pattern, 43);
|
assert_eq!(chardat.customize.race_feature_size, 50);
|
||||||
assert_eq!(chardat.ear_muscle_tail_size, 50);
|
assert_eq!(chardat.customize.race_feature_type, 0);
|
||||||
assert_eq!(chardat.tail_ears_type, 0);
|
assert_eq!(chardat.customize.bust, 0);
|
||||||
assert_eq!(chardat.bust, 0);
|
assert_eq!(chardat.customize.face_paint_color, 36);
|
||||||
assert_eq!(chardat.face_paint_color, 36);
|
assert_eq!(chardat.customize.face_paint, 0);
|
||||||
assert_eq!(chardat.face_paint, 0);
|
assert_eq!(chardat.customize.voice, 1);
|
||||||
assert_eq!(chardat.voice, 1);
|
|
||||||
assert_eq!(chardat.comment, "Custom Comment Text");
|
assert_eq!(chardat.comment, "Custom Comment Text");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,34 +338,33 @@ mod tests {
|
||||||
let chardat = common_setup("heavensward.dat");
|
let chardat = common_setup("heavensward.dat");
|
||||||
|
|
||||||
assert_eq!(chardat.version, 2);
|
assert_eq!(chardat.version, 2);
|
||||||
assert_eq!(chardat.checksum, 781137881);
|
assert_eq!(chardat.customize.race, Race::AuRa);
|
||||||
assert_eq!(chardat.race, Race::AuRa);
|
assert_eq!(chardat.customize.gender, Gender::Female);
|
||||||
assert_eq!(chardat.gender, Gender::Female);
|
assert_eq!(chardat.customize.age, 1);
|
||||||
assert_eq!(chardat.age, 1);
|
assert_eq!(chardat.customize.height, 50);
|
||||||
assert_eq!(chardat.height, 50);
|
assert_eq!(chardat.customize.subrace, Subrace::Xaela);
|
||||||
assert_eq!(chardat.subrace, Subrace::Xaela);
|
assert_eq!(chardat.customize.face, 3);
|
||||||
assert_eq!(chardat.face, 3);
|
assert_eq!(chardat.customize.hair, 5);
|
||||||
assert_eq!(chardat.hair, 5);
|
assert_eq!(chardat.customize.enable_highlights, false);
|
||||||
assert_eq!(chardat.enable_highlights, false);
|
assert_eq!(chardat.customize.skin_tone, 160);
|
||||||
assert_eq!(chardat.skin_tone, 160);
|
assert_eq!(chardat.customize.right_eye_color, 91);
|
||||||
assert_eq!(chardat.right_eye_color, 91);
|
assert_eq!(chardat.customize.hair_tone, 159);
|
||||||
assert_eq!(chardat.hair_tone, 159);
|
assert_eq!(chardat.customize.highlights, 0);
|
||||||
assert_eq!(chardat.highlights, 0);
|
assert_eq!(chardat.customize.facial_features, 127);
|
||||||
assert_eq!(chardat.facial_features, 127);
|
assert_eq!(chardat.customize.facial_feature_color, 99);
|
||||||
assert_eq!(chardat.facial_feature_color, 99);
|
assert_eq!(chardat.customize.eyebrows, 0);
|
||||||
assert_eq!(chardat.eyebrows, 0);
|
assert_eq!(chardat.customize.left_eye_color, 91);
|
||||||
assert_eq!(chardat.left_eye_color, 91);
|
assert_eq!(chardat.customize.eyes, 0);
|
||||||
assert_eq!(chardat.eyes, 0);
|
assert_eq!(chardat.customize.nose, 0);
|
||||||
assert_eq!(chardat.nose, 0);
|
assert_eq!(chardat.customize.jaw, 0);
|
||||||
assert_eq!(chardat.jaw, 0);
|
assert_eq!(chardat.customize.mouth, 0);
|
||||||
assert_eq!(chardat.mouth, 0);
|
assert_eq!(chardat.customize.lips_tone_fur_pattern, 0);
|
||||||
assert_eq!(chardat.lips_tone_fur_pattern, 0);
|
assert_eq!(chardat.customize.race_feature_size, 50);
|
||||||
assert_eq!(chardat.ear_muscle_tail_size, 50);
|
assert_eq!(chardat.customize.race_feature_type, 1);
|
||||||
assert_eq!(chardat.tail_ears_type, 1);
|
assert_eq!(chardat.customize.bust, 25);
|
||||||
assert_eq!(chardat.bust, 25);
|
assert_eq!(chardat.customize.face_paint_color, 0);
|
||||||
assert_eq!(chardat.face_paint_color, 0);
|
assert_eq!(chardat.customize.face_paint, 0);
|
||||||
assert_eq!(chardat.face_paint, 0);
|
assert_eq!(chardat.customize.voice, 112);
|
||||||
assert_eq!(chardat.voice, 112);
|
|
||||||
assert_eq!(chardat.comment, "Heavensward Comment Text");
|
assert_eq!(chardat.comment, "Heavensward Comment Text");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,34 +373,33 @@ mod tests {
|
||||||
let chardat = common_setup("stormblood.dat");
|
let chardat = common_setup("stormblood.dat");
|
||||||
|
|
||||||
assert_eq!(chardat.version, 3);
|
assert_eq!(chardat.version, 3);
|
||||||
assert_eq!(chardat.checksum, 564060181);
|
assert_eq!(chardat.customize.race, Race::Lalafell);
|
||||||
assert_eq!(chardat.race, Race::Lalafell);
|
assert_eq!(chardat.customize.gender, Gender::Male);
|
||||||
assert_eq!(chardat.gender, Gender::Male);
|
assert_eq!(chardat.customize.age, 1);
|
||||||
assert_eq!(chardat.age, 1);
|
assert_eq!(chardat.customize.height, 50);
|
||||||
assert_eq!(chardat.height, 50);
|
assert_eq!(chardat.customize.subrace, Subrace::Plainsfolk);
|
||||||
assert_eq!(chardat.subrace, Subrace::Plainsfolk);
|
assert_eq!(chardat.customize.face, 1);
|
||||||
assert_eq!(chardat.face, 1);
|
assert_eq!(chardat.customize.hair, 8);
|
||||||
assert_eq!(chardat.hair, 8);
|
assert_eq!(chardat.customize.enable_highlights, false);
|
||||||
assert_eq!(chardat.enable_highlights, false);
|
assert_eq!(chardat.customize.skin_tone, 25);
|
||||||
assert_eq!(chardat.skin_tone, 25);
|
assert_eq!(chardat.customize.right_eye_color, 11);
|
||||||
assert_eq!(chardat.right_eye_color, 11);
|
assert_eq!(chardat.customize.hair_tone, 45);
|
||||||
assert_eq!(chardat.hair_tone, 45);
|
assert_eq!(chardat.customize.highlights, 0);
|
||||||
assert_eq!(chardat.highlights, 0);
|
assert_eq!(chardat.customize.facial_features, 0);
|
||||||
assert_eq!(chardat.facial_features, 0);
|
assert_eq!(chardat.customize.facial_feature_color, 2);
|
||||||
assert_eq!(chardat.facial_feature_color, 2);
|
assert_eq!(chardat.customize.eyebrows, 0);
|
||||||
assert_eq!(chardat.eyebrows, 0);
|
assert_eq!(chardat.customize.left_eye_color, 11);
|
||||||
assert_eq!(chardat.left_eye_color, 11);
|
assert_eq!(chardat.customize.eyes, 0);
|
||||||
assert_eq!(chardat.eyes, 0);
|
assert_eq!(chardat.customize.nose, 0);
|
||||||
assert_eq!(chardat.nose, 0);
|
assert_eq!(chardat.customize.jaw, 0);
|
||||||
assert_eq!(chardat.jaw, 0);
|
assert_eq!(chardat.customize.mouth, 0);
|
||||||
assert_eq!(chardat.mouth, 0);
|
assert_eq!(chardat.customize.lips_tone_fur_pattern, 43);
|
||||||
assert_eq!(chardat.lips_tone_fur_pattern, 43);
|
assert_eq!(chardat.customize.race_feature_size, 25);
|
||||||
assert_eq!(chardat.ear_muscle_tail_size, 25);
|
assert_eq!(chardat.customize.race_feature_type, 2);
|
||||||
assert_eq!(chardat.tail_ears_type, 2);
|
assert_eq!(chardat.customize.bust, 0);
|
||||||
assert_eq!(chardat.bust, 0);
|
assert_eq!(chardat.customize.face_paint_color, 36);
|
||||||
assert_eq!(chardat.face_paint_color, 36);
|
assert_eq!(chardat.customize.face_paint, 0);
|
||||||
assert_eq!(chardat.face_paint, 0);
|
assert_eq!(chardat.customize.voice, 19);
|
||||||
assert_eq!(chardat.voice, 19);
|
|
||||||
assert_eq!(chardat.comment, "Stormblood Comment Text");
|
assert_eq!(chardat.comment, "Stormblood Comment Text");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,34 +408,33 @@ mod tests {
|
||||||
let chardat = common_setup("shadowbringers.dat");
|
let chardat = common_setup("shadowbringers.dat");
|
||||||
|
|
||||||
assert_eq!(chardat.version, 4);
|
assert_eq!(chardat.version, 4);
|
||||||
assert_eq!(chardat.checksum, 404034306);
|
assert_eq!(chardat.customize.race, Race::Viera);
|
||||||
assert_eq!(chardat.race, Race::Viera);
|
assert_eq!(chardat.customize.gender, Gender::Female);
|
||||||
assert_eq!(chardat.gender, Gender::Female);
|
assert_eq!(chardat.customize.age, 1);
|
||||||
assert_eq!(chardat.age, 1);
|
assert_eq!(chardat.customize.height, 50);
|
||||||
assert_eq!(chardat.height, 50);
|
assert_eq!(chardat.customize.subrace, Subrace::Rava);
|
||||||
assert_eq!(chardat.subrace, Subrace::Rava);
|
assert_eq!(chardat.customize.face, 1);
|
||||||
assert_eq!(chardat.face, 1);
|
assert_eq!(chardat.customize.hair, 8);
|
||||||
assert_eq!(chardat.hair, 8);
|
assert_eq!(chardat.customize.enable_highlights, false);
|
||||||
assert_eq!(chardat.enable_highlights, false);
|
assert_eq!(chardat.customize.skin_tone, 12);
|
||||||
assert_eq!(chardat.skin_tone, 12);
|
assert_eq!(chardat.customize.right_eye_color, 43);
|
||||||
assert_eq!(chardat.right_eye_color, 43);
|
assert_eq!(chardat.customize.hair_tone, 53);
|
||||||
assert_eq!(chardat.hair_tone, 53);
|
assert_eq!(chardat.customize.highlights, 0);
|
||||||
assert_eq!(chardat.highlights, 0);
|
assert_eq!(chardat.customize.facial_features, 4);
|
||||||
assert_eq!(chardat.facial_features, 4);
|
assert_eq!(chardat.customize.facial_feature_color, 0);
|
||||||
assert_eq!(chardat.facial_feature_color, 0);
|
assert_eq!(chardat.customize.eyebrows, 2);
|
||||||
assert_eq!(chardat.eyebrows, 2);
|
assert_eq!(chardat.customize.left_eye_color, 43);
|
||||||
assert_eq!(chardat.left_eye_color, 43);
|
assert_eq!(chardat.customize.eyes, 131);
|
||||||
assert_eq!(chardat.eyes, 131);
|
assert_eq!(chardat.customize.nose, 2);
|
||||||
assert_eq!(chardat.nose, 2);
|
assert_eq!(chardat.customize.jaw, 1);
|
||||||
assert_eq!(chardat.jaw, 1);
|
assert_eq!(chardat.customize.mouth, 131);
|
||||||
assert_eq!(chardat.mouth, 131);
|
assert_eq!(chardat.customize.lips_tone_fur_pattern, 171);
|
||||||
assert_eq!(chardat.lips_tone_fur_pattern, 171);
|
assert_eq!(chardat.customize.race_feature_size, 50);
|
||||||
assert_eq!(chardat.ear_muscle_tail_size, 50);
|
assert_eq!(chardat.customize.race_feature_type, 2);
|
||||||
assert_eq!(chardat.tail_ears_type, 2);
|
assert_eq!(chardat.customize.bust, 100);
|
||||||
assert_eq!(chardat.bust, 100);
|
assert_eq!(chardat.customize.face_paint_color, 131);
|
||||||
assert_eq!(chardat.face_paint_color, 131);
|
assert_eq!(chardat.customize.face_paint, 3);
|
||||||
assert_eq!(chardat.face_paint, 3);
|
assert_eq!(chardat.customize.voice, 160);
|
||||||
assert_eq!(chardat.voice, 160);
|
|
||||||
assert_eq!(chardat.comment, "Shadowbringers Comment Text");
|
assert_eq!(chardat.comment, "Shadowbringers Comment Text");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue