use std::iter::IntoIterator;
use std::slice::Iter;
use crate::{error::ValidateError, Cache};
use nom::{combinator::cond, number::complete::be_u32};
use runefs::{
codec::{Buffer, Encoded},
REFERENCE_TABLE_ID,
};
#[cfg(feature = "rs3")]
use num_bigint::{BigInt, Sign};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "rs3")]
use whirlpool::{Digest, Whirlpool};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(not(feature = "rs3"), derive(Default))]
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Entry {
pub(crate) crc: u32,
pub(crate) version: u32,
#[cfg(feature = "rs3")]
pub(crate) hash: Vec<u8>,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct Checksum {
index_count: usize,
entries: Vec<Entry>,
}
impl Checksum {
pub fn new(cache: &Cache) -> crate::Result<Self> {
Ok(Self {
index_count: cache.indices.count(),
entries: Self::entries(cache)?,
})
}
fn entries(cache: &Cache) -> crate::Result<Vec<Entry>> {
let entries: Vec<Entry> = (0..cache.indices.count())
.into_iter()
.filter_map(|idx_id| cache.read(REFERENCE_TABLE_ID, idx_id as u32).ok())
.enumerate()
.map(|(idx_id, buffer)| -> crate::Result<Entry> {
if buffer.is_empty() || idx_id == 47 {
Ok(Entry::default())
} else {
#[cfg(feature = "rs3")]
let hash = {
let mut hasher = Whirlpool::new();
hasher.update(&buffer);
hasher.finalize().as_slice().to_vec()
};
let checksum = crc32fast::hash(&buffer);
let data = buffer.decode()?;
let (_, version) = cond(data[0] >= 6, be_u32)(&data[1..5])?;
let version = version.unwrap_or(0);
Ok(Entry {
crc: checksum,
version,
#[cfg(feature = "rs3")]
hash,
})
}
})
.filter_map(crate::Result::ok)
.collect();
Ok(entries)
}
pub fn encode(self) -> crate::Result<Buffer<Encoded>> {
let mut buffer = Vec::with_capacity(self.entries.len() * 8);
for entry in self.entries {
buffer.extend(u32::to_be_bytes(entry.crc));
buffer.extend(u32::to_be_bytes(entry.version));
}
Ok(Buffer::from(buffer).encode()?)
}
pub fn validate<'b, I>(&self, crcs: I) -> Result<(), ValidateError>
where
I: IntoIterator<Item = &'b u32>,
<I as IntoIterator>::IntoIter: ExactSizeIterator,
{
let crcs = crcs.into_iter();
if self.entries.len() != crcs.len() {
return Err(ValidateError::InvalidLength {
expected: self.entries.len(),
actual: crcs.len(),
});
}
for (index, (internal, external)) in self
.entries
.iter()
.map(|entry| &entry.crc)
.zip(crcs)
.enumerate()
{
if internal != external {
return Err(ValidateError::InvalidCrc {
idx: index,
internal: *internal,
external: *external,
});
}
}
Ok(())
}
#[allow(missing_docs)]
#[inline]
pub const fn index_count(&self) -> usize {
self.index_count
}
#[allow(missing_docs)]
#[inline]
pub fn iter(&self) -> Iter<'_, Entry> {
self.entries.iter()
}
}
#[cfg(feature = "rs3")]
#[cfg_attr(docsrs, doc(cfg(feature = "rs3")))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct RsaKeys<'a> {
pub(crate) exponent: &'a [u8],
pub(crate) modulus: &'a [u8],
}
#[cfg(feature = "rs3")]
#[cfg_attr(docsrs, doc(cfg(feature = "rs3")))]
impl<'a> RsaKeys<'a> {
pub const fn new(exponent: &'a [u8], modulus: &'a [u8]) -> Self {
Self { exponent, modulus }
}
pub fn encrypt(&self, hash: &[u8]) -> Vec<u8> {
let exp = BigInt::parse_bytes(self.exponent, 10).unwrap_or_default();
let mud = BigInt::parse_bytes(self.modulus, 10).unwrap_or_default();
BigInt::from_bytes_be(Sign::Plus, hash)
.modpow(&exp, &mud)
.to_bytes_be()
.1
}
}
#[cfg(feature = "rs3")]
#[cfg_attr(docsrs, doc(cfg(feature = "rs3")))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct RsaChecksum<'a> {
checksum: Checksum,
#[cfg_attr(feature = "serde", serde(borrow))]
rsa_keys: RsaKeys<'a>,
}
#[cfg(feature = "rs3")]
impl<'a> RsaChecksum<'a> {
pub fn with_keys(cache: &Cache, rsa_keys: RsaKeys<'a>) -> crate::Result<Self> {
Ok(Self {
checksum: Checksum::new(cache)?,
rsa_keys,
})
}
pub fn encode(self) -> crate::Result<Buffer<Encoded>> {
let index_count = self.checksum.index_count - 1;
let mut buffer = vec![0; 81 * index_count];
buffer[0] = index_count as u8;
for (index, entry) in self.checksum.entries.iter().enumerate() {
let offset = index * 80;
buffer[offset + 1..=offset + 4].copy_from_slice(&u32::to_be_bytes(entry.crc));
buffer[offset + 5..=offset + 8].copy_from_slice(&u32::to_be_bytes(entry.version));
buffer[offset + 9..=offset + 12].copy_from_slice(&u32::to_be_bytes(0));
buffer[offset + 13..=offset + 16].copy_from_slice(&u32::to_be_bytes(0));
buffer[offset + 17..=offset + 80].copy_from_slice(&entry.hash);
}
let mut hasher = Whirlpool::new();
hasher.update(&buffer);
let mut hash = hasher.finalize().as_slice().to_vec();
hash.insert(0, 0);
buffer.extend(self.rsa_keys.encrypt(&hash));
Ok(Buffer::from(buffer))
}
}
#[cfg(feature = "rs3")]
impl<'a> From<(&'a [u8], &'a [u8])> for RsaKeys<'a> {
fn from(keys: (&'a [u8], &'a [u8])) -> Self {
RsaKeys::new(keys.0, keys.1)
}
}
impl IntoIterator for Checksum {
type Item = Entry;
type IntoIter = std::vec::IntoIter<Entry>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.entries.into_iter()
}
}
impl<'a> IntoIterator for &'a Checksum {
type Item = &'a Entry;
type IntoIter = Iter<'a, Entry>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.entries.iter()
}
}
#[cfg(feature = "rs3")]
impl<'a> IntoIterator for RsaChecksum<'a> {
type Item = Entry;
type IntoIter = std::vec::IntoIter<Entry>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.checksum.entries.into_iter()
}
}
#[cfg(feature = "rs3")]
impl<'a> IntoIterator for &'a RsaChecksum<'a> {
type Item = &'a Entry;
type IntoIter = Iter<'a, Entry>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.checksum.entries.iter()
}
}
#[cfg(feature = "rs3")]
impl Default for Entry {
#[inline]
fn default() -> Self {
Self {
crc: 0,
version: 0,
hash: vec![0; 64],
}
}
}