Last active
November 12, 2022 02:49
-
-
Save barncastle/be026bbbab5c469f4e9e1317e95276e9 to your computer and use it in GitHub Desktop.
Code for parsing TMNT: Shredder's Revenge's SoundSettings and SFXData files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Paris.Engine.SoundSettings | |
using System.Collections.Generic; | |
using System.IO; | |
using System.IO.Compression; | |
public class SoundSettings | |
{ | |
public GlobalSoundSettings Settings; | |
public List<SoundChannel> Channels = new List<SoundChannel>(); | |
public List<SoundGroupInfo> SoundGroups = new List<SoundGroupInfo>(); | |
public List<SoundInfo> Sounds = new List<SoundInfo>(); | |
public List<SoundInfo> Music = new List<SoundInfo>(); | |
/// <summary> | |
/// Loads settings from SoundsSettings.zpbn | |
/// </summary> | |
public void Load(string path) | |
{ | |
using (var fs = File.OpenRead(path)) | |
using (var ds = new DeflateStream(fs, CompressionMode.Decompress)) | |
using (var br = new BinaryReader(ds)) | |
{ | |
var serializerType = br.ReadString(); | |
int soundChannelCount = br.ReadInt32(); | |
for (int i = 0; i < soundChannelCount; i++) | |
{ | |
Channels.Add(new SoundChannel | |
{ | |
MaxSounds = br.ReadInt32(), | |
Name = br.ReadString(), | |
PlayType = (PlayRule)br.ReadInt32(), | |
Volume = br.ReadSingle() | |
}); | |
} | |
int musicSoundInfoCount = br.ReadInt32(); | |
for (int j = 0; j < musicSoundInfoCount; j++) | |
{ | |
SoundInfo soundInfo = new SoundInfo | |
{ | |
Cooldown = br.ReadSingle(), | |
CutOffType = (SoundCutOffType)br.ReadInt32(), | |
IsVO = br.ReadBoolean(), | |
LoopType = (SoundLoopType)br.ReadInt32(), | |
PitchMax = br.ReadSingle(), | |
PitchMin = br.ReadSingle() | |
}; | |
Music.Add(soundInfo); | |
int playbackChannelCount = br.ReadInt32(); | |
for (int k = 0; k < playbackChannelCount; k++) | |
soundInfo.PlaybackChannels.Add(br.ReadString()); | |
soundInfo.PoolSize = br.ReadInt32(); | |
soundInfo.SoundID = br.ReadString(); | |
soundInfo.Volume = br.ReadSingle(); | |
} | |
Settings.MasterMusicVolume = br.ReadSingle(); | |
Settings.MasterSoundVolume = br.ReadSingle(); | |
Settings.MasterVolume = br.ReadSingle(); | |
int soundGroupInfoCount = br.ReadInt32(); | |
for (int l = 0; l < soundGroupInfoCount; l++) | |
{ | |
var soundGroupInfo = new SoundGroupInfo | |
{ | |
ChanceToPlay = br.ReadInt32(), | |
Cooldown = br.ReadSingle(), | |
ID = br.ReadString() | |
}; | |
SoundGroups.Add(soundGroupInfo); | |
int soundIDCount = br.ReadInt32(); | |
for (int m = 0; m < soundIDCount; m++) | |
{ | |
br.ReadString(); // serializerName | |
br.ReadString(); // collectionID->ID | |
soundGroupInfo.SoundList.Add(br.ReadString()); | |
} | |
} | |
int settingsSoundInfoCount = br.ReadInt32(); | |
for (int n = 0; n < settingsSoundInfoCount; n++) | |
{ | |
SoundInfo soundInfo = new SoundInfo | |
{ | |
Cooldown = br.ReadSingle(), | |
CutOffType = (SoundCutOffType)br.ReadInt32(), | |
IsVO = br.ReadBoolean(), | |
LoopType = (SoundLoopType)br.ReadInt32(), | |
PitchMax = br.ReadSingle(), | |
PitchMin = br.ReadSingle() | |
}; | |
Sounds.Add(soundInfo); | |
int num7 = br.ReadInt32(); | |
for (int num8 = 0; num8 < num7; num8++) | |
soundInfo.PlaybackChannels.Add(br.ReadString()); | |
soundInfo.PoolSize = br.ReadInt32(); | |
soundInfo.SoundID = br.ReadString(); | |
soundInfo.Volume = br.ReadSingle(); | |
} | |
} | |
} | |
/// <summary> | |
/// Loads audio data from SFXPack.pbn | |
/// </summary> | |
public void LoadData(string path) | |
{ | |
using (var fs = File.OpenRead(path)) | |
using (var br = new BinaryReader(fs)) | |
{ | |
for (var i = 0; i < Sounds.Count; i++) | |
{ | |
var fileSize = br.ReadInt32(); | |
var data = br.ReadBytes(fileSize); | |
Sounds[i].RawData = data; | |
} | |
} | |
} | |
/// <summary> | |
/// Exports to WAV | |
/// </summary> | |
public void ExportToWAV(string path, SoundInfo soundInfo) | |
{ | |
const ushort channels = 2; | |
const uint sampleRate = 48000; | |
var filename = Path.Combine(path, $"{soundInfo.SoundID}.wav"); | |
for (var i = 0; File.Exists(filename); i++) | |
filename = Path.Combine(path, $"{soundInfo.SoundID}_{i:X2}.wav"); | |
using (var fs = File.Create(filename)) | |
using (var bw = new BinaryWriter(fs)) | |
{ | |
bw.Write("RIFF".ToCharArray()); | |
bw.Write(soundInfo.RawData.Length + 36); | |
bw.Write("WAVE".ToCharArray()); | |
bw.Write("fmt ".ToCharArray()); | |
bw.Write(16); | |
bw.Write((ushort)1); // wFormatTag | |
bw.Write(channels); | |
bw.Write(sampleRate); | |
bw.Write(sampleRate * channels * 2); // nAvgBytesPerSec | |
bw.Write((ushort)(channels * 2)); // nBlockAlign | |
bw.Write(16); // wBitsPerSample | |
bw.Write("data".ToCharArray()); | |
bw.Write(soundInfo.RawData.Length); | |
bw.Write(soundInfo.RawData); | |
} | |
} | |
public enum SoundCutOffType | |
{ | |
CutOff, | |
NoCutOff, | |
MuteUntilChannelFree | |
} | |
public enum PlayRule | |
{ | |
AlwaysPlay, | |
PlayOnlyIfFree | |
} | |
public enum SoundLoopType | |
{ | |
NoLoop, | |
Loop, | |
LoopNoInterrupt | |
} | |
public class GlobalSoundSettings | |
{ | |
public float MasterVolume = 100f; | |
public float MasterMusicVolume = 100f; | |
public float MasterSoundVolume = 100f; | |
} | |
public class SoundChannel | |
{ | |
public string Name; | |
public float Volume = 100f; | |
public int MaxSounds; | |
public PlayRule PlayType; | |
} | |
public class SoundGroupInfo | |
{ | |
public string ID; | |
public List<string> SoundList = new List<string>(); | |
public float Cooldown; | |
public int ChanceToPlay = 100; | |
} | |
public class SoundInfo | |
{ | |
public string SoundID; | |
public float Volume; | |
public SoundLoopType LoopType; | |
public int PoolSize; | |
public float Cooldown; | |
public bool IsVO; | |
public SoundCutOffType CutOffType; | |
public float PitchMin; | |
public float PitchMax; | |
public List<string> PlaybackChannels = new List<string>(); | |
public byte[] RawData; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you for this code!
I am having some issues. In exporting the WAV files - they all are silent. The names are right and their total size do add up to the size of the full SFXPack.pbn. Did I miss something? Any ideas?
Best regards!