1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright (c) 2019 FaultyRAM
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option. This file may not be copied,
// modified, or distributed except according to those terms.

//! Provides support for the Redshirt 1 and Redshirt 2 data encoding schemes.
//!
//! This crate provides utilities for reading and writing Redshirt 1- or Redshirt 2-encoded data.
//! The Redshirt encoding schemes are used in *Uplink*, a 2001 computer hacking simulation game
//! developed by Introversion Software.
//!
//! # Reading Redshirt data
//!
//! redshirt provides `v1::Reader` for reading Redshirt 1 streams, and `v2::Reader` for reading
//! Redshirt 2 streams:
//!
//! ```no_run
//! use redshirt::v1::Reader;
//! use std::{fs::OpenOptions, io::Read};
//!
//! fn main() {
//!     let file = OpenOptions::new().read(true).open("data.dat").unwrap();
//!     let mut reader = Reader::new(file).unwrap();
//!     let mut buffer = [u8::default(); 4];
//!     reader.read_exact(&mut buffer).unwrap();
//!     println!("{:#?}", buffer);
//! }
//! ```
//!
//! Both types offer the same features: support for reading and seeking via the standard `Read` and
//! `Seek` traits, and destructuring to the underlying reader via `Reader::into_inner`.
//!
//! # Writing Redshirt data
//!
//! redshirt provides `v1::Writer` for writing Redshirt 1 streams, and `v2::Writer` for writing
//! Redshirt 2 streams:
//!
//! ```no_run
//! use redshirt::v1::Writer;
//! use std::{fs::OpenOptions, io::Write};
//!
//! fn main() {
//!     let file = OpenOptions::new().write(true).open("data.dat").unwrap();
//!     let mut writer = Writer::new(file).unwrap();
//!     let data = b"foobar";
//!     writer.write_all(&data[..]).unwrap();
//! }
//! ```
//!
//! Both types support writing via the standard `Write` trait, and destructuring to the underlying
//! writer via `Writer::into_inner`. `v1::Writer` also supports seeking via the standard `Seek`
//! trait. (See below for why `v2::Writer` doesn't support seeking.)
//!
//! ## `v2::Writer` additional notes
//!
//! Redshirt 2 stores a [SHA-1] hash of the encoded data in the header. This means that using
//! `v2::Writer` (which writes Redshirt 2 data) has two implications:
//!
//! * Seeking isn't supported, because it's costly to implement; the data would need to be re-read,
//!   and possibly stored in heap memory, in order to generate a correct hash.
//! * Currently the SHA-1 hash is finalised and written into the header either when the `v2::Writer`
//!   is dropped, or when `v2::Writer::into_inner` is called.
//!   **The `drop` call will panic if an error occurs**, so it's highly recommended that you call
//!   `into_inner`, which returns a `Result<T, Error>` instead:
//!
//! ```no_run
//! use redshirt::v2::Writer;
//! use std::{fs::OpenOptions, io::Write};
//!
//! fn main() {
//!     let file = OpenOptions::new().write(true).open("User.usr").unwrap();
//!     let mut writer = Writer::new(file).unwrap();
//!     let data = b"foobar";
//!     writer.write_all(&data[..]).unwrap();
//!     let _ = writer.into_inner().unwrap(); // Triggers a panic if writing the checksum fails.
//! }
//! ```
//!
//! [SHA-1]: https://en.wikipedia.org/wiki/SHA-1

#![deny(
    warnings,
    future_incompatible,
    rust_2018_idioms,
    rustdoc,
    unused,
    missing_copy_implementations,
    missing_debug_implementations,
    missing_docs,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unused_results,
    clippy::all,
    clippy::pedantic
)]

#[cfg(any(feature = "redshirt1", feature = "redshirt2"))]
macro_rules! array {
    ($len:expr) => {
        array!(_, $len)
    };
    ($tyname:ty, $len:expr) => {
        [<$tyname>::default(); $len]
    };
}

#[cfg(any(feature = "redshirt1", feature = "redshirt2"))]
#[inline]
pub(crate) fn xor_bytes(bytes: &mut [u8]) {
    for n in bytes {
        *n ^= 0b1000_0000;
    }
}

#[cfg(any(feature = "redshirt1", feature = "redshirt2"))]
mod cursor;
#[cfg(any(feature = "redshirt1", feature = "redshirt2"))]
mod error;
#[cfg(any(feature = "redshirt1", feature = "redshirt2"))]
pub use error::Error;
#[cfg(feature = "redshirt1")]
pub mod v1;
#[cfg(feature = "redshirt2")]
pub mod v2;