Skip to content

Commit

Permalink
Merge pull request #47 from hozuki/mltd-compat
Browse files Browse the repository at this point in the history
MLTD Compatibility
  • Loading branch information
hozuki committed Feb 26, 2018
2 parents 2a14cbc + 5974a6c commit 94e2265
Show file tree
Hide file tree
Showing 15 changed files with 339 additions and 142 deletions.
5 changes: 5 additions & 0 deletions Apps/Hcaenc/DereTore.Apps.Hcaenc.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,19 @@
<LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="CommandLine, Version=2.2.1.0, Culture=neutral, PublicKeyToken=de6f01bd326f8c32, processorArchitecture=MSIL">
<HintPath>..\..\packages\CommandLineParser.2.2.1\lib\net40\CommandLine.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="Options.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Expand Down
16 changes: 16 additions & 0 deletions Apps/Hcaenc/Options.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using CommandLine;

namespace DereTore.Apps.Hcaenc {
public sealed class Options {

[Option('q', "quality", HelpText = "Audio quality (1 to 5)", Default = 1, Required = false)]
public int Quaility { get; set; } = 1;

[Value(0, HelpText = "Input file name", Required = true)]
public string InputFileName { get; set; }

[Value(1, HelpText = "Output HCA file name", Default = null, Required = false)]
public string OutputFileName { get; set; }

}
}
43 changes: 38 additions & 5 deletions Apps/Hcaenc/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System;
using System.IO;
using System.Runtime.InteropServices;
using CommandLine;

namespace DereTore.Apps.Hcaenc {
internal static class Program {
Expand All @@ -9,19 +11,50 @@ private static int Main(string[] args) {
Console.WriteLine(UnsupportedBuildMessage);
return -2;
}
if (args.Length != 2) {

var parser = new Parser(settings => { settings.IgnoreUnknownArguments = true; });

var parserResult = parser.ParseArguments<Options>(args);

if (parserResult.Tag == ParserResultType.NotParsed) {
Console.WriteLine(HelpMessage);
return -1;
}
int quality = 1, cutoff = 0;

var options = ((Parsed<Options>)parserResult).Value;

var inputFile = Path.GetFullPath(options.InputFileName);

string outputFile;

if (!string.IsNullOrEmpty(options.OutputFileName)) {
outputFile = options.OutputFileName;
} else {
var inputFileInfo = new FileInfo(inputFile);
var inputFileDir = inputFileInfo.DirectoryName;
outputFile = Path.Combine(inputFileDir, inputFileInfo.Name.Substring(0, inputFileInfo.Name.Length - inputFileInfo.Extension.Length) + ".hca");
}

var quality = options.Quaility;

if (quality < 1 || quality > 5) {
Console.WriteLine("Warning: Quality should be 1 to 5. Using q=1.");
quality = 1;
}

Console.WriteLine("Encoding {0} to {1} (q={2}) ...", inputFile, outputFile, quality);

// Quality = 3 (~128 Kbps) for MLTD, 1 (~256 Kbps) for CGSS
int cutoff = 0;
ulong key = 0;
return hcaencEncodeToFile(args[0], args[1], quality, cutoff, key);

return hcaencEncodeToFile(inputFile, outputFile, quality, cutoff, key);
}

[DllImport("hcaenc_lite", CallingConvention = CallingConvention.StdCall)]
private static extern int hcaencEncodeToFile([MarshalAs(UnmanagedType.LPStr)] string lpstrInputFile, [MarshalAs(UnmanagedType.LPStr)] string lpstrOutputFile, int nQuality, int nCutoff, ulong ullKey);

private static readonly string HelpMessage = "Usage: hcaenc.exe <input WAVE> <output HCA>";
private static readonly string HelpMessage = "Usage: hcaenc.exe <input WAVE> [<output HCA> -q <quality>]";
private static readonly string UnsupportedBuildMessage = "hcaenc only has 32-bit version due to the limits of hcaenc_lite.dll.";

}
Expand Down
4 changes: 4 additions & 0 deletions Apps/Hcaenc/packages.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommandLineParser" version="2.2.1" targetFramework="net40" />
</packages>
Original file line number Diff line number Diff line change
@@ -1,62 +1,93 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace DereTore.Exchange.Archive.ACB.Serialization {
public partial class AcbSerializer {

private byte[] GetTableBytes<T>(T[] tableRows) where T : UtfRowBase {
/// <summary>
/// Get the unaligned byte array image of a table (UTF row).
/// Returned array should be aligned by <see cref="ArrayExtensions.RoundUpTo(byte[], int)"/>.
/// </summary>
/// <param name="tableRows">Rows in this table.</param>
/// <returns></returns>
private byte[] GetTableBytes(UtfRowBase[] tableRows) {
var tableImage = PrepareTable(tableRows);

byte[] buffer;

using (var memory = new MemoryStream()) {
tableImage.WriteTo(memory);
//memory.Capacity = (int)AcbHelper.RoundUpToAlignment((int)memory.Length, Alignment);
buffer = new byte[memory.Length];
//memory.GetBuffer().CopyTo(buffer, 0);
Array.Copy(memory.GetBuffer(), buffer, buffer.Length);
buffer = memory.ToArray();
}

return buffer;
}

private UtfTableImage PrepareTable<T>(T[] tableRows) where T : UtfRowBase {
var tableName = GetTableName(tableRows);
private UtfTableImage PrepareTable(UtfRowBase[] tableRows) {
if (tableRows.Length < 1) {
throw new ArgumentException("There should be at least one row in a table.", nameof(tableRows));
}

var tableRowTypes = tableRows.Select(row => row.GetType()).ToArray();

if (tableRowTypes.Length > 1) {
for (var i = 1; i < tableRowTypes.Length; ++i) {
if (tableRowTypes[i] != tableRowTypes[i - 1]) {
throw new ArgumentException("All rows must have the same CLR type.");
}
}
}

var tableName = GetTableName(tableRows, tableRowTypes[0]);
var tableImage = new UtfTableImage(tableName, Alignment);

foreach (var tableRow in tableRows) {
var targetMembers = SerializationHelper.GetSearchTargetFieldsAndProperties(tableRow);
var tableImageRow = new List<UtfFieldImage>();

tableImage.Rows.Add(tableImageRow);

// TODO: Save misc data first, then the tables.
foreach (var member in targetMembers) {
var fieldInfo = member.FieldInfo;
var fieldType = fieldInfo.FieldType;
var fieldAttribute = member.FieldAttribute;

CheckFieldType(fieldType);

var fieldValue = member.FieldInfo.GetValue(tableRow);

var fieldImage = new UtfFieldImage {
Order = fieldAttribute.Order,
Name = string.IsNullOrEmpty(fieldAttribute.FieldName) ? fieldInfo.Name : fieldAttribute.FieldName,
Storage = fieldAttribute.Storage,
};

// Empty tables are treated as empty data.
if (IsTypeRowList(fieldType) && fieldValue != null && ((UtfRowBase[])fieldValue).Length > 0) {
var tableBytes = GetTableBytes((UtfRowBase[])fieldValue);

fieldImage.SetValue(tableBytes);
fieldImage.IsTable = true;
} else if (fieldType == typeof(byte[]) && member.ArchiveAttribute != null) {
var files = new List<byte[]> {
(byte[])fieldValue
};

var archiveBytes = SerializationHelper.GetAfs2ArchiveBytes(files.AsReadOnly(), Alignment);

fieldImage.SetValue(archiveBytes);
fieldImage.IsTable = true;
} else {
if (fieldValue == null) {
fieldImage.SetNullValue(MapFromRawType(fieldType));
fieldImage.SetNullValue(MapUtfColumeTypeFromRawType(fieldType));
} else {
fieldImage.SetValue(fieldValue);
}
}

tableImageRow.Add(fieldImage);
}
}
Expand All @@ -73,31 +104,32 @@ private static bool IsTypeRowList(Type type) {
return type.IsArray && type.HasElementType && type.GetElementType().IsSubclassOf(typeof(UtfRowBase));
}

private static ColumnType MapFromRawType(Type rawType) {
private static ColumnType MapUtfColumeTypeFromRawType(Type rawType) {
if (IsTypeRowList(rawType)) {
return ColumnType.Data;
}

var index = SupportedTypes.IndexOf(rawType);

return (ColumnType)index;
}

private static string GetTableName<T>(T[] tableRows) where T : UtfRowBase {
private static string GetTableName(UtfRowBase[] tableRows, Type tableType) {
if (tableRows == null) {
throw new ArgumentNullException(nameof(tableRows));
}
if (tableRows.Length < 1) {
throw new ArgumentException("There should be at least one row in a table.", nameof(tableRows));
}
// Assuming all the rows are of the same type.
var tableType = tableRows[0].GetType();
var tableAttributes = tableType.GetCustomAttributes(typeof(UtfTableAttribute), false);
if (tableAttributes.Length == 1) {
return ((UtfTableAttribute)tableAttributes[0]).Name;

var tableAttribute = SerializationHelper.GetCustomAttribute<UtfTableAttribute>(tableType);

if (!string.IsNullOrEmpty(tableAttribute?.Name)) {
return tableAttribute.Name;
} else {
var s = tableType.Name;

if (s.EndsWith("Table")) {
s = s.Substring(0, s.Length - 5);
}

return s;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using DereTore.Common;

Expand All @@ -9,22 +9,23 @@ public AcbSerializer() {
Alignment = 32;
}

public void Serialize<T>(T[] tableRows, Stream serializationStream) where T : UtfRowBase {
public void Serialize(UtfRowBase[] tableRows, Stream serializationStream) {
if (tableRows == null) {
throw new ArgumentNullException(nameof(tableRows));
}

var tableData = GetTableBytes(tableRows).RoundUpTo(Alignment);

serializationStream.WriteBytes(tableData);
}

public uint Alignment {
get {
return _alignment;
}
get { return _alignment; }
set {
if (value <= 0 || value % 16 != 0) {
throw new ArgumentException("Alignment should be a positive integer, also a multiple of 16.", nameof(value));
}

_alignment = value;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
namespace DereTore.Exchange.Archive.ACB.Serialization {
namespace DereTore.Exchange.Archive.ACB.Serialization {
internal static class ArrayExtensions {

public static byte[] RoundUpTo(this byte[] data, int alignment) {
var newLength = AcbHelper.RoundUpToAlignment(data.Length, alignment);
var buffer = new byte[newLength];

data.CopyTo(buffer, 0);

return buffer;
}

public static byte[] RoundUpTo(this byte[] data, uint alignment) {
var newLength = AcbHelper.RoundUpToAlignment(data.Length, alignment);
var buffer = new byte[newLength];

data.CopyTo(buffer, 0);

return buffer;
}

Expand Down
Loading

0 comments on commit 94e2265

Please sign in to comment.