diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 91dafc6..b2f2d49 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1074,15 +1074,15 @@ jobs:
name: rb-cli-linux-${{ matrix.arch }}-${{ needs.generate-version.outputs.version }}
path: rb-cli-linux-${{ matrix.arch }}-${{ needs.generate-version.outputs.version }}.tar.gz
- # `rb-cli` for Windows on ARM (aarch64-pc-windows-msvc), cross-compiled on the
- # x64 Windows runner (its Visual Studio carries the ARM64 MSVC libs). CLI only:
- # a full ARM64 desktop build can't link because the `chd`/`optical` core
- # (libchdman-rs) publishes no aarch64-pc-windows-msvc prebuilt — only
- # x86_64 / i686 Windows — so the GUI lane is shipped from the same pure-Rust
- # slim feature set as the PowerPC/RISC-V builds. Build-only: an ARM64 exe can't
- # execute on the x64 runner, so the test legs stay on the x64/x86 Windows jobs.
- build-rb-cli-win-arm64:
- name: Build rb-cli (Windows ARM64)
+ # Full desktop app + `rb-cli` for Windows on ARM64 (aarch64-pc-windows-msvc),
+ # cross-compiled on the x64 Windows runner (its Visual Studio carries the ARM64
+ # MSVC libs). First-class GUI build: libchdman-rs 0.288.8 publishes an
+ # aarch64-pc-windows-msvc prebuilt, so the `chd`/`optical` core links and the
+ # app ships the same default feature set as the x64/x86 Windows builds.
+ # Build-only (continue-on-error): an ARM64 exe can't execute on the x64 runner,
+ # so the test legs stay on the x64/x86 Windows jobs.
+ build-windows-arm64:
+ name: Build Windows ARM64 (GUI + rb-cli)
# Skipped only when a manual run unticks "desktop"; every push builds it.
if: ${{ github.event_name != 'workflow_dispatch' || inputs.desktop }}
needs: generate-version
@@ -1118,8 +1118,26 @@ jobs:
path: target
key: ${{ runner.os }}-aarch64-pc-windows-msvc-cargo-build-${{ hashFiles('**/Cargo.lock') }}
- - name: Build rb-cli (pure-Rust slim — no GUI / CHD / optical)
- run: cargo build --release --bin rb-cli --no-default-features --features pure-zstd,remote --target aarch64-pc-windows-msvc
+ # Default features (GUI + chd + optical + native-zstd/zlib + remote) — one
+ # build produces both rusty-backup.exe and rb-cli.exe, like build-windows.
+ - name: Build (GUI + rb-cli, default features)
+ run: cargo build --release --target aarch64-pc-windows-msvc
+
+ - name: Package app
+ shell: bash
+ run: |
+ cd target/aarch64-pc-windows-msvc/release
+ if [ -f "../../../assets/icons/icon.ico" ]; then
+ 7z a ../../../Rusty-Backup-windows-arm64-${{ needs.generate-version.outputs.version }}.zip rusty-backup.exe ../../../assets/icons/icon.ico
+ else
+ 7z a ../../../Rusty-Backup-windows-arm64-${{ needs.generate-version.outputs.version }}.zip rusty-backup.exe
+ fi
+
+ - name: Upload app artifact
+ uses: actions/upload-artifact@v7
+ with:
+ name: Rusty-Backup-windows-arm64-${{ needs.generate-version.outputs.version }}
+ path: Rusty-Backup-windows-arm64-${{ needs.generate-version.outputs.version }}.zip
- name: Stage CLI binary
shell: bash
@@ -1135,6 +1153,121 @@ jobs:
name: rb-cli-windows-arm64-${{ needs.generate-version.outputs.version }}
path: rb-cli-windows-arm64-${{ needs.generate-version.outputs.version }}.zip
+ # Full desktop GUI for Linux RISC-V 64 (riscv64gc-unknown-linux-gnu),
+ # cross-compiled on x64. There is no GitHub-hosted riscv64 runner, so unlike
+ # the x64/arm64 AppImage jobs this can't build/package natively: we cross-link
+ # the default feature set (GUI + CHD + optical) against a riscv64 GTK/GL sysroot
+ # pulled in via dpkg multiarch (ubuntu-ports), then ship a plain dynamically-
+ # linked tarball — NOT a self-contained AppImage; it uses the user's system
+ # GTK3 at runtime. libchdman-rs 0.288.8 publishes the riscv64gc prebuilt that
+ # makes the CHD core link. continue-on-error: best-effort, can't block release.
+ build-gui-linux-riscv64:
+ name: Build Linux RISC-V 64 GUI (rusty-backup)
+ # Skipped only when a manual run unticks "desktop"; every push builds it.
+ if: ${{ github.event_name != 'workflow_dispatch' || inputs.desktop }}
+ needs: generate-version
+ runs-on: ubuntu-latest
+ continue-on-error: true
+ env:
+ RELEASE_VERSION: ${{ needs.generate-version.outputs.version }}
+ # libchdman is C++; link through g++ so libstdc++ is pulled in.
+ CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER: riscv64-linux-gnu-g++
+ # cc-rs (cd-da-reader's C shim) — the riscv64gc triple doesn't auto-map to
+ # the riscv64-linux-gnu-* cross tools, so point it at them explicitly.
+ CC_riscv64gc_unknown_linux_gnu: riscv64-linux-gnu-gcc
+ CXX_riscv64gc_unknown_linux_gnu: riscv64-linux-gnu-g++
+ # gtk-sys/glib-sys (system-deps) must read the riscv64 multiarch .pc files
+ # and accept a cross build.
+ PKG_CONFIG_ALLOW_CROSS: "1"
+ PKG_CONFIG_LIBDIR: /usr/lib/riscv64-linux-gnu/pkgconfig:/usr/share/pkgconfig
+ PKG_CONFIG_SYSROOT_DIR: /
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Install Rust
+ uses: dtolnay/rust-toolchain@stable
+ with:
+ targets: riscv64gc-unknown-linux-gnu
+
+ - name: Add riscv64 as a foreign dpkg architecture (ubuntu-ports)
+ run: |
+ set -euxo pipefail
+ # riscv64 lives in ubuntu-ports, not the amd64 archive. Pin every
+ # existing source to amd64 so `apt-get update` doesn't 404 trying to
+ # fetch riscv64 indexes from archive.ubuntu.com / PPAs, then add one
+ # riscv64-only ports source.
+ sudo dpkg --add-architecture riscv64
+ for f in /etc/apt/sources.list.d/*.sources; do
+ [ -f "$f" ] || continue
+ grep -q '^Architectures:' "$f" || sudo sed -i '/^Types:/a Architectures: amd64' "$f"
+ done
+ for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list; do
+ [ -f "$f" ] || continue
+ sudo sed -i -E 's|^deb (http)|deb [arch=amd64] \1|; s|^deb-src (http)|deb-src [arch=amd64] \1|' "$f"
+ done
+ printf '%s\n' \
+ 'Types: deb' \
+ 'URIs: http://ports.ubuntu.com/ubuntu-ports' \
+ 'Suites: noble noble-updates noble-backports noble-security' \
+ 'Components: main universe' \
+ 'Architectures: riscv64' \
+ 'Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg' \
+ | sudo tee /etc/apt/sources.list.d/riscv64-ports.sources >/dev/null
+ sudo apt-get update
+
+ - name: Install riscv64 cross toolchain + GTK/GL sysroot
+ run: |
+ set -euxo pipefail
+ sudo apt-get install -y \
+ g++-riscv64-linux-gnu binutils-riscv64-linux-gnu file \
+ libgtk-3-dev:riscv64 libglib2.0-dev:riscv64 \
+ libgl1-mesa-dev:riscv64 libegl1-mesa-dev:riscv64 \
+ libxkbcommon-dev:riscv64 libxkbcommon-x11-dev:riscv64 \
+ libx11-dev:riscv64 libxcb1-dev:riscv64 libxcursor-dev:riscv64 \
+ libxi-dev:riscv64 libxrandr-dev:riscv64 libwayland-dev:riscv64
+
+ - name: Cache cargo registry
+ uses: actions/cache@v5
+ with:
+ path: ~/.cargo/registry
+ key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
+
+ - name: Cache cargo index
+ uses: actions/cache@v5
+ with:
+ path: ~/.cargo/git
+ key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
+
+ - name: Cache cargo build
+ uses: actions/cache@v5
+ with:
+ path: target
+ key: ${{ runner.os }}-riscv64gc-gui-cargo-build-${{ hashFiles('**/Cargo.lock') }}
+
+ - name: Build (default features = GUI + CHD + optical)
+ run: cargo build --release --target riscv64gc-unknown-linux-gnu
+
+ - name: Strip + verify ELF
+ run: |
+ set -e
+ ELF=target/riscv64gc-unknown-linux-gnu/release/rusty-backup
+ riscv64-linux-gnu-strip "$ELF"
+ file "$ELF"
+ ls -lh "$ELF"
+ riscv64-linux-gnu-objdump -p "$ELF" | grep -E 'NEEDED|GLIBC_' || true
+
+ - name: Package app
+ run: |
+ TGZ="Rusty-Backup-linux-riscv64-${{ needs.generate-version.outputs.version }}.tar.gz"
+ tar -C target/riscv64gc-unknown-linux-gnu/release -czf "$TGZ" rusty-backup
+ ls -lh "$TGZ"
+
+ - name: Upload app artifact
+ uses: actions/upload-artifact@v7
+ with:
+ name: Rusty-Backup-linux-riscv64-${{ needs.generate-version.outputs.version }}
+ path: Rusty-Backup-linux-riscv64-${{ needs.generate-version.outputs.version }}.tar.gz
+
# Turn the `appliance` choice input into a JSON arch list for the matrix
# below. A manual dispatch honours none / i586 / i686 / both. A push to
# main/master builds both arches automatically (the full Buildroot build is
@@ -1350,11 +1483,13 @@ jobs:
- build-linux-appimage
- build-linux-packages
- build-rb-cli-mini-armv7
- # CLI-only cross-builds for extra CPU arches (PowerPC BE 32/64, RISC-V 64,
- # Windows ARM64). continue-on-error like mini-armv7: listed here so the
- # release waits for their tarballs/zips, but a failure can't block it.
+ # Cross-builds for extra CPU arches: PowerPC BE 32/64 (rb-cli only), plus
+ # full GUI + rb-cli for Windows ARM64 and Linux RISC-V 64. continue-on-error
+ # like mini-armv7: listed here so the release waits for their tarballs/zips,
+ # but a failure can't block the desktop release.
- build-rb-cli-extra-arch
- - build-rb-cli-win-arm64
+ - build-windows-arm64
+ - build-gui-linux-riscv64
# build-appliance is skipped on feature branches and on a manual
# appliance=none run. A skipped `needs` job would normally skip the whole
# release, so the release `if` below uses `!cancelled()` + explicit
@@ -1406,6 +1541,7 @@ jobs:
"Rusty-Backup-linux-x64-${VER}.rpm" "Rusty-Backup-linux-x64-${VER}.pkg.tar.zst" \
"Rusty-Backup-linux-arm64-${VER}.AppImage" "Rusty-Backup-linux-arm64-${VER}.deb" \
"Rusty-Backup-linux-arm64-${VER}.rpm" "Rusty-Backup-linux-arm64-${VER}.pkg.tar.zst" \
+ "Rusty-Backup-windows-arm64-${VER}.zip" "Rusty-Backup-linux-riscv64-${VER}.tar.gz" \
"rb-cli-windows-x64-${VER}.zip" "rb-cli-windows-x86-${VER}.zip" \
"rb-cli-macos-arm64-${VER}.zip" "rb-cli-macos-x64-${VER}.zip" \
"rb-cli-linux-x64-${VER}.tar.gz" "rb-cli-linux-arm64-${VER}.tar.gz" \
@@ -1429,13 +1565,15 @@ jobs:
|---|---|---|---|
| **Windows 11 / 10** | x64 (64-bit) | [Installer](${BASE}/Rusty-Backup-Setup-windows-x64-${VER}.exe) · [Portable zip](${BASE}/Rusty-Backup-windows-x64-${VER}.zip) | [rb-cli](${BASE}/rb-cli-windows-x64-${VER}.zip) |
| **Windows 10** | x86 (32-bit) | [Installer](${BASE}/Rusty-Backup-Setup-windows-x86-${VER}.exe) · [Portable zip](${BASE}/Rusty-Backup-windows-x86-${VER}.zip) | [rb-cli](${BASE}/rb-cli-windows-x86-${VER}.zip) |
+ | **Windows 11** | ARM64 (AArch64) | [Portable zip](${BASE}/Rusty-Backup-windows-arm64-${VER}.zip) | [rb-cli](${BASE}/rb-cli-windows-arm64-${VER}.zip) |
| **macOS 10.13+** | Apple Silicon (arm64) | [.dmg](${BASE}/Rusty-Backup-macos-arm64-${VER}.dmg) | [rb-cli](${BASE}/rb-cli-macos-arm64-${VER}.zip) |
| **macOS 10.13+** | Intel (x64) | [.dmg](${BASE}/Rusty-Backup-macos-x64-${VER}.dmg) | [rb-cli](${BASE}/rb-cli-macos-x64-${VER}.zip) |
| **Linux** (glibc >= 2.39) | x64 | [AppImage](${BASE}/Rusty-Backup-linux-x64-${VER}.AppImage) · [deb](${BASE}/Rusty-Backup-linux-x64-${VER}.deb) · [rpm](${BASE}/Rusty-Backup-linux-x64-${VER}.rpm) · [pkg](${BASE}/Rusty-Backup-linux-x64-${VER}.pkg.tar.zst) | [rb-cli](${BASE}/rb-cli-linux-x64-${VER}.tar.gz) |
| **Linux** (glibc >= 2.39) | arm64 | [AppImage](${BASE}/Rusty-Backup-linux-arm64-${VER}.AppImage) · [deb](${BASE}/Rusty-Backup-linux-arm64-${VER}.deb) · [rpm](${BASE}/Rusty-Backup-linux-arm64-${VER}.rpm) · [pkg](${BASE}/Rusty-Backup-linux-arm64-${VER}.pkg.tar.zst) | [rb-cli](${BASE}/rb-cli-linux-arm64-${VER}.tar.gz) |
+ | **Linux** (glibc >= 2.39) | RISC-V 64-bit (\`riscv64gc\`) | [tar.gz](${BASE}/Rusty-Backup-linux-riscv64-${VER}.tar.gz) | [rb-cli](${BASE}/rb-cli-linux-riscv64-${VER}.tar.gz) |
| **MiSTer FPGA** (Cortex-A9, glibc 2.31) | armv7 hardfloat | _(GUI not built for this target)_ | [rb-cli-mini](${BASE}/rb-cli-mini-armv7-linux-${VER}.tar.gz) |
- **Windows:** x86 = 32-bit (Windows 10, the last Windows with 32-bit support). · **macOS:** requires 10.13 High Sierra or later. · **Linux:** deb = Debian/Ubuntu, rpm = Fedora/RHEL/openSUSE, pkg = Arch/Manjaro; the packages and \`rb-cli\` are built against **glibc 2.39** (Ubuntu 24.04), while the **AppImage** is self-contained and runs on most distributions regardless of glibc. · **MiSTer FPGA:** \`rb-cli-mini\` is a slim \`rb-cli\` cross-compiled for the MiSTer's armv7 hardfloat Intel Cyclone V SoC against Ubuntu 20.04 glibc 2.31 — same baseline as the MiSTer Buildroot rootfs and the upstream \`libchdman-rs\` armv7 prebuilt. GUI/optical features are off; CHD is in. Drop it onto the SD at \`/media/fat/Scripts/rb-cli\`. · Every **rb-cli** download contains a single \`rb-cli\` executable (\`rb-cli.exe\` on Windows).
+ **Windows:** x86 = 32-bit (Windows 10, the last Windows with 32-bit support). · **macOS:** requires 10.13 High Sierra or later. · **Linux:** deb = Debian/Ubuntu, rpm = Fedora/RHEL/openSUSE, pkg = Arch/Manjaro; the packages and \`rb-cli\` are built against **glibc 2.39** (Ubuntu 24.04), while the **AppImage** is self-contained and runs on most distributions regardless of glibc. · **Windows ARM64 / RISC-V 64:** cross-compiled, build-only legs — the Windows ARM64 app is a portable zip and the RISC-V 64 app is a plain dynamically-linked tarball that uses your system GTK3 (not a self-contained AppImage like x64/arm64). · **MiSTer FPGA:** \`rb-cli-mini\` is a slim \`rb-cli\` cross-compiled for the MiSTer's armv7 hardfloat Intel Cyclone V SoC against Ubuntu 20.04 glibc 2.31 — same baseline as the MiSTer Buildroot rootfs and the upstream \`libchdman-rs\` armv7 prebuilt. GUI/optical features are off; CHD is in. Drop it onto the SD at \`/media/fat/Scripts/rb-cli\`. · Every **rb-cli** download contains a single \`rb-cli\` executable (\`rb-cli.exe\` on Windows).
### Command-line (\`rb-cli\`) for other CPU architectures
@@ -1445,10 +1583,8 @@ jobs:
|---|---|---|
| **Linux** (glibc >= 2.39) | PowerPC 64-bit, big-endian (G5 / POWER) | [rb-cli](${BASE}/rb-cli-linux-ppc64-${VER}.tar.gz) |
| **Linux** (glibc >= 2.39) | PowerPC 32-bit, big-endian (G3 / G4) | [rb-cli](${BASE}/rb-cli-linux-ppc32-${VER}.tar.gz) |
- | **Linux** (glibc >= 2.39) | RISC-V 64-bit (\`riscv64gc\`) | [rb-cli](${BASE}/rb-cli-linux-riscv64-${VER}.tar.gz) |
- | **Windows 11** | ARM64 (AArch64) | [rb-cli](${BASE}/rb-cli-windows-arm64-${VER}.zip) |
- The Linux builds use the same **glibc 2.39** (Ubuntu 24.04) baseline as the x64/arm64 \`rb-cli\`. **Windows ARM64 is the CLI only** — a full ARM64 desktop app can't be built yet because the CHD core (\`libchdman-rs\`) publishes no \`aarch64-pc-windows-msvc\` prebuilt. Each download contains a single \`rb-cli\` executable (\`rb-cli.exe\` on Windows).
+ These PowerPC builds use the same **glibc 2.39** (Ubuntu 24.04) baseline as the x64/arm64 \`rb-cli\`, and each download contains a single \`rb-cli\` executable. (Windows ARM64 and RISC-V 64 now ship a full desktop app — see the main table above.)
### Install on a MiSTer FPGA (one line, auto-updating)
diff --git a/Cargo.lock b/Cargo.lock
index 7fd35a4..88a26f1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -305,7 +305,7 @@ dependencies = [
"objc2-foundation 0.3.2",
"parking_lot",
"percent-encoding",
- "windows-sys 0.52.0",
+ "windows-sys 0.60.2",
"x11rb",
]
@@ -1317,7 +1317,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -1650,7 +1650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -2773,9 +2773,9 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "libchdman-rs"
-version = "0.288.5"
+version = "0.288.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f23c6f7338dec4f8dd7ae07e2956b5d9633be9b0dd81782aa28b6db3b9239534"
+checksum = "f9e349d24a5de45ed8a8c9f01db055d0f393771881169c39ae39707618cffd22"
dependencies = [
"cc",
"hex",
@@ -3545,9 +3545,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
[[package]]
name = "opticaldiscs"
-version = "0.4.5"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91726e09312bbe825916a5c1589e9a080d91e6e1fa4f43995c2941c8812f12ab"
+checksum = "ee8945c35cfd0e2364cea3597617e0e9c74e09d7a0c62df4bdc381903ae88ec4"
dependencies = [
"cue_sheet",
"libchdman-rs",
@@ -3985,7 +3985,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
- "windows-sys 0.52.0",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -4366,7 +4366,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.12.1",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -4425,7 +4425,7 @@ dependencies = [
"security-framework",
"security-framework-sys",
"webpki-root-certs",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -5029,7 +5029,7 @@ dependencies = [
"getrandom 0.4.2",
"once_cell",
"rustix 1.1.4",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -6044,7 +6044,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -6405,7 +6405,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d6f32a0ff4a9f6f01231eb2059cc85479330739333e0e58cadf03b6af2cca10"
dependencies = [
"cfg-if",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index e62d51e..d6723da 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -97,7 +97,7 @@ chd = ["dep:libchdman-rs"]
# Physical optical-drive I/O (rip / data-track read) plus optical-image
# convert/browse. Builds on armv7 (MiSTer) too: cd-da-reader is cc+libc
# (a self-compiled SG_IO shim — NO system libcdio link), and opticaldiscs
-# (>=0.4.5) reuses the SAME libchdman-rs 0.288.5 prebuilt as the `chd` feature
+# (>=0.4.5) reuses the SAME libchdman-rs 0.288.8 prebuilt as the `chd` feature
# — which ships an armv7-gnueabihf-glibc2.31 build — so the two dedupe to one
# libchdman. At runtime it needs a drive present (e.g. /dev/sr0); harmless to
# include when none is attached.
@@ -116,13 +116,13 @@ remote = []
eframe = { version = "0.34", features = ["glow"], optional = true }
egui = { version = "0.34", optional = true }
rfd = { version = "0.17", default-features = false, features = ["gtk3"], optional = true }
-libchdman-rs = { version = "0.288.5", features = ["prebuilt"], optional = true }
+libchdman-rs = { version = "0.288.8", features = ["prebuilt"], optional = true }
# rustls-tls keeps reqwest off openssl-sys so the gui-feature build doesn't drag
# in OpenSSL native libs; the cross/armv7 path doesn't need reqwest at all
# (it's behind `gui`).
reqwest = { version = "0.13", default-features = false, features = ["blocking", "json", "rustls"], optional = true }
webbrowser = { version = "1.0", optional = true }
-opticaldiscs = { version = "0.4.2", features = ["drives"], optional = true }
+opticaldiscs = { version = "0.5", features = ["drives"], optional = true }
cd-da-reader = { git = "https://github.com/danifunker/rust-cd-da-reader", branch = "add-data-support-0-4-1", optional = true }
# Always-on dependencies — used by rb-cli (mini included).
diff --git a/src/cli/verbs/optical.rs b/src/cli/verbs/optical.rs
index e0c9522..15d5fcd 100644
--- a/src/cli/verbs/optical.rs
+++ b/src/cli/verbs/optical.rs
@@ -393,9 +393,9 @@ fn walk_tree(
out.push_str(&format!(" (rsrc: {})", format_size(rs)));
}
}
- if let Some(tc) = &child.type_code {
+ if let Some(tc) = child.type_code_string() {
out.push_str(&format!(" {tc}"));
- if let Some(cc) = &child.creator_code {
+ if let Some(cc) = child.creator_code_string() {
out.push_str(&format!("/{cc}"));
}
}
@@ -528,8 +528,8 @@ fn extract(
.read_resource_fork(entry)
.map_err(|e| anyhow::anyhow!("read_resource_fork: {e}"))?
.unwrap_or_default();
- let type_code = fourcc(entry.type_code.as_deref());
- let creator_code = fourcc(entry.creator_code.as_deref());
+ let type_code = entry.type_code.unwrap_or([0; 4]);
+ let creator_code = entry.creator_code.unwrap_or([0; 4]);
let mb = resource_fork::build_macbinary(
&safe_name,
&type_code,
@@ -550,8 +550,8 @@ fn extract(
f.write_all(&data)?;
f.flush()?;
if has_rsrc && mode != M::DataForkOnly {
- let type_code = fourcc(entry.type_code.as_deref());
- let creator_code = fourcc(entry.creator_code.as_deref());
+ let type_code = entry.type_code.unwrap_or([0; 4]);
+ let creator_code = entry.creator_code.unwrap_or([0; 4]);
let rsrc = fs
.read_resource_fork(entry)
.map_err(|e| anyhow::anyhow!("read_resource_fork: {e}"))?
@@ -597,16 +597,6 @@ fn extract(
Ok(())
}
-fn fourcc(s: Option<&str>) -> [u8; 4] {
- let mut out = [0u8; 4];
- if let Some(code) = s {
- let bytes = code.as_bytes();
- let n = bytes.len().min(4);
- out[..n].copy_from_slice(&bytes[..n]);
- }
- out
-}
-
fn parse_resource_fork_mode(s: &str) -> Option {
match s.to_ascii_lowercase().replace('-', "").as_str() {
"dataonly" | "data" => Some(CliResourceForkMode::DataOnly),
diff --git a/src/optical/browse_view.rs b/src/optical/browse_view.rs
index bc76778..01f7419 100644
--- a/src/optical/browse_view.rs
+++ b/src/optical/browse_view.rs
@@ -364,10 +364,10 @@ impl OpticalDiscBrowseView {
ui.label(egui::RichText::new(&entry.name).strong());
ui.horizontal(|ui| {
ui.label(format!("Size: {}", entry.size_string()));
- if let Some(ref tc) = entry.type_code {
+ if let Some(tc) = entry.type_code_string() {
ui.label(format!("Type: {tc}"));
}
- if let Some(ref cc) = entry.creator_code {
+ if let Some(cc) = entry.creator_code_string() {
ui.label(format!("Creator: {cc}"));
}
if let Some(rsrc) = entry.resource_fork_size {
@@ -684,9 +684,9 @@ fn walk_optical_tree(
out.push_str(&format!(" (rsrc: {})", format_size(rsrc)));
}
}
- if let Some(ref tc) = child.type_code {
+ if let Some(tc) = child.type_code_string() {
out.push_str(&format!(" {tc}"));
- if let Some(ref cc) = child.creator_code {
+ if let Some(cc) = child.creator_code_string() {
out.push_str(&format!("/{cc}"));
}
}
@@ -768,16 +768,8 @@ fn extract_entry(
let data = fs.read_file(entry)?;
let rsrc_data = fs.read_resource_fork(entry)?.unwrap_or_default();
- let type_code = entry
- .type_code
- .as_ref()
- .map(|s| fourcc_bytes(s))
- .unwrap_or([0; 4]);
- let creator_code = entry
- .creator_code
- .as_ref()
- .map(|s| fourcc_bytes(s))
- .unwrap_or([0; 4]);
+ let type_code = entry.type_code.unwrap_or([0; 4]);
+ let creator_code = entry.creator_code.unwrap_or([0; 4]);
let mb = resource_fork::build_macbinary(
&safe_name,
@@ -809,16 +801,8 @@ fn extract_entry(
// Handle resource fork
if has_rsrc && resource_fork_mode != ResourceForkMode::DataForkOnly {
- let type_code = entry
- .type_code
- .as_ref()
- .map(|s| fourcc_bytes(s))
- .unwrap_or([0; 4]);
- let creator_code = entry
- .creator_code
- .as_ref()
- .map(|s| fourcc_bytes(s))
- .unwrap_or([0; 4]);
+ let type_code = entry.type_code.unwrap_or([0; 4]);
+ let creator_code = entry.creator_code.unwrap_or([0; 4]);
let rsrc_data = fs.read_resource_fork(entry)?.unwrap_or_default();
@@ -873,16 +857,6 @@ fn extract_entry(
Ok(())
}
-/// Convert a 4-character type/creator string to a byte array.
-fn fourcc_bytes(s: &str) -> [u8; 4] {
- let bytes = s.as_bytes();
- let mut result = [b' '; 4];
- for (i, &b) in bytes.iter().take(4).enumerate() {
- result[i] = b;
- }
- result
-}
-
/// Human-friendly size string.
fn format_size(bytes: u64) -> String {
match bytes {