SonicAudioTools/Source/SonicAudioLib/Archive/CriAfs2Archive.cs
2016-12-25 21:43:22 +03:00

195 lines
6.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using SonicAudioLib.IO;
using SonicAudioLib.Module;
namespace SonicAudioLib.Archive
{
public class CriAfs2Entry : EntryBase
{
public ushort CueIndex { get; set; }
}
public class CriAfs2Archive : ArchiveBase<CriAfs2Entry>
{
public uint Align { get; set; }
public uint CueIndexFieldLength { get; set; }
public uint PositionFieldLength { get; set; }
public override void Read(Stream source)
{
if (EndianStream.ReadCString(source, 4) != "AFS2")
{
throw new Exception("No AFS2 signature found.");
}
uint information = EndianStream.ReadUInt32(source);
uint type = information & 0xFF;
if (type != 1)
{
throw new Exception($"Invalid AFS2 type ({type}). Please report the error with the AWB file.");
}
CueIndexFieldLength = (information & 0x00FF0000) >> 16;
PositionFieldLength = (information & 0x0000FF00) >> 8;
ushort entryCount = (ushort)EndianStream.ReadUInt32(source);
Align = EndianStream.ReadUInt32(source);
CriAfs2Entry previousEntry = null;
for (uint i = 0; i < entryCount; i++)
{
CriAfs2Entry afs2Entry = new CriAfs2Entry();
long cueIndexPosition = 16 + (i * CueIndexFieldLength);
source.Seek(cueIndexPosition, SeekOrigin.Begin);
switch (CueIndexFieldLength)
{
case 2:
afs2Entry.CueIndex = EndianStream.ReadUInt16(source);
break;
default:
throw new Exception($"Unknown CueIndexFieldLength ({CueIndexFieldLength}). Please report the error with the AWB file.");
}
long positionPosition = 16 + (entryCount * CueIndexFieldLength) + (i * PositionFieldLength);
source.Seek(positionPosition, SeekOrigin.Begin);
switch (PositionFieldLength)
{
case 2:
afs2Entry.Position = EndianStream.ReadUInt16(source);
break;
case 4:
afs2Entry.Position = EndianStream.ReadUInt32(source);
break;
default:
throw new Exception($"Unknown PositionFieldLength ({PositionFieldLength}). Please report the error with the AWB file.");
}
if (previousEntry != null)
{
previousEntry.Length = afs2Entry.Position - previousEntry.Position;
}
while ((afs2Entry.Position % Align) != 0)
{
afs2Entry.Position++;
}
if (i == entryCount - 1)
{
switch (PositionFieldLength)
{
case 2:
afs2Entry.Length = EndianStream.ReadUInt16(source) - afs2Entry.Position;
break;
case 4:
afs2Entry.Length = EndianStream.ReadUInt32(source) - afs2Entry.Position;
break;
}
}
entries.Add(afs2Entry);
previousEntry = afs2Entry;
}
}
public override void Write(Stream destination)
{
uint headerLength = (uint)(16 + (entries.Count * CueIndexFieldLength) + (entries.Count * PositionFieldLength) + PositionFieldLength);
EndianStream.WriteCString(destination, "AFS2", 4);
EndianStream.WriteUInt32(destination, 1 | (CueIndexFieldLength << 16) | (PositionFieldLength << 8));
EndianStream.WriteUInt32(destination, (ushort)entries.Count);
EndianStream.WriteUInt32(destination, 1);
// FIXME: Alignment support
VldPool vldPool = new VldPool(1);
foreach (CriAfs2Entry afs2Entry in entries)
{
switch (CueIndexFieldLength)
{
case 2:
EndianStream.WriteUInt16(destination, (ushort)afs2Entry.CueIndex);
break;
default:
throw new Exception($"Unknown CueIndexFieldLength ({CueIndexFieldLength}). Please set a valid length.");
}
}
foreach (CriAfs2Entry afs2Entry in entries)
{
uint entryPosition = (uint)(headerLength + vldPool.Put(afs2Entry.FilePath));
switch (PositionFieldLength)
{
case 2:
EndianStream.WriteUInt16(destination, (ushort)entryPosition);
break;
case 4:
EndianStream.WriteUInt32(destination, entryPosition);
break;
default:
throw new Exception($"Unknown PositionFieldLength ({PositionFieldLength}). Please set a valid length.");
}
afs2Entry.Position = entryPosition;
}
EndianStream.WriteUInt32(destination, (uint)(headerLength + vldPool.Length));
vldPool.Write(destination);
vldPool.Clear();
}
public CriAfs2Entry GetByCueIndex(uint cueIndex)
{
return entries.Single(e => e.CueIndex == cueIndex);
}
public override long CalculateLength()
{
long length = 16 + (entries.Count * CueIndexFieldLength) + (entries.Count * PositionFieldLength) + PositionFieldLength;
foreach (CriAfs2Entry afs2Entry in entries)
{
while ((length % Align) != 0)
{
length++;
}
length += afs2Entry.Length;
}
return length;
}
public void Order()
{
entries = entries.OrderBy(entry => entry.CueIndex).ToList();
}
public CriAfs2Archive()
{
Align = 32;
CueIndexFieldLength = 2;
PositionFieldLength = 4;
}
}
}