2016-11-12 19:02:48 +03:00
using System ;
using System.IO ;
using System.Windows.Forms ;
2016-12-31 15:56:10 +03:00
using System.Text ;
2017-04-23 19:22:25 +03:00
using System.Threading.Tasks ;
2016-11-12 19:02:48 +03:00
2017-04-23 19:22:25 +03:00
using AcbEditor.Properties ;
using SonicAudioLib ;
2016-11-12 19:02:48 +03:00
using SonicAudioLib.CriMw ;
using SonicAudioLib.IO ;
using SonicAudioLib.Archive ;
namespace AcbEditor
{
class Program
{
static void Main ( string [ ] args )
{
if ( args . Length < 1 )
{
Console . WriteLine ( Properties . Resources . Description ) ;
Console . ReadLine ( ) ;
return ;
}
2016-12-31 15:56:10 +03:00
#if ! DEBUG
2016-11-12 19:02:48 +03:00
try
{
2017-04-23 19:22:25 +03:00
#endif
2016-11-12 19:02:48 +03:00
if ( args [ 0 ] . EndsWith ( ".acb" ) )
{
2017-04-23 19:22:25 +03:00
var extractor = new DataExtractor ( ) ;
extractor . ProgressChanged + = OnProgressChanged ;
extractor . BufferSize = Settings . Default . BufferSize ;
extractor . EnableThreading = Settings . Default . EnableThreading ;
extractor . MaxThreads = Settings . Default . MaxThreads ;
2016-11-12 19:02:48 +03:00
string baseDirectory = Path . GetDirectoryName ( args [ 0 ] ) ;
2017-03-09 20:48:17 +03:00
string outputDirectoryPath = Path . ChangeExtension ( args [ 0 ] , null ) ;
2016-11-12 19:02:48 +03:00
string extAfs2ArchivePath = string . Empty ;
Directory . CreateDirectory ( outputDirectoryPath ) ;
using ( CriTableReader acbReader = CriTableReader . Create ( args [ 0 ] ) )
{
acbReader . Read ( ) ;
CriAfs2Archive afs2Archive = new CriAfs2Archive ( ) ;
CriAfs2Archive extAfs2Archive = new CriAfs2Archive ( ) ;
2016-12-31 15:56:10 +03:00
CriCpkArchive cpkArchive = new CriCpkArchive ( ) ;
CriCpkArchive extCpkArchive = null ;
extAfs2ArchivePath = outputDirectoryPath + ".awb" ;
bool found = File . Exists ( extAfs2ArchivePath ) ;
if ( ! found )
{
extAfs2ArchivePath = outputDirectoryPath + "_streamfiles.awb" ;
found = File . Exists ( extAfs2ArchivePath ) ;
}
if ( ! found )
{
extAfs2ArchivePath = outputDirectoryPath + "_STR.awb" ;
found = File . Exists ( extAfs2ArchivePath ) ;
}
bool cpkMode = true ;
2017-04-23 19:22:25 +03:00
long awbPosition = acbReader . GetPosition ( "AwbFile" ) ;
2016-11-12 19:02:48 +03:00
if ( acbReader . GetLength ( "AwbFile" ) > 0 )
{
using ( Substream afs2Stream = acbReader . GetSubstream ( "AwbFile" ) )
{
2016-12-31 15:56:10 +03:00
cpkMode = ! CheckIfAfs2 ( afs2Stream ) ;
if ( cpkMode )
{
cpkArchive . Read ( afs2Stream ) ;
}
else
{
afs2Archive . Read ( afs2Stream ) ;
}
2016-11-12 19:02:48 +03:00
}
}
if ( acbReader . GetLength ( "StreamAwbAfs2Header" ) > 0 )
{
2016-12-31 15:56:10 +03:00
cpkMode = false ;
2016-11-12 19:02:48 +03:00
using ( Substream extAfs2Stream = acbReader . GetSubstream ( "StreamAwbAfs2Header" ) )
{
extAfs2Archive . Read ( extAfs2Stream ) ;
}
if ( ! found )
{
2017-03-09 20:48:17 +03:00
throw new FileNotFoundException ( "Cannot find the external .AWB file for this .ACB file. Please ensure that the external .AWB file is stored in the directory where the .ACB file is." ) ;
2016-11-12 19:02:48 +03:00
}
}
using ( Substream waveformTableStream = acbReader . GetSubstream ( "WaveformTable" ) )
using ( CriTableReader waveformReader = CriTableReader . Create ( waveformTableStream ) )
{
while ( waveformReader . Read ( ) )
{
2016-12-31 15:56:10 +03:00
ushort id = waveformReader . GetUInt16 ( "Id" ) ;
2016-11-12 19:02:48 +03:00
byte encodeType = waveformReader . GetByte ( "EncodeType" ) ;
bool streaming = waveformReader . GetBoolean ( "Streaming" ) ;
2016-12-31 15:56:10 +03:00
string outputName = id . ToString ( "D5" ) ;
2016-11-12 19:02:48 +03:00
if ( streaming )
{
outputName + = "_streaming" ;
}
outputName + = GetExtension ( encodeType ) ;
outputName = Path . Combine ( outputDirectoryPath , outputName ) ;
if ( streaming )
{
2016-12-31 15:56:10 +03:00
if ( ! found )
{
throw new Exception ( "Cannot find the external .AWB file for this .ACB file. Please ensure that the external .AWB file is stored in the directory where the .ACB file is." ) ;
}
else if ( extCpkArchive = = null & & cpkMode )
{
extCpkArchive = new CriCpkArchive ( ) ;
2017-04-23 19:22:25 +03:00
extCpkArchive . Load ( extAfs2ArchivePath , Settings . Default . BufferSize ) ;
2016-12-31 15:56:10 +03:00
}
EntryBase afs2Entry = null ;
if ( cpkMode )
{
afs2Entry = extCpkArchive . GetById ( id ) ;
}
else
{
afs2Entry = extAfs2Archive . GetById ( id ) ;
}
2016-11-12 19:02:48 +03:00
2017-04-23 19:22:25 +03:00
extractor . Add ( extAfs2ArchivePath , outputName , afs2Entry . Position , afs2Entry . Length ) ;
2016-11-12 19:02:48 +03:00
}
else
{
2016-12-31 15:56:10 +03:00
EntryBase afs2Entry = null ;
if ( cpkMode )
{
afs2Entry = cpkArchive . GetById ( id ) ;
}
else
{
afs2Entry = afs2Archive . GetById ( id ) ;
}
2016-11-12 19:02:48 +03:00
2017-04-23 19:22:25 +03:00
extractor . Add ( args [ 0 ] , outputName , awbPosition + afs2Entry . Position , afs2Entry . Length ) ;
2016-11-12 19:02:48 +03:00
}
}
}
}
2017-04-23 19:22:25 +03:00
extractor . Run ( ) ;
2016-11-12 19:02:48 +03:00
}
else if ( File . GetAttributes ( args [ 0 ] ) . HasFlag ( FileAttributes . Directory ) )
{
string baseDirectory = Path . GetDirectoryName ( args [ 0 ] ) ;
string acbPath = args [ 0 ] + ".acb" ;
string awbPath = args [ 0 ] + "_streamfiles.awb" ;
bool found = File . Exists ( awbPath ) ;
if ( ! found )
{
awbPath = args [ 0 ] + "_STR.awb" ;
found = File . Exists ( awbPath ) ;
}
if ( ! found )
{
awbPath = args [ 0 ] + ".awb" ;
}
if ( ! File . Exists ( acbPath ) )
{
2017-04-23 19:22:25 +03:00
throw new FileNotFoundException ( "Cannot find the .ACB file for this directory. Please ensure that the .ACB file is stored in the directory where this directory is." ) ;
2016-11-12 19:02:48 +03:00
}
CriTable acbFile = new CriTable ( ) ;
2017-04-23 19:22:25 +03:00
acbFile . Load ( acbPath , Settings . Default . BufferSize ) ;
2016-11-12 19:02:48 +03:00
CriAfs2Archive afs2Archive = new CriAfs2Archive ( ) ;
CriAfs2Archive extAfs2Archive = new CriAfs2Archive ( ) ;
2016-12-31 15:56:10 +03:00
CriCpkArchive cpkArchive = new CriCpkArchive ( ) ;
CriCpkArchive extCpkArchive = new CriCpkArchive ( ) ;
cpkArchive . Mode = extCpkArchive . Mode = CriCpkMode . Id ;
2017-04-23 19:22:25 +03:00
afs2Archive . ProgressChanged + = OnProgressChanged ;
extAfs2Archive . ProgressChanged + = OnProgressChanged ;
cpkArchive . ProgressChanged + = OnProgressChanged ;
extCpkArchive . ProgressChanged + = OnProgressChanged ;
2016-12-31 15:56:10 +03:00
bool cpkMode = true ;
byte [ ] awbFile = ( byte [ ] ) acbFile . Rows [ 0 ] [ "AwbFile" ] ;
byte [ ] streamAwbAfs2Header = ( byte [ ] ) acbFile . Rows [ 0 ] [ "StreamAwbAfs2Header" ] ;
cpkMode = ! ( awbFile ! = null & & awbFile . Length > = 4 & & Encoding . ASCII . GetString ( awbFile , 0 , 4 ) = = "AFS2" ) & & ( streamAwbAfs2Header = = null | | streamAwbAfs2Header . Length = = 0 ) ;
2016-11-12 19:02:48 +03:00
using ( CriTableReader reader = CriTableReader . Create ( ( byte [ ] ) acbFile . Rows [ 0 ] [ "WaveformTable" ] ) )
{
while ( reader . Read ( ) )
{
2016-12-31 15:56:10 +03:00
ushort id = reader . GetUInt16 ( "Id" ) ;
2016-11-12 19:02:48 +03:00
byte encodeType = reader . GetByte ( "EncodeType" ) ;
bool streaming = reader . GetBoolean ( "Streaming" ) ;
2016-12-31 15:56:10 +03:00
string inputName = id . ToString ( "D5" ) ;
2016-11-12 19:02:48 +03:00
if ( streaming )
{
2017-04-23 19:22:25 +03:00
inputName + = "_streaming" ;
2016-11-12 19:02:48 +03:00
}
inputName + = GetExtension ( encodeType ) ;
inputName = Path . Combine ( args [ 0 ] , inputName ) ;
if ( ! File . Exists ( inputName ) )
{
2017-04-23 19:22:25 +03:00
throw new FileNotFoundException ( $"Cannot find audio file with id {id} for replacement.\nPath attempt: {inputName}" ) ;
2016-11-12 19:02:48 +03:00
}
2016-12-31 15:56:10 +03:00
if ( cpkMode )
2016-11-12 19:02:48 +03:00
{
2016-12-31 15:56:10 +03:00
CriCpkEntry entry = new CriCpkEntry ( ) ;
entry . FilePath = new FileInfo ( inputName ) ;
entry . Id = id ;
if ( streaming )
{
extCpkArchive . Add ( entry ) ;
}
else
{
cpkArchive . Add ( entry ) ;
}
2016-11-12 19:02:48 +03:00
}
else
{
2016-12-31 15:56:10 +03:00
CriAfs2Entry entry = new CriAfs2Entry ( ) ;
entry . FilePath = new FileInfo ( inputName ) ;
entry . Id = id ;
if ( streaming )
{
extAfs2Archive . Add ( entry ) ;
}
else
{
afs2Archive . Add ( entry ) ;
}
2016-11-12 19:02:48 +03:00
}
}
}
acbFile . Rows [ 0 ] [ "AwbFile" ] = null ;
acbFile . Rows [ 0 ] [ "StreamAwbAfs2Header" ] = null ;
2016-12-31 15:56:10 +03:00
if ( afs2Archive . Count > 0 | | cpkArchive . Count > 0 )
2016-11-12 19:02:48 +03:00
{
Console . WriteLine ( "Saving internal AWB..." ) ;
2016-12-31 15:56:10 +03:00
acbFile . Rows [ 0 ] [ "AwbFile" ] = cpkMode ? cpkArchive . Save ( ) : afs2Archive . Save ( ) ;
2017-04-23 19:22:25 +03:00
Console . WriteLine ( ) ;
2016-11-12 19:02:48 +03:00
}
2016-12-31 15:56:10 +03:00
if ( extAfs2Archive . Count > 0 | | extCpkArchive . Count > 0 )
2016-11-12 19:02:48 +03:00
{
Console . WriteLine ( "Saving external AWB..." ) ;
2016-12-31 15:56:10 +03:00
if ( cpkMode )
2016-11-12 19:02:48 +03:00
{
2017-04-23 19:22:25 +03:00
extCpkArchive . Save ( awbPath , Settings . Default . BufferSize ) ;
2016-11-12 19:02:48 +03:00
}
2016-12-31 15:56:10 +03:00
else
{
2017-04-23 19:22:25 +03:00
extAfs2Archive . Save ( awbPath , Settings . Default . BufferSize ) ;
acbFile . Rows [ 0 ] [ "StreamAwbAfs2Header" ] = extAfs2Archive . Header ;
2016-12-31 15:56:10 +03:00
}
2016-11-12 19:02:48 +03:00
}
acbFile . WriterSettings = CriTableWriterSettings . Adx2Settings ;
2017-04-23 19:22:25 +03:00
acbFile . Save ( acbPath , Settings . Default . BufferSize ) ;
}
2016-12-31 15:56:10 +03:00
#if ! DEBUG
2016-11-12 19:02:48 +03:00
}
catch ( Exception exception )
{
MessageBox . Show ( $"{exception.Message}" , "ACB Editor" , MessageBoxButtons . OK , MessageBoxIcon . Error ) ;
}
2016-12-31 15:56:10 +03:00
#endif
2016-11-12 19:02:48 +03:00
}
static string GetExtension ( byte encodeType )
{
switch ( encodeType )
{
case 0 :
case 3 :
return ".adx" ;
case 1 :
return ".ahx" ;
case 2 :
return ".hca" ;
case 4 :
return ".wiiadpcm" ;
case 5 :
return ".dsadpcm" ;
case 6 :
return ".hcamx" ;
case 10 :
case 7 :
return ".vag" ;
case 8 :
return ".at3" ;
case 9 :
2016-12-31 15:56:10 +03:00
return ".bcwav" ;
2016-11-12 19:02:48 +03:00
case 18 :
case 11 :
return ".at9" ;
case 12 :
return ".xma" ;
case 13 :
2016-12-31 15:56:10 +03:00
return ".dsp" ;
2016-11-12 19:02:48 +03:00
default :
return ".bin" ;
}
}
2016-12-31 15:56:10 +03:00
static bool CheckIfAfs2 ( Stream source )
{
long oldPosition = source . Position ;
2017-03-09 20:48:17 +03:00
bool result = EndianStream . ReadCString ( source , 4 ) = = "AFS2" ;
2016-12-31 15:56:10 +03:00
source . Seek ( oldPosition , SeekOrigin . Begin ) ;
return result ;
}
2017-04-23 19:22:25 +03:00
private static string buffer = new string ( ' ' , 17 ) ;
private static void OnProgressChanged ( object sender , ProgressChangedEventArgs e )
{
int left = Console . CursorLeft ;
int top = Console . CursorTop ;
Console . Write ( buffer ) ;
Console . SetCursorPosition ( left , top ) ;
Console . WriteLine ( "Progress: {0}%" , e . Progress ) ;
Console . SetCursorPosition ( left , top ) ;
}
2016-11-12 19:02:48 +03:00
}
}