mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-09-05 11:25:49 -04:00
Added FSO.Files for use with the API server
Don't ask me. FreeSO is the prime example of dependency hell.
This commit is contained in:
parent
4b5e584eeb
commit
8fec258215
104 changed files with 14653 additions and 163 deletions
96
server/tso.files/HIT/EVT.cs
Executable file
96
server/tso.files/HIT/EVT.cs
Executable file
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace FSO.Files.HIT
|
||||
{
|
||||
/// <summary>
|
||||
/// EVT is a CSV format that defines a list of events for HIT to listen for.
|
||||
/// </summary>
|
||||
public class EVT
|
||||
{
|
||||
public List<EVTEntry> Entries;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new evt file.
|
||||
/// </summary>
|
||||
/// <param name="Filedata">The data to create the evt from.</param>
|
||||
public EVT(byte[] Filedata)
|
||||
{
|
||||
ReadFile(new MemoryStream(Filedata));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new evt file.
|
||||
/// </summary>
|
||||
/// <param name="Filedata">The path to the data to create the evt from.</param>
|
||||
public EVT(string Filepath)
|
||||
{
|
||||
ReadFile(File.Open(Filepath, FileMode.Open, FileAccess.Read, FileShare.Read));
|
||||
}
|
||||
|
||||
private void ReadFile(Stream data)
|
||||
{
|
||||
Entries = new List<EVTEntry>();
|
||||
BinaryReader Reader = new BinaryReader(data);
|
||||
|
||||
string CommaSeparatedValues = new string(Reader.ReadChars((int)data.Length));
|
||||
string[] Lines = CommaSeparatedValues.Split(new string[] { "\r\n" }, StringSplitOptions.None);
|
||||
|
||||
for (int i = 0; i < Lines.Length; i++)
|
||||
{
|
||||
if (Lines[i] == "") continue;
|
||||
string[] Values = Lines[i].Split(',');
|
||||
|
||||
var Entry = new EVTEntry();
|
||||
Entry.Name = Values[0].ToLowerInvariant();
|
||||
Entry.EventType = ParseHexString(Values[1]);
|
||||
Entry.TrackID = ParseHexString(Values[2]);
|
||||
Entry.Unknown = ParseHexString(Values[3]);
|
||||
Entry.Unknown2 = ParseHexString(Values[4]);
|
||||
Entry.Unknown3 = ParseHexString(Values[5]);
|
||||
Entry.Unknown4 = ParseHexString(Values[6]);
|
||||
Entries.Add(Entry);
|
||||
}
|
||||
|
||||
Reader.Close();
|
||||
}
|
||||
|
||||
private uint ParseHexString(string input)
|
||||
{
|
||||
bool IsHex = false;
|
||||
input = input.ToLowerInvariant();
|
||||
|
||||
if (input == "") return 0;
|
||||
if (input.StartsWith("0x"))
|
||||
{
|
||||
input = input.Substring(2);
|
||||
IsHex = true;
|
||||
}
|
||||
else if (input.Contains("a") || input.Contains("b") || input.Contains("c") || input.Contains("d") || input.Contains("e") || input.Contains("f"))
|
||||
{
|
||||
IsHex = true;
|
||||
}
|
||||
|
||||
if (IsHex)
|
||||
{
|
||||
return Convert.ToUInt32(input, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Convert.ToUInt32(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class EVTEntry
|
||||
{
|
||||
public string Name;
|
||||
public uint EventType;
|
||||
public uint TrackID;
|
||||
public uint Unknown;
|
||||
public uint Unknown2;
|
||||
public uint Unknown3;
|
||||
public uint Unknown4;
|
||||
}
|
||||
}
|
153
server/tso.files/HIT/FSC.cs
Executable file
153
server/tso.files/HIT/FSC.cs
Executable file
|
@ -0,0 +1,153 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace FSO.Files.HIT
|
||||
{
|
||||
public class FSC
|
||||
{
|
||||
/// <summary>
|
||||
/// FSC is a tabulated plaintext format that describes a sequence of notes to be played. In this game it is used to sequence the ambient sounds.
|
||||
/// The conditions in which the sequence is randomized are not entirely apparent, and have been mostly guessed.
|
||||
/// </summary>
|
||||
///
|
||||
|
||||
public List<FSCNote> Notes;
|
||||
|
||||
public string VersionCode;
|
||||
|
||||
public ushort MasterVolume;
|
||||
public ushort Priority;
|
||||
public ushort Min;
|
||||
public ushort Max;
|
||||
public ushort Rows; //these seem to be outright lies, but let's leave them in
|
||||
public ushort Columns;
|
||||
public ushort Tempo;
|
||||
public ushort BPB; //beats per bar
|
||||
public ushort SelX;
|
||||
public ushort SelY;
|
||||
public ushort QuanX;
|
||||
public ushort QuanY;
|
||||
public ushort DiffX;
|
||||
public ushort DiffY;
|
||||
|
||||
public List<int> RandomJumpPoints;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new hsm file.
|
||||
/// </summary>
|
||||
/// <param name="Filedata">The data to create the hsm from.</param>
|
||||
public FSC(byte[] Filedata)
|
||||
{
|
||||
ReadFile(new MemoryStream(Filedata));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new hsm file.
|
||||
/// </summary>
|
||||
/// <param name="Filedata">The path to the data to create the hsm from.</param>
|
||||
public FSC(string Filepath)
|
||||
{
|
||||
ReadFile(File.Open(Filepath, FileMode.Open, FileAccess.Read, FileShare.Read));
|
||||
}
|
||||
|
||||
private void ReadFile(Stream stream)
|
||||
{
|
||||
var io = new StreamReader(stream);
|
||||
|
||||
Notes = new List<FSCNote>();
|
||||
RandomJumpPoints = new List<int>();
|
||||
VersionCode = io.ReadLine();
|
||||
var line = io.ReadLine();
|
||||
|
||||
while (line.StartsWith("#"))
|
||||
line = io.ReadLine();
|
||||
|
||||
//Header
|
||||
string[] Head = line.Split('\t');
|
||||
MasterVolume = Convert.ToUInt16(Head[1]);
|
||||
Priority = Convert.ToUInt16(Head[2]);
|
||||
Min = Convert.ToUInt16(Head[3]);
|
||||
Max = Convert.ToUInt16(Head[4]);
|
||||
Rows = Convert.ToUInt16(Head[5]);
|
||||
Columns = Convert.ToUInt16(Head[6]);
|
||||
Tempo = Convert.ToUInt16(Head[7]);
|
||||
BPB = Convert.ToUInt16(Head[8]);
|
||||
SelX = Convert.ToUInt16(Head[9]);
|
||||
SelY = Convert.ToUInt16(Head[10]);
|
||||
if(Head[11][0] != '-') QuanX = Convert.ToUInt16(Head[11]);
|
||||
if (Head[12][0] != '-') QuanY = Convert.ToUInt16(Head[12]);
|
||||
DiffX = Convert.ToUInt16(Head[13]);
|
||||
DiffY = Convert.ToUInt16(Head[14]);
|
||||
|
||||
line = io.ReadLine();
|
||||
|
||||
while (line.StartsWith("#") || line.StartsWith("cells"))
|
||||
line = io.ReadLine();
|
||||
|
||||
while (!io.EndOfStream) //read notes
|
||||
{
|
||||
string line2 = io.ReadLine();
|
||||
string[] Values = line2.Split('\t');
|
||||
if (!line.StartsWith("#") && Values.Length == 20)
|
||||
{
|
||||
var note = new FSCNote()
|
||||
{
|
||||
Volume = Convert.ToUInt16(Values[1]),
|
||||
Rand = Values[2] != "0",
|
||||
LRPan = Convert.ToUInt16(Values[3]),
|
||||
FBPan = Convert.ToUInt16(Values[4]),
|
||||
Rand2 = Values[5] != "0",
|
||||
|
||||
Fin = Convert.ToUInt16(Values[6]),
|
||||
FOut = Convert.ToUInt16(Values[7]),
|
||||
dly = Convert.ToUInt16(Values[8]),
|
||||
Rand3 = Values[9] != "0",
|
||||
Loop = Convert.ToUInt16(Values[10]),
|
||||
|
||||
Loop2 = Values[11] != "0",
|
||||
Quant = Convert.ToUInt16(Values[12]),
|
||||
Prob = Convert.ToUInt16(Values[13]),
|
||||
pitchL = Convert.ToInt16(Values[14]),
|
||||
pitchR = Convert.ToInt16(Values[15]),
|
||||
|
||||
Fast = Values[16] != "0",
|
||||
GroupID = Convert.ToUInt16(Values[17]),
|
||||
Stereo = Values[18] != "0",
|
||||
Filename = Values[19]
|
||||
};
|
||||
if (note.Rand) RandomJumpPoints.Add(Notes.Count);
|
||||
Notes.Add(note);
|
||||
}
|
||||
}
|
||||
|
||||
io.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public struct FSCNote
|
||||
{
|
||||
public ushort Volume; //0-1024
|
||||
public bool Rand;
|
||||
public ushort LRPan; //0-1024
|
||||
public ushort FBPan; //0-1024, front back
|
||||
public bool Rand2;
|
||||
|
||||
public ushort Fin;
|
||||
public ushort FOut;
|
||||
public ushort dly;
|
||||
public bool Rand3; //what
|
||||
public ushort Loop;
|
||||
|
||||
public bool Loop2; //might be count then decider here
|
||||
public ushort Quant; //but then what is this?
|
||||
public ushort Prob; //probably random probability, not sure of range (0-16?)
|
||||
public short pitchL; //pitch offsets
|
||||
public short pitchR;
|
||||
|
||||
public bool Fast;
|
||||
public ushort GroupID;
|
||||
public bool Stereo;
|
||||
public string Filename;
|
||||
}
|
||||
}
|
72
server/tso.files/HIT/HITConstants.cs
Executable file
72
server/tso.files/HIT/HITConstants.cs
Executable file
|
@ -0,0 +1,72 @@
|
|||
namespace FSO.Files.HIT
|
||||
{
|
||||
public enum HITArgs
|
||||
{
|
||||
kArgsNormal = 0,
|
||||
kArgsVolPan = 1,
|
||||
kArgsIdVolPan = 2,
|
||||
kArgsXYZ = 3
|
||||
}
|
||||
|
||||
public enum HITControlGroups
|
||||
{
|
||||
kGroupSFX = 1,
|
||||
kGroupMusic = 2,
|
||||
kGroupVox = 3
|
||||
}
|
||||
|
||||
public enum HITDuckingPriorities
|
||||
{
|
||||
duckpri_unknown1 = 32,
|
||||
duckpri_unknown2 = 5000,
|
||||
duckpri_always = 0x0,
|
||||
duckpri_low = 0x1,
|
||||
duckpri_normal = 0x14,
|
||||
duckpri_high = 0x1e,
|
||||
duckpri_higher = 0x28,
|
||||
duckpri_evenhigher = 0x32,
|
||||
duckpri_never = 0x64
|
||||
}
|
||||
|
||||
public enum HITEvents
|
||||
{
|
||||
kSoundobPlay = 1,
|
||||
kSoundobStop = 2,
|
||||
kSoundobKill = 3,
|
||||
kSoundobUpdate = 4,
|
||||
kSoundobSetVolume = 5,
|
||||
kSoundobSetPitch = 6,
|
||||
kSoundobSetPan = 7,
|
||||
kSoundobSetPosition = 8,
|
||||
kSoundobSetFxType = 9,
|
||||
kSoundobSetFxLevel = 10,
|
||||
kSoundobPause = 11,
|
||||
kSoundobUnpause = 12,
|
||||
kSoundobLoad = 13,
|
||||
kSoundobUnload = 14,
|
||||
kSoundobCache = 15,
|
||||
kSoundobUncache = 16,
|
||||
kSoundobCancelNote = 19,
|
||||
kKillAll = 20,
|
||||
kPause = 21,
|
||||
kUnpause = 22,
|
||||
kKillInstance = 23,
|
||||
kTurnOnTV = 30,
|
||||
kTurnOffTV = 31,
|
||||
kUpdateSourceVolPan = 32,
|
||||
kSetMusicMode = 36,
|
||||
kPlayPiano = 43,
|
||||
debugeventson = 44,
|
||||
debugeventsoff = 45,
|
||||
debugsampleson = 46,
|
||||
debugsamplesoff = 47,
|
||||
debugtrackson = 48,
|
||||
debugtracksoff = 49
|
||||
}
|
||||
|
||||
public enum HITPerson
|
||||
{
|
||||
Instance = 0x0,
|
||||
Gender = 0x1
|
||||
}
|
||||
}
|
101
server/tso.files/HIT/HITFile.cs
Executable file
101
server/tso.files/HIT/HITFile.cs
Executable file
|
@ -0,0 +1,101 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace FSO.Files.HIT
|
||||
{
|
||||
/// <summary>
|
||||
/// HIT files contain the bytecode to be executed when plaing track events.
|
||||
/// </summary>
|
||||
public class HITFile
|
||||
{
|
||||
public string MagicNumber;
|
||||
public uint MajorVersion;
|
||||
public uint MinorVersion;
|
||||
public byte[] Data;
|
||||
public Dictionary<uint, uint> EntryPointByTrackID;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new track.
|
||||
/// </summary>
|
||||
/// <param name="Filedata">The data to create the track from.</param>
|
||||
public HITFile(byte[] Filedata)
|
||||
{
|
||||
ReadFile(new MemoryStream(Filedata));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new track.
|
||||
/// </summary>
|
||||
/// <param name="Filedata">The path to the data to create the track from.</param>
|
||||
public HITFile(string Filepath)
|
||||
{
|
||||
ReadFile(File.Open(Filepath, FileMode.Open, FileAccess.Read, FileShare.Read));
|
||||
}
|
||||
|
||||
private void ReadFile(Stream data)
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(data);
|
||||
|
||||
MagicNumber = new string(Reader.ReadChars(4));
|
||||
MajorVersion = Reader.ReadUInt32();
|
||||
MinorVersion = Reader.ReadUInt32();
|
||||
var signature = new string(Reader.ReadChars(4));
|
||||
|
||||
var tableLoc = FindBytePattern(Reader.BaseStream, new byte[] { (byte)'E', (byte)'N', (byte)'T', (byte)'P' });
|
||||
if (tableLoc != -1)
|
||||
{
|
||||
Reader.BaseStream.Seek(tableLoc, SeekOrigin.Begin);
|
||||
EntryPointByTrackID = new Dictionary<uint, uint>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
||||
var EndTest = ASCIIEncoding.ASCII.GetString(Reader.ReadBytes(4)); //can be invalid chars
|
||||
if (EndTest.Equals("EENT", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Reader.BaseStream.Position -= 4; //go back to read it as a table entry
|
||||
var track = Reader.ReadUInt32();
|
||||
var address = Reader.ReadUInt32();
|
||||
EntryPointByTrackID.Add(track, address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reader.BaseStream.Seek(0, SeekOrigin.Begin);
|
||||
this.Data = Reader.ReadBytes((int)Reader.BaseStream.Length);
|
||||
|
||||
Reader.Close();
|
||||
}
|
||||
|
||||
public int FindBytePattern(Stream stream, byte[] pattern)
|
||||
{ //a simple pattern matcher
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
for (int i = 0; i < stream.Length; i++)
|
||||
{
|
||||
var b = stream.ReadByte();
|
||||
if (b == pattern[0])
|
||||
{
|
||||
bool match = true;
|
||||
for (int j = 1; j < pattern.Length; j++)
|
||||
{
|
||||
var b2 = stream.ReadByte();
|
||||
if (b2 != pattern[j])
|
||||
{
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) return (int)stream.Position;
|
||||
else stream.Seek(i+1, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
return -1; //no match
|
||||
}
|
||||
}
|
||||
}
|
50
server/tso.files/HIT/HSM.cs
Executable file
50
server/tso.files/HIT/HSM.cs
Executable file
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace FSO.Files.HIT
|
||||
{
|
||||
public class HSM
|
||||
{
|
||||
/// <summary>
|
||||
/// HSM is a plaintext format that names various HIT constants including subroutine locations.
|
||||
/// </summary>
|
||||
///
|
||||
public Dictionary<string, int> Constants;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new hsm file.
|
||||
/// </summary>
|
||||
/// <param name="Filedata">The data to create the hsm from.</param>
|
||||
public HSM(byte[] Filedata)
|
||||
{
|
||||
ReadFile(new MemoryStream(Filedata));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new hsm file.
|
||||
/// </summary>
|
||||
/// <param name="Filedata">The path to the data to create the hsm from.</param>
|
||||
public HSM(string Filepath)
|
||||
{
|
||||
ReadFile(File.Open(Filepath, FileMode.Open, FileAccess.Read, FileShare.Read));
|
||||
}
|
||||
|
||||
private void ReadFile(Stream stream)
|
||||
{
|
||||
var io = new StreamReader(stream);
|
||||
Constants = new Dictionary<string, int>();
|
||||
|
||||
while (!io.EndOfStream)
|
||||
{
|
||||
string line = io.ReadLine();
|
||||
string[] Values = line.Split(' ');
|
||||
|
||||
var name = Values[0].ToLowerInvariant();
|
||||
if (!Constants.ContainsKey(name)) Constants.Add(name, Convert.ToInt32(Values[1])); //the repeats are just labels for locations (usually called gotit)
|
||||
}
|
||||
|
||||
io.Close();
|
||||
}
|
||||
}
|
||||
}
|
103
server/tso.files/HIT/Hitlist.cs
Executable file
103
server/tso.files/HIT/Hitlist.cs
Executable file
|
@ -0,0 +1,103 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace FSO.Files.HIT
|
||||
{
|
||||
/// <summary>
|
||||
/// HLS refers to two binary formats that both define a list of IDs, known as a hitlist.
|
||||
/// One format is a Pascal string with a 4-byte, little-endian length, representing a
|
||||
/// comma-seperated list of decimal values, or decimal ranges (e.g. "1025-1035"), succeeded
|
||||
/// by a single LF newline.
|
||||
/// </summary>
|
||||
public class Hitlist
|
||||
{
|
||||
private uint m_IDCount;
|
||||
public List<uint> IDs; //variable length so it's easier to fill with ranges
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new hitlist.
|
||||
/// </summary>
|
||||
/// <param name="Filedata">The data to create the hitlist from.</param>
|
||||
public Hitlist(byte[] Filedata)
|
||||
{
|
||||
Read(new MemoryStream(Filedata));
|
||||
}
|
||||
|
||||
private void Read(Stream data)
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(data);
|
||||
|
||||
IDs = new List<uint>();
|
||||
var VerOrCount = Reader.ReadUInt32();
|
||||
|
||||
try
|
||||
{
|
||||
if (VerOrCount == 1) //binary format, no hitlist is ever going to have length 1... (i hope)
|
||||
{
|
||||
m_IDCount = Reader.ReadUInt32();
|
||||
|
||||
for (int i = 0; i < m_IDCount; i++)
|
||||
IDs.Add(Reader.ReadUInt32());
|
||||
|
||||
Reader.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
var str = new string(Reader.ReadChars((int)VerOrCount));
|
||||
Populate(str);
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
Reader.BaseStream.Seek(4, SeekOrigin.Begin); //attempt 3rd mystery format, count+int32
|
||||
for (int i = 0; i < VerOrCount; i++)
|
||||
IDs.Add(Reader.ReadUInt32());
|
||||
}
|
||||
}
|
||||
|
||||
public Hitlist()
|
||||
{
|
||||
IDs = new List<uint>();
|
||||
}
|
||||
|
||||
public void Populate(string str)
|
||||
{
|
||||
var commaSplit = str.Split(',');
|
||||
for (int i = 0; i < commaSplit.Length; i++)
|
||||
{
|
||||
var dashSplit = commaSplit[i].Split('-');
|
||||
if (dashSplit.Length > 1)
|
||||
{ //range, parse two values and fill in the gap
|
||||
var min = Convert.ToUInt32(dashSplit[0]);
|
||||
var max = Convert.ToUInt32(dashSplit[1]);
|
||||
for (uint j = min; j <= max; j++)
|
||||
{
|
||||
IDs.Add(j);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //literal entry, add to list
|
||||
IDs.Add(Convert.ToUInt32(commaSplit[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Hitlist HitlistFromString(string str)
|
||||
{
|
||||
var result = new Hitlist();
|
||||
result.Populate(str);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new hitlist.
|
||||
/// </summary>
|
||||
/// <param name="Filepath">The path to the hitlist to read.</param>
|
||||
public Hitlist(string Filepath)
|
||||
{
|
||||
Read(File.Open(Filepath, FileMode.Open, FileAccess.Read, FileShare.Read));
|
||||
}
|
||||
}
|
||||
}
|
236
server/tso.files/HIT/Hot.cs
Executable file
236
server/tso.files/HIT/Hot.cs
Executable file
|
@ -0,0 +1,236 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace FSO.Files.HIT
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration constants.
|
||||
/// </summary>
|
||||
public struct EventMappingEquate
|
||||
{
|
||||
public string Label;
|
||||
public int Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This section assigns a sound ID to a number of subroutines
|
||||
/// (exported or not) in the corresponding HIT file.
|
||||
/// </summary>
|
||||
public struct TrackData
|
||||
{
|
||||
//The syntax for TrackData is A = B, where A is the sound's File ID
|
||||
//and B is the offset to the subroutine in the accompanying HIT file.
|
||||
public long FileID;
|
||||
public int SubRoutineOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HOT (short for HIT Options Table) is an ini format that defines
|
||||
/// enumeration constants and track data for HIT binary files.
|
||||
/// </summary>
|
||||
public class Hot
|
||||
{
|
||||
private int m_Version;
|
||||
private int m_LoadPriority;
|
||||
private List<EventMappingEquate> m_EventMappingEquations = new List<EventMappingEquate>();
|
||||
private List<TrackData> m_TrackDataList = new List<TrackData>();
|
||||
public Dictionary<uint, Track> Tracks = new Dictionary<uint, Track>();
|
||||
public Dictionary<uint, Patch> Patches = new Dictionary<uint, Patch>();
|
||||
public Dictionary<uint, Hitlist> Hitlists = new Dictionary<uint, Hitlist>();
|
||||
public Dictionary<uint, uint> TrackData = new Dictionary<uint, uint>();
|
||||
public Dictionary<string, EVTEntry> Events = new Dictionary<string, EVTEntry>();
|
||||
private Dictionary<string, int> EventMappingEquate = new Dictionary<string, int>();
|
||||
public HSM AsmNames;
|
||||
|
||||
/// <summary>
|
||||
/// Gets this Hot instance's list of EventMappingEquate instances.
|
||||
/// </summary>
|
||||
public List<EventMappingEquate> EventMappingEquations
|
||||
{
|
||||
get { return m_EventMappingEquations; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets this Hot instance's list of TrackData instances.
|
||||
/// </summary>
|
||||
public List<TrackData> TrackDataList
|
||||
{
|
||||
get { return m_TrackDataList; }
|
||||
}
|
||||
|
||||
public Hot(byte[] FileData)
|
||||
{
|
||||
LoadFrom(FileData);
|
||||
}
|
||||
|
||||
public void LoadFrom(byte[] FileData)
|
||||
{
|
||||
StreamReader Reader = new StreamReader(new MemoryStream(FileData));
|
||||
HotReadMode ActiveState = HotReadMode.None;
|
||||
|
||||
while (!Reader.EndOfStream)
|
||||
{
|
||||
string CurrentLine = Reader.ReadLine().Trim().Replace("\r\n", "");
|
||||
|
||||
switch (CurrentLine)
|
||||
{
|
||||
case "[EventMappingEquate]":
|
||||
ActiveState = HotReadMode.EventMappingEquate;
|
||||
break;
|
||||
case "[Options]":
|
||||
ActiveState = HotReadMode.Options;
|
||||
break;
|
||||
case "[TrackData]":
|
||||
ActiveState = HotReadMode.TrackData;
|
||||
break;
|
||||
case "[EventMapping]":
|
||||
//equivalent to .evt file
|
||||
//(name)=(eventMappingEquate as event type),(trackid),0,0,0,0
|
||||
ActiveState = HotReadMode.EventMapping;
|
||||
break;
|
||||
case "[Track]":
|
||||
//equivalent to a lot of track files
|
||||
//(trackid)=0,(subroutine),(volume),(arguments),(duckingPriority),(controlGroup),(soundPressureLevel),@(hitlistID),(patchID)
|
||||
//patch id is usually 0, in favor of single item hitlists
|
||||
ActiveState = HotReadMode.Track;
|
||||
break;
|
||||
case "[Patch]":
|
||||
//(patchid)=(name),(filenameInQuotes),(looped),(piano),0,0,0
|
||||
ActiveState = HotReadMode.Patch;
|
||||
break;
|
||||
case "[GlobalHitList]":
|
||||
//(hitlistid)=(hitlistString)
|
||||
//note: many hitlists contain just one patch
|
||||
ActiveState = HotReadMode.GlobalHitList;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!CurrentLine.Contains("["))
|
||||
{
|
||||
if (!CurrentLine.Contains("]"))
|
||||
{
|
||||
if (CurrentLine != "")
|
||||
{
|
||||
string[] Params = CurrentLine.Split("=".ToCharArray());
|
||||
//EventMappingEquate fields look like: kSndobPlay=1
|
||||
switch (ActiveState)
|
||||
{
|
||||
case HotReadMode.EventMappingEquate:
|
||||
EventMappingEquate EMappingEquate = new EventMappingEquate();
|
||||
EMappingEquate.Label = Params[0];
|
||||
EMappingEquate.Value = int.Parse(Params[1]);
|
||||
EventMappingEquate[EMappingEquate.Label] = EMappingEquate.Value;
|
||||
break;
|
||||
//Options fields look like: Version=1
|
||||
case HotReadMode.Options:
|
||||
switch (Params[0])
|
||||
{
|
||||
case "Version":
|
||||
m_Version = int.Parse(Params[1]);
|
||||
break;
|
||||
case "LoadPriority":
|
||||
m_LoadPriority = int.Parse(Params[1]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
//TrackData fields look like: 0xb0f4=0x10
|
||||
case HotReadMode.TrackData:
|
||||
TrackData.Add(Convert.ToUInt32(Params[0], 16), Convert.ToUInt32(Params[1], 16));
|
||||
break;
|
||||
case HotReadMode.EventMapping:
|
||||
var commaSplit = Params[1].Split(',');
|
||||
Events[Params[0].ToLowerInvariant()] = new EVTEntry
|
||||
{
|
||||
Name = Params[0].ToLowerInvariant(),
|
||||
EventType = (uint)ParseEME(commaSplit[0]),
|
||||
TrackID = (commaSplit.Length>1)?(uint)ParseEME(commaSplit[1]):0
|
||||
};
|
||||
break;
|
||||
case HotReadMode.Track:
|
||||
var tid = uint.Parse(Params[0]);
|
||||
var tcSplit = Params[1].Split(',');
|
||||
|
||||
var trk = new Track()
|
||||
{
|
||||
SubroutineID = 0,//(uint)HSMConst(tcSplit[1]),
|
||||
Volume = (uint)ParseEME(tcSplit[2]),
|
||||
ArgType = (HITArgs)ParseEME(tcSplit[3]),
|
||||
DuckingPriority = (HITDuckingPriorities)ParseEME(tcSplit[4]),
|
||||
ControlGroup = (HITControlGroups)ParseEME(tcSplit[5]),
|
||||
HitlistID = (uint)HSMConst(tcSplit[7].Substring(1)), //cut out @
|
||||
SoundID = (uint)ParseEME(tcSplit[8])
|
||||
};
|
||||
|
||||
if (trk.HitlistID != 0 && TrackData.ContainsKey(trk.HitlistID)) trk.SubroutineID = TrackData[trk.HitlistID];
|
||||
if (trk.SoundID != 0 && TrackData.ContainsKey(trk.SoundID)) trk.SubroutineID = TrackData[trk.SoundID];
|
||||
|
||||
Tracks[tid] = trk;
|
||||
break;
|
||||
case HotReadMode.Patch:
|
||||
var pid = uint.Parse(Params[0]);
|
||||
var patch = new Patch(Params[1]);
|
||||
Patches[pid] = patch;
|
||||
break;
|
||||
case HotReadMode.GlobalHitList:
|
||||
var hid = uint.Parse(Params[0]);
|
||||
try
|
||||
{
|
||||
var hitlist = Hitlist.HitlistFromString(Params[1]);
|
||||
Hitlists[hid] = hitlist;
|
||||
} catch (Exception)
|
||||
{
|
||||
/*
|
||||
* todo: saxophone seems to reference an hsm.
|
||||
* 20016=sulsaxj_way_aa
|
||||
* 20017=sulsaxk_way_solo
|
||||
* 20018=sulsaxl_way_fin
|
||||
* these labels are in the hsm but they have a different case...
|
||||
*/
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int HSMConst(string input)
|
||||
{
|
||||
int result = 0;
|
||||
AsmNames?.Constants?.TryGetValue(input, out result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private int ParseEME(string eme)
|
||||
{
|
||||
int result = 0;
|
||||
if (int.TryParse(eme, out result)) return result;
|
||||
EventMappingEquate.TryGetValue(eme, out result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Hot(string Filepath) : this(File.ReadAllBytes(Filepath))
|
||||
{
|
||||
}
|
||||
|
||||
public Hot(string Filepath, HSM myAsm)
|
||||
{
|
||||
AsmNames = myAsm;
|
||||
LoadFrom(File.ReadAllBytes(Filepath));
|
||||
}
|
||||
}
|
||||
|
||||
public enum HotReadMode
|
||||
{
|
||||
None,
|
||||
EventMappingEquate,
|
||||
Options,
|
||||
TrackData,
|
||||
EventMapping,
|
||||
Track,
|
||||
Patch,
|
||||
GlobalHitList
|
||||
}
|
||||
}
|
28
server/tso.files/HIT/Patch.cs
Executable file
28
server/tso.files/HIT/Patch.cs
Executable file
|
@ -0,0 +1,28 @@
|
|||
namespace FSO.Files.HIT
|
||||
{
|
||||
public class Patch
|
||||
{
|
||||
public string Name;
|
||||
public string Filename;
|
||||
public bool Looped;
|
||||
public bool Piano;
|
||||
|
||||
public uint FileID; //patches are stubbed out in TSO.
|
||||
public bool TSO;
|
||||
|
||||
public Patch(uint id)
|
||||
{
|
||||
FileID = id;
|
||||
TSO = true;
|
||||
}
|
||||
|
||||
public Patch(string patchString)
|
||||
{
|
||||
var elems = patchString.Split(',');
|
||||
if (elems.Length > 1) Name = elems[1];
|
||||
if (elems.Length > 2) Filename = elems[2].Substring(1, elems[2].Length-2).Replace('\\', '/');
|
||||
if (elems.Length > 3) Looped = elems[3] != "0";
|
||||
if (elems.Length > 4) Piano = elems[4] != "0";
|
||||
}
|
||||
}
|
||||
}
|
91
server/tso.files/HIT/TLO.cs
Executable file
91
server/tso.files/HIT/TLO.cs
Executable file
|
@ -0,0 +1,91 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
//ok, why do we have support for this? it's only used by hitlab and only the hitlab test files have them...
|
||||
|
||||
namespace FSO.Files.HIT
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a TLO file.
|
||||
/// TLO (short for Track Logic according to hitlab.ini) is a format
|
||||
/// used solely for Hitlab. Integers are little-endian.
|
||||
/// </summary>
|
||||
public class TLO
|
||||
{
|
||||
private uint m_Count;
|
||||
public List<TLOSection> Sections = new List<TLOSection>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new tracklogic instance.
|
||||
/// </summary>
|
||||
/// <param name="Filedata">The data to create the tracklogic instance from.</param>
|
||||
public TLO(byte[] Filedata)
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(new MemoryStream(Filedata));
|
||||
|
||||
Reader.ReadBytes(4); //Reserved.
|
||||
m_Count = Reader.ReadUInt32();
|
||||
|
||||
for (int i = 0; i < m_Count; i++)
|
||||
{
|
||||
TLOSection Section = new TLOSection();
|
||||
Section.Name = new string(Reader.ReadChars(Reader.ReadInt32()));
|
||||
Section.GroupID1 = Reader.ReadUInt32();
|
||||
Section.FileID1 = Reader.ReadUInt32();
|
||||
Section.GroupID2 = Reader.ReadUInt32();
|
||||
Section.FileID1 = Reader.ReadUInt32();
|
||||
Section.TypeID = Reader.ReadUInt32();
|
||||
Section.GroupID3 = Reader.ReadUInt32();
|
||||
Section.FileID3 = Reader.ReadUInt32();
|
||||
|
||||
Sections.Add(Section);
|
||||
}
|
||||
|
||||
Reader.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new tracklogic instance.
|
||||
/// </summary>
|
||||
/// <param name="Filepath">The path to the tracklogic file to read.</param>
|
||||
public TLO(string Filepath)
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(File.Open(Filepath, FileMode.Open, FileAccess.Read, FileShare.Read));
|
||||
|
||||
Reader.ReadBytes(4); //Reserved.
|
||||
m_Count = Reader.ReadUInt32();
|
||||
|
||||
for (int i = 0; i < m_Count; i++)
|
||||
{
|
||||
TLOSection Section = new TLOSection();
|
||||
Section.Name = new string(Reader.ReadChars(Reader.ReadInt32()));
|
||||
Section.GroupID1 = Reader.ReadUInt32();
|
||||
Section.FileID1 = Reader.ReadUInt32();
|
||||
Section.GroupID2 = Reader.ReadUInt32();
|
||||
Section.FileID1 = Reader.ReadUInt32();
|
||||
Section.TypeID = Reader.ReadUInt32();
|
||||
Section.GroupID3 = Reader.ReadUInt32();
|
||||
Section.FileID3 = Reader.ReadUInt32();
|
||||
|
||||
Sections.Add(Section);
|
||||
}
|
||||
|
||||
Reader.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a section in a tracklogic file.
|
||||
/// </summary>
|
||||
public class TLOSection
|
||||
{
|
||||
public string Name;
|
||||
public uint GroupID1;
|
||||
public uint FileID1;
|
||||
public uint GroupID2;
|
||||
public uint FileID2;
|
||||
public uint TypeID;
|
||||
public uint GroupID3;
|
||||
public uint FileID3;
|
||||
}
|
||||
}
|
111
server/tso.files/HIT/Track.cs
Executable file
111
server/tso.files/HIT/Track.cs
Executable file
|
@ -0,0 +1,111 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace FSO.Files.HIT
|
||||
{
|
||||
/// <summary>
|
||||
/// TRK is a CSV format that defines a HIT track.
|
||||
/// </summary>
|
||||
public class Track
|
||||
{
|
||||
private bool TWODKT = false; //Optional encoding as Pascal string, typical Maxis...
|
||||
public string MagicNumber;
|
||||
public uint Version;
|
||||
public string TrackName;
|
||||
public uint SoundID;
|
||||
public uint TrackID;
|
||||
public HITArgs ArgType;
|
||||
public HITControlGroups ControlGroup;
|
||||
public HITDuckingPriorities DuckingPriority;
|
||||
public uint Looped;
|
||||
public uint Volume;
|
||||
|
||||
public bool LoopDefined = false;
|
||||
|
||||
//ts1
|
||||
public uint SubroutineID;
|
||||
public uint HitlistID;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new track.
|
||||
/// </summary>
|
||||
/// <param name="Filedata">The data to create the track from.</param>
|
||||
public Track(byte[] Filedata)
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(new MemoryStream(Filedata));
|
||||
|
||||
MagicNumber = new string(Reader.ReadChars(4));
|
||||
|
||||
if(MagicNumber == "2DKT")
|
||||
TWODKT = true;
|
||||
|
||||
int CurrentVal = 8;
|
||||
string data;
|
||||
|
||||
if(!TWODKT)
|
||||
data = new string(Reader.ReadChars(Filedata.Length));
|
||||
else
|
||||
data = new string(Reader.ReadChars(Reader.ReadInt32()));
|
||||
string[] Values = data.Split(',');
|
||||
|
||||
//MagicNumber = Values[0];
|
||||
Version = ParseHexString(Values[1]);
|
||||
TrackName = Values[2];
|
||||
SoundID = ParseHexString(Values[3]);
|
||||
TrackID = ParseHexString(Values[4]);
|
||||
if (Values[5] != "\r\n" && Values[5] != "ETKD" && Values[5] != "") //some tracks terminate here...
|
||||
{
|
||||
ArgType = (HITArgs)ParseHexString(Values[5]);
|
||||
ControlGroup = (HITControlGroups)ParseHexString(Values[7]);
|
||||
|
||||
if (Version == 2)
|
||||
CurrentVal++;
|
||||
|
||||
CurrentVal += 3; //skip two unknowns and clsid
|
||||
|
||||
DuckingPriority = (HITDuckingPriorities)ParseHexString(Values[CurrentVal]);
|
||||
CurrentVal++;
|
||||
Looped = ParseHexString(Values[CurrentVal]);
|
||||
LoopDefined = true;
|
||||
CurrentVal++;
|
||||
Volume = ParseHexString(Values[CurrentVal]);
|
||||
}
|
||||
|
||||
Reader.Close();
|
||||
}
|
||||
|
||||
public Track() { }
|
||||
|
||||
private uint ParseHexString(string input)
|
||||
{
|
||||
bool IsHex = false;
|
||||
|
||||
if (input == "") return 0;
|
||||
if (input.StartsWith("0x"))
|
||||
{
|
||||
input = input.Substring(2);
|
||||
IsHex = true;
|
||||
}
|
||||
else if (input.Contains("a") || input.Contains("b") || input.Contains("c") || input.Contains("d") || input.Contains("e") || input.Contains("f"))
|
||||
{
|
||||
IsHex = true;
|
||||
}
|
||||
|
||||
if (IsHex)
|
||||
{
|
||||
return Convert.ToUInt32(input, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
return Convert.ToUInt32(input);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue