GTA 1 File Formats:  SDT + (Audio) RAW

Back to main page

Description

RAW file in the AUDIO directory (and ONLY in the AUDIO directory) contain raw sound data.
SDT files contain the offsets, length, and bitrate of the individual sounds.

The SDT file does NOT contain all information that is required to convert to WAV.
The number of channels (1=mono, 2=stereo) as well as the bits per sample (8, 16) must be provided from outside (i.e. hardcoded in the EXE file), and is NOT part of the SDT file.

The values are:

Known files and their parameters

Format

Sdt {
    Sounds[var] {
        DWORD startOffset;
        DWORD length;
        DWORD bitrate
    }
}

PHP Script to decode files

<?php

$sdtFile 
'D:\\GTADATA\\UK\\AUDIO\\VOCALCOM.SDT';
$rawFile 'D:\\GTADATA\\UK\\AUDIO\\VOCALCOM.RAW';
$outDir  'C:\\Temp\\';

if (!
file_exists($sdtFile) || !file_exists($rawFile)) {
    die(
"SDT or RAW file not found.\n");
}

if (!
is_dir($outDir)) {
    
mkdir($outDir0777true);
}

$sdtData file_get_contents($sdtFile);
$rawData file_get_contents($rawFile);

$entrySize 12;
$totalEntries intdiv(strlen($sdtData), $entrySize);

echo 
"Found entries: $totalEntries\n";

$baseName pathinfo($sdtFilePATHINFO_FILENAME);

for (
$i 0$i $totalEntries$i++) {
    
$entry substr($sdtData$i $entrySize$entrySize);

    
// Little Endian 3x uint32
    
$data unpack("Voffset/Vlength/Vsamplerate"$entry);

    
$offset $data['offset'];
    
$length $data['length'];
    
$rate   $data['samplerate'];

    if (
$length <= 0) continue;

    
$audioData substr($rawData$offset$length);

    if (
strlen($audioData) !== $length) {
        echo 
"Warnung: Invalid length at entry $i\n";
        continue;
    }

    if (
stripos($sdtFile,'LEVEL000') !== false) {
        
$numChannels $i<1;
        
$bitsPerSample 16;
    } else {
        
$numChannels 1;
        
$bitsPerSample 8;
    }

    
$wavData createWav($audioData$rate$numChannels$bitsPerSample);

    
$index str_pad($i 13"0"STR_PAD_LEFT);
    
$outFile "$outDir/{$baseName}-$index.wav";

    
file_put_contents($outFile$wavData);

    echo 
"Exportiert: $outFile (Rate: $rate Hz)\n";
}

function 
createWav($audioData$sampleRate$numChannels$bitsPerSample)
{
    
$header  "RIFF"// ChunkID
    
$header .= pack("V"36 strlen($audioData)); // ChunkSize
    
$header .= "WAVE"// Format
    
$header .= "fmt "// Subchunk1ID
    
$header .= pack("V"16); // Subchunk1Size
    
$header .= pack("v"1); // AudioFormat 1=PCM
    
$header .= pack("v"$numChannels); // NumChannels
    
$header .= pack("V"$sampleRate); // SampleRate
    
$header .= pack("V"$sampleRate $numChannels * ($bitsPerSample 8)); // ByteRate
    
$header .= pack("v"$numChannels * ($bitsPerSample 8)); // BlockAlign
    
$header .= pack("v"$bitsPerSample); // BitsPerSample
    // Space for extra parameters (if PCM, then doesn't exist):
    //$header .= pack("v", strlen($ExtraParams)); // ExtraParamSize
    //$header .= $ExtraParams; // ExtraParams
    
$header .= "data"// Subchunk2ID
    
$header .= pack("V"strlen($audioData)); // Subchunk2Size
    
return $header $audioData// Data
}