| Crates.io | pf8 |
| lib.rs | pf8 |
| version | 0.1.5 |
| created_at | 2025-08-10 11:08:08.873812+00 |
| updated_at | 2025-12-18 07:16:17.186475+00 |
| description | A Rust library for encoding and decoding artemis PF8 archive files |
| homepage | |
| repository | https://github.com/sakarie9/pfs-rs |
| max_upload_size | |
| id | 1788793 |
| size | 118,224 |
A comprehensive Rust library for encoding and decoding PF6 and PF8 archive files. This library provides both high-level convenience APIs and low-level control for working with these archive formats.
PF6 and PF8 are proprietary archive formats for the Artemis galgame engine.
display feature)Add this to your Cargo.toml:
[dependencies]
pf8 = "0.1"
# Without display features (pretty-printed tables)
pf8 = { version = "0.1", default-features = false }
For simple operations, use the convenience functions:
use pf8::{extract, create_from_dir, Result};
fn main() -> Result<()> {
// Extract an archive
extract("root.pfs", "output_directory")?;
// Create an archive from a directory
create_from_dir("input_directory", "new_archive.pfs")?;
Ok(())
}
use pf8::{Pf8Archive, Result};
fn main() -> Result<()> {
// Open an existing PF8 archive
let mut archive = Pf8Archive::open("root.pfs")?;
// List all files in the archive
for entry in archive.entries() {
println!("{}: {} bytes", entry.path().display(), entry.size());
}
// Extract all files to a directory
archive.extract_all("output_dir")?;
// Extract a specific file
if let Some(_entry) = archive.get_entry("system/table/list_windows.tbl") {
let data = archive.read_file("system/table/list_windows.tbl")?;
std::fs::write("extracted_list_windows.tbl", data)?;
}
Ok(())
}
use pf8::{Pf8Builder, Result};
fn main() -> Result<()> {
// Create a new archive builder
let mut builder = Pf8Builder::new();
// Configure encryption filters (files matching these patterns won't be encrypted)
// Ignore this will use default unencrypted lists
// builder.unencrypted_extensions(&[".mp4", ".flv"]);
// Add files and directories
builder.add_dir("scripts")?;
builder.add_file("single_file.txt")?;
builder.add_file_as("list_windows.tbl", "system/table/list_windows.tbl")?;
// Write the archive to a file
builder.write_to_file("root.pfs")?;
Ok(())
}
With the display feature enabled, you can pretty-print archive contents:
use pf8::display::list_archive;
fn main() -> pf8::Result<()> {
list_archive("root.pfs")?;
Ok(())
}
This will output a formatted table like:
archive.pfs
| File | Size |
|-------------------|-----------|
| config/game.ini | 1.2 KB |
| image/image1.png | 45.6 MB |
| scripts/main.ast | 3.4 KB |
Total: 3 files, Total size: 45.6 MB
use pf8::{Pf8Reader, Result};
fn main() -> Result<()> {
let mut reader = Pf8Reader::open("root.pfs")?;
for entry in reader.entries() {
println!("File: {}", entry.path().display());
println!("Size: {} bytes", entry.size());
println!("Encrypted: {}", entry.is_encrypted());
// Read file data
let data = reader.read_file(entry.path())?;
// Process data...
}
Ok(())
}
use pf8::{Pf8Archive, Pf8Builder, Result};
fn main() -> Result<()> {
// When reading, specify which files should be unencrypted
let unencrypted_patterns = &[".mp4", ".flv"];
let archive = Pf8Archive::open_with_patterns("root.pfs", unencrypted_patterns)?;
// When creating, specify patterns for unencrypted files
let mut builder = Pf8Builder::new();
builder.unencrypted_patterns(&[".mp4", ".flv"]);
builder.add_dir("src")?;
builder.write_to_file("root.pfs")?;
Ok(())
}
For large archives, you can use streaming operations to avoid loading everything into memory:
use pf8::{Pf8Reader, Result};
fn extract_large_archive(archive_path: &str, output_dir: &str) -> Result<()> {
let mut reader = Pf8Reader::open(archive_path)?;
for entry in reader.entries() {
let output_path = std::path::Path::new(output_dir).join(entry.path());
// Create parent directories
if let Some(parent) = output_path.parent() {
std::fs::create_dir_all(parent)?;
}
// Stream file data directly to disk
use std::fs::File;
use std::io::Write;
let mut output_file = File::create(&output_path)?;
reader.read_file_streaming(entry.path(), |chunk| {
output_file.write_all(chunk)?;
Ok(())
})?;
}
Ok(())
}
For better user experience, especially in GUI applications or JNI scenarios, you can track extraction progress and cancel operations:
use pf8::{Pf8Archive, ProgressCallback, ProgressInfo, CancellationToken, Result};
use std::path::Path;
// Implement a progress callback
struct MyCallback;
impl ProgressCallback for MyCallback {
fn on_progress(&mut self, progress: &ProgressInfo) -> Result<()> {
println!(
"Progress: {:.1}% - Processing: {} ({}/{})",
progress.overall_progress(),
progress.current_file,
progress.current_file_index + 1,
progress.total_files
);
Ok(())
}
fn on_file_start(&mut self, path: &Path, index: usize, total: usize) -> Result<()> {
println!("[{}/{}] Starting: {}", index + 1, total, path.display());
Ok(())
}
}
fn main() -> Result<()> {
let mut callback = MyCallback;
// Extract with progress reporting
extract_with_progress("root.pfs", "output_directory", &mut callback)?;
Ok(())
}
For cancellable operations:
use pf8::{CancellationToken, CancellableCallback};
fn main() -> Result<()> {
let token = CancellationToken::new();
let token_clone = token.clone();
// In another thread, you can call token_clone.cancel()
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_secs(5));
token_clone.cancel();
});
let mut callback = CancellableCallback::new(MyCallback, token);
match extract_all_with_progress("archive.pf8", "output/", &mut callback) {
Ok(_) => println!("Completed"),
Err(pf8::Error::Cancelled) => println!("Cancelled by user"),
Err(e) => eprintln!("Error: {}", e),
}
Ok(())
}
The library provides comprehensive error types:
use pf8::{Error, Result};
fn handle_errors() -> Result<()> {
match pf8::extract("root.pfs", "output") {
Ok(()) => println!("Extraction successful"),
Err(Error::Io(e)) => eprintln!("I/O error: {}", e),
Err(Error::InvalidFormat(msg)) => eprintln!("Invalid format: {}", msg),
Err(Error::FileNotFound(name)) => eprintln!("File not found: {}", name),
Err(Error::Corrupted(msg)) => eprintln!("Archive corrupted: {}", msg),
Err(Error::Cancelled) => eprintln!("Operation was cancelled"),
Err(e) => eprintln!("Other error: {}", e),
}
Ok(())
}
The PF8 format is a custom archive format with the following features:
display feature adds dependencies - disable if not neededThis project is licensed under the MIT license - see the LICENSE file for details.