aboutsummaryrefslogtreecommitdiff
path: root/crates/nres
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-02-19 13:51:54 +0300
committerValentin Popov <valentin@popov.link>2026-02-19 13:51:54 +0300
commit598137ed132d95a3e3bf9b95e9e27286cc2186ac (patch)
treed237acbbde569fc9e4143af4945fc6d0d6cbc2d4 /crates/nres
parentcb0ca2f2f0775e62365e873a2dcce5b93082ac9a (diff)
downloadfparkan-598137ed132d95a3e3bf9b95e9e27286cc2186ac.tar.xz
fparkan-598137ed132d95a3e3bf9b95e9e27286cc2186ac.zip
feat(resource-viewer): добавить новый ресурсный просмотрщик с базовой функциональностью
feat(nres): улучшить структуру архива с добавлением заголовка и информации о записях feat(rsli): добавить поддержку заголовка библиотеки и улучшить обработку записей
Diffstat (limited to 'crates/nres')
-rw-r--r--crates/nres/src/lib.rs80
1 files changed, 73 insertions, 7 deletions
diff --git a/crates/nres/src/lib.rs b/crates/nres/src/lib.rs
index 69cb814..571b395 100644
--- a/crates/nres/src/lib.rs
+++ b/crates/nres/src/lib.rs
@@ -26,10 +26,28 @@ pub enum OpenMode {
ReadWrite,
}
+#[derive(Clone, Debug)]
+pub struct ArchiveHeader {
+ pub magic: [u8; 4],
+ pub version: u32,
+ pub entry_count: u32,
+ pub total_size: u32,
+ pub directory_offset: u64,
+ pub directory_size: u64,
+}
+
+#[derive(Clone, Debug)]
+pub struct ArchiveInfo {
+ pub raw_mode: bool,
+ pub file_size: u64,
+ pub header: Option<ArchiveHeader>,
+}
+
#[derive(Debug)]
pub struct Archive {
bytes: Arc<[u8]>,
entries: Vec<EntryRecord>,
+ info: ArchiveInfo,
raw_mode: bool,
}
@@ -54,6 +72,13 @@ pub struct EntryRef<'a> {
pub meta: &'a EntryMeta,
}
+#[derive(Copy, Clone, Debug)]
+pub struct EntryInspect<'a> {
+ pub id: EntryId,
+ pub meta: &'a EntryMeta,
+ pub name_raw: &'a [u8; 36],
+}
+
#[derive(Clone, Debug)]
struct EntryRecord {
meta: EntryMeta,
@@ -76,17 +101,27 @@ impl Archive {
}
pub fn open_bytes(bytes: Arc<[u8]>, opts: OpenOptions) -> Result<Self> {
- let (entries, _) = parse_archive(&bytes, opts.raw_mode)?;
+ let file_size = u64::try_from(bytes.len()).map_err(|_| Error::IntegerOverflow)?;
+ let (entries, header) = parse_archive(&bytes, opts.raw_mode)?;
if opts.prefetch_pages {
prefetch_pages(&bytes);
}
Ok(Self {
bytes,
entries,
+ info: ArchiveInfo {
+ raw_mode: opts.raw_mode,
+ file_size,
+ header,
+ },
raw_mode: opts.raw_mode,
})
}
+ pub fn info(&self) -> &ArchiveInfo {
+ &self.info
+ }
+
pub fn entry_count(&self) -> usize {
self.entries.len()
}
@@ -101,6 +136,17 @@ impl Archive {
})
}
+ pub fn entries_inspect(&self) -> impl Iterator<Item = EntryInspect<'_>> {
+ self.entries.iter().enumerate().filter_map(|(idx, entry)| {
+ let id = u32::try_from(idx).ok()?;
+ Some(EntryInspect {
+ id: EntryId(id),
+ meta: &entry.meta,
+ name_raw: &entry.name_raw,
+ })
+ })
+ }
+
pub fn find(&self, name: &str) -> Option<EntryId> {
if self.entries.is_empty() {
return None;
@@ -153,6 +199,16 @@ impl Archive {
})
}
+ pub fn inspect(&self, id: EntryId) -> Option<EntryInspect<'_>> {
+ let idx = usize::try_from(id.0).ok()?;
+ let entry = self.entries.get(idx)?;
+ Some(EntryInspect {
+ id,
+ meta: &entry.meta,
+ name_raw: &entry.name_raw,
+ })
+ }
+
pub fn read(&self, id: EntryId) -> Result<ResourceData<'_>> {
let range = self.entry_range(id)?;
Ok(ResourceData::Borrowed(&self.bytes[range]))
@@ -377,7 +433,10 @@ impl Editor {
}
}
-fn parse_archive(bytes: &[u8], raw_mode: bool) -> Result<(Vec<EntryRecord>, u64)> {
+fn parse_archive(
+ bytes: &[u8],
+ raw_mode: bool,
+) -> Result<(Vec<EntryRecord>, Option<ArchiveHeader>)> {
if raw_mode {
let data_size = u32::try_from(bytes.len()).map_err(|_| Error::IntegerOverflow)?;
let entry = EntryRecord {
@@ -398,10 +457,7 @@ fn parse_archive(bytes: &[u8], raw_mode: bool) -> Result<(Vec<EntryRecord>, u64)
name
},
};
- return Ok((
- vec![entry],
- u64::try_from(bytes.len()).map_err(|_| Error::IntegerOverflow)?,
- ));
+ return Ok((vec![entry], None));
}
if bytes.len() < 16 {
@@ -526,7 +582,17 @@ fn parse_archive(bytes: &[u8], raw_mode: bool) -> Result<(Vec<EntryRecord>, u64)
});
}
- Ok((entries, directory_offset))
+ Ok((
+ entries,
+ Some(ArchiveHeader {
+ magic: *b"NRes",
+ version,
+ entry_count: u32::try_from(entry_count).map_err(|_| Error::IntegerOverflow)?,
+ total_size,
+ directory_offset,
+ directory_size: directory_len,
+ }),
+ ))
}
fn checked_range(offset: u64, size: u32, bytes_len: usize) -> Result<Range<usize>> {