1use std::{fs::File, io::{BufReader, BufWriter, Read, Seek, Write}, path::Path};
4use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
5use std::path::PathBuf;
6use tokio::{fs::File as AsyncFile, io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, AsyncWrite, AsyncWriteExt, BufReader as AsyncBufReader, BufWriter as AsyncBufWriter,SeekFrom}};
7use crate::{Error, Result};
8
9const IO_BYTE_SIZE: usize = 8388608; #[derive(Debug)]
14pub struct Header {
15 table_offset: u32,
17 table_size: u32,
19}
20
21impl Header {
22 pub(crate) const PAK_FILE_DESIGNATOR: [u8; 4] = [b'P', b'A', b'C', b'K'];
23
24 pub fn new(table_offset: u32, table_size: u32) -> Self {
25 Header { table_offset, table_size }
26 }
27
28 pub async fn read<R>(reader: &mut R) -> Result<Self>
30 where
31 R: AsyncRead + AsyncSeek + Unpin,
32 {
33 let mut magic = [0u8; 4];
34 reader.read(&mut magic).await?;
35 if magic != Self::PAK_FILE_DESIGNATOR {
36 return Err(Error::InvalidMagicNumber(magic));
37 }
38
39 let table_offset = reader.read_u32_le().await?;
40 let table_size = reader.read_u32_le().await?;
41
42 Ok(Header::new(table_offset, table_size))
43 }
44
45 pub fn read_sync<R>(reader: &mut R) -> Result<Self>
47 where
48 R: Read + Seek
49 {
50 let mut magic = [0u8; 4];
51 reader.read(&mut magic)?;
52 if magic != Self::PAK_FILE_DESIGNATOR {
53 return Err(Error::InvalidMagicNumber(magic));
54 }
55
56 let table_offset = reader.read_u32::<LittleEndian>()?;
57 let table_size = reader.read_u32::<LittleEndian>()?;
58
59 Ok(Header::new(table_offset, table_size))
60 }
61
62 pub async fn write<R>(&self, writer: &mut R) -> Result<()>
64 where
65 R: AsyncWrite + Unpin
66 {
67 writer.write_all(&Self::PAK_FILE_DESIGNATOR).await?;
68 writer.write_u32_le(self.table_offset).await?;
69 writer.write_u32_le(self.table_size).await?;
70 Ok(())
71 }
72
73 pub fn write_sync<R>(&self, writer: &mut R) -> Result<()>
75 where
76 R: Write
77 {
78 writer.write_all(&Self::PAK_FILE_DESIGNATOR)?;
79 writer.write_u32::<LittleEndian>(self.table_offset)?;
80 writer.write_u32::<LittleEndian>(self.table_size)?;
81 Ok(())
82 }
83
84 pub fn table_offset(&self) -> u32 {
86 self.table_offset
87 }
88
89 pub fn table_size(&self) -> u32 {
91 self.table_size
92 }
93}
94
95#[derive(Debug)]
98pub struct Table {
99 entries: Vec<TableEntry>,
100}
101
102impl Table {
103 pub fn new(entries: Vec<TableEntry>) -> Self {
104 Table { entries }
105 }
106
107 pub async fn read<R>(reader: &mut R, header: &Header) -> Result<Self>
109 where
110 R: AsyncRead + AsyncSeek + Unpin
111 {
112 reader.seek(SeekFrom::Start(header.table_offset as u64)).await?;
113
114 let mut entries = Vec::new();
115 for _ in 0..header.table_size / TableEntry::SIZE as u32 {
116 let entry = TableEntry::read(reader).await?;
117 entries.push(entry);
118 }
119
120 Ok(Table::new(entries))
121 }
122
123 pub fn read_sync<R>(reader: &mut R, header: &Header) -> Result<Self>
125 where
126 R: Read + Seek
127 {
128 reader.seek(SeekFrom::Start(header.table_offset as u64))?;
129
130 let mut entries = Vec::new();
131 for _ in 0..header.table_size / TableEntry::SIZE as u32 {
132 let entry = TableEntry::read_sync(reader)?;
133 entries.push(entry);
134 }
135
136 Ok(Table::new(entries))
137 }
138
139 pub fn entries(&self) -> &Vec<TableEntry> {
141 &self.entries
142 }
143
144 pub fn contains<P: AsRef<Path>>(&self, path: P) -> bool {
146 let path = path.as_ref().to_string_lossy();
147 self.entries.iter().any(|entry| entry.path == path)
148 }
149
150 pub async fn write<W>(&self, writer: &mut W) -> Result<()>
152 where
153 W: AsyncWrite + Unpin,
154 {
155 for entry in &self.entries {
156 entry.write(writer).await?;
157 }
158
159 Ok(())
160 }
161
162 pub fn write_sync<W>(&self, writer: &mut W) -> Result<()>
164 where
165 W: Write,
166 {
167 for entry in &self.entries {
168 entry.write_sync(writer)?;
169 }
170
171 Ok(())
172 }
173}
174
175#[derive(Debug, Clone)]
178pub struct TableEntry {
179 path: String,
181 offset: u32,
183 size: u32,
185}
186
187impl TableEntry {
188 const PATH_SIZE: usize = 56;
190 const SIZE: usize = Self::PATH_SIZE + size_of::<u32>() + size_of::<u32>();
192
193 pub fn new(path: String, offset: u32, size: u32) -> Result<Self> {
195 if path.len() > Self::PATH_SIZE {
196 return Err(Error::FilenameTooLong(path));
197 }
198
199 Ok(TableEntry { path, offset, size })
200 }
201
202 pub async fn read<R>(reader: &mut R) -> Result<Self>
204 where
205 R: AsyncRead + Unpin
206 {
207 let mut path = [0u8; Self::PATH_SIZE];
208 reader.read(&mut path).await?;
209 let path_end = path.iter()
210 .position(|&b| b == 0)
211 .ok_or(Error::FilenameTooLong(String::from_utf8_lossy(&path).into_owned()))?;
212 let path = String::from_utf8(path[0..path_end].to_vec())
213 .map_err(|e| Error::NonUtf8Filename(e))?;
214
215 let offset = reader.read_u32_le().await?;
216 let size = reader.read_u32_le().await?;
217
218 Ok(TableEntry::new(path, offset, size)?)
219 }
220
221 pub fn read_sync<R>(reader: &mut R) -> Result<Self>
223 where
224 R: Read
225 {
226 let mut path = [0u8; Self::PATH_SIZE];
227 reader.read(&mut path)?;
228 let path_end = path.iter()
229 .position(|b| *b == 0)
230 .ok_or(Error::FilenameTooLong(String::from_utf8_lossy(&path).into_owned()))?;
231 let path = String::from_utf8(path[0..path_end].to_vec())
232 .map_err(|e| Error::NonUtf8Filename(e))?;
233
234 let offset = reader.read_u32::<LittleEndian>()?;
235 let size = reader.read_u32::<LittleEndian>()?;
236
237 Ok(TableEntry::new(path, offset, size)?)
238 }
239
240 pub fn path(&self) -> &str {
243 &self.path
244 }
245
246 pub fn offset(&self) -> u32 {
248 self.offset
249 }
250
251 pub fn size(&self) -> u32 {
253 self.size
254 }
255
256 pub async fn write<W>(&self, writer: &mut W) -> Result<()>
258 where
259 W: AsyncWrite + Unpin
260 {
261
262 let mut path = [0u8; Self::PATH_SIZE];
263 for (i, byte) in self.path.as_bytes().iter().enumerate() {
264 path[i] = *byte;
265 }
266
267 writer.write_all(&path).await?;
268 writer.write_u32_le(self.offset).await?;
269 writer.write_u32_le(self.size).await?;
270 Ok(())
271 }
272
273 pub fn write_sync<W>(&self, writer: &mut W) -> Result<()>
275 where
276 W: Write,
277 {
278 let mut path = [0u8; Self::PATH_SIZE];
279 for (i, byte) in self.path.as_bytes().iter().enumerate() {
280 path[i] = *byte;
281 }
282
283 writer.write_all(&path)?;
284 writer.write_u32::<LittleEndian>(self.offset)?;
285 writer.write_u32::<LittleEndian>(self.size)?;
286 Ok(())
287 }
288}
289
290#[derive(Debug)]
292pub struct PakManifest {
293 header: Header,
294 table: Table,
295}
296
297impl PakManifest {
298 const ITEMS_OFFSET: u32 = ((Header::PAK_FILE_DESIGNATOR.len() * size_of::<u8>()) + size_of::<u32>() + size_of::<u32>()) as u32;
300
301 pub fn new(header: Header, table: Table) -> Self {
302 PakManifest { header, table }
303 }
304
305 pub async fn read<R>(reader: &mut R) -> Result<Self>
307 where
308 R: AsyncRead + AsyncSeek + Unpin
309 {
310 let header = Header::read(reader).await?;
311 let table = Table::read(reader, &header).await?;
312 Ok(Self::new(header, table))
313 }
314
315 pub fn read_sync<R>(reader: &mut R) -> Result<Self>
317 where
318 R: Read + Seek
319 {
320 let header = Header::read_sync(reader)?;
321 let table = Table::read_sync(reader, &header)?;
322 Ok(Self::new(header, table))
323 }
324
325 pub fn from_dir_sync<P: AsRef<Path>>(input_dir: P) -> Result<Self> {
328 let files = walk_pak_dir_sync(input_dir)?;
329 Self::from_walk_results(files)
330 }
331
332 pub async fn from_dir<P: AsRef<Path>>(input_dir: P) -> Result<Self> {
335 let files = walk_pak_dir(input_dir).await?;
336 Self::from_walk_results(files)
337 }
338
339 pub fn from_walk_results(files: Vec<(PathBuf, u64)>) -> Result<Self> {
340 let mut total_size = 0;
341 for (_, size) in &files {
342 total_size += size;
343 }
344
345 let table_size = (files.len() * TableEntry::SIZE) as u32;
346 let header = Header::new(Self::ITEMS_OFFSET + total_size as u32, table_size);
347
348 let mut table_entries = Vec::with_capacity(files.len());
349 let mut entry_offset = Self::ITEMS_OFFSET;
350 for (path, size) in files {
351 let path = path.to_str()
352 .ok_or_else(|| Error::NonUtf8Path(path.clone()))?
353 .to_string();
354 let size = size as u32;
355 let entry = TableEntry::new(path, entry_offset, size)?;
356 table_entries.push(entry);
357 entry_offset += size;
358 }
359
360 let table = Table::new(table_entries);
361 Ok(Self::new(header, table))
362 }
363
364 pub fn header(&self) -> &Header {
366 &self.header
367 }
368
369 pub fn table(&self) -> &Table {
371 &self.table
372 }
373
374 pub fn table_entries(&self) -> &Vec<TableEntry> {
376 &self.table.entries
377 }
378}
379
380#[derive(Debug)]
383pub struct PakFile {
384 filepath: PathBuf,
386 manifest: PakManifest
388}
389
390impl PakFile {
391 pub fn new(filepath: PathBuf, manifest: PakManifest) -> Self {
392 PakFile { filepath, manifest }
393 }
394
395 pub async fn from_file<P>(filepath: P) -> Result<Self>
398 where
399 P: AsRef<Path>,
400 {
401 let filepath = PathBuf::from(filepath.as_ref());
402 let file = AsyncFile::open(&filepath).await
403 .map_err(|e| Error::OpenPak(e))?;
404
405 let mut reader = AsyncBufReader::new(file);
406 let manifest = PakManifest::read(&mut reader).await
407 .map_err(|e| Error::ReadPak(filepath.to_path_buf(), e.to_string()))?;
408
409 Ok(Self::new(filepath, manifest))
410 }
411
412 pub fn from_file_sync<P>(filepath: P) -> Result<Self>
415 where
416 P: AsRef<Path>,
417 {
418 let filepath = PathBuf::from(filepath.as_ref());
419 let file = File::open(&filepath)
420 .map_err(|e| Error::OpenPak(e))?;
421
422 let mut reader = BufReader::new(file);
423 let manifest = PakManifest::read_sync(&mut reader)
424 .map_err(|e| Error::ReadPak(filepath.to_path_buf(), e.to_string()))?;
425
426 Ok(Self::new(filepath, manifest))
427 }
428
429 pub async fn create_from_dir<P>(input_dir: P, manifest: PakManifest, output_filepath: P) -> Result<Self>
431 where
432 P: AsRef<Path>,
433 {
434 let out_file = AsyncFile::create(output_filepath.as_ref()).await?;
435 let mut writer = AsyncBufWriter::new(out_file);
436
437 manifest.header.write(&mut writer).await?;
438
439 for entry in manifest.table.entries.iter() {
440 let input_path = input_dir.as_ref().join(&entry.path);
441 let in_file = AsyncFile::open(&input_path).await?;
442 let mut reader = AsyncBufReader::new(in_file);
443
444 let mut size_remaining = entry.size as usize;
445 while size_remaining > 0 {
446 let chunk_size = std::cmp::min(size_remaining, IO_BYTE_SIZE);
447 let mut chunk = vec![0; chunk_size];
448 reader.read_exact(&mut chunk).await?;
449 writer.write_all(&chunk).await?;
450 size_remaining -= chunk_size;
451 }
452 }
453
454 manifest.table.write(&mut writer).await?;
455 writer.flush().await?;
456
457 Ok(Self::new(PathBuf::from(output_filepath.as_ref()), manifest))
458 }
459
460 pub fn create_from_dir_sync<P>(input_dir: P, manifest: PakManifest, output_filepath: P) -> Result<Self>
462 where
463 P: AsRef<Path>,
464 {
465 let out_file = File::create(output_filepath.as_ref())?;
466 let mut writer = BufWriter::new(out_file);
467
468 manifest.header.write_sync(&mut writer)?;
469
470 for entry in manifest.table.entries.iter() {
471 let input_path = input_dir.as_ref().join(&entry.path);
472 let input_file = File::open(&input_path)?;
473 let mut reader = BufReader::new(input_file);
474
475 let mut size_remaining = entry.size as usize;
476 while size_remaining > 0 {
477 let chunk_size = std::cmp::min(size_remaining, IO_BYTE_SIZE);
478 let mut chunk = vec![0; chunk_size];
479 reader.read_exact(&mut chunk)?;
480 writer.write_all(&chunk)?;
481 size_remaining -= chunk_size;
482 }
483 }
484
485 manifest.table.write_sync(&mut writer)?;
486 writer.flush()?;
487
488 Ok(Self::new(PathBuf::from(output_filepath.as_ref()), manifest))
489 }
490
491 pub fn extract_sync<P: AsRef<Path>>(&self, dest_dir: P) -> Result<()> {
494 for pak_item in self.read_items_sync()? {
495 let pak_item = pak_item?;
496
497 let mut path = std::path::PathBuf::from(dest_dir.as_ref());
498 path.push(&pak_item.table_entry().path());
499
500 if let Some(parent) = path.parent() {
501 std::fs::create_dir_all(parent)
502 .map_err(|e| Error::CreateDirectory(e))?;
503 }
504
505 let file = File::create(&path)
506 .map_err(|e| Error::OpenPak(e))?;
507
508 let mut writer = BufWriter::new(file);
509 writer.write_all(pak_item.data().as_ref())
510 .map_err(|e| Error::WritePak(e))?;
511
512 writer.flush()?;
513 }
514
515 Ok(())
516 }
517
518 pub async fn extract<P: AsRef<Path>>(&self, dest_dir: P) -> Result<()> {
521 use tokio_stream::StreamExt;
522 let mut items = std::pin::pin!(self.read_items());
523 while let Some(pak_item) = items.try_next().await? {
524 let mut path = std::path::PathBuf::from(dest_dir.as_ref());
525 path.push(&pak_item.table_entry().path());
526
527 if let Some(parent) = path.parent() {
528 tokio::fs::create_dir_all(parent).await
529 .map_err(|e| Error::CreateDirectory(e))?;
530 }
531
532 let file = AsyncFile::create(&path).await
533 .map_err(|e| Error::OpenPak(e))?;
534
535 let mut writer = AsyncBufWriter::new(file);
536 writer.write_all(pak_item.data().as_ref()).await
537 .map_err(|e| Error::WritePak(e))?;
538
539 writer.flush().await
540 .map_err(|e| Error::WritePak(e))?;
541 }
542
543 Ok(())
544 }
545
546 pub fn read_items<'p>(&'p self) -> impl tokio_stream::Stream<Item = Result<PakItem<'p>>> {
549 async_stream::try_stream!{
550 let table_entries = &self.manifest.table.entries;
551 let file = AsyncFile::open(&self.filepath).await
552 .map_err(|e| Error::OpenPak(e))?;
553 let mut reader = AsyncBufReader::new(file);
554
555 for i in 0..table_entries.len() {
556 let table_entry = table_entries.get(i).unwrap();
557 reader.seek(SeekFrom::Start(table_entry.offset as u64)).await
558 .map_err(|e| Error::ReadPak(self.filepath.to_path_buf(), e.to_string()))?;
559
560 let mut data: Vec<u8> = Vec::with_capacity(table_entry.size as usize);
561 (&mut reader)
562 .take(table_entry.size as u64)
563 .read_to_end(&mut data).await
564 .map_err(|e| Error::ReadPak(self.filepath.to_path_buf(), e.to_string()))?;
565
566 let item = PakItem { table_entry, data };
567 yield item;
568 }
569 }
570 }
571
572 pub fn read_items_sync<'p>(&'p self) -> Result<impl Iterator<Item = Result<PakItem<'p>>>> {
575 let table_entries = &self.manifest.table.entries;
576 let file = File::open(&self.filepath)
577 .map_err(|e| Error::OpenPak(e))?;
578 let mut reader = BufReader::new(file);
579
580 let map = table_entries.iter().map(move |table_entry| {
582 reader.seek(SeekFrom::Start(table_entry.offset as u64))
583 .map_err(|e| Error::ReadPak(self.filepath.to_path_buf(), e.to_string()))?;
584
585 let mut data: Vec<u8> = Vec::with_capacity(table_entry.size as usize);
586 (&mut reader)
587 .take(table_entry.size as u64)
588 .read_to_end(&mut data)
589 .map_err(|e| Error::ReadPak(self.filepath.to_path_buf(), e.to_string()))?;
590
591 Ok(PakItem { table_entry, data })
592 });
593
594 Ok(map)
595 }
596
597 pub fn manifest(&self) -> &PakManifest {
599 &self.manifest
600 }
601}
602
603#[derive(Debug)]
605pub struct PakItem<'t> {
606 table_entry: &'t TableEntry,
608 data: Vec<u8>
610}
611
612impl PakItem<'_> {
613 pub fn table_entry(&self) -> &TableEntry {
615 self.table_entry
616 }
617
618 pub fn data(&self) -> &Vec<u8> {
620 &self.data
621 }
622}
623
624pub fn walk_pak_dir_sync<P: AsRef<Path>>(dir: P) -> Result<Vec<(PathBuf, u64)>> {
630 let mut entries = walkdir::WalkDir::new(&dir)
631 .follow_links(true)
632 .into_iter()
633 .filter_map(|entry| entry.ok())
634 .filter(|entry| !entry.file_name().to_string_lossy().starts_with('.'))
635 .filter(|entry| entry.metadata().is_ok_and(|metadata| metadata.is_file()))
636 .map(|entry| {
637 let metadata = entry.metadata()
638 .map_err(|e| Error::ReadDirectory(entry.path().to_path_buf(), e.to_string()))?;
639 let path = entry.path().strip_prefix(&dir)
640 .map_err(|e| Error::ReadDirectory(entry.path().to_path_buf(), e.to_string()))?
641 .to_path_buf();
642 let len = metadata.len();
643 Ok::<(PathBuf, u64), Error>((path, len))
644 })
645 .collect::<Vec<_>>()
646 .into_iter()
647 .collect::<Result<Vec<_>>>()?;
648
649 sort_walk_paths(&mut entries);
650 Ok(entries)
651}
652
653pub fn sort_walk_paths(entries: &mut Vec<(PathBuf, u64)>) {
655 entries.sort_by(|a, b| {
656 match a.0.parent().cmp(&b.0.parent()) {
657 std::cmp::Ordering::Equal => a.0.file_name().cmp(&b.0.file_name()),
658 other => other,
659 }
660 });
661}
662
663pub async fn walk_pak_dir<P: AsRef<Path>>(dir: P) -> Result<Vec<(PathBuf, u64)>> {
669 let dir = dir.as_ref().to_path_buf();
670 tokio::task::spawn(async move {
671 walk_pak_dir_sync(dir)
672 }).await.expect("Expected task to join properly")
673}