Browse Source

Initial commit

master
Rikux3 4 years ago
commit
174bf2f357
  1. 2
      .gitattributes
  2. 353
      .gitignore
  3. 21
      LICENSE
  4. 25
      UsmToolkit.sln
  5. 24
      UsmToolkit/Helpers.cs
  6. 197
      UsmToolkit/Program.cs
  7. 8
      UsmToolkit/Properties/launchSettings.json
  8. 28
      UsmToolkit/UsmToolkit.csproj
  9. 226
      UsmToolkit/VGMToolbox/CriUsmStream.cs
  10. 39
      UsmToolkit/VGMToolbox/Mpeg1Stream.cs
  11. 506
      UsmToolkit/VGMToolbox/MpegStream.cs
  12. 57
      UsmToolkit/VGMToolbox/SofdecStream.cs
  13. 1
      UsmToolkit/build.bat
  14. 5
      UsmToolkit/config.json
  15. 4
      UsmToolkit/deps.json
  16. BIN
      UsmToolkit/deps/vgmtutil.dll

2
.gitattributes

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

353
.gitignore

@ -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/

21
LICENSE

@ -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.

25
UsmToolkit.sln

@ -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

24
UsmToolkit/Helpers.cs

@ -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();
}
}
}

197
UsmToolkit/Program.cs

@ -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;
}
}
}
}

8
UsmToolkit/Properties/launchSettings.json

@ -0,0 +1,8 @@
{
"profiles": {
"UsmToolkit": {
"commandName": "Project",
"commandLineArgs": "extract \"F:\\CUSA05787\\tresgame\\content\\crimovie\\rebellis02\\en\\cs_dw009_mv.usm\" --join"
}
}
}

28
UsmToolkit/UsmToolkit.csproj

@ -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>

226
UsmToolkit/VGMToolbox/CriUsmStream.cs

@ -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);
}
}
}
}
}

39
UsmToolkit/VGMToolbox/Mpeg1Stream.cs

@ -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;
}
}
}

506
UsmToolkit/VGMToolbox/MpegStream.cs

@ -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;
}
}
}

57
UsmToolkit/VGMToolbox/SofdecStream.cs

@ -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;
}
}
}

1
UsmToolkit/build.bat

@ -0,0 +1 @@
dotnet publish --configuration Release --framework netcoreapp3.1 --output publish /p:DebugType=None /p:DebugSymbols=false

5
UsmToolkit/config.json

@ -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"
}

4
UsmToolkit/deps.json

@ -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"
}

BIN
UsmToolkit/deps/vgmtutil.dll

Binary file not shown.
Loading…
Cancel
Save