# SF3
An implementation of SF3 (Simple File Format Family) in Rust. See the spec and reference implementations at <https://shirakumo.org/docs/sf3>.
> [!WARNING]
> This crate is an experimental work-in-progress still early in the design phase. Expect major breaking changes in the near future.
>
> Feedback is appreciated, either via an [issue](https://git.average.name/AverageHelper/sf3-rust/issues/new)
> or contacting me [directly](https://average.name/contact)!
## Features
This crate is a work-in-progress. The following functions have been implemented and tested:
- Read SF3 files from bytes in memory, a file on disc, or any `std::io::Read` reader
- Read metadata or headers alone, for easy parsing of large files
- Create new SF3 files of supported formats
- MIME types and preferred file extensions for all supported formats
- `no_std` support
- Formats
- Archive
- Audio
- Log
- Text
- ...
## Roadmap
The following formats and functions have yet to be implemented:
- [ ] Image
- [ ] Model
- [ ] Physics-Model
- [ ] Table (serde serializer?)
- [ ] Vector Graphic
- [ ] Utilities for reading from important parts of SF3 files without needing to load the entire file into memory
- [ ] Utilities for writing parts of SF3 files in-place, without needing to load the entire file into memory
## Examples
See the [`examples`](examples) directory. Run an example using `cargo run --example <example>`
## Dependencies
- Stable Rust (2024 edition)
- [`chrono`](https://github.com/chronotope/chrono) for parsing timestamp values (see the `Archive` and `Log` file types)
- [`const-str`](https://github.com/Nugine/const-str) and [`const_panic`](https://github.com/rodrimati1992/const_panic/) for constructing sized strings from string literals
- [`crc32fast`](https://github.com/srijs/rust-crc32fast) for computing CRC32 checksums
- [`half`](https://github.com/VoidStarKat/half-rs) for handling 16-bit floats [until `f16` stabilizes](https://github.com/rust-lang/rust/issues/116909) (see the `Audio` file type)
- [`mediatype`](https://github.com/picoHz/mediatype) for parsing and serving MIME types
- [`timeout-readwrite`](https://github.com/jcreekmore/timeout-readwrite-rs) for ensuring File I/O doesn't hang when it should time out
## Installation
This crate is available on [crates.io](https://crates.io/crates/sf3) and [our own package registry](https://git.average.name/AverageHelper/-/packages/cargo/sf3/). Add it to your project like so:
```sh
# via crates.io
cargo add sf3
# or via git.avg.name
cargo add --index sparse+https://git.average.name/api/packages/AverageHelper/cargo/ sf3
```
> [!NOTE]
> [crates.io](https://crates.io/) does not allow packages to be published with dependencies on code published outside of [crates.io](https://crates.io/). Only use our package registry if you intend to self-publish. See [The Cargo Book](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-other-registries) for details.
## Usage
Parse from memory:
```rust
use sf3::file::Sf3File;
use sf3::Archive;
let archive_bytes: &[u8] = /* ... */;
let archive = Archive::parse_bytes(archive_bytes).unwrap();
assert_eq!(archive.payload.len(), 2);
```
Parse from file:
```rust
use sf3::file::Sf3File;
use sf3::Archive;
let file = std::fs::File::open("/path/to/example.ar.sf3")?;
let archive = Archive::parse_file(file).unwrap();
assert_eq!(archive.payload.len(), 2);
```
Parse from Reader or custom buffer:
```rust
use sf3::file::Sf3File;
use sf3::Archive;
let stream: impl std::io::Read = /* ... */;
let buf = std::io::BufReader::new(stream);
let archive = Archive::parse_io(Box::new(buf)).unwrap();
assert_eq!(archive.payload.len(), 2);
```
MIME type (`const` compatible):
```rust
use mediatype::MediaType;
// mime-types for specific formats
let archive_type: MediaType = sf3::Archive::MIME;
let audio_type: MediaType = sf3::Archive::MIME;
// mime-type for a general SF3 file (may change in future with IANA registration)
let sf3_type: MediaType = sf3::file::MIME;
```
## Feature flags
### `std`
This feature is enabled by default, and enables support for parsing files from `Read` types, and certain features in `chrono`, `const-str`, `crc32fast`, and `half` which require Rust's `std` crate. All functions work well enough without `std`, including parsing from byte slices and generating new byte representations of SF3 files, so if you're writing a `no_std` crate or targeting an environment where `std` isn't available but `alloc` and `core` are, then you may disable this feature without much consequence.
### `serde`
Disabled by default. Enables `serde` support for `chrono`, `half`, and `mediatype` types.
## Testing
Run code-coverage tests using the `test.sh` script.
Regular unit tests can be run using `cargo test`. Use the `--no-default-features` flag to test `no_std` support.
The `tests/` directory contains sample files to test against. These are copied directly from the [Shirakumo/sf3](https://shirakumo.org/projects/sf3/src/branch/master/samples) repository, and may be out of date.
## Contributing
Suggestions and code contributions are welcome! The codebase lives primarily at [git.average.name](https://git.average.name/AverageHelper/sf3-rust), where you may file issues or pull requests using a fresh account there or an existing account with Codeberg or the like.
A read-only code mirror of this project also exists at [Codeberg](https://codeberg.org/AverageHelper/sf3-rust).
## Acknowledgements
The SF3 spec was written by [Yukari Hafner](https://shinmera.com/) at [Shirakumo](https://shirakumo.org/), under the [zlib License](https://shirakumo.org/projects/sf3/src/branch/master/LICENSE).
The API was somewhat inspired by the [`yaml`](https://crates.io/crates/yaml) and [`toml`](https://crates.io/crates/toml) crates.