use rayon::prelude::*;
use ron::de::from_bytes;
use serde_derive::{Serialize, Deserialize};
use std::collections::{BTreeMap, HashMap};
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::Path;
use crate::error::Result;
use crate::schema::Schema as SchemaV5;
use crate::schema::Definition as DefinitionV5;
use crate::schema::FieldType as FieldTypeV5;
use crate::schema::Field as FieldV5;
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub struct SchemaV4 {
version: u16,
versioned_files: Vec<VersionedFileV4>
}
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub enum VersionedFileV4 {
AnimFragment(Vec<DefinitionV4>),
AnimTable(Vec<DefinitionV4>),
DB(String, Vec<DefinitionV4>),
DepManager(Vec<DefinitionV4>),
Loc(Vec<DefinitionV4>),
MatchedCombat(Vec<DefinitionV4>),
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Debug, Default, Serialize, Deserialize)]
pub struct DefinitionV4 {
version: i32,
fields: Vec<FieldV4>,
localised_fields: Vec<FieldV4>,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Debug, Serialize, Deserialize)]
pub struct FieldV4 {
pub name: String,
pub field_type: FieldTypeV4,
pub is_key: bool,
pub default_value: Option<String>,
pub is_filename: bool,
pub filename_relative_path: Option<String>,
pub is_reference: Option<(String, String)>,
pub lookup: Option<Vec<String>>,
pub description: String,
pub ca_order: i16,
pub is_bitwise: i32,
pub enum_values: BTreeMap<i32, String>,
pub is_part_of_colour: Option<u8>,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Debug, Serialize, Deserialize)]
pub enum FieldTypeV4 {
Boolean,
F32,
F64,
I16,
I32,
I64,
ColourRGB,
StringU8,
StringU16,
OptionalStringU8,
OptionalStringU16,
SequenceU16(Box<DefinitionV4>),
SequenceU32(Box<DefinitionV4>)
}
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Default)]
pub struct SchemaPatches {
patches: HashMap<String, SchemaPatch>
}
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Default)]
pub struct SchemaPatch{
tables: HashMap<String, HashMap<String, HashMap<String, String>>>,
}
impl SchemaV4 {
pub fn load(path: &Path) -> Result<Self> {
let mut file = BufReader::new(File::open(path)?);
let mut data = Vec::with_capacity(file.get_ref().metadata()?.len() as usize);
file.read_to_end(&mut data)?;
from_bytes(&data).map_err(From::from)
}
pub fn update(schema_path: &Path, patches_path: &Path, game_name: &str) -> Result<()> {
let schema_legacy = Self::load(schema_path)?;
let mut schema = SchemaV5::from(&schema_legacy);
schema.definitions.par_iter_mut().for_each(|(table_name, definitions)| {
definitions.iter_mut().for_each(|definition| {
definition.fields.iter_mut().for_each(|field| {
if let Some((ref_table, ref_column)) = field.is_reference(None) {
if ref_table.trim().is_empty() || ref_column.trim().is_empty() {
dbg!(&table_name);
dbg!(field.name());
field.is_reference = None;
}
}
})
})
});
let schema_patches = SchemaPatches::load(patches_path);
if let Ok(schema_patches) = schema_patches {
if let Some(patches) = schema_patches.patches.get(game_name) {
schema.patches = patches.tables.clone();
}
}
schema.save(schema_path)?;
Ok(())
}
}
impl DefinitionV4 {
pub fn new(version: i32) -> DefinitionV4 {
DefinitionV4 {
version,
localised_fields: vec![],
fields: vec![],
}
}
pub fn version(&self) -> i32 {
self.version
}
pub fn fields_mut(&mut self) -> &mut Vec<FieldV4> {
&mut self.fields
}
pub fn localised_fields_mut(&mut self) -> &mut Vec<FieldV4> {
&mut self.localised_fields
}
}
impl Default for FieldV4 {
fn default() -> Self {
Self {
name: String::from("new_field"),
field_type: FieldTypeV4::StringU8,
is_key: false,
default_value: None,
is_filename: false,
filename_relative_path: None,
is_reference: None,
lookup: None,
description: String::from(""),
ca_order: -1,
is_bitwise: 0,
enum_values: BTreeMap::new(),
is_part_of_colour: None,
}
}
}
impl Default for SchemaV4 {
fn default() -> Self {
Self {
version: 3,
versioned_files: vec![]
}
}
}
impl From<&SchemaV4> for SchemaV5 {
fn from(legacy_schema: &SchemaV4) -> Self {
let mut schema = Self::default();
legacy_schema.versioned_files.iter()
.filter_map(|versioned| if let VersionedFileV4::DB(name, definitions) = versioned { Some((name, definitions)) } else { None })
.for_each(|(name, definitions)| {
definitions.iter().for_each(|definition| {
schema.add_definition(name, &From::from(definition));
})
});
schema
}
}
impl From<&DefinitionV4> for DefinitionV5 {
fn from(legacy_table_definition: &DefinitionV4) -> Self {
let mut definition = Self::new(legacy_table_definition.version, None);
let fields = legacy_table_definition.fields.iter().map(From::from).collect::<Vec<FieldV5>>();
definition.set_fields(fields);
let fields = legacy_table_definition.localised_fields.iter().map(From::from).collect::<Vec<FieldV5>>();
definition.set_localised_fields(fields);
definition
}
}
impl From<&FieldV4> for FieldV5 {
fn from(legacy_field: &FieldV4) -> Self {
Self {
name: legacy_field.name.to_owned(),
field_type: From::from(&legacy_field.field_type),
is_key: legacy_field.is_key,
default_value: legacy_field.default_value.clone(),
is_filename: legacy_field.is_filename,
filename_relative_path: legacy_field.filename_relative_path.clone(),
is_reference: legacy_field.is_reference.clone(),
lookup: legacy_field.lookup.clone(),
description: legacy_field.description.to_owned(),
ca_order: legacy_field.ca_order,
..Default::default()
}
}
}
impl From<&FieldTypeV4> for FieldTypeV5 {
fn from(legacy_field_type: &FieldTypeV4) -> Self {
match legacy_field_type {
FieldTypeV4::Boolean => Self::Boolean,
FieldTypeV4::I16 => Self::I16,
FieldTypeV4::I32 => Self::I32,
FieldTypeV4::I64 => Self::I64,
FieldTypeV4::F32 => Self::F32,
FieldTypeV4::F64 => Self::F64,
FieldTypeV4::ColourRGB => Self::ColourRGB,
FieldTypeV4::StringU8 => Self::StringU8,
FieldTypeV4::StringU16 => Self::StringU16,
FieldTypeV4::OptionalStringU8 => Self::OptionalStringU8,
FieldTypeV4::OptionalStringU16 => Self::OptionalStringU16,
FieldTypeV4::SequenceU16(sequence) => Self::SequenceU16(Box::new(From::from(&**sequence))),
FieldTypeV4::SequenceU32(sequence) => Self::SequenceU32(Box::new(From::from(&**sequence))),
}
}
}
impl SchemaPatches {
pub fn load(file_path: &Path) -> Result<Self> {
let mut file = BufReader::new(File::open(file_path)?);
let mut data = Vec::with_capacity(file.get_ref().metadata()?.len() as usize);
file.read_to_end(&mut data)?;
from_bytes(&data).map_err(From::from)
}
}