Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

is there a way for you to make an encoder for .dspw files please ? #116

Open
wants to merge 16 commits into
base: opus
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build/Build.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Cake.Frosting" Version="0.1.0-alpha0078" />
<PackageReference Include="Cake.Common" Version="0.28.0" />
<PackageReference Include="Cake.Frosting" Version="0.31.0" />
<PackageReference Include="Cake.Common" Version="0.31.0" />
<PackageReference Include="ILRepack.Lib" Version="2.0.16" NoWarn="NU1701" />
</ItemGroup>

Expand Down
6 changes: 0 additions & 6 deletions build/NuGet.Config

This file was deleted.

10 changes: 9 additions & 1 deletion src/VGAudio.Cli/CliArguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,9 @@ public static Options Parse(string[] args)
case "NAMCO":
nxHeaderType = NxOpusHeaderType.Namco;
break;
case "KTSS":
nxHeaderType = NxOpusHeaderType.Ktss;
break;
default:
Console.WriteLine("Invalid header type");
return null;
Expand All @@ -361,6 +364,9 @@ public static Options Parse(string[] args)
options.NxOpusHeaderType = nxHeaderType;
i++;
continue;
case "-CBR":
options.EncodeCbr = true;
continue;
}
}

Expand Down Expand Up @@ -560,12 +566,14 @@ private static void PrintUsage()

Console.WriteLine("\nHCA Options:");
Console.WriteLine(" --hcaquality The quality level to use for the HCA file");
Console.WriteLine(" --bitrate The bitrate in bps of the output HCA file");
Console.WriteLine(" --bitrate The bitrate in bits per second of the output HCA file");
Console.WriteLine(" --bitrate takes precedence over --hcaquality");
Console.WriteLine(" --limit-bitrate This flag sets a limit on how low the bitrate can go");
Console.WriteLine(" This limit depends on the properties of the input file");

Console.WriteLine("\nSwitch Opus Options:");
Console.WriteLine(" --bitrate The bitrate in bits per second of the output file");
Console.WriteLine(" --cbr Encode the file using a constant bitrate");
Console.WriteLine(" --opusheader The type of header to use for the generated Opus file");
Console.WriteLine(" Available types: " + opusHeaderTypes);
}
Expand Down
4 changes: 2 additions & 2 deletions src/VGAudio.Cli/ContainerTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ internal static class ContainerTypes
[FileType.Bcstm] = new ContainerType(new[] { "bcstm" }, () => new BCFstmReader(), () => new BCFstmWriter(NwTarget.Ctr), CreateConfiguration.Bxstm),
[FileType.Bfstm] = new ContainerType(new[] { "bfstm" }, () => new BCFstmReader(), () => new BCFstmWriter(NwTarget.Cafe), CreateConfiguration.Bxstm),
[FileType.Brwav] = new ContainerType(new[] { "brwav", "rwav" }, () => new BrwavReader(), null, CreateConfiguration.Bxstm),
[FileType.Bcwav] = new ContainerType(new[] { "bcwav", "cwav" }, () => new BCFstmReader(), null, CreateConfiguration.Bxstm),
[FileType.Bcwav] = new ContainerType(new[] { "bcwav", "cwav", "bms" }, () => new BCFstmReader(), null, CreateConfiguration.Bxstm),
[FileType.Bfwav] = new ContainerType(new[] { "bfwav" }, () => new BCFstmReader(), null, CreateConfiguration.Bxstm),
[FileType.Bcstp] = new ContainerType(new[] { "bcstp" }, () => new BCFstmReader(), null, CreateConfiguration.Bxstm),
[FileType.Bfstp] = new ContainerType(new[] { "bfstp" }, () => new BCFstmReader(), null, CreateConfiguration.Bxstm),
Expand All @@ -35,7 +35,7 @@ internal static class ContainerTypes
[FileType.Hca] = new ContainerType(new[] { "hca" }, () => new HcaReader(), () => new HcaWriter(), CreateConfiguration.Hca),
[FileType.Genh] = new ContainerType(new[] { "genh" }, () => new GenhReader(), null, null),
[FileType.Atrac9] = new ContainerType(new[] { "at9" }, () => new At9Reader(), null, null),
[FileType.NxOpus] = new ContainerType(new[] { "lopus", "nop" }, () => new NxOpusReader(), () => new NxOpusWriter(), CreateConfiguration.NxOpus),
[FileType.NxOpus] = new ContainerType(new[] { "lopus", "nop", "ktss", "kns" }, () => new NxOpusReader(), () => new NxOpusWriter(), CreateConfiguration.NxOpus),
[FileType.OggOpus] = new ContainerType(new[] { "opus" }, () => new OggOpusReader(), () => new OggOpusWriter(), CreateConfiguration.NxOpus)
};

