Skip to content

Commit

Permalink
Upgrade to nom v5
Browse files Browse the repository at this point in the history
  • Loading branch information
Ortham committed Jun 25, 2019
1 parent 752bd48 commit adcd218
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 131 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ coveralls = { repository = "Ortham/esplugin" }
byteorder = "1.2.0"
encoding = "0.2.33"
memmap = "0.7.0"
nom = "4.0.0"
nom = "5.0.0"
unicase = "2.0.0"
flate2 = { version = "1.0.1", optional = true }

Expand Down
4 changes: 2 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ pub enum Error {
DecodeError(Cow<'static, str>),
}

impl<I> From<Err<I>> for Error {
fn from(error: Err<I>) -> Self {
impl<E> From<Err<E>> for Error {
fn from(error: Err<E>) -> Self {
match error {
Err::Incomplete(_) => Error::ParsingIncomplete,
_ => Error::ParsingError,
Expand Down
47 changes: 24 additions & 23 deletions src/group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@

use std::mem;

use nom::{le_u32, IResult};
use nom::bytes::complete::{tag, take};
use nom::combinator::{map, peek};
use nom::multi::length_data;
use nom::number::complete::le_u32;
use nom::sequence::delimited;
use nom::IResult;

use game_id::GameId;
use record::Record;
Expand All @@ -36,13 +41,10 @@ impl Group {
game_id: GameId,
form_ids: &mut Vec<u32>,
) -> IResult<&'a [u8], ()> {
do_parse!(
input,
length_value!(
apply!(parse_header, game_id),
apply!(parse_records, game_id, form_ids)
) >> ()
)
let (remaining_input, records_data) = length_data(parse_header(game_id))(input)?;
parse_records(records_data, game_id, form_ids)?;

Ok((remaining_input, ()))
}
}

Expand All @@ -53,17 +55,17 @@ fn get_header_length_to_skip(game_id: GameId) -> u8 {
}
}

fn parse_header(input: &[u8], game_id: GameId) -> IResult<&[u8], u32> {
let skip_length = get_header_length_to_skip(game_id);
let group_header_length = GROUP_TYPE.len() as u8 + mem::size_of::<u32>() as u8 + skip_length;

do_parse!(
input,
tag!(GROUP_TYPE)
>> group_size: le_u32
>> take!(skip_length)
>> (group_size - u32::from(group_header_length))
)
fn parse_header(game_id: GameId) -> impl Fn(&[u8]) -> IResult<&[u8], u32> {
move |input| {
let skip_length = get_header_length_to_skip(game_id);
let group_header_length =
GROUP_TYPE.len() as u8 + mem::size_of::<u32>() as u8 + skip_length;

map(
delimited(tag(GROUP_TYPE), le_u32, take(skip_length)),
move |group_size| group_size - u32::from(group_header_length),
)(input)
}
}

fn parse_records<'a>(
Expand All @@ -74,14 +76,13 @@ fn parse_records<'a>(
let mut input1 = input;

while !input1.is_empty() {
let (_, next_type) = try_parse!(input1, peek!(take!(GROUP_TYPE.len())));
let (_, next_type) = peek(take(GROUP_TYPE.len()))(input1)?;

if next_type == GROUP_TYPE {
let (input2, _) =
try_parse!(input1, apply!(Group::parse_for_form_ids, game_id, form_ids));
let (input2, _) = Group::parse_for_form_ids(input1, game_id, form_ids)?;
input1 = input2;
} else {
let (input2, record_id) = try_parse!(input1, apply!(Record::parse_record_id, game_id));
let (input2, record_id) = Record::parse_record_id(input1, game_id)?;
input1 = input2;
if let Some(RecordId::FormId(form_id)) = record_id {
form_ids.push(form_id.get());
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
extern crate byteorder;
extern crate encoding;
extern crate memmap;
#[macro_use]
extern crate nom;
extern crate unicase;

Expand Down
8 changes: 5 additions & 3 deletions src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use byteorder::{ByteOrder, LittleEndian};
use encoding::all::WINDOWS_1252;
use encoding::{DecoderTrap, Encoding};

use nom::{self, ErrorKind, IResult};
use nom::{self, IResult};

use memmap::Mmap;

Expand Down Expand Up @@ -397,8 +397,10 @@ fn parse_record_ids<'a>(
if game_id == GameId::Morrowind {
parse_morrowind_record_ids(input)
} else {
// Map to the Alpha error kind even though it's misleading, because it
// doesn't actually matter what is chosen, the detail is discarded.
let masters = masters(&header_record)
.map_err(|_| nom::Err::Error(nom::Context::Code(input, ErrorKind::Custom(1))))?;
.map_err(|_| nom::Err::Error((input, nom::error::ErrorKind::Alpha)))?;

let (remaining_input, form_ids) = parse_form_ids(input, game_id)?;
let form_ids = hashed_form_ids(&form_ids, filename, &masters).into();
Expand All @@ -413,7 +415,7 @@ fn parse_plugin<'a>(
filename: &str,
load_header_only: bool,
) -> IResult<&'a [u8], PluginData> {
let (input1, header_record) = try_parse!(input, apply!(Record::parse, game_id, false));
let (input1, header_record) = Record::parse(input, game_id, false)?;

if load_header_only {
return Ok((
Expand Down
129 changes: 65 additions & 64 deletions src/record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,28 @@
* You should have received a copy of the GNU General Public License
* along with esplugin. If not, see <http://www.gnu.org/licenses/>.
*/
use std::convert::TryInto;
use std::io;
use std::num::NonZeroU32;

use byteorder::{ByteOrder, LittleEndian};
use nom::bytes::complete::take;
use nom::combinator::{cond, map};
use nom::number::complete::le_u32;
use nom::sequence::{delimited, pair, terminated, tuple};
use nom::IResult;
use nom::{le_u32, le_u8};

use error::Error;
use game_id::GameId;
use record_id::{NamespacedId, RecordId};
use subrecord::{parse_subrecord_data_as_u32, Subrecord, SubrecordRef};
use subrecord::{parse_subrecord_data_as_u32, Subrecord, SubrecordRef, SubrecordType};

const RECORD_TYPE_LENGTH: usize = 4;
pub type RecordType = [u8; 4];

#[derive(Clone, PartialEq, Eq, Debug, Hash, Default)]
pub struct RecordHeader {
record_type: [u8; 4],
record_type: RecordType,
flags: u32,
form_id: Option<NonZeroU32>,
size_of_subrecords: u32,
Expand Down Expand Up @@ -84,14 +89,14 @@ impl Record {

pub fn parse_record_id(input: &[u8], game_id: GameId) -> IResult<&[u8], Option<RecordId>> {
if game_id == GameId::Morrowind {
let (remaining_input, header) = try_parse!(input, apply!(record_header, game_id));
let (remaining_input, header) = record_header(input, game_id)?;
let (remaining_input, subrecords_data) =
try_parse!(remaining_input, take!(header.size_of_subrecords));
take(header.size_of_subrecords)(remaining_input)?;

// Header is parsed, now parse subrecords until the id subrecord has been found.
let types = record_id_subrecord_types(header.record_type);
if !types.is_empty() {
let subrecords = try_parse!(subrecords_data, apply!(parse_id_subrecords, types)).1;
let subrecords = parse_id_subrecords(subrecords_data, types)?.1;
let data = record_id_subrecord_mapper(header.record_type, &subrecords);

let namespaced_id = data.map(|data| {
Expand All @@ -103,17 +108,24 @@ impl Record {
Ok((remaining_input, None))
}
} else {
do_parse!(
input,
take!(RECORD_TYPE_LENGTH)
>> size_of_subrecords: le_u32
>> take!(4)
>> form_id: le_u32
>> take!(4)
>> cond!(game_id != GameId::Oblivion, take!(4))
>> take!(size_of_subrecords)
>> (NonZeroU32::new(form_id).map(RecordId::FormId))
)
let parser = tuple((
delimited(take(RECORD_TYPE_LENGTH), le_u32, take(4usize)),
terminated(le_u32, take(4usize)),
));

let (remaining_input, (size_of_subrecords, form_id)) = parser(input)?;

let parser = pair(
cond(game_id != GameId::Oblivion, take(4usize)),
take(size_of_subrecords),
);

let (remaining_input, _) = parser(remaining_input)?;

Ok((
remaining_input,
NonZeroU32::new(form_id).map(RecordId::FormId),
))
}
}

Expand Down Expand Up @@ -182,7 +194,7 @@ fn map_pgrd_id_data<'a>(subrecords: &[SubrecordRef<'a>]) -> Option<&'a [u8]> {
}

fn record_id_subrecord_mapper<'a>(
record_type: [u8; 4],
record_type: RecordType,
subrecords: &[SubrecordRef<'a>],
) -> Option<&'a [u8]> {
match &record_type {
Expand All @@ -193,7 +205,7 @@ fn record_id_subrecord_mapper<'a>(
}
}

fn record_id_subrecord_types(record_type: [u8; 4]) -> Vec<&'static [u8; 4]> {
fn record_id_subrecord_types(record_type: RecordType) -> Vec<&'static SubrecordType> {
match &record_type {
b"GMST" | b"GLOB" | b"CLAS" | b"FACT" | b"RACE" | b"SOUN" | b"REGN" | b"BSGN" | b"LTEX"
| b"STAT" | b"DOOR" | b"MISC" | b"WEAP" | b"CONT" | b"SPEL" | b"CREA" | b"BODY"
Expand All @@ -219,49 +231,47 @@ fn header_length(game_id: GameId) -> usize {
}
}

named!(
record_type<[u8; 4]>,
count_fixed!(u8, le_u8, RECORD_TYPE_LENGTH)
);

named_args!(record_header(game_id: GameId) <RecordHeader>,
do_parse!(
record_type: record_type >>
size_of_subrecords: le_u32 >>
cond!(game_id == GameId::Morrowind, take!(4)) >>
flags: le_u32 >>
form_id: cond!(game_id != GameId::Morrowind, le_u32) >>
cond!(game_id != GameId::Morrowind, take!(4)) >>
cond!(game_id != GameId::Morrowind && game_id != GameId::Oblivion, take!(4)) >>

(RecordHeader {
fn record_type(input: &[u8]) -> IResult<&[u8], RecordType> {
map(take(RECORD_TYPE_LENGTH), |s: &[u8]| {
s.try_into()
.expect("record type slice should be the required length")
})(input)
}

fn record_header(input: &[u8], game_id: GameId) -> IResult<&[u8], RecordHeader> {
map(
tuple((
record_type,
le_u32,
cond(game_id == GameId::Morrowind, take(4usize)),
le_u32,
cond(game_id != GameId::Morrowind, le_u32),
cond(game_id != GameId::Morrowind, take(4usize)),
cond(
game_id != GameId::Morrowind && game_id != GameId::Oblivion,
take(4usize),
),
)),
|(record_type, size_of_subrecords, _, flags, form_id, _, _)| RecordHeader {
record_type,
flags,
form_id: form_id.and_then(NonZeroU32::new),
size_of_subrecords,
})
)
);
},
)(input)
}

fn record(input: &[u8], game_id: GameId, skip_subrecords: bool) -> IResult<&[u8], Record> {
let (input1, header) = try_parse!(input, apply!(record_header, game_id));
let (input2, subrecords_data) = try_parse!(input1, take!(header.size_of_subrecords));
let (remaining_input, header) = record_header(input, game_id)?;
let (remaining_input, subrecords_data) = take(header.size_of_subrecords)(remaining_input)?;

let subrecords: Vec<Subrecord> = if !skip_subrecords {
try_parse!(
subrecords_data,
apply!(
parse_subrecords,
game_id,
header.are_subrecords_compressed()
)
)
.1
parse_subrecords(subrecords_data, game_id, header.are_subrecords_compressed())?.1
} else {
Vec::new()
};

Ok((input2, Record { header, subrecords }))
Ok((remaining_input, Record { header, subrecords }))
}

fn parse_subrecords(
Expand All @@ -274,15 +284,9 @@ fn parse_subrecords(
let mut large_subrecord_size: u32 = 0;

while !input1.is_empty() {
let (input2, subrecord) = try_parse!(
input1,
apply!(
Subrecord::new,
game_id,
large_subrecord_size,
are_compressed
)
);
let (input2, subrecord) =
Subrecord::new(input1, game_id, large_subrecord_size, are_compressed)?;

if subrecord.subrecord_type() == b"XXXX" {
large_subrecord_size = parse_subrecord_data_as_u32(input1)?.1;
} else {
Expand All @@ -299,7 +303,7 @@ fn parse_subrecords(
/// skips to the end of the record and returns.
fn parse_id_subrecords<'a>(
input: &'a [u8],
mut target_subrecord_types: Vec<&[u8; 4]>,
mut target_subrecord_types: Vec<&SubrecordType>,
) -> IResult<&'a [u8], Vec<SubrecordRef<'a>>> {
let mut remaining_input: &[u8] = input;
let mut subrecords = Vec::with_capacity(target_subrecord_types.len());
Expand All @@ -308,10 +312,7 @@ fn parse_id_subrecords<'a>(
if target_subrecord_types.is_empty() {
break;
}
let (input2, subrecord) = try_parse!(
remaining_input,
apply!(SubrecordRef::new, GameId::Morrowind, 0)
);
let (input2, subrecord) = SubrecordRef::new(remaining_input, GameId::Morrowind, 0)?;
remaining_input = input2;
if let Some(index) = target_subrecord_types
.iter()
Expand Down
Loading

0 comments on commit adcd218

Please sign in to comment.