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