#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(
clippy::all,
clippy::correctness,
clippy::suspicious,
clippy::style,
clippy::complexity,
clippy::perf
)]
#[macro_use]
pub mod util;
pub mod checksum;
pub mod definition;
pub mod error;
pub mod extension;
pub mod loader;
#[doc(inline)]
pub use error::Error;
use error::Result;
use checksum::Checksum;
#[cfg(feature = "rs3")]
use checksum::{RsaChecksum, RsaKeys};
use runefs::codec::{Buffer, Decoded, Encoded};
use runefs::error::{Error as RuneFsError, ReadError};
use runefs::{ArchiveRef, Dat2, Indices, MAIN_DATA};
use std::{io::Write, path::Path};
#[derive(Debug)]
pub struct Cache {
data: Dat2,
pub(crate) indices: Indices,
}
impl Cache {
pub fn new<P: AsRef<Path>>(path: P) -> crate::Result<Self> {
Ok(Self {
data: Dat2::new(path.as_ref().join(MAIN_DATA))?,
indices: Indices::new(path)?,
})
}
pub fn checksum(&self) -> crate::Result<Checksum> {
Checksum::new(self)
}
#[cfg(feature = "rs3")]
#[cfg_attr(docsrs, doc(cfg(feature = "rs3")))]
pub fn checksum_with<'a>(&self, keys: RsaKeys<'a>) -> crate::Result<RsaChecksum<'a>> {
RsaChecksum::with_keys(self, keys)
}
pub fn read(&self, index_id: u8, archive_id: u32) -> crate::Result<Buffer<Encoded>> {
let index = self
.indices
.get(&index_id)
.ok_or(RuneFsError::Read(ReadError::IndexNotFound(index_id)))?;
let archive = index
.archive_refs
.get(&archive_id)
.ok_or(RuneFsError::Read(ReadError::ArchiveNotFound {
idx: index_id,
arc: archive_id,
}))?;
let buffer = self.data.read(archive)?;
assert_eq!(buffer.len(), archive.length);
Ok(buffer)
}
pub(crate) fn read_archive(&self, archive: &ArchiveRef) -> crate::Result<Buffer<Encoded>> {
self.read(archive.index_id, archive.id)
}
pub fn read_into_writer<W: Write>(
&self,
index_id: u8,
archive_id: u32,
writer: &mut W,
) -> crate::Result<()> {
let index = self
.indices
.get(&index_id)
.ok_or(RuneFsError::Read(ReadError::IndexNotFound(index_id)))?;
let archive = index
.archive_refs
.get(&archive_id)
.ok_or(RuneFsError::Read(ReadError::ArchiveNotFound {
idx: index_id,
arc: archive_id,
}))?;
Ok(self.data.read_into_writer(archive, writer)?)
}
pub fn huffman_table(&self) -> crate::Result<Buffer<Decoded>> {
let index_id = 10;
let archive = self.archive_by_name(index_id, "huffman")?;
let buffer = self.read_archive(archive)?;
assert_eq!(buffer.len(), archive.length);
Ok(buffer.decode()?)
}
pub(crate) fn archive_by_name<T: AsRef<str>>(
&self,
index_id: u8,
name: T,
) -> crate::Result<&ArchiveRef> {
let index = self
.indices
.get(&index_id)
.ok_or(RuneFsError::Read(ReadError::IndexNotFound(index_id)))?;
let hash = util::djd2::hash(&name);
let archive = index
.metadata
.iter()
.find(|archive| archive.name_hash == hash)
.ok_or_else(|| crate::error::NameHashMismatch {
hash,
name: name.as_ref().into(),
idx: index_id,
})?;
let archive_ref = index
.archive_refs
.get(&archive.id)
.ok_or(RuneFsError::Read(ReadError::ArchiveNotFound {
idx: index_id,
arc: archive.id,
}))?;
Ok(archive_ref)
}
}
#[cfg(test)]
mod test_util {
use super::Cache;
use sha1::Sha1;
fn is_normal<T: Send + Sync + Sized + Unpin>() {}
#[test]
fn normal_types() {
is_normal::<super::Cache>();
}
pub fn osrs_cache() -> crate::Result<Cache> {
Cache::new("./data/osrs_cache")
}
#[cfg(all(test, feature = "rs3"))]
pub fn rs3_cache() -> crate::Result<Cache> {
Cache::new("./data/rs3_cache")
}
pub fn hash(buffer: &[u8]) -> String {
let mut m = Sha1::new();
m.update(buffer);
m.digest().to_string()
}
}
#[cfg(test)]
mod read {
mod osrs {
use crate::test_util;
#[test]
fn ref_table() -> crate::Result<()> {
let cache = test_util::osrs_cache()?;
let buffer = cache.read(255, 10)?;
let hash = test_util::hash(&buffer);
assert_eq!(&hash, "9a9a50a0bd25d6246ad5fe3ebc5b14e9a5df7227");
assert_eq!(buffer.len(), 77);
Ok(())
}
#[test]
fn random_read() -> crate::Result<()> {
let cache = test_util::osrs_cache()?;
let buffer = cache.read(0, 191)?;
let hash = test_util::hash(&buffer);
assert_eq!(&hash, "cd459f6ccfbd81c1e3bfadf899624f2519e207a9");
assert_eq!(buffer.len(), 2055);
Ok(())
}
#[test]
fn large_read() -> crate::Result<()> {
let cache = test_util::osrs_cache()?;
let buffer = cache.read(2, 10)?;
let hash = test_util::hash(&buffer);
assert_eq!(&hash, "6d3396a9d5a7729b3b8069ac32e384e5da58096b");
assert_eq!(buffer.len(), 282396);
Ok(())
}
#[test]
fn deep_archive() -> crate::Result<()> {
let cache = test_util::osrs_cache()?;
let buffer = cache.read(7, 24918)?;
let hash = test_util::hash(&buffer);
assert_eq!(&hash, "fe91e9e9170a5a05ed2684c1db1169aa7ef4906e");
assert_eq!(buffer.len(), 803);
Ok(())
}
#[test]
fn single_data_len() -> crate::Result<()> {
let cache = test_util::osrs_cache()?;
let buffer = cache.read(3, 278)?;
let hash = test_util::hash(&buffer);
assert_eq!(&hash, "036abb64d3f1734d892f69b1253a87639b7bcb44");
assert_eq!(buffer.len(), 512);
Ok(())
}
#[test]
fn double_data_len() -> crate::Result<()> {
let cache = test_util::osrs_cache()?;
let buffer = cache.read(0, 1077)?;
let hash = test_util::hash(&buffer);
assert_eq!(&hash, "fbe9d365cf0c3efa94e0d4a2c5e607b28a1279b9");
assert_eq!(buffer.len(), 1024);
Ok(())
}
#[test]
fn fails() -> crate::Result<()> {
let cache = test_util::osrs_cache()?;
assert!(cache.read(2, 25_000).is_err());
Ok(())
}
}
#[cfg(feature = "rs3")]
mod rs3 {
use crate::test_util;
#[test]
fn random_0_read() -> crate::Result<()> {
let cache = test_util::rs3_cache()?;
let buffer = cache.read(0, 25)?;
let hash = test_util::hash(&buffer);
assert_eq!(&hash, "81e455fc58fe5ac98fee4df5b78600bbf43e83f7");
assert_eq!(buffer.len(), 1576);
Ok(())
}
#[test]
fn between_single_double() -> crate::Result<()> {
let cache = test_util::rs3_cache()?;
let buffer = cache.read(7, 0)?;
let hash = test_util::hash(&buffer);
assert_eq!(&hash, "b33919c6e4677abc6ec1c0bdd9557f820a163559");
assert_eq!(buffer.len(), 529);
Ok(())
}
#[test]
fn fails() -> crate::Result<()> {
let cache = test_util::rs3_cache()?;
assert!(cache.read(2, 25_000).is_err());
Ok(())
}
}
}
#[cfg(test)]
mod osrs {
use super::test_util;
use super::Cache;
#[test]
fn new() {
assert!(Cache::new("./data/osrs_cache").is_ok());
}
#[test]
fn new_wrong_path() {
assert!(Cache::new("./wrong/path").is_err());
}
#[test]
fn huffman_table() -> crate::Result<()> {
let cache = test_util::osrs_cache()?;
let buffer = cache.huffman_table()?;
let hash = test_util::hash(&buffer);
assert_eq!(&hash, "664e89cf25a0af7da138dd0f3904ca79cd1fe767");
assert_eq!(buffer.len(), 256);
Ok(())
}
}
#[cfg(all(test, feature = "rs3"))]
mod rs3 {
use super::test_util;
use super::Cache;
#[test]
fn new() {
assert!(Cache::new("./data/rs3_cache").is_ok());
}
#[test]
fn new_wrong_path() {
assert!(Cache::new("./wrong/path").is_err());
}
#[test]
fn huffman_table() -> crate::Result<()> {
let cache = test_util::rs3_cache()?;
let buffer = cache.huffman_table()?;
let hash = test_util::hash(&buffer);
assert_eq!(&hash, "664e89cf25a0af7da138dd0f3904ca79cd1fe767");
assert_eq!(buffer.len(), 256);
Ok(())
}
}