Add documentation
This commit is contained in:
parent
447ad25c8a
commit
af8e66fa66
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -231,7 +231,7 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "skyrim_cell_dump"
|
name = "skyrim-cell-dump"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "skyrim_cell_dump"
|
name = "skyrim-cell-dump"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Tyler Hallada <tyler@hallada.net>"]
|
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
authors = ["Tyler Hallada <tyler@hallada.net>"]
|
||||||
|
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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
10
README.md
10
README.md
@ -7,7 +7,7 @@ The main objective of this library is to extract the form ID and X and Y coordin
|
|||||||
## Install
|
## Install
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo install skyrim_cell_dump
|
cargo install skyrim-cell-dump
|
||||||
```
|
```
|
||||||
|
|
||||||
Or, build yourself by checking out the repository and running:
|
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
|
## Import
|
||||||
|
|
||||||
You can include this crate in your `Cargo.toml` and get the parsed `Plugin` struct with:
|
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
|
```rust
|
||||||
use skyrim_cell_dump::parse_plugin;
|
use skyrim_cell_dump::parse_plugin;
|
||||||
|
|
||||||
fn main() {
|
let plugin_contents = std::fs::read("Plugin.esp").unwrap();
|
||||||
let plugin_contents = std::fs::read("Plugin.esp").unwrap();
|
let plugin = parse_plugin(&plugin_contents).unwrap();
|
||||||
let plugin = parse_plugin(&plugin_contents).unwrap();
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
@ -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]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
|
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
pub use parser::{decompress_cells, parse_cell, parse_plugin};
|
pub use parser::{parse_plugin, Cell, Plugin, PluginHeader};
|
||||||
|
@ -14,12 +14,16 @@ use serde::Serialize;
|
|||||||
|
|
||||||
const HEADER_SIZE: u32 = 24;
|
const HEADER_SIZE: u32 = 24;
|
||||||
|
|
||||||
|
/// A parsed TES5 Skyrim plugin file
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
pub struct Plugin<'a> {
|
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>,
|
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<Cell>,
|
pub cells: Vec<Cell>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parsed [TES4 header record](https://en.uesp.net/wiki/Skyrim_Mod:Mod_File_Format/TES4)
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
pub struct PluginHeader<'a> {
|
pub struct PluginHeader<'a> {
|
||||||
pub version: f32,
|
pub version: f32,
|
||||||
@ -30,12 +34,14 @@ pub struct PluginHeader<'a> {
|
|||||||
pub masters: Vec<&'a str>,
|
pub masters: Vec<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parsed [CELL records](https://en.uesp.net/wiki/Skyrim_Mod:Mod_File_Format/CELL)
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
pub struct Cell {
|
pub struct Cell {
|
||||||
pub form_id: u32,
|
pub form_id: u32,
|
||||||
pub editor_id: Option<String>,
|
pub editor_id: Option<String>,
|
||||||
pub x: Option<i32>,
|
pub x: Option<i32>,
|
||||||
pub y: Option<i32>,
|
pub y: Option<i32>,
|
||||||
|
/// Indicates that this cell is a special persistent worldspace cell where all persistent references for the worldspace are stored
|
||||||
pub is_persistent: bool,
|
pub is_persistent: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,8 +60,9 @@ pub struct UnparsedCell<'a> {
|
|||||||
data: &'a [u8],
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct DecompressedCell {
|
struct DecompressedCell {
|
||||||
pub form_id: u32,
|
pub form_id: u32,
|
||||||
pub is_persistent: bool,
|
pub is_persistent: bool,
|
||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
@ -122,11 +129,8 @@ struct FieldHeader<'a> {
|
|||||||
size: u16,
|
size: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_cell<'a>(
|
/// Parses fields from the decompressed bytes of a CELL record. Returns remaining bytes of the input after parsing and the parsed Cell struct.
|
||||||
input: &'a [u8],
|
fn parse_cell<'a>(input: &'a [u8], form_id: u32, is_persistent: bool) -> IResult<&'a [u8], Cell> {
|
||||||
form_id: u32,
|
|
||||||
is_persistent: bool,
|
|
||||||
) -> IResult<&'a [u8], Cell> {
|
|
||||||
let (input, cell_data) = parse_cell_fields(input)?;
|
let (input, cell_data) = parse_cell_fields(input)?;
|
||||||
Ok((
|
Ok((
|
||||||
input,
|
input,
|
||||||
@ -140,7 +144,8 @@ pub fn parse_cell<'a>(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decompress_cells(unparsed_cells: Vec<UnparsedCell>) -> Result<Vec<DecompressedCell>> {
|
/// 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<UnparsedCell>) -> Result<Vec<DecompressedCell>> {
|
||||||
let mut decompressed_cells = Vec::new();
|
let mut decompressed_cells = Vec::new();
|
||||||
for unparsed_cell in unparsed_cells {
|
for unparsed_cell in unparsed_cells {
|
||||||
let decompressed_data = if unparsed_cell.is_compressed {
|
let decompressed_data = if unparsed_cell.is_compressed {
|
||||||
@ -160,14 +165,27 @@ pub fn decompress_cells(unparsed_cells: Vec<UnparsedCell>) -> Result<Vec<Decompr
|
|||||||
Ok(decompressed_cells)
|
Ok(decompressed_cells)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_header_and_cell_bytes(
|
/// Parses the plugin header and finds and extracts the headers and unparsed (and possibly compressed) data sections of every CELL record in the file.
|
||||||
input: &[u8],
|
fn parse_header_and_cell_bytes(input: &[u8]) -> IResult<&[u8], (PluginHeader, Vec<UnparsedCell>)> {
|
||||||
) -> IResult<&[u8], (PluginHeader, Vec<UnparsedCell>)> {
|
|
||||||
let (input, header) = parse_plugin_header(input)?;
|
let (input, header) = parse_plugin_header(input)?;
|
||||||
let (input, unparsed_cells) = parse_group_data(input, input.len() as u32, 0)?;
|
let (input, unparsed_cells) = parse_group_data(input, input.len() as u32, 0)?;
|
||||||
Ok((input, (header, unparsed_cells)))
|
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<Plugin> {
|
pub fn parse_plugin(input: &[u8]) -> Result<Plugin> {
|
||||||
let (_, (header, unparsed_cells)) = parse_header_and_cell_bytes(&input)
|
let (_, (header, unparsed_cells)) = parse_header_and_cell_bytes(&input)
|
||||||
.map_err(|_err| anyhow!("Failed to parse plugin header and find CELL data"))?;
|
.map_err(|_err| anyhow!("Failed to parse plugin header and find CELL data"))?;
|
||||||
|
Loading…
Reference in New Issue
Block a user