mirror of
https://github.com/blueskythlikesclouds/SonicAudioTools.git
synced 2025-02-08 23:19:44 +01:00
Improve ACB/CSB/CPK support
This commit is contained in:
parent
3cc98ff257
commit
a1541a648b
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -37,7 +37,6 @@ Global
|
|||||||
{4CF49665-3DFD-4A5C-B231-B97842F091B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{4CF49665-3DFD-4A5C-B231-B97842F091B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{4CF49665-3DFD-4A5C-B231-B97842F091B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{4CF49665-3DFD-4A5C-B231-B97842F091B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{4CF49665-3DFD-4A5C-B231-B97842F091B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{4CF49665-3DFD-4A5C-B231-B97842F091B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{4CF49665-3DFD-4A5C-B231-B97842F091B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
using SonicAudioLib.CriMw;
|
using SonicAudioLib.CriMw;
|
||||||
using SonicAudioLib.IO;
|
using SonicAudioLib.IO;
|
||||||
@ -18,9 +19,10 @@ namespace AcbEditor
|
|||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#if !DEBUG
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
#endif
|
||||||
if (args[0].EndsWith(".acb"))
|
if (args[0].EndsWith(".acb"))
|
||||||
{
|
{
|
||||||
string baseDirectory = Path.GetDirectoryName(args[0]);
|
string baseDirectory = Path.GetDirectoryName(args[0]);
|
||||||
@ -36,37 +38,53 @@ namespace AcbEditor
|
|||||||
CriAfs2Archive afs2Archive = new CriAfs2Archive();
|
CriAfs2Archive afs2Archive = new CriAfs2Archive();
|
||||||
CriAfs2Archive extAfs2Archive = new CriAfs2Archive();
|
CriAfs2Archive extAfs2Archive = new CriAfs2Archive();
|
||||||
|
|
||||||
|
CriCpkArchive cpkArchive = new CriCpkArchive();
|
||||||
|
CriCpkArchive extCpkArchive = null;
|
||||||
|
|
||||||
|
extAfs2ArchivePath = outputDirectoryPath + ".awb";
|
||||||
|
bool found = File.Exists(extAfs2ArchivePath);
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
extAfs2ArchivePath = outputDirectoryPath + "_streamfiles.awb";
|
||||||
|
found = File.Exists(extAfs2ArchivePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
extAfs2ArchivePath = outputDirectoryPath + "_STR.awb";
|
||||||
|
found = File.Exists(extAfs2ArchivePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cpkMode = true;
|
||||||
|
|
||||||
if (acbReader.GetLength("AwbFile") > 0)
|
if (acbReader.GetLength("AwbFile") > 0)
|
||||||
{
|
{
|
||||||
using (Substream afs2Stream = acbReader.GetSubstream("AwbFile"))
|
using (Substream afs2Stream = acbReader.GetSubstream("AwbFile"))
|
||||||
{
|
{
|
||||||
afs2Archive.Read(afs2Stream);
|
cpkMode = !CheckIfAfs2(afs2Stream);
|
||||||
|
|
||||||
|
if (cpkMode)
|
||||||
|
{
|
||||||
|
cpkArchive.Read(afs2Stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
afs2Archive.Read(afs2Stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (acbReader.GetLength("StreamAwbAfs2Header") > 0)
|
if (acbReader.GetLength("StreamAwbAfs2Header") > 0)
|
||||||
{
|
{
|
||||||
|
cpkMode = false;
|
||||||
|
|
||||||
using (Substream extAfs2Stream = acbReader.GetSubstream("StreamAwbAfs2Header"))
|
using (Substream extAfs2Stream = acbReader.GetSubstream("StreamAwbAfs2Header"))
|
||||||
{
|
{
|
||||||
extAfs2Archive.Read(extAfs2Stream);
|
extAfs2Archive.Read(extAfs2Stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
// cheatingggggg
|
|
||||||
extAfs2ArchivePath = outputDirectoryPath + ".awb";
|
|
||||||
bool found = File.Exists(extAfs2ArchivePath);
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
extAfs2ArchivePath = outputDirectoryPath + "_streamfiles.awb";
|
|
||||||
found = File.Exists(extAfs2ArchivePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
extAfs2ArchivePath = outputDirectoryPath + "_STR.awb";
|
|
||||||
found = File.Exists(extAfs2ArchivePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
throw new Exception("Cannot find the external .AWB file for this .ACB file. Please ensure that the external .AWB file is stored in the directory where the .ACB file is.");
|
throw new Exception("Cannot find the external .AWB file for this .ACB file. Please ensure that the external .AWB file is stored in the directory where the .ACB file is.");
|
||||||
@ -78,11 +96,11 @@ namespace AcbEditor
|
|||||||
{
|
{
|
||||||
while (waveformReader.Read())
|
while (waveformReader.Read())
|
||||||
{
|
{
|
||||||
ushort index = waveformReader.GetUInt16("Id");
|
ushort id = waveformReader.GetUInt16("Id");
|
||||||
byte encodeType = waveformReader.GetByte("EncodeType");
|
byte encodeType = waveformReader.GetByte("EncodeType");
|
||||||
bool streaming = waveformReader.GetBoolean("Streaming");
|
bool streaming = waveformReader.GetBoolean("Streaming");
|
||||||
|
|
||||||
string outputName = index.ToString("D5");
|
string outputName = id.ToString("D5");
|
||||||
if (streaming)
|
if (streaming)
|
||||||
{
|
{
|
||||||
outputName += "_streaming";
|
outputName += "_streaming";
|
||||||
@ -91,11 +109,32 @@ namespace AcbEditor
|
|||||||
outputName += GetExtension(encodeType);
|
outputName += GetExtension(encodeType);
|
||||||
outputName = Path.Combine(outputDirectoryPath, outputName);
|
outputName = Path.Combine(outputDirectoryPath, outputName);
|
||||||
|
|
||||||
Console.WriteLine("Extracting {0} file with index {1}...", GetExtension(encodeType).ToUpper(), index);
|
Console.WriteLine("Extracting {0} file with id {1}...", GetExtension(encodeType).ToUpper(), id);
|
||||||
|
|
||||||
if (streaming)
|
if (streaming)
|
||||||
{
|
{
|
||||||
CriAfs2Entry afs2Entry = extAfs2Archive.GetByCueIndex(index);
|
if (!found)
|
||||||
|
{
|
||||||
|
throw new Exception("Cannot find the external .AWB file for this .ACB file. Please ensure that the external .AWB file is stored in the directory where the .ACB file is.");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (extCpkArchive == null && cpkMode)
|
||||||
|
{
|
||||||
|
extCpkArchive = new CriCpkArchive();
|
||||||
|
extCpkArchive.Load(extAfs2ArchivePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntryBase afs2Entry = null;
|
||||||
|
|
||||||
|
if (cpkMode)
|
||||||
|
{
|
||||||
|
afs2Entry = extCpkArchive.GetById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
afs2Entry = extAfs2Archive.GetById(id);
|
||||||
|
}
|
||||||
|
|
||||||
using (Stream extAfs2Stream = File.OpenRead(extAfs2ArchivePath))
|
using (Stream extAfs2Stream = File.OpenRead(extAfs2ArchivePath))
|
||||||
using (Stream afs2EntryStream = afs2Entry.Open(extAfs2Stream))
|
using (Stream afs2EntryStream = afs2Entry.Open(extAfs2Stream))
|
||||||
@ -107,10 +146,20 @@ namespace AcbEditor
|
|||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CriAfs2Entry entry = afs2Archive.GetByCueIndex(index);
|
EntryBase afs2Entry = null;
|
||||||
|
|
||||||
|
if (cpkMode)
|
||||||
|
{
|
||||||
|
afs2Entry = cpkArchive.GetById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
afs2Entry = afs2Archive.GetById(id);
|
||||||
|
}
|
||||||
|
|
||||||
using (Substream afs2Stream = acbReader.GetSubstream("AwbFile"))
|
using (Substream afs2Stream = acbReader.GetSubstream("AwbFile"))
|
||||||
using (Stream afs2EntryStream = entry.Open(afs2Stream))
|
using (Stream afs2EntryStream = afs2Entry.Open(afs2Stream))
|
||||||
using (Stream afs2EntryDestination = File.Create(outputName))
|
using (Stream afs2EntryDestination = File.Create(outputName))
|
||||||
{
|
{
|
||||||
afs2EntryStream.CopyTo(afs2EntryDestination);
|
afs2EntryStream.CopyTo(afs2EntryDestination);
|
||||||
@ -151,15 +200,26 @@ namespace AcbEditor
|
|||||||
CriAfs2Archive afs2Archive = new CriAfs2Archive();
|
CriAfs2Archive afs2Archive = new CriAfs2Archive();
|
||||||
CriAfs2Archive extAfs2Archive = new CriAfs2Archive();
|
CriAfs2Archive extAfs2Archive = new CriAfs2Archive();
|
||||||
|
|
||||||
|
CriCpkArchive cpkArchive = new CriCpkArchive();
|
||||||
|
CriCpkArchive extCpkArchive = new CriCpkArchive();
|
||||||
|
cpkArchive.Mode = extCpkArchive.Mode = CriCpkMode.Id;
|
||||||
|
|
||||||
|
bool cpkMode = true;
|
||||||
|
|
||||||
|
byte[] awbFile = (byte[])acbFile.Rows[0]["AwbFile"];
|
||||||
|
byte[] streamAwbAfs2Header = (byte[])acbFile.Rows[0]["StreamAwbAfs2Header"];
|
||||||
|
|
||||||
|
cpkMode = !(awbFile != null && awbFile.Length >= 4 && Encoding.ASCII.GetString(awbFile, 0, 4) == "AFS2") && (streamAwbAfs2Header == null || streamAwbAfs2Header.Length == 0);
|
||||||
|
|
||||||
using (CriTableReader reader = CriTableReader.Create((byte[])acbFile.Rows[0]["WaveformTable"]))
|
using (CriTableReader reader = CriTableReader.Create((byte[])acbFile.Rows[0]["WaveformTable"]))
|
||||||
{
|
{
|
||||||
while (reader.Read())
|
while (reader.Read())
|
||||||
{
|
{
|
||||||
ushort index = reader.GetUInt16("Id");
|
ushort id = reader.GetUInt16("Id");
|
||||||
byte encodeType = reader.GetByte("EncodeType");
|
byte encodeType = reader.GetByte("EncodeType");
|
||||||
bool streaming = reader.GetBoolean("Streaming");
|
bool streaming = reader.GetBoolean("Streaming");
|
||||||
|
|
||||||
string inputName = index.ToString("D5");
|
string inputName = id.ToString("D5");
|
||||||
if (streaming)
|
if (streaming)
|
||||||
{
|
{
|
||||||
inputName += "_streaming";
|
inputName += "_streaming";
|
||||||
@ -170,23 +230,43 @@ namespace AcbEditor
|
|||||||
|
|
||||||
if (!File.Exists(inputName))
|
if (!File.Exists(inputName))
|
||||||
{
|
{
|
||||||
throw new Exception($"Cannot find audio file with index {index} for replacement.\nPath attempt: {inputName}");
|
throw new Exception($"Cannot find audio file with id {id} for replacement.\nPath attempt: {inputName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
CriAfs2Entry entry = new CriAfs2Entry();
|
|
||||||
entry.CueIndex = index;
|
|
||||||
entry.FilePath = new FileInfo(inputName);
|
|
||||||
|
|
||||||
Console.WriteLine("Adding {0}...", Path.GetFileName(inputName));
|
Console.WriteLine("Adding {0}...", Path.GetFileName(inputName));
|
||||||
|
|
||||||
if (streaming)
|
if (cpkMode)
|
||||||
{
|
{
|
||||||
extAfs2Archive.Add(entry);
|
CriCpkEntry entry = new CriCpkEntry();
|
||||||
|
entry.FilePath = new FileInfo(inputName);
|
||||||
|
entry.Id = id;
|
||||||
|
|
||||||
|
if (streaming)
|
||||||
|
{
|
||||||
|
extCpkArchive.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cpkArchive.Add(entry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
afs2Archive.Add(entry);
|
CriAfs2Entry entry = new CriAfs2Entry();
|
||||||
|
entry.FilePath = new FileInfo(inputName);
|
||||||
|
entry.Id = id;
|
||||||
|
|
||||||
|
if (streaming)
|
||||||
|
{
|
||||||
|
extAfs2Archive.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
afs2Archive.Add(entry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,43 +274,49 @@ namespace AcbEditor
|
|||||||
acbFile.Rows[0]["AwbFile"] = null;
|
acbFile.Rows[0]["AwbFile"] = null;
|
||||||
acbFile.Rows[0]["StreamAwbAfs2Header"] = null;
|
acbFile.Rows[0]["StreamAwbAfs2Header"] = null;
|
||||||
|
|
||||||
if (afs2Archive.Count > 0)
|
if (afs2Archive.Count > 0 || cpkArchive.Count > 0)
|
||||||
{
|
{
|
||||||
afs2Archive.Order();
|
|
||||||
|
|
||||||
Console.WriteLine("Saving internal AWB...");
|
Console.WriteLine("Saving internal AWB...");
|
||||||
acbFile.Rows[0]["AwbFile"] = afs2Archive.Save();
|
acbFile.Rows[0]["AwbFile"] = cpkMode ? cpkArchive.Save() : afs2Archive.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extAfs2Archive.Count > 0)
|
if (extAfs2Archive.Count > 0 || extCpkArchive.Count > 0)
|
||||||
{
|
{
|
||||||
extAfs2Archive.Order();
|
|
||||||
|
|
||||||
Console.WriteLine("Saving external AWB...");
|
Console.WriteLine("Saving external AWB...");
|
||||||
extAfs2Archive.Save(awbPath);
|
if (cpkMode)
|
||||||
|
|
||||||
byte[] afs2Header = new byte[16 +
|
|
||||||
(extAfs2Archive.Count * extAfs2Archive.CueIndexFieldLength) +
|
|
||||||
(extAfs2Archive.Count * extAfs2Archive.PositionFieldLength) +
|
|
||||||
extAfs2Archive.PositionFieldLength];
|
|
||||||
|
|
||||||
using (FileStream fileStream = File.OpenRead(awbPath))
|
|
||||||
{
|
{
|
||||||
fileStream.Read(afs2Header, 0, afs2Header.Length);
|
extCpkArchive.Save(awbPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
acbFile.Rows[0]["StreamAwbAfs2Header"] = afs2Header;
|
else
|
||||||
|
{
|
||||||
|
extAfs2Archive.Save(awbPath);
|
||||||
|
|
||||||
|
byte[] afs2Header = new byte[16 +
|
||||||
|
(extAfs2Archive.Count * extAfs2Archive.IdFieldLength) +
|
||||||
|
(extAfs2Archive.Count * extAfs2Archive.PositionFieldLength) +
|
||||||
|
extAfs2Archive.PositionFieldLength];
|
||||||
|
|
||||||
|
using (FileStream fileStream = File.OpenRead(awbPath))
|
||||||
|
{
|
||||||
|
fileStream.Read(afs2Header, 0, afs2Header.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
acbFile.Rows[0]["StreamAwbAfs2Header"] = afs2Header;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
acbFile.WriterSettings = CriTableWriterSettings.Adx2Settings;
|
acbFile.WriterSettings = CriTableWriterSettings.Adx2Settings;
|
||||||
acbFile.Save(acbPath);
|
acbFile.Save(acbPath);
|
||||||
}
|
}
|
||||||
|
#if !DEBUG
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MessageBox.Show($"{exception.Message}", "ACB Editor", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show($"{exception.Message}", "ACB Editor", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static string GetExtension(byte encodeType)
|
static string GetExtension(byte encodeType)
|
||||||
@ -256,17 +342,28 @@ namespace AcbEditor
|
|||||||
case 8:
|
case 8:
|
||||||
return ".at3";
|
return ".at3";
|
||||||
case 9:
|
case 9:
|
||||||
return ".3dsadpcm";
|
return ".bcwav";
|
||||||
case 18:
|
case 18:
|
||||||
case 11:
|
case 11:
|
||||||
return ".at9";
|
return ".at9";
|
||||||
case 12:
|
case 12:
|
||||||
return ".xma";
|
return ".xma";
|
||||||
case 13:
|
case 13:
|
||||||
return ".wiiuadpcm";
|
return ".dsp";
|
||||||
default:
|
default:
|
||||||
return ".bin";
|
return ".bin";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool CheckIfAfs2(Stream source)
|
||||||
|
{
|
||||||
|
long oldPosition = source.Position;
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
result = EndianStream.ReadCString(source, 4) == "AFS2";
|
||||||
|
source.Seek(oldPosition, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ namespace CsbBuilder
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class CriTableAax
|
public class CriTableAax
|
||||||
{
|
{
|
||||||
public enum EnumLoopFlag
|
public enum EnumLoopFlag : byte
|
||||||
{
|
{
|
||||||
Intro = 0,
|
Intro = 0,
|
||||||
Loop = 1,
|
Loop = 1,
|
||||||
@ -33,15 +33,15 @@ namespace CsbBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
[CriField("lpflg", 1)]
|
[CriField("lpflg", 1)]
|
||||||
public byte LoopFlag
|
public EnumLoopFlag LoopFlag
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return (byte)_lpflg;
|
return _lpflg;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_lpflg = (EnumLoopFlag)value;
|
_lpflg = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ namespace CsbBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
[CriField("id", 1)]
|
[CriField("id", 1)]
|
||||||
public uint Index
|
public uint Id
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@ namespace CsbBuilder
|
|||||||
[CriSerializable("TBLCSB")]
|
[CriSerializable("TBLCSB")]
|
||||||
public class CriTableCueSheet
|
public class CriTableCueSheet
|
||||||
{
|
{
|
||||||
public enum EnumTableType
|
public enum EnumTableType : byte
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
Cue = 1,
|
Cue = 1,
|
||||||
@ -42,15 +42,15 @@ namespace CsbBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
[CriField("ttype", 1)]
|
[CriField("ttype", 1)]
|
||||||
public byte TableType
|
public EnumTableType TableType
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return (byte)_ttype;
|
return _ttype;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_ttype = (EnumTableType)value;
|
_ttype = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,22 +12,16 @@ namespace CsbBuilder
|
|||||||
[CriSerializable("TBLSDL")]
|
[CriSerializable("TBLSDL")]
|
||||||
public class CriTableSoundElement
|
public class CriTableSoundElement
|
||||||
{
|
{
|
||||||
public enum EnumFormat
|
public enum EnumFormat : byte
|
||||||
{
|
{
|
||||||
Adx = 0,
|
Adx = 0,
|
||||||
Dsp = 4,
|
Dsp = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
public enum EnumStreamFlag
|
|
||||||
{
|
|
||||||
Internal = 0,
|
|
||||||
External = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
private string _name = string.Empty;
|
private string _name = string.Empty;
|
||||||
private EnumFormat _fmt = EnumFormat.Adx;
|
private EnumFormat _fmt = EnumFormat.Adx;
|
||||||
private byte _nch = 0;
|
private byte _nch = 0;
|
||||||
private EnumStreamFlag _stmflg = EnumStreamFlag.Internal;
|
private bool _stmflg = false;
|
||||||
private uint _sfreq = 0;
|
private uint _sfreq = 0;
|
||||||
private uint _nsmpl = 0;
|
private uint _nsmpl = 0;
|
||||||
|
|
||||||
@ -58,20 +52,23 @@ namespace CsbBuilder
|
|||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
DataList = CriTableSerializer.Deserialize<CriTableAax>(value);
|
if (value.Length > 0)
|
||||||
|
{
|
||||||
|
DataList = CriTableSerializer.Deserialize<CriTableAax>(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[CriField("fmt", 2)]
|
[CriField("fmt", 2)]
|
||||||
public byte Format
|
public EnumFormat Format
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return (byte)_fmt;
|
return _fmt;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_fmt = (EnumFormat)value;
|
_fmt = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,15 +86,15 @@ namespace CsbBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
[CriField("stmflg", 4)]
|
[CriField("stmflg", 4)]
|
||||||
public byte StreamFlag
|
public bool Streaming
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return (byte)_stmflg;
|
return _stmflg;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_stmflg = (EnumStreamFlag)value;
|
_stmflg = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ namespace CsbBuilder
|
|||||||
[CriSerializable("TBLSYN")]
|
[CriSerializable("TBLSYN")]
|
||||||
public class CriTableSynth
|
public class CriTableSynth
|
||||||
{
|
{
|
||||||
public enum EnumSynthType
|
public enum EnumSynthType : byte
|
||||||
{
|
{
|
||||||
Waveform = 0,
|
Waveform = 0,
|
||||||
Polyphonic = 1,
|
Polyphonic = 1,
|
||||||
@ -123,28 +123,28 @@ namespace CsbBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
[CriField("syntype", 1)]
|
[CriField("syntype", 1)]
|
||||||
public byte SynthType
|
public EnumSynthType SynthType
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return (byte)_syntype;
|
return _syntype;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_syntype = (EnumSynthType)value;
|
_syntype = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[CriField("cmplxtype", 2)]
|
[CriField("cmplxtype", 2)]
|
||||||
public byte ComplexType
|
public EnumSynthType ComplexType
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return (byte)_cmplxtype;
|
return _cmplxtype;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_cmplxtype = (EnumSynthType)value;
|
_cmplxtype = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ using SonicAudioLib.Archive;
|
|||||||
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Xml.Serialization;
|
using System.Xml.Serialization;
|
||||||
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
namespace CsbBuilder
|
namespace CsbBuilder
|
||||||
{
|
{
|
||||||
@ -25,48 +27,50 @@ namespace CsbBuilder
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
if (args[0].EndsWith(".csb"))
|
||||||
{
|
{
|
||||||
if (args[0].EndsWith(".csb"))
|
List<CriTableCueSheet> tables = CriTableSerializer.Deserialize<CriTableCueSheet>(args[0]);
|
||||||
{
|
Serialize(args[0] + ".xml", tables);
|
||||||
List<CriTableCueSheet> cueSheets = CriTableSerializer.Deserialize<CriTableCueSheet>(args[0]);
|
|
||||||
//CriTableSerializer.Serialize(args[0] + ".new", cueSheets, CriTableWriterSettings.AdxSettings);
|
|
||||||
|
|
||||||
XmlSerializer serializer = new XmlSerializer(typeof(List<CriTableCueSheet>));
|
foreach (CriTableCueSheet table in tables)
|
||||||
using (Stream dest = File.Create(Path.GetFileNameWithoutExtension(args[0]) + ".xml"))
|
{
|
||||||
|
DirectoryInfo baseDirectory = new DirectoryInfo(
|
||||||
|
Path.Combine(
|
||||||
|
Path.GetDirectoryName(args[0]),
|
||||||
|
Path.GetFileNameWithoutExtension(args[0]),
|
||||||
|
Enum.GetName(typeof(CriTableCueSheet.EnumTableType), table.TableType)));
|
||||||
|
|
||||||
|
baseDirectory.Create();
|
||||||
|
|
||||||
|
foreach (CriTableCue cue in table.DataList.OfType<CriTableCue>())
|
||||||
{
|
{
|
||||||
serializer.Serialize(dest, cueSheets);
|
Serialize(Path.Combine(baseDirectory.FullName, cue.Name + ".xml"), cue);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
else if (File.GetAttributes(args[0]).HasFlag(FileAttributes.Directory))
|
foreach (CriTableSynth synth in table.DataList.OfType<CriTableSynth>())
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (args[0].EndsWith(".xml"))
|
|
||||||
{
|
|
||||||
XmlSerializer serializer = new XmlSerializer(typeof(List<CriTableCueSheet>));
|
|
||||||
using (Stream dest = File.OpenRead(args[0]))
|
|
||||||
{
|
{
|
||||||
CriTableSerializer.Serialize(Path.GetFileNameWithoutExtension(args[0]) + ".csb", (List<CriTableCueSheet>)serializer.Deserialize(dest), CriTableWriterSettings.AdxSettings);
|
Directory.CreateDirectory(Path.Combine(baseDirectory.FullName, Path.GetDirectoryName(synth.SynthName)));
|
||||||
|
Serialize(Path.Combine(baseDirectory.FullName, synth.SynthName + ".xml"), synth);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (CriTableSoundElement soundElement in table.DataList.OfType<CriTableSoundElement>())
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.Combine(baseDirectory.FullName, Path.GetDirectoryName(soundElement.Name)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (Exception exception)
|
else if (File.GetAttributes(args[0]).HasFlag(FileAttributes.Directory))
|
||||||
{
|
{
|
||||||
//MessageBox.Show($"{exception.Message}", "CSB Builder", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
||||||
throw exception;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReadAdx(FileInfo fileInfo, out uint sampleRate, out byte numberChannels)
|
static void Serialize(string path, object obj)
|
||||||
{
|
{
|
||||||
using (Stream source = fileInfo.OpenRead())
|
XmlSerializer serializer = new XmlSerializer(obj.GetType());
|
||||||
|
using (Stream destination = File.Create(path))
|
||||||
{
|
{
|
||||||
source.Seek(7, SeekOrigin.Begin);
|
serializer.Serialize(destination, obj);
|
||||||
numberChannels = EndianStream.ReadByte(source);
|
|
||||||
sampleRate = EndianStream.ReadUInt32BE(source);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using SonicAudioLib.CriMw;
|
using SonicAudioLib.CriMw;
|
||||||
using SonicAudioLib.IO;
|
using SonicAudioLib.IO;
|
||||||
@ -22,13 +23,19 @@ namespace CsbEditor
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !DEBUG
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
#endif
|
||||||
if (args[0].EndsWith(".csb"))
|
if (args[0].EndsWith(".csb"))
|
||||||
{
|
{
|
||||||
string baseDirectory = Path.GetDirectoryName(args[0]);
|
string baseDirectory = Path.GetDirectoryName(args[0]);
|
||||||
string outputDirectoryName = Path.Combine(baseDirectory, Path.GetFileNameWithoutExtension(args[0]));
|
string outputDirectoryName = Path.Combine(baseDirectory, Path.GetFileNameWithoutExtension(args[0]));
|
||||||
|
|
||||||
|
CriCpkArchive cpkArchive = null;
|
||||||
|
string cpkPath = outputDirectoryName + ".cpk";
|
||||||
|
bool found = File.Exists(cpkPath);
|
||||||
|
|
||||||
using (CriTableReader reader = CriTableReader.Create(args[0]))
|
using (CriTableReader reader = CriTableReader.Create(args[0]))
|
||||||
{
|
{
|
||||||
while (reader.Read())
|
while (reader.Read())
|
||||||
@ -39,31 +46,66 @@ namespace CsbEditor
|
|||||||
{
|
{
|
||||||
while (sdlReader.Read())
|
while (sdlReader.Read())
|
||||||
{
|
{
|
||||||
if (sdlReader.GetByte("stmflg") != 0)
|
|
||||||
{
|
|
||||||
throw new Exception("The given CSB file contains external audio data. Those kind of CSB files are not supported yet.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sdlReader.GetByte("fmt") != 0)
|
if (sdlReader.GetByte("fmt") != 0)
|
||||||
{
|
{
|
||||||
throw new Exception("The given CSB file contains an audio file which is not an ADX. Only CSB files with ADXs are supported.");
|
throw new Exception("The given CSB file contains an audio file which is not an ADX. Only CSB files with ADXs are supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool streaming = sdlReader.GetBoolean("stmflg");
|
||||||
|
if (streaming && !found)
|
||||||
|
{
|
||||||
|
throw new Exception("Cannot find the external .CPK file for this .CSB file. Please ensure that the external .CPK file is stored in the directory where the .CPK file is.");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (streaming && found && cpkArchive == null)
|
||||||
|
{
|
||||||
|
cpkArchive = new CriCpkArchive();
|
||||||
|
cpkArchive.Load(cpkPath);
|
||||||
|
}
|
||||||
|
|
||||||
string sdlName = sdlReader.GetString("name");
|
string sdlName = sdlReader.GetString("name");
|
||||||
DirectoryInfo destinationPath = new DirectoryInfo(Path.Combine(outputDirectoryName, sdlName));
|
DirectoryInfo destinationPath = new DirectoryInfo(Path.Combine(outputDirectoryName, sdlName));
|
||||||
destinationPath.Create();
|
destinationPath.Create();
|
||||||
|
|
||||||
Console.WriteLine("Extracting {0}...", sdlName);
|
Console.WriteLine("Extracting {0}...", sdlName);
|
||||||
using (CriTableReader aaxReader = CriTableReader.Create(sdlReader.GetSubstream("data")))
|
|
||||||
{
|
|
||||||
while (aaxReader.Read())
|
|
||||||
{
|
|
||||||
string outputName = Path.Combine(destinationPath.FullName, aaxReader.GetBoolean("lpflg") ? "Loop.adx" : "Intro.adx");
|
|
||||||
|
|
||||||
using (Stream source = aaxReader.GetSubstream("data"))
|
CriAaxArchive aaxArchive = new CriAaxArchive();
|
||||||
using (Stream destination = File.Create(outputName))
|
|
||||||
|
if (streaming)
|
||||||
|
{
|
||||||
|
CriCpkEntry cpkEntry = cpkArchive.GetByPath(sdlName);
|
||||||
|
|
||||||
|
using (Stream cpkSource = File.OpenRead(cpkPath))
|
||||||
|
using (Stream aaxSource = cpkEntry.Open(cpkSource))
|
||||||
|
{
|
||||||
|
aaxArchive.Read(aaxSource);
|
||||||
|
|
||||||
|
foreach (CriAaxEntry entry in aaxArchive)
|
||||||
{
|
{
|
||||||
source.CopyTo(destination);
|
using (Stream destination = File.Create(Path.Combine(destinationPath.FullName,
|
||||||
|
entry.Flag == CriAaxEntryFlag.Intro ? "Intro.adx" : "Loop.adx")))
|
||||||
|
using (Stream entrySource = entry.Open(aaxSource))
|
||||||
|
{
|
||||||
|
entrySource.CopyTo(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using (Stream aaxSource = sdlReader.GetSubstream("data"))
|
||||||
|
{
|
||||||
|
aaxArchive.Read(aaxSource);
|
||||||
|
|
||||||
|
foreach (CriAaxEntry entry in aaxArchive)
|
||||||
|
{
|
||||||
|
using (Stream destination = File.Create(Path.Combine(destinationPath.FullName,
|
||||||
|
entry.Flag == CriAaxEntryFlag.Intro ? "Intro.adx" : "Loop.adx")))
|
||||||
|
using (Stream entrySource = entry.Open(aaxSource))
|
||||||
|
{
|
||||||
|
entrySource.CopyTo(destination);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,14 +128,18 @@ namespace CsbEditor
|
|||||||
throw new Exception("Cannot find the .CSB file for this directory. Please ensure that the .CSB file is stored in the directory where this directory is.");
|
throw new Exception("Cannot find the .CSB file for this directory. Please ensure that the .CSB file is stored in the directory where this directory is.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CriCpkArchive cpkArchive = new CriCpkArchive();
|
||||||
|
|
||||||
CriTable csbFile = new CriTable();
|
CriTable csbFile = new CriTable();
|
||||||
csbFile.Load(csbPath);
|
csbFile.Load(csbPath);
|
||||||
|
|
||||||
CriRow soundElementRow = csbFile.Rows.Single(row => (string)row["name"] == "SOUND_ELEMENT");
|
CriRow soundElementRow = csbFile.Rows.First(row => (string)row["name"] == "SOUND_ELEMENT");
|
||||||
|
|
||||||
CriTable soundElementTable = new CriTable();
|
CriTable soundElementTable = new CriTable();
|
||||||
soundElementTable.Load((byte[])soundElementRow["utf"]);
|
soundElementTable.Load((byte[])soundElementRow["utf"]);
|
||||||
|
|
||||||
|
List<FileInfo> junks = new List<FileInfo>();
|
||||||
|
|
||||||
foreach (CriRow sdlRow in soundElementTable.Rows)
|
foreach (CriRow sdlRow in soundElementTable.Rows)
|
||||||
{
|
{
|
||||||
string sdlName = (string)sdlRow["name"];
|
string sdlName = (string)sdlRow["name"];
|
||||||
@ -105,37 +151,62 @@ namespace CsbEditor
|
|||||||
throw new Exception($"Cannot find sound element directory for replacement.\nPath attempt: {sdlDirectory.FullName}");
|
throw new Exception($"Cannot find sound element directory for replacement.\nPath attempt: {sdlDirectory.FullName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool streaming = (byte)sdlRow["stmflg"] != 0;
|
||||||
uint sampleRate = (uint)sdlRow["sfreq"];
|
uint sampleRate = (uint)sdlRow["sfreq"];
|
||||||
byte numberChannels = (byte)sdlRow["nch"];
|
byte numberChannels = (byte)sdlRow["nch"];
|
||||||
|
|
||||||
Console.WriteLine("Adding {0}...", sdlName);
|
Console.WriteLine("Adding {0}...", sdlName);
|
||||||
|
|
||||||
using (MemoryStream memoryStream = new MemoryStream())
|
CriAaxArchive aaxArchive = new CriAaxArchive();
|
||||||
using (CriTableWriter writer = CriTableWriter.Create(memoryStream, CriTableWriterSettings.AdxSettings))
|
foreach (FileInfo file in sdlDirectory.GetFiles("*.adx"))
|
||||||
{
|
{
|
||||||
writer.WriteStartTable("AAX");
|
CriAaxEntry entry = new CriAaxEntry();
|
||||||
|
if (file.Name.ToLower(CultureInfo.GetCultureInfo("en-US")) == "intro.adx")
|
||||||
writer.WriteField("data", typeof(byte[]));
|
|
||||||
writer.WriteField("lpflg", typeof(byte));
|
|
||||||
|
|
||||||
foreach (FileInfo audioFile in sdlDirectory.GetFiles("*.adx"))
|
|
||||||
{
|
{
|
||||||
// In Turkish, lowercase I is ı so you get the idea
|
entry.Flag = CriAaxEntryFlag.Intro;
|
||||||
if (audioFile.Name.ToLower(CultureInfo.GetCultureInfo("en-US")) == "intro.adx")
|
entry.FilePath = file;
|
||||||
{
|
aaxArchive.Add(entry);
|
||||||
ReadAdx(audioFile, out sampleRate, out numberChannels);
|
|
||||||
writer.WriteRow(true, audioFile, (byte)0);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (audioFile.Name.ToLower() == "loop.adx")
|
ReadAdx(file, out sampleRate, out numberChannels);
|
||||||
{
|
|
||||||
ReadAdx(audioFile, out sampleRate, out numberChannels);
|
|
||||||
writer.WriteRow(true, audioFile, (byte)1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteEndTable();
|
else if (file.Name.ToLower(CultureInfo.GetCultureInfo("en-US")) == "loop.adx")
|
||||||
sdlRow["data"] = memoryStream.ToArray();
|
{
|
||||||
|
entry.Flag = CriAaxEntryFlag.Loop;
|
||||||
|
entry.FilePath = file;
|
||||||
|
aaxArchive.Add(entry);
|
||||||
|
|
||||||
|
ReadAdx(file, out sampleRate, out numberChannels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streaming)
|
||||||
|
{
|
||||||
|
CriCpkEntry entry = new CriCpkEntry();
|
||||||
|
|
||||||
|
int lastSlash = sdlName.LastIndexOf('/');
|
||||||
|
if (lastSlash != -1)
|
||||||
|
{
|
||||||
|
entry.Name = sdlName.Substring(lastSlash + 1);
|
||||||
|
entry.DirectoryName = sdlName.Substring(0, lastSlash);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry.Name = sdlName;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Id = (uint)cpkArchive.Count;
|
||||||
|
entry.FilePath = new FileInfo(Path.GetTempFileName());
|
||||||
|
junks.Add(entry.FilePath);
|
||||||
|
|
||||||
|
cpkArchive.Add(entry);
|
||||||
|
aaxArchive.Save(entry.FilePath.FullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sdlRow["data"] = aaxArchive.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
sdlRow["sfreq"] = sampleRate;
|
sdlRow["sfreq"] = sampleRate;
|
||||||
@ -147,13 +218,25 @@ namespace CsbEditor
|
|||||||
|
|
||||||
csbFile.WriterSettings = CriTableWriterSettings.AdxSettings;
|
csbFile.WriterSettings = CriTableWriterSettings.AdxSettings;
|
||||||
csbFile.Save(args[0] + ".csb");
|
csbFile.Save(args[0] + ".csb");
|
||||||
|
|
||||||
|
if (cpkArchive.Count > 0)
|
||||||
|
{
|
||||||
|
cpkArchive.Save(args[0] + ".cpk");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (FileInfo junk in junks)
|
||||||
|
{
|
||||||
|
junk.Delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#if !DEBUG
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
MessageBox.Show($"{exception.Message}", "CSB Editor", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show($"{exception.Message}", "CSB Editor", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReadAdx(FileInfo fileInfo, out uint sampleRate, out byte numberChannels)
|
static void ReadAdx(FileInfo fileInfo, out uint sampleRate, out byte numberChannels)
|
||||||
|
@ -69,13 +69,14 @@ namespace CsbEditor.Properties {
|
|||||||
///The directory will have the same name as the .CSB file, but without
|
///The directory will have the same name as the .CSB file, but without
|
||||||
///its extension.
|
///its extension.
|
||||||
///
|
///
|
||||||
|
///Also the .CPK file will be extracted too, if it exists.
|
||||||
|
///
|
||||||
///In the deepest directories, you will see .ADX files, named "Intro.adx"
|
///In the deepest directories, you will see .ADX files, named "Intro.adx"
|
||||||
///or "Loop.adx". The ADX files are literally what the names say. You can
|
///or "Loop.adx". The ADX files are literally what the names say. You can
|
||||||
///add/delete/modify them freely.
|
///add/delete/modify them freely.
|
||||||
///
|
///
|
||||||
///The sample rate and channel count information will be automatically
|
///The sample rate and channel count information will be automatically
|
||||||
///updated in the CSB file if you use an ADX file with different sample
|
///updated in the CSB [rest of string was truncated]";.
|
||||||
///rate or [rest of string was truncated]";.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string Description {
|
internal static string Description {
|
||||||
get {
|
get {
|
||||||
|
@ -126,6 +126,8 @@ Drag and drop a .CSB file to unpack its contents to a directory.
|
|||||||
The directory will have the same name as the .CSB file, but without
|
The directory will have the same name as the .CSB file, but without
|
||||||
its extension.
|
its extension.
|
||||||
|
|
||||||
|
Also the .CPK file will be extracted too, if it exists.
|
||||||
|
|
||||||
In the deepest directories, you will see .ADX files, named "Intro.adx"
|
In the deepest directories, you will see .ADX files, named "Intro.adx"
|
||||||
or "Loop.adx". The ADX files are literally what the names say. You can
|
or "Loop.adx". The ADX files are literally what the names say. You can
|
||||||
add/delete/modify them freely.
|
add/delete/modify them freely.
|
||||||
|
@ -9,7 +9,6 @@ using System.Collections;
|
|||||||
|
|
||||||
using SonicAudioLib;
|
using SonicAudioLib;
|
||||||
using SonicAudioLib.Archive;
|
using SonicAudioLib.Archive;
|
||||||
using SonicAudioLib.Collections;
|
|
||||||
using SonicAudioLib.IO;
|
using SonicAudioLib.IO;
|
||||||
using SonicAudioLib.CriMw;
|
using SonicAudioLib.CriMw;
|
||||||
|
|
||||||
@ -25,7 +24,7 @@ namespace SonicAudioCmd
|
|||||||
{
|
{
|
||||||
CriCpkArchive archive = new CriCpkArchive();
|
CriCpkArchive archive = new CriCpkArchive();
|
||||||
archive.Load(args[0]);
|
archive.Load(args[0]);
|
||||||
archive.Print();
|
//archive.Print();
|
||||||
|
|
||||||
foreach (CriCpkEntry entry in archive)
|
foreach (CriCpkEntry entry in archive)
|
||||||
{
|
{
|
||||||
@ -46,7 +45,7 @@ namespace SonicAudioCmd
|
|||||||
{
|
{
|
||||||
CriCpkArchive archive = new CriCpkArchive();
|
CriCpkArchive archive = new CriCpkArchive();
|
||||||
archive.Align = 16;
|
archive.Align = 16;
|
||||||
archive.Mode = CriCpkMode.FileName;
|
archive.Mode = CriCpkMode.Id;
|
||||||
|
|
||||||
uint id = 0;
|
uint id = 0;
|
||||||
foreach (string file in Directory.GetFiles(args[0], "*", SearchOption.AllDirectories))
|
foreach (string file in Directory.GetFiles(args[0], "*", SearchOption.AllDirectories))
|
||||||
|
74
Source/SonicAudioLib/Archive/CriAaxArchive.cs
Normal file
74
Source/SonicAudioLib/Archive/CriAaxArchive.cs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,13 +11,13 @@ namespace SonicAudioLib.Archive
|
|||||||
{
|
{
|
||||||
public class CriAfs2Entry : EntryBase
|
public class CriAfs2Entry : EntryBase
|
||||||
{
|
{
|
||||||
public ushort CueIndex { get; set; }
|
public ushort Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CriAfs2Archive : ArchiveBase<CriAfs2Entry>
|
public class CriAfs2Archive : ArchiveBase<CriAfs2Entry>
|
||||||
{
|
{
|
||||||
public uint Align { get; set; }
|
public uint Align { get; set; }
|
||||||
public uint CueIndexFieldLength { get; set; }
|
public uint IdFieldLength { get; set; }
|
||||||
public uint PositionFieldLength { get; set; }
|
public uint PositionFieldLength { get; set; }
|
||||||
|
|
||||||
public override void Read(Stream source)
|
public override void Read(Stream source)
|
||||||
@ -35,7 +35,7 @@ namespace SonicAudioLib.Archive
|
|||||||
throw new Exception($"Invalid AFS2 type ({type}). Please report the error with the AWB file.");
|
throw new Exception($"Invalid AFS2 type ({type}). Please report the error with the AWB file.");
|
||||||
}
|
}
|
||||||
|
|
||||||
CueIndexFieldLength = (information & 0x00FF0000) >> 16;
|
IdFieldLength = (information & 0x00FF0000) >> 16;
|
||||||
PositionFieldLength = (information & 0x0000FF00) >> 8;
|
PositionFieldLength = (information & 0x0000FF00) >> 8;
|
||||||
|
|
||||||
ushort entryCount = (ushort)EndianStream.ReadUInt32(source);
|
ushort entryCount = (ushort)EndianStream.ReadUInt32(source);
|
||||||
@ -46,20 +46,20 @@ namespace SonicAudioLib.Archive
|
|||||||
{
|
{
|
||||||
CriAfs2Entry afs2Entry = new CriAfs2Entry();
|
CriAfs2Entry afs2Entry = new CriAfs2Entry();
|
||||||
|
|
||||||
long cueIndexPosition = 16 + (i * CueIndexFieldLength);
|
long idPosition = 16 + (i * IdFieldLength);
|
||||||
source.Seek(cueIndexPosition, SeekOrigin.Begin);
|
source.Seek(idPosition, SeekOrigin.Begin);
|
||||||
|
|
||||||
switch (CueIndexFieldLength)
|
switch (IdFieldLength)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
afs2Entry.CueIndex = EndianStream.ReadUInt16(source);
|
afs2Entry.Id = EndianStream.ReadUInt16(source);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Exception($"Unknown CueIndexFieldLength ({CueIndexFieldLength}). Please report the error with the AWB file.");
|
throw new Exception($"Unknown CueIndexFieldLength ({IdFieldLength}). Please report the error with the AWB file.");
|
||||||
}
|
}
|
||||||
|
|
||||||
long positionPosition = 16 + (entryCount * CueIndexFieldLength) + (i * PositionFieldLength);
|
long positionPosition = 16 + (entryCount * IdFieldLength) + (i * PositionFieldLength);
|
||||||
source.Seek(positionPosition, SeekOrigin.Begin);
|
source.Seek(positionPosition, SeekOrigin.Begin);
|
||||||
|
|
||||||
switch (PositionFieldLength)
|
switch (PositionFieldLength)
|
||||||
@ -81,10 +81,7 @@ namespace SonicAudioLib.Archive
|
|||||||
previousEntry.Length = afs2Entry.Position - previousEntry.Position;
|
previousEntry.Length = afs2Entry.Position - previousEntry.Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((afs2Entry.Position % Align) != 0)
|
afs2Entry.Position = Methods.Align(afs2Entry.Position, Align);
|
||||||
{
|
|
||||||
afs2Entry.Position++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == entryCount - 1)
|
if (i == entryCount - 1)
|
||||||
{
|
{
|
||||||
@ -107,32 +104,33 @@ namespace SonicAudioLib.Archive
|
|||||||
|
|
||||||
public override void Write(Stream destination)
|
public override void Write(Stream destination)
|
||||||
{
|
{
|
||||||
uint headerLength = (uint)(16 + (entries.Count * CueIndexFieldLength) + (entries.Count * PositionFieldLength) + PositionFieldLength);
|
uint headerLength = (uint)(16 + (entries.Count * IdFieldLength) + (entries.Count * PositionFieldLength) + PositionFieldLength);
|
||||||
|
|
||||||
EndianStream.WriteCString(destination, "AFS2", 4);
|
EndianStream.WriteCString(destination, "AFS2", 4);
|
||||||
EndianStream.WriteUInt32(destination, 1 | (CueIndexFieldLength << 16) | (PositionFieldLength << 8));
|
EndianStream.WriteUInt32(destination, 1 | (IdFieldLength << 16) | (PositionFieldLength << 8));
|
||||||
EndianStream.WriteUInt32(destination, (ushort)entries.Count);
|
EndianStream.WriteUInt32(destination, (ushort)entries.Count);
|
||||||
EndianStream.WriteUInt32(destination, 1);
|
EndianStream.WriteUInt32(destination, Align);
|
||||||
|
|
||||||
// FIXME: Alignment support
|
VldPool vldPool = new VldPool(Align, headerLength);
|
||||||
VldPool vldPool = new VldPool(1);
|
|
||||||
|
|
||||||
foreach (CriAfs2Entry afs2Entry in entries)
|
var orderedEntries = entries.OrderBy(entry => entry.Id);
|
||||||
|
foreach (CriAfs2Entry afs2Entry in orderedEntries)
|
||||||
{
|
{
|
||||||
switch (CueIndexFieldLength)
|
switch (IdFieldLength)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
EndianStream.WriteUInt16(destination, (ushort)afs2Entry.CueIndex);
|
EndianStream.WriteUInt16(destination, (ushort)afs2Entry.Id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Exception($"Unknown CueIndexFieldLength ({CueIndexFieldLength}). Please set a valid length.");
|
throw new Exception($"Unknown CueIndexFieldLength ({IdFieldLength}). Please set a valid length.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (CriAfs2Entry afs2Entry in entries)
|
foreach (CriAfs2Entry afs2Entry in orderedEntries)
|
||||||
{
|
{
|
||||||
uint entryPosition = (uint)(headerLength + vldPool.Put(afs2Entry.FilePath));
|
uint entryPosition = (uint)vldPool.Length;
|
||||||
|
vldPool.Put(afs2Entry.FilePath);
|
||||||
|
|
||||||
switch (PositionFieldLength)
|
switch (PositionFieldLength)
|
||||||
{
|
{
|
||||||
@ -151,43 +149,21 @@ namespace SonicAudioLib.Archive
|
|||||||
afs2Entry.Position = entryPosition;
|
afs2Entry.Position = entryPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
EndianStream.WriteUInt32(destination, (uint)(headerLength + vldPool.Length));
|
EndianStream.WriteUInt32(destination, (uint)vldPool.Length);
|
||||||
|
|
||||||
vldPool.Write(destination);
|
vldPool.Write(destination);
|
||||||
vldPool.Clear();
|
vldPool.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CriAfs2Entry GetByCueIndex(uint cueIndex)
|
public CriAfs2Entry GetById(uint cueIndex)
|
||||||
{
|
{
|
||||||
return entries.Single(e => e.CueIndex == cueIndex);
|
return entries.FirstOrDefault(e => (e.Id == cueIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override long CalculateLength()
|
|
||||||
{
|
|
||||||
long length = 16 + (entries.Count * CueIndexFieldLength) + (entries.Count * PositionFieldLength) + PositionFieldLength;
|
|
||||||
|
|
||||||
foreach (CriAfs2Entry afs2Entry in entries)
|
|
||||||
{
|
|
||||||
while ((length % Align) != 0)
|
|
||||||
{
|
|
||||||
length++;
|
|
||||||
}
|
|
||||||
|
|
||||||
length += afs2Entry.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Order()
|
|
||||||
{
|
|
||||||
entries = entries.OrderBy(entry => entry.CueIndex).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CriAfs2Archive()
|
public CriAfs2Archive()
|
||||||
{
|
{
|
||||||
Align = 32;
|
Align = 32;
|
||||||
CueIndexFieldLength = 2;
|
IdFieldLength = 2;
|
||||||
PositionFieldLength = 4;
|
PositionFieldLength = 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,10 +145,7 @@ namespace SonicAudioLib.Archive
|
|||||||
entry.Position += tocPosition;
|
entry.Position += tocPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((entry.Position % align) != 0)
|
entry.Position = Methods.Align(entry.Position, align);
|
||||||
{
|
|
||||||
entry.Position++;
|
|
||||||
}
|
|
||||||
|
|
||||||
etocReader.MoveToRow(tocReader.CurrentRow);
|
etocReader.MoveToRow(tocReader.CurrentRow);
|
||||||
entry.UpdateDateTime = DateTimeFromCpkDateTime(etocReader.GetUInt64("UpdateDateTime"));
|
entry.UpdateDateTime = DateTimeFromCpkDateTime(etocReader.GetUInt64("UpdateDateTime"));
|
||||||
@ -171,8 +168,6 @@ namespace SonicAudioLib.Archive
|
|||||||
|
|
||||||
else if (mode == CriCpkMode.Id)
|
else if (mode == CriCpkMode.Id)
|
||||||
{
|
{
|
||||||
long currentPosition = contentPosition;
|
|
||||||
|
|
||||||
using (CriTableReader itocReader = CriCpkSection.Open(source, itocPosition))
|
using (CriTableReader itocReader = CriCpkSection.Open(source, itocPosition))
|
||||||
{
|
{
|
||||||
while (itocReader.Read())
|
while (itocReader.Read())
|
||||||
@ -192,15 +187,7 @@ namespace SonicAudioLib.Archive
|
|||||||
entry.IsCompressed = true;
|
entry.IsCompressed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((currentPosition % align) != 0)
|
|
||||||
{
|
|
||||||
currentPosition++;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.Position = currentPosition;
|
|
||||||
entries.Add(entry);
|
entries.Add(entry);
|
||||||
|
|
||||||
currentPosition += entry.Length;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,20 +207,21 @@ namespace SonicAudioLib.Archive
|
|||||||
entry.IsCompressed = true;
|
entry.IsCompressed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((currentPosition % align) != 0)
|
|
||||||
{
|
|
||||||
currentPosition++;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.Position = currentPosition;
|
|
||||||
entries.Add(entry);
|
entries.Add(entry);
|
||||||
|
|
||||||
currentPosition += entry.Length;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long entryPosition = contentPosition;
|
||||||
|
foreach (CriCpkEntry entry in entries.OrderBy(entry => entry.Id))
|
||||||
|
{
|
||||||
|
entryPosition = Methods.Align(entryPosition, align);
|
||||||
|
|
||||||
|
entry.Position = entryPosition;
|
||||||
|
entryPosition += entry.Length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -247,8 +235,7 @@ namespace SonicAudioLib.Archive
|
|||||||
|
|
||||||
public override void Write(Stream destination)
|
public override void Write(Stream destination)
|
||||||
{
|
{
|
||||||
// FIXME: Alignment support (ugh same thing for AFS2 archives)
|
VldPool vldPool = new VldPool(Align, 2048);
|
||||||
VldPool vldPool = new VldPool();
|
|
||||||
|
|
||||||
using (CriCpkSection cpkSection = new CriCpkSection(destination, "CPK "))
|
using (CriCpkSection cpkSection = new CriCpkSection(destination, "CPK "))
|
||||||
{
|
{
|
||||||
@ -358,8 +345,20 @@ namespace SonicAudioLib.Archive
|
|||||||
|
|
||||||
foreach (CriCpkEntry entry in entries)
|
foreach (CriCpkEntry entry in entries)
|
||||||
{
|
{
|
||||||
tocSection.Writer.WriteRow(true, entry.DirectoryName, entry.Name, Convert.ToUInt32(entry.Length), Convert.ToUInt32(entry.Length), Convert.ToUInt64(vldPool.Put(entry.FilePath)), entry.Id, entry.Comment);
|
tocSection.Writer.WriteRow(true,
|
||||||
etocSection.Writer.WriteRow(true, CpkDateTimeFromDateTime(entry.UpdateDateTime), entry.DirectoryName);
|
(entry.DirectoryName).Replace('\\', '/'),
|
||||||
|
entry.Name,
|
||||||
|
(uint)entry.Length,
|
||||||
|
(uint)entry.Length,
|
||||||
|
(ulong)(vldPool.Length - 2048),
|
||||||
|
entry.Id,
|
||||||
|
entry.Comment);
|
||||||
|
|
||||||
|
etocSection.Writer.WriteRow(true,
|
||||||
|
CpkDateTimeFromDateTime(entry.UpdateDateTime),
|
||||||
|
entry.DirectoryName);
|
||||||
|
|
||||||
|
vldPool.Put(entry.FilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
tocSection.Writer.WriteEndTable();
|
tocSection.Writer.WriteEndTable();
|
||||||
@ -379,7 +378,9 @@ namespace SonicAudioLib.Archive
|
|||||||
|
|
||||||
foreach (CriCpkEntry entry in entries)
|
foreach (CriCpkEntry entry in entries)
|
||||||
{
|
{
|
||||||
itocSection.Writer.WriteRow(true, Convert.ToInt32(entry.Id), entries.IndexOf(entry));
|
itocSection.Writer.WriteRow(true,
|
||||||
|
(int)entry.Id,
|
||||||
|
entries.IndexOf(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
itocSection.Writer.WriteEndTable();
|
itocSection.Writer.WriteEndTable();
|
||||||
@ -403,8 +404,6 @@ namespace SonicAudioLib.Archive
|
|||||||
List<CriCpkEntry> filesL = entries.Where(entry => entry.Length < ushort.MaxValue).ToList();
|
List<CriCpkEntry> filesL = entries.Where(entry => entry.Length < ushort.MaxValue).ToList();
|
||||||
List<CriCpkEntry> filesH = entries.Where(entry => entry.Length > ushort.MaxValue).ToList();
|
List<CriCpkEntry> filesH = entries.Where(entry => entry.Length > ushort.MaxValue).ToList();
|
||||||
|
|
||||||
ushort id = 0;
|
|
||||||
|
|
||||||
itocSection.Writer.WriteStartRow();
|
itocSection.Writer.WriteStartRow();
|
||||||
|
|
||||||
using (MemoryStream dataMemoryStream = new MemoryStream())
|
using (MemoryStream dataMemoryStream = new MemoryStream())
|
||||||
@ -418,9 +417,10 @@ namespace SonicAudioLib.Archive
|
|||||||
|
|
||||||
foreach (CriCpkEntry entry in filesL)
|
foreach (CriCpkEntry entry in filesL)
|
||||||
{
|
{
|
||||||
dataWriter.WriteRow(true, id, Convert.ToUInt16(entry.Length), Convert.ToUInt16(entry.Length));
|
dataWriter.WriteRow(true,
|
||||||
vldPool.Put(entry.FilePath);
|
(ushort)entry.Id,
|
||||||
id++;
|
(ushort)entry.Length,
|
||||||
|
(ushort)entry.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
dataWriter.WriteEndTable();
|
dataWriter.WriteEndTable();
|
||||||
@ -439,9 +439,10 @@ namespace SonicAudioLib.Archive
|
|||||||
|
|
||||||
foreach (CriCpkEntry entry in filesH)
|
foreach (CriCpkEntry entry in filesH)
|
||||||
{
|
{
|
||||||
dataWriter.WriteRow(true, id, Convert.ToUInt32(entry.Length), Convert.ToUInt32(entry.Length));
|
dataWriter.WriteRow(true,
|
||||||
vldPool.Put(entry.FilePath);
|
(ushort)entry.Id,
|
||||||
id++;
|
(uint)entry.Length,
|
||||||
|
(uint)entry.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
dataWriter.WriteEndTable();
|
dataWriter.WriteEndTable();
|
||||||
@ -454,6 +455,11 @@ namespace SonicAudioLib.Archive
|
|||||||
itocSection.Writer.WriteEndRow();
|
itocSection.Writer.WriteEndRow();
|
||||||
itocSection.Writer.WriteEndTable();
|
itocSection.Writer.WriteEndTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (CriCpkEntry entry in entries.OrderBy(entry => entry.Id))
|
||||||
|
{
|
||||||
|
vldPool.Put(entry.FilePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -464,63 +470,74 @@ namespace SonicAudioLib.Archive
|
|||||||
cpkSection.Writer.WriteStartRow();
|
cpkSection.Writer.WriteStartRow();
|
||||||
cpkSection.Writer.WriteValue("UpdateDateTime", CpkDateTimeFromDateTime(DateTime.Now));
|
cpkSection.Writer.WriteValue("UpdateDateTime", CpkDateTimeFromDateTime(DateTime.Now));
|
||||||
|
|
||||||
cpkSection.Writer.WriteValue("ContentOffset", Convert.ToUInt64(2048));
|
cpkSection.Writer.WriteValue("ContentOffset", (ulong)2048);
|
||||||
cpkSection.Writer.WriteValue("ContentSize", Convert.ToUInt64(vldPool.Length));
|
cpkSection.Writer.WriteValue("ContentSize", (ulong)(vldPool.Length - 2048));
|
||||||
|
|
||||||
if (tocMemoryStream != null)
|
if (tocMemoryStream != null)
|
||||||
{
|
{
|
||||||
cpkSection.Writer.WriteValue("TocOffset", Convert.ToUInt64(2048 + vldPool.Put(tocMemoryStream)));
|
cpkSection.Writer.WriteValue("TocOffset", (ulong)vldPool.Put(tocMemoryStream));
|
||||||
cpkSection.Writer.WriteValue("TocSize", Convert.ToUInt64(tocMemoryStream.Length));
|
cpkSection.Writer.WriteValue("TocSize", (ulong)tocMemoryStream.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itocMemoryStream != null)
|
if (itocMemoryStream != null)
|
||||||
{
|
{
|
||||||
cpkSection.Writer.WriteValue("ItocOffset", Convert.ToUInt64(2048 + vldPool.Put(itocMemoryStream)));
|
cpkSection.Writer.WriteValue("ItocOffset", (ulong)vldPool.Put(itocMemoryStream));
|
||||||
cpkSection.Writer.WriteValue("ItocSize", Convert.ToUInt64(itocMemoryStream.Length));
|
cpkSection.Writer.WriteValue("ItocSize", (ulong)itocMemoryStream.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (etocMemoryStream != null)
|
if (etocMemoryStream != null)
|
||||||
{
|
{
|
||||||
cpkSection.Writer.WriteValue("EtocOffset", Convert.ToUInt64(2048 + vldPool.Put(etocMemoryStream)));
|
cpkSection.Writer.WriteValue("EtocOffset", (ulong)vldPool.Put(etocMemoryStream));
|
||||||
cpkSection.Writer.WriteValue("EtocSize", Convert.ToUInt64(etocMemoryStream.Length));
|
cpkSection.Writer.WriteValue("EtocSize", (ulong)etocMemoryStream.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint totalDataSize = 0;
|
long totalDataSize = 0;
|
||||||
foreach (CriCpkEntry entry in entries)
|
foreach (CriCpkEntry entry in entries)
|
||||||
{
|
{
|
||||||
totalDataSize += (uint)entry.Length;
|
totalDataSize += entry.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
cpkSection.Writer.WriteValue("EnabledPackedSize", totalDataSize);
|
cpkSection.Writer.WriteValue("EnabledPackedSize", totalDataSize);
|
||||||
cpkSection.Writer.WriteValue("EnabledDataSize", totalDataSize);
|
cpkSection.Writer.WriteValue("EnabledDataSize", totalDataSize);
|
||||||
|
|
||||||
cpkSection.Writer.WriteValue("Files", Convert.ToUInt32(entries.Count));
|
cpkSection.Writer.WriteValue("Files", (uint)entries.Count);
|
||||||
|
|
||||||
cpkSection.Writer.WriteValue("Version", (ushort)7);
|
cpkSection.Writer.WriteValue("Version", (ushort)7);
|
||||||
cpkSection.Writer.WriteValue("Revision", (ushort)2);
|
cpkSection.Writer.WriteValue("Revision", (ushort)2);
|
||||||
cpkSection.Writer.WriteValue("Align", (ushort)1);
|
cpkSection.Writer.WriteValue("Align", Align);
|
||||||
cpkSection.Writer.WriteValue("Sorted", (ushort)1);
|
cpkSection.Writer.WriteValue("Sorted", (ushort)1);
|
||||||
|
|
||||||
cpkSection.Writer.WriteValue("CpkMode", (uint)mode);
|
cpkSection.Writer.WriteValue("CpkMode", (uint)mode);
|
||||||
cpkSection.Writer.WriteValue("Tvers", "SonicAudioLib");
|
cpkSection.Writer.WriteValue("Tvers", "SONICAUDIOLIB, DLL1.0.0.0");
|
||||||
cpkSection.Writer.WriteValue("Comment", Comment);
|
cpkSection.Writer.WriteValue("Comment", Comment);
|
||||||
|
|
||||||
cpkSection.Writer.WriteValue("FileSize", Convert.ToUInt64(2048 + (ulong)vldPool.Length));
|
cpkSection.Writer.WriteValue("FileSize", (ulong)vldPool.Length);
|
||||||
|
|
||||||
cpkSection.Writer.WriteEndRow();
|
cpkSection.Writer.WriteEndRow();
|
||||||
cpkSection.Writer.WriteEndTable();
|
cpkSection.Writer.WriteEndTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((destination.Position % 2042) != 0)
|
EndianStream.Pad(destination, 2042);
|
||||||
{
|
|
||||||
destination.WriteByte(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
EndianStream.WriteCString(destination, "(c)CRI", 6);
|
EndianStream.WriteCString(destination, "(c)CRI", 6);
|
||||||
|
|
||||||
vldPool.Write(destination);
|
vldPool.Write(destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CriCpkEntry GetById(uint id)
|
||||||
|
{
|
||||||
|
return entries.FirstOrDefault(entry => (entry.Id == id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CriCpkEntry GetByName(string name)
|
||||||
|
{
|
||||||
|
return entries.FirstOrDefault(entry => (entry.Name == name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CriCpkEntry GetByPath(string path)
|
||||||
|
{
|
||||||
|
return entries.FirstOrDefault(entry => ((entry.DirectoryName + "/" + entry.Name) == path));
|
||||||
|
}
|
||||||
|
|
||||||
private DateTime DateTimeFromCpkDateTime(ulong dateTime)
|
private DateTime DateTimeFromCpkDateTime(ulong dateTime)
|
||||||
{
|
{
|
||||||
if (dateTime == 0)
|
if (dateTime == 0)
|
||||||
|
@ -11,7 +11,7 @@ namespace SonicAudioLib.Archive
|
|||||||
{
|
{
|
||||||
public class HeroesPacEntry : EntryBase
|
public class HeroesPacEntry : EntryBase
|
||||||
{
|
{
|
||||||
public uint GlobalIndex { get; set; }
|
public uint Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HeroesPacArchive : ArchiveBase<HeroesPacEntry>
|
public class HeroesPacArchive : ArchiveBase<HeroesPacEntry>
|
||||||
@ -30,7 +30,7 @@ namespace SonicAudioLib.Archive
|
|||||||
{
|
{
|
||||||
HeroesPacEntry pacEntry = new HeroesPacEntry();
|
HeroesPacEntry pacEntry = new HeroesPacEntry();
|
||||||
|
|
||||||
pacEntry.GlobalIndex = EndianStream.ReadUInt32(source);
|
pacEntry.Id = EndianStream.ReadUInt32(source);
|
||||||
pacEntry.Position = vldPoolPosition + EndianStream.ReadUInt32(source);
|
pacEntry.Position = vldPoolPosition + EndianStream.ReadUInt32(source);
|
||||||
|
|
||||||
if (previousEntry != null)
|
if (previousEntry != null)
|
||||||
@ -74,7 +74,7 @@ namespace SonicAudioLib.Archive
|
|||||||
{
|
{
|
||||||
uint entryPosition = (uint)vldPool.Put(pacEntry.FilePath);
|
uint entryPosition = (uint)vldPool.Put(pacEntry.FilePath);
|
||||||
|
|
||||||
EndianStream.WriteUInt32(destination, pacEntry.GlobalIndex);
|
EndianStream.WriteUInt32(destination, pacEntry.Id);
|
||||||
EndianStream.WriteUInt32(destination, entryPosition);
|
EndianStream.WriteUInt32(destination, entryPosition);
|
||||||
|
|
||||||
while ((destination.Position % 16) != 0)
|
while ((destination.Position % 16) != 0)
|
||||||
|
@ -1,175 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace SonicAudioLib.Collections
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a key/value pair for an <see cref="OrderedDictionary{TKey, TValue}"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey"></typeparam>
|
|
||||||
/// <typeparam name="TValue"></typeparam>
|
|
||||||
public class KeyValuePair<TKey, TValue>
|
|
||||||
{
|
|
||||||
public TKey Key { get; set; }
|
|
||||||
public TValue Value { get; set; }
|
|
||||||
|
|
||||||
public KeyValuePair(TKey key, TValue value)
|
|
||||||
{
|
|
||||||
Key = key;
|
|
||||||
Value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a key/value pair collection that is accessable by its key or index.
|
|
||||||
/// </summary>
|
|
||||||
public class OrderedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
|
|
||||||
{
|
|
||||||
private List<KeyValuePair<TKey, TValue>> items = new List<KeyValuePair<TKey, TValue>>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the count of key/value pairs.
|
|
||||||
/// </summary>
|
|
||||||
public int Count
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return items.Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value at the specified index.
|
|
||||||
/// </summary>
|
|
||||||
public TValue this[int index]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return items[index].Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
items[index].Value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value by the specified key.
|
|
||||||
/// </summary>
|
|
||||||
public TValue this[TKey key]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return items.Single(k => (k.Key).Equals(key)).Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
items.Single(k => (k.Key).Equals(key)).Value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the collection contains the specified key.
|
|
||||||
/// </summary>
|
|
||||||
public bool ContainsKey(TKey key)
|
|
||||||
{
|
|
||||||
return items.Any(k => (k.Key).Equals(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a key/value pair to end of the collection.
|
|
||||||
/// </summary>
|
|
||||||
public void Add(TKey key, TValue value)
|
|
||||||
{
|
|
||||||
items.Add(new KeyValuePair<TKey, TValue>(key, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the key/value pair by its key.
|
|
||||||
/// </summary>
|
|
||||||
public bool Remove(TKey key)
|
|
||||||
{
|
|
||||||
return items.Remove(items.Single(k => (k.Key).Equals(key)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clears all the key/value pairs.
|
|
||||||
/// </summary>
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
items.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the index of the specified key.
|
|
||||||
/// </summary>
|
|
||||||
public int IndexOf(TKey key)
|
|
||||||
{
|
|
||||||
return items.IndexOf(items.Single(k => (k.Key).Equals(key)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Inserts a key/value pair to the specified index.
|
|
||||||
/// </summary>
|
|
||||||
public void Insert(int index, TKey key, TValue value)
|
|
||||||
{
|
|
||||||
items.Insert(index, new KeyValuePair<TKey, TValue>(key, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes key/value pair at the specified index.
|
|
||||||
/// </summary>
|
|
||||||
public void RemoveAt(int index)
|
|
||||||
{
|
|
||||||
items.RemoveAt(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets key at the specified index.
|
|
||||||
/// </summary>
|
|
||||||
public TKey GetKeyByIndex(int index)
|
|
||||||
{
|
|
||||||
return items[index].Key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets value at the specified index.
|
|
||||||
/// </summary>
|
|
||||||
public TValue GetValueByIndex(int index)
|
|
||||||
{
|
|
||||||
return items[index].Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets key at the specified index.
|
|
||||||
/// </summary>
|
|
||||||
public void SetKeyByIndex(int index, TKey key)
|
|
||||||
{
|
|
||||||
items[index].Key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets value at the specified index.
|
|
||||||
/// </summary>
|
|
||||||
public void SetValueByIndex(int index, TValue value)
|
|
||||||
{
|
|
||||||
items[index].Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns an enumerator of key/value pairs.
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
|
||||||
{
|
|
||||||
return items.GetEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
|
||||||
{
|
|
||||||
return items.GetEnumerator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -22,7 +22,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return fields.Single(f => f.FieldName == name);
|
return fields.FirstOrDefault(field => field.FieldName == name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,10 +86,10 @@ namespace SonicAudioLib.CriMw
|
|||||||
{
|
{
|
||||||
fields.Remove(criField);
|
fields.Remove(criField);
|
||||||
|
|
||||||
// Update the objects
|
// Update the rows
|
||||||
foreach (CriRow criRow in parent.Rows)
|
foreach (CriRow criRow in parent.Rows)
|
||||||
{
|
{
|
||||||
criRow.Records.Remove(criField);
|
criRow.Records.RemoveAll(record => record.Field == criField);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,24 +1,31 @@
|
|||||||
using SonicAudioLib.Collections;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace SonicAudioLib.CriMw
|
namespace SonicAudioLib.CriMw
|
||||||
{
|
{
|
||||||
|
internal class CriRowRecord
|
||||||
|
{
|
||||||
|
public CriField Field { get; set; }
|
||||||
|
public object Value { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class CriRow : IEnumerable
|
public class CriRow : IEnumerable
|
||||||
{
|
{
|
||||||
private OrderedDictionary<CriField, object> records = new OrderedDictionary<CriField, object>();
|
private List<CriRowRecord> records = new List<CriRowRecord>();
|
||||||
private CriTable parent;
|
private CriTable parent;
|
||||||
|
|
||||||
public object this[CriField criField]
|
public object this[CriField criField]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return records[criField];
|
return this[records.FindIndex(record => record.Field == criField)];
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
records[criField] = value;
|
this[records.FindIndex(record => record.Field == criField)] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,12 +33,22 @@ namespace SonicAudioLib.CriMw
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return records[index];
|
if (index < 0 || index >= records.Count)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return records[index].Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
records[index] = value;
|
if (index < 0 || index >= records.Count)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
records[index].Value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,12 +56,12 @@ namespace SonicAudioLib.CriMw
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return this[records.Single(k => (k.Key).FieldName == name).Key];
|
return this[records.FindIndex(record => record.Field.FieldName == name)];
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
this[records.Single(k => (k.Key).FieldName == name).Key] = value;
|
this[records.FindIndex(record => record.Field.FieldName == name)] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +78,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal OrderedDictionary<CriField, object> Records
|
internal List<CriRowRecord> Records
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@ -83,7 +100,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
|
|
||||||
for (int i = 0; i < records.Count; i++)
|
for (int i = 0; i < records.Count; i++)
|
||||||
{
|
{
|
||||||
values[i] = records[i];
|
values[i] = records[i].Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
@ -91,9 +108,9 @@ namespace SonicAudioLib.CriMw
|
|||||||
|
|
||||||
public IEnumerator GetEnumerator()
|
public IEnumerator GetEnumerator()
|
||||||
{
|
{
|
||||||
foreach (var keyValPair in records)
|
foreach (var record in records)
|
||||||
{
|
{
|
||||||
yield return keyValPair.Value;
|
yield return record.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield break;
|
yield break;
|
||||||
|
@ -68,7 +68,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
|
|
||||||
foreach (CriField criField in fields)
|
foreach (CriField criField in fields)
|
||||||
{
|
{
|
||||||
criRow.Records.Add(criField, criField.DefaultValue);
|
criRow.Records.Add(new CriRowRecord { Field = criField, Value = criField.DefaultValue });
|
||||||
}
|
}
|
||||||
|
|
||||||
return criRow;
|
return criRow;
|
||||||
@ -115,6 +115,11 @@ namespace SonicAudioLib.CriMw
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (rows.Count == 0)
|
||||||
|
{
|
||||||
|
useDefaultValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (useDefaultValue)
|
if (useDefaultValue)
|
||||||
{
|
{
|
||||||
writer.WriteField(criField.FieldName, criField.FieldType, defaultValue);
|
writer.WriteField(criField.FieldName, criField.FieldType, defaultValue);
|
||||||
@ -136,12 +141,6 @@ namespace SonicAudioLib.CriMw
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override long CalculateLength()
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
return base.CalculateLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CriTable()
|
public CriTable()
|
||||||
{
|
{
|
||||||
fields = new CriFieldCollection(this);
|
fields = new CriFieldCollection(this);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using SonicAudioLib.Collections;
|
using SonicAudioLib.IO;
|
||||||
using SonicAudioLib.IO;
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
{
|
{
|
||||||
public class CriTableReader : IDisposable
|
public class CriTableReader : IDisposable
|
||||||
{
|
{
|
||||||
private OrderedDictionary<string, CriTableField> fields;
|
private List<CriTableField> fields;
|
||||||
private Stream source;
|
private Stream source;
|
||||||
private CriTableHeader header;
|
private CriTableHeader header;
|
||||||
private int rowIndex = -1;
|
private int rowIndex = -1;
|
||||||
@ -142,7 +142,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fields.Add(field.Name, field);
|
fields.Add(field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
|
|
||||||
public Type GetFieldType(string fieldName)
|
public Type GetFieldType(string fieldName)
|
||||||
{
|
{
|
||||||
return CriField.FieldTypes[(byte)fields[fieldName].Flag & 0x0F];
|
return CriField.FieldTypes[(byte)fields[GetFieldIndex(fieldName)].Flag & 0x0F];
|
||||||
}
|
}
|
||||||
|
|
||||||
public object GetFieldValue(int fieldIndex)
|
public object GetFieldValue(int fieldIndex)
|
||||||
@ -168,7 +168,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
|
|
||||||
internal CriFieldFlag GetFieldFlag(string fieldName)
|
internal CriFieldFlag GetFieldFlag(string fieldName)
|
||||||
{
|
{
|
||||||
return fields[fieldName].Flag;
|
return fields[GetFieldIndex(fieldName)].Flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal CriFieldFlag GetFieldFlag(int fieldIndex)
|
internal CriFieldFlag GetFieldFlag(int fieldIndex)
|
||||||
@ -178,7 +178,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
|
|
||||||
public object GetFieldValue(string fieldName)
|
public object GetFieldValue(string fieldName)
|
||||||
{
|
{
|
||||||
return fields[fieldName].Value;
|
return fields[GetFieldIndex(fieldName)].Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CriField GetField(int fieldIndex)
|
public CriField GetField(int fieldIndex)
|
||||||
@ -190,6 +190,11 @@ namespace SonicAudioLib.CriMw
|
|||||||
{
|
{
|
||||||
return new CriField(fieldName, GetFieldType(fieldName), GetFieldValue(fieldName));
|
return new CriField(fieldName, GetFieldType(fieldName), GetFieldValue(fieldName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int GetFieldIndex(string fieldName)
|
||||||
|
{
|
||||||
|
return fields.FindIndex(field => field.Name == fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
private void GoToValue(int fieldIndex)
|
private void GoToValue(int fieldIndex)
|
||||||
{
|
{
|
||||||
@ -295,7 +300,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
|
|
||||||
public object GetValue(string fieldName)
|
public object GetValue(string fieldName)
|
||||||
{
|
{
|
||||||
return GetValue(fields.IndexOf(fieldName));
|
return GetValue(GetFieldIndex(fieldName));
|
||||||
}
|
}
|
||||||
|
|
||||||
public T GetValue<T>(int fieldIndex)
|
public T GetValue<T>(int fieldIndex)
|
||||||
@ -435,7 +440,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
|
|
||||||
public byte[] GetData(string fieldName)
|
public byte[] GetData(string fieldName)
|
||||||
{
|
{
|
||||||
return GetData(fields.IndexOf(fieldName));
|
return GetData(GetFieldIndex(fieldName));
|
||||||
}
|
}
|
||||||
|
|
||||||
public CriTableReader GetCriTableReader(string fieldName)
|
public CriTableReader GetCriTableReader(string fieldName)
|
||||||
@ -470,7 +475,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
|
|
||||||
public uint GetLength(string fieldName)
|
public uint GetLength(string fieldName)
|
||||||
{
|
{
|
||||||
return GetLength(fields.IndexOf(fieldName));
|
return GetLength(GetFieldIndex(fieldName));
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint GetPosition(int fieldIndex)
|
public uint GetPosition(int fieldIndex)
|
||||||
@ -495,7 +500,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
|
|
||||||
public uint GetPosition(string fieldName)
|
public uint GetPosition(string fieldName)
|
||||||
{
|
{
|
||||||
return GetPosition(fields.IndexOf(fieldName));
|
return GetPosition(GetFieldIndex(fieldName));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool GetBoolean(int fieldIndex)
|
public bool GetBoolean(int fieldIndex)
|
||||||
@ -582,12 +587,12 @@ namespace SonicAudioLib.CriMw
|
|||||||
|
|
||||||
private string ReadString()
|
private string ReadString()
|
||||||
{
|
{
|
||||||
int stringPosition = ReadInt32();
|
uint stringPosition = ReadUInt32();
|
||||||
|
|
||||||
long previousPosition = source.Position;
|
long previousPosition = source.Position;
|
||||||
|
|
||||||
source.Position = headerPosition + header.StringPoolPosition + stringPosition;
|
source.Position = headerPosition + header.StringPoolPosition + stringPosition;
|
||||||
string strResult = EndianStream.ReadCString(source, Encoding.Default);
|
string strResult = EndianStream.ReadCString(source, Encoding.GetEncoding("shift-jis"));
|
||||||
source.Position = previousPosition;
|
source.Position = previousPosition;
|
||||||
|
|
||||||
if (strResult == "<NULL>" ||
|
if (strResult == "<NULL>" ||
|
||||||
@ -703,7 +708,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
{
|
{
|
||||||
this.source = source;
|
this.source = source;
|
||||||
header = new CriTableHeader();
|
header = new CriTableHeader();
|
||||||
fields = new OrderedDictionary<string, CriTableField>();
|
fields = new List<CriTableField>();
|
||||||
this.leaveOpen = leaveOpen;
|
this.leaveOpen = leaveOpen;
|
||||||
|
|
||||||
ReadTable();
|
ReadTable();
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using SonicAudioLib.Collections;
|
|
||||||
using SonicAudioLib.IO;
|
using SonicAudioLib.IO;
|
||||||
using SonicAudioLib.Module;
|
using SonicAudioLib.Module;
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CriTableWriterSettings settings;
|
private CriTableWriterSettings settings;
|
||||||
private OrderedDictionary<string, CriTableField> fields;
|
private List<CriTableField> fields;
|
||||||
private Stream destination;
|
private Stream destination;
|
||||||
private CriTableHeader header;
|
private CriTableHeader header;
|
||||||
private VldPool vldPool;
|
private VldPool vldPool;
|
||||||
@ -101,18 +101,12 @@ namespace SonicAudioLib.CriMw
|
|||||||
stringPool.Write(destination);
|
stringPool.Write(destination);
|
||||||
header.StringPoolPosition = (uint)stringPool.Position - headerPosition;
|
header.StringPoolPosition = (uint)stringPool.Position - headerPosition;
|
||||||
|
|
||||||
while ((destination.Position % vldPool.Align) != 0)
|
EndianStream.Pad(destination, vldPool.Align);
|
||||||
{
|
|
||||||
destination.WriteByte(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
vldPool.Write(destination);
|
vldPool.Write(destination);
|
||||||
header.DataPoolPosition = (uint)vldPool.Position - headerPosition;
|
header.DataPoolPosition = (uint)vldPool.Position - headerPosition;
|
||||||
|
|
||||||
while ((destination.Position % vldPool.Align) != 0)
|
EndianStream.Pad(destination, vldPool.Align);
|
||||||
{
|
|
||||||
destination.WriteByte(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
long previousPosition = destination.Position;
|
long previousPosition = destination.Position;
|
||||||
|
|
||||||
@ -183,7 +177,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
WriteValue(defaultValue);
|
WriteValue(defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
fields.Add(fieldName, field);
|
fields.Add(field);
|
||||||
header.NumberOfFields++;
|
header.NumberOfFields++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,7 +208,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
WriteString(field.Name);
|
WriteString(field.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
fields.Add(fieldName, field);
|
fields.Add(field);
|
||||||
header.NumberOfFields++;
|
header.NumberOfFields++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,7 +264,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
|
|
||||||
public void WriteValue(string fieldName, object rowValue)
|
public void WriteValue(string fieldName, object rowValue)
|
||||||
{
|
{
|
||||||
WriteValue(fields.IndexOf(fieldName), rowValue);
|
WriteValue(fields.FindIndex(field => field.Name == fieldName), rowValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GoToValue(int fieldIndex)
|
private void GoToValue(int fieldIndex)
|
||||||
@ -601,7 +595,7 @@ namespace SonicAudioLib.CriMw
|
|||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
|
||||||
header = new CriTableHeader();
|
header = new CriTableHeader();
|
||||||
fields = new OrderedDictionary<string, CriTableField>();
|
fields = new List<CriTableField>();
|
||||||
stringPool = new StringPool(settings.EncodingType);
|
stringPool = new StringPool(settings.EncodingType);
|
||||||
vldPool = new VldPool(settings.Align);
|
vldPool = new VldPool(settings.Align);
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,8 @@ namespace SonicAudioLib.CriMw.Serialization
|
|||||||
// Also ignore the properties that are not supportable (except FileInfo and Stream)
|
// Also ignore the properties that are not supportable (except FileInfo and Stream)
|
||||||
if (propertyInfo.PropertyType != typeof(FileInfo) &&
|
if (propertyInfo.PropertyType != typeof(FileInfo) &&
|
||||||
propertyInfo.PropertyType != typeof(Stream) &&
|
propertyInfo.PropertyType != typeof(Stream) &&
|
||||||
|
propertyInfo.PropertyType != typeof(bool) &&
|
||||||
|
!propertyInfo.PropertyType.IsEnum &&
|
||||||
!CriField.FieldTypes.Contains(propertyInfo.PropertyType))
|
!CriField.FieldTypes.Contains(propertyInfo.PropertyType))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@ -100,19 +102,27 @@ namespace SonicAudioLib.CriMw.Serialization
|
|||||||
string fieldName = propertyInfo.Name;
|
string fieldName = propertyInfo.Name;
|
||||||
Type fieldType = propertyInfo.PropertyType;
|
Type fieldType = propertyInfo.PropertyType;
|
||||||
object defaultValue = null;
|
object defaultValue = null;
|
||||||
|
|
||||||
// Since the invalid types were cleaned, we can assume that those can be FileInfo or Stream
|
if (fieldType == typeof(FileInfo) || fieldType == typeof(Stream))
|
||||||
// so directly change the type to byte[]
|
|
||||||
if (!CriField.FieldTypes.Contains(fieldType))
|
|
||||||
{
|
{
|
||||||
fieldType = typeof(byte[]);
|
fieldType = typeof(byte[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (fieldType == typeof(bool))
|
||||||
|
{
|
||||||
|
fieldType = typeof(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (fieldType.IsEnum)
|
||||||
|
{
|
||||||
|
fieldType = Enum.GetUnderlyingType(propertyInfo.PropertyType);
|
||||||
|
}
|
||||||
|
|
||||||
if (fieldAttribute != null)
|
if (fieldAttribute != null)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(fieldAttribute.FieldName))
|
if (!string.IsNullOrEmpty(fieldAttribute.FieldName))
|
||||||
{
|
{
|
||||||
fieldName = fieldAttribute.FieldName;
|
fieldName = fieldAttribute.FieldName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +155,22 @@ namespace SonicAudioLib.CriMw.Serialization
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (arrayList == null || (arrayList != null && arrayList.Count == 0))
|
||||||
|
{
|
||||||
|
useDefaultValue = true;
|
||||||
|
defaultValue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultValue is bool)
|
||||||
|
{
|
||||||
|
defaultValue = (bool)defaultValue == true ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (defaultValue is Enum)
|
||||||
|
{
|
||||||
|
defaultValue = Convert.ChangeType(defaultValue, fieldType);
|
||||||
|
}
|
||||||
|
|
||||||
if (useDefaultValue)
|
if (useDefaultValue)
|
||||||
{
|
{
|
||||||
tableWriter.WriteField(fieldName, fieldType, defaultValue);
|
tableWriter.WriteField(fieldName, fieldType, defaultValue);
|
||||||
@ -169,9 +195,18 @@ namespace SonicAudioLib.CriMw.Serialization
|
|||||||
foreach (PropertyInfo propertyInfo in sortedProperties.Values)
|
foreach (PropertyInfo propertyInfo in sortedProperties.Values)
|
||||||
{
|
{
|
||||||
object value = propertyInfo.GetValue(obj);
|
object value = propertyInfo.GetValue(obj);
|
||||||
|
|
||||||
Type propertyType = propertyInfo.PropertyType;
|
Type propertyType = propertyInfo.PropertyType;
|
||||||
|
|
||||||
|
if (value is bool)
|
||||||
|
{
|
||||||
|
value = (bool)value == true ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (value is Enum)
|
||||||
|
{
|
||||||
|
value = Convert.ChangeType(value, Enum.GetUnderlyingType(propertyType));
|
||||||
|
}
|
||||||
|
|
||||||
tableWriter.WriteValue(index, value);
|
tableWriter.WriteValue(index, value);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
@ -250,6 +285,16 @@ namespace SonicAudioLib.CriMw.Serialization
|
|||||||
value = ((Substream)value).ToArray();
|
value = ((Substream)value).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (propertyInfo.PropertyType == typeof(bool))
|
||||||
|
{
|
||||||
|
value = (byte)value != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (propertyInfo.PropertyType.IsEnum)
|
||||||
|
{
|
||||||
|
value = Enum.ToObject(propertyInfo.PropertyType, value);
|
||||||
|
}
|
||||||
|
|
||||||
propertyInfo.SetValue(obj, value);
|
propertyInfo.SetValue(obj, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -378,6 +378,14 @@ namespace SonicAudioLib.IO
|
|||||||
byte[] buffer = new byte[length];
|
byte[] buffer = new byte[length];
|
||||||
source.Read(buffer, 0, length);
|
source.Read(buffer, 0, length);
|
||||||
|
|
||||||
|
for (int i = 0; i < buffer.Length; i++)
|
||||||
|
{
|
||||||
|
if (buffer[i] == 0)
|
||||||
|
{
|
||||||
|
return encoding.GetString(buffer, 0, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return encoding.GetString(buffer);
|
return encoding.GetString(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,5 +399,13 @@ namespace SonicAudioLib.IO
|
|||||||
byte[] buffer = encoding.GetBytes(value.ToCharArray(), 0, length);
|
byte[] buffer = encoding.GetBytes(value.ToCharArray(), 0, length);
|
||||||
destination.Write(buffer, 0, length);
|
destination.Write(buffer, 0, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Pad(Stream destination, long alignment)
|
||||||
|
{
|
||||||
|
while ((destination.Position % alignment) != 0)
|
||||||
|
{
|
||||||
|
destination.WriteByte(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
Source/SonicAudioLib/IO/Methods.cs
Normal file
21
Source/SonicAudioLib/IO/Methods.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SonicAudioLib.IO
|
||||||
|
{
|
||||||
|
public class Methods
|
||||||
|
{
|
||||||
|
public static long Align(long value, long alignment)
|
||||||
|
{
|
||||||
|
while ((value % alignment) != 0)
|
||||||
|
{
|
||||||
|
value++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -44,10 +44,7 @@ namespace SonicAudioLib.IO
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((length % align) != 0)
|
length = Methods.Align(length, align);
|
||||||
{
|
|
||||||
length++;
|
|
||||||
}
|
|
||||||
|
|
||||||
long position = length;
|
long position = length;
|
||||||
length += data.Length;
|
length += data.Length;
|
||||||
@ -63,10 +60,7 @@ namespace SonicAudioLib.IO
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((length % align) != 0)
|
length = Methods.Align(length, align);
|
||||||
{
|
|
||||||
length++;
|
|
||||||
}
|
|
||||||
|
|
||||||
long position = length;
|
long position = length;
|
||||||
length += stream.Length;
|
length += stream.Length;
|
||||||
@ -82,10 +76,7 @@ namespace SonicAudioLib.IO
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((length % align) != 0)
|
length = Methods.Align(length, align);
|
||||||
{
|
|
||||||
length++;
|
|
||||||
}
|
|
||||||
|
|
||||||
long position = length;
|
long position = length;
|
||||||
length += fileInfo.Length;
|
length += fileInfo.Length;
|
||||||
@ -101,10 +92,7 @@ namespace SonicAudioLib.IO
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((length % align) != 0)
|
length = Methods.Align(length, align);
|
||||||
{
|
|
||||||
length++;
|
|
||||||
}
|
|
||||||
|
|
||||||
long position = length;
|
long position = length;
|
||||||
length += module.CalculateLength();
|
length += module.CalculateLength();
|
||||||
@ -119,10 +107,7 @@ namespace SonicAudioLib.IO
|
|||||||
|
|
||||||
foreach (object item in items)
|
foreach (object item in items)
|
||||||
{
|
{
|
||||||
while ((destination.Position % align) != 0)
|
EndianStream.Pad(destination, align);
|
||||||
{
|
|
||||||
destination.WriteByte(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item is byte[])
|
if (item is byte[])
|
||||||
{
|
{
|
||||||
@ -159,6 +144,12 @@ namespace SonicAudioLib.IO
|
|||||||
{
|
{
|
||||||
items.Clear();
|
items.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public VldPool(uint align, long baseLength)
|
||||||
|
{
|
||||||
|
this.align = align;
|
||||||
|
length = baseLength;
|
||||||
|
}
|
||||||
|
|
||||||
public VldPool(uint align)
|
public VldPool(uint align)
|
||||||
{
|
{
|
||||||
|
@ -40,11 +40,11 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Archive\CriAaxArchive.cs" />
|
||||||
<Compile Include="Archive\CriAfs2Archive.cs" />
|
<Compile Include="Archive\CriAfs2Archive.cs" />
|
||||||
<Compile Include="Archive\ArchiveBase.cs" />
|
<Compile Include="Archive\ArchiveBase.cs" />
|
||||||
<Compile Include="Archive\CriCpkArchive.cs" />
|
<Compile Include="Archive\CriCpkArchive.cs" />
|
||||||
<Compile Include="Archive\HeroesPacArchive.cs" />
|
<Compile Include="Archive\HeroesPacArchive.cs" />
|
||||||
<Compile Include="Collections\OrderedDictionary.cs" />
|
|
||||||
<Compile Include="CriMw\CriTable.cs" />
|
<Compile Include="CriMw\CriTable.cs" />
|
||||||
<Compile Include="CriMw\CriField.cs" />
|
<Compile Include="CriMw\CriField.cs" />
|
||||||
<Compile Include="CriMw\CriFieldCollection.cs" />
|
<Compile Include="CriMw\CriFieldCollection.cs" />
|
||||||
@ -60,6 +60,7 @@
|
|||||||
<Compile Include="IO\EndianStream.cs" />
|
<Compile Include="IO\EndianStream.cs" />
|
||||||
<Compile Include="IO\StringPool.cs" />
|
<Compile Include="IO\StringPool.cs" />
|
||||||
<Compile Include="IO\Substream.cs" />
|
<Compile Include="IO\Substream.cs" />
|
||||||
|
<Compile Include="IO\Methods.cs" />
|
||||||
<Compile Include="IO\VldPool.cs" />
|
<Compile Include="IO\VldPool.cs" />
|
||||||
<Compile Include="Module\ModuleBase.cs" />
|
<Compile Include="Module\ModuleBase.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user