|
@ -97,7 +97,6 @@ fn read_dat_section_header<T: Read + Seek>(cursor: &mut T, episode: &Episode) -> |
|
|
fn quest_episode(bin: &[u8]) -> Option<Episode> {
|
|
|
fn quest_episode(bin: &[u8]) -> Option<Episode> {
|
|
|
for bytes in bin.windows(3) {
|
|
|
for bytes in bin.windows(3) {
|
|
|
if bytes[0] == 0xF8 && bytes[1] == 0xBC {
|
|
|
if bytes[0] == 0xF8 && bytes[1] == 0xBC {
|
|
|
warn!("ep? {:?}", bytes[2]);
|
|
|
|
|
|
return Some(Episode::from_quest(bytes[2]).ok()?)
|
|
|
return Some(Episode::from_quest(bytes[2]).ok()?)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@ -136,6 +135,10 @@ fn parse_dat(dat: &[u8], episode: &Episode) -> Result<(Vec<Option<MapEnemy>>, Ve |
|
|
#[error("")]
|
|
|
#[error("")]
|
|
|
enum QuestLoadError {
|
|
|
enum QuestLoadError {
|
|
|
ParseDatError(#[from] ParseDatError),
|
|
|
ParseDatError(#[from] ParseDatError),
|
|
|
|
|
|
CouldNotReadMetadata,
|
|
|
|
|
|
CouldNotLoadConfigFile,
|
|
|
|
|
|
QuestFileNotFound(String),
|
|
|
|
|
|
CouldNotLoadFile(String),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
#[derive(Debug)]
|
|
@ -153,8 +156,8 @@ pub struct Quest { |
|
|
|
|
|
|
|
|
impl Quest {
|
|
|
impl Quest {
|
|
|
fn from_bin_dat(bin: Vec<u8>, dat: Vec<u8>) -> Result<Quest, QuestLoadError> {
|
|
|
fn from_bin_dat(bin: Vec<u8>, dat: Vec<u8>) -> Result<Quest, QuestLoadError> {
|
|
|
let id = u16::from_le_bytes(bin[16..18].try_into().unwrap());
|
|
|
|
|
|
let language = u16::from_le_bytes(bin[18..20].try_into().unwrap());
|
|
|
|
|
|
|
|
|
let id = u16::from_le_bytes(bin[16..18].try_into().map_err(|_| QuestLoadError::CouldNotReadMetadata)?);
|
|
|
|
|
|
let language = u16::from_le_bytes(bin[18..20].try_into().map_err(|_| QuestLoadError::CouldNotReadMetadata)?);
|
|
|
let name = array_to_utf16(&bin[24..88]);
|
|
|
let name = array_to_utf16(&bin[24..88]);
|
|
|
let description = array_to_utf16(&bin[88..334]);
|
|
|
let description = array_to_utf16(&bin[88..334]);
|
|
|
let full_description = array_to_utf16(&bin[334..920]);
|
|
|
let full_description = array_to_utf16(&bin[334..920]);
|
|
@ -173,8 +176,8 @@ impl Quest { |
|
|
full_description: full_description,
|
|
|
full_description: full_description,
|
|
|
id: id,
|
|
|
id: id,
|
|
|
language: language,
|
|
|
language: language,
|
|
|
bin_blob: prs_bin.into_inner().unwrap(),
|
|
|
|
|
|
dat_blob: prs_dat.into_inner().unwrap(),
|
|
|
|
|
|
|
|
|
bin_blob: prs_bin.into_inner().map_err(|_| QuestLoadError::CouldNotReadMetadata)?,
|
|
|
|
|
|
dat_blob: prs_dat.into_inner().map_err(|_| QuestLoadError::CouldNotReadMetadata)?,
|
|
|
enemies: enemies,
|
|
|
enemies: enemies,
|
|
|
objects: objects,
|
|
|
objects: objects,
|
|
|
})
|
|
|
})
|
|
@ -184,31 +187,39 @@ impl Quest { |
|
|
// QuestCollection
|
|
|
// QuestCollection
|
|
|
pub type QuestList = BTreeMap<QuestCategory, Vec<Quest>>;
|
|
|
pub type QuestList = BTreeMap<QuestCategory, Vec<Quest>>;
|
|
|
|
|
|
|
|
|
pub fn load_quests(quest_path: PathBuf) -> QuestList {
|
|
|
|
|
|
let mut f = File::open(quest_path).unwrap();
|
|
|
|
|
|
|
|
|
pub fn load_quests(quest_path: PathBuf) -> Result<QuestList, QuestLoadError> {
|
|
|
|
|
|
let mut f = File::open(quest_path).map_err(|_| QuestLoadError::CouldNotLoadConfigFile)?;
|
|
|
let mut s = String::new();
|
|
|
let mut s = String::new();
|
|
|
f.read_to_string(&mut s);
|
|
|
f.read_to_string(&mut s);
|
|
|
|
|
|
|
|
|
let mut used_quest_ids = BTreeSet::new();
|
|
|
let mut used_quest_ids = BTreeSet::new();
|
|
|
let ql: BTreeMap<String, QuestListCategory> = toml::from_str(s.as_str()).unwrap();
|
|
|
|
|
|
|
|
|
let ql: BTreeMap<String, QuestListCategory> = toml::from_str(s.as_str()).map_err(|_| QuestLoadError::CouldNotLoadConfigFile)?;
|
|
|
|
|
|
|
|
|
ql.into_iter().map(|(category, category_details)| {
|
|
|
|
|
|
|
|
|
Ok(ql.into_iter().map(|(category, category_details)| {
|
|
|
let quests = category_details.quests
|
|
|
let quests = category_details.quests
|
|
|
.into_iter()
|
|
|
.into_iter()
|
|
|
.filter_map(|quest| {
|
|
|
.filter_map(|quest| {
|
|
|
warn!("{:?}", quest.bin);
|
|
|
|
|
|
let dat_file = File::open(format!("data/quests/{}", quest.dat)).unwrap();
|
|
|
|
|
|
let bin_file = File::open(format!("data/quests/{}", quest.bin)).unwrap();
|
|
|
|
|
|
|
|
|
let dat_file = File::open(format!("data/quests/{}", quest.dat))
|
|
|
|
|
|
.map_err(|err| {
|
|
|
|
|
|
warn!("could not load quest file {}: {:?}", quest.dat, err)
|
|
|
|
|
|
}).ok()?;
|
|
|
|
|
|
let bin_file = File::open(format!("data/quests/{}", quest.bin))
|
|
|
|
|
|
.map_err(|err| {
|
|
|
|
|
|
warn!("could not load quest file {}: {:?}", quest.bin, err)
|
|
|
|
|
|
}).ok()?;
|
|
|
let mut dat_prs = LegacyPrsDecoder::new(dat_file);
|
|
|
let mut dat_prs = LegacyPrsDecoder::new(dat_file);
|
|
|
let mut bin_prs = LegacyPrsDecoder::new(bin_file);
|
|
|
let mut bin_prs = LegacyPrsDecoder::new(bin_file);
|
|
|
|
|
|
|
|
|
let mut dat = Vec::new();
|
|
|
let mut dat = Vec::new();
|
|
|
let mut bin = Vec::new();
|
|
|
let mut bin = Vec::new();
|
|
|
dat_prs.read_to_end(&mut dat).unwrap();
|
|
|
|
|
|
bin_prs.read_to_end(&mut bin).unwrap();
|
|
|
|
|
|
|
|
|
dat_prs.read_to_end(&mut dat).ok()?;
|
|
|
|
|
|
bin_prs.read_to_end(&mut bin).ok()?;
|
|
|
|
|
|
|
|
|
let quest = Quest::from_bin_dat(bin, dat).unwrap();
|
|
|
|
|
|
|
|
|
let quest = Quest::from_bin_dat(bin, dat).map_err(|err| {
|
|
|
|
|
|
warn!("could not parse quest file {}/{}: {:?}", quest.bin, quest.dat, err)
|
|
|
|
|
|
}).ok()?;
|
|
|
if used_quest_ids.contains(&quest.id) {
|
|
|
if used_quest_ids.contains(&quest.id) {
|
|
|
|
|
|
warn!("quest id already exists: {}", quest.id);
|
|
|
return None;
|
|
|
return None;
|
|
|
}
|
|
|
}
|
|
|
used_quest_ids.insert(quest.id);
|
|
|
used_quest_ids.insert(quest.id);
|
|
@ -219,6 +230,5 @@ pub fn load_quests(quest_path: PathBuf) -> QuestList { |
|
|
name: category,
|
|
|
name: category,
|
|
|
description: category_details.description,
|
|
|
description: category_details.description,
|
|
|
}, quests.collect())
|
|
|
}, quests.collect())
|
|
|
}).collect()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}).collect())
|
|
|
}
|
|
|
}
|