mirror of
https://github.com/blueskythlikesclouds/SonicAudioTools.git
synced 2024-11-24 15:10:11 +01:00
592 lines
16 KiB
C#
592 lines
16 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.ComponentModel;
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
using SonicAudioLib.IO;
|
|
using SonicAudioLib.FileBases;
|
|
|
|
namespace SonicAudioLib.CriMw
|
|
{
|
|
public class CriTableWriter : IDisposable
|
|
{
|
|
public enum Status
|
|
{
|
|
Begin,
|
|
Start,
|
|
FieldCollection,
|
|
Row,
|
|
Idle,
|
|
End,
|
|
}
|
|
|
|
private CriTableWriterSettings settings;
|
|
private List<CriTableField> fields;
|
|
private Stream destination;
|
|
private CriTableHeader header;
|
|
private DataPool vldPool;
|
|
private StringPool stringPool;
|
|
private uint headerPosition;
|
|
|
|
private Status status = Status.Begin;
|
|
|
|
public Status CurrentStatus
|
|
{
|
|
get
|
|
{
|
|
return status;
|
|
}
|
|
}
|
|
|
|
public Stream DestinationStream
|
|
{
|
|
get
|
|
{
|
|
return destination;
|
|
}
|
|
}
|
|
|
|
public void WriteStartTable()
|
|
{
|
|
WriteStartTable("(no name)");
|
|
}
|
|
|
|
public void WriteStartTable(string tableName)
|
|
{
|
|
if (status != Status.Begin)
|
|
{
|
|
throw new InvalidOperationException("Attempted to start table when the status wasn't Begin");
|
|
}
|
|
|
|
status = Status.Start;
|
|
|
|
headerPosition = (uint)destination.Position;
|
|
|
|
if (settings.PutBlankString)
|
|
{
|
|
stringPool.Put(StringPool.AdxBlankString);
|
|
}
|
|
|
|
header.TableNamePosition = (uint)stringPool.Put(tableName);
|
|
|
|
var buffer = new byte[32];
|
|
destination.Write(buffer, 0, 32);
|
|
}
|
|
|
|
public void WriteEndTable()
|
|
{
|
|
if (status == Status.FieldCollection)
|
|
{
|
|
WriteEndFieldCollection();
|
|
}
|
|
|
|
if (status == Status.Row)
|
|
{
|
|
WriteEndRow();
|
|
}
|
|
|
|
status = Status.End;
|
|
|
|
destination.Seek(headerPosition + header.RowsPosition + (header.RowLength * header.RowCount), SeekOrigin.Begin);
|
|
|
|
stringPool.Write(destination);
|
|
header.StringPoolPosition = (uint)stringPool.Position - headerPosition;
|
|
|
|
DataStream.Pad(destination, vldPool.Align);
|
|
|
|
vldPool.Write(destination);
|
|
header.DataPoolPosition = (uint)vldPool.Position - headerPosition;
|
|
|
|
DataStream.Pad(destination, vldPool.Align);
|
|
|
|
long previousPosition = destination.Position;
|
|
|
|
header.Length = (uint)destination.Position - headerPosition;
|
|
|
|
if (settings.EncodingType == Encoding.GetEncoding("shift-jis"))
|
|
{
|
|
header.EncodingType = CriTableHeader.EncodingTypeShiftJis;
|
|
}
|
|
|
|
else if (settings.EncodingType == Encoding.UTF8)
|
|
{
|
|
header.EncodingType = CriTableHeader.EncodingTypeUtf8;
|
|
}
|
|
|
|
destination.Seek(headerPosition, SeekOrigin.Begin);
|
|
|
|
destination.Write(CriTableHeader.SignatureBytes, 0, 4);
|
|
DataStream.WriteUInt32BE(destination, header.Length - 8);
|
|
DataStream.WriteByte(destination, header.UnknownByte);
|
|
DataStream.WriteByte(destination, header.EncodingType);
|
|
DataStream.WriteUInt16BE(destination, (ushort)(header.RowsPosition - 8));
|
|
DataStream.WriteUInt32BE(destination, header.StringPoolPosition - 8);
|
|
DataStream.WriteUInt32BE(destination, header.DataPoolPosition - 8);
|
|
DataStream.WriteUInt32BE(destination, header.TableNamePosition);
|
|
DataStream.WriteUInt16BE(destination, header.FieldCount);
|
|
DataStream.WriteUInt16BE(destination, header.RowLength);
|
|
DataStream.WriteUInt32BE(destination, header.RowCount);
|
|
|
|
if (settings.EnableMask)
|
|
{
|
|
destination.Seek(headerPosition, SeekOrigin.Begin);
|
|
CriTableMasker.Mask(destination, header.Length, settings.MaskXor, settings.MaskXorMultiplier);
|
|
}
|
|
|
|
destination.Seek(previousPosition, SeekOrigin.Begin);
|
|
}
|
|
|
|
public void WriteStartFieldCollection()
|
|
{
|
|
if (status != Status.Start)
|
|
{
|
|
throw new InvalidOperationException("Attempted to start field collection when the status wasn't Start");
|
|
}
|
|
|
|
status = Status.FieldCollection;
|
|
}
|
|
|
|
public void WriteField(string fieldName, Type fieldType, object defaultValue)
|
|
{
|
|
if (status != Status.FieldCollection)
|
|
{
|
|
WriteStartFieldCollection();
|
|
}
|
|
|
|
CriFieldFlag fieldFlag = (CriFieldFlag)Array.IndexOf(CriField.FieldTypes, fieldType);
|
|
|
|
if (!string.IsNullOrEmpty(fieldName))
|
|
{
|
|
fieldFlag |= CriFieldFlag.Name;
|
|
}
|
|
|
|
if (defaultValue != null)
|
|
{
|
|
fieldFlag |= CriFieldFlag.DefaultValue;
|
|
}
|
|
|
|
CriTableField field = new CriTableField
|
|
{
|
|
Flag = fieldFlag,
|
|
Name = fieldName,
|
|
Value = defaultValue
|
|
};
|
|
|
|
DataStream.WriteByte(destination, (byte)field.Flag);
|
|
|
|
if (!string.IsNullOrEmpty(fieldName))
|
|
{
|
|
WriteString(field.Name);
|
|
}
|
|
|
|
if (defaultValue != null)
|
|
{
|
|
WriteValue(defaultValue);
|
|
}
|
|
|
|
fields.Add(field);
|
|
header.FieldCount++;
|
|
}
|
|
|
|
public void WriteField(string fieldName, Type fieldType)
|
|
{
|
|
if (status != Status.FieldCollection)
|
|
{
|
|
WriteStartFieldCollection();
|
|
}
|
|
|
|
CriFieldFlag fieldFlag = (CriFieldFlag)Array.IndexOf(CriField.FieldTypes, fieldType) | CriFieldFlag.RowStorage;
|
|
|
|
if (!string.IsNullOrEmpty(fieldName))
|
|
{
|
|
fieldFlag |= CriFieldFlag.Name;
|
|
}
|
|
|
|
CriTableField field = new CriTableField
|
|
{
|
|
Flag = fieldFlag,
|
|
Name = fieldName
|
|
};
|
|
|
|
DataStream.WriteByte(destination, (byte)field.Flag);
|
|
|
|
if (!string.IsNullOrEmpty(fieldName))
|
|
{
|
|
WriteString(field.Name);
|
|
}
|
|
|
|
field.Offset = header.RowLength;
|
|
switch (field.Flag & CriFieldFlag.TypeMask)
|
|
{
|
|
case CriFieldFlag.Byte:
|
|
case CriFieldFlag.SByte:
|
|
header.RowLength += 1;
|
|
break;
|
|
case CriFieldFlag.Int16:
|
|
case CriFieldFlag.UInt16:
|
|
header.RowLength += 2;
|
|
break;
|
|
case CriFieldFlag.Int32:
|
|
case CriFieldFlag.UInt32:
|
|
case CriFieldFlag.Single:
|
|
case CriFieldFlag.String:
|
|
header.RowLength += 4;
|
|
break;
|
|
case CriFieldFlag.Int64:
|
|
case CriFieldFlag.UInt64:
|
|
case CriFieldFlag.Double:
|
|
case CriFieldFlag.Data:
|
|
header.RowLength += 8;
|
|
break;
|
|
}
|
|
|
|
fields.Add(field);
|
|
header.FieldCount++;
|
|
}
|
|
|
|
public void WriteField(CriField criField)
|
|
{
|
|
WriteField(criField.FieldName, criField.FieldType);
|
|
}
|
|
|
|
public void WriteEndFieldCollection()
|
|
{
|
|
if (status != Status.FieldCollection)
|
|
{
|
|
throw new InvalidOperationException("Attempted to end field collection when the status wasn't FieldCollection");
|
|
}
|
|
|
|
status = Status.Idle;
|
|
|
|
header.RowsPosition = (ushort)(destination.Position - headerPosition);
|
|
}
|
|
|
|
public void WriteStartRow()
|
|
{
|
|
if (status == Status.FieldCollection)
|
|
{
|
|
WriteEndFieldCollection();
|
|
}
|
|
|
|
if (status != Status.Idle)
|
|
{
|
|
throw new InvalidOperationException("Attempted to start row when the status wasn't Idle");
|
|
}
|
|
|
|
status = Status.Row;
|
|
|
|
header.RowCount++;
|
|
|
|
destination.Seek(headerPosition + header.RowsPosition + (header.RowCount * header.RowLength), SeekOrigin.Begin);
|
|
byte[] buffer = new byte[header.RowLength];
|
|
destination.Write(buffer, 0, buffer.Length);
|
|
}
|
|
|
|
public void WriteValue(int fieldIndex, object rowValue)
|
|
{
|
|
if (fieldIndex >= fields.Count || fieldIndex < 0 || !fields[fieldIndex].Flag.HasFlag(CriFieldFlag.RowStorage) || rowValue == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GoToValue(fieldIndex);
|
|
WriteValue(rowValue);
|
|
}
|
|
|
|
public void WriteValue(string fieldName, object rowValue)
|
|
{
|
|
WriteValue(fields.FindIndex(field => field.Name == fieldName), rowValue);
|
|
}
|
|
|
|
private void GoToValue(int fieldIndex)
|
|
{
|
|
destination.Seek(headerPosition + header.RowsPosition + (header.RowLength * (header.RowCount - 1)) + fields[fieldIndex].Offset, SeekOrigin.Begin);
|
|
}
|
|
|
|
public void WriteEndRow()
|
|
{
|
|
if (status != Status.Row)
|
|
{
|
|
throw new InvalidOperationException("Attempted to end row when the status wasn't Row");
|
|
}
|
|
|
|
status = Status.Idle;
|
|
}
|
|
|
|
public void WriteRow(bool close, params object[] rowValues)
|
|
{
|
|
WriteStartRow();
|
|
|
|
for (int i = 0; i < Math.Min(rowValues.Length, fields.Count); i++)
|
|
{
|
|
WriteValue(i, rowValues[i]);
|
|
}
|
|
|
|
if (close)
|
|
{
|
|
WriteEndRow();
|
|
}
|
|
}
|
|
|
|
private void WriteString(string value)
|
|
{
|
|
if (settings.RemoveDuplicateStrings && stringPool.ContainsString(value))
|
|
{
|
|
DataStream.WriteUInt32BE(destination, (uint)stringPool.GetStringPosition(value));
|
|
}
|
|
|
|
else
|
|
{
|
|
DataStream.WriteUInt32BE(destination, (uint)stringPool.Put(value));
|
|
}
|
|
}
|
|
|
|
private void WriteValue(object val)
|
|
{
|
|
switch (val)
|
|
{
|
|
case byte value:
|
|
DataStream.WriteByte(destination, value);
|
|
break;
|
|
|
|
case sbyte value:
|
|
DataStream.WriteSByte(destination, value);
|
|
break;
|
|
|
|
case ushort value:
|
|
DataStream.WriteUInt16BE(destination, value);
|
|
break;
|
|
|
|
case short value:
|
|
DataStream.WriteInt16BE(destination, value);
|
|
break;
|
|
|
|
case uint value:
|
|
DataStream.WriteUInt32BE(destination, value);
|
|
break;
|
|
|
|
case int value:
|
|
DataStream.WriteInt32BE(destination, value);
|
|
break;
|
|
|
|
case ulong value:
|
|
DataStream.WriteUInt64BE(destination, value);
|
|
break;
|
|
|
|
case long value:
|
|
DataStream.WriteInt64BE(destination, value);
|
|
break;
|
|
|
|
case float value:
|
|
DataStream.WriteSingleBE(destination, value);
|
|
break;
|
|
|
|
case double value:
|
|
DataStream.WriteDoubleBE(destination, value);
|
|
break;
|
|
|
|
case string value:
|
|
WriteString(value);
|
|
break;
|
|
|
|
case byte[] value:
|
|
DataStream.WriteUInt32BE(destination, (uint)vldPool.Put(value));
|
|
DataStream.WriteUInt32BE(destination, (uint)value.Length);
|
|
break;
|
|
|
|
case Guid value:
|
|
destination.Write(value.ToByteArray(), 0, 16);
|
|
break;
|
|
|
|
case Stream value:
|
|
DataStream.WriteUInt32BE(destination, (uint)vldPool.Put(value));
|
|
DataStream.WriteUInt32BE(destination, (uint)value.Length);
|
|
break;
|
|
|
|
case FileInfo value:
|
|
DataStream.WriteUInt32BE(destination, (uint)vldPool.Put(value));
|
|
DataStream.WriteUInt32BE(destination, (uint)value.Length);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (status != Status.End)
|
|
{
|
|
WriteEndTable();
|
|
}
|
|
|
|
fields.Clear();
|
|
stringPool.Clear();
|
|
vldPool.Clear();
|
|
|
|
if (!settings.LeaveOpen)
|
|
{
|
|
destination.Close();
|
|
}
|
|
}
|
|
|
|
public static CriTableWriter Create(string destinationFileName)
|
|
{
|
|
return Create(destinationFileName, new CriTableWriterSettings());
|
|
}
|
|
|
|
public static CriTableWriter Create(string destinationFileName, CriTableWriterSettings settings)
|
|
{
|
|
Stream destination = File.Create(destinationFileName);
|
|
return new CriTableWriter(destination, settings);
|
|
}
|
|
|
|
public static CriTableWriter Create(Stream destination)
|
|
{
|
|
return new CriTableWriter(destination, new CriTableWriterSettings());
|
|
}
|
|
|
|
public static CriTableWriter Create(Stream destination, CriTableWriterSettings settings)
|
|
{
|
|
return new CriTableWriter(destination, settings);
|
|
}
|
|
|
|
private CriTableWriter(Stream destination, CriTableWriterSettings settings)
|
|
{
|
|
this.destination = destination;
|
|
this.settings = settings;
|
|
|
|
header = new CriTableHeader();
|
|
fields = new List<CriTableField>();
|
|
stringPool = new StringPool(settings.EncodingType);
|
|
vldPool = new DataPool(settings.Align);
|
|
}
|
|
}
|
|
|
|
public class CriTableWriterSettings
|
|
{
|
|
private uint align = 1;
|
|
private bool putBlankString = true;
|
|
private bool leaveOpen = false;
|
|
private Encoding encodingType = Encoding.GetEncoding("shift-jis");
|
|
private bool removeDuplicateStrings = true;
|
|
private bool enableMask = false;
|
|
|
|
public uint Align
|
|
{
|
|
get
|
|
{
|
|
return align;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (value <= 0)
|
|
{
|
|
value = 1;
|
|
}
|
|
|
|
align = value;
|
|
}
|
|
}
|
|
|
|
public bool PutBlankString
|
|
{
|
|
get
|
|
{
|
|
return putBlankString;
|
|
}
|
|
|
|
set
|
|
{
|
|
putBlankString = value;
|
|
}
|
|
}
|
|
|
|
public bool LeaveOpen
|
|
{
|
|
get
|
|
{
|
|
return leaveOpen;
|
|
}
|
|
|
|
set
|
|
{
|
|
leaveOpen = value;
|
|
}
|
|
}
|
|
|
|
public Encoding EncodingType
|
|
{
|
|
get
|
|
{
|
|
return encodingType;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (value != Encoding.UTF8 || value != Encoding.GetEncoding("shift-jis"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
encodingType = value;
|
|
}
|
|
}
|
|
|
|
public bool RemoveDuplicateStrings
|
|
{
|
|
get
|
|
{
|
|
return removeDuplicateStrings;
|
|
}
|
|
|
|
set
|
|
{
|
|
removeDuplicateStrings = value;
|
|
}
|
|
}
|
|
|
|
public bool EnableMask
|
|
{
|
|
get
|
|
{
|
|
return enableMask;
|
|
}
|
|
|
|
set
|
|
{
|
|
enableMask = value;
|
|
}
|
|
}
|
|
|
|
public uint MaskXor { get; set; }
|
|
public uint MaskXorMultiplier { get; set; }
|
|
|
|
public static CriTableWriterSettings AdxSettings
|
|
{
|
|
get
|
|
{
|
|
return new CriTableWriterSettings()
|
|
{
|
|
Align = 8,
|
|
PutBlankString = true,
|
|
RemoveDuplicateStrings = true,
|
|
};
|
|
}
|
|
}
|
|
|
|
public static CriTableWriterSettings Adx2Settings
|
|
{
|
|
get
|
|
{
|
|
return new CriTableWriterSettings()
|
|
{
|
|
Align = 32,
|
|
PutBlankString = false,
|
|
RemoveDuplicateStrings = false,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|