Improve ACB/CSB/CPK support

This commit is contained in:
Skyth 2016-12-31 15:56:10 +03:00
parent 3cc98ff257
commit a1541a648b
30 changed files with 674 additions and 511 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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

View File

@ -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;
}
} }
} }

View File

@ -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;
} }
} }
} }

View File

@ -30,7 +30,7 @@ namespace CsbBuilder
} }
[CriField("id", 1)] [CriField("id", 1)]
public uint Index public uint Id
{ {
get get
{ {

View File

@ -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;
} }
} }

View File

@ -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;
} }
} }

View File

@ -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;
} }
} }

View File

@ -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);
} }
} }
} }

View File

@ -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)

View File

@ -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 &quot;Intro.adx&quot; ///In the deepest directories, you will see .ADX files, named &quot;Intro.adx&quot;
///or &quot;Loop.adx&quot;. The ADX files are literally what the names say. You can ///or &quot;Loop.adx&quot;. 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]&quot;;.
///rate or [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string Description { internal static string Description {
get { get {

View File

@ -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.

View File

@ -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))

View 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);
}
}
}

View File

@ -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;
} }
} }

View File

@ -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)

View File

@ -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)

View File

@ -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();
}
}
}

View File

@ -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);
} }
} }

View File

@ -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;

View File

@ -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);

View File

@ -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();

View File

@ -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);
} }

View File

@ -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;
} }

View File

@ -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);
}
}
} }
} }

View 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;
}
}
}

View File

@ -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)
{ {

View File

@ -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" />