UTS Format (Sound Object Blueprint)
Description: The Sound Object (.uts) blueprint defines dynamic, positional, and ambient audio emitters placed throughout a game map. Ranging from environmental hums and randomized crowd chatter to highly localized looping sound effects, .uts files act as physical sound nodes combining strict spatial coordinates with randomized pitch, interval, and varying volume matrices.
At a Glance
| Property | Value |
|---|---|
| Extension(s) | .uts |
| Magic Signature | UTS / V3.2 |
| Type | Sound Object Blueprint |
| Rust Reference | View rakata_generics::Uts in Rustdocs |
Data Model Structure
Rakata maps a Sound Object into the rakata_generics::Uts struct. The struct’s Rustdocs document every field’s binary schema and GFF mapping; the table below is the high-level anatomy.
| Category | Covers | Representative fields |
|---|---|---|
| Audio Emitters | The .wav clips the engine sequences or shuffles through | Sounds |
| Spatial Geometry | The distance boundaries that decide where the sound is audible | MinDistance, MaxDistance |
| Playback Automation | How the sound loops and strings together | Continuous, Random, Active, Looping |
| Algorithmic Variation | Runtime distortion of pitch and volume | PitchVariation, FixedVariance, VolumeVrtn |
| Procedural Generators | Marks the sound as engine-generated ambiance such as crowd chatter or combat noise | GeneratedType |
rakata-lint validates these fields against the engine constraints documented below.
Engine Audits & Decompilation
(Decompilation logic for this section was entirely audited and verified via native Ghidra pipeline against swkotor.exe, explicitly pulling from CSWSSoundObject::Load at 0x005c9040.)
Sound Objects represent one of the most streamlined parsers in the engine. They completely lack script triggers and rely almost entirely on mathematically calculating randomized positional matrices and variations natively.
Structural Load Phasing
| Function | Size | Behavior |
|---|---|---|
Load | 1345 B | The primary physical parser evaluating 24 core audio metric bounds, defining spatial positioning, volume variation, pitch scales, and active looping capabilities. |
Sounds List | – | Iterates through the list of associated audio clips, actively loading sound resrefs into memory sequentially for playback. |
Core Structural Findings
| Engine Rule | Runtime Behavior |
|---|---|
| Generated Type Truncation | The engine reads GeneratedType as a massive 32-bit integer from the file, but forcefully truncates it and stores only the bottom single byte in memory. Setting this number astronomically high physically corrupts the expected generator type. |
| Constructor Defaults | If fields are missing from the .uts binary, the engine physically relies on its internal C++ constructor to populate default values, completely avoiding hardcoded literal checks during parse time. Nearly every playback-tuning scalar (Active, Positional, Looping, Volume, VolumeVrtn, Times, PitchVariation, Hours, GeneratedType, Interval, IntervalVrtn, MinDistance, MaxDistance, Continuous, Random, FixedVariance, RandomPosition, RandomRangeX, RandomRangeY) actually falls back to whatever value the object already holds, not a fixed literal – a freshly constructed sound object starts at Active=1, Positional=1, Looping=0, Volume=127, VolumeVrtn=0, Times=3, PitchVariation=0.0, Hours=0, GeneratedType=0, Interval=0, IntervalVrtn=0, MinDistance=10.0, MaxDistance=20.0, Continuous=0, Random=0, FixedVariance=1.0, RandomPosition=0, RandomRangeX=0.0, RandomRangeY=0.0. Tag follows the identical mechanism: the read’s own fallback argument is the object’s current tag, which starts as an empty string on a freshly constructed sound object, and the result is unconditionally re-applied through SetTag either way. |
| Position Defaults to the Origin | XPosition/YPosition/ZPosition are the one trio in Load that falls back to a fixed literal (0.0 each) instead of the constructor’s carried-over value, and the result is applied through SetPosition unconditionally regardless of source. This is the read that “Spatial Loading Context” below refers to: for a sound placed via the area’s .git layout, the .git instance’s own position values win in practice, so a .uts blueprint’s 0.0 fallback only surfaces for a sound opened outside that placement path. |
| Spatial Loading Context | When loaded globally via a static map (CSWSArea::LoadSounds), the engine skips reading positional coordinates from the .uts file entirely and strictly enforces the XPosition / YPosition / ZPosition coordinates defined in the area’s .git file. A sound instance carries no orientation; it uses Positional / RandomPosition flags plus RandomRangeX / RandomRangeY for placement. |
| Silent Sound Lists | When pulling the list of sounds, the engine actively ignores missing entries. It only pushes a sound struct into playable memory if the file actually provided a valid Sound reference string. |
| Return-Value Fragility | The loader’s return value doubles as the found-flag of whichever field it happened to read last: either the final Sound resref in the Sounds list, or the object’s ZPosition if the list was absent or empty. The area-level save loader deletes the sound object outright if that flag comes back false. A vanilla sound object always carries a ZPosition, so this never bites real saves, but a hand-authored .git sound entry lacking both a populated Sounds list and a ZPosition would be silently dropped on load. |
Legacy & Ignored Data
| Finding Type | Explanation |
|---|---|
| Legacy Engine Artifacts | Some older tools and legacy file revisions include values like TemplateResRef, LocName, Comment, Elevation, Priority, and PaletteID. These are artifacts from other Odyssey Engine branches (like Neverwinter Nights) and the KOTOR engine never evaluates them natively. |
Implemented Linter Rules (Rakata-Lint)
Phase 1 (intra-resource, no context)
Implemented under rakata_lint::rules::uts.
- UTS-001 (Volume Ceiling): Warns when
Volume > 127; values outside the engine’s byte threshold cause distortion or clipping. - UTS-002 (Audio Integrity): Warns when the
Soundslist contains blank entries; the engine skips them silently. - UTS-003 (Emitter Verification): Errors when the
Soundslist is empty; the object loads as a dead audio node. - UTS-004 (GeneratedType Truncation): Errors when
GeneratedType > 255; the engine truncates to a single byte and corrupts intended behavior. - UTS-005 (Legacy Engine Artifacts): Informs when
TemplateResRef,Elevation,Priority, orPaletteIDare populated; never natively evaluated by the K1 engine.
Phase 2 (resource existence, requires LintContext)
Implemented under rakata_lint::rules::uts_range.
- UTS-006 (Sound Resref Existence): Warns when any non-blank
Sounds[i].Sounddoes not resolve to a.wavresource in the configured sources. Blank entries are skipped (UTS-002 already covers them).