Skip to content
Vitaly Murashov edited this page Oct 3, 2026 · 3 revisions

Usage

SLTextureDecoder 0.x.y
Decodes Second Life / Firestorm JPEG 2000 texture caches into PNG.

Usage:
  SLTextureDecoder --cache-dir <dir> [options]

Options:
  --cache-dir <dir>   texture cache directory to read (required)
  --out-dir <dir>     where the PNG files go (default: <cache-dir>/png)
  --id <uuid>         decode one texture instead of the whole cache
  --index <n>         decode only the record at this index
  --limit <n>         stop after n textures
  --complete-only     skip records whose cached codestream is truncated
  --overwrite         write over existing PNG files
  -v, --verbose       log every record
  -h, --help          show this help
  -V, --version       show version and linked libraries

Output:
  <uuid>.png          decoded from a complete codestream
  <uuid>.partial.png  best effort result from a truncated codestream

Run the tool without any argument to print the same text (and exit with code 1, because the invocation is incomplete). --help exits with 0.

What gets converted

Invocation Selection
--cache-dir <dir> every record that has a body file (mBodySize > 0)
--cache-dir <dir> --complete-only only the records whose codestream is complete on disk
--limit <n> the first n of that selection, in cache index order
--index <n> exactly the record n, whatever --limit/--complete-only say
--id <uuid> the record with that texture id (looked up in texture.entries)
  • --index wins over --id when both are given.
  • The next run skips a texture whose PNG is already in the output directory (that is what makes resuming an interrupted run cheap); --overwrite rewrites it. The name that is checked is the one the record would produce, i.e. <uuid>.partial.png for a truncated record, <uuid>.png otherwise.
  • A record without a body file (mBodySize <= 0, a free slot) is counted as failed and reported - it cannot be decoded by definition.
  • The output directory is created on demand, including <cacheDir>/png when --out-dir is omitted.

Output naming

<uuid>.png           the whole codestream was on disk and decoded losslessly
<uuid>.partial.png   only the cached prefix existed; OpenJPEG recovered a blurry
                     but usable image (best effort, not the original texture)

Two files for the same texture can never appear at once, so the suffix is a reliable "this output is not the real thing" marker for scripts.

Exit codes

The numeric exit code is the error category, so a script can react without parsing the log (src/utils/Errors.h):

Code Name Typical cause
0 Ok every selected texture was written (best effort results count as written)
1 Usage bad command line, missing --cache-dir, unknown option
2 CacheNotFound --cache-dir is not a directory, or texture.entries / texture.cache is missing
3 EntryNotFound --id is not in the cache, --index out of range, body file missing
4 CacheTooSmall header block or texture.cache shorter than the record announces
5 SizeMismatch body file size differs from mBodySize
6 DecodeError at least one selected texture could not be decoded even leniently, or a codestream is inconsistent
7 WriteError output directory/file could not be created or written
8 IoError read failed, short read, file vanished
9 InvalidFormat texture.entries is not a plausible cache (too small, truncated, absurd count)

Exit code 0 with a non-zero written count is the normal case for a cache full of truncated records: the tool reports them as best effort instead of failing.

Logging

Plain lines on stdout/stderr, no colour codes, so redirecting works:

[INFO] cache D:\FS Cache\texturecache: 196700 records, encoder KDU v8.4.1
[INFO] 120 texture(s) to decode into D:\textures
[INFO] 250/54825 written
[WARN] record 123386: texture has 5 components, dropping the surplus plane
[INFO] done: 120 PNG written (83 best effort), 0 skipped, 0 failed
Level When
INFO cache opened, selection, every 250 written PNGs, the final summary
WARN a texture decoded with a caveat (5 components, lenient recovery)
ERROR a record failed; the run continues
verbose (-v) one line per record: index, uuid, file name, size, components, OpenJPEG diagnostics

Examples

# the whole cache, default output <cache-dir>\png, skip what exists
SLTextureDecoder --cache-dir "$env:APPDATA\Firestorm_x64\texturecache"

# a quick smoke test: five records, each one logged
SLTextureDecoder --cache-dir "D:\FS Cache\texturecache" --out-dir D:\textures --limit 5 -v

# only losslessly recoverable textures, into a separate tree
SLTextureDecoder --cache-dir "D:\FS Cache\texturecache" --out-dir D:\textures\complete --complete-only

# one texture you know the id of
SLTextureDecoder --cache-dir "D:\FS Cache\texturecache" --id 8f7d1b1e-0a1b-4c5d-8e9f-0123456789ab

# record 123386 by index
SLTextureDecoder --cache-dir "D:\FS Cache\texturecache" --out-dir D:\one --index 123386 -v

# scripted, quiet: only failures matter
SLTextureDecoder --cache-dir "D:\FS Cache\texturecache" 2> errors.log
if ($LASTEXITCODE -ne 0) { Write-Host "see errors.log" }

Options of the GUI binary

SLTextureDecoderGUI.exe accepts the same options plus:

--jobs <n>   worker threads (0 = one per logical CPU)
--batch      convert without opening a window
# the whole cache on four threads, unattended, log to a file
SLTextureDecoderGUI.exe --batch --jobs 4 --cache-dir "D:\FS Cache\texturecache" `
    --out-dir D:\textures > run.log

See GUI for the window and Performance for what --jobs buys you.

Clone this wiki locally