From af8e66fa666c9c2ead9385abcf0bba6ad22f564e Mon Sep 17 00:00:00 2001 From: Tyler Hallada Date: Thu, 20 May 2021 20:47:50 -0400 Subject: [PATCH] Add documentation --- Cargo.lock | 2 +- Cargo.toml | 8 ++++++-- README.md | 10 +++++----- src/lib.rs | 5 ++++- src/parser.rs | 38 ++++++++++++++++++++++++++++---------- 5 files changed, 44 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7cdb018..f9dea1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -231,7 +231,7 @@ dependencies = [ ] [[package]] -name = "skyrim_cell_dump" +name = "skyrim-cell-dump" version = "0.1.0" dependencies = [ "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 543c7e5..d50875d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,12 @@ [package] -name = "skyrim_cell_dump" +name = "skyrim-cell-dump" version = "0.1.0" -authors = ["Tyler Hallada "] edition = "2018" +authors = ["Tyler Hallada "] +description = "Library and binary for parsing Skyrim plugin files and extracing CELL data" +repository = "https://github.com/thallada/skyrim-cell-dump" +readme = "README.md" +license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/README.md b/README.md index 2946525..a47e26b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ The main objective of this library is to extract the form ID and X and Y coordin ## Install ``` -cargo install skyrim_cell_dump +cargo install skyrim-cell-dump ``` Or, build yourself by checking out the repository and running: @@ -87,6 +87,8 @@ The pretty JSON format looks something like: } ``` +Note: I have only tested parsing Skyrim Special Edition `.esp`, `.esm`, and `.esl` files. + ## Import You can include this crate in your `Cargo.toml` and get the parsed `Plugin` struct with: @@ -94,8 +96,6 @@ You can include this crate in your `Cargo.toml` and get the parsed `Plugin` stru ```rust use skyrim_cell_dump::parse_plugin; -fn main() { - let plugin_contents = std::fs::read("Plugin.esp").unwrap(); - let plugin = parse_plugin(&plugin_contents).unwrap(); -} +let plugin_contents = std::fs::read("Plugin.esp").unwrap(); +let plugin = parse_plugin(&plugin_contents).unwrap(); ``` diff --git a/src/lib.rs b/src/lib.rs index 77c7e19..e7b91bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,9 @@ +//! # Skyrim Cell Dump +//! +//! `skyrim-cell-dump` is a library for parsing Skyrim plugin files and extracing CELL data into Rust structs. #[macro_use] extern crate bitflags; mod parser; -pub use parser::{decompress_cells, parse_cell, parse_plugin}; +pub use parser::{parse_plugin, Cell, Plugin, PluginHeader}; diff --git a/src/parser.rs b/src/parser.rs index eefdea4..29e8974 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -14,12 +14,16 @@ use serde::Serialize; const HEADER_SIZE: u32 = 24; +/// A parsed TES5 Skyrim plugin file #[derive(Debug, PartialEq, Serialize)] pub struct Plugin<'a> { + /// Parsed [TES4 header record](https://en.uesp.net/wiki/Skyrim_Mod:Mod_File_Format/TES4) with metadata about the plugin pub header: PluginHeader<'a>, + /// Parsed [CELL records](https://en.uesp.net/wiki/Skyrim_Mod:Mod_File_Format/CELL) contained in the plugin pub cells: Vec, } +/// Parsed [TES4 header record](https://en.uesp.net/wiki/Skyrim_Mod:Mod_File_Format/TES4) #[derive(Debug, PartialEq, Serialize)] pub struct PluginHeader<'a> { pub version: f32, @@ -30,12 +34,14 @@ pub struct PluginHeader<'a> { pub masters: Vec<&'a str>, } +/// Parsed [CELL records](https://en.uesp.net/wiki/Skyrim_Mod:Mod_File_Format/CELL) #[derive(Debug, PartialEq, Serialize)] pub struct Cell { pub form_id: u32, pub editor_id: Option, pub x: Option, pub y: Option, + /// Indicates that this cell is a special persistent worldspace cell where all persistent references for the worldspace are stored pub is_persistent: bool, } @@ -54,8 +60,9 @@ pub struct UnparsedCell<'a> { data: &'a [u8], } +/// A CELL record that has had it's header parsed and data decompressed, but not yet parsed into individual fields #[derive(Debug)] -pub struct DecompressedCell { +struct DecompressedCell { pub form_id: u32, pub is_persistent: bool, pub data: Vec, @@ -122,11 +129,8 @@ struct FieldHeader<'a> { size: u16, } -pub fn parse_cell<'a>( - input: &'a [u8], - form_id: u32, - is_persistent: bool, -) -> IResult<&'a [u8], Cell> { +/// Parses fields from the decompressed bytes of a CELL record. Returns remaining bytes of the input after parsing and the parsed Cell struct. +fn parse_cell<'a>(input: &'a [u8], form_id: u32, is_persistent: bool) -> IResult<&'a [u8], Cell> { let (input, cell_data) = parse_cell_fields(input)?; Ok(( input, @@ -140,7 +144,8 @@ pub fn parse_cell<'a>( )) } -pub fn decompress_cells(unparsed_cells: Vec) -> Result> { +/// Maps the input `UnparsedCell`s to `DecompressedCell`s and decompresses the zlib compressed data sections of the record if necessary +fn decompress_cells(unparsed_cells: Vec) -> Result> { let mut decompressed_cells = Vec::new(); for unparsed_cell in unparsed_cells { let decompressed_data = if unparsed_cell.is_compressed { @@ -160,14 +165,27 @@ pub fn decompress_cells(unparsed_cells: Vec) -> Result IResult<&[u8], (PluginHeader, Vec)> { +/// Parses the plugin header and finds and extracts the headers and unparsed (and possibly compressed) data sections of every CELL record in the file. +fn parse_header_and_cell_bytes(input: &[u8]) -> IResult<&[u8], (PluginHeader, Vec)> { let (input, header) = parse_plugin_header(input)?; let (input, unparsed_cells) = parse_group_data(input, input.len() as u32, 0)?; Ok((input, (header, unparsed_cells))) } +/// Parses header and cell records from input bytes of a plugin file and outputs `Plugin` struct with extracted fields. +/// +/// # Arguments +/// +/// * `input` - A slice of bytes read from the plugin file +/// +/// # Examples +/// +/// ``` +/// use skyrim_cell_dump::parse_plugin; +/// +/// let plugin_contents = std::fs::read("Plugin.esp").unwrap(); +/// let plugin = parse_plugin(&plugin_contents).unwrap(); +/// ``` pub fn parse_plugin(input: &[u8]) -> Result { let (_, (header, unparsed_cells)) = parse_header_and_cell_bytes(&input) .map_err(|_err| anyhow!("Failed to parse plugin header and find CELL data"))?;