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.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.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using System.Text;
|
||||
|
||||
using SonicAudioLib.CriMw;
|
||||
using SonicAudioLib.IO;
|
||||
@ -18,9 +19,10 @@ namespace AcbEditor
|
||||
Console.ReadLine();
|
||||
return;
|
||||
}
|
||||
|
||||
#if !DEBUG
|
||||
try
|
||||
{
|
||||
#endif
|
||||
if (args[0].EndsWith(".acb"))
|
||||
{
|
||||
string baseDirectory = Path.GetDirectoryName(args[0]);
|
||||
@ -36,37 +38,53 @@ namespace AcbEditor
|
||||
CriAfs2Archive afs2Archive = 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
cpkMode = false;
|
||||
|
||||
using (Substream extAfs2Stream = acbReader.GetSubstream("StreamAwbAfs2Header"))
|
||||
{
|
||||
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)
|
||||
{
|
||||
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())
|
||||
{
|
||||
ushort index = waveformReader.GetUInt16("Id");
|
||||
ushort id = waveformReader.GetUInt16("Id");
|
||||
byte encodeType = waveformReader.GetByte("EncodeType");
|
||||
bool streaming = waveformReader.GetBoolean("Streaming");
|
||||
|
||||
string outputName = index.ToString("D5");
|
||||
string outputName = id.ToString("D5");
|
||||
if (streaming)
|
||||
{
|
||||
outputName += "_streaming";
|
||||
@ -91,11 +109,32 @@ namespace AcbEditor
|
||||
outputName += GetExtension(encodeType);
|
||||
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)
|
||||
{
|
||||
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 afs2EntryStream = afs2Entry.Open(extAfs2Stream))
|
||||
@ -107,10 +146,20 @@ namespace AcbEditor
|
||||
|
||||
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 (Stream afs2EntryStream = entry.Open(afs2Stream))
|
||||
using (Stream afs2EntryStream = afs2Entry.Open(afs2Stream))
|
||||
using (Stream afs2EntryDestination = File.Create(outputName))
|
||||
{
|
||||
afs2EntryStream.CopyTo(afs2EntryDestination);
|
||||
@ -151,15 +200,26 @@ namespace AcbEditor
|
||||
CriAfs2Archive afs2Archive = 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"]))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
ushort index = reader.GetUInt16("Id");
|
||||
ushort id = reader.GetUInt16("Id");
|
||||
byte encodeType = reader.GetByte("EncodeType");
|
||||
bool streaming = reader.GetBoolean("Streaming");
|
||||
|
||||
string inputName = index.ToString("D5");
|
||||
string inputName = id.ToString("D5");
|
||||
if (streaming)
|
||||
{
|
||||
inputName += "_streaming";
|
||||
@ -170,23 +230,43 @@ namespace AcbEditor
|
||||
|
||||
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));
|
||||
|
||||
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
|
||||
{
|
||||
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]["StreamAwbAfs2Header"] = null;
|
||||
|
||||
if (afs2Archive.Count > 0)
|
||||
if (afs2Archive.Count > 0 || cpkArchive.Count > 0)
|
||||
{
|
||||
afs2Archive.Order();
|
||||
|
||||
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...");
|
||||
extAfs2Archive.Save(awbPath);
|
||||
|
||||
byte[] afs2Header = new byte[16 +
|
||||
(extAfs2Archive.Count * extAfs2Archive.CueIndexFieldLength) +
|
||||
(extAfs2Archive.Count * extAfs2Archive.PositionFieldLength) +
|
||||
extAfs2Archive.PositionFieldLength];
|
||||
|
||||
using (FileStream fileStream = File.OpenRead(awbPath))
|
||||
if (cpkMode)
|
||||
{
|
||||
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.Save(acbPath);
|
||||
}
|
||||
#if !DEBUG
|
||||
}
|
||||
|
||||
catch (Exception exception)
|
||||
{
|
||||
MessageBox.Show($"{exception.Message}", "ACB Editor", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static string GetExtension(byte encodeType)
|
||||
@ -256,17 +342,28 @@ namespace AcbEditor
|
||||
case 8:
|
||||
return ".at3";
|
||||
case 9:
|
||||
return ".3dsadpcm";
|
||||
return ".bcwav";
|
||||
case 18:
|
||||
case 11:
|
||||
return ".at9";
|
||||
case 12:
|
||||
return ".xma";
|
||||
case 13:
|
||||
return ".wiiuadpcm";
|
||||
return ".dsp";
|
||||
default:
|
||||
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]
|
||||
public class CriTableAax
|
||||
{
|
||||
public enum EnumLoopFlag
|
||||
public enum EnumLoopFlag : byte
|
||||
{
|
||||
Intro = 0,
|
||||
Loop = 1,
|
||||
@ -33,15 +33,15 @@ namespace CsbBuilder
|
||||
}
|
||||
|
||||
[CriField("lpflg", 1)]
|
||||
public byte LoopFlag
|
||||
public EnumLoopFlag LoopFlag
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)_lpflg;
|
||||
return _lpflg;
|
||||
}
|
||||
set
|
||||
{
|
||||
_lpflg = (EnumLoopFlag)value;
|
||||
_lpflg = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ namespace CsbBuilder
|
||||
}
|
||||
|
||||
[CriField("id", 1)]
|
||||
public uint Index
|
||||
public uint Id
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -14,7 +14,7 @@ namespace CsbBuilder
|
||||
[CriSerializable("TBLCSB")]
|
||||
public class CriTableCueSheet
|
||||
{
|
||||
public enum EnumTableType
|
||||
public enum EnumTableType : byte
|
||||
{
|
||||
None = 0,
|
||||
Cue = 1,
|
||||
@ -42,15 +42,15 @@ namespace CsbBuilder
|
||||
}
|
||||
|
||||
[CriField("ttype", 1)]
|
||||
public byte TableType
|
||||
public EnumTableType TableType
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)_ttype;
|
||||
return _ttype;
|
||||
}
|
||||
set
|
||||
{
|
||||
_ttype = (EnumTableType)value;
|
||||
_ttype = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,22 +12,16 @@ namespace CsbBuilder
|
||||
[CriSerializable("TBLSDL")]
|
||||
public class CriTableSoundElement
|
||||
{
|
||||
public enum EnumFormat
|
||||
public enum EnumFormat : byte
|
||||
{
|
||||
Adx = 0,
|
||||
Dsp = 4,
|
||||
};
|
||||
|
||||
public enum EnumStreamFlag
|
||||
{
|
||||
Internal = 0,
|
||||
External = 1,
|
||||
};
|
||||
|
||||
private string _name = string.Empty;
|
||||
private EnumFormat _fmt = EnumFormat.Adx;
|
||||
private byte _nch = 0;
|
||||
private EnumStreamFlag _stmflg = EnumStreamFlag.Internal;
|
||||
private bool _stmflg = false;
|
||||
private uint _sfreq = 0;
|
||||
private uint _nsmpl = 0;
|
||||
|
||||
@ -58,20 +52,23 @@ namespace CsbBuilder
|
||||
|
||||
set
|
||||
{
|
||||
DataList = CriTableSerializer.Deserialize<CriTableAax>(value);
|
||||
if (value.Length > 0)
|
||||
{
|
||||
DataList = CriTableSerializer.Deserialize<CriTableAax>(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CriField("fmt", 2)]
|
||||
public byte Format
|
||||
public EnumFormat Format
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)_fmt;
|
||||
return _fmt;
|
||||
}
|
||||
set
|
||||
{
|
||||
_fmt = (EnumFormat)value;
|
||||
_fmt = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,15 +86,15 @@ namespace CsbBuilder
|
||||
}
|
||||
|
||||
[CriField("stmflg", 4)]
|
||||
public byte StreamFlag
|
||||
public bool Streaming
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)_stmflg;
|
||||
return _stmflg;
|
||||
}
|
||||
set
|
||||
{
|
||||
_stmflg = (EnumStreamFlag)value;
|
||||
_stmflg = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ namespace CsbBuilder
|
||||
[CriSerializable("TBLSYN")]
|
||||
public class CriTableSynth
|
||||
{
|
||||
public enum EnumSynthType
|
||||
public enum EnumSynthType : byte
|
||||
{
|
||||
Waveform = 0,
|
||||
Polyphonic = 1,
|
||||
@ -123,28 +123,28 @@ namespace CsbBuilder
|
||||
}
|
||||
|
||||
[CriField("syntype", 1)]
|
||||
public byte SynthType
|
||||
public EnumSynthType SynthType
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)_syntype;
|
||||
return _syntype;
|
||||
}
|
||||
set
|
||||
{
|
||||
_syntype = (EnumSynthType)value;
|
||||
_syntype = value;
|
||||
}
|
||||
}
|
||||
|
||||
[CriField("cmplxtype", 2)]
|
||||
public byte ComplexType
|
||||
public EnumSynthType ComplexType
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)_cmplxtype;
|
||||
return _cmplxtype;
|
||||
}
|
||||
set
|
||||
{
|
||||
_cmplxtype = (EnumSynthType)value;
|
||||
_cmplxtype = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,8 @@ using SonicAudioLib.Archive;
|
||||
|
||||
using System.Globalization;
|
||||
using System.Xml.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace CsbBuilder
|
||||
{
|
||||
@ -25,48 +27,50 @@ namespace CsbBuilder
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
if (args[0].EndsWith(".csb"))
|
||||
{
|
||||
if (args[0].EndsWith(".csb"))
|
||||
{
|
||||
List<CriTableCueSheet> cueSheets = CriTableSerializer.Deserialize<CriTableCueSheet>(args[0]);
|
||||
//CriTableSerializer.Serialize(args[0] + ".new", cueSheets, CriTableWriterSettings.AdxSettings);
|
||||
List<CriTableCueSheet> tables = CriTableSerializer.Deserialize<CriTableCueSheet>(args[0]);
|
||||
Serialize(args[0] + ".xml", tables);
|
||||
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(List<CriTableCueSheet>));
|
||||
using (Stream dest = File.Create(Path.GetFileNameWithoutExtension(args[0]) + ".xml"))
|
||||
foreach (CriTableCueSheet table in tables)
|
||||
{
|
||||
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))
|
||||
{
|
||||
}
|
||||
|
||||
else if (args[0].EndsWith(".xml"))
|
||||
{
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(List<CriTableCueSheet>));
|
||||
using (Stream dest = File.OpenRead(args[0]))
|
||||
foreach (CriTableSynth synth in table.DataList.OfType<CriTableSynth>())
|
||||
{
|
||||
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);
|
||||
numberChannels = EndianStream.ReadByte(source);
|
||||
sampleRate = EndianStream.ReadUInt32BE(source);
|
||||
serializer.Serialize(destination, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using SonicAudioLib.CriMw;
|
||||
using SonicAudioLib.IO;
|
||||
@ -22,13 +23,19 @@ namespace CsbEditor
|
||||
return;
|
||||
}
|
||||
|
||||
#if !DEBUG
|
||||
try
|
||||
{
|
||||
#endif
|
||||
if (args[0].EndsWith(".csb"))
|
||||
{
|
||||
string baseDirectory = Path.GetDirectoryName(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]))
|
||||
{
|
||||
while (reader.Read())
|
||||
@ -39,31 +46,66 @@ namespace CsbEditor
|
||||
{
|
||||
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)
|
||||
{
|
||||
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");
|
||||
DirectoryInfo destinationPath = new DirectoryInfo(Path.Combine(outputDirectoryName, sdlName));
|
||||
destinationPath.Create();
|
||||
|
||||
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"))
|
||||
using (Stream destination = File.Create(outputName))
|
||||
CriAaxArchive aaxArchive = new CriAaxArchive();
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
CriCpkArchive cpkArchive = new CriCpkArchive();
|
||||
|
||||
CriTable csbFile = new CriTable();
|
||||
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();
|
||||
soundElementTable.Load((byte[])soundElementRow["utf"]);
|
||||
|
||||
List<FileInfo> junks = new List<FileInfo>();
|
||||
|
||||
foreach (CriRow sdlRow in soundElementTable.Rows)
|
||||
{
|
||||
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}");
|
||||
}
|
||||
|
||||
bool streaming = (byte)sdlRow["stmflg"] != 0;
|
||||
uint sampleRate = (uint)sdlRow["sfreq"];
|
||||
byte numberChannels = (byte)sdlRow["nch"];
|
||||
|
||||
Console.WriteLine("Adding {0}...", sdlName);
|
||||
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
using (CriTableWriter writer = CriTableWriter.Create(memoryStream, CriTableWriterSettings.AdxSettings))
|
||||
CriAaxArchive aaxArchive = new CriAaxArchive();
|
||||
foreach (FileInfo file in sdlDirectory.GetFiles("*.adx"))
|
||||
{
|
||||
writer.WriteStartTable("AAX");
|
||||
|
||||
writer.WriteField("data", typeof(byte[]));
|
||||
writer.WriteField("lpflg", typeof(byte));
|
||||
|
||||
foreach (FileInfo audioFile in sdlDirectory.GetFiles("*.adx"))
|
||||
CriAaxEntry entry = new CriAaxEntry();
|
||||
if (file.Name.ToLower(CultureInfo.GetCultureInfo("en-US")) == "intro.adx")
|
||||
{
|
||||
// In Turkish, lowercase I is ı so you get the idea
|
||||
if (audioFile.Name.ToLower(CultureInfo.GetCultureInfo("en-US")) == "intro.adx")
|
||||
{
|
||||
ReadAdx(audioFile, out sampleRate, out numberChannels);
|
||||
writer.WriteRow(true, audioFile, (byte)0);
|
||||
}
|
||||
entry.Flag = CriAaxEntryFlag.Intro;
|
||||
entry.FilePath = file;
|
||||
aaxArchive.Add(entry);
|
||||
|
||||
else if (audioFile.Name.ToLower() == "loop.adx")
|
||||
{
|
||||
ReadAdx(audioFile, out sampleRate, out numberChannels);
|
||||
writer.WriteRow(true, audioFile, (byte)1);
|
||||
}
|
||||
ReadAdx(file, out sampleRate, out numberChannels);
|
||||
}
|
||||
|
||||
writer.WriteEndTable();
|
||||
sdlRow["data"] = memoryStream.ToArray();
|
||||
else if (file.Name.ToLower(CultureInfo.GetCultureInfo("en-US")) == "loop.adx")
|
||||
{
|
||||
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;
|
||||
@ -147,13 +218,25 @@ namespace CsbEditor
|
||||
|
||||
csbFile.WriterSettings = CriTableWriterSettings.AdxSettings;
|
||||
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)
|
||||
{
|
||||
MessageBox.Show($"{exception.Message}", "CSB Editor", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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
|
||||
///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"
|
||||
///or "Loop.adx". The ADX files are literally what the names say. You can
|
||||
///add/delete/modify them freely.
|
||||
///
|
||||
///The sample rate and channel count information will be automatically
|
||||
///updated in the CSB file if you use an ADX file with different sample
|
||||
///rate or [rest of string was truncated]";.
|
||||
///updated in the CSB [rest of string was truncated]";.
|
||||
/// </summary>
|
||||
internal static string Description {
|
||||
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
|
||||
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"
|
||||
or "Loop.adx". The ADX files are literally what the names say. You can
|
||||
add/delete/modify them freely.
|
||||
|
@ -9,7 +9,6 @@ using System.Collections;
|
||||
|
||||
using SonicAudioLib;
|
||||
using SonicAudioLib.Archive;
|
||||
using SonicAudioLib.Collections;
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.CriMw;
|
||||
|
||||
@ -25,7 +24,7 @@ namespace SonicAudioCmd
|
||||
{
|
||||
CriCpkArchive archive = new CriCpkArchive();
|
||||
archive.Load(args[0]);
|
||||
archive.Print();
|
||||
//archive.Print();
|
||||
|
||||
foreach (CriCpkEntry entry in archive)
|
||||
{
|
||||
@ -46,7 +45,7 @@ namespace SonicAudioCmd
|
||||
{
|
||||
CriCpkArchive archive = new CriCpkArchive();
|
||||
archive.Align = 16;
|
||||
archive.Mode = CriCpkMode.FileName;
|
||||
archive.Mode = CriCpkMode.Id;
|
||||
|
||||
uint id = 0;
|
||||
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 ushort CueIndex { get; set; }
|
||||
public ushort Id { get; set; }
|
||||
}
|
||||
|
||||
public class CriAfs2Archive : ArchiveBase<CriAfs2Entry>
|
||||
{
|
||||
public uint Align { get; set; }
|
||||
public uint CueIndexFieldLength { get; set; }
|
||||
public uint IdFieldLength { get; set; }
|
||||
public uint PositionFieldLength { get; set; }
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
CueIndexFieldLength = (information & 0x00FF0000) >> 16;
|
||||
IdFieldLength = (information & 0x00FF0000) >> 16;
|
||||
PositionFieldLength = (information & 0x0000FF00) >> 8;
|
||||
|
||||
ushort entryCount = (ushort)EndianStream.ReadUInt32(source);
|
||||
@ -46,20 +46,20 @@ namespace SonicAudioLib.Archive
|
||||
{
|
||||
CriAfs2Entry afs2Entry = new CriAfs2Entry();
|
||||
|
||||
long cueIndexPosition = 16 + (i * CueIndexFieldLength);
|
||||
source.Seek(cueIndexPosition, SeekOrigin.Begin);
|
||||
long idPosition = 16 + (i * IdFieldLength);
|
||||
source.Seek(idPosition, SeekOrigin.Begin);
|
||||
|
||||
switch (CueIndexFieldLength)
|
||||
switch (IdFieldLength)
|
||||
{
|
||||
case 2:
|
||||
afs2Entry.CueIndex = EndianStream.ReadUInt16(source);
|
||||
afs2Entry.Id = EndianStream.ReadUInt16(source);
|
||||
break;
|
||||
|
||||
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);
|
||||
|
||||
switch (PositionFieldLength)
|
||||
@ -81,10 +81,7 @@ namespace SonicAudioLib.Archive
|
||||
previousEntry.Length = afs2Entry.Position - previousEntry.Position;
|
||||
}
|
||||
|
||||
while ((afs2Entry.Position % Align) != 0)
|
||||
{
|
||||
afs2Entry.Position++;
|
||||
}
|
||||
afs2Entry.Position = Methods.Align(afs2Entry.Position, Align);
|
||||
|
||||
if (i == entryCount - 1)
|
||||
{
|
||||
@ -107,32 +104,33 @@ namespace SonicAudioLib.Archive
|
||||
|
||||
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.WriteUInt32(destination, 1 | (CueIndexFieldLength << 16) | (PositionFieldLength << 8));
|
||||
EndianStream.WriteUInt32(destination, 1 | (IdFieldLength << 16) | (PositionFieldLength << 8));
|
||||
EndianStream.WriteUInt32(destination, (ushort)entries.Count);
|
||||
EndianStream.WriteUInt32(destination, 1);
|
||||
EndianStream.WriteUInt32(destination, Align);
|
||||
|
||||
// FIXME: Alignment support
|
||||
VldPool vldPool = new VldPool(1);
|
||||
VldPool vldPool = new VldPool(Align, headerLength);
|
||||
|
||||
foreach (CriAfs2Entry afs2Entry in entries)
|
||||
var orderedEntries = entries.OrderBy(entry => entry.Id);
|
||||
foreach (CriAfs2Entry afs2Entry in orderedEntries)
|
||||
{
|
||||
switch (CueIndexFieldLength)
|
||||
switch (IdFieldLength)
|
||||
{
|
||||
case 2:
|
||||
EndianStream.WriteUInt16(destination, (ushort)afs2Entry.CueIndex);
|
||||
EndianStream.WriteUInt16(destination, (ushort)afs2Entry.Id);
|
||||
break;
|
||||
|
||||
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)
|
||||
{
|
||||
@ -151,43 +149,21 @@ namespace SonicAudioLib.Archive
|
||||
afs2Entry.Position = entryPosition;
|
||||
}
|
||||
|
||||
EndianStream.WriteUInt32(destination, (uint)(headerLength + vldPool.Length));
|
||||
EndianStream.WriteUInt32(destination, (uint)vldPool.Length);
|
||||
|
||||
vldPool.Write(destination);
|
||||
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()
|
||||
{
|
||||
Align = 32;
|
||||
CueIndexFieldLength = 2;
|
||||
IdFieldLength = 2;
|
||||
PositionFieldLength = 4;
|
||||
}
|
||||
}
|
||||
|
@ -145,10 +145,7 @@ namespace SonicAudioLib.Archive
|
||||
entry.Position += tocPosition;
|
||||
}
|
||||
|
||||
while ((entry.Position % align) != 0)
|
||||
{
|
||||
entry.Position++;
|
||||
}
|
||||
entry.Position = Methods.Align(entry.Position, align);
|
||||
|
||||
etocReader.MoveToRow(tocReader.CurrentRow);
|
||||
entry.UpdateDateTime = DateTimeFromCpkDateTime(etocReader.GetUInt64("UpdateDateTime"));
|
||||
@ -171,8 +168,6 @@ namespace SonicAudioLib.Archive
|
||||
|
||||
else if (mode == CriCpkMode.Id)
|
||||
{
|
||||
long currentPosition = contentPosition;
|
||||
|
||||
using (CriTableReader itocReader = CriCpkSection.Open(source, itocPosition))
|
||||
{
|
||||
while (itocReader.Read())
|
||||
@ -192,15 +187,7 @@ namespace SonicAudioLib.Archive
|
||||
entry.IsCompressed = true;
|
||||
}
|
||||
|
||||
while ((currentPosition % align) != 0)
|
||||
{
|
||||
currentPosition++;
|
||||
}
|
||||
|
||||
entry.Position = currentPosition;
|
||||
entries.Add(entry);
|
||||
|
||||
currentPosition += entry.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -220,20 +207,21 @@ namespace SonicAudioLib.Archive
|
||||
entry.IsCompressed = true;
|
||||
}
|
||||
|
||||
while ((currentPosition % align) != 0)
|
||||
{
|
||||
currentPosition++;
|
||||
}
|
||||
|
||||
entry.Position = currentPosition;
|
||||
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
|
||||
@ -247,8 +235,7 @@ namespace SonicAudioLib.Archive
|
||||
|
||||
public override void Write(Stream destination)
|
||||
{
|
||||
// FIXME: Alignment support (ugh same thing for AFS2 archives)
|
||||
VldPool vldPool = new VldPool();
|
||||
VldPool vldPool = new VldPool(Align, 2048);
|
||||
|
||||
using (CriCpkSection cpkSection = new CriCpkSection(destination, "CPK "))
|
||||
{
|
||||
@ -358,8 +345,20 @@ namespace SonicAudioLib.Archive
|
||||
|
||||
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);
|
||||
etocSection.Writer.WriteRow(true, CpkDateTimeFromDateTime(entry.UpdateDateTime), entry.DirectoryName);
|
||||
tocSection.Writer.WriteRow(true,
|
||||
(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();
|
||||
@ -379,7 +378,9 @@ namespace SonicAudioLib.Archive
|
||||
|
||||
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();
|
||||
@ -403,8 +404,6 @@ namespace SonicAudioLib.Archive
|
||||
List<CriCpkEntry> filesL = 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();
|
||||
|
||||
using (MemoryStream dataMemoryStream = new MemoryStream())
|
||||
@ -418,9 +417,10 @@ namespace SonicAudioLib.Archive
|
||||
|
||||
foreach (CriCpkEntry entry in filesL)
|
||||
{
|
||||
dataWriter.WriteRow(true, id, Convert.ToUInt16(entry.Length), Convert.ToUInt16(entry.Length));
|
||||
vldPool.Put(entry.FilePath);
|
||||
id++;
|
||||
dataWriter.WriteRow(true,
|
||||
(ushort)entry.Id,
|
||||
(ushort)entry.Length,
|
||||
(ushort)entry.Length);
|
||||
}
|
||||
|
||||
dataWriter.WriteEndTable();
|
||||
@ -439,9 +439,10 @@ namespace SonicAudioLib.Archive
|
||||
|
||||
foreach (CriCpkEntry entry in filesH)
|
||||
{
|
||||
dataWriter.WriteRow(true, id, Convert.ToUInt32(entry.Length), Convert.ToUInt32(entry.Length));
|
||||
vldPool.Put(entry.FilePath);
|
||||
id++;
|
||||
dataWriter.WriteRow(true,
|
||||
(ushort)entry.Id,
|
||||
(uint)entry.Length,
|
||||
(uint)entry.Length);
|
||||
}
|
||||
|
||||
dataWriter.WriteEndTable();
|
||||
@ -454,6 +455,11 @@ namespace SonicAudioLib.Archive
|
||||
itocSection.Writer.WriteEndRow();
|
||||
itocSection.Writer.WriteEndTable();
|
||||
}
|
||||
|
||||
foreach (CriCpkEntry entry in entries.OrderBy(entry => entry.Id))
|
||||
{
|
||||
vldPool.Put(entry.FilePath);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
@ -464,63 +470,74 @@ namespace SonicAudioLib.Archive
|
||||
cpkSection.Writer.WriteStartRow();
|
||||
cpkSection.Writer.WriteValue("UpdateDateTime", CpkDateTimeFromDateTime(DateTime.Now));
|
||||
|
||||
cpkSection.Writer.WriteValue("ContentOffset", Convert.ToUInt64(2048));
|
||||
cpkSection.Writer.WriteValue("ContentSize", Convert.ToUInt64(vldPool.Length));
|
||||
cpkSection.Writer.WriteValue("ContentOffset", (ulong)2048);
|
||||
cpkSection.Writer.WriteValue("ContentSize", (ulong)(vldPool.Length - 2048));
|
||||
|
||||
if (tocMemoryStream != null)
|
||||
{
|
||||
cpkSection.Writer.WriteValue("TocOffset", Convert.ToUInt64(2048 + vldPool.Put(tocMemoryStream)));
|
||||
cpkSection.Writer.WriteValue("TocSize", Convert.ToUInt64(tocMemoryStream.Length));
|
||||
cpkSection.Writer.WriteValue("TocOffset", (ulong)vldPool.Put(tocMemoryStream));
|
||||
cpkSection.Writer.WriteValue("TocSize", (ulong)tocMemoryStream.Length);
|
||||
}
|
||||
|
||||
if (itocMemoryStream != null)
|
||||
{
|
||||
cpkSection.Writer.WriteValue("ItocOffset", Convert.ToUInt64(2048 + vldPool.Put(itocMemoryStream)));
|
||||
cpkSection.Writer.WriteValue("ItocSize", Convert.ToUInt64(itocMemoryStream.Length));
|
||||
cpkSection.Writer.WriteValue("ItocOffset", (ulong)vldPool.Put(itocMemoryStream));
|
||||
cpkSection.Writer.WriteValue("ItocSize", (ulong)itocMemoryStream.Length);
|
||||
}
|
||||
|
||||
if (etocMemoryStream != null)
|
||||
{
|
||||
cpkSection.Writer.WriteValue("EtocOffset", Convert.ToUInt64(2048 + vldPool.Put(etocMemoryStream)));
|
||||
cpkSection.Writer.WriteValue("EtocSize", Convert.ToUInt64(etocMemoryStream.Length));
|
||||
cpkSection.Writer.WriteValue("EtocOffset", (ulong)vldPool.Put(etocMemoryStream));
|
||||
cpkSection.Writer.WriteValue("EtocSize", (ulong)etocMemoryStream.Length);
|
||||
}
|
||||
|
||||
uint totalDataSize = 0;
|
||||
long totalDataSize = 0;
|
||||
foreach (CriCpkEntry entry in entries)
|
||||
{
|
||||
totalDataSize += (uint)entry.Length;
|
||||
totalDataSize += entry.Length;
|
||||
}
|
||||
|
||||
cpkSection.Writer.WriteValue("EnabledPackedSize", 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("Revision", (ushort)2);
|
||||
cpkSection.Writer.WriteValue("Align", (ushort)1);
|
||||
cpkSection.Writer.WriteValue("Align", Align);
|
||||
cpkSection.Writer.WriteValue("Sorted", (ushort)1);
|
||||
|
||||
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("FileSize", Convert.ToUInt64(2048 + (ulong)vldPool.Length));
|
||||
cpkSection.Writer.WriteValue("FileSize", (ulong)vldPool.Length);
|
||||
|
||||
cpkSection.Writer.WriteEndRow();
|
||||
cpkSection.Writer.WriteEndTable();
|
||||
}
|
||||
|
||||
while ((destination.Position % 2042) != 0)
|
||||
{
|
||||
destination.WriteByte(0);
|
||||
}
|
||||
|
||||
EndianStream.Pad(destination, 2042);
|
||||
EndianStream.WriteCString(destination, "(c)CRI", 6);
|
||||
|
||||
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)
|
||||
{
|
||||
if (dateTime == 0)
|
||||
|
@ -11,7 +11,7 @@ namespace SonicAudioLib.Archive
|
||||
{
|
||||
public class HeroesPacEntry : EntryBase
|
||||
{
|
||||
public uint GlobalIndex { get; set; }
|
||||
public uint Id { get; set; }
|
||||
}
|
||||
|
||||
public class HeroesPacArchive : ArchiveBase<HeroesPacEntry>
|
||||
@ -30,7 +30,7 @@ namespace SonicAudioLib.Archive
|
||||
{
|
||||
HeroesPacEntry pacEntry = new HeroesPacEntry();
|
||||
|
||||
pacEntry.GlobalIndex = EndianStream.ReadUInt32(source);
|
||||
pacEntry.Id = EndianStream.ReadUInt32(source);
|
||||
pacEntry.Position = vldPoolPosition + EndianStream.ReadUInt32(source);
|
||||
|
||||
if (previousEntry != null)
|
||||
@ -74,7 +74,7 @@ namespace SonicAudioLib.Archive
|
||||
{
|
||||
uint entryPosition = (uint)vldPool.Put(pacEntry.FilePath);
|
||||
|
||||
EndianStream.WriteUInt32(destination, pacEntry.GlobalIndex);
|
||||
EndianStream.WriteUInt32(destination, pacEntry.Id);
|
||||
EndianStream.WriteUInt32(destination, entryPosition);
|
||||
|
||||
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
|
||||
{
|
||||
return fields.Single(f => f.FieldName == name);
|
||||
return fields.FirstOrDefault(field => field.FieldName == name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,10 +86,10 @@ namespace SonicAudioLib.CriMw
|
||||
{
|
||||
fields.Remove(criField);
|
||||
|
||||
// Update the objects
|
||||
// Update the 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.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace SonicAudioLib.CriMw
|
||||
{
|
||||
internal class CriRowRecord
|
||||
{
|
||||
public CriField Field { get; set; }
|
||||
public object Value { get; set; }
|
||||
}
|
||||
|
||||
public class CriRow : IEnumerable
|
||||
{
|
||||
private OrderedDictionary<CriField, object> records = new OrderedDictionary<CriField, object>();
|
||||
private List<CriRowRecord> records = new List<CriRowRecord>();
|
||||
private CriTable parent;
|
||||
|
||||
public object this[CriField criField]
|
||||
{
|
||||
get
|
||||
{
|
||||
return records[criField];
|
||||
return this[records.FindIndex(record => record.Field == criField)];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
records[criField] = value;
|
||||
this[records.FindIndex(record => record.Field == criField)] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,12 +33,22 @@ namespace SonicAudioLib.CriMw
|
||||
{
|
||||
get
|
||||
{
|
||||
return records[index];
|
||||
if (index < 0 || index >= records.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return records[index].Value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
records[index] = value;
|
||||
if (index < 0 || index >= records.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
records[index].Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,12 +56,12 @@ namespace SonicAudioLib.CriMw
|
||||
{
|
||||
get
|
||||
{
|
||||
return this[records.Single(k => (k.Key).FieldName == name).Key];
|
||||
return this[records.FindIndex(record => record.Field.FieldName == name)];
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
@ -83,7 +100,7 @@ namespace SonicAudioLib.CriMw
|
||||
|
||||
for (int i = 0; i < records.Count; i++)
|
||||
{
|
||||
values[i] = records[i];
|
||||
values[i] = records[i].Value;
|
||||
}
|
||||
|
||||
return values;
|
||||
@ -91,9 +108,9 @@ namespace SonicAudioLib.CriMw
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
foreach (var keyValPair in records)
|
||||
foreach (var record in records)
|
||||
{
|
||||
yield return keyValPair.Value;
|
||||
yield return record.Value;
|
||||
}
|
||||
|
||||
yield break;
|
||||
|
@ -68,7 +68,7 @@ namespace SonicAudioLib.CriMw
|
||||
|
||||
foreach (CriField criField in fields)
|
||||
{
|
||||
criRow.Records.Add(criField, criField.DefaultValue);
|
||||
criRow.Records.Add(new CriRowRecord { Field = criField, Value = criField.DefaultValue });
|
||||
}
|
||||
|
||||
return criRow;
|
||||
@ -115,6 +115,11 @@ namespace SonicAudioLib.CriMw
|
||||
}
|
||||
}
|
||||
|
||||
else if (rows.Count == 0)
|
||||
{
|
||||
useDefaultValue = true;
|
||||
}
|
||||
|
||||
if (useDefaultValue)
|
||||
{
|
||||
writer.WriteField(criField.FieldName, criField.FieldType, defaultValue);
|
||||
@ -136,12 +141,6 @@ namespace SonicAudioLib.CriMw
|
||||
}
|
||||
}
|
||||
|
||||
public override long CalculateLength()
|
||||
{
|
||||
// TODO
|
||||
return base.CalculateLength();
|
||||
}
|
||||
|
||||
public CriTable()
|
||||
{
|
||||
fields = new CriFieldCollection(this);
|
||||
|
@ -1,7 +1,7 @@
|
||||
using SonicAudioLib.Collections;
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
@ -9,7 +9,7 @@ namespace SonicAudioLib.CriMw
|
||||
{
|
||||
public class CriTableReader : IDisposable
|
||||
{
|
||||
private OrderedDictionary<string, CriTableField> fields;
|
||||
private List<CriTableField> fields;
|
||||
private Stream source;
|
||||
private CriTableHeader header;
|
||||
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)
|
||||
{
|
||||
return CriField.FieldTypes[(byte)fields[fieldName].Flag & 0x0F];
|
||||
return CriField.FieldTypes[(byte)fields[GetFieldIndex(fieldName)].Flag & 0x0F];
|
||||
}
|
||||
|
||||
public object GetFieldValue(int fieldIndex)
|
||||
@ -168,7 +168,7 @@ namespace SonicAudioLib.CriMw
|
||||
|
||||
internal CriFieldFlag GetFieldFlag(string fieldName)
|
||||
{
|
||||
return fields[fieldName].Flag;
|
||||
return fields[GetFieldIndex(fieldName)].Flag;
|
||||
}
|
||||
|
||||
internal CriFieldFlag GetFieldFlag(int fieldIndex)
|
||||
@ -178,7 +178,7 @@ namespace SonicAudioLib.CriMw
|
||||
|
||||
public object GetFieldValue(string fieldName)
|
||||
{
|
||||
return fields[fieldName].Value;
|
||||
return fields[GetFieldIndex(fieldName)].Value;
|
||||
}
|
||||
|
||||
public CriField GetField(int fieldIndex)
|
||||
@ -190,6 +190,11 @@ namespace SonicAudioLib.CriMw
|
||||
{
|
||||
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)
|
||||
{
|
||||
@ -295,7 +300,7 @@ namespace SonicAudioLib.CriMw
|
||||
|
||||
public object GetValue(string fieldName)
|
||||
{
|
||||
return GetValue(fields.IndexOf(fieldName));
|
||||
return GetValue(GetFieldIndex(fieldName));
|
||||
}
|
||||
|
||||
public T GetValue<T>(int fieldIndex)
|
||||
@ -435,7 +440,7 @@ namespace SonicAudioLib.CriMw
|
||||
|
||||
public byte[] GetData(string fieldName)
|
||||
{
|
||||
return GetData(fields.IndexOf(fieldName));
|
||||
return GetData(GetFieldIndex(fieldName));
|
||||
}
|
||||
|
||||
public CriTableReader GetCriTableReader(string fieldName)
|
||||
@ -470,7 +475,7 @@ namespace SonicAudioLib.CriMw
|
||||
|
||||
public uint GetLength(string fieldName)
|
||||
{
|
||||
return GetLength(fields.IndexOf(fieldName));
|
||||
return GetLength(GetFieldIndex(fieldName));
|
||||
}
|
||||
|
||||
public uint GetPosition(int fieldIndex)
|
||||
@ -495,7 +500,7 @@ namespace SonicAudioLib.CriMw
|
||||
|
||||
public uint GetPosition(string fieldName)
|
||||
{
|
||||
return GetPosition(fields.IndexOf(fieldName));
|
||||
return GetPosition(GetFieldIndex(fieldName));
|
||||
}
|
||||
|
||||
public bool GetBoolean(int fieldIndex)
|
||||
@ -582,12 +587,12 @@ namespace SonicAudioLib.CriMw
|
||||
|
||||
private string ReadString()
|
||||
{
|
||||
int stringPosition = ReadInt32();
|
||||
uint stringPosition = ReadUInt32();
|
||||
|
||||
long previousPosition = source.Position;
|
||||
|
||||
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;
|
||||
|
||||
if (strResult == "<NULL>" ||
|
||||
@ -703,7 +708,7 @@ namespace SonicAudioLib.CriMw
|
||||
{
|
||||
this.source = source;
|
||||
header = new CriTableHeader();
|
||||
fields = new OrderedDictionary<string, CriTableField>();
|
||||
fields = new List<CriTableField>();
|
||||
this.leaveOpen = leaveOpen;
|
||||
|
||||
ReadTable();
|
||||
|
@ -2,8 +2,8 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using SonicAudioLib.Collections;
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.Module;
|
||||
|
||||
@ -22,7 +22,7 @@ namespace SonicAudioLib.CriMw
|
||||
}
|
||||
|
||||
private CriTableWriterSettings settings;
|
||||
private OrderedDictionary<string, CriTableField> fields;
|
||||
private List<CriTableField> fields;
|
||||
private Stream destination;
|
||||
private CriTableHeader header;
|
||||
private VldPool vldPool;
|
||||
@ -101,18 +101,12 @@ namespace SonicAudioLib.CriMw
|
||||
stringPool.Write(destination);
|
||||
header.StringPoolPosition = (uint)stringPool.Position - headerPosition;
|
||||
|
||||
while ((destination.Position % vldPool.Align) != 0)
|
||||
{
|
||||
destination.WriteByte(0);
|
||||
}
|
||||
EndianStream.Pad(destination, vldPool.Align);
|
||||
|
||||
vldPool.Write(destination);
|
||||
header.DataPoolPosition = (uint)vldPool.Position - headerPosition;
|
||||
|
||||
while ((destination.Position % vldPool.Align) != 0)
|
||||
{
|
||||
destination.WriteByte(0);
|
||||
}
|
||||
EndianStream.Pad(destination, vldPool.Align);
|
||||
|
||||
long previousPosition = destination.Position;
|
||||
|
||||
@ -183,7 +177,7 @@ namespace SonicAudioLib.CriMw
|
||||
WriteValue(defaultValue);
|
||||
}
|
||||
|
||||
fields.Add(fieldName, field);
|
||||
fields.Add(field);
|
||||
header.NumberOfFields++;
|
||||
}
|
||||
|
||||
@ -214,7 +208,7 @@ namespace SonicAudioLib.CriMw
|
||||
WriteString(field.Name);
|
||||
}
|
||||
|
||||
fields.Add(fieldName, field);
|
||||
fields.Add(field);
|
||||
header.NumberOfFields++;
|
||||
}
|
||||
|
||||
@ -270,7 +264,7 @@ namespace SonicAudioLib.CriMw
|
||||
|
||||
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)
|
||||
@ -601,7 +595,7 @@ namespace SonicAudioLib.CriMw
|
||||
this.settings = settings;
|
||||
|
||||
header = new CriTableHeader();
|
||||
fields = new OrderedDictionary<string, CriTableField>();
|
||||
fields = new List<CriTableField>();
|
||||
stringPool = new StringPool(settings.EncodingType);
|
||||
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)
|
||||
if (propertyInfo.PropertyType != typeof(FileInfo) &&
|
||||
propertyInfo.PropertyType != typeof(Stream) &&
|
||||
propertyInfo.PropertyType != typeof(bool) &&
|
||||
!propertyInfo.PropertyType.IsEnum &&
|
||||
!CriField.FieldTypes.Contains(propertyInfo.PropertyType))
|
||||
{
|
||||
continue;
|
||||
@ -100,19 +102,27 @@ namespace SonicAudioLib.CriMw.Serialization
|
||||
string fieldName = propertyInfo.Name;
|
||||
Type fieldType = propertyInfo.PropertyType;
|
||||
object defaultValue = null;
|
||||
|
||||
// Since the invalid types were cleaned, we can assume that those can be FileInfo or Stream
|
||||
// so directly change the type to byte[]
|
||||
if (!CriField.FieldTypes.Contains(fieldType))
|
||||
|
||||
if (fieldType == typeof(FileInfo) || fieldType == typeof(Stream))
|
||||
{
|
||||
fieldType = typeof(byte[]);
|
||||
}
|
||||
|
||||
else if (fieldType == typeof(bool))
|
||||
{
|
||||
fieldType = typeof(byte);
|
||||
}
|
||||
|
||||
else if (fieldType.IsEnum)
|
||||
{
|
||||
fieldType = Enum.GetUnderlyingType(propertyInfo.PropertyType);
|
||||
}
|
||||
|
||||
if (fieldAttribute != null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
tableWriter.WriteField(fieldName, fieldType, defaultValue);
|
||||
@ -169,9 +195,18 @@ namespace SonicAudioLib.CriMw.Serialization
|
||||
foreach (PropertyInfo propertyInfo in sortedProperties.Values)
|
||||
{
|
||||
object value = propertyInfo.GetValue(obj);
|
||||
|
||||
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);
|
||||
index++;
|
||||
}
|
||||
@ -250,6 +285,16 @@ namespace SonicAudioLib.CriMw.Serialization
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
@ -378,6 +378,14 @@ namespace SonicAudioLib.IO
|
||||
byte[] buffer = new byte[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);
|
||||
}
|
||||
|
||||
@ -391,5 +399,13 @@ namespace SonicAudioLib.IO
|
||||
byte[] buffer = encoding.GetBytes(value.ToCharArray(), 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;
|
||||
}
|
||||
|
||||
while ((length % align) != 0)
|
||||
{
|
||||
length++;
|
||||
}
|
||||
length = Methods.Align(length, align);
|
||||
|
||||
long position = length;
|
||||
length += data.Length;
|
||||
@ -63,10 +60,7 @@ namespace SonicAudioLib.IO
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((length % align) != 0)
|
||||
{
|
||||
length++;
|
||||
}
|
||||
length = Methods.Align(length, align);
|
||||
|
||||
long position = length;
|
||||
length += stream.Length;
|
||||
@ -82,10 +76,7 @@ namespace SonicAudioLib.IO
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((length % align) != 0)
|
||||
{
|
||||
length++;
|
||||
}
|
||||
length = Methods.Align(length, align);
|
||||
|
||||
long position = length;
|
||||
length += fileInfo.Length;
|
||||
@ -101,10 +92,7 @@ namespace SonicAudioLib.IO
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((length % align) != 0)
|
||||
{
|
||||
length++;
|
||||
}
|
||||
length = Methods.Align(length, align);
|
||||
|
||||
long position = length;
|
||||
length += module.CalculateLength();
|
||||
@ -119,10 +107,7 @@ namespace SonicAudioLib.IO
|
||||
|
||||
foreach (object item in items)
|
||||
{
|
||||
while ((destination.Position % align) != 0)
|
||||
{
|
||||
destination.WriteByte(0);
|
||||
}
|
||||
EndianStream.Pad(destination, align);
|
||||
|
||||
if (item is byte[])
|
||||
{
|
||||
@ -159,6 +144,12 @@ namespace SonicAudioLib.IO
|
||||
{
|
||||
items.Clear();
|
||||
}
|
||||
|
||||
public VldPool(uint align, long baseLength)
|
||||
{
|
||||
this.align = align;
|
||||
length = baseLength;
|
||||
}
|
||||
|
||||
public VldPool(uint align)
|
||||
{
|
||||
|
@ -40,11 +40,11 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Archive\CriAaxArchive.cs" />
|
||||
<Compile Include="Archive\CriAfs2Archive.cs" />
|
||||
<Compile Include="Archive\ArchiveBase.cs" />
|
||||
<Compile Include="Archive\CriCpkArchive.cs" />
|
||||
<Compile Include="Archive\HeroesPacArchive.cs" />
|
||||
<Compile Include="Collections\OrderedDictionary.cs" />
|
||||
<Compile Include="CriMw\CriTable.cs" />
|
||||
<Compile Include="CriMw\CriField.cs" />
|
||||
<Compile Include="CriMw\CriFieldCollection.cs" />
|
||||
@ -60,6 +60,7 @@
|
||||
<Compile Include="IO\EndianStream.cs" />
|
||||
<Compile Include="IO\StringPool.cs" />
|
||||
<Compile Include="IO\Substream.cs" />
|
||||
<Compile Include="IO\Methods.cs" />
|
||||
<Compile Include="IO\VldPool.cs" />
|
||||
<Compile Include="Module\ModuleBase.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user