use std::{
fs,
io::{Cursor, Seek, SeekFrom, Write},
path::Path,
};
use binrw::{BinReaderExt, BinResult, BinWrite, Endian, binread};
use galaxy_save_core::{
mem::Checksum,
save::{CheckSaveFileError, SaveFileHeader},
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
mod user_file;
pub use user_file::{SaveDataUserFile, SaveDataUserFileInfo};
#[binread]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug)]
pub struct SaveDataFile {
#[br(temp)]
header: SaveDataFileHeader,
#[br(count = header.user_file_info_num)]
pub user_file_info: Vec<SaveDataUserFileInfo>,
}
impl SaveDataFile {
pub fn read_be_file<P: AsRef<Path>>(path: P) -> BinResult<Self> {
let mut reader = Cursor::new(fs::read(path)?);
reader.read_be()
}
pub fn read_le_file<P: AsRef<Path>>(path: P) -> BinResult<Self> {
let mut reader = Cursor::new(fs::read(path)?);
reader.read_le()
}
pub fn write_be_file<P: AsRef<Path>>(&self, path: P) -> BinResult<()> {
let mut writer = Cursor::new(Vec::with_capacity(
SaveDataFileHeader::FILE_SIZE_MAX as usize,
));
self.write_be(&mut writer)?;
writer.rewind()?;
let buf = &writer.get_ref()[size_of::<Checksum>()..];
let checksum = Checksum::from_be_bytes(buf);
checksum.write_be(&mut writer)?;
fs::write(path, writer.get_mut())?;
Ok(())
}
pub fn write_le_file<P: AsRef<Path>>(&self, path: P) -> BinResult<()> {
let mut writer = Cursor::new(Vec::with_capacity(
SaveDataFileHeader::FILE_SIZE_MAX as usize,
));
self.write_le(&mut writer)?;
writer.rewind()?;
let buf = &writer.get_ref()[size_of::<Checksum>()..];
let checksum = Checksum::from_le_bytes(buf);
checksum.write_le(&mut writer)?;
fs::write(path, writer.get_mut())?;
Ok(())
}
pub fn check_be_file<P: AsRef<Path>>(path: P) -> Result<(), CheckSaveFileError> {
let mut reader = Cursor::new(fs::read(path)?);
let header = reader.read_be::<SaveDataFileHeader>()?;
header.check_be(reader.into_inner().as_slice())
}
pub fn check_le_file<P: AsRef<Path>>(path: P) -> Result<(), CheckSaveFileError> {
let mut reader = Cursor::new(fs::read(path)?);
let header = reader.read_le::<SaveDataFileHeader>()?;
header.check_le(reader.into_inner().as_slice())
}
}
impl BinWrite for SaveDataFile {
type Args<'a> = ();
fn write_options<W: Write + Seek>(
&self,
writer: &mut W,
endian: Endian,
_args: Self::Args<'_>,
) -> BinResult<()> {
writer.seek(SeekFrom::Start(size_of::<SaveDataFileHeader>() as u64))?;
let mut data_offset = writer.stream_position()? as u32
+ self.user_file_info.len() as u32 * SaveDataUserFileInfo::data_size() as u32;
for user_file_info in &self.user_file_info {
user_file_info.write_options(writer, endian, (data_offset,))?;
let pos = writer.stream_position()?;
data_offset = writer.seek(SeekFrom::End(0))? as u32;
writer.seek(SeekFrom::Start(pos))?;
}
let version = SaveDataFileHeader::VERSION;
let file_info_num = self.user_file_info.len() as u32;
let file_size = writer.seek(SeekFrom::End(0))? as u32;
writer.seek(SeekFrom::Start(size_of::<Checksum>() as u64))?;
version.write_options(writer, endian, ())?;
file_info_num.write_options(writer, endian, ())?;
file_size.write_options(writer, endian, ())?;
Ok(())
}
}
#[binread]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SaveDataFileHeader {
pub checksum: Checksum,
pub version: u32,
pub user_file_info_num: u32,
pub file_size: u32,
}
impl SaveFileHeader for SaveDataFileHeader {
const VERSION: u32 = 3;
const USER_FILE_INFO_MAX: u32 = 14;
const FILE_SIZE_MAX: u32 = 0x7FFF;
fn checksum(&self) -> Checksum {
self.checksum
}
fn version(&self) -> u32 {
self.version
}
fn user_file_info_num(&self) -> u32 {
self.user_file_info_num
}
fn file_size(&self) -> u32 {
self.file_size
}
}