Files
1605296300@qq.com 7607c8b4d2 add acb&awb support
2022-09-07 01:22:44 +08:00

703 lines
20 KiB
C#

using CriWareFormats.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CriWareFormats
{
struct CueName
{
public ushort CueIndex;
public string Name;
}
struct Cue
{
public byte ReferenceType;
public ushort ReferenceIndex;
}
struct BlockSequence
{
public ushort NumTracks;
public uint TrackIndexOffset;
public uint TrackIndexSize;
public ushort NumBlocks;
public uint BlockIndexOffset;
public uint BlockIndexSize;
}
struct Block
{
public ushort NumTracks;
public uint TrackIndexOffset;
public uint TrackIndexSize;
}
struct Sequence
{
public ushort NumTracks;
public uint TrackIndexOffset;
public uint TrackIndexSize;
public byte Type;
}
struct Track
{
public ushort EventIndex;
}
struct TrackCommand
{
public uint CommandOffset;
public uint CommandSize;
}
struct Synth
{
public byte Type;
public uint ReferenceItemsOffset;
public uint ReferenceItemsSize;
}
struct Waveform
{
public ushort Id;
public ushort PortNo;
public byte Streaming;
}
public class AcbNameLoader
{
private Stream acbStream;
private UtfTable header;
private UtfTable cueNames;
private BinaryReaderEndian cueReader;
private BinaryReaderEndian cueNameReader;
private BinaryReaderEndian blockSequenceReader;
private BinaryReaderEndian blockReader;
private BinaryReaderEndian sequenceReader;
private BinaryReaderEndian trackReader;
private BinaryReaderEndian trackCommandReader;
private BinaryReaderEndian synthReader;
private BinaryReaderEndian waveformReader;
private Cue[] cue;
private CueName[] cueName;
private BlockSequence[] blockSequence;
private Block[] block;
private Sequence[] sequence;
private Track[] track;
private TrackCommand[] trackCommand;
private Synth[] synth;
private Waveform[] waveform;
private int cueRows;
private int cueNameRows;
private int blockSequenceRows;
private int blockRows;
private int sequenceRows;
private int trackRows;
private int trackCommandRows;
private int synthRows;
private int waveFormRows;
private bool isMemory;
private int targetWaveId;
private int targetPort;
private int synthDepth;
private int sequenceDepth;
private short cueNameIndex;
private string cueNameName;
private int awbNameCount;
private List<short> awbNameList;
private string name;
public AcbNameLoader(Stream acb)
{
acbStream = acb;
header = new UtfTable(acb, (uint)acbStream.Position);
}
bool OpenUtfSubtable(out BinaryReaderEndian tableReader, out UtfTable table, string tableName, out int rows)
{
if (!header.Query(0, tableName, out VLData data))
throw new ArgumentException("Error reading table.");
tableReader = new BinaryReaderEndian(acbStream);
table = new UtfTable(tableReader.BaseStream, data.Offset, out rows, out string _);
return true;
}
void AddAcbName(byte streaming)
{
if (cueNameName.Length == 0)
return;
for (int i = 0; i < awbNameCount; i++)
{
if (awbNameList[i] == cueNameIndex)
return;
}
if (awbNameCount > 0)
{
name += "; ";
name += cueNameName;
}
else
name = cueNameName;
if (streaming == 2 && isMemory)
name += " [pre]";
awbNameList.Add(cueNameIndex);
awbNameCount++;
}
void PreloadAcbWaveForm()
{
ref int rows = ref waveFormRows;
if (rows != 0) return;
if (!OpenUtfSubtable(out waveformReader, out UtfTable table, "WaveformTable", out rows))
throw new Exception("Failure opening Waveform table.");
if (rows == 0) return;
waveform = new Waveform[rows];
int cId = table.GetColumn("Id");
int cMemoryAwbId = table.GetColumn("MemoryAwbId");
int cStreamAwbId = table.GetColumn("StreamAwbId");
int cStreamAwbPortNo = table.GetColumn("StreamAwbPortNo");
int cStreaming = table.GetColumn("Streaming");
for (int i = 0; i < rows; i++)
{
ref Waveform r = ref waveform[i];
if (!table.Query(i, cId, out r.Id))
{
if (isMemory)
{
table.Query(i, cMemoryAwbId, out r.Id);
waveform[i].PortNo = 0xFFFF;
}
else
{
table.Query(i, cStreamAwbId, out r.Id);
table.Query(i, cStreamAwbPortNo, out r.PortNo);
}
}
else
waveform[i].PortNo = 0xFFFF;
table.Query(i, cStreaming, out r.Streaming);
}
}
void LoadAcbWaveForm(ushort index)
{
PreloadAcbWaveForm();
if (index > waveFormRows)
throw new ArgumentOutOfRangeException(nameof(index));
ref Waveform r = ref waveform[index];
if (r.Id != targetWaveId)
return;
if (targetPort >= 0 && r.PortNo != 0xFFFF && r.PortNo != targetPort)
return;
if ((isMemory && r.Streaming == 1) || (!isMemory && r.Streaming == 0))
return;
AddAcbName(r.Streaming);
return;
}
void PreloadAcbSynth()
{
ref int rows = ref synthRows;
if (rows != 0) return;
if (!OpenUtfSubtable(out synthReader, out UtfTable table, "SynthTable", out rows))
throw new Exception("Failure opening Synth table.");
if (rows == 0) return;
synth = new Synth[rows];
int cType = table.GetColumn("Type");
int cReferenceItems = table.GetColumn("ReferenceItems");
for (int i = 0; i < rows; i++)
{
ref Synth r = ref synth[i];
table.Query(i, cType, out r.Type);
table.Query(i, cReferenceItems, out r.ReferenceItemsOffset, out r.ReferenceItemsSize);
}
}
void LoadAcbSynth(ushort index)
{
PreloadAcbSynth();
if (index > synthRows)
throw new ArgumentOutOfRangeException(nameof(index));
ref Synth r = ref synth[index];
synthDepth++;
if (synthDepth > 3)
throw new Exception("Synth depth too high");
int count = (int)(r.ReferenceItemsSize / 4);
for (int i = 0; i < count; i++)
{
synthReader.BaseStream.Position = r.ReferenceItemsOffset + i * 4;
ushort itemType = synthReader.ReadUInt16BE();
ushort itemIndex = synthReader.ReadUInt16BE();
switch (itemType)
{
case 0:
count = 0;
break;
case 1:
LoadAcbWaveForm(itemIndex);
break;
case 2:
LoadAcbSynth(itemIndex);
break;
case 3:
LoadAcbSequence(itemIndex);
break;
case 6:
default:
count = 0;
break;
}
}
synthDepth--;
}
void LoadAcbCommandTlvs(BinaryReaderEndian reader, uint commandOffset, uint commandSize)
{
uint pos = 0;
uint maxPos = commandSize;
while (pos < maxPos)
{
reader.BaseStream.Position = commandOffset + pos;
ushort tlvCode = reader.ReadUInt16BE();
ushort tlvSize = reader.ReadByte();
pos += 3;
switch (tlvCode)
{
case 2000:
case 2003:
if (tlvSize < 4)
break;
reader.BaseStream.Position = commandOffset + pos;
ushort tlvType = reader.ReadUInt16BE();
ushort tlvIndex = reader.ReadUInt16BE();
switch (tlvType)
{
case 2:
LoadAcbSynth(tlvIndex);
break;
case 3:
LoadAcbSequence(tlvIndex);
break;
default:
maxPos = 0;
break;
}
break;
default:
break;
}
pos += tlvSize;
}
}
void PreloadAcbTrackCommand()
{
ref int rows = ref trackCommandRows;
if (rows != 0) return;
if (!OpenUtfSubtable(out trackCommandReader, out UtfTable table, "TrackEventTable", out rows))
{
if (!OpenUtfSubtable(out trackCommandReader, out table, "CommandTable", out rows))
throw new Exception("Failure opening Command table.");
}
if (rows == 0) return;
trackCommand = new TrackCommand[rows];
int cCommand = table.GetColumn("Command");
for (int i = 0; i < rows; i++)
{
ref TrackCommand r = ref trackCommand[i];
table.Query(i, cCommand, out r.CommandOffset, out r.CommandSize);
}
}
void LoadAcbTrackCommand(ushort index)
{
PreloadAcbTrackCommand();
if (index > trackCommandRows)
throw new ArgumentOutOfRangeException(nameof(index));
LoadAcbCommandTlvs(
trackCommandReader,
trackCommand[index].CommandOffset,
trackCommand[index].CommandSize);
}
void PreloadAcbTrack()
{
ref int rows = ref trackRows;
if (rows != 0) return;
if (!OpenUtfSubtable(out trackReader, out UtfTable table, "TrackTable", out rows))
throw new Exception("Failure opening Track table.");
if (rows == 0) return;
track = new Track[rows];
int cEventIndex = table.GetColumn("EventIndex");
for (int i = 0; i < rows; i++)
{
ref Track r = ref track[i];
table.Query(i, cEventIndex, out r.EventIndex);
}
}
void LoadAcbTrack(ushort index)
{
PreloadAcbTrack();
if (index > trackRows)
throw new ArgumentOutOfRangeException(nameof(index));
ref Track r = ref track[index];
if (r.EventIndex == 65535)
return;
LoadAcbTrackCommand(r.EventIndex);
}
void PreloadAcbSequence()
{
ref int rows = ref sequenceRows;
if (rows != 0) return;
if (!OpenUtfSubtable(out sequenceReader, out UtfTable table, "SequenceTable", out rows))
throw new Exception("Failure opening Sequence table.");
if (rows == 0) return;
sequence = new Sequence[rows];
int cNumTracks = table.GetColumn("NumTracks");
int cTrackIndex = table.GetColumn("TrackIndex");
int cType = table.GetColumn("Type");
for (int i = 0; i < rows; i++)
{
ref Sequence r = ref sequence[i];
table.Query(i, cNumTracks, out r.NumTracks);
table.Query(i, cTrackIndex, out r.TrackIndexOffset, out r.TrackIndexSize);
table.Query(i, cType, out r.Type);
}
}
void LoadAcbSequence(uint index)
{
PreloadAcbSequence();
if (index > sequenceRows)
throw new ArgumentOutOfRangeException(nameof(index));
ref Sequence r = ref sequence[index];
sequenceDepth++;
if (sequenceDepth > 3)
throw new Exception("Sequence depth too high.");
if (r.NumTracks * 2 > r.TrackIndexSize)
throw new Exception("Wrong Sequence.TrackIndex size.");
switch (r.Type)
{
default:
for (int i = 0; i < r.NumTracks; i++)
{
sequenceReader.BaseStream.Position = r.TrackIndexOffset + i * 2;
short trackIndexIndex = sequenceReader.ReadInt16BE();
LoadAcbTrack((ushort)trackIndexIndex);
}
break;
}
sequenceDepth--;
}
void PreloadAcbBlock()
{
ref int rows = ref blockRows;
if (rows != 0) return;
if (!OpenUtfSubtable(out blockReader, out UtfTable table, "BlockTable", out rows))
throw new Exception("Failure opening Block table.");
if (rows == 0) return;
block = new Block[rows];
int cNumTracks = table.GetColumn("NumTracks");
int cTrackIndex = table.GetColumn("TrackIndex");
for (int i = 0; i < rows; i++)
{
ref Block r = ref block[i];
table.Query(i, cNumTracks, out r.NumTracks);
table.Query(i, cTrackIndex, out VLData data);
r.TrackIndexOffset = data.Offset;
r.TrackIndexSize = data.Size;
}
}
void LoadAcbBlock(ushort index)
{
PreloadAcbBlock();
if (index > blockRows)
throw new ArgumentOutOfRangeException(nameof(index));
ref Block r = ref block[index];
if (r.NumTracks * 2 > r.TrackIndexSize)
throw new Exception("Wrong Block.TrackIndex size.");
for (int i = 0; i < r.NumTracks; i++)
{
blockReader.BaseStream.Position = r.TrackIndexOffset + i * 2;
short trackIndexIndex = blockReader.ReadInt16BE();
LoadAcbTrack((ushort)trackIndexIndex);
}
}
void PreloadAcbBlockSequence()
{
ref int rows = ref blockSequenceRows;
if (rows != 0) return;
if (!OpenUtfSubtable(out blockSequenceReader, out UtfTable table, "BlockSequenceTable", out rows))
throw new Exception("Failure opening BlockSequence table.");
if (rows == 0) return;
blockSequence = new BlockSequence[rows];
int cNumTracks = table.GetColumn("NumTracks");
int cTrackIndex = table.GetColumn("TrackIndex");
int cNumBlocks = table.GetColumn("NumBlocks");
int cBlockIndex = table.GetColumn("BlockIndex");
for (int i = 0; i < rows; i++)
{
ref BlockSequence r = ref blockSequence[i];
table.Query(i, cNumTracks, out r.NumTracks);
table.Query(i, cTrackIndex, out r.TrackIndexOffset, out r.TrackIndexSize);
table.Query(i, cNumBlocks, out r.NumBlocks);
table.Query(i, cBlockIndex, out r.BlockIndexOffset, out r.BlockIndexSize);
}
}
void LoadAcbBlockSequence(ushort index)
{
PreloadAcbBlockSequence();
if (index > blockSequenceRows)
throw new ArgumentOutOfRangeException(nameof(index));
ref BlockSequence r = ref blockSequence[index];
if (r.NumTracks * 2 > r.TrackIndexSize)
throw new Exception("Wrong BlockSequence.TrackIndex size.");
for (int i = 0; i < r.NumTracks; i++)
{
blockSequenceReader.BaseStream.Position = r.TrackIndexOffset + i * 2;
short trackIndexIndex = blockSequenceReader.ReadInt16();
LoadAcbTrack((ushort)trackIndexIndex);
}
if (r.NumBlocks * 2 > r.BlockIndexSize)
throw new Exception("Wrong BlockSequence.BlockIndex size.");
for (int i = 0; i < r.NumBlocks; i++)
{
blockSequenceReader.BaseStream.Position = r.BlockIndexOffset + i * 2;
short blockIndexIndex = blockSequenceReader.ReadInt16();
LoadAcbBlock((ushort)blockIndexIndex);
}
}
void PreloadAcbCue()
{
ref int rows = ref cueRows;
if (rows != 0) return;
if (!OpenUtfSubtable(out cueReader, out UtfTable table, "CueTable", out rows))
throw new Exception("Failure opening Cue table.");
if (rows == 0) return;
cue = new Cue[rows];
int cReferenceType = table.GetColumn("ReferenceType");
int cReferenceIndex = table.GetColumn("ReferenceIndex");
for (int i = 0; i < rows; i++)
{
ref Cue r = ref cue[i];
table.Query(i, cReferenceType, out r.ReferenceType);
table.Query(i, cReferenceIndex, out r.ReferenceIndex);
}
}
void LoadAcbCue(ushort index)
{
PreloadAcbCue();
if (index > cueRows)
throw new ArgumentOutOfRangeException(nameof(index));
ref Cue r = ref cue[index];
switch (r.ReferenceType)
{
case 1:
LoadAcbWaveForm(r.ReferenceIndex);
break;
case 2:
LoadAcbSynth(r.ReferenceIndex);
break;
case 3:
LoadAcbSequence(r.ReferenceIndex);
break;
case 8:
LoadAcbBlockSequence(r.ReferenceIndex);
break;
default:
break;
}
}
void PreloadAcbCueName()
{
ref UtfTable table = ref cueNames;
ref int rows = ref cueNameRows;
if (rows != 0) return;
if (!OpenUtfSubtable(out cueNameReader, out table, "CueNameTable", out rows))
throw new Exception("Failure opening CueName table.");
if (rows == 0) return;
cueName = new CueName[rows];
int cCueIndex = table.GetColumn("CueIndex");
int cCueName = table.GetColumn("CueName");
for (int i = 0; i < rows; i++)
{
ref CueName r = ref cueName[i];
table.Query(i, cCueIndex, out r.CueIndex);
table.Query(i, cCueName, out r.Name);
}
}
void LoadAcbCueName(ushort index)
{
PreloadAcbCueName();
if (index > cueNameRows)
throw new ArgumentOutOfRangeException(nameof(index));
ref CueName r = ref cueName[index];
cueNameIndex = (short)index;
cueNameName = r.Name;
LoadAcbCue(r.CueIndex);
}
public string LoadWaveName(int waveId, int port, bool memory)
{
targetWaveId = waveId;
targetPort = port;
isMemory = memory;
name = "";
awbNameCount = 0;
awbNameList = new List<short>();
PreloadAcbCueName();
for (ushort i = 0; i < cueNameRows; i++)
{
LoadAcbCueName(i);
}
return name;
}
}
}