Rikux3
4 years ago
commit
174bf2f357
16 changed files with 1496 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||
# Auto detect text files and perform LF normalization |
|||
* text=auto |
@ -0,0 +1,353 @@ |
|||
## Ignore Visual Studio temporary files, build results, and |
|||
## files generated by popular Visual Studio add-ons. |
|||
## |
|||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore |
|||
|
|||
# User-specific files |
|||
*.rsuser |
|||
*.suo |
|||
*.user |
|||
*.userosscache |
|||
*.sln.docstates |
|||
|
|||
# User-specific files (MonoDevelop/Xamarin Studio) |
|||
*.userprefs |
|||
|
|||
# Mono auto generated files |
|||
mono_crash.* |
|||
|
|||
# Build results |
|||
[Dd]ebug/ |
|||
[Dd]ebugPublic/ |
|||
[Rr]elease/ |
|||
[Rr]eleases/ |
|||
x64/ |
|||
x86/ |
|||
[Aa][Rr][Mm]/ |
|||
[Aa][Rr][Mm]64/ |
|||
bld/ |
|||
[Bb]in/ |
|||
[Oo]bj/ |
|||
[Ll]og/ |
|||
[Ll]ogs/ |
|||
|
|||
# Visual Studio 2015/2017 cache/options directory |
|||
.vs/ |
|||
# Uncomment if you have tasks that create the project's static files in wwwroot |
|||
#wwwroot/ |
|||
|
|||
# Visual Studio 2017 auto generated files |
|||
Generated\ Files/ |
|||
|
|||
# MSTest test Results |
|||
[Tt]est[Rr]esult*/ |
|||
[Bb]uild[Ll]og.* |
|||
|
|||
# NUnit |
|||
*.VisualState.xml |
|||
TestResult.xml |
|||
nunit-*.xml |
|||
|
|||
# Build Results of an ATL Project |
|||
[Dd]ebugPS/ |
|||
[Rr]eleasePS/ |
|||
dlldata.c |
|||
|
|||
# Benchmark Results |
|||
BenchmarkDotNet.Artifacts/ |
|||
|
|||
# .NET Core |
|||
project.lock.json |
|||
project.fragment.lock.json |
|||
artifacts/ |
|||
|
|||
# StyleCop |
|||
StyleCopReport.xml |
|||
|
|||
# Files built by Visual Studio |
|||
*_i.c |
|||
*_p.c |
|||
*_h.h |
|||
*.ilk |
|||
*.meta |
|||
*.obj |
|||
*.iobj |
|||
*.pch |
|||
*.pdb |
|||
*.ipdb |
|||
*.pgc |
|||
*.pgd |
|||
*.rsp |
|||
*.sbr |
|||
*.tlb |
|||
*.tli |
|||
*.tlh |
|||
*.tmp |
|||
*.tmp_proj |
|||
*_wpftmp.csproj |
|||
*.log |
|||
*.vspscc |
|||
*.vssscc |
|||
.builds |
|||
*.pidb |
|||
*.svclog |
|||
*.scc |
|||
|
|||
# Chutzpah Test files |
|||
_Chutzpah* |
|||
|
|||
# Visual C++ cache files |
|||
ipch/ |
|||
*.aps |
|||
*.ncb |
|||
*.opendb |
|||
*.opensdf |
|||
*.sdf |
|||
*.cachefile |
|||
*.VC.db |
|||
*.VC.VC.opendb |
|||
|
|||
# Visual Studio profiler |
|||
*.psess |
|||
*.vsp |
|||
*.vspx |
|||
*.sap |
|||
|
|||
# Visual Studio Trace Files |
|||
*.e2e |
|||
|
|||
# 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 |
|||
|
|||
# AxoCover is a Code Coverage Tool |
|||
.axoCover/* |
|||
!.axoCover/settings.json |
|||
|
|||
# Visual Studio code coverage results |
|||
*.coverage |
|||
*.coveragexml |
|||
|
|||
# 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 |
|||
# Note: Comment the next line if you want to checkin your web deploy settings, |
|||
# but database connection strings (with potential passwords) will be unencrypted |
|||
*.pubxml |
|||
*.publishproj |
|||
|
|||
# Microsoft Azure Web App publish settings. Comment the next line if you want to |
|||
# checkin your Azure Web App publish settings, but sensitive information contained |
|||
# in these scripts will be unencrypted |
|||
PublishScripts/ |
|||
|
|||
# NuGet Packages |
|||
*.nupkg |
|||
# NuGet Symbol Packages |
|||
*.snupkg |
|||
# The packages folder can be ignored because of Package Restore |
|||
**/[Pp]ackages/* |
|||
# except build/, which is used as an MSBuild target. |
|||
!**/[Pp]ackages/build/ |
|||
# Uncomment if necessary however generally it will be regenerated when needed |
|||
#!**/[Pp]ackages/repositories.config |
|||
# NuGet v3's project.json files produces more ignorable files |
|||
*.nuget.props |
|||
*.nuget.targets |
|||
|
|||
# Microsoft Azure Build Output |
|||
csx/ |
|||
*.build.csdef |
|||
|
|||
# Microsoft Azure Emulator |
|||
ecf/ |
|||
rcf/ |
|||
|
|||
# Windows Store app package directories and files |
|||
AppPackages/ |
|||
BundleArtifacts/ |
|||
Package.StoreAssociation.xml |
|||
_pkginfo.txt |
|||
*.appx |
|||
*.appxbundle |
|||
*.appxupload |
|||
|
|||
# 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/ |
|||
~$* |
|||
*~ |
|||
*.dbmdl |
|||
*.dbproj.schemaview |
|||
*.jfm |
|||
*.pfx |
|||
*.publishsettings |
|||
orleans.codegen.cs |
|||
|
|||
# Including strong name files can present a security risk |
|||
# (https://github.com/github/gitignore/pull/2483#issue-259490424) |
|||
#*.snk |
|||
|
|||
# Since there are multiple workflows, uncomment next line to ignore bower_components |
|||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) |
|||
#bower_components/ |
|||
|
|||
# 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 |
|||
ServiceFabricBackup/ |
|||
*.rptproj.bak |
|||
|
|||
# SQL Server files |
|||
*.mdf |
|||
*.ldf |
|||
*.ndf |
|||
|
|||
# Business Intelligence projects |
|||
*.rdl.data |
|||
*.bim.layout |
|||
*.bim_*.settings |
|||
*.rptproj.rsuser |
|||
*- [Bb]ackup.rdl |
|||
*- [Bb]ackup ([0-9]).rdl |
|||
*- [Bb]ackup ([0-9][0-9]).rdl |
|||
|
|||
# Microsoft Fakes |
|||
FakesAssemblies/ |
|||
|
|||
# GhostDoc plugin setting file |
|||
*.GhostDoc.xml |
|||
|
|||
# Node.js Tools for Visual Studio |
|||
.ntvs_analysis.dat |
|||
node_modules/ |
|||
|
|||
# Visual Studio 6 build log |
|||
*.plg |
|||
|
|||
# Visual Studio 6 workspace options file |
|||
*.opt |
|||
|
|||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) |
|||
*.vbw |
|||
|
|||
# Visual Studio LightSwitch build output |
|||
**/*.HTMLClient/GeneratedArtifacts |
|||
**/*.DesktopClient/GeneratedArtifacts |
|||
**/*.DesktopClient/ModelManifest.xml |
|||
**/*.Server/GeneratedArtifacts |
|||
**/*.Server/ModelManifest.xml |
|||
_Pvt_Extensions |
|||
|
|||
# Paket dependency manager |
|||
.paket/paket.exe |
|||
paket-files/ |
|||
|
|||
# FAKE - F# Make |
|||
.fake/ |
|||
|
|||
# CodeRush personal settings |
|||
.cr/personal |
|||
|
|||
# Python Tools for Visual Studio (PTVS) |
|||
__pycache__/ |
|||
*.pyc |
|||
|
|||
# Cake - Uncomment if you are using it |
|||
# tools/** |
|||
# !tools/packages.config |
|||
|
|||
# Tabs Studio |
|||
*.tss |
|||
|
|||
# Telerik's JustMock configuration file |
|||
*.jmconfig |
|||
|
|||
# BizTalk build output |
|||
*.btp.cs |
|||
*.btm.cs |
|||
*.odx.cs |
|||
*.xsd.cs |
|||
|
|||
# OpenCover UI analysis results |
|||
OpenCover/ |
|||
|
|||
# Azure Stream Analytics local run output |
|||
ASALocalRun/ |
|||
|
|||
# MSBuild Binary and Structured Log |
|||
*.binlog |
|||
|
|||
# NVidia Nsight GPU debugger configuration file |
|||
*.nvuser |
|||
|
|||
# MFractors (Xamarin productivity tool) working folder |
|||
.mfractor/ |
|||
|
|||
# Local History for Visual Studio |
|||
.localhistory/ |
|||
|
|||
# BeatPulse healthcheck temp database |
|||
healthchecksdb |
|||
|
|||
# Backup folder for Package Reference Convert tool in Visual Studio 2017 |
|||
MigrationBackup/ |
|||
|
|||
# Ionide (cross platform F# VS Code tools) working folder |
|||
.ionide/ |
@ -0,0 +1,21 @@ |
|||
MIT License |
|||
|
|||
Copyright (c) 2020 Rikux3 |
|||
|
|||
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. |
@ -0,0 +1,25 @@ |
|||
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00 |
|||
# Visual Studio Version 16 |
|||
VisualStudioVersion = 16.0.30225.117 |
|||
MinimumVisualStudioVersion = 10.0.40219.1 |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UsmToolkit", "UsmToolkit\UsmToolkit.csproj", "{1C7F9474-2096-4039-9F6F-9B9940717D06}" |
|||
EndProject |
|||
Global |
|||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
|||
Debug|Any CPU = Debug|Any CPU |
|||
Release|Any CPU = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
|||
{1C7F9474-2096-4039-9F6F-9B9940717D06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{1C7F9474-2096-4039-9F6F-9B9940717D06}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{1C7F9474-2096-4039-9F6F-9B9940717D06}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{1C7F9474-2096-4039-9F6F-9B9940717D06}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(SolutionProperties) = preSolution |
|||
HideSolutionNode = FALSE |
|||
EndGlobalSection |
|||
GlobalSection(ExtensibilityGlobals) = postSolution |
|||
SolutionGuid = {C8EB6C12-4B73-41DD-9F88-BDB76671892C} |
|||
EndGlobalSection |
|||
EndGlobal |
@ -0,0 +1,24 @@ |
|||
using System.Diagnostics; |
|||
|
|||
namespace UsmToolkit |
|||
{ |
|||
public static class Helpers |
|||
{ |
|||
public static void ExecuteProcess(string fileName, string arguments) |
|||
{ |
|||
ProcessStartInfo startInfo = new ProcessStartInfo() |
|||
{ |
|||
FileName = fileName, |
|||
Arguments = arguments, |
|||
RedirectStandardOutput = true, |
|||
UseShellExecute = false, |
|||
}; |
|||
|
|||
Process process = new Process(); |
|||
process.StartInfo = startInfo; |
|||
|
|||
process.Start(); |
|||
process.WaitForExit(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,197 @@ |
|||
using McMaster.Extensions.CommandLineUtils; |
|||
using Newtonsoft.Json; |
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using System.IO; |
|||
using System.IO.Compression; |
|||
using System.Linq; |
|||
using System.Net; |
|||
using System.Reflection; |
|||
using System.Text; |
|||
using VGMToolbox.format; |
|||
|
|||
namespace UsmToolkit |
|||
{ |
|||
[Command("UsmToolkit")] |
|||
[VersionOptionFromMember("--version", MemberName = nameof(GetVersion))] |
|||
[Subcommand(typeof(ExtractCommand), typeof(GetDependenciesCommand))] |
|||
class Program |
|||
{ |
|||
|
|||
static int Main(string[] args) |
|||
{ |
|||
try |
|||
{ |
|||
return CommandLineApplication.Execute<Program>(args); |
|||
} |
|||
catch (FileNotFoundException e) |
|||
{ |
|||
Console.WriteLine($"The file {e.FileName} cannot be found. The program will now exit."); |
|||
return 2; |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Console.WriteLine($"FATAL ERROR: {e.Message}\n{e.StackTrace}"); |
|||
return -1; |
|||
} |
|||
} |
|||
|
|||
protected int OnExecute(CommandLineApplication app) |
|||
{ |
|||
app.ShowHelp(); |
|||
return 1; |
|||
} |
|||
|
|||
private static string GetVersion() |
|||
=> typeof(Program).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion; |
|||
|
|||
private class ExtractCommand |
|||
{ |
|||
private class JoinConfig |
|||
{ |
|||
public string VideoParameter { get; set; } |
|||
public string AudioParameter { get; set; } |
|||
public string OutputFormat { get; set; } |
|||
} |
|||
|
|||
[Required] |
|||
[FileOrDirectoryExists] |
|||
[Argument(0, Description = "File or folder containing usm files")] |
|||
public string InputPath { get; set; } |
|||
|
|||
[Option(CommandOptionType.NoValue, Description = "Join files after extraction.", ShortName = "j", LongName = "join")] |
|||
public bool JoinOutput { get; set; } |
|||
|
|||
[Option(CommandOptionType.NoValue, Description = "Remove temporary m2v and audio after joining.", ShortName = "c", LongName = "clean")] |
|||
public bool CleanTempFiles { get; set; } |
|||
|
|||
protected int OnExecute(CommandLineApplication app) |
|||
{ |
|||
FileAttributes attr = File.GetAttributes(InputPath); |
|||
if (attr.HasFlag(FileAttributes.Directory)) |
|||
{ |
|||
foreach (var file in Directory.GetFiles(InputPath, "*.usm")) |
|||
{ |
|||
Convert(file); |
|||
} |
|||
} |
|||
else |
|||
Convert(InputPath); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
private void Convert(string fileName) |
|||
{ |
|||
Console.WriteLine($"File: {fileName}"); |
|||
var usmStream = new CriUsmStream(fileName); |
|||
|
|||
usmStream.DemultiplexStreams(new MpegStream.DemuxOptionsStruct() |
|||
{ |
|||
AddHeader = false, |
|||
AddPlaybackHacks = false, |
|||
ExtractAudio = true, |
|||
ExtractVideo = true, |
|||
SplitAudioStreams = false |
|||
}); |
|||
|
|||
if (JoinOutput) |
|||
{ |
|||
JoinOutputFile(usmStream); |
|||
} |
|||
} |
|||
|
|||
private void JoinOutputFile(CriUsmStream usmStream) |
|||
{ |
|||
var audioFormat = usmStream.FinalAudioExtension; |
|||
var pureFileName = Path.GetFileNameWithoutExtension(usmStream.FilePath); |
|||
|
|||
if (audioFormat == ".adx") |
|||
{ |
|||
//ffmpeg can not handle .adx from 0.2 for whatever reason
|
|||
//need vgmstream to format that to wav
|
|||
if (!Directory.Exists("vgmstream")) |
|||
{ |
|||
Console.WriteLine("WARNING: vgmstream folder not found!"); |
|||
} |
|||
|
|||
Helpers.ExecuteProcess("vgmstream/test.exe", $"\"{Path.ChangeExtension(usmStream.FilePath, usmStream.FinalAudioExtension)}\" -o \"{Path.ChangeExtension(usmStream.FilePath, "wav")}\""); |
|||
|
|||
usmStream.FinalAudioExtension = ".wav"; |
|||
} |
|||
|
|||
if (!File.Exists("config.json")) |
|||
{ |
|||
Console.WriteLine("ERROR: config.json not found!"); |
|||
return; |
|||
} |
|||
|
|||
Helpers.ExecuteProcess("ffmpeg", CreateFFmpegParameters(usmStream, pureFileName)); |
|||
|
|||
if (CleanTempFiles) |
|||
{ |
|||
File.Delete(Path.ChangeExtension(usmStream.FilePath, "wav")); |
|||
File.Delete(Path.ChangeExtension(usmStream.FilePath, "adx")); |
|||
File.Delete(Path.ChangeExtension(usmStream.FilePath, "hca")); |
|||
File.Delete(Path.ChangeExtension(usmStream.FilePath, "m2v")); |
|||
} |
|||
} |
|||
|
|||
private string CreateFFmpegParameters(CriUsmStream usmStream, string pureFileName) |
|||
{ |
|||
JoinConfig conf = JsonConvert.DeserializeObject<JoinConfig>(File.ReadAllText("config.json")); |
|||
|
|||
StringBuilder sb = new StringBuilder(); |
|||
sb.Append($"-i \"{Path.ChangeExtension(usmStream.FilePath, usmStream.FileExtensionVideo)}\" "); |
|||
|
|||
if (usmStream.HasAudio) |
|||
sb.Append($"-i \"{Path.ChangeExtension(usmStream.FilePath, usmStream.FinalAudioExtension)}\" "); |
|||
|
|||
sb.Append($"{conf.VideoParameter} "); |
|||
|
|||
if (usmStream.HasAudio) |
|||
sb.Append($"{conf.AudioParameter} "); |
|||
|
|||
sb.Append($"{pureFileName}.{conf.OutputFormat}"); |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
} |
|||
|
|||
private class GetDependenciesCommand |
|||
{ |
|||
private class DepsConfig |
|||
{ |
|||
public string Vgmstream { get; set; } |
|||
public string FFmpeg { get; set; } |
|||
} |
|||
|
|||
protected int OnExecute(CommandLineApplication app) |
|||
{ |
|||
DepsConfig conf = JsonConvert.DeserializeObject<DepsConfig>(File.ReadAllText("deps.json")); |
|||
WebClient client = new WebClient(); |
|||
|
|||
//ffmpeg
|
|||
client.DownloadFile(conf.FFmpeg, "ffmpeg.zip"); |
|||
|
|||
using (ZipArchive archive = ZipFile.OpenRead("ffmpeg.zip")) |
|||
{ |
|||
var ent = archive.Entries.FirstOrDefault(x => x.Name == "ffmpeg.exe"); |
|||
if (ent != null) |
|||
{ |
|||
ent.ExtractToFile("ffmpeg.exe", true); |
|||
} |
|||
} |
|||
|
|||
File.Delete("ffmpeg.zip"); |
|||
|
|||
//vgmstream
|
|||
client.DownloadFile(conf.Vgmstream, "vgmstream.zip"); |
|||
ZipFile.ExtractToDirectory("vgmstream.zip", "vgmstream"); |
|||
File.Delete("vgmstream.zip"); |
|||
|
|||
return 0; |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,8 @@ |
|||
{ |
|||
"profiles": { |
|||
"UsmToolkit": { |
|||
"commandName": "Project", |
|||
"commandLineArgs": "extract \"F:\\CUSA05787\\tresgame\\content\\crimovie\\rebellis02\\en\\cs_dw009_mv.usm\" --join" |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,28 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<OutputType>Exe</OutputType> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="3.0.0" /> |
|||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Reference Include="vgmtutil"> |
|||
<HintPath>deps\vgmtutil.dll</HintPath> |
|||
</Reference> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Update="config.json"> |
|||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> |
|||
</None> |
|||
<None Update="deps.json"> |
|||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> |
|||
</None> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
@ -0,0 +1,226 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
|
|||
using VGMToolbox.util; |
|||
|
|||
namespace VGMToolbox.format |
|||
{ |
|||
public class CriUsmStream : MpegStream |
|||
{ |
|||
public const string DefaultAudioExtension = ".adx"; |
|||
public const string DefaultVideoExtension = ".m2v"; |
|||
public const string HcaAudioExtension = ".hca"; |
|||
|
|||
static readonly byte[] HCA_SIG_BYTES = new byte[] { 0x48, 0x43, 0x41, 0x00 }; |
|||
|
|||
protected static readonly byte[] ALP_BYTES = new byte[] { 0x40, 0x41, 0x4C, 0x50 }; |
|||
protected static readonly byte[] CRID_BYTES = new byte[] { 0x43, 0x52, 0x49, 0x44 }; |
|||
protected static readonly byte[] SFV_BYTES = new byte[] { 0x40, 0x53, 0x46, 0x56 }; |
|||
protected static readonly byte[] SFA_BYTES = new byte[] { 0x40, 0x53, 0x46, 0x41 }; |
|||
protected static readonly byte[] SBT_BYTES = new byte[] { 0x40, 0x53, 0x42, 0x54 }; |
|||
protected static readonly byte[] CUE_BYTES = new byte[] { 0x40, 0x43, 0x55, 0x45 }; |
|||
|
|||
protected static readonly byte[] UTF_BYTES = new byte[] { 0x40, 0x55, 0x54, 0x46 }; |
|||
|
|||
protected static readonly byte[] HEADER_END_BYTES = |
|||
new byte[] { 0x23, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x20, |
|||
0x45, 0x4E, 0x44, 0x20, 0x20, 0x20, 0x20, 0x20, |
|||
0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, |
|||
0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x00 }; |
|||
|
|||
protected static readonly byte[] METADATA_END_BYTES = |
|||
new byte[] { 0x23, 0x4D, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, |
|||
0x41, 0x20, 0x45, 0x4E, 0x44, 0x20, 0x20, 0x20, |
|||
0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, |
|||
0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x00 }; |
|||
|
|||
protected static readonly byte[] CONTENTS_END_BYTES = |
|||
new byte[] { 0x23, 0x43, 0x4F, 0x4E, 0x54, 0x45, 0x4E, 0x54, |
|||
0x53, 0x20, 0x45, 0x4E, 0x44, 0x20, 0x20, 0x20, |
|||
0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, |
|||
0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x00 }; |
|||
|
|||
public CriUsmStream(string path) |
|||
: base(path) |
|||
{ |
|||
this.UsesSameIdForMultipleAudioTracks = true; |
|||
this.FileExtensionAudio = DefaultAudioExtension; |
|||
this.FileExtensionVideo = DefaultVideoExtension; |
|||
|
|||
base.BlockIdDictionary.Clear(); |
|||
base.BlockIdDictionary[BitConverter.ToUInt32(ALP_BYTES, 0)] = new BlockSizeStruct(PacketSizeType.SizeBytes, 4); // @ALP
|
|||
base.BlockIdDictionary[BitConverter.ToUInt32(CRID_BYTES, 0)] = new BlockSizeStruct(PacketSizeType.SizeBytes, 4); // CRID
|
|||
base.BlockIdDictionary[BitConverter.ToUInt32(SFV_BYTES, 0)] = new BlockSizeStruct(PacketSizeType.SizeBytes, 4); // @SFV
|
|||
base.BlockIdDictionary[BitConverter.ToUInt32(SFA_BYTES, 0)] = new BlockSizeStruct(PacketSizeType.SizeBytes, 4); // @SFA
|
|||
base.BlockIdDictionary[BitConverter.ToUInt32(SBT_BYTES, 0)] = new BlockSizeStruct(PacketSizeType.SizeBytes, 4); // @SBT
|
|||
base.BlockIdDictionary[BitConverter.ToUInt32(CUE_BYTES, 0)] = new BlockSizeStruct(PacketSizeType.SizeBytes, 4); // @CUE
|
|||
} |
|||
|
|||
protected override byte[] GetPacketStartBytes() { return CRID_BYTES; } |
|||
|
|||
protected override int GetAudioPacketHeaderSize(Stream readStream, long currentOffset) |
|||
{ |
|||
UInt16 checkBytes; |
|||
OffsetDescription od = new OffsetDescription(); |
|||
|
|||
od.OffsetByteOrder = Constants.BigEndianByteOrder; |
|||
od.OffsetSize = "2"; |
|||
od.OffsetValue = "8"; |
|||
|
|||
checkBytes = (UInt16)ParseFile.GetVaryingByteValueAtRelativeOffset(readStream, od, currentOffset); |
|||
|
|||
return checkBytes; |
|||
} |
|||
protected override int GetVideoPacketHeaderSize(Stream readStream, long currentOffset) |
|||
{ |
|||
UInt16 checkBytes; |
|||
OffsetDescription od = new OffsetDescription(); |
|||
|
|||
od.OffsetByteOrder = Constants.BigEndianByteOrder; |
|||
od.OffsetSize = "2"; |
|||
od.OffsetValue = "8"; |
|||
|
|||
checkBytes = (UInt16)ParseFile.GetVaryingByteValueAtRelativeOffset(readStream, od, currentOffset); |
|||
|
|||
return checkBytes; |
|||
} |
|||
|
|||
protected override bool IsThisAnAudioBlock(byte[] blockToCheck) |
|||
{ |
|||
return ParseFile.CompareSegment(blockToCheck, 0, SFA_BYTES); |
|||
} |
|||
protected override bool IsThisAVideoBlock(byte[] blockToCheck) |
|||
{ |
|||
return ParseFile.CompareSegment(blockToCheck, 0, SFV_BYTES); |
|||
} |
|||
|
|||
protected override byte GetStreamId(Stream readStream, long currentOffset) |
|||
{ |
|||
byte streamId; |
|||
|
|||
streamId = ParseFile.ParseSimpleOffset(readStream, currentOffset + 0xC, 1)[0]; |
|||
|
|||
return streamId; |
|||
} |
|||
|
|||
protected override int GetAudioPacketFooterSize(Stream readStream, long currentOffset) |
|||
{ |
|||
UInt16 checkBytes; |
|||
OffsetDescription od = new OffsetDescription(); |
|||
|
|||
od.OffsetByteOrder = Constants.BigEndianByteOrder; |
|||
od.OffsetSize = "2"; |
|||
od.OffsetValue = "0xA"; |
|||
|
|||
checkBytes = (UInt16)ParseFile.GetVaryingByteValueAtRelativeOffset(readStream, od, currentOffset); |
|||
|
|||
return checkBytes; |
|||
} |
|||
|
|||
protected override int GetVideoPacketFooterSize(Stream readStream, long currentOffset) |
|||
{ |
|||
UInt16 checkBytes; |
|||
OffsetDescription od = new OffsetDescription(); |
|||
|
|||
od.OffsetByteOrder = Constants.BigEndianByteOrder; |
|||
od.OffsetSize = "2"; |
|||
od.OffsetValue = "0xA"; |
|||
|
|||
checkBytes = (UInt16)ParseFile.GetVaryingByteValueAtRelativeOffset(readStream, od, currentOffset); |
|||
|
|||
return checkBytes; |
|||
} |
|||
|
|||
protected override void DoFinalTasks(FileStream sourceFileStream, Dictionary<uint, FileStream> outputFiles, bool addHeader) |
|||
{ |
|||
long headerEndOffset; |
|||
long metadataEndOffset; |
|||
long headerSize; |
|||
|
|||
long footerOffset; |
|||
long footerSize; |
|||
|
|||
string sourceFileName; |
|||
string workingFile; |
|||
string fileExtension; |
|||
string destinationFileName; |
|||
|
|||
foreach (uint streamId in outputFiles.Keys) |
|||
{ |
|||
sourceFileName = outputFiles[streamId].Name; |
|||
|
|||
//--------------------------
|
|||
// get header size
|
|||
//--------------------------
|
|||
headerEndOffset = ParseFile.GetNextOffset(outputFiles[streamId], 0, HEADER_END_BYTES); |
|||
metadataEndOffset = ParseFile.GetNextOffset(outputFiles[streamId], 0, METADATA_END_BYTES); |
|||
|
|||
if (metadataEndOffset > headerEndOffset) |
|||
{ |
|||
headerSize = metadataEndOffset + METADATA_END_BYTES.Length; |
|||
} |
|||
else |
|||
{ |
|||
headerSize = headerEndOffset + METADATA_END_BYTES.Length; |
|||
} |
|||
|
|||
//-----------------
|
|||
// get footer size
|
|||
//-----------------
|
|||
footerOffset = ParseFile.GetNextOffset(outputFiles[streamId], 0, CONTENTS_END_BYTES) - headerSize; |
|||
footerSize = outputFiles[streamId].Length - footerOffset; |
|||
|
|||
//------------------------------------------
|
|||
// check data to adjust extension if needed
|
|||
//------------------------------------------
|
|||
if (this.IsThisAnAudioBlock(BitConverter.GetBytes(streamId & 0xFFFFFFF0))) // may need to change mask if more than 0xF streams
|
|||
{ |
|||
byte[] checkBytes = ParseFile.ParseSimpleOffset(outputFiles[streamId], headerSize, 4); |
|||
|
|||
if (ParseFile.CompareSegment(checkBytes, 0, SofdecStream.AixSignatureBytes)) |
|||
{ |
|||
fileExtension = SofdecStream.AixAudioExtension; |
|||
} |
|||
else if (checkBytes[0] == 0x80) |
|||
{ |
|||
fileExtension = SofdecStream.AdxAudioExtension; |
|||
} |
|||
else if (ParseFile.CompareSegment(checkBytes, 0, HCA_SIG_BYTES)) |
|||
{ |
|||
fileExtension = HcaAudioExtension; |
|||
} |
|||
else |
|||
{ |
|||
fileExtension = ".bin"; |
|||
} |
|||
|
|||
this.FinalAudioExtension = fileExtension; |
|||
this.HasAudio = true; |
|||
} |
|||
else |
|||
{ |
|||
fileExtension = Path.GetExtension(sourceFileName); |
|||
} |
|||
|
|||
outputFiles[streamId].Close(); |
|||
outputFiles[streamId].Dispose(); |
|||
|
|||
workingFile = FileUtil.RemoveChunkFromFile(sourceFileName, 0, headerSize); |
|||
File.Copy(workingFile, sourceFileName, true); |
|||
File.Delete(workingFile); |
|||
|
|||
workingFile = FileUtil.RemoveChunkFromFile(sourceFileName, footerOffset, footerSize); |
|||
destinationFileName = Path.ChangeExtension(sourceFileName, fileExtension); |
|||
destinationFileName = destinationFileName.Substring(0, destinationFileName.LastIndexOf("_"))+fileExtension; |
|||
File.Copy(workingFile, destinationFileName, true); |
|||
File.Delete(workingFile); |
|||
|
|||
if ((sourceFileName != destinationFileName) && (File.Exists(sourceFileName))) |
|||
{ |
|||
File.Delete(sourceFileName); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,39 @@ |
|||
using System; |
|||
using System.IO; |
|||
|
|||
namespace VGMToolbox.format |
|||
{ |
|||
public class Mpeg1Stream : MpegStream |
|||
{ |
|||
public const string DefaultAudioExtension = ".mp2"; |
|||
public const string DefaultVideoExtension = ".m1v"; |
|||
|
|||
public Mpeg1Stream(string path) |
|||
: base(path) |
|||
{ |
|||
this.FileExtensionAudio = DefaultAudioExtension; |
|||
this.FileExtensionVideo = DefaultVideoExtension; |
|||
|
|||
base.BlockIdDictionary[BitConverter.ToUInt32(MpegStream.PacketStartBytes, 0)] = new BlockSizeStruct(PacketSizeType.Static, 0xC); // Pack Header
|
|||
} |
|||
|
|||
protected override int GetAudioPacketHeaderSize(Stream readStream, long currentOffset) |
|||
{ |
|||
int paddingByteCount = 0; |
|||
readStream.Position = currentOffset + 6; |
|||
|
|||
// skip stuffing bytes
|
|||
while (readStream.ReadByte() == 0xFF) |
|||
{ |
|||
paddingByteCount++; |
|||
} |
|||
|
|||
return paddingByteCount + 7; |
|||
} |
|||
|
|||
protected override int GetVideoPacketHeaderSize(Stream readStream, long currentOffset) |
|||
{ |
|||
return 0xC; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,506 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Text; |
|||
|
|||
using VGMToolbox.util; |
|||
|
|||
namespace VGMToolbox.format |
|||
{ |
|||
public abstract class MpegStream |
|||
{ |
|||
protected static readonly byte[] PacketStartBytes = new byte[] { 0x00, 0x00, 0x01, 0xBA }; |
|||
protected static readonly byte[] PacketEndBytes = new byte[] { 0x00, 0x00, 0x01, 0xB9 }; |
|||
|
|||
public MpegStream(string path) |
|||
{ |
|||
this.FilePath = path; |
|||
this.UsesSameIdForMultipleAudioTracks = false; |
|||
this.SubTitleExtractionSupported = false; |
|||
this.BlockSizeIsLittleEndian = false; |
|||
|
|||
//********************
|
|||
// Add Slice Packets
|
|||
//********************
|
|||
byte[] sliceBytes; |
|||
uint sliceBytesValue; |
|||
BlockSizeStruct blockSize = new BlockSizeStruct(PacketSizeType.Static, 0xE); |
|||
|
|||
for (byte i = 0; i <= 0xAF; i++) |
|||
{ |
|||
sliceBytes = new byte[] { 0x00, 0x00, 0x01, i }; |
|||
sliceBytesValue = BitConverter.ToUInt32(sliceBytes, 0); |
|||
this.BlockIdDictionary.Add(sliceBytesValue, blockSize); |
|||
} |
|||
} |
|||
|
|||
public enum PacketSizeType |
|||
{ |
|||
Static, |
|||
SizeBytes, |
|||
Eof |
|||
} |
|||
|
|||
public struct MpegDemuxOptions |
|||
{ |
|||
public bool AddHeader { set; get; } |
|||
} |
|||
|
|||
public struct BlockSizeStruct |
|||
{ |
|||
public PacketSizeType SizeType; |
|||
public int Size; |
|||
|
|||
public BlockSizeStruct(PacketSizeType sizeTypeValue, int sizeValue) |
|||
{ |
|||
this.SizeType = sizeTypeValue; |
|||
this.Size = sizeValue; |
|||
} |
|||
} |
|||
|
|||
public struct DemuxOptionsStruct |
|||
{ |
|||
public bool ExtractVideo { set; get; } |
|||
public bool ExtractAudio { set; get; } |
|||
|
|||
public bool AddHeader { set; get; } |
|||
public bool SplitAudioStreams { set; get; } |
|||
public bool AddPlaybackHacks { set; get; } |
|||
} |
|||
|
|||
#region Dictionary Initialization
|
|||
|
|||
protected Dictionary<uint, BlockSizeStruct> BlockIdDictionary = |
|||
new Dictionary<uint, BlockSizeStruct> |
|||
{ |
|||
//********************
|
|||
// System Packets
|
|||
//********************
|
|||
{BitConverter.ToUInt32(MpegStream.PacketEndBytes, 0), new BlockSizeStruct(PacketSizeType.Eof, -1)}, // Program End
|
|||
{BitConverter.ToUInt32(MpegStream.PacketStartBytes, 0), new BlockSizeStruct(PacketSizeType.Static, 0xE)}, // Pack Header
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xBB }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // System Header, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xBD }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Private Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xBE }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Padding Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xBF }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Private Stream, two bytes following equal length (Big Endian)
|
|||
|
|||
//****************************
|
|||
// Audio Streams
|
|||
//****************************
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xC0 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xC1 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xC2 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xC3 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xC4 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xC5 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xC6 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xC7 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xC8 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xC9 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xCA }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xCB }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xCC }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xCD }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xCE }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xCF }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xD0 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xD1 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xD2 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xD3 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xD4 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xD5 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xD6 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xD7 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xD8 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xD9 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xDA }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xDB }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xDC }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xDD }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xDE }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xDF }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Audio Stream, two bytes following equal length (Big Endian)
|
|||
|
|||
//****************************
|
|||
// Video Streams
|
|||
//****************************
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xE0 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xE1 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xE2 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xE3 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xE4 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xE5 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xE6 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xE7 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xE8 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xE9 }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xEA }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xEB }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xEC }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xED }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xEE }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
{BitConverter.ToUInt32(new byte[] { 0x00, 0x00, 0x01, 0xEF }, 0), new BlockSizeStruct(PacketSizeType.SizeBytes, 2)}, // Video Stream, two bytes following equal length (Big Endian)
|
|||
}; |
|||
#endregion
|
|||
|
|||
public string FilePath { get; set; } |
|||
public string FileExtensionAudio { get; set; } |
|||
public string FileExtensionVideo { get; set; } |
|||
|
|||
public bool HasAudio { get; set; } |
|||
public string FinalAudioExtension { get; set; } |
|||
|
|||
protected Dictionary<byte, string> StreamIdFileType = new Dictionary<byte, string>(); |
|||
|
|||
public bool UsesSameIdForMultipleAudioTracks { set; get; } // for PMF/PAM/DVD, who use 000001BD for all audio tracks
|
|||
public bool SubTitleExtractionSupported { set; get; } // assume not supported.
|
|||
|
|||
public bool BlockSizeIsLittleEndian { set; get; } |
|||
|
|||
protected virtual byte[] GetPacketStartBytes() { return MpegStream.PacketStartBytes; } |
|||
|
|||
protected virtual byte[] GetPacketEndBytes() { return MpegStream.PacketEndBytes; } |
|||
|
|||
protected abstract int GetAudioPacketHeaderSize(Stream readStream, long currentOffset); |
|||
|
|||
protected virtual int GetAudioPacketSubHeaderSize(Stream readStream, long currentOffset, byte streamId) { return 0; } |
|||
|
|||
protected abstract int GetVideoPacketHeaderSize(Stream readStream, long currentOffset); |
|||
|
|||
protected virtual int GetAudioPacketFooterSize(Stream readStream, long currentOffset) { return 0; } |
|||
|
|||
protected virtual int GetVideoPacketFooterSize(Stream readStream, long currentOffset) { return 0; } |
|||
|
|||
protected virtual bool IsThisAnAudioBlock(byte[] blockToCheck) |
|||
{ |
|||
return ((blockToCheck[3] >= 0xC0) && |
|||
(blockToCheck[3] <= 0xDF)); |
|||
} |
|||
|
|||
protected virtual bool IsThisAVideoBlock(byte[] blockToCheck) |
|||
{ |
|||
return ((blockToCheck[3] >= 0xE0) && (blockToCheck[3] <= 0xEF)); |
|||
} |
|||
|
|||
protected virtual bool IsThisASubPictureBlock(byte[] blockToCheck) |
|||
{ |
|||
return ((blockToCheck[3] >= 0xE0) && (blockToCheck[3] <= 0xEF)); |
|||
} |
|||
|
|||
protected virtual string GetAudioFileExtension(Stream readStream, long currentOffset) |
|||
{ |
|||
return this.FileExtensionAudio; |
|||
} |
|||
|
|||
protected virtual string GetVideoFileExtension(Stream readStream, long currentOffset) |
|||
{ |
|||
return this.FileExtensionVideo; |
|||
} |
|||
|
|||
protected virtual byte GetStreamId(Stream readStream, long currentOffset) { return 0; } |
|||
|
|||
protected virtual long GetStartOffset(Stream readStream, long currentOffset) { return 0; } |
|||
|
|||
protected virtual void DoFinalTasks(FileStream sourceFileStream, Dictionary<uint, FileStream> outputFiles, bool addHeader) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public virtual void DemultiplexStreams(DemuxOptionsStruct demuxOptions) |
|||
{ |
|||
using (FileStream fs = File.OpenRead(this.FilePath)) |
|||
{ |
|||
long fileSize = fs.Length; |
|||
long currentOffset = 0; |
|||
|
|||
byte[] currentBlockId; |
|||
uint currentBlockIdVal; |
|||
byte[] currentBlockIdNaming; |
|||
|
|||
BlockSizeStruct blockStruct = new BlockSizeStruct(); |
|||
byte[] blockSizeArray; |
|||
uint blockSize; |
|||
|
|||
int audioBlockSkipSize; |
|||
int videoBlockSkipSize; |
|||
|
|||
int audioBlockFooterSize; |
|||
int videoBlockFooterSize; |
|||
|
|||
int cutSize; |
|||
|
|||
bool eofFlagFound = false; |
|||
|
|||
Dictionary<uint, FileStream> streamOutputWriters = new Dictionary<uint, FileStream>(); |
|||
string outputFileName; |
|||
|
|||
byte streamId = 0; // for types that have multiple streams in the same block ID
|
|||
uint currentStreamKey; // hash key for each file
|
|||
bool isAudioBlock; |
|||
string audioFileExtension; |
|||
|
|||
// look for first packet
|
|||
currentOffset = this.GetStartOffset(fs, currentOffset); |
|||
currentOffset = ParseFile.GetNextOffset(fs, currentOffset, this.GetPacketStartBytes()); |
|||
|
|||
if (currentOffset != -1) |
|||
{ |
|||
while (currentOffset < fileSize) |
|||
{ |
|||
#if DEBUG
|
|||
//if (currentOffset == 0x414080e)
|
|||
//{
|
|||
// int gggg = 1;
|
|||
//}
|
|||
|
|||
//// hack for bad data (ni no kuni s09.pam)
|
|||
//if ((currentOffset & 1) == 1)
|
|||
//{
|
|||
// currentOffset = MathUtil.RoundUpToByteAlignment(currentOffset, 0x800);
|
|||
//}
|
|||
#endif
|
|||
|
|||
try |
|||
{ |
|||
// get the current block
|
|||
currentBlockId = ParseFile.ParseSimpleOffset(fs, currentOffset, 4); |
|||
|
|||
// get value to use as key to hash table
|
|||
currentBlockIdVal = BitConverter.ToUInt32(currentBlockId, 0); |
|||
|
|||
if (BlockIdDictionary.ContainsKey(currentBlockIdVal)) |
|||
{ |
|||
// get info about this block type
|
|||
blockStruct = BlockIdDictionary[currentBlockIdVal]; |
|||
|
|||
switch (blockStruct.SizeType) |
|||
{ |
|||
/////////////////////
|
|||
// Static Block Size
|
|||
/////////////////////
|
|||
case PacketSizeType.Static: |
|||
currentOffset += blockStruct.Size; // skip this block
|
|||
break; |
|||
|
|||
//////////////////
|
|||
// End of Stream
|
|||
//////////////////
|
|||
case PacketSizeType.Eof: |
|||
eofFlagFound = true; // set EOF block found so we can exit the loop
|
|||
break; |
|||
|
|||
//////////////////////
|
|||
// Varying Block Size
|
|||
//////////////////////
|
|||
case PacketSizeType.SizeBytes: |
|||
|
|||
// Get the block size
|
|||
blockSizeArray = ParseFile.ParseSimpleOffset(fs, currentOffset + currentBlockId.Length, blockStruct.Size); |
|||
|
|||
if (!this.BlockSizeIsLittleEndian) |
|||
{ |
|||
Array.Reverse(blockSizeArray); |
|||
} |
|||
|
|||
switch (blockStruct.Size) |
|||
{ |
|||
case 4: |
|||
blockSize = (uint)BitConverter.ToUInt32(blockSizeArray, 0); |
|||
break; |
|||
case 2: |
|||
blockSize = (uint)BitConverter.ToUInt16(blockSizeArray, 0); |
|||
break; |
|||
case 1: |
|||
blockSize = (uint)blockSizeArray[0]; |
|||
break; |
|||
default: |
|||
throw new ArgumentOutOfRangeException(String.Format("Unhandled size block size.{0}", Environment.NewLine)); |
|||
} |
|||
|
|||
|
|||
// if block type is audio or video, extract it
|
|||
isAudioBlock = this.IsThisAnAudioBlock(currentBlockId); |
|||
|
|||
if ((demuxOptions.ExtractAudio && isAudioBlock) || |
|||
(demuxOptions.ExtractVideo && this.IsThisAVideoBlock(currentBlockId))) |
|||
{ |
|||
// reset stream id
|
|||
streamId = 0; |
|||
|
|||
// if audio block, get the stream number from the queue
|
|||
if (isAudioBlock && this.UsesSameIdForMultipleAudioTracks) |
|||
{ |
|||
streamId = this.GetStreamId(fs, currentOffset); |
|||
currentStreamKey = (streamId | currentBlockIdVal); |
|||
} |
|||
else |
|||
{ |
|||
currentStreamKey = currentBlockIdVal; |
|||
} |
|||
|
|||
// check if we've already started parsing this stream
|
|||
if (!streamOutputWriters.ContainsKey(currentStreamKey)) |
|||
{ |
|||
// convert block id to little endian for naming
|
|||
currentBlockIdNaming = BitConverter.GetBytes(currentStreamKey); |
|||
Array.Reverse(currentBlockIdNaming); |
|||
|
|||
// build output file name
|
|||
outputFileName = Path.GetFileNameWithoutExtension(this.FilePath); |
|||
outputFileName = outputFileName + "_" + BitConverter.ToUInt32(currentBlockIdNaming, 0).ToString("X8"); |
|||
|
|||
// add proper extension
|
|||
if (this.IsThisAnAudioBlock(currentBlockId)) |
|||
{ |
|||
audioFileExtension = this.GetAudioFileExtension(fs, currentOffset); |
|||
outputFileName += audioFileExtension; |
|||
|
|||
if (!this.StreamIdFileType.ContainsKey(streamId)) |
|||
{ |
|||
this.StreamIdFileType.Add(streamId, audioFileExtension); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
this.FileExtensionVideo = this.GetVideoFileExtension(fs, currentOffset); |
|||
outputFileName += this.FileExtensionVideo; |
|||
} |
|||
|
|||
// add output directory
|
|||
outputFileName = Path.Combine(Path.GetDirectoryName(this.FilePath), outputFileName); |
|||
|
|||
// add an output stream for writing
|
|||
streamOutputWriters[currentStreamKey] = new FileStream(outputFileName, FileMode.Create, FileAccess.ReadWrite); |
|||
} |
|||
|
|||
// write the block
|
|||
if (this.IsThisAnAudioBlock(currentBlockId)) |
|||
{ |
|||
// write audio
|
|||
audioBlockSkipSize = this.GetAudioPacketHeaderSize(fs, currentOffset) + GetAudioPacketSubHeaderSize(fs, currentOffset, streamId); |
|||
audioBlockFooterSize = this.GetAudioPacketFooterSize(fs, currentOffset); |
|||
cutSize = (int)(blockSize - audioBlockSkipSize - audioBlockFooterSize); |
|||
if (cutSize > 0) |
|||
{ |
|||
streamOutputWriters[currentStreamKey].Write(ParseFile.ParseSimpleOffset(fs, currentOffset + currentBlockId.Length + blockSizeArray.Length + audioBlockSkipSize, (int)(blockSize - audioBlockSkipSize)), 0, cutSize); |
|||
} |
|||
#if DEBUG
|
|||
//else
|
|||
//{
|
|||
// int aaa = 1;
|
|||
//}
|
|||
#endif
|
|||
} |
|||
else |
|||
{ |
|||
// write video
|
|||
videoBlockSkipSize = this.GetVideoPacketHeaderSize(fs, currentOffset); |
|||
videoBlockFooterSize = this.GetVideoPacketFooterSize(fs, currentOffset); |
|||
cutSize = (int)(blockSize - videoBlockSkipSize - videoBlockFooterSize); |
|||
if (cutSize > 0) |
|||
{ |
|||
streamOutputWriters[currentStreamKey].Write(ParseFile.ParseSimpleOffset(fs, currentOffset + currentBlockId.Length + blockSizeArray.Length + videoBlockSkipSize, (int)(blockSize - videoBlockSkipSize)), 0, cutSize); |
|||
} |
|||
#if DEBUG
|
|||
//else
|
|||
//{
|
|||
// int vvv = 1;
|
|||
//}
|
|||
#endif
|
|||
} |
|||
} |
|||
|
|||
// move to next block
|
|||
currentOffset += currentBlockId.Length + blockSizeArray.Length + blockSize; |
|||
blockSizeArray = new byte[] { }; |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
else // this is an undexpected block type
|
|||
{ |
|||
this.closeAllWriters(streamOutputWriters); |
|||
Array.Reverse(currentBlockId); |
|||
throw new FormatException(String.Format("Block ID at 0x{0} not found in table: 0x{1}", currentOffset.ToString("X8"), BitConverter.ToUInt32(currentBlockId, 0).ToString("X8"))); |
|||
} |
|||
|
|||
// exit loop if EOF block found
|
|||
if (eofFlagFound) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
catch (Exception _ex) |
|||
{ |
|||
this.closeAllWriters(streamOutputWriters); |
|||
throw new Exception(String.Format("Error parsing file at offset {0), '{1}'", currentOffset.ToString("X8"), _ex.Message), _ex); |
|||
} |
|||
} // while (currentOffset < fileSize)
|
|||
} |
|||
else |
|||
{ |
|||
this.closeAllWriters(streamOutputWriters); |
|||
throw new FormatException(String.Format("Cannot find Pack Header for file: {0}{1}", Path.GetFileName(this.FilePath), Environment.NewLine)); |
|||
} |
|||
|
|||
///////////////////////////////////
|
|||
// Perform any final tasks needed
|
|||
///////////////////////////////////
|
|||
this.DoFinalTasks(fs, streamOutputWriters, demuxOptions.AddHeader); |
|||
|
|||
//////////////////////////
|
|||
// close all open writers
|
|||
//////////////////////////
|
|||
this.closeAllWriters(streamOutputWriters); |
|||
|
|||
} // using (FileStream fs = File.OpenRead(path))
|
|||
} |
|||
|
|||
private void closeAllWriters(Dictionary<uint, FileStream> writers) |
|||
{ |
|||
//////////////////////////
|
|||
// close all open writers
|
|||
//////////////////////////
|
|||
foreach (uint b in writers.Keys) |
|||
{ |
|||
if (writers[b].CanRead) |
|||
{ |
|||
writers[b].Close(); |
|||
writers[b].Dispose(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static int GetMpegStreamType(string path) |
|||
{ |
|||
int mpegType = -1; |
|||
|
|||
using (FileStream fs = File.OpenRead(path)) |
|||
{ |
|||
// look for first packet
|
|||
long currentOffset = ParseFile.GetNextOffset(fs, 0, MpegStream.PacketStartBytes); |
|||
|
|||
if (currentOffset != -1) |
|||
{ |
|||
currentOffset += 4; |
|||
fs.Position = currentOffset; |
|||
byte idByte = (byte)fs.ReadByte(); |
|||
|
|||
if ((int)ByteConversion.GetHighNibble(idByte) == 2) |
|||
{ |
|||
mpegType = 1; |
|||
} |
|||
else if ((int)ByteConversion.GetHighNibble(idByte) == 4) |
|||
{ |
|||
mpegType = 2; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
throw new FormatException(String.Format("Cannot find Pack Header for file: {0}{1}", Path.GetFileName(path), Environment.NewLine)); |
|||
} |
|||
} |
|||
|
|||
return mpegType; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,57 @@ |
|||
using System; |
|||
using System.IO; |
|||
using VGMToolbox.util; |
|||
|
|||
namespace VGMToolbox.format |
|||
{ |
|||
public class SofdecStream : Mpeg1Stream |
|||
{ |
|||
new public const string DefaultVideoExtension = ".m2v"; |
|||
|
|||
public const string AdxAudioExtension = ".adx"; |
|||
public const string AixAudioExtension = ".aix"; |
|||
public const string Ac3AudioExtension = ".ac3"; |
|||
|
|||
public static readonly byte[] AixSignatureBytes = new byte[] { 0x41, 0x49, 0x58, 0x46 }; |
|||
public static readonly byte[] Ac3SignatureBytes = new byte[] { 0x0B, 0x77 }; |
|||
|
|||
public SofdecStream(string path): base(path) |
|||
{ |
|||
this.FileExtensionAudio = AdxAudioExtension; |
|||
this.FileExtensionVideo = DefaultVideoExtension; |
|||
} |
|||
|
|||
protected override string GetAudioFileExtension(Stream readStream, long currentOffset) |
|||
{ |
|||
string fileExtension; |
|||
byte[] checkBytes, checkBytesAc3; |
|||
|
|||
int headerSize = this.GetAudioPacketHeaderSize(readStream, currentOffset); |
|||
checkBytes = ParseFile.ParseSimpleOffset(readStream, (currentOffset + 6 + headerSize), 4); |
|||
|
|||
if (ParseFile.CompareSegment(checkBytes, 0, AixSignatureBytes)) |
|||
{ |
|||
fileExtension = AixAudioExtension; |
|||
} |
|||
else if (checkBytes[0] == 0x80) |
|||
{ |
|||
fileExtension = AdxAudioExtension; |
|||
} |
|||
else |
|||
{ |
|||
checkBytesAc3 = ParseFile.ParseSimpleOffset(readStream, (currentOffset + 6 + headerSize), 2); |
|||
|
|||
if (ParseFile.CompareSegment(checkBytesAc3, 0, Ac3SignatureBytes)) |
|||
{ |
|||
fileExtension = Ac3AudioExtension; |
|||
} |
|||
else |
|||
{ |
|||
fileExtension = ".bin"; |
|||
} |
|||
} |
|||
|
|||
return fileExtension; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1 @@ |
|||
dotnet publish --configuration Release --framework netcoreapp3.1 --output publish /p:DebugType=None /p:DebugSymbols=false |
@ -0,0 +1,5 @@ |
|||
{ |
|||
"VideoParameter" : "-c:v copy", |
|||
"AudioParameter" : "-c:a ac3 -b:a 640k -af pan='stereo|FL=FL+FC+0.5*BL+BR|FR=FR+LFE+0.5*BL+BR'", |
|||
"OutputFormat" : "mp4" |
|||
} |
@ -0,0 +1,4 @@ |
|||
{ |
|||
"Vgmstream" : "https://github.com/losnoco/vgmstream/releases/latest/download/test.zip", |
|||
"FFmpeg" : "https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-latest-win64-static.zip" |
|||
} |
Binary file not shown.
Loading…
Reference in new issue