mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/PCK-Studio.git
synced 2026-09-27 07:50:44 +00:00
Add AudioPCKFile
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user