1717// DEALINGS IN THE SOFTWARE.
1818
1919using System ;
20+ using System . Collections . Generic ;
2021using System . Composition ;
2122using System . Diagnostics ;
2223using System . IO ;
@@ -45,7 +46,7 @@ sealed class ExtractPackageEntryContextMenuEntry(DockWorkspace dockWorkspace) :
4546 {
4647 public void Execute ( TextViewContext context )
4748 {
48- var selectedNodes = Array . FindAll ( context . SelectedTreeNodes , x => x is AssemblyTreeNode { PackageEntry : { } } or PackageFolderTreeNode ) ;
49+ var selectedNodes = Array . FindAll ( context . SelectedTreeNodes , IsBundleItem ) ;
4950 // Get root assembly to infer the initial directory for the save dialog.
5051 var bundleNode = selectedNodes . FirstOrDefault ( ) ? . Ancestors ( ) . OfType < AssemblyTreeNode > ( )
5152 . FirstOrDefault ( asm => asm . PackageEntry == null ) ;
@@ -57,21 +58,17 @@ public void Execute(TextViewContext context)
5758 dlg . FileName = Path . GetFileName ( WholeProjectDecompiler . SanitizeFileName ( assembly . Name ) ) ;
5859 dlg . Filter = ".NET assemblies|*.dll;*.exe;*.winmd" + Resources . AllFiles ;
5960 dlg . InitialDirectory = Path . GetDirectoryName ( bundleNode . LoadedAssembly . FileName ) ;
60- if ( dlg . ShowDialog ( ) != true )
61- return ;
62-
63- string fileName = dlg . FileName ;
64- dockWorkspace . RunWithCancellation ( ct => Task < AvalonEditTextOutput > . Factory . StartNew ( ( ) => {
65- AvalonEditTextOutput output = new AvalonEditTextOutput ( ) ;
66- Stopwatch stopwatch = Stopwatch . StartNew ( ) ;
67- SaveEntry ( output , assembly , fileName ) ;
68- stopwatch . Stop ( ) ;
69- output . WriteLine ( Resources . GenerationCompleteInSeconds , stopwatch . Elapsed . TotalSeconds . ToString ( "F1" ) ) ;
70- output . WriteLine ( ) ;
71- output . AddButton ( null , Resources . OpenExplorer , delegate { Process . Start ( "explorer" , "/select,\" " + fileName + "\" " ) ; } ) ;
72- output . WriteLine ( ) ;
73- return output ;
74- } , ct ) ) . Then ( dockWorkspace . ShowText ) . HandleExceptions ( ) ;
61+ if ( dlg . ShowDialog ( ) == true )
62+ Save ( dockWorkspace , selectedNodes , dlg . FileName , true ) ;
63+ }
64+ else if ( selectedNodes is [ ResourceTreeNode { Resource : { } resource } ] )
65+ {
66+ SaveFileDialog dlg = new SaveFileDialog ( ) ;
67+ dlg . FileName = Path . GetFileName ( WholeProjectDecompiler . SanitizeFileName ( resource . Name ) ) ;
68+ dlg . Filter = Resources . AllFiles [ 1 ..] ;
69+ dlg . InitialDirectory = Path . GetDirectoryName ( bundleNode . LoadedAssembly . FileName ) ;
70+ if ( dlg . ShowDialog ( ) == true )
71+ Save ( dockWorkspace , selectedNodes , dlg . FileName , true ) ;
7572 }
7673 else
7774 {
@@ -91,40 +88,7 @@ public void Execute(TextViewContext context)
9188 return ;
9289 }
9390
94- dockWorkspace . RunWithCancellation ( ct => Task < AvalonEditTextOutput > . Factory . StartNew ( ( ) => {
95- AvalonEditTextOutput output = new AvalonEditTextOutput ( ) ;
96- Stopwatch stopwatch = Stopwatch . StartNew ( ) ;
97- foreach ( var selectedNode in selectedNodes )
98- {
99- if ( selectedNode is AssemblyTreeNode { PackageEntry : { } assembly } )
100- {
101- string fileName = Path . Combine ( folderName , GetFullPath ( selectedNode . Parent ) + WholeProjectDecompiler . SanitizeFileName ( assembly . Name ) ) ;
102- SaveEntry ( output , assembly , fileName ) ;
103- }
104- else if ( selectedNode is PackageFolderTreeNode )
105- {
106- selectedNode . EnsureLazyChildren ( ) ;
107- foreach ( var node in selectedNode . DescendantsAndSelf ( ) )
108- {
109- if ( node is AssemblyTreeNode { PackageEntry : { } asm } )
110- {
111- string fileName = Path . Combine ( folderName , GetFullPath ( node . Parent ) + WholeProjectDecompiler . SanitizeFileName ( asm . Name ) ) ;
112- SaveEntry ( output , asm , fileName ) ;
113- }
114- else if ( node is PackageFolderTreeNode )
115- {
116- Directory . CreateDirectory ( Path . Combine ( folderName , GetFullPath ( node ) ) ) ;
117- }
118- }
119- }
120- }
121- stopwatch . Stop ( ) ;
122- output . WriteLine ( Resources . GenerationCompleteInSeconds , stopwatch . Elapsed . TotalSeconds . ToString ( "F1" ) ) ;
123- output . WriteLine ( ) ;
124- output . AddButton ( null , Resources . OpenExplorer , delegate { Process . Start ( "explorer" , "\" " + folderName + "\" " ) ; } ) ;
125- output . WriteLine ( ) ;
126- return output ;
127- } , ct ) ) . Then ( dockWorkspace . ShowText ) . HandleExceptions ( ) ;
91+ Save ( dockWorkspace , selectedNodes , folderName , false ) ;
12892 }
12993 }
13094
@@ -140,7 +104,55 @@ static string GetFullPath(SharpTreeNode node)
140104 return null ;
141105 }
142106
143- void SaveEntry ( ITextOutput output , PackageEntry entry , string targetFileName )
107+ internal static void Save ( DockWorkspace dockWorkspace , IEnumerable < SharpTreeNode > nodes , string path , bool isFile )
108+ {
109+ dockWorkspace . RunWithCancellation ( ct => Task < AvalonEditTextOutput > . Factory . StartNew ( ( ) => {
110+ AvalonEditTextOutput output = new AvalonEditTextOutput ( ) ;
111+ Stopwatch stopwatch = Stopwatch . StartNew ( ) ;
112+ foreach ( var node in nodes )
113+ {
114+ if ( node is AssemblyTreeNode { PackageEntry : { } assembly } )
115+ {
116+ string fileName = isFile ? path : Path . Combine ( path , GetFullPath ( node . Parent ) + WholeProjectDecompiler . SanitizeFileName ( assembly . Name ) ) ;
117+ SaveEntry ( output , assembly , fileName ) ;
118+ }
119+ else if ( node is ResourceTreeNode { Resource : PackageEntry { } resource } )
120+ {
121+ string fileName = isFile ? path : Path . Combine ( path , GetFullPath ( node . Parent ) + WholeProjectDecompiler . SanitizeFileName ( resource . Name ) ) ;
122+ SaveEntry ( output , resource , fileName ) ;
123+ }
124+ else if ( node is PackageFolderTreeNode )
125+ {
126+ node . EnsureLazyChildren ( ) ;
127+ foreach ( var item in node . DescendantsAndSelf ( ) )
128+ {
129+ if ( item is AssemblyTreeNode { PackageEntry : { } asm } )
130+ {
131+ string fileName = Path . Combine ( path , GetFullPath ( item . Parent ) + WholeProjectDecompiler . SanitizeFileName ( asm . Name ) ) ;
132+ SaveEntry ( output , asm , fileName ) ;
133+ }
134+ else if ( item is ResourceTreeNode { Resource : PackageEntry { } entry } )
135+ {
136+ string fileName = Path . Combine ( path , GetFullPath ( item . Parent ) + WholeProjectDecompiler . SanitizeFileName ( entry . Name ) ) ;
137+ SaveEntry ( output , entry , fileName ) ;
138+ }
139+ else if ( item is PackageFolderTreeNode )
140+ {
141+ Directory . CreateDirectory ( Path . Combine ( path , GetFullPath ( item ) ) ) ;
142+ }
143+ }
144+ }
145+ }
146+ stopwatch . Stop ( ) ;
147+ output . WriteLine ( Resources . GenerationCompleteInSeconds , stopwatch . Elapsed . TotalSeconds . ToString ( "F1" ) ) ;
148+ output . WriteLine ( ) ;
149+ output . AddButton ( null , Resources . OpenExplorer , delegate { Process . Start ( "explorer" , isFile ? $ "/select,\" { path } \" " : $ "\" { path } \" ") ; } ) ;
150+ output . WriteLine ( ) ;
151+ return output ;
152+ } , ct ) ) . Then ( dockWorkspace . ShowText ) . HandleExceptions ( ) ;
153+ }
154+
155+ static void SaveEntry ( ITextOutput output , PackageEntry entry , string targetFileName )
144156 {
145157 output . Write ( entry . Name + ": " ) ;
146158 using Stream stream = entry . TryOpenStream ( ) ;
@@ -158,8 +170,16 @@ void SaveEntry(ITextOutput output, PackageEntry entry, string targetFileName)
158170
159171 public bool IsEnabled ( TextViewContext context ) => true ;
160172
161- public bool IsVisible ( TextViewContext context ) =>
162- context . SelectedTreeNodes ? . Any ( x => x is AssemblyTreeNode { PackageEntry : { } } or PackageFolderTreeNode ) == true ;
173+ public bool IsVisible ( TextViewContext context ) => context . SelectedTreeNodes ? . Any ( IsBundleItem ) == true ;
174+
175+ static bool IsBundleItem ( SharpTreeNode node )
176+ {
177+ if ( node is AssemblyTreeNode { PackageEntry : { } } or PackageFolderTreeNode )
178+ return true ;
179+ if ( node is ResourceTreeNode { Resource : PackageEntry { } resource } && resource . FullName . StartsWith ( "bundle://" ) )
180+ return true ;
181+ return false ;
182+ }
163183 }
164184
165185 [ ExportContextMenuEntry ( Header = nameof ( Resources . ExtractAllPackageEntries ) , Category = nameof ( Resources . Save ) , Icon = "Images/Save" ) ]
@@ -186,57 +206,8 @@ public void Execute(TextViewContext context)
186206 return ;
187207 }
188208
189- dockWorkspace . RunWithCancellation ( ct => Task < AvalonEditTextOutput > . Factory . StartNew ( ( ) => {
190- AvalonEditTextOutput output = new AvalonEditTextOutput ( ) ;
191- Stopwatch stopwatch = Stopwatch . StartNew ( ) ;
192- asm . EnsureLazyChildren ( ) ;
193- foreach ( var node in asm . Descendants ( ) )
194- {
195- if ( node is AssemblyTreeNode { PackageEntry : { } assembly } )
196- {
197- string fileName = Path . Combine ( folderName , GetFullPath ( node . Parent ) + WholeProjectDecompiler . SanitizeFileName ( assembly . Name ) ) ;
198- SaveEntry ( output , assembly , fileName ) ;
199- }
200- else if ( node is PackageFolderTreeNode )
201- {
202- Directory . CreateDirectory ( Path . Combine ( folderName , GetFullPath ( node ) ) ) ;
203- }
204- }
205- stopwatch . Stop ( ) ;
206- output . WriteLine ( Resources . GenerationCompleteInSeconds , stopwatch . Elapsed . TotalSeconds . ToString ( "F1" ) ) ;
207- output . WriteLine ( ) ;
208- output . AddButton ( null , Resources . OpenExplorer , delegate { Process . Start ( "explorer" , "\" " + folderName + "\" " ) ; } ) ;
209- output . WriteLine ( ) ;
210- return output ;
211- } , ct ) ) . Then ( dockWorkspace . ShowText ) . HandleExceptions ( ) ;
212- }
213-
214- static string GetFullPath ( SharpTreeNode node )
215- {
216- if ( node is PackageFolderTreeNode )
217- {
218- string name = node . Text + "\\ " ;
219- if ( GetFullPath ( node . Parent ) is string parent )
220- return parent + "\\ " + name ;
221- return name ;
222- }
223- return null ;
224- }
225-
226- void SaveEntry ( ITextOutput output , PackageEntry entry , string targetFileName )
227- {
228- output . Write ( entry . Name + ": " ) ;
229- using Stream stream = entry . TryOpenStream ( ) ;
230- if ( stream == null )
231- {
232- output . WriteLine ( "Could not open stream!" ) ;
233- return ;
234- }
235-
236- stream . Position = 0 ;
237- using FileStream fileStream = new FileStream ( targetFileName , FileMode . OpenOrCreate ) ;
238- stream . CopyTo ( fileStream ) ;
239- output . WriteLine ( "Written to " + targetFileName ) ;
209+ asm . EnsureLazyChildren ( ) ;
210+ ExtractPackageEntryContextMenuEntry . Save ( dockWorkspace , asm . Descendants ( ) , folderName , false ) ;
240211 }
241212
242213 public bool IsEnabled ( TextViewContext context ) => true ;
0 commit comments