Implement many things and add vgmstream

This commit is contained in:
Skyth 2017-06-21 01:19:47 +03:00
parent 2131ac6799
commit 6f0437eaf7
56 changed files with 2276 additions and 1627 deletions

Binary file not shown.

View File

@ -0,0 +1,20 @@
Copyright (c) 2008-2010 Adam Gashlin, Fastelbja, Ronny Elfert
Portions Copyright (c) 2004-2008, Marko Kreen
Portions Copyright 2001-2007 jagarl / Kazunori Ueno <jagarl@creator.club.ne.jp>
Portions Copyright (c) 1998, Justin Frankel/Nullsoft Inc.
Portions Copyright (C) 2006 Nullsoft, Inc.
Portions Copyright (c) 2005-2007 Paul Hsieh
Portions Public Domain originating with Sun Microsystems
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

BIN
Libraries/vgmstream.dll Normal file

Binary file not shown.

View File

@ -9,9 +9,10 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>AcbEditor</RootNamespace>
<AssemblyName>AcbEditor</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -22,6 +23,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
@ -6,7 +6,7 @@
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
<userSettings>
<AcbEditor.Properties.Settings>

View File

@ -9,7 +9,7 @@ using AcbEditor.Properties;
using SonicAudioLib;
using SonicAudioLib.CriMw;
using SonicAudioLib.IO;
using SonicAudioLib.Archive;
using SonicAudioLib.Archives;
namespace AcbEditor
{
@ -17,9 +17,15 @@ namespace AcbEditor
{
static void Main(string[] args)
{
if (!File.Exists(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile))
{
Settings.Default.Reset();
Settings.Default.Save();
}
if (args.Length < 1)
{
Console.WriteLine(Properties.Resources.Description);
Console.WriteLine(Resources.Description);
Console.ReadLine();
return;
}
@ -72,7 +78,7 @@ namespace AcbEditor
long awbPosition = acbReader.GetPosition("AwbFile");
if (acbReader.GetLength("AwbFile") > 0)
{
using (Substream afs2Stream = acbReader.GetSubstream("AwbFile"))
using (SubStream afs2Stream = acbReader.GetSubStream("AwbFile"))
{
cpkMode = !CheckIfAfs2(afs2Stream);
@ -92,7 +98,7 @@ namespace AcbEditor
{
cpkMode = false;
using (Substream extAfs2Stream = acbReader.GetSubstream("StreamAwbAfs2Header"))
using (SubStream extAfs2Stream = acbReader.GetSubStream("StreamAwbAfs2Header"))
{
extAfs2Archive.Read(extAfs2Stream);
}
@ -103,7 +109,7 @@ namespace AcbEditor
}
}
using (Substream waveformTableStream = acbReader.GetSubstream("WaveformTable"))
using (SubStream waveformTableStream = acbReader.GetSubStream("WaveformTable"))
using (CriTableReader waveformReader = CriTableReader.Create(waveformTableStream))
{
while (waveformReader.Read())
@ -353,7 +359,7 @@ namespace AcbEditor
static bool CheckIfAfs2(Stream source)
{
long oldPosition = source.Position;
bool result = EndianStream.ReadCString(source, 4) == "AFS2";
bool result = DataStream.ReadCString(source, 4) == "AFS2";
source.Seek(oldPosition, SeekOrigin.Begin);
return result;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
</configuration>

View File

@ -1,478 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using SonicAudioLib.IO;
using NAudio.Wave;
using NAudio.Wave.SampleProviders;
namespace CsbBuilder.Audio
{
public struct AdxHeader
{
public ushort Identifier;
public ushort DataPosition;
public byte EncodeType;
public byte BlockLength;
public byte SampleBitdepth;
public byte ChannelCount;
public uint SampleRate;
public uint SampleCount;
public ushort CutoffFrequency;
public ushort Version;
public short[][] SampleHistories;
}
public class AdxFileReader : WaveStream
{
private class SampleHistory
{
public short Sample1 = 0;
public short Sample2 = 0;
}
private Stream source;
private AdxHeader header;
private WaveFormat waveFormat;
private short coef1;
private short coef2;
private SampleHistory[] histories;
private int sampleCount;
private int readSamples;
private byte[] previousSamples;
private double volume = 1;
private double pitch = 0;
private long delayTime = 0;
private DateTime startTime;
public override WaveFormat WaveFormat
{
get
{
return waveFormat;
}
}
public override long Length
{
get
{
return sampleCount * 2;
}
}
public override long Position
{
get
{
return readSamples * 2;
}
set
{
throw new NotImplementedException();
}
}
public bool IsFinished
{
get
{
return readSamples >= sampleCount;
}
}
public bool IsLoopEnabled { get; set; }
public double Volume
{
set
{
volume = value;
}
}
public double Pitch
{
set
{
pitch = value;
}
}
public int DelayTime
{
set
{
startTime = DateTime.Now;
delayTime = value * 10000;
}
}
public override int Read(byte[] buffer, int offset, int count)
{
if (IsFinished && !IsLoopEnabled)
{
return 0;
}
if (delayTime > 0)
{
if ((DateTime.Now - startTime).Ticks < delayTime)
{
return count;
}
delayTime = 0;
}
int length = count;
while ((length % (header.ChannelCount * 64)) != 0)
{
length++;
}
byte[] samples = new byte[length];
int currentLength = 0;
while (currentLength < length)
{
int sampleLength = GetNextSamples(samples, currentLength);
if (sampleLength < 0)
{
count = count > currentLength ? currentLength : count;
break;
}
currentLength = sampleLength;
}
if (previousSamples != null)
{
samples = previousSamples.Concat(samples).ToArray();
length = samples.Length;
}
if (length > count)
{
previousSamples = samples.Skip(count).ToArray();
}
else if (length < count)
{
previousSamples = null;
count = length;
}
else
{
previousSamples = null;
}
Array.Copy(samples, 0, buffer, offset, count);
return count;
}
private int GetNextSamples(byte[] destination, int startIndex)
{
short[][] channelSamples = new short[header.ChannelCount][];
for (int i = 0; i < header.ChannelCount; i++)
{
if (!DecodeBlock(i, out short[] samples))
{
if (IsLoopEnabled)
{
Reset();
DecodeBlock(i, out samples);
}
else
{
readSamples = sampleCount;
return -1;
}
}
channelSamples[i] = samples;
}
int position = startIndex;
for (int i = 0; i < 32; i++)
{
for (int j = 0; j < header.ChannelCount; j++)
{
short sample = (short)(channelSamples[j][i] * volume);
destination[position++] = (byte)sample;
destination[position++] = (byte)(sample >> 8);
}
}
return position;
}
private bool DecodeBlock(int c, out short[] samples)
{
int scale = EndianStream.ReadUInt16BE(source) + 1;
// There seems to be a null sample block at the end of every adx file.
// It always added a half second delay between intro and loop, so
// I wanted to get rid of it.
if (scale > short.MaxValue + 1)
{
samples = null;
return false;
}
samples = new short[32];
int sampleByte = 0;
SampleHistory history = histories[c];
for (int i = 0; i < 32; i++)
{
if ((i % 2) == 0)
{
sampleByte = source.ReadByte();
}
int sample = ((i & 1) != 0 ?
(sampleByte & 7) - (sampleByte & 8) :
((sampleByte & 0x70) - (sampleByte & 0x80)) >> 4) * scale +
((coef1 * history.Sample1 + coef2 * history.Sample2) >> 12);
sample = sample > short.MaxValue ? short.MaxValue : sample < short.MinValue ? short.MinValue : sample;
samples[i] = (short)sample;
history.Sample2 = history.Sample1;
history.Sample1 = (short)sample;
readSamples++;
}
return true;
}
public void Reset()
{
source.Seek(header.DataPosition + 4, SeekOrigin.Begin);
readSamples = 0;
}
public void ReplaceHistories(AdxFileReader reader)
{
histories = reader.histories;
}
public static AdxHeader LoadHeader(string sourceFileName)
{
using (Stream source = File.OpenRead(sourceFileName))
{
return ReadHeader(source);
}
}
public static AdxHeader ReadHeader(Stream source)
{
AdxHeader header = new AdxHeader();
header.Identifier = EndianStream.ReadUInt16BE(source);
header.DataPosition = EndianStream.ReadUInt16BE(source);
header.EncodeType = EndianStream.ReadByte(source);
header.BlockLength = EndianStream.ReadByte(source);
header.SampleBitdepth = EndianStream.ReadByte(source);
header.ChannelCount = EndianStream.ReadByte(source);
header.SampleRate = EndianStream.ReadUInt32BE(source);
header.SampleCount = EndianStream.ReadUInt32BE(source);
header.CutoffFrequency = EndianStream.ReadUInt16BE(source);
header.Version = EndianStream.ReadUInt16BE(source);
return header;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
source.Close();
}
base.Dispose(disposing);
}
public AdxFileReader(string fileName) : this(File.OpenRead(fileName))
{
}
public AdxFileReader(Stream source)
{
this.source = source;
header = AdxFileReader.ReadHeader(this.source);
source.Seek(header.DataPosition + 4, SeekOrigin.Begin);
// Calculate coefficients
double a = Math.Sqrt(2.0);
double b = a - Math.Cos(header.CutoffFrequency * Math.PI * 2.0 / header.SampleRate);
double c = (b - Math.Sqrt((b - (a - 1.0)) * (a - 1.0 + b))) / (a - 1.0);
coef1 = (short)(8192.0 * c);
coef2 = (short)(c * c * -4096.0);
histories = new SampleHistory[header.ChannelCount];
for (int i = 0; i < histories.Length; i++)
{
histories[i] = new SampleHistory();
}
sampleCount = (int)(header.SampleCount * header.ChannelCount);
waveFormat = new WaveFormat((int)header.SampleRate, 16, header.ChannelCount);
}
}
public class ExtendedAdxFileReader : WaveStream
{
private List<AdxFileReader> readers = new List<AdxFileReader>();
private int currentIndex = 0;
public override long Length
{
get
{
long totalLength = 0;
foreach (AdxFileReader reader in readers)
{
totalLength += reader.Length;
}
return totalLength;
}
}
public override long Position
{
get
{
long position = 0;
foreach (AdxFileReader reader in readers)
{
if (reader == readers[currentIndex])
{
position += reader.Position;
break;
}
position += reader.Length;
}
return position;
}
set
{
throw new NotImplementedException();
}
}
public override WaveFormat WaveFormat
{
get
{
return readers[currentIndex].WaveFormat;
}
}
public double Volume
{
set
{
readers.ForEach(reader => reader.Volume = value);
}
}
public double Pitch
{
set
{
readers.ForEach(reader => reader.Pitch = value);
}
}
public int DelayTime
{
set
{
readers.First().DelayTime = value;
}
}
public override int Read(byte[] buffer, int offset, int count)
{
int num = readers[currentIndex].Read(buffer, 0, count);
if ((num < count) && !readers[currentIndex].IsLoopEnabled)
{
currentIndex++;
readers[currentIndex].ReplaceHistories(readers[currentIndex - 1]);
int num2 = readers[currentIndex].Read(buffer, num, count - num);
return num + num2;
}
else if (readers[currentIndex].IsFinished && !readers[currentIndex].IsLoopEnabled)
{
currentIndex++;
readers[currentIndex].ReplaceHistories(readers[currentIndex - 1]);
num = readers[currentIndex].Read(buffer, 0, count);
}
return num;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
readers.ForEach(reader => reader.Dispose());
}
base.Dispose(disposing);
}
public ExtendedAdxFileReader(params string[] fileNames) : this(fileNames.Select(fileName => File.OpenRead(fileName)).ToArray())
{
}
public ExtendedAdxFileReader(params Stream[] sources)
{
foreach (Stream source in sources)
{
readers.Add(new AdxFileReader(source));
}
// The last one is the one to loop
readers.Last().IsLoopEnabled = true;
}
}
}

View File

@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NAudio.Wave;
namespace CsbBuilder.Audio
{
public class ExtendedWaveStream : WaveStream
{
private int currentStreamIndex = 0;
private readonly List<WaveStream> streams = new List<WaveStream>();
private double volume = 1;
private double pitch = 0;
private long delayTime = 0;
private DateTime startTime;
public override WaveFormat WaveFormat
{
get
{
WaveFormat waveFormat = streams[currentStreamIndex].WaveFormat;
return new WaveFormat(waveFormat.SampleRate + (int)(waveFormat.SampleRate * pitch), waveFormat.Channels);
}
}
public override long Length
{
get
{
return streams.Sum(reader => reader.Length);
}
}
public override long Position
{
get
{
return streams.Take(currentStreamIndex).Sum(reader => reader.Length) + streams[currentStreamIndex].Position;
}
set
{
long position = 0;
for (int i = 0; i < streams.Count; i++)
{
if (position + streams[i].Length > value)
{
currentStreamIndex = i;
streams[i].Position = value - position;
break;
}
position += streams[i].Length;
}
}
}
public double Volume
{
set
{
volume = value;
}
}
public double Pitch
{
set
{
pitch = value;
}
}
public int DelayTime
{
set
{
startTime = DateTime.Now;
delayTime = value * 10000;
}
}
public bool ForceLoop { get; set; }
public override int Read(byte[] buffer, int offset, int count)
{
if (delayTime > 0)
{
if ((DateTime.Now - startTime).Ticks < delayTime)
{
return count;
}
delayTime = 0;
}
int num = streams[currentStreamIndex].Read(buffer, 0, count);
if (num < count && currentStreamIndex != streams.Count - 1)
{
currentStreamIndex++;
num += streams[currentStreamIndex].Read(buffer, num, count - num);
}
else if (ForceLoop && num < count && currentStreamIndex == streams.Count - 1)
{
streams[currentStreamIndex].Position = 0;
num += streams[currentStreamIndex].Read(buffer, num, count - num);
}
if (volume != 1)
{
PostSampleEditor.ApplyVolume(buffer, offset, num, volume);
}
return num;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
foreach (var reader in streams)
{
reader.Dispose();
}
}
base.Dispose(disposing);
}
public ExtendedWaveStream(params WaveStream[] waveStreams)
{
if (waveStreams.Length == 0)
{
throw new ArgumentException("You must at least specify one source!", nameof(waveStreams));
}
streams.AddRange(waveStreams);
}
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CsbBuilder.Audio
{
public static class PostSampleEditor
{
public static void ApplyVolume(byte[] buffer, int offset, int count, double volume)
{
for (int i = offset; i < count; i += 2)
{
ApplyVolume(buffer, i, volume);
}
}
public static void ApplyVolume(byte[] buffer, int offset, double volume)
{
int sample = (int)((short)(buffer[offset] | buffer[offset + 1] << 8) * volume);
short sample16 =
sample > short.MaxValue ? short.MaxValue :
sample < short.MinValue ? short.MinValue :
(short)sample;
buffer[offset] = (byte)sample16;
buffer[offset + 1] = (byte)(sample16 >> 8);
}
}
}

View File

@ -0,0 +1,313 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace CsbBuilder.Audio
{
/// <summary>
/// Represents a static class for calling native VGMSTREAM methods.
/// </summary>
public static class VGMStreamNative
{
/// <summary>
/// Path of the VGMSTREAM DLL file.
/// </summary>
public const string DllName = "vgmstream.dll";
/// <summary>
/// Size of VGMSTREAM structure.
/// </summary>
public const int SizeOfVgmStream = 152;
/// <summary>
/// Size of VGMSTREAMCHANNEL structure.
/// </summary>
public const int SizeOfVgmStreamChannel = 552;
#region VGMStream Exports
/// <summary>
/// Initializes a VGMSTREAM from source file name by doing format detection and returns a usable pointer
/// to it, or NULL on failure.
/// </summary>
/// <param name="sourceFileName">Path to source file name.</param>
/// <returns>Pointer to VGMSTREAM or NULL on failure.</returns>
[DllImport(DllName, EntryPoint = "init_vgmstream", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Initialize(string sourceFileName);
/// <summary>
/// Initializes a VGMSTREAM from stream file by doing format detection and returns a usable pointer
/// to it, or NULL on failure.
/// </summary>
/// <param name="streamFile">Pointer to stream file.</param>
/// <returns>Pointer to VGMSTREAM or NULL on failure.</returns>
[DllImport(DllName, EntryPoint = "init_from_STREAMFILE", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr InitializeFromStreamFile(IntPtr streamFile);
/// <summary>
/// Resets a VGMSTREAM to start of stream.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
[DllImport(DllName, EntryPoint = "reset_vgmstream", CallingConvention = CallingConvention.Cdecl)]
public static extern void Reset(IntPtr vgmstream);
/// <summary>
/// Closes an open VGMSTREAM.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
[DllImport(DllName, EntryPoint = "close_vgmstream", CallingConvention = CallingConvention.Cdecl)]
public static extern void Close(IntPtr vgmstream);
/// <summary>
/// Calculates the number of samples to be played based on looping parameters.
/// </summary>
[DllImport(DllName, EntryPoint = "get_vgmstream_play_samples", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetPlaySamples(double loopTimes, double fadeSeconds, double fadeDelaySeconds, IntPtr vgmstream);
/// <summary>
/// Renders VGMSTREAM to sample buffer.
/// </summary>
/// <param name="buffer">Destination sample buffer.</param>
/// <param name="sampleCount">Amount of samples to render.</param>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
[DllImport(DllName, EntryPoint = "render_vgmstream", CallingConvention = CallingConvention.Cdecl)]
public static extern void Render(short[] buffer, int sampleCount, IntPtr vgmstream);
/// <summary>
/// Renders VGMSTREAM to byte buffer.
/// </summary>
/// <param name="buffer">Destination byte buffer.</param>
/// <param name="sampleCount">Amount of samples to render.</param>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
[DllImport(DllName, EntryPoint = "render_vgmstream", CallingConvention = CallingConvention.Cdecl)]
public static extern void Render8(byte[] buffer, int sampleCount, IntPtr vgmstream);
/// <summary>
/// Writes a description of the stream into array pointed by description,
/// which must be length bytes long. Will always be null-terminated if length > 0
/// </summary>
[DllImport(DllName, EntryPoint = "describe_vgmstream", CallingConvention = CallingConvention.Cdecl)]
public static extern void Describe(IntPtr vgmstream, IntPtr description, int length);
/// <summary>
/// Returns the average bitrate in bps of all unique files contained within
/// this stream. Compares files by absolute paths.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
[DllImport(DllName, EntryPoint = "get_vgmstream_average_bitrate", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetAverageBitrate(IntPtr vgmstream);
/// <summary>
/// Allocates a VGMSTREAM.
/// </summary>
[DllImport(DllName, EntryPoint = "allocate_vgmstream", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Allocate(int channelCount, [MarshalAs(UnmanagedType.I4)]bool looped);
/// <summary>
/// smallest self-contained group of samples is a frame
/// </summary>
[DllImport(DllName, EntryPoint = "get_vgmstream_samples_per_frame", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSamplesPerFrame(IntPtr vgmstream);
/// <summary>
/// Gets the number of bytes per frame.
/// </summary>
[DllImport(DllName, EntryPoint = "get_vgmstream_frame_size", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetFrameSize(IntPtr vgmstream);
/// <summary>
/// in NDS IMA the frame size is the block size, so the last one is short
/// </summary>
[DllImport(DllName, EntryPoint = "get_vgmstream_samples_per_shortframe", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSamplesPerShortframe(IntPtr vgmstream);
[DllImport(DllName, EntryPoint = "get_vgmstream_shortframe_size", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetShortframeSize(IntPtr vgmstream);
/// <summary>
/// Assumes that we have written samplesWrittem into the buffer already, and we have samplesToDo consecutive
/// samples ahead of us. Decode those samples into the buffer.
/// </summary>
[DllImport(DllName, EntryPoint = "decode_vgmstream", CallingConvention = CallingConvention.Cdecl)]
public static extern void Decode(IntPtr vgmstream, int samplesWritten, int samplesToDo, short[] buffer);
/// <summary>
/// Assumes additionally that we have samplesToDo consecutive samples in "data",
/// and this this is for channel number "channel".
/// </summary>
[DllImport(DllName, EntryPoint = "decode_vgmstream_mem", CallingConvention = CallingConvention.Cdecl)]
public static extern void DecodeMem(IntPtr vgmstream, int samplesWritten, int samplesToDo, short[] buffer, byte[] data, int channel);
/// <summary>
/// Calculates number of consecutive samples to do (taking into account stopping for loop start and end.)
/// </summary>
[DllImport(DllName, EntryPoint = "vgmstream_samples_to_do", CallingConvention = CallingConvention.Cdecl)]
public static extern int SamplesToDo(int samplesThisBlock, int samplesPerFrame, IntPtr vgmstream);
/// <summary>
/// Detects start and save values, also detects end and restore values. Only works on exact sample values.
/// </summary>
[DllImport(DllName, EntryPoint = "vgmstream_do_loop", CallingConvention = CallingConvention.Cdecl)]
public static extern int DoLoop(IntPtr vgmstream);
/// <summary>
/// Opens a stream for reading at offset (standarized taking into account layouts, channels and so on.)
/// returns 0 on failure
/// </summary>
[DllImport(DllName, EntryPoint = "vgmstream_open_stream", CallingConvention = CallingConvention.Cdecl)]
public static extern int OpenStream(IntPtr vgmstream, IntPtr streamFile, long position);
#endregion
#region Format Exports
/// <summary>
/// Gets a pointer to the array of supported formats.
/// </summary>
[DllImport(DllName, EntryPoint = "vgmstream_get_formats", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetFormatsPtr();
/// <summary>
/// Gets the length of the format array.
/// </summary>
/// <returns></returns>
[DllImport(DllName, EntryPoint = "vgmstream_get_formats_length", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetFormatsLength();
[DllImport(DllName, EntryPoint = "get_vgmstream_coding_description", CallingConvention = CallingConvention.Cdecl)]
public static extern string GetCodingDescription(int codingEnumCode);
[DllImport(DllName, EntryPoint = "get_vgmstream_layout_description", CallingConvention = CallingConvention.Cdecl)]
public static extern string GetLayoutDescription(int layoutEnumCode);
[DllImport(DllName, EntryPoint = "get_vgmstream_meta_description", CallingConvention = CallingConvention.Cdecl)]
public static extern string GetMetaDescription(int metaEnumCode);
#endregion
#region Helper Methods
/// <summary>
/// Gets the sample count of a VGMSTREAM.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
public static int GetSampleCount(IntPtr vgmstream)
{
return Marshal.ReadInt32(vgmstream);
}
/// <summary>
/// Gets the sample rate of a VGMSTREAM.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
public static int GetSampleRate(IntPtr vgmstream)
{
return Marshal.ReadInt32(vgmstream, 4);
}
/// <summary>
/// Gets the channel count of a VGMSTREAM.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
public static int GetChannelCount(IntPtr vgmstream)
{
return Marshal.ReadInt32(vgmstream, 8);
}
/// <summary>
/// Gets the absolute sample count of a VGMSTREAM.
/// (sample count * channel count)
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
public static int GetAbsoluteSampleCount(IntPtr vgmstream)
{
return GetSampleCount(vgmstream) * GetChannelCount(vgmstream);
}
/// <summary>
/// Gets the loop flag of a VGMSTREAM.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
public static bool GetLoopFlag(IntPtr vgmstream)
{
return Marshal.ReadInt32(vgmstream, 28) != 0;
}
/// <summary>
/// Sets the loop flag of a VGMSTREAM.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
public static void SetLoopFlag(IntPtr vgmstream, bool value)
{
if (value && !GetLoopFlag(vgmstream))
{
Marshal.WriteIntPtr(vgmstream, 48, Marshal.AllocHGlobal(GetChannelCount(vgmstream) * SizeOfVgmStreamChannel));
}
Marshal.WriteInt32(vgmstream, 28, value ? 1 : 0);
}
/// <summary>
/// Gets the loop start sample of a VGMSTREAM.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
public static int GetLoopStartSample(IntPtr vgmstream)
{
return Marshal.ReadInt32(vgmstream, 32);
}
/// <summary>
/// Sets the loop start sample of a VGMSTREAM.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
public static void SetLoopStartSample(IntPtr vgmstream, int value)
{
Marshal.WriteInt32(vgmstream, 32, value);
}
/// <summary>
/// Gets the loop end sample of a VGMSTREAM.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
public static int GetLoopEndSample(IntPtr vgmstream)
{
return Marshal.ReadInt32(vgmstream, 36);
}
/// <summary>
/// Sets the loop end sample of a VGMSTREAM.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
public static void SetLoopEndSample(IntPtr vgmstream, int value)
{
Marshal.WriteInt32(vgmstream, 36, value);
}
/// <summary>
/// Gets the current sample of a VGMSTREAM.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
public static int GetCurrentSample(IntPtr vgmstream)
{
return Marshal.ReadInt32(vgmstream, 52);
}
/// <summary>
/// Gets an array of supported formats.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
public static string[] GetFormats()
{
string[] formats = new string[GetFormatsLength()];
IntPtr ptr = GetFormatsPtr();
for (int i = 0; i < formats.Length; i++)
{
IntPtr stringPtr = Marshal.ReadIntPtr(ptr, i * IntPtr.Size);
formats[i] = Marshal.PtrToStringAnsi(stringPtr);
}
return formats;
}
#endregion
}
}

View File

@ -0,0 +1,282 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.InteropServices;
using NAudio.Wave;
using NAudio.Wave.SampleProviders;
namespace CsbBuilder.Audio
{
/// <summary>
/// Represents a NAudio WaveStream for VGMSTREAM playback.
/// </summary>
public class VGMStreamReader : WaveStream
{
private IntPtr vgmstream;
private WaveFormat waveFormat;
private int sampleCount;
private bool loopFlag;
private byte[] cache;
/// <summary>
/// Gets the wave format of the VGMSTREAM.
/// </summary>
public override WaveFormat WaveFormat
{
get
{
return waveFormat;
}
}
/// <summary>
/// Gets the length of this VGMSTREAM in bytes.
/// </summary>
public override long Length
{
get
{
return sampleCount * waveFormat.Channels * 2;
}
}
/// <summary>
/// Gets or sets the position of this VGMSTREAM in bytes.
/// </summary>
public override long Position
{
get
{
return VGMStreamNative.GetCurrentSample(vgmstream) * waveFormat.Channels * 2;
}
set
{
CurrentSample = (int)(value / waveFormat.Channels / 2);
}
}
/// <summary>
/// Gets or sets the current sample of this VGMSTREAM.
/// </summary>
public int CurrentSample
{
get
{
return VGMStreamNative.GetCurrentSample(vgmstream);
}
set
{
int currentSample = VGMStreamNative.GetCurrentSample(vgmstream);
if (value == currentSample || value > sampleCount)
{
return;
}
if (value > currentSample)
{
value = value - currentSample;
}
else if (value < currentSample)
{
VGMStreamNative.Reset(vgmstream);
}
int cacheSampleLength = cache.Length / waveFormat.Channels / 2;
int length = 0;
while (length < value)
{
if (length + cacheSampleLength >= value)
{
cacheSampleLength = value - length;
}
VGMStreamNative.Render8(cache, cacheSampleLength, vgmstream);
length += cacheSampleLength;
}
}
}
/// <summary>
/// Determines whether the loop is enabled for the VGMSTREAM.
/// </summary>
public bool LoopFlag
{
get
{
return loopFlag;
}
}
/// <summary>
/// Gets the loop start sample of the VGMSTREAM.
/// </summary>
public long LoopStartSample
{
get
{
return VGMStreamNative.GetLoopStartSample(vgmstream);
}
}
/// <summary>
/// Gets the loop start position of the VGMSTREAM in bytes.
/// </summary>
public long LoopStartPosition
{
get
{
return LoopStartSample * waveFormat.Channels * 2;
}
}
/// <summary>
/// Gets the loop end sample of the VGMSTREAM.
/// </summary>
public long LoopEndSample
{
get
{
return VGMStreamNative.GetLoopEndSample(vgmstream);
}
}
/// <summary>
/// Gets the loop end position of the VGMSTREAM in bytes.
/// </summary>
public long LoopEndPosition
{
get
{
return LoopEndSample * waveFormat.Channels * 2;
}
}
/// <summary>
/// Forces the VGMSTREAM to loop from start to end.
/// This method destroys the previous loop points.
/// </summary>
public void ForceLoop()
{
VGMStreamNative.SetLoopFlag(vgmstream, true);
VGMStreamNative.SetLoopStartSample(vgmstream, 0);
VGMStreamNative.SetLoopEndSample(vgmstream, sampleCount);
loopFlag = true;
}
/// <summary>
/// Disables the loop for this VGMSTREAM.
/// </summary>
public void DisableLoop()
{
loopFlag = false;
VGMStreamNative.SetLoopFlag(vgmstream, false);
}
/// <summary>
/// Resets the VGMSTREAM to beginning.
/// </summary>
public void Reset()
{
VGMStreamNative.Reset(vgmstream);
}
/// <summary>
/// Renders the VGMSTREAM to byte buffer.
/// </summary>
/// <param name="buffer">Destination byte buffer.</param>
/// <param name="offset">Offset within buffer to write to.</param>
/// <param name="count">Count of bytes to render.</param>
/// <returns>Number of bytes read.</returns>
public override int Read(byte[] buffer, int offset, int count)
{
if (cache == null || cache.Length < buffer.Length)
{
cache = new byte[buffer.Length];
}
int currentSample = VGMStreamNative.GetCurrentSample(vgmstream);
int sampleCount_vgmstream = count / waveFormat.Channels / 2;
if (currentSample >= sampleCount && !loopFlag)
{
return 0;
}
if (!loopFlag && currentSample + sampleCount_vgmstream > sampleCount)
{
sampleCount_vgmstream = sampleCount - currentSample;
}
count = sampleCount_vgmstream * waveFormat.Channels * 2;
VGMStreamNative.Render8(cache, sampleCount_vgmstream, vgmstream);
Array.Copy(cache, 0, buffer, offset, count);
return count;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
VGMStreamNative.Close(vgmstream);
}
base.Dispose(disposing);
}
private void FromPtr(IntPtr vgmstream)
{
if (vgmstream == IntPtr.Zero)
{
throw new NullReferenceException("VGMSTREAM pointer is set to null!");
}
waveFormat = new WaveFormat(VGMStreamNative.GetSampleRate(vgmstream), 16, VGMStreamNative.GetChannelCount(vgmstream));
sampleCount = VGMStreamNative.GetSampleCount(vgmstream);
loopFlag = VGMStreamNative.GetLoopFlag(vgmstream);
cache = new byte[4096];
}
/// <summary>
/// Constructs from source file name.
/// </summary>
/// <param name="sourceFileName">Source file name to load.</param>
public VGMStreamReader(string sourceFileName)
{
if (!File.Exists(sourceFileName))
{
throw new FileNotFoundException($"VGMStream could not find file: {sourceFileName}");
}
vgmstream = VGMStreamNative.Initialize(sourceFileName);
if (vgmstream == IntPtr.Zero)
{
throw new NullReferenceException($"VGMStream could not initialize file: {sourceFileName}");
}
FromPtr(vgmstream);
}
/// <summary>
/// Constructs from pointer.
/// </summary>
/// <param name="vgmstream">Pointer to VGMSTREAM.</param>
public VGMStreamReader(IntPtr vgmstream)
{
FromPtr(vgmstream);
}
}
}

View File

@ -5,12 +5,12 @@ using System.Text;
using System.IO;
using CsbBuilder.Project;
using CsbBuilder.BuilderNode;
using CsbBuilder.BuilderNodes;
using CsbBuilder.Serialization;
using SonicAudioLib.CriMw;
using SonicAudioLib.CriMw.Serialization;
using SonicAudioLib.Archive;
using SonicAudioLib.Archives;
namespace CsbBuilder.Builder
{
@ -192,6 +192,8 @@ namespace CsbBuilder.Builder
Flag = CriAaxEntryFlag.Intro,
FilePath = new FileInfo(Path.Combine(project.AudioDirectory.FullName, soundElementNode.Intro)),
});
aaxArchive.SetModeFromExtension(soundElementNode.Intro);
}
if (!string.IsNullOrEmpty(soundElementNode.Loop))
@ -201,6 +203,8 @@ namespace CsbBuilder.Builder
Flag = CriAaxEntryFlag.Loop,
FilePath = new FileInfo(Path.Combine(project.AudioDirectory.FullName, soundElementNode.Loop)),
});
aaxArchive.SetModeFromExtension(soundElementNode.Loop);
}
byte[] data = new byte[0];
@ -228,7 +232,7 @@ namespace CsbBuilder.Builder
{
Name = soundElementNode.Name,
Data = data,
FormatType = 0,
FormatType = (byte)aaxArchive.Mode,
SoundFrequency = soundElementNode.SampleRate,
NumberChannels = soundElementNode.ChannelCount,
Streaming = soundElementNode.Streaming,

View File

@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
namespace CsbBuilder.BuilderNode
namespace CsbBuilder.BuilderNodes
{
public class BuilderAisacGraphNode : BuilderBaseNode
{

View File

@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
namespace CsbBuilder.BuilderNode
namespace CsbBuilder.BuilderNodes
{
public class BuilderAisacNode : BuilderBaseNode
{

View File

@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
namespace CsbBuilder.BuilderNode
namespace CsbBuilder.BuilderNodes
{
public class BuilderAisacPointNode : BuilderBaseNode
{

View File

@ -6,12 +6,12 @@ using System.Threading.Tasks;
using System.ComponentModel;
using System.Globalization;
namespace CsbBuilder.BuilderNode
namespace CsbBuilder.BuilderNodes
{
public abstract class BuilderBaseNode : ICloneable
{
[Category("General"), ReadOnly(true)]
[Description("The name of this node. Shift JIS encoding is used for this in the Cue Sheet Binary, so try to avoid using special characters that this codec does not support.")]
[Description("The name of this node.")]
public string Name { get; set; }
public object Clone()

View File

@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
namespace CsbBuilder.BuilderNode
namespace CsbBuilder.BuilderNodes
{
public class BuilderCueNode : BuilderBaseNode
{
@ -19,7 +19,7 @@ namespace CsbBuilder.BuilderNode
public string SynthReference { get; set; }
[Category("General"), DisplayName("User Comment")]
[Description("User comment of this Cue. Shift JIS encoding is used for this in the Cue Sheet Binary, so try to avoid using special characters that this codec does not support.")]
[Description("User comment of this Cue.")]
public string UserComment { get; set; }
[Category("General")]

View File

@ -5,7 +5,7 @@ using System.Text;
using System.IO;
using System.ComponentModel;
namespace CsbBuilder.BuilderNode
namespace CsbBuilder.BuilderNodes
{
public class BuilderSoundElementNode : BuilderBaseNode
{

View File

@ -6,7 +6,7 @@ using System.Threading.Tasks;
using System.ComponentModel;
using System.Reflection;
namespace CsbBuilder.BuilderNode
namespace CsbBuilder.BuilderNodes
{
public enum BuilderSynthType
{
@ -28,7 +28,7 @@ namespace CsbBuilder.BuilderNode
{
private BuilderSynthPlaybackType playbackType = BuilderSynthPlaybackType.Polyphonic;
private Random random = new Random();
private List<int> indices = new List<int>();
private int previousChild = -1;
private int nextChild = -1;
private byte playbackProbability = 100;
@ -362,20 +362,15 @@ namespace CsbBuilder.BuilderNode
{
if (playbackType == BuilderSynthPlaybackType.RandomNoRepeat)
{
if (indices.Count == Children.Count)
int randomChild = random.Next(Children.Count);
while (randomChild == previousChild)
{
indices.Clear();
randomChild = random.Next(Children.Count);
}
int nextChild = random.Next(Children.Count);
while (indices.Contains(nextChild))
{
nextChild = random.Next(Children.Count);
}
indices.Add(nextChild);
return nextChild;
previousChild = randomChild;
return randomChild;
}
return random.Next(Children.Count);

View File

@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
namespace CsbBuilder.BuilderNode
namespace CsbBuilder.BuilderNodes
{
public class BuilderVoiceLimitGroupNode : BuilderBaseNode
{

View File

@ -9,9 +9,10 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CsbBuilder</RootNamespace>
<AssemblyName>CsbBuilder</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -22,6 +23,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -53,15 +55,18 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Audio\AdxFileReader.cs" />
<Compile Include="BuilderNode\BuilderAisacGraphNode.cs" />
<Compile Include="BuilderNode\BuilderAisacNode.cs" />
<Compile Include="BuilderNode\BuilderAisacPointNode.cs" />
<Compile Include="BuilderNode\BuilderCueNode.cs" />
<Compile Include="BuilderNode\BuilderBaseNode.cs" />
<Compile Include="BuilderNode\BuilderSoundElementNode.cs" />
<Compile Include="BuilderNode\BuilderSynthNode.cs" />
<Compile Include="BuilderNode\BuilderVoiceLimitGroupNode.cs" />
<Compile Include="Audio\ExtendedWaveStream.cs" />
<Compile Include="Audio\PostSampleEditor.cs" />
<Compile Include="Audio\VGMStreamNative.cs" />
<Compile Include="Audio\VGMStreamReader.cs" />
<Compile Include="BuilderNodes\BuilderAisacGraphNode.cs" />
<Compile Include="BuilderNodes\BuilderAisacNode.cs" />
<Compile Include="BuilderNodes\BuilderAisacPointNode.cs" />
<Compile Include="BuilderNodes\BuilderCueNode.cs" />
<Compile Include="BuilderNodes\BuilderBaseNode.cs" />
<Compile Include="BuilderNodes\BuilderSoundElementNode.cs" />
<Compile Include="BuilderNodes\BuilderSynthNode.cs" />
<Compile Include="BuilderNodes\BuilderVoiceLimitGroupNode.cs" />
<Compile Include="Builder\CsbBuilder.cs" />
<Compile Include="ExceptionForm.cs">
<SubType>Form</SubType>
@ -228,7 +233,11 @@
<None Include="Resources\Undo_grey_16x.png" />
<None Include="Resources\Settings_16x.png" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>copy "$(SolutionDir)Libraries\*.dll" "$(TargetDir)"</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@ -7,12 +7,12 @@ using System.ComponentModel;
using CsbBuilder.Audio;
using CsbBuilder.Project;
using CsbBuilder.BuilderNode;
using CsbBuilder.BuilderNodes;
using CsbBuilder.Serialization;
using SonicAudioLib.IO;
using SonicAudioLib.CriMw.Serialization;
using SonicAudioLib.Archive;
using SonicAudioLib.Archives;
using System.Windows.Forms;
@ -25,7 +25,7 @@ namespace CsbBuilder.Importer
var extractor = new DataExtractor();
extractor.BufferSize = MainForm.Settings.BufferSize;
extractor.EnableThreading = MainForm.Settings.EnableThreading;
extractor.MaxThreads = MainForm.Settings.MaxCores;
extractor.MaxThreads = MainForm.Settings.MaxThreads;
// Find the CPK first
string cpkPath = Path.ChangeExtension(path, "cpk");
@ -83,16 +83,22 @@ namespace CsbBuilder.Importer
CriAaxArchive aaxArchive = new CriAaxArchive();
CriCpkEntry cpkEntry = null;
if (soundElementNode.Streaming)
CriCpkEntry cpkEntry = cpkArchive.GetByPath(soundElementTable.Name);
if (soundElementNode.Streaming && cpkEntry != null)
{
using (Stream source = File.OpenRead(cpkPath))
using (Stream entrySource = (cpkEntry = cpkArchive.GetByPath(soundElementTable.Name)).Open(source))
using (Stream entrySource = cpkEntry.Open(source))
{
aaxArchive.Read(entrySource);
}
}
else if (soundElementNode.Streaming && cpkEntry == null)
{
soundElementNode.Intro = soundElementNode.Loop = string.Empty;
soundElementNode.SampleRate = soundElementNode.SampleCount = soundElementNode.ChannelCount = 0;
}
else
{
aaxArchive.Load(soundElementTable.Data);
@ -103,13 +109,13 @@ namespace CsbBuilder.Importer
string outputFileName = Path.Combine(project.AudioDirectory.FullName, soundElementTable.Name.Replace('/', '_'));
if (entry.Flag == CriAaxEntryFlag.Intro)
{
outputFileName += "_Intro.adx";
outputFileName += $"_Intro{aaxArchive.GetModeExtension()}";
soundElementNode.Intro = Path.GetFileName(outputFileName);
}
else if (entry.Flag == CriAaxEntryFlag.Loop)
{
outputFileName += "_Loop.adx";
outputFileName += $"_Loop{aaxArchive.GetModeExtension()}";
soundElementNode.Loop = Path.GetFileName(outputFileName);
}

View File

@ -46,11 +46,11 @@
this.buildCurrentProjectAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem49 = new System.Windows.Forms.ToolStripMenuItem();
this.aDXToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.convertADXsToWAVToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.aAXToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.extractAAXToFolderToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.packFolderToAAXToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.convertADXsToWAVToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.convertAndSplitLoopingToWAVToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator26 = new System.Windows.Forms.ToolStripSeparator();
this.settingsButton = new System.Windows.Forms.ToolStripMenuItem();
this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@ -182,6 +182,7 @@
this.toolStripMenuItem41 = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem42 = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem43 = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator27 = new System.Windows.Forms.ToolStripSeparator();
this.mainMenu.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout();
@ -375,42 +376,29 @@
this.toolStripSeparator26,
this.settingsButton});
this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem";
this.toolsToolStripMenuItem.Size = new System.Drawing.Size(48, 20);
this.toolsToolStripMenuItem.Size = new System.Drawing.Size(47, 20);
this.toolsToolStripMenuItem.Text = "Tools";
//
// toolStripMenuItem49
//
this.toolStripMenuItem49.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.aDXToolStripMenuItem,
this.convertADXsToWAVToolStripMenuItem,
this.convertAndSplitLoopingToWAVToolStripMenuItem,
this.toolStripSeparator27,
this.aAXToolStripMenuItem});
this.toolStripMenuItem49.Image = global::CsbBuilder.Properties.Resources.Sound;
this.toolStripMenuItem49.Name = "toolStripMenuItem49";
this.toolStripMenuItem49.Size = new System.Drawing.Size(138, 22);
this.toolStripMenuItem49.Size = new System.Drawing.Size(152, 22);
this.toolStripMenuItem49.Text = "Audio Tools";
//
// aDXToolStripMenuItem
//
this.aDXToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.convertADXsToWAVToolStripMenuItem});
this.aDXToolStripMenuItem.Name = "aDXToolStripMenuItem";
this.aDXToolStripMenuItem.Size = new System.Drawing.Size(97, 22);
this.aDXToolStripMenuItem.Text = "ADX";
//
// convertADXsToWAVToolStripMenuItem
//
this.convertADXsToWAVToolStripMenuItem.Name = "convertADXsToWAVToolStripMenuItem";
this.convertADXsToWAVToolStripMenuItem.Size = new System.Drawing.Size(198, 22);
this.convertADXsToWAVToolStripMenuItem.Text = "Convert ADX(s) to WAV";
this.convertADXsToWAVToolStripMenuItem.Click += new System.EventHandler(this.convertADXsToWAVToolStripMenuItem_Click);
//
// aAXToolStripMenuItem
//
this.aAXToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.extractAAXToFolderToolStripMenuItem,
this.packFolderToAAXToolStripMenuItem});
this.aAXToolStripMenuItem.Name = "aAXToolStripMenuItem";
this.aAXToolStripMenuItem.Size = new System.Drawing.Size(97, 22);
this.aAXToolStripMenuItem.Text = "AAX";
this.aAXToolStripMenuItem.Size = new System.Drawing.Size(240, 22);
this.aAXToolStripMenuItem.Text = "AAX Archive";
//
// extractAAXToFolderToolStripMenuItem
//
@ -423,19 +411,36 @@
//
this.packFolderToAAXToolStripMenuItem.Name = "packFolderToAAXToolStripMenuItem";
this.packFolderToAAXToolStripMenuItem.Size = new System.Drawing.Size(196, 22);
this.packFolderToAAXToolStripMenuItem.Text = "Pack ADX(s) to AAX";
this.packFolderToAAXToolStripMenuItem.Text = "Pack file(s) to AAX";
this.packFolderToAAXToolStripMenuItem.Click += new System.EventHandler(this.packFolderToAAXToolStripMenuItem_Click);
//
// convertADXsToWAVToolStripMenuItem
//
this.convertADXsToWAVToolStripMenuItem.Name = "convertADXsToWAVToolStripMenuItem";
this.convertADXsToWAVToolStripMenuItem.Size = new System.Drawing.Size(240, 22);
this.convertADXsToWAVToolStripMenuItem.Text = "Convert to WAV File(s)";
this.convertADXsToWAVToolStripMenuItem.ToolTipText = "Convert audio files to WAV files";
this.convertADXsToWAVToolStripMenuItem.Click += new System.EventHandler(this.convertADXsToWAVToolStripMenuItem_Click);
//
// convertAndSplitLoopingToWAVToolStripMenuItem
//
this.convertAndSplitLoopingToWAVToolStripMenuItem.Name = "convertAndSplitLoopingToWAVToolStripMenuItem";
this.convertAndSplitLoopingToWAVToolStripMenuItem.Size = new System.Drawing.Size(240, 22);
this.convertAndSplitLoopingToWAVToolStripMenuItem.Text = "Convert and split to WAV File(s)";
this.convertAndSplitLoopingToWAVToolStripMenuItem.ToolTipText = "Convert and split audio files to Intro and Loop parts using loop information in t" +
"he file";
this.convertAndSplitLoopingToWAVToolStripMenuItem.Click += new System.EventHandler(this.convertAndSplitLoopingToWAVToolStripMenuItem_Click);
//
// toolStripSeparator26
//
this.toolStripSeparator26.Name = "toolStripSeparator26";
this.toolStripSeparator26.Size = new System.Drawing.Size(135, 6);
this.toolStripSeparator26.Size = new System.Drawing.Size(149, 6);
//
// settingsButton
//
this.settingsButton.Image = global::CsbBuilder.Properties.Resources.Settings;
this.settingsButton.Name = "settingsButton";
this.settingsButton.Size = new System.Drawing.Size(138, 22);
this.settingsButton.Size = new System.Drawing.Size(152, 22);
this.settingsButton.Text = "Settings";
this.settingsButton.Click += new System.EventHandler(this.OpenSettings);
//
@ -453,6 +458,7 @@
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.propertyGrid.Enabled = false;
this.propertyGrid.LineColor = System.Drawing.SystemColors.ControlDark;
this.propertyGrid.Location = new System.Drawing.Point(252, 24);
this.propertyGrid.Name = "propertyGrid";
this.propertyGrid.Size = new System.Drawing.Size(374, 556);
@ -498,7 +504,7 @@
this.tabPage3.Padding = new System.Windows.Forms.Padding(3);
this.tabPage3.Size = new System.Drawing.Size(242, 221);
this.tabPage3.TabIndex = 0;
this.tabPage3.Text = "Cue";
this.tabPage3.Text = "Cue Nodes";
this.tabPage3.UseVisualStyleBackColor = true;
//
// cueTree
@ -580,7 +586,7 @@
this.tabPage4.Padding = new System.Windows.Forms.Padding(3);
this.tabPage4.Size = new System.Drawing.Size(242, 279);
this.tabPage4.TabIndex = 0;
this.tabPage4.Text = "Synth";
this.tabPage4.Text = "Synth Nodes";
this.tabPage4.UseVisualStyleBackColor = true;
//
// synthTree
@ -739,7 +745,7 @@
this.tabPage5.Padding = new System.Windows.Forms.Padding(3);
this.tabPage5.Size = new System.Drawing.Size(242, 221);
this.tabPage5.TabIndex = 0;
this.tabPage5.Text = "Sound Element";
this.tabPage5.Text = "Sound Element Nodes";
this.tabPage5.UseVisualStyleBackColor = true;
//
// soundElementTree
@ -783,7 +789,7 @@
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(242, 279);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "AISAC";
this.tabPage1.Text = "AISAC Nodes";
this.tabPage1.UseVisualStyleBackColor = true;
//
// aisacTree
@ -819,13 +825,13 @@
this.toolStripMenuItem47});
this.aisacTreeMenu.Name = "contextMenuStrip1";
this.aisacTreeMenu.RenderMode = System.Windows.Forms.ToolStripRenderMode.System;
this.aisacTreeMenu.Size = new System.Drawing.Size(190, 104);
this.aisacTreeMenu.Size = new System.Drawing.Size(189, 104);
//
// toolStripMenuItem45
//
this.toolStripMenuItem45.Image = global::CsbBuilder.Properties.Resources.Create;
this.toolStripMenuItem45.Name = "toolStripMenuItem45";
this.toolStripMenuItem45.Size = new System.Drawing.Size(189, 22);
this.toolStripMenuItem45.Size = new System.Drawing.Size(188, 22);
this.toolStripMenuItem45.Text = "Create";
this.toolStripMenuItem45.ToolTipText = "Create a new node.";
this.toolStripMenuItem45.Click += new System.EventHandler(this.CreateNode);
@ -834,7 +840,7 @@
//
this.toolStripMenuItem46.Image = global::CsbBuilder.Properties.Resources.Folder;
this.toolStripMenuItem46.Name = "toolStripMenuItem46";
this.toolStripMenuItem46.Size = new System.Drawing.Size(189, 22);
this.toolStripMenuItem46.Size = new System.Drawing.Size(188, 22);
this.toolStripMenuItem46.Text = "Create Folder";
this.toolStripMenuItem46.ToolTipText = "Create a new folder.";
this.toolStripMenuItem46.Click += new System.EventHandler(this.CreateFolder);
@ -842,26 +848,26 @@
// toolStripSeparator24
//
this.toolStripSeparator24.Name = "toolStripSeparator24";
this.toolStripSeparator24.Size = new System.Drawing.Size(186, 6);
this.toolStripSeparator24.Size = new System.Drawing.Size(185, 6);
//
// toolStripMenuItem48
//
this.toolStripMenuItem48.Image = global::CsbBuilder.Properties.Resources.Template;
this.toolStripMenuItem48.Name = "toolStripMenuItem48";
this.toolStripMenuItem48.Size = new System.Drawing.Size(189, 22);
this.toolStripMenuItem48.Size = new System.Drawing.Size(188, 22);
this.toolStripMenuItem48.Text = "Load AISAC Template";
this.toolStripMenuItem48.Click += new System.EventHandler(this.LoadTemplate);
//
// toolStripSeparator25
//
this.toolStripSeparator25.Name = "toolStripSeparator25";
this.toolStripSeparator25.Size = new System.Drawing.Size(186, 6);
this.toolStripSeparator25.Size = new System.Drawing.Size(185, 6);
//
// toolStripMenuItem47
//
this.toolStripMenuItem47.Image = global::CsbBuilder.Properties.Resources.Paste;
this.toolStripMenuItem47.Name = "toolStripMenuItem47";
this.toolStripMenuItem47.Size = new System.Drawing.Size(189, 22);
this.toolStripMenuItem47.Size = new System.Drawing.Size(188, 22);
this.toolStripMenuItem47.Text = "Paste";
this.toolStripMenuItem47.ToolTipText = "Paste the copied node.";
this.toolStripMenuItem47.Click += new System.EventHandler(this.PasteNodeOnTree);
@ -874,7 +880,7 @@
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
this.tabPage2.Size = new System.Drawing.Size(242, 279);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "Voice Limit Group";
this.tabPage2.Text = "Voice Limit Group Nodes";
this.tabPage2.UseVisualStyleBackColor = true;
//
// voiceLimitGroupTree
@ -1032,13 +1038,13 @@
this.setVoiceLimitGroupReferenceToolStripMenuItem});
this.trackMenu.Name = "cueAndVoiceLimitGroupMenu";
this.trackMenu.RenderMode = System.Windows.Forms.ToolStripRenderMode.System;
this.trackMenu.Size = new System.Drawing.Size(259, 220);
this.trackMenu.Size = new System.Drawing.Size(258, 220);
//
// toolStripMenuItem16
//
this.toolStripMenuItem16.Image = global::CsbBuilder.Properties.Resources.Create;
this.toolStripMenuItem16.Name = "toolStripMenuItem16";
this.toolStripMenuItem16.Size = new System.Drawing.Size(258, 22);
this.toolStripMenuItem16.Size = new System.Drawing.Size(257, 22);
this.toolStripMenuItem16.Text = "Create Block";
this.toolStripMenuItem16.ToolTipText = "Create a new child block node.";
this.toolStripMenuItem16.Click += new System.EventHandler(this.CreateChildTrackNode);
@ -1047,7 +1053,7 @@
//
this.createToolStripMenuItem3.Image = global::CsbBuilder.Properties.Resources.Sound;
this.createToolStripMenuItem3.Name = "createToolStripMenuItem3";
this.createToolStripMenuItem3.Size = new System.Drawing.Size(258, 22);
this.createToolStripMenuItem3.Size = new System.Drawing.Size(257, 22);
this.createToolStripMenuItem3.Text = "Create Sound";
this.createToolStripMenuItem3.ToolTipText = "Create a new child sound node.";
this.createToolStripMenuItem3.Click += new System.EventHandler(this.CreateChildNode);
@ -1055,13 +1061,13 @@
// toolStripSeparator10
//
this.toolStripSeparator10.Name = "toolStripSeparator10";
this.toolStripSeparator10.Size = new System.Drawing.Size(255, 6);
this.toolStripSeparator10.Size = new System.Drawing.Size(254, 6);
//
// toolStripMenuItem20
//
this.toolStripMenuItem20.Image = global::CsbBuilder.Properties.Resources.Copy;
this.toolStripMenuItem20.Name = "toolStripMenuItem20";
this.toolStripMenuItem20.Size = new System.Drawing.Size(258, 22);
this.toolStripMenuItem20.Size = new System.Drawing.Size(257, 22);
this.toolStripMenuItem20.Text = "Copy";
this.toolStripMenuItem20.ToolTipText = "Copy the selected node.";
this.toolStripMenuItem20.Click += new System.EventHandler(this.CopyNode);
@ -1070,7 +1076,7 @@
//
this.toolStripMenuItem21.Image = global::CsbBuilder.Properties.Resources.Paste;
this.toolStripMenuItem21.Name = "toolStripMenuItem21";
this.toolStripMenuItem21.Size = new System.Drawing.Size(258, 22);
this.toolStripMenuItem21.Size = new System.Drawing.Size(257, 22);
this.toolStripMenuItem21.Text = "Paste";
this.toolStripMenuItem21.ToolTipText = "Paste the copied node as child.";
this.toolStripMenuItem21.Click += new System.EventHandler(this.PasteNode);
@ -1079,7 +1085,7 @@
//
this.toolStripMenuItem1.Image = global::CsbBuilder.Properties.Resources.Remove;
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(258, 22);
this.toolStripMenuItem1.Size = new System.Drawing.Size(257, 22);
this.toolStripMenuItem1.Text = "Remove";
this.toolStripMenuItem1.ToolTipText = "Remove the selected node.";
this.toolStripMenuItem1.Click += new System.EventHandler(this.RemoveNode);
@ -1087,13 +1093,13 @@
// toolStripSeparator3
//
this.toolStripSeparator3.Name = "toolStripSeparator3";
this.toolStripSeparator3.Size = new System.Drawing.Size(255, 6);
this.toolStripSeparator3.Size = new System.Drawing.Size(254, 6);
//
// selectAisacReferenceToolStripMenuItem
//
this.selectAisacReferenceToolStripMenuItem.Image = global::CsbBuilder.Properties.Resources.Select;
this.selectAisacReferenceToolStripMenuItem.Name = "selectAisacReferenceToolStripMenuItem";
this.selectAisacReferenceToolStripMenuItem.Size = new System.Drawing.Size(258, 22);
this.selectAisacReferenceToolStripMenuItem.Size = new System.Drawing.Size(257, 22);
this.selectAisacReferenceToolStripMenuItem.Text = "Select AISAC Reference";
this.selectAisacReferenceToolStripMenuItem.ToolTipText = "Select the referenced AISAC node from the AISAC tree.";
this.selectAisacReferenceToolStripMenuItem.Click += new System.EventHandler(this.SelectAisacReference);
@ -1102,7 +1108,7 @@
//
this.selectVoiceLimitGroupReferenceToolStripMenuItem.Image = global::CsbBuilder.Properties.Resources.Select;
this.selectVoiceLimitGroupReferenceToolStripMenuItem.Name = "selectVoiceLimitGroupReferenceToolStripMenuItem";
this.selectVoiceLimitGroupReferenceToolStripMenuItem.Size = new System.Drawing.Size(258, 22);
this.selectVoiceLimitGroupReferenceToolStripMenuItem.Size = new System.Drawing.Size(257, 22);
this.selectVoiceLimitGroupReferenceToolStripMenuItem.Text = "Select Voice Limit Group Reference";
this.selectVoiceLimitGroupReferenceToolStripMenuItem.ToolTipText = "Select the referenced Voice Limit Group node from the Voice Limit Group tree.";
this.selectVoiceLimitGroupReferenceToolStripMenuItem.Click += new System.EventHandler(this.SelectVoiceLimitGroupReference);
@ -1110,13 +1116,13 @@
// toolStripSeparator4
//
this.toolStripSeparator4.Name = "toolStripSeparator4";
this.toolStripSeparator4.Size = new System.Drawing.Size(255, 6);
this.toolStripSeparator4.Size = new System.Drawing.Size(254, 6);
//
// setAisacReferenceToolStripMenuItem
//
this.setAisacReferenceToolStripMenuItem.Image = global::CsbBuilder.Properties.Resources.SetReference;
this.setAisacReferenceToolStripMenuItem.Name = "setAisacReferenceToolStripMenuItem";
this.setAisacReferenceToolStripMenuItem.Size = new System.Drawing.Size(258, 22);
this.setAisacReferenceToolStripMenuItem.Size = new System.Drawing.Size(257, 22);
this.setAisacReferenceToolStripMenuItem.Text = "Set AISAC Reference";
this.setAisacReferenceToolStripMenuItem.ToolTipText = "Set the AISAC reference from the AISAC tree.";
this.setAisacReferenceToolStripMenuItem.Click += new System.EventHandler(this.SetAisacReference);
@ -1125,7 +1131,7 @@
//
this.setVoiceLimitGroupReferenceToolStripMenuItem.Image = global::CsbBuilder.Properties.Resources.SetReference;
this.setVoiceLimitGroupReferenceToolStripMenuItem.Name = "setVoiceLimitGroupReferenceToolStripMenuItem";
this.setVoiceLimitGroupReferenceToolStripMenuItem.Size = new System.Drawing.Size(258, 22);
this.setVoiceLimitGroupReferenceToolStripMenuItem.Size = new System.Drawing.Size(257, 22);
this.setVoiceLimitGroupReferenceToolStripMenuItem.Text = "Set Voice Limit Group Reference";
this.setVoiceLimitGroupReferenceToolStripMenuItem.ToolTipText = "Set the Voice Limit Group reference from the Voice Limit Group tree.";
this.setVoiceLimitGroupReferenceToolStripMenuItem.Click += new System.EventHandler(this.SetVoiceLimitGroupReference);
@ -1233,13 +1239,13 @@
this.toolStripMenuItem8});
this.trackItemMenu.Name = "cueAndVoiceLimitGroupMenu";
this.trackItemMenu.RenderMode = System.Windows.Forms.ToolStripRenderMode.System;
this.trackItemMenu.Size = new System.Drawing.Size(259, 242);
this.trackItemMenu.Size = new System.Drawing.Size(258, 242);
//
// toolStripMenuItem33
//
this.toolStripMenuItem33.Image = global::CsbBuilder.Properties.Resources.Create;
this.toolStripMenuItem33.Name = "toolStripMenuItem33";
this.toolStripMenuItem33.Size = new System.Drawing.Size(258, 22);
this.toolStripMenuItem33.Size = new System.Drawing.Size(257, 22);
this.toolStripMenuItem33.Text = "Create";
this.toolStripMenuItem33.ToolTipText = "Create a new node.";
this.toolStripMenuItem33.Click += new System.EventHandler(this.CreateAndInsertSoundNode);
@ -1247,13 +1253,13 @@
// toolStripSeparator18
//
this.toolStripSeparator18.Name = "toolStripSeparator18";
this.toolStripSeparator18.Size = new System.Drawing.Size(255, 6);
this.toolStripSeparator18.Size = new System.Drawing.Size(254, 6);
//
// toolStripMenuItem23
//
this.toolStripMenuItem23.Image = global::CsbBuilder.Properties.Resources.Copy;
this.toolStripMenuItem23.Name = "toolStripMenuItem23";
this.toolStripMenuItem23.Size = new System.Drawing.Size(258, 22);
this.toolStripMenuItem23.Size = new System.Drawing.Size(257, 22);
this.toolStripMenuItem23.Text = "Copy";
this.toolStripMenuItem23.ToolTipText = "Copy the selected node.";
this.toolStripMenuItem23.Click += new System.EventHandler(this.CopyNode);
@ -1262,7 +1268,7 @@
//
this.toolStripMenuItem30.Image = global::CsbBuilder.Properties.Resources.Paste;
this.toolStripMenuItem30.Name = "toolStripMenuItem30";
this.toolStripMenuItem30.Size = new System.Drawing.Size(258, 22);
this.toolStripMenuItem30.Size = new System.Drawing.Size(257, 22);
this.toolStripMenuItem30.Text = "Paste";
this.toolStripMenuItem30.ToolTipText = "Paste the copied node after the selected node.";
this.toolStripMenuItem30.Click += new System.EventHandler(this.PasteAndInsertNode);
@ -1271,7 +1277,7 @@
//
this.toolStripMenuItem4.Image = global::CsbBuilder.Properties.Resources.Remove;
this.toolStripMenuItem4.Name = "toolStripMenuItem4";
this.toolStripMenuItem4.Size = new System.Drawing.Size(258, 22);
this.toolStripMenuItem4.Size = new System.Drawing.Size(257, 22);
this.toolStripMenuItem4.Text = "Remove";
this.toolStripMenuItem4.ToolTipText = "Remove the selected node.";
this.toolStripMenuItem4.Click += new System.EventHandler(this.RemoveNode);
@ -1279,13 +1285,13 @@
// toolStripSeparator6
//
this.toolStripSeparator6.Name = "toolStripSeparator6";
this.toolStripSeparator6.Size = new System.Drawing.Size(255, 6);
this.toolStripSeparator6.Size = new System.Drawing.Size(254, 6);
//
// selectSoundElementReferenceToolStripMenuItem
//
this.selectSoundElementReferenceToolStripMenuItem.Image = global::CsbBuilder.Properties.Resources.Select;
this.selectSoundElementReferenceToolStripMenuItem.Name = "selectSoundElementReferenceToolStripMenuItem";
this.selectSoundElementReferenceToolStripMenuItem.Size = new System.Drawing.Size(258, 22);
this.selectSoundElementReferenceToolStripMenuItem.Size = new System.Drawing.Size(257, 22);
this.selectSoundElementReferenceToolStripMenuItem.Text = "Select Sound Element Reference";
this.selectSoundElementReferenceToolStripMenuItem.ToolTipText = "Select the referenced Sound Element node from the Sound Element tree.";
this.selectSoundElementReferenceToolStripMenuItem.Click += new System.EventHandler(this.SelectSoundElementReference);
@ -1294,7 +1300,7 @@
//
this.toolStripMenuItem5.Image = global::CsbBuilder.Properties.Resources.Select;
this.toolStripMenuItem5.Name = "toolStripMenuItem5";
this.toolStripMenuItem5.Size = new System.Drawing.Size(258, 22);
this.toolStripMenuItem5.Size = new System.Drawing.Size(257, 22);
this.toolStripMenuItem5.Text = "Select AISAC Reference";
this.toolStripMenuItem5.ToolTipText = "Select the referenced AISAC node from the AISAC tree.";
this.toolStripMenuItem5.Click += new System.EventHandler(this.SelectAisacReference);
@ -1303,7 +1309,7 @@
//
this.toolStripMenuItem6.Image = global::CsbBuilder.Properties.Resources.Select;
this.toolStripMenuItem6.Name = "toolStripMenuItem6";
this.toolStripMenuItem6.Size = new System.Drawing.Size(258, 22);
this.toolStripMenuItem6.Size = new System.Drawing.Size(257, 22);
this.toolStripMenuItem6.Text = "Select Voice Limit Group Reference";
this.toolStripMenuItem6.ToolTipText = "Select the referenced Voice Limit Group node from the Voice Limit Group tree.";
this.toolStripMenuItem6.Click += new System.EventHandler(this.SelectVoiceLimitGroupReference);
@ -1311,13 +1317,13 @@
// toolStripSeparator7
//
this.toolStripSeparator7.Name = "toolStripSeparator7";
this.toolStripSeparator7.Size = new System.Drawing.Size(255, 6);
this.toolStripSeparator7.Size = new System.Drawing.Size(254, 6);
//
// setSoundElementReferenceToolStripMenuItem
//
this.setSoundElementReferenceToolStripMenuItem.Image = global::CsbBuilder.Properties.Resources.SetReference;
this.setSoundElementReferenceToolStripMenuItem.Name = "setSoundElementReferenceToolStripMenuItem";
this.setSoundElementReferenceToolStripMenuItem.Size = new System.Drawing.Size(258, 22);
this.setSoundElementReferenceToolStripMenuItem.Size = new System.Drawing.Size(257, 22);
this.setSoundElementReferenceToolStripMenuItem.Text = "Set Sound Element Reference";
this.setSoundElementReferenceToolStripMenuItem.ToolTipText = "Set the Sound Element reference from the Sound Element tree.";
this.setSoundElementReferenceToolStripMenuItem.Click += new System.EventHandler(this.SetSoundElementReference);
@ -1326,7 +1332,7 @@
//
this.toolStripMenuItem7.Image = global::CsbBuilder.Properties.Resources.SetReference;
this.toolStripMenuItem7.Name = "toolStripMenuItem7";
this.toolStripMenuItem7.Size = new System.Drawing.Size(258, 22);
this.toolStripMenuItem7.Size = new System.Drawing.Size(257, 22);
this.toolStripMenuItem7.Text = "Set AISAC Reference";
this.toolStripMenuItem7.ToolTipText = "Set the AISAC reference from the AISAC tree.";
this.toolStripMenuItem7.Click += new System.EventHandler(this.SetAisacReference);
@ -1335,7 +1341,7 @@
//
this.toolStripMenuItem8.Image = global::CsbBuilder.Properties.Resources.SetReference;
this.toolStripMenuItem8.Name = "toolStripMenuItem8";
this.toolStripMenuItem8.Size = new System.Drawing.Size(258, 22);
this.toolStripMenuItem8.Size = new System.Drawing.Size(257, 22);
this.toolStripMenuItem8.Text = "Set Voice Limit Group Reference";
this.toolStripMenuItem8.ToolTipText = "Set the Voice Limit Group reference from the Voice Limit Group tree.";
this.toolStripMenuItem8.Click += new System.EventHandler(this.SetVoiceLimitGroupReference);
@ -1533,13 +1539,13 @@
this.toolStripMenuItem38});
this.aisacNodeMenu.Name = "cueAndVoiceLimitGroupMenu";
this.aisacNodeMenu.RenderMode = System.Windows.Forms.ToolStripRenderMode.System;
this.aisacNodeMenu.Size = new System.Drawing.Size(154, 148);
this.aisacNodeMenu.Size = new System.Drawing.Size(153, 148);
//
// toolStripMenuItem35
//
this.toolStripMenuItem35.Image = global::CsbBuilder.Properties.Resources.Create;
this.toolStripMenuItem35.Name = "toolStripMenuItem35";
this.toolStripMenuItem35.Size = new System.Drawing.Size(153, 22);
this.toolStripMenuItem35.Size = new System.Drawing.Size(152, 22);
this.toolStripMenuItem35.Text = "Create";
this.toolStripMenuItem35.ToolTipText = "Create a new node after the selected node.";
this.toolStripMenuItem35.Click += new System.EventHandler(this.CreateAndInsertNode);
@ -1547,13 +1553,13 @@
// toolStripSeparator20
//
this.toolStripSeparator20.Name = "toolStripSeparator20";
this.toolStripSeparator20.Size = new System.Drawing.Size(150, 6);
this.toolStripSeparator20.Size = new System.Drawing.Size(149, 6);
//
// loadTemplateToolStripMenuItem
//
this.loadTemplateToolStripMenuItem.Image = global::CsbBuilder.Properties.Resources.Template;
this.loadTemplateToolStripMenuItem.Name = "loadTemplateToolStripMenuItem";
this.loadTemplateToolStripMenuItem.Size = new System.Drawing.Size(153, 22);
this.loadTemplateToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.loadTemplateToolStripMenuItem.Text = "Load Template";
this.loadTemplateToolStripMenuItem.Click += new System.EventHandler(this.LoadTemplate);
//
@ -1561,20 +1567,20 @@
//
this.saveTemplateToolStripMenuItem.Image = global::CsbBuilder.Properties.Resources.Template;
this.saveTemplateToolStripMenuItem.Name = "saveTemplateToolStripMenuItem";
this.saveTemplateToolStripMenuItem.Size = new System.Drawing.Size(153, 22);
this.saveTemplateToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.saveTemplateToolStripMenuItem.Text = "Save Template";
this.saveTemplateToolStripMenuItem.Click += new System.EventHandler(this.SaveTemplate);
//
// toolStripSeparator21
//
this.toolStripSeparator21.Name = "toolStripSeparator21";
this.toolStripSeparator21.Size = new System.Drawing.Size(150, 6);
this.toolStripSeparator21.Size = new System.Drawing.Size(149, 6);
//
// toolStripMenuItem36
//
this.toolStripMenuItem36.Image = global::CsbBuilder.Properties.Resources.Copy;
this.toolStripMenuItem36.Name = "toolStripMenuItem36";
this.toolStripMenuItem36.Size = new System.Drawing.Size(153, 22);
this.toolStripMenuItem36.Size = new System.Drawing.Size(152, 22);
this.toolStripMenuItem36.Text = "Copy";
this.toolStripMenuItem36.ToolTipText = "Copy the selected node.";
this.toolStripMenuItem36.Click += new System.EventHandler(this.CopyNode);
@ -1583,7 +1589,7 @@
//
this.toolStripMenuItem37.Image = global::CsbBuilder.Properties.Resources.Paste;
this.toolStripMenuItem37.Name = "toolStripMenuItem37";
this.toolStripMenuItem37.Size = new System.Drawing.Size(153, 22);
this.toolStripMenuItem37.Size = new System.Drawing.Size(152, 22);
this.toolStripMenuItem37.Text = "Paste";
this.toolStripMenuItem37.ToolTipText = "Paste the copied node after the selected node.";
this.toolStripMenuItem37.Click += new System.EventHandler(this.PasteAndInsertNode);
@ -1592,7 +1598,7 @@
//
this.toolStripMenuItem38.Image = global::CsbBuilder.Properties.Resources.Remove;
this.toolStripMenuItem38.Name = "toolStripMenuItem38";
this.toolStripMenuItem38.Size = new System.Drawing.Size(153, 22);
this.toolStripMenuItem38.Size = new System.Drawing.Size(152, 22);
this.toolStripMenuItem38.Text = "Remove";
this.toolStripMenuItem38.ToolTipText = "Remove the selected node.";
this.toolStripMenuItem38.Click += new System.EventHandler(this.RemoveNode);
@ -1610,13 +1616,13 @@
this.toolStripMenuItem43});
this.aisacFolderMenu.Name = "synthSoundElementAndAisacMenu";
this.aisacFolderMenu.RenderMode = System.Windows.Forms.ToolStripRenderMode.System;
this.aisacFolderMenu.Size = new System.Drawing.Size(190, 148);
this.aisacFolderMenu.Size = new System.Drawing.Size(189, 148);
//
// toolStripMenuItem39
//
this.toolStripMenuItem39.Image = global::CsbBuilder.Properties.Resources.Create;
this.toolStripMenuItem39.Name = "toolStripMenuItem39";
this.toolStripMenuItem39.Size = new System.Drawing.Size(189, 22);
this.toolStripMenuItem39.Size = new System.Drawing.Size(188, 22);
this.toolStripMenuItem39.Text = "Create";
this.toolStripMenuItem39.ToolTipText = "Create a new child node.";
this.toolStripMenuItem39.Click += new System.EventHandler(this.CreateChildNode);
@ -1625,7 +1631,7 @@
//
this.toolStripMenuItem40.Image = global::CsbBuilder.Properties.Resources.Folder;
this.toolStripMenuItem40.Name = "toolStripMenuItem40";
this.toolStripMenuItem40.Size = new System.Drawing.Size(189, 22);
this.toolStripMenuItem40.Size = new System.Drawing.Size(188, 22);
this.toolStripMenuItem40.Text = "Create Folder";
this.toolStripMenuItem40.ToolTipText = "Create a new child folder.";
this.toolStripMenuItem40.Click += new System.EventHandler(this.CreateChildFolder);
@ -1633,26 +1639,26 @@
// toolStripSeparator22
//
this.toolStripSeparator22.Name = "toolStripSeparator22";
this.toolStripSeparator22.Size = new System.Drawing.Size(186, 6);
this.toolStripSeparator22.Size = new System.Drawing.Size(185, 6);
//
// toolStripMenuItem44
//
this.toolStripMenuItem44.Image = global::CsbBuilder.Properties.Resources.Template;
this.toolStripMenuItem44.Name = "toolStripMenuItem44";
this.toolStripMenuItem44.Size = new System.Drawing.Size(189, 22);
this.toolStripMenuItem44.Size = new System.Drawing.Size(188, 22);
this.toolStripMenuItem44.Text = "Load AISAC Template";
this.toolStripMenuItem44.Click += new System.EventHandler(this.LoadTemplate);
//
// toolStripSeparator23
//
this.toolStripSeparator23.Name = "toolStripSeparator23";
this.toolStripSeparator23.Size = new System.Drawing.Size(186, 6);
this.toolStripSeparator23.Size = new System.Drawing.Size(185, 6);
//
// toolStripMenuItem41
//
this.toolStripMenuItem41.Image = global::CsbBuilder.Properties.Resources.Copy;
this.toolStripMenuItem41.Name = "toolStripMenuItem41";
this.toolStripMenuItem41.Size = new System.Drawing.Size(189, 22);
this.toolStripMenuItem41.Size = new System.Drawing.Size(188, 22);
this.toolStripMenuItem41.Text = "Copy";
this.toolStripMenuItem41.ToolTipText = "Copy the selected node.";
this.toolStripMenuItem41.Click += new System.EventHandler(this.CopyNode);
@ -1661,7 +1667,7 @@
//
this.toolStripMenuItem42.Image = global::CsbBuilder.Properties.Resources.Paste;
this.toolStripMenuItem42.Name = "toolStripMenuItem42";
this.toolStripMenuItem42.Size = new System.Drawing.Size(189, 22);
this.toolStripMenuItem42.Size = new System.Drawing.Size(188, 22);
this.toolStripMenuItem42.Text = "Paste";
this.toolStripMenuItem42.ToolTipText = "Paste the copied node as child.";
this.toolStripMenuItem42.Click += new System.EventHandler(this.PasteNode);
@ -1670,11 +1676,16 @@
//
this.toolStripMenuItem43.Image = global::CsbBuilder.Properties.Resources.Remove;
this.toolStripMenuItem43.Name = "toolStripMenuItem43";
this.toolStripMenuItem43.Size = new System.Drawing.Size(189, 22);
this.toolStripMenuItem43.Size = new System.Drawing.Size(188, 22);
this.toolStripMenuItem43.Text = "Remove";
this.toolStripMenuItem43.ToolTipText = "Remove the selected node.";
this.toolStripMenuItem43.Click += new System.EventHandler(this.RemoveNode);
//
// toolStripSeparator27
//
this.toolStripSeparator27.Name = "toolStripSeparator27";
this.toolStripSeparator27.Size = new System.Drawing.Size(203, 6);
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -1879,11 +1890,12 @@
private System.Windows.Forms.ToolStripSeparator toolStripSeparator26;
private System.Windows.Forms.ToolStripMenuItem settingsButton;
private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem49;
private System.Windows.Forms.ToolStripMenuItem aDXToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem aAXToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem extractAAXToFolderToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem packFolderToAAXToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem convertADXsToWAVToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem convertAndSplitLoopingToWAVToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator27;
}
}

View File

@ -13,9 +13,10 @@ using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.InteropServices;
using CsbBuilder.Builder;
using CsbBuilder.BuilderNode;
using CsbBuilder.BuilderNodes;
using CsbBuilder.Audio;
using CsbBuilder.Importer;
using CsbBuilder.Project;
@ -23,7 +24,7 @@ using CsbBuilder.Serialization;
using CsbBuilder.Properties;
using SonicAudioLib.IO;
using SonicAudioLib.Archive;
using SonicAudioLib.Archives;
using NAudio.Wave;
@ -42,6 +43,25 @@ namespace CsbBuilder
private List<IWavePlayer> sounds = new List<IWavePlayer>();
private string filters = null;
private string Filters
{
get
{
if (string.IsNullOrEmpty(filters))
{
var culture = Thread.CurrentThread.CurrentUICulture;
var formats = VGMStreamNative.GetFormats().OrderBy(format => format);
var formatFilter = formats.Select(format => $"{format.ToUpper(culture)} Files|*.{format}");
filters = $"All Formats|{string.Join(";", formats.Select(format => $"*.{format}"))}|{string.Join("|", formatFilter)}";
}
return filters;
}
}
public MainForm()
{
InitializeComponent();
@ -1388,10 +1408,12 @@ namespace CsbBuilder
public void ReadAdx(string path, out uint sampleRate, out byte channelCount, out uint sampleCount)
{
AdxHeader header = AdxFileReader.LoadHeader(path);
sampleRate = header.SampleRate;
channelCount = header.ChannelCount;
sampleCount = header.SampleCount;
using (var reader = new VGMStreamReader(path))
{
sampleRate = (uint)reader.WaveFormat.SampleRate;
channelCount = (byte)reader.WaveFormat.Channels;
sampleCount = (uint)(reader.Length / 2);
}
}
private void CreateChildSoundNode(object sender, EventArgs e)
@ -1424,36 +1446,100 @@ namespace CsbBuilder
return (int)(miliseconds * sampleRate / 1000.0) * channelCount * bitsPerSample / 8;
}
private void AddSoundElementSound(BuilderSoundElementNode soundElementNode, double volume, double pitch, int sampleCount, int delayTime)
private int GetMilisecondsFromByteCount(int byteCount, int sampleRate, int channelCount, int bitsPerSample)
{
return (int)((8000.0 * byteCount) / (sampleRate * channelCount * bitsPerSample));
}
private WaveStream GetWaveStream(string path)
{
if (path.EndsWith(".wav", StringComparison.OrdinalIgnoreCase))
{
return new WaveFileReader(project.GetFullAudioPath(path));
}
return new VGMStreamReader(project.GetFullAudioPath(path));
}
private void AddSoundElementSound(BuilderSoundElementNode soundElementNode, double volume, double pitch, int delayTime)
{
WaveStream waveStream = null;
if (!string.IsNullOrEmpty(soundElementNode.Intro) && string.IsNullOrEmpty(soundElementNode.Loop))
{
waveStream = new AdxFileReader(project.GetFullAudioPath(soundElementNode.Intro)) { Volume = volume, Pitch = pitch, DelayTime = delayTime };
var reader = new VGMStreamReader(project.GetFullAudioPath(soundElementNode.Intro));
reader.DisableLoop();
waveStream = new ExtendedWaveStream(reader)
{
DelayTime = delayTime,
Volume = volume,
Pitch = pitch,
};
}
else if (string.IsNullOrEmpty(soundElementNode.Intro) && !string.IsNullOrEmpty(soundElementNode.Loop))
{
waveStream = new ExtendedAdxFileReader(project.GetFullAudioPath(soundElementNode.Loop)) { Volume = volume, Pitch = pitch, DelayTime = delayTime };
var reader = new VGMStreamReader(project.GetFullAudioPath(soundElementNode.Loop));
reader.ForceLoop();
waveStream = new ExtendedWaveStream(reader)
{
DelayTime = delayTime,
Volume = volume,
Pitch = pitch,
ForceLoop = true,
};
}
else if (!string.IsNullOrEmpty(soundElementNode.Intro) && !string.IsNullOrEmpty(soundElementNode.Loop))
{
waveStream = new ExtendedAdxFileReader(project.GetFullAudioPath(soundElementNode.Intro), project.GetFullAudioPath(soundElementNode.Loop)) { Volume = volume, Pitch = pitch, DelayTime = delayTime };
var intro = new VGMStreamReader(project.GetFullAudioPath(soundElementNode.Intro));
intro.DisableLoop();
var loop = new VGMStreamReader(project.GetFullAudioPath(soundElementNode.Loop));
loop.ForceLoop();
waveStream = new ExtendedWaveStream(intro, loop)
{
DelayTime = delayTime,
Volume = volume,
Pitch = pitch,
ForceLoop = true,
};
}
if (waveStream != null)
{
DirectSoundOut waveOut = new DirectSoundOut();
waveOut.Init(waveStream);
sounds.Add(waveOut);
IWavePlayer wavePlayer = null;
switch (Settings.WavePlayer)
{
case Settings.NAudioWavePlayer.WaveOut:
wavePlayer = new WaveOut();
break;
case Settings.NAudioWavePlayer.WasapiOut:
wavePlayer = new WasapiOut();
break;
case Settings.NAudioWavePlayer.DirectSoundOut:
wavePlayer = new DirectSoundOut();
break;
case Settings.NAudioWavePlayer.AsioOut:
wavePlayer = new AsioOut();
break;
}
wavePlayer.Init(waveStream);
sounds.Add(wavePlayer);
}
}
private void AddSoundElementSound(TreeNode soundElementTreeNode, double volume, double pitch, int sampleCount, int delayTime)
private void AddSoundElementSound(TreeNode soundElementTreeNode, double volume, double pitch, int delayTime)
{
AddSoundElementSound((BuilderSoundElementNode)soundElementTreeNode.Tag, volume, pitch, sampleCount, delayTime);
AddSoundElementSound((BuilderSoundElementNode)soundElementTreeNode.Tag, volume, pitch, delayTime);
}
private double GetSecondsFromSampleCount(int sampleCount, int sampleRate)
@ -1504,7 +1590,7 @@ namespace CsbBuilder
synthTreeNode = synthTreeNode.Parent;
}
return pitch / 100.0;
return pitch / 1500.0;
}
private double GetAbsoluteVolume(TreeNode synthTreeNode)
@ -1515,7 +1601,7 @@ namespace CsbBuilder
{
if (synthTreeNode.Tag is BuilderSynthNode synthNode)
{
volume = (volume * synthNode.Volume) / 1000;
volume = (volume * synthNode.Volume) / 1000.0;
}
synthTreeNode = synthTreeNode.Parent;
@ -1541,7 +1627,7 @@ namespace CsbBuilder
return delayTime;
}
private void AddSynthSound(TreeNode synthTreeNode, int sampleCount)
private void AddSynthSound(TreeNode synthTreeNode)
{
BuilderSynthNode synthNode = (BuilderSynthNode)synthTreeNode.Tag;
@ -1554,12 +1640,12 @@ namespace CsbBuilder
if (childSynthNode.Type == BuilderSynthType.Single && !string.IsNullOrEmpty(childSynthNode.SoundElementReference) && childSynthNode.PlayThisTurn)
{
AddSoundElementSound(soundElementTree.FindNodeByFullPath(childSynthNode.SoundElementReference), GetAbsoluteVolume(childNode), GetAbsolutePitch(childNode), sampleCount, GetAbsoluteDelayTime(childNode));
AddSoundElementSound(soundElementTree.FindNodeByFullPath(childSynthNode.SoundElementReference), GetAbsoluteVolume(childNode), GetAbsolutePitch(childNode), GetAbsoluteDelayTime(childNode));
}
else if (childSynthNode.Type == BuilderSynthType.WithChildren)
{
AddSynthSound(childNode, sampleCount);
AddSynthSound(childNode);
}
}
@ -1570,12 +1656,12 @@ namespace CsbBuilder
if (childSynthNode.Type == BuilderSynthType.Single && !string.IsNullOrEmpty(childSynthNode.SoundElementReference) && childSynthNode.PlayThisTurn)
{
AddSoundElementSound(soundElementTree.FindNodeByFullPath(childSynthNode.SoundElementReference), GetAbsoluteVolume(childNode), GetAbsolutePitch(childNode), sampleCount, GetAbsoluteDelayTime(childNode));
AddSoundElementSound(soundElementTree.FindNodeByFullPath(childSynthNode.SoundElementReference), GetAbsoluteVolume(childNode), GetAbsolutePitch(childNode), GetAbsoluteDelayTime(childNode));
}
else if (childSynthNode.Type == BuilderSynthType.WithChildren)
{
AddSynthSound(childNode, sampleCount);
AddSynthSound(childNode);
}
}
@ -1587,12 +1673,12 @@ namespace CsbBuilder
if (childSynthNode.Type == BuilderSynthType.Single && !string.IsNullOrEmpty(childSynthNode.SoundElementReference) && childSynthNode.PlayThisTurn)
{
AddSoundElementSound(soundElementTree.FindNodeByFullPath(childSynthNode.SoundElementReference), GetAbsoluteVolume(childNode), GetAbsolutePitch(childNode), sampleCount, GetAbsoluteDelayTime(childNode));
AddSoundElementSound(soundElementTree.FindNodeByFullPath(childSynthNode.SoundElementReference), GetAbsoluteVolume(childNode), GetAbsolutePitch(childNode), GetAbsoluteDelayTime(childNode));
}
else if (childSynthNode.Type == BuilderSynthType.WithChildren)
{
AddSynthSound(childNode, sampleCount);
AddSynthSound(childNode);
}
}
}
@ -1602,7 +1688,7 @@ namespace CsbBuilder
{
if (!string.IsNullOrEmpty(synthNode.SoundElementReference) && synthNode.PlayThisTurn)
{
AddSoundElementSound(soundElementTree.FindNodeByFullPath(synthNode.SoundElementReference), synthNode.Volume / 1000.0, synthNode.Pitch / 1000.0, GetTheBiggestSampleCount(synthTreeNode), (int)synthNode.DelayTime);
AddSoundElementSound(soundElementTree.FindNodeByFullPath(synthNode.SoundElementReference), synthNode.Volume / 1000.0, synthNode.Pitch / 1500.0, (int)synthNode.DelayTime);
}
}
}
@ -1624,18 +1710,18 @@ namespace CsbBuilder
if (!string.IsNullOrEmpty(cueNode.SynthReference))
{
TreeNode synthNode = synthTree.FindNodeByFullPath(cueNode.SynthReference);
AddSynthSound(synthNode, GetTheBiggestSampleCount(synthNode));
AddSynthSound(synthNode);
}
}
else if (soundElementTree.Focused && soundElementTree.SelectedNode != null && soundElementTree.SelectedNode.Tag is BuilderSoundElementNode)
{
AddSoundElementSound(soundElementTree.SelectedNode, 1, 0, -1, 0);
AddSoundElementSound(soundElementTree.SelectedNode, 1, 0, 0);
}
else if (synthTree.Focused && synthTree.SelectedNode != null && synthTree.SelectedNode.Tag is BuilderSynthNode)
{
AddSynthSound(synthTree.SelectedNode, GetTheBiggestSampleCount(synthTree.SelectedNode));
AddSynthSound(synthTree.SelectedNode);
}
sounds.ForEach(sound => sound.Play());
@ -2140,10 +2226,9 @@ namespace CsbBuilder
{
using (OpenFileDialog openFileDialog = new OpenFileDialog
{
Title = "Convert ADX Files",
FileName = "Select ADX files you want to convert and press Open",
Filter = "ADX Files|*.adx",
DefaultExt = "adx",
Title = "Convert Audio Files",
FileName = "Select audio files you want to convert and press Open",
Filter = Filters,
Multiselect = true,
})
{
@ -2157,23 +2242,88 @@ namespace CsbBuilder
{
if (saveFileDialog.ShowDialog(this) == DialogResult.OK)
{
foreach (string fileName in openFileDialog.FileNames)
var failedFiles = new List<string>();
Action<string> action = fileName =>
{
using (AdxFileReader reader = new AdxFileReader(fileName))
VGMStreamReader reader = null;
try
{
reader = new VGMStreamReader(fileName);
}
catch (NullReferenceException)
{
failedFiles.Add(Path.GetFileName(fileName));
return;
}
using (reader)
using (WaveFileWriter writer = new WaveFileWriter(
Path.Combine(
Path.GetDirectoryName(saveFileDialog.FileName),
Path.GetFileNameWithoutExtension(fileName) + ".wav"),
reader.WaveFormat))
{
int num;
if (reader.LoopFlag)
{
// Intro
DataStream.CopyPartTo(reader, writer, reader.LoopStartPosition, Settings.BufferSize);
// Loops
DataStream.CopyPartTo(reader, writer, (reader.LoopEndPosition - reader.LoopStartPosition) * Settings.LoopCount, Settings.BufferSize);
// Last loop, fade away
byte[] buffer = new byte[Settings.BufferSize];
while ((num = reader.Read(buffer, 0, Settings.BufferSize)) != 0)
// Fade will be 10 seconds
double sampleCount = (int)(reader.WaveFormat.AverageBytesPerSecond * Settings.FadeTime);
int i;
int j = (int)(sampleCount + reader.WaveFormat.AverageBytesPerSecond * Settings.FadeDelay);
while (j > 0)
{
writer.Write(buffer, 0, num);
reader.Read(buffer, 0, buffer.Length);
for (i = 0; i < buffer.Length && j > 0; i += 2, j--)
{
double volume = j / sampleCount;
// Clamp to 0.0-1.0 range so delay actually works
volume = volume > 1.0 ? 1.0 : volume;
PostSampleEditor.ApplyVolume(buffer, i, volume);
}
writer.Write(buffer, 0, i);
}
}
else
{
DataStream.CopyTo(reader, writer, Settings.BufferSize);
}
}
};
if (Settings.EnableThreading)
{
Parallel.ForEach(openFileDialog.FileNames, new ParallelOptions { MaxDegreeOfParallelism = Settings.MaxThreads }, action);
}
else
{
foreach (var item in openFileDialog.FileNames)
{
action(item);
}
}
if (failedFiles.Count != 0)
{
MessageBox.Show($"Following files could not be converted:\n{string.Join("\n", failedFiles)}", "CSB Builder", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
@ -2202,6 +2352,11 @@ namespace CsbBuilder
{
if (saveFileDialog.ShowDialog(this) == DialogResult.OK)
{
DataExtractor extractor = new DataExtractor();
extractor.EnableThreading = Settings.EnableThreading;
extractor.MaxThreads = Settings.MaxThreads;
extractor.BufferSize = Settings.BufferSize;
foreach (string fileName in openFileDialog.FileNames)
{
CriAaxArchive aaxArchive = new CriAaxArchive();
@ -2210,15 +2365,17 @@ namespace CsbBuilder
foreach (CriAaxEntry entry in aaxArchive)
{
using (Stream source = File.OpenRead(fileName))
using (Stream destination = File.Create(
Path.Combine(
Path.GetDirectoryName(saveFileDialog.FileName),
$"{Path.GetFileNameWithoutExtension(fileName)}_{entry.Flag}.adx")))
{
EndianStream.CopyPartTo(source, destination, entry.Position, entry.Length, Settings.BufferSize);
extractor.Add(fileName, Path.Combine(
Path.GetDirectoryName(saveFileDialog.FileName),
$"{Path.GetFileNameWithoutExtension(fileName)}_{entry.Flag}{aaxArchive.GetModeExtension()}"),
entry.Position,
entry.Length);
}
}
}
extractor.Run();
}
}
}
@ -2229,10 +2386,9 @@ namespace CsbBuilder
{
using (OpenFileDialog openFileDialog = new OpenFileDialog
{
Title = "Extract ADX Files",
Title = "Pack ADX Files",
FileName = "Select ADX files you want to pack and press Open",
Filter = "ADX Files|*.adx",
DefaultExt = "adx",
Filter = "All Files|*.adx;*.wav|ADX Files|*.adx|WAV Files|*.wav",
Multiselect = true,
})
{
@ -2246,6 +2402,13 @@ namespace CsbBuilder
MessageBox.Show("You can select maximum 2 ADX files.", "CSB Builder", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else if (openFileDialog.FileNames.Length == 2 &&
!Path.GetExtension(openFileDialog.FileNames[0]).Equals(
Path.GetExtension(openFileDialog.FileNames[1]), StringComparison.OrdinalIgnoreCase))
{
MessageBox.Show("You can select only the same type of files.", "CSB Builder", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
files = openFileDialog.FileNames;
@ -2258,7 +2421,7 @@ namespace CsbBuilder
using (SaveFileDialog saveFileDialog = new SaveFileDialog
{
Title = "Output File",
FileName = "*.aax",
FileName = $"{Path.GetFileNameWithoutExtension(files[0])}.aax",
Filter = "AAX Files|*.aax",
DefaultExt = "aax",
})
@ -2266,6 +2429,7 @@ namespace CsbBuilder
if (saveFileDialog.ShowDialog(this) == DialogResult.OK)
{
CriAaxArchive archive = new CriAaxArchive();
archive.SetModeFromExtension(files[0]);
for (int i = 0; i < files.Length; i++)
{
@ -2278,5 +2442,96 @@ namespace CsbBuilder
}
}
}
private void convertAndSplitLoopingToWAVToolStripMenuItem_Click(object sender, EventArgs e)
{
using (OpenFileDialog openFileDialog = new OpenFileDialog
{
Title = "Convert And Split Audio Files",
FileName = "Select audio files you want to convert and press Open",
Filter = Filters,
Multiselect = true,
})
{
if (openFileDialog.ShowDialog(this) == DialogResult.OK)
{
using (SaveFileDialog saveFileDialog = new SaveFileDialog
{
Title = "Output Directory",
FileName = "Enter into a directory and press Save",
})
{
if (saveFileDialog.ShowDialog(this) == DialogResult.OK)
{
var failedFiles = new List<string>();
Action<string> action = fileName =>
{
VGMStreamReader reader = null;
try
{
reader = new VGMStreamReader(fileName);
}
catch (NullReferenceException)
{
failedFiles.Add(Path.GetFileName(fileName));
return;
}
using (reader)
{
string outputFileName =
Path.Combine(
Path.GetDirectoryName(saveFileDialog.FileName),
Path.GetFileNameWithoutExtension(fileName));
WaveFileWriter introWriter;
WaveFileWriter loopWriter;
if (reader.LoopFlag)
{
using (introWriter = new WaveFileWriter(outputFileName + "_Intro.wav", reader.WaveFormat))
using (loopWriter = new WaveFileWriter(outputFileName + "_Loop.wav", reader.WaveFormat))
{
DataStream.CopyPartTo(reader, introWriter, 0, reader.LoopStartPosition, Settings.BufferSize);
DataStream.CopyPartTo(reader, loopWriter, reader.LoopStartPosition, reader.LoopEndPosition - reader.LoopStartPosition, Settings.BufferSize);
}
}
// No loop enabled, directly extract the intro
else
{
using (introWriter = new WaveFileWriter(outputFileName + "_Intro.wav", reader.WaveFormat))
{
DataStream.CopyTo(reader, introWriter, Settings.BufferSize);
}
}
}
};
if (Settings.EnableThreading)
{
Parallel.ForEach(openFileDialog.FileNames, new ParallelOptions { MaxDegreeOfParallelism = Settings.MaxThreads }, action);
}
else
{
foreach (var item in openFileDialog.FileNames)
{
action(item);
}
}
if (failedFiles.Count != 0)
{
MessageBox.Show($"Following files could not be converted:\n{string.Join("\n", failedFiles)}", "CSB Builder", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
}
}
}
}

View File

@ -172,6 +172,6 @@
<value>17, 95</value>
</metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>172</value>
<value>74</value>
</metadata>
</root>

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.IO;
using CsbBuilder.BuilderNode;
using CsbBuilder.BuilderNodes;
using System.Xml.Serialization;
using System.Windows.Forms;

View File

@ -50,9 +50,8 @@ namespace CsbBuilder
{
using (OpenFileDialog openAdx = new OpenFileDialog
{
Title = "Please select your ADX file.",
Filter = "ADX Files|*.adx",
DefaultExt = "adx",
Title = "Select Your Audio File",
Filter = "All Files|*.adx;*.wav|ADX Files|*.adx|WAV Files|*.wav",
})
{
if (openAdx.ShowDialog() == DialogResult.OK)
@ -66,9 +65,8 @@ namespace CsbBuilder
{
using (OpenFileDialog openAdx = new OpenFileDialog
{
Title = "Please select your ADX file.",
Filter = "ADX Files|*.adx",
DefaultExt = "adx",
Title = "Select Your Audio File",
Filter = "All Files|*.adx;*.wav|ADX Files|*.adx|WAV Files|*.wav",
})
{
if (openAdx.ShowDialog() == DialogResult.OK)
@ -88,9 +86,12 @@ namespace CsbBuilder
e.Cancel = true;
}
if ((!string.IsNullOrEmpty(Intro) && !Intro.EndsWith(".adx")) || (!string.IsNullOrEmpty(Loop) && !Loop.EndsWith(".adx")))
string introExtension = Path.GetExtension(Intro);
string loopExtension = Path.GetExtension(Loop);
if (!string.IsNullOrEmpty(Intro) && !string.IsNullOrEmpty(Loop) && !introExtension.Equals(loopExtension, StringComparison.OrdinalIgnoreCase))
{
MessageBox.Show("Please use an .ADX file.", "CSB Builder", MessageBoxButtons.OK, MessageBoxIcon.Warning);
MessageBox.Show("Please use the same types of audio files for Intro and Loop.", "CSB Builder", MessageBoxButtons.OK, MessageBoxIcon.Warning);
e.Cancel = true;
}
}

View File

@ -8,7 +8,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using CsbBuilder.BuilderNode;
using CsbBuilder.BuilderNodes;
namespace CsbBuilder
{

View File

@ -19,6 +19,14 @@ namespace CsbBuilder.Project
DirectoryOfCsb,
}
public enum NAudioWavePlayer
{
WaveOut,
WasapiOut,
DirectSoundOut,
AsioOut,
}
[DisplayName("Name node after its parent"), Category("General")]
[Description("Names a node after its parent if it exists, or the node tree name.")]
public bool NameNodeAfterParent { get; set; }
@ -48,7 +56,23 @@ namespace CsbBuilder.Project
[DisplayName("Maximum amount of cores"), Category("Stream")]
[Description("Maximum amount of threads used to extract data from CSB/CPK files during importing.")]
public int MaxCores { get; set; }
public int MaxThreads { get; set; }
[DisplayName("Wave Player"), Category("Sound")]
[Description("Sound device for audio playback. If not supported, application is going to crash.")]
public NAudioWavePlayer WavePlayer { get; set; }
[DisplayName("Loop Count"), Category("Audio Converter")]
[Description("Count of loop times for audio converter if input audio has loop information.")]
public int LoopCount { get; set; }
[DisplayName("Fade Out Time"), Category("Audio Converter")]
[Description("Fade out time in seconds after total 'Loop Count' loops for audio with loop information.")]
public double FadeTime { get; set; }
[DisplayName("Fade Out Delay Time"), Category("Audio Converter")]
[Description("Delay time in seconds before audio starts to fade out.")]
public double FadeDelay { get; set; }
public static Settings Load()
{
@ -92,6 +116,7 @@ namespace CsbBuilder.Project
public Settings()
{
WavePlayer = NAudioWavePlayer.WaveOut;
NameNodeAfterParent = true;
BufferSize = 4096;
ProjectsDirectory = "Projects";
@ -99,7 +124,10 @@ namespace CsbBuilder.Project
ImportedCsbProjectDirectory = ProjectDirectory.DirectoryOfCsb;
RenameToSoundElement = true;
EnableThreading = true;
MaxCores = 4;
MaxThreads = 4;
LoopCount = 2;
FadeTime = 10;
FadeDelay = 0;
}
}
}

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
@ -6,7 +6,7 @@
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
<userSettings>
<CsbEditor.Properties.Settings>

View File

@ -9,9 +9,10 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CsbEditor</RootNamespace>
<AssemblyName>CsbEditor</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -22,6 +23,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>

View File

@ -9,7 +9,7 @@ using CsbEditor.Properties;
using SonicAudioLib;
using SonicAudioLib.CriMw;
using SonicAudioLib.IO;
using SonicAudioLib.Archive;
using SonicAudioLib.Archives;
using System.Globalization;
@ -19,6 +19,8 @@ namespace CsbEditor
{
static void Main(string[] args)
{
Settings.Default.Save();
if (args.Length < 1)
{
Console.WriteLine(Resources.Description);
@ -53,7 +55,7 @@ namespace CsbEditor
if (reader.GetString("name") == "SOUND_ELEMENT")
{
long tablePosition = reader.GetPosition("utf");
using (CriTableReader sdlReader = CriTableReader.Create(reader.GetSubstream("utf")))
using (CriTableReader sdlReader = CriTableReader.Create(reader.GetSubStream("utf")))
{
while (sdlReader.Read())
{
@ -105,7 +107,7 @@ namespace CsbEditor
else
{
long aaxPosition = sdlReader.GetPosition("data");
using (Stream aaxSource = sdlReader.GetSubstream("data"))
using (Stream aaxSource = sdlReader.GetSubStream("data"))
{
aaxArchive.Read(aaxSource);
@ -243,8 +245,8 @@ namespace CsbEditor
using (Stream source = fileInfo.OpenRead())
{
source.Seek(7, SeekOrigin.Begin);
numberChannels = EndianStream.ReadByte(source);
sampleRate = EndianStream.ReadUInt32BE(source);
numberChannels = DataStream.ReadByte(source);
sampleRate = DataStream.ReadUInt32BE(source);
}
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
</configuration>

View File

@ -7,12 +7,13 @@ using System.IO;
using System.IO.Compression;
using SonicAudioLib;
using SonicAudioLib.Archive;
using SonicAudioLib.Archives;
using SonicAudioLib.IO;
using SonicAudioLib.CriMw;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
namespace SonicAudioCmd
{
@ -20,48 +21,26 @@ namespace SonicAudioCmd
{
static void Main(string[] args)
{
try
using (Stream source = File.OpenRead(args[0]))
{
var extractor = new DataExtractor();
extractor.ProgressChanged += OnProgressChanged;
CriTableReader reader = CriTableReader.Create(source);
reader.MoveToRow(3);
CriCpkArchive archive = new CriCpkArchive();
archive.Load(args[0]);
long pos = reader.GetPosition("utf");
CriTableReader soundElementReader = reader.GetTableReader("utf");
foreach (CriCpkEntry entry in archive)
while (soundElementReader.Read())
{
extractor.Add(args[0], Path.Combine(Path.ChangeExtension(args[0], null), entry.DirectoryName, entry.Name), entry.Position, entry.Length, entry.IsCompressed);
}
CriTable table = new CriTable();
table.Read(soundElementReader.GetSubStream("data"));
DateTime dateTime = DateTime.Now;
extractor.Run();
Console.WriteLine("Elapsed time: {0}", DateTime.Now - dateTime);
Console.ReadLine();
/*archive.EnableMask = true;
archive.Save("test.cpk");*/
}
catch (Exception exception)
using (Stream output = File.Create(Path.GetFileName(soundElementReader.GetString("name") + "_" + table.Rows[0].GetValue<byte>("lpflg") + "_" + ".dsp")))
{
Console.WriteLine(exception);
Console.ReadLine();
}
}
private static string buffer = new string(' ', 17);
private static void OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
int left = Console.CursorLeft;
int top = Console.CursorTop;
Console.Write(buffer);
Console.SetCursorPosition(left, top);
Console.WriteLine("Progress: {0}%", e.Progress);
Console.SetCursorPosition(left, top);
DataStream.WriteBytes(output, table.Rows[0].GetValue<byte[]>("header"));
DataStream.WriteBytes(output, table.Rows[0].GetValue<byte[]>("data"));
}
}
}
}
}
}

View File

@ -9,9 +9,10 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SonicAudioCmd</RootNamespace>
<AssemblyName>SonicAudioCmd</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -22,6 +23,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>

View File

@ -1,74 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using SonicAudioLib.Archive;
using SonicAudioLib.IO;
using SonicAudioLib.CriMw;
using SonicAudioLib.Module;
namespace SonicAudioLib.Archive
{
public enum CriAaxEntryFlag
{
Intro = 0,
Loop = 1,
}
public class CriAaxEntry : EntryBase
{
public CriAaxEntryFlag Flag { get; set; }
}
public class CriAaxArchive : ArchiveBase<CriAaxEntry>
{
public override void Read(Stream source)
{
using (CriTableReader reader = CriTableReader.Create(source))
{
if (reader.TableName != "AAX")
{
throw new Exception("Unknown AAX type. Please report the error with the file.");
}
while (reader.Read())
{
CriAaxEntry entry = new CriAaxEntry();
entry.Flag = (CriAaxEntryFlag)reader.GetByte("lpflg");
entry.Position = reader.GetPosition("data");
entry.Length = reader.GetLength("data");
entries.Add(entry);
}
}
}
public override void Write(Stream destination)
{
using (CriTableWriter writer = CriTableWriter.Create(destination, CriTableWriterSettings.AdxSettings))
{
writer.WriteStartTable("AAX");
writer.WriteField("data", typeof(byte[]));
writer.WriteField("lpflg", typeof(byte));
foreach (CriAaxEntry entry in entries.OrderBy(entry => entry.Flag))
{
writer.WriteRow(true, entry.FilePath, (byte)entry.Flag);
}
writer.WriteEndTable();
}
}
public override void Add(CriAaxEntry item)
{
if (entries.Count == 2 || entries.Exists(entry => (entry.Flag == item.Flag)))
{
return;
}
base.Add(item);
}
}
}

View File

@ -6,10 +6,10 @@ using System.IO;
using System.Reflection;
using SonicAudioLib.IO;
using SonicAudioLib.Module;
using SonicAudioLib.FileBases;
using System.Collections;
namespace SonicAudioLib.Archive
namespace SonicAudioLib.Archives
{
public abstract class EntryBase
{
@ -39,7 +39,7 @@ namespace SonicAudioLib.Archive
public virtual Stream Open(Stream source)
{
return new Substream(source, Position, length);
return new SubStream(source, Position, length);
}
public virtual Stream Open()
@ -48,20 +48,10 @@ namespace SonicAudioLib.Archive
}
}
public abstract class ArchiveBase<T> : ModuleBase, IEnumerable<T>
public abstract class ArchiveBase<T> : FileBase, IList<T>
{
protected List<T> entries = new List<T>();
public virtual event ProgressChanged ProgressChanged;
public virtual T this[int index]
{
get
{
return entries[index];
}
}
public virtual int Count
{
get
@ -70,16 +60,49 @@ namespace SonicAudioLib.Archive
}
}
public virtual bool IsReadOnly
{
get
{
return false;
}
}
public virtual T this[int index]
{
get
{
return entries[index];
}
set
{
entries[index] = value;
}
}
public virtual event ProgressChanged ProgressChanged;
public virtual int IndexOf(T item)
{
return entries.IndexOf(item);
}
public virtual void Insert(int index, T item)
{
entries.Insert(index, item);
}
public virtual void RemoveAt(int index)
{
entries.RemoveAt(index);
}
public virtual void Add(T item)
{
entries.Add(item);
}
public virtual T Get(int index)
{
return entries[index];
}
public virtual void Clear()
{
entries.Clear();
@ -95,28 +118,28 @@ namespace SonicAudioLib.Archive
entries.CopyTo(array, arrayIndex);
}
public virtual IEnumerator<T> GetEnumerator()
{
return entries.GetEnumerator();
}
public virtual bool Remove(T item)
{
return entries.Remove(item);
}
public virtual IEnumerator<T> GetEnumerator()
{
return entries.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return entries.GetEnumerator();
}
protected void OnProgressChanged(object sender, ProgressChangedEventArgs e)
protected virtual void OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
ProgressChanged?.Invoke(this, e);
}
#if DEBUG
public void Print()
public virtual void Print()
{
Type archiveType = GetType();
Console.WriteLine("{0}:", archiveType.Name);

View File

@ -0,0 +1,167 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using SonicAudioLib.Archives;
using SonicAudioLib.IO;
using SonicAudioLib.CriMw;
using SonicAudioLib.FileBases;
namespace SonicAudioLib.Archives
{
public enum CriAaxEntryFlag
{
Intro = 0,
Loop = 1,
}
public enum CriAaxArchiveMode
{
Adx = 0,
Dsp = 4,
Wav = 5,
}
public class CriAaxEntry : EntryBase
{
public CriAaxEntryFlag Flag { get; set; }
// DSP
public byte[] DspHeader { get; set; }
public uint DspSampleRate { get; set; }
public uint DspSampleCount { get; set; }
public byte DspChannelCount { get; set; }
}
public class CriAaxArchive : ArchiveBase<CriAaxEntry>
{
public CriAaxArchiveMode Mode { get; set; }
public override void Read(Stream source)
{
using (CriTableReader reader = CriTableReader.Create(source))
{
switch (reader.TableName)
{
// ADX wrapper
case "AAX":
Mode = CriAaxArchiveMode.Adx;
break;
// DSP wrapper
case "ADPCM_WII":
Mode = CriAaxArchiveMode.Dsp;
break;
// WAV wrapper
case "SWLPCM":
Mode = CriAaxArchiveMode.Wav;
break;
// Unknown
default:
throw new InvalidDataException($"Unknown AAX type '{reader.TableName}'. Please report the error with the file(s).");
}
if (Mode == CriAaxArchiveMode.Dsp)
{
// TODO
throw new NotImplementedException("DSP files aren't supported yet.");
}
else
{
while (reader.Read())
{
CriAaxEntry entry = new CriAaxEntry();
entry.Flag = (CriAaxEntryFlag)reader.GetByte("lpflg");
entry.Position = reader.GetPosition("data");
entry.Length = reader.GetLength("data");
entries.Add(entry);
}
}
}
}
public override void Write(Stream destination)
{
using (CriTableWriter writer = CriTableWriter.Create(destination, CriTableWriterSettings.AdxSettings))
{
string tableName = "AAX";
switch (Mode)
{
case CriAaxArchiveMode.Adx:
tableName = "AAX";
break;
case CriAaxArchiveMode.Dsp:
tableName = "ADPCM_WII";
break;
case CriAaxArchiveMode.Wav:
tableName = "SWLPCM";
break;
}
writer.WriteStartTable(tableName);
writer.WriteField("data", typeof(byte[]));
writer.WriteField("lpflg", typeof(byte));
foreach (CriAaxEntry entry in entries.OrderBy(entry => entry.Flag))
{
writer.WriteRow(true, entry.FilePath, (byte)entry.Flag);
}
writer.WriteEndTable();
}
}
public override void Add(CriAaxEntry item)
{
if (entries.Count == 2 || entries.Exists(entry => (entry.Flag == item.Flag)))
{
return;
}
base.Add(item);
}
public string GetModeExtension()
{
switch (Mode)
{
case CriAaxArchiveMode.Adx:
return ".adx";
case CriAaxArchiveMode.Dsp:
return ".dsp";
case CriAaxArchiveMode.Wav:
return ".wav";
}
return string.Empty;
}
public void SetModeFromExtension(string extension)
{
switch (Path.GetExtension(extension).ToLower())
{
case ".adx":
Mode = CriAaxArchiveMode.Adx;
break;
case ".dsp":
Mode = CriAaxArchiveMode.Dsp;
break;
case ".wav":
Mode = CriAaxArchiveMode.Wav;
break;
}
}
}
}

View File

@ -6,9 +6,9 @@ using System.IO;
using System.Diagnostics;
using SonicAudioLib.IO;
using SonicAudioLib.Module;
using SonicAudioLib.FileBases;
namespace SonicAudioLib.Archive
namespace SonicAudioLib.Archives
{
public class CriAfs2Entry : EntryBase
{
@ -32,25 +32,25 @@ namespace SonicAudioLib.Archive
switch (length)
{
case 2:
return EndianStream.ReadUInt16(source);
return DataStream.ReadUInt16(source);
case 4:
return EndianStream.ReadUInt32(source);
return DataStream.ReadUInt32(source);
case 8:
return EndianStream.ReadInt64(source);
return DataStream.ReadInt64(source);
default:
throw new ArgumentException($"Unimplemented field length ({length})", nameof(length));
}
}
if (EndianStream.ReadCString(source, 4) != "AFS2")
if (DataStream.ReadCString(source, 4) != "AFS2")
{
throw new InvalidDataException("'AFS2' signature could not be found.");
}
uint information = EndianStream.ReadUInt32(source);
uint information = DataStream.ReadUInt32(source);
uint type = information & 0xFF;
if (type != 1)
@ -61,8 +61,8 @@ namespace SonicAudioLib.Archive
uint idFieldLength = (information >> 16) & 0xFF;
uint positionFieldLength = (information >> 8) & 0xFF;
uint entryCount = EndianStream.ReadUInt32(source);
Align = EndianStream.ReadUInt32(source);
uint entryCount = DataStream.ReadUInt32(source);
Align = DataStream.ReadUInt32(source);
CriAfs2Entry previousEntry = null;
for (uint i = 0; i < entryCount; i++)
@ -138,15 +138,15 @@ namespace SonicAudioLib.Archive
switch (length)
{
case 2:
EndianStream.WriteUInt16(mDestination, (ushort)value);
DataStream.WriteUInt16(mDestination, (ushort)value);
break;
case 4:
EndianStream.WriteUInt32(mDestination, (uint)value);
DataStream.WriteUInt32(mDestination, (uint)value);
break;
case 8:
EndianStream.WriteInt64(mDestination, value);
DataStream.WriteInt64(mDestination, value);
break;
default:
@ -156,12 +156,12 @@ namespace SonicAudioLib.Archive
long headerLength = Calculate(out uint idFieldLength, out uint positionFieldLength);
EndianStream.WriteCString(mDestination, "AFS2", 4);
EndianStream.WriteUInt32(mDestination, 1 | (idFieldLength << 16) | (positionFieldLength << 8));
EndianStream.WriteUInt32(mDestination, (uint)entries.Count);
EndianStream.WriteUInt32(mDestination, Align);
DataStream.WriteCString(mDestination, "AFS2", 4);
DataStream.WriteUInt32(mDestination, 1 | (idFieldLength << 16) | (positionFieldLength << 8));
DataStream.WriteUInt32(mDestination, (uint)entries.Count);
DataStream.WriteUInt32(mDestination, Align);
VldPool vldPool = new VldPool(Align, headerLength);
DataPool vldPool = new DataPool(Align, headerLength);
vldPool.ProgressChanged += OnProgressChanged;
var orderedEntries = entries.OrderBy(entry => entry.Id);

View File

@ -8,7 +8,7 @@ using System.Reflection;
using SonicAudioLib.IO;
using SonicAudioLib.CriMw;
namespace SonicAudioLib.Archive
namespace SonicAudioLib.Archives
{
public class CriCpkEntry : EntryBase
{
@ -19,6 +19,7 @@ namespace SonicAudioLib.Archive
public uint Id { get; set; }
public string Comment { get; set; }
public bool IsCompressed { get; set; }
public long UncompressedLength { get; set; }
public DateTime UpdateDateTime
{
@ -67,7 +68,7 @@ namespace SonicAudioLib.Archive
{
if (align != 1)
{
new NotImplementedException("Alignment is currently not implemented.");
throw new NotImplementedException("Alignment is currently not implemented.");
}
align = value;
@ -168,7 +169,8 @@ namespace SonicAudioLib.Archive
entry.Position = (long)tocReader.GetUInt64("FileOffset");
entry.Id = isLatestVersion ? tocReader.GetUInt32("ID") : tocReader.GetUInt32("Info");
entry.Comment = tocReader.GetString("UserString");
entry.IsCompressed = entry.Length != tocReader.GetUInt32("ExtractSize");
entry.UncompressedLength = tocReader.GetUInt32("ExtractSize");
entry.IsCompressed = entry.Length != entry.UncompressedLength;
if (contentPosition < tocPosition)
{
@ -209,14 +211,15 @@ namespace SonicAudioLib.Archive
{
if (itocReader.GetUInt32("FilesL") > 0)
{
using (CriTableReader dataReader = itocReader.GetCriTableReader("DataL"))
using (CriTableReader dataReader = itocReader.GetTableReader("DataL"))
{
while (dataReader.Read())
{
CriCpkEntry entry = new CriCpkEntry();
entry.Id = dataReader.GetUInt16("ID");
entry.Length = dataReader.GetUInt16("FileSize");
entry.IsCompressed = entry.Length != dataReader.GetUInt16("ExtractSize");
entry.UncompressedLength = dataReader.GetUInt16("ExtractSize");
entry.IsCompressed = entry.Length != entry.UncompressedLength;
entries.Add(entry);
}
@ -225,14 +228,15 @@ namespace SonicAudioLib.Archive
if (itocReader.GetUInt32("FilesH") > 0)
{
using (CriTableReader dataReader = itocReader.GetCriTableReader("DataH"))
using (CriTableReader dataReader = itocReader.GetTableReader("DataH"))
{
while (dataReader.Read())
{
CriCpkEntry entry = new CriCpkEntry();
entry.Id = dataReader.GetUInt16("ID");
entry.Length = dataReader.GetUInt32("FileSize");
entry.IsCompressed = entry.Length != dataReader.GetUInt32("ExtractSize");
entry.UncompressedLength = dataReader.GetUInt32("ExtractSize");
entry.IsCompressed = entry.Length != entry.UncompressedLength;
entries.Add(entry);
}
@ -268,7 +272,7 @@ namespace SonicAudioLib.Archive
return $"{assemblyName.Name}, {assemblyName.Version.ToString()}";
}
VldPool vldPool = new VldPool(Align, 2048);
DataPool vldPool = new DataPool(Align, 2048);
vldPool.ProgressChanged += OnProgressChanged;
using (CriCpkSection cpkSection = new CriCpkSection(destination, "CPK ", enableMask))
@ -553,8 +557,8 @@ namespace SonicAudioLib.Archive
cpkSection.Writer.WriteEndTable();
}
EndianStream.Pad(destination, 2042);
EndianStream.WriteCString(destination, "(c)CRI", 6);
DataStream.Pad(destination, 2042);
DataStream.WriteCString(destination, "(c)CRI", 6);
vldPool.Write(destination);
}
@ -587,97 +591,80 @@ namespace SonicAudioLib.Archive
});
}
public static void Decompress(Stream source, long position, Stream destination)
public static void Decompress(byte[] source, byte[] destination)
{
source.Position = position;
if (EndianStream.ReadCString(source, 8) != "CRILAYLA")
if (Encoding.ASCII.GetString(source, 0, 8) != "CRILAYLA")
{
throw new InvalidDataException("'CRILAYLA' signature could not be found.");
}
uint uncompressedLength = EndianStream.ReadUInt32(source);
uint compressedLength = EndianStream.ReadUInt32(source);
int uncompressedLength = BitConverter.ToInt32(source, 8);
int compressedLength = BitConverter.ToInt32(source, 12);
// Copy the uncompressed header to destination
EndianStream.CopyPartTo(source, destination, position + 0x10 + compressedLength, 0x100, 4096);
Array.Copy(source, 16 + compressedLength, destination, 0, 256);
int srcEnd = 16 + compressedLength - 1;
int dstEnd = 256 + uncompressedLength - 1;
int dstOut = 0;
// Bit methods
int bitCount = 0;
int bitData = 0;
int bitNum = 0;
int ReadBits(int count)
{
if (bitCount < count)
if (bitNum < count)
{
int neededBytes = ((24 - bitCount) >> 3) + 1;
bitCount += neededBytes * 8;
int byteNum = ((24 - bitNum) >> 3) + 1;
bitNum += byteNum * 8;
while (neededBytes > 0)
while (byteNum > 0)
{
bitData = (bitData << 8) | source.ReadByte();
neededBytes--;
source.Position -= 2;
bitData = source[srcEnd--] | bitData << 8;
byteNum--;
}
}
bitCount -= count;
return (bitData >> bitCount) & ((1 << count) - 1);
bitNum -= count;
return (bitData >> bitNum) & ((1 << count) - 1);
}
// Deflate levels
IEnumerable<int> GetDeflateLevels()
while (dstOut < uncompressedLength)
{
yield return 2;
yield return 3;
yield return 5;
if (ReadBits(1) != 0)
{
int position = dstEnd - dstOut + ReadBits(13) + 3;
int length = 3 + ReadBits(2);
while (true)
if (length == 3 + 3)
{
yield return 8;
length += ReadBits(3);
if (length == 3 + 3 + 7)
{
length += ReadBits(5);
if (length == 3 + 3 + 7 + 31)
{
int bits;
do
{
bits = ReadBits(8);
length += bits;
} while (bits == 255);
}
}
}
// Decompression
source.Position = position + 0x10 + compressedLength - 1;
long writtenBytes = 0;
byte[] buffer = new byte[uncompressedLength];
while (writtenBytes < uncompressedLength)
{
// Verbatim byte, directly copy
if (ReadBits(1) == 0)
{
buffer[uncompressedLength - (writtenBytes++) - 1] = (byte)ReadBits(8);
dstOut += length;
Array.Copy(destination, position - length + 1, destination, dstEnd - dstOut + 1, length);
}
// Referenced bytes
else
{
long pos = uncompressedLength - 1 - writtenBytes + ReadBits(13) + 3;
long length = 3;
foreach (var deflateLevel in GetDeflateLevels())
{
long len = ReadBits(deflateLevel);
length += len;
if (len != ((1 << deflateLevel) - 1))
{
break;
destination[dstEnd - dstOut++] = (byte)ReadBits(8);
}
}
while (length > 0)
{
buffer[uncompressedLength - 1 - (writtenBytes++)] = buffer[pos--];
length--;
}
}
}
destination.Write(buffer, 0, buffer.Length);
}
private DateTime DateTimeFromCpkDateTime(ulong dateTime)
@ -727,7 +714,7 @@ namespace SonicAudioLib.Archive
uint length = (uint)(position - (headerPosition) - 16);
destination.Seek(headerPosition + 8, SeekOrigin.Begin);
EndianStream.WriteUInt32(destination, length);
DataStream.WriteUInt32(destination, length);
destination.Seek(position, SeekOrigin.Begin);
}
@ -736,16 +723,16 @@ namespace SonicAudioLib.Archive
{
source.Seek(position, SeekOrigin.Begin);
if (EndianStream.ReadCString(source, 4) != expectedSignature)
if (DataStream.ReadCString(source, 4) != expectedSignature)
{
throw new InvalidDataException($"'{expectedSignature}' signature could not be found.");
}
uint flag = EndianStream.ReadUInt32(source);
uint tableLength = EndianStream.ReadUInt32(source);
uint unknown = EndianStream.ReadUInt32(source);
uint flag = DataStream.ReadUInt32(source);
uint tableLength = DataStream.ReadUInt32(source);
uint unknown = DataStream.ReadUInt32(source);
return CriTableReader.Create(new Substream(source, source.Position, tableLength));
return CriTableReader.Create(new SubStream(source, source.Position, tableLength));
}
public CriCpkSection(Stream destination, string signature, bool enableMask)
@ -753,11 +740,17 @@ namespace SonicAudioLib.Archive
this.destination = destination;
headerPosition = destination.Position;
EndianStream.WriteCString(destination, signature, 4);
EndianStream.WriteUInt32(destination, 0xFF);
DataStream.WriteCString(destination, signature, 4);
DataStream.WriteUInt32(destination, 0xFF);
destination.Seek(8, SeekOrigin.Current);
writer = CriTableWriter.Create(destination, new CriTableWriterSettings() { LeaveOpen = true, EnableMask = enableMask, MaskXor = 25951, MaskXorMultiplier = 16661 });
writer = CriTableWriter.Create(destination, new CriTableWriterSettings()
{
LeaveOpen = true,
EnableMask = enableMask,
MaskXor = 25951,
MaskXorMultiplier = 16661
});
}
}
}

View File

@ -5,9 +5,9 @@ using System.Text;
using System.IO;
using SonicAudioLib.IO;
using SonicAudioLib.Module;
using SonicAudioLib.FileBases;
namespace SonicAudioLib.Archive
namespace SonicAudioLib.Archives
{
public class HeroesPacEntry : EntryBase
{
@ -18,10 +18,10 @@ namespace SonicAudioLib.Archive
{
public override void Read(Stream source)
{
uint entryCount = EndianStream.ReadUInt32(source);
uint tablePosition = EndianStream.ReadUInt32(source);
uint vldPoolLength = EndianStream.ReadUInt32(source);
uint vldPoolPosition = EndianStream.ReadUInt32(source);
uint entryCount = DataStream.ReadUInt32(source);
uint tablePosition = DataStream.ReadUInt32(source);
uint vldPoolLength = DataStream.ReadUInt32(source);
uint vldPoolPosition = DataStream.ReadUInt32(source);
source.Seek(tablePosition, SeekOrigin.Begin);
@ -30,8 +30,8 @@ namespace SonicAudioLib.Archive
{
HeroesPacEntry pacEntry = new HeroesPacEntry();
pacEntry.Id = EndianStream.ReadUInt32(source);
pacEntry.Position = vldPoolPosition + EndianStream.ReadUInt32(source);
pacEntry.Id = DataStream.ReadUInt32(source);
pacEntry.Position = vldPoolPosition + DataStream.ReadUInt32(source);
if (previousEntry != null)
{
@ -68,14 +68,14 @@ namespace SonicAudioLib.Archive
uint tableLength = (uint)(entries.Count * 16);
uint tablePosition = (uint)destination.Position;
VldPool vldPool = new VldPool();
DataPool vldPool = new DataPool();
foreach (HeroesPacEntry pacEntry in entries)
{
uint entryPosition = (uint)vldPool.Put(pacEntry.FilePath);
EndianStream.WriteUInt32(destination, pacEntry.Id);
EndianStream.WriteUInt32(destination, entryPosition);
DataStream.WriteUInt32(destination, pacEntry.Id);
DataStream.WriteUInt32(destination, entryPosition);
while ((destination.Position % 16) != 0)
{
@ -89,10 +89,10 @@ namespace SonicAudioLib.Archive
vldPool.Clear();
destination.Seek(0, SeekOrigin.Begin);
EndianStream.WriteUInt32(destination, (uint)entries.Count);
EndianStream.WriteUInt32(destination, tablePosition);
EndianStream.WriteUInt32(destination, (uint)vldPool.Length);
EndianStream.WriteUInt32(destination, (uint)vldPool.Position);
DataStream.WriteUInt32(destination, (uint)entries.Count);
DataStream.WriteUInt32(destination, tablePosition);
DataStream.WriteUInt32(destination, (uint)vldPool.Length);
DataStream.WriteUInt32(destination, (uint)vldPool.Position);
}
}
}

View File

@ -94,6 +94,21 @@ namespace SonicAudioLib.CriMw
}
}
public T GetValue<T>(CriField criField)
{
return (T)this[criField];
}
public T GetValue<T>(string fieldName)
{
return (T)this[fieldName];
}
public T GetValue<T>(int fieldIndex)
{
return (T)this[fieldIndex];
}
public object[] GetValueArray()
{
object[] values = new object[records.Count];

View File

@ -5,20 +5,23 @@ namespace SonicAudioLib.CriMw
{
struct CriTableHeader
{
public const string Signature = "@UTF";
public static readonly byte[] SignatureBytes = { 0x40, 0x55, 0x54, 0x46 };
public const byte EncodingTypeShiftJis = 0;
public const byte EncodingTypeUtf8 = 1;
public byte[] Signature;
public uint Length;
public byte UnknownByte;
public byte EncodingType;
public ushort RowsPosition;
public uint StringPoolPosition;
public uint DataPoolPosition;
public uint TableNamePosition;
public string TableName;
public ushort NumberOfFields;
public ushort FieldCount;
public ushort RowLength;
public uint NumberOfRows;
public uint RowCount;
}
[Flags]
@ -47,6 +50,7 @@ namespace SonicAudioLib.CriMw
struct CriTableField
{
public uint Offset;
public CriFieldFlag Flag;
public string Name;
public uint Position;
@ -61,13 +65,13 @@ namespace SonicAudioLib.CriMw
for (byte x = 0; x <= byte.MaxValue; x++)
{
// Find XOR using first byte
if ((signature[0] ^ x) == CriTableHeader.Signature[0])
if ((signature[0] ^ x) == CriTableHeader.SignatureBytes[0])
{
// Matched the first byte, try finding the multiplier with the second byte
for (byte m = 0; m <= byte.MaxValue; m++)
{
// Matched the second byte, now make sure the other bytes match as well
if ((signature[1] ^ (byte)(x * m)) == CriTableHeader.Signature[1])
if ((signature[1] ^ (byte)(x * m)) == CriTableHeader.SignatureBytes[1])
{
byte _x = (byte)(x * m);
@ -76,7 +80,7 @@ namespace SonicAudioLib.CriMw
{
_x *= m;
if ((signature[i] ^ _x) != CriTableHeader.Signature[i])
if ((signature[i] ^ _x) != CriTableHeader.SignatureBytes[i])
{
allMatches = false;
break;

View File

@ -3,11 +3,13 @@ using System.IO;
using System.Linq;
using SonicAudioLib.IO;
using SonicAudioLib.Module;
using SonicAudioLib.FileBases;
using System.Xml;
using System.Xml.Linq;
namespace SonicAudioLib.CriMw
{
public class CriTable : ModuleBase
public class CriTable : FileXmlBase
{
private CriFieldCollection fields;
private CriRowCollection rows;
@ -141,6 +143,86 @@ namespace SonicAudioLib.CriMw
}
}
public override void ReadXml(XmlReader reader)
{
var document = XDocument.Load(reader);
foreach (XElement element in document.Root.Element(nameof(Fields)).Elements(nameof(CriField)))
{
fields.Add(
element.Element(nameof(CriField.FieldName)).Value,
Type.GetType(element.Element(nameof(CriField.FieldType)).Value)
);
}
foreach (XElement element in document.Root.Element(nameof(Rows)).Elements(nameof(CriRow)))
{
CriRow row = NewRow();
foreach (CriRowRecord record in row.Records)
{
if (record.Field.FieldType == typeof(byte[]))
{
record.Value = Convert.FromBase64String(element.Element(record.Field.FieldName).Value);
}
else
{
record.Value = Convert.ChangeType(element.Element(record.Field.FieldName).Value, record.Field.FieldType);
}
}
rows.Add(row);
}
}
public override void WriteXml(XmlWriter writer)
{
var document = new XDocument(new XElement(nameof(CriTable)));
document.Root.Add(new XElement(nameof(TableName), TableName));
var fieldsElement = new XElement(nameof(Fields));
foreach (CriField field in fields)
{
var fieldElement = new XElement(nameof(CriField));
fieldElement.Add(
new XElement(nameof(field.FieldName), field.FieldName),
new XElement(nameof(field.FieldType), field.FieldType.Name)
);
fieldsElement.Add(fieldElement);
}
document.Root.Add(fieldsElement);
var rowsElement = new XElement(nameof(Rows));
foreach (CriRow row in rows)
{
var rowElement = new XElement(nameof(CriRow));
foreach (CriRowRecord record in row.Records)
{
if (record.Value is byte[] bytes)
{
rowElement.Add(new XElement(record.Field.FieldName, Convert.ToBase64String(bytes)));
}
else
{
rowElement.Add(new XElement(record.Field.FieldName, record.Value));
}
}
rowsElement.Add(rowElement);
}
document.Root.Add(rowsElement);
document.Save(writer);
}
public CriTable()
{
fields = new CriFieldCollection(this);

View File

@ -3,6 +3,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
using System.Collections;
namespace SonicAudioLib.CriMw
{
@ -13,7 +15,7 @@ namespace SonicAudioLib.CriMw
private CriTableHeader header;
private Encoding encoding;
private long rowIndex = -1;
private uint headerPosition;
private long headerPosition;
private bool leaveOpen;
public object this[int fieldIndex]
@ -36,7 +38,7 @@ namespace SonicAudioLib.CriMw
{
get
{
return header.NumberOfFields;
return header.FieldCount;
}
}
@ -44,7 +46,7 @@ namespace SonicAudioLib.CriMw
{
get
{
return header.NumberOfRows;
return header.RowCount;
}
}
@ -82,16 +84,15 @@ namespace SonicAudioLib.CriMw
private void ReadTable()
{
headerPosition = (uint)source.Position;
headerPosition = source.Position;
if (EndianStream.ReadCString(source, 4) != CriTableHeader.Signature)
if (!(header.Signature = DataStream.ReadBytes(source, 4)).SequenceEqual(CriTableHeader.SignatureBytes))
{
MemoryStream unmaskedSource = new MemoryStream();
source.Position = headerPosition;
CriTableMasker.FindKeys(EndianStream.ReadBytes(source, 4), out uint x, out uint m);
CriTableMasker.FindKeys(header.Signature, out uint x, out uint m);
source.Position = headerPosition;
source.Seek(headerPosition, SeekOrigin.Begin);
CriTableMasker.Mask(source, unmaskedSource, source.Length, x, m);
// Close the old stream
@ -101,12 +102,12 @@ namespace SonicAudioLib.CriMw
}
source = unmaskedSource;
source.Position = 4;
source.Seek(4, SeekOrigin.Begin);
}
header.Length = ReadUInt32() + 0x8;
header.UnknownByte = ReadByte();
header.EncodingType = ReadByte();
header.Length = DataStream.ReadUInt32BE(source) + 0x8;
header.UnknownByte = DataStream.ReadByte(source);
header.EncodingType = DataStream.ReadByte(source);
if (header.UnknownByte != 0)
{
@ -127,18 +128,19 @@ namespace SonicAudioLib.CriMw
throw new InvalidDataException($"Unknown encoding type ({header.EncodingType}). Please report this error with the file(s).");
}
header.RowsPosition = (ushort)(ReadUInt16() + 0x8);
header.StringPoolPosition = ReadUInt32() + 0x8;
header.DataPoolPosition = ReadUInt32() + 0x8;
header.RowsPosition = (ushort)(DataStream.ReadUInt16BE(source) + 0x8);
header.StringPoolPosition = DataStream.ReadUInt32BE(source) + 0x8;
header.DataPoolPosition = DataStream.ReadUInt32BE(source) + 0x8;
header.TableName = ReadString();
header.NumberOfFields = ReadUInt16();
header.RowLength = ReadUInt16();
header.NumberOfRows = ReadUInt32();
header.FieldCount = DataStream.ReadUInt16BE(source);
header.RowLength = DataStream.ReadUInt16BE(source);
header.RowCount = DataStream.ReadUInt32BE(source);
for (ushort i = 0; i < header.NumberOfFields; i++)
uint offset = 0;
for (ushort i = 0; i < header.FieldCount; i++)
{
CriTableField field = new CriTableField();
field.Flag = (CriFieldFlag)ReadByte();
field.Flag = (CriFieldFlag)DataStream.ReadByte(source);
if (field.Flag.HasFlag(CriFieldFlag.Name))
{
@ -149,7 +151,8 @@ namespace SonicAudioLib.CriMw
{
if (field.Flag.HasFlag(CriFieldFlag.Data))
{
ReadData(out field.Position, out field.Length);
field.Position = DataStream.ReadUInt32BE(source);
field.Length = DataStream.ReadUInt32BE(source);
}
else
@ -164,6 +167,36 @@ namespace SonicAudioLib.CriMw
field.Value = CriField.NullValues[(byte)field.Flag & 0x0F];
}
// Row storage, calculate the offset
if (field.Flag.HasFlag(CriFieldFlag.RowStorage))
{
field.Offset = offset;
switch (field.Flag & CriFieldFlag.TypeMask)
{
case CriFieldFlag.Byte:
case CriFieldFlag.SByte:
offset += 1;
break;
case CriFieldFlag.Int16:
case CriFieldFlag.UInt16:
offset += 2;
break;
case CriFieldFlag.Int32:
case CriFieldFlag.UInt32:
case CriFieldFlag.Single:
case CriFieldFlag.String:
offset += 4;
break;
case CriFieldFlag.Int64:
case CriFieldFlag.UInt64:
case CriFieldFlag.Double:
case CriFieldFlag.Data:
offset += 8;
break;
}
}
fields.Add(field);
}
}
@ -225,46 +258,12 @@ namespace SonicAudioLib.CriMw
private void GoToValue(int fieldIndex)
{
long position = headerPosition + header.RowsPosition + (header.RowLength * rowIndex);
for (int i = 0; i < fieldIndex; i++)
{
if (!fields[i].Flag.HasFlag(CriFieldFlag.RowStorage))
{
continue;
}
switch (fields[i].Flag & CriFieldFlag.TypeMask)
{
case CriFieldFlag.Byte:
case CriFieldFlag.SByte:
position += 1;
break;
case CriFieldFlag.Int16:
case CriFieldFlag.UInt16:
position += 2;
break;
case CriFieldFlag.Int32:
case CriFieldFlag.UInt32:
case CriFieldFlag.Single:
case CriFieldFlag.String:
position += 4;
break;
case CriFieldFlag.Int64:
case CriFieldFlag.UInt64:
case CriFieldFlag.Double:
case CriFieldFlag.Data:
position += 8;
break;
}
}
source.Position = position;
source.Seek(headerPosition + header.RowsPosition + (header.RowLength * rowIndex) + fields[fieldIndex].Offset, SeekOrigin.Begin);
}
public bool Read()
{
if (rowIndex + 1 >= header.NumberOfRows)
if (rowIndex + 1 >= header.RowCount)
{
return false;
}
@ -275,7 +274,7 @@ namespace SonicAudioLib.CriMw
public bool MoveToRow(long rowIndex)
{
if (rowIndex >= header.NumberOfRows)
if (rowIndex >= header.RowCount)
{
return false;
}
@ -286,9 +285,9 @@ namespace SonicAudioLib.CriMw
public object[] GetValueArray()
{
object[] values = new object[header.NumberOfFields];
object[] values = new object[header.FieldCount];
for (int i = 0; i < header.NumberOfFields; i++)
for (int i = 0; i < header.FieldCount; i++)
{
if (fields[i].Flag.HasFlag(CriFieldFlag.Data))
{
@ -304,6 +303,14 @@ namespace SonicAudioLib.CriMw
return values;
}
public IEnumerable GetValues()
{
for (int i = 0; i < header.FieldCount; i++)
{
yield return GetValue(i);
}
}
public object GetValue(int fieldIndex)
{
if (fieldIndex < 0 || fieldIndex >= fields.Count)
@ -315,7 +322,7 @@ namespace SonicAudioLib.CriMw
{
if (fields[fieldIndex].Flag.HasFlag(CriFieldFlag.Data))
{
return new Substream(source, 0, 0);
return new SubStream(source, 0, 0);
}
return fields[fieldIndex].Value;
@ -450,19 +457,19 @@ namespace SonicAudioLib.CriMw
return (string)GetValue(fieldName);
}
public Substream GetSubstream(int fieldIndex)
public SubStream GetSubStream(int fieldIndex)
{
return (Substream)GetValue(fieldIndex);
return (SubStream)GetValue(fieldIndex);
}
public Substream GetSubstream(string fieldName)
public SubStream GetSubStream(string fieldName)
{
return (Substream)GetValue(fieldName);
return (SubStream)GetValue(fieldName);
}
public byte[] GetData(int fieldIndex)
{
return GetSubstream(fieldIndex).ToArray();
return GetSubStream(fieldIndex).ToArray();
}
public byte[] GetData(string fieldName)
@ -470,14 +477,14 @@ namespace SonicAudioLib.CriMw
return GetData(GetFieldIndex(fieldName));
}
public CriTableReader GetCriTableReader(string fieldName)
public CriTableReader GetTableReader(string fieldName)
{
return new CriTableReader(GetSubstream(fieldName), false);
return new CriTableReader(GetSubStream(fieldName), false);
}
public CriTableReader GetCriTableReader(int fieldIndex)
public CriTableReader GetTableReader(int fieldIndex)
{
return new CriTableReader(GetSubstream(fieldIndex), false);
return new CriTableReader(GetSubStream(fieldIndex), false);
}
public uint GetLength(int fieldIndex)
@ -493,8 +500,9 @@ namespace SonicAudioLib.CriMw
}
GoToValue(fieldIndex);
ReadData(out uint vldPosition, out uint vldLength);
return vldLength;
source.Seek(4, SeekOrigin.Current);
return DataStream.ReadUInt32BE(source);
}
public uint GetLength(string fieldName)
@ -515,8 +523,7 @@ namespace SonicAudioLib.CriMw
}
GoToValue(fieldIndex);
ReadData(out uint vldPosition, out uint vldLength);
return headerPosition + header.DataPoolPosition + vldPosition;
return (uint)(headerPosition + header.DataPoolPosition + DataStream.ReadUInt32BE(source));
}
public uint GetPosition(string fieldName)
@ -544,75 +551,15 @@ namespace SonicAudioLib.CriMw
return (Guid)GetValue(fieldName);
}
private byte[] ReadBytes(int length)
{
return EndianStream.ReadBytes(source, length);
}
private byte ReadByte()
{
return EndianStream.ReadByte(source);
}
private bool ReadBoolean()
{
return EndianStream.ReadBoolean(source);
}
private sbyte ReadSByte()
{
return EndianStream.ReadSByte(source);
}
private ushort ReadUInt16()
{
return EndianStream.ReadUInt16BE(source);
}
private short ReadInt16()
{
return EndianStream.ReadInt16BE(source);
}
private uint ReadUInt32()
{
return EndianStream.ReadUInt32BE(source);
}
private int ReadInt32()
{
return EndianStream.ReadInt32BE(source);
}
private ulong ReadUInt64()
{
return EndianStream.ReadUInt64BE(source);
}
private long ReadInt64()
{
return EndianStream.ReadInt64BE(source);
}
private float ReadSingle()
{
return EndianStream.ReadSingleBE(source);
}
private double ReadDouble()
{
return EndianStream.ReadDoubleBE(source);
}
private string ReadString()
{
uint stringPosition = ReadUInt32();
uint stringPosition = DataStream.ReadUInt32BE(source);
long previousPosition = source.Position;
source.Position = headerPosition + header.StringPoolPosition + stringPosition;
string readString = EndianStream.ReadCString(source, encoding);
source.Seek(headerPosition + header.StringPoolPosition + stringPosition, SeekOrigin.Begin);
string readString = DataStream.ReadCString(source, encoding);
source.Position = previousPosition;
source.Seek(previousPosition, SeekOrigin.Begin);
if (readString == StringPool.AdxBlankString || (readString == header.TableName && stringPosition == 0))
{
@ -622,64 +569,52 @@ namespace SonicAudioLib.CriMw
return readString;
}
private void ReadData(out uint vldPosition, out uint vldLength)
{
vldPosition = ReadUInt32();
vldLength = ReadUInt32();
}
private Guid ReadGuid()
{
byte[] buffer = new byte[16];
source.Read(buffer, 0, buffer.Length);
return new Guid(buffer);
}
private object ReadValue(CriFieldFlag fieldFlag)
{
switch (fieldFlag & CriFieldFlag.TypeMask)
{
case CriFieldFlag.Byte:
return ReadByte();
return DataStream.ReadByte(source);
case CriFieldFlag.SByte:
return ReadSByte();
return DataStream.ReadSByte(source);
case CriFieldFlag.UInt16:
return ReadUInt16();
return DataStream.ReadUInt16BE(source);
case CriFieldFlag.Int16:
return ReadInt16();
return DataStream.ReadInt16BE(source);
case CriFieldFlag.UInt32:
return ReadUInt32();
return DataStream.ReadUInt32BE(source);
case CriFieldFlag.Int32:
return ReadInt32();
return DataStream.ReadInt32BE(source);
case CriFieldFlag.UInt64:
return ReadUInt64();
return DataStream.ReadUInt64BE(source);
case CriFieldFlag.Int64:
return ReadInt64();
return DataStream.ReadInt64BE(source);
case CriFieldFlag.Single:
return ReadSingle();
return DataStream.ReadSingleBE(source);
case CriFieldFlag.Double:
return ReadDouble();
return DataStream.ReadDoubleBE(source);
case CriFieldFlag.String:
return ReadString();
case CriFieldFlag.Data:
{
ReadData(out uint vldPosition, out uint vldLength);
uint position = DataStream.ReadUInt32BE(source);
uint length = DataStream.ReadUInt32BE(source);
// Some ACB files have the length info set to zero for UTF table fields, so find the correct length
if (vldPosition > 0 && vldLength == 0)
if (position > 0 && length == 0)
{
source.Position = headerPosition + header.DataPoolPosition + vldPosition;
source.Seek(headerPosition + header.DataPoolPosition + position, SeekOrigin.Begin);
if (EndianStream.ReadCString(source, 4) == CriTableHeader.Signature)
if (DataStream.ReadBytes(source, 4).SequenceEqual(CriTableHeader.SignatureBytes))
{
vldLength = ReadUInt32() + 8;
length = DataStream.ReadUInt32BE(source) + 8;
}
}
return new Substream(source, headerPosition + header.DataPoolPosition + vldPosition, vldLength);
return new SubStream(source, headerPosition + header.DataPoolPosition + position, length);
}
case CriFieldFlag.Guid:
return ReadGuid();
return new Guid(DataStream.ReadBytes(source, 16));
}
return null;
@ -693,8 +628,6 @@ namespace SonicAudioLib.CriMw
{
source.Close();
}
GC.SuppressFinalize(this);
}
public static CriTableReader Create(byte[] sourceByteArray)

View File

@ -4,8 +4,9 @@ using System.Text;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using SonicAudioLib.IO;
using SonicAudioLib.Module;
using SonicAudioLib.FileBases;
namespace SonicAudioLib.CriMw
{
@ -25,7 +26,7 @@ namespace SonicAudioLib.CriMw
private List<CriTableField> fields;
private Stream destination;
private CriTableHeader header;
private VldPool vldPool;
private DataPool vldPool;
private StringPool stringPool;
private uint headerPosition;
@ -62,24 +63,16 @@ namespace SonicAudioLib.CriMw
status = Status.Start;
headerPosition = (uint)destination.Position;
header.TableName = tableName;
if (settings.PutBlankString)
{
stringPool.Put(StringPool.AdxBlankString);
}
EndianStream.WriteCString(destination, CriTableHeader.Signature, 4);
WriteUInt32(uint.MinValue);
WriteByte(byte.MinValue);
WriteByte(byte.MinValue);
WriteUInt16(ushort.MinValue);
WriteUInt32(uint.MinValue);
WriteUInt32(uint.MinValue);
WriteString(tableName);
WriteUInt16(ushort.MinValue);
WriteUInt16(ushort.MinValue);
WriteUInt32(uint.MinValue);
header.TableNamePosition = (uint)stringPool.Put(tableName);
var buffer = new byte[32];
destination.Write(buffer, 0, 32);
}
public void WriteEndTable()
@ -96,17 +89,17 @@ namespace SonicAudioLib.CriMw
status = Status.End;
destination.Seek(headerPosition + header.RowsPosition + (header.RowLength * header.NumberOfRows), SeekOrigin.Begin);
destination.Seek(headerPosition + header.RowsPosition + (header.RowLength * header.RowCount), SeekOrigin.Begin);
stringPool.Write(destination);
header.StringPoolPosition = (uint)stringPool.Position - headerPosition;
EndianStream.Pad(destination, vldPool.Align);
DataStream.Pad(destination, vldPool.Align);
vldPool.Write(destination);
header.DataPoolPosition = (uint)vldPool.Position - headerPosition;
EndianStream.Pad(destination, vldPool.Align);
DataStream.Pad(destination, vldPool.Align);
long previousPosition = destination.Position;
@ -122,21 +115,23 @@ namespace SonicAudioLib.CriMw
header.EncodingType = CriTableHeader.EncodingTypeUtf8;
}
destination.Position = headerPosition + 4;
WriteUInt32(header.Length - 8);
WriteByte(header.UnknownByte);
WriteByte(header.EncodingType);
WriteUInt16((ushort)(header.RowsPosition - 8));
WriteUInt32(header.StringPoolPosition - 8);
WriteUInt32(header.DataPoolPosition - 8);
destination.Seek(4, SeekOrigin.Current);
WriteUInt16(header.NumberOfFields);
WriteUInt16(header.RowLength);
WriteUInt32(header.NumberOfRows);
destination.Seek(headerPosition, SeekOrigin.Begin);
destination.Write(CriTableHeader.SignatureBytes, 0, 4);
DataStream.WriteUInt32BE(destination, header.Length - 8);
DataStream.WriteByte(destination, header.UnknownByte);
DataStream.WriteByte(destination, header.EncodingType);
DataStream.WriteUInt16BE(destination, (ushort)(header.RowsPosition - 8));
DataStream.WriteUInt32BE(destination, header.StringPoolPosition - 8);
DataStream.WriteUInt32BE(destination, header.DataPoolPosition - 8);
DataStream.WriteUInt32BE(destination, header.TableNamePosition);
DataStream.WriteUInt16BE(destination, header.FieldCount);
DataStream.WriteUInt16BE(destination, header.RowLength);
DataStream.WriteUInt32BE(destination, header.RowCount);
if (settings.EnableMask)
{
destination.Position = headerPosition;
destination.Seek(headerPosition, SeekOrigin.Begin);
CriTableMasker.Mask(destination, header.Length, settings.MaskXor, settings.MaskXorMultiplier);
}
@ -179,7 +174,7 @@ namespace SonicAudioLib.CriMw
Value = defaultValue
};
WriteByte((byte)field.Flag);
DataStream.WriteByte(destination, (byte)field.Flag);
if (!string.IsNullOrEmpty(fieldName))
{
@ -192,7 +187,7 @@ namespace SonicAudioLib.CriMw
}
fields.Add(field);
header.NumberOfFields++;
header.FieldCount++;
}
public void WriteField(string fieldName, Type fieldType)
@ -215,15 +210,40 @@ namespace SonicAudioLib.CriMw
Name = fieldName
};
WriteByte((byte)field.Flag);
DataStream.WriteByte(destination, (byte)field.Flag);
if (!string.IsNullOrEmpty(fieldName))
{
WriteString(field.Name);
}
field.Offset = header.RowLength;
switch (field.Flag & CriFieldFlag.TypeMask)
{
case CriFieldFlag.Byte:
case CriFieldFlag.SByte:
header.RowLength += 1;
break;
case CriFieldFlag.Int16:
case CriFieldFlag.UInt16:
header.RowLength += 2;
break;
case CriFieldFlag.Int32:
case CriFieldFlag.UInt32:
case CriFieldFlag.Single:
case CriFieldFlag.String:
header.RowLength += 4;
break;
case CriFieldFlag.Int64:
case CriFieldFlag.UInt64:
case CriFieldFlag.Double:
case CriFieldFlag.Data:
header.RowLength += 8;
break;
}
fields.Add(field);
header.NumberOfFields++;
header.FieldCount++;
}
public void WriteField(CriField criField)
@ -241,7 +261,6 @@ namespace SonicAudioLib.CriMw
status = Status.Idle;
header.RowsPosition = (ushort)(destination.Position - headerPosition);
header.RowLength = CalculateRowLength();
}
public void WriteStartRow()
@ -258,9 +277,9 @@ namespace SonicAudioLib.CriMw
status = Status.Row;
header.NumberOfRows++;
header.RowCount++;
destination.Position = headerPosition + header.RowsPosition + (header.NumberOfRows * header.RowLength);
destination.Seek(headerPosition + header.RowsPosition + (header.RowCount * header.RowLength), SeekOrigin.Begin);
byte[] buffer = new byte[header.RowLength];
destination.Write(buffer, 0, buffer.Length);
}
@ -283,80 +302,7 @@ namespace SonicAudioLib.CriMw
private void GoToValue(int fieldIndex)
{
long position = headerPosition + header.RowsPosition + (header.RowLength * (header.NumberOfRows - 1));
for (int i = 0; i < fieldIndex; i++)
{
if (!fields[i].Flag.HasFlag(CriFieldFlag.RowStorage))
{
continue;
}
switch (fields[i].Flag & CriFieldFlag.TypeMask)
{
case CriFieldFlag.Byte:
case CriFieldFlag.SByte:
position += 1;
break;
case CriFieldFlag.Int16:
case CriFieldFlag.UInt16:
position += 2;
break;
case CriFieldFlag.Int32:
case CriFieldFlag.UInt32:
case CriFieldFlag.Single:
case CriFieldFlag.String:
position += 4;
break;
case CriFieldFlag.Int64:
case CriFieldFlag.UInt64:
case CriFieldFlag.Double:
case CriFieldFlag.Data:
position += 8;
break;
}
}
destination.Position = position;
}
private ushort CalculateRowLength()
{
ushort length = 0;
for (int i = 0; i < fields.Count; i++)
{
if (!fields[i].Flag.HasFlag(CriFieldFlag.RowStorage))
{
continue;
}
switch (fields[i].Flag & CriFieldFlag.TypeMask)
{
case CriFieldFlag.Byte:
case CriFieldFlag.SByte:
length += 1;
break;
case CriFieldFlag.Int16:
case CriFieldFlag.UInt16:
length += 2;
break;
case CriFieldFlag.Int32:
case CriFieldFlag.UInt32:
case CriFieldFlag.Single:
case CriFieldFlag.String:
length += 4;
break;
case CriFieldFlag.Int64:
case CriFieldFlag.UInt64:
case CriFieldFlag.Double:
case CriFieldFlag.Data:
length += 8;
break;
}
}
return length;
destination.Seek(headerPosition + header.RowsPosition + (header.RowLength * (header.RowCount - 1)) + fields[fieldIndex].Offset, SeekOrigin.Begin);
}
public void WriteEndRow()
@ -384,146 +330,61 @@ namespace SonicAudioLib.CriMw
}
}
private void WriteByte(byte value)
{
EndianStream.WriteByte(destination, value);
}
private void WriteBoolean(bool value)
{
EndianStream.WriteBoolean(destination, value);
}
private void WriteSByte(sbyte value)
{
EndianStream.WriteSByte(destination, value);
}
private void WriteUInt16(ushort value)
{
EndianStream.WriteUInt16BE(destination, value);
}
private void WriteInt16(short value)
{
EndianStream.WriteInt16BE(destination, value);
}
private void WriteUInt32(uint value)
{
EndianStream.WriteUInt32BE(destination, value);
}
private void WriteInt32(int value)
{
EndianStream.WriteInt32BE(destination, value);
}
private void WriteUInt64(ulong value)
{
EndianStream.WriteUInt64BE(destination, value);
}
private void WriteInt64(long value)
{
EndianStream.WriteInt64BE(destination, value);
}
private void WriteSingle(float value)
{
EndianStream.WriteSingleBE(destination, value);
}
private void WriteDouble(double value)
{
EndianStream.WriteDoubleBE(destination, value);
}
private void WriteString(string value)
{
if (settings.RemoveDuplicateStrings && stringPool.ContainsString(value))
{
WriteUInt32((uint)stringPool.GetStringPosition(value));
DataStream.WriteUInt32BE(destination, (uint)stringPool.GetStringPosition(value));
}
else
{
WriteUInt32((uint)stringPool.Put(value));
DataStream.WriteUInt32BE(destination, (uint)stringPool.Put(value));
}
}
private void WriteData(byte[] data)
{
WriteUInt32((uint)vldPool.Put(data));
WriteUInt32((uint)data.Length);
}
private void WriteStream(Stream stream)
{
WriteUInt32((uint)vldPool.Put(stream));
WriteUInt32((uint)stream.Length);
}
private void WriteFile(FileInfo fileInfo)
{
WriteUInt32((uint)vldPool.Put(fileInfo));
WriteUInt32((uint)fileInfo.Length);
}
private void WriteModule(ModuleBase module)
{
WriteUInt32((uint)vldPool.Put(module));
WriteUInt32(0);
}
private void WriteGuid(Guid guid)
{
byte[] buffer = guid.ToByteArray();
destination.Write(buffer, 0, buffer.Length);
}
private void WriteValue(object val)
{
switch (val)
{
case byte value:
WriteByte(value);
DataStream.WriteByte(destination, value);
break;
case sbyte value:
WriteSByte(value);
DataStream.WriteSByte(destination, value);
break;
case ushort value:
WriteUInt16(value);
DataStream.WriteUInt16BE(destination, value);
break;
case short value:
WriteInt16(value);
DataStream.WriteInt16BE(destination, value);
break;
case uint value:
WriteUInt32(value);
DataStream.WriteUInt32BE(destination, value);
break;
case int value:
WriteInt32(value);
DataStream.WriteInt32BE(destination, value);
break;
case ulong value:
WriteUInt64(value);
DataStream.WriteUInt64BE(destination, value);
break;
case long value:
WriteInt64(value);
DataStream.WriteInt64BE(destination, value);
break;
case float value:
WriteSingle(value);
DataStream.WriteSingleBE(destination, value);
break;
case double value:
WriteDouble(value);
DataStream.WriteDoubleBE(destination, value);
break;
case string value:
@ -531,23 +392,22 @@ namespace SonicAudioLib.CriMw
break;
case byte[] value:
WriteData(value);
DataStream.WriteUInt32BE(destination, (uint)vldPool.Put(value));
DataStream.WriteUInt32BE(destination, (uint)value.Length);
break;
case Guid value:
WriteGuid(value);
destination.Write(value.ToByteArray(), 0, 16);
break;
case Stream stream:
WriteStream(stream);
case Stream value:
DataStream.WriteUInt32BE(destination, (uint)vldPool.Put(value));
DataStream.WriteUInt32BE(destination, (uint)value.Length);
break;
case ModuleBase module:
WriteModule(module);
break;
case FileInfo fileInfo:
WriteFile(fileInfo);
case FileInfo value:
DataStream.WriteUInt32BE(destination, (uint)vldPool.Put(value));
DataStream.WriteUInt32BE(destination, (uint)value.Length);
break;
}
}
@ -598,7 +458,7 @@ namespace SonicAudioLib.CriMw
header = new CriTableHeader();
fields = new List<CriTableField>();
stringPool = new StringPool(settings.EncodingType);
vldPool = new VldPool(settings.Align);
vldPool = new DataPool(settings.Align);
}
}

View File

@ -160,7 +160,7 @@ namespace SonicAudioLib.CriMw.Serialization
}
}
else if (arrayList == null || (arrayList != null && arrayList.Count == 0))
else if (arrayList == null || (arrayList != null && (arrayList.Count == 0 || (arrayList.Count == 1 && propertyInfo.GetValue(arrayList[0]) is null))))
{
useDefaultValue = true;
defaultValue = null;
@ -168,7 +168,7 @@ namespace SonicAudioLib.CriMw.Serialization
if (defaultValue is bool boolean)
{
defaultValue = boolean ? (byte)1 : (byte)0;
defaultValue = (byte)(boolean ? 1 : 0);
}
else if (defaultValue is Enum)
@ -204,7 +204,7 @@ namespace SonicAudioLib.CriMw.Serialization
if (value is bool boolean)
{
value = boolean ? (byte)1 : (byte)0;
value = (byte)(boolean ? 1 : 0);
}
else if (value is Enum)
@ -309,7 +309,7 @@ namespace SonicAudioLib.CriMw.Serialization
object value = tableReader.GetValue(fieldName);
if (value is Substream substream)
if (value is SubStream substream)
{
value = substream.ToArray();
}

View File

@ -4,9 +4,9 @@ using System.Linq;
using System.Text;
using System.IO;
namespace SonicAudioLib.Module
namespace SonicAudioLib.FileBases
{
public abstract class ModuleBase
public abstract class FileBase
{
protected int bufferSize = 4096;

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace SonicAudioLib.FileBases
{
// Because C# doesn't allow you to inherit
// more than 1 abstract class.
public abstract class FileXmlBase : FileBase
{
public abstract void ReadXml(XmlReader reader);
public abstract void WriteXml(XmlWriter writer);
public virtual void LoadXml(string sourceFileName)
{
using (XmlReader reader = XmlReader.Create(sourceFileName))
{
ReadXml(reader);
}
}
public virtual void SaveXml(string destinationFileName)
{
var settings = new XmlWriterSettings();
settings.Indent = true;
using (XmlWriter writer = XmlWriter.Create(destinationFileName, settings))
{
WriteXml(writer);
}
}
}
}

View File

@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using System.IO;
using SonicAudioLib.Archive;
using SonicAudioLib.Archives;
namespace SonicAudioLib.IO
{
@ -23,7 +23,6 @@ namespace SonicAudioLib.IO
public string DestinationFileName { get; set; }
public long Position { get; set; }
public long Length { get; set; }
public bool DecompressFromCriLayla { get; set; }
}
private List<Item> items = new List<Item>();
@ -34,9 +33,9 @@ namespace SonicAudioLib.IO
public event ProgressChanged ProgressChanged;
public void Add(object source, string destinationFileName, long position, long length, bool decompressFromCriLayla = false)
public void Add(object source, string destinationFileName, long position, long length)
{
items.Add(new Item { Source = source, DestinationFileName = destinationFileName, Position = position, Length = length, DecompressFromCriLayla = decompressFromCriLayla });
items.Add(new Item { Source = source, DestinationFileName = destinationFileName, Position = position, Length = length, });
}
public void Run()
@ -72,15 +71,7 @@ namespace SonicAudioLib.IO
)
using (Stream destination = destinationFileName.Create())
{
if (item.DecompressFromCriLayla)
{
CriCpkArchive.Decompress(source, item.Position, destination);
}
else
{
EndianStream.CopyPartTo(source, destination, item.Position, item.Length, BufferSize);
}
DataStream.CopyPartTo(source, destination, item.Position, item.Length, BufferSize);
}
};

View File

@ -1,11 +1,11 @@
using System.Collections;
using System.IO;
using SonicAudioLib.Module;
using SonicAudioLib.FileBases;
namespace SonicAudioLib.IO
{
public class VldPool
public class DataPool
{
private ArrayList items = new ArrayList();
@ -88,29 +88,13 @@ namespace SonicAudioLib.IO
return position;
}
public long Put(ModuleBase module)
{
if (module == null)
{
return 0;
}
length = Helpers.Align(length, align);
long position = length;
length += 0;
items.Add(module);
return position;
}
public void Write(Stream destination)
{
startPosition = destination.Position;
foreach (object item in items)
{
EndianStream.Pad(destination, align);
DataStream.Pad(destination, align);
if (item is byte[] bytes)
{
@ -131,11 +115,6 @@ namespace SonicAudioLib.IO
}
}
else if (item is ModuleBase module)
{
module.Write(destination);
}
ProgressChanged?.Invoke(this, new ProgressChangedEventArgs(((destination.Position - startPosition) / (double)(length - baseLength)) * 100.0));
}
}
@ -145,7 +124,7 @@ namespace SonicAudioLib.IO
items.Clear();
}
public VldPool(uint align, long baseLength)
public DataPool(uint align, long baseLength)
{
this.align = align;
@ -153,12 +132,12 @@ namespace SonicAudioLib.IO
length = this.baseLength;
}
public VldPool(uint align)
public DataPool(uint align)
{
this.align = align;
}
public VldPool()
public DataPool()
{
}
}

View File

@ -6,7 +6,7 @@ using System.Runtime.InteropServices;
namespace SonicAudioLib.IO
{
public static class EndianStream
public static class DataStream
{
[StructLayout(LayoutKind.Explicit)]
private struct SingleUnion
@ -28,6 +28,9 @@ namespace SonicAudioLib.IO
public ulong ULong;
}
// Not thread safe... but I guess it's OK.
private static byte[] buffer = new byte[8];
public static void CopyTo(Stream source, Stream destination)
{
CopyTo(source, destination, 4096);
@ -44,43 +47,44 @@ namespace SonicAudioLib.IO
}
}
public static void CopyPartTo(Stream source, Stream destination, long position, long length, int bufferSize)
public static void CopyPartTo(Stream source, Stream destination, long length, int bufferSize)
{
source.Position = position;
int num;
byte[] buffer = new byte[bufferSize];
int num;
long totalWrittenBytes = 0;
while ((num = source.Read(buffer, 0, bufferSize)) != 0)
long copiedBytes = 0;
while (copiedBytes < length && (num = source.Read(buffer, 0, bufferSize)) != 0)
{
totalWrittenBytes += num;
if (totalWrittenBytes > length)
if (copiedBytes + num >= length)
{
num -= (int)(totalWrittenBytes - length);
destination.Write(buffer, 0, num);
break;
num = (int)(length - copiedBytes);
}
copiedBytes += num;
destination.Write(buffer, 0, num);
}
}
public static void CopyPartTo(Stream source, Stream destination, long position, long length, int bufferSize)
{
source.Seek(position, SeekOrigin.Begin);
CopyPartTo(source, destination, length, bufferSize);
}
public static byte[] ReadBytes(Stream source, int length)
{
byte[] buffer = new byte[length];
source.Read(buffer, 0, length);
return buffer;
}
public static byte[] ReadBytesAt(Stream source, int length, long position)
{
long oldPosition = source.Position;
source.Position = position;
source.Seek(position, SeekOrigin.Begin);
var result = ReadBytes(source, length);
source.Position = oldPosition;
source.Seek(oldPosition, SeekOrigin.Begin);
return result;
}
@ -97,234 +101,288 @@ namespace SonicAudioLib.IO
public static byte ReadByte(Stream source)
{
int value = source.ReadByte();
if (value == -1)
{
throw new EndOfStreamException();
}
source.Read(buffer, 0, 1);
return (byte)value;
return buffer[0];
}
public static byte ReadByteAt(Stream source, long position)
{
long oldPosition = source.Position;
source.Position = position;
source.Seek(position, SeekOrigin.Begin);
byte value = ReadByte(source);
source.Position = oldPosition;
source.Seek(oldPosition, SeekOrigin.Begin);
return value;
}
public static void WriteByte(Stream destination, byte value)
{
destination.WriteByte(value);
buffer[0] = value;
destination.Write(buffer, 0, 1);
}
public static void WriteByteAt(Stream destination, byte value, long position)
{
long oldPosition = destination.Position;
destination.Position = position;
destination.Seek(position, SeekOrigin.Begin);
WriteByte(destination, value);
destination.Position = oldPosition;
destination.Seek(oldPosition, SeekOrigin.Begin);
}
public static bool ReadBoolean(Stream source)
{
return source.ReadByte() > 0;
source.Read(buffer, 0, 1);
return buffer[0] == 1;
}
public static void WriteBoolean(Stream destination, bool value)
{
WriteByte(destination, (byte)(value ? 1 : 0));
buffer[0] = (byte)(value ? 1 : 0);
destination.Write(buffer, 0, 1);
}
public static sbyte ReadSByte(Stream source)
{
return (sbyte)ReadByte(source);
source.Read(buffer, 0, 1);
return (sbyte)buffer[0];
}
public static void WriteSByte(Stream source, sbyte value)
public static void WriteSByte(Stream destination, sbyte value)
{
WriteByte(source, (byte)value);
buffer[0] = (byte)value;
destination.Write(buffer, 0, 1);
}
public static ushort ReadUInt16(Stream source)
{
return (ushort)(source.ReadByte() | source.ReadByte() << 8);
source.Read(buffer, 0, 2);
return (ushort)(buffer[0] | buffer[1] << 8);
}
public static ushort ReadUInt16BE(Stream source)
{
return (ushort)(source.ReadByte() << 8 | source.ReadByte());
source.Read(buffer, 0, 2);
return (ushort)(buffer[0] << 8 | buffer[1]);
}
public static void WriteUInt16(Stream destination, ushort value)
{
destination.WriteByte((byte)(value));
destination.WriteByte((byte)(value >> 8));
buffer[0] = (byte)(value);
buffer[1] = (byte)(value >> 8);
destination.Write(buffer, 0, 2);
}
public static void WriteUInt16BE(Stream destination, ushort value)
{
destination.WriteByte((byte)(value >> 8));
destination.WriteByte((byte)(value));
buffer[0] = (byte)(value >> 8);
buffer[1] = (byte)(value);
destination.Write(buffer, 0, 2);
}
public static short ReadInt16(Stream source)
{
return (short)(source.ReadByte() | source.ReadByte() << 8);
source.Read(buffer, 0, 2);
return (short)(buffer[0] | buffer[1] << 8);
}
public static short ReadInt16BE(Stream source)
{
return (short)(source.ReadByte() << 8 | source.ReadByte());
source.Read(buffer, 0, 2);
return (short)(buffer[0] << 8 | buffer[1]);
}
public static void WriteInt16(Stream destination, short value)
{
destination.WriteByte((byte)(value));
destination.WriteByte((byte)(value >> 8));
buffer[0] = (byte)(value);
buffer[1] = (byte)(value >> 8);
destination.Write(buffer, 0, 2);
}
public static void WriteInt16BE(Stream destination, short value)
{
destination.WriteByte((byte)(value >> 8));
destination.WriteByte((byte)(value));
buffer[0] = (byte)(value >> 8);
buffer[1] = (byte)(value);
destination.Write(buffer, 0, 2);
}
public static uint ReadUInt32(Stream source)
{
return (uint)(source.ReadByte() | source.ReadByte() << 8 | source.ReadByte() << 16 | source.ReadByte() << 24);
source.Read(buffer, 0, 4);
return (uint)(buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24);
}
public static uint ReadUInt32BE(Stream source)
{
return (uint)(source.ReadByte() << 24 | source.ReadByte() << 16 | source.ReadByte() << 8 | source.ReadByte());
source.Read(buffer, 0, 4);
return (uint)(buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3]);
}
public static void WriteUInt32(Stream destination, uint value)
{
destination.WriteByte((byte)(value));
destination.WriteByte((byte)(value >> 8));
destination.WriteByte((byte)(value >> 16));
destination.WriteByte((byte)(value >> 24));
buffer[0] = (byte)(value);
buffer[1] = (byte)(value >> 8);
buffer[2] = (byte)(value >> 16);
buffer[3] = (byte)(value >> 24);
destination.Write(buffer, 0, 4);
}
public static void WriteUInt32BE(Stream destination, uint value)
{
destination.WriteByte((byte)((value >> 24)));
destination.WriteByte((byte)((value >> 16)));
destination.WriteByte((byte)((value >> 8)));
destination.WriteByte((byte)(value));
buffer[0] = (byte)((value >> 24));
buffer[1] = (byte)((value >> 16));
buffer[2] = (byte)((value >> 8));
buffer[3] = (byte)(value);
destination.Write(buffer, 0, 4);
}
public static int ReadInt32(Stream source)
{
return source.ReadByte() | source.ReadByte() << 8 | source.ReadByte() << 16 | source.ReadByte() << 24;
source.Read(buffer, 0, 4);
return buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24;
}
public static int ReadInt32BE(Stream source)
{
return source.ReadByte() << 24 | source.ReadByte() << 16 | source.ReadByte() << 8 | source.ReadByte();
source.Read(buffer, 0, 4);
return buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
}
public static void WriteInt32(Stream destination, int value)
{
destination.WriteByte((byte)(value));
destination.WriteByte((byte)(value >> 8));
destination.WriteByte((byte)(value >> 16));
destination.WriteByte((byte)(value >> 24));
buffer[0] = (byte)(value);
buffer[1] = (byte)(value >> 8);
buffer[2] = (byte)(value >> 16);
buffer[3] = (byte)(value >> 24);
destination.Write(buffer, 0, 4);
}
public static void WriteInt32BE(Stream destination, int value)
{
destination.WriteByte((byte)((value >> 24)));
destination.WriteByte((byte)((value >> 16)));
destination.WriteByte((byte)((value >> 8)));
destination.WriteByte((byte)(value));
buffer[0] = (byte)((value >> 24));
buffer[1] = (byte)((value >> 16));
buffer[2] = (byte)((value >> 8));
buffer[3] = (byte)(value);
destination.Write(buffer, 0, 4);
}
public static ulong ReadUInt64(Stream source)
{
return ((uint)source.ReadByte() | (ulong)source.ReadByte() << 8 |
(ulong)source.ReadByte() << 16 | (ulong)source.ReadByte() << 24 |
(ulong)source.ReadByte() << 32 | (ulong)source.ReadByte() << 40 |
(ulong)source.ReadByte() << 48 | (ulong)source.ReadByte() << 56);
source.Read(buffer, 0, 8);
return (buffer[0] | (ulong)buffer[1] << 8 |
(ulong)buffer[2] << 16 | (ulong)buffer[3] << 24 |
(ulong)buffer[4] << 32 | (ulong)buffer[5] << 40 |
(ulong)buffer[6] << 48 | (ulong)buffer[7] << 56);
}
public static ulong ReadUInt64BE(Stream source)
{
return ((ulong)source.ReadByte() << 56 | (ulong)source.ReadByte() << 48 |
(ulong)source.ReadByte() << 40 | (ulong)source.ReadByte() << 32 |
(ulong)source.ReadByte() << 24 | (ulong)source.ReadByte() << 16 |
(ulong)source.ReadByte() << 8 | (uint)source.ReadByte());
source.Read(buffer, 0, 8);
return ((ulong)buffer[0] << 56 | (ulong)buffer[1] << 48 |
(ulong)buffer[2] << 40 | (ulong)buffer[3] << 32 |
(ulong)buffer[4] << 24 | (ulong)buffer[5] << 16 |
(ulong)buffer[6] << 8 | buffer[7]);
}
public static void WriteUInt64(Stream destination, ulong value)
{
destination.WriteByte((byte)(value));
destination.WriteByte((byte)(value >> 8));
destination.WriteByte((byte)(value >> 16));
destination.WriteByte((byte)(value >> 24));
destination.WriteByte((byte)(value >> 32));
destination.WriteByte((byte)(value >> 40));
destination.WriteByte((byte)(value >> 48));
destination.WriteByte((byte)(value >> 56));
buffer[0] = (byte)(value);
buffer[1] = (byte)(value >> 8);
buffer[2] = (byte)(value >> 16);
buffer[3] = (byte)(value >> 24);
buffer[4] = (byte)(value >> 32);
buffer[5] = (byte)(value >> 40);
buffer[6] = (byte)(value >> 48);
buffer[7] = (byte)(value >> 56);
destination.Write(buffer, 0, 8);
}
public static void WriteUInt64BE(Stream destination, ulong value)
{
destination.WriteByte((byte)((value >> 56)));
destination.WriteByte((byte)((value >> 48)));
destination.WriteByte((byte)((value >> 40)));
destination.WriteByte((byte)((value >> 32)));
destination.WriteByte((byte)((value >> 24)));
destination.WriteByte((byte)((value >> 16)));
destination.WriteByte((byte)((value >> 8)));
destination.WriteByte((byte)(value));
buffer[0] = (byte)((value >> 56));
buffer[1] = (byte)((value >> 48));
buffer[2] = (byte)((value >> 40));
buffer[3] = (byte)((value >> 32));
buffer[4] = (byte)((value >> 24));
buffer[5] = (byte)((value >> 16));
buffer[6] = (byte)((value >> 8));
buffer[7] = (byte)(value);
destination.Write(buffer, 0, 8);
}
public static long ReadInt64(Stream source)
{
return source.ReadByte() | source.ReadByte() << 8 |
source.ReadByte() << 16 | source.ReadByte() << 24 |
source.ReadByte() << 32 | source.ReadByte() << 40 |
source.ReadByte() << 48 | source.ReadByte() << 56;
source.Read(buffer, 0, 8);
return buffer[0] | buffer[1] << 8 |
buffer[2] << 16 | buffer[3] << 24 |
buffer[4] << 32 | buffer[5] << 40 |
buffer[6] << 48 | buffer[7] << 56;
}
public static long ReadInt64BE(Stream source)
{
return source.ReadByte() << 56 | source.ReadByte() << 48 |
source.ReadByte() << 40 | source.ReadByte() << 32 |
source.ReadByte() << 24 | source.ReadByte() << 16 |
source.ReadByte() << 8 | source.ReadByte();
source.Read(buffer, 0, 8);
return buffer[0] << 56 | buffer[1] << 48 |
buffer[2] << 40 | buffer[3] << 32 |
buffer[4] << 24 | buffer[5] << 16 |
buffer[6] << 8 | buffer[7];
}
public static void WriteInt64(Stream destination, long value)
{
destination.WriteByte((byte)(value));
destination.WriteByte((byte)(value >> 8));
destination.WriteByte((byte)(value >> 16));
destination.WriteByte((byte)(value >> 24));
destination.WriteByte((byte)(value >> 32));
destination.WriteByte((byte)(value >> 40));
destination.WriteByte((byte)(value >> 48));
destination.WriteByte((byte)(value >> 56));
buffer[0] = (byte)(value);
buffer[1] = (byte)(value >> 8);
buffer[2] = (byte)(value >> 16);
buffer[3] = (byte)(value >> 24);
buffer[4] = (byte)(value >> 32);
buffer[5] = (byte)(value >> 40);
buffer[6] = (byte)(value >> 48);
buffer[7] = (byte)(value >> 56);
destination.Write(buffer, 0, 8);
}
public static void WriteInt64BE(Stream destination, long value)
{
destination.WriteByte((byte)((value >> 56)));
destination.WriteByte((byte)((value >> 48)));
destination.WriteByte((byte)((value >> 40)));
destination.WriteByte((byte)((value >> 32)));
destination.WriteByte((byte)((value >> 24)));
destination.WriteByte((byte)((value >> 16)));
destination.WriteByte((byte)((value >> 8)));
destination.WriteByte((byte)(value));
buffer[0] = (byte)((value >> 56));
buffer[1] = (byte)((value >> 48));
buffer[2] = (byte)((value >> 40));
buffer[3] = (byte)((value >> 32));
buffer[4] = (byte)((value >> 24));
buffer[5] = (byte)((value >> 16));
buffer[6] = (byte)((value >> 8));
buffer[7] = (byte)(value);
destination.Write(buffer, 0, 8);
}
public static float ReadSingle(Stream source)
@ -400,11 +458,11 @@ namespace SonicAudioLib.IO
{
var characters = new List<byte>();
byte character = (byte)source.ReadByte();
while (character != 0)
source.Read(buffer, 0, 1);
while (buffer[0] != 0)
{
characters.Add(character);
character = (byte)source.ReadByte();
characters.Add(buffer[0]);
source.Read(buffer, 0, 1);
}
return encoding.GetString(characters.ToArray());
@ -420,7 +478,9 @@ namespace SonicAudioLib.IO
byte[] buffer = encoding.GetBytes(value);
destination.Write(buffer, 0, buffer.Length);
destination.WriteByte(0);
buffer[0] = 0;
destination.Write(buffer, 0, 1);
}
public static string ReadCString(Stream source, int length)
@ -457,10 +517,15 @@ namespace SonicAudioLib.IO
public static void Pad(Stream destination, long alignment)
{
while ((destination.Position % alignment) != 0)
long value = destination.Position;
while ((value % alignment) != 0)
{
destination.WriteByte(0);
}
value++;
}
byte[] buffer = new byte[value - destination.Position];
destination.Write(buffer, 0, buffer.Length);
}
}
}

View File

@ -13,7 +13,7 @@ namespace SonicAudioLib.IO
private long length = 0;
private Encoding encoding = Encoding.Default;
public static readonly string AdxBlankString = "<NULL>";
public const string AdxBlankString = "<NULL>";
public long Position
{
@ -51,7 +51,7 @@ namespace SonicAudioLib.IO
foreach (StringItem item in items)
{
EndianStream.WriteCString(destination, item.Value, encoding);
DataStream.WriteCString(destination, item.Value, encoding);
}
}

View File

@ -4,18 +4,12 @@ using System.Linq;
namespace SonicAudioLib.IO
{
/// <summary>
/// Represents a <see cref="Stream"/> based substream for viewing a portion of a Stream.
/// </summary>
public class Substream : Stream
public sealed class SubStream : Stream
{
private Stream baseStream;
private long streamPosition;
private long streamLength;
private long basePosition;
private long baseLength;
/// <summary>
/// Determines whether the base Stream supports reading.
/// </summary>
public override bool CanRead
{
get
@ -24,9 +18,6 @@ namespace SonicAudioLib.IO
}
}
/// <summary>
/// Determines whether the base Stream supports seeking.
/// </summary>
public override bool CanSeek
{
get
@ -35,84 +26,50 @@ namespace SonicAudioLib.IO
}
}
/// <summary>
/// Always returns false.
/// </summary>
public override bool CanWrite
{
get
{
return false;
return baseStream.CanWrite;
}
}
/// <summary>
/// Gets the length of the substream.
/// </summary>
public override long Length
{
get
{
return streamLength;
return baseLength;
}
}
/// <summary>
/// Gets or sets the position of the substream.
/// </summary>
public override long Position
{
get
{
return baseStream.Position - streamPosition;
return baseStream.Position - basePosition;
}
set
{
baseStream.Position = value + streamPosition;
baseStream.Position = basePosition + value;
}
}
/// <summary>
/// Gets or sets the position of the base Stream.
/// </summary>
public long AbsolutePosition
{
get
{
return baseStream.Position;
}
set
{
baseStream.Position = value;
}
}
/// <summary>
/// Closes the substream.
/// </summary>
public override void Close()
{
base.Close();
}
/// <summary>
/// Does nothing.
/// </summary>
public override void Flush()
{
baseStream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
if (baseStream.Position >= streamPosition + streamLength)
if (baseStream.Position >= basePosition + baseLength)
{
count = 0;
}
else if (baseStream.Position + count > streamPosition + streamLength)
else if (baseStream.Position + count > basePosition + baseLength)
{
count = (int)(streamPosition + streamLength - baseStream.Position);
count = (int)(basePosition + baseLength - baseStream.Position);
}
return baseStream.Read(buffer, offset, count);
@ -122,79 +79,69 @@ namespace SonicAudioLib.IO
{
if (origin == SeekOrigin.Begin)
{
offset += streamPosition;
offset += basePosition;
}
else if (origin == SeekOrigin.End)
{
offset = streamPosition + streamLength - offset;
offset = basePosition + baseLength - offset;
origin = SeekOrigin.Begin;
}
return baseStream.Seek(offset, origin);
}
/// <summary>
/// Seeks to the start of the substream.
/// </summary>
public void SeekToStart()
{
baseStream.Position = streamPosition;
}
/// <summary>
/// Throws <see cref="NotSupportedException"/>.
/// </summary>
public override void SetLength(long value)
{
throw new NotSupportedException();
baseLength = value;
if (basePosition + baseLength > baseStream.Length)
{
baseStream.SetLength(basePosition + baseLength);
}
}
/// <summary>
/// Throws <see cref="NotSupportedException"/>.
/// </summary>
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
/// <summary>
/// Throws <see cref="NotSupportedException"/>.
/// </summary>
public override void WriteByte(byte value)
if (baseStream.Position >= basePosition + baseLength)
{
throw new NotSupportedException();
count = 0;
}
else if (baseStream.Position + count > basePosition + baseLength)
{
count = (int)(basePosition + baseLength - baseStream.Position);
}
baseStream.Write(buffer, 0, count);
}
/// <summary>
/// Gets an array of the data that the substream covers.
/// </summary>
public byte[] ToArray()
{
using (MemoryStream destination = new MemoryStream())
long previousPosition = baseStream.Position;
baseStream.Seek(basePosition, SeekOrigin.Begin);
using (MemoryStream memoryStream = new MemoryStream())
{
CopyTo(destination);
return destination.ToArray();
CopyTo(memoryStream);
baseStream.Seek(previousPosition, SeekOrigin.Begin);
return memoryStream.ToArray();
}
}
/// <summary>
/// Creates a substream by the specified base Stream at the specified offset.
/// </summary>
public Substream(Stream baseStream, long streamPosition) : this(baseStream, streamPosition, baseStream.Length - streamPosition)
public SubStream(Stream baseStream, long basePosition) : this(baseStream, basePosition, baseStream.Length - basePosition)
{
}
/// <summary>
/// Creates a substream by the specified base Stream at the specified offset and with the specified length.
/// </summary>
public Substream(Stream baseStream, long streamPosition, long streamLength)
public SubStream(Stream baseStream, long basePosition, long baseLength)
{
this.baseStream = baseStream;
this.streamPosition = streamPosition;
this.streamLength = streamLength;
this.basePosition = basePosition;
this.baseLength = baseLength;
SeekToStart();
baseStream.Seek(this.basePosition, SeekOrigin.Begin);
}
}
}

View File

@ -9,8 +9,9 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SonicAudioLib</RootNamespace>
<AssemblyName>SonicAudioLib</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -21,6 +22,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@ -30,6 +32,9 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
@ -41,11 +46,11 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Archive\CriAaxArchive.cs" />
<Compile Include="Archive\CriAfs2Archive.cs" />
<Compile Include="Archive\ArchiveBase.cs" />
<Compile Include="Archive\CriCpkArchive.cs" />
<Compile Include="Archive\HeroesPacArchive.cs" />
<Compile Include="Archives\CriAaxArchive.cs" />
<Compile Include="Archives\CriAfs2Archive.cs" />
<Compile Include="Archives\ArchiveBase.cs" />
<Compile Include="Archives\CriCpkArchive.cs" />
<Compile Include="Archives\HeroesPacArchive.cs" />
<Compile Include="CriMw\CriTable.cs" />
<Compile Include="CriMw\CriField.cs" />
<Compile Include="CriMw\CriFieldCollection.cs" />
@ -58,13 +63,14 @@
<Compile Include="CriMw\CriTableReader.cs" />
<Compile Include="CriMw\Serialization\CriTableSerializer.cs" />
<Compile Include="CriMw\CriTableWriter.cs" />
<Compile Include="IO\EndianStream.cs" />
<Compile Include="FileBases\FileXmlBase.cs" />
<Compile Include="IO\DataStream.cs" />
<Compile Include="IO\DataExtractor.cs" />
<Compile Include="IO\StringPool.cs" />
<Compile Include="IO\Substream.cs" />
<Compile Include="Helpers.cs" />
<Compile Include="IO\VldPool.cs" />
<Compile Include="Module\ModuleBase.cs" />
<Compile Include="IO\SubStream.cs" />
<Compile Include="IO\DataPool.cs" />
<Compile Include="FileBases\FileBase.cs" />
<Compile Include="ProgressChangedEvent.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>