Skip to content

Commit

Permalink
Organise SFX data hierarchically (LostArtefacts#620)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahm86 authored Apr 20, 2024
1 parent 1667d66 commit 5a3e7be
Show file tree
Hide file tree
Showing 61 changed files with 3,283 additions and 8,508 deletions.
15 changes: 4 additions & 11 deletions SFXExport/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,12 @@ private static void ExtractFromSFX(string file, TRGameVersion version, bool rema
private static void ExtractFromPHD(string file, TRGameVersion version)
{
TR1Level level = new TR1LevelControl().Read(file);
for (int i = 0; i < level.SampleIndices.Count; i++)
foreach (var (sfxID, effect) in level.SoundEffects)
{
uint sampleStart = level.SampleIndices[i];
uint sampleEnd = i < level.SampleIndices.Count - 1 ? level.SampleIndices[i + 1] : (uint)level.Samples.Count;
if (sampleEnd > level.Samples.Count)
for (int i = 0; i < effect.Samples.Count; i++)
{
sampleEnd = (uint)level.Samples.Count;
}

using BinaryWriter writer = new(File.Create(Path.Combine(version.ToString(), i + ".wav")));
for (uint j = sampleStart; j < sampleEnd; j++)
{
writer.Write(level.Samples[(int)j]);
string path = Path.Combine(version.ToString(), $"{((int)sfxID).ToString().PadLeft(3, '0')}_{i}.wav");
File.WriteAllBytes(path, effect.Samples[i]);
}
}
}
Expand Down
126 changes: 95 additions & 31 deletions TRLevelControl/Control/TR1LevelControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -252,30 +252,7 @@ protected override void Read(TRLevelReader reader)
ushort numDemoData = reader.ReadUInt16();
_level.DemoData = reader.ReadBytes(numDemoData);

//Sound Map & Sound Details
_level.SoundMap = new short[256];

for (int i = 0; i < _level.SoundMap.Length; i++)
{
_level.SoundMap[i] = reader.ReadInt16();
}

uint numSoundDetails = reader.ReadUInt32();
_level.SoundDetails = new();
for (int i = 0; i < numSoundDetails; i++)
{
_level.SoundDetails.Add(TR2FileReadUtilities.ReadSoundDetails(reader));
}

uint numSamples = reader.ReadUInt32();
_level.Samples = new();
for (int i = 0; i < numSamples; i++)
{
_level.Samples.Add(reader.ReadByte());
}

uint numSampleIndices = reader.ReadUInt32();
_level.SampleIndices = reader.ReadUInt32s(numSampleIndices).ToList();
ReadSoundEffects(reader);
}

protected override void Write(TRLevelWriter writer)
Expand Down Expand Up @@ -353,13 +330,7 @@ protected override void Write(TRLevelWriter writer)
writer.Write((ushort)_level.DemoData.Length);
writer.Write(_level.DemoData);

foreach (short sound in _level.SoundMap) { writer.Write(sound); }
writer.Write((uint)_level.SoundDetails.Count);
foreach (TRSoundDetails snddetail in _level.SoundDetails) { writer.Write(snddetail.Serialize()); }
writer.Write((uint)_level.Samples.Count);
writer.Write(_level.Samples.ToArray());
writer.Write((uint)_level.SampleIndices.Count);
writer.Write(_level.SampleIndices);
WriteSoundEffects(writer);
}

private static TRRoomData ConvertToRoomData(TRRoom room)
Expand Down Expand Up @@ -570,4 +541,97 @@ private static List<TRMesh> ConstructMeshData(List<uint> meshPointers, ushort[]

return meshes;
}

private void ReadSoundEffects(TRLevelReader reader)
{
_level.SoundEffects = new();
short[] soundMap = reader.ReadInt16s(Enum.GetValues<TR1SFX>().Length);

uint numSoundDetails = reader.ReadUInt32();
List<TR1SoundEffect> sfx = new();

Dictionary<int, ushort> sampleMap = new();
for (int i = 0; i < numSoundDetails; i++)
{
sampleMap[i] = reader.ReadUInt16();
sfx.Add(new()
{
Volume = reader.ReadUInt16(),
Chance = reader.ReadUInt16(),
Samples = new()
});

sfx[i].SetFlags(reader.ReadUInt16());
}

uint numSamples = reader.ReadUInt32();
byte[] allSamples = reader.ReadUInt8s(numSamples);

uint numSampleIndices = reader.ReadUInt32();
uint[] sampleIndices = reader.ReadUInt32s(numSampleIndices);

foreach (int soundID in sampleMap.Keys)
{
TR1SoundEffect effect = sfx[soundID];
ushort baseIndex = sampleMap[soundID];
for (int i = 0; i < effect.Samples.Capacity; i++)
{
uint start = sampleIndices[baseIndex + i];
uint end = baseIndex + i + 1 == sampleIndices.Length
? (uint)allSamples.Length
: sampleIndices[baseIndex + i + 1];

byte[] sample = new byte[end - start];
for (int j = 0; j < sample.Length; j++)
{
sample[j] = allSamples[start + j];
}

effect.Samples.Add(sample);
}
}

for (int i = 0; i < soundMap.Length; i++)
{
if (soundMap[i] == -1)
{
continue;
}

_level.SoundEffects[(TR1SFX)i] = sfx[soundMap[i]];
}
}

