mirror of
https://github.com/encounter/nod.git
synced 2026-07-10 12:18:48 -07:00
Use full LTO, update dependencies & CI
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
name: Build
|
||||
|
||||
on: [ push, pull_request ]
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
- 'LICENSE*'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BUILD_PROFILE: release-lto
|
||||
CARGO_BIN_NAME: nodtool
|
||||
CARGO_TARGET_DIR: target
|
||||
|
||||
jobs:
|
||||
@@ -79,7 +84,9 @@ jobs:
|
||||
run: cargo test --release
|
||||
|
||||
build:
|
||||
name: Build
|
||||
name: Build nodtool
|
||||
env:
|
||||
CARGO_BIN_NAME: nodtool
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@@ -88,11 +95,11 @@ jobs:
|
||||
name: linux-x86_64
|
||||
build: zigbuild
|
||||
features: asm
|
||||
# - platform: ubuntu-latest
|
||||
# target: i686-unknown-linux-musl
|
||||
# name: linux-i686
|
||||
# build: zigbuild
|
||||
# features: asm
|
||||
- platform: ubuntu-latest
|
||||
target: i686-unknown-linux-musl
|
||||
name: linux-i686
|
||||
build: zigbuild
|
||||
features: asm
|
||||
- platform: ubuntu-latest
|
||||
target: aarch64-unknown-linux-musl
|
||||
name: linux-aarch64
|
||||
@@ -103,6 +110,11 @@ jobs:
|
||||
name: linux-armv7l
|
||||
build: zigbuild
|
||||
features: default
|
||||
- platform: windows-latest
|
||||
target: i686-pc-windows-msvc
|
||||
name: windows-x86
|
||||
build: build
|
||||
features: default
|
||||
- platform: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
name: windows-x86_64
|
||||
@@ -135,20 +147,20 @@ jobs:
|
||||
sudo apt-get -y install ${{ matrix.packages }}
|
||||
- name: Install cargo-zigbuild
|
||||
if: matrix.build == 'zigbuild'
|
||||
run: pip install ziglang==0.11.0 cargo-zigbuild==0.18.3
|
||||
run: pip install ziglang==0.13.0 cargo-zigbuild==0.19.1
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
- name: Cargo build
|
||||
run: cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }} --bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
|
||||
run: >
|
||||
cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
||||
--bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.name }}
|
||||
name: ${{ env.CARGO_BIN_NAME }}-${{ matrix.name }}
|
||||
path: |
|
||||
${{ env.CARGO_TARGET_DIR }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}
|
||||
${{ env.CARGO_TARGET_DIR }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
|
||||
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}
|
||||
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
|
||||
if-no-files-found: error
|
||||
@@ -159,6 +171,22 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ build ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Check git tag against Cargo version
|
||||
shell: bash
|
||||
run: |
|
||||
set -eou pipefail
|
||||
tag='${{github.ref}}'
|
||||
tag="${tag#refs/tags/}"
|
||||
for file in */Cargo.toml; do
|
||||
version=$(grep '^version' $file | head -1 | awk -F' = ' '{print $2}' | tr -d '"')
|
||||
version="v$version"
|
||||
if [ "$tag" != "$version" ]; then
|
||||
echo "::error::Git tag doesn't match the Cargo version! ($tag != $version)"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -166,12 +194,26 @@ jobs:
|
||||
- name: Rename artifacts
|
||||
working-directory: artifacts
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir ../out
|
||||
for i in */*/$BUILD_PROFILE/$CARGO_BIN_NAME*; do
|
||||
mv "$i" "../out/$(sed -E "s/([^/]+)\/[^/]+\/$BUILD_PROFILE\/($CARGO_BIN_NAME)/\2-\1/" <<< "$i")"
|
||||
for dir in */; do
|
||||
for file in "$dir"*; do
|
||||
base=$(basename "$file")
|
||||
name="${base%.*}"
|
||||
ext="${base##*.}"
|
||||
if [ "$ext" = "$base" ]; then
|
||||
ext=""
|
||||
else
|
||||
ext=".$ext"
|
||||
fi
|
||||
arch="${dir%/}" # remove trailing slash
|
||||
arch="${arch##"$name-"}" # remove bin name
|
||||
dst="../out/${name}-${arch}${ext}"
|
||||
mv "$file" "$dst"
|
||||
done
|
||||
done
|
||||
ls -R ../out
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: out/*
|
||||
|
||||
Generated
+150
-120
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -4,5 +4,5 @@ resolver = "2"
|
||||
|
||||
[profile.release-lto]
|
||||
inherits = "release"
|
||||
lto = "thin"
|
||||
lto = "fat"
|
||||
strip = "debuginfo"
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nod"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
edition = "2021"
|
||||
rust-version = "1.73.0"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
@@ -31,11 +31,11 @@ cbc = "0.1"
|
||||
digest = "0.10"
|
||||
dyn-clone = "1.0"
|
||||
encoding_rs = "0.8"
|
||||
itertools = "0.12"
|
||||
liblzma = { version = "0.2", features = ["static"], optional = true }
|
||||
itertools = "0.13"
|
||||
liblzma = { version = "0.3", features = ["static"], optional = true }
|
||||
log = "0.4"
|
||||
miniz_oxide = { version = "0.7", optional = true }
|
||||
rayon = "1.8"
|
||||
miniz_oxide = { version = "0.8", optional = true }
|
||||
rayon = "1.10"
|
||||
sha1 = "0.10"
|
||||
thiserror = "1.0"
|
||||
zerocopy = { version = "0.7", features = ["alloc", "derive"] }
|
||||
|
||||
@@ -64,9 +64,11 @@ static_assert!(size_of::<DiscHeader>() == 0x400);
|
||||
|
||||
impl DiscHeader {
|
||||
/// Game ID as a string.
|
||||
#[inline]
|
||||
pub fn game_id_str(&self) -> &str { from_utf8(&self.game_id).unwrap_or("[invalid]") }
|
||||
|
||||
/// Game title as a string.
|
||||
#[inline]
|
||||
pub fn game_title_str(&self) -> &str {
|
||||
CStr::from_bytes_until_nul(&self.game_title)
|
||||
.ok()
|
||||
@@ -75,9 +77,11 @@ impl DiscHeader {
|
||||
}
|
||||
|
||||
/// Whether this is a GameCube disc.
|
||||
#[inline]
|
||||
pub fn is_gamecube(&self) -> bool { self.gcn_magic.get() == 0xC2339F3D }
|
||||
|
||||
/// Whether this is a Wii disc.
|
||||
#[inline]
|
||||
pub fn is_wii(&self) -> bool { self.wii_magic.get() == 0x5D1C9EA3 }
|
||||
}
|
||||
|
||||
@@ -117,6 +121,7 @@ static_assert!(size_of::<PartitionHeader>() == 0x40);
|
||||
|
||||
impl PartitionHeader {
|
||||
/// Offset within the partition to the main DOL.
|
||||
#[inline]
|
||||
pub fn dol_offset(&self, is_wii: bool) -> u64 {
|
||||
if is_wii {
|
||||
self.dol_offset.get() as u64 * 4
|
||||
@@ -126,6 +131,7 @@ impl PartitionHeader {
|
||||
}
|
||||
|
||||
/// Offset within the partition to the file system table (FST).
|
||||
#[inline]
|
||||
pub fn fst_offset(&self, is_wii: bool) -> u64 {
|
||||
if is_wii {
|
||||
self.fst_offset.get() as u64 * 4
|
||||
@@ -135,6 +141,7 @@ impl PartitionHeader {
|
||||
}
|
||||
|
||||
/// Size of the file system table (FST).
|
||||
#[inline]
|
||||
pub fn fst_size(&self, is_wii: bool) -> u64 {
|
||||
if is_wii {
|
||||
self.fst_size.get() as u64 * 4
|
||||
@@ -144,6 +151,7 @@ impl PartitionHeader {
|
||||
}
|
||||
|
||||
/// Maximum size of the file system table (FST) across multi-disc games.
|
||||
#[inline]
|
||||
pub fn fst_max_size(&self, is_wii: bool) -> u64 {
|
||||
if is_wii {
|
||||
self.fst_max_size.get() as u64 * 4
|
||||
@@ -171,6 +179,7 @@ pub struct ApploaderHeader {
|
||||
|
||||
impl ApploaderHeader {
|
||||
/// Apploader build date as a string.
|
||||
#[inline]
|
||||
pub fn date_str(&self) -> Option<&str> {
|
||||
CStr::from_bytes_until_nul(&self.date).ok().and_then(|c| c.to_str().ok())
|
||||
}
|
||||
@@ -222,6 +231,7 @@ pub enum PartitionKind {
|
||||
}
|
||||
|
||||
impl Display for PartitionKind {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Data => write!(f, "Data"),
|
||||
@@ -237,6 +247,7 @@ impl Display for PartitionKind {
|
||||
|
||||
impl PartitionKind {
|
||||
/// Returns the directory name for the partition kind.
|
||||
#[inline]
|
||||
pub fn dir_name(&self) -> Cow<str> {
|
||||
match self {
|
||||
Self::Data => Cow::Borrowed("DATA"),
|
||||
@@ -251,6 +262,7 @@ impl PartitionKind {
|
||||
}
|
||||
|
||||
impl From<u32> for PartitionKind {
|
||||
#[inline]
|
||||
fn from(v: u32) -> Self {
|
||||
match v {
|
||||
0 => Self::Data,
|
||||
@@ -334,32 +346,39 @@ pub struct PartitionMeta {
|
||||
|
||||
impl PartitionMeta {
|
||||
/// A view into the disc header.
|
||||
#[inline]
|
||||
pub fn header(&self) -> &DiscHeader {
|
||||
DiscHeader::ref_from(&self.raw_boot[..size_of::<DiscHeader>()]).unwrap()
|
||||
}
|
||||
|
||||
/// A view into the partition header.
|
||||
#[inline]
|
||||
pub fn partition_header(&self) -> &PartitionHeader {
|
||||
PartitionHeader::ref_from(&self.raw_boot[size_of::<DiscHeader>()..]).unwrap()
|
||||
}
|
||||
|
||||
/// A view into the apploader header.
|
||||
#[inline]
|
||||
pub fn apploader_header(&self) -> &ApploaderHeader {
|
||||
ApploaderHeader::ref_from_prefix(&self.raw_apploader).unwrap()
|
||||
}
|
||||
|
||||
/// A view into the file system table (FST).
|
||||
#[inline]
|
||||
pub fn fst(&self) -> Result<Fst, &'static str> { Fst::new(&self.raw_fst) }
|
||||
|
||||
/// A view into the DOL header.
|
||||
#[inline]
|
||||
pub fn dol_header(&self) -> &DolHeader { DolHeader::ref_from_prefix(&self.raw_dol).unwrap() }
|
||||
|
||||
/// A view into the ticket. (Wii only)
|
||||
#[inline]
|
||||
pub fn ticket(&self) -> Option<&Ticket> {
|
||||
self.raw_ticket.as_ref().and_then(|v| Ticket::ref_from(v))
|
||||
}
|
||||
|
||||
/// A view into the TMD. (Wii only)
|
||||
#[inline]
|
||||
pub fn tmd_header(&self) -> Option<&TmdHeader> {
|
||||
self.raw_tmd.as_ref().and_then(|v| TmdHeader::ref_from_prefix(v))
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ static_assert!(size_of::<Node>() == 12);
|
||||
|
||||
impl Node {
|
||||
/// File system node kind.
|
||||
#[inline]
|
||||
pub fn kind(&self) -> NodeKind {
|
||||
match self.kind {
|
||||
0 => NodeKind::File,
|
||||
@@ -42,12 +43,15 @@ impl Node {
|
||||
}
|
||||
|
||||
/// Whether the node is a file.
|
||||
#[inline]
|
||||
pub fn is_file(&self) -> bool { self.kind == 0 }
|
||||
|
||||
/// Whether the node is a directory.
|
||||
#[inline]
|
||||
pub fn is_dir(&self) -> bool { self.kind == 1 }
|
||||
|
||||
/// Offset in the string table to the filename.
|
||||
#[inline]
|
||||
pub fn name_offset(&self) -> u32 {
|
||||
u32::from_be_bytes([0, self.name_offset[0], self.name_offset[1], self.name_offset[2]])
|
||||
}
|
||||
@@ -55,6 +59,7 @@ impl Node {
|
||||
/// For files, this is the partition offset of the file data. (Wii: >> 2)
|
||||
///
|
||||
/// For directories, this is the parent node index in the FST.
|
||||
#[inline]
|
||||
pub fn offset(&self, is_wii: bool) -> u64 {
|
||||
if is_wii && self.kind == 0 {
|
||||
self.offset.get() as u64 * 4
|
||||
@@ -68,6 +73,7 @@ impl Node {
|
||||
/// For directories, this is the child end index in the FST.
|
||||
///
|
||||
/// Number of child files and directories recursively is `length - offset`.
|
||||
#[inline]
|
||||
pub fn length(&self) -> u64 { self.length.get() as u64 }
|
||||
}
|
||||
|
||||
@@ -81,6 +87,7 @@ pub struct Fst<'a> {
|
||||
|
||||
impl<'a> Fst<'a> {
|
||||
/// Create a new FST view from a buffer.
|
||||
#[allow(clippy::missing_inline_in_public_items)]
|
||||
pub fn new(buf: &'a [u8]) -> Result<Self, &'static str> {
|
||||
let Some(root_node) = Node::ref_from_prefix(buf) else {
|
||||
return Err("FST root node not found");
|
||||
@@ -96,9 +103,11 @@ impl<'a> Fst<'a> {
|
||||
}
|
||||
|
||||
/// Iterate over the nodes in the FST.
|
||||
#[inline]
|
||||
pub fn iter(&self) -> FstIter { FstIter { fst: self, idx: 1 } }
|
||||
|
||||
/// Get the name of a node.
|
||||
#[allow(clippy::missing_inline_in_public_items)]
|
||||
pub fn get_name(&self, node: &Node) -> Result<Cow<str>, String> {
|
||||
let name_buf = self.string_table.get(node.name_offset() as usize..).ok_or_else(|| {
|
||||
format!(
|
||||
@@ -118,6 +127,7 @@ impl<'a> Fst<'a> {
|
||||
}
|
||||
|
||||
/// Finds a particular file or directory by path.
|
||||
#[allow(clippy::missing_inline_in_public_items)]
|
||||
pub fn find(&self, path: &str) -> Option<(usize, &Node)> {
|
||||
let mut split = path.trim_matches('/').split('/');
|
||||
let mut current = split.next()?;
|
||||
|
||||
@@ -46,6 +46,7 @@ pub enum Format {
|
||||
}
|
||||
|
||||
impl fmt::Display for Format {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Format::Iso => write!(f, "ISO"),
|
||||
@@ -81,6 +82,7 @@ pub enum Compression {
|
||||
}
|
||||
|
||||
impl fmt::Display for Compression {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Compression::None => write!(f, "None"),
|
||||
|
||||
+16
-1
@@ -1,4 +1,4 @@
|
||||
#![warn(missing_docs)]
|
||||
#![warn(missing_docs, clippy::missing_inline_in_public_items)]
|
||||
//! Library for traversing & reading Nintendo Optical Disc (GameCube and Wii) images.
|
||||
//!
|
||||
//! Originally based on the C++ library [nod](https://github.com/AxioDL/nod),
|
||||
@@ -92,10 +92,12 @@ pub enum Error {
|
||||
}
|
||||
|
||||
impl From<&str> for Error {
|
||||
#[inline]
|
||||
fn from(s: &str) -> Error { Error::Other(s.to_string()) }
|
||||
}
|
||||
|
||||
impl From<String> for Error {
|
||||
#[inline]
|
||||
fn from(s: String) -> Error { Error::Other(s) }
|
||||
}
|
||||
|
||||
@@ -109,6 +111,7 @@ pub trait ErrorContext {
|
||||
}
|
||||
|
||||
impl ErrorContext for std::io::Error {
|
||||
#[inline]
|
||||
fn context(self, context: impl Into<String>) -> Error { Error::Io(context.into(), self) }
|
||||
}
|
||||
|
||||
@@ -125,10 +128,12 @@ pub trait ResultContext<T> {
|
||||
impl<T, E> ResultContext<T> for Result<T, E>
|
||||
where E: ErrorContext
|
||||
{
|
||||
#[inline]
|
||||
fn context(self, context: impl Into<String>) -> Result<T> {
|
||||
self.map_err(|e| e.context(context))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_context<F>(self, f: F) -> Result<T>
|
||||
where F: FnOnce() -> String {
|
||||
self.map_err(|e| e.context(f()))
|
||||
@@ -155,11 +160,13 @@ pub struct Disc {
|
||||
|
||||
impl Disc {
|
||||
/// Opens a disc image from a file path.
|
||||
#[inline]
|
||||
pub fn new<P: AsRef<Path>>(path: P) -> Result<Disc> {
|
||||
Disc::new_with_options(path, &OpenOptions::default())
|
||||
}
|
||||
|
||||
/// Opens a disc image from a file path with custom options.
|
||||
#[inline]
|
||||
pub fn new_with_options<P: AsRef<Path>>(path: P, options: &OpenOptions) -> Result<Disc> {
|
||||
let io = io::block::open(path.as_ref())?;
|
||||
let reader = disc::reader::DiscReader::new(io, options)?;
|
||||
@@ -167,22 +174,27 @@ impl Disc {
|
||||
}
|
||||
|
||||
/// The disc's primary header.
|
||||
#[inline]
|
||||
pub fn header(&self) -> &DiscHeader { self.reader.header() }
|
||||
|
||||
/// Returns extra metadata included in the disc file format, if any.
|
||||
#[inline]
|
||||
pub fn meta(&self) -> DiscMeta { self.reader.meta() }
|
||||
|
||||
/// The disc's size in bytes, or an estimate if not stored by the format.
|
||||
#[inline]
|
||||
pub fn disc_size(&self) -> u64 { self.reader.disc_size() }
|
||||
|
||||
/// A list of Wii partitions on the disc.
|
||||
///
|
||||
/// **GameCube**: This will return an empty slice.
|
||||
#[inline]
|
||||
pub fn partitions(&self) -> &[PartitionInfo] { self.reader.partitions() }
|
||||
|
||||
/// Opens a decrypted partition read stream for the specified partition index.
|
||||
///
|
||||
/// **GameCube**: `index` must always be 0.
|
||||
#[inline]
|
||||
pub fn open_partition(&self, index: usize) -> Result<Box<dyn PartitionBase>> {
|
||||
self.reader.open_partition(index, &self.options)
|
||||
}
|
||||
@@ -191,15 +203,18 @@ impl Disc {
|
||||
/// the specified kind.
|
||||
///
|
||||
/// **GameCube**: `kind` must always be [`PartitionKind::Data`].
|
||||
#[inline]
|
||||
pub fn open_partition_kind(&self, kind: PartitionKind) -> Result<Box<dyn PartitionBase>> {
|
||||
self.reader.open_partition_kind(kind, &self.options)
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Disc {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { self.reader.read(buf) }
|
||||
}
|
||||
|
||||
impl Seek for Disc {
|
||||
#[inline]
|
||||
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> { self.reader.seek(pos) }
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ pub trait ReadStream: Read + Seek {
|
||||
/// Creates a windowed read sub-stream with offset and size.
|
||||
///
|
||||
/// Seeks underlying stream immediately.
|
||||
#[inline]
|
||||
fn new_window(&mut self, offset: u64, size: u64) -> io::Result<SharedWindowedReadStream> {
|
||||
self.seek(SeekFrom::Start(offset))?;
|
||||
Ok(SharedWindowedReadStream { base: self.as_dyn(), begin: offset, end: offset + size })
|
||||
@@ -22,6 +23,7 @@ pub trait ReadStream: Read + Seek {
|
||||
impl<T> ReadStream for T
|
||||
where T: Read + Seek
|
||||
{
|
||||
#[inline]
|
||||
fn as_dyn(&mut self) -> &mut dyn ReadStream { self }
|
||||
}
|
||||
|
||||
|
||||
+28
-28
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nodtool"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
edition = "2021"
|
||||
rust-version = "1.73.0"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
@@ -20,32 +20,32 @@ asm = ["md-5/asm", "nod/asm", "sha1/asm"]
|
||||
nightly = ["crc32fast/nightly"]
|
||||
|
||||
[dependencies]
|
||||
argp = "0.3.0"
|
||||
base16ct = "0.2.0"
|
||||
crc32fast = "1.4.2"
|
||||
digest = "0.10.7"
|
||||
enable-ansi-support = "0.2.1"
|
||||
hex = { version = "0.4.3", features = ["serde"] }
|
||||
indicatif = "0.17.8"
|
||||
itertools = "0.12.1"
|
||||
log = "0.4.20"
|
||||
md-5 = "0.10.6"
|
||||
nod = { version = "1.2.0", path = "../nod" }
|
||||
quick-xml = { version = "0.31.0", features = ["serialize"] }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
sha1 = "0.10.6"
|
||||
size = "0.4.1"
|
||||
supports-color = "3.0.0"
|
||||
tracing = "0.1.40"
|
||||
tracing-attributes = "0.1.27"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
xxhash-rust = { version = "0.8.10", features = ["xxh64"] }
|
||||
zerocopy = { version = "0.7.32", features = ["alloc", "derive"] }
|
||||
zstd = "0.13.1"
|
||||
argp = "0.3"
|
||||
base16ct = "0.2"
|
||||
crc32fast = "1.4"
|
||||
digest = "0.10"
|
||||
enable-ansi-support = "0.2"
|
||||
hex = { version = "0.4", features = ["serde"] }
|
||||
indicatif = "0.17"
|
||||
itertools = "0.13"
|
||||
log = "0.4"
|
||||
md-5 = "0.10"
|
||||
nod = { version = "1.2", path = "../nod" }
|
||||
quick-xml = { version = "0.36", features = ["serialize"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
sha1 = "0.10"
|
||||
size = "0.4"
|
||||
supports-color = "3.0"
|
||||
tracing = "0.1"
|
||||
tracing-attributes = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
xxhash-rust = { version = "0.8", features = ["xxh64"] }
|
||||
zerocopy = { version = "0.7", features = ["alloc", "derive"] }
|
||||
zstd = "0.13"
|
||||
|
||||
[build-dependencies]
|
||||
hex = { version = "0.4.3", features = ["serde"] }
|
||||
quick-xml = { version = "0.31.0", features = ["serialize"] }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
zerocopy = { version = "0.7.32", features = ["alloc", "derive"] }
|
||||
zstd = "0.13.1"
|
||||
hex = { version = "0.4", features = ["serde"] }
|
||||
quick-xml = { version = "0.36", features = ["serialize"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
zerocopy = { version = "0.7", features = ["alloc", "derive"] }
|
||||
zstd = "0.13"
|
||||
|
||||
Reference in New Issue
Block a user