add acb&awb support

This commit is contained in:
1605296300@qq.com
2022-09-07 01:22:44 +08:00
parent d55c0ecd4d
commit 7607c8b4d2
57 changed files with 5676 additions and 34 deletions
Binary file not shown.
+33
View File
@@ -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:
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e9030d1ab7c7dce4eb3d45b93c88e3c2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7fc4162661dc2f8469c9c2f7f36d6357
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+115
View File
@@ -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:
+139
View File
@@ -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:
+37
View File
@@ -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:
+329
View File
@@ -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:
+335
View File
@@ -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:
+8
View File
@@ -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:
+172
View File
@@ -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:
+54
View File
@@ -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:
+118
View File
@@ -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);
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f0f51605a8223f8489a6c386d8d06198
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+55 -9
View File
@@ -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)
+43 -19
View File
@@ -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)
+2 -5
View File
@@ -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
View File
@@ -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>