private void WriteSoundEffects(TRLevelWriter writer)
{
short detailsIndex = 0;
List<uint> samplePointers = new();
List<byte> wavData = new();

foreach (TR1SFX id in Enum.GetValues<TR1SFX>())
{
writer.Write(_level.SoundEffects.ContainsKey(id) ? detailsIndex++ : (short)-1);
}

writer.Write((uint)_level.SoundEffects.Count);
foreach (var (_, effect) in _level.SoundEffects)
{
writer.Write((ushort)samplePointers.Count);
writer.Write(effect.Volume);
writer.Write(effect.Chance);
writer.Write(effect.GetFlags());

foreach (byte[] wav in effect.Samples)
{
samplePointers.Add((uint)wavData.Count);
wavData.AddRange(wav);
}
}

writer.Write((uint)wavData.Count);
writer.Write(wavData);

writer.Write((uint)samplePointers.Count);
writer.Write(samplePointers);
}
}
102 changes: 80 additions & 22 deletions TRLevelControl/Control/TR2LevelControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,23 +256,7 @@ protected override void Read(TRLevelReader reader)
ushort numDemoData = reader.ReadUInt16();
_level.DemoData = reader.ReadBytes(numDemoData);

//Sound Map (370 shorts = 740 bytes) & Sound Details
_level.SoundMap = new short[370];

for (int i = 0; i < _level.SoundMap.Length; i++)
{
_level.SoundMap[i] = reader.ReadInt16();
}

uint numSoundDetails = reader.ReadUInt32();
_level.SoundDetails = new();
for (int i = 0; i < numSoundDetails; i++)
{
_level.SoundDetails.Add(TR2FileReadUtilities.ReadSoundDetails(reader));
}

uint numSampleIndices = reader.ReadUInt32();
_level.SampleIndices = reader.ReadUInt32s(numSampleIndices).ToList();
ReadSoundEffects(reader);
}

protected override void Write(TRLevelWriter writer)
Expand Down Expand Up @@ -355,11 +339,7 @@ protected override void Write(TRLevelWriter writer)
writer.Write((ushort)_level.DemoData.Length);
writer.Write(_level.DemoData);

foreach (short sound in _level.SoundMap) { writer.Write(sound); }
writer.Write((uint)_level.SoundDetails.Count);
foreach (TRSoundDetails snddetail in _level.SoundDetails) { writer.Write(snddetail.Serialize()); }
writer.Write((uint)_level.SampleIndices.Count);
writer.Write(_level.SampleIndices);
WriteSoundEffects(writer);
}

private static TR2RoomData ConvertToRoomData(TR2Room room)
Expand Down Expand Up @@ -574,4 +554,82 @@ private static List<TRMesh> ConstructMeshData(List<uint> meshPointers, ushort[]

return meshes;
}

private void ReadSoundEffects(TRLevelReader reader)
{
_level.SoundEffects = new();
short[] soundMap = reader.ReadInt16s(Enum.GetValues<TR2SFX>().Length);

uint numSoundDetails = reader.ReadUInt32();
List<TR2SoundEffect> sfx = new();

Dictionary<int, ushort> sampleMap = new();
for (int i = 0; i < numSoundDetails; i++)
{
sampleMap[i] = reader.ReadUInt16();
sfx.Add(new()
{
Volume = reader.ReadUInt16(),
Chance = reader.ReadUInt16(),
Samples = new()
});

sfx[i].SetFlags(reader.ReadUInt16());
}

uint numSampleIndices = reader.ReadUInt32();
uint[] sampleIndices = reader.ReadUInt32s(numSampleIndices);

foreach (int soundID in sampleMap.Keys)
{
TR2SoundEffect effect = sfx[soundID];
ushort baseIndex = sampleMap[soundID];
for (int i = 0; i < effect.Samples.Capacity; i++)
{
effect.Samples.Add(sampleIndices[baseIndex + i]);
}
}

for (int i = 0; i < soundMap.Length; i++)
{
if (soundMap[i] < 0 || soundMap[i] >= sfx.Count)
{
continue;
}

_level.SoundEffects[(TR2SFX)i] = sfx[soundMap[i]];
}
}

private void WriteSoundEffects(TRLevelWriter writer)
{
short detailsIndex = 0;
foreach (TR2SFX id in Enum.GetValues<TR2SFX>())
{
writer.Write(_level.SoundEffects.ContainsKey(id) ? detailsIndex++ : (short)-1);
}

List<uint> samplePointers = new();
foreach (var (_, effect) in _level.SoundEffects)
{
if (!samplePointers.Contains(effect.Samples.First()))
{
samplePointers.AddRange(effect.Samples);
}
}
samplePointers.Sort();

writer.Write((uint)_level.SoundEffects.Count);
foreach (var (_, effect) in _level.SoundEffects)
{
uint firstSample = effect.Samples.First();
writer.Write((ushort)samplePointers.IndexOf(firstSample));
writer.Write(effect.Volume);
writer.Write(effect.Chance);
writer.Write(effect.GetFlags());
}

writer.Write((uint)samplePointers.Count);
writer.Write(samplePointers);
}
}
Loading

0 comments on commit 5a3e7be

Please sign in to comment.