1mod direct;
2pub mod error;
3mod header;
4mod jpeg;
5pub mod types;
6
7#[cfg(test)]
8mod tests;
9
10use super::types::*;
11use crate::path::make_mipmap_path;
12use direct::parse_direct_content;
13pub use error::{Error, LoadError};
14use header::parse_header;
15use jpeg::parse_jpeg_content;
16use nom::error::context;
17use std::path::{Path, PathBuf};
18use types::Parser;
19
20pub fn load_blp<Q>(path: Q) -> Result<BlpImage, LoadError>
22where
23 Q: AsRef<Path>,
24{
25 let input =
26 std::fs::read(&path).map_err(|e| LoadError::FileSystem(path.as_ref().to_owned(), e))?;
27 load_blp_ex(Some(path), &input)
28}
29
30pub fn load_blp_from_buf(buf: &[u8]) -> Result<BlpImage, LoadError> {
34 let input = buf;
35 let path: Option<PathBuf> = None;
36 load_blp_ex(path, input)
37}
38
39fn load_blp_ex<Q>(path: Option<Q>, input: &[u8]) -> Result<BlpImage, LoadError>
40where
41 Q: AsRef<Path>,
42{
43 let mut mipmaps = vec![];
46 if let Some(path) = path.as_ref() {
47 for i in 0..16 {
48 let mipmap_path = make_mipmap_path(path, i)
49 .ok_or_else(|| LoadError::InvalidFilename(path.as_ref().to_owned()))?;
50 if mipmap_path.is_file() {
51 let mipmap = std::fs::read(mipmap_path)
52 .map_err(|e| LoadError::FileSystem(path.as_ref().to_owned(), e))?;
53 mipmaps.push(mipmap);
54 } else {
55 break;
56 }
57 }
58 }
59
60 let image = match parse_blp_with_externals(input, |i| preloaded_mipmaps(&mipmaps, i)) {
61 Ok((_, image)) => Ok(image),
62 Err(nom::Err::Incomplete(needed)) => Err(LoadError::Incomplete(needed)),
63 Err(nom::Err::Error(e)) => Err(LoadError::Parsing(format!("{}", e))),
64 Err(nom::Err::Failure(e)) => Err(LoadError::Parsing(format!("{}", e))),
65 }?;
66 Ok(image)
67}
68
69pub fn parse_blp(input: &[u8]) -> Parser<BlpImage> {
71 parse_blp_with_externals(input, no_mipmaps)
72}
73
74pub fn no_mipmaps<'a>(_: usize) -> Result<Option<&'a [u8]>, Box<dyn std::error::Error>> {
76 Ok(None)
77}
78
79pub fn preloaded_mipmaps(
82 mipmaps: &[Vec<u8>],
83 i: usize,
84) -> Result<Option<&[u8]>, Box<dyn std::error::Error>> {
85 if i >= mipmaps.len() {
86 Ok(None)
87 } else {
88 Ok(Some(&mipmaps[i]))
89 }
90}
91
92pub fn parse_blp_with_externals<'a, F>(
94 root_input: &'a [u8],
95 external_mipmaps: F,
96) -> Parser<'a, BlpImage>
97where
98 F: FnMut(usize) -> Result<Option<&'a [u8]>, Box<dyn std::error::Error>> + Clone,
99{
100 let (input, header) = context("header", parse_header)(root_input)?;
102
103 let (input, content) = context("image content", |input| {
105 parse_content(&header, external_mipmaps.clone(), root_input, input)
106 })(input)?;
107
108 Ok((input, BlpImage { header, content }))
109}
110
111fn parse_content<'a, F>(
112 blp_header: &BlpHeader,
113 external_mipmaps: F,
114 original_input: &'a [u8],
115 input: &'a [u8],
116) -> Parser<'a, BlpContent>
117where
118 F: FnMut(usize) -> Result<Option<&'a [u8]>, Box<dyn std::error::Error>> + Clone,
119{
120 match blp_header.content {
121 BlpContentTag::Jpeg => {
122 let (input, content) = context("jpeg content", |input| {
123 parse_jpeg_content(blp_header, external_mipmaps.clone(), original_input, input)
124 })(input)?;
125 Ok((input, BlpContent::Jpeg(content)))
126 }
127 BlpContentTag::Direct => {
128 let (input, content) = context("direct content", |input| {
129 parse_direct_content(blp_header, external_mipmaps.clone(), original_input, input)
130 })(input)?;
131 Ok((input, content))
132 }
133 }
134}