Expand Down
3 changes: 3 additions & 0 deletions src/VGAudio.Cli/CreateConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ public static Configuration Hca(Options options, Configuration inConfig = null)
config.LimitBitrate = options.LimitBitrate;
if (options.Bitrate != 0) config.Bitrate = options.Bitrate;

if (options.KeyCode != 0) config.EncryptionKey = new CriHcaKey(options.KeyCode);

return config;
}

Expand All @@ -152,6 +154,7 @@ public static Configuration NxOpus(Options options, Configuration inConfig = nul
NxOpusConfiguration config = inConfig as NxOpusConfiguration ?? new NxOpusConfiguration();

config.HeaderType = options.NxOpusHeaderType;
config.EncodeCbr = options.EncodeCbr;
if (options.Bitrate != 0) config.Bitrate = options.Bitrate;

return config;
Expand Down
1 change: 1 addition & 0 deletions src/VGAudio.Cli/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ internal class Options
public bool LimitBitrate { get; set; }

public NxOpusHeaderType NxOpusHeaderType { get; set; } // Switch Opus
public bool EncodeCbr { get; set; }
}

internal class JobFiles
Expand Down
20 changes: 14 additions & 6 deletions src/VGAudio/Codecs/CriHca/CriHcaEncryption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,28 @@ public static partial class CriHcaEncryption
{
private const int FramesToTest = 10;

public static void Decrypt(HcaInfo hca, byte[][] audio, CriHcaKey key)
private static Crc16 Crc { get; } = new Crc16(0x8005);

public static void Crypt(HcaInfo hca, byte[][] audio, CriHcaKey key, bool doDecrypt)
{
for (int frame = 0; frame < hca.FrameCount; frame++)
{
DecryptFrame(hca, audio[frame], key);
CryptFrame(hca, audio[frame], key, doDecrypt);
}
}

public static void DecryptFrame(HcaInfo hca, byte[] audio, CriHcaKey key)
public static void CryptFrame(HcaInfo hca, byte[] audio, CriHcaKey key, bool doDecrypt)
{
for (int b = 0; b < hca.FrameSize; b++)
byte[] substitutionTable = doDecrypt ? key.DecryptionTable : key.EncryptionTable;

for (int b = 0; b < hca.FrameSize - 2; b++)
{
audio[b] = key.DecryptionTable[audio[b]];
audio[b] = substitutionTable[audio[b]];
}

ushort crc = Crc.Compute(audio, hca.FrameSize - 2);
audio[hca.FrameSize - 2] = (byte)(crc >> 8);
audio[hca.FrameSize - 1] = (byte)crc;
}

public static CriHcaKey FindKey(HcaInfo hca, byte[][] audio)
Expand All @@ -44,7 +52,7 @@ private static bool TestKey(CriHcaFrame frame, byte[][] audio, CriHcaKey key, by
for (int i = startFrame; i < endFrame; i++)
{
Array.Copy(audio[i], buffer, audio[i].Length);
DecryptFrame(frame.Hca, buffer, key);
CryptFrame(frame.Hca, buffer, key, true);
var reader = new BitReader(buffer);
if (!CriHcaPacking.UnpackFrame(frame, reader))
{
Expand Down
4 changes: 4 additions & 0 deletions src/VGAudio/Codecs/CriHca/CriHcaKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public CriHcaKey(ulong keyCode)
KeyCode = keyCode;
DecryptionTable = CreateDecryptionTable(keyCode);
EncryptionTable = InvertTable(DecryptionTable);
KeyType = 56;
}

public CriHcaKey(Type type)
Expand All @@ -18,9 +19,11 @@ public CriHcaKey(Type type)
{
case Type.Type0:
DecryptionTable = CreateDecryptionTableType0();
KeyType = 0;
break;
case Type.Type1:
DecryptionTable = CreateDecryptionTableType1();
KeyType = 1;
break;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
Expand All @@ -29,6 +32,7 @@ public CriHcaKey(Type type)
EncryptionTable = InvertTable(DecryptionTable);
}

public int KeyType { get; }
public ulong KeyCode { get; }
public byte[] DecryptionTable { get; }
public byte[] EncryptionTable { get; }
Expand Down
2 changes: 2 additions & 0 deletions src/VGAudio/Codecs/CriHca/HcaInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,7 @@ public void CalculateHfrValues()
HfrBandCount = TotalBandCount - BaseBandCount - StereoBandCount;
HfrGroupCount = HfrBandCount.DivideByRoundUp(BandsPerHfrGroup);
}

public HcaInfo GetClone() => (HcaInfo)MemberwiseClone();
}
}
1 change: 1 addition & 0 deletions src/VGAudio/Codecs/Opus/OpusParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ public class OpusParameters : CodecParameters
public OpusParameters() { }
public OpusParameters(CodecParameters source) : base(source) { }
public int Bitrate { get; set; }
public bool EncodeCbr { get; set; }
}
}
5 changes: 4 additions & 1 deletion src/VGAudio/Containers/Hca/HcaReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ protected override IAudioFormat ToAudioStream(HcaStructure structure)
{
if (structure.EncryptionKey != null)
{
CriHcaEncryption.Decrypt(structure.Hca, structure.AudioData, structure.EncryptionKey);
CriHcaEncryption.Crypt(structure.Hca, structure.AudioData, structure.EncryptionKey, true);

structure.Hca.EncryptionType = 0;
}

return new CriHcaFormatBuilder(structure.AudioData, structure.Hca).Build();
Expand All @@ -60,6 +62,7 @@ private static void ReadHcaHeader(BinaryReader reader, HcaStructure structure)
string signature = ReadChunkId(reader);
structure.Version = reader.ReadInt16();
structure.HeaderSize = reader.ReadInt16();
hca.HeaderSize = structure.HeaderSize;

if (signature != "HCA\0")
{
Expand Down
39 changes: 31 additions & 8 deletions src/VGAudio/Containers/Hca/HcaWriter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System.Text;
using VGAudio.Codecs.CriHca;
using VGAudio.Formats;
using VGAudio.Formats.CriHca;
Expand Down Expand Up @@ -34,6 +35,13 @@ protected override void SetupWriter(AudioData audio)
var hcaFormat = audio.GetFormat<CriHcaFormat>(encodingConfig);
Hca = hcaFormat.Hca;
AudioData = hcaFormat.AudioData;

if (Configuration.EncryptionKey != null)
{
CriHcaEncryption.Crypt(Hca, AudioData, Configuration.EncryptionKey, false);

Hca.EncryptionType = Configuration.EncryptionKey.KeyType;
}
}

protected override void WriteStream(Stream stream)
Expand Down Expand Up @@ -72,14 +80,14 @@ private void WriteHeader(BinaryWriter writer)

private void WriteHcaChunk(BinaryWriter writer)
{
writer.WriteUTF8("HCA\0");
WriteChunkId(writer, "HCA\0");
writer.Write(Version);
writer.Write((short)HeaderSize);
}

private void WriteFmtChunk(BinaryWriter writer)
{
writer.WriteUTF8("fmt\0");
WriteChunkId(writer, "fmt\0");
writer.Write((byte)Hca.ChannelCount);

// Sample Rate is 24-bit
Expand All @@ -93,7 +101,7 @@ private void WriteFmtChunk(BinaryWriter writer)

private void WriteCompChunk(BinaryWriter writer)
{
writer.WriteUTF8("comp");
WriteChunkId(writer, "comp");
writer.Write((short)Hca.FrameSize);
writer.Write((byte)Hca.MinResolution);
writer.Write((byte)Hca.MaxResolution);
Expand All @@ -110,7 +118,7 @@ private void WriteLoopChunk(BinaryWriter writer)
{
if (!Hca.Looping) return;

writer.WriteUTF8("loop");
WriteChunkId(writer, "loop");
writer.Write(Hca.LoopStartFrame);
writer.Write(Hca.LoopEndFrame);
writer.Write((short)Hca.PreLoopSamples);
Expand All @@ -119,7 +127,7 @@ private void WriteLoopChunk(BinaryWriter writer)

private void WriteCiphChunk(BinaryWriter writer)
{
writer.WriteUTF8("ciph");
WriteChunkId(writer, "ciph");
writer.Write((short)Hca.EncryptionType);
}

Expand All @@ -129,20 +137,35 @@ private void WriteRvaChunk(BinaryWriter writer)
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (volume != 1)
{
writer.WriteUTF8("rva\0");
WriteChunkId(writer, "rva\0");
writer.Write(volume);
}
}

private void WriteCommChunk(BinaryWriter writer)
{
writer.WriteUTF8("comm\0");
WriteChunkId(writer, "comm\0");
writer.WriteUTF8Z(Hca.Comment);
}

private void WritePadChunk(BinaryWriter writer)
{
writer.WriteUTF8("pad");
WriteChunkId(writer, "pad");
}

private void WriteChunkId(BinaryWriter writer, string value)
{
byte[] bytes = Encoding.UTF8.GetBytes(value);

if (Configuration.EncryptionKey != null)
{
for (int i = 0; i < bytes.Length; i++)
{
if (bytes[i] != 0) bytes[i] |= 0x80;
}
}

writer.Write(bytes);
}

private void WriteData(BinaryWriter writer)
Expand Down
1 change: 1 addition & 0 deletions src/VGAudio/Containers/Opus/NxOpusConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ public class NxOpusConfiguration : Configuration
{
public NxOpusHeaderType HeaderType { get; set; }
public int Bitrate { get; set; }
public bool EncodeCbr { get; set; }
}
}
42 changes: 41 additions & 1 deletion src/VGAudio/Containers/Opus/NxOpusReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ protected override NxOpusStructure ReadFile(Stream stream, bool readAudioData =
stream.Position = startPos + structure.SadfDataOffset;
ReadStandardHeader(GetBinaryReader(stream, Endianness.LittleEndian), structure);
break;
case NxOpusHeaderType.Ktss:
ReadKtssHeader(GetBinaryReader(stream, Endianness.LittleEndian), structure);
break;
}

BinaryReader reader = GetBinaryReader(stream, Endianness.BigEndian);
Expand Down Expand Up @@ -70,6 +73,7 @@ private static NxOpusHeaderType DetectHeader(Stream stream)
case 0x80000001: return NxOpusHeaderType.Standard;
case 0x5355504F: return NxOpusHeaderType.Namco; // OPUS
case 0x66646173: return NxOpusHeaderType.Sadf; // sadf
case 0x5353544B: return NxOpusHeaderType.Ktss; // KTSS
default: throw new NotImplementedException("This Opus header is not supported");
}
}
Expand Down Expand Up @@ -134,6 +138,41 @@ private static void ReadSadfHeader(BinaryReader reader, NxOpusStructure structur
structure.LoopEnd = reader.ReadInt32();
}

