@@ -15,8 +15,10 @@ use ruffle_render_wgpu::wgpu;
1515use std:: any:: Any ;
1616use std:: fs:: create_dir_all;
1717use std:: io:: { self , Write } ;
18+ use std:: num:: NonZeroUsize ;
1819use std:: panic:: catch_unwind;
1920use std:: path:: { Path , PathBuf } ;
21+ use std:: str:: FromStr ;
2022use std:: sync:: Arc ;
2123use std:: sync:: Mutex ;
2224use walkdir:: { DirEntry , WalkDir } ;
@@ -36,6 +38,29 @@ pub struct SizeOpt {
3638 height : Option < u32 > ,
3739}
3840
41+ #[ derive( Debug , Clone , Copy ) ]
42+ enum FrameSelection {
43+ All ,
44+ Count ( NonZeroUsize ) ,
45+ }
46+
47+ impl FromStr for FrameSelection {
48+ type Err = String ;
49+
50+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
51+ let s_lower = s. to_ascii_lowercase ( ) ;
52+ if s_lower == "all" {
53+ Ok ( FrameSelection :: All )
54+ } else if let Ok ( n) = s. parse :: < u32 > ( ) {
55+ let non_zero = NonZeroUsize :: new ( n as usize )
56+ . ok_or_else ( || "Frame count must be greater than 0" . to_string ( ) ) ?;
57+ Ok ( FrameSelection :: Count ( non_zero) )
58+ } else {
59+ Err ( format ! ( "Invalid value for --frames: {s}" ) )
60+ }
61+ }
62+ }
63+
3964#[ derive( Parser , Debug ) ]
4065#[ clap( name = "Ruffle Exporter" , author, version) ]
4166pub struct Opt {
@@ -51,9 +76,9 @@ pub struct Opt {
5176 #[ clap( name = "output" ) ]
5277 output_path : Option < PathBuf > ,
5378
54- /// Number of frames to capture per file
79+ /// Number of frames to capture per file. Use 'all' to capture all frames.
5580 #[ clap( short = 'f' , long = "frames" , default_value = "1" ) ]
56- frames : u32 ,
81+ frames : FrameSelection ,
5782
5883 /// Number of frames to skip
5984 #[ clap( long = "skipframes" , default_value = "0" ) ]
@@ -91,7 +116,7 @@ pub struct Opt {
91116fn take_screenshot (
92117 descriptors : Arc < Descriptors > ,
93118 swf_path : & Path ,
94- frames : u32 ,
119+ frames : FrameSelection , // TODO Figure out a way to get framecount before calling take_screenshot, so that we can have accurate progress bars when using --frames all
95120 skipframes : u32 ,
96121 progress : & Option < ProgressBar > ,
97122 size : SizeOpt ,
@@ -122,7 +147,15 @@ fn take_screenshot(
122147 . build ( ) ;
123148
124149 let mut result = Vec :: new ( ) ;
125- let totalframes = frames + skipframes;
150+ let totalframes = match frames {
151+ FrameSelection :: All => player. lock ( ) . unwrap ( ) . mutate_with_update_context ( |ctx| {
152+ ctx. stage
153+ . root_clip ( )
154+ . and_then ( |root_clip| root_clip. as_movie_clip ( ) )
155+ . map_or ( 1 , |movie_clip| movie_clip. total_frames ( ) as u32 )
156+ } ) ,
157+ FrameSelection :: Count ( n) => n. get ( ) as u32 + skipframes,
158+ } ;
126159
127160 for i in 0 ..totalframes {
128161 if let Some ( progress) = & progress {
@@ -164,8 +197,10 @@ fn take_screenshot(
164197 }
165198 }
166199
167- if let Some ( progress) = & progress {
168- progress. inc ( 1 ) ;
200+ if !matches ! ( frames, FrameSelection :: All ) {
201+ if let Some ( progress) = & progress {
202+ progress. inc ( 1 ) ;
203+ }
169204 }
170205 }
171206 Ok ( result)
@@ -225,18 +260,21 @@ fn capture_single_swf(descriptors: Arc<Descriptors>, opt: &Opt) -> Result<()> {
225260 let output = opt. output_path . clone ( ) . unwrap_or_else ( || {
226261 let mut result = PathBuf :: new ( ) ;
227262 result. set_file_name ( opt. swf . file_stem ( ) . unwrap ( ) ) ;
228- if opt. frames == 1 {
263+ if matches ! ( opt. frames, FrameSelection :: Count ( n ) if n . get ( ) == 1 ) {
229264 result. set_extension ( "png" ) ;
230265 }
231266 result
232267 } ) ;
233268
234- if opt. frames > 1 {
269+ if ! matches ! ( opt. frames, FrameSelection :: Count ( n ) if n . get ( ) == 1 ) {
235270 let _ = create_dir_all ( & output) ;
236271 }
237272
238273 let progress = if !opt. silent {
239- let progress = ProgressBar :: new ( opt. frames as u64 ) ;
274+ let progress = match opt. frames {
275+ FrameSelection :: Count ( n) => ProgressBar :: new ( n. get ( ) as u64 ) ,
276+ _ => ProgressBar :: new_spinner ( ) , // TODO Once we figure out a way to get framecount before calling take_screenshot, then this can be changed back to a progress bar when using --frames all
277+ } ;
240278 progress. set_style (
241279 ProgressStyle :: with_template (
242280 "[{elapsed_precise}] {bar:40.cyan/blue} [{eta_precise}] {pos:>7}/{len:7} {msg}" ,
@@ -320,7 +358,10 @@ fn capture_multiple_swfs(descriptors: Arc<Descriptors>, opt: &Opt) -> Result<()>
320358 let files = find_files ( & opt. swf , !opt. silent ) ;
321359
322360 let progress = if !opt. silent {
323- let progress = ProgressBar :: new ( ( files. len ( ) as u64 ) * ( opt. frames as u64 ) ) ;
361+ let progress = match opt. frames {
362+ FrameSelection :: Count ( n) => ProgressBar :: new ( ( files. len ( ) as u64 ) * ( n. get ( ) as u64 ) ) ,
363+ _ => ProgressBar :: new ( files. len ( ) as u64 ) ,
364+ } ;
324365 progress. set_style (
325366 ProgressStyle :: with_template (
326367 "[{elapsed_precise}] {bar:40.cyan/blue} [{eta_precise}] {pos:>7}/{len:7} {msg}" ,
@@ -383,19 +424,23 @@ fn capture_multiple_swfs(descriptors: Arc<Descriptors>, opt: &Opt) -> Result<()>
383424 Ok ( ( ) )
384425 } ) ?;
385426
386- let message = if opt. frames == 1 {
387- format ! (
427+ let message = match opt. frames {
428+ FrameSelection :: Count ( n ) if n . get ( ) == 1 => format ! (
388429 "Saved first frame of {} files to {}" ,
389430 files. len( ) ,
390431 output. to_string_lossy( )
391- )
392- } else {
393- format ! (
432+ ) ,
433+ FrameSelection :: All => format ! (
434+ "Saved all frames of {} files to {}" ,
435+ files. len( ) ,
436+ output. to_string_lossy( )
437+ ) ,
438+ FrameSelection :: Count ( n) => format ! (
394439 "Saved first {} frames of {} files to {}" ,
395- opt . frames ,
440+ n ,
396441 files. len( ) ,
397442 output. to_string_lossy( )
398- )
443+ ) ,
399444 } ;
400445
401446 if let Some ( progress) = progress {
0 commit comments