Add AudioPCKFile

This commit is contained in:
miku-666
2022-08-05 20:38:21 +02:00
parent d99409ee74
commit 1302ed5a0e
5 changed files with 572 additions and 269 deletions
@@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PckStudio.Classes.FileTypes
{
public class PCKAudioFile
{
public class InvalidCategoryException : Exception
{
public InvalidCategoryException(string message) : base(message)
{ }
}
public readonly int type = 1;
public AudioCategory[] Categories { get; } = new AudioCategory[8];
public Dictionary<string, string> Credits { get; } = new Dictionary<string, string>();
public class AudioCategory
{
public enum EAudioType : int
{
Overworld,
Nether,
End,
Creative,
Menu,
Battle,
Tumble,
Glide,
Unused,
}
public enum EAudioParameterType : int
{
unk0, // dimension music
unk1, // unused music ?
}
public string Name { get; set; } = string.Empty;
public EAudioParameterType parameterType { get; }
public EAudioType audioType { get; }
public List<string> SongNames { get; } = new List<string>();
public AudioCategory(EAudioParameterType parameterType, EAudioType audioType)
{
this.parameterType = parameterType;
this.audioType = audioType;
}
public AudioCategory(string name, EAudioParameterType parameterType, EAudioType audioType) : this(parameterType, audioType)
{
Name = name;
}
}
public string[] GetCredits() => Credits.Values.ToArray();
public void AddCredits(params string[] credits)
{
foreach (var credit in credits)
{
AddCredit(credit);
}
}
/// <summary>
/// Applies internal Credits to loc file
/// </summary>
public void ApplyCredits(LOCFile locFile)
{
foreach (var credit in Credits)
{
locFile.SetLocEntry(credit.Key, credit.Value);
}
}
/// <summary>
/// Clears and sets the new supplied <paramref name="credits"/>
/// </summary>
public void SetCredits(params string[] credits)
{
Credits.Clear();
foreach (var credit in credits)
{
AddCredit(credit);
}
}
public bool SetCredit(string creditId, string s)
{
if (!Credits.ContainsKey(creditId))
return false;
Credits[creditId] = s;
return true;
}
public void AddCredit(string credit)
{
Credits.Add($"IDS_CREDIT{(Credits.Count > 1 ? $"_{Credits.Count}" : string.Empty)}", credit);
}
public void AddCreditId(string creditId) => Credits.Add(creditId, string.Empty);
/// <exception cref="InvalidCategoryException"></exception>
public bool HasCategory(AudioCategory.EAudioType category) => GetCategory(category) is AudioCategory;
/// <exception cref="InvalidCategoryException"></exception>
public AudioCategory GetCategory(AudioCategory.EAudioType category)
{
if (category < AudioCategory.EAudioType.Overworld ||
category > AudioCategory.EAudioType.Unused)
throw new InvalidCategoryException(nameof(category));
return Categories[(int)category];
}
/// <exception cref="InvalidCategoryException"></exception>
public bool TryGetCategory(AudioCategory.EAudioType category, out AudioCategory audioCategory)
{
if (GetCategory(category) is AudioCategory a)
{
audioCategory = a;
return true;
}
audioCategory = null;
return false;
}
public int GetCategoryCount()
{
int count = 0;
Array.ForEach(Categories, c => { if (c is not null) count++; });
return count;
}
/// <returns>True when category was created, otherwise false</returns>
/// <exception cref="InvalidCategoryException"></exception>
public bool AddCategory(AudioCategory.EAudioParameterType parameterType, AudioCategory.EAudioType category)
{
if (category < AudioCategory.EAudioType.Overworld ||
category > AudioCategory.EAudioType.Unused)
throw new InvalidCategoryException(nameof(category));
bool exists = HasCategory(category);
if (!exists) Categories[(int)category] = new AudioCategory(parameterType, category);
return !exists;
}
/// <returns>True when category was created, otherwise false</returns>
/// <exception cref="InvalidCategoryException"></exception>
public bool AddCategory(AudioCategory.EAudioType category)
=> AddCategory(AudioCategory.EAudioParameterType.unk0, category);
/// <returns>True when category was removed, otherwise false</returns>
/// <exception cref="InvalidCategoryException"></exception>
public bool RemoveCategory(AudioCategory.EAudioType category)
{
bool exists = HasCategory(category);
if (exists) Categories[(int)category] = null;
return exists;
}
}
}
@@ -0,0 +1,115 @@
using PckStudio.Classes.FileTypes;
using PckStudio.Classes.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PckStudio.Classes.IO.PCK
{
public class InvalidAudioPckException : Exception
{
public InvalidAudioPckException(string message) : base(message)
{ }
}
internal class PCKAudioFileReader : StreamDataReader
{
private PCKAudioFile _file;
private List<string> LUT = new List<string>();
public static PCKAudioFile Read(Stream stream, bool isLittleEndian)
{
return new PCKAudioFileReader(isLittleEndian).ReadFromStream(stream);
}
private PCKAudioFileReader(bool isLittleEndian) : base(isLittleEndian)
{
}
private PCKAudioFile ReadFromStream(Stream stream)
{
int pck_type = ReadInt(stream);
if (pck_type > 0xf00000) // 03 00 00 00 == true
throw new OverflowException(nameof(pck_type));
if (pck_type > 1)
throw new InvalidAudioPckException(nameof(pck_type));
_file = new PCKAudioFile();
ReadLookUpTabel(stream);
ReadCategories(stream);
ReadCategorySongs(stream);
return _file;
}
private void ReadLookUpTabel(Stream stream)
{
int count = ReadInt(stream);
LUT = new List<string>(count);
for (int i = 0; i < count; i++)
{
int index = ReadInt(stream);
LUT.Insert(index, ReadString(stream));
}
}
private void ReadCategories(Stream stream)
{
int categoryEntryCount = ReadInt(stream);
for (; 0 < categoryEntryCount; categoryEntryCount--)
{
var parameterType = (PCKAudioFile.AudioCategory.EAudioParameterType)ReadInt(stream);
var audioType = (PCKAudioFile.AudioCategory.EAudioType)ReadInt(stream);
string name = ReadString(stream);
Console.WriteLine(name);
_file.AddCategory(parameterType, audioType);
}
}
private void ReadCategorySongs(Stream stream)
{
List<string> credits = new List<string>();
List<string> creditIds = new List<string>();
foreach (var c in _file.Categories)
{
if (c is null) continue;
int audioCount = ReadInt(stream);
for (; 0 < audioCount; audioCount--)
{
string key = LUT[ReadInt(stream)];
string value = ReadString(stream);
switch (key)
{
case "CUENAME":
c.SongNames.Add(value);
break;
case "CREDIT":
credits.Add(value);
break;
case "CREDITID":
creditIds.Add(value);
_file.AddCreditId(value);
break;
default:
throw new InvalidDataException(nameof(key));
}
}
};
foreach (var credit in credits.Zip(creditIds, (str, id) => (str, id)))
{
_file.SetCredit(credit.id, credit.str);
}
}
private string ReadString(Stream stream)
{
int len = ReadInt(stream);
string s = ReadString(stream, len, IsUsingLittleEndian ? Encoding.Unicode : Encoding.BigEndianUnicode);
ReadInt(stream); // padding
return s;
}
}
}
@@ -0,0 +1,96 @@
using PckStudio.Classes.FileTypes;
using PckStudio.Classes.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PckStudio.Classes.IO.PCK
{
internal class PCKAudioFileWriter : StreamDataWriter
{
private PCKAudioFile _file;
private static readonly List<string> LUT = new List<string>
{
"CUENAME",
"CREDIT",
"CREDITID"
};
public static void Write(Stream stream, PCKAudioFile file, bool isLittleEndian)
{
new PCKAudioFileWriter(file, isLittleEndian).WriteToStream(stream);
}
private PCKAudioFileWriter(PCKAudioFile file, bool isLittleEndian) : base(isLittleEndian)
{
_file = file;
}
private void WriteToStream(Stream stream)
{
WriteInt(stream, _file.type);
WriteLookUpTable(stream);
WriteCategories(stream);
WriteCategorySongs(stream);
}
private void WriteString(Stream stream, string s)
{
WriteInt(stream, s.Length);
WriteString(stream, s, IsUsingLittleEndian ? Encoding.Unicode : Encoding.BigEndianUnicode);
WriteInt(stream, 0); // padding
}
private void WriteLookUpTable(Stream stream)
{
WriteInt(stream, 3);
for (int i = 0; i < 3; i++)
{
WriteInt(stream, i);
WriteString(stream, LUT[i]);
}
}
private void WriteCategories(Stream stream)
{
WriteInt(stream, _file.GetCategoryCount());
foreach (var category in _file.Categories)
{
if (category is null) continue;
WriteInt(stream, (int)category.parameterType);
WriteInt(stream, (int)category.audioType);
WriteString(stream, category.Name);
}
}
private void WriteCategorySongs(Stream stream)
{
bool addCredit = true;
foreach (var category in _file.Categories)
{
if (category is null) continue;
WriteInt(stream, category.SongNames.Count + (addCredit ? _file.Credits.Count * 2 : 0));
foreach (var name in category.SongNames)
{
WriteInt(stream, LUT.IndexOf("CUENAME"));
WriteString(stream, name);
}
if (addCredit)
{
foreach (var credit in _file.Credits)
{
WriteInt(stream, LUT.IndexOf("CREDIT"));
WriteString(stream, credit.Value);
WriteInt(stream, LUT.IndexOf("CREDITID"));
WriteString(stream, credit.Key);
}
}
addCredit = false;
}
}
}
}