▲ 86 r/LGR

Found at the MIT Flea Market

Good day at the MIT Flea Market / Swapfest today.

Gateway Colorbook CB4DX240 (486 laptop) with charger, Sound card / modem with Analog Devices AD1845JP, EVGA 500 W PSU, 2x8 Corsair Vengeance DDR3 2400, Audigy 2 ZS (Dell), GeForce FX 5700 LE, 2x240GB Corsair MX500 SSDs, Zalman CNPS 9700 LED, Asus MIO-Audio 888, Lite-On ATA DVD-R drive, Soldier of Fortune Gold Edition, Where in the World is Carmen Sandiego (C64 box).

u/majestic_ubertrout — 3 days ago
▲ 8 r/civ

Bit of a Ratity: Civ 3 Conquests Bonus/Promo CD

Found this and it looked like it hadn't been uploaded before, so I took the initiative. Doesn't look like there's that much not otherwise available, but still cool to share: https://archive.org/details/c-3-c-presell

*that should be "rarity" in the title. Darn it.

u/majestic_ubertrout — 5 days ago

Try to restore or admit defeat?

Got this 2005 G5 for cheap with another one in much better shape. Seller warned me it looked like it had water damage. He wasn't kidding. Is this a lost cause?

Fwiw it would be very cool to have a late air cooled G5, so it would be nice to get working.

Update: The other G5 I purchased works great so probably going to try to give this to someone locally or keep for parts.

u/majestic_ubertrout — 7 days ago

How does the 980 Ti Work in Linux?

I have a system that I have connected to a CRT monitor which is triple booting Windows XP (overkill), 7 (period correct) and 11 (for modern games on a CRT). It's running a overclocked i5-4690k, 16 GB of fast DDR3, and. MSI GTX 980 Ti Golden Gamer. The 980 Ti is mostly non-negotiable - it's the most powerful card with XP drivers and I like the DVI-I port for the CRT. The closest AMD alternative, the Radeon 7970/280x, is a great card, but the 980 Ti leaves it in the dust - and the 6 gb variant is seriously rare and overpriced anyway. Not relevant for XP games but even at CRT resolutions useful for modern ones. I also have a X-Fi Titanium in the box - forgot that until I started writing this.

Win11 technically isn't supported on my system (even though it runs fine) and Microsoft seems determined to make it worse. I don't use this system for anything but gaming and I don't play anything multiplayer. But is the video (and sound card) an issue? Does it vary by distro?

reddit.com
u/majestic_ubertrout — 15 days ago
▲ 40 r/LGR

The Last Big Box?

Expansion for scale. So weird to see Games for Windows branding on a big box.

Probably should explain a bit what a big box is - it's a box that's substantially bigger than a DVD box (usually 9x7 or so and rectangular where the vertical side is taller). Collector's editions are a different thing. The LGR vid clarifies the subject - and shows why having one from 2006 is so cool: https://www.youtube.com/watch?v=QhZkmAscdw0

u/majestic_ubertrout — 17 days ago

Rebel Assault - All Cutscenes Upscaled

A few weeks ago I posted the "Hidden Movie" upscaled from the Rebel Assault 2 cutscenes: https://www.youtube.com/watch?v=X1G6OsomYoI

I meant to post the raw upscales of the cutscenes (up to 1280x800 using Topaz Starlight Precise 2.5 but...I only just got around to it. Here they are. These include many short cutscenes I didn't include/use, mainly the death/retry cutscenes. I used the same filenames if someone else wants to take a shot at modding the game to include them.

Kind of amusingly, the thumbnail it chose here is actually something I didn't quite use for the YouTube edit. Starlight Precise gave the dude from the Corellia Star facial hair instead of properly making the cutscene fuzzy, so I used a simpler upscaling algorithm (Gaia CG) for the shots of him and cut it into the video. But this way all the upscales are out there and people can use them as they see fit.

Also, the YouTube version is frame interpolated to 30 FPS using Topaz Apollo, these are at the original 15.

I also included the documentation of what I was able to get on the audio. It turns out the video is relatively simple (maybe because FFMPEG already did the work) but the audio is mixed live and incredibly complicated. I used Claude to decompile the actual executable to learn what different flags meant for audio. Even then it's not perfect, although it's close.

archive.org
u/majestic_ubertrout — 22 days ago

Another Floppy - Savegame From The Dig

Another one I found in my collection. To be honest I'm less positive where it's from but it looks official.

u/majestic_ubertrout — 24 days ago

Freeing Up Memory on Surface Laptop using Smokeless UMAF

Anyone tried this? A family member uses a Surface Laptop 3/4 with the Ryzen chip and of its 8 GB of soldered memory 2 GB is allocated to VRAM. They don't play any games or run anything demanding on it, but W11 is getting poky on 6 GB of memory. I'd really like to change the VRAM allocation to 512 MB but it's not a BIOS option.

There's a tool called Smokeless UMAF to access hidden BIOS features but I'm a little wary of using it. People have reported success with other Ryzen machines but anyone try it with the Surface?

reddit.com
u/majestic_ubertrout — 25 days ago

Found in my files

So useful back in the day. I'm sure some did, but I can't imagine completing the game without the strategy guide.

u/majestic_ubertrout — 25 days ago

What we used before YouTube Existed

I mean, we really mostly used printed guides and walkthroughs (plus UHS and the like). But still fun to have in my collection.

u/majestic_ubertrout — 1 month ago

I got The Sims 2!

Now, the regular edition of this is about as common as games get. The DVD edition is a little rarer but not much. The big box on the other hand...

u/majestic_ubertrout — 1 month ago
▲ 4 r/ffmpeg

Rebel Assault 2 .SAN Audio

So, I know last year it was in the news and somewhat controversial that FFMPEG added a bug fix for the .san ("SMUSH") video format used in Star Wars Rebel Assault 2 in 1995. However, it still can't play the audio reliably afaik.

As part of a project to upscale that video I've done a bit of a deep dive on how it handles audio, and I've included it below. I apologize if this is way too much information, an unwelcome distraction from what are admittedly much more important formats/codecs/concerns, and more. But since FFMPEG intentionally supports the format...

(this was also drafted in Claude - another strike against this post I suppose)

Rebel Assault II .SAN Audio Format Specification

Format: PSAD audio in LucasArts SMUSH/INSANE ANIM containers Title: Star Wars: Rebel Assault II — The Hidden Empire (LucasArts, 1995, DOS) Sources: Retail RA2.EXE (Watcom C/C++ 32-bit, LE/DOS-4GW) disassembly; retail .SAN data files Status of claims: Statements in this document are established from the engine disassembly and/or validated against retail data unless explicitly marked (inferred) or (unresolved).

1. Overview

Rebel Assault II cutscenes are stored in .SAN files using the SMUSH container (top-level tag ANIM). Audio is carried in PSAD chunks interleaved with video inside per-frame FRME chunks: one PSAD chunk per active sound per video frame.

Audio characteristics:

  • Codec: unsigned 8-bit linear PCM, mono, silence value 128
  • Sample rate: per-sound, declared in each sound's STRK header; observed values 5512, 7000, 7500, 11025, 22050 Hz
  • Multiple sounds play simultaneously and are mixed by the engine at runtime
  • Per-chunk volume (0–128, linear) and pan (signed byte) automation
  • Per-sound STRK bytecode controlling stream playback, including looping
  • Sound-class flags distinguish effects, music, and speech

This ANIM-era audio scheme predates and is unrelated to Digital iMUSE (DiMUSE), the audio engine of later SMUSH titles (Full Throttle, The Dig, Curse of Monkey Island). DiMUSE documentation — including its pseudo-logarithmic volume scaling — does not apply to this format.

2. Container structure

"ANIM" size:u32be
└─ "AHDR" size:u32be
     version      u16le        (2 in retail RA2 files)
     frameCount   u16le
     unknown      u16le
     palette      768 bytes    (256 × RGB)
     ── if version >= 2 ──
     frameRate         u32le   (15 in retail files)
     unknown           u32le
     defaultAudioRate  u32le   (11025 in retail files)
     (remainder of AHDR: zero padding)
└─ "FRME" size:u32be    × frameCount
     └─ sub-chunks: FOBJ, XPAL, NPAL, IACT, TRES, PSAD, ...

General rules:

  • All chunk sizes are big-endian u32; a chunk occupies 8 + size bytes, padded to an even boundary (pad byte not included in size).
  • Multi-byte scalars inside AHDR are little-endian. Multi-byte scalars inside SAUD/STRK/SDAT are big-endian.
  • defaultAudioRate is a fallback only. The authoritative sample rate of each sound is in its STRK header (§5).

The video frame rate (frameRate, 15 fps) defines the audio delivery timeline: each FRME represents 1/15 s, and a sound beginning in frame N starts at N/15 seconds.

3. PSAD chunk

3.1 Header (Rebel Assault II variant)

10 bytes, little-endian:

Offset Type Name Description
0 u16le trackID Mixer channel slot (see §3.3)
2 u16le index Sequence number within a sound; 0 = first chunk
4 u16le maxFrames Number of frames the sound spans
6 u16le flags Sound class in bits 6–7 (see §7)
8 u8 vol Volume, 0–128 (see §6)
9 u8 pan Pan position, signed, with sentinel (see §8)

The chunk tags PSAD, PSD2, and PVOC are all dispatched to the same handler by the engine and share this layout.

3.2 Older header variant

Earlier SMUSH titles use a 12-byte big-endian header instead:

Offset Type Name
0 u32be trackID
4 u32be index
8 u32be maxFrames

Detection: in the old variant, payload bytes 4–7 of the first chunk of any sound are always zero (a 32-bit index of 0). In the new variant, byte 4–5 hold maxFrames (nonzero) for a first chunk. Rebel Assault II files use the 10-byte variant exclusively.

3.3 Track ID semantics

trackID identifies a reusable mixer channel slot, not a sound. The same track ID is assigned to many unrelated sounds over the course of one movie. (Example from retail data: in O_OPEN_D.SAN, trackID 1 carries ten distinct sounds, beginning at frames 201, 471, 570, 619, 685, 783, 825, 848, 864, 911.)

Sound boundaries are defined as follows:

  • A chunk with index == 0 whose payload begins with a SAUD header starts a new sound on that track, unconditionally.
  • A chunk with index > 0 continues the sound currently open on that track.
  • Chunk indices within one sound are consecutive (0, 1, 2, …), one per frame.

3.4 Payload

First chunk of a sound (index == 0):

"SAUD" size:u32be
├─ "STRK" size:u32be   — parameter block (§4)
└─ "SDAT" size:u32be   — PCM data begins; this chunk carries the first portion

The SDAT size field declares the total PCM byte length of the sound. Continuation chunks (index > 0) contain raw PCM only, with no sub-chunk headers; concatenated in index order they form the SDAT stream. The concatenation may exceed the declared SDAT size by a few bytes and must be truncated to it.

4. STRK chunk — a bytecode program

STRK is not a table of records. It is a small bytecode program that the mixer interprets while refilling the channel from SDAT. Observed payload sizes in retail data: 14, 20, 26, 32, 38, 176, 356, 368 bytes.

Instruction encoding:

opcode:u8  length:u8  payload:length bytes

The interpreter reads the opcode, subtracts 1, rejects values above 10, and dispatches through a jump table at virtual address 0x29F80. On a normal (non-jump) instruction it advances the program counter by length + 2. Any opcode outside 1–11 — including the 00 00 terminator — stops the channel.

4.1 Instruction set

Opcode Length Handler Operation
1 12 0x2A706 Set stream: offset:u32be, size:u32be. Rate is set to the global mixer rate (the third u32 is ignored)
2 4 0x2ABC8 Jump if selector ≠ 0; unconditional when the selector byte is 0
3 2 0x2AC4C Set: selector = value
4 2 0x2AD02 Add: selector += value
5 0 0x2AD6F Stop channel (state := 0)
6 12 0x2A82B Set stream: offset:u32be, size:u32be, sampleRate:u32be
7 18 0x2A986 Loop with counter (see §4.4)
8 4 0x2ABD6 Jump if selector > operand
9 4 0x2ABDE Jump if selector < operand
10 4 0x2ABE6 Jump if selector = operand
11 4 0x2ABEE Jump if selector ≠ operand
0, >11 0x2AD6F Stop channel

4.2 Operand encodings

Jump instructions (2, 8, 9, 10, 11), payload 4 bytes:

Byte Meaning
0–1 offset:s16be — added to the address of the jump instruction itself
2 selector (source of the compared value)
3 operand (compared against)

Set/add instructions (3, 4), payload 2 bytes:

Byte Meaning
0 selector (destination)
1 value

4.3 Selectors

A selector byte names either a mixer register or a general-purpose variable:

Selector Refers to
0xFF master volume (global 0x2E382)
0xFE channel volume (chan+0x04)
0xFD channel pan (chan+0x05)
any other index into a 256-byte variable table at 0x6A61C

The variable table is what makes the conditional jumps meaningful: the game engine can set these bytes, and a sound's STRK program can branch on them. This is an interactive/branching audio system, not a static descriptor block. Extraction tools that have no game state should treat the table as zero.

4.4 Opcode 7 (stream with repeat count)

Opcode 7 (payload 18 bytes) is a stream descriptor carrying a repeat count:

Bytes Meaning
0–3 offset:u32be
4–7 size:u32be
8–11 sampleRate:u32be
12–15 repeat count:u32be
16–17 (unresolved; 0x0014 or 0x0000 in retail data)

It maintains two extra channel fields, a base (mix+0x24) and a working counter (mix+0x28), to iterate the repeats. In retail data opcode 7 is always paired with a following conditional jump on a game variable (see §4.6), so in practice its repeats are also game-terminated.

4.5 The common forms

A simple, non-looping sound is a single opcode-6 instruction (payload 14 bytes total):

06 0C  00 00 00 00  00 00 0E A0  00 00 2B 11
op len   offset=0     size=3744    rate=11025

A looping sound appends an unconditional backward jump. Example: the music track of 01P01.SAN (STRK payload, 20 bytes):

06 0C  00000000  000BE0CC  00002B11      ; stream: offset 0, 778444 bytes, 11025 Hz
02 04  FF F2     00        00            ; jump -14, selector 0 → unconditional

The jump instruction begins at payload offset 14; adding −14 returns the program counter to offset 0, re-executing the stream descriptor and restarting playback from the beginning of SDAT. There is no terminator. The music loops forever.

This is the mechanism by which background music repeats for the duration of a level. A decoder that treats STRK as a flat list of descriptors will play such a cue exactly once and then fall silent for the remainder of the file.

Note also that a program may contain several stream descriptors in sequence, tiling SDAT contiguously (verified: each descriptor's offset + size equals the next descriptor's offset), interleaved with opcode-3 volume changes. Such a program is straight-line and must be allowed to run to its end; only a backward jump indicates a loop.

4.6 Two kinds of loop: free-running vs. game-terminated

Not every backward jump means "loop forever for playback purposes." There are two distinct idioms, and conflating them produces audible artefacts.

Free-running loop — the backward jump is unconditional (opcode 2 with a zero selector), as in the 01P01.SAN music above. Nothing in the program or the game can stop it from within the audio system; it simply repeats until the file ends. This is real, sustained looping and should be reproduced.

Game-terminated loop — the program guards the loop with a conditional jump that tests a general-purpose variable it sets itself. Example, an effect in 15END.SAN (track 4):

op3   var[0x64] = 0x11              ; claim a variable
op6   stream: 559 bytes @ 7500 Hz   ; ~75 ms
op11  jump past the loop if var[0x64] != 0x11
op2   jump back to the stream (unconditional)
op5   stop

As bytecode this loops forever, because var[0x64] is only ever changed by the game engine at runtime (when the event that triggered the sound ends). Under in-game conditions it is a sound that plays until told to stop. An offline extractor has no such signal: if it honours the backward jump it will repeat a 75 ms fragment for the entire remaining clip — an unintended machine-gun strobe.

The distinguishing test is purely structural and needs no game state: a loop is game-terminated if its control flow contains a conditional jump (opcodes 8–11, or opcode 2 with a non-zero selector) whose selector is a general-purpose variable (≤ 0xFC) rather than a mixer register (0xFD–0xFF). Such sounds should be played once (or for the count in an opcode-7 descriptor), not sustained. Only a loop with no game-variable guard — an unconditional backward jump — is reproduced as a true repeat.

Opcode-7 repeat counts are treated the same way: honoured when the program is free-running, collapsed to a single play when it is game-terminated.

5. Sample rate

The sample rate of each sound comes from its STRK stream descriptor: opcode 6 carries an explicit sampleRate, while opcode 1 implicitly uses the global mixer rate. It varies per sound within a single file.

Distribution across six retail files (239 sounds):

Rate (Hz) Count
5512 1
7000 2
7500 1
11025 193
22050 12

Speech is not confined to the default rate: in O_OPEN_D.SAN all ten speech sounds are 22050 Hz while AHDR.defaultAudioRate is 11025.

The engine resamples each channel from its own rate to the global mixer rate. Mixer code (virtual address 0x2A2F6; see §10 for addressing):

asm

movsx edx, word ptr [esp + 0x40]      ; output samples requested
imul  edx, dword ptr [ebx + 0x6A336]  ; × channel sample rate
mov   ecx, dword ptr [0x2E390]        ; ÷ global mixer rate
idiv  ecx                             ; = source samples to consume

The interpolation kernel used by the low-level resampler has not been characterised (unresolved).

6. Volume

6.1 Scaling law

Volume is applied in two multiplicative stages:

Stage 1 — SMUSH frame handler (0x292C1), on each PSAD chunk, with a group volume of 127:

asm

mov  dl, byte ptr [eax + 0x10]   ; chunk vol byte, 0..128
imul edx, ebx                    ; × group volume (127)
sar  edx, 7                      ; ÷ 128  →  channelVolume ∈ 0..127

Stage 2 — mixer (0x2A0D1), per mixing block:

asm

movsx edx, word ptr [0x2E382]        ; master volume
movsx eax, byte ptr [ebx + 0x6A730]  ; channelVolume
imul  edx, eax
mov   ecx, 0x7F
idiv  ecx                            ; ÷ 127

Net gain: (vol × 127/128) × master/127 = vol/128 × master/128 × 128/127 × 127/128, i.e. with master at maximum the amplitude multiplier is exactly:

>

Notes:

  • There is no logarithmic or perceptual volume mapping in this engine.
  • The stage-2 divisor of 127 is cancelled by the stage-1 factor of 127; the net normalisation is 128. Implementations should divide by 128 once, not by 127.

6.2 Temporal behaviour

  • The authored vol byte can change on every chunk, i.e. every video frame (~66.7 ms at 15 fps). Retail data contains authored fades expressed this way (e.g. successive chunk volumes 15, 18, 20, … 118, 128).
  • The mixer processes each channel in blocks of at most 256 source samples (~23 ms at 11025 Hz) and re-evaluates the channel gain scalar once per block (block-size test at 0x2A327).
  • An additional per-block gain term at 0x2A342 involves a per-tick counter (initialised to 0xC00 = 3072 samples) and divisor 0x5F400 (= 0xC00 × 127). The expression reduces to master × channelVolume / 127 at tick start; the counter's exact role in gain evolution within a tick is (unresolved).

Implementation guidance derived from the above: gain changes should be applied with a transition on the order of the mixer's block granularity (~20 ms). An instantaneous step per chunk produces zipper noise the engine does not exhibit; a ramp spanning the full 66 ms chunk smears transient onsets and prevents short single-chunk sounds from reaching full level (inferred).

6.3 Mixing model

The engine performs no automatic ducking and no dynamic rebalancing of any kind. All level relationships between simultaneous sounds (speech over music, etc.) are authored directly into the per-chunk volume bytes.

7. Sound classes

Bits 6–7 of the PSAD flags field classify the sound. The mixer masks with 0xC0 (0x2A0EB; also used in voice-allocation priority at 0x29A65):

flags & 0xC0 Class Runtime treatment
0x00 Sound effect Base gain only
0x40 Music Gain additionally scaled by the user's music-volume setting: gain × musicVol / 256
0x80 Speech Base gain only

The music scaling factor (global 0x2E398) is a user preference applied at runtime; it is not part of the file data. Extraction at unity music volume reproduces the authored mix.

Class distribution in six retail files:

File Effects Music Speech
01DIE_A.SAN 4 1 0
02P01_A.SAN 15 1 0
14BEG.SAN 56 2 7
15CUT.SAN 32 1 8
15END.SAN 19 1 0
O_OPEN_D.SAN 57 0 10

8. Pan

The pan byte (PSAD offset 9) is a signed value with a sentinel:

Byte value Signed value Meaning
0x00 0 Centre
0x01–0x7F +1 … +127 Panned toward one side, magnitude proportional
0x81–0xFF −127 … −1 Panned toward the other side
0x80 −128 Sentinel: no pan. Channel retains its default of 0 (centre)

Engine evidence: the chunk handler (0x29286) tests pan &lt; −127 and substitutes the sentinel path; the channel setter (0x29DB3) stores the value only if it is strictly within (−128, +128), leaving the channel's default of 0 otherwise.

The value 0x80 therefore does not mean "hard right" under an unsigned reading; files whose pan bytes are uniformly 0x80 are fully centred (mono) content. Retail examples: 15CUT.SAN and 15END.SAN contain only 0x80; 02P01_A.SAN contains 19–55 and 203/239 (= −53/−17); 14BEG.SAN and O_OPEN_D.SAN use wide ranges including 0 and 247.

A global reverse-stereo option exists (flag at 0x2E38C; pan negated at 0x2A39E when set). The absolute sign convention (whether positive pan is right or left) and the pan law (mapping of pan position to left/right gains) are implemented in the low-level mixing primitive and are (unresolved). A constant-power law is a reasonable implementation choice (inferred).

9. Engine data structures (reference)

Channel state is held in two parallel arrays indexed by channel number.

Control array — stride 0x2F bytes, base 0x6A72C:

Address Offset Field
0x6A724 −8 current data pointer
0x6A728 −4 end pointer
0x6A730 +4 volume (0–127)
0x6A731 +5 pan (signed; 0 = centre)
0x6A732 +6 state (0 = free)
0x6A733 +7 flags (class in & 0xC0)
0x6A735 +9 id
0x6A747 +0x1B length / end

Mixing array — stride 0x30 bytes, base 0x6A320:

Offset Field
+0x00 source buffer pointer
+0x10 sample rate
+0x16 working rate (copied from +0x10)
+0x1A working volume (copied from control-array volume)
+0x1E per-tick sample counter (initialised to 0xC00)

Globals:

Address Field
0x2E380 channel count
0x2E382 master volume
0x2E38C reverse-stereo flag
0x2E390 mixer sample rate
0x2E398 music-volume preference

10. Executable addressing and verification

RA2.EXE is a Watcom linear-executable (LE) binary run under DOS/4GW. (The accompanying REBEL2.EXE is a 16-bit launcher stub containing no engine code.) Relevant LE parameters of the retail build:

LE header offset (e_lfanew)  0x28B8
page size                    0x1000
data pages offset            0x19E00
object 1 (code)              vsize 0x520F9, base 0x10000, 83 pages
entry point                  object 1, EIP 0x36784

File-offset mapping: file_offset = 0x19E00 + virtual_address. All virtual addresses in this document use that scheme.

Chunk tags are compared in code as byte-swapped 32-bit immediates (cmp edx, 0x50534144 for "PSAD"); to locate them by byte search, search for the reversed ASCII (DASP, DUAS, COVP) or the immediate value.

Verification snippet (requires the capstone Python package):

python

import capstone

data = open('RA2.EXE', 'rb').read()
BASE = 0x19E00
md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_32)

def dis(vaddr, n=0x60):
    for i in md.disasm(data[BASE + vaddr : BASE + vaddr + n], vaddr):
        print(f"{i.address:08x}: {i.mnemonic} {i.op_str}")

dis(0x290C2)   # chunk-tag dispatch (PSAD / PVOC / SAUD)
dis(0x292C1)   # volume stage 1: vol * 127 &gt;&gt; 7
dis(0x2A0C7)   # volume stage 2: master * chanVol / 127; music-class scaling
dis(0x2A2F6)   # rate conversion: outSamples * chanRate / mixerRate
dis(0x29D86)   # channel setup: volume default 0x7F, pan range check
dis(0x2A6E2)   # STRK interpreter: opcode fetch and jump-table dispatch
dis(0x2ABA4)   # STRK conditional jumps: compare, then pc += s16 offset

11. Decoding procedure

To reconstruct the audio of a .SAN file:

  1. Read AHDR: obtain frameCount and frameRate. Use defaultAudioRate only as a fallback.
  2. Iterate FRME chunks, maintaining a frame index.
  3. For each PSAD/PSD2/PVOC chunk, read the 10-byte header.
    • index == 0 with a following SAUD header: open a new sound segment on that track (closing any segment previously open there). Parse STRK for the sample rate; record the SDAT declared size; store the PCM that follows.
    • index &gt; 0: append the chunk payload to the open segment on that track.
  4. Per segment: concatenate chunks in index order and truncate to the SDAT size. This byte stream is what the STRK program addresses.
  5. Execute the STRK program (§4) to obtain a playback plan: an ordered list of (offset, size, rate) slices of SDAT. Terminate on opcode 5, on the terminator, or — for programs containing a backward jump — once the plan covers the remaining video duration. A straight-line program must be allowed to run to its natural end even if it outlasts the picture; retail files do this routinely.
  6. Decode PCM as (byte − 128); apply gain = vol/128 and the pan curve, both indexed by SDAT position, with a short (~20 ms) transition between differing per-chunk values, mapping pan byte 0x80 to centre.
  7. Resample each plan slice from its rate to a common output rate (mixing at the maximum source rate present avoids downsampling).
  8. Place each segment at startFrame / frameRate seconds and sum. Apply no ducking, no volume curve other than linear, and unity music volume.

