This commit is contained in:
Robin Appelman 2022-10-23 16:06:39 +02:00
commit 8846944bc1
7 changed files with 87 additions and 88 deletions

View file

@ -10,14 +10,16 @@ crate-type = ["cdylib", "rlib"]
[[bin]] [[bin]]
name = "edit" name = "edit"
path = "src/edit.rs" path = "src/edit.rs"
required-features = ["cli"]
[features] [features]
default = ["console_error_panic_hook"] default = ["console_error_panic_hook"]
cli = ["clap"]
[dependencies] [dependencies]
bitbuffer = "0.10.5" bitbuffer = "0.10.5"
#tf-demo-parser = { version = "0.4", git = "https://github.com/demostf/parser" } tf-demo-parser = { version = "0.4", git = "https://github.com/demostf/parser" }
tf-demo-parser = { version = "0.4", path = "../tf-demo-parser" } #tf-demo-parser = { version = "0.4", path = "../tf-demo-parser" }
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] } wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
web-sys = { version = "0.3", features = ["console"] } web-sys = { version = "0.3", features = ["console"] }
@ -33,7 +35,7 @@ console_error_panic_hook = { version = "0.1.6", optional = true }
# #
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. # Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
wee_alloc = { version = "0.4.5", optional = true } wee_alloc = { version = "0.4.5", optional = true }
clap = { version = "3.1.9", features = ["derive"] } clap = { version = "4.0.18", features = ["derive"], optional = true }
num_enum = "0.5.7" num_enum = "0.5.7"
parse-display = "0.5.5" parse-display = "0.5.5"
serde = { version = "1.0.139", features = ["derive"] } serde = { version = "1.0.139", features = ["derive"] }

View file

@ -1,21 +0,0 @@
use clap::Parser;
use democutter::cut;
use std::fs;
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
/// Path to the source demo
path: String,
/// Start tick
start: u32,
/// End tick
end: Option<u32>,
}
fn main() {
let args = Args::parse();
let file = fs::read(&args.path).unwrap();
let output = cut(&file, args.start, args.end.unwrap_or(u32::MAX));
fs::write("out.dem", output).unwrap();
}

View file

@ -1,4 +1,4 @@
use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::collections::{BTreeMap, BTreeSet};
use std::iter::once; use std::iter::once;
use std::mem::{replace, take}; use std::mem::{replace, take};
use std::num::NonZeroU32; use std::num::NonZeroU32;
@ -92,7 +92,7 @@ impl ActiveEntities {
mut self, mut self,
state: &ParserState, state: &ParserState,
delta: ServerTick, delta: ServerTick,
tick: u32, tick: DemoTick,
parser_state: &ParserState, parser_state: &ParserState,
) -> ( ) -> (
impl IntoIterator<Item = PacketEntitiesMessage>, impl IntoIterator<Item = PacketEntitiesMessage>,

View file

@ -16,13 +16,13 @@ use tf_demo_parser::demo::packet::stop::StopPacket;
use tf_demo_parser::demo::packet::{Packet, PacketType}; use tf_demo_parser::demo::packet::{Packet, PacketType};
use tf_demo_parser::demo::parser::{DemoHandler, Encode, NullHandler, RawPacketStream}; use tf_demo_parser::demo::parser::{DemoHandler, Encode, NullHandler, RawPacketStream};
use tf_demo_parser::{Demo, DemoParser, MessageType, ParserState}; use tf_demo_parser::{Demo, DemoParser, MessageType, ParserState};
use tf_demo_parser::demo::data::ServerTick; use tf_demo_parser::demo::data::{DemoTick, ServerTick};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use web_sys::console; use web_sys::console;
use crate::cut::entity::ActiveEntities; use crate::cut::entity::ActiveEntities;
use crate::cut::string_tables::StringTablesUpdates; use crate::cut::string_tables::StringTablesUpdates;
use crate::mutate::MessageMutator; use crate::mutate::MessageMutator;
use crate::{MutatorList, PacketMutator}; use crate::{EditOptions, find_stv, MutatorList, PacketMutator};
const PRESERVE_PACKETS: &[PacketType] = &[ const PRESERVE_PACKETS: &[PacketType] = &[
PacketType::Signon, PacketType::Signon,
@ -31,22 +31,26 @@ const PRESERVE_PACKETS: &[PacketType] = &[
PacketType::SyncTick, PacketType::SyncTick,
]; ];
#[wasm_bindgen] pub fn cut(input: &[u8], options: EditOptions) -> Vec<u8> {
pub fn cut(input: &[u8], start_tick: u32, end_tick: u32) -> Vec<u8> {
let mut out_buffer = Vec::with_capacity(input.len()); let mut out_buffer = Vec::with_capacity(input.len());
{ {
let mut out_stream = BitWriteStream::new(&mut out_buffer, LittleEndian); let mut out_stream = BitWriteStream::new(&mut out_buffer, LittleEndian);
let demo = Demo::new(&input); let demo = Demo::new(&input);
let spectator_id = find_stv(&demo).unwrap_or_else(|| EntityId::from(1u32));
let mut stream = demo.get_stream(); let mut stream = demo.get_stream();
let mut header = Header::read(&mut stream).unwrap(); let mut header = Header::read(&mut stream).unwrap();
let start_tick = min(header.ticks - 10, start_tick); let mut mutators = options.as_mutator(spectator_id);
let end_tick = min(header.ticks, end_tick); let start_tick = options.cut.unwrap().from;
let end_tick = options.cut.unwrap().to;
let start_tick = min(DemoTick::from(header.ticks - 10), start_tick);
let end_tick = min(DemoTick::from(header.ticks), end_tick);
let duration_per_tick = header.ticks as f32 / header.duration; let duration_per_tick = header.ticks as f32 / header.duration;
header.ticks = end_tick - start_tick; header.ticks = (end_tick - start_tick).into();
header.duration = (end_tick - start_tick) as f32 * duration_per_tick; header.duration = header.ticks as f32 * duration_per_tick;
header.write(&mut out_stream).unwrap(); header.write(&mut out_stream).unwrap();
let mut packets = RawPacketStream::new(stream.clone()); let mut packets = RawPacketStream::new(stream.clone());
@ -122,7 +126,6 @@ pub fn cut(input: &[u8], start_tick: u32, end_tick: u32) -> Vec<u8> {
.unwrap(); .unwrap();
} }
let mut mutators = MutatorList::new();
mutators.push_message_mutator(DeleteFilter::new(start_entities, start_state.server_tick)); mutators.push_message_mutator(DeleteFilter::new(start_entities, start_state.server_tick));
mutators.push_packet_mutator(move |packet: &mut Packet| { mutators.push_packet_mutator(move |packet: &mut Packet| {
packet.set_tick(packet.tick() - start_tick) packet.set_tick(packet.tick() - start_tick)
@ -166,7 +169,7 @@ struct StartState<'a> {
fn skip_start<'a>( fn skip_start<'a>(
handler: &mut DemoHandler<'a, NullHandler>, handler: &mut DemoHandler<'a, NullHandler>,
packets: &mut RawPacketStream<'a>, packets: &mut RawPacketStream<'a>,
start_tick: u32, start_tick: DemoTick,
) -> StartState<'a> { ) -> StartState<'a> {
let mut entities = ActiveEntities::default(); let mut entities = ActiveEntities::default();
let mut table_updates = StringTablesUpdates::default(); let mut table_updates = StringTablesUpdates::default();

View file

@ -1,17 +1,20 @@
use clap::Parser; use clap::Parser;
use edit::{EditOptions, rust_edit}; use edit::{edit_inner, EditOptions};
use std::fs; use std::fs;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
struct Args { struct Args {
/// Path to the source demo /// Path to the source demo
path: String, path: String,
#[arg(long)]
unlock_pov: bool,
} }
fn main() { fn main() {
let args = Args::parse(); let args = Args::parse();
let file = fs::read(&args.path).unwrap(); let file = fs::read(&args.path).unwrap();
let output = rust_edit(&file, EditOptions::default()); let output = edit_inner(&file, EditOptions::default());
fs::write("out.dem", output).unwrap(); fs::write("out.dem", output).unwrap();
} }

View file

@ -3,6 +3,7 @@ mod pov;
mod clean; mod clean;
mod cond; mod cond;
mod cut; mod cut;
mod options;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use tf_demo_parser::{Demo, DemoParser}; use tf_demo_parser::{Demo, DemoParser};
@ -16,7 +17,9 @@ use bitbuffer::BitWrite;
use tf_demo_parser::demo::data::DemoTick; use tf_demo_parser::demo::data::DemoTick;
use crate::clean::clean_demo; use crate::clean::clean_demo;
use crate::cond::strip_cond; use crate::cond::strip_cond;
use crate::cut::cut;
use crate::mutate::{MutatorList, PacketMutator}; use crate::mutate::{MutatorList, PacketMutator};
pub use crate::options::EditOptions;
use crate::pov::unlock_pov; use crate::pov::unlock_pov;
extern crate web_sys; extern crate web_sys;
@ -28,50 +31,32 @@ extern crate web_sys;
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
fn set_panic_hook() { fn set_panic_hook() {
// When the `console_error_panic_hook` feature is enabled, we can call the
// `set_panic_hook` function at least once during initialization, and then
// we will get better error messages if our code ever panics.
//
// For more details see
// https://github.com/rustwasm/console_error_panic_hook#readme
#[cfg(feature = "console_error_panic_hook")] #[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
} }
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct EditOptions {
pub unlock_pov: bool,
pub remove_conditions: Vec<CondOptions>,
#[serde(default)]
pub cut: Option<TickRange>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CondOptions {
entity: EntityId,
mask: u32,
}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct TickRange {
from: DemoTick,
to: DemoTick,
}
#[wasm_bindgen] #[wasm_bindgen]
pub fn edit(input: &[u8], options: JsValue) -> Vec<u8> { pub fn edit(input: &[u8], options: JsValue) -> Vec<u8> {
set_panic_hook(); set_panic_hook();
let options: EditOptions = options.into_serde().expect("invalid options"); let options: EditOptions = options.into_serde().expect("invalid options");
rust_edit(input, options) edit_inner(input, options)
} }
pub fn rust_edit(input: &[u8], options: EditOptions) -> Vec<u8> { pub fn edit_inner(input: &[u8], options: EditOptions) -> Vec<u8> {
if options.cut.is_some() {
cut(input, options)
} else {
no_cut(input, options)
}
}
fn no_cut(input: &[u8], options: EditOptions) -> Vec<u8> {
let mut out_buffer = Vec::with_capacity(input.len()); let mut out_buffer = Vec::with_capacity(input.len());
{ {
let mut out_stream = BitWriteStream::new(&mut out_buffer, LittleEndian); let mut out_stream = BitWriteStream::new(&mut out_buffer, LittleEndian);
let demo = Demo::new(&input); let demo = Demo::new(&input);
let spectator_id = find_stv(&demo).expect("no stv bot found"); let spectator_id = find_stv(&demo).unwrap_or_else(|| EntityId::from(1u32));
let mut stream = demo.get_stream(); let mut stream = demo.get_stream();
let header = Header::read(&mut stream).unwrap(); let header = Header::read(&mut stream).unwrap();
@ -81,26 +66,7 @@ pub fn rust_edit(input: &[u8], options: EditOptions) -> Vec<u8> {
let mut handler = DemoHandler::default(); let mut handler = DemoHandler::default();
handler.handle_header(&header); handler.handle_header(&header);
let mut mutators = MutatorList::new(); let mutators = options.as_mutator(spectator_id);
clean_demo(&mut mutators);
for cond_options in options.remove_conditions {
let entity = if cond_options.entity > 0 {
Some(EntityId::from(cond_options.entity))
} else {
None
};
strip_cond(&mut mutators, entity, cond_options.mask);
}
if options.unlock_pov {
unlock_pov(&mut mutators, spectator_id);
}
if let Some(cut) = options.cut {
}
while let Some(mut packet) = packets.next(&handler.state_handler).unwrap() { while let Some(mut packet) = packets.next(&handler.state_handler).unwrap() {
mutators.mutate_packet(&mut packet, &handler.state_handler); mutators.mutate_packet(&mut packet, &handler.state_handler);

46
src/options.rs Normal file
View file

@ -0,0 +1,46 @@
use tf_demo_parser::demo::data::DemoTick;
use tf_demo_parser::demo::message::packetentities::EntityId;
use crate::{clean_demo, MutatorList, strip_cond, unlock_pov};
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct EditOptions {
pub unlock_pov: bool,
pub remove_conditions: Vec<CondOptions>,
#[serde(default)]
pub cut: Option<TickRange>,
}
impl EditOptions {
pub fn as_mutator(&self, spectator_id: EntityId) -> MutatorList {
let mut mutators = MutatorList::new();
clean_demo(&mut mutators);
for cond_options in self.remove_conditions.iter() {
let entity = if cond_options.entity > 0 {
Some(EntityId::from(cond_options.entity))
} else {
None
};
strip_cond(&mut mutators, entity, cond_options.mask);
}
if self.unlock_pov {
unlock_pov(&mut mutators, spectator_id);
}
mutators
}
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
pub struct CondOptions {
entity: EntityId,
mask: u32,
}
#[derive(Debug, Serialize, Deserialize, Default, Copy, Clone)]
pub struct TickRange {
pub from: DemoTick,
pub to: DemoTick,
}