private static void ReadKtssHeader(BinaryReader reader, NxOpusStructure structure)
{
if (reader.ReadUInt32() != 0x5353544B) throw new InvalidDataException();
reader.ReadInt32(); // fileSize
reader.BaseStream.Position += 0x18; // padding
reader.ReadUInt16(); // Codec ID
reader.ReadUInt16(); // Unknown
reader.ReadUInt32(); // Subsection start offset
reader.ReadByte(); // Layer count
structure.ChannelCount = reader.ReadByte();
reader.ReadUInt16(); // Unknown
structure.SampleRate = reader.ReadInt32();
structure.SampleCount = reader.ReadInt32();

structure.LoopStart = reader.ReadInt32();
int loopLength = reader.ReadInt32();
structure.LoopEnd = structure.LoopStart + loopLength;
structure.Looping = loopLength != 0;

reader.ReadInt32(); // Padding. Moaaar padding. Koei Tecmo loves padding.
structure.DataOffset = reader.ReadInt32(); // Audio section address
structure.DataSize = reader.ReadInt32(); // Audio section size
reader.ReadInt32(); // Unknown
reader.ReadInt32(); // Frame count
structure.FrameSize = reader.ReadInt16();
reader.ReadInt16(); // Unknown. Always 0x3C0
reader.ReadInt32(); // Original sample rate?
structure.PreSkip = reader.ReadUInt16(); // Pre-skip
reader.ReadByte(); // Stream count
reader.ReadByte(); // Coupled count
reader.ReadBytes(structure.ChannelCount); // Channel mapping

reader.BaseStream.Position = structure.DataOffset;
}

private static void ReadData(BinaryReader reader, NxOpusStructure structure)
{
long startPos = reader.BaseStream.Position;
Expand Down Expand Up @@ -165,6 +204,7 @@ public enum NxOpusHeaderType
{
Standard,
Namco,
Sadf
Sadf,
Ktss
}
}
Loading