add acb&awb support
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c0b0ac3e281b3f4c87f02a42e65baad
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+1335
-1
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9030d1ab7c7dce4eb3d45b93c88e3c2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7fc4162661dc2f8469c9c2f7f36d6357
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using static ClHcaSharp.Constants;
|
||||
|
||||
namespace ClHcaSharp
|
||||
{
|
||||
internal static class Ath
|
||||
{
|
||||
private static readonly byte[] baseCurve = new byte[656]
|
||||
{
|
||||
0x78, 0x5F, 0x56, 0x51, 0x4E, 0x4C, 0x4B, 0x49, 0x48, 0x48, 0x47, 0x46, 0x46, 0x45, 0x45, 0x45,
|
||||
0x44, 0x44, 0x44, 0x44, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
|
||||
0x42, 0x42, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x40, 0x40, 0x40, 0x40,
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D,
|
||||
0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B,
|
||||
0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B,
|
||||
0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C,
|
||||
0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
|
||||
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43,
|
||||
0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x44, 0x44,
|
||||
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45,
|
||||
0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46,
|
||||
0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48,
|
||||
0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x4A, 0x4A, 0x4A, 0x4A,
|
||||
0x4A, 0x4A, 0x4A, 0x4A, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C,
|
||||
0x4C, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4F, 0x4F, 0x4F,
|
||||
0x4F, 0x4F, 0x4F, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52,
|
||||
0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x56,
|
||||
0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x5A,
|
||||
0x5A, 0x5A, 0x5A, 0x5B, 0x5B, 0x5B, 0x5B, 0x5C, 0x5C, 0x5C, 0x5D, 0x5D, 0x5D, 0x5D, 0x5E, 0x5E,
|
||||
0x5E, 0x5F, 0x5F, 0x5F, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63,
|
||||
0x63, 0x64, 0x64, 0x64, 0x65, 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x69,
|
||||
0x69, 0x6A, 0x6A, 0x6A, 0x6B, 0x6B, 0x6B, 0x6C, 0x6C, 0x6D, 0x6D, 0x6D, 0x6E, 0x6E, 0x6F, 0x6F,
|
||||
0x70, 0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x76, 0x76,
|
||||
0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x7A, 0x7A, 0x7B, 0x7B, 0x7C, 0x7C, 0x7D, 0x7D, 0x7E,
|
||||
0x7E, 0x7F, 0x7F, 0x80, 0x80, 0x81, 0x81, 0x82, 0x83, 0x83, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
|
||||
0x87, 0x88, 0x88, 0x89, 0x89, 0x8A, 0x8A, 0x8B, 0x8C, 0x8C, 0x8D, 0x8D, 0x8E, 0x8F, 0x8F, 0x90,
|
||||
0x90, 0x91, 0x92, 0x92, 0x93, 0x94, 0x94, 0x95, 0x95, 0x96, 0x97, 0x97, 0x98, 0x99, 0x99, 0x9A,
|
||||
0x9B, 0x9B, 0x9C, 0x9D, 0x9D, 0x9E, 0x9F, 0xA0, 0xA0, 0xA1, 0xA2, 0xA2, 0xA3, 0xA4, 0xA5, 0xA5,
|
||||
0xA6, 0xA7, 0xA7, 0xA8, 0xA9, 0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAE, 0xAF, 0xB0, 0xB1, 0xB1,
|
||||
0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
|
||||
0xC0, 0xC1, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD,
|
||||
0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD,
|
||||
0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xED, 0xEE,
|
||||
0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
public static byte[] Init(int type, int sampleRate)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
0 => Init0(),
|
||||
1 => Init1(sampleRate),
|
||||
_ => throw new ArgumentException("Invalid ATH type."),
|
||||
};
|
||||
}
|
||||
|
||||
private static byte[] Init0()
|
||||
{
|
||||
byte[] athCurve = new byte[SamplesPerSubframe];
|
||||
for (int i = 0; i < athCurve.Length; i++)
|
||||
{
|
||||
athCurve[i] = 0;
|
||||
}
|
||||
|
||||
return athCurve;
|
||||
}
|
||||
|
||||
private static byte[] Init1(int sampleRate)
|
||||
{
|
||||
byte[] athCurve = new byte[SamplesPerSubframe];
|
||||
int acc = 0;
|
||||
|
||||
for (int i = 0; i < athCurve.Length; i++)
|
||||
{
|
||||
acc += sampleRate;
|
||||
int index = acc >> 13;
|
||||
|
||||
if (index >= 654)
|
||||
{
|
||||
Fill<byte>(athCurve, i, SamplesPerSubframe - i,0xFF);
|
||||
break;
|
||||
}
|
||||
athCurve[i] = baseCurve[index];
|
||||
}
|
||||
|
||||
return athCurve;
|
||||
}
|
||||
|
||||
public static void Fill<T>(T[] array, int start, int end, T value)
|
||||
{
|
||||
if (array == null)
|
||||
{
|
||||
throw new ArgumentNullException("array");
|
||||
}
|
||||
if (start < 0 || start >= end)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("fromIndex");
|
||||
}
|
||||
if (end >= array.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("toIndex");
|
||||
}
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
array[i] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a39556ddea0390c469c2d1363dedb0c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,107 @@
|
||||
namespace ClHcaSharp
|
||||
{
|
||||
internal class BitReader
|
||||
{
|
||||
private static readonly uint[] mask32 = new uint[]
|
||||
{
|
||||
0xFFFFFFFF, 0x7FFFFFFF, 0x3FFFFFFF, 0x1FFFFFFF,
|
||||
0x0FFFFFFF, 0x07FFFFFF, 0x03FFFFFF, 0x01FFFFFF
|
||||
};
|
||||
|
||||
private static readonly uint[] mask24 = new uint[]
|
||||
{
|
||||
0xFFFFFF, 0x7FFFFF, 0x3FFFFF, 0x1FFFFF,
|
||||
0x0FFFFF, 0x07FFFF, 0x03FFFF, 0x01FFFF
|
||||
};
|
||||
|
||||
private static readonly uint[] mask16 = new uint[]
|
||||
{
|
||||
0xFFFF, 0x7FFF, 0x3FFF, 0x1FFF,
|
||||
0x0FFF, 0x07FF, 0x03FF, 0x01FF
|
||||
};
|
||||
|
||||
private static readonly uint[] mask8 = new uint[]
|
||||
{
|
||||
0xFF, 0x7F, 0x3F, 0x1F,
|
||||
0x0F, 0x07, 0x03, 0x01
|
||||
};
|
||||
|
||||
private readonly byte[] data;
|
||||
private readonly int dataSize;
|
||||
private int currentBit;
|
||||
|
||||
public BitReader(byte[] data)
|
||||
{
|
||||
this.data = data;
|
||||
dataSize = data.Length * 8;
|
||||
}
|
||||
|
||||
public int Bit => currentBit;
|
||||
|
||||
public int Peek(int bits)
|
||||
{
|
||||
int bit = currentBit;
|
||||
int bitsRemaining = bit & 7;
|
||||
int size = dataSize;
|
||||
|
||||
uint value = 0;
|
||||
|
||||
if (bit + bits <= size)
|
||||
{
|
||||
int bitOffset = bits + bitsRemaining;
|
||||
int bitLeft = size - bit;
|
||||
|
||||
if (bitLeft >= 32 && bitOffset >= 25)
|
||||
{
|
||||
int dataOffset = bit >> 3;
|
||||
value = data[dataOffset];
|
||||
value = (value << 8) | data[dataOffset + 1];
|
||||
value = (value << 8) | data[dataOffset + 2];
|
||||
value = (value << 8) | data[dataOffset + 3];
|
||||
value &= mask32[bitsRemaining];
|
||||
value >>= 32 - bitsRemaining - bits;
|
||||
}
|
||||
else if (bitLeft >= 24 && bitOffset >= 17)
|
||||
{
|
||||
int dataOffset = bit >> 3;
|
||||
value = data[dataOffset];
|
||||
value = (value << 8) | data[dataOffset + 1];
|
||||
value = (value << 8) | data[dataOffset + 2];
|
||||
value &= mask24[bitsRemaining];
|
||||
value >>= 24 - bitsRemaining - bits;
|
||||
}
|
||||
else if (bitLeft >= 16 && bitOffset >= 9)
|
||||
{
|
||||
int dataOffset = bit >> 3;
|
||||
value = data[dataOffset];
|
||||
value = (value << 8) | data[dataOffset + 1];
|
||||
value &= mask16[bitsRemaining];
|
||||
value >>= 16 - bitsRemaining - bits;
|
||||
}
|
||||
else
|
||||
{
|
||||
int dataOffset = bit >> 3;
|
||||
value = data[dataOffset];
|
||||
value &= mask8[bitsRemaining];
|
||||
value >>= 8 - bitsRemaining - bits;
|
||||
}
|
||||
|
||||
return (int)value;
|
||||
}
|
||||
|
||||
return (int)value;
|
||||
}
|
||||
|
||||
public int Read(int bits)
|
||||
{
|
||||
int value = Peek(bits);
|
||||
currentBit += bits;
|
||||
return value;
|
||||
}
|
||||
|
||||
public void Skip(int bits)
|
||||
{
|
||||
currentBit += bits;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc08b7eb89558ea4d88cb74e8379b9bb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
using static ClHcaSharp.Constants;
|
||||
|
||||
namespace ClHcaSharp
|
||||
{
|
||||
internal class Channel
|
||||
{
|
||||
public ChannelType Type { get; set; }
|
||||
public int CodedCount { get; set; }
|
||||
|
||||
public byte[] Intensity { get; } = new byte[Subframes];
|
||||
public byte[] ScaleFactors { get; } = new byte[SamplesPerSubframe];
|
||||
public byte[] Resolution { get; } = new byte[SamplesPerSubframe];
|
||||
public byte[] Noises { get; } = new byte[SamplesPerSubframe];
|
||||
public int NoiseCount { get; set; }
|
||||
public int ValidCount { get; set; }
|
||||
|
||||
public float[] Gain { get; } = new float[SamplesPerSubframe];
|
||||
public float[][] Spectra { get; } =
|
||||
JaggedArray.CreateJaggedArray<float[][]>(
|
||||
Subframes, SamplesPerSubframe);
|
||||
public float[] Temp { get; } = new float[SamplesPerSubframe];
|
||||
public float[] Dct { get; } = new float[SamplesPerSubframe];
|
||||
public float[] ImdctPrevious { get; } = new float[SamplesPerSubframe];
|
||||
|
||||
public float[][] Wave { get; } =
|
||||
JaggedArray.CreateJaggedArray<float[][]>(
|
||||
Subframes, SamplesPerSubframe);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 498326e4dde9bc442b3552cb8902aabb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
|
||||
namespace ClHcaSharp
|
||||
{
|
||||
internal static class Cipher
|
||||
{
|
||||
public static void Decrypt(byte[] cipherData, byte[] data)
|
||||
{
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
data[i] = cipherData[data[i]];
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] Init(int type, ulong keyCode)
|
||||
{
|
||||
if (type == 56 && keyCode == 0) type = 0;
|
||||
|
||||
return type switch
|
||||
{
|
||||
0 => Init0(),
|
||||
1 => Init1(),
|
||||
56 => Init56(keyCode),
|
||||
_ => throw new ArgumentException("Invalid cipher type."),
|
||||
};
|
||||
}
|
||||
|
||||
private static byte[] Init0()
|
||||
{
|
||||
byte[] cipherTable = new byte[256];
|
||||
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
cipherTable[i] = (byte)i;
|
||||
}
|
||||
|
||||
return cipherTable;
|
||||
}
|
||||
|
||||
private static byte[] Init1()
|
||||
{
|
||||
byte[] cipherTable = new byte[256];
|
||||
|
||||
const int mul = 13;
|
||||
const int add = 11;
|
||||
|
||||
uint v = 0;
|
||||
for (int i = 1; i < 256 - 1; i++)
|
||||
{
|
||||
v = (v * mul + add) & 0xFF;
|
||||
if (v == 0 || v == 0xFF)
|
||||
v = (v * mul + add) & 0xFF;
|
||||
cipherTable[i] = (byte)v;
|
||||
}
|
||||
cipherTable[0] = 0;
|
||||
cipherTable[0xFF] = 0xFF;
|
||||
|
||||
return cipherTable;
|
||||
}
|
||||
|
||||
private static void Init56CreateTable(byte[] table, byte key)
|
||||
{
|
||||
int mul = ((key & 1) << 3) | 5;
|
||||
int add = (key & 0xE) | 1;
|
||||
|
||||
key >>= 4;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
key = (byte)((key * mul + add) & 0xF);
|
||||
table[i] = key;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] Init56(ulong keyCode)
|
||||
{
|
||||
byte[] cipherTable = new byte[256];
|
||||
|
||||
byte[] kc = new byte[8];
|
||||
byte[] seed = new byte[16];
|
||||
byte[] baseTable = new byte[256];
|
||||
byte[] baseTableR = new byte[16];
|
||||
byte[] baseTableC = new byte[16];
|
||||
|
||||
if (keyCode != 0) keyCode--;
|
||||
|
||||
for (int r = 0; r < (8 - 1); r++)
|
||||
{
|
||||
kc[r] = (byte)(keyCode & 0xFF);
|
||||
keyCode >>= 8;
|
||||
}
|
||||
|
||||
seed[0x00] = kc[1];
|
||||
seed[0x01] = (byte)(kc[1] ^ kc[6]);
|
||||
seed[0x02] = (byte)(kc[2] ^ kc[3]);
|
||||
seed[0x03] = kc[2];
|
||||
seed[0x04] = (byte)(kc[2] ^ kc[1]);
|
||||
seed[0x05] = (byte)(kc[3] ^ kc[4]);
|
||||
seed[0x06] = kc[3];
|
||||
seed[0x07] = (byte)(kc[3] ^ kc[2]);
|
||||
seed[0x08] = (byte)(kc[4] ^ kc[5]);
|
||||
seed[0x09] = kc[4];
|
||||
seed[0x0A] = (byte)(kc[4] ^ kc[3]);
|
||||
seed[0x0B] = (byte)(kc[5] ^ kc[6]);
|
||||
seed[0x0C] = kc[5];
|
||||
seed[0x0D] = (byte)(kc[5] ^ kc[4]);
|
||||
seed[0x0E] = (byte)(kc[6] ^ kc[1]);
|
||||
seed[0x0F] = kc[6];
|
||||
|
||||
Init56CreateTable(baseTableR, kc[0]);
|
||||
|
||||
for (int r = 0; r < 16; r++)
|
||||
{
|
||||
byte nb;
|
||||
Init56CreateTable(baseTableC, seed[r]);
|
||||
nb = (byte)(baseTableR[r] << 4);
|
||||
for (int c = 0; c < 16; c++)
|
||||
{
|
||||
baseTable[r * 16 + c] = (byte)(nb | baseTableC[c]);
|
||||
}
|
||||
}
|
||||
|
||||
uint x = 0;
|
||||
uint pos = 1;
|
||||
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
x = (x + 17) & 0xFF;
|
||||
if (baseTable[x] != 0 && baseTable[x] != 0xFF)
|
||||
cipherTable[pos++] = baseTable[x];
|
||||
}
|
||||
|
||||
cipherTable[0] = 0;
|
||||
cipherTable[0xFF] = 0xFF;
|
||||
|
||||
return cipherTable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2df350f56f9f56749b44a838345aa543
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace ClHcaSharp
|
||||
{
|
||||
internal static class Crc
|
||||
{
|
||||
private static readonly ushort[] crcMaskTable = new ushort[]
|
||||
{
|
||||
0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
|
||||
0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
|
||||
0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
|
||||
0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
|
||||
0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
|
||||
0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
|
||||
0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
|
||||
0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
|
||||
0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
|
||||
0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
|
||||
0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
|
||||
0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
|
||||
0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
|
||||
0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
|
||||
0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
|
||||
0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
|
||||
};
|
||||
|
||||
public static ushort Crc16Checksum(byte[] data)
|
||||
{
|
||||
ushort sum = 0;
|
||||
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
sum = (ushort)((sum << 8) ^ crcMaskTable[(sum >> 8) ^ data[i]]);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12364682399d45244bee9579676101b4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using Assets.Scripts.Audio.CriWareFormats.Common;
|
||||
using System;
|
||||
|
||||
namespace ClHcaSharp
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
public const int Version101 = 0x0101;
|
||||
public const int Version102 = 0x0102;
|
||||
public const int Version103 = 0x0103;
|
||||
public const int Version200 = 0x0200;
|
||||
public const int Version300 = 0x0300;
|
||||
|
||||
public const int MinFrameSize = 0x8;
|
||||
public const int MaxFrameSize = 0xFFFF;
|
||||
|
||||
public const int Mask = 0x7F7F7F7F;
|
||||
public const int Subframes = 8;
|
||||
public const int SamplesPerSubframe = 128;
|
||||
public const int SamplesPerFrame = Subframes * SamplesPerSubframe;
|
||||
public const int MdctBits = 7;
|
||||
|
||||
public const int MinChannels = 1;
|
||||
public const int MaxChannels = 16;
|
||||
public const int MinSampleRate = 1;
|
||||
public const int MaxSampleRate = 0x7FFFFF;
|
||||
|
||||
public const int DefaultRandom = 1;
|
||||
|
||||
public static readonly float MsStereoRatio = BinaryPrimitives.Int32BitsToSingle(0x3F3504F3);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public enum ChannelType
|
||||
{
|
||||
Discrete = 0,
|
||||
StereoPrimary = 1,
|
||||
StereoSecondary = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9097dc5440eaa4241a8902e43ba0d270
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
namespace ClHcaSharp
|
||||
{
|
||||
internal class HcaContext
|
||||
{
|
||||
public int Version { get; set; }
|
||||
public int HeaderSize { get; set; }
|
||||
|
||||
public int ChannelCount { get; set; }
|
||||
public int SampleRate { get; set; }
|
||||
public int FrameCount { get; set; }
|
||||
public int EncoderDelay { get; set; }
|
||||
public int EncoderPadding { get; set; }
|
||||
|
||||
public int FrameSize { get; set; }
|
||||
public int MinResolution { get; set; }
|
||||
public int MaxResolution { get; set; }
|
||||
public int TrackCount { get; set; }
|
||||
public int ChannelConfig { get; set; }
|
||||
public int StereoType { get; set; }
|
||||
public int TotalBandCount { get; set; }
|
||||
public int BaseBandCount { get; set; }
|
||||
public int StereoBandCount { get; set; }
|
||||
public int BandsPerHfrGroup { get; set; }
|
||||
public int MsStereo { get; set; }
|
||||
public int Reserved { get; set; }
|
||||
|
||||
public int VbrMaxFrameSize { get; set; }
|
||||
public int VbrNoiseLevel { get; set; }
|
||||
|
||||
public int AthType { get; set; }
|
||||
|
||||
public int LoopStartFrame { get; set; }
|
||||
public int LoopEndFrame { get; set; }
|
||||
public int LoopStartDelay { get; set; }
|
||||
public int LoopEndPadding { get; set; }
|
||||
public bool LoopFlag { get; set; }
|
||||
|
||||
public int CiphType { get; set; }
|
||||
public ulong KeyCode { get; set; }
|
||||
|
||||
public float RvaVolume { get; set; }
|
||||
|
||||
public int CommentLength { get; set; }
|
||||
public string Comment { get; set; }
|
||||
|
||||
public int HfrGroupCount { get; set; }
|
||||
public byte[] AthCurve { get; set; }
|
||||
public byte[] CipherTable { get; set; }
|
||||
|
||||
public int Random { get; set; }
|
||||
public Channel[] Channels { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58df776bbf57b404fac50ddf009fff77
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,732 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using static ClHcaSharp.Constants;
|
||||
using static ClHcaSharp.Header;
|
||||
using static ClHcaSharp.Tables;
|
||||
|
||||
namespace ClHcaSharp
|
||||
{
|
||||
public class HcaDecoder
|
||||
{
|
||||
private readonly HcaContext hca;
|
||||
|
||||
public HcaDecoder(Stream hcaStream, ulong key)
|
||||
{
|
||||
hca = new HcaContext();
|
||||
|
||||
InitializeTables();
|
||||
|
||||
DecodeHeader(hca, hcaStream);
|
||||
SetKey(key);
|
||||
}
|
||||
|
||||
public HcaInfo GetInfo()
|
||||
{
|
||||
HcaInfo info = new HcaInfo()
|
||||
{
|
||||
Version = hca.Version,
|
||||
HeaderSize = hca.HeaderSize,
|
||||
SamplingRate = hca.SampleRate,
|
||||
ChannelCount = hca.ChannelCount,
|
||||
BlockSize = hca.FrameSize,
|
||||
BlockCount = hca.FrameCount,
|
||||
EncoderDelay = hca.EncoderDelay,
|
||||
EncoderPadding = hca.EncoderPadding,
|
||||
LoopEnabled = hca.LoopFlag,
|
||||
LoopStartBlock = hca.LoopStartFrame,
|
||||
LoopEndBlock = hca.LoopEndFrame,
|
||||
LoopStartDelay = hca.LoopStartDelay,
|
||||
LoopEndPadding = hca.LoopEndPadding,
|
||||
SamplesPerBlock = SamplesPerFrame,
|
||||
Comment = hca.Comment,
|
||||
EncryptionEnabled = hca.CiphType == 56
|
||||
};
|
||||
return info;
|
||||
}
|
||||
|
||||
public void SetKey(ulong key)
|
||||
{
|
||||
hca.KeyCode = key;
|
||||
hca.CipherTable = Cipher.Init(hca.CiphType, hca.KeyCode);
|
||||
}
|
||||
|
||||
public int TestBlock(byte[] data)
|
||||
{
|
||||
const int frameSamples = Subframes * SamplesPerSubframe;
|
||||
const float scale = 32768.0f;
|
||||
|
||||
int status;
|
||||
int clips = 0;
|
||||
int blanks = 0;
|
||||
int[] channelBlanks = new int[MaxChannels];
|
||||
|
||||
byte[] buffer = data;
|
||||
|
||||
{
|
||||
bool isEmpty = true;
|
||||
|
||||
for (int i = 0x02; i < data.Length - 0x02; i++)
|
||||
{
|
||||
if (buffer[i] != 0)
|
||||
{
|
||||
isEmpty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isEmpty) return 0;
|
||||
}
|
||||
|
||||
status = DecodeBlockUnpack(data);
|
||||
if (status < 0) return -1;
|
||||
|
||||
{
|
||||
int bitsMax = hca.FrameSize * 8;
|
||||
int byteStart;
|
||||
|
||||
if (status + 14 > bitsMax)
|
||||
return -1;
|
||||
//throw new Exception("BitReader error.");
|
||||
|
||||
byteStart = (status / 8) + (status % 8 > 0 ? 0x01 : 0);
|
||||
|
||||
for (int i = byteStart; i < hca.FrameSize - 0x02; i++)
|
||||
{
|
||||
if (buffer[i] != 0)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
DecodeBlockTransform();
|
||||
for (int ch = 0; ch < hca.ChannelCount; ch++)
|
||||
{
|
||||
for (int sf = 0; sf < Subframes; sf++)
|
||||
{
|
||||
for (int s = 0; s < SamplesPerSubframe; s++)
|
||||
{
|
||||
float fSample = hca.Channels[ch].Wave[sf][s];
|
||||
|
||||
if (fSample > 1.0f || fSample < -1.0f)
|
||||
clips++;
|
||||
else
|
||||
{
|
||||
int pSample = (int)(fSample * scale);
|
||||
if (pSample == 0 || pSample == -1)
|
||||
{
|
||||
blanks++;
|
||||
channelBlanks[ch]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clips == 1)
|
||||
clips++;
|
||||
if (clips > 1)
|
||||
return clips;
|
||||
|
||||
if (blanks == hca.ChannelCount * frameSamples)
|
||||
return 0;
|
||||
|
||||
if (hca.ChannelCount >= 2)
|
||||
{
|
||||
if (channelBlanks[0] == frameSamples && channelBlanks[1] != frameSamples)
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public void ReadSamples16(short[][] samples)
|
||||
{
|
||||
for (int subframe = 0; subframe < Subframes; subframe++)
|
||||
{
|
||||
for (int sample = 0; sample < SamplesPerSubframe; sample++)
|
||||
{
|
||||
for (int channel = 0; channel < hca.ChannelCount; channel++)
|
||||
{
|
||||
float f = hca.Channels[channel].Wave[subframe][sample];
|
||||
int sInt = (int)(32768 * f);
|
||||
if (sInt > 32767)
|
||||
sInt = 32767;
|
||||
else if (sInt < -32767)
|
||||
sInt = -32767;
|
||||
samples[channel][(SamplesPerSubframe * subframe) + sample] = (short)sInt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ReadSamples16(short[] samples)
|
||||
{
|
||||
int sampleIndex = 0;
|
||||
for (int subframe = 0; subframe < Subframes; subframe++)
|
||||
{
|
||||
for (int sample = 0; sample < SamplesPerSubframe; sample++)
|
||||
{
|
||||
for (int channel = 0; channel < hca.ChannelCount; channel++)
|
||||
{
|
||||
float f = hca.Channels[channel].Wave[subframe][sample];
|
||||
int sInt = (int)(32768 * f);
|
||||
if (sInt > 32767)
|
||||
sInt = 32767;
|
||||
else if (sInt < -32767)
|
||||
sInt = -32767;
|
||||
samples[sampleIndex++] = (short)sInt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float[] ReadSamples16()
|
||||
{
|
||||
List<float> data = new List<float>();
|
||||
for (int subframe = 0; subframe < Subframes; subframe++)
|
||||
{
|
||||
for (int sample = 0; sample < SamplesPerSubframe; sample++)
|
||||
{
|
||||
for (int channel = 0; channel < hca.ChannelCount; channel++)
|
||||
{
|
||||
float f = hca.Channels[channel].Wave[subframe][sample];
|
||||
data.Add(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data.ToArray();
|
||||
}
|
||||
|
||||
public void DecodeReset()
|
||||
{
|
||||
if (hca != null)
|
||||
{
|
||||
hca.Random = DefaultRandom;
|
||||
|
||||
for (int i = 0; i < hca.ChannelCount; i++)
|
||||
{
|
||||
Channel channel = hca.Channels[i];
|
||||
|
||||
Array.Clear(channel.ImdctPrevious, 0, channel.ImdctPrevious.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int DecodeBlock(byte[] data)
|
||||
{
|
||||
int result = DecodeBlockUnpack(data);
|
||||
|
||||
if (result < 0) return result;
|
||||
|
||||
DecodeBlockTransform();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int DecodeBlockUnpack(byte[] data)
|
||||
{
|
||||
if (data.Length < hca.FrameSize)
|
||||
throw new ArgumentException("Data is less than expected frame size.");
|
||||
|
||||
BitReader bitReader = new BitReader(data);
|
||||
|
||||
ushort sync = (ushort)bitReader.Read(16);
|
||||
if (sync != 0xFFFF) throw new InvalidDataException("Sync error.");
|
||||
|
||||
if (Crc.Crc16Checksum(data) > 0) throw new InvalidDataException("Checksum error.");
|
||||
|
||||
Cipher.Decrypt(hca.CipherTable, data);
|
||||
|
||||
int frameAcceptableNoiseLevel = bitReader.Read(9);
|
||||
int frameEvaluationBoundary = bitReader.Read(7);
|
||||
|
||||
int packedNoiseLevel = (frameAcceptableNoiseLevel << 8) - frameEvaluationBoundary;
|
||||
|
||||
for (int channel = 0; channel < hca.ChannelCount; channel++)
|
||||
{
|
||||
UnpackScaleFactors(hca.Channels[channel], bitReader, hca.HfrGroupCount, hca.Version);
|
||||
UnpackIntensity(hca.Channels[channel], bitReader, hca.HfrGroupCount, hca.Version);
|
||||
CalculateResolution(hca.Channels[channel], packedNoiseLevel, hca.AthCurve, hca.MinResolution, hca.MaxResolution);
|
||||
CalculateGain(hca.Channels[channel]);
|
||||
}
|
||||
|
||||
for (int subframe = 0; subframe < Subframes; subframe++)
|
||||
{
|
||||
for (int channel = 0; channel < hca.ChannelCount; channel++)
|
||||
{
|
||||
DequantizeCoefficients(hca.Channels[channel], bitReader, subframe);
|
||||
}
|
||||
}
|
||||
|
||||
return bitReader.Bit;
|
||||
}
|
||||
|
||||
private void DecodeBlockTransform()
|
||||
{
|
||||
for (int subframe = 0; subframe < Subframes; subframe++)
|
||||
{
|
||||
for (int channel = 0; channel < hca.ChannelCount; channel++)
|
||||
{
|
||||
int random = hca.Random;
|
||||
ReconstructNoise(hca.Channels[channel], hca.MinResolution, hca.MsStereo, ref random, subframe);
|
||||
hca.Random = random;
|
||||
ReconstructHighFrequency(hca.Channels[channel], hca.HfrGroupCount, hca.BandsPerHfrGroup,
|
||||
hca.StereoBandCount, hca.BaseBandCount, hca.TotalBandCount, hca.Version, subframe);
|
||||
}
|
||||
|
||||
if (hca.StereoBandCount > 0)
|
||||
{
|
||||
for (int ch = 0; ch < hca.ChannelCount - 1; ch++)
|
||||
{
|
||||
ApplyIntensityStereo(hca.Channels, ch * 2, subframe, hca.BaseBandCount, hca.TotalBandCount);
|
||||
ApplyMsStereo(hca.Channels, ch * 2, hca.MsStereo, hca.BaseBandCount, hca.TotalBandCount, subframe);
|
||||
}
|
||||
}
|
||||
|
||||
for (int channel = 0; channel < hca.ChannelCount; channel++)
|
||||
{
|
||||
ImdctTransform(hca.Channels[channel], subframe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void UnpackScaleFactors(Channel channel, BitReader bitReader, int hfrGroupCount, int version)
|
||||
{
|
||||
int csCount = channel.CodedCount;
|
||||
int extraCount;
|
||||
byte deltaBits = (byte)bitReader.Read(3);
|
||||
|
||||
if (channel.Type == ChannelType.StereoSecondary || hfrGroupCount <= 0 || version <= Version200)
|
||||
extraCount = 0;
|
||||
else
|
||||
{
|
||||
extraCount = hfrGroupCount;
|
||||
csCount += extraCount;
|
||||
|
||||
if (csCount > SamplesPerSubframe)
|
||||
return;
|
||||
//throw new InvalidDataException("Invalid scale count.");
|
||||
}
|
||||
|
||||
if (deltaBits >= 6)
|
||||
{
|
||||
for (int i = 0; i < csCount; i++)
|
||||
{
|
||||
channel.ScaleFactors[i] = (byte)bitReader.Read(6);
|
||||
}
|
||||
}
|
||||
else if (deltaBits > 0)
|
||||
{
|
||||
byte expectedDelta = (byte)((1 << deltaBits) - 1);
|
||||
byte value = (byte)bitReader.Read(6);
|
||||
|
||||
channel.ScaleFactors[0] = value;
|
||||
for (int i = 1; i < csCount; i++)
|
||||
{
|
||||
byte delta = (byte)bitReader.Read(deltaBits);
|
||||
|
||||
if (delta == expectedDelta)
|
||||
value = (byte)bitReader.Read(6);
|
||||
else
|
||||
{
|
||||
int scaleFactorTest = value + (delta - (expectedDelta >> 1));
|
||||
if (scaleFactorTest < 0 || scaleFactorTest >= 64)
|
||||
return;
|
||||
//throw new InvalidDataException("Invalid scale factor.");
|
||||
|
||||
value = (byte)(value - (expectedDelta >> 1) + delta);
|
||||
value = (byte)(value & 0x3F);
|
||||
}
|
||||
|
||||
channel.ScaleFactors[i] = value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < SamplesPerSubframe; i++)
|
||||
{
|
||||
channel.ScaleFactors[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < extraCount; i++)
|
||||
{
|
||||
channel.ScaleFactors[SamplesPerSubframe - 1 - i] = channel.ScaleFactors[csCount - i];
|
||||
}
|
||||
}
|
||||
|
||||
private static void UnpackIntensity(Channel channel, BitReader bitReader, int hfrGroupCount, int version)
|
||||
{
|
||||
if (channel.Type == ChannelType.StereoSecondary)
|
||||
{
|
||||
if (version <= Version200)
|
||||
{
|
||||
byte value = (byte)bitReader.Peek(4);
|
||||
|
||||
channel.Intensity[0] = value;
|
||||
if (value < 15)
|
||||
{
|
||||
bitReader.Skip(4);
|
||||
for (int i = 1; i < Subframes; i++)
|
||||
{
|
||||
channel.Intensity[i] = (byte)bitReader.Read(4);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
byte value = (byte)bitReader.Peek(4);
|
||||
|
||||
if (value < 15)
|
||||
{
|
||||
bitReader.Skip(4);
|
||||
|
||||
byte deltaBits = (byte)bitReader.Read(2);
|
||||
|
||||
channel.Intensity[0] = value;
|
||||
if (deltaBits == 3)
|
||||
{
|
||||
for (int i = 1; i < Subframes; i++)
|
||||
{
|
||||
channel.Intensity[i] = (byte)bitReader.Read(4);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
byte bMax = (byte)((2 << deltaBits) - 1);
|
||||
byte bits = (byte)(deltaBits + 1);
|
||||
|
||||
for (int i = 1; i < Subframes; i++)
|
||||
{
|
||||
byte delta = (byte)bitReader.Read(bits);
|
||||
if (delta == bMax)
|
||||
value = (byte)bitReader.Read(4);
|
||||
else
|
||||
{
|
||||
value = (byte)(value - (bMax >> 1) + delta);
|
||||
if (value > 15)
|
||||
return;
|
||||
//throw new InvalidDataException("Intensity value out of range.");
|
||||
}
|
||||
|
||||
channel.Intensity[i] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bitReader.Skip(4);
|
||||
for (int i = 0; i < Subframes; i++)
|
||||
{
|
||||
channel.Intensity[i] = 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (version <= Version200)
|
||||
{
|
||||
byte[] hfrScales = channel.ScaleFactors;
|
||||
int hfrScalesOffset = 128 - hfrGroupCount;
|
||||
|
||||
for (int i = 0; i < hfrGroupCount; i++)
|
||||
{
|
||||
hfrScales[hfrScalesOffset + i] = (byte)bitReader.Read(6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void CalculateResolution(Channel channel, int packedNoiseLevel, byte[] athCurve, int minResolution, int maxResolution)
|
||||
{
|
||||
int crCount = channel.CodedCount;
|
||||
int noiseCount = 0;
|
||||
int validCount = 0;
|
||||
|
||||
for (int i = 0; i < crCount; i++)
|
||||
{
|
||||
byte newResolution = 0;
|
||||
byte scaleFactor = channel.ScaleFactors[i];
|
||||
|
||||
if (scaleFactor > 0)
|
||||
{
|
||||
int noiseLevel = athCurve[i] + ((packedNoiseLevel + i) >> 8);
|
||||
int curvePosition = noiseLevel + 1 - ((5 * scaleFactor) >> 1);
|
||||
|
||||
if (curvePosition < 0)
|
||||
newResolution = 15;
|
||||
else if (curvePosition <= 65)
|
||||
newResolution = InvertTable[curvePosition];
|
||||
else
|
||||
newResolution = 0;
|
||||
|
||||
if (newResolution > maxResolution)
|
||||
newResolution = (byte)maxResolution;
|
||||
else if (newResolution < minResolution)
|
||||
newResolution = (byte)minResolution;
|
||||
|
||||
if (newResolution < 1)
|
||||
{
|
||||
channel.Noises[noiseCount] = (byte)i;
|
||||
noiseCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
channel.Noises[SamplesPerSubframe - 1 - validCount] = (byte)i;
|
||||
validCount++;
|
||||
}
|
||||
}
|
||||
channel.Resolution[i] = newResolution;
|
||||
}
|
||||
|
||||
channel.NoiseCount = noiseCount;
|
||||
channel.ValidCount = validCount;
|
||||
|
||||
Array.Clear(channel.Resolution, crCount, SamplesPerSubframe - crCount);
|
||||
}
|
||||
|
||||
private static void CalculateGain(Channel channel)
|
||||
{
|
||||
int cgCount = channel.CodedCount;
|
||||
for (int i = 0; i < cgCount; i++)
|
||||
{
|
||||
float scaleFactorScale = DequantizerScalingTable[channel.ScaleFactors[i]];
|
||||
float resolutionScale = DequantizerRangeTable[channel.Resolution[i]];
|
||||
channel.Gain[i] = scaleFactorScale * resolutionScale;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DequantizeCoefficients(Channel channel, BitReader bitReader, int subframe)
|
||||
{
|
||||
int ccCount = channel.CodedCount;
|
||||
|
||||
for (int i = 0; i < ccCount; i++)
|
||||
{
|
||||
float qc;
|
||||
byte resolution = channel.Resolution[i];
|
||||
byte bits = MaxBitTable[resolution];
|
||||
int code = bitReader.Read(bits);
|
||||
|
||||
if (resolution > 7)
|
||||
{
|
||||
int signedCode = (1 - ((code & 1) << 1)) * (code >> 1);
|
||||
if (signedCode == 0)
|
||||
bitReader.Skip(-1);
|
||||
qc = signedCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = (resolution << 4) + code;
|
||||
int skip = ReadBitTable[index] - bits;
|
||||
bitReader.Skip(skip);
|
||||
qc = ReadValueTable[index];
|
||||
}
|
||||
|
||||
channel.Spectra[subframe][i] = channel.Gain[i] * qc;
|
||||
}
|
||||
|
||||
Array.Clear(channel.Spectra[subframe], ccCount, SamplesPerSubframe - ccCount);
|
||||
}
|
||||
|
||||
private static void ReconstructNoise(Channel channel, int minResolution, int msStereo, ref int random, int subframe)
|
||||
{
|
||||
if (minResolution > 0) return;
|
||||
if (channel.ValidCount <= 0 || channel.NoiseCount <= 0) return;
|
||||
if (msStereo != 0 && channel.Type == ChannelType.StereoPrimary) return;
|
||||
|
||||
for (int i = 0; i < channel.NoiseCount; i++)
|
||||
{
|
||||
random = 0x343FD * random + 0x269EC3;
|
||||
|
||||
int randomIndex = SamplesPerSubframe - channel.ValidCount + (((random & 0x7FFF) * channel.ValidCount) >> 15);
|
||||
|
||||
int noiseIndex = channel.Noises[i];
|
||||
int validIndex = channel.Noises[randomIndex];
|
||||
|
||||
int sfNoise = channel.ScaleFactors[noiseIndex];
|
||||
int sfValid = channel.ScaleFactors[validIndex];
|
||||
int scIndex = (sfNoise - sfValid + 62) & ~((sfNoise - sfValid + 62) >> 31);
|
||||
|
||||
channel.Spectra[subframe][noiseIndex] = ScaleConversionTable[scIndex] * channel.Spectra[subframe][validIndex];
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReconstructHighFrequency(Channel channel, int hfrGroupCount, int bandsPerHfrGroup,
|
||||
int stereoBandCount, int baseBandCount, int totalBandCount, int version, int subframe)
|
||||
{
|
||||
if (bandsPerHfrGroup == 0) return;
|
||||
if (channel.Type == ChannelType.StereoSecondary) return;
|
||||
|
||||
int groupLimit;
|
||||
int startBand = stereoBandCount + baseBandCount;
|
||||
int highBand = startBand;
|
||||
int lowBand = startBand - 1;
|
||||
|
||||
int hfrScalesOffset = 128 - hfrGroupCount;
|
||||
byte[] hfrScales = channel.ScaleFactors;
|
||||
|
||||
if (version <= Version200)
|
||||
groupLimit = hfrGroupCount;
|
||||
else
|
||||
{
|
||||
groupLimit = hfrGroupCount >= 0 ? hfrGroupCount : hfrGroupCount + 1;
|
||||
groupLimit >>= 1;
|
||||
}
|
||||
|
||||
for (int group = 0; group < hfrGroupCount; group++)
|
||||
{
|
||||
int lowBandSub = group < groupLimit ? 1 : 0;
|
||||
|
||||
for (int i = 0; i < bandsPerHfrGroup; i++)
|
||||
{
|
||||
if (highBand >= totalBandCount || lowBand < 0) break;
|
||||
|
||||
int scIndex = hfrScales[hfrScalesOffset + group];
|
||||
scIndex &= ~(scIndex >> 31);
|
||||
|
||||
channel.Spectra[subframe][highBand] = ScaleConversionTable[scIndex] * channel.Spectra[subframe][lowBand];
|
||||
|
||||
highBand++;
|
||||
lowBand -= lowBandSub;
|
||||
}
|
||||
}
|
||||
|
||||
channel.Spectra[subframe][highBand - 1] = 0.0f;
|
||||
}
|
||||
|
||||
private static void ApplyIntensityStereo(Channel[] channelPair, int channelOffset, int subframe, int baseBandCount, int totalBandCount)
|
||||
{
|
||||
if (channelPair[channelOffset + 0].Type != ChannelType.StereoPrimary) return;
|
||||
|
||||
float ratioL = IntensityRatioTable[channelPair[channelOffset + 1].Intensity[subframe]];
|
||||
float ratioR = 2.0f - ratioL;
|
||||
float[] spectraL = channelPair[channelOffset + 0].Spectra[subframe];
|
||||
float[] spectraR = channelPair[channelOffset + 1].Spectra[subframe];
|
||||
|
||||
for (int band = baseBandCount; band < totalBandCount; band++)
|
||||
{
|
||||
float coefL = spectraL[band] * ratioL;
|
||||
float coefR = spectraR[band] * ratioR;
|
||||
spectraL[band] = coefL;
|
||||
spectraR[band] = coefR;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplyMsStereo(Channel[] channelPair, int channelOffset, int msStereo, int baseBandCount, int totalBandCount, int subframe)
|
||||
{
|
||||
if (msStereo != 0) return;
|
||||
if (channelPair[channelOffset + 0].Type != ChannelType.StereoPrimary) return;
|
||||
|
||||
float ratio = MsStereoRatio;
|
||||
float[] spectraL = channelPair[channelOffset + 0].Spectra[subframe];
|
||||
float[] spectraR = channelPair[channelOffset + 1].Spectra[subframe];
|
||||
|
||||
for (int band = baseBandCount; band < totalBandCount; band++)
|
||||
{
|
||||
float coefL = (spectraL[band] + spectraR[band]) * ratio;
|
||||
float coefR = (spectraL[band] - spectraR[band]) * ratio;
|
||||
spectraL[band] = coefL;
|
||||
spectraR[band] = coefR;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ImdctTransform(Channel channel, int subframe)
|
||||
{
|
||||
const int size = SamplesPerSubframe;
|
||||
const int half = SamplesPerSubframe / 2;
|
||||
const int mdctBits = MdctBits;
|
||||
|
||||
int count1 = 1;
|
||||
int count2 = half;
|
||||
float[] temp1 = channel.Spectra[subframe];
|
||||
float[] temp2 = channel.Temp;
|
||||
int temp1Index = 0;
|
||||
|
||||
for (int i = 0; i < mdctBits; i++)
|
||||
{
|
||||
float[] swap;
|
||||
float[] d = temp2;
|
||||
int d1Index = 0;
|
||||
int d2Index = count2;
|
||||
|
||||
for (int j = 0; j < count1; j++)
|
||||
{
|
||||
for (int k = 0; k < count2; k++)
|
||||
{
|
||||
float a = temp1[temp1Index++];
|
||||
float b = temp1[temp1Index++];
|
||||
d[d1Index++] = a + b;
|
||||
d[d2Index++] = a - b;
|
||||
}
|
||||
|
||||
d1Index += count2;
|
||||
d2Index += count2;
|
||||
}
|
||||
|
||||
temp1Index -= SamplesPerSubframe;
|
||||
swap = temp1;
|
||||
temp1 = temp2;
|
||||
temp2 = swap;
|
||||
|
||||
count1 <<= 1;
|
||||
count2 >>= 1;
|
||||
}
|
||||
|
||||
count1 = half;
|
||||
count2 = 1;
|
||||
temp1 = channel.Temp;
|
||||
temp2 = channel.Spectra[subframe];
|
||||
|
||||
for (int i = 0; i < mdctBits; i++)
|
||||
{
|
||||
int sinTableIndex = 0;
|
||||
int cosTableIndex = 0;
|
||||
|
||||
float[] swap;
|
||||
float[] d = temp2;
|
||||
float[] s = temp1;
|
||||
int d1Index = 0;
|
||||
int d2Index = count2 * 2 - 1;
|
||||
int s1Index = 0;
|
||||
int s2Index = count2;
|
||||
|
||||
for (int j = 0; j < count1; j++)
|
||||
{
|
||||
for (int k = 0; k < count2; k++)
|
||||
{
|
||||
float a = s[s1Index++];
|
||||
float b = s[s2Index++];
|
||||
float sin = SinTable[i][sinTableIndex++];
|
||||
float cos = CosTable[i][cosTableIndex++];
|
||||
d[d1Index++] = a * sin - b * cos;
|
||||
d[d2Index--] = a * cos + b * sin;
|
||||
}
|
||||
|
||||
s1Index += count2;
|
||||
s2Index += count2;
|
||||
d1Index += count2;
|
||||
d2Index += count2 * 3;
|
||||
}
|
||||
|
||||
swap = temp1;
|
||||
temp1 = temp2;
|
||||
temp2 = swap;
|
||||
|
||||
count1 >>= 1;
|
||||
count2 <<= 1;
|
||||
}
|
||||
|
||||
float[] dct = channel.Spectra[subframe];
|
||||
float[] prev = channel.ImdctPrevious;
|
||||
for (int i = 0; i < half; i++)
|
||||
{
|
||||
channel.Wave[subframe][i] = ImdctWindow[i] * dct[i + half] + prev[i];
|
||||
channel.Wave[subframe][i + half] = ImdctWindow[i + half] * dct[size - 1 - i] - prev[i + half];
|
||||
channel.ImdctPrevious[i] = ImdctWindow[size - 1 - i] * dct[half - i - 1];
|
||||
channel.ImdctPrevious[i + half] = ImdctWindow[half - i - 1] * dct[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac4b2f835ba632b46a03cf7190f73045
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace ClHcaSharp
|
||||
{
|
||||
public class HcaInfo
|
||||
{
|
||||
public int Version { get; set; }
|
||||
public int HeaderSize { get; set; }
|
||||
public int SamplingRate { get; set; }
|
||||
public int ChannelCount { get; set; }
|
||||
public int BlockSize { get; set; }
|
||||
public int BlockCount { get; set; }
|
||||
public int EncoderDelay { get; set; }
|
||||
public int EncoderPadding { get; set; }
|
||||
public bool LoopEnabled { get; set; }
|
||||
public int LoopStartBlock { get; set; }
|
||||
public int LoopEndBlock { get; set; }
|
||||
public int LoopStartDelay { get; set; }
|
||||
public int LoopEndPadding { get; set; }
|
||||
public int SamplesPerBlock { get; set; }
|
||||
public string Comment { get; set; }
|
||||
public bool EncryptionEnabled { get; set; }
|
||||
|
||||
public int SampleCount => BlockCount * SamplesPerBlock - EncoderDelay - EncoderPadding;
|
||||
public int LoopStartSample => LoopStartBlock * SamplesPerBlock - EncoderDelay + LoopStartDelay;
|
||||
public int LoopEndSample => LoopEndBlock * SamplesPerBlock - EncoderDelay + (SamplesPerBlock - LoopEndPadding);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 951e0209b6d7c7e4e9d47d922cd7b490
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,329 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using static ClHcaSharp.Constants;
|
||||
|
||||
namespace ClHcaSharp
|
||||
{
|
||||
internal static class Header
|
||||
{
|
||||
public static bool IsHeaderValid(Stream hcaStream, out int headerSize)
|
||||
{
|
||||
BitReader bitReader = new BitReader(new BinaryReader(hcaStream).ReadBytes(8));
|
||||
|
||||
headerSize = 0;
|
||||
if ((bitReader.Peek(32) & Mask) == StringToUInt32("HCA"))
|
||||
{
|
||||
bitReader.Skip(32 + 16);
|
||||
headerSize = bitReader.Read(16);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void DecodeHeader(HcaContext hca, Stream hcaStream)
|
||||
{
|
||||
if (!IsHeaderValid(hcaStream, out int headerSize))
|
||||
throw new InvalidDataException("Invalid HCA header.");
|
||||
|
||||
BinaryReader binaryReader = new BinaryReader(hcaStream);
|
||||
binaryReader.BaseStream.Position = 0;
|
||||
|
||||
BitReader bitReader = new BitReader(binaryReader.ReadBytes((int)headerSize));
|
||||
|
||||
if ((bitReader.Peek(32) & Mask) == StringToUInt32("HCA"))
|
||||
{
|
||||
bitReader.Skip(32);
|
||||
hca.Version = bitReader.Read(16);
|
||||
hca.HeaderSize = bitReader.Read(16);
|
||||
|
||||
headerSize -= 8;
|
||||
}
|
||||
else throw new InvalidDataException("Not an HCA file.");
|
||||
|
||||
if (headerSize >= 16 && (bitReader.Peek(32) & Mask) == StringToUInt32("fmt"))
|
||||
{
|
||||
bitReader.Skip(32);
|
||||
hca.ChannelCount = bitReader.Read(8);
|
||||
hca.SampleRate = bitReader.Read(24);
|
||||
hca.FrameCount = bitReader.Read(32);
|
||||
hca.EncoderDelay = bitReader.Read(16);
|
||||
hca.EncoderPadding = bitReader.Read(16);
|
||||
|
||||
if (!(hca.ChannelCount >= MinChannels && hca.ChannelCount <= MaxChannels))
|
||||
throw new InvalidDataException("Invalid channel count.");
|
||||
|
||||
if (hca.FrameCount == 0)
|
||||
throw new InvalidDataException("Frame count is zero.");
|
||||
|
||||
if (!(hca.SampleRate >= MinSampleRate && hca.SampleRate <= MaxSampleRate))
|
||||
throw new InvalidDataException("Invalid sample rate.");
|
||||
|
||||
headerSize -= 16;
|
||||
}
|
||||
else throw new InvalidDataException("No format chunk.");
|
||||
|
||||
if (headerSize >= 16 && (bitReader.Peek(32) & Mask) == StringToUInt32("comp"))
|
||||
{
|
||||
bitReader.Skip(32);
|
||||
hca.FrameSize = bitReader.Read(16);
|
||||
hca.MinResolution = bitReader.Read(8);
|
||||
hca.MaxResolution = bitReader.Read(8);
|
||||
hca.TrackCount = bitReader.Read(8);
|
||||
hca.ChannelConfig = bitReader.Read(8);
|
||||
hca.TotalBandCount = bitReader.Read(8);
|
||||
hca.BaseBandCount = bitReader.Read(8);
|
||||
hca.StereoBandCount = bitReader.Read(8);
|
||||
hca.BandsPerHfrGroup = bitReader.Read(8);
|
||||
hca.MsStereo = bitReader.Read(8);
|
||||
hca.Reserved = bitReader.Read(8);
|
||||
|
||||
headerSize -= 16;
|
||||
}
|
||||
else if (headerSize >= 12 && (bitReader.Peek(32) & Mask) == StringToUInt32("dec"))
|
||||
{
|
||||
bitReader.Skip(32);
|
||||
hca.FrameSize = bitReader.Read(16);
|
||||
hca.MinResolution = bitReader.Read(8);
|
||||
hca.MaxResolution = bitReader.Read(8);
|
||||
hca.TotalBandCount = bitReader.Read(8) + 1;
|
||||
hca.BaseBandCount = bitReader.Read(8) + 1;
|
||||
hca.TrackCount = bitReader.Read(4);
|
||||
hca.ChannelConfig = bitReader.Read(4);
|
||||
hca.StereoType = bitReader.Read(8);
|
||||
|
||||
if (hca.StereoType == 0) hca.BaseBandCount = hca.TotalBandCount;
|
||||
hca.StereoBandCount = hca.TotalBandCount - hca.BaseBandCount;
|
||||
hca.BandsPerHfrGroup = 0;
|
||||
|
||||
headerSize -= 12;
|
||||
}
|
||||
else throw new InvalidDataException("No compression or decode chunk.");
|
||||
|
||||
if (headerSize >= 8 && (bitReader.Peek(32) & Mask) == StringToUInt32("vbr"))
|
||||
{
|
||||
bitReader.Skip(32);
|
||||
hca.VbrMaxFrameSize = bitReader.Read(16);
|
||||
hca.VbrNoiseLevel = bitReader.Read(16);
|
||||
|
||||
if (!(hca.FrameSize == 0 && hca.VbrMaxFrameSize > 8 && hca.VbrMaxFrameSize <= 511))
|
||||
throw new InvalidDataException("Invalid frame size.");
|
||||
|
||||
headerSize -= 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
hca.VbrMaxFrameSize = 0;
|
||||
hca.VbrNoiseLevel = 0;
|
||||
}
|
||||
|
||||
if (headerSize >= 6 && (bitReader.Peek(32) & Mask) == StringToUInt32("ath"))
|
||||
{
|
||||
bitReader.Skip(32);
|
||||
hca.AthType = bitReader.Read(16);
|
||||
}
|
||||
else hca.AthType = (hca.Version < Version200) ? 1 : 0;
|
||||
|
||||
if (headerSize >= 16 && (bitReader.Peek(32) & Mask) == StringToUInt32("loop"))
|
||||
{
|
||||
bitReader.Skip(32);
|
||||
hca.LoopStartFrame = bitReader.Read(32);
|
||||
hca.LoopEndFrame = bitReader.Read(32);
|
||||
hca.LoopStartDelay = bitReader.Read(16);
|
||||
hca.LoopEndPadding = bitReader.Read(16);
|
||||
|
||||
hca.LoopFlag = true;
|
||||
|
||||
if (!(hca.LoopStartFrame >= 0 && hca.LoopStartFrame <= hca.LoopEndFrame
|
||||
&& hca.LoopEndFrame < hca.FrameCount))
|
||||
throw new InvalidDataException("Invalid loop frames.");
|
||||
|
||||
headerSize -= 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
hca.LoopStartFrame = 0;
|
||||
hca.LoopEndFrame = 0;
|
||||
hca.LoopStartDelay = 0;
|
||||
hca.LoopEndPadding = 0;
|
||||
|
||||
hca.LoopFlag = false;
|
||||
}
|
||||
|
||||
if (headerSize >= 6 && (bitReader.Peek(32) & Mask) == StringToUInt32("ciph"))
|
||||
{
|
||||
bitReader.Skip(32);
|
||||
hca.CiphType = bitReader.Read(16);
|
||||
|
||||
if (!(hca.CiphType == 0 || hca.CiphType == 1 || hca.CiphType == 56))
|
||||
throw new InvalidDataException("Invalid cipher type.");
|
||||
|
||||
headerSize -= 6;
|
||||
}
|
||||
|
||||
if (headerSize >= 8 && (bitReader.Peek(32) & Mask) == StringToUInt32("rva"))
|
||||
{
|
||||
bitReader.Skip(32);
|
||||
int rvaVolumeInt = bitReader.Read(32);
|
||||
hca.RvaVolume = BitConverter.ToSingle(BitConverter.GetBytes(rvaVolumeInt),0);
|
||||
|
||||
headerSize -= 8;
|
||||
}
|
||||
else hca.RvaVolume = 1.0f;
|
||||
|
||||
if (headerSize >= 5 && (bitReader.Peek(32) & Mask) == StringToUInt32("comm"))
|
||||
{
|
||||
bitReader.Skip(32);
|
||||
hca.CommentLength = bitReader.Read(8);
|
||||
|
||||
if (hca.CommentLength > headerSize) throw new InvalidDataException("Comment string out of bounds.");
|
||||
|
||||
StringBuilder commentStringBuilder = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < hca.CommentLength; i++)
|
||||
{
|
||||
commentStringBuilder.Append(bitReader.Read(8));
|
||||
}
|
||||
|
||||
hca.Comment = commentStringBuilder.ToString();
|
||||
|
||||
//headerSize -= 5 + hca.CommentLength;
|
||||
}
|
||||
else hca.CommentLength = 0;
|
||||
|
||||
// IDE0059
|
||||
//if (headerSize >= 4 && (bitReader.Peek(32) & Mask) == StringToUInt32("pad"))
|
||||
//{
|
||||
// headerSize -= (headerSize - 2);
|
||||
//}
|
||||
|
||||
if (hca.FrameSize < MinFrameSize || hca.FrameSize > MaxFrameSize)
|
||||
throw new InvalidDataException("Invalid frame size.");
|
||||
|
||||
if (hca.Version <= Version200)
|
||||
{
|
||||
if (hca.MinResolution != 1 || hca.MaxResolution != 15)
|
||||
throw new InvalidDataException("Incompatible resolution.");
|
||||
}
|
||||
|
||||
if (hca.TrackCount == 0) hca.TrackCount = 1;
|
||||
|
||||
if (hca.TrackCount > hca.ChannelCount) throw new InvalidDataException("Invalid track count.");
|
||||
|
||||
if (hca.TotalBandCount > SamplesPerSubframe ||
|
||||
hca.BaseBandCount > SamplesPerSubframe ||
|
||||
hca.StereoBandCount > SamplesPerSubframe ||
|
||||
hca.BaseBandCount + hca.StereoBandCount > SamplesPerSubframe ||
|
||||
hca.BandsPerHfrGroup > SamplesPerSubframe)
|
||||
throw new InvalidDataException("Invalid bands.");
|
||||
|
||||
hca.HfrGroupCount = HeaderCeil2(
|
||||
hca.TotalBandCount - hca.BaseBandCount - hca.StereoBandCount,
|
||||
hca.BandsPerHfrGroup);
|
||||
|
||||
hca.AthCurve = Ath.Init(hca.AthType, hca.SampleRate);
|
||||
hca.CipherTable = Cipher.Init(hca.CiphType, hca.KeyCode);
|
||||
|
||||
int channelsPerTrack = hca.ChannelCount / hca.TrackCount;
|
||||
ChannelType[] channelTypes = new ChannelType[channelsPerTrack];
|
||||
|
||||
for (int i = 0; i < channelTypes.Length; i++)
|
||||
{
|
||||
channelTypes[i] = ChannelType.Discrete;
|
||||
}
|
||||
|
||||
if (hca.StereoBandCount > 0 && channelsPerTrack > 1)
|
||||
{
|
||||
for (int i = 0; i < hca.TrackCount; i++)
|
||||
{
|
||||
switch (channelsPerTrack)
|
||||
{
|
||||
case 2:
|
||||
case 3:
|
||||
channelTypes[0] = ChannelType.StereoPrimary;
|
||||
channelTypes[1] = ChannelType.StereoSecondary;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
channelTypes[0] = ChannelType.StereoPrimary;
|
||||
channelTypes[1] = ChannelType.StereoSecondary;
|
||||
if (hca.ChannelConfig == 0)
|
||||
{
|
||||
channelTypes[2] = ChannelType.StereoPrimary;
|
||||
channelTypes[3] = ChannelType.StereoSecondary;
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
channelTypes[0] = ChannelType.StereoPrimary;
|
||||
channelTypes[1] = ChannelType.StereoSecondary;
|
||||
if (hca.ChannelConfig <= 2)
|
||||
{
|
||||
channelTypes[3] = ChannelType.StereoPrimary;
|
||||
channelTypes[4] = ChannelType.StereoSecondary;
|
||||
}
|
||||
break;
|
||||
|
||||
case 6:
|
||||
case 7:
|
||||
channelTypes[0] = ChannelType.StereoPrimary;
|
||||
channelTypes[1] = ChannelType.StereoSecondary;
|
||||
channelTypes[4] = ChannelType.StereoPrimary;
|
||||
channelTypes[5] = ChannelType.StereoSecondary;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
channelTypes[0] = ChannelType.StereoPrimary;
|
||||
channelTypes[1] = ChannelType.StereoSecondary;
|
||||
channelTypes[4] = ChannelType.StereoPrimary;
|
||||
channelTypes[5] = ChannelType.StereoSecondary;
|
||||
channelTypes[6] = ChannelType.StereoPrimary;
|
||||
channelTypes[7] = ChannelType.StereoSecondary;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hca.Channels = new Channel[hca.ChannelCount];
|
||||
for (int i = 0; i < hca.ChannelCount; i++)
|
||||
{
|
||||
hca.Channels[i] = new Channel
|
||||
{
|
||||
Type = channelTypes[i],
|
||||
CodedCount =
|
||||
channelTypes[i] != ChannelType.StereoSecondary ?
|
||||
hca.BaseBandCount + hca.StereoBandCount :
|
||||
hca.BaseBandCount
|
||||
};
|
||||
}
|
||||
|
||||
hca.Random = DefaultRandom;
|
||||
|
||||
if (hca.MsStereo > 0) throw new InvalidDataException();
|
||||
if (hca.HfrGroupCount > 0 && hca.Version == Version300)
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
private static int HeaderCeil2(int a, int b)
|
||||
{
|
||||
if (b < 1) return 0;
|
||||
return a / b + ((a % b) > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
private static uint StringToUInt32(string value)
|
||||
{
|
||||
uint result = 0;
|
||||
int bytePos = 3;
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
result |= (uint)(value[i] << 8 * bytePos--);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 366eb88aab2749c40bdd2255f763b075
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace ClHcaSharp
|
||||
{
|
||||
internal static class JaggedArray
|
||||
{
|
||||
public static T CreateJaggedArray<T>(params int[] lengths)
|
||||
{
|
||||
return (T)InitJaggedArray(typeof(T).GetElementType(), lengths, 0);
|
||||
}
|
||||
|
||||
private static object InitJaggedArray(Type type, int[] lengths, int arrayIndex)
|
||||
{
|
||||
Array array = Array.CreateInstance(type, lengths[arrayIndex]);
|
||||
Type subElementType = type.GetElementType();
|
||||
|
||||
if (type.HasElementType)
|
||||
{
|
||||
for (int i = 0; i < lengths[arrayIndex]; i++)
|
||||
{
|
||||
array.SetValue(InitJaggedArray(subElementType, lengths, arrayIndex + 1), i);
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b866b2adde0b7ae4dbeaeb482c11f042
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,335 @@
|
||||
using Assets.Scripts.Audio.CriWareFormats.Common;
|
||||
using System;
|
||||
|
||||
namespace ClHcaSharp
|
||||
{
|
||||
internal static class Tables
|
||||
{
|
||||
public static void InitializeTables()
|
||||
{
|
||||
for (int i = 0; i < DequantizerScalingTableHex.Length; i++)
|
||||
{
|
||||
DequantizerScalingTable[i] = BinaryPrimitives.Int32BitsToSingle((int)DequantizerScalingTableHex[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < DequantizerRangeTableHex.Length; i++)
|
||||
{
|
||||
DequantizerRangeTable[i] = BinaryPrimitives.Int32BitsToSingle((int)DequantizerRangeTableHex[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ScaleConversionTableHex.Length; i++)
|
||||
{
|
||||
ScaleConversionTable[i] = BinaryPrimitives.Int32BitsToSingle((int)ScaleConversionTableHex[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < IntensityRatioTableHex.Length; i++)
|
||||
{
|
||||
IntensityRatioTable[i] = BinaryPrimitives.Int32BitsToSingle((int)IntensityRatioTableHex[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < SinTableHex.Length; i++)
|
||||
{
|
||||
SinTable[i] = new float[64];
|
||||
|
||||
for (int j = 0; j < SinTableHex[i].Length; j++)
|
||||
{
|
||||
SinTable[i][j] = BinaryPrimitives.Int32BitsToSingle((int)SinTableHex[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < CosTableHex.Length; i++)
|
||||
{
|
||||
CosTable[i] = new float[64];
|
||||
|
||||
for (int j = 0; j < CosTableHex[i].Length; j++)
|
||||
{
|
||||
CosTable[i][j] = BinaryPrimitives.Int32BitsToSingle((int)CosTableHex[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ImdctWindowHex.Length; i++)
|
||||
{
|
||||
ImdctWindow[i] = BinaryPrimitives.Int32BitsToSingle((int)ImdctWindowHex[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly byte[] InvertTable = new byte[66]
|
||||
{
|
||||
14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12,
|
||||
12, 12, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 9,
|
||||
9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 7, 6, 6, 5, 4,
|
||||
4, 4, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1
|
||||
};
|
||||
|
||||
public static readonly uint[] DequantizerScalingTableHex = new uint[64]
|
||||
{
|
||||
0x342A8D26, 0x34633F89, 0x3497657D, 0x34C9B9BE, 0x35066491, 0x353311C4, 0x356E9910, 0x359EF532,
|
||||
0x35D3CCF1, 0x360D1ADF, 0x363C034A, 0x367A83B3, 0x36A6E595, 0x36DE60F5, 0x371426FF, 0x3745672A,
|
||||
0x37838359, 0x37AF3B79, 0x37E97C38, 0x381B8D3A, 0x384F4319, 0x388A14D5, 0x38B7FBF0, 0x38F5257D,
|
||||
0x3923520F, 0x39599D16, 0x3990FA4D, 0x39C12C4D, 0x3A00B1ED, 0x3A2B7A3A, 0x3A647B6D, 0x3A9837F0,
|
||||
0x3ACAD226, 0x3B071F62, 0x3B340AAF, 0x3B6FE4BA, 0x3B9FD228, 0x3BD4F35B, 0x3C0DDF04, 0x3C3D08A4,
|
||||
0x3C7BDFED, 0x3CA7CD94, 0x3CDF9613, 0x3D14F4F0, 0x3D467991, 0x3D843A29, 0x3DB02F0E, 0x3DEAC0C7,
|
||||
0x3E1C6573, 0x3E506334, 0x3E8AD4C6, 0x3EB8FBAF, 0x3EF67A41, 0x3F243516, 0x3F5ACB94, 0x3F91C3D3,
|
||||
0x3FC238D2, 0x400164D2, 0x402C6897, 0x4065B907, 0x40990B88, 0x40CBEC15, 0x4107DB35, 0x413504F3
|
||||
};
|
||||
|
||||
public static readonly float[] DequantizerScalingTable = new float[64];
|
||||
|
||||
|
||||
public static readonly uint[] DequantizerRangeTableHex = new uint[16]
|
||||
{
|
||||
0x3F800000, 0x3F2AAAAB, 0x3ECCCCCD, 0x3E924925, 0x3E638E39, 0x3E3A2E8C, 0x3E1D89D9, 0x3E088889,
|
||||
0x3D842108, 0x3D020821, 0x3C810204, 0x3C008081, 0x3B804020, 0x3B002008, 0x3A801002, 0x3A000801
|
||||
};
|
||||
|
||||
public static readonly float[] DequantizerRangeTable = new float[16];
|
||||
|
||||
public static readonly byte[] MaxBitTable = new byte[16]
|
||||
{
|
||||
0, 2, 3, 3, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12
|
||||
};
|
||||
|
||||
public static readonly byte[] ReadBitTable = new byte[128]
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
2, 2, 2, 2, 2, 2, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
2, 2, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4,
|
||||
3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
|
||||
};
|
||||
|
||||
public static readonly float[] ReadValueTable = new float[128]
|
||||
{
|
||||
+0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f,
|
||||
+0.0f, +0.0f, +1.0f, -1.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f,
|
||||
+0.0f, +0.0f, +1.0f, +1.0f, -1.0f, -1.0f, +2.0f, -2.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f,
|
||||
+0.0f, +0.0f, +1.0f, -1.0f, +2.0f, -2.0f, +3.0f, -3.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f,
|
||||
+0.0f, +0.0f, +1.0f, +1.0f, -1.0f, -1.0f, +2.0f, +2.0f, -2.0f, -2.0f, +3.0f, +3.0f, -3.0f, -3.0f, +4.0f, -4.0f,
|
||||
+0.0f, +0.0f, +1.0f, +1.0f, -1.0f, -1.0f, +2.0f, +2.0f, -2.0f, -2.0f, +3.0f, -3.0f, +4.0f, -4.0f, +5.0f, -5.0f,
|
||||
+0.0f, +0.0f, +1.0f, +1.0f, -1.0f, -1.0f, +2.0f, -2.0f, +3.0f, -3.0f, +4.0f, -4.0f, +5.0f, -5.0f, +6.0f, -6.0f,
|
||||
+0.0f, +0.0f, +1.0f, -1.0f, +2.0f, -2.0f, +3.0f, -3.0f, +4.0f, -4.0f, +5.0f, -5.0f, +6.0f, -6.0f, +7.0f, -7.0f
|
||||
};
|
||||
|
||||
public static readonly uint[] ScaleConversionTableHex = new uint[128]
|
||||
{
|
||||
0x00000000, 0x32A0B051, 0x32D61B5E, 0x330EA43A, 0x333E0F68, 0x337D3E0C, 0x33A8B6D5, 0x33E0CCDF,
|
||||
0x3415C3FF, 0x34478D75, 0x3484F1F6, 0x34B123F6, 0x34EC0719, 0x351D3EDA, 0x355184DF, 0x358B95C2,
|
||||
0x35B9FCD2, 0x35F7D0DF, 0x36251958, 0x365BFBB8, 0x36928E72, 0x36C346CD, 0x370218AF, 0x372D583F,
|
||||
0x3766F85B, 0x3799E046, 0x37CD078C, 0x3808980F, 0x38360094, 0x38728177, 0x38A18FAF, 0x38D744FD,
|
||||
0x390F6A81, 0x393F179A, 0x397E9E11, 0x39A9A15B, 0x39E2055B, 0x3A16942D, 0x3A48A2D8, 0x3A85AAC3,
|
||||
0x3AB21A32, 0x3AED4F30, 0x3B1E196E, 0x3B52A81E, 0x3B8C57CA, 0x3BBAFF5B, 0x3BF9295A, 0x3C25FED7,
|
||||
0x3C5D2D82, 0x3C935A2B, 0x3CC4563F, 0x3D02CD87, 0x3D2E4934, 0x3D68396A, 0x3D9AB62B, 0x3DCE248C,
|
||||
0x3E0955EE, 0x3E36FD92, 0x3E73D290, 0x3EA27043, 0x3ED87039, 0x3F1031DC, 0x3F40213B, 0x3F800000,
|
||||
0x3FAA8D26, 0x3FE33F89, 0x4017657D, 0x4049B9BE, 0x40866491, 0x40B311C4, 0x40EE9910, 0x411EF532,
|
||||
0x4153CCF1, 0x418D1ADF, 0x41BC034A, 0x41FA83B3, 0x4226E595, 0x425E60F5, 0x429426FF, 0x42C5672A,
|
||||
0x43038359, 0x432F3B79, 0x43697C38, 0x439B8D3A, 0x43CF4319, 0x440A14D5, 0x4437FBF0, 0x4475257D,
|
||||
0x44A3520F, 0x44D99D16, 0x4510FA4D, 0x45412C4D, 0x4580B1ED, 0x45AB7A3A, 0x45E47B6D, 0x461837F0,
|
||||
0x464AD226, 0x46871F62, 0x46B40AAF, 0x46EFE4BA, 0x471FD228, 0x4754F35B, 0x478DDF04, 0x47BD08A4,
|
||||
0x47FBDFED, 0x4827CD94, 0x485F9613, 0x4894F4F0, 0x48C67991, 0x49043A29, 0x49302F0E, 0x496AC0C7,
|
||||
0x499C6573, 0x49D06334, 0x4A0AD4C6, 0x4A38FBAF, 0x4A767A41, 0x4AA43516, 0x4ADACB94, 0x4B11C3D3,
|
||||
0x4B4238D2, 0x4B8164D2, 0x4BAC6897, 0x4BE5B907, 0x4C190B88, 0x4C4BEC15, 0x00000000, 0x00000000
|
||||
};
|
||||
|
||||
public static float[] ScaleConversionTable = new float[128];
|
||||
|
||||
public static readonly uint[] IntensityRatioTableHex = new uint[16]
|
||||
{
|
||||
0x40000000, 0x3FEDB6DB, 0x3FDB6DB7, 0x3FC92492, 0x3FB6DB6E, 0x3FA49249, 0x3F924925, 0x3F800000,
|
||||
0x3F5B6DB7, 0x3F36DB6E, 0x3F124925, 0x3EDB6DB7, 0x3E924925, 0x3E124925, 0x00000000, 0x00000000
|
||||
};
|
||||
|
||||
public static float[] IntensityRatioTable = new float[16];
|
||||
|
||||
public static readonly uint[][] SinTableHex = new uint[7][]
|
||||
{
|
||||
new uint[64]
|
||||
{
|
||||
0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75,
|
||||
0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75,
|
||||
0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75,
|
||||
0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75,
|
||||
0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75,
|
||||
0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75,
|
||||
0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75,
|
||||
0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75
|
||||
},
|
||||
new uint[64]
|
||||
{
|
||||
0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31,
|
||||
0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31,
|
||||
0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31,
|
||||
0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31,
|
||||
0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31,
|
||||
0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31,
|
||||
0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31,
|
||||
0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31
|
||||
},
|
||||
new uint[64]
|
||||
{
|
||||
0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403,
|
||||
0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403,
|
||||
0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403,
|
||||
0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403,
|
||||
0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403,
|
||||
0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403,
|
||||
0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403,
|
||||
0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403
|
||||
},
|
||||
new uint[64]
|
||||
{
|
||||
0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9,
|
||||
0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9,
|
||||
0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9,
|
||||
0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9,
|
||||
0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9,
|
||||
0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9,
|
||||
0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9,
|
||||
0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9
|
||||
},
|
||||
new uint[64]
|
||||
{
|
||||
0x3F7FEC43, 0x3F7F4E6D, 0x3F7E1324, 0x3F7C3B28, 0x3F79C79D, 0x3F76BA07, 0x3F731447, 0x3F6ED89E,
|
||||
0x3F6A09A7, 0x3F64AA59, 0x3F5EBE05, 0x3F584853, 0x3F514D3D, 0x3F49D112, 0x3F41D870, 0x3F396842,
|
||||
0x3F7FEC43, 0x3F7F4E6D, 0x3F7E1324, 0x3F7C3B28, 0x3F79C79D, 0x3F76BA07, 0x3F731447, 0x3F6ED89E,
|
||||
0x3F6A09A7, 0x3F64AA59, 0x3F5EBE05, 0x3F584853, 0x3F514D3D, 0x3F49D112, 0x3F41D870, 0x3F396842,
|
||||
0x3F7FEC43, 0x3F7F4E6D, 0x3F7E1324, 0x3F7C3B28, 0x3F79C79D, 0x3F76BA07, 0x3F731447, 0x3F6ED89E,
|
||||
0x3F6A09A7, 0x3F64AA59, 0x3F5EBE05, 0x3F584853, 0x3F514D3D, 0x3F49D112, 0x3F41D870, 0x3F396842,
|
||||
0x3F7FEC43, 0x3F7F4E6D, 0x3F7E1324, 0x3F7C3B28, 0x3F79C79D, 0x3F76BA07, 0x3F731447, 0x3F6ED89E,
|
||||
0x3F6A09A7, 0x3F64AA59, 0x3F5EBE05, 0x3F584853, 0x3F514D3D, 0x3F49D112, 0x3F41D870, 0x3F396842
|
||||
},
|
||||
new uint[64]
|
||||
{
|
||||
0x3F7FFB11, 0x3F7FD397, 0x3F7F84AB, 0x3F7F0E58, 0x3F7E70B0, 0x3F7DABCC, 0x3F7CBFC9, 0x3F7BACCD,
|
||||
0x3F7A7302, 0x3F791298, 0x3F778BC5, 0x3F75DEC6, 0x3F740BDD, 0x3F721352, 0x3F6FF573, 0x3F6DB293,
|
||||
0x3F6B4B0C, 0x3F68BF3C, 0x3F660F88, 0x3F633C5A, 0x3F604621, 0x3F5D2D53, 0x3F59F26A, 0x3F5695E5,
|
||||
0x3F531849, 0x3F4F7A1F, 0x3F4BBBF8, 0x3F47DE65, 0x3F43E200, 0x3F3FC767, 0x3F3B8F3B, 0x3F373A23,
|
||||
0x3F7FFB11, 0x3F7FD397, 0x3F7F84AB, 0x3F7F0E58, 0x3F7E70B0, 0x3F7DABCC, 0x3F7CBFC9, 0x3F7BACCD,
|
||||
0x3F7A7302, 0x3F791298, 0x3F778BC5, 0x3F75DEC6, 0x3F740BDD, 0x3F721352, 0x3F6FF573, 0x3F6DB293,
|
||||
0x3F6B4B0C, 0x3F68BF3C, 0x3F660F88, 0x3F633C5A, 0x3F604621, 0x3F5D2D53, 0x3F59F26A, 0x3F5695E5,
|
||||
0x3F531849, 0x3F4F7A1F, 0x3F4BBBF8, 0x3F47DE65, 0x3F43E200, 0x3F3FC767, 0x3F3B8F3B, 0x3F373A23
|
||||
},
|
||||
new uint[64]
|
||||
{
|
||||
0x3F7FFEC4, 0x3F7FF4E6, 0x3F7FE129, 0x3F7FC38F, 0x3F7F9C18, 0x3F7F6AC7, 0x3F7F2F9D, 0x3F7EEA9D,
|
||||
0x3F7E9BC9, 0x3F7E4323, 0x3F7DE0B1, 0x3F7D7474, 0x3F7CFE73, 0x3F7C7EB0, 0x3F7BF531, 0x3F7B61FC,
|
||||
0x3F7AC516, 0x3F7A1E84, 0x3F796E4E, 0x3F78B47B, 0x3F77F110, 0x3F772417, 0x3F764D97, 0x3F756D97,
|
||||
0x3F748422, 0x3F73913F, 0x3F7294F8, 0x3F718F57, 0x3F708066, 0x3F6F6830, 0x3F6E46BE, 0x3F6D1C1D,
|
||||
0x3F6BE858, 0x3F6AAB7B, 0x3F696591, 0x3F6816A8, 0x3F66BECC, 0x3F655E0B, 0x3F63F473, 0x3F628210,
|
||||
0x3F6106F2, 0x3F5F8327, 0x3F5DF6BE, 0x3F5C61C7, 0x3F5AC450, 0x3F591E6A, 0x3F577026, 0x3F55B993,
|
||||
0x3F53FAC3, 0x3F5233C6, 0x3F5064AF, 0x3F4E8D90, 0x3F4CAE79, 0x3F4AC77F, 0x3F48D8B3, 0x3F46E22A,
|
||||
0x3F44E3F5, 0x3F42DE29, 0x3F40D0DA, 0x3F3EBC1B, 0x3F3CA003, 0x3F3A7CA4, 0x3F385216, 0x3F36206C
|
||||
}
|
||||
};
|
||||
|
||||
public static float[][] SinTable = new float[7][];
|
||||
|
||||
public static readonly uint[][] CosTableHex = new uint[7][]
|
||||
{
|
||||
new uint[64]
|
||||
{
|
||||
0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4,
|
||||
0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4,
|
||||
0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4,
|
||||
0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4,
|
||||
0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4,
|
||||
0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4,
|
||||
0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4,
|
||||
0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4
|
||||
},
|
||||
new uint[64]
|
||||
{
|
||||
0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA,
|
||||
0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA,
|
||||
0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA,
|
||||
0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA,
|
||||
0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA,
|
||||
0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA,
|
||||
0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA,
|
||||
0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA
|
||||
},
|
||||
new uint[64]
|
||||
{
|
||||
0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799, 0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799,
|
||||
0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799, 0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799,
|
||||
0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799, 0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799,
|
||||
0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799, 0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799,
|
||||
0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799, 0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799,
|
||||
0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799, 0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799,
|
||||
0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799, 0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799,
|
||||
0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799, 0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799
|
||||
},
|
||||
new uint[64]
|
||||
{
|
||||
0xBD48FB30, 0xBE164083, 0xBE78CFCC, 0xBEAC7CD4, 0xBEDAE880, 0xBF039C3D, 0xBF187FC0, 0xBF2BEB4A,
|
||||
0x3D48FB30, 0x3E164083, 0x3E78CFCC, 0x3EAC7CD4, 0x3EDAE880, 0x3F039C3D, 0x3F187FC0, 0x3F2BEB4A,
|
||||
0x3D48FB30, 0x3E164083, 0x3E78CFCC, 0x3EAC7CD4, 0x3EDAE880, 0x3F039C3D, 0x3F187FC0, 0x3F2BEB4A,
|
||||
0xBD48FB30, 0xBE164083, 0xBE78CFCC, 0xBEAC7CD4, 0xBEDAE880, 0xBF039C3D, 0xBF187FC0, 0xBF2BEB4A,
|
||||
0x3D48FB30, 0x3E164083, 0x3E78CFCC, 0x3EAC7CD4, 0x3EDAE880, 0x3F039C3D, 0x3F187FC0, 0x3F2BEB4A,
|
||||
0xBD48FB30, 0xBE164083, 0xBE78CFCC, 0xBEAC7CD4, 0xBEDAE880, 0xBF039C3D, 0xBF187FC0, 0xBF2BEB4A,
|
||||
0xBD48FB30, 0xBE164083, 0xBE78CFCC, 0xBEAC7CD4, 0xBEDAE880, 0xBF039C3D, 0xBF187FC0, 0xBF2BEB4A,
|
||||
0x3D48FB30, 0x3E164083, 0x3E78CFCC, 0x3EAC7CD4, 0x3EDAE880, 0x3F039C3D, 0x3F187FC0, 0x3F2BEB4A
|
||||
},
|
||||
new uint[64]
|
||||
{
|
||||
0xBCC90AB0, 0xBD96A905, 0xBDFAB273, 0xBE2F10A2, 0xBE605C13, 0xBE888E93, 0xBEA09AE5, 0xBEB8442A,
|
||||
0xBECF7BCA, 0xBEE63375, 0xBEFC5D27, 0xBF08F59B, 0xBF13682A, 0xBF1D7FD1, 0xBF273656, 0xBF3085BB,
|
||||
0x3CC90AB0, 0x3D96A905, 0x3DFAB273, 0x3E2F10A2, 0x3E605C13, 0x3E888E93, 0x3EA09AE5, 0x3EB8442A,
|
||||
0x3ECF7BCA, 0x3EE63375, 0x3EFC5D27, 0x3F08F59B, 0x3F13682A, 0x3F1D7FD1, 0x3F273656, 0x3F3085BB,
|
||||
0x3CC90AB0, 0x3D96A905, 0x3DFAB273, 0x3E2F10A2, 0x3E605C13, 0x3E888E93, 0x3EA09AE5, 0x3EB8442A,
|
||||
0x3ECF7BCA, 0x3EE63375, 0x3EFC5D27, 0x3F08F59B, 0x3F13682A, 0x3F1D7FD1, 0x3F273656, 0x3F3085BB,
|
||||
0xBCC90AB0, 0xBD96A905, 0xBDFAB273, 0xBE2F10A2, 0xBE605C13, 0xBE888E93, 0xBEA09AE5, 0xBEB8442A,
|
||||
0xBECF7BCA, 0xBEE63375, 0xBEFC5D27, 0xBF08F59B, 0xBF13682A, 0xBF1D7FD1, 0xBF273656, 0xBF3085BB
|
||||
},
|
||||
new uint[64]
|
||||
{
|
||||
0xBC490E90, 0xBD16C32C, 0xBD7B2B74, 0xBDAFB680, 0xBDE1BC2E, 0xBE09CF86, 0xBE22ABB6, 0xBE3B6ECF,
|
||||
0xBE541501, 0xBE6C9A7F, 0xBE827DC0, 0xBE8E9A22, 0xBE9AA086, 0xBEA68F12, 0xBEB263EF, 0xBEBE1D4A,
|
||||
0xBEC9B953, 0xBED53641, 0xBEE0924F, 0xBEEBCBBB, 0xBEF6E0CB, 0xBF00E7E4, 0xBF064B82, 0xBF0B9A6B,
|
||||
0xBF10D3CD, 0xBF15F6D9, 0xBF1B02C6, 0xBF1FF6CB, 0xBF24D225, 0xBF299415, 0xBF2E3BDE, 0xBF32C8C9,
|
||||
0x3C490E90, 0x3D16C32C, 0x3D7B2B74, 0x3DAFB680, 0x3DE1BC2E, 0x3E09CF86, 0x3E22ABB6, 0x3E3B6ECF,
|
||||
0x3E541501, 0x3E6C9A7F, 0x3E827DC0, 0x3E8E9A22, 0x3E9AA086, 0x3EA68F12, 0x3EB263EF, 0x3EBE1D4A,
|
||||
0x3EC9B953, 0x3ED53641, 0x3EE0924F, 0x3EEBCBBB, 0x3EF6E0CB, 0x3F00E7E4, 0x3F064B82, 0x3F0B9A6B,
|
||||
0x3F10D3CD, 0x3F15F6D9, 0x3F1B02C6, 0x3F1FF6CB, 0x3F24D225, 0x3F299415, 0x3F2E3BDE, 0x3F32C8C9
|
||||
},
|
||||
new uint[64]
|
||||
{
|
||||
0xBBC90F88, 0xBC96C9B6, 0xBCFB49BA, 0xBD2FE007, 0xBD621469, 0xBD8A200A, 0xBDA3308C, 0xBDBC3AC3,
|
||||
0xBDD53DB9, 0xBDEE3876, 0xBE039502, 0xBE1008B7, 0xBE1C76DE, 0xBE28DEFC, 0xBE354098, 0xBE419B37,
|
||||
0xBE4DEE60, 0xBE5A3997, 0xBE667C66, 0xBE72B651, 0xBE7EE6E1, 0xBE8586CE, 0xBE8B9507, 0xBE919DDD,
|
||||
0xBE97A117, 0xBE9D9E78, 0xBEA395C5, 0xBEA986C4, 0xBEAF713A, 0xBEB554EC, 0xBEBB31A0, 0xBEC1071E,
|
||||
0xBEC6D529, 0xBECC9B8B, 0xBED25A09, 0xBED8106B, 0xBEDDBE79, 0xBEE363FA, 0xBEE900B7, 0xBEEE9479,
|
||||
0xBEF41F07, 0xBEF9A02D, 0xBEFF17B2, 0xBF0242B1, 0xBF04F484, 0xBF07A136, 0xBF0A48AD, 0xBF0CEAD0,
|
||||
0xBF0F8784, 0xBF121EB0, 0xBF14B039, 0xBF173C07, 0xBF19C200, 0xBF1C420C, 0xBF1EBC12, 0xBF212FF9,
|
||||
0xBF239DA9, 0xBF26050A, 0xBF286605, 0xBF2AC082, 0xBF2D1469, 0xBF2F61A5, 0xBF31A81D, 0xBF33E7BC
|
||||
}
|
||||
};
|
||||
|
||||
public static float[][] CosTable = new float[7][];
|
||||
|
||||
public static readonly uint[] ImdctWindowHex = new uint[128]
|
||||
{
|
||||
0x3A3504F0, 0x3B0183B8, 0x3B70C538, 0x3BBB9268, 0x3C04A809, 0x3C308200, 0x3C61284C, 0x3C8B3F17,
|
||||
0x3CA83992, 0x3CC77FBD, 0x3CE91110, 0x3D0677CD, 0x3D198FC4, 0x3D2DD35C, 0x3D434643, 0x3D59ECC1,
|
||||
0x3D71CBA8, 0x3D85741E, 0x3D92A413, 0x3DA078B4, 0x3DAEF522, 0x3DBE1C9E, 0x3DCDF27B, 0x3DDE7A1D,
|
||||
0x3DEFB6ED, 0x3E00D62B, 0x3E0A2EDA, 0x3E13E72A, 0x3E1E00B1, 0x3E287CF2, 0x3E335D55, 0x3E3EA321,
|
||||
0x3E4A4F75, 0x3E56633F, 0x3E62DF37, 0x3E6FC3D1, 0x3E7D1138, 0x3E8563A2, 0x3E8C72B7, 0x3E93B561,
|
||||
0x3E9B2AEF, 0x3EA2D26F, 0x3EAAAAAB, 0x3EB2B222, 0x3EBAE706, 0x3EC34737, 0x3ECBD03D, 0x3ED47F46,
|
||||
0x3EDD5128, 0x3EE6425C, 0x3EEF4EFF, 0x3EF872D7, 0x3F00D4A9, 0x3F0576CA, 0x3F0A1D3B, 0x3F0EC548,
|
||||
0x3F136C25, 0x3F180EF2, 0x3F1CAAC2, 0x3F213CA2, 0x3F25C1A5, 0x3F2A36E7, 0x3F2E9998, 0x3F32E705,
|
||||
0xBF371C9E, 0xBF3B37FE, 0xBF3F36F2, 0xBF431780, 0xBF46D7E6, 0xBF4A76A4, 0xBF4DF27C, 0xBF514A6F,
|
||||
0xBF547DC5, 0xBF578C03, 0xBF5A74EE, 0xBF5D3887, 0xBF5FD707, 0xBF6250DA, 0xBF64A699, 0xBF66D908,
|
||||
0xBF68E90E, 0xBF6AD7B1, 0xBF6CA611, 0xBF6E5562, 0xBF6FE6E7, 0xBF715BEF, 0xBF72B5D1, 0xBF73F5E6,
|
||||
0xBF751D89, 0xBF762E13, 0xBF7728D7, 0xBF780F20, 0xBF78E234, 0xBF79A34C, 0xBF7A5397, 0xBF7AF439,
|
||||
0xBF7B8648, 0xBF7C0ACE, 0xBF7C82C8, 0xBF7CEF26, 0xBF7D50CB, 0xBF7DA88E, 0xBF7DF737, 0xBF7E3D86,
|
||||
0xBF7E7C2A, 0xBF7EB3CC, 0xBF7EE507, 0xBF7F106C, 0xBF7F3683, 0xBF7F57CA, 0xBF7F74B6, 0xBF7F8DB6,
|
||||
0xBF7FA32E, 0xBF7FB57B, 0xBF7FC4F6, 0xBF7FD1ED, 0xBF7FDCAD, 0xBF7FE579, 0xBF7FEC90, 0xBF7FF22E,
|
||||
0xBF7FF688, 0xBF7FF9D0, 0xBF7FFC32, 0xBF7FFDDA, 0xBF7FFEED, 0xBF7FFF8F, 0xBF7FFFDF, 0xBF7FFFFC
|
||||
};
|
||||
|
||||
public static float[] ImdctWindow = new float[128];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50f6f92edea4fac4f9241320ffa4d4c3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d19d8ce2b0439342b93e9026e190de3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,702 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 612a4d57772b3ad429b40eafbe7d0e3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,57 @@
|
||||
using CriWareFormats.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace CriWareFormats
|
||||
{
|
||||
public sealed class AcbReader : IDisposable
|
||||
{
|
||||
private readonly Stream outerStream;
|
||||
private readonly long positionOffset;
|
||||
private readonly uint awbOffset;
|
||||
private readonly uint awbLength;
|
||||
|
||||
private readonly AcbNameLoader acbNameLoader;
|
||||
|
||||
public AcbReader(Stream acbStream) : this(acbStream, 0) { }
|
||||
|
||||
public AcbReader(Stream acbStream, long offset)
|
||||
{
|
||||
outerStream = acbStream;
|
||||
positionOffset = offset;
|
||||
|
||||
acbStream.Position = positionOffset;
|
||||
|
||||
UtfTable utfTable = new UtfTable(acbStream, (uint)offset, out int rows, out string name);
|
||||
|
||||
if (rows != 1 || !name.Equals("Header"))
|
||||
throw new InvalidDataException("No Header table.");
|
||||
|
||||
if (!utfTable.Query(0, "AwbFile", out VLData awbValueData))
|
||||
throw new InvalidDataException("No embedded AWB file.");
|
||||
|
||||
awbOffset = awbValueData.Offset;
|
||||
awbLength = awbValueData.Size;
|
||||
|
||||
outerStream.Position = positionOffset;
|
||||
acbNameLoader = new AcbNameLoader(outerStream);
|
||||
}
|
||||
|
||||
public AwbReader GetAwb()
|
||||
{
|
||||
return new AwbReader(new SpliceStream(outerStream, awbOffset, awbLength), true);
|
||||
}
|
||||
|
||||
public string GetWaveName(int waveId, int port, bool memory)
|
||||
{
|
||||
outerStream.Position = positionOffset;
|
||||
return acbNameLoader.LoadWaveName(waveId, port, memory);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
outerStream.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 407dbae8a66935f499cad063a996b044
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,139 @@
|
||||
using CriWareFormats.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace CriWareFormats
|
||||
{
|
||||
internal class Row
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public ushort Id { get; set; }
|
||||
}
|
||||
|
||||
public struct Wave
|
||||
{
|
||||
public int WaveId { get; set; }
|
||||
public long Offset { get; set; }
|
||||
public long Length { get; set; }
|
||||
}
|
||||
|
||||
public sealed class AwbReader : IDisposable
|
||||
{
|
||||
private readonly BinaryReader binaryReader;
|
||||
|
||||
private readonly byte offsetSize;
|
||||
private readonly int totalSubsongs;
|
||||
private readonly ushort alignment;
|
||||
private readonly ushort subkey;
|
||||
|
||||
private readonly List<Row> cueNames;
|
||||
private readonly List<Wave> waves;
|
||||
|
||||
public AwbReader(Stream awbStream, bool isEmbedded) : this(awbStream, 0)
|
||||
{
|
||||
IsEmbedded = isEmbedded;
|
||||
}
|
||||
|
||||
public AwbReader(Stream awbStream) : this(awbStream, 0) { }
|
||||
|
||||
public AwbReader(Stream awbStream, long positionOffset)
|
||||
{
|
||||
binaryReader = new BinaryReader(awbStream);
|
||||
|
||||
binaryReader.BaseStream.Position = positionOffset;
|
||||
|
||||
if (!binaryReader.ReadChars(4).SequenceEqual("AFS2".ToCharArray()))
|
||||
throw new InvalidDataException("Incorrect magic.");
|
||||
|
||||
binaryReader.BaseStream.Position += 0x1;
|
||||
offsetSize = binaryReader.ReadByte();
|
||||
binaryReader.BaseStream.Position += 0x2;
|
||||
totalSubsongs = binaryReader.ReadInt32();
|
||||
alignment = binaryReader.ReadUInt16();
|
||||
subkey = binaryReader.ReadUInt16();
|
||||
|
||||
waves = new List<Wave>(totalSubsongs);
|
||||
|
||||
for (int subsong = 1; subsong <= totalSubsongs; subsong++)
|
||||
{
|
||||
long offset = 0x10;
|
||||
|
||||
long waveIdOffset = offset + (subsong - 1) * 0x2;
|
||||
|
||||
binaryReader.BaseStream.Position = waveIdOffset;
|
||||
|
||||
int waveId = binaryReader.ReadUInt16();
|
||||
|
||||
offset += totalSubsongs * 0x2;
|
||||
|
||||
long subfileOffset = 0;
|
||||
long subfileNext = 0;
|
||||
long fileSize = binaryReader.BaseStream.Length;
|
||||
|
||||
offset += (subsong - 1) * offsetSize;
|
||||
|
||||
binaryReader.BaseStream.Position = offset;
|
||||
|
||||
switch (offsetSize)
|
||||
{
|
||||
case 0x4:
|
||||
subfileOffset = binaryReader.ReadUInt32();
|
||||
subfileNext = binaryReader.ReadUInt32();
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
subfileOffset = binaryReader.ReadUInt16();
|
||||
subfileNext = binaryReader.ReadUInt16();
|
||||
break;
|
||||
|
||||
default:
|
||||
Fail();
|
||||
break;
|
||||
}
|
||||
|
||||
subfileOffset += subfileOffset % alignment > 0 ?
|
||||
alignment - subfileOffset % alignment : 0;
|
||||
subfileNext += subfileNext % alignment > 0 && subfileNext < fileSize ?
|
||||
alignment - subfileNext % alignment : 0;
|
||||
long subfileSize = subfileNext - subfileOffset;
|
||||
|
||||
waves.Add(new Wave()
|
||||
{
|
||||
WaveId = waveId,
|
||||
Offset = subfileOffset,
|
||||
Length = subfileSize
|
||||
});
|
||||
|
||||
//if (CueNames.Count > 0) Console.Write("{0,-36}", CueNames.Where(row => row.Index == waveId).First().Name);
|
||||
//else Console.Write(waveId);
|
||||
|
||||
//Console.WriteLine(subfileOffset);
|
||||
}
|
||||
|
||||
cueNames = new List<Row>();
|
||||
}
|
||||
|
||||
public ushort Subkey => subkey;
|
||||
|
||||
public bool IsEmbedded { get; }
|
||||
|
||||
public List<Wave> Waves => waves;
|
||||
|
||||
public Stream GetWaveSubfileStream(Wave wave)
|
||||
{
|
||||
return new SpliceStream(binaryReader.BaseStream, wave.Offset, wave.Length);
|
||||
}
|
||||
|
||||
private static void Fail()
|
||||
{
|
||||
throw new Exception("Failure reading AWB file.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
binaryReader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e1fffc1a1dfe27459a35520448ccaf0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13c5ca40b071c634bb7e480a0479ad81
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Assets.Scripts.Audio.CriWareFormats.Common
|
||||
{
|
||||
public class BinaryPrimitives
|
||||
{
|
||||
internal static ushort ReverseEndianness(ushort le)
|
||||
{
|
||||
byte[] tmp = BitConverter.GetBytes(le);
|
||||
Array.Reverse(tmp);
|
||||
return BitConverter.ToUInt16(tmp,0);
|
||||
}
|
||||
|
||||
internal static int ReverseEndianness(int le)
|
||||
{
|
||||
byte[] tmp = BitConverter.GetBytes(le);
|
||||
Array.Reverse(tmp);
|
||||
return BitConverter.ToInt32(tmp, 0);
|
||||
}
|
||||
|
||||
internal static uint ReverseEndianness(uint le)
|
||||
{
|
||||
byte[] tmp = BitConverter.GetBytes(le);
|
||||
Array.Reverse(tmp);
|
||||
return BitConverter.ToUInt32(tmp, 0);
|
||||
}
|
||||
|
||||
internal static long ReverseEndianness(long le)
|
||||
{
|
||||
byte[] tmp = BitConverter.GetBytes(le);
|
||||
Array.Reverse(tmp);
|
||||
return BitConverter.ToInt64(tmp, 0);
|
||||
}
|
||||
|
||||
internal static ulong ReverseEndianness(ulong le)
|
||||
{
|
||||
byte[] tmp = BitConverter.GetBytes(le);
|
||||
Array.Reverse(tmp);
|
||||
return BitConverter.ToUInt64(tmp, 0);
|
||||
}
|
||||
|
||||
public static float Int32BitsToSingle(int m)
|
||||
{
|
||||
return BitConverter.ToSingle(BitConverter.GetBytes(m), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35c4bac6c1dd0664b975a22cf7ea445a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime;
|
||||
using Assets.Scripts.Audio.CriWareFormats.Common;
|
||||
|
||||
namespace CriWareFormats.Common
|
||||
{
|
||||
public class BinaryReaderEndian : BinaryReader
|
||||
|
||||
{
|
||||
public BinaryReaderEndian(Stream input) : base(input)
|
||||
{
|
||||
}
|
||||
|
||||
public short ReadInt16BE()
|
||||
{
|
||||
ushort le = base.ReadUInt16();
|
||||
return (short)BinaryPrimitives.ReverseEndianness(le);
|
||||
}
|
||||
|
||||
public ushort ReadUInt16BE()
|
||||
{
|
||||
ushort le = base.ReadUInt16();
|
||||
return BinaryPrimitives.ReverseEndianness(le);
|
||||
}
|
||||
|
||||
public int ReadInt32BE()
|
||||
{
|
||||
uint le = base.ReadUInt32();
|
||||
return (int)BinaryPrimitives.ReverseEndianness(le);
|
||||
}
|
||||
|
||||
public uint ReadUInt32BE()
|
||||
{
|
||||
uint le = base.ReadUInt32();
|
||||
return BinaryPrimitives.ReverseEndianness(le);
|
||||
}
|
||||
|
||||
public long ReadInt64BE()
|
||||
{
|
||||
ulong le = base.ReadUInt64();
|
||||
return (long)BinaryPrimitives.ReverseEndianness(le);
|
||||
}
|
||||
|
||||
public ulong ReadUInt64BE()
|
||||
{
|
||||
ulong le = base.ReadUInt64();
|
||||
return BinaryPrimitives.ReverseEndianness(le);
|
||||
}
|
||||
|
||||
public float ReadSingleBE()
|
||||
{
|
||||
float le = base.ReadSingle();
|
||||
byte[] floatBytes = BitConverter.GetBytes(le);
|
||||
byte[] reversed = new byte[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
reversed[i] = floatBytes[3 - i];
|
||||
}
|
||||
return BitConverter.ToSingle(reversed, 0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f17047d6b7698144588e1d5181473cf9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace CriWareFormats.Common
|
||||
{
|
||||
public static class Endian
|
||||
{
|
||||
public static ushort Reverse(ushort value)
|
||||
{
|
||||
return (ushort)(value << 8 | value >> 8);
|
||||
}
|
||||
|
||||
public static uint Reverse(uint value)
|
||||
{
|
||||
value = (value & 0x00FF00FFu) << 8 | (value & 0xFF00FF00u) >> 8;
|
||||
return (value & 0x0000FFFFu) << 16 | (value & 0xFFFF0000u) >> 16;
|
||||
}
|
||||
|
||||
public static ulong Reverse(ulong value)
|
||||
{
|
||||
value = (value & 0x00FF00FF00FF00FFu) << 8 | (value & 0xFF00FF00FF00FF00u) >> 8;
|
||||
value = (value & 0x0000FFFF0000FFFFu) << 16 | (value & 0xFFFF0000FFFF0000u) >> 16;
|
||||
return value << 32 | value >> 32;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfc4d0df7cc8c294ead855ed1ccc762d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace CriWareFormats
|
||||
{
|
||||
public class SpliceStream : Stream
|
||||
{
|
||||
private readonly Stream innerStream;
|
||||
private readonly long realPosition;
|
||||
|
||||
private long internalPosition;
|
||||
|
||||
private readonly object positionLock = new object();
|
||||
|
||||
public SpliceStream(Stream sourceStream, long offset, long length)
|
||||
{
|
||||
innerStream = sourceStream;
|
||||
realPosition = offset;
|
||||
internalPosition = 0;
|
||||
|
||||
Length = length;
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek => true;
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override long Length { get; }
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (positionLock)
|
||||
{
|
||||
return internalPosition;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (positionLock)
|
||||
{
|
||||
long checkValue = value + realPosition;
|
||||
if (value < 0 || value >= Length) throw new ArgumentOutOfRangeException(nameof(value));
|
||||
internalPosition = value;
|
||||
|
||||
if (innerStream.CanSeek) innerStream.Position = checkValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
lock (positionLock)
|
||||
{
|
||||
if (innerStream.CanRead)
|
||||
{
|
||||
long restore = innerStream.Position;
|
||||
innerStream.Position = realPosition + internalPosition;
|
||||
int read = innerStream.Read(buffer, offset, count);
|
||||
internalPosition += read;
|
||||
innerStream.Position = restore;
|
||||
return read;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
lock (positionLock)
|
||||
{
|
||||
switch (origin)
|
||||
{
|
||||
case SeekOrigin.Begin:
|
||||
if (offset >= Length) throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
internalPosition = offset;
|
||||
break;
|
||||
|
||||
case SeekOrigin.Current:
|
||||
if (internalPosition + offset >= Length) throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
internalPosition += offset;
|
||||
break;
|
||||
|
||||
case SeekOrigin.End:
|
||||
if (internalPosition - offset < 0) throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
internalPosition = Length;
|
||||
internalPosition -= offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return internalPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e2b71da36bdac14fae41451686dac14
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,435 @@
|
||||
using CriWareFormats.Common;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace CriWareFormats
|
||||
{
|
||||
[Flags]
|
||||
public enum ColumnFlag : byte
|
||||
{
|
||||
Name = 0x10,
|
||||
Default = 0x20,
|
||||
Row = 0x40,
|
||||
Undefined = 0x80
|
||||
}
|
||||
|
||||
public enum ColumnType : byte
|
||||
{
|
||||
Byte = 0x00,
|
||||
SByte = 0x01,
|
||||
UInt16 = 0x02,
|
||||
Int16 = 0x03,
|
||||
UInt32 = 0x04,
|
||||
Int32 = 0x05,
|
||||
UInt64 = 0x06,
|
||||
Int64 = 0x07,
|
||||
Float = 0x08,
|
||||
Double = 0x09,
|
||||
String = 0x0A,
|
||||
VLData = 0x0B,
|
||||
UInt128 = 0x0C,
|
||||
Undefined = 0xFF
|
||||
}
|
||||
|
||||
public struct Column
|
||||
{
|
||||
public ColumnFlag Flag;
|
||||
public ColumnType Type;
|
||||
public string Name;
|
||||
public uint Offset;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
public struct VLData
|
||||
{
|
||||
public uint Offset;
|
||||
public uint Size;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Offset: {Offset}, Length: {Size}";
|
||||
}
|
||||
}
|
||||
|
||||
public struct Result
|
||||
{
|
||||
public ColumnType Type;
|
||||
public object Value;
|
||||
}
|
||||
|
||||
public sealed class UtfTable
|
||||
{
|
||||
private readonly BinaryReaderEndian binaryReader;
|
||||
private readonly uint tableOffset;
|
||||
|
||||
private readonly uint tableSize;
|
||||
private readonly ushort version;
|
||||
private readonly ushort rowsOffset;
|
||||
private readonly uint stringsOffset;
|
||||
private readonly uint dataOffset;
|
||||
private readonly uint nameOffset;
|
||||
|
||||
private readonly ushort columns;
|
||||
private readonly ushort rowWidth;
|
||||
private readonly uint rows;
|
||||
|
||||
private readonly byte[] schemaBuffer;
|
||||
private readonly Column[] schema;
|
||||
|
||||
private readonly uint schemaOffset;
|
||||
private readonly uint schemaSize;
|
||||
private readonly uint rowsSize;
|
||||
private readonly uint dataSize;
|
||||
private readonly uint stringsSize;
|
||||
|
||||
private readonly byte[] stringTable;
|
||||
private readonly string tableName;
|
||||
|
||||
public UtfTable(Stream utfTableStream, out int utfTableRows, out string utfTableRowName) :
|
||||
this(utfTableStream, 0, out utfTableRows, out utfTableRowName)
|
||||
{ }
|
||||
|
||||
public UtfTable(Stream utfTableStream, uint offset) :
|
||||
this(utfTableStream, offset, out int _, out string _)
|
||||
{ }
|
||||
|
||||
public UtfTable(Stream utfTableStream, uint offset, out int utfTableRows, out string rowName)
|
||||
{
|
||||
binaryReader = new BinaryReaderEndian(utfTableStream);
|
||||
tableOffset = offset;
|
||||
|
||||
binaryReader.BaseStream.Position = offset;
|
||||
|
||||
if (!binaryReader.ReadChars(4).SequenceEqual("@UTF".ToCharArray()))
|
||||
throw new InvalidDataException("Incorrect magic.");
|
||||
tableSize = binaryReader.ReadUInt32BE() + 0x08;
|
||||
version = binaryReader.ReadUInt16BE();
|
||||
rowsOffset = (ushort)(binaryReader.ReadUInt16BE() + 0x08);
|
||||
stringsOffset = binaryReader.ReadUInt32BE() + 0x08;
|
||||
dataOffset = binaryReader.ReadUInt32BE() + 0x08;
|
||||
nameOffset = binaryReader.ReadUInt32BE();
|
||||
columns = binaryReader.ReadUInt16BE();
|
||||
rowWidth = binaryReader.ReadUInt16BE();
|
||||
rows = binaryReader.ReadUInt32BE();
|
||||
|
||||
schemaOffset = 0x20;
|
||||
schemaSize = rowsOffset - schemaOffset;
|
||||
rowsSize = stringsOffset - rowsOffset;
|
||||
stringsSize = dataOffset - stringsOffset;
|
||||
dataSize = tableSize - dataOffset;
|
||||
|
||||
if (version != 0x00 && version != 0x01)
|
||||
throw new InvalidDataException("Unknown @UTF version.");
|
||||
if (tableOffset + tableSize > utfTableStream.Length)
|
||||
throw new InvalidDataException("Table size exceeds bounds of file.");
|
||||
if (rowsOffset > tableSize || stringsOffset > tableSize || dataOffset > tableSize)
|
||||
throw new InvalidDataException("Offsets out of bounds.");
|
||||
if (stringsSize <= 0 || nameOffset > stringsSize)
|
||||
throw new InvalidDataException("Invalid string table size.");
|
||||
if (Columns <= 0)
|
||||
throw new InvalidDataException("Table has no columns.");
|
||||
|
||||
schemaBuffer = new byte[schemaSize];
|
||||
binaryReader.BaseStream.Position = tableOffset + schemaOffset;
|
||||
if (binaryReader.Read(schemaBuffer, 0, (int)schemaSize) != schemaSize)
|
||||
throw new InvalidDataException("Failed to read schema.");
|
||||
|
||||
stringTable = new byte[stringsSize];
|
||||
binaryReader.BaseStream.Position = tableOffset + stringsOffset;
|
||||
if (binaryReader.Read(stringTable, 0, (int)stringsSize) != stringsSize)
|
||||
throw new InvalidDataException("Failed to read string table.");
|
||||
|
||||
uint columnOffset = 0;
|
||||
uint schemaPos = 0;
|
||||
|
||||
tableName = GetStringFromTable(nameOffset);
|
||||
|
||||
schema = new Column[columns];
|
||||
|
||||
BinaryReaderEndian bytesReader = new BinaryReaderEndian(new MemoryStream(schemaBuffer) { Position = 0 });
|
||||
for (int i = 0; i < columns; i++)
|
||||
{
|
||||
bytesReader.BaseStream.Position = schemaPos;
|
||||
|
||||
byte info = bytesReader.ReadByte();
|
||||
uint nameOffset = bytesReader.ReadUInt32BE();
|
||||
|
||||
if (nameOffset > stringsSize)
|
||||
throw new InvalidDataException("String offset out of bounds.");
|
||||
schemaPos += 0x1 + 0x4;
|
||||
|
||||
bytesReader.BaseStream.Position = schemaPos;
|
||||
|
||||
schema[i] = new Column()
|
||||
{
|
||||
Flag = (ColumnFlag)(info & 0xF0),
|
||||
Type = (ColumnType)(info & 0x0F),
|
||||
Name = "",
|
||||
Offset = 0
|
||||
};
|
||||
|
||||
if (schema[i].Flag == 0 ||
|
||||
!schema[i].Flag.HasFlag(ColumnFlag.Name) ||
|
||||
schema[i].Flag.HasFlag(ColumnFlag.Default) && schema[i].Flag.HasFlag(ColumnFlag.Row) ||
|
||||
schema[i].Flag.HasFlag(ColumnFlag.Undefined))
|
||||
throw new InvalidDataException("Unknown column flag combo found.");
|
||||
|
||||
uint valueSize;
|
||||
switch (schema[i].Type)
|
||||
{
|
||||
case ColumnType.Byte:
|
||||
case ColumnType.SByte:
|
||||
valueSize = 0x1;
|
||||
break;
|
||||
case ColumnType.UInt16:
|
||||
case ColumnType.Int16:
|
||||
valueSize = 0x2;
|
||||
break;
|
||||
case ColumnType.UInt32:
|
||||
case ColumnType.Int32:
|
||||
case ColumnType.Float:
|
||||
case ColumnType.String:
|
||||
valueSize = 0x4;
|
||||
break;
|
||||
case ColumnType.UInt64:
|
||||
case ColumnType.Int64:
|
||||
case ColumnType.VLData:
|
||||
valueSize = 0x8;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidDataException("Invalid column type.");
|
||||
};
|
||||
|
||||
if (schema[i].Flag.HasFlag(ColumnFlag.Name))
|
||||
schema[i].Name = GetStringFromTable(nameOffset);
|
||||
|
||||
if (schema[i].Flag.HasFlag(ColumnFlag.Default))
|
||||
{
|
||||
schema[i].Offset = schemaPos;
|
||||
schemaPos += valueSize;
|
||||
|
||||
bytesReader.BaseStream.Position = schemaPos;
|
||||
|
||||
}
|
||||
|
||||
if (schema[i].Flag.HasFlag(ColumnFlag.Row))
|
||||
{
|
||||
schema[i].Offset = columnOffset;
|
||||
columnOffset += valueSize;
|
||||
}
|
||||
}
|
||||
|
||||
utfTableRows = (int)rows;
|
||||
rowName = GetStringFromTable(nameOffset);
|
||||
|
||||
bytesReader.Dispose();
|
||||
}
|
||||
|
||||
public ushort Columns => columns;
|
||||
|
||||
public uint Rows => rows;
|
||||
|
||||
public Column[] Schema => schema;
|
||||
|
||||
public string TableName => tableName;
|
||||
|
||||
public int GetColumn(string columnName)
|
||||
{
|
||||
for (int i = 0; i < columns; i++)
|
||||
{
|
||||
Column column = schema[i];
|
||||
|
||||
if (column.Name is null || !column.Name.Equals(columnName))
|
||||
continue;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private bool Query(int row, int column, out Result result)
|
||||
{
|
||||
result = new Result();
|
||||
|
||||
if (row >= rows || row < 0)
|
||||
//throw new ArgumentOutOfRangeException(nameof(row));
|
||||
return false;
|
||||
if (column >= columns || column < 0)
|
||||
//throw new ArgumentOutOfRangeException(nameof(column));
|
||||
return false;
|
||||
|
||||
Column col = schema[column];
|
||||
uint dataOffset = 0;
|
||||
BinaryReaderEndian bytesReader = null;
|
||||
|
||||
result.Type = col.Type;
|
||||
|
||||
if (col.Flag.HasFlag(ColumnFlag.Default))
|
||||
{
|
||||
if (schemaBuffer != null)
|
||||
{
|
||||
bytesReader = new BinaryReaderEndian(new MemoryStream(schemaBuffer));
|
||||
bytesReader.BaseStream.Position = col.Offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataOffset = tableOffset + schemaOffset + col.Offset;
|
||||
}
|
||||
}
|
||||
else if (col.Flag.HasFlag(ColumnFlag.Row))
|
||||
{
|
||||
dataOffset = (uint)(tableOffset + rowsOffset + row * rowWidth + col.Offset);
|
||||
}
|
||||
else
|
||||
throw new InvalidDataException("Invalid flag.");
|
||||
|
||||
binaryReader.BaseStream.Position = dataOffset;
|
||||
|
||||
switch (col.Type)
|
||||
{
|
||||
case ColumnType.Byte:
|
||||
result.Value = (bytesReader != null) ? bytesReader.ReadByte() : binaryReader.ReadByte();
|
||||
break;
|
||||
case ColumnType.SByte:
|
||||
result.Value = (bytesReader != null) ? bytesReader.ReadSByte() : binaryReader.ReadSByte();
|
||||
break;
|
||||
case ColumnType.UInt16:
|
||||
result.Value = (bytesReader != null) ? bytesReader.ReadUInt16BE() : binaryReader.ReadUInt16BE();
|
||||
break;
|
||||
case ColumnType.Int16:
|
||||
result.Value = (bytesReader != null) ? bytesReader.ReadInt16BE() : binaryReader.ReadInt16BE();
|
||||
break;
|
||||
case ColumnType.UInt32:
|
||||
result.Value = (bytesReader != null) ? bytesReader.ReadUInt32BE() : binaryReader.ReadUInt32BE();
|
||||
break;
|
||||
case ColumnType.Int32:
|
||||
result.Value = (bytesReader != null) ? bytesReader.ReadInt32BE() : binaryReader.ReadInt32BE();
|
||||
break;
|
||||
case ColumnType.UInt64:
|
||||
result.Value = (bytesReader != null) ? bytesReader.ReadUInt64BE() : binaryReader.ReadUInt64BE();
|
||||
break;
|
||||
case ColumnType.Int64:
|
||||
result.Value = (bytesReader != null) ? bytesReader.ReadInt64BE() : binaryReader.ReadInt64BE();
|
||||
break;
|
||||
case ColumnType.Float:
|
||||
result.Value = (bytesReader != null) ? bytesReader.ReadSingleBE() : binaryReader.ReadSingleBE();
|
||||
break;
|
||||
//case ColumnType.Double:
|
||||
// break;
|
||||
case ColumnType.String:
|
||||
uint nameOffset = (bytesReader != null) ? bytesReader.ReadUInt32BE() : binaryReader.ReadUInt32BE();
|
||||
if (nameOffset > stringsSize)
|
||||
throw new InvalidDataException("Name offset out of bounds.");
|
||||
result.Value = GetStringFromTable(nameOffset);
|
||||
break;
|
||||
case ColumnType.VLData:
|
||||
if ((bytesReader != null))
|
||||
{
|
||||
result.Value = new VLData()
|
||||
{
|
||||
Offset = bytesReader.ReadUInt32BE(),
|
||||
Size = bytesReader.ReadUInt32BE()
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Value = new VLData()
|
||||
{
|
||||
Offset = binaryReader.ReadUInt32BE(),
|
||||
Size = binaryReader.ReadUInt32BE()
|
||||
};
|
||||
}
|
||||
break;
|
||||
//case ColumnType.UInt128:
|
||||
// break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Query<T>(int row, int column, out T value)
|
||||
{
|
||||
bool valid = Query(row, column, out Result result);
|
||||
|
||||
bool enumParseResult = Enum.TryParse<ColumnType>(typeof(T).Name, out ColumnType type);
|
||||
|
||||
if (!valid || !enumParseResult || result.Type != type)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = (T)result.Value;
|
||||
|
||||
if (value is VLData vlData)
|
||||
{
|
||||
vlData.Offset += tableOffset + dataOffset;
|
||||
value = (T)(object)vlData;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Query<T>(int row, string columnName, out T value) =>
|
||||
Query(row, GetColumn(columnName), out value);
|
||||
|
||||
public bool Query(int row, int column, out uint offset, out uint size)
|
||||
{
|
||||
if (!Query(row, column, out VLData data))
|
||||
{
|
||||
offset = 0;
|
||||
size = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
offset = data.Offset;
|
||||
size = data.Size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Query(int row, string columnName, out uint offset, out uint size)
|
||||
=> Query(row, GetColumn(columnName), out offset, out size);
|
||||
|
||||
private string GetStringFromTable(uint offset)
|
||||
{
|
||||
if (offset > stringsSize)
|
||||
throw new InvalidDataException("Invalid string offset.");
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
int i = 0;
|
||||
while (i < stringsSize)
|
||||
{
|
||||
stringBuilder.Append((char)stringTable[offset + i++]);
|
||||
if (stringTable[offset + i] == '\0') break;
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
public UtfTable OpenSubtable(string tableName)
|
||||
{
|
||||
if (!Query(0, tableName, out VLData tableValueData))
|
||||
throw new ArgumentException("Subtable does not exist.");
|
||||
|
||||
binaryReader.BaseStream.Position = tableValueData.Offset;
|
||||
|
||||
return new UtfTable(
|
||||
binaryReader.BaseStream,
|
||||
tableValueData.Offset,
|
||||
out int _,
|
||||
out string _);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d662e0678754d5945b5223b3ab889993
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,172 @@
|
||||
using ClHcaSharp;
|
||||
using NAudio.Wave;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace UmaMusumeAudio
|
||||
{
|
||||
public class HcaWaveStream : WaveStream
|
||||
{
|
||||
private readonly Stream hcaFileStream;
|
||||
private readonly BinaryReader hcaFileReader;
|
||||
private readonly HcaDecoder decoder;
|
||||
private readonly HcaInfo info;
|
||||
private readonly long dataStart;
|
||||
private readonly object positionLock = new object();
|
||||
|
||||
private readonly short[][] sampleBuffer;
|
||||
|
||||
private long samplePosition;
|
||||
|
||||
public HcaWaveStream(Stream hcaFile, ulong key)
|
||||
{
|
||||
hcaFileStream = hcaFile;
|
||||
hcaFileReader = new BinaryReader(hcaFile);
|
||||
decoder = new HcaDecoder(hcaFile, key);
|
||||
info = decoder.GetInfo();
|
||||
dataStart = hcaFile.Position;
|
||||
|
||||
sampleBuffer = new short[info.ChannelCount][];
|
||||
for (int i = 0; i < info.ChannelCount; i++)
|
||||
{
|
||||
sampleBuffer[i] = new short[info.SamplesPerBlock];
|
||||
}
|
||||
|
||||
Loop = info.LoopEnabled;
|
||||
LoopStartSample = info.LoopStartSample;
|
||||
LoopEndSample = info.LoopEndSample;
|
||||
|
||||
WaveFormat = new WaveFormat(info.SamplingRate, info.ChannelCount);
|
||||
|
||||
samplePosition = info.EncoderDelay;
|
||||
FillBuffer(samplePosition);
|
||||
}
|
||||
|
||||
public HcaInfo Info => Info1;
|
||||
|
||||
public bool Loop { get; set; }
|
||||
|
||||
public long LoopStartSample { get; set; }
|
||||
|
||||
public long LoopEndSample { get; set; }
|
||||
|
||||
public override WaveFormat WaveFormat { get; }
|
||||
|
||||
public override long Length => Info1.SampleCount * Info1.ChannelCount * sizeof(short);
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (positionLock)
|
||||
{
|
||||
return (samplePosition - Info1.EncoderDelay) * Info1.ChannelCount * sizeof(short);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (positionLock)
|
||||
{
|
||||
samplePosition = value / Info1.ChannelCount / sizeof(short);
|
||||
samplePosition += Info1.EncoderDelay;
|
||||
|
||||
if (Position < Length) FillBuffer(samplePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public HcaInfo Info1 => info;
|
||||
|
||||
public short[][] SampleBuffer => sampleBuffer;
|
||||
|
||||
public HcaDecoder Decoder => decoder;
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
lock (positionLock)
|
||||
{
|
||||
int read = 0;
|
||||
|
||||
for (int i = 0; i < count / Info1.ChannelCount / sizeof(short); i++)
|
||||
{
|
||||
if (samplePosition - Info1.EncoderDelay >= LoopEndSample && Loop)
|
||||
{
|
||||
samplePosition = LoopStartSample + Info1.EncoderDelay;
|
||||
FillBuffer(samplePosition);
|
||||
}
|
||||
else if (Position >= Length) break;
|
||||
|
||||
if (samplePosition % Info1.SamplesPerBlock == 0) FillBuffer(samplePosition);
|
||||
|
||||
for (int j = 0; j < Info1.ChannelCount; j++)
|
||||
{
|
||||
int bufferOffset = (i * Info1.ChannelCount + j) * sizeof(short);
|
||||
buffer[offset + bufferOffset] = (byte)SampleBuffer[j][samplePosition % Info1.SamplesPerBlock];
|
||||
buffer[offset + bufferOffset + 1] = (byte)(SampleBuffer[j][samplePosition % Info1.SamplesPerBlock] >> 8);
|
||||
|
||||
read += sizeof(short);
|
||||
}
|
||||
|
||||
samplePosition++;
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetLoop()
|
||||
{
|
||||
Loop = Info1.LoopEnabled;
|
||||
LoopStartSample = Info1.LoopStartSample;
|
||||
LoopEndSample = Info1.LoopEndSample;
|
||||
}
|
||||
|
||||
private void FillBuffer(long sample)
|
||||
{
|
||||
int block = (int)(sample / Info1.SamplesPerBlock);
|
||||
FillBuffer(block);
|
||||
}
|
||||
|
||||
private void FillBuffer(int block)
|
||||
{
|
||||
if (block >= 0) hcaFileStream.Position = dataStart + block * Info1.BlockSize;
|
||||
|
||||
if (hcaFileStream.Position < hcaFileStream.Length)
|
||||
{
|
||||
byte[] blockBytes = hcaFileReader.ReadBytes(Info1.BlockSize);
|
||||
if (blockBytes.Length > 0)
|
||||
{
|
||||
Decoder.DecodeBlock(blockBytes);
|
||||
Decoder.ReadSamples16(SampleBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//private void FillBuffer()
|
||||
//{
|
||||
// if (hcaFileStream.Position >= hcaFileStream.Length)
|
||||
// hcaFileStream.Position = dataStart;
|
||||
|
||||
// byte[] blockBytes = hcaFileReader.ReadBytes(info.BlockSize);
|
||||
// if (blockBytes.Length > 0)
|
||||
// {
|
||||
// decoder.DecodeBlock(blockBytes);
|
||||
// decoder.ReadSamples16(sampleBuffer);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// for (int i = 0; i < sampleBuffer.Length; i++)
|
||||
// {
|
||||
// for (int j = 0; j < sampleBuffer[i].Length; j++)
|
||||
// {
|
||||
// sampleBuffer[i][j] = 0;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d2df19f53207594cbf031ad1620c2ea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
using NAudio.Wave;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using CriWareFormats;
|
||||
using ClHcaSharp;
|
||||
|
||||
namespace UmaMusumeAudio
|
||||
{
|
||||
public class UmaWaveStream : WaveStream
|
||||
{
|
||||
/*
|
||||
* Open Umamusume > umamusume_Data > resources.assets with a hex editor.
|
||||
* Check the results when finding the string "StreamingAssets".
|
||||
* The key should be near a string named "cri_auth".
|
||||
*/
|
||||
private const ulong umaMusumeKey = 75923756697503;
|
||||
|
||||
private readonly HcaWaveStream hcaWaveStream;
|
||||
|
||||
public UmaWaveStream(AwbReader awbReader, int waveId)
|
||||
{
|
||||
Stream awbSubfile = awbReader.GetWaveSubfileStream(awbReader.Waves.Find((wave) => wave.WaveId == waveId));
|
||||
hcaWaveStream = new HcaWaveStream(awbSubfile, MixKey(umaMusumeKey, awbReader.Subkey));
|
||||
}
|
||||
|
||||
public override WaveFormat WaveFormat => hcaWaveStream.WaveFormat;
|
||||
|
||||
public bool Loop { get => hcaWaveStream.Loop; set { hcaWaveStream.Loop = value; } }
|
||||
|
||||
public long LoopStartSample { get => hcaWaveStream.LoopStartSample; set { hcaWaveStream.LoopStartSample = value; } }
|
||||
|
||||
public long LoopEndSample { get => hcaWaveStream.LoopEndSample; set { hcaWaveStream.LoopEndSample = value; } }
|
||||
|
||||
public override long Length => hcaWaveStream.Length;
|
||||
|
||||
public override long Position { get => hcaWaveStream.Position; set => hcaWaveStream.Position = value; }
|
||||
|
||||
public short[][] SampleBuffer => hcaWaveStream.SampleBuffer;
|
||||
|
||||
public HcaDecoder Decoder => hcaWaveStream.Decoder;
|
||||
public HcaInfo Info => hcaWaveStream.Info;
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return hcaWaveStream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
public void ResetLoop() => hcaWaveStream.ResetLoop();
|
||||
|
||||
private static ulong MixKey(ulong key, ushort subkey) =>
|
||||
key * (((ulong)subkey << 16) | ((ushort)~subkey + 2u));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d159c340b0cc08c47bfafb5e5641a630
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,118 @@
|
||||
public class WAV
|
||||
{
|
||||
|
||||
// convert two bytes to one float in the range -1 to 1
|
||||
static float bytesToFloat(byte firstByte, byte secondByte)
|
||||
{
|
||||
// convert two bytes to one short (little endian)
|
||||
short s = (short)((secondByte << 8) | firstByte);
|
||||
// convert to range from -1 to (just below) 1
|
||||
return s / 32768.0F;
|
||||
}
|
||||
|
||||
static int bytesToInt(byte[] bytes, int offset = 0)
|
||||
{
|
||||
int value = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
value |= ((int)bytes[offset + i]) << (i * 8);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
// properties
|
||||
public float[] LeftChannel { get; internal set; }
|
||||
public float[] RightChannel { get; internal set; }
|
||||
public float[] StereoChannel { get; internal set; }
|
||||
public int ChannelCount { get; internal set; }
|
||||
public int SampleCount { get; internal set; }
|
||||
public int Frequency { get; internal set; }
|
||||
|
||||
public WAV(byte[] wav)
|
||||
{
|
||||
|
||||
// Determine if mono or stereo
|
||||
ChannelCount = wav[22]; // Forget byte 23 as 99.999% of WAVs are 1 or 2 channels
|
||||
|
||||
// Get the frequency
|
||||
Frequency = bytesToInt(wav, 24);
|
||||
|
||||
// Get past all the other sub chunks to get to the data subchunk:
|
||||
int pos = 12; // First Subchunk ID from 12 to 16
|
||||
|
||||
// Keep iterating until we find the data chunk (i.e. 64 61 74 61 ...... (i.e. 100 97 116 97 in decimal))
|
||||
while (!(wav[pos] == 100 && wav[pos + 1] == 97 && wav[pos + 2] == 116 && wav[pos + 3] == 97))
|
||||
{
|
||||
pos += 4;
|
||||
int chunkSize = wav[pos] + wav[pos + 1] * 256 + wav[pos + 2] * 65536 + wav[pos + 3] * 16777216;
|
||||
pos += 4 + chunkSize;
|
||||
}
|
||||
pos += 8;
|
||||
|
||||
// Pos is now positioned to start of actual sound data.
|
||||
SampleCount = (wav.Length - pos) / 2; // 2 bytes per sample (16 bit sound mono)
|
||||
if (ChannelCount == 2) SampleCount /= 2; // 4 bytes per sample (16 bit stereo)
|
||||
|
||||
// Allocate memory (right will be null if only mono sound)
|
||||
LeftChannel = new float[SampleCount];
|
||||
if (ChannelCount == 2) RightChannel = new float[SampleCount];
|
||||
else RightChannel = null;
|
||||
|
||||
// Write to double array/s:
|
||||
int i = 0;
|
||||
while (pos < wav.Length)
|
||||
{
|
||||
LeftChannel[i] = bytesToFloat(wav[pos], wav[pos + 1]);
|
||||
pos += 2;
|
||||
if (ChannelCount == 2)
|
||||
{
|
||||
RightChannel[i] = bytesToFloat(wav[pos], wav[pos + 1]);
|
||||
pos += 2;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
//Merge left and right channels for stereo sound
|
||||
if (ChannelCount == 2)
|
||||
{
|
||||
StereoChannel = new float[SampleCount * 2];
|
||||
//Current position in our left and right channels
|
||||
int channelPos = 0;
|
||||
//After we've changed two values for our Stereochannel, we want to increase our channelPos
|
||||
short posChange = 0;
|
||||
|
||||
for (int index = 0; index < (SampleCount * 2); index++)
|
||||
{
|
||||
|
||||
if (index % 2 == 0)
|
||||
{
|
||||
StereoChannel[index] = LeftChannel[channelPos];
|
||||
posChange++;
|
||||
}
|
||||
else
|
||||
{
|
||||
StereoChannel[index] = RightChannel[channelPos];
|
||||
posChange++;
|
||||
}
|
||||
//Two values have been changed, so update our channelPos
|
||||
if (posChange % 2 == 0)
|
||||
{
|
||||
if (channelPos < SampleCount)
|
||||
{
|
||||
channelPos++;
|
||||
//Reset the counter for next iterations
|
||||
posChange = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StereoChannel = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[WAV: LeftChannel={0}, RightChannel={1}, ChannelCount={2}, SampleCount={3}, Frequency={4}]", LeftChannel, RightChannel, ChannelCount, SampleCount, Frequency);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0f51605a8223f8489a6c386d8d06198
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,10 +1,15 @@
|
||||
using Gallop;
|
||||
using CriWareFormats;
|
||||
using Gallop;
|
||||
using NAudio.Wave;
|
||||
using NAudio.Wave.SampleProviders;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UmaMusumeAudio;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -21,7 +26,7 @@ public class UmaViewerBuilder : MonoBehaviour
|
||||
public UmaContainer CurrentUMAContainer;
|
||||
public UmaContainer CurrentLiveContainer;
|
||||
public UmaContainer CurrentOtherContainer;
|
||||
|
||||
public AudioSource CurrentAudioSource;
|
||||
public AnimatorOverrideController OverrideController;
|
||||
|
||||
|
||||
@@ -337,10 +342,10 @@ public class UmaViewerBuilder : MonoBehaviour
|
||||
|
||||
public void LoadLive(int id)
|
||||
{
|
||||
var asset = UmaViewerMain.Instance.AbList.FirstOrDefault(a => a.Name.EndsWith("cutt_son"+id));
|
||||
var asset = UmaViewerMain.Instance.AbList.FirstOrDefault(a => a.Name.EndsWith("cutt_son" + id));
|
||||
if (CurrentLiveContainer != null)
|
||||
{
|
||||
Destroy(CurrentLiveContainer);
|
||||
Destroy(CurrentLiveContainer.gameObject);
|
||||
}
|
||||
UnloadAllBundle();
|
||||
CurrentLiveContainer = new GameObject(Path.GetFileName(asset.Name)).AddComponent<UmaContainer>();
|
||||
@@ -348,6 +353,47 @@ public class UmaViewerBuilder : MonoBehaviour
|
||||
|
||||
}
|
||||
|
||||
public void LoadLiveSound(int songid, UmaDatabaseEntry SongAwb)
|
||||
{
|
||||
if (CurrentAudioSource)
|
||||
{
|
||||
Destroy(CurrentAudioSource.gameObject);
|
||||
}
|
||||
AudioClip clip = LoadAudio(SongAwb);
|
||||
if (clip != null)
|
||||
{
|
||||
CurrentAudioSource = new GameObject("SoundController").AddComponent<AudioSource>();
|
||||
CurrentAudioSource.clip = clip;
|
||||
CurrentAudioSource.Play();
|
||||
}
|
||||
}
|
||||
|
||||
public AudioClip LoadAudio(UmaDatabaseEntry awb)
|
||||
{
|
||||
UmaViewerUI.Instance.LoadedAssetsAdd(awb);
|
||||
|
||||
string awbPath = $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "Low"}\\Cygames\\umamusume\\dat\\{awb.Url.Substring(0, 2)}\\{awb.Url}";
|
||||
if (!File.Exists(awbPath)) return null;
|
||||
|
||||
FileStream awbFile = File.OpenRead(awbPath);
|
||||
AwbReader awbReader = new AwbReader(awbFile);
|
||||
|
||||
|
||||
var stream = new UmaWaveStream(awbReader, 0);
|
||||
MemoryStream outputStream = new MemoryStream();
|
||||
WaveFileWriter.WriteWavFileToStream(outputStream, stream);
|
||||
|
||||
WAV wav = new WAV(outputStream.ToArray());
|
||||
AudioClip clip = AudioClip.Create(Path.GetFileName(awb.Name).Replace(".awb", ""),wav.SampleCount,wav.ChannelCount,wav.Frequency,false);
|
||||
clip.SetData(wav.ChannelCount > 1 ? wav.StereoChannel : wav.LeftChannel, 0);
|
||||
|
||||
awbReader.Dispose();
|
||||
awbFile.Dispose();
|
||||
outputStream.Dispose();
|
||||
|
||||
return clip;
|
||||
}
|
||||
|
||||
private void RecursiveLoadAsset(UmaDatabaseEntry entry, bool IsSubAsset = false)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(entry.Prerequisites))
|
||||
@@ -397,17 +443,17 @@ public class UmaViewerBuilder : MonoBehaviour
|
||||
Type aType = asset.GetType();
|
||||
if (aType == typeof(AnimationClip))
|
||||
{
|
||||
if (CurrentUMAContainer&&CurrentUMAContainer.Body)
|
||||
if (CurrentUMAContainer && CurrentUMAContainer.Body)
|
||||
{
|
||||
AnimationClip bbb = asset as AnimationClip;
|
||||
bbb.wrapMode = WrapMode.Loop;
|
||||
CurrentUMAContainer.OverrideController["clip"] = bbb;
|
||||
CurrentUMAContainer.UmaAnimator.Play("clip", 0, 0);
|
||||
|
||||
|
||||
}
|
||||
|
||||
if(!CurrentLiveContainer)
|
||||
UnloadBundle(bundle, false);
|
||||
if (!CurrentLiveContainer)
|
||||
UnloadBundle(bundle, false);
|
||||
}
|
||||
else if (aType == typeof(GameObject))
|
||||
{
|
||||
@@ -678,7 +724,7 @@ public class UmaViewerBuilder : MonoBehaviour
|
||||
|
||||
private void LoadProp(GameObject go)
|
||||
{
|
||||
var prop = Instantiate(go, (go.name.Contains("Cutt_son")?CurrentLiveContainer:CurrentOtherContainer).transform);
|
||||
var prop = Instantiate(go, (go.name.Contains("Cutt_son") ? CurrentLiveContainer : CurrentOtherContainer).transform);
|
||||
foreach (Renderer r in prop.GetComponentsInChildren<Renderer>())
|
||||
{
|
||||
foreach (Material m in r.sharedMaterials)
|
||||
|
||||
@@ -30,6 +30,8 @@ public class UmaViewerUI : MonoBehaviour,FaceLoadCallBack
|
||||
|
||||
public ScrollRect FacialList;
|
||||
public ScrollRect LiveList;
|
||||
public ScrollRect LiveSoundList;
|
||||
public ScrollRect LiveCharaSoundList;
|
||||
|
||||
//settings
|
||||
public TMPro.TMP_InputField SSWidth, SSHeight;
|
||||
@@ -117,7 +119,11 @@ public class UmaViewerUI : MonoBehaviour,FaceLoadCallBack
|
||||
{
|
||||
Destroy(ui.gameObject);
|
||||
}
|
||||
foreach (FacialMorph morph in target.EyeBrowMorphs)
|
||||
List<FacialMorph> tempMorph = new List<FacialMorph>();
|
||||
tempMorph.AddRange(target.EyeBrowMorphs);
|
||||
tempMorph.AddRange(target.EyeMorphs);
|
||||
tempMorph.AddRange(target.MouthMorphs);
|
||||
foreach (FacialMorph morph in tempMorph)
|
||||
{
|
||||
var container = Instantiate(UmaContainerSliderPrefab, FacialList.content).GetComponent<UmaUIContainer>();
|
||||
container.Name.text = morph.name;
|
||||
@@ -126,24 +132,6 @@ public class UmaViewerUI : MonoBehaviour,FaceLoadCallBack
|
||||
container.Slider.minValue = 0;
|
||||
container.Slider.onValueChanged.AddListener((a) => { morph.Weight = a;});
|
||||
}
|
||||
foreach (FacialMorph morph in target.EyeMorphs)
|
||||
{
|
||||
var container = Instantiate(UmaContainerSliderPrefab, FacialList.content).GetComponent<UmaUIContainer>();
|
||||
container.Name.text = morph.name;
|
||||
container.Slider.value = morph.Weight;
|
||||
container.Slider.maxValue = 1;
|
||||
container.Slider.minValue = 0;
|
||||
container.Slider.onValueChanged.AddListener((a) => { morph.Weight = a; });
|
||||
}
|
||||
foreach (FacialMorph morph in target.MouthMorphs)
|
||||
{
|
||||
var container = Instantiate(UmaContainerSliderPrefab, FacialList.content).GetComponent<UmaUIContainer>();
|
||||
container.Name.text = morph.name;
|
||||
container.Slider.value = morph.Weight;
|
||||
container.Slider.maxValue = 1;
|
||||
container.Slider.minValue = 0;
|
||||
container.Slider.onValueChanged.AddListener((a) => { morph.Weight = a; });
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadLivePanels()
|
||||
@@ -159,7 +147,17 @@ public class UmaViewerUI : MonoBehaviour,FaceLoadCallBack
|
||||
Builder.LoadLive(live.MusicId);
|
||||
});
|
||||
|
||||
var CharaContainer = Instantiate(UmaContainerNoTMPPrefab, LiveSoundList.content).GetComponent<UmaUIContainer>();
|
||||
CharaContainer.GetComponentInChildren<Text>().text = " " + live.MusicId + " " + live.songName;
|
||||
var CharaimageInstance1 = container.GetComponent<Image>();
|
||||
CharaContainer.Button.onClick.AddListener(() => {
|
||||
HighlightChildImage(LiveList.content, imageInstance1);
|
||||
ListLiveSounds(live.MusicId);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void LoadMiniModelPanels()
|
||||
@@ -263,6 +261,32 @@ public class UmaViewerUI : MonoBehaviour,FaceLoadCallBack
|
||||
}
|
||||
}
|
||||
|
||||
void ListLiveSounds(int songid)
|
||||
{
|
||||
for (int i = LiveCharaSoundList.content.childCount - 1; i >= 0; i--)
|
||||
{
|
||||
Destroy(LiveCharaSoundList.content.GetChild(i).gameObject);
|
||||
}
|
||||
string nameVar = $"snd_bgm_live_{songid}_chara";
|
||||
foreach (var entry in Main.AbList.Where(a => a.Name.Contains(nameVar) && a.Name.EndsWith("awb")))
|
||||
{
|
||||
var container = Instantiate(UmaContainerPrefab, LiveCharaSoundList.content).GetComponent<UmaUIContainer>();
|
||||
string[] split = entry.Name.Split('_');
|
||||
string name = split[split.Length - 2] + getCharaName(split[split.Length - 2]) + " " + split[split.Length - 1].Replace(".awb","");
|
||||
container.Name.text = name;
|
||||
container.Button.onClick.AddListener(() => {
|
||||
HighlightChildImage(LiveCharaSoundList.content, container.GetComponent<Image>());
|
||||
Builder.LoadLiveSound(songid, entry);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
string getCharaName(string id)
|
||||
{
|
||||
var entry = Main.Characters.FirstOrDefault(a => a.Id.ToString().Equals(id));
|
||||
return (entry == null) ? id.ToString() : entry.Name;
|
||||
}
|
||||
|
||||
public static string GetCostumeName(string costumeId)
|
||||
{
|
||||
switch (costumeId)
|
||||
|
||||
@@ -161,7 +161,6 @@ PlayerSettings:
|
||||
m_ColorGamuts: 00000000
|
||||
targetPixelDensity: 30
|
||||
resolutionScalingMode: 0
|
||||
resetResolutionOnWindowResize: 0
|
||||
androidSupportedAspectRatio: 1
|
||||
androidMaxAspectRatio: 2.1
|
||||
applicationIdentifier:
|
||||
@@ -514,9 +513,7 @@ PlayerSettings:
|
||||
switchPlayerConnectionEnabled: 1
|
||||
switchUseNewStyleFilepaths: 0
|
||||
switchUseMicroSleepForYield: 1
|
||||
switchEnableRamDiskSupport: 0
|
||||
switchMicroSleepForYieldTime: 25
|
||||
switchRamDiskSpaceSize: 12
|
||||
ps4NPAgeRating: 12
|
||||
ps4NPTitleSecret:
|
||||
ps4NPTrophyPackPath:
|
||||
@@ -631,7 +628,8 @@ PlayerSettings:
|
||||
gcIncremental: 0
|
||||
assemblyVersionValidation: 1
|
||||
gcWBarrierValidation: 0
|
||||
apiCompatibilityLevelPerPlatform: {}
|
||||
apiCompatibilityLevelPerPlatform:
|
||||
Standalone: 6
|
||||
m_RenderingPath: 1
|
||||
m_MobileRenderingPath: 1
|
||||
metroPackageName: Template_3D
|
||||
@@ -661,7 +659,6 @@ PlayerSettings:
|
||||
metroFTAName:
|
||||
metroFTAFileTypes: []
|
||||
metroProtocolName:
|
||||
vcxProjDefaultLanguage:
|
||||
XboxOneProductId:
|
||||
XboxOneUpdateKey:
|
||||
XboxOneSandboxId:
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user