DX/CX Asset unpacker/repacker
Find a file
2026-03-13 18:51:57 +01:00
include/teampandory/pandorydx/threepack experimental callback status api 2026-03-13 18:51:57 +01:00
packaging Initial commit of new C++-based DX/CX repacker 2026-03-10 21:33:40 +01:00
po Initial commit of new C++-based DX/CX repacker 2026-03-10 21:33:40 +01:00
src experimental callback status api 2026-03-13 18:51:57 +01:00
.clang-format Initial commit of new C++-based DX/CX repacker 2026-03-10 21:33:40 +01:00
.gitignore Initial commit of new C++-based DX/CX repacker 2026-03-10 21:33:40 +01:00
AGENTS.md Initial commit of new C++-based DX/CX repacker 2026-03-10 21:33:40 +01:00
build.sh Initial commit of new C++-based DX/CX repacker 2026-03-10 21:33:40 +01:00
LICENSE added gpl2 2026-03-10 21:39:26 +01:00
meson.build added gpl2 2026-03-10 21:39:26 +01:00
README.md experimental callback status api 2026-03-13 18:51:57 +01:00

threepack

threepack is a C++ library and CLI for inspecting, extracting, repacking, and identifying Pandora DX/CX firmware asset archives.

License: GPL-2.0. See LICENSE.

It supports:

  • top-level archive listing
  • recursive archive listing
  • full and shallow extraction
  • targeted file extraction
  • archive repacking
  • firmware identification by SHA-1
  • optional JSON CLI output
  • gettext-based CLI translations

Dependencies

Required build dependencies:

  • a C++20 compiler
  • meson
  • ninja
  • pkg-config
  • gettext

Optional build dependencies:

  • nlohmann-json for --json
  • wasi-sdk and a WASI runtime such as wasmtime for wasi builds

Install Dependencies

Debian/Ubuntu:

sudo apt-get update
sudo apt-get install -y \
  build-essential meson ninja-build pkg-config \
  gettext \
  nlohmann-json3-dev

Fedora:

sudo dnf install -y \
  gcc-c++ meson ninja-build pkgconf-pkg-config \
  gettext \
  nlohmann-json-devel

Arch Linux:

sudo pacman -S --needed \
  base-devel meson ninja pkgconf \
  gettext \
  nlohmann-json

Build

Simple wrapper script:

./build.sh

With no arguments, build.sh prints its help.

Supported build.sh targets:

  • linux32
  • linux64
  • deb32
  • deb64
  • rpm32
  • rpm64
  • appimage32
  • appimage64
  • wasi
  • rpi32
  • rpi64
  • armv7
  • armv8
  • mingw32
  • mingw64

MSYS2 notes:

  • inside MINGW64, UCRT64, or MINGW32 shells, build.sh prints pacman package install commands instead of Debian/Fedora ones
  • mingw64 on UCRT64 will suggest mingw-w64-ucrt-x86_64-* packages
  • mingw64 on MINGW64 will suggest mingw-w64-x86_64-* packages
  • under MSYS2, only mingw32 and mingw64 targets are supported
  • mingw32 targets 32-bit Windows with an XP-era minimum
  • mingw64 targets 64-bit Windows 10 and Windows 11

Examples:

./build.sh
./build.sh linux64
./build.sh linux32
./build.sh deb32
./build.sh deb64
./build.sh rpm32
./build.sh rpm64
./build.sh appimage64
./build.sh appimage32
./build.sh wasi
./build.sh rpi64
./build.sh mingw32
./build.sh mingw64

Debian/Ubuntu ARM cross-build note:

  • rpi32 and armv7 use the Debian/Ubuntu cross toolchain packages such as crossbuild-essential-armhf, libc6-dev-armhf-cross, and linux-libc-dev-armhf-cross
  • rpi64 and armv8 use the matching arm64 cross toolchain packages such as crossbuild-essential-arm64, libc6-dev-arm64-cross, and linux-libc-dev-arm64-cross
  • build.sh attempts to install those automatically if they are missing

Linux 32-bit note:

  • linux32 uses the host GCC toolchain with -m32
  • on Debian/Ubuntu this usually means gcc-multilib, g++-multilib, and libc6-dev-i386
  • build.sh attempts to install these automatically if they are missing

Native package note:

  • deb32 builds a 32-bit .deb package and deb64 builds a 64-bit .deb package for Debian-family systems such as Debian, Ubuntu, Linux Mint, and Pop!_OS
  • rpm32 builds a 32-bit .rpm package and rpm64 builds a 64-bit .rpm package for RPM-family systems such as Fedora, RHEL, Rocky Linux, AlmaLinux, and openSUSE
  • package output defaults to out/packages/
  • both targets stage a normal /usr install tree via meson install before producing the final package

AppImage note:

  • appimage64 builds a 64-bit Linux AppImage
  • appimage32 builds a 32-bit Linux AppImage using the same -m32 toolchain setup as linux32
  • both targets download appimagetool from https://github.com/AppImage/appimagetool before the Meson build starts
  • on systems without curl, build.sh attempts to install it automatically
  • packaged output defaults to out/threepack-x86_64.AppImage or out/threepack-i686.AppImage

Direct Meson usage:

meson setup build
meson compile -C build

Install:

meson install -C build

Installed artifacts:

  • libthreepack.so
  • threepack
  • C++ header: teampandory/pandorydx/threepack/threepack.hpp
  • C header: teampandory/pandorydx/threepack/threepack.h
  • public headers under include/teampandory/...
  • gettext catalogs
  • threepack.pc

Library Usage

The C++ API exposes the reusable archive logic through teampandory::pandorydx::threepack::ArchiveTool.

For extract and repack operations, callers may supply a progress callback to receive coarse-grained status updates while archive entries, nested image packages, libcfg archives, matched files, and the final outer package are processed.

Example:

#include <iostream>
#include <teampandory/pandorydx/threepack/threepack.hpp>

using namespace teampandory::pandorydx::threepack;

int main() {
    ArchiveTool archiveTool;
    archiveTool.extractAssetFile(
        "rads.zip",
        "rads-out",
        [](const ProgressInfo &progress) {
            std::cout << "[extract] " << progress.message;
            if (!progress.path.empty()) {
                std::cout << ": " << progress.path.string();
            }
            if (progress.total > 0) {
                std::cout << " (" << progress.current << "/" << progress.total << ")";
            }
            std::cout << '\n';
        });

    archiveTool.packAssetFile(
        "rads.zip",
        "rads-repacked.zip",
        [](const ProgressInfo &progress) {
            std::cout << "[pack] ";
            std::cout << progress.message;
            if (!progress.path.empty()) {
                std::cout << ": " << progress.path.string();
            }
            if (progress.total > 0) {
                std::cout << " (" << progress.current << "/" << progress.total << ")";
            }
            std::cout << '\n';
        });
}

Progress phases currently include:

  • ProgressPhase::scanning
  • ProgressPhase::extractingOuterEntry
  • ProgressPhase::extractingImage
  • ProgressPhase::extractingLibcfg
  • ProgressPhase::copyingMatch
  • ProgressPhase::packingImage
  • ProgressPhase::packingLibcfg
  • ProgressPhase::packingOuterArchive
  • ProgressPhase::finished

Build output notes:

  • threepack now uses internal AES-256-ECB, SHA-1, and CRC32 logic instead of OpenSSL and zlib
  • threepack builds as the normal dynamically linked CLI

WASI

threepack can also be built as a command-line WebAssembly binary for WASI runtimes such as wasmtime.

Build a WASI binary:

./build.sh wasi

Run it with a WASI runtime:

wasmtime run ./out/wasi/threepack -- --help
wasmtime run ./out/wasi/threepack -- l /path/to/rads.zip

Notes:

  • the wasi target expects wasm32-wasi-clang, wasm32-wasi-clang++, llvm-ar, and llvm-strip
  • build.sh wasi now tries to install wasi-sdk from the package manager when the platform provides it
  • if that still does not provide wasm32-wasi-clang, it falls back to a matching upstream wasi-sdk release from GitHub
  • the GitHub fallback follows the upstream wasi-sdk install layout using bin/clang, bin/clang++, and share/wasi-sysroot
  • when using that SDK layout, build.sh injects the matching --sysroot=... arguments into the Meson cross file automatically
  • WASI_SDK_PATH is honored when it already points at an installed SDK
  • override the bootstrap location with THREEPACK_WASI_SDK_ROOT
  • override the release version or URL with THREEPACK_WASI_SDK_VERSION or THREEPACK_WASI_SDK_VERSION_FULL / THREEPACK_WASI_SDK_URL
  • build.sh wasi now stages nlohmann/json.hpp automatically for the WASI compiler include path
  • it first tries distro packages and then falls back to downloading the pinned upstream release tarball from GitHub
  • the output is a command-line WebAssembly binary intended for WASI, not a browser build

CLI Usage

Show help:

threepack --help

List top-level entries:

threepack l /path/to/asset_zip_file.zip

Example:

Listing archive: ./cx1.2jamma/rads.zip

   Size     Packed       CRC   HdrOff      Off Name
---------- ---------- -------- ------ -------- ------------------------------
   3368960    3368976 4d599dbf     16    32784 libcfg_me4.so
   4065280    4065296 0eecb9ea     28  3401760 libcfg_mk.so
   3734880    3734880 98af8143     40  7467056 imagesce.so

List recursively:

threepack lr /path/to/asset_zip_file.zip

Example:

Recursive listing: ./cx1.2jamma/rads.zip

   Size     Packed       CRC   HdrOff      Off Name
---------- ---------- -------- ------ -------- ------------------------------
   3368960    3368976 4d599dbf     16    32784 libcfg_me4.so
---------- ---------- -------- ------ -------- ------------------------------
                                              [libcfg_me4.so <tar archive>]
     26952      26952                                credit.bmp
      8904       8904                                cursor_coin.bmp

Extract recursively:

threepack x /path/to/rads.zip /tmp/out

Extract top level only:

threepack e /path/to/rads.zip /tmp/out

Extract one matching file recursively:

threepack xf /path/to/rads.zip config_fav.png /tmp/out

Extract one matching top-level file only:

threepack ef /path/to/rads.zip imagesce.so /tmp/out

Repack:

threepack a /tmp/out/rads.zip /tmp/repacked-rads.zip

Test readability:

threepack t /path/to/rads.zip

Example:

Testing archive: /path/to/asset_zip_file.zip
Entries: 29
Everything is Ok

Identify firmware:

threepack id /path/to/had.zip

Example:

cx1.2jamma - CX, Jamma variant, Firmware 1.2

CLI Output Formats

JSON:

threepack l /path/to/rads.zip --json

Example:

{
  "files": 29,
  "rows": [
    {
      "crc": 1297718719,
      "depth": 0,
      "file_offset": 32784,
      "header_offset": 16,
      "kind": "entry",
      "name": "libcfg_me4.so",
      "packed": 3368976,
      "size": 3368976
    }
  ],
  "title": "Listing archive: /path/to/asset_zip_file.zip",
  "total_packed": 86887808
}

If JSON support was not compiled in, threepack prints a runtime error when --json is used.

Localization

By default, the CLI uses the process locale from the environment, like a normal gettext application.

Standard gettext precedence applies:

  • LC_ALL
  • LC_MESSAGES
  • LANG

Examples:

LANG=ja_JP.UTF-8 threepack --help
LC_MESSAGES=de_DE.UTF-8 threepack id /path/to/rade.zip

You can still override the language explicitly:

threepack --lang ja --help

Supported catalogs:

  • en
  • de
  • fr
  • es
  • it
  • pt
  • zh_CN
  • ja
  • cy

Library Usage

Public header:

#include <teampandory/pandorydx/threepack/threepack.hpp>

Namespace:

teampandory::pandorydx::threepack

Basic example:

#include <filesystem>
#include <iostream>
#include <teampandory/pandorydx/threepack/threepack.hpp>

int main() {
    namespace fs = std::filesystem;
    using teampandory::pandorydx::threepack::ArchiveTool;

    ArchiveTool archiveTool;
    const auto entries = archiveTool.inspectZipPackage("/work/original/cx1.2jamma/rads.zip");

    for (const auto &entry : entries) {
        std::cout << std::hex << entry.crc << " " << std::dec << entry.len << '\n';
    }

    archiveTool.extractAssetFile("/work/original/cx1.2jamma/rads.zip", "/tmp/threepack-out");
    archiveTool.packAssetFile("/tmp/threepack-out/rads.zip", "/tmp/rads-repacked.zip");
}

Compile with pkg-config:

c++ -std=c++20 example.cpp $(pkg-config --cflags --libs threepack) -o example

For C callers, include the C ABI header:

#include <teampandory/pandorydx/threepack/threepack.h>

Meson consumer example:

project('example', 'cpp', default_options: ['cpp_std=c++20'])

threepack_dep = dependency('threepack')

executable(
  'example',
  'example.cpp',
  dependencies: [threepack_dep],
)

Using the build tree directly:

c++ -std=c++20 example.cpp \
  -I/work/threepack/include \
  -L/work/threepack/build -lthreepack \
  -Wl,-rpath,/work/threepack/build \
  -o example

C Projects

The public API is C++, not C. A plain C project will need a thin C++ wrapper layer or a dedicated C API.

Notes

  • unknown archive entries not present in the embedded names table are preserved and repacked using metadata sidecars
  • CRC 0x312ffb03 is blacklisted and repacked as an empty payload.
  • unchanged supported archives are expected to round-trip byte-for-byte