mirror of
https://github.com/blueskythlikesclouds/SonicAudioTools.git
synced 2024-11-27 17:00:53 +01:00
Initial commit
This commit is contained in:
commit
e3aa58f135
63
.gitattributes
vendored
Normal file
63
.gitattributes
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
244
.gitignore
vendored
Normal file
244
.gitignore
vendored
Normal file
@ -0,0 +1,244 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]eleases/
|
||||
[Xx]64/
|
||||
[Xx]86/
|
||||
[Bb]uild/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# DNX
|
||||
project.lock.json
|
||||
artifacts/
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
|
||||
# TODO: Un-comment the next line if you do not want to checkin
|
||||
# your web deploy settings because they may include unencrypted
|
||||
# passwords
|
||||
#*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignoreable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Microsoft Azure ApplicationInsights config file
|
||||
ApplicationInsights.config
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
orleans.codegen.cs
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# LightSwitch generated files
|
||||
GeneratedArtifacts/
|
||||
ModelManifest.xml
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Skyth
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
BIN
Release/AcbEditor.exe
Normal file
BIN
Release/AcbEditor.exe
Normal file
Binary file not shown.
BIN
Release/CsbEditor.exe
Normal file
BIN
Release/CsbEditor.exe
Normal file
Binary file not shown.
BIN
Release/SonicAudioLib.dll
Normal file
BIN
Release/SonicAudioLib.dll
Normal file
Binary file not shown.
39
SonicAudioTools.sln
Normal file
39
SonicAudioTools.sln
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SonicAudioLib", "Source\SonicAudioLib\SonicAudioLib.csproj", "{63138773-1F47-474C-9345-15EB6183ECC6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SonicAudioCmd", "Source\SonicAudioCmd\SonicAudioCmd.csproj", "{C412FC07-0C07-4361-88BA-C9C182CF8D70}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AcbEditor", "Source\AcbEditor\AcbEditor.csproj", "{45A72DAF-370A-42D1-833E-CEE27EA5E311}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsbEditor", "Source\CsbEditor\CsbEditor.csproj", "{91F6B6A6-5D95-4C7A-B22E-A35BD32DB67A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{63138773-1F47-474C-9345-15EB6183ECC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{63138773-1F47-474C-9345-15EB6183ECC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{63138773-1F47-474C-9345-15EB6183ECC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{63138773-1F47-474C-9345-15EB6183ECC6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C412FC07-0C07-4361-88BA-C9C182CF8D70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C412FC07-0C07-4361-88BA-C9C182CF8D70}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C412FC07-0C07-4361-88BA-C9C182CF8D70}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{45A72DAF-370A-42D1-833E-CEE27EA5E311}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{45A72DAF-370A-42D1-833E-CEE27EA5E311}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{45A72DAF-370A-42D1-833E-CEE27EA5E311}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{45A72DAF-370A-42D1-833E-CEE27EA5E311}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{91F6B6A6-5D95-4C7A-B22E-A35BD32DB67A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{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
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
78
Source/AcbEditor/AcbEditor.csproj
Normal file
78
Source/AcbEditor/AcbEditor.csproj
Normal file
@ -0,0 +1,78 @@
|
||||
<?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>{45A72DAF-370A-42D1-833E-CEE27EA5E311}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>AcbEditor</RootNamespace>
|
||||
<AssemblyName>AcbEditor</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="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>
|
6
Source/AcbEditor/App.config
Normal file
6
Source/AcbEditor/App.config
Normal 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>
|
272
Source/AcbEditor/Program.cs
Normal file
272
Source/AcbEditor/Program.cs
Normal file
@ -0,0 +1,272 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using SonicAudioLib.CriMw;
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.Archive;
|
||||
|
||||
namespace AcbEditor
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
Console.WriteLine(Properties.Resources.Description);
|
||||
Console.ReadLine();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (args[0].EndsWith(".acb"))
|
||||
{
|
||||
string baseDirectory = Path.GetDirectoryName(args[0]);
|
||||
string outputDirectoryPath = Path.Combine(baseDirectory, Path.GetFileNameWithoutExtension(args[0]));
|
||||
string extAfs2ArchivePath = string.Empty;
|
||||
|
||||
Directory.CreateDirectory(outputDirectoryPath);
|
||||
|
||||
using (CriTableReader acbReader = CriTableReader.Create(args[0]))
|
||||
{
|
||||
acbReader.Read();
|
||||
|
||||
CriAfs2Archive afs2Archive = new CriAfs2Archive();
|
||||
CriAfs2Archive extAfs2Archive = new CriAfs2Archive();
|
||||
|
||||
if (acbReader.GetLength("AwbFile") > 0)
|
||||
{
|
||||
using (Substream afs2Stream = acbReader.GetSubstream("AwbFile"))
|
||||
{
|
||||
afs2Archive.Read(afs2Stream);
|
||||
}
|
||||
}
|
||||
|
||||
if (acbReader.GetLength("StreamAwbAfs2Header") > 0)
|
||||
{
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
using (Substream waveformTableStream = acbReader.GetSubstream("WaveformTable"))
|
||||
using (CriTableReader waveformReader = CriTableReader.Create(waveformTableStream))
|
||||
{
|
||||
while (waveformReader.Read())
|
||||
{
|
||||
ushort index = waveformReader.GetUInt16("Id");
|
||||
byte encodeType = waveformReader.GetByte("EncodeType");
|
||||
bool streaming = waveformReader.GetBoolean("Streaming");
|
||||
|
||||
string outputName = index.ToString("D5");
|
||||
if (streaming)
|
||||
{
|
||||
outputName += "_streaming";
|
||||
}
|
||||
|
||||
outputName += GetExtension(encodeType);
|
||||
outputName = Path.Combine(outputDirectoryPath, outputName);
|
||||
|
||||
Console.WriteLine("Extracting {0} file with index {1}...", GetExtension(encodeType).ToUpper(), index);
|
||||
|
||||
if (streaming)
|
||||
{
|
||||
CriAfs2Entry afs2Entry = extAfs2Archive.GetByCueIndex(index);
|
||||
|
||||
using (Stream extAfs2Stream = File.OpenRead(extAfs2ArchivePath))
|
||||
using (Stream afs2EntryStream = afs2Entry.Open(extAfs2Stream))
|
||||
using (Stream afs2EntryDestination = File.Create(outputName))
|
||||
{
|
||||
afs2EntryStream.CopyTo(afs2EntryDestination);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
CriAfs2Entry entry = afs2Archive.GetByCueIndex(index);
|
||||
|
||||
using (Substream afs2Stream = acbReader.GetSubstream("AwbFile"))
|
||||
using (Stream afs2EntryStream = entry.Open(afs2Stream))
|
||||
using (Stream afs2EntryDestination = File.Create(outputName))
|
||||
{
|
||||
afs2EntryStream.CopyTo(afs2EntryDestination);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (File.GetAttributes(args[0]).HasFlag(FileAttributes.Directory))
|
||||
{
|
||||
string baseDirectory = Path.GetDirectoryName(args[0]);
|
||||
string acbPath = args[0] + ".acb";
|
||||
|
||||
string awbPath = args[0] + "_streamfiles.awb";
|
||||
bool found = File.Exists(awbPath);
|
||||
|
||||
if (!found)
|
||||
{
|
||||
awbPath = args[0] + "_STR.awb";
|
||||
found = File.Exists(awbPath);
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
awbPath = args[0] + ".awb";
|
||||
}
|
||||
|
||||
if (!File.Exists(acbPath))
|
||||
{
|
||||
throw new Exception("Cannot find the .ACB file for this directory. Please ensure that the .ACB file is stored in the directory where this directory is.");
|
||||
}
|
||||
|
||||
CriTable acbFile = new CriTable();
|
||||
acbFile.Load(acbPath);
|
||||
|
||||
CriAfs2Archive afs2Archive = new CriAfs2Archive();
|
||||
CriAfs2Archive extAfs2Archive = new CriAfs2Archive();
|
||||
|
||||
using (CriTableReader reader = CriTableReader.Create((byte[])acbFile.Rows[0]["WaveformTable"]))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
ushort index = reader.GetUInt16("Id");
|
||||
byte encodeType = reader.GetByte("EncodeType");
|
||||
bool streaming = reader.GetBoolean("Streaming");
|
||||
|
||||
string inputName = index.ToString("D5");
|
||||
if (streaming)
|
||||
{
|
||||
inputName += "_streaming";
|
||||
}
|
||||
|
||||
inputName += GetExtension(encodeType);
|
||||
inputName = Path.Combine(args[0], inputName);
|
||||
|
||||
if (!File.Exists(inputName))
|
||||
{
|
||||
throw new Exception($"Cannot find audio file with index {index} 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)
|
||||
{
|
||||
extAfs2Archive.Add(entry);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
afs2Archive.Add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
acbFile.Rows[0]["AwbFile"] = null;
|
||||
acbFile.Rows[0]["StreamAwbAfs2Header"] = null;
|
||||
|
||||
if (afs2Archive.Count > 0)
|
||||
{
|
||||
afs2Archive.Order();
|
||||
|
||||
Console.WriteLine("Saving internal AWB...");
|
||||
acbFile.Rows[0]["AwbFile"] = afs2Archive.Save();
|
||||
}
|
||||
|
||||
if (extAfs2Archive.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))
|
||||
{
|
||||
fileStream.Read(afs2Header, 0, afs2Header.Length);
|
||||
}
|
||||
|
||||
acbFile.Rows[0]["StreamAwbAfs2Header"] = afs2Header;
|
||||
}
|
||||
|
||||
acbFile.WriterSettings = CriTableWriterSettings.Adx2Settings;
|
||||
acbFile.Save(acbPath);
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception exception)
|
||||
{
|
||||
MessageBox.Show($"{exception.Message}", "ACB Editor", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
static string GetExtension(byte encodeType)
|
||||
{
|
||||
switch (encodeType)
|
||||
{
|
||||
case 0:
|
||||
case 3:
|
||||
return ".adx";
|
||||
case 1:
|
||||
return ".ahx";
|
||||
case 2:
|
||||
return ".hca";
|
||||
case 4:
|
||||
return ".wiiadpcm";
|
||||
case 5:
|
||||
return ".dsadpcm";
|
||||
case 6:
|
||||
return ".hcamx";
|
||||
case 10:
|
||||
case 7:
|
||||
return ".vag";
|
||||
case 8:
|
||||
return ".at3";
|
||||
case 9:
|
||||
return ".3dsadpcm";
|
||||
case 18:
|
||||
case 11:
|
||||
return ".at9";
|
||||
case 12:
|
||||
return ".xma";
|
||||
case 13:
|
||||
return ".wiiuadpcm";
|
||||
default:
|
||||
return ".bin";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
Source/AcbEditor/Properties/AssemblyInfo.cs
Normal file
36
Source/AcbEditor/Properties/AssemblyInfo.cs
Normal 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("AcbEditor")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("AcbEditor")]
|
||||
[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("45a72daf-370a-42d1-833e-cee27ea5e311")]
|
||||
|
||||
// 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")]
|
86
Source/AcbEditor/Properties/Resources.Designer.cs
generated
Normal file
86
Source/AcbEditor/Properties/Resources.Designer.cs
generated
Normal file
@ -0,0 +1,86 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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 AcbEditor.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("AcbEditor.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 ACB Editor
|
||||
///==========
|
||||
///
|
||||
///Usage:
|
||||
///Drag and drop an .ACB file to unpack its contents to a directory.
|
||||
///The directory will have the same name as the .ACB file, but without
|
||||
///its extension.
|
||||
///
|
||||
///In the directory, there will be the audio files, extracted straight
|
||||
///from the .ACB file (and the external .AWB file if it is present.)
|
||||
///Files with "_streaming" suffix means that it was extracted from the
|
||||
///external .AWB file.
|
||||
///
|
||||
///You can edit those files as you want, but you shouldn't rename the
|
||||
///files, or remove any of them [rest of string was truncated]";.
|
||||
/// </summary>
|
||||
internal static string Description {
|
||||
get {
|
||||
return ResourceManager.GetString("Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
142
Source/AcbEditor/Properties/Resources.resx
Normal file
142
Source/AcbEditor/Properties/Resources.resx
Normal file
@ -0,0 +1,142 @@
|
||||
<?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>ACB Editor
|
||||
==========
|
||||
|
||||
Usage:
|
||||
Drag and drop an .ACB file to unpack its contents to a directory.
|
||||
The directory will have the same name as the .ACB file, but without
|
||||
its extension.
|
||||
|
||||
In the directory, there will be the audio files, extracted straight
|
||||
from the .ACB file (and the external .AWB file if it is present.)
|
||||
Files with "_streaming" suffix means that it was extracted from the
|
||||
external .AWB file.
|
||||
|
||||
You can edit those files as you want, but you shouldn't rename the
|
||||
files, or remove any of them.
|
||||
|
||||
To pack the .ACB file back, you gotta have the extracted directory
|
||||
and the .ACB file in the same directory, and also the external .AWB
|
||||
file if it exists. Drag and drop the directory to the .EXE file,
|
||||
it will collect all the files inside directory and pack them back.</value>
|
||||
</data>
|
||||
</root>
|
6
Source/CsbEditor/App.config
Normal file
6
Source/CsbEditor/App.config
Normal 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>
|
78
Source/CsbEditor/CsbEditor.csproj
Normal file
78
Source/CsbEditor/CsbEditor.csproj
Normal file
@ -0,0 +1,78 @@
|
||||
<?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>{91F6B6A6-5D95-4C7A-B22E-A35BD32DB67A}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>CsbEditor</RootNamespace>
|
||||
<AssemblyName>CsbEditor</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="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>
|
169
Source/CsbEditor/Program.cs
Normal file
169
Source/CsbEditor/Program.cs
Normal file
@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using SonicAudioLib.CriMw;
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.Archive;
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
namespace CsbEditor
|
||||
{
|
||||
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"))
|
||||
{
|
||||
string baseDirectory = Path.GetDirectoryName(args[0]);
|
||||
string outputDirectoryName = Path.Combine(baseDirectory, Path.GetFileNameWithoutExtension(args[0]));
|
||||
|
||||
using (CriTableReader reader = CriTableReader.Create(args[0]))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.GetString("name") == "SOUND_ELEMENT")
|
||||
{
|
||||
using (CriTableReader sdlReader = CriTableReader.Create(reader.GetSubstream("utf")))
|
||||
{
|
||||
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.");
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
source.CopyTo(destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (File.GetAttributes(args[0]).HasFlag(FileAttributes.Directory))
|
||||
{
|
||||
string baseDirectory = Path.GetDirectoryName(args[0]);
|
||||
string csbPath = args[0] + ".csb";
|
||||
|
||||
if (!File.Exists(csbPath))
|
||||
{
|
||||
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.");
|
||||
}
|
||||
|
||||
CriTable csbFile = new CriTable();
|
||||
csbFile.Load(csbPath);
|
||||
|
||||
CriRow soundElementRow = csbFile.Rows.Single(row => (string)row["name"] == "SOUND_ELEMENT");
|
||||
|
||||
CriTable soundElementTable = new CriTable();
|
||||
soundElementTable.Load((byte[])soundElementRow["utf"]);
|
||||
|
||||
foreach (CriRow sdlRow in soundElementTable.Rows)
|
||||
{
|
||||
string sdlName = (string)sdlRow["name"];
|
||||
|
||||
DirectoryInfo sdlDirectory = new DirectoryInfo(Path.Combine(args[0], sdlName));
|
||||
|
||||
if (!sdlDirectory.Exists)
|
||||
{
|
||||
throw new Exception($"Cannot find sound element directory for replacement.\nPath attempt: {sdlDirectory.FullName}");
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
writer.WriteStartTable("AAX");
|
||||
|
||||
writer.WriteField("data", typeof(byte[]));
|
||||
writer.WriteField("lpflg", typeof(byte));
|
||||
|
||||
foreach (FileInfo audioFile in sdlDirectory.GetFiles("*.adx"))
|
||||
{
|
||||
// In Turkish, lowercase I is ı so you get the idea
|
||||
if (audioFile.Name.ToLower(CultureInfo.GetCultureInfo("en-US")) == "intro.adx")
|
||||
{
|
||||
ReadAdx(audioFile, out sampleRate, out numberChannels);
|
||||
writer.WriteRow(true, audioFile, (byte)0);
|
||||
}
|
||||
|
||||
else if (audioFile.Name.ToLower() == "loop.adx")
|
||||
{
|
||||
ReadAdx(audioFile, out sampleRate, out numberChannels);
|
||||
writer.WriteRow(true, audioFile, (byte)1);
|
||||
}
|
||||
}
|
||||
|
||||
writer.WriteEndTable();
|
||||
sdlRow["data"] = memoryStream.ToArray();
|
||||
}
|
||||
|
||||
sdlRow["sfreq"] = sampleRate;
|
||||
sdlRow["nch"] = numberChannels;
|
||||
}
|
||||
|
||||
soundElementTable.WriterSettings = CriTableWriterSettings.AdxSettings;
|
||||
soundElementRow["utf"] = soundElementTable.Save();
|
||||
|
||||
csbFile.WriterSettings = CriTableWriterSettings.AdxSettings;
|
||||
csbFile.Save(args[0] + ".csb");
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception exception)
|
||||
{
|
||||
MessageBox.Show($"{exception.Message}", "CSB Editor", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
Source/CsbEditor/Properties/AssemblyInfo.cs
Normal file
36
Source/CsbEditor/Properties/AssemblyInfo.cs
Normal 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("CsbEditor")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("CsbEditor")]
|
||||
[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")]
|
86
Source/CsbEditor/Properties/Resources.Designer.cs
generated
Normal file
86
Source/CsbEditor/Properties/Resources.Designer.cs
generated
Normal file
@ -0,0 +1,86 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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 CsbEditor.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("CsbEditor.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 CSB Editor
|
||||
///==========
|
||||
///
|
||||
///Usage:
|
||||
///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.
|
||||
///
|
||||
///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]";.
|
||||
/// </summary>
|
||||
internal static string Description {
|
||||
get {
|
||||
return ResourceManager.GetString("Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
142
Source/CsbEditor/Properties/Resources.resx
Normal file
142
Source/CsbEditor/Properties/Resources.resx
Normal file
@ -0,0 +1,142 @@
|
||||
<?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>CSB Editor
|
||||
==========
|
||||
|
||||
Usage:
|
||||
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.
|
||||
|
||||
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 channel count than original.
|
||||
|
||||
To pack the .CSB file back, you gotta have the extracted directory
|
||||
and the .CSB file in the same directory. Drag and drop the directory
|
||||
to the .EXE file, it will collect all the files inside directory
|
||||
and pack them back.</value>
|
||||
</data>
|
||||
</root>
|
6
Source/SonicAudioCmd/App.config
Normal file
6
Source/SonicAudioCmd/App.config
Normal 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>
|
43
Source/SonicAudioCmd/Program.cs
Normal file
43
Source/SonicAudioCmd/Program.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.ComponentModel;
|
||||
using System.Collections;
|
||||
|
||||
using SonicAudioLib;
|
||||
using SonicAudioLib.Archive;
|
||||
using SonicAudioLib.Collections;
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.CriMw;
|
||||
|
||||
using System.Xml;
|
||||
|
||||
namespace SonicAudioCmd
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
CriCpkArchive archive = new CriCpkArchive();
|
||||
archive.Load(args[0]);
|
||||
|
||||
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.Directory.Create();
|
||||
using (Stream destination = fileInfo.Create())
|
||||
{
|
||||
substream.CopyTo(destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
}
|
36
Source/SonicAudioCmd/Properties/AssemblyInfo.cs
Normal file
36
Source/SonicAudioCmd/Properties/AssemblyInfo.cs
Normal 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("SonicAudioCmd")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("SonicAudioCmd")]
|
||||
[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("c412fc07-0c07-4361-88ba-c9c182cf8d70")]
|
||||
|
||||
// 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")]
|
85
Source/SonicAudioCmd/Properties/Resources.Designer.cs
generated
Normal file
85
Source/SonicAudioCmd/Properties/Resources.Designer.cs
generated
Normal file
@ -0,0 +1,85 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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 SonicAudioCmd.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("SonicAudioCmd.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 CSB Compiler/Extractor
|
||||
///
|
||||
///Usage: [fileName]
|
||||
///
|
||||
///This tool will extract the contents of a CSB file into a folder,
|
||||
///resulting also an XML file. Just simply drag and drop your CSB file.
|
||||
///You can modify the .ADX files inside as you want!
|
||||
///
|
||||
///In the XML file, do not touch the paths, but modify the SampleRate
|
||||
///or NumberChannels elements if your custom ADX has a different
|
||||
///sample rate or channel count than the original. Otherwise, the
|
||||
///modifications in game will result pitched/faster/slower.
|
||||
///
|
||||
///To compile the CSB ba [rest of string was truncated]";.
|
||||
/// </summary>
|
||||
internal static string HelpText {
|
||||
get {
|
||||
return ResourceManager.GetString("HelpText", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
139
Source/SonicAudioCmd/Properties/Resources.resx
Normal file
139
Source/SonicAudioCmd/Properties/Resources.resx
Normal file
@ -0,0 +1,139 @@
|
||||
<?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="HelpText" xml:space="preserve">
|
||||
<value>CSB Compiler/Extractor
|
||||
|
||||
Usage: [fileName]
|
||||
|
||||
This tool will extract the contents of a CSB file into a folder,
|
||||
resulting also an XML file. Just simply drag and drop your CSB file.
|
||||
You can modify the .ADX files inside as you want!
|
||||
|
||||
In the XML file, do not touch the paths, but modify the SampleRate
|
||||
or NumberChannels elements if your custom ADX has a different
|
||||
sample rate or channel count than the original. Otherwise, the
|
||||
modifications in game will result pitched/faster/slower.
|
||||
|
||||
To compile the CSB back, you gotta have your extracted folder,
|
||||
CSB file and XML file in the same folder. Drag and drop the XML
|
||||
to the tool, it will load all the ADXs and compile them back to
|
||||
the CSB file. (The existing CSB file will be overwritten!)</value>
|
||||
</data>
|
||||
</root>
|
77
Source/SonicAudioCmd/SonicAudioCmd.csproj
Normal file
77
Source/SonicAudioCmd/SonicAudioCmd.csproj
Normal file
@ -0,0 +1,77 @@
|
||||
<?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>{C412FC07-0C07-4361-88BA-C9C182CF8D70}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SonicAudioCmd</RootNamespace>
|
||||
<AssemblyName>SonicAudioCmd</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.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="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>
|
43
Source/SonicAudioCmd/acb_extractor.txt
Normal file
43
Source/SonicAudioCmd/acb_extractor.txt
Normal file
@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.ComponentModel;
|
||||
using System.Collections;
|
||||
|
||||
using SonicAudioLib;
|
||||
using SonicAudioLib.Archive;
|
||||
using SonicAudioLib.Collections;
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.CriMw;
|
||||
|
||||
using System.Xml;
|
||||
|
||||
namespace SonicAudioCmd
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
using (CriTableReader reader = new CriTableReader(args[0]))
|
||||
{
|
||||
reader.Read();
|
||||
|
||||
using (Stream substream = reader.GetSubstream("AwbFile"))
|
||||
{
|
||||
Afs2Archive archive = new Afs2Archive(substream);
|
||||
|
||||
foreach (Afs2Entry entry in archive.Entries)
|
||||
{
|
||||
using (Stream entryStream = entry.Open(substream), outStream = File.Create(entry.CueIndex.ToString() + ".hca"))
|
||||
{
|
||||
entryStream.CopyTo(outStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
171
Source/SonicAudioCmd/csb_file_replacer.txt
Normal file
171
Source/SonicAudioCmd/csb_file_replacer.txt
Normal file
@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.ComponentModel;
|
||||
using System.Collections;
|
||||
|
||||
using SonicAudioLib;
|
||||
using SonicAudioLib.Archive;
|
||||
using SonicAudioLib.Collections;
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.CriMw;
|
||||
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace SonicAudioCmd
|
||||
{
|
||||
[Serializable]
|
||||
public class SoundElement
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public uint SampleRate { get; set; }
|
||||
public byte NumberChannels { get; set; }
|
||||
public List<string> Sounds = new List<string>();
|
||||
}
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
Console.WriteLine(Properties.Resources.HelpText);
|
||||
Console.ReadLine();
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0].EndsWith(".csb"))
|
||||
{
|
||||
string directoryName = Path.Combine(Path.GetDirectoryName(args[0]), Path.GetFileNameWithoutExtension(args[0]));
|
||||
|
||||
List<SoundElement> soundElements = new List<SoundElement>();
|
||||
using (CriTableReader reader = CriTableReader.Create(args[0]))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.GetString("name") == "SOUND_ELEMENT")
|
||||
{
|
||||
using (CriTableReader sdlReader = CriTableReader.Create(reader.GetSubstream("utf")))
|
||||
{
|
||||
while (sdlReader.Read())
|
||||
{
|
||||
SoundElement soundElement = new SoundElement();
|
||||
soundElement.Path = sdlReader.GetString("name");
|
||||
soundElement.SampleRate = sdlReader.GetUInt32("sfreq");
|
||||
soundElement.NumberChannels = sdlReader.GetByte("nch");
|
||||
using (CriTableReader aaxReader = CriTableReader.Create(sdlReader.GetSubstream("data")))
|
||||
{
|
||||
while (aaxReader.Read())
|
||||
{
|
||||
string fileName = Path.Combine(directoryName, soundElement.Path,
|
||||
$"{soundElement.Path.Replace('/', '_')}_{aaxReader.GetByte("lpflg")}.adx");
|
||||
soundElement.Sounds.Add(fileName);
|
||||
|
||||
FileInfo fileInfo = new FileInfo(fileName);
|
||||
fileInfo.Directory.Create();
|
||||
|
||||
using (Stream inStream = aaxReader.GetSubstream("data"), outStream = fileInfo.Create())
|
||||
{
|
||||
inStream.CopyTo(outStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
soundElements.Add(soundElement);
|
||||
}
|
||||
}
|
||||
|
||||
XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<SoundElement>));
|
||||
|
||||
using (XmlWriter xmlWriter = XmlWriter.Create(directoryName + ".xml", new XmlWriterSettings() { Indent = true }))
|
||||
{
|
||||
xmlSerializer.Serialize(xmlWriter, soundElements);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (args[0].EndsWith(".xml"))
|
||||
{
|
||||
XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<SoundElement>));
|
||||
List<SoundElement> soundElements;
|
||||
|
||||
using (XmlReader xmlReader = XmlReader.Create(args[0]))
|
||||
{
|
||||
soundElements = (List<SoundElement>)xmlSerializer.Deserialize(xmlReader);
|
||||
}
|
||||
|
||||
CriTable criTable = new CriTable();
|
||||
string directoryName = Path.Combine(Path.GetDirectoryName(args[0]), Path.GetFileNameWithoutExtension(args[0]));
|
||||
|
||||
if (!File.Exists(directoryName + ".csb"))
|
||||
{
|
||||
throw new FileNotFoundException("No valid CSB file found for this XML!");
|
||||
}
|
||||
|
||||
criTable.Load(directoryName + ".csb");
|
||||
|
||||
Directory.CreateDirectory(directoryName + "-temp");
|
||||
|
||||
CriTable soundElementTable = new CriTable();
|
||||
CriRow soundElementRow = criTable.Rows.Single(row => (string)row["name"] == "SOUND_ELEMENT");
|
||||
|
||||
soundElementTable.Load((byte[])soundElementRow["utf"]);
|
||||
|
||||
foreach (SoundElement soundElement in soundElements)
|
||||
{
|
||||
CriRow elementRow = soundElementTable.Rows.Single(row => (string)row["name"] == soundElement.Path);
|
||||
elementRow["nch"] = soundElement.NumberChannels;
|
||||
elementRow["sfreq"] = soundElement.SampleRate;
|
||||
|
||||
using (CriTableWriter aaxWriter = CriTableWriter.Create($"{directoryName}-temp/{soundElement.Path.Replace('/', '_')}"))
|
||||
{
|
||||
aaxWriter.WriteStartTable("AAX");
|
||||
aaxWriter.WriteStartFieldCollection();
|
||||
aaxWriter.WriteField("data", typeof(byte[]));
|
||||
aaxWriter.WriteField("lpflg", typeof(byte));
|
||||
aaxWriter.WriteEndFieldCollection();
|
||||
|
||||
foreach (string sound in soundElement.Sounds)
|
||||
{
|
||||
if (!File.Exists(sound))
|
||||
{
|
||||
Directory.Delete(directoryName + "-temp", true);
|
||||
throw new FileNotFoundException($"Can't find file! {sound}");
|
||||
}
|
||||
|
||||
string baseName = Path.GetFileNameWithoutExtension(sound);
|
||||
int lastUnderscore = baseName.LastIndexOf('_');
|
||||
baseName = baseName.Substring(lastUnderscore + 1);
|
||||
|
||||
byte loopFlag = byte.Parse(baseName);
|
||||
|
||||
aaxWriter.WriteRow(true, new FileInfo(sound), loopFlag);
|
||||
}
|
||||
|
||||
aaxWriter.WriteEndTable();
|
||||
}
|
||||
|
||||
elementRow["data"] = File.ReadAllBytes($"{directoryName}-temp/{soundElement.Path.Replace('/', '_')}");
|
||||
}
|
||||
|
||||
soundElementTable.Save($"{directoryName}-temp/SOUND_ELEMENT");
|
||||
soundElementRow["utf"] = File.ReadAllBytes($"{directoryName}-temp/SOUND_ELEMENT");
|
||||
criTable.Save(directoryName + ".csb");
|
||||
|
||||
Directory.Delete(directoryName + "-temp", true);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException("No valid file found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
Source/SonicAudioCmd/csb_frequence_subdivision.txt
Normal file
45
Source/SonicAudioCmd/csb_frequence_subdivision.txt
Normal file
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.ComponentModel;
|
||||
using System.Collections;
|
||||
|
||||
using SonicAudioLib;
|
||||
using SonicAudioLib.Archive;
|
||||
using SonicAudioLib.Collections;
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.CriMw;
|
||||
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace SonicAudioCmd
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
CriTable table = new CriTable();
|
||||
table.Load(args[0]);
|
||||
|
||||
CriRow soundElementTableRow = table.Rows.Single(row => (string)row["name"] == "SOUND_ELEMENT");
|
||||
|
||||
CriTable sdlTable = new CriTable();
|
||||
sdlTable.Load(soundElementTableRow["utf"] as byte[]);
|
||||
|
||||
foreach (CriRow criRow in sdlTable.Rows)
|
||||
{
|
||||
criRow["sfreq"] = (uint)criRow["sfreq"] / 2;
|
||||
}
|
||||
|
||||
sdlTable.WriterSettings = CriTableWriterSettings.AdxSettings;
|
||||
soundElementTableRow["utf"] = sdlTable.Save();
|
||||
|
||||
table.WriterSettings = CriTableWriterSettings.AdxSettings;
|
||||
table.Save(args[0]);
|
||||
}
|
||||
}
|
||||
}
|
116
Source/SonicAudioCmd/file_replacer_acb.txt
Normal file
116
Source/SonicAudioCmd/file_replacer_acb.txt
Normal file
@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.ComponentModel;
|
||||
using System.Collections;
|
||||
|
||||
using SonicAudioLib;
|
||||
using SonicAudioLib.Archive;
|
||||
using SonicAudioLib.Collections;
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.CriMw;
|
||||
|
||||
using System.Xml;
|
||||
|
||||
namespace SonicAudioCmd
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
Console.WriteLine("Usage: [path]");
|
||||
Console.WriteLine("Drag and drop an ACB file to unpack its contents.");
|
||||
Console.WriteLine("Drag and drop a folder to pack its contents back into the ACB. (The ACB file must be in the same directory.)");
|
||||
Console.ReadLine();
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0].EndsWith(".acb"))
|
||||
{
|
||||
string baseDirectory = Path.Combine(
|
||||
Path.GetDirectoryName(args[0]), Path.GetFileNameWithoutExtension(args[0]));
|
||||
|
||||
Directory.CreateDirectory(baseDirectory);
|
||||
|
||||
using (CriTableReader reader = CriTableReader.Create(args[0]))
|
||||
{
|
||||
reader.Read();
|
||||
|
||||
using (Stream afs2Stream = reader.GetSubstream("AwbFile"))
|
||||
{
|
||||
Afs2Archive archive = new Afs2Archive(afs2Stream);
|
||||
|
||||
using (CriTableReader waveformReader = CriTableReader.Create(reader.GetSubstream("WaveformTable")))
|
||||
{
|
||||
while (waveformReader.Read())
|
||||
{
|
||||
if (!waveformReader.GetBoolean("Streaming"))
|
||||
{
|
||||
ushort cueIndex = waveformReader.GetUInt16("Id");
|
||||
Afs2Entry entry = archive.GetEntryByCueIndex(cueIndex);
|
||||
|
||||
string fileName = entry.CueIndex.ToString();
|
||||
switch (waveformReader.GetByte("EncodeType"))
|
||||
{
|
||||
case 0:
|
||||
fileName += ".adx";
|
||||
break;
|
||||
case 2:
|
||||
fileName += ".hca";
|
||||
break;
|
||||
case 13:
|
||||
fileName += ".dsp";
|
||||
break;
|
||||
default:
|
||||
fileName += ".bin";
|
||||
break;
|
||||
}
|
||||
|
||||
using (Stream entryIn = entry.Open(), entryOut = File.Create(Path.Combine(baseDirectory, fileName)))
|
||||
{
|
||||
entryIn.CopyTo(entryOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
CriTable criTable = new CriTable();
|
||||
criTable.Load(args[0] + ".acb");
|
||||
|
||||
Afs2Archive afs2Archive = new Afs2Archive();
|
||||
|
||||
List<string> files = new List<string>();
|
||||
files.AddRange(Directory.GetFiles(args[0], "*.adx"));
|
||||
files.AddRange(Directory.GetFiles(args[0], "*.hca"));
|
||||
files.AddRange(Directory.GetFiles(args[0], "*.dsp"));
|
||||
files.AddRange(Directory.GetFiles(args[0], "*.bin"));
|
||||
|
||||
foreach (string file in Directory.GetFiles(args[0], "*.*"))
|
||||
{
|
||||
string baseName = Path.GetFileNameWithoutExtension(file);
|
||||
|
||||
Afs2Entry afs2Entry = new Afs2Entry();
|
||||
afs2Entry.CueIndex = int.Parse(baseName);
|
||||
afs2Entry.LocalFilePath = new FileInfo(file);
|
||||
afs2Archive.Entries.Add(afs2Entry);
|
||||
}
|
||||
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
afs2Archive.Write(memoryStream);
|
||||
|
||||
criTable.Rows[0]["AwbFile"] = memoryStream.ToArray();
|
||||
criTable.Save(args[0] + ".acb", CriTableFileMode.Adx2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
134
Source/SonicAudioCmd/utf_to_cs.txt
Normal file
134
Source/SonicAudioCmd/utf_to_cs.txt
Normal file
@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.ComponentModel;
|
||||
using System.Collections;
|
||||
|
||||
using SonicAudioLib;
|
||||
using SonicAudioLib.Archive;
|
||||
using SonicAudioLib.Collections;
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.CriMw;
|
||||
|
||||
using System.Xml;
|
||||
|
||||
namespace SonicAudioCmd
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
CreateCSharpSource(File.OpenRead(args[0]));
|
||||
}
|
||||
|
||||
static void CreateCSharpSource(Stream stream)
|
||||
{
|
||||
using (CriTableReader reader = new CriTableReader(stream))
|
||||
using (TextWriter textWriter = File.CreateText(reader.TableName + ".cs"))
|
||||
{
|
||||
textWriter.WriteLine("using System.IO;");
|
||||
textWriter.WriteLine("using SonicAudioLib.CriMw;");
|
||||
textWriter.WriteLine();
|
||||
textWriter.WriteLine("namespace {0} {{", "SonicAudioLib.CriMw");
|
||||
textWriter.WriteLine(" [CriSerializable(\"{0}\")]", reader.TableName);
|
||||
textWriter.WriteLine(" public class {0} {{", reader.TableName);
|
||||
|
||||
for (int i = 0; i < reader.NumberOfFields; i++)
|
||||
{
|
||||
textWriter.Write(" private {0} _{1}", GetSimplifiedName(reader.GetFieldType(i)), reader.GetFieldName(i));
|
||||
|
||||
object defaultValue = reader.GetFieldValue(i);
|
||||
if (defaultValue != null)
|
||||
{
|
||||
textWriter.Write(" = {0}", defaultValue);
|
||||
}
|
||||
|
||||
textWriter.WriteLine(";");
|
||||
}
|
||||
|
||||
for (int i = 0; i < reader.NumberOfFields; i++)
|
||||
{
|
||||
string name = reader.GetFieldName(i);
|
||||
Type type = reader.GetFieldType(i);
|
||||
object defaultValue = reader.GetFieldValue(i);
|
||||
|
||||
textWriter.WriteLine();
|
||||
if (defaultValue != null)
|
||||
{
|
||||
textWriter.WriteLine(" [CriField(\"{0}\", {1}, {2})]", name, defaultValue, i);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
textWriter.WriteLine(" [CriField(\"{0}\", {1})]", name, i);
|
||||
}
|
||||
|
||||
textWriter.WriteLine(" public {0} {1} {{", GetSimplifiedName(reader.GetFieldType(i)), name);
|
||||
textWriter.WriteLine(" get {");
|
||||
textWriter.WriteLine(" return _{0};", name);
|
||||
textWriter.WriteLine(" }");
|
||||
textWriter.WriteLine(" set {");
|
||||
textWriter.WriteLine(" _{0} = value;", name);
|
||||
textWriter.WriteLine(" }");
|
||||
textWriter.WriteLine(" }");
|
||||
}
|
||||
textWriter.WriteLine(" }");
|
||||
textWriter.WriteLine("}");
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
for (int i = 0; i < reader.NumberOfFields; i++)
|
||||
{
|
||||
if (reader.GetFieldType(i) == typeof(byte[]))
|
||||
{
|
||||
stream.Position = reader.GetPosition(i);
|
||||
if (EndianStream.ReadInt32(stream) == 0x46545540)
|
||||
{
|
||||
CreateCSharpSource(reader.GetSubstream(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static string GetSimplifiedName(Type type)
|
||||
{
|
||||
switch (Type.GetTypeCode(type))
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
return "byte";
|
||||
case TypeCode.SByte:
|
||||
return "sbyte";
|
||||
case TypeCode.Int16:
|
||||
return "short";
|
||||
case TypeCode.UInt16:
|
||||
return "ushort";
|
||||
case TypeCode.Int32:
|
||||
return "int";
|
||||
case TypeCode.UInt32:
|
||||
return "uint";
|
||||
case TypeCode.Int64:
|
||||
return "long";
|
||||
case TypeCode.UInt64:
|
||||
return "ulong";
|
||||
case TypeCode.Single:
|
||||
return "float";
|
||||
case TypeCode.Double:
|
||||
return "double";
|
||||
case TypeCode.String:
|
||||
return "string";
|
||||
}
|
||||
|
||||
if (type == typeof(byte[]))
|
||||
{
|
||||
return "FileInfo";
|
||||
}
|
||||
|
||||
return "DBNull";
|
||||
}
|
||||
}
|
||||
}
|
139
Source/SonicAudioCmd/utf_to_cs_memory.txt
Normal file
139
Source/SonicAudioCmd/utf_to_cs_memory.txt
Normal file
@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.ComponentModel;
|
||||
using System.Collections;
|
||||
|
||||
using SonicAudioLib;
|
||||
using SonicAudioLib.Archive;
|
||||
using SonicAudioLib.Collections;
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.CriMw;
|
||||
|
||||
using System.Xml;
|
||||
|
||||
namespace SonicAudioCmd
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
byte[] bytes = File.ReadAllBytes(args[0]);
|
||||
|
||||
for (int i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
if (bytes[i] == '@' && bytes[i + 1] == 'U' && bytes[i + 2] == 'T' && bytes[i + 3] == 'F')
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream(bytes))
|
||||
{
|
||||
memoryStream.Position = i;
|
||||
CreateCSharpSource(memoryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CreateCSharpSource(Stream stream)
|
||||
{
|
||||
using (CriTableReader reader = CriTableReader.Create(stream))
|
||||
using (TextWriter textWriter = File.CreateText(reader.TableName + ".cs"))
|
||||
{
|
||||
textWriter.WriteLine("using System.IO;");
|
||||
textWriter.WriteLine("using SonicAudioLib.CriMw;");
|
||||
textWriter.WriteLine();
|
||||
textWriter.WriteLine("namespace {0} {{", "SonicAudioLib.CriMw");
|
||||
textWriter.WriteLine(" [CriSerializable(\"{0}\")]", reader.TableName);
|
||||
textWriter.WriteLine(" public class {0} {{", reader.TableName);
|
||||
|
||||
for (int i = 0; i < reader.NumberOfFields; i++)
|
||||
{
|
||||
textWriter.Write(" private {0} _{1}", GetSimplifiedName(reader.GetFieldType(i)), reader.GetFieldName(i));
|
||||
|
||||
object defaultValue = reader.GetFieldValue(i);
|
||||
if (defaultValue != null)
|
||||
{
|
||||
textWriter.Write(" = {0}", defaultValue);
|
||||
}
|
||||
|
||||
textWriter.WriteLine(";");
|
||||
}
|
||||
|
||||
for (int i = 0; i < reader.NumberOfFields; i++)
|
||||
{
|
||||
string name = reader.GetFieldName(i);
|
||||
Type type = reader.GetFieldType(i);
|
||||
object defaultValue = reader.GetFieldValue(i);
|
||||
string fieldFlag = reader.GetFieldFlag(i).ToString("X2");
|
||||
|
||||
textWriter.WriteLine();
|
||||
textWriter.WriteLine(" // Field flag: {0}", fieldFlag);
|
||||
textWriter.WriteLine(" [CriField(\"{0}\", {1})]", name, i);
|
||||
textWriter.WriteLine(" public {0} {1} {{", GetSimplifiedName(reader.GetFieldType(i)), name);
|
||||
textWriter.WriteLine(" get {");
|
||||
textWriter.WriteLine(" return _{0};", name);
|
||||
textWriter.WriteLine(" }");
|
||||
textWriter.WriteLine(" set {");
|
||||
textWriter.WriteLine(" _{0} = value;", name);
|
||||
textWriter.WriteLine(" }");
|
||||
textWriter.WriteLine(" }");
|
||||
}
|
||||
textWriter.WriteLine(" }");
|
||||
textWriter.WriteLine("}");
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
for (int i = 0; i < reader.NumberOfFields; i++)
|
||||
{
|
||||
if (reader.GetFieldType(i) == typeof(byte[]))
|
||||
{
|
||||
stream.Position = reader.GetPosition(i);
|
||||
if (EndianStream.ReadInt32(stream) == 0x46545540)
|
||||
{
|
||||
CreateCSharpSource(reader.GetSubstream(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static string GetSimplifiedName(Type type)
|
||||
{
|
||||
switch (Type.GetTypeCode(type))
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
return "byte";
|
||||
case TypeCode.SByte:
|
||||
return "sbyte";
|
||||
case TypeCode.Int16:
|
||||
return "short";
|
||||
case TypeCode.UInt16:
|
||||
return "ushort";
|
||||
case TypeCode.Int32:
|
||||
return "int";
|
||||
case TypeCode.UInt32:
|
||||
return "uint";
|
||||
case TypeCode.Int64:
|
||||
return "long";
|
||||
case TypeCode.UInt64:
|
||||
return "ulong";
|
||||
case TypeCode.Single:
|
||||
return "float";
|
||||
case TypeCode.Double:
|
||||
return "double";
|
||||
case TypeCode.String:
|
||||
return "string";
|
||||
}
|
||||
|
||||
if (type == typeof(byte[]))
|
||||
{
|
||||
return "FileInfo";
|
||||
}
|
||||
|
||||
return "DBNull";
|
||||
}
|
||||
}
|
||||
}
|
110
Source/SonicAudioLib/Archive/ArchiveBase.cs
Normal file
110
Source/SonicAudioLib/Archive/ArchiveBase.cs
Normal file
@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.Module;
|
||||
using System.Collections;
|
||||
|
||||
namespace SonicAudioLib.Archive
|
||||
{
|
||||
public abstract class EntryBase
|
||||
{
|
||||
protected long length;
|
||||
|
||||
public virtual long Position { get; set; }
|
||||
|
||||
public virtual long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (FilePath != null)
|
||||
{
|
||||
return FilePath.Length;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
length = value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual FileInfo FilePath { get; set; }
|
||||
|
||||
public virtual Stream Open(Stream source)
|
||||
{
|
||||
return new Substream(source, Position, length);
|
||||
}
|
||||
|
||||
public virtual Stream Open()
|
||||
{
|
||||
return FilePath.OpenRead();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ArchiveBase<T> : ModuleBase, IEnumerable<T>
|
||||
{
|
||||
protected List<T> entries = new List<T>();
|
||||
|
||||
public virtual T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return entries[index];
|
||||
}
|
||||
}
|
||||
|
||||
public virtual int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return entries.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Add(T item)
|
||||
{
|
||||
entries.Add(item);
|
||||
}
|
||||
|
||||
public virtual T Get(int index)
|
||||
{
|
||||
return entries[index];
|
||||
}
|
||||
|
||||
public virtual void Clear()
|
||||
{
|
||||
entries.Clear();
|
||||
}
|
||||
|
||||
public virtual bool Contains(T item)
|
||||
{
|
||||
return entries.Contains(item);
|
||||
}
|
||||
|
||||
public virtual void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
entries.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public virtual IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return entries.GetEnumerator();
|
||||
}
|
||||
|
||||
public virtual bool Remove(T item)
|
||||
{
|
||||
return entries.Remove(item);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return entries.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
194
Source/SonicAudioLib/Archive/CriAfs2Archive.cs
Normal file
194
Source/SonicAudioLib/Archive/CriAfs2Archive.cs
Normal file
@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.Module;
|
||||
|
||||
namespace SonicAudioLib.Archive
|
||||
{
|
||||
public class CriAfs2Entry : EntryBase
|
||||
{
|
||||
public ushort CueIndex { get; set; }
|
||||
}
|
||||
|
||||
public class CriAfs2Archive : ArchiveBase<CriAfs2Entry>
|
||||
{
|
||||
public uint Align { get; set; }
|
||||
public uint CueIndexFieldLength { get; set; }
|
||||
public uint PositionFieldLength { get; set; }
|
||||
|
||||
public override void Read(Stream source)
|
||||
{
|
||||
if (EndianStream.ReadCString(source, 4) != "AFS2")
|
||||
{
|
||||
throw new Exception("No AFS2 signature found.");
|
||||
}
|
||||
|
||||
uint information = EndianStream.ReadUInt32(source);
|
||||
|
||||
uint type = information & 0xFF;
|
||||
if (type != 1)
|
||||
{
|
||||
throw new Exception($"Invalid AFS2 type ({type}). Please report the error with the AWB file.");
|
||||
}
|
||||
|
||||
CueIndexFieldLength = (information & 0x00FF0000) >> 16;
|
||||
PositionFieldLength = (information & 0x0000FF00) >> 8;
|
||||
|
||||
ushort entryCount = (ushort)EndianStream.ReadUInt32(source);
|
||||
Align = EndianStream.ReadUInt32(source);
|
||||
|
||||
CriAfs2Entry previousEntry = null;
|
||||
for (uint i = 0; i < entryCount; i++)
|
||||
{
|
||||
CriAfs2Entry afs2Entry = new CriAfs2Entry();
|
||||
|
||||
long cueIndexPosition = 16 + (i * CueIndexFieldLength);
|
||||
source.Seek(cueIndexPosition, SeekOrigin.Begin);
|
||||
|
||||
switch (CueIndexFieldLength)
|
||||
{
|
||||
case 2:
|
||||
afs2Entry.CueIndex = EndianStream.ReadUInt16(source);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception($"Unknown CueIndexFieldLength ({CueIndexFieldLength}). Please report the error with the AWB file.");
|
||||
}
|
||||
|
||||
long positionPosition = 16 + (entryCount * CueIndexFieldLength) + (i * PositionFieldLength);
|
||||
source.Seek(positionPosition, SeekOrigin.Begin);
|
||||
|
||||
switch (PositionFieldLength)
|
||||
{
|
||||
case 2:
|
||||
afs2Entry.Position = EndianStream.ReadUInt16(source);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
afs2Entry.Position = EndianStream.ReadUInt32(source);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception($"Unknown PositionFieldLength ({PositionFieldLength}). Please report the error with the AWB file.");
|
||||
}
|
||||
|
||||
if (previousEntry != null)
|
||||
{
|
||||
previousEntry.Length = afs2Entry.Position - previousEntry.Position;
|
||||
}
|
||||
|
||||
while ((afs2Entry.Position % Align) != 0)
|
||||
{
|
||||
afs2Entry.Position++;
|
||||
}
|
||||
|
||||
if (i == entryCount - 1)
|
||||
{
|
||||
switch (PositionFieldLength)
|
||||
{
|
||||
case 2:
|
||||
afs2Entry.Length = EndianStream.ReadUInt16(source) - afs2Entry.Position;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
afs2Entry.Length = EndianStream.ReadUInt32(source) - afs2Entry.Position;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
entries.Add(afs2Entry);
|
||||
previousEntry = afs2Entry;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(Stream destination)
|
||||
{
|
||||
uint headerLength = (uint)(16 + (entries.Count * CueIndexFieldLength) + (entries.Count * PositionFieldLength) + PositionFieldLength);
|
||||
|
||||
EndianStream.WriteCString(destination, "AFS2", 4);
|
||||
EndianStream.WriteUInt32(destination, 1 | (CueIndexFieldLength << 16) | (PositionFieldLength << 8));
|
||||
EndianStream.WriteUInt32(destination, (ushort)entries.Count);
|
||||
EndianStream.WriteUInt32(destination, 1);
|
||||
|
||||
// FIXME: Alignment support
|
||||
VldPool vldPool = new VldPool(1);
|
||||
|
||||
foreach (CriAfs2Entry afs2Entry in entries)
|
||||
{
|
||||
switch (CueIndexFieldLength)
|
||||
{
|
||||
case 2:
|
||||
EndianStream.WriteUInt16(destination, (ushort)afs2Entry.CueIndex);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception($"Unknown CueIndexFieldLength ({CueIndexFieldLength}). Please set a valid length.");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (CriAfs2Entry afs2Entry in entries)
|
||||
{
|
||||
uint entryPosition = (uint)(headerLength + vldPool.Put(afs2Entry.FilePath));
|
||||
|
||||
switch (PositionFieldLength)
|
||||
{
|
||||
case 2:
|
||||
EndianStream.WriteUInt16(destination, (ushort)entryPosition);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
EndianStream.WriteUInt32(destination, entryPosition);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception($"Unknown PositionFieldLength ({PositionFieldLength}). Please set a valid length.");
|
||||
}
|
||||
|
||||
afs2Entry.Position = entryPosition;
|
||||
}
|
||||
|
||||
EndianStream.WriteUInt32(destination, (uint)(headerLength + vldPool.Length));
|
||||
|
||||
vldPool.Write(destination);
|
||||
vldPool.Clear();
|
||||
}
|
||||
|
||||
public CriAfs2Entry GetByCueIndex(uint cueIndex)
|
||||
{
|
||||
return entries.Single(e => e.CueIndex == 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;
|
||||
PositionFieldLength = 4;
|
||||
}
|
||||
}
|
||||
}
|
136
Source/SonicAudioLib/Archive/CriCpkArchive.cs
Normal file
136
Source/SonicAudioLib/Archive/CriCpkArchive.cs
Normal file
@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.CriMw;
|
||||
|
||||
namespace SonicAudioLib.Archive
|
||||
{
|
||||
public class CriCpkEntry : EntryBase
|
||||
{
|
||||
public string DirectoryName { get; set; }
|
||||
public string Name { get; set; }
|
||||
public uint Index { get; set; }
|
||||
public string Comment { get; set; }
|
||||
public bool IsCompressed { get; set; }
|
||||
}
|
||||
|
||||
public class CriCpkArchive : ArchiveBase<CriCpkEntry>
|
||||
{
|
||||
public override void Read(Stream source)
|
||||
{
|
||||
using (CriTableReader reader = CriCpkSection.Open(source, source.Position))
|
||||
{
|
||||
reader.Read();
|
||||
|
||||
if (reader.GetUInt32("CpkMode") != 1)
|
||||
{
|
||||
throw new Exception("Unsupported CPK type! Only TOC CPKs are supported for now.");
|
||||
}
|
||||
|
||||
long tocPosition = (long)reader.GetUInt64("TocOffset");
|
||||
long contentPosition = (long)reader.GetUInt64("ContentOffset");
|
||||
ushort align = reader.GetUInt16("Align");
|
||||
|
||||
using (CriTableReader tocReader = CriCpkSection.Open(source, tocPosition))
|
||||
{
|
||||
while (tocReader.Read())
|
||||
{
|
||||
CriCpkEntry entry = new CriCpkEntry();
|
||||
entry.DirectoryName = tocReader.GetString("DirName");
|
||||
entry.Name = tocReader.GetString("FileName");
|
||||
entry.Length = tocReader.GetUInt32("FileSize");
|
||||
entry.Position = (long)tocReader.GetUInt64("FileOffset");
|
||||
entry.Index = tocReader.GetUInt32("ID");
|
||||
entry.Comment = tocReader.GetString("UserString");
|
||||
|
||||
if (entry.Length != tocReader.GetUInt32("ExtractSize"))
|
||||
{
|
||||
entry.IsCompressed = true;
|
||||
}
|
||||
|
||||
if (contentPosition < tocPosition)
|
||||
{
|
||||
entry.Position += contentPosition;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
entry.Position += tocPosition;
|
||||
}
|
||||
|
||||
while ((entry.Position % align) != 0)
|
||||
{
|
||||
entry.Position++;
|
||||
}
|
||||
|
||||
entries.Add(entry);
|
||||
|
||||
Console.WriteLine(Path.Combine(entry.DirectoryName, entry.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(Stream destination)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class CriCpkSection : IDisposable
|
||||
{
|
||||
private Stream destination;
|
||||
private long headerPosition;
|
||||
|
||||
private CriTableWriter writer;
|
||||
|
||||
public CriTableWriter Writer
|
||||
{
|
||||
get
|
||||
{
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
writer.Dispose();
|
||||
|
||||
long position = destination.Position;
|
||||
uint length = (uint)(position - (headerPosition - 8));
|
||||
|
||||
destination.Seek(headerPosition + 8, SeekOrigin.Begin);
|
||||
EndianStream.WriteUInt32(destination, length);
|
||||
|
||||
destination.Seek(position, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
public static CriTableReader Open(Stream source, long position)
|
||||
{
|
||||
source.Seek(position, SeekOrigin.Begin);
|
||||
|
||||
string signature = EndianStream.ReadCString(source, 4);
|
||||
uint flag = EndianStream.ReadUInt32(source);
|
||||
uint tableLength = EndianStream.ReadUInt32(source);
|
||||
uint unknown = EndianStream.ReadUInt32(source);
|
||||
|
||||
return CriTableReader.Create(new Substream(source, source.Position, tableLength));
|
||||
}
|
||||
|
||||
public CriCpkSection(Stream destination, string signature)
|
||||
{
|
||||
this.destination = destination;
|
||||
headerPosition = destination.Position;
|
||||
|
||||
EndianStream.WriteCString(destination, signature, 4);
|
||||
EndianStream.WriteUInt32(destination, byte.MaxValue);
|
||||
destination.Seek(8, SeekOrigin.Begin);
|
||||
|
||||
writer = CriTableWriter.Create(destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
98
Source/SonicAudioLib/Archive/HeroesPacArchive.cs
Normal file
98
Source/SonicAudioLib/Archive/HeroesPacArchive.cs
Normal file
@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.Module;
|
||||
|
||||
namespace SonicAudioLib.Archive
|
||||
{
|
||||
public class HeroesPacEntry : EntryBase
|
||||
{
|
||||
public uint GlobalIndex { get; set; }
|
||||
}
|
||||
|
||||
public class HeroesPacArchive : ArchiveBase<HeroesPacEntry>
|
||||
{
|
||||
public override void Read(Stream source)
|
||||
{
|
||||
uint entryCount = EndianStream.ReadUInt32(source);
|
||||
uint tablePosition = EndianStream.ReadUInt32(source);
|
||||
uint vldPoolLength = EndianStream.ReadUInt32(source);
|
||||
uint vldPoolPosition = EndianStream.ReadUInt32(source);
|
||||
|
||||
source.Seek(tablePosition, SeekOrigin.Begin);
|
||||
|
||||
HeroesPacEntry previousEntry = null;
|
||||
for (uint i = 0; i < entryCount; i++)
|
||||
{
|
||||
HeroesPacEntry pacEntry = new HeroesPacEntry();
|
||||
|
||||
pacEntry.GlobalIndex = EndianStream.ReadUInt32(source);
|
||||
pacEntry.Position = vldPoolPosition + EndianStream.ReadUInt32(source);
|
||||
|
||||
if (previousEntry != null)
|
||||
{
|
||||
previousEntry.Length = pacEntry.Position - previousEntry.Position;
|
||||
}
|
||||
|
||||
if (i == entryCount - 1)
|
||||
{
|
||||
pacEntry.Length = (uint)source.Length - pacEntry.Position;
|
||||
}
|
||||
|
||||
entries.Add(pacEntry);
|
||||
previousEntry = pacEntry;
|
||||
|
||||
source.Seek(8, SeekOrigin.Current);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(Stream destination)
|
||||
{
|
||||
long headerPosition = destination.Position;
|
||||
|
||||
uint headerLength = 16;
|
||||
for (uint i = 0; i < headerLength; i++)
|
||||
{
|
||||
destination.WriteByte(0);
|
||||
}
|
||||
|
||||
while ((destination.Position % 32) != 0)
|
||||
{
|
||||
destination.WriteByte(0);
|
||||
}
|
||||
|
||||
uint tableLength = (uint)(entries.Count * 16);
|
||||
uint tablePosition = (uint)destination.Position;
|
||||
|
||||
VldPool vldPool = new VldPool();
|
||||
|
||||
foreach (HeroesPacEntry pacEntry in entries)
|
||||
{
|
||||
uint entryPosition = (uint)vldPool.Put(pacEntry.FilePath);
|
||||
|
||||
EndianStream.WriteUInt32(destination, pacEntry.GlobalIndex);
|
||||
EndianStream.WriteUInt32(destination, entryPosition);
|
||||
|
||||
while ((destination.Position % 16) != 0)
|
||||
{
|
||||
destination.WriteByte(0);
|
||||
}
|
||||
|
||||
pacEntry.Position = tablePosition + tableLength + entryPosition;
|
||||
}
|
||||
|
||||
vldPool.Write(destination);
|
||||
vldPool.Clear();
|
||||
|
||||
destination.Seek(0, SeekOrigin.Begin);
|
||||
EndianStream.WriteUInt32(destination, (uint)entries.Count);
|
||||
EndianStream.WriteUInt32(destination, tablePosition);
|
||||
EndianStream.WriteUInt32(destination, (uint)vldPool.Length);
|
||||
EndianStream.WriteUInt32(destination, (uint)vldPool.Position);
|
||||
}
|
||||
}
|
||||
}
|
175
Source/SonicAudioLib/Collections/OrderedDictionary.cs
Normal file
175
Source/SonicAudioLib/Collections/OrderedDictionary.cs
Normal file
@ -0,0 +1,175 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
134
Source/SonicAudioLib/CriMw/CriField.cs
Normal file
134
Source/SonicAudioLib/CriMw/CriField.cs
Normal file
@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace SonicAudioLib.CriMw
|
||||
{
|
||||
public class CriField
|
||||
{
|
||||
public static readonly Type[] FieldTypes =
|
||||
{
|
||||
typeof(byte),
|
||||
typeof(sbyte),
|
||||
typeof(ushort),
|
||||
typeof(short),
|
||||
typeof(uint),
|
||||
typeof(int),
|
||||
typeof(ulong),
|
||||
typeof(long),
|
||||
typeof(float),
|
||||
typeof(double),
|
||||
typeof(string),
|
||||
typeof(byte[]),
|
||||
typeof(Guid),
|
||||
};
|
||||
|
||||
public static object[] NullValues =
|
||||
{
|
||||
(byte)0,
|
||||
(sbyte)0,
|
||||
(ushort)0,
|
||||
(short)0,
|
||||
(uint)0,
|
||||
(int)0,
|
||||
(ulong)0,
|
||||
(long)0,
|
||||
(float)0.0f,
|
||||
(double)0.0f,
|
||||
(string)string.Empty,
|
||||
(byte[])new byte[0],
|
||||
(Guid)Guid.Empty,
|
||||
};
|
||||
|
||||
private Type fieldType;
|
||||
private string fieldName;
|
||||
private object defaultValue;
|
||||
private CriTable parent;
|
||||
|
||||
public int FieldTypeIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return Array.IndexOf(FieldTypes, fieldType);
|
||||
}
|
||||
}
|
||||
|
||||
public Type FieldType
|
||||
{
|
||||
get
|
||||
{
|
||||
return fieldType;
|
||||
}
|
||||
}
|
||||
|
||||
public object DefaultValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
defaultValue = ConvertObject(value);
|
||||
}
|
||||
}
|
||||
|
||||
public string FieldName
|
||||
{
|
||||
get
|
||||
{
|
||||
return fieldName;
|
||||
}
|
||||
}
|
||||
|
||||
public object ConvertObject(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return NullValues[FieldTypeIndex];
|
||||
}
|
||||
|
||||
Type typ = obj.GetType();
|
||||
|
||||
if (typ == fieldType)
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
|
||||
TypeConverter typeConverter = TypeDescriptor.GetConverter(fieldType);
|
||||
|
||||
if (typeConverter.CanConvertFrom(typ))
|
||||
{
|
||||
return typeConverter.ConvertFrom(obj);
|
||||
}
|
||||
|
||||
return DefaultValue;
|
||||
}
|
||||
|
||||
public CriTable Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
internal set
|
||||
{
|
||||
parent = value;
|
||||
}
|
||||
}
|
||||
|
||||
public CriField(string name, Type type)
|
||||
{
|
||||
fieldName = name;
|
||||
fieldType = type;
|
||||
}
|
||||
|
||||
public CriField(string name, Type type, object defaultValue)
|
||||
{
|
||||
fieldName = name;
|
||||
fieldType = type;
|
||||
this.defaultValue = ConvertObject(defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
121
Source/SonicAudioLib/CriMw/CriFieldCollection.cs
Normal file
121
Source/SonicAudioLib/CriMw/CriFieldCollection.cs
Normal file
@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace SonicAudioLib.CriMw
|
||||
{
|
||||
public class CriFieldCollection : IEnumerable<CriField>
|
||||
{
|
||||
private CriTable parent;
|
||||
private List<CriField> fields = new List<CriField>();
|
||||
|
||||
public CriField this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return fields[index];
|
||||
}
|
||||
}
|
||||
|
||||
public CriField this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
return fields.Single(f => f.FieldName == name);
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return fields.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public CriTable Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
internal set
|
||||
{
|
||||
parent = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(CriField criField)
|
||||
{
|
||||
criField.Parent = parent;
|
||||
fields.Add(criField);
|
||||
}
|
||||
|
||||
public CriField Add(string name, Type type)
|
||||
{
|
||||
CriField criField = new CriField(name, type);
|
||||
Add(criField);
|
||||
|
||||
return criField;
|
||||
}
|
||||
|
||||
public CriField Add(string name, Type type, object defaultValue)
|
||||
{
|
||||
CriField criField = new CriField(name, type, defaultValue);
|
||||
Add(criField);
|
||||
|
||||
return criField;
|
||||
}
|
||||
|
||||
public void Insert(int index, CriField criField)
|
||||
{
|
||||
if (index >= fields.Count || index < 0)
|
||||
{
|
||||
fields.Add(criField);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
fields.Insert(index, criField);
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(CriField criField)
|
||||
{
|
||||
fields.Remove(criField);
|
||||
|
||||
// Update the objects
|
||||
foreach (CriRow criRow in parent.Rows)
|
||||
{
|
||||
criRow.Records.Remove(criField);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
Remove(fields[index]);
|
||||
}
|
||||
|
||||
internal void Clear()
|
||||
{
|
||||
fields.Clear();
|
||||
}
|
||||
|
||||
public IEnumerator<CriField> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<CriField>)fields).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<CriField>)fields).GetEnumerator();
|
||||
}
|
||||
|
||||
public CriFieldCollection(CriTable parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
107
Source/SonicAudioLib/CriMw/CriRow.cs
Normal file
107
Source/SonicAudioLib/CriMw/CriRow.cs
Normal file
@ -0,0 +1,107 @@
|
||||
using SonicAudioLib.Collections;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
|
||||
namespace SonicAudioLib.CriMw
|
||||
{
|
||||
public class CriRow : IEnumerable
|
||||
{
|
||||
private OrderedDictionary<CriField, object> records = new OrderedDictionary<CriField, object>();
|
||||
private CriTable parent;
|
||||
|
||||
public object this[CriField criField]
|
||||
{
|
||||
get
|
||||
{
|
||||
return records[criField];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
records[criField] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public object this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return records[index];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
records[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public object this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
return this[records.Single(k => (k.Key).FieldName == name).Key];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this[records.Single(k => (k.Key).FieldName == name).Key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public CriTable Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
internal set
|
||||
{
|
||||
parent = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal OrderedDictionary<CriField, object> Records
|
||||
{
|
||||
get
|
||||
{
|
||||
return records;
|
||||
}
|
||||
}
|
||||
|
||||
public int FieldCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return records.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public object[] GetValueArray()
|
||||
{
|
||||
object[] values = new object[records.Count];
|
||||
|
||||
for (int i = 0; i < records.Count; i++)
|
||||
{
|
||||
values[i] = records[i];
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
foreach (var keyValPair in records)
|
||||
{
|
||||
yield return keyValPair.Value;
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
internal CriRow(CriTable parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
83
Source/SonicAudioLib/CriMw/CriRowCollection.cs
Normal file
83
Source/SonicAudioLib/CriMw/CriRowCollection.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SonicAudioLib.CriMw
|
||||
{
|
||||
public class CriRowCollection : IEnumerable<CriRow>
|
||||
{
|
||||
private CriTable parent;
|
||||
private List<CriRow> rows = new List<CriRow>();
|
||||
|
||||
public CriRow this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return rows[index];
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return rows.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public CriTable Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
internal set
|
||||
{
|
||||
parent = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(CriRow criRow)
|
||||
{
|
||||
criRow.Parent = parent;
|
||||
rows.Add(criRow);
|
||||
}
|
||||
|
||||
public CriRow Add(params object[] objs)
|
||||
{
|
||||
CriRow criRow = parent.NewRow();
|
||||
|
||||
object[] objects = new object[criRow.FieldCount];
|
||||
Array.Copy(objs, objects, Math.Min(objs.Length, objects.Length));
|
||||
|
||||
for (int i = 0; i < criRow.FieldCount; i++)
|
||||
{
|
||||
criRow[i] = objects[i];
|
||||
}
|
||||
|
||||
Add(criRow);
|
||||
return criRow;
|
||||
}
|
||||
|
||||
internal void Clear()
|
||||
{
|
||||
rows.Clear();
|
||||
}
|
||||
|
||||
public IEnumerator<CriRow> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<CriRow>)rows).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<CriRow>)rows).GetEnumerator();
|
||||
}
|
||||
|
||||
public CriRowCollection(CriTable parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
52
Source/SonicAudioLib/CriMw/CriTable.Internal.cs
Normal file
52
Source/SonicAudioLib/CriMw/CriTable.Internal.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
|
||||
namespace SonicAudioLib.CriMw
|
||||
{
|
||||
struct CriTableHeader
|
||||
{
|
||||
public static readonly string Signature = "@UTF";
|
||||
public uint Length { get; set; }
|
||||
public bool FirstBoolean { get; set; }
|
||||
public bool SecondBoolean { get; set; }
|
||||
public ushort RowsPosition { get; set; }
|
||||
public uint StringPoolPosition { get; set; }
|
||||
public uint DataPoolPosition { get; set; }
|
||||
public string TableName { get; set; }
|
||||
public ushort NumberOfFields { get; set; }
|
||||
public ushort RowLength { get; set; }
|
||||
public uint NumberOfRows { get; set; }
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum CriFieldFlag
|
||||
{
|
||||
Name = 16,
|
||||
DefaultValue = 32,
|
||||
RowStorage = 64,
|
||||
|
||||
Byte = 0,
|
||||
SByte = 1,
|
||||
UInt16 = 2,
|
||||
Int16 = 3,
|
||||
UInt32 = 4,
|
||||
Int32 = 5,
|
||||
UInt64 = 6,
|
||||
Int64 = 7,
|
||||
Float = 8,
|
||||
Double = 9,
|
||||
String = 10,
|
||||
Data = 11,
|
||||
Guid = 12,
|
||||
|
||||
TypeMask = 15,
|
||||
};
|
||||
|
||||
struct CriTableField
|
||||
{
|
||||
public CriFieldFlag Flag { get; set; }
|
||||
public string Name { get; set; }
|
||||
public uint Position { get; set; }
|
||||
public uint Length { get; set; }
|
||||
public object Value { get; set; }
|
||||
}
|
||||
}
|
143
Source/SonicAudioLib/CriMw/CriTable.cs
Normal file
143
Source/SonicAudioLib/CriMw/CriTable.cs
Normal file
@ -0,0 +1,143 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.Module;
|
||||
|
||||
namespace SonicAudioLib.CriMw
|
||||
{
|
||||
public class CriTable : ModuleBase
|
||||
{
|
||||
private CriFieldCollection fields;
|
||||
private CriRowCollection rows;
|
||||
private string tableName = "(no name)";
|
||||
private CriTableWriterSettings writerSettings;
|
||||
|
||||
public CriFieldCollection Fields
|
||||
{
|
||||
get
|
||||
{
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
|
||||
public CriRowCollection Rows
|
||||
{
|
||||
get
|
||||
{
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
||||
public string TableName
|
||||
{
|
||||
get
|
||||
{
|
||||
return tableName;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
tableName = value;
|
||||
}
|
||||
}
|
||||
|
||||
public CriTableWriterSettings WriterSettings
|
||||
{
|
||||
get
|
||||
{
|
||||
return writerSettings;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
writerSettings = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
rows.Clear();
|
||||
fields.Clear();
|
||||
}
|
||||
|
||||
public CriRow NewRow()
|
||||
{
|
||||
CriRow criRow = new CriRow(this);
|
||||
|
||||
foreach (CriField criField in fields)
|
||||
{
|
||||
criRow.Records.Add(criField, criField.DefaultValue);
|
||||
}
|
||||
|
||||
return criRow;
|
||||
}
|
||||
|
||||
public override void Read(Stream source)
|
||||
{
|
||||
using (CriTableReader reader = CriTableReader.Create(source))
|
||||
{
|
||||
tableName = reader.TableName;
|
||||
|
||||
for (int i = 0; i < reader.NumberOfFields; i++)
|
||||
{
|
||||
fields.Add(reader.GetFieldName(i), reader.GetFieldType(i), reader.GetFieldValue(i));
|
||||
}
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
rows.Add(reader.GetValueArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(Stream destination)
|
||||
{
|
||||
using (CriTableWriter writer = CriTableWriter.Create(destination, writerSettings))
|
||||
{
|
||||
writer.WriteStartTable(tableName);
|
||||
|
||||
writer.WriteStartFieldCollection();
|
||||
foreach (CriField criField in fields)
|
||||
{
|
||||
if (!rows.Any(row => row[criField] != criField.DefaultValue))
|
||||
{
|
||||
writer.WriteField(criField.FieldName, criField.FieldType, criField.DefaultValue);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
writer.WriteField(criField.FieldName, criField.FieldType);
|
||||
}
|
||||
}
|
||||
writer.WriteEndFieldCollection();
|
||||
|
||||
foreach (CriRow criRow in rows)
|
||||
{
|
||||
writer.WriteRow(true, criRow.GetValueArray());
|
||||
}
|
||||
|
||||
writer.WriteEndTable();
|
||||
}
|
||||
}
|
||||
|
||||
public override long CalculateLength()
|
||||
{
|
||||
// TODO
|
||||
return base.CalculateLength();
|
||||
}
|
||||
|
||||
public CriTable()
|
||||
{
|
||||
fields = new CriFieldCollection(this);
|
||||
rows = new CriRowCollection(this);
|
||||
writerSettings = new CriTableWriterSettings();
|
||||
}
|
||||
|
||||
public CriTable(string tableName) : this()
|
||||
{
|
||||
this.tableName = tableName;
|
||||
}
|
||||
}
|
||||
}
|
702
Source/SonicAudioLib/CriMw/CriTableReader.cs
Normal file
702
Source/SonicAudioLib/CriMw/CriTableReader.cs
Normal file
@ -0,0 +1,702 @@
|
||||
using SonicAudioLib.Collections;
|
||||
using SonicAudioLib.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SonicAudioLib.CriMw
|
||||
{
|
||||
public class CriTableReader : IDisposable
|
||||
{
|
||||
private OrderedDictionary<string, CriTableField> fields;
|
||||
private Stream source;
|
||||
private CriTableHeader header;
|
||||
private int rowIndex = -1;
|
||||
private uint headerPosition;
|
||||
private bool leaveOpen;
|
||||
|
||||
public object this[int fieldIndex]
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetValue(fieldIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public object this[string fieldName]
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetValue(fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort NumberOfFields
|
||||
{
|
||||
get
|
||||
{
|
||||
return header.NumberOfFields;
|
||||
}
|
||||
}
|
||||
|
||||
public uint NumberOfRows
|
||||
{
|
||||
get
|
||||
{
|
||||
return header.NumberOfRows;
|
||||
}
|
||||
}
|
||||
|
||||
public string TableName
|
||||
{
|
||||
get
|
||||
{
|
||||
return header.TableName;
|
||||
}
|
||||
}
|
||||
|
||||
public int CurrentRow
|
||||
{
|
||||
get
|
||||
{
|
||||
return rowIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public Stream SourceStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadTable()
|
||||
{
|
||||
headerPosition = (uint)source.Position;
|
||||
|
||||
if (EndianStream.ReadCString(source, 4) != CriTableHeader.Signature)
|
||||
{
|
||||
throw new Exception("No @UTF signature found.");
|
||||
}
|
||||
|
||||
header.Length = ReadUInt32() + 0x8;
|
||||
header.FirstBoolean = ReadBoolean();
|
||||
header.SecondBoolean = ReadBoolean();
|
||||
header.RowsPosition = (ushort)(ReadUInt16() + 0x8);
|
||||
header.StringPoolPosition = ReadUInt32() + 0x8;
|
||||
header.DataPoolPosition = ReadUInt32() + 0x8;
|
||||
header.TableName = ReadString();
|
||||
header.NumberOfFields = ReadUInt16();
|
||||
header.RowLength = ReadUInt16();
|
||||
header.NumberOfRows = ReadUInt32();
|
||||
|
||||
if (header.FirstBoolean)
|
||||
{
|
||||
throw new Exception($"Invalid boolean ({header.FirstBoolean}. Please report the error with the file.");
|
||||
}
|
||||
|
||||
for (ushort i = 0; i < header.NumberOfFields; i++)
|
||||
{
|
||||
CriTableField field = new CriTableField();
|
||||
|
||||
field.Flag = (CriFieldFlag)ReadByte();
|
||||
|
||||
if (field.Flag.HasFlag(CriFieldFlag.Name))
|
||||
{
|
||||
field.Name = ReadString();
|
||||
}
|
||||
|
||||
if (field.Flag.HasFlag(CriFieldFlag.DefaultValue))
|
||||
{
|
||||
if (field.Flag.HasFlag(CriFieldFlag.Data))
|
||||
{
|
||||
uint vldPosition;
|
||||
uint vldLength;
|
||||
|
||||
ReadData(out vldPosition, out vldLength);
|
||||
|
||||
field.Position = vldPosition;
|
||||
field.Length = vldLength;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
field.Value = ReadValue(field.Flag);
|
||||
}
|
||||
}
|
||||
|
||||
// Not even per row, and not even constant value? Then there's no storage.
|
||||
else if (!field.Flag.HasFlag(CriFieldFlag.RowStorage) && !field.Flag.HasFlag(CriFieldFlag.DefaultValue))
|
||||
{
|
||||
if (field.Flag.HasFlag(CriFieldFlag.Data))
|
||||
{
|
||||
field.Position = 0;
|
||||
field.Length = 0;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
field.Value = CriField.NullValues[(byte)field.Flag & 0x0F];
|
||||
}
|
||||
}
|
||||
|
||||
fields.Add(field.Name, field);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetFieldName(int fieldIndex)
|
||||
{
|
||||
return fields[fieldIndex].Name;
|
||||
}
|
||||
|
||||
public Type GetFieldType(int fieldIndex)
|
||||
{
|
||||
return CriField.FieldTypes[(byte)fields[fieldIndex].Flag & 0x0F];
|
||||
}
|
||||
|
||||
public Type GetFieldType(string fieldName)
|
||||
{
|
||||
return CriField.FieldTypes[(byte)fields[fieldName].Flag & 0x0F];
|
||||
}
|
||||
|
||||
public object GetFieldValue(int fieldIndex)
|
||||
{
|
||||
return fields[fieldIndex].Value;
|
||||
}
|
||||
|
||||
public byte GetFieldFlag(string fieldName)
|
||||
{
|
||||
return (byte)fields[fieldName].Flag;
|
||||
}
|
||||
|
||||
public byte GetFieldFlag(int fieldIndex)
|
||||
{
|
||||
return (byte)fields[fieldIndex].Flag;
|
||||
}
|
||||
|
||||
public object GetFieldValue(string fieldName)
|
||||
{
|
||||
return fields[fieldName].Value;
|
||||
}
|
||||
|
||||
public CriField GetField(int fieldIndex)
|
||||
{
|
||||
return new CriField(GetFieldName(fieldIndex), GetFieldType(fieldIndex), GetFieldValue(fieldIndex));
|
||||
}
|
||||
|
||||
public CriField GetField(string fieldName)
|
||||
{
|
||||
return new CriField(fieldName, GetFieldType(fieldName), GetFieldValue(fieldName));
|
||||
}
|
||||
|
||||
private void GoToValue(int fieldIndex)
|
||||
{
|
||||
long position = headerPosition + header.RowsPosition + (header.RowLength * rowIndex);
|
||||
|
||||
for (int i = 0; i < fieldIndex; i++)
|
||||
{
|
||||
if (!fields[i].Flag.HasFlag(CriFieldFlag.RowStorage))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (fields[i].Flag & CriFieldFlag.TypeMask)
|
||||
{
|
||||
case CriFieldFlag.Byte:
|
||||
case CriFieldFlag.SByte:
|
||||
position += 1;
|
||||
break;
|
||||
case CriFieldFlag.Int16:
|
||||
case CriFieldFlag.UInt16:
|
||||
position += 2;
|
||||
break;
|
||||
case CriFieldFlag.Int32:
|
||||
case CriFieldFlag.UInt32:
|
||||
case CriFieldFlag.Float:
|
||||
case CriFieldFlag.String:
|
||||
position += 4;
|
||||
break;
|
||||
case CriFieldFlag.Int64:
|
||||
case CriFieldFlag.UInt64:
|
||||
case CriFieldFlag.Double:
|
||||
case CriFieldFlag.Data:
|
||||
position += 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
source.Position = position;
|
||||
}
|
||||
|
||||
public bool Read()
|
||||
{
|
||||
if (rowIndex + 1 >= header.NumberOfRows)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rowIndex++;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool MoveToRow(int rowIndex)
|
||||
{
|
||||
if (rowIndex >= header.NumberOfRows)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this.rowIndex = rowIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
public object[] GetValueArray()
|
||||
{
|
||||
object[] values = new object[header.NumberOfFields];
|
||||
|
||||
for (int i = 0; i < header.NumberOfFields; i++)
|
||||
{
|
||||
if (fields[i].Flag.HasFlag(CriFieldFlag.Data))
|
||||
{
|
||||
values[i] = GetData(i);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
values[i] = GetValue(i);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
public object GetValue(int fieldIndex)
|
||||
{
|
||||
if (fieldIndex < 0 || fieldIndex >= fields.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!fields[fieldIndex].Flag.HasFlag(CriFieldFlag.RowStorage))
|
||||
{
|
||||
if (fields[fieldIndex].Flag.HasFlag(CriFieldFlag.Data))
|
||||
{
|
||||
return new Substream(source, 0, 0);
|
||||
}
|
||||
|
||||
return fields[fieldIndex].Value;
|
||||
}
|
||||
|
||||
GoToValue(fieldIndex);
|
||||
return ReadValue(fields[fieldIndex].Flag);
|
||||
}
|
||||
|
||||
public object GetValue(string fieldName)
|
||||
{
|
||||
return GetValue(fields.IndexOf(fieldName));
|
||||
}
|
||||
|
||||
public T GetValue<T>(int fieldIndex)
|
||||
{
|
||||
return (T)GetValue(fieldIndex);
|
||||
}
|
||||
|
||||
public T GetValue<T>(string fieldName)
|
||||
{
|
||||
return (T)GetValue(fieldName);
|
||||
}
|
||||
|
||||
public byte GetByte(int fieldIndex)
|
||||
{
|
||||
return (byte)GetValue(fieldIndex);
|
||||
}
|
||||
|
||||
public byte GetByte(string fieldName)
|
||||
{
|
||||
return (byte)GetValue(fieldName);
|
||||
}
|
||||
|
||||
public sbyte GetSByte(int fieldIndex)
|
||||
{
|
||||
return (sbyte)GetValue(fieldIndex);
|
||||
}
|
||||
|
||||
public sbyte GetSByte(string fieldName)
|
||||
{
|
||||
return (sbyte)GetValue(fieldName);
|
||||
}
|
||||
|
||||
public ushort GetUInt16(int fieldIndex)
|
||||
{
|
||||
return (ushort)GetValue(fieldIndex);
|
||||
}
|
||||
|
||||
public ushort GetUInt16(string fieldName)
|
||||
{
|
||||
return (ushort)GetValue(fieldName);
|
||||
}
|
||||
|
||||
public short GetInt16(int fieldIndex)
|
||||
{
|
||||
return (short)GetValue(fieldIndex);
|
||||
}
|
||||
|
||||
public short GetInt16(string fieldName)
|
||||
{
|
||||
return (short)GetValue(fieldName);
|
||||
}
|
||||
|
||||
public uint GetUInt32(int fieldIndex)
|
||||
{
|
||||
return (uint)GetValue(fieldIndex);
|
||||
}
|
||||
|
||||
public uint GetUInt32(string fieldName)
|
||||
{
|
||||
return (uint)GetValue(fieldName);
|
||||
}
|
||||
|
||||
public int GetInt32(int fieldIndex)
|
||||
{
|
||||
return (int)GetValue(fieldIndex);
|
||||
}
|
||||
|
||||
public int GetInt32(string fieldName)
|
||||
{
|
||||
return (int)GetValue(fieldName);
|
||||
}
|
||||
|
||||
public ulong GetUInt64(int fieldIndex)
|
||||
{
|
||||
return (ulong)GetValue(fieldIndex);
|
||||
}
|
||||
|
||||
public ulong GetUInt64(string fieldName)
|
||||
{
|
||||
return (ulong)GetValue(fieldName);
|
||||
}
|
||||
|
||||
public long GetInt64(int fieldIndex)
|
||||
{
|
||||
return (long)GetValue(fieldIndex);
|
||||
}
|
||||
|
||||
public long GetInt64(string fieldName)
|
||||
{
|
||||
return (long)GetValue(fieldName);
|
||||
}
|
||||
|
||||
public float GetFloat(int fieldIndex)
|
||||
{
|
||||
return (float)GetValue(fieldIndex);
|
||||
}
|
||||
|
||||
public float GetFloat(string fieldName)
|
||||
{
|
||||
return (float)GetValue(fieldName);
|
||||
}
|
||||
|
||||
public double GetDouble(int fieldIndex)
|
||||
{
|
||||
return (double)GetValue(fieldIndex);
|
||||
}
|
||||
|
||||
public double GetDouble(string fieldName)
|
||||
{
|
||||
return (double)GetValue(fieldName);
|
||||
}
|
||||
|
||||
public string GetString(int fieldIndex)
|
||||
{
|
||||
return (string)GetValue(fieldIndex);
|
||||
}
|
||||
|
||||
public string GetString(string fieldName)
|
||||
{
|
||||
return (string)GetValue(fieldName);
|
||||
}
|
||||
|
||||
public Substream GetSubstream(int fieldIndex)
|
||||
{
|
||||
return (Substream)GetValue(fieldIndex);
|
||||
}
|
||||
|
||||
public Substream GetSubstream(string fieldName)
|
||||
{
|
||||
return (Substream)GetValue(fieldName);
|
||||
}
|
||||
|
||||
public byte[] GetData(int fieldIndex)
|
||||
{
|
||||
return GetSubstream(fieldIndex).ToArray();
|
||||
}
|
||||
|
||||
public byte[] GetData(string fieldName)
|
||||
{
|
||||
return GetData(fields.IndexOf(fieldName));
|
||||
}
|
||||
|
||||
public uint GetLength(int fieldIndex)
|
||||
{
|
||||
if (fieldIndex < 0 || fieldIndex >= fields.Count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!fields[fieldIndex].Flag.HasFlag(CriFieldFlag.RowStorage))
|
||||
{
|
||||
return fields[fieldIndex].Length;
|
||||
}
|
||||
|
||||
uint vldPosition;
|
||||
uint vldLength;
|
||||
|
||||
GoToValue(fieldIndex);
|
||||
ReadData(out vldPosition, out vldLength);
|
||||
return vldLength;
|
||||
}
|
||||
|
||||
public uint GetLength(string fieldName)
|
||||
{
|
||||
return GetLength(fields.IndexOf(fieldName));
|
||||
}
|
||||
|
||||
public uint GetPosition(int fieldIndex)
|
||||
{
|
||||
if (fieldIndex < 0 || fieldIndex >= fields.Count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!fields[fieldIndex].Flag.HasFlag(CriFieldFlag.RowStorage))
|
||||
{
|
||||
return fields[fieldIndex].Position;
|
||||
}
|
||||
|
||||
uint vldPosition;
|
||||
uint vldLength;
|
||||
|
||||
GoToValue(fieldIndex);
|
||||
ReadData(out vldPosition, out vldLength);
|
||||
return (uint)(headerPosition + header.DataPoolPosition + vldPosition);
|
||||
}
|
||||
|
||||
public uint GetPosition(string fieldName)
|
||||
{
|
||||
return GetPosition(fields.IndexOf(fieldName));
|
||||
}
|
||||
|
||||
public bool GetBoolean(int fieldIndex)
|
||||
{
|
||||
return (byte)GetValue(fieldIndex) > 0;
|
||||
}
|
||||
|
||||
public bool GetBoolean(string fieldName)
|
||||
{
|
||||
return (byte)GetValue(fieldName) > 0;
|
||||
}
|
||||
|
||||
public Guid GetGuid(int fieldIndex)
|
||||
{
|
||||
return (Guid)GetValue(fieldIndex);
|
||||
}
|
||||
|
||||
public Guid GetGuid(string fieldName)
|
||||
{
|
||||
return (Guid)GetValue(fieldName);
|
||||
}
|
||||
|
||||
private byte[] ReadBytes(int length)
|
||||
{
|
||||
byte[] buff = new byte[length];
|
||||
source.Read(buff, 0, length);
|
||||
return buff;
|
||||
}
|
||||
|
||||
private byte ReadByte()
|
||||
{
|
||||
return EndianStream.ReadByte(source);
|
||||
}
|
||||
|
||||
private bool ReadBoolean()
|
||||
{
|
||||
return EndianStream.ReadBoolean(source);
|
||||
}
|
||||
|
||||
private sbyte ReadSByte()
|
||||
{
|
||||
return EndianStream.ReadSByte(source);
|
||||
}
|
||||
|
||||
private ushort ReadUInt16()
|
||||
{
|
||||
return EndianStream.ReadUInt16BE(source);
|
||||
}
|
||||
|
||||
private short ReadInt16()
|
||||
{
|
||||
return EndianStream.ReadInt16BE(source);
|
||||
}
|
||||
|
||||
private uint ReadUInt32()
|
||||
{
|
||||
return EndianStream.ReadUInt32BE(source);
|
||||
}
|
||||
|
||||
private int ReadInt32()
|
||||
{
|
||||
return EndianStream.ReadInt32BE(source);
|
||||
}
|
||||
|
||||
private ulong ReadUInt64()
|
||||
{
|
||||
return EndianStream.ReadUInt64BE(source);
|
||||
}
|
||||
|
||||
private long ReadInt64()
|
||||
{
|
||||
return EndianStream.ReadInt64BE(source);
|
||||
}
|
||||
|
||||
private float ReadFloat()
|
||||
{
|
||||
return EndianStream.ReadFloatBE(source);
|
||||
}
|
||||
|
||||
private double ReadDouble()
|
||||
{
|
||||
return EndianStream.ReadDoubleBE(source);
|
||||
}
|
||||
|
||||
private string ReadString()
|
||||
{
|
||||
int stringPosition = ReadInt32();
|
||||
|
||||
long previousPosition = source.Position;
|
||||
|
||||
source.Position = headerPosition + header.StringPoolPosition + stringPosition;
|
||||
string strResult = EndianStream.ReadCString(source, Encoding.Default);
|
||||
source.Position = previousPosition;
|
||||
|
||||
if (strResult == "<NULL>" ||
|
||||
(strResult == header.TableName && stringPosition == 0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return strResult;
|
||||
}
|
||||
|
||||
private void ReadData(out uint vldPosition, out uint vldLength)
|
||||
{
|
||||
vldPosition = ReadUInt32();
|
||||
vldLength = ReadUInt32();
|
||||
}
|
||||
|
||||
private Guid ReadGuid()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
source.Read(buffer, 0, buffer.Length);
|
||||
return new Guid(buffer);
|
||||
}
|
||||
|
||||
private object ReadValue(CriFieldFlag fieldFlag)
|
||||
{
|
||||
switch (fieldFlag & CriFieldFlag.TypeMask)
|
||||
{
|
||||
case CriFieldFlag.Byte:
|
||||
return ReadByte();
|
||||
case CriFieldFlag.SByte:
|
||||
return ReadSByte();
|
||||
case CriFieldFlag.UInt16:
|
||||
return ReadUInt16();
|
||||
case CriFieldFlag.Int16:
|
||||
return ReadInt16();
|
||||
case CriFieldFlag.UInt32:
|
||||
return ReadUInt32();
|
||||
case CriFieldFlag.Int32:
|
||||
return ReadInt32();
|
||||
case CriFieldFlag.UInt64:
|
||||
return ReadUInt64();
|
||||
case CriFieldFlag.Int64:
|
||||
return ReadInt64();
|
||||
case CriFieldFlag.Float:
|
||||
return ReadFloat();
|
||||
case CriFieldFlag.Double:
|
||||
return ReadDouble();
|
||||
case CriFieldFlag.String:
|
||||
return ReadString();
|
||||
case CriFieldFlag.Data:
|
||||
{
|
||||
uint vldPosition;
|
||||
uint vldLength;
|
||||
|
||||
ReadData(out vldPosition, out vldLength);
|
||||
|
||||
// SecondBoolean being true, check if utf table
|
||||
if (vldPosition > 0 && vldLength == 0)
|
||||
{
|
||||
source.Position = headerPosition + header.DataPoolPosition + vldPosition;
|
||||
|
||||
if (Encoding.ASCII.GetString(ReadBytes(4)) == "@UTF")
|
||||
{
|
||||
vldLength = ReadUInt32() + 8;
|
||||
}
|
||||
}
|
||||
|
||||
return new Substream(source, headerPosition + header.DataPoolPosition + vldPosition, vldLength);
|
||||
}
|
||||
case CriFieldFlag.Guid:
|
||||
return ReadGuid();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
fields.Clear();
|
||||
|
||||
if (!leaveOpen)
|
||||
{
|
||||
source.Close();
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public static CriTableReader Create(byte[] sourceByteArray)
|
||||
{
|
||||
Stream source = new MemoryStream(sourceByteArray);
|
||||
return Create(source);
|
||||
}
|
||||
|
||||
public static CriTableReader Create(string sourceFileName)
|
||||
{
|
||||
Stream source = File.OpenRead(sourceFileName);
|
||||
return Create(source);
|
||||
}
|
||||
|
||||
public static CriTableReader Create(Stream source)
|
||||
{
|
||||
return Create(source, false);
|
||||
}
|
||||
|
||||
public static CriTableReader Create(Stream source, bool leaveOpen)
|
||||
{
|
||||
return new CriTableReader(source, leaveOpen);
|
||||
}
|
||||
|
||||
private CriTableReader(Stream source, bool leaveOpen)
|
||||
{
|
||||
this.source = source;
|
||||
header = new CriTableHeader();
|
||||
fields = new OrderedDictionary<string, CriTableField>();
|
||||
this.leaveOpen = leaveOpen;
|
||||
|
||||
ReadTable();
|
||||
}
|
||||
}
|
||||
}
|
713
Source/SonicAudioLib/CriMw/CriTableWriter.cs
Normal file
713
Source/SonicAudioLib/CriMw/CriTableWriter.cs
Normal file
@ -0,0 +1,713 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
using SonicAudioLib.Collections;
|
||||
using SonicAudioLib.IO;
|
||||
using SonicAudioLib.Module;
|
||||
|
||||
namespace SonicAudioLib.CriMw
|
||||
{
|
||||
public class CriTableWriter : IDisposable
|
||||
{
|
||||
public enum Status
|
||||
{
|
||||
Begin,
|
||||
Start,
|
||||
FieldCollection,
|
||||
Row,
|
||||
Idle,
|
||||
End,
|
||||
}
|
||||
|
||||
private CriTableWriterSettings settings;
|
||||
private OrderedDictionary<string, CriTableField> fields;
|
||||
private Stream destination;
|
||||
private CriTableHeader header;
|
||||
private VldPool vldPool;
|
||||
private StringPool stringPool;
|
||||
private uint headerPosition;
|
||||
private uint endPosition;
|
||||
|
||||
private Status status = Status.Begin;
|
||||
|
||||
public Status CurrentStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
public Stream DestinationStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteStartTable()
|
||||
{
|
||||
WriteStartTable("(no name)");
|
||||
}
|
||||
|
||||
public void WriteStartTable(string tableName)
|
||||
{
|
||||
if (status != Status.Begin)
|
||||
{
|
||||
throw new InvalidOperationException("Attempted to start table when the status wasn't Begin");
|
||||
}
|
||||
|
||||
status = Status.Start;
|
||||
|
||||
headerPosition = (uint)destination.Position;
|
||||
header.TableName = tableName;
|
||||
|
||||
if (settings.PutBlankString)
|
||||
{
|
||||
stringPool.Put(StringPool.AdxBlankString);
|
||||
}
|
||||
|
||||
EndianStream.WriteCString(destination, CriTableHeader.Signature, 4);
|
||||
WriteUInt32(uint.MinValue);
|
||||
WriteBoolean(false);
|
||||
WriteBoolean(false);
|
||||
WriteUInt16(ushort.MinValue);
|
||||
WriteUInt32(uint.MinValue);
|
||||
WriteUInt32(uint.MinValue);
|
||||
WriteString(tableName);
|
||||
WriteUInt16(ushort.MinValue);
|
||||
WriteUInt16(ushort.MinValue);
|
||||
WriteUInt32(uint.MinValue);
|
||||
}
|
||||
|
||||
public void WriteEndTable()
|
||||
{
|
||||
if (status == Status.FieldCollection)
|
||||
{
|
||||
WriteEndFieldCollection();
|
||||
}
|
||||
|
||||
if (status == Status.Row)
|
||||
{
|
||||
WriteEndRow();
|
||||
}
|
||||
|
||||
status = Status.End;
|
||||
|
||||
destination.Seek(headerPosition + header.RowsPosition + (header.RowLength * header.NumberOfRows), SeekOrigin.Begin);
|
||||
|
||||
stringPool.Write(destination);
|
||||
header.StringPoolPosition = (uint)stringPool.Position - headerPosition;
|
||||
|
||||
while ((destination.Position % vldPool.Align) != 0)
|
||||
{
|
||||
destination.WriteByte(0);
|
||||
}
|
||||
|
||||
vldPool.Write(destination);
|
||||
header.DataPoolPosition = (uint)vldPool.Position - headerPosition;
|
||||
|
||||
while ((destination.Position % vldPool.Align) != 0)
|
||||
{
|
||||
destination.WriteByte(0);
|
||||
}
|
||||
|
||||
header.Length = (uint)destination.Position - headerPosition;
|
||||
|
||||
header.FirstBoolean = false;
|
||||
header.SecondBoolean = false;
|
||||
|
||||
destination.Position = headerPosition + 4;
|
||||
WriteUInt32(header.Length - 8);
|
||||
WriteBoolean(header.FirstBoolean);
|
||||
WriteBoolean(header.SecondBoolean);
|
||||
WriteUInt16((ushort)(header.RowsPosition - 8));
|
||||
WriteUInt32(header.StringPoolPosition - 8);
|
||||
WriteUInt32(header.DataPoolPosition - 8);
|
||||
destination.Seek(4, SeekOrigin.Current);
|
||||
WriteUInt16(header.NumberOfFields);
|
||||
WriteUInt16(header.RowLength);
|
||||
WriteUInt32(header.NumberOfRows);
|
||||
destination.Seek(0, SeekOrigin.End);
|
||||
}
|
||||
|
||||
public void WriteStartFieldCollection()
|
||||
{
|
||||
if (status != Status.Start)
|
||||
{
|
||||
throw new InvalidOperationException("Attempted to start field collection when the status wasn't Start");
|
||||
}
|
||||
|
||||
status = Status.FieldCollection;
|
||||
}
|
||||
|
||||
public void WriteField(string fieldName, Type fieldType, object defaultValue)
|
||||
{
|
||||
if (status != Status.FieldCollection)
|
||||
{
|
||||
WriteStartFieldCollection();
|
||||
}
|
||||
|
||||
CriFieldFlag fieldFlag = (CriFieldFlag)Array.IndexOf(CriField.FieldTypes, fieldType);
|
||||
|
||||
if (!string.IsNullOrEmpty(fieldName))
|
||||
{
|
||||
fieldFlag |= CriFieldFlag.Name;
|
||||
}
|
||||
|
||||
if (defaultValue != null)
|
||||
{
|
||||
fieldFlag |= CriFieldFlag.DefaultValue;
|
||||
}
|
||||
|
||||
CriTableField field = new CriTableField
|
||||
{
|
||||
Flag = fieldFlag,
|
||||
Name = fieldName,
|
||||
Value = defaultValue
|
||||
};
|
||||
|
||||
WriteByte((byte)field.Flag);
|
||||
|
||||
if (!string.IsNullOrEmpty(fieldName))
|
||||
{
|
||||
WriteString(field.Name);
|
||||
}
|
||||
|
||||
if (defaultValue != null)
|
||||
{
|
||||
WriteValue(defaultValue);
|
||||
}
|
||||
|
||||
fields.Add(fieldName, field);
|
||||
header.NumberOfFields++;
|
||||
}
|
||||
|
||||
public void WriteField(string fieldName, Type fieldType)
|
||||
{
|
||||
if (status != Status.FieldCollection)
|
||||
{
|
||||
WriteStartFieldCollection();
|
||||
}
|
||||
|
||||
CriFieldFlag fieldFlag = (CriFieldFlag)Array.IndexOf(CriField.FieldTypes, fieldType) | CriFieldFlag.RowStorage;
|
||||
|
||||
if (!string.IsNullOrEmpty(fieldName))
|
||||
{
|
||||
fieldFlag |= CriFieldFlag.Name;
|
||||
}
|
||||
|
||||
CriTableField field = new CriTableField
|
||||
{
|
||||
Flag = fieldFlag,
|
||||
Name = fieldName
|
||||
};
|
||||
|
||||
WriteByte((byte)field.Flag);
|
||||
|
||||
if (!string.IsNullOrEmpty(fieldName))
|
||||
{
|
||||
WriteString(field.Name);
|
||||
}
|
||||
|
||||
fields.Add(fieldName, field);
|
||||
header.NumberOfFields++;
|
||||
}
|
||||
|
||||
public void WriteField(CriField criField)
|
||||
{
|
||||
WriteField(criField.FieldName, criField.FieldType);
|
||||
}
|
||||
|
||||
public void WriteEndFieldCollection()
|
||||
{
|
||||
if (status != Status.FieldCollection)
|
||||
{
|
||||
throw new InvalidOperationException("Attempted to end field collection when the status wasn't FieldCollection");
|
||||
}
|
||||
|
||||
status = Status.Idle;
|
||||
|
||||
header.RowsPosition = (ushort)(destination.Position - headerPosition);
|
||||
header.RowLength = CalculateRowLength();
|
||||
}
|
||||
|
||||
public void WriteStartRow()
|
||||
{
|
||||
if (status == Status.FieldCollection)
|
||||
{
|
||||
WriteEndFieldCollection();
|
||||
}
|
||||
|
||||
if (status != Status.Idle)
|
||||
{
|
||||
throw new InvalidOperationException("Attempted to start row when the status wasn't Idle");
|
||||
}
|
||||
|
||||
status = Status.Row;
|
||||
|
||||
header.NumberOfRows++;
|
||||
|
||||
destination.Position = headerPosition + header.RowsPosition + (header.NumberOfRows * header.RowLength);
|
||||
byte[] buffer = new byte[header.RowLength];
|
||||
destination.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
public void WriteValue(int fieldIndex, object rowValue)
|
||||
{
|
||||
if (!fields[fieldIndex].Flag.HasFlag(CriFieldFlag.RowStorage) || rowValue == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GoToValue(fieldIndex);
|
||||
WriteValue(rowValue);
|
||||
}
|
||||
|
||||
public void WriteValue(string fieldName, object rowValue)
|
||||
{
|
||||
WriteValue(fields.IndexOf(fieldName));
|
||||
}
|
||||
|
||||
private void GoToValue(int fieldIndex)
|
||||
{
|
||||
long position = headerPosition + header.RowsPosition + (header.RowLength * (header.NumberOfRows - 1));
|
||||
|
||||
for (int i = 0; i < fieldIndex; i++)
|
||||
{
|
||||
if (!fields[i].Flag.HasFlag(CriFieldFlag.RowStorage))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (fields[i].Flag & CriFieldFlag.TypeMask)
|
||||
{
|
||||
case CriFieldFlag.Byte:
|
||||
case CriFieldFlag.SByte:
|
||||
position += 1;
|
||||
break;
|
||||
case CriFieldFlag.Int16:
|
||||
case CriFieldFlag.UInt16:
|
||||
position += 2;
|
||||
break;
|
||||
case CriFieldFlag.Int32:
|
||||
case CriFieldFlag.UInt32:
|
||||
case CriFieldFlag.Float:
|
||||
case CriFieldFlag.String:
|
||||
position += 4;
|
||||
break;
|
||||
case CriFieldFlag.Int64:
|
||||
case CriFieldFlag.UInt64:
|
||||
case CriFieldFlag.Double:
|
||||
case CriFieldFlag.Data:
|
||||
position += 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
destination.Position = position;
|
||||
}
|
||||
|
||||
private ushort CalculateRowLength()
|
||||
{
|
||||
ushort length = 0;
|
||||
|
||||
for (int i = 0; i < fields.Count; i++)
|
||||
{
|
||||
if (!fields[i].Flag.HasFlag(CriFieldFlag.RowStorage))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (fields[i].Flag & CriFieldFlag.TypeMask)
|
||||
{
|
||||
case CriFieldFlag.Byte:
|
||||
case CriFieldFlag.SByte:
|
||||
length += 1;
|
||||
break;
|
||||
case CriFieldFlag.Int16:
|
||||
case CriFieldFlag.UInt16:
|
||||
length += 2;
|
||||
break;
|
||||
case CriFieldFlag.Int32:
|
||||
case CriFieldFlag.UInt32:
|
||||
case CriFieldFlag.Float:
|
||||
case CriFieldFlag.String:
|
||||
length += 4;
|
||||
break;
|
||||
case CriFieldFlag.Int64:
|
||||
case CriFieldFlag.UInt64:
|
||||
case CriFieldFlag.Double:
|
||||
case CriFieldFlag.Data:
|
||||
length += 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
public void WriteEndRow()
|
||||
{
|
||||
if (status != Status.Row)
|
||||
{
|
||||
throw new InvalidOperationException("Attempted to end row when the status wasn't Row");
|
||||
}
|
||||
|
||||
status = Status.Idle;
|
||||
}
|
||||
|
||||
public void WriteRow(bool close, params object[] rowValues)
|
||||
{
|
||||
WriteStartRow();
|
||||
|
||||
for (int i = 0; i < Math.Min(rowValues.Length, fields.Count); i++)
|
||||
{
|
||||
WriteValue(i, rowValues[i]);
|
||||
}
|
||||
|
||||
if (close)
|
||||
{
|
||||
WriteEndRow();
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteByte(byte value)
|
||||
{
|
||||
EndianStream.WriteByte(destination, value);
|
||||
}
|
||||
|
||||
private void WriteBoolean(bool value)
|
||||
{
|
||||
EndianStream.WriteBoolean(destination, value);
|
||||
}
|
||||
|
||||
private void WriteSByte(sbyte value)
|
||||
{
|
||||
EndianStream.WriteSByte(destination, value);
|
||||
}
|
||||
|
||||
private void WriteUInt16(ushort value)
|
||||
{
|
||||
EndianStream.WriteUInt16BE(destination, value);
|
||||
}
|
||||
|
||||
private void WriteInt16(short value)
|
||||
{
|
||||
EndianStream.WriteInt16BE(destination, value);
|
||||
}
|
||||
|
||||
private void WriteUInt32(uint value)
|
||||
{
|
||||
EndianStream.WriteUInt32BE(destination, value);
|
||||
}
|
||||
|
||||
private void WriteInt32(int value)
|
||||
{
|
||||
EndianStream.WriteInt32BE(destination, value);
|
||||
}
|
||||
|
||||
private void WriteUInt64(ulong value)
|
||||
{
|
||||
EndianStream.WriteUInt64BE(destination, value);
|
||||
}
|
||||
|
||||
private void WriteInt64(long value)
|
||||
{
|
||||
EndianStream.WriteInt64BE(destination, value);
|
||||
}
|
||||
|
||||
private void WriteFloat(float value)
|
||||
{
|
||||
EndianStream.WriteFloatBE(destination, value);
|
||||
}
|
||||
|
||||
private void WriteDouble(double value)
|
||||
{
|
||||
EndianStream.WriteDoubleBE(destination, value);
|
||||
}
|
||||
|
||||
private void WriteString(string value)
|
||||
{
|
||||
if (settings.RemoveDuplicateStrings && stringPool.ContainsString(value))
|
||||
{
|
||||
WriteUInt32((uint)stringPool.GetStringPosition(value));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
WriteUInt32((uint)stringPool.Put(value));
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteData(byte[] data)
|
||||
{
|
||||
WriteUInt32((uint)vldPool.Put(data));
|
||||
WriteUInt32((uint)data.Length);
|
||||
}
|
||||
|
||||
private void WriteStream(Stream stream)
|
||||
{
|
||||
WriteUInt32((uint)vldPool.Put(stream));
|
||||
WriteUInt32((uint)stream.Length);
|
||||
}
|
||||
|
||||
private void WriteFile(FileInfo fileInfo)
|
||||
{
|
||||
WriteUInt32((uint)vldPool.Put(fileInfo));
|
||||
WriteUInt32((uint)fileInfo.Length);
|
||||
}
|
||||
|
||||
private void WriteModule(ModuleBase module)
|
||||
{
|
||||
WriteUInt32((uint)vldPool.Put(module));
|
||||
WriteUInt32((uint)module.CalculateLength());
|
||||
}
|
||||
|
||||
private void WriteGuid(Guid guid)
|
||||
{
|
||||
byte[] buffer = guid.ToByteArray();
|
||||
destination.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
private void WriteValue(object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (value is byte)
|
||||
{
|
||||
WriteByte((byte)value);
|
||||
}
|
||||
|
||||
else if (value is sbyte)
|
||||
{
|
||||
WriteSByte((sbyte)value);
|
||||
}
|
||||
|
||||
else if (value is ushort)
|
||||
{
|
||||
WriteUInt16((ushort)value);
|
||||
}
|
||||
|
||||
else if (value is short)
|
||||
{
|
||||
WriteInt16((short)value);
|
||||
}
|
||||
|
||||
else if (value is uint)
|
||||
{
|
||||
WriteUInt32((uint)value);
|
||||
}
|
||||
|
||||
else if (value is int)
|
||||
{
|
||||
WriteInt32((int)value);
|
||||
}
|
||||
|
||||
else if (value is ulong)
|
||||
{
|
||||
WriteUInt64((ulong)value);
|
||||
}
|
||||
|
||||
else if (value is long)
|
||||
{
|
||||
WriteInt64((long)value);
|
||||
}
|
||||
|
||||
else if (value is float)
|
||||
{
|
||||
WriteFloat((float)value);
|
||||
}
|
||||
|
||||
else if (value is double)
|
||||
{
|
||||
WriteDouble((double)value);
|
||||
}
|
||||
|
||||
else if (value is string)
|
||||
{
|
||||
WriteString((string)value);
|
||||
}
|
||||
|
||||
else if (value is byte[])
|
||||
{
|
||||
WriteData((byte[])value);
|
||||
}
|
||||
|
||||
else if (value is Stream)
|
||||
{
|
||||
WriteStream((Stream)value);
|
||||
}
|
||||
|
||||
else if (value is FileInfo)
|
||||
{
|
||||
WriteFile((FileInfo)value);
|
||||
}
|
||||
|
||||
else if (value is ModuleBase)
|
||||
{
|
||||
WriteModule((ModuleBase)value);
|
||||
}
|
||||
|
||||
else if (value is Guid)
|
||||
{
|
||||
WriteGuid((Guid)value);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
fields.Clear();
|
||||
stringPool.Clear();
|
||||
vldPool.Clear();
|
||||
|
||||
if (!settings.LeaveOpen)
|
||||
{
|
||||
destination.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public static CriTableWriter Create(string destinationFileName)
|
||||
{
|
||||
return Create(destinationFileName, new CriTableWriterSettings());
|
||||
}
|
||||
|
||||
public static CriTableWriter Create(string destinationFileName, CriTableWriterSettings settings)
|
||||
{
|
||||
Stream destination = File.Create(destinationFileName);
|
||||
return new CriTableWriter(destination, settings);
|
||||
}
|
||||
|
||||
public static CriTableWriter Create(Stream destination)
|
||||
{
|
||||
return new CriTableWriter(destination, new CriTableWriterSettings());
|
||||
}
|
||||
|
||||
public static CriTableWriter Create(Stream destination, CriTableWriterSettings settings)
|
||||
{
|
||||
return new CriTableWriter(destination, settings);
|
||||
}
|
||||
|
||||
private CriTableWriter(Stream destination, CriTableWriterSettings settings)
|
||||
{
|
||||
this.destination = destination;
|
||||
this.settings = settings;
|
||||
|
||||
header = new CriTableHeader();
|
||||
fields = new OrderedDictionary<string, CriTableField>();
|
||||
stringPool = new StringPool(settings.EncodingType);
|
||||
vldPool = new VldPool(settings.Align);
|
||||
}
|
||||
}
|
||||
|
||||
public class CriTableWriterSettings
|
||||
{
|
||||
private uint align = 1;
|
||||
private bool putBlankString = true;
|
||||
private bool leaveOpen = false;
|
||||
private Encoding encodingType = Encoding.GetEncoding("shift-jis");
|
||||
private bool removeDuplicateStrings = true;
|
||||
|
||||
public uint Align
|
||||
{
|
||||
get
|
||||
{
|
||||
return align;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
value = 1;
|
||||
}
|
||||
|
||||
align = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool PutBlankString
|
||||
{
|
||||
get
|
||||
{
|
||||
return putBlankString;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
putBlankString = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool LeaveOpen
|
||||
{
|
||||
get
|
||||
{
|
||||
return leaveOpen;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
leaveOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Encoding EncodingType
|
||||
{
|
||||
get
|
||||
{
|
||||
return encodingType;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
encodingType = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool RemoveDuplicateStrings
|
||||
{
|
||||
get
|
||||
{
|
||||
return removeDuplicateStrings;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
removeDuplicateStrings = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static CriTableWriterSettings AdxSettings
|
||||
{
|
||||
get
|
||||
{
|
||||
return new CriTableWriterSettings()
|
||||
{
|
||||
Align = 4,
|
||||
PutBlankString = true,
|
||||
RemoveDuplicateStrings = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static CriTableWriterSettings Adx2Settings
|
||||
{
|
||||
get
|
||||
{
|
||||
return new CriTableWriterSettings()
|
||||
{
|
||||
Align = 32,
|
||||
PutBlankString = false,
|
||||
RemoveDuplicateStrings = false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SonicAudioLib.CriMw.Serialization
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
|
||||
public class CriFieldAttribute : Attribute
|
||||
{
|
||||
private string fieldName;
|
||||
private ushort order = ushort.MaxValue;
|
||||
|
||||
public string FieldName
|
||||
{
|
||||
get
|
||||
{
|
||||
return fieldName;
|
||||
}
|
||||
}
|
||||
|
||||
public ushort Order
|
||||
{
|
||||
get
|
||||
{
|
||||
return order;
|
||||
}
|
||||
}
|
||||
|
||||
public CriFieldAttribute(ushort order)
|
||||
{
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public CriFieldAttribute(string fieldName)
|
||||
{
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
public CriFieldAttribute(string fieldName, ushort order)
|
||||
{
|
||||
this.fieldName = fieldName;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public CriFieldAttribute() { }
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SonicAudioLib.CriMw.Serialization
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
|
||||
public class CriIgnoreAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SonicAudioLib.CriMw.Serialization
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)]
|
||||
public class CriSerializableAttribute : Attribute
|
||||
{
|
||||
private string tableName;
|
||||
|
||||
public string TableName
|
||||
{
|
||||
get
|
||||
{
|
||||
return tableName;
|
||||
}
|
||||
}
|
||||
|
||||
public CriSerializableAttribute(string tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
public CriSerializableAttribute() { }
|
||||
}
|
||||
}
|
201
Source/SonicAudioLib/CriMw/Serialization/CriTableSerializer.cs
Normal file
201
Source/SonicAudioLib/CriMw/Serialization/CriTableSerializer.cs
Normal file
@ -0,0 +1,201 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
|
||||
using SonicAudioLib.IO;
|
||||
|
||||
namespace SonicAudioLib.CriMw.Serialization
|
||||
{
|
||||
public static class CriTableSerializer
|
||||
{
|
||||
public static void Serialize(Stream destination, Type type, ICollection objects, CriTableWriterSettings settings)
|
||||
{
|
||||
ArrayList arrayList = null;
|
||||
|
||||
if (objects != null)
|
||||
{
|
||||
arrayList = new ArrayList(objects);
|
||||
}
|
||||
|
||||
CriTableWriter tableWriter = CriTableWriter.Create(destination, settings);
|
||||
|
||||
string tableName = type.Name;
|
||||
CriSerializableAttribute serAttribute = type.GetCustomAttribute<CriSerializableAttribute>();
|
||||
if (serAttribute != null && !string.IsNullOrEmpty(serAttribute.TableName))
|
||||
{
|
||||
tableName = serAttribute.TableName;
|
||||
}
|
||||
|
||||
tableWriter.WriteStartTable(tableName);
|
||||
|
||||
SortedList<int, PropertyInfo> sortedProperties = new SortedList<int, PropertyInfo>();
|
||||
|
||||
foreach (PropertyInfo propertyInfo in type.GetProperties())
|
||||
{
|
||||
// Add the properties in order
|
||||
CriIgnoreAttribute ignoreAttribute = propertyInfo.GetCustomAttribute<CriIgnoreAttribute>();
|
||||
if (ignoreAttribute != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Also ignore the properties that are not supportable (except FileInfo, Stream and ICollection<>)
|
||||
if (propertyInfo.PropertyType != typeof(FileInfo) &&
|
||||
propertyInfo.PropertyType != typeof(Stream) &&
|
||||
!CriField.FieldTypes.Contains(propertyInfo.PropertyType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CriFieldAttribute fieldAttribute = propertyInfo.GetCustomAttribute<CriFieldAttribute>();
|
||||
|
||||
int order = ushort.MaxValue;
|
||||
if (fieldAttribute != null)
|
||||
{
|
||||
order = fieldAttribute.Order;
|
||||
}
|
||||
|
||||
while (sortedProperties.ContainsKey(order))
|
||||
{
|
||||
order++;
|
||||
}
|
||||
|
||||
sortedProperties.Add(order, propertyInfo);
|
||||
}
|
||||
|
||||
tableWriter.WriteStartFieldCollection();
|
||||
foreach (var keyValuePair in sortedProperties)
|
||||
{
|
||||
PropertyInfo propertyInfo = keyValuePair.Value;
|
||||
CriFieldAttribute fieldAttribute = propertyInfo.GetCustomAttribute<CriFieldAttribute>();
|
||||
|
||||
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))
|
||||
{
|
||||
fieldType = typeof(byte[]);
|
||||
}
|
||||
|
||||
if (fieldAttribute != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(fieldAttribute.FieldName))
|
||||
{
|
||||
fieldName = fieldAttribute.FieldName;
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if all the rows have the same value for this field
|
||||
bool useDefaultValue = true;
|
||||
|
||||
if (arrayList != null && arrayList.Count > 0)
|
||||
{
|
||||
useDefaultValue = true;
|
||||
|
||||
foreach (object obj in arrayList)
|
||||
{
|
||||
if (propertyInfo.GetValue(obj) != propertyInfo.GetValue(arrayList[0]))
|
||||
{
|
||||
useDefaultValue = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (useDefaultValue)
|
||||
{
|
||||
defaultValue = propertyInfo.GetValue(arrayList[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (useDefaultValue)
|
||||
{
|
||||
tableWriter.WriteField(fieldName, fieldType, defaultValue);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
tableWriter.WriteField(fieldName, fieldType);
|
||||
}
|
||||
}
|
||||
|
||||
tableWriter.WriteEndFieldCollection();
|
||||
|
||||
// Time for objects.
|
||||
if (arrayList != null)
|
||||
{
|
||||
foreach (object obj in arrayList)
|
||||
{
|
||||
tableWriter.WriteStartRow();
|
||||
|
||||
int index = 0;
|
||||
foreach (PropertyInfo propertyInfo in sortedProperties.Values)
|
||||
{
|
||||
object value = propertyInfo.GetValue(obj);
|
||||
|
||||
Type propertyType = propertyInfo.PropertyType;
|
||||
|
||||
tableWriter.WriteValue(index, value);
|
||||
index++;
|
||||
}
|
||||
|
||||
tableWriter.WriteEndRow();
|
||||
}
|
||||
}
|
||||
|
||||
tableWriter.WriteEndTable();
|
||||
tableWriter.Dispose();
|
||||
}
|
||||
|
||||
public static ArrayList Deserialize(Stream source, Type type)
|
||||
{
|
||||
ArrayList arrayList = new ArrayList();
|
||||
|
||||
using (CriTableReader tableReader = CriTableReader.Create(source, true))
|
||||
{
|
||||
PropertyInfo[] propertyInfos = type.GetProperties();
|
||||
|
||||
while (tableReader.Read())
|
||||
{
|
||||
object obj = Activator.CreateInstance(type);
|
||||
|
||||
for (int i = 0; i < tableReader.NumberOfFields; i++)
|
||||
{
|
||||
string fieldName = tableReader.GetFieldName(i);
|
||||
|
||||
foreach (PropertyInfo propertyInfo in propertyInfos)
|
||||
{
|
||||
string fieldNameMatch = propertyInfo.Name;
|
||||
|
||||
CriFieldAttribute fieldAttribute = propertyInfo.GetCustomAttribute<CriFieldAttribute>();
|
||||
|
||||
if (fieldAttribute != null && !string.IsNullOrEmpty(fieldAttribute.FieldName))
|
||||
{
|
||||
fieldNameMatch = fieldAttribute.FieldName;
|
||||
}
|
||||
|
||||
if (fieldName == fieldNameMatch)
|
||||
{
|
||||
object value = tableReader.GetValue(i);
|
||||
propertyInfo.SetValue(obj, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arrayList.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
return arrayList;
|
||||
}
|
||||
}
|
||||
}
|
395
Source/SonicAudioLib/IO/EndianStream.cs
Normal file
395
Source/SonicAudioLib/IO/EndianStream.cs
Normal file
@ -0,0 +1,395 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace SonicAudioLib.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a static class for reading various data types in any endian format from a <see cref="Stream"/>.
|
||||
/// </summary>
|
||||
public static class EndianStream
|
||||
{
|
||||
private static byte[] buffer;
|
||||
|
||||
/// <summary>
|
||||
/// Fills <see cref="buffer"/> in the given length.
|
||||
/// </summary>
|
||||
private static void FillBuffer(Stream source, int length)
|
||||
{
|
||||
buffer = new byte[length];
|
||||
source.Read(buffer, 0, length);
|
||||
}
|
||||
|
||||
public static void CopyTo(Stream source, Stream destination)
|
||||
{
|
||||
CopyTo(source, destination, 4096);
|
||||
}
|
||||
|
||||
public static void CopyTo(Stream source, Stream destination, int bufferSize)
|
||||
{
|
||||
int read;
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
|
||||
while ((read = source.Read(buffer, 0, buffer.Length)) != 0)
|
||||
{
|
||||
destination.Write(buffer, 0, read);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] ReadBytes(Stream source, int length)
|
||||
{
|
||||
FillBuffer(source, length);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static void WriteBytes(Stream destination, byte[] value)
|
||||
{
|
||||
destination.Write(value, 0, value.Length);
|
||||
}
|
||||
|
||||
public static void WriteBytes(Stream destination, byte[] value, int length)
|
||||
{
|
||||
destination.Write(value, 0, length);
|
||||
}
|
||||
|
||||
public static byte ReadByte(Stream source)
|
||||
{
|
||||
int value = source.ReadByte();
|
||||
if (value == -1)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
return (byte)value;
|
||||
}
|
||||
|
||||
public static byte ReadByteAt(Stream source, long position)
|
||||
{
|
||||
long oldPosition = source.Position;
|
||||
source.Position = position;
|
||||
|
||||
byte value = ReadByte(source);
|
||||
source.Position = oldPosition;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static void WriteByte(Stream destination, byte value)
|
||||
{
|
||||
destination.WriteByte(value);
|
||||
}
|
||||
|
||||
public static void WriteByteAt(Stream destination, byte value, long position)
|
||||
{
|
||||
long oldPosition = destination.Position;
|
||||
destination.Position = position;
|
||||
|
||||
WriteByte(destination, value);
|
||||
destination.Position = oldPosition;
|
||||
}
|
||||
|
||||
public static bool ReadBoolean(Stream source)
|
||||
{
|
||||
return ReadByte(source) > 0;
|
||||
}
|
||||
|
||||
public static void WriteBoolean(Stream destination, bool value)
|
||||
{
|
||||
WriteByte(destination, (byte)(value == true ? 1 : 0));
|
||||
}
|
||||
|
||||
public static sbyte ReadSByte(Stream source)
|
||||
{
|
||||
return (sbyte)ReadByte(source);
|
||||
}
|
||||
|
||||
public static void WriteSByte(Stream source, sbyte value)
|
||||
{
|
||||
WriteByte(source, (byte)value);
|
||||
}
|
||||
|
||||
public static ushort ReadUInt16(Stream source)
|
||||
{
|
||||
FillBuffer(source, 2);
|
||||
return BitConverter.ToUInt16(buffer, 0);
|
||||
}
|
||||
|
||||
public static ushort ReadUInt16BE(Stream source)
|
||||
{
|
||||
FillBuffer(source, 2);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToUInt16(buffer, 0);
|
||||
}
|
||||
|
||||
public static void WriteUInt16(Stream destination, ushort value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
destination.Write(buffer, 0, 2);
|
||||
}
|
||||
|
||||
public static void WriteUInt16BE(Stream destination, ushort value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
destination.Write(buffer, 0, 2);
|
||||
}
|
||||
|
||||
public static short ReadInt16(Stream source)
|
||||
{
|
||||
FillBuffer(source, 2);
|
||||
return BitConverter.ToInt16(buffer, 0);
|
||||
}
|
||||
|
||||
public static short ReadInt16BE(Stream source)
|
||||
{
|
||||
FillBuffer(source, 2);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToInt16(buffer, 0);
|
||||
}
|
||||
|
||||
public static void WriteInt16(Stream destination, short value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
destination.Write(buffer, 0, 2);
|
||||
}
|
||||
|
||||
public static void WriteInt16BE(Stream destination, short value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
destination.Write(buffer, 0, 2);
|
||||
}
|
||||
|
||||
public static uint ReadUInt32(Stream source)
|
||||
{
|
||||
FillBuffer(source, 4);
|
||||
return BitConverter.ToUInt32(buffer, 0);
|
||||
}
|
||||
|
||||
public static uint ReadUInt32BE(Stream source)
|
||||
{
|
||||
FillBuffer(source, 4);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToUInt32(buffer, 0);
|
||||
}
|
||||
|
||||
public static void WriteUInt32(Stream destination, uint value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
destination.Write(buffer, 0, 4);
|
||||
}
|
||||
|
||||
public static void WriteUInt32BE(Stream destination, uint value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
destination.Write(buffer, 0, 4);
|
||||
}
|
||||
|
||||
public static int ReadInt32(Stream source)
|
||||
{
|
||||
FillBuffer(source, 4);
|
||||
return BitConverter.ToInt32(buffer, 0);
|
||||
}
|
||||
|
||||
public static int ReadInt32BE(Stream source)
|
||||
{
|
||||
FillBuffer(source, 4);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToInt32(buffer, 0);
|
||||
}
|
||||
|
||||
public static void WriteInt32(Stream destination, int value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
destination.Write(buffer, 0, 4);
|
||||
}
|
||||
|
||||
public static void WriteInt32BE(Stream destination, int value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
destination.Write(buffer, 0, 4);
|
||||
}
|
||||
|
||||
public static ulong ReadUInt64(Stream source)
|
||||
{
|
||||
FillBuffer(source, 8);
|
||||
return BitConverter.ToUInt64(buffer, 0);
|
||||
}
|
||||
|
||||
public static ulong ReadUInt64BE(Stream source)
|
||||
{
|
||||
FillBuffer(source, 8);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToUInt64(buffer, 0);
|
||||
}
|
||||
|
||||
public static void WriteUInt64(Stream destination, ulong value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
destination.Write(buffer, 0, 8);
|
||||
}
|
||||
|
||||
public static void WriteUInt64BE(Stream destination, ulong value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
destination.Write(buffer, 0, 8);
|
||||
}
|
||||
|
||||
public static long ReadInt64(Stream source)
|
||||
{
|
||||
FillBuffer(source, 8);
|
||||
return BitConverter.ToInt64(buffer, 0);
|
||||
}
|
||||
|
||||
public static long ReadInt64BE(Stream source)
|
||||
{
|
||||
FillBuffer(source, 8);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToInt64(buffer, 0);
|
||||
}
|
||||
|
||||
public static void WriteInt64(Stream destination, long value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
destination.Write(buffer, 0, 8);
|
||||
}
|
||||
|
||||
public static void WriteInt64BE(Stream destination, long value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
destination.Write(buffer, 0, 8);
|
||||
}
|
||||
|
||||
public static float ReadFloat(Stream source)
|
||||
{
|
||||
FillBuffer(source, 4);
|
||||
return BitConverter.ToSingle(buffer, 0);
|
||||
}
|
||||
|
||||
public static float ReadFloatBE(Stream source)
|
||||
{
|
||||
FillBuffer(source, 4);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToSingle(buffer, 0);
|
||||
}
|
||||
|
||||
public static void WriteFloat(Stream destination, float value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
destination.Write(buffer, 0, 4);
|
||||
}
|
||||
|
||||
public static void WriteFloatBE(Stream destination, float value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
destination.Write(buffer, 0, 4);
|
||||
}
|
||||
|
||||
public static double ReadDouble(Stream source)
|
||||
{
|
||||
FillBuffer(source, 8);
|
||||
return BitConverter.ToDouble(buffer, 0);
|
||||
}
|
||||
|
||||
public static double ReadDoubleBE(Stream source)
|
||||
{
|
||||
FillBuffer(source, 8);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToDouble(buffer, 0);
|
||||
}
|
||||
|
||||
public static void WriteDouble(Stream destination, double value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
destination.Write(buffer, 0, 8);
|
||||
}
|
||||
|
||||
public static void WriteDoubleBE(Stream destination, double value)
|
||||
{
|
||||
buffer = BitConverter.GetBytes(value);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
destination.Write(buffer, 0, 8);
|
||||
}
|
||||
|
||||
public static string ReadCString(Stream source)
|
||||
{
|
||||
return ReadCString(source, Encoding.ASCII);
|
||||
}
|
||||
|
||||
public static string ReadCString(Stream source, Encoding encoding)
|
||||
{
|
||||
var list = new List<byte>();
|
||||
|
||||
byte buff;
|
||||
while ((buff = ReadByte(source)) != 0)
|
||||
{
|
||||
list.Add(buff);
|
||||
}
|
||||
|
||||
return encoding.GetString(list.ToArray());
|
||||
}
|
||||
|
||||
public static void WriteCString(Stream destination, string value)
|
||||
{
|
||||
WriteCString(destination, value, Encoding.ASCII);
|
||||
}
|
||||
|
||||
public static void WriteCString(Stream destination, string value, Encoding encoding)
|
||||
{
|
||||
var buff = encoding.GetBytes(value);
|
||||
|
||||
foreach (byte _buff in buff)
|
||||
{
|
||||
WriteByte(destination, _buff);
|
||||
}
|
||||
|
||||
WriteByte(destination, 0);
|
||||
}
|
||||
|
||||
public static string ReadCString(Stream source, int length)
|
||||
{
|
||||
return ReadCString(source, length, Encoding.ASCII);
|
||||
}
|
||||
|
||||
public static string ReadCString(Stream source, int length, Encoding encoding)
|
||||
{
|
||||
byte[] buffer = new byte[length];
|
||||
source.Read(buffer, 0, length);
|
||||
|
||||
return encoding.GetString(buffer);
|
||||
}
|
||||
|
||||
public static void WriteCString(Stream destination, string value, int length)
|
||||
{
|
||||
WriteCString(destination, value, length, Encoding.ASCII);
|
||||
}
|
||||
|
||||
public static void WriteCString(Stream destination, string value, int length, Encoding encoding)
|
||||
{
|
||||
byte[] buffer = encoding.GetBytes(value.ToCharArray(), 0, length);
|
||||
destination.Write(buffer, 0, length);
|
||||
}
|
||||
}
|
||||
}
|
88
Source/SonicAudioLib/IO/StringPool.cs
Normal file
88
Source/SonicAudioLib/IO/StringPool.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace SonicAudioLib.IO
|
||||
{
|
||||
public class StringPool
|
||||
{
|
||||
private List<StringItem> items = new List<StringItem>();
|
||||
|
||||
private long startPosition = 0;
|
||||
private long length = 0;
|
||||
private Encoding encoding = Encoding.Default;
|
||||
|
||||
public static readonly string AdxBlankString = "<NULL>";
|
||||
|
||||
public long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return startPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
public long Put(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
long position = length;
|
||||
items.Add(new StringItem() { Value = value, Position = position });
|
||||
|
||||
length += value.Length + 1;
|
||||
return position;
|
||||
}
|
||||
|
||||
public void Write(Stream destination)
|
||||
{
|
||||
startPosition = (uint)destination.Position;
|
||||
|
||||
foreach (StringItem item in items)
|
||||
{
|
||||
EndianStream.WriteCString(destination, item.Value, encoding);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsString(string value)
|
||||
{
|
||||
return items.Any(item => item.Value == value);
|
||||
}
|
||||
|
||||
public long GetStringPosition(string value)
|
||||
{
|
||||
return items.First(item => item.Value == value).Position;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
items.Clear();
|
||||
}
|
||||
|
||||
public StringPool(Encoding encoding)
|
||||
{
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
public StringPool()
|
||||
{
|
||||
}
|
||||
|
||||
private class StringItem
|
||||
{
|
||||
public string Value { get; set; }
|
||||
public long Position { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
199
Source/SonicAudioLib/IO/Substream.cs
Normal file
199
Source/SonicAudioLib/IO/Substream.cs
Normal file
@ -0,0 +1,199 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SonicAudioLib.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="Stream"/> based substream for viewing a portion of a Stream.
|
||||
/// </summary>
|
||||
public class Substream : Stream
|
||||
{
|
||||
private Stream baseStream;
|
||||
private long streamPosition;
|
||||
private long streamLength;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the base Stream supports reading.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream.CanRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the base Stream supports seeking.
|
||||
/// </summary>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream.CanSeek;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always returns false.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the substream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return streamLength;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the substream.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream.Position - streamPosition;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
baseStream.Position = value + streamPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the base Stream.
|
||||
/// </summary>
|
||||
public long AbsolutePosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream.Position;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
baseStream.Position = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the substream.
|
||||
/// </summary>
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does nothing.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (baseStream.Position >= streamPosition + streamLength)
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
else if (baseStream.Position + count > streamPosition + streamLength)
|
||||
{
|
||||
count = (int)(streamPosition + streamLength - baseStream.Position);
|
||||
}
|
||||
|
||||
return baseStream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
if (origin == SeekOrigin.Begin)
|
||||
{
|
||||
offset += streamPosition;
|
||||
}
|
||||
|
||||
else if (origin == SeekOrigin.End)
|
||||
{
|
||||
offset = streamPosition + streamLength - offset;
|
||||
origin = SeekOrigin.Begin;
|
||||
}
|
||||
|
||||
return baseStream.Seek(offset, origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seeks to the start of the substream.
|
||||
/// </summary>
|
||||
public void SeekToStart()
|
||||
{
|
||||
baseStream.Position = streamPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws <see cref="NotSupportedException"/>.
|
||||
/// </summary>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws <see cref="NotSupportedException"/>.
|
||||
/// </summary>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws <see cref="NotSupportedException"/>.
|
||||
/// </summary>
|
||||
public override void WriteByte(byte value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an array of the data that the substream covers.
|
||||
/// </summary>
|
||||
public byte[] ToArray()
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
CopyTo(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a substream by the specified base Stream at the specified offset.
|
||||
/// </summary>
|
||||
public Substream(Stream baseStream, long streamPosition) : this(baseStream, streamPosition, baseStream.Length - streamPosition)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a substream by the specified base Stream at the specified offset and with the specified length.
|
||||
/// </summary>
|
||||
public Substream(Stream baseStream, long streamPosition, long streamLength)
|
||||
{
|
||||
this.baseStream = baseStream;
|
||||
this.streamPosition = streamPosition;
|
||||
this.streamLength = streamLength;
|
||||
|
||||
SeekToStart();
|
||||
}
|
||||
}
|
||||
}
|
172
Source/SonicAudioLib/IO/VldPool.cs
Normal file
172
Source/SonicAudioLib/IO/VldPool.cs
Normal file
@ -0,0 +1,172 @@
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
|
||||
using SonicAudioLib.Module;
|
||||
|
||||
namespace SonicAudioLib.IO
|
||||
{
|
||||
public class VldPool
|
||||
{
|
||||
private ArrayList items = new ArrayList();
|
||||
|
||||
private long startPosition = 0;
|
||||
private uint align = 1;
|
||||
private long length = 0;
|
||||
|
||||
public long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return startPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
public long Align
|
||||
{
|
||||
get
|
||||
{
|
||||
return align;
|
||||
}
|
||||
}
|
||||
|
||||
public long Put(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((length % align) != 0)
|
||||
{
|
||||
length++;
|
||||
}
|
||||
|
||||
long position = length;
|
||||
length += data.Length;
|
||||
items.Add(data);
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
public long Put(Stream stream)
|
||||
{
|
||||
if (stream == null || stream.Length <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((length % align) != 0)
|
||||
{
|
||||
length++;
|
||||
}
|
||||
|
||||
long position = length;
|
||||
length += stream.Length;
|
||||
items.Add(stream);
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
public long Put(FileInfo fileInfo)
|
||||
{
|
||||
if (fileInfo == null || fileInfo.Length <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((length % align) != 0)
|
||||
{
|
||||
length++;
|
||||
}
|
||||
|
||||
long position = length;
|
||||
length += fileInfo.Length;
|
||||
items.Add(fileInfo);
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
public long Put(ModuleBase module)
|
||||
{
|
||||
if (module == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((length % align) != 0)
|
||||
{
|
||||
length++;
|
||||
}
|
||||
|
||||
long position = length;
|
||||
length += module.CalculateLength();
|
||||
items.Add(module);
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
public void Write(Stream destination)
|
||||
{
|
||||
startPosition = destination.Position;
|
||||
|
||||
foreach (object item in items)
|
||||
{
|
||||
while ((destination.Position % align) != 0)
|
||||
{
|
||||
destination.WriteByte(0);
|
||||
}
|
||||
|
||||
if (item is byte[])
|
||||
{
|
||||
byte[] output = (byte[])item;
|
||||
destination.Write(output, 0, output.Length);
|
||||
}
|
||||
|
||||
else if (item is Stream)
|
||||
{
|
||||
Stream output = (Stream)item;
|
||||
output.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
output.CopyTo(destination);
|
||||
}
|
||||
|
||||
else if (item is FileInfo)
|
||||
{
|
||||
FileInfo fileInfo = (FileInfo)item;
|
||||
|
||||
Stream output = fileInfo.OpenRead();
|
||||
output.CopyTo(destination);
|
||||
output.Close();
|
||||
}
|
||||
|
||||
else if (item is ModuleBase)
|
||||
{
|
||||
ModuleBase module = (ModuleBase)item;
|
||||
module.Write(destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
items.Clear();
|
||||
}
|
||||
|
||||
public VldPool(uint align)
|
||||
{
|
||||
this.align = align;
|
||||
}
|
||||
|
||||
public VldPool()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
52
Source/SonicAudioLib/Module/ModuleBase.cs
Normal file
52
Source/SonicAudioLib/Module/ModuleBase.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace SonicAudioLib.Module
|
||||
{
|
||||
public abstract class ModuleBase
|
||||
{
|
||||
public abstract void Read(Stream source);
|
||||
public abstract void Write(Stream destination);
|
||||
|
||||
public virtual void Load(string sourceFileName)
|
||||
{
|
||||
using (Stream source = File.OpenRead(sourceFileName))
|
||||
{
|
||||
Read(source);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(byte[] sourceByteArray)
|
||||
{
|
||||
using (Stream source = new MemoryStream(sourceByteArray))
|
||||
{
|
||||
Read(source);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Save(string destinationFileName)
|
||||
{
|
||||
using (Stream destination = File.Create(destinationFileName))
|
||||
{
|
||||
Write(destination);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual byte[] Save()
|
||||
{
|
||||
using (MemoryStream destination = new MemoryStream())
|
||||
{
|
||||
Write(destination);
|
||||
return destination.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual long CalculateLength()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
36
Source/SonicAudioLib/Properties/AssemblyInfo.cs
Normal file
36
Source/SonicAudioLib/Properties/AssemblyInfo.cs
Normal 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("Sonic Audio Library")]
|
||||
[assembly: AssemblyDescription("Class library for various audio formats for Sonic the Hedgehog games.")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("SonicAudioLib")]
|
||||
[assembly: AssemblyCopyright("Copyright © blueskythlikesclouds 2014-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("63138773-1f47-474c-9345-15eb6183ecc6")]
|
||||
|
||||
// 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")]
|
76
Source/SonicAudioLib/SonicAudioLib.csproj
Normal file
76
Source/SonicAudioLib/SonicAudioLib.csproj
Normal file
@ -0,0 +1,76 @@
|
||||
<?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>{63138773-1F47-474C-9345-15EB6183ECC6}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SonicAudioLib</RootNamespace>
|
||||
<AssemblyName>SonicAudioLib</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<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' ">
|
||||
<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.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="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" />
|
||||
<Compile Include="CriMw\CriRow.cs" />
|
||||
<Compile Include="CriMw\CriRowCollection.cs" />
|
||||
<Compile Include="CriMw\Serialization\CriFieldAttribute.cs" />
|
||||
<Compile Include="CriMw\Serialization\CriIgnoreAttribute.cs" />
|
||||
<Compile Include="CriMw\Serialization\CriSerializableAttribute.cs" />
|
||||
<Compile Include="CriMw\CriTable.Internal.cs" />
|
||||
<Compile Include="CriMw\CriTableReader.cs" />
|
||||
<Compile Include="CriMw\Serialization\CriTableSerializer.cs" />
|
||||
<Compile Include="CriMw\CriTableWriter.cs" />
|
||||
<Compile Include="IO\EndianStream.cs" />
|
||||
<Compile Include="IO\StringPool.cs" />
|
||||
<Compile Include="IO\Substream.cs" />
|
||||
<Compile Include="IO\VldPool.cs" />
|
||||
<Compile Include="Module\ModuleBase.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<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>
|
Loading…
Reference in New Issue
Block a user