171 lines
5.8 KiB
C#
Raw Normal View History

2016-11-12 19:02:48 +03:00
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
{
2016-12-31 15:56:10 +03:00
public ushort Id { get; set; }
2016-11-12 19:02:48 +03:00
}
public class CriAfs2Archive : ArchiveBase<CriAfs2Entry>
{
public uint Align { get; set; }
2016-12-31 15:56:10 +03:00
public uint IdFieldLength { get; set; }
2016-11-12 19:02:48 +03:00
public uint PositionFieldLength { get; set; }
public override void Read(Stream source)
{
if (EndianStream.ReadCString(source, 4) != "AFS2")
{
throw new InvalidDataException("'AFS2' signature could not be found.");
2016-11-12 19:02:48 +03:00
}
uint information = EndianStream.ReadUInt32(source);
uint type = information & 0xFF;
if (type != 1)
{
throw new InvalidDataException($"Unknown AFS2 type ({type}). Please report this error with the file(s).");
2016-11-12 19:02:48 +03:00
}
IdFieldLength = (information >> 16) & 0xFF;
PositionFieldLength = (information >> 8) & 0xFF;
2016-11-12 19:02:48 +03:00
ushort entryCount = (ushort)EndianStream.ReadUInt32(source);
Align = EndianStream.ReadUInt32(source);
CriAfs2Entry previousEntry = null;
for (uint i = 0; i < entryCount; i++)
{
CriAfs2Entry afs2Entry = new CriAfs2Entry();
2016-12-31 15:56:10 +03:00
long idPosition = 16 + (i * IdFieldLength);
source.Seek(idPosition, SeekOrigin.Begin);
2016-11-12 19:02:48 +03:00
2016-12-31 15:56:10 +03:00
switch (IdFieldLength)
2016-11-12 19:02:48 +03:00
{
case 2:
2016-12-31 15:56:10 +03:00
afs2Entry.Id = EndianStream.ReadUInt16(source);
2016-11-12 19:02:48 +03:00
break;
default:
throw new InvalidDataException($"Unknown IdFieldLength ({IdFieldLength}). Please report this error with the file(s).");
2016-11-12 19:02:48 +03:00
}
2016-12-31 15:56:10 +03:00
long positionPosition = 16 + (entryCount * IdFieldLength) + (i * PositionFieldLength);
2016-11-12 19:02:48 +03:00
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 InvalidDataException($"Unknown PositionFieldLength ({PositionFieldLength}). Please report this error with the file(s).");
2016-11-12 19:02:48 +03:00
}
if (previousEntry != null)
{
previousEntry.Length = afs2Entry.Position - previousEntry.Position;
}
afs2Entry.Position = Helpers.Align(afs2Entry.Position, Align);
2016-11-12 19:02:48 +03:00
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)
{
2016-12-31 15:56:10 +03:00
uint headerLength = (uint)(16 + (entries.Count * IdFieldLength) + (entries.Count * PositionFieldLength) + PositionFieldLength);
2016-11-12 19:02:48 +03:00
EndianStream.WriteCString(destination, "AFS2", 4);
2016-12-31 15:56:10 +03:00
EndianStream.WriteUInt32(destination, 1 | (IdFieldLength << 16) | (PositionFieldLength << 8));
2016-11-12 19:02:48 +03:00
EndianStream.WriteUInt32(destination, (ushort)entries.Count);
2016-12-31 15:56:10 +03:00
EndianStream.WriteUInt32(destination, Align);
2016-12-25 21:43:22 +03:00
2016-12-31 15:56:10 +03:00
VldPool vldPool = new VldPool(Align, headerLength);
2016-11-12 19:02:48 +03:00
2016-12-31 15:56:10 +03:00
var orderedEntries = entries.OrderBy(entry => entry.Id);
foreach (CriAfs2Entry afs2Entry in orderedEntries)
2016-11-12 19:02:48 +03:00
{
2016-12-31 15:56:10 +03:00
switch (IdFieldLength)
2016-11-12 19:02:48 +03:00
{
case 2:
2016-12-31 15:56:10 +03:00
EndianStream.WriteUInt16(destination, (ushort)afs2Entry.Id);
2016-11-12 19:02:48 +03:00
break;
default:
throw new NotImplementedException($"Unimplemented IdFieldLength ({IdFieldLength}). Implemented length: (2)");
2016-11-12 19:02:48 +03:00
}
}
2016-12-31 15:56:10 +03:00
foreach (CriAfs2Entry afs2Entry in orderedEntries)
2016-11-12 19:02:48 +03:00
{
2016-12-31 15:56:10 +03:00
uint entryPosition = (uint)vldPool.Length;
vldPool.Put(afs2Entry.FilePath);
2016-11-12 19:02:48 +03:00
switch (PositionFieldLength)
{
case 2:
EndianStream.WriteUInt16(destination, (ushort)entryPosition);
break;
case 4:
EndianStream.WriteUInt32(destination, entryPosition);
break;
default:
throw new InvalidDataException($"Unimplemented PositionFieldLength ({PositionFieldLength}). Implemented lengths: (2, 4)");
2016-11-12 19:02:48 +03:00
}
afs2Entry.Position = entryPosition;
}
2016-12-31 15:56:10 +03:00
EndianStream.WriteUInt32(destination, (uint)vldPool.Length);
2016-11-12 19:02:48 +03:00
vldPool.Write(destination);
vldPool.Clear();
}
2016-12-31 15:56:10 +03:00
public CriAfs2Entry GetById(uint cueIndex)
2016-11-12 19:02:48 +03:00
{
2016-12-31 15:56:10 +03:00
return entries.FirstOrDefault(e => (e.Id == cueIndex));
2016-11-12 19:02:48 +03:00
}
2016-12-31 15:56:10 +03:00
2016-11-12 19:02:48 +03:00
public CriAfs2Archive()
{
Align = 32;
2016-12-31 15:56:10 +03:00
IdFieldLength = 2;
2016-11-12 19:02:48 +03:00
PositionFieldLength = 4;
}
}
}