12. Known data anomalies in retail files

  • 15END.SAN: the music track contains a genuine waveform discontinuity at t ≈ 32.07 s (adjacent PCM bytes 117 → 36) mid-chunk with constant volume; present in the file data.
  • 15END.SAN: the music track's PCM ends at t ≈ 32.93 s against a 33.73 s video, leaving ~0.8 s of silence; in game, playback chains directly into the next file (inferred).
  • Some cutscenes end on held, near-static shots; these are authored frames, not decoder stalls.

13. Related but distinct

  • SANM-tagged files (later SMUSH v2, 16-bit colour) use different video codecs and VIMA-compressed audio; not covered here.
  • IACT audio (The Dig and later): compressed audio in IACT chunks; the RA2 engine parses IACT chunks for other purposes, and this scheme is not used for RA2 cutscene sound.
  • DiMUSE volume semantics (pseudo-logarithmic scaling) belong to a later engine and do not apply to this format.
reddit.com
u/majestic_ubertrout — 1 month ago
▲ 51 r/starwarsgames+1 crossposts

I Upscaled the Rebel Assault 2 Cutscenes

Having done the Dark Forces 2: Jedi Knight cutscenes a while ago this was the logical choice to do next - it was the very first game with live action cutscenes and the first live action Star Wars footage since the 1980s. And it was done with original costumes and more! Of course, it's not as good of a game as Jedi Knight and it's even cheesier somehow, but that wasn't what took so long.

