mirror of
https://codeberg.org/demostf/edit.git
synced 2026-06-03 11:54:07 +02:00
wip
This commit is contained in:
parent
9a103fa13b
commit
8846944bc1
7 changed files with 87 additions and 88 deletions
|
|
@ -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"] }
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
@ -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>,
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
64
src/lib.rs
64
src/lib.rs
|
|
@ -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
46
src/options.rs
Normal 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,
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue