Files
PCK-Studio/PckStudio.Core/IO/PckAudio/PckAudioFileReader.cs

140 lines
5.0 KiB
C#

using OMI;
using OMI.Workers;
using PckStudio.Core.FileFormats;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace PckStudio.Core.IO.PckAudio
{
public class InvalidAudioPckException : Exception
{
public InvalidAudioPckException(string message) : base(message)
{ }
}
public class PckAudioFileReader : IDataFormatReader<PckAudioFile>, IDataFormatReader
{
private PckAudioFile _file;
private Endianness _endianness;
private List<string> LUT = new List<string>();
private List<PckAudioFile.AudioCategory.EAudioType> _OriginalAudioTypeOrder = new List<PckAudioFile.AudioCategory.EAudioType>();
public PckAudioFileReader(Endianness endianness)
{
_endianness = endianness;
}
public PckAudioFile FromFile(string filename)
{
if(File.Exists(filename))
{
PckAudioFile file;
using(FileStream fs = File.OpenRead(filename))
{
file = FromStream(fs);
}
return file;
}
throw new FileNotFoundException(filename);
}
public PckAudioFile FromStream(Stream stream)
{
using (var reader = new EndiannessAwareBinaryReader(stream,
_endianness == Endianness.BigEndian
? Encoding.BigEndianUnicode
: Encoding.Unicode,
leaveOpen: true, _endianness))
{
int pck_type = reader.ReadInt32();
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();
ReadLookUpTable(reader);
ReadCategories(reader);
ReadCategorySongs(reader);
}
return _file;
}
private void ReadLookUpTable(EndiannessAwareBinaryReader reader)
{
int count = reader.ReadInt32();
LUT = new List<string>(count);
for (int i = 0; i < count; i++)
{
int index = reader.ReadInt32();
string value = ReadString(reader);
LUT.Insert(index, value);
}
}
private void ReadCategories(EndiannessAwareBinaryReader reader)
{
int categoryEntryCount = reader.ReadInt32();
for (; 0 < categoryEntryCount; categoryEntryCount--)
{
var parameterType = (PckAudioFile.AudioCategory.EAudioParameterType)reader.ReadInt32();
var audioType = (PckAudioFile.AudioCategory.EAudioType)reader.ReadInt32();
string name = ReadString(reader);
// AddCategory puts the file's categories out of order and causes some songs to be put in the wrong categories
// This is my simple fix for the issue.
_OriginalAudioTypeOrder.Add(audioType);
_file.AddCategory(parameterType, audioType, name);
}
}
private void ReadCategorySongs(EndiannessAwareBinaryReader reader)
{
List<string> credits = new List<string>();
List<string> creditIds = new List<string>();
foreach (PckAudioFile.AudioCategory.EAudioType c in _OriginalAudioTypeOrder)
{
int audioCount = reader.ReadInt32();
for (; 0 < audioCount; audioCount--)
{
string key = LUT[reader.ReadInt32()];
string value = ReadString(reader);
switch (key)
{
case "CUENAME":
_file.GetCategory(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 ((string str, string id) credit in credits.Zip(creditIds, (str, id) => (str, id)))
{
_file.SetCredit(credit.id, credit.str);
}
}
private string ReadString(EndiannessAwareBinaryReader reader)
{
int len = reader.ReadInt32();
string s = reader.ReadString(len);
reader.ReadInt32(); // padding
return s;
}
object IDataFormatReader.FromStream(Stream stream) => FromStream(stream);
object IDataFormatReader.FromFile(string filename) => FromFile(filename);
}
}