Basically, there's many more video files in this game and the format they use is much more obtuse. This kind of comes down to how the entire game actually _is_ a movie. All the action plays out along pre-rendered video, and I've included the mission video files in the video I made. I realize not everyone wants this but I think it works. Chapter markers are in the description.

Video exported via FFMPEG, which natively supports it. Audio was much, much more complicated. The game is mixing dialogue, sound fx, and music on the fly even in the prerendered cutscenes, and there isn't much documentation of what format it's using. I ended up using Claude a lot in this, to the point of actually giving it the RA2.exe executable to decompile and figure out how audio is working under the hood. It's still not perfect but it's functional I think. I'll post the documentation of what I found about the audio as a comment.

Video upscaled about 3x to 1280x800 and then downscaled a bit to 1280x720 with minor black bars on the sides. This felt more sensible than trying for another upscale to 1080p- it's already on the edge of the tech. I used Topaz Starlight Precise mostly. For the first scenes of the Corellia Star I used Gaia CG to keep the distortion - Starlight was trying to fix it. Later on when you see these scenes cleaned up I let Starlight handle. The footage is originally 15 FPS. I frame-doubled it to 30 FPS and then reduced to 24 FPS using Topaz Apollo.

This was a fun project and I learned a lot doing it. Hope you enjoy!

