This document describes the unified pak architecture for LessUI, enabling self-contained, cross-platform paks that include source code, scripts, resources, and platform-specific assets in a single location.
- Duplication: Tool paks like
Clock.pakhadlaunch.shduplicated 11 times across platforms - Scattered code: Native source lived in separate directories while pak scaffolding lived elsewhere
- No single source of truth: Updates required touching many files
- Inconsistencies: Minor differences crept in (trailing newlines, debug comments)
- EXTRAS complexity: Separate release package created confusion for users
A unified directory structure where each pak is fully self-contained in workspace/all/paks/:
- Metadata (
pak.json) - Source code (if native)
- Shell scripts (cross-platform with platform branching)
- Resources and platform-specific assets
Paks can reference shared code in workspace/all/common/ since they're in the same repository.
workspace/all/paks/
├── Makefile # Master makefile - discovers and builds all paks
├── Clock/
│ ├── pak.json # Metadata (name, platforms, build type)
│ ├── launch.sh # Entry point (cross-platform)
│ ├── src/ # Native C code
│ │ ├── clock.c
│ │ └── Makefile # Build configuration
│ └── build/ # Generated output (per-platform)
│ └── miyoomini/
│ └── clock.elf
│
├── WiFi/ # WiFi configuration pak
│ ├── pak.json
│ ├── launch.sh
│ ├── bin/
│ │ ├── service-on # Cross-platform shell scripts
│ │ └── miyoomini/iw # Platform-specific binary
│ ├── lib/
│ │ └── miyoomini/ # Platform-specific libraries
│ └── res/
│ └── wpa_supplicant.conf.tmpl
│
├── Input/ # Future: migrate from workspace/all/minput
│ ├── pak.json
│ ├── launch.sh
│ └── src/
│ ├── minput.c
│ └── Makefile
│
└── Files/ # Future: file manager pak
├── pak.json
├── launch.sh # Platform branching for different binaries
└── bin/
├── miyoomini/DinguxCommander
└── magicmini/351Files
# Note: Emulator paks are in workspace/all/paks/Emus/
# They use a template system due to high uniformity across cores
{
"name": "WiFi",
"type": "tool",
"description": "Manage WiFi settings",
"version": "1.0.0",
"platforms": ["miyoomini", "my282", "my355", "rg35xxplus", "tg5040"],
"build": {
"type": "native",
"source": "src/",
"output": "{{PLATFORM}}/clock.elf",
"makefile": "src/makefile"
},
"dependencies": {
"jq": {
"version": "1.7.1",
"arch_binary": true,
"url_template": "https://github.com/jqlang/jq/releases/download/jq-{{VERSION}}/jq-linux-{{ARCH}}"
},
"launcher-presenter": {
"version": "0.10.0",
"platform_binary": true,
"url_template": "https://github.com/josegonzalez/launcher-presenter/releases/download/{{VERSION}}/launcher-presenter-{{PLATFORM}}"
}
},
"launch": "launch.sh"
}| Field | Required | Description |
|---|---|---|
name |
Yes | Display name of the pak |
type |
Yes | One of: tool, emulator, app |
description |
No | Brief description |
version |
No | Semantic version |
debug |
No | If true, pak is only included in DEBUG=1 builds |
platforms |
Yes | Array of supported platform IDs |
build |
No | Native code build configuration |
build.type |
- | native (compile C), download (fetch binaries), none |
build.source |
- | Path to source directory |
build.output |
- | Output path pattern (supports {{PLATFORM}}) |
build.makefile |
- | Path to makefile |
dependencies |
No | External binaries to download |
dependencies.<name>.version |
- | Version to download |
dependencies.<name>.arch_binary |
- | true if binary varies by arch (arm/arm64) |
dependencies.<name>.platform_binary |
- | true if binary varies by platform |
dependencies.<name>.url_template |
- | Download URL with placeholders |
launch |
Yes | Entry point script |
Simplest pak type - just shell scripts, no compilation needed.
Bootlogo.pak/
├── pak.json
├── launch.sh
└── res/
└── template.bmp
pak.json:
{
"name": "Bootlogo",
"type": "tool",
"platforms": ["miyoomini", "trimuismart", "rg35xxplus", "tg5040"],
"launch": "launch.sh"
}Pak includes C source code that's cross-compiled for each platform.
Clock.pak/
├── pak.json
├── launch.sh
└── src/
├── clock.c
└── makefile
pak.json:
{
"name": "Clock",
"type": "tool",
"platforms": ["all"],
"build": {
"type": "native",
"source": "src/",
"makefile": "src/makefile"
},
"launch": "launch.sh"
}launch.sh:
#!/bin/sh
cd "$(dirname "$0")"
./clock.elfExternal binaries fetched during build (like sftpgo, jq).
FTP.Server.pak/
├── pak.json
├── launch.sh
├── bin/
│ └── .gitkeep
└── settings.json
pak.json:
{
"name": "FTP Server",
"type": "tool",
"platforms": ["miyoomini", "my282", "my355", "rg35xxplus", "tg5040"],
"dependencies": {
"sftpgo": {
"version": "v2.6.6",
"arch_binary": true,
"url_template": "https://github.com/drakkan/sftpgo/releases/download/{{VERSION}}/sftpgo_{{VERSION}}_linux_{{ARCH}}.tar.xz"
},
"jq": {
"version": "1.7.1",
"arch_binary": true,
"url_template": "https://github.com/jqlang/jq/releases/download/jq-{{VERSION}}/jq-linux-{{ARCH}}"
}
},
"launch": "launch.sh"
}Pre-built binaries that differ per-platform (like Files.pak with different file managers).
Files.pak/
├── pak.json
├── launch.sh
└── bin/
├── miyoomini/DinguxCommander
├── magicmini/351Files
└── rg35xxplus/ # Empty - uses system binary
launch.sh:
#!/bin/sh
cd "$(dirname "$0")"
case "$PLATFORM" in
rg35xxplus)
# Use system file manager
DIR="/mnt/vendor/bin/fileM"
if [ -d "$DIR" ]; then
cd "$DIR"
HOME="$SDCARD_PATH"
./dinguxCommand_en.dge
else
show.elf "$PAK_DIR/res/missing.png" 4
fi
;;
magicmini)
HOME="$SDCARD_PATH"
./bin/$PLATFORM/351Files
;;
*)
HOME="$SDCARD_PATH"
./bin/$PLATFORM/DinguxCommander
;;
esacEmulators use templates with variable substitution.
GB.pak/
├── pak.json
├── launch.sh.template
└── configs/
├── default.cfg
└── tg5040/
└── default-brick.cfg
pak.json:
{
"name": "GB",
"type": "emulator",
"platforms": ["all"],
"core": {
"emu_exe": "gambatte",
"arm64_only": false
},
"launch": "launch.sh.template"
}launch.sh.template:
#!/bin/sh
EMU_EXE={{EMU_EXE}}
EMU_TAG=$(basename "$(dirname "$0")" .pak)
ROM="$1"
mkdir -p "$BIOS_PATH/$EMU_TAG"
mkdir -p "$SAVES_PATH/$EMU_TAG"
HOME="$USERDATA_PATH"
cd "$HOME"
{{NICE_PREFIX}}player.elf "$CORES_PATH/${EMU_EXE}_libretro.so" "$ROM"Scripts can use environment variables set by Launcher:
$PLATFORM- Platform ID (e.g., "miyoomini", "rg35xxplus")$DEVICE- Device variant (e.g., "miyoominiplus", "brick")$SDCARD_PATH- SD card mount point$USERDATA_PATH- User data directory
#!/bin/sh
case "$PLATFORM" in
miyoomini|my282|my355)
# Miyoo family handling
;;
rg35xxplus)
# Anbernic handling
;;
tg5040)
# TrimUI handling
;;
esacUse directory structure for platform-specific assets:
bin/
├── service-on # Cross-platform script
├── arm/jq # Architecture-specific binary
├── arm64/jq
├── miyoomini/iw # Platform-specific binary
└── rg35xxplus/
Loading pattern:
export PATH="$PAK_DIR/bin/$ARCH:$PAK_DIR/bin/$PLATFORM:$PAK_DIR/bin:$PATH"
export LD_LIBRARY_PATH="$PAK_DIR/lib/$PLATFORM:$PAK_DIR/lib:$LD_LIBRARY_PATH"For config files, use base + override pattern:
res/
├── wpa_supplicant.conf.tmpl # Base template
├── wpa_supplicant.conf.miyoomini.tmpl # Platform override
└── wpa_supplicant.conf.my355.tmpl # Platform override
Selection logic:
template="$PAK_DIR/res/wpa_supplicant.conf.tmpl"
if [ -f "$PAK_DIR/res/wpa_supplicant.conf.$PLATFORM.tmpl" ]; then
template="$PAK_DIR/res/wpa_supplicant.conf.$PLATFORM.tmpl"
fimake setup
└── scripts/generate-paks.sh
└── Generates emulator paks only (GB.pak, GBA.pak, etc.)
└── Tool paks are NOT generated here
make build PLATFORM=miyoomini
└── workspace/Makefile calls workspace/all/paks/makefile
└── For each pak with src/makefile:
└── Cross-compile in Docker
└── Output: workspace/all/paks/<pak>/build/<platform>/<binary>.elf
make system PLATFORM=miyoomini
└── For each pak in workspace/all/paks/:
├── Check pak.json for platform compatibility
├── Create build/SYSTEM/<platform>/paks/<pak>.pak/
└── Copy launch.sh, pak.json, res/, bin/, lib/, AND compiled binary
Tool paks are constructed entirely during make system, not make setup. This means:
- No duplicate copy steps (everything happens once)
- Binary and assets are assembled together
- Platform filtering happens at construction time
workspace/all/paks/makefile- Master makefile that discovers and builds all paks with native codeMakefile(system target) - Constructs complete tool paks in build directoryscripts/generate-paks.sh- Only handles emulator paks (not tool paks)
All high and medium priority tool paks have been successfully migrated:
| Pak | Type | Old Location | New Location | Highlights |
|---|---|---|---|---|
| Clock | Native | workspace/all/clock/ |
workspace/all/paks/Clock/src/ |
First migration, 11 duplicates eliminated |
| Input | Native | workspace/all/minput/ |
workspace/all/paks/Input/src/ |
Second native pak, validates pattern |
| Bootlogo | Hybrid | (was duplicated ×7) | workspace/all/paks/Bootlogo/ |
Native for miyoomini, shell for others; platform-specific resources; launcher-presenter integration |
| Files | Platform-Specific Binaries | (was duplicated ×10) | workspace/all/paks/Files/ |
Multiple file managers (DinguxCommander, 351Files); bin/<platform>/ pattern |
For future pak migrations:
- Examine: Check all skeleton copies to understand platform differences
- Create structure:
mkdir -p workspace/all/paks/<Name>/src(if native code) - Move files: Source code, resources, binaries to unified location
- Create pak.json: Define name, platforms, build configuration
- Create launch.sh: Single cross-platform launcher (use
case "$PLATFORM"for branching) - Update makefiles: Remove old build/copy rules from workspace/Makefile and Makefile.copy files
- Test:
make clean && make setup && make build PLATFORM=<platform> && make system PLATFORM=<platform>
The emulator pak templates in workspace/all/paks/Emus/ remain largely unchanged because:
- High uniformity - All emulator paks have identical structure
- Template efficiency - One template generates ~200+ paks
- Core-based - Variation is in libretro core, not pak structure
However, emulator pak metadata could move to individual pak directories:
paks/
├── GB.pak/
│ ├── pak.json # Contains core info
│ ├── launch.sh.template
│ └── configs/
└── GBA.pak/
├── pak.json
├── launch.sh.template
└── configs/
This is optional and can be evaluated after tool paks are migrated.
# Create pak directory
mkdir -p paks/MyTool.pak
# Create pak.json
cat > paks/MyTool.pak/pak.json << 'EOF'
{
"name": "MyTool",
"type": "tool",
"platforms": ["miyoomini", "rg35xxplus", "tg5040"],
"launch": "launch.sh"
}
EOF
# Create launch.sh
cat > paks/MyTool.pak/launch.sh << 'EOF'
#!/bin/sh
cd "$(dirname "$0")"
echo "Hello from $PLATFORM!"
EOF
chmod +x paks/MyTool.pak/launch.sh# Create pak directory
mkdir -p paks/MyApp.pak/src
# Create pak.json
cat > paks/MyApp.pak/pak.json << 'EOF'
{
"name": "MyApp",
"type": "tool",
"platforms": ["all"],
"build": {
"type": "native",
"source": "src/",
"makefile": "src/makefile"
},
"launch": "launch.sh"
}
EOF
# Create launch.sh
cat > paks/MyApp.pak/launch.sh << 'EOF'
#!/bin/sh
cd "$(dirname "$0")"
./myapp.elf
EOF
# Copy source and makefile (based on existing patterns)
cp workspace/all/clock/clock.c paks/MyApp.pak/src/myapp.c
cp workspace/all/clock/makefile paks/MyApp.pak/src/makefile
# Edit makefile to change TARGET = myapp- Emulator integration - Should emulator paks fully migrate to this structure?
- Dependency caching - Should downloaded binaries be cached across builds?
- Version pinning - How to handle pak versioning for updates?
- External paks - How to support third-party paks (like Jose's)?
- Jose Gonzalez's Launcher paks: https://github.com/josegonzalez?tab=repositories&q=launcher
- Emulator pak templates:
workspace/all/paks/Emus/ - Pak generation script:
scripts/generate-paks.sh