exporter: Allow exporting of multiple swfs
This commit is contained in:
parent
9ab03b4da6
commit
07e4b1a224
|
@ -603,10 +603,12 @@ dependencies = [
|
||||||
"futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"image 0.23.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"image 0.23.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"path-slash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ruffle_core 0.1.0",
|
"ruffle_core 0.1.0",
|
||||||
"ruffle_render_wgpu 0.1.0",
|
"ruffle_render_wgpu 0.1.0",
|
||||||
"sample 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sample 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"structopt 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"structopt 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"wgpu 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"wgpu 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"wgpu-native 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"wgpu-native 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
|
@ -15,6 +15,8 @@ structopt = "0.3.14"
|
||||||
futures = "0.3.4"
|
futures = "0.3.4"
|
||||||
wgpu = "0.5"
|
wgpu = "0.5"
|
||||||
wgpu-native = "0.5"
|
wgpu-native = "0.5"
|
||||||
|
path-slash = "0.1.1"
|
||||||
|
walkdir = "2.3.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
avm_debug = ["ruffle_core/avm_debug"]
|
avm_debug = ["ruffle_core/avm_debug"]
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use futures::executor::block_on;
|
use futures::executor::block_on;
|
||||||
|
use image::RgbaImage;
|
||||||
use ruffle_core::backend::audio::NullAudioBackend;
|
use ruffle_core::backend::audio::NullAudioBackend;
|
||||||
use ruffle_core::backend::input::NullInputBackend;
|
use ruffle_core::backend::input::NullInputBackend;
|
||||||
use ruffle_core::backend::navigator::NullNavigatorBackend;
|
use ruffle_core::backend::navigator::NullNavigatorBackend;
|
||||||
|
@ -6,41 +7,38 @@ use ruffle_core::tag_utils::SwfMovie;
|
||||||
use ruffle_core::Player;
|
use ruffle_core::Player;
|
||||||
use ruffle_render_wgpu::target::TextureTarget;
|
use ruffle_render_wgpu::target::TextureTarget;
|
||||||
use ruffle_render_wgpu::WgpuRenderBackend;
|
use ruffle_render_wgpu::WgpuRenderBackend;
|
||||||
|
use std::error::Error;
|
||||||
use std::fs::create_dir_all;
|
use std::fs::create_dir_all;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
use walkdir::{DirEntry, WalkDir};
|
||||||
|
|
||||||
#[derive(StructOpt, Debug)]
|
#[derive(StructOpt, Debug)]
|
||||||
struct Opt {
|
struct Opt {
|
||||||
/// The swf file to export frames from
|
/// The file or directory of files to export frames from
|
||||||
#[structopt(name = "swf", parse(from_os_str))]
|
#[structopt(name = "swf", parse(from_os_str))]
|
||||||
swf: PathBuf,
|
swf: PathBuf,
|
||||||
|
|
||||||
/// The file or directory (if multiple frames) to store the capture in
|
/// The file or directory (if multiple frames/files) to store the capture in.
|
||||||
|
/// The default value will either be:
|
||||||
|
/// - If given one swf and one frame, the name of the swf + ".png"
|
||||||
|
/// - If given one swf and multiple frames, the name of the swf as a directory
|
||||||
|
/// - If given multiple swfs, this field is required.
|
||||||
#[structopt(name = "output", parse(from_os_str))]
|
#[structopt(name = "output", parse(from_os_str))]
|
||||||
output_path: Option<PathBuf>,
|
output_path: Option<PathBuf>,
|
||||||
|
|
||||||
/// Number of frames to capture
|
/// Number of frames to capture per file
|
||||||
#[structopt(short = "f", long = "frames", default_value = "1")]
|
#[structopt(short = "f", long = "frames", default_value = "1")]
|
||||||
frames: u32,
|
frames: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_screenshot<P: AsRef<Path>>(
|
fn take_screenshot(
|
||||||
swf_path: P,
|
adapter: &wgpu::Adapter,
|
||||||
output: P,
|
swf_path: &Path,
|
||||||
frames: u32,
|
frames: u32,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<Vec<RgbaImage>, Box<dyn std::error::Error>> {
|
||||||
let movie = SwfMovie::from_path(swf_path)?;
|
let movie = SwfMovie::from_path(swf_path)?;
|
||||||
|
|
||||||
let adapter = block_on(wgpu::Adapter::request(
|
|
||||||
&wgpu::RequestAdapterOptions {
|
|
||||||
power_preference: wgpu::PowerPreference::Default,
|
|
||||||
compatible_surface: None,
|
|
||||||
},
|
|
||||||
wgpu::BackendBit::PRIMARY,
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let (device, queue) = block_on(adapter.request_device(&wgpu::DeviceDescriptor {
|
let (device, queue) = block_on(adapter.request_device(&wgpu::DeviceDescriptor {
|
||||||
extensions: wgpu::Extensions {
|
extensions: wgpu::Extensions {
|
||||||
anisotropic_filtering: false,
|
anisotropic_filtering: false,
|
||||||
|
@ -56,6 +54,7 @@ fn take_screenshot<P: AsRef<Path>>(
|
||||||
movie,
|
movie,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let mut result = Vec::new();
|
||||||
for i in 0..frames {
|
for i in 0..frames {
|
||||||
player.lock().unwrap().run_frame();
|
player.lock().unwrap().run_frame();
|
||||||
player.lock().unwrap().render();
|
player.lock().unwrap().render();
|
||||||
|
@ -67,54 +66,155 @@ fn take_screenshot<P: AsRef<Path>>(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let target = renderer.target();
|
let target = renderer.target();
|
||||||
if let Some(image) = target.capture(renderer.device()) {
|
if let Some(image) = target.capture(renderer.device()) {
|
||||||
if frames > 1 {
|
result.push(image);
|
||||||
let mut path = PathBuf::from(output.as_ref());
|
|
||||||
path.push(format!("frame_{}.png", i));
|
|
||||||
image.save(&path)?;
|
|
||||||
} else {
|
} else {
|
||||||
image.save(&output)?;
|
return Err(format!("Unable to capture frame {} of {:?}", i, swf_path).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_files(root: &Path) -> Vec<DirEntry> {
|
||||||
|
let mut results = Vec::new();
|
||||||
|
|
||||||
|
for entry in WalkDir::new(root)
|
||||||
|
.follow_links(true)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|e| e.ok())
|
||||||
|
{
|
||||||
|
let f_name = entry.file_name().to_string_lossy();
|
||||||
|
|
||||||
|
if f_name.ends_with(".swf") {
|
||||||
|
results.push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results
|
||||||
|
}
|
||||||
|
|
||||||
|
fn capture_single_swf(
|
||||||
|
adapter: wgpu::Adapter,
|
||||||
|
swf: &Path,
|
||||||
|
frames: u32,
|
||||||
|
output: Option<PathBuf>,
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
let output = output.unwrap_or_else(|| {
|
||||||
|
let mut result = PathBuf::new();
|
||||||
|
if frames == 1 {
|
||||||
|
result.set_file_name(swf.file_stem().unwrap());
|
||||||
|
result.set_extension("png");
|
||||||
|
} else {
|
||||||
|
result.set_file_name(swf.file_stem().unwrap());
|
||||||
|
}
|
||||||
|
result
|
||||||
|
});
|
||||||
|
|
||||||
|
if frames > 1 {
|
||||||
|
let _ = create_dir_all(&output);
|
||||||
|
}
|
||||||
|
|
||||||
|
let frames = take_screenshot(&adapter, &swf, frames)?;
|
||||||
|
|
||||||
|
if frames.len() == 1 {
|
||||||
|
frames.get(0).unwrap().save(&output)?;
|
||||||
|
println!(
|
||||||
|
"Saved first frame of {} to {}",
|
||||||
|
swf.to_string_lossy(),
|
||||||
|
output.to_string_lossy()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
for (frame, image) in frames.iter().enumerate() {
|
||||||
|
let mut path = PathBuf::from(&output);
|
||||||
|
path.push(format!("{}.png", frame));
|
||||||
|
image.save(&path)?;
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
"Saved first {} frames of {} to {}",
|
||||||
|
frames.len(),
|
||||||
|
swf.to_string_lossy(),
|
||||||
|
output.to_string_lossy()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn capture_multiple_swfs(
|
||||||
let opt: Opt = Opt::from_args();
|
adapter: wgpu::Adapter,
|
||||||
let output = opt.output_path.clone().unwrap_or_else(|| {
|
directory: &Path,
|
||||||
let mut result = PathBuf::new();
|
frames: u32,
|
||||||
if opt.frames == 1 {
|
output: &Path,
|
||||||
result.set_file_name(opt.swf.file_stem().unwrap());
|
) -> Result<(), Box<dyn Error>> {
|
||||||
result.set_extension("png");
|
let files = find_files(directory);
|
||||||
|
|
||||||
|
for file in &files {
|
||||||
|
let frames = take_screenshot(&adapter, &file.path(), frames)?;
|
||||||
|
let mut relative_path = file
|
||||||
|
.path()
|
||||||
|
.strip_prefix(directory)
|
||||||
|
.unwrap_or_else(|_| &file.path())
|
||||||
|
.to_path_buf();
|
||||||
|
|
||||||
|
if frames.len() == 1 {
|
||||||
|
let mut destination = PathBuf::from(output);
|
||||||
|
relative_path.set_extension("png");
|
||||||
|
destination.push(relative_path);
|
||||||
|
if let Some(parent) = destination.parent() {
|
||||||
|
let _ = create_dir_all(parent);
|
||||||
|
}
|
||||||
|
frames.get(0).unwrap().save(&destination)?;
|
||||||
} else {
|
} else {
|
||||||
result.set_file_name(opt.swf.file_stem().unwrap());
|
let mut parent = PathBuf::from(output);
|
||||||
|
relative_path.set_extension("");
|
||||||
|
parent.push(&relative_path);
|
||||||
|
let _ = create_dir_all(&parent);
|
||||||
|
for (frame, image) in frames.iter().enumerate() {
|
||||||
|
let mut destination = parent.clone();
|
||||||
|
destination.push(format!("{}.png", frame));
|
||||||
|
image.save(&destination)?;
|
||||||
}
|
}
|
||||||
result
|
|
||||||
});
|
|
||||||
if opt.frames > 1 {
|
|
||||||
let _ = create_dir_all(&output);
|
|
||||||
}
|
}
|
||||||
match take_screenshot(opt.swf.clone(), output.clone(), opt.frames) {
|
}
|
||||||
Ok(_) => {
|
|
||||||
if opt.frames == 1 {
|
if frames == 1 {
|
||||||
println!(
|
println!(
|
||||||
"Saved first frame of {} to {}",
|
"Saved first frame of {} files to {}",
|
||||||
opt.swf.to_string_lossy(),
|
files.len(),
|
||||||
output.to_string_lossy()
|
output.to_string_lossy()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"Saved first {} frames of {} to {}",
|
"Saved first {} frames of {} files to {}",
|
||||||
opt.frames,
|
frames,
|
||||||
opt.swf.to_string_lossy(),
|
files.len(),
|
||||||
output.to_string_lossy()
|
output.to_string_lossy()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => {
|
Ok(())
|
||||||
println!("Couldn't capture swf: {}", e);
|
}
|
||||||
std::process::exit(1);
|
|
||||||
}
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
}
|
let opt: Opt = Opt::from_args();
|
||||||
|
let adapter = block_on(wgpu::Adapter::request(
|
||||||
|
&wgpu::RequestAdapterOptions {
|
||||||
|
power_preference: wgpu::PowerPreference::Default,
|
||||||
|
compatible_surface: None,
|
||||||
|
},
|
||||||
|
wgpu::BackendBit::PRIMARY,
|
||||||
|
))
|
||||||
|
.ok_or_else(|| {
|
||||||
|
"This tool requires hardware acceleration, but no compatible graphics device was found."
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if opt.swf.is_file() {
|
||||||
|
capture_single_swf(adapter, &opt.swf, opt.frames, opt.output_path)?;
|
||||||
|
} else if let Some(output) = opt.output_path {
|
||||||
|
capture_multiple_swfs(adapter, &opt.swf, opt.frames, &output)?;
|
||||||
|
} else {
|
||||||
|
return Err("Output directory is required when exporting multiple files.".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue