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;