2019-06-25 20:56:19 +02:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Text ;
using Switch_Toolbox ;
using System.Windows.Forms ;
using SARCExt ;
using Switch_Toolbox.Library ;
using Switch_Toolbox.Library.IO ;
using Switch_Toolbox.Library.Forms ;
2019-06-26 19:26:12 +02:00
namespace FirstPlugin
2019-06-25 20:56:19 +02:00
{
public class SARC : TreeNodeFile , IFileFormat
{
public FileType FileType { get ; set ; } = FileType . Archive ;
public bool CanSave { get ; set ; }
2019-07-01 21:44:19 +02:00
public string [ ] Description { get ; set ; } = new string [ ] { "Sorted ARChive" } ;
2019-06-25 20:56:19 +02:00
public string [ ] Extension { get ; set ; } = new string [ ] { "*.pack" , "*.sarc" , "*.bgenv" , "*.sblarc" , "*.sbactorpack" , ".arc" } ;
public string FileName { get ; set ; }
public string FilePath { get ; set ; }
public IFileInfo IFileInfo { get ; set ; }
public bool Identify ( System . IO . Stream stream )
{
using ( var reader = new Switch_Toolbox . Library . IO . FileReader ( stream , true ) )
{
return reader . CheckSignature ( 4 , "SARC" ) ;
}
}
public Type [ ] Types
{
get
{
List < Type > types = new List < Type > ( ) ;
return types . ToArray ( ) ;
}
}
2019-06-26 02:03:50 +02:00
public List < SarcEntry > Files = new List < SarcEntry > ( ) ;
2019-06-25 20:56:19 +02:00
public Dictionary < string , byte [ ] > OpenedFiles = new Dictionary < string , byte [ ] > ( ) ;
public SarcData sarcData ;
public string SarcHash ;
public void Load ( System . IO . Stream stream )
{
CanSave = true ;
IFileInfo . UseEditMenu = true ;
var SzsFiles = SARCExt . SARC . UnpackRamN ( stream ) ;
sarcData = new SarcData ( ) ;
sarcData . HashOnly = SzsFiles . HashOnly ;
sarcData . Files = SzsFiles . Files ;
sarcData . endianness = GetByteOrder ( stream ) ;
SarcHash = Utils . GenerateUniqueHashID ( ) ;
FillTreeNodes ( this , SzsFiles . Files , sarcData . HashOnly ) ;
Text = FileName ;
ContextMenuStrip = new STContextMenuStrip ( ) ;
2019-06-26 02:03:50 +02:00
ContextMenuStrip . Items . Add ( new STToolStipMenuItem ( "Save" , null , Save , Keys . Control | Keys . S ) ) ;
2019-06-25 20:56:19 +02:00
ContextMenuStrip . Items . Add ( new STToolStipMenuItem ( "Rename Actor Files (Odyssey)" , null , RenameActors , Keys . Control | Keys . S ) ) ;
// ContextMenuStrip.Items.Add(new STToolStipMenuItem("Unpack to Folder", null, UnpackToFolder, Keys.Control | Keys.E));
// ContextMenuStrip.Items.Add(new STToolStipMenuItem("Pack From Folder", null, PackFromFolder, Keys.Control | Keys.R));
ContextMenuStrip . Items . Add ( new STToolStripSeparator ( ) ) ;
ContextMenuStrip . Items . Add ( new STToolStipMenuItem ( "Batch Texture Editor" , null , PreviewTextures , Keys . Control | Keys . P ) ) ;
ContextMenuStrip . Items . Add ( new STToolStripSeparator ( ) ) ;
ContextMenuStrip . Items . Add ( new STToolStipMenuItem ( "Sort Childern" , null , SortChildern , Keys . Control | Keys . E ) ) ;
CanDelete = true ;
sarcData . Files . Clear ( ) ;
}
private void RenameActors ( object sender , EventArgs args )
{
string ActorName = Path . GetFileNameWithoutExtension ( Text ) ;
RenameDialog dialog = new RenameDialog ( ) ;
dialog . SetString ( ActorName ) ;
if ( dialog . ShowDialog ( ) = = DialogResult . OK )
{
string NewActorName = dialog . textBox1 . Text ;
Text = NewActorName + ".szs" ;
foreach ( TreeNode node in Nodes )
{
string NodeName = Path . GetFileNameWithoutExtension ( node . Text ) ;
string ext = Utils . GetExtension ( node . Text ) ;
if ( NodeName = = ActorName )
{
node . Text = $"{NewActorName}{ext}" ;
}
else if ( node . Text . Contains ( "Attribute.byml" ) )
{
node . Text = $"{NewActorName}Attribute.byml" ;
}
}
}
}
2019-06-26 02:03:50 +02:00
2019-06-25 20:56:19 +02:00
private void UnpackToFolder ( object sender , EventArgs args )
{
}
private void PackFromFolder ( object sender , EventArgs args )
{
}
2019-06-26 02:03:50 +02:00
private void Delete ( object sender , EventArgs args ) {
2019-06-25 20:56:19 +02:00
Unload ( ) ;
var editor = LibraryGUI . Instance . GetObjectEditor ( ) ;
if ( editor ! = null )
editor . ResetControls ( ) ;
}
private void SortChildern ( object sender , EventArgs args )
{
TreeView . TreeViewNodeSorter = new TreeChildSorter ( ) ;
TreeView . Sort ( ) ;
}
2019-06-26 02:03:50 +02:00
public class FolderEntry : TreeNode , IContextMenuNode
2019-06-25 20:56:19 +02:00
{
public FolderEntry ( string text , int imageIndex , int selectedImageIndex )
{
Text = text ;
ImageIndex = imageIndex ;
SelectedImageIndex = selectedImageIndex ;
2019-06-26 02:03:50 +02:00
}
2019-06-25 20:56:19 +02:00
2019-06-26 02:03:50 +02:00
public ToolStripItem [ ] GetContextMenuItems ( )
{
List < ToolStripItem > Items = new List < ToolStripItem > ( ) ;
Items . Add ( new ToolStripMenuItem ( "Sort Childern" , null , SortChildern , Keys . Control | Keys . W ) ) ;
return Items . ToArray ( ) ;
2019-06-25 20:56:19 +02:00
}
private void SortChildern ( object sender , EventArgs args )
{
TreeView . TreeViewNodeSorter = new TreeChildSorter ( ) ;
TreeView . Sort ( ) ;
}
}
public Syroot . BinaryData . ByteOrder GetByteOrder ( System . IO . Stream data )
{
using ( FileReader reader = new FileReader ( data ) )
{
reader . ByteOrder = Syroot . BinaryData . ByteOrder . BigEndian ;
reader . Seek ( 6 ) ;
ushort bom = reader . ReadUInt16 ( ) ;
reader . Close ( ) ;
reader . Dispose ( ) ;
if ( bom = = 0xFFFE )
return Syroot . BinaryData . ByteOrder . LittleEndian ;
else
return Syroot . BinaryData . ByteOrder . BigEndian ;
}
}
public void Unload ( )
{
2019-06-29 03:20:02 +02:00
foreach ( var file in Files )
file . FileData = null ;
2019-06-29 18:33:27 +02:00
Files . Clear ( ) ;
2019-06-25 20:56:19 +02:00
Nodes . Clear ( ) ;
2019-06-29 18:33:27 +02:00
GC . SuppressFinalize ( this ) ;
2019-06-25 20:56:19 +02:00
}
IEnumerable < TreeNode > Collect ( TreeNodeCollection nodes )
{
foreach ( TreeNode node in nodes )
{
yield return node ;
bool IsNodeFile = node is IFileFormat ;
if ( ! IsNodeFile )
{
//We don't need to save the child of IFIleFormats
//If opened the file should save it's own children
foreach ( var child in Collect ( node . Nodes ) )
yield return child ;
}
}
}
public byte [ ] Save ( )
{
Console . WriteLine ( "Saving sarc" ) ;
sarcData . Files . Clear ( ) ;
foreach ( TreeNode node in Collect ( Nodes ) )
{
if ( node is SarcEntry )
{
Console . WriteLine ( "Saving " + node ) ;
SaveFileEntryData ( ( SarcEntry ) node ) ;
}
else if ( node is IFileFormat & & node ! = this )
{
IFileFormat fileFormat = ( IFileFormat ) node ;
if ( fileFormat ! = null & & ( ( IFileFormat ) node ) . CanSave )
{
sarcData . Files . Add ( SetSarcPath ( node , this ) ,
STLibraryCompression . CompressFile ( fileFormat . Save ( ) , fileFormat ) ) ;
}
else
{
sarcData . Files . Add ( SetSarcPath ( node , this ) ,
STLibraryCompression . CompressFile ( OpenedFiles [ node . FullPath ] , fileFormat ) ) ;
}
}
}
Tuple < int , byte [ ] > sarc = SARCExt . SARC . PackN ( sarcData ) ;
IFileInfo . Alignment = sarc . Item1 ;
return sarc . Item2 ;
}
public static string SetSarcPath ( TreeNode node , TreeNode sarcNode )
{
string nodePath = node . FullPath ;
int startIndex = nodePath . IndexOf ( sarcNode . Text ) ;
if ( startIndex > 0 )
nodePath = nodePath . Substring ( startIndex ) ;
string slash = Path . DirectorySeparatorChar . ToString ( ) ;
string slashAlt = Path . AltDirectorySeparatorChar . ToString ( ) ;
string SetPath = nodePath . Replace ( sarcNode . Text + slash , string . Empty ) . Replace ( slash ? ? "" , slashAlt ) ;
return ! ( SetPath = = string . Empty ) ? SetPath : node . Text ;
}
private void SaveFileEntryData ( SarcEntry sarc )
{
2019-06-29 03:15:23 +02:00
string dir = Path . GetDirectoryName ( sarc . FileName ) ;
2019-06-25 20:56:19 +02:00
if ( ! sarcData . HashOnly )
{
if ( dir = = string . Empty )
2019-06-29 03:15:23 +02:00
sarc . FileName = sarc . Text ;
2019-06-25 20:56:19 +02:00
else
2019-06-29 03:15:23 +02:00
sarc . FileName = Path . Combine ( dir , sarc . Text ) ;
2019-06-25 20:56:19 +02:00
2019-06-29 03:15:23 +02:00
sarc . FileName = sarc . FileName . Replace ( @"\" , "/" ) ;
2019-06-25 20:56:19 +02:00
}
2019-06-29 03:15:23 +02:00
sarcData . Files . Add ( sarc . FileName , sarc . FileData ) ;
2019-06-25 20:56:19 +02:00
}
public static void ReplaceNode ( TreeNode node , TreeNode replaceNode , TreeNode NewNode )
{
if ( NewNode = = null )
return ;
int index = node . Nodes . IndexOf ( replaceNode ) ;
node . Nodes . RemoveAt ( index ) ;
node . Nodes . Insert ( index , NewNode ) ;
if ( NewNode is TreeNodeFile )
( ( TreeNodeFile ) NewNode ) . OnAfterAdded ( ) ;
}
private void Save ( object sender , EventArgs args )
{
List < IFileFormat > formats = new List < IFileFormat > ( ) ;
formats . Add ( this ) ;
SaveFileDialog sfd = new SaveFileDialog ( ) ;
sfd . Filter = Utils . GetAllFilters ( formats ) ;
sfd . FileName = FileName ;
if ( sfd . ShowDialog ( ) = = DialogResult . OK )
{
STFileSaver . SaveFileFormat ( this , sfd . FileName ) ;
}
}
public static bool SuppressFormDialog = false ;
private void PreviewTextures ( object sender , EventArgs args )
{
SuppressFormDialog = true ;
List < IFileFormat > Formats = new List < IFileFormat > ( ) ;
try
{
CallRecursive ( TreeView , Formats ) ;
}
catch ( Exception ex )
{
Console . WriteLine ( ex . ToString ( ) ) ;
}
ArchiveListPreviewForm editor = new ArchiveListPreviewForm ( ) ;
editor . LoadArchive ( Formats ) ;
editor . Show ( ) ;
SuppressFormDialog = false ;
}
private void CallRecursive ( TreeView treeView , List < IFileFormat > Formats )
{
// Print each node recursively.
TreeNodeCollection nodes = treeView . Nodes ;
foreach ( TreeNode n in nodes )
{
GetNodeFileFormat ( n , Formats ) ;
}
}
private void GetNodeFileFormat ( TreeNode treeNode , List < IFileFormat > Formats )
{
// Print the node.
if ( treeNode is SarcEntry )
{
var format = ( ( SarcEntry ) treeNode ) . OpenFile ( ) ;
if ( format ! = null )
Formats . Add ( format ) ;
}
// Print each node recursively.
foreach ( TreeNode tn in treeNode . Nodes )
{
GetNodeFileFormat ( tn , Formats ) ;
}
}
2019-06-26 02:03:50 +02:00
public class SarcEntry : TreeNodeCustom , IContextMenuNode
2019-06-25 20:56:19 +02:00
{
public SARC sarc ; //Sarc file the entry is located in
2019-06-29 03:15:23 +02:00
public byte [ ] FileData ;
2019-06-25 20:56:19 +02:00
public string sarcHash ;
public SarcEntry ( )
{
ImageKey = "fileBlank" ;
SelectedImageKey = "fileBlank" ;
2019-06-26 02:03:50 +02:00
}
2019-06-25 20:56:19 +02:00
2019-06-26 02:03:50 +02:00
public ToolStripItem [ ] GetContextMenuItems ( )
{
List < ToolStripItem > Items = new List < ToolStripItem > ( ) ;
Items . Add ( new STToolStipMenuItem ( "Export Raw Data" , null , Export , Keys . Control | Keys . E ) ) ;
Items . Add ( new STToolStipMenuItem ( "Export Raw Data to File Location" , null , ExportToFileLoc , Keys . Control | Keys . F ) ) ;
Items . Add ( new STToolStipMenuItem ( "Replace Raw Data" , null , Replace , Keys . Control | Keys . R ) ) ;
Items . Add ( new STToolStripSeparator ( ) ) ;
Items . Add ( new STToolStipMenuItem ( "Open With Text Editor" , null , OpenTextEditor , Keys . Control | Keys . T ) ) ;
Items . Add ( new STToolStripSeparator ( ) ) ;
Items . Add ( new STToolStipMenuItem ( "Remove" , null , Remove , Keys . Control | Keys . Delete ) ) ;
Items . Add ( new STToolStipMenuItem ( "Rename" , null , Rename , Keys . Control | Keys . N ) ) ;
return Items . ToArray ( ) ;
2019-06-25 20:56:19 +02:00
}
2019-06-26 02:03:50 +02:00
2019-06-25 20:56:19 +02:00
public override void OnClick ( TreeView treeView )
{
UpdateHexView ( ) ;
}
private void UpdateHexView ( )
{
HexEditor editor = ( HexEditor ) LibraryGUI . Instance . GetActiveContent ( typeof ( HexEditor ) ) ;
if ( editor = = null )
{
editor = new HexEditor ( ) ;
LibraryGUI . Instance . LoadEditor ( editor ) ;
}
editor . Text = Text ;
editor . Dock = DockStyle . Fill ;
2019-06-29 03:15:23 +02:00
editor . LoadData ( FileData ) ;
2019-06-25 20:56:19 +02:00
}
public IFileFormat OpenFile ( )
{
2019-06-29 03:15:23 +02:00
return STFileLoader . OpenFileFormat ( FileName , FileData , false , true , this ) ;
2019-06-25 20:56:19 +02:00
}
public override void OnDoubleMouseClick ( TreeView treeView )
{
2019-06-29 03:15:23 +02:00
if ( FileData . Length < = 0 )
2019-06-25 20:56:19 +02:00
return ;
IFileFormat file = OpenFile ( ) ;
if ( file = = null ) //File returns null if no supported format is found
2019-06-26 02:03:50 +02:00
return ;
2019-06-25 20:56:19 +02:00
if ( Utils . HasInterface ( file . GetType ( ) , typeof ( IEditor < > ) ) & & ! SuppressFormDialog )
{
OpenFormDialog ( file ) ;
}
else if ( file ! = null )
{
2019-06-29 03:15:23 +02:00
sarc . OpenedFiles . Add ( FullPath , FileData ) ;
2019-06-25 20:56:19 +02:00
ReplaceNode ( this . Parent , this , ( TreeNode ) file ) ;
}
}
private void OpenFormDialog ( IFileFormat fileFormat )
{
UserControl form = GetEditorForm ( fileFormat ) ;
form . Text = ( ( ( IFileFormat ) fileFormat ) . FileName ) ;
var parentForm = LibraryGUI . Instance . GetActiveForm ( ) ;
GenericEditorForm editorForm = new GenericEditorForm ( true , form ) ;
editorForm . FormClosing + = ( sender , e ) = > FormClosing ( sender , e , fileFormat ) ;
if ( editorForm . ShowDialog ( ) = = DialogResult . OK )
{
if ( fileFormat . CanSave )
{
2019-06-29 03:15:23 +02:00
FileData = fileFormat . Save ( ) ;
2019-06-25 20:56:19 +02:00
UpdateHexView ( ) ;
}
}
}
private void FormClosing ( object sender , EventArgs args , IFileFormat fileFormat )
{
if ( ( ( Form ) sender ) . DialogResult ! = DialogResult . OK )
return ;
if ( fileFormat . CanSave )
{
2019-06-29 03:15:23 +02:00
FileData = fileFormat . Save ( ) ;
2019-06-25 20:56:19 +02:00
UpdateHexView ( ) ;
}
}
private UserControl GetEditorForm ( IFileFormat fileFormat )
{
Type objectType = fileFormat . GetType ( ) ;
foreach ( var inter in objectType . GetInterfaces ( ) )
{
if ( inter . IsGenericType & & inter . GetGenericTypeDefinition ( ) = = typeof ( IEditor < > ) )
{
System . Reflection . MethodInfo method = objectType . GetMethod ( "OpenForm" ) ;
return ( UserControl ) method . Invoke ( fileFormat , new object [ 0 ] ) ;
}
}
return null ;
}
private void treeView1_AfterSelect ( object sender , TreeViewEventArgs e )
{
TreeNode node = TreeView . SelectedNode ;
// Determine by checking the Text property.
}
2019-06-29 03:15:23 +02:00
public string FileName ;
2019-06-25 20:56:19 +02:00
private void Replace ( object sender , EventArgs args )
{
OpenFileDialog ofd = new OpenFileDialog ( ) ;
ofd . FileName = Text ;
ofd . DefaultExt = Path . GetExtension ( Text ) ;
ofd . Filter = "Raw Data (*.*)|*.*" ;
if ( ofd . ShowDialog ( ) = = DialogResult . OK )
{
2019-06-29 03:15:23 +02:00
FileData = File . ReadAllBytes ( ofd . FileName ) ;
2019-06-25 20:56:19 +02:00
}
}
private void ExportToFileLoc ( object sender , EventArgs args )
{
Cursor . Current = Cursors . WaitCursor ;
2019-06-29 03:15:23 +02:00
File . WriteAllBytes ( $"{Path.GetDirectoryName(sarc.FilePath)}/{Text}" , FileData ) ;
2019-06-25 20:56:19 +02:00
Cursor . Current = Cursors . Default ;
}
private void Export ( object sender , EventArgs args )
{
SaveFileDialog sfd = new SaveFileDialog ( ) ;
sfd . FileName = Text ;
sfd . DefaultExt = Path . GetExtension ( Text ) ;
sfd . Filter = "Raw Data (*.*)|*.*" ;
if ( sfd . ShowDialog ( ) = = DialogResult . OK )
{
2019-06-29 03:15:23 +02:00
File . WriteAllBytes ( sfd . FileName , FileData ) ;
2019-06-25 20:56:19 +02:00
}
}
private void OpenTextEditor ( object sender , EventArgs args )
{
TextEditor editor = ( TextEditor ) LibraryGUI . Instance . GetActiveContent ( typeof ( TextEditor ) ) ;
if ( editor = = null )
{
editor = new TextEditor ( ) ;
LibraryGUI . Instance . LoadEditor ( editor ) ;
}
editor . Text = Text ;
editor . Dock = DockStyle . Fill ;
2019-06-29 03:15:23 +02:00
editor . FillEditor ( FileData ) ;
2019-06-25 20:56:19 +02:00
}
private void Remove ( object sender , EventArgs args )
{
DialogResult result = MessageBox . Show ( $"Are your sure you want to remove {Text}? This cannot be undone!" , "" , MessageBoxButtons . YesNo , MessageBoxIcon . Question ) ;
if ( result = = DialogResult . Yes )
{
Parent . Nodes . Remove ( this ) ;
}
}
private void Rename ( object sender , EventArgs args )
{
RenameDialog dialog = new RenameDialog ( ) ;
dialog . SetString ( Text ) ;
if ( dialog . ShowDialog ( ) = = DialogResult . OK )
{
Text = dialog . textBox1 . Text ;
}
}
}
void FillTreeNodes ( TreeNode root , Dictionary < string , byte [ ] > files , bool HashOnly )
{
var rootText = root . Text ;
var rootTextLength = rootText . Length ;
var nodeStrings = files ;
foreach ( var node in nodeStrings )
{
string nodeString = node . Key ;
if ( HashOnly )
nodeString = SARCExt . SARC . TryGetNameFromHashTable ( nodeString ) ;
var roots = nodeString . Split ( new char [ ] { '/' } ,
StringSplitOptions . RemoveEmptyEntries ) ;
// The initial parent is the root node
var parentNode = root ;
var sb = new StringBuilder ( rootText , nodeString . Length + rootTextLength ) ;
for ( int rootIndex = 0 ; rootIndex < roots . Length ; rootIndex + + )
{
// Build the node name
var parentName = roots [ rootIndex ] ;
sb . Append ( "/" ) ;
sb . Append ( parentName ) ;
var nodeName = sb . ToString ( ) ;
// Search for the node
var index = parentNode . Nodes . IndexOfKey ( nodeName ) ;
if ( index = = - 1 )
{
// Node was not found, add it
var folder = new FolderEntry ( parentName , 0 , 0 ) ;
if ( rootIndex = = roots . Length - 1 )
{
var file = SetupFileEntry ( node . Value , parentName , node . Key ) ;
file . Name = nodeName ;
parentNode . Nodes . Add ( file ) ;
parentNode = file ;
}
else
{
folder . Name = nodeName ;
parentNode . Nodes . Add ( folder ) ;
parentNode = folder ;
}
}
else
{
// Node was found, set that as parent and continue
parentNode = parentNode . Nodes [ index ] ;
}
}
}
}
List < string > BuildFinalList ( List < string > paths )
{
var finalList = new List < string > ( ) ;
foreach ( var path in paths )
{
bool found = false ;
foreach ( var item in finalList )
{
if ( item . StartsWith ( path , StringComparison . Ordinal ) )
{
found = true ;
break ;
}
}
if ( ! found )
{
finalList . Add ( path ) ;
}
}
return finalList ;
}
public SarcEntry SetupFileEntry ( byte [ ] data , string name , string fullName )
{
SarcEntry sarcEntry = new SarcEntry ( ) ;
2019-06-29 03:15:23 +02:00
sarcEntry . FileName = fullName ;
2019-06-25 20:56:19 +02:00
sarcEntry . Name = name ;
sarcEntry . Text = name ;
sarcEntry . sarc = this ;
2019-06-29 03:15:23 +02:00
sarcEntry . FileData = data ;
2019-06-25 20:56:19 +02:00
2019-06-26 02:03:50 +02:00
Files . Add ( sarcEntry ) ;
2019-06-25 20:56:19 +02:00
string ext = Path . GetExtension ( name ) ;
string SarcEx = SARCExt . SARC . GuessFileExtension ( data ) ;
if ( SarcEx = = ".bfres" | | ext = = ".sbfres" )
{
sarcEntry . ImageKey = "bfres" ;
sarcEntry . SelectedImageKey = "bfres" ;
}
if ( SarcEx = = ".bntx" )
{
sarcEntry . ImageKey = "bntx" ;
sarcEntry . SelectedImageKey = "bntx" ;
}
if ( SarcEx = = ".byaml" )
{
sarcEntry . ImageKey = "byaml" ;
sarcEntry . SelectedImageKey = "byaml" ;
}
if ( SarcEx = = ".aamp" )
{
sarcEntry . ImageKey = "aamp" ;
sarcEntry . SelectedImageKey = "aamp" ;
}
return sarcEntry ;
}
}
2019-06-26 02:03:50 +02:00
}