youtu.be
u/majestic_ubertrout — 1 month ago

Xpand XPO - Virtual Mac Trade Show From 1994

This disk was in the Apple Educator Advantage pack I linked to earlier - it's a series of videos about Apple products then available, I believe all with the same narrator.

youtu.be
u/majestic_ubertrout — 1 month ago
▲ 267 r/GVCDesign+1 crossposts

Found this Apple Education Set from 1995ish

Purchased a lot of boxed and loose software from what I'm pretty sure was a computer teacher from the 1990s from a public school. This was in there.

u/herseyhawkins33 — 1 month ago

Into the Great Wide Open - Attempt at Upscaling

Upscaling tech is getting better and better, and I figured I'd try the video from Into the Great Wide Open. It looks really good most of the time; there's a few lingering issues which prevent it from being perfect which I suspect will be fixed as things continue to improve.

Upscaled with Starlight Precision 2.5 in Topaz Video to 1440p.

youtube.com
u/majestic_ubertrout — 2 months ago
▲ 338 r/quake

30 Years of Quakes

The Quake 2 box on top is the LRG reissue (as is Powerslave). I still remember playing it on a Pentium 133 and trying to get this newfangled 3d acceleration and GL Quake working, mostly to no avail. Hard to believe it's been 30 years!

u/majestic_ubertrout — 2 months ago