Skip to content

Commit

Permalink
Add command for stripping a specific property from Instances to rbx-u…
Browse files Browse the repository at this point in the history
…til (#426)

When debugging an issue that cropped up on the Roblox devforums
yesterday, I wanted an easy way to remove every instance of property
from a file (specifically, `SurfaceAppearance.Color`). Given that
there's no easy way to do this without converting a file to XML,
removing the property, then converting it back, I think it'd be helpful
to include in rbx-util in the future.
  • Loading branch information
Dekkonot authored Jul 19, 2024
1 parent a542332 commit 3f689f5
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 6 deletions.
8 changes: 6 additions & 2 deletions rbx_util/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# Changelog

## Version 0.2.1

- Added `remove-prop` command to strip a property from a file

## Version 0.2.0

- Refactor commands into seperate files
- Add `verbosity` and `color` global flags to control logging and terminal color

# Version 0.1.0
## Version 0.1.0

- Initial implementation
- Initial implementation
2 changes: 1 addition & 1 deletion rbx_util/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rbx_util"
version = "0.2.0"
version = "0.2.1"
description = "Utilities for working with Roblox model and place files"
license = "MIT"
documentation = "https://docs.rs/rbx_util"
Expand Down
7 changes: 6 additions & 1 deletion rbx_util/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# rbx_util

Command line tool to convert and inspect Roblox model and place files using the rbx-dom family of libraries.

Usage:
Expand All @@ -9,4 +10,8 @@ rbx-util convert input.rbxmx output.rbxm

# Debug the contents of a binary model
rbx-util view-binary output.rbxm
```

# Strip the specified PropertyName from all Instances of ClassName in the provided input.
# Then, write the resulting file the provided output.
rbx-util remove-prop input.rbxmx ClassName PropertyName --output output.rbxm
```
9 changes: 7 additions & 2 deletions rbx_util/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
mod convert;
mod remove_prop;
mod view_binary;

use std::process;
use std::{path::Path, str::FromStr};

use clap::Parser;
use convert::ConvertCommand;

use convert::ConvertCommand;
use remove_prop::RemovePropCommand;
use view_binary::ViewBinaryCommand;

#[derive(Debug, Parser)]
#[clap(name = "rbx_util", about)]
#[clap(name = "rbx_util", about, version)]
struct Options {
#[clap(flatten)]
global: GlobalOptions,
Expand All @@ -23,6 +25,7 @@ impl Options {
match self.subcommand {
Subcommand::ViewBinary(command) => command.run(),
Subcommand::Convert(command) => command.run(),
Subcommand::RemoveProp(command) => command.run(),
}
}
}
Expand All @@ -33,6 +36,8 @@ enum Subcommand {
ViewBinary(ViewBinaryCommand),
/// Convert between the XML and binary formats for places and models.
Convert(ConvertCommand),
/// Removes a specific property from a specific class within a Roblox file.
RemoveProp(RemovePropCommand),
}

#[derive(Debug, Parser, Clone, Copy)]
Expand Down
80 changes: 80 additions & 0 deletions rbx_util/src/remove_prop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use std::{
io::{BufReader, BufWriter},
path::PathBuf,
};

use anyhow::Context as _;
use clap::Parser;
use fs_err::File;

use crate::ModelKind;

#[derive(Debug, Parser)]
pub struct RemovePropCommand {
/// The file to remove the property from.
input: PathBuf,
#[clap(long, short)]
/// The place to write the stripped file to.
output: PathBuf,
/// The class name to remove the property from.
class_name: String,
/// The property to remove from the provided class.
prop_name: String,
}

impl RemovePropCommand {
pub fn run(&self) -> anyhow::Result<()> {
let input_kind = ModelKind::from_path(&self.input)?;
let output_kind = ModelKind::from_path(&self.output)?;

let input_file = BufReader::new(File::open(&self.input)?);

log::debug!("Reading from {input_kind:?} file {}", self.input.display());
let mut dom = match input_kind {
ModelKind::Xml => {
let options = rbx_xml::DecodeOptions::new()
.property_behavior(rbx_xml::DecodePropertyBehavior::ReadUnknown);

rbx_xml::from_reader(input_file, options)
.with_context(|| format!("Failed to read {}", self.input.display()))?
}

ModelKind::Binary => rbx_binary::from_reader(input_file)
.with_context(|| format!("Failed to read {}", self.input.display()))?,
};

let mut queue = vec![dom.root_ref()];
while let Some(referent) = queue.pop() {
let inst = dom.get_by_ref_mut(referent).unwrap();
if inst.class == self.class_name {
log::trace!("Removed property {}.{}", inst.name, self.prop_name);
inst.properties.remove(&self.prop_name);
}
queue.extend_from_slice(inst.children());
}

let output_file = BufWriter::new(File::create(&self.output)?);

let root_ids = dom.root().children();
match output_kind {
ModelKind::Xml => {
let options = rbx_xml::EncodeOptions::new()
.property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown);

rbx_xml::to_writer(output_file, &dom, root_ids, options)
.with_context(|| format!("Failed to write {}", self.output.display()))?;
}

ModelKind::Binary => {
rbx_binary::to_writer(output_file, &dom, root_ids)
.with_context(|| format!("Failed to write {}", self.output.display()))?;
}
}
log::info!(
"Wrote stripped {output_kind:?} file to {}",
self.output.display()
);

Ok(())
}
}

0 comments on commit 3f689f5

Please sign in to comment.