First work for CPK archives

This commit is contained in:
Skyth 2016-12-25 21:43:22 +03:00
parent e3aa58f135
commit 3cc98ff257
26 changed files with 3133 additions and 74 deletions

View File

@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AcbEditor", "Source\AcbEdit
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsbEditor", "Source\CsbEditor\CsbEditor.csproj", "{91F6B6A6-5D95-4C7A-B22E-A35BD32DB67A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsbBuilder", "Source\CsbBuilder\CsbBuilder.csproj", "{4CF49665-3DFD-4A5C-B231-B97842F091B9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -32,6 +34,10 @@ Global
{91F6B6A6-5D95-4C7A-B22E-A35BD32DB67A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91F6B6A6-5D95-4C7A-B22E-A35BD32DB67A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91F6B6A6-5D95-4C7A-B22E-A35BD32DB67A}.Release|Any CPU.Build.0 = Release|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}.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

View File

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

View File

@ -0,0 +1,48 @@
using System.IO;
using SonicAudioLib.CriMw.Serialization;
using System;
using System.Xml.Serialization;
namespace CsbBuilder
{
[CriSerializable("AAX")]
[Serializable]
public class CriTableAax
{
public enum EnumLoopFlag
{
Intro = 0,
Loop = 1,
};
private byte[] _data = new byte[0];
private EnumLoopFlag _lpflg = EnumLoopFlag.Intro;
[CriField("data", 0)]
public byte[] Data
{
get
{
return _data;
}
set
{
_data = value;
}
}
[CriField("lpflg", 1)]
public byte LoopFlag
{
get
{
return (byte)_lpflg;
}
set
{
_lpflg = (EnumLoopFlag)value;
}
}
}
}

View File

@ -0,0 +1,90 @@
using System.Collections.Generic;
using System.IO;
using SonicAudioLib.CriMw;
using SonicAudioLib.CriMw.Serialization;
using System;
using System.Xml.Serialization;
namespace CsbBuilder
{
[Serializable]
[CriSerializable("TBLISC")]
public class CriTableAisac
{
private string _name = string.Empty;
private string _ptname = string.Empty;
private byte _type = 0;
private byte _rndrng = 0;
[CriField("name", 0)]
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
[CriField("ptname", 1)]
public string PathName
{
get
{
return _ptname;
}
set
{
_ptname = value;
}
}
[CriField("type", 2)]
public byte Type
{
get
{
return _type;
}
set
{
_type = value;
}
}
[CriIgnore]
public List<CriTableAisacGraph> GraphList { get; set; }
[XmlIgnore]
[CriField("grph", 3)]
public byte[] Graph
{
get
{
return CriTableSerializer.Serialize(GraphList, CriTableWriterSettings.AdxSettings);
}
set
{
GraphList = CriTableSerializer.Deserialize<CriTableAisacGraph>(value);
}
}
[CriField("rndrng", 4)]
public byte RandomRange
{
get
{
return _rndrng;
}
set
{
_rndrng = value;
}
}
}
}

View File

@ -0,0 +1,104 @@
using System.Collections.Generic;
using System.IO;
using SonicAudioLib.CriMw;
using SonicAudioLib.CriMw.Serialization;
using System;
using System.Xml.Serialization;
namespace CsbBuilder
{
[Serializable]
[CriSerializable("TBLIGR")]
public class CriTableAisacGraph
{
private byte _type = 0;
private float _imax = 0;
private float _imin = 0;
private float _omax = 0;
private float _omin = 0;
[CriField("type", 0)]
public byte Type
{
get
{
return _type;
}
set
{
_type = value;
}
}
[CriField("imax", 1)]
public float InMaximum
{
get
{
return _imax;
}
set
{
_imax = value;
}
}
[CriField("imin", 2)]
public float InMinimum
{
get
{
return _imin;
}
set
{
_imin = value;
}
}
[CriField("omax", 3)]
public float OutMaximum
{
get
{
return _omax;
}
set
{
_omax = value;
}
}
[CriField("omin", 4)]
public float OutMinimum
{
get
{
return _omin;
}
set
{
_omin = value;
}
}
[CriIgnore]
public List<CriTableAisacPoint> PointsList { get; set; }
[XmlIgnore]
[CriField("points", 5)]
public byte[] Points
{
get
{
return CriTableSerializer.Serialize(PointsList, CriTableWriterSettings.AdxSettings);
}
set
{
PointsList = CriTableSerializer.Deserialize<CriTableAisacPoint>(value);
}
}
}
}

View File

@ -0,0 +1,42 @@
using System.IO;
using SonicAudioLib.CriMw.Serialization;
using System;
using System.Xml.Serialization;
namespace CsbBuilder
{
[Serializable]
[CriSerializable("TBLIPT")]
public class CriTableAisacPoint
{
private ushort _in = 0;
private ushort _out = 0;
[CriField("in", 0)]
public ushort In
{
get
{
return _in;
}
set
{
_in = value;
}
}
[CriField("out", 1)]
public ushort Out
{
get
{
return _out;
}
set
{
_out = value;
}
}
}
}

View File

@ -0,0 +1,84 @@
using System.IO;
using SonicAudioLib.CriMw.Serialization;
using System;
using System.Xml.Serialization;
namespace CsbBuilder
{
[Serializable]
[CriSerializable("TBLCUE")]
public class CriTableCue
{
private string _name = string.Empty;
private uint _id = 0;
private string _synth = string.Empty;
private string _udata = string.Empty;
private byte _flags = 0;
[CriField("name", 0)]
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
[CriField("id", 1)]
public uint Index
{
get
{
return _id;
}
set
{
_id = value;
}
}
[CriField("synth", 2)]
public string Synth
{
get
{
return _synth;
}
set
{
_synth = value;
}
}
[CriField("udata", 3)]
public string UserData
{
get
{
return _udata;
}
set
{
_udata = value;
}
}
[CriField("flags", 4)]
public byte Flags
{
get
{
return _flags;
}
set
{
_flags = value;
}
}
}
}

View File

@ -0,0 +1,134 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using SonicAudioLib.CriMw;
using SonicAudioLib.CriMw.Serialization;
using System.Linq;
using System.Xml.Serialization;
namespace CsbBuilder
{
[Serializable]
[CriSerializable("TBLCSB")]
public class CriTableCueSheet
{
public enum EnumTableType
{
None = 0,
Cue = 1,
Synth = 2,
SoundElement = 4,
Aisac = 5,
VoiceLimitGroup = 6,
VersionInfo = 7,
};
private string _name = string.Empty;
private EnumTableType _ttype = EnumTableType.None;
[CriField("name", 0)]
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
[CriField("ttype", 1)]
public byte TableType
{
get
{
return (byte)_ttype;
}
set
{
_ttype = (EnumTableType)value;
}
}
[CriIgnore]
[XmlArray]
[XmlArrayItem(typeof(CriTableCue))]
[XmlArrayItem(typeof(CriTableSynth))]
[XmlArrayItem(typeof(CriTableSoundElement))]
[XmlArrayItem(typeof(CriTableAisac))]
[XmlArrayItem(typeof(CriTableVoiceLimitGroup))]
[XmlArrayItem(typeof(CriTableVersionInfo))]
public ArrayList DataList { get; set; }
[XmlIgnore]
[CriField("utf", 2)]
public byte[] Data
{
get
{
switch (_ttype)
{
case EnumTableType.None:
return new byte[0];
case EnumTableType.Cue:
return CriTableSerializer.Serialize(DataList.OfType<CriTableCue>().ToList(), CriTableWriterSettings.AdxSettings);
case EnumTableType.Synth:
return CriTableSerializer.Serialize(DataList.OfType<CriTableSynth>().ToList(), CriTableWriterSettings.AdxSettings);
case EnumTableType.SoundElement:
return CriTableSerializer.Serialize(DataList.OfType<CriTableSoundElement>().ToList(), CriTableWriterSettings.AdxSettings);
case EnumTableType.Aisac:
return CriTableSerializer.Serialize(DataList.OfType<CriTableAisac>().ToList(), CriTableWriterSettings.AdxSettings);
case EnumTableType.VoiceLimitGroup:
return CriTableSerializer.Serialize(DataList.OfType<CriTableVoiceLimitGroup>().ToList(), CriTableWriterSettings.AdxSettings);
case EnumTableType.VersionInfo:
return CriTableSerializer.Serialize(DataList.OfType<CriTableVersionInfo>().ToList(), CriTableWriterSettings.AdxSettings);
}
throw new ArgumentException($"Unknown table type {_ttype}, please report the error with the file.", "_ttype");
}
set
{
switch (_ttype)
{
case EnumTableType.Cue:
DataList = CriTableSerializer.Deserialize(value, typeof(CriTableCue));
break;
case EnumTableType.Synth:
DataList = CriTableSerializer.Deserialize(value, typeof(CriTableSynth));
break;
case EnumTableType.SoundElement:
DataList = CriTableSerializer.Deserialize(value, typeof(CriTableSoundElement));
break;
case EnumTableType.Aisac:
DataList = CriTableSerializer.Deserialize(value, typeof(CriTableAisac));
break;
case EnumTableType.VoiceLimitGroup:
DataList = CriTableSerializer.Deserialize(value, typeof(CriTableVoiceLimitGroup));
break;
case EnumTableType.VersionInfo:
DataList = CriTableSerializer.Deserialize(value, typeof(CriTableVersionInfo));
break;
default:
throw new ArgumentException($"Unknown table type {_ttype}, please report the error with the file.", "_ttype");
}
}
}
}
}

View File

@ -0,0 +1,130 @@
using System.Collections.Generic;
using System.IO;
using SonicAudioLib.CriMw;
using SonicAudioLib.CriMw.Serialization;
using System;
using System.Xml.Serialization;
namespace CsbBuilder
{
[Serializable]
[CriSerializable("TBLSDL")]
public class CriTableSoundElement
{
public enum EnumFormat
{
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 uint _sfreq = 0;
private uint _nsmpl = 0;
[CriField("name", 0)]
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
[CriIgnore]
public List<CriTableAax> DataList { get; set; }
[XmlIgnore]
[CriField("data", 1)]
public byte[] Data
{
get
{
return CriTableSerializer.Serialize(DataList, CriTableWriterSettings.AdxSettings);
}
set
{
DataList = CriTableSerializer.Deserialize<CriTableAax>(value);
}
}
[CriField("fmt", 2)]
public byte Format
{
get
{
return (byte)_fmt;
}
set
{
_fmt = (EnumFormat)value;
}
}
[CriField("nch", 3)]
public byte NumberChannels
{
get
{
return _nch;
}
set
{
_nch = value;
}
}
[CriField("stmflg", 4)]
public byte StreamFlag
{
get
{
return (byte)_stmflg;
}
set
{
_stmflg = (EnumStreamFlag)value;
}
}
[CriField("sfreq", 5)]
public uint SoundFrequence
{
get
{
return _sfreq;
}
set
{
_sfreq = value;
}
}
[CriField("nsmpl", 6)]
public uint NumberSamples
{
get
{
return _nsmpl;
}
set
{
_nsmpl = value;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
using System.IO;
using SonicAudioLib.CriMw.Serialization;
using System;
using System.Xml.Serialization;
namespace CsbBuilder
{
[Serializable]
[CriSerializable("TBL_INFO")]
public class CriTableVersionInfo
{
private uint _DataFmtVer = 0x940000;
private uint _ExtSize = 0;
[CriField("DataFmtVer", 0)]
public uint DataFormatVersion
{
get
{
return _DataFmtVer;
}
set
{
_DataFmtVer = value;
}
}
[CriField("ExtSize", 1)]
public uint ExtensionSize
{
get
{
return _ExtSize;
}
set
{
_ExtSize = value;
}
}
}
}

View File

@ -0,0 +1,42 @@
using System.IO;
using SonicAudioLib.CriMw.Serialization;
using System;
using System.Xml.Serialization;
namespace CsbBuilder
{
[Serializable]
[CriSerializable("TBLVLG")]
public class CriTableVoiceLimitGroup
{
private string _vlgname = string.Empty;
private uint _vlgnvoice = 0;
[CriField("vlgname", 0)]
public string VoiceLimitGroupName
{
get
{
return _vlgname;
}
set
{
_vlgname = value;
}
}
[CriField("vlgnvoice", 1)]
public uint VoiceLimitGroupNum
{
get
{
return _vlgnvoice;
}
set
{
_vlgnvoice = value;
}
}
}
}

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{4CF49665-3DFD-4A5C-B231-B97842F091B9}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CsbBuilder</RootNamespace>
<AssemblyName>CsbBuilder</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CriTableAax.cs" />
<Compile Include="CriTableAisac.cs" />
<Compile Include="CriTableAisacGraph.cs" />
<Compile Include="CriTableAisacPoint.cs" />
<Compile Include="CriTableCue.cs" />
<Compile Include="CriTableCueSheet.cs" />
<Compile Include="CriTableSoundElement.cs" />
<Compile Include="CriTableSynth.cs" />
<Compile Include="CriTableVersionInfo.cs" />
<Compile Include="CriTableVoiceLimitGroup.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SonicAudioLib\SonicAudioLib.csproj">
<Project>{63138773-1f47-474c-9345-15eb6183ecc6}</Project>
<Name>SonicAudioLib</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Windows.Forms;
using SonicAudioLib.CriMw;
using SonicAudioLib.CriMw.Serialization;
using SonicAudioLib.IO;
using SonicAudioLib.Archive;
using System.Globalization;
using System.Xml.Serialization;
namespace CsbBuilder
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine(Properties.Resources.Description);
Console.ReadLine();
return;
}
try
{
if (args[0].EndsWith(".csb"))
{
List<CriTableCueSheet> cueSheets = CriTableSerializer.Deserialize<CriTableCueSheet>(args[0]);
//CriTableSerializer.Serialize(args[0] + ".new", cueSheets, CriTableWriterSettings.AdxSettings);
XmlSerializer serializer = new XmlSerializer(typeof(List<CriTableCueSheet>));
using (Stream dest = File.Create(Path.GetFileNameWithoutExtension(args[0]) + ".xml"))
{
serializer.Serialize(dest, cueSheets);
}
}
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]))
{
CriTableSerializer.Serialize(Path.GetFileNameWithoutExtension(args[0]) + ".csb", (List<CriTableCueSheet>)serializer.Deserialize(dest), CriTableWriterSettings.AdxSettings);
}
}
}
catch (Exception exception)
{
//MessageBox.Show($"{exception.Message}", "CSB Builder", MessageBoxButtons.OK, MessageBoxIcon.Error);
throw exception;
}
}
static void ReadAdx(FileInfo fileInfo, out uint sampleRate, out byte numberChannels)
{
using (Stream source = fileInfo.OpenRead())
{
source.Seek(7, SeekOrigin.Begin);
numberChannels = EndianStream.ReadByte(source);
sampleRate = EndianStream.ReadUInt32BE(source);
}
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CsbBuilder")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CsbBuilder")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("91f6b6a6-5d95-4c7a-b22e-a35bd32db67a")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,72 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace CsbBuilder.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CsbBuilder.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to placeholder.
/// </summary>
internal static string Description {
get {
return ResourceManager.GetString("Description", resourceCulture);
}
}
}
}

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Description" xml:space="preserve">
<value>placeholder</value>
</data>
</root>

View File

@ -20,24 +20,48 @@ namespace SonicAudioCmd
class Program
{
static void Main(string[] args)
{
if (args[0].EndsWith(".cpk"))
{
CriCpkArchive archive = new CriCpkArchive();
archive.Load(args[0]);
archive.Print();
foreach (CriCpkEntry entry in archive)
{
using (Stream source = File.OpenRead(args[0]), substream = entry.Open(source))
{
FileInfo fileInfo = new FileInfo(Path.Combine(Path.GetFileNameWithoutExtension(args[0]), entry.DirectoryName, entry.Name));
FileInfo fileInfo = new FileInfo(Path.Combine(Path.GetFileNameWithoutExtension(args[0]), entry.DirectoryName != null ? entry.DirectoryName : "", entry.Name != null ? entry.Name : entry.Id.ToString() + ".bin"));
fileInfo.Directory.Create();
using (Stream destination = fileInfo.Create())
{
substream.CopyTo(destination);
}
fileInfo.LastWriteTime = entry.UpdateDateTime;
}
}
}
Console.ReadLine();
else
{
CriCpkArchive archive = new CriCpkArchive();
archive.Align = 16;
archive.Mode = CriCpkMode.FileName;
uint id = 0;
foreach (string file in Directory.GetFiles(args[0], "*", SearchOption.AllDirectories))
{
CriCpkEntry entry = new CriCpkEntry();
entry.Id = id;
entry.Name = Path.GetFileName(file);
entry.DirectoryName = Path.GetDirectoryName(file.Replace(args[0] + "\\", ""));
entry.FilePath = new FileInfo(file);
archive.Add(entry);
id++;
}
archive.Save(args[0] + ".cpk");
}
}
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using SonicAudioLib.IO;
using SonicAudioLib.Module;
@ -106,5 +107,29 @@ namespace SonicAudioLib.Archive
{
return entries.GetEnumerator();
}
#if DEBUG
public void Print()
{
Type archiveType = GetType();
Console.WriteLine("{0}:", archiveType.Name);
foreach (PropertyInfo property in archiveType.GetProperties().Where(p => p.GetIndexParameters().Length == 0))
{
Console.WriteLine(" {0}: {1}", property.Name, property.GetValue(this));
}
foreach (T entry in entries)
{
Type entryType = entry.GetType();
Console.WriteLine("{0}:", entryType.Name);
foreach (PropertyInfo property in entryType.GetProperties().Where(p => p.GetIndexParameters().Length == 0))
{
Console.WriteLine(" {0}: {1}", property.Name, property.GetValue(entry));
}
}
}
#endif
}
}

View File

@ -11,31 +11,114 @@ namespace SonicAudioLib.Archive
{
public class CriCpkEntry : EntryBase
{
private DateTime updateDateTime;
public string DirectoryName { get; set; }
public string Name { get; set; }
public uint Index { get; set; }
public uint Id { get; set; }
public string Comment { get; set; }
public bool IsCompressed { get; set; }
public DateTime UpdateDateTime
{
get
{
if (FilePath != null)
{
return FilePath.LastWriteTime;
}
return updateDateTime;
}
set
{
updateDateTime = value;
}
}
}
public enum CriCpkMode
{
None = -1,
FileNameIdAndGroup = 5,
IdAndGroup = 4,
FileNameAndGroup = 3,
FileNameAndId = 2,
FileName = 1,
Id = 0,
}
public class CriCpkArchive : ArchiveBase<CriCpkEntry>
{
private ushort align = 1;
private CriCpkMode mode = CriCpkMode.FileName;
public ushort Align
{
get
{
return align;
}
set
{
if (value <= 0)
{
value = 1;
}
align = value;
}
}
public CriCpkMode Mode
{
get
{
return mode;
}
set
{
mode = value;
}
}
public string Comment { get; set; }
public override void Read(Stream source)
{
using (CriTableReader reader = CriCpkSection.Open(source, source.Position))
{
reader.Read();
if (reader.GetUInt32("CpkMode") != 1)
#if DEBUG
for (int i = 0; i < reader.NumberOfFields; i++)
{
throw new Exception("Unsupported CPK type! Only TOC CPKs are supported for now.");
Console.WriteLine("{0} ({1}): {2}", reader.GetFieldName(i), reader.GetFieldFlag(i), reader.GetValue(i));
}
#endif
mode = (CriCpkMode)reader.GetUInt32("CpkMode");
// No need to waste time, stop right there.
if (mode == CriCpkMode.None)
{
return;
}
long tocPosition = (long)reader.GetUInt64("TocOffset");
long itocPosition = (long)reader.GetUInt64("ItocOffset");
long etocPosition = (long)reader.GetUInt64("EtocOffset");
long contentPosition = (long)reader.GetUInt64("ContentOffset");
ushort align = reader.GetUInt16("Align");
align = reader.GetUInt16("Align");
if (mode == CriCpkMode.FileName || mode == CriCpkMode.FileNameAndId)
{
using (CriTableReader tocReader = CriCpkSection.Open(source, tocPosition))
using (CriTableReader etocReader = CriCpkSection.Open(source, etocPosition))
{
while (tocReader.Read())
{
@ -44,7 +127,7 @@ namespace SonicAudioLib.Archive
entry.Name = tocReader.GetString("FileName");
entry.Length = tocReader.GetUInt32("FileSize");
entry.Position = (long)tocReader.GetUInt64("FileOffset");
entry.Index = tocReader.GetUInt32("ID");
entry.Id = tocReader.GetUInt32("ID");
entry.Comment = tocReader.GetString("UserString");
if (entry.Length != tocReader.GetUInt32("ExtractSize"))
@ -67,17 +150,398 @@ namespace SonicAudioLib.Archive
entry.Position++;
}
etocReader.MoveToRow(tocReader.CurrentRow);
entry.UpdateDateTime = DateTimeFromCpkDateTime(etocReader.GetUInt64("UpdateDateTime"));
entries.Add(entry);
}
}
if (mode == CriCpkMode.FileNameAndId)
{
using (CriTableReader itocReader = CriCpkSection.Open(source, itocPosition))
{
while (itocReader.Read())
{
entries[itocReader.GetInt32("TocIndex")].Id = (uint)itocReader.GetInt32("ID");
}
}
}
}
else if (mode == CriCpkMode.Id)
{
long currentPosition = contentPosition;
using (CriTableReader itocReader = CriCpkSection.Open(source, itocPosition))
{
while (itocReader.Read())
{
if (itocReader.GetUInt32("FilesL") > 0)
{
using (CriTableReader dataReader = itocReader.GetCriTableReader("DataL"))
{
while (dataReader.Read())
{
CriCpkEntry entry = new CriCpkEntry();
entry.Id = dataReader.GetUInt16("ID");
entry.Length = dataReader.GetUInt16("FileSize");
if (entry.Length != dataReader.GetUInt16("ExtractSize"))
{
entry.IsCompressed = true;
}
while ((currentPosition % align) != 0)
{
currentPosition++;
}
entry.Position = currentPosition;
entries.Add(entry);
Console.WriteLine(Path.Combine(entry.DirectoryName, entry.Name));
currentPosition += entry.Length;
}
}
}
if (itocReader.GetUInt32("FilesH") > 0)
{
using (CriTableReader dataReader = itocReader.GetCriTableReader("DataH"))
{
while (dataReader.Read())
{
CriCpkEntry entry = new CriCpkEntry();
entry.Id = dataReader.GetUInt16("ID");
entry.Length = dataReader.GetUInt32("FileSize");
if (entry.Length != dataReader.GetUInt32("ExtractSize"))
{
entry.IsCompressed = true;
}
while ((currentPosition % align) != 0)
{
currentPosition++;
}
entry.Position = currentPosition;
entries.Add(entry);
currentPosition += entry.Length;
}
}
}
}
}
}
else
{
throw new Exception($"This CPK mode ({mode}) needs to be implemented. Please report this error with the file.");
}
Comment = reader.GetString("Comment");
}
}
public override void Write(Stream destination)
{
throw new NotImplementedException();
// FIXME: Alignment support (ugh same thing for AFS2 archives)
VldPool vldPool = new VldPool();
using (CriCpkSection cpkSection = new CriCpkSection(destination, "CPK "))
{
cpkSection.Writer.WriteStartTable("CpkHeader");
cpkSection.Writer.WriteField("UpdateDateTime", typeof(ulong));
cpkSection.Writer.WriteField("FileSize", typeof(ulong));
cpkSection.Writer.WriteField("ContentOffset", typeof(ulong));
cpkSection.Writer.WriteField("ContentSize", typeof(ulong));
if (mode == CriCpkMode.FileName || mode == CriCpkMode.FileNameAndId)
{
cpkSection.Writer.WriteField("TocOffset", typeof(ulong));
cpkSection.Writer.WriteField("TocSize", typeof(ulong));
cpkSection.Writer.WriteField("TocCrc", typeof(uint), null);
cpkSection.Writer.WriteField("EtocOffset", typeof(ulong));
cpkSection.Writer.WriteField("EtocSize", typeof(ulong));
}
else
{
cpkSection.Writer.WriteField("TocOffset", typeof(ulong), null);
cpkSection.Writer.WriteField("TocSize", typeof(ulong), null);
cpkSection.Writer.WriteField("TocCrc", typeof(uint), null);
cpkSection.Writer.WriteField("EtocOffset", typeof(ulong), null);
cpkSection.Writer.WriteField("EtocSize", typeof(ulong), null);
}
if (mode == CriCpkMode.Id || mode == CriCpkMode.FileNameAndId)
{
cpkSection.Writer.WriteField("ItocOffset", typeof(ulong));
cpkSection.Writer.WriteField("ItocSize", typeof(ulong));
cpkSection.Writer.WriteField("ItocCrc", typeof(uint), null);
}
else
{
cpkSection.Writer.WriteField("ItocOffset", typeof(ulong), null);
cpkSection.Writer.WriteField("ItocSize", typeof(ulong), null);
cpkSection.Writer.WriteField("ItocCrc", typeof(uint), null);
}
cpkSection.Writer.WriteField("GtocOffset", typeof(ulong), null);
cpkSection.Writer.WriteField("GtocSize", typeof(ulong), null);
cpkSection.Writer.WriteField("GtocCrc", typeof(uint), null);
cpkSection.Writer.WriteField("EnabledPackedSize", typeof(ulong));
cpkSection.Writer.WriteField("EnabledDataSize", typeof(ulong));
cpkSection.Writer.WriteField("TotalDataSize", typeof(ulong), null);
cpkSection.Writer.WriteField("Tocs", typeof(uint), null);
cpkSection.Writer.WriteField("Files", typeof(uint));
cpkSection.Writer.WriteField("Groups", typeof(uint));
cpkSection.Writer.WriteField("Attrs", typeof(uint));
cpkSection.Writer.WriteField("TotalFiles", typeof(uint), null);
cpkSection.Writer.WriteField("Directories", typeof(uint), null);
cpkSection.Writer.WriteField("Updates", typeof(uint), null);
cpkSection.Writer.WriteField("Version", typeof(ushort));
cpkSection.Writer.WriteField("Revision", typeof(ushort));
cpkSection.Writer.WriteField("Align", typeof(ushort));
cpkSection.Writer.WriteField("Sorted", typeof(ushort));
cpkSection.Writer.WriteField("EID", typeof(ushort), null);
cpkSection.Writer.WriteField("CpkMode", typeof(uint));
cpkSection.Writer.WriteField("Tvers", typeof(string));
if (!string.IsNullOrEmpty(Comment))
{
cpkSection.Writer.WriteField("Comment", typeof(string));
}
else
{
cpkSection.Writer.WriteField("Comment", typeof(string), null);
}
cpkSection.Writer.WriteField("Codec", typeof(uint));
cpkSection.Writer.WriteField("DpkItoc", typeof(uint));
MemoryStream tocMemoryStream = null;
MemoryStream itocMemoryStream = null;
MemoryStream etocMemoryStream = null;
if (mode == CriCpkMode.FileName || mode == CriCpkMode.FileNameAndId)
{
tocMemoryStream = new MemoryStream();
etocMemoryStream = new MemoryStream();
using (CriCpkSection tocSection = new CriCpkSection(tocMemoryStream, "TOC "))
using (CriCpkSection etocSection = new CriCpkSection(etocMemoryStream, "ETOC"))
{
tocSection.Writer.WriteStartTable("CpkTocInfo");
tocSection.Writer.WriteField("DirName", typeof(string));
tocSection.Writer.WriteField("FileName", typeof(string));
tocSection.Writer.WriteField("FileSize", typeof(uint));
tocSection.Writer.WriteField("ExtractSize", typeof(uint));
tocSection.Writer.WriteField("FileOffset", typeof(ulong));
tocSection.Writer.WriteField("ID", typeof(uint));
tocSection.Writer.WriteField("UserString", typeof(string));
etocSection.Writer.WriteStartTable("CpkEtocInfo");
etocSection.Writer.WriteField("UpdateDateTime", typeof(ulong));
etocSection.Writer.WriteField("LocalDir", typeof(string));
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.WriteEndTable();
etocSection.Writer.WriteEndTable();
}
if (mode == CriCpkMode.FileNameAndId)
{
itocMemoryStream = new MemoryStream();
using (CriCpkSection itocSection = new CriCpkSection(itocMemoryStream, "ITOC"))
{
itocSection.Writer.WriteStartTable("CpkExtendId");
itocSection.Writer.WriteField("ID", typeof(int));
itocSection.Writer.WriteField("TocIndex", typeof(int));
foreach (CriCpkEntry entry in entries)
{
itocSection.Writer.WriteRow(true, Convert.ToInt32(entry.Id), entries.IndexOf(entry));
}
itocSection.Writer.WriteEndTable();
}
}
}
else if (mode == CriCpkMode.Id)
{
itocMemoryStream = new MemoryStream();
using (CriCpkSection itocSection = new CriCpkSection(itocMemoryStream, "ITOC"))
{
itocSection.Writer.WriteStartTable("CpkItocInfo");
itocSection.Writer.WriteField("FilesL", typeof(uint));
itocSection.Writer.WriteField("FilesH", typeof(uint));
itocSection.Writer.WriteField("DataL", typeof(byte[]));
itocSection.Writer.WriteField("DataH", typeof(byte[]));
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())
using (CriTableWriter dataWriter = CriTableWriter.Create(dataMemoryStream))
{
dataWriter.WriteStartTable("CpkItocL");
dataWriter.WriteField("ID", typeof(ushort));
dataWriter.WriteField("FileSize", typeof(ushort));
dataWriter.WriteField("ExtractSize", typeof(ushort));
foreach (CriCpkEntry entry in filesL)
{
dataWriter.WriteRow(true, id, Convert.ToUInt16(entry.Length), Convert.ToUInt16(entry.Length));
vldPool.Put(entry.FilePath);
id++;
}
dataWriter.WriteEndTable();
itocSection.Writer.WriteValue("DataL", dataMemoryStream.ToArray());
}
using (MemoryStream dataMemoryStream = new MemoryStream())
using (CriTableWriter dataWriter = CriTableWriter.Create(dataMemoryStream))
{
dataWriter.WriteStartTable("CpkItocH");
dataWriter.WriteField("ID", typeof(ushort));
dataWriter.WriteField("FileSize", typeof(uint));
dataWriter.WriteField("ExtractSize", typeof(uint));
foreach (CriCpkEntry entry in filesH)
{
dataWriter.WriteRow(true, id, Convert.ToUInt32(entry.Length), Convert.ToUInt32(entry.Length));
vldPool.Put(entry.FilePath);
id++;
}
dataWriter.WriteEndTable();
itocSection.Writer.WriteValue("DataH", dataMemoryStream.ToArray());
}
itocSection.Writer.WriteValue("FilesL", (uint)filesL.Count);
itocSection.Writer.WriteValue("FilesH", (uint)filesH.Count);
itocSection.Writer.WriteEndRow();
itocSection.Writer.WriteEndTable();
}
}
else
{
throw new Exception($"This CPK mode ({mode}) isn't implemented yet! Please choose another mode.");
}
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));
if (tocMemoryStream != null)
{
cpkSection.Writer.WriteValue("TocOffset", Convert.ToUInt64(2048 + vldPool.Put(tocMemoryStream)));
cpkSection.Writer.WriteValue("TocSize", Convert.ToUInt64(tocMemoryStream.Length));
}
if (itocMemoryStream != null)
{
cpkSection.Writer.WriteValue("ItocOffset", Convert.ToUInt64(2048 + vldPool.Put(itocMemoryStream)));
cpkSection.Writer.WriteValue("ItocSize", Convert.ToUInt64(itocMemoryStream.Length));
}
if (etocMemoryStream != null)
{
cpkSection.Writer.WriteValue("EtocOffset", Convert.ToUInt64(2048 + vldPool.Put(etocMemoryStream)));
cpkSection.Writer.WriteValue("EtocSize", Convert.ToUInt64(etocMemoryStream.Length));
}
uint totalDataSize = 0;
foreach (CriCpkEntry entry in entries)
{
totalDataSize += (uint)entry.Length;
}
cpkSection.Writer.WriteValue("EnabledPackedSize", totalDataSize);
cpkSection.Writer.WriteValue("EnabledDataSize", totalDataSize);
cpkSection.Writer.WriteValue("Files", Convert.ToUInt32(entries.Count));
cpkSection.Writer.WriteValue("Version", (ushort)7);
cpkSection.Writer.WriteValue("Revision", (ushort)2);
cpkSection.Writer.WriteValue("Align", (ushort)1);
cpkSection.Writer.WriteValue("Sorted", (ushort)1);
cpkSection.Writer.WriteValue("CpkMode", (uint)mode);
cpkSection.Writer.WriteValue("Tvers", "SonicAudioLib");
cpkSection.Writer.WriteValue("Comment", Comment);
cpkSection.Writer.WriteValue("FileSize", Convert.ToUInt64(2048 + (ulong)vldPool.Length));
cpkSection.Writer.WriteEndRow();
cpkSection.Writer.WriteEndTable();
}
while ((destination.Position % 2042) != 0)
{
destination.WriteByte(0);
}
EndianStream.WriteCString(destination, "(c)CRI", 6);
vldPool.Write(destination);
}
private DateTime DateTimeFromCpkDateTime(ulong dateTime)
{
if (dateTime == 0)
{
return new DateTime();
}
return new DateTime(
(int)(uint)(dateTime >> 32) >> 16, // year
(int)(uint)(dateTime >> 32) >> 8 & byte.MaxValue, // month
(int)(uint)(dateTime >> 32) & byte.MaxValue, // day
(int)(uint)dateTime >> 24, // hour
(int)(uint)dateTime >> 16 & byte.MaxValue, // minute
(int)(uint)dateTime >> 8 & byte.MaxValue // second
);
}
private ulong CpkDateTimeFromDateTime(DateTime dateTime)
{
return ((((ulong)dateTime.Year * 0x100 + (uint)dateTime.Month) * 0x100 + (uint)dateTime.Day) * 0x100000000) +
((((ulong)dateTime.Hour * 0x100 + (uint)dateTime.Minute) * 0x100 + (uint)dateTime.Second) * 0x100);
}
private class CriCpkSection : IDisposable
@ -100,7 +564,7 @@ namespace SonicAudioLib.Archive
writer.Dispose();
long position = destination.Position;
uint length = (uint)(position - (headerPosition - 8));
uint length = (uint)(position - (headerPosition) - 16);
destination.Seek(headerPosition + 8, SeekOrigin.Begin);
EndianStream.WriteUInt32(destination, length);
@ -117,9 +581,17 @@ namespace SonicAudioLib.Archive
uint tableLength = EndianStream.ReadUInt32(source);
uint unknown = EndianStream.ReadUInt32(source);
try
{
return CriTableReader.Create(new Substream(source, source.Position, tableLength));
}
catch
{
throw new Exception("CPK file decryption needs to be implemented or it's an unknown error. Please report this error with the file.");
}
}
public CriCpkSection(Stream destination, string signature)
{
this.destination = destination;
@ -127,9 +599,9 @@ namespace SonicAudioLib.Archive
EndianStream.WriteCString(destination, signature, 4);
EndianStream.WriteUInt32(destination, byte.MaxValue);
destination.Seek(8, SeekOrigin.Begin);
destination.Seek(8, SeekOrigin.Current);
writer = CriTableWriter.Create(destination);
writer = CriTableWriter.Create(destination, new CriTableWriterSettings() { LeaveOpen = true });
}
}
}

View File

@ -101,9 +101,23 @@ namespace SonicAudioLib.CriMw
writer.WriteStartFieldCollection();
foreach (CriField criField in fields)
{
if (!rows.Any(row => row[criField] != criField.DefaultValue))
bool useDefaultValue = false;
object defaultValue = null;
if (rows.Count > 1)
{
writer.WriteField(criField.FieldName, criField.FieldType, criField.DefaultValue);
useDefaultValue = true;
defaultValue = rows[0][criField];
if (rows.Any(row => !row[criField].Equals(defaultValue)))
{
useDefaultValue = false;
}
}
if (useDefaultValue)
{
writer.WriteField(criField.FieldName, criField.FieldType, defaultValue);
}
else

View File

@ -166,14 +166,14 @@ namespace SonicAudioLib.CriMw
return fields[fieldIndex].Value;
}
public byte GetFieldFlag(string fieldName)
internal CriFieldFlag GetFieldFlag(string fieldName)
{
return (byte)fields[fieldName].Flag;
return fields[fieldName].Flag;
}
public byte GetFieldFlag(int fieldIndex)
internal CriFieldFlag GetFieldFlag(int fieldIndex)
{
return (byte)fields[fieldIndex].Flag;
return fields[fieldIndex].Flag;
}
public object GetFieldValue(string fieldName)
@ -438,6 +438,16 @@ namespace SonicAudioLib.CriMw
return GetData(fields.IndexOf(fieldName));
}
public CriTableReader GetCriTableReader(string fieldName)
{
return new CriTableReader(GetSubstream(fieldName), false);
}
public CriTableReader GetCriTableReader(int fieldIndex)
{
return new CriTableReader(GetSubstream(fieldIndex), false);
}
public uint GetLength(int fieldIndex)
{
if (fieldIndex < 0 || fieldIndex >= fields.Count)
@ -583,7 +593,7 @@ namespace SonicAudioLib.CriMw
if (strResult == "<NULL>" ||
(strResult == header.TableName && stringPosition == 0))
{
return null;
return string.Empty;
}
return strResult;

View File

@ -28,7 +28,6 @@ namespace SonicAudioLib.CriMw
private VldPool vldPool;
private StringPool stringPool;
private uint headerPosition;
private uint endPosition;
private Status status = Status.Begin;
@ -115,6 +114,8 @@ namespace SonicAudioLib.CriMw
destination.WriteByte(0);
}
long previousPosition = destination.Position;
header.Length = (uint)destination.Position - headerPosition;
header.FirstBoolean = false;
@ -131,7 +132,7 @@ namespace SonicAudioLib.CriMw
WriteUInt16(header.NumberOfFields);
WriteUInt16(header.RowLength);
WriteUInt32(header.NumberOfRows);
destination.Seek(0, SeekOrigin.End);
destination.Seek(previousPosition, SeekOrigin.Begin);
}
public void WriteStartFieldCollection()
@ -269,7 +270,7 @@ namespace SonicAudioLib.CriMw
public void WriteValue(string fieldName, object rowValue)
{
WriteValue(fields.IndexOf(fieldName));
WriteValue(fields.IndexOf(fieldName), rowValue);
}
private void GoToValue(int fieldIndex)
@ -654,7 +655,7 @@ namespace SonicAudioLib.CriMw
set
{
leaveOpen = true;
leaveOpen = value;
}
}
@ -690,7 +691,7 @@ namespace SonicAudioLib.CriMw
{
return new CriTableWriterSettings()
{
Align = 4,
Align = 8,
PutBlankString = true,
RemoveDuplicateStrings = true,
};

View File

@ -14,6 +14,28 @@ namespace SonicAudioLib.CriMw.Serialization
{
public static class CriTableSerializer
{
public static byte[] Serialize<T>(List<T> objects, CriTableWriterSettings settings)
{
using (MemoryStream destination = new MemoryStream())
{
Serialize(destination, objects, settings);
return destination.ToArray();
}
}
public static void Serialize<T>(string destinationFileName, List<T> objects, CriTableWriterSettings settings)
{
using (Stream destination = File.Create(destinationFileName))
{
Serialize(destination, objects, settings);
}
}
public static void Serialize<T>(Stream destination, List<T> objects, CriTableWriterSettings settings)
{
Serialize(destination, typeof(T), objects, settings);
}
public static void Serialize(Stream destination, Type type, ICollection objects, CriTableWriterSettings settings)
{
ArrayList arrayList = null;
@ -45,7 +67,7 @@ namespace SonicAudioLib.CriMw.Serialization
continue;
}
// Also ignore the properties that are not supportable (except FileInfo, Stream and ICollection<>)
// Also ignore the properties that are not supportable (except FileInfo and Stream)
if (propertyInfo.PropertyType != typeof(FileInfo) &&
propertyInfo.PropertyType != typeof(Stream) &&
!CriField.FieldTypes.Contains(propertyInfo.PropertyType))
@ -94,25 +116,32 @@ namespace SonicAudioLib.CriMw.Serialization
}
}
// Checks if all the rows have the same value for this field
bool useDefaultValue = true;
bool useDefaultValue = false;
if (arrayList != null && arrayList.Count > 0)
if (arrayList != null && arrayList.Count > 1)
{
useDefaultValue = true;
foreach (object obj in arrayList)
defaultValue = propertyInfo.GetValue(arrayList[0]);
for (int i = 1; i < arrayList.Count; i++)
{
if (propertyInfo.GetValue(obj) != propertyInfo.GetValue(arrayList[0]))
object objectValue = propertyInfo.GetValue(arrayList[i]);
if (defaultValue != null)
{
if (!defaultValue.Equals(objectValue))
{
useDefaultValue = false;
defaultValue = null;
break;
}
}
if (useDefaultValue)
else if (objectValue != null)
{
defaultValue = propertyInfo.GetValue(arrayList[0]);
useDefaultValue = false;
defaultValue = null;
break;
}
}
}
@ -155,6 +184,37 @@ namespace SonicAudioLib.CriMw.Serialization
tableWriter.Dispose();
}
public static List<T> Deserialize<T>(byte[] sourceByteArray)
{
return Deserialize(sourceByteArray, typeof(T)).OfType<T>().ToList();
}
public static List<T> Deserialize<T>(string sourceFileName)
{
return Deserialize(sourceFileName, typeof(T)).OfType<T>().ToList();
}
public static List<T> Deserialize<T>(Stream source)
{
return Deserialize(source, typeof(T)).OfType<T>().ToList();
}
public static ArrayList Deserialize(byte[] sourceByteArray, Type type)
{
using (MemoryStream source = new MemoryStream(sourceByteArray))
{
return Deserialize(source, type);
}
}
public static ArrayList Deserialize(string sourceFileName, Type type)
{
using (Stream source = File.OpenRead(sourceFileName))
{
return Deserialize(source, type);
}
}
public static ArrayList Deserialize(Stream source, Type type)
{
ArrayList arrayList = new ArrayList();
@ -185,6 +245,11 @@ namespace SonicAudioLib.CriMw.Serialization
if (fieldName == fieldNameMatch)
{
object value = tableReader.GetValue(i);
if (propertyInfo.PropertyType == typeof(byte[]) && value is Substream)
{
value = ((Substream)value).ToArray();
}
propertyInfo.SetValue(obj, value);
break;
}