├── common ├── generate │ ├── __init__.py │ └── types.py ├── src │ ├── lib.rs │ ├── num │ │ └── mod.rs │ ├── vec.rs │ ├── slice.rs │ ├── map_iter.rs │ ├── macros.rs │ ├── takeable.rs │ ├── str.rs │ ├── bytes.rs │ ├── io.rs │ └── pretty.rs ├── Cargo.toml └── benches │ └── cast.rs ├── uniffi ├── .gitignore ├── Cargo.toml ├── huffman │ ├── build.rs │ ├── src │ │ ├── libtw2_huffman.udl │ │ └── lib.rs │ ├── pyproject.toml │ ├── Cargo.toml │ └── README.md └── tests │ └── huffman.py ├── logger ├── src │ └── lib.rs └── Cargo.toml ├── server ├── dm1.map └── Cargo.toml ├── serverbrowse ├── src │ └── lib.rs ├── IDEAS └── Cargo.toml ├── stats-browser ├── WISHLIST ├── src │ ├── lib.rs │ ├── lookup.rs │ ├── base64.rs │ ├── hashmap_ext.rs │ ├── config.rs │ ├── vec_map.rs │ └── socket.rs └── Cargo.toml ├── _old ├── common_tw.py ├── assumptions ├── compression.h ├── setup.py ├── common.c ├── main.py ├── compression.c ├── io.h ├── gamemap.py ├── datafile.h ├── datafile_format.h ├── error.h ├── common.h ├── error_inlines.h ├── gamemap.c ├── common_inlines.h ├── datafile_raw.h ├── datafile_raw.pxd ├── io.c ├── gamemap.h ├── error.c ├── datafile.py └── main.c ├── gamenet ├── common │ ├── src │ │ ├── lib.rs │ │ ├── debug.rs │ │ ├── error.rs │ │ ├── snap_obj.rs │ │ └── traits.rs │ └── Cargo.toml ├── generate │ ├── snap_obj.py │ └── generate.py ├── ddnet │ ├── src │ │ ├── lib.rs │ │ ├── msg │ │ │ └── mod.rs │ │ └── traits.rs │ └── Cargo.toml ├── teeworlds-0.5 │ ├── src │ │ ├── lib.rs │ │ ├── msg │ │ │ └── mod.rs │ │ └── traits.rs │ └── Cargo.toml ├── teeworlds-0.6 │ ├── src │ │ ├── lib.rs │ │ ├── msg │ │ │ └── mod.rs │ │ └── traits.rs │ └── Cargo.toml ├── teeworlds-0.7 │ ├── src │ │ ├── lib.rs │ │ ├── msg │ │ │ └── mod.rs │ │ └── traits.rs │ └── Cargo.toml ├── spec │ └── Cargo.toml ├── snap │ └── Cargo.toml └── generate_all ├── huffman ├── src │ ├── instances │ │ └── mod.rs │ └── main.rs ├── reference │ ├── sys │ │ ├── src │ │ │ ├── include │ │ │ │ └── base │ │ │ │ │ └── system.h │ │ │ ├── api.h │ │ │ ├── lib.rs │ │ │ ├── api.cpp │ │ │ └── teeworlds │ │ │ │ └── huffman.h │ │ ├── build.rs │ │ └── Cargo.toml │ └── Cargo.toml ├── Cargo.toml └── data │ └── frequencies ├── polyfill-1-63 ├── src │ ├── lib.rs │ └── option.rs └── Cargo.toml ├── tools ├── src │ ├── lib.rs │ ├── warn_stdout.rs │ ├── warn_stderr.rs │ ├── bin │ │ ├── basic.rs │ │ ├── datafile.rs │ │ ├── ok.rs │ │ ├── settings.rs │ │ ├── test_gamenet_spec.rs │ │ ├── versions.rs │ │ ├── int_decode.rs │ │ ├── servercount.rs │ │ ├── serverlist.rs │ │ ├── gamelayers.rs │ │ ├── serverinfo.rs │ │ ├── entities.rs │ │ ├── jungle.rs │ │ ├── integer.rs │ │ ├── author.rs │ │ ├── tune_layer.rs │ │ ├── entities_flags.rs │ │ ├── ddnet_emptytele.rs │ │ ├── serverinfo7.rs │ │ ├── servercount7.rs │ │ ├── serverlist7.rs │ │ └── teehistorian_dump.rs │ ├── client.rs │ └── unhexdump.rs └── Cargo.toml ├── map ├── src │ ├── lib.rs │ └── structs.rs └── Cargo.toml ├── net ├── src │ ├── collections │ │ ├── mod.rs │ │ └── peer_set.rs │ └── lib.rs └── Cargo.toml ├── .gitignore ├── CONTRIBUTING.md ├── wireshark-dissector ├── sys │ ├── bindgen.h │ ├── Cargo.toml │ └── generate ├── build.rs ├── README.md ├── Cargo.toml └── src │ └── format.rs ├── httphook ├── src │ ├── main.rs │ ├── runtime.rs │ └── json.rs └── Cargo.toml ├── demo ├── src │ ├── ddnet.rs │ └── lib.rs └── Cargo.toml ├── zlib-minimal └── Cargo.toml ├── generate_all ├── teehistorian ├── src │ ├── lib.rs │ ├── bitmagic.rs │ └── file.rs └── Cargo.toml ├── world └── Cargo.toml ├── httphook-ldpreload ├── Cargo.toml ├── src │ └── leaky_vec.rs └── README.md ├── datafile ├── src │ ├── lib.rs │ └── writer.rs └── Cargo.toml ├── packer └── Cargo.toml ├── socket └── Cargo.toml ├── snapshot ├── src │ └── lib.rs ├── Cargo.toml └── tests │ └── correctness.rs ├── event-loop └── Cargo.toml ├── render-map └── Cargo.toml ├── doc ├── connection.md ├── quirks.md ├── uncompressed_snap.ksy ├── packet.md ├── packet7.md ├── int.md ├── datafile_v4.ksy ├── tee_rendering.md └── int.ksy ├── register └── Cargo.toml ├── downloader └── Cargo.toml ├── Cargo.toml ├── LICENSE-MIT └── .github └── workflows └── build.yaml /common/generate/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /uniffi/.gitignore: -------------------------------------------------------------------------------- 1 | */*dist* 2 | -------------------------------------------------------------------------------- /logger/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn init() { 2 | env_logger::init().unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /server/dm1.map: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heinrich5991/libtw2/HEAD/server/dm1.map -------------------------------------------------------------------------------- /serverbrowse/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | pub mod protocol; 5 | -------------------------------------------------------------------------------- /uniffi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "huffman", 5 | ] 6 | -------------------------------------------------------------------------------- /stats-browser/WISHLIST: -------------------------------------------------------------------------------- 1 | Correlate server joins with refresh times. 2 | Load maps from servers. 3 | -------------------------------------------------------------------------------- /_old/common_tw.py: -------------------------------------------------------------------------------- 1 | from binascii import crc32 2 | 3 | def crc32tw(data, crc=0): 4 | return crc32(data, crc) 5 | -------------------------------------------------------------------------------- /_old/assumptions: -------------------------------------------------------------------------------- 1 | "%d" prints 32bit integers 2 | for some reason the datafile size check has an off-by-one-error 3 | -------------------------------------------------------------------------------- /uniffi/huffman/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | uniffi::generate_scaffolding("src/libtw2_huffman.udl").unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /gamenet/common/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod debug; 2 | pub mod error; 3 | pub mod msg; 4 | pub mod snap_obj; 5 | pub mod traits; 6 | -------------------------------------------------------------------------------- /huffman/src/instances/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::teeworlds::INSTANCE as TEEWORLDS; 2 | 3 | #[rustfmt::skip] 4 | mod teeworlds; 5 | -------------------------------------------------------------------------------- /polyfill-1-63/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use self::option::OptionExt; 2 | pub use once_cell::sync::OnceCell as OnceLock; 3 | 4 | mod option; 5 | -------------------------------------------------------------------------------- /tools/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod client; 2 | pub mod map_stats; 3 | pub mod unhexdump; 4 | pub mod warn_stderr; 5 | pub mod warn_stdout; 6 | -------------------------------------------------------------------------------- /map/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use self::reader::Error; 2 | pub use self::reader::Reader; 3 | 4 | #[rustfmt::skip] 5 | pub mod format; 6 | pub mod reader; 7 | -------------------------------------------------------------------------------- /net/src/collections/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod peer_map; 2 | pub mod peer_set; 3 | 4 | pub use self::peer_map::PeerMap; 5 | pub use self::peer_set::PeerSet; 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | 3 | *.pyc 4 | *.o 5 | *.rlib 6 | *.so 7 | 8 | *.map 9 | 10 | /build 11 | /.cargo 12 | a.out 13 | main 14 | 15 | target/ 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Format of commit messages is 2 | 3 | crate_name: Do the foobar in `abc::def` 4 | 5 | Further comments go into the third and further lines. 6 | -------------------------------------------------------------------------------- /serverbrowse/IDEAS: -------------------------------------------------------------------------------- 1 | - Query for 64-player server 2 | - Use less memory by not saving the complete server info struct for every 3 | server. 4 | - Support restarting. 5 | -------------------------------------------------------------------------------- /wireshark-dissector/sys/bindgen.h: -------------------------------------------------------------------------------- 1 | #define HAVE_PLUGINS 2 | #include 3 | #include 4 | #include 5 | #include 6 | -------------------------------------------------------------------------------- /httphook/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::time::Duration; 3 | 4 | fn main() { 5 | libtw2_httphook::register_server_6(8303); 6 | thread::sleep(Duration::from_millis(100)); 7 | } 8 | -------------------------------------------------------------------------------- /tools/src/warn_stdout.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use warn::Warn; 3 | 4 | pub struct Stdout; 5 | 6 | impl Warn for Stdout { 7 | fn warn(&mut self, warning: W) { 8 | println!("WARN: {:?}", warning); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tools/src/warn_stderr.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use warn::Warn; 3 | 4 | pub struct Stderr; 5 | 6 | impl Warn for Stderr { 7 | fn warn(&mut self, warning: W) { 8 | eprintln!("WARN: {:?}", warning); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /huffman/reference/sys/src/include/base/system.h: -------------------------------------------------------------------------------- 1 | #ifndef BASE_SYSTEM_H_SHIM 2 | #define BASE_SYSTEM_H_SHIM 3 | #include 4 | static inline void mem_zero(void *memory, unsigned size) 5 | { 6 | memset(memory, 0, size); 7 | } 8 | #endif // BASE_SYSTEM_H_SHIM 9 | -------------------------------------------------------------------------------- /logger/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-logger" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | env_logger = "0.3.0" 11 | -------------------------------------------------------------------------------- /demo/src/ddnet.rs: -------------------------------------------------------------------------------- 1 | mod reader; 2 | mod writer; 3 | 4 | pub use self::reader::Chunk; 5 | pub use self::reader::DemoReader; 6 | pub use self::reader::ReadError; 7 | pub use self::reader::Warning; 8 | pub use self::writer::DemoWriter; 9 | pub use self::writer::WriteError; 10 | -------------------------------------------------------------------------------- /gamenet/generate/snap_obj.py: -------------------------------------------------------------------------------- 1 | import network 2 | import fix_network 3 | import datatypes 4 | 5 | emit = datatypes.Emit() 6 | with emit: 7 | datatypes.emit_header_snap_obj() 8 | datatypes.emit_enum_obj_module("SnapObj", network.Objects, network.Flags) 9 | emit.dump() 10 | -------------------------------------------------------------------------------- /wireshark-dissector/sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-wireshark-dissector-sys" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /polyfill-1-63/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-polyfill-1-63" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | once_cell = "1.20.3" 11 | -------------------------------------------------------------------------------- /huffman/reference/sys/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | cc::Build::new() 3 | .include("src/include") 4 | .cpp(true) 5 | .cpp_link_stdlib(None) 6 | .file("src/teeworlds/huffman.cpp") 7 | .file("src/api.cpp") 8 | .compile("huffman"); 9 | } 10 | -------------------------------------------------------------------------------- /net/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod collections; 2 | pub mod connection; 3 | pub mod net; 4 | pub mod protocol; 5 | pub mod protocol7; 6 | pub mod time; 7 | 8 | pub use self::connection::Connection; 9 | pub use self::net::Net; 10 | pub use self::time::Timeout; 11 | pub use self::time::Timestamp; 12 | -------------------------------------------------------------------------------- /uniffi/huffman/src/libtw2_huffman.udl: -------------------------------------------------------------------------------- 1 | [Error] 2 | enum DecompressionError { 3 | "InvalidInput", 4 | }; 5 | 6 | namespace libtw2_huffman { 7 | bytes compress([ByRef] bytes input); 8 | [Throws = DecompressionError] 9 | bytes decompress([ByRef] bytes input); 10 | }; 11 | -------------------------------------------------------------------------------- /uniffi/huffman/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "libtw2-huffman" 3 | version = "0.2.1" 4 | 5 | [project.urls] 6 | repository = "https://github.com/heinrich5991/libtw2" 7 | 8 | [build-system] 9 | requires = ["maturin>=1.0,<2.0", "uniffi-bindgen>=0.27.0,<0.28.0"] 10 | build-backend = "maturin" 11 | -------------------------------------------------------------------------------- /huffman/reference/sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-huffman-reference-sys" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | edition = "2021" 6 | rust-version = "1.63.0" 7 | 8 | [build-dependencies] 9 | cc = "1.0.3" 10 | 11 | [dependencies] 12 | libc = "0.2.0" 13 | -------------------------------------------------------------------------------- /_old/compression.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | uint32_t tw_comp_crc(uint32_t crc, void *buf, size_t length); 5 | 6 | int tw_comp_uncompress(void *dst, size_t *dst_size, const void *src, size_t src_size); 7 | 8 | enum 9 | { 10 | TW_ERRNO_COMP=400, 11 | TW_ERRNO_COMP_ERROR, 12 | }; 13 | -------------------------------------------------------------------------------- /zlib-minimal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-zlib-minimal" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | libc = ">=0.1.5,<0.3.0" 11 | libz-sys = ">=0.1.3,<2.0.0" 12 | -------------------------------------------------------------------------------- /gamenet/ddnet/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[rustfmt::skip] 2 | pub mod enums; 3 | #[rustfmt::skip] 4 | pub mod msg; 5 | #[rustfmt::skip] 6 | pub mod snap_obj; 7 | 8 | mod traits; 9 | 10 | pub use self::snap_obj::SnapObj; 11 | pub use self::traits::Protocol; 12 | pub use libtw2_gamenet_common::error; 13 | pub use libtw2_gamenet_common::error::Error; 14 | -------------------------------------------------------------------------------- /gamenet/teeworlds-0.5/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[rustfmt::skip] 2 | pub mod enums; 3 | #[rustfmt::skip] 4 | pub mod msg; 5 | #[rustfmt::skip] 6 | pub mod snap_obj; 7 | 8 | mod traits; 9 | 10 | pub use self::snap_obj::SnapObj; 11 | pub use self::traits::Protocol; 12 | pub use libtw2_gamenet_common::error; 13 | pub use libtw2_gamenet_common::error::Error; 14 | -------------------------------------------------------------------------------- /gamenet/teeworlds-0.6/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[rustfmt::skip] 2 | pub mod enums; 3 | #[rustfmt::skip] 4 | pub mod msg; 5 | #[rustfmt::skip] 6 | pub mod snap_obj; 7 | 8 | mod traits; 9 | 10 | pub use self::snap_obj::SnapObj; 11 | pub use self::traits::Protocol; 12 | pub use libtw2_gamenet_common::error; 13 | pub use libtw2_gamenet_common::error::Error; 14 | -------------------------------------------------------------------------------- /gamenet/teeworlds-0.7/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[rustfmt::skip] 2 | pub mod enums; 3 | #[rustfmt::skip] 4 | pub mod msg; 5 | #[rustfmt::skip] 6 | pub mod snap_obj; 7 | 8 | mod traits; 9 | 10 | pub use self::snap_obj::SnapObj; 11 | pub use self::traits::Protocol; 12 | pub use libtw2_gamenet_common::error; 13 | pub use libtw2_gamenet_common::error::Error; 14 | -------------------------------------------------------------------------------- /tools/src/bin/basic.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | use libtw2_datafile as df; 4 | use std::path::Path; 5 | 6 | fn process(_: &Path, _: df::Reader, _: &mut ()) -> Result<(), libtw2_map::Error> { 7 | Ok(()) 8 | } 9 | 10 | fn print_stats(_: &()) {} 11 | 12 | fn main() { 13 | libtw2_tools::map_stats::stats(process, print_stats); 14 | } 15 | -------------------------------------------------------------------------------- /demo/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod ddnet; 2 | mod format; 3 | mod reader; 4 | mod writer; 5 | 6 | pub use self::format::DemoKind; 7 | pub use self::format::RawChunk; 8 | pub use self::format::Version; 9 | pub use self::format::Warning; 10 | pub use self::reader::ReadError; 11 | pub use self::reader::Reader; 12 | pub use self::writer::WriteError; 13 | pub use self::writer::Writer; 14 | -------------------------------------------------------------------------------- /map/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-map" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | libtw2-common = { path = "../common/" } 11 | libtw2-datafile = { path = "../datafile/" } 12 | ndarray = "0.9.1" 13 | zerocopy = "0.7.32" 14 | -------------------------------------------------------------------------------- /generate_all: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | pushd common > /dev/null 7 | python3 -m generate.cast > src/num/cast.rs 8 | popd > /dev/null 9 | 10 | pushd gamenet > /dev/null 11 | ./generate_all 12 | popd > /dev/null 13 | 14 | pushd map > /dev/null 15 | python3 src/generate_format.py > src/format.rs 16 | popd > /dev/null 17 | -------------------------------------------------------------------------------- /teehistorian/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod bitmagic; 2 | mod file; 3 | pub mod format; 4 | mod raw; 5 | 6 | pub use self::file::Buffer; 7 | pub use self::file::Error; 8 | pub use self::file::Item; 9 | pub use self::file::Reader; 10 | pub use self::raw::Header; 11 | pub use self::raw::Input; 12 | pub use self::raw::Player; 13 | pub use self::raw::PlayerChange; 14 | pub use self::raw::Pos; 15 | -------------------------------------------------------------------------------- /tools/src/bin/datafile.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | use libtw2_datafile as df; 4 | use std::path::Path; 5 | 6 | fn process(_: &Path, mut df: df::Reader, _: &mut ()) -> Result<(), libtw2_map::Error> { 7 | df.debug_dump()?; 8 | Ok(()) 9 | } 10 | 11 | fn print_stats(_: &()) {} 12 | 13 | fn main() { 14 | libtw2_tools::map_stats::stats(process, print_stats); 15 | } 16 | -------------------------------------------------------------------------------- /tools/src/bin/ok.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | use libtw2_datafile as df; 4 | use std::path::Path; 5 | 6 | fn process(p: &Path, _: df::Reader, _: &mut ()) -> Result<(), libtw2_map::Error> { 7 | println!("{}", p.display()); 8 | Ok(()) 9 | } 10 | 11 | fn print_stats(_: &()) {} 12 | 13 | fn main() { 14 | libtw2_tools::map_stats::stats(process, print_stats); 15 | } 16 | -------------------------------------------------------------------------------- /world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-world" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | libtw2-common = { path = "../common/" } 11 | libtw2-gamenet = { package = "libtw2-gamenet-teeworlds-0-6", path = "../gamenet/teeworlds-0.6/" } 12 | -------------------------------------------------------------------------------- /gamenet/spec/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-gamenet-spec" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | libtw2-gamenet-common = { path = "../common/" } 11 | serde = "1.0.23" 12 | serde_derive = "1.0.7" 13 | uuid = { version = "0.8.1", features = ["serde"] } 14 | -------------------------------------------------------------------------------- /huffman/reference/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-huffman-reference" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | edition = "2021" 6 | rust-version = "1.63.0" 7 | 8 | [dependencies] 9 | buffer = "0.1.9" 10 | libc = "0.2.0" 11 | libtw2-common = { path = "../../common" } 12 | libtw2-huffman = { path = ".." } 13 | libtw2-huffman-reference-sys = { path = "sys" } 14 | -------------------------------------------------------------------------------- /polyfill-1-63/src/option.rs: -------------------------------------------------------------------------------- 1 | pub trait OptionExt { 2 | type Inner; 3 | fn is_none_or(self, f: impl FnOnce(Self::Inner) -> bool) -> bool; 4 | } 5 | 6 | impl OptionExt for Option { 7 | type Inner = T; 8 | fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool { 9 | match self { 10 | None => true, 11 | Some(inner) => f(inner), 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /common/generate/types.py: -------------------------------------------------------------------------------- 1 | SIZES = "8 16 32 64 size".split() 2 | TYPES = [signedness + size for size in SIZES for signedness in "iu"] 3 | 4 | def formatt(string, type): 5 | trait = type[0].upper() + type[1:] 6 | return string.format(**{ 7 | "type": type, 8 | "trait": trait, 9 | "ntrait": "N" + trait, 10 | }) 11 | 12 | def printt(string, type): 13 | print(formatt(string, type)) 14 | -------------------------------------------------------------------------------- /common/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use self::map_iter::MapIterator; 2 | pub use self::slice::relative_size_of; 3 | pub use self::slice::relative_size_of_mult; 4 | pub use self::takeable::Takeable; 5 | 6 | #[macro_use] 7 | mod macros; 8 | 9 | pub mod bytes; 10 | pub mod digest; 11 | pub mod io; 12 | pub mod map_iter; 13 | pub mod num; 14 | pub mod pretty; 15 | pub mod slice; 16 | pub mod str; 17 | pub mod takeable; 18 | pub mod vec; 19 | -------------------------------------------------------------------------------- /httphook-ldpreload/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-httphook-ldpreload" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | hexdump = "0.1.1" 14 | libc = "0.2.16" 15 | libtw2-httphook = { path = "../httphook/" } 16 | log = "0.3.1" 17 | redhook = "2.0" 18 | -------------------------------------------------------------------------------- /_old/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from distutils.core import setup 3 | from distutils.extension import Extension 4 | from Cython.Distutils import build_ext 5 | 6 | setup( 7 | name="Teeworlds datafile library", 8 | cmdclass={'build_ext': build_ext}, 9 | ext_modules=[Extension( 10 | "datafile_py", 11 | sources=[ 12 | "datafile_py.pyx", 13 | "datafile_raw.c", 14 | "common.c", 15 | ], 16 | libraries=["z"], 17 | )], 18 | ) 19 | -------------------------------------------------------------------------------- /uniffi/huffman/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-uniffi-huffman" 3 | version = "0.2.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | 8 | [lib] 9 | name = "libtw2_huffman" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | libtw2-huffman = { path = "../../huffman" } 14 | uniffi = "0.27" 15 | 16 | [build-dependencies] 17 | uniffi = { version = "0.27", features = ["build"] } 18 | -------------------------------------------------------------------------------- /gamenet/snap/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-gamenet-snap" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | buffer = "0.1.9" 11 | libtw2-common = { path = "../../common" } 12 | libtw2-gamenet-common = { path = "../common" } 13 | libtw2-packer = { path = "../../packer/", features = ["uuid"] } 14 | warn = ">=0.1.1,<0.3.0" 15 | -------------------------------------------------------------------------------- /datafile/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | pub use self::file::DataIter; 5 | pub use self::file::Error; 6 | pub use self::file::Reader; 7 | pub use self::format::ItemView; 8 | pub use self::format::OnlyI32; 9 | pub use self::raw::ItemTypeItems; 10 | pub use self::raw::ItemTypes; 11 | pub use self::raw::Items; 12 | pub use self::raw::Version; 13 | 14 | mod bitmagic; 15 | pub mod buffer; 16 | mod file; 17 | pub mod format; 18 | pub mod raw; 19 | mod writer; 20 | -------------------------------------------------------------------------------- /packer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-packer" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | arrayvec = "0.5.2" 11 | buffer = "0.1.9" 12 | libtw2-common = { path = "../common/" } 13 | uuid = { version = "0.8.1", optional = true } 14 | warn = ">=0.1.1,<0.3.0" 15 | 16 | [dev-dependencies] 17 | hexdump = "0.1.1" 18 | quickcheck = "0.4.1" 19 | -------------------------------------------------------------------------------- /stats-browser/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | #[macro_use] 4 | extern crate log; 5 | 6 | pub use self::stats_browser::StatsBrowser; 7 | pub use self::stats_browser::StatsBrowserCb; 8 | 9 | pub mod addr; 10 | pub mod base64; 11 | pub mod config; 12 | pub mod entry; 13 | pub mod hashmap_ext; 14 | pub mod lookup; 15 | pub mod socket; 16 | pub mod stats_browser; 17 | pub mod time; 18 | pub mod tracker_fstd; 19 | pub mod tracker_json; 20 | pub mod vec_map; 21 | pub mod work_queue; 22 | -------------------------------------------------------------------------------- /common/src/num/mod.rs: -------------------------------------------------------------------------------- 1 | #[rustfmt::skip] 2 | mod cast; 3 | 4 | pub use self::cast::Cast; 5 | 6 | pub trait CastFloat { 7 | fn round_to_i32(self) -> i32; 8 | fn trunc_to_i32(self) -> i32; 9 | } 10 | 11 | impl CastFloat for f32 { 12 | fn round_to_i32(self) -> i32 { 13 | // TODO: Do overflow checking? 14 | self.round() as i32 15 | } 16 | fn trunc_to_i32(self) -> i32 { 17 | // TODO: Do overflow checking? 18 | self.trunc() as i32 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /socket/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-socket" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | buffer = "0.1.9" 11 | hexdump = "0.1.1" 12 | itertools = ">=0.3.0,<0.5.0" 13 | libc = "0.2.16" 14 | libtw2-common = { path = "../common/" } 15 | libtw2-net = { path = "../net/" } 16 | log = "0.3.1" 17 | mio = "0.6.6" 18 | net2 = "0.2.0" 19 | rand = "0.8.3" 20 | -------------------------------------------------------------------------------- /_old/common.c: -------------------------------------------------------------------------------- 1 | 2 | #include "common.h" 3 | 4 | void tw_endian_swap(void *data, size_t size, int count) 5 | { 6 | unsigned char *data_c = (unsigned char *)data; 7 | for(; count >= 0; count--) 8 | { 9 | unsigned char *src = (unsigned char *)data_c; 10 | unsigned char *dst = (unsigned char *)data_c + (size - 1); 11 | 12 | for(; src < dst; src++, dst--) 13 | { 14 | unsigned char tmp; 15 | tmp = *src; 16 | *src = *dst; 17 | *dst = tmp; 18 | } 19 | 20 | data_c += size; 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /datafile/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-datafile" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | hexdump = "0.1.1" 11 | itertools = ">=0.3.0,<0.5.0" 12 | libtw2-common = { path = "../common/", features = ["file_offset"] } 13 | libtw2-logger = { path = "../logger/" } 14 | libtw2-zlib = { package = "libtw2-zlib-minimal", path = "../zlib-minimal/" } 15 | log = "0.3.0" 16 | -------------------------------------------------------------------------------- /snapshot/src/lib.rs: -------------------------------------------------------------------------------- 1 | use libtw2_common::num::Cast; 2 | use std::ops; 3 | 4 | pub mod format; 5 | pub mod manager; 6 | pub mod receiver; 7 | pub mod snap; 8 | pub mod storage; 9 | 10 | pub use self::manager::Manager; 11 | pub use self::receiver::DeltaReceiver; 12 | pub use self::receiver::ReceivedDelta; 13 | pub use self::snap::Delta; 14 | pub use self::snap::Snap; 15 | pub use self::storage::Storage; 16 | 17 | fn to_usize(r: ops::Range) -> ops::Range { 18 | r.start.usize()..r.end.usize() 19 | } 20 | -------------------------------------------------------------------------------- /serverbrowse/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-serverbrowse" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | arrayvec = "0.5.2" 11 | libtw2-common = { path = "../common/" } 12 | libtw2-logger = { path = "../logger/" } 13 | libtw2-packer = { path = "../packer/" } 14 | log = "0.3.0" 15 | time = "0.1.25" 16 | warn = "0.2.2" 17 | zerocopy = "0.7.32" 18 | zerocopy-derive = "0.7.32" 19 | -------------------------------------------------------------------------------- /common/src/vec.rs: -------------------------------------------------------------------------------- 1 | use crate::relative_size_of_mult; 2 | use crate::slice; 3 | use std::mem; 4 | 5 | pub unsafe fn transmute(vec: Vec) -> Vec { 6 | slice::transmute::(&vec); // Error checking done there. 7 | 8 | let ptr = vec.as_ptr(); 9 | let len = vec.len(); 10 | let cap = vec.capacity(); 11 | mem::forget(vec); 12 | let new_cap = cap * mem::size_of::() / mem::size_of::(); 13 | 14 | Vec::from_raw_parts(ptr as *mut U, relative_size_of_mult::(len), new_cap) 15 | } 16 | -------------------------------------------------------------------------------- /gamenet/generate_all: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | python3 generate/generate.py generate/spec/teeworlds-0.5.json teeworlds-0.5 libtw2-gamenet-teeworlds-0-5 7 | python3 generate/generate.py generate/spec/teeworlds-0.6.json teeworlds-0.6 libtw2-gamenet-teeworlds-0-6 8 | python3 generate/generate.py generate/spec/teeworlds-0.7-trunk.json teeworlds-0.7 libtw2-gamenet-teeworlds-0-7 9 | python3 generate/generate.py generate/spec/ddnet-19.1.json ddnet libtw2-gamenet-ddnet 10 | -------------------------------------------------------------------------------- /common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-common" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | arrayvec = "0.5.2" 11 | file_offset = { version = "0.1.0", optional = true } 12 | serde = { version = "1.0.23", optional = true } 13 | zerocopy = "0.7.32" 14 | 15 | [dev-dependencies] 16 | bencher = "0.1.5" 17 | quickcheck = "0.4.1" 18 | 19 | [[bench]] 20 | name = "cast" 21 | harness = false 22 | -------------------------------------------------------------------------------- /event-loop/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-event-loop" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | arrayvec = "0.5.2" 11 | hexdump = "0.1.1" 12 | itertools = ">=0.3.0,<0.5.0" 13 | libtw2-common = { path = "../common/" } 14 | libtw2-logger = { path = "../logger/" } 15 | libtw2-net = { path = "../net/" } 16 | libtw2-socket = { path = "../socket/" } 17 | log = "0.3.1" 18 | warn = ">=0.1.1,<0.3.0" 19 | -------------------------------------------------------------------------------- /_old/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from datafile import Datafile, DatafileError 3 | 4 | import argparse 5 | 6 | def do(filename): 7 | try: 8 | with Datafile(filename) as df: 9 | pass 10 | except DatafileError as dfe: 11 | print("{}: {}".format(filename, dfe)) 12 | 13 | 14 | if __name__ == '__main__': 15 | p = argparse.ArgumentParser() 16 | p.add_argument('filenames', metavar='datafile', type=str, nargs='+', help='the datafiles to be processed') 17 | args = p.parse_args() 18 | for filename in args.filenames: 19 | do(filename) 20 | -------------------------------------------------------------------------------- /demo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-demo" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | 8 | [dependencies] 9 | arrayvec = "0.5.2" 10 | binrw = "0.11.1" 11 | buffer = "0.1.9" 12 | libtw2-common = { path = "../common/" } 13 | libtw2-gamenet-common = { path = "../gamenet/common/" } 14 | libtw2-huffman = { path = "../huffman/" } 15 | libtw2-packer = { path = "../packer/" } 16 | libtw2-snapshot = { path = "../snapshot/" } 17 | thiserror = "1.0.0" 18 | warn = "0.2.1" 19 | -------------------------------------------------------------------------------- /gamenet/ddnet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-gamenet-ddnet" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | arrayvec = "0.5.2" 11 | buffer = "0.1.9" 12 | libtw2-common = { path = "../../common/" } 13 | libtw2-gamenet-common = { path = "../common/" } 14 | libtw2-gamenet-snap = { path = "../snap/" } 15 | libtw2-packer = { path = "../../packer/", features = ["uuid"] } 16 | uuid = "0.8.1" 17 | warn = ">=0.1.1,<0.3.0" 18 | -------------------------------------------------------------------------------- /_old/compression.c: -------------------------------------------------------------------------------- 1 | #include "compression.h" 2 | 3 | #include "error.h" 4 | 5 | #include 6 | 7 | uint32_t tw_comp_crc(uint32_t crc, void *buf, size_t length) 8 | { 9 | return crc32(crc, buf, length); 10 | } 11 | 12 | int tw_comp_uncompress(void *dst, size_t *dst_size, const void *src, size_t src_size) 13 | { 14 | int zlib_err = uncompress(dst, dst_size, src, src_size); 15 | if(zlib_err != Z_OK) 16 | { 17 | tw_error_set(TW_ERRNO_COMP_ERROR, "zlib error during uncompression, zlib_err=%d", zlib_err); 18 | return 1; 19 | } 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /huffman/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-huffman" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | arrayvec = "0.5.2" 11 | buffer = "0.1.9" 12 | itertools = ">=0.3.0,<0.5.0" 13 | libtw2-common = { path = "../common/" } 14 | 15 | [dev-dependencies] 16 | bencher = "0.1.5" 17 | libtw2-huffman-reference = { path = "reference" } 18 | quickcheck = "0.4.1" 19 | 20 | [[bench]] 21 | name = "compression" 22 | harness = false 23 | -------------------------------------------------------------------------------- /gamenet/common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-gamenet-common" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | arrayvec = "0.5.2" 11 | buffer = "0.1.9" 12 | libtw2-common = { path = "../../common" } 13 | libtw2-packer = { path = "../../packer/", features = ["uuid"] } 14 | serde = "1.0.23" 15 | serde_derive = "1.0.7" 16 | uuid = { version = "0.8.1", features = ["serde"] } 17 | warn = ">=0.1.1,<0.3.0" 18 | zerocopy = "0.7.32" 19 | -------------------------------------------------------------------------------- /_old/io.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct tw_io tw_io; 4 | 5 | tw_io *tw_io_open(const char *filename, const char *mode); 6 | int tw_io_close(tw_io *io); 7 | size_t tw_io_read(tw_io *io, void *buffer, size_t size); 8 | size_t tw_io_write(tw_io *io, const void *buffer, size_t size); 9 | long tw_io_tell(tw_io *io); 10 | int tw_io_seek(tw_io *io, long offset); 11 | int tw_io_seek_end(tw_io *io); 12 | size_t io_size(tw_io *io); 13 | int tw_io_flush(tw_io *io); 14 | 15 | enum 16 | { 17 | TW_ERRNO_IO=200, 18 | TW_ERRNO_IO_EOF, 19 | TW_ERRNO_IO_ERROR, 20 | }; 21 | -------------------------------------------------------------------------------- /gamenet/teeworlds-0.5/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-gamenet-teeworlds-0-5" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | arrayvec = "0.5.2" 11 | buffer = "0.1.9" 12 | libtw2-common = { path = "../../common/" } 13 | libtw2-gamenet-common = { path = "../common/" } 14 | libtw2-gamenet-snap = { path = "../snap/" } 15 | libtw2-packer = { path = "../../packer/", features = ["uuid"] } 16 | uuid = "0.8.1" 17 | warn = ">=0.1.1,<0.3.0" 18 | -------------------------------------------------------------------------------- /gamenet/teeworlds-0.6/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-gamenet-teeworlds-0-6" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | arrayvec = "0.5.2" 11 | buffer = "0.1.9" 12 | libtw2-common = { path = "../../common/" } 13 | libtw2-gamenet-common = { path = "../common/" } 14 | libtw2-gamenet-snap = { path = "../snap/" } 15 | libtw2-packer = { path = "../../packer/", features = ["uuid"] } 16 | uuid = "0.8.1" 17 | warn = ">=0.1.1,<0.3.0" 18 | -------------------------------------------------------------------------------- /gamenet/teeworlds-0.7/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-gamenet-teeworlds-0-7" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | arrayvec = "0.5.2" 11 | buffer = "0.1.9" 12 | libtw2-common = { path = "../../common/" } 13 | libtw2-gamenet-common = { path = "../common/" } 14 | libtw2-gamenet-snap = { path = "../snap/" } 15 | libtw2-packer = { path = "../../packer/", features = ["uuid"] } 16 | uuid = "0.8.1" 17 | warn = ">=0.1.1,<0.3.0" 18 | -------------------------------------------------------------------------------- /huffman/reference/sys/src/api.h: -------------------------------------------------------------------------------- 1 | #ifndef HUFFMAN_API_H 2 | #define HUFFMAN_API_H 3 | 4 | #include 5 | 6 | struct huffman; 7 | 8 | extern "C" size_t huffman_size(void); 9 | 10 | extern "C" void huffman_init(struct huffman *huffman, const unsigned frequencies[256]); 11 | extern "C" int huffman_compress(const struct huffman *huffman, const void *input, 12 | int input_size, void *output, int output_size); 13 | extern "C" int huffman_decompress(const struct huffman *huffman, const void *input, 14 | int input_size, void *output, int output_size); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /render-map/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-render-map" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | clap = "2.31.2" 11 | image = { version = "0.24.0", features = ["png", "rayon"], default-features = false } 12 | libtw2-common = { path = "../common/" } 13 | libtw2-datafile = { path = "../datafile/" } 14 | libtw2-logger = { path = "../logger/" } 15 | libtw2-map = { path = "../map/" } 16 | ndarray = "0.9.1" 17 | num-traits = "0.1.32" 18 | -------------------------------------------------------------------------------- /huffman/src/main.rs: -------------------------------------------------------------------------------- 1 | use libtw2_huffman::Huffman; 2 | use std::fs::File; 3 | use std::io::BufRead; 4 | use std::io::BufReader; 5 | 6 | fn read_file T>(filename: &str, f: F) -> Vec { 7 | BufReader::new(File::open(filename).unwrap()) 8 | .lines() 9 | .map(|ml| ml.unwrap()) 10 | .map(f) 11 | .collect() 12 | } 13 | 14 | fn main() { 15 | let input = read_file("data/frequencies", |l| u32::from_str_radix(&l, 10).unwrap()); 16 | 17 | for r in Huffman::from_frequencies(&input).repr() { 18 | println!("{}", r); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /stats-browser/src/lookup.rs: -------------------------------------------------------------------------------- 1 | use crate::addr::Addr; 2 | use std::io; 3 | use std::net::ToSocketAddrs; 4 | 5 | /// Looks up a hostname and returns the first associated IP address. 6 | /// 7 | /// If an error occurs during the lookup, it is returned, if no address is 8 | /// found, `None` is returned. 9 | /// 10 | /// Note that this function might block. 11 | pub fn lookup_host(domain: &str, port: u16) -> io::Result> { 12 | for socket_addr in (domain, port).to_socket_addrs()? { 13 | return Ok(Some(Addr::from_socket_addr(socket_addr))); 14 | } 15 | Ok(None) 16 | } 17 | -------------------------------------------------------------------------------- /wireshark-dissector/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn non_windows_msvc() { 4 | println!("cargo:rustc-cdylib-link-arg=-Wl,-undefined,dynamic_lookup"); 5 | } 6 | 7 | fn main() { 8 | let msvc = env::var_os("CARGO_CFG_TARGET_VENDOR") 9 | .map(|v| v == "pc") 10 | .unwrap_or(false) 11 | && env::var_os("CARGO_CFG_TARGET_FAMILY") 12 | .map(|v| v == "windows") 13 | .unwrap_or(false) 14 | && env::var_os("CARGO_CFG_TARGET_ENV") 15 | .map(|v| v == "msvc") 16 | .unwrap_or(false); 17 | if !msvc { 18 | non_windows_msvc(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /httphook/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-httphook" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | env_logger = "0.3.0" 11 | envy = "0.4.2" 12 | libtw2-polyfill-1-63 = { path = "../polyfill-1-63/" } 13 | libtw2-register = { path = "../register/" } 14 | libtw2-serverbrowse = { path = "../serverbrowse/" } 15 | log = "0.3.1" 16 | reqwest = "0.11.27" 17 | serde = "1.0.23" 18 | serde_derive = "1.0.27" 19 | serde_json = "1.0.7" 20 | tokio = { version = "1.38.1", features = ["net", "rt", "time"] } 21 | -------------------------------------------------------------------------------- /doc/connection.md: -------------------------------------------------------------------------------- 1 | -> s:info 2 | <- s:map_change <----------------------+ 3 | [ -> s:request_map_data ] | 4 | [ <- s:map_data ] | 5 | -> s:ready | 6 | [ <- g:sv_motd ] | 7 | <- s:con_ready | 8 | -> g:client_start_info | 9 | [ <- g:sv_vote_clear_options ] | 10 | [ <- g:sv_tune_params ] | 11 | <- g:sv_ready_to_enter | 12 | -> s:enter_game | 13 | ingame --------------------------------+ 14 | -------------------------------------------------------------------------------- /net/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-net" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | arrayvec = "0.5.2" 11 | buffer = "0.1.9" 12 | libtw2-common = { path = "../common/" } 13 | libtw2-huffman = { path = "../huffman/" } 14 | linear-map = "1.0.0" 15 | optional = "0.0.12" 16 | void = ">=0.0.4,<2.0.0" 17 | warn = ">=0.1.1,<0.3.0" 18 | zerocopy = "0.7.32" 19 | zerocopy-derive = "0.7.32" 20 | 21 | [dev-dependencies] 22 | hexdump = "0.1.1" 23 | itertools = ">=0.3.0,<0.5.0" 24 | quickcheck = "0.4.1" 25 | -------------------------------------------------------------------------------- /snapshot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-snapshot" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | buffer = "0.1.9" 11 | libtw2-common = { path = "../common/" } 12 | libtw2-gamenet-common = { path = "../gamenet/common/" } 13 | libtw2-gamenet-snap = { path = "../gamenet/snap/" } 14 | libtw2-packer = { path = "../packer/" } 15 | uuid = "0.8.1" 16 | vec_map = "0.8.0" 17 | warn = ">=0.1.1,<0.3.0" 18 | 19 | [dev-dependencies] 20 | libtw2-gamenet = { package = "libtw2-gamenet-teeworlds-0-6", path = "../gamenet/teeworlds-0.6/" } 21 | -------------------------------------------------------------------------------- /register/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-register" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | bitflags = "2.9.4" 11 | libtw2-packer = { path = "../packer/" } 12 | libtw2-polyfill-1-63 = { path = "../polyfill-1-63/" } 13 | libtw2-serverbrowse = { path = "../serverbrowse/" } 14 | log = "0.3.1" 15 | reqwest = "0.11.27" 16 | serde = { version = "1.0.23", features = ["rc"] } 17 | serde_derive = "1.0.27" 18 | serde_json = "1.0.7" 19 | tokio = { version = "1.38.1", features = ["macros", "time"] } 20 | uuid = { version = "0.8.1", features = ["v4"] } 21 | -------------------------------------------------------------------------------- /uniffi/huffman/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::error; 2 | use std::fmt; 3 | 4 | pub use libtw2_huffman::compress; 5 | 6 | #[derive(Debug)] 7 | pub enum DecompressionError { 8 | InvalidInput, 9 | } 10 | 11 | impl fmt::Display for DecompressionError { 12 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 13 | libtw2_huffman::InvalidInput.fmt(f) 14 | } 15 | } 16 | 17 | impl error::Error for DecompressionError {} 18 | 19 | pub fn decompress(bytes: &[u8]) -> Result, DecompressionError> { 20 | libtw2_huffman::decompress(bytes) 21 | .map_err(|libtw2_huffman::InvalidInput| DecompressionError::InvalidInput) 22 | } 23 | 24 | uniffi::include_scaffolding!("libtw2_huffman"); 25 | -------------------------------------------------------------------------------- /huffman/reference/sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[link(name = "huffman")] 2 | extern "C" { 3 | pub fn huffman_size() -> libc::size_t; 4 | pub fn huffman_init(huffman: *mut libc::c_void, frequencies: *const [libc::c_uint; 256]); 5 | pub fn huffman_compress( 6 | huffman: *const libc::c_void, 7 | input: *const libc::c_void, 8 | input_size: libc::c_int, 9 | output: *mut libc::c_void, 10 | output_size: libc::c_int, 11 | ) -> libc::c_int; 12 | pub fn huffman_decompress( 13 | huffman: *const libc::c_void, 14 | input: *const libc::c_void, 15 | input_size: libc::c_int, 16 | output: *mut libc::c_void, 17 | output_size: libc::c_int, 18 | ) -> libc::c_int; 19 | } 20 | -------------------------------------------------------------------------------- /_old/gamemap.py: -------------------------------------------------------------------------------- 1 | class GameMap: 2 | def __init__(self, df): 3 | self.df, self._df_owned = _get_datafile(df) 4 | self.name = self.df.name 5 | 6 | def __enter__(self): 7 | return self 8 | 9 | def __exit__(self, type_, value, traceback): 10 | self.close() 11 | 12 | def __repr__(self): 13 | return ''.format(self.name) 14 | 15 | def close(self): 16 | if self.df is not None: 17 | if self._df_owned: 18 | self.df.close() 19 | self.df = None 20 | 21 | def _get_datafile(self, df): 22 | try: 23 | df.items 24 | df.data 25 | df.types 26 | # this would call the property, which is very expensive 27 | #df.crc 28 | except AttributeError: 29 | return Datafile(df), True 30 | else: 31 | return df, False 32 | -------------------------------------------------------------------------------- /teehistorian/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-teehistorian" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | arrayvec = { version = "0.5.2", features = ["serde"] } 11 | buffer = "0.1.9" 12 | chrono = "0.4.0" 13 | itertools = "0.7.4" 14 | libtw2-common = { path = "../common/", features = ["serde"] } 15 | libtw2-packer = { path = "../packer/", features = ["uuid"] } 16 | serde = "1.0.23" 17 | serde_derive = "1.0.7" 18 | serde_json = "1.0.7" 19 | uuid = { version = "0.8.1", features = ["serde"] } 20 | vec_map = "0.8.0" 21 | warn = "0.2.2" 22 | 23 | [dev-dependencies] 24 | uuid = { version = "0.8.1", features = ["v3"] } 25 | -------------------------------------------------------------------------------- /stats-browser/src/base64.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A struct for printing a byte array as [Base64][wiki]. 4 | /// 5 | /// [wiki]: https://en.wikipedia.org/wiki/Base64 6 | #[derive(Copy, Clone)] 7 | pub struct B64<'a>(pub &'a [u8]); 8 | 9 | impl<'a> fmt::Debug for B64<'a> { 10 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 11 | let B64(bytes) = *self; 12 | fmt::Display::fmt(&base64::encode(bytes), f) 13 | } 14 | } 15 | 16 | // --------------------------------------- 17 | // Boilerplate trait implementations below 18 | // --------------------------------------- 19 | 20 | impl<'a> fmt::Display for B64<'a> { 21 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 22 | fmt::Debug::fmt(self, f) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /downloader/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-downloader" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | arrayvec = "0.5.2" 11 | clap = "2.31.2" 12 | hexdump = "0.1.1" 13 | itertools = ">=0.3.0,<0.5.0" 14 | libtw2-common = { path = "../common/" } 15 | libtw2-event-loop = { path = "../event-loop/" } 16 | libtw2-gamenet = { package = "libtw2-gamenet-teeworlds-0-6", path = "../gamenet/teeworlds-0.6/" } 17 | libtw2-logger = { path = "../logger/" } 18 | libtw2-packer = { path = "../packer/" } 19 | libtw2-snapshot = { path = "../snapshot/" } 20 | log = "0.3.1" 21 | rand = "0.8.3" 22 | tempfile = "3.0.0" 23 | warn = ">=0.1.1,<0.3.0" 24 | -------------------------------------------------------------------------------- /doc/quirks.md: -------------------------------------------------------------------------------- 1 | - Huffman compression has an extra byte if the actual end is at a byte 2 | boundary. 3 | - The size field has a weird splitting in the chunk header. 4 | - The ack field of the packet header is 12 instead of 10 bit wide. 5 | - The sequence field of the chunk header has overlapping bits. 6 | - The client doesn't care about the actual lengths of the snapshot chunks, 7 | it'll always pad to `MAX_SNAPSHOT_PADSIZE`. 8 | - The client wants to receive the last snapshot part last, otherwise the 9 | resulting delta is too long. 10 | - CCharacterCore has unused fields `m_HookDx`, `m_HookDy` 11 | - The packet payload can be empty. This happens when chunks from the peer are 12 | lost, a resend of these is requested but no new chunks to the peer are queued 13 | yet. 14 | -------------------------------------------------------------------------------- /uniffi/huffman/README.md: -------------------------------------------------------------------------------- 1 | # libtw2_huffman 2 | 3 | Python bindings for `libtw2-huffman` which implements a homebrew compression 4 | format using huffman coding that is used in Teeworlds/DDNet networking and demo 5 | file format. 6 | 7 | ## Building from source 8 | 9 | ```bash 10 | python -m pip install --upgrade build 11 | python -m build 12 | python -m pip install dist/libtw2_huffman-*.tar.gz 13 | ``` 14 | 15 | ## Install from Pypi 16 | 17 | ``` 18 | pip install libtw2_huffman 19 | ``` 20 | 21 | ## Sample usage 22 | 23 | ```python 24 | import libtw2_huffman 25 | 26 | assert libtw2_huffman.decompress(b'\xae\x95\x13\x5c\x09\x57\xc2\x16\x29\x6e\x00') == b'hello' 27 | assert libtw2_huffman.compress(b'hello') == b'\xae\x95\x13\x5c\x09\x57\xc2\x16\x29\x6e\x00' 28 | ``` 29 | 30 | -------------------------------------------------------------------------------- /teehistorian/src/bitmagic.rs: -------------------------------------------------------------------------------- 1 | use crate::raw::Callback; 2 | use buffer::with_buffer; 3 | use buffer::Buffer; 4 | use buffer::BufferRef; 5 | use libtw2_common::unwrap_or_return; 6 | 7 | pub trait CallbackExt: Callback { 8 | fn read_buffer<'d, B: Buffer<'d>>(&mut self, buf: B) -> Result, Self::Error> { 9 | with_buffer(buf, |buf| self.read_buffer_ref(buf)) 10 | } 11 | fn read_buffer_ref<'d, 's>( 12 | &mut self, 13 | mut buf: BufferRef<'d, 's>, 14 | ) -> Result, Self::Error> { 15 | unsafe { 16 | let read = unwrap_or_return!(self.read_at_most(buf.uninitialized_mut())?, Ok(None)); 17 | buf.advance(read); 18 | Ok(Some(buf.initialized())) 19 | } 20 | } 21 | } 22 | 23 | impl CallbackExt for CB {} 24 | -------------------------------------------------------------------------------- /gamenet/common/src/debug.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::marker::PhantomData; 3 | 4 | pub struct DebugSlice<'a, T: Clone + 'a, D: fmt::Debug, F: Fn(T) -> D> { 5 | slice: &'a [T], 6 | f: F, 7 | phantom: PhantomData, 8 | } 9 | 10 | impl<'a, T: Clone + 'a, D: fmt::Debug, F: Fn(T) -> D> DebugSlice<'a, T, D, F> { 11 | pub fn new(slice: &[T], f: F) -> DebugSlice { 12 | DebugSlice { 13 | slice: slice, 14 | f: f, 15 | phantom: PhantomData, 16 | } 17 | } 18 | } 19 | 20 | impl<'a, T: Clone + 'a, D: fmt::Debug, F: Fn(T) -> D> fmt::Debug for DebugSlice<'a, T, D, F> { 21 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 22 | f.debug_list() 23 | .entries(self.slice.iter().cloned().map(&self.f)) 24 | .finish() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tools/src/bin/settings.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | use libtw2_common::pretty; 4 | use libtw2_datafile as df; 5 | use libtw2_map::format; 6 | use std::path::Path; 7 | 8 | fn process(_: &Path, dfr: df::Reader, _: &mut ()) -> Result<(), libtw2_map::Error> { 9 | let mut map = libtw2_map::Reader::from_datafile(dfr); 10 | let maybe_info = map.info(); 11 | if let Err(format::Error::MissingInfo) = maybe_info { 12 | return Ok(()); 13 | } 14 | let info = maybe_info?; 15 | if let Some(s) = info.settings { 16 | let settings = map.settings(s)?; 17 | for line in settings.iter() { 18 | println!("{:?}", pretty::AlmostString::new(line)); 19 | } 20 | } 21 | Ok(()) 22 | } 23 | 24 | fn nothing(_: &()) {} 25 | 26 | fn main() { 27 | libtw2_tools::map_stats::stats(process, nothing); 28 | } 29 | -------------------------------------------------------------------------------- /wireshark-dissector/README.md: -------------------------------------------------------------------------------- 1 | wireshark-dissector 2 | =================== 3 | 4 | 1. Run `cargo build --release` to get a `libwireshark_dissector.so` (Linux), 5 | `libwireshark_dissector.dylib` (macOS) or `wireshark_dissector.dll` 6 | (Windows). On macOS, rename it to `libwireshark_dissector.so`, else 7 | Wireshark will not recognize it as a plugin. 8 | 9 | 2. Place the above mentioned file into your plugin folder, on Linux and macOS, 10 | it's `~/.local/lib/wireshark/plugins/4.4/epan/`, on Windows it's 11 | `%APPDATA%\Wireshark\plugins\4.4\epan\`. You'll likely have to create these 12 | folders. 13 | 14 | 3. Start Wireshark, go to Help → About Wireshark, click on the Plugins tab. You 15 | should see the previously copied file in the list. 16 | 17 | 4. Start capturing Teeworlds traffic. It should automatically get dissected. 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "common", 5 | "datafile", 6 | "demo", 7 | "downloader", 8 | "event-loop", 9 | "gamenet/common", 10 | "gamenet/ddnet", 11 | "gamenet/snap", 12 | "gamenet/spec", 13 | "gamenet/teeworlds-0.5", 14 | "gamenet/teeworlds-0.6", 15 | "gamenet/teeworlds-0.7", 16 | "httphook", 17 | "httphook-ldpreload", 18 | "huffman", 19 | "huffman/reference", 20 | "huffman/reference/sys", 21 | "logger", 22 | "map", 23 | "net", 24 | "packer", 25 | "register", 26 | "polyfill-1-63", 27 | "render-map", 28 | "server", 29 | "serverbrowse", 30 | "snapshot", 31 | "socket", 32 | "stats-browser", 33 | "teehistorian", 34 | "tools", 35 | "wireshark-dissector", 36 | "world", 37 | "zlib-minimal", 38 | ] 39 | -------------------------------------------------------------------------------- /_old/datafile.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef struct tw_datafile tw_datafile; 5 | 6 | // datafile reader 7 | tw_datafile *tw_df_open(const char *filename); 8 | int tw_df_close(tw_datafile *df); 9 | 10 | void *tw_df_data_load(tw_datafile *df, int index, size_t *size); 11 | void tw_df_data_unload(tw_datafile *df, int index); 12 | int tw_df_num_data(tw_datafile *df); 13 | 14 | int32_t *tw_df_item_read(tw_datafile *df, int index, size_t *count, int *type_id, int *id); 15 | int32_t *tw_df_item_find(tw_datafile *df, size_t *count, int type_id, int id); 16 | void tw_df_type_indexes(tw_datafile *df, int type_id, int *start, int *num); 17 | int tw_df_num_items(tw_datafile *df); 18 | 19 | uint32_t tw_df_crc(tw_datafile *df); 20 | 21 | // errors 22 | enum 23 | { 24 | TW_ERRNO_DF=300, 25 | // see datafile_raw.h, add 300 to each 26 | }; 27 | -------------------------------------------------------------------------------- /httphook/src/runtime.rs: -------------------------------------------------------------------------------- 1 | use libtw2_polyfill_1_63::OnceLock; 2 | use std::future; 3 | use std::future::Future; 4 | use std::thread; 5 | use tokio::runtime; 6 | 7 | pub fn spawn + Send + 'static>(future: F) { 8 | fn handle() -> &'static runtime::Handle { 9 | static HANDLE: OnceLock = OnceLock::new(); 10 | HANDLE.get_or_init(|| { 11 | let runtime = runtime::Builder::new_current_thread() 12 | .enable_io() 13 | .enable_time() 14 | .build() 15 | .unwrap(); 16 | let handle = runtime.handle().clone(); 17 | // give the runtime a thread to work with 18 | thread::spawn(move || runtime.block_on(future::pending::<()>())); 19 | handle 20 | }) 21 | } 22 | let _ = handle().spawn(future); 23 | } 24 | -------------------------------------------------------------------------------- /huffman/reference/sys/src/api.cpp: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | 3 | #include "teeworlds/huffman.h" 4 | 5 | struct huffman 6 | { 7 | mutable CHuffman Huffman; 8 | }; 9 | 10 | extern "C" size_t huffman_size(void) 11 | { 12 | return sizeof(struct huffman); 13 | } 14 | 15 | extern "C" void huffman_init(struct huffman *huffman, const unsigned frequencies[256]) 16 | { 17 | huffman->Huffman.Init(frequencies); 18 | } 19 | 20 | extern "C" int huffman_compress(const struct huffman *huffman, const void *input, 21 | int input_size, void *output, int output_size) 22 | { 23 | return huffman->Huffman.Compress(input, input_size, output, output_size); 24 | } 25 | 26 | extern "C" int huffman_decompress(const struct huffman *huffman, const void *input, 27 | int input_size, void *output, int output_size) 28 | { 29 | return huffman->Huffman.Decompress(input, input_size, output, output_size); 30 | } 31 | -------------------------------------------------------------------------------- /server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-server" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [dependencies] 10 | arrayvec = "0.5.2" 11 | hexdump = "0.1.1" 12 | itertools = ">=0.3.0,<0.5.0" 13 | libtw2-common = { path = "../common/" } 14 | libtw2-datafile = { path = "../datafile/" } 15 | libtw2-event-loop = { path = "../event-loop/" } 16 | libtw2-gamenet = { package = "libtw2-gamenet-teeworlds-0-6", path = "../gamenet/teeworlds-0.6/" } 17 | libtw2-logger = { path = "../logger/" } 18 | libtw2-map = { path = "../map/" } 19 | libtw2-packer = { path = "../packer/" } 20 | libtw2-snapshot = { path = "../snapshot/" } 21 | libtw2-socket = { path = "../socket/" } 22 | libtw2-world = { path = "../world/" } 23 | log = "0.3.1" 24 | ndarray = "0.9.1" 25 | warn = ">=0.1.1,<0.3.0" 26 | -------------------------------------------------------------------------------- /common/src/slice.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::slice; 3 | 4 | pub fn relative_size_of_mult(mult: usize) -> usize { 5 | // Panics if mem::size_of::() is 0. 6 | assert!(mult * mem::size_of::() % mem::size_of::() == 0); 7 | mult * mem::size_of::() / mem::size_of::() 8 | } 9 | 10 | pub fn relative_size_of() -> usize { 11 | relative_size_of_mult::(1) 12 | } 13 | 14 | pub unsafe fn transmute(x: &[T]) -> &[U] { 15 | assert!(mem::align_of::() % mem::align_of::() == 0); 16 | slice::from_raw_parts( 17 | x.as_ptr() as *const U, 18 | relative_size_of_mult::(x.len()), 19 | ) 20 | } 21 | 22 | pub unsafe fn transmute_mut(x: &mut [T]) -> &mut [U] { 23 | transmute::(x); // For the error checking. 24 | slice::from_raw_parts_mut(x.as_ptr() as *mut U, relative_size_of_mult::(x.len())) 25 | } 26 | -------------------------------------------------------------------------------- /doc/uncompressed_snap.ksy: -------------------------------------------------------------------------------- 1 | meta: 2 | id: tw_uncompressed_snap 3 | endian: be 4 | license: MIT/Apache-2.0 5 | doc-ref: https://github.com/heinrich5991/libtw2/blob/83f22dbfe713682f7c528473c2f727049928a9dd/doc/snapshot.md#snapshots 6 | seq: 7 | - id: header 8 | type: header 9 | - id: offsets 10 | type: u4 11 | repeat: expr 12 | repeat-expr: header.num_items 13 | - id: items 14 | type: item 15 | size: '(_index == header.num_items - 1 ? header.data_size : offsets[_index + 1]) - offsets[_index]' 16 | repeat: expr 17 | repeat-expr: header.num_items 18 | types: 19 | header: 20 | seq: 21 | - id: data_size 22 | type: s4 23 | - id: num_items 24 | type: s4 25 | item: 26 | seq: 27 | - id: type_id 28 | type: u2 29 | - id: id 30 | type: u2 31 | - id: data 32 | type: s4 33 | repeat: eos 34 | -------------------------------------------------------------------------------- /stats-browser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-stats-browser" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [[bin]] 10 | name = "libtw2-stats-browser" 11 | doc = false 12 | 13 | [dependencies] 14 | arrayvec = { version = "0.5.2", features = ["serde"] } 15 | base64 = "0.13.0" 16 | clap = "2.23.1" 17 | libloc = { version = "0.1.1", default-features = false, features = ["compat-0-1-1"] } 18 | libtw2-common = { path = "../common/" } 19 | libtw2-logger = { path = "../logger/" } 20 | libtw2-serverbrowse = { path = "../serverbrowse/" } 21 | log = "0.3.1" 22 | mio = "0.6.6" 23 | rand = "0.8.3" 24 | serde = "1.0.23" 25 | serde_derive = "1.0.27" 26 | serde_json = "1.0.7" 27 | time = "0.1.34" 28 | uuid = { version = "0.8.1", features = ["serde", "v4", "v5"] } 29 | zerocopy = "0.7.32" 30 | -------------------------------------------------------------------------------- /wireshark-dissector/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-wireshark-dissector" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | rust-version = "1.63.0" 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | anyhow = "1.0.38" 14 | arrayvec = "0.5.2" 15 | libtw2-common = { path = "../common/" } 16 | libtw2-gamenet-common = { path = "../gamenet/common/" } 17 | libtw2-gamenet-spec = { path = "../gamenet/spec/" } 18 | libtw2-huffman = { path = "../huffman/" } 19 | libtw2-net = { path = "../net/" } 20 | libtw2-packer = { path = "../packer/", features = ["uuid"] } 21 | libtw2-wireshark-dissector-sys = { path = "sys" } 22 | serde_json = "1.0.7" 23 | uuid = "0.8.1" 24 | warn = "0.2.2" 25 | zerocopy = "0.7.32" 26 | 27 | [build-dependencies] 28 | cc = "1.0.67" 29 | 30 | [dev-dependencies] 31 | lazy_static = "1.3.0" 32 | -------------------------------------------------------------------------------- /tools/src/client.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::net::SocketAddr; 3 | use std::net::ToSocketAddrs; 4 | use std::net::UdpSocket; 5 | 6 | fn to_socket_addr_or_panic(addr: &str) -> SocketAddr { 7 | addr.to_socket_addrs().unwrap().next().unwrap() 8 | // | | | | 9 | // io::Result Iterator Option SocketAddr 10 | } 11 | 12 | pub fn client(do_: D) { 13 | libtw2_logger::init(); 14 | 15 | let mut args = env::args(); 16 | let program_name = args.next().unwrap(); 17 | let param = args.next(); 18 | if let (Some(param), None) = (param, args.next()) { 19 | let bindaddr = "0.0.0.0:0"; 20 | let addr = to_socket_addr_or_panic(¶m); 21 | let socket = UdpSocket::bind(bindaddr).unwrap(); 22 | do_(socket, addr); 23 | } else { 24 | println!("USAGE: {} ...", program_name); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /common/src/map_iter.rs: -------------------------------------------------------------------------------- 1 | pub struct MapIterator { 2 | data: D, 3 | iterator: I, 4 | // `map` is already an function of an iterator, so we can't use `map` as a 5 | // name here. 6 | map_fn: fn(I::Item, &mut D) -> T, 7 | } 8 | 9 | impl MapIterator { 10 | pub fn new(data: D, iterator: I, map_fn: fn(I::Item, &mut D) -> T) -> MapIterator { 11 | MapIterator { 12 | data: data, 13 | iterator: iterator, 14 | map_fn: map_fn, 15 | } 16 | } 17 | } 18 | 19 | impl Iterator for MapIterator { 20 | type Item = T; 21 | fn next(&mut self) -> Option { 22 | self.iterator 23 | .next() 24 | .map(|x| (self.map_fn)(x, &mut self.data)) 25 | } 26 | fn size_hint(&self) -> (usize, Option) { 27 | self.iterator.size_hint() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /gamenet/common/src/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 2 | pub enum Error { 3 | ControlCharacters, 4 | IntOutOfRange, 5 | InvalidIntString, 6 | UnexpectedEnd, 7 | UnknownId, 8 | } 9 | 10 | pub struct InvalidIntString; 11 | 12 | impl From for Error { 13 | fn from(_: libtw2_packer::ControlCharacters) -> Error { 14 | Error::ControlCharacters 15 | } 16 | } 17 | 18 | impl From for Error { 19 | fn from(_: libtw2_packer::IntOutOfRange) -> Error { 20 | Error::IntOutOfRange 21 | } 22 | } 23 | 24 | impl From for Error { 25 | fn from(_: InvalidIntString) -> Error { 26 | Error::InvalidIntString 27 | } 28 | } 29 | 30 | impl From for Error { 31 | fn from(_: libtw2_packer::UnexpectedEnd) -> Error { 32 | Error::UnexpectedEnd 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tools/src/bin/test_gamenet_spec.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fs; 3 | use std::path::Path; 4 | use std::process; 5 | 6 | fn process(path: &Path) -> Result<(), Box> { 7 | let _spec: libtw2_gamenet_spec::Spec = serde_json::from_slice(&fs::read(path)?)?; 8 | Ok(()) 9 | } 10 | 11 | fn main() { 12 | use clap::App; 13 | use clap::Arg; 14 | 15 | libtw2_logger::init(); 16 | 17 | let matches = App::new("Gamenet spec reader") 18 | .about("Reads a gamenet spec file and does nothing with it.") 19 | .arg( 20 | Arg::with_name("SPEC") 21 | .help("Sets the gamenet spec file to read") 22 | .required(true), 23 | ) 24 | .get_matches(); 25 | 26 | let path = Path::new(matches.value_of_os("SPEC").unwrap()); 27 | 28 | match process(path) { 29 | Ok(()) => {} 30 | Err(err) => { 31 | eprintln!("{}: {:?}", path.display(), err); 32 | process::exit(1); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /_old/datafile_format.h: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #include 4 | 5 | typedef struct tw_dfr_header_ver 6 | { 7 | tw_byte magic[4]; 8 | int32_t version; 9 | } tw_dfr_header_ver; 10 | 11 | typedef struct tw_dfr_header 12 | { 13 | int32_t size; 14 | int32_t swaplen; 15 | int32_t num_item_types; 16 | int32_t num_items; 17 | int32_t num_data; 18 | int32_t size_items; 19 | int32_t size_data; 20 | } tw_dfr_header; 21 | 22 | typedef struct tw_dfr_item_type 23 | { 24 | int32_t type_id; 25 | int32_t start; 26 | int32_t num; 27 | } tw_dfr_item_type; 28 | 29 | typedef struct tw_dfr_item 30 | { 31 | int32_t type_id__id; 32 | int32_t size; 33 | } tw_dfr_item; 34 | 35 | #define TW_DFR_ITEM__TYPE_ID(type_id__id) ((type_id__id >> 16) & 0xffff) 36 | #define TW_DFR_ITEM__ID(type_id__id) (type_id__id & 0xffff) 37 | #define TW_DFR_ITEM__TYPE_ID__ID(type_id, id) ((type_id << 16) | id) 38 | 39 | static const tw_byte TW_DFR_MAGIC[] = {'D', 'A', 'T', 'A'}; 40 | static const tw_byte TW_DFR_MAGIC_BIGENDIAN[] = {'A', 'T', 'A', 'D'}; 41 | -------------------------------------------------------------------------------- /tools/src/bin/versions.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | use libtw2_datafile::Version as DfVersion; 4 | use std::io; 5 | use std::io::Write; 6 | use std::path::Path; 7 | 8 | #[derive(Default)] 9 | struct Stats { 10 | v3: u64, 11 | v4_crude: u64, 12 | v4: u64, 13 | } 14 | 15 | fn process( 16 | _: &Path, 17 | dfr: libtw2_datafile::Reader, 18 | stats: &mut Stats, 19 | ) -> Result<(), libtw2_map::Error> { 20 | match dfr.version() { 21 | DfVersion::V3 => stats.v3 += 1, 22 | DfVersion::V4Crude => stats.v4_crude += 1, 23 | DfVersion::V4 => stats.v4 += 1, 24 | } 25 | print!( 26 | "v3={} v4_crude={} v4={}\r", 27 | stats.v3, stats.v4_crude, stats.v4 28 | ); 29 | io::stdout().flush().unwrap(); 30 | Ok(()) 31 | } 32 | 33 | fn print_stats(stats: &Stats) { 34 | println!( 35 | "v3={} v4_crude={} v4={}", 36 | stats.v3, stats.v4_crude, stats.v4 37 | ); 38 | } 39 | 40 | fn main() { 41 | libtw2_tools::map_stats::stats(process, print_stats); 42 | } 43 | -------------------------------------------------------------------------------- /tools/src/bin/int_decode.rs: -------------------------------------------------------------------------------- 1 | use clap::App; 2 | use libtw2_packer::UnexpectedEnd; 3 | use libtw2_packer::Unpacker; 4 | use libtw2_tools::warn_stderr::Stderr; 5 | use std::io; 6 | use std::io::Read as _; 7 | use std::io::Write as _; 8 | 9 | fn main() -> Result<(), io::Error> { 10 | let _ = App::new("Teeworlds variable-length integer decoding") 11 | .about( 12 | "Decodes stdin as a list of Teeworlds variable-length integers\ 13 | to big-endian 32-bit integers", 14 | ) 15 | .get_matches(); 16 | 17 | let mut stdin = Vec::new(); 18 | io::stdin().read_to_end(&mut stdin)?; 19 | let mut unpacker = Unpacker::new(&stdin); 20 | let mut result = Vec::new(); 21 | while !unpacker.is_empty() { 22 | result.extend_from_slice( 23 | &unpacker 24 | .read_int(&mut Stderr) 25 | .map_err(|UnexpectedEnd| io::Error::other("unexpected end"))? 26 | .to_be_bytes(), 27 | ); 28 | } 29 | io::stdout().write_all(&result)?; 30 | Ok(()) 31 | } 32 | -------------------------------------------------------------------------------- /gamenet/common/src/snap_obj.rs: -------------------------------------------------------------------------------- 1 | use serde_derive::Deserialize; 2 | use serde_derive::Serialize; 3 | use std::fmt; 4 | use uuid::Uuid; 5 | 6 | #[derive(Clone, Copy, Deserialize, Eq, Ord, Hash, PartialEq, PartialOrd, Serialize)] 7 | #[serde(untagged)] 8 | pub enum TypeId { 9 | Ordinal(u16), 10 | Uuid(Uuid), 11 | } 12 | 13 | impl fmt::Debug for TypeId { 14 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 15 | match *self { 16 | TypeId::Ordinal(i) => i.fmt(f), 17 | TypeId::Uuid(u) => u.fmt(f), 18 | } 19 | } 20 | } 21 | 22 | impl fmt::Display for TypeId { 23 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 24 | fmt::Debug::fmt(self, f) 25 | } 26 | } 27 | 28 | impl From for TypeId { 29 | fn from(i: u16) -> TypeId { 30 | TypeId::Ordinal(i) 31 | } 32 | } 33 | 34 | impl From for TypeId { 35 | fn from(u: Uuid) -> TypeId { 36 | TypeId::Uuid(u) 37 | } 38 | } 39 | 40 | #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 41 | pub struct Tick(pub i32); 42 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /uniffi/tests/huffman.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import os.path 3 | import unittest 4 | 5 | import libtw2_huffman as huffman 6 | 7 | @functools.cache 8 | def test_cases(): 9 | with open(os.path.join(os.path.dirname(__file__), "../../huffman/data/test_cases")) as f: 10 | test_cases = f.read() 11 | return [tuple(bytes.fromhex(part) for part in line.split("#")) for line in test_cases.splitlines()] 12 | 13 | class Huffman(unittest.TestCase): 14 | def test_decompress(self): 15 | for uncompressed, compressed in test_cases(): 16 | self.assertEqual(huffman.decompress(compressed), uncompressed) 17 | 18 | def test_compress(self): 19 | for uncompressed, compressed in test_cases(): 20 | # The reference implementation sometimes adds an unnecessary null 21 | # byte at the end of the compression. 22 | our_compressed = huffman.compress(uncompressed) 23 | if len(our_compressed) + 1 == len(compressed): 24 | our_compressed += b"\x00" 25 | self.assertEqual(our_compressed, compressed) 26 | 27 | if __name__ == '__main__': 28 | unittest.main() 29 | -------------------------------------------------------------------------------- /common/src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! unwrap_or_return { 3 | ($e:expr, $r:expr) => { 4 | match $e { 5 | Some(e) => e, 6 | None => return $r, 7 | } 8 | }; 9 | ($e:expr) => { 10 | unwrap_or_return!($e, None) 11 | }; 12 | } 13 | 14 | #[macro_export] 15 | macro_rules! unwrap_or { 16 | ($e:expr, $f:expr) => { 17 | match $e { 18 | Some(e) => e, 19 | None => $f, 20 | } 21 | }; 22 | } 23 | 24 | #[macro_export] 25 | macro_rules! boilerplate_packed { 26 | ($t:ty, $size:expr, $ts:ident) => { 27 | #[test] 28 | fn $ts() { 29 | assert_eq!(::std::mem::size_of::<$t>(), $size); 30 | } 31 | impl ::libtw2_common::bytes::ByteArray for $t { 32 | type ByteArray = [u8; $size]; 33 | } 34 | }; 35 | } 36 | 37 | #[macro_export] 38 | macro_rules! boilerplate_packed_internal { 39 | ($t:ty, $size:expr, $ts:ident) => { 40 | #[test] 41 | fn $ts() { 42 | assert_eq!(::std::mem::size_of::<$t>(), $size); 43 | } 44 | impl crate::bytes::ByteArray for $t { 45 | type ByteArray = [u8; $size]; 46 | } 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /_old/error.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define tw_error_set(errno, ...) tw_error_set_impl(__FILE__, __LINE__, __FUNCTION__, (errno), __VA_ARGS__) 4 | #define tw_error_set_v(errno, va_args) tw_error_set_impl_v(__FILE__, __LINE__, __FUNCTION__, (errno), (va_args)) 5 | 6 | static const char *tw_error_string(void); 7 | static void tw_error_clear(void); 8 | 9 | static void tw_error_set_impl(const char *file, int line, const char *function, int errno, const char *fmt, ...); 10 | static void tw_error_set_impl_v(const char *file, int line, const char *function, int errno, const char *fmt, va_list va_args); 11 | 12 | int tw_error_errno(void); 13 | void tw_error_errno_set(int errno); 14 | void tw_error_errno_clear(void); 15 | 16 | static void tw_error_msg_set(const char *file, int line, const char *function, int errno, const char *fmt, ...); 17 | void tw_error_msg_set_v(const char *file, int line, const char *function, int errno, const char *fmt, va_list va_args); 18 | void tw_error_msg_clear(void); 19 | const char *tw_error_string_impl(void); 20 | 21 | enum 22 | { 23 | TW_ERRNO_NONE=0, 24 | 25 | TW_ERRNO_GENERAL=100, 26 | TW_ERRNO_TYPEERROR, 27 | TW_ERRNO_NOTIMPLEMENTED, 28 | TW_ERRNO_OUTOFRANGE, 29 | }; 30 | 31 | #include "error_inlines.h" 32 | -------------------------------------------------------------------------------- /_old/common.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef unsigned char tw_byte; 5 | 6 | static void tw_str_copy(char *dst, const char *src, size_t dst_size); 7 | static void tw_str_append(char *dst, const char *src, size_t dst_size); 8 | static int tw_str_comp(const char *str1, const char *str2); 9 | static size_t tw_str_length(const char *str); 10 | static void tw_str_format(char *dst, size_t dst_size, const char *fmt, ...); 11 | static void tw_str_format_v(char *dst, size_t dst_size, const char *fmt, va_list va_args); 12 | 13 | static void tw_mem_copy(void *dst, const void *src, size_t size); 14 | static void tw_mem_move(void *dst, const void *src, size_t size); 15 | static int tw_mem_comp(const void *mem1, const void *mem2, size_t size); 16 | static void tw_mem_zero(void *mem, size_t size); 17 | 18 | void tw_endian_swap(void *data, size_t size, int count); 19 | 20 | static void tw_endian_tolittle(void *data, size_t size, int count); 21 | static void tw_endian_tobig(void *data, size_t size, int count); 22 | static void tw_endian_fromlittle(void *data, size_t size, int count); 23 | static void tw_endian_frombig(void *data, size_t size, int count); 24 | 25 | enum 26 | { 27 | TW_BUFSIZE=4096, 28 | }; 29 | 30 | #include "common_inlines.h" 31 | -------------------------------------------------------------------------------- /tools/src/bin/servercount.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | #[macro_use] 4 | extern crate log; 5 | 6 | use libtw2_serverbrowse::protocol as browse_protocol; 7 | use libtw2_serverbrowse::protocol::CountResponse; 8 | use libtw2_serverbrowse::protocol::Response; 9 | use std::net::SocketAddr; 10 | use std::net::UdpSocket; 11 | 12 | const BUFSIZE: usize = 2048; 13 | 14 | fn do_(socket: UdpSocket, addr: SocketAddr) { 15 | let mut buf = [0; BUFSIZE]; 16 | 17 | socket 18 | .send_to(&browse_protocol::request_count(), addr) 19 | .unwrap(); 20 | 21 | loop { 22 | let (len, from) = socket.recv_from(&mut buf).unwrap(); 23 | if from != addr { 24 | error!( 25 | "received response from non-peer, wanted={} got={}", 26 | addr, from 27 | ); 28 | continue; 29 | } 30 | 31 | match browse_protocol::parse_response(&buf[..len]) { 32 | Some(Response::Count(CountResponse(x))) => { 33 | println!("{}", x); 34 | break; 35 | } 36 | _ => { 37 | error!("received non-info response from peer"); 38 | } 39 | } 40 | } 41 | } 42 | 43 | fn main() { 44 | libtw2_tools::client::client(do_); 45 | } 46 | -------------------------------------------------------------------------------- /_old/error_inlines.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // define the light versions of the functions if TW_ERROR isn't set 4 | static inline void tw_error_set_impl(const char *file, int line, const char *function, int errno, const char *fmt, ...) 5 | { 6 | va_list va_args; 7 | va_start(va_args, fmt); 8 | tw_error_set_impl_v(file, line, function, errno, fmt, va_args); 9 | va_end(va_args); 10 | } 11 | 12 | static inline void tw_error_set_impl_v(const char *file, int line, const char *function, int errno, const char *fmt, va_list va_args) 13 | { 14 | tw_error_errno_set(errno); 15 | #ifdef TW_ERROR 16 | tw_error_msg_set_v(file, line, function, errno, fmt, va_args); 17 | #else 18 | (void)file; (void)line; (void)function; (void)fmt; (void)va_args; 19 | #endif 20 | } 21 | 22 | static inline void tw_error_msg_set(const char *file, int line, const char *function, int errno, const char *fmt, ...) 23 | { 24 | va_list va_args; 25 | va_start(va_args, fmt); 26 | tw_error_msg_set_v(file, line, function, errno, fmt, va_args); 27 | va_end(va_args); 28 | } 29 | 30 | static inline const char *tw_error_string(void) 31 | { 32 | return tw_error_string_impl(); 33 | } 34 | 35 | static inline void tw_error_clear(void) 36 | { 37 | tw_error_errno_clear(); 38 | #ifdef TW_ERROR 39 | tw_error_msg_clear(); 40 | #endif 41 | } 42 | -------------------------------------------------------------------------------- /common/src/takeable.rs: -------------------------------------------------------------------------------- 1 | use std::ops; 2 | 3 | pub struct Takeable { 4 | inner: Option, 5 | } 6 | 7 | impl Default for Takeable { 8 | fn default() -> Takeable { 9 | Takeable::new(Default::default()) 10 | } 11 | } 12 | 13 | impl Takeable { 14 | pub fn new(value: T) -> Takeable { 15 | Takeable { inner: Some(value) } 16 | } 17 | pub fn empty() -> Takeable { 18 | Takeable { inner: None } 19 | } 20 | pub fn take(&mut self) -> T { 21 | self.inner 22 | .take() 23 | .unwrap_or_else(|| panic!("value taken when absent")) 24 | } 25 | pub fn restore(&mut self, value: T) { 26 | assert!(self.inner.is_none(), "value restored when already present"); 27 | self.inner = Some(value); 28 | } 29 | } 30 | 31 | impl ops::Deref for Takeable { 32 | type Target = T; 33 | fn deref(&self) -> &T { 34 | self.inner 35 | .as_ref() 36 | .unwrap_or_else(|| panic!("value borrowed when absent")) 37 | } 38 | } 39 | 40 | impl ops::DerefMut for Takeable { 41 | fn deref_mut(&mut self) -> &mut T { 42 | self.inner 43 | .as_mut() 44 | .unwrap_or_else(|| panic!("value mutably borrowed when absent")) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /doc/packet.md: -------------------------------------------------------------------------------- 1 | This document describes packet headers and chunk headers in Teeworlds up to 2 | 0.6.x and in DDNet. 3 | 4 | All sizes in bits. 5 | 6 | packet_header: 7 | [ 1] flag_compression 8 | [ 1] flag_request_resend 9 | [ 1] flag_connless 10 | [ 1] flag_control 11 | [ 2] padding 12 | [10] ack 13 | [ 8] num_chunks 14 | 15 | FFFF ppAA AAAA AAAA nnnn nnnn 16 | 17 | NOTE: `padding` must be zeroed, it's incorrectly used as part of the `ack` 18 | field while unpacking in the reference implementation. 19 | 20 | chunk_header_nonvital: 21 | [ 1] flag_resend 22 | [ 1] flag_vital 23 | [ 6] <---------- 24 | [ 4] padding |-- size 25 | [ 4] <---------- 26 | 27 | FFss ssss PPPP ssss 28 | 29 | 30 | chunk_header_vital: 31 | [ 1] flag_resend 32 | [ 1] flag_vital 33 | [ 6] <---------- 34 | [ 4] sequence |-- size 35 | [ 4] <---------- 36 | [ 8] sequence part 2 37 | 38 | FFss ssss SSSS ssss SSSS SSSS 39 | 40 | In the packed form, the first four bits of sequence correspond to bits 10 to 6, 41 | and the second part corresponds to the bits 8 to 1. 42 | 43 | NOTE: The reference implementation just uses bitwise-or to resolve 44 | contradictions in the overlapping bits. 45 | -------------------------------------------------------------------------------- /tools/src/bin/serverlist.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | #[macro_use] 4 | extern crate log; 5 | 6 | use libtw2_serverbrowse::protocol as browse_protocol; 7 | use libtw2_serverbrowse::protocol::List6Response; 8 | use libtw2_serverbrowse::protocol::Response; 9 | use std::net::SocketAddr; 10 | use std::net::UdpSocket; 11 | 12 | const BUFSIZE: usize = 2048; 13 | 14 | fn do_(socket: UdpSocket, addr: SocketAddr) { 15 | let mut buf = [0; BUFSIZE]; 16 | 17 | socket 18 | .send_to(&browse_protocol::request_list_6(), addr) 19 | .unwrap(); 20 | 21 | loop { 22 | let (len, from) = socket.recv_from(&mut buf).unwrap(); 23 | if from != addr { 24 | error!( 25 | "received response from non-peer, wanted={} got={}", 26 | addr, from 27 | ); 28 | continue; 29 | } 30 | 31 | match browse_protocol::parse_response(&buf[..len]) { 32 | Some(Response::List6(List6Response(list))) => { 33 | for &s in list.iter() { 34 | println!("{}", s.unpack()); 35 | } 36 | } 37 | _ => { 38 | error!("received non-list response from peer"); 39 | } 40 | } 41 | } 42 | } 43 | 44 | fn main() { 45 | libtw2_tools::client::client(do_); 46 | } 47 | -------------------------------------------------------------------------------- /tools/src/bin/gamelayers.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | use libtw2_datafile as df; 4 | use std::path::Path; 5 | 6 | #[derive(Default)] 7 | struct Stats { 8 | game: u64, 9 | teleport: u64, 10 | speedup: u64, 11 | front: u64, 12 | switch: u64, 13 | tune: u64, 14 | } 15 | 16 | fn process(_: &Path, dfr: df::Reader, stats: &mut Stats) -> Result<(), libtw2_map::Error> { 17 | let map = libtw2_map::Reader::from_datafile(dfr); 18 | let game_layers = map.game_layers()?; 19 | stats.game += 1; 20 | if game_layers.teleport_raw.is_some() { 21 | stats.teleport += 1; 22 | } 23 | if game_layers.speedup_raw.is_some() { 24 | stats.speedup += 1; 25 | } 26 | if game_layers.front_raw.is_some() { 27 | stats.front += 1; 28 | } 29 | if game_layers.switch_raw.is_some() { 30 | stats.switch += 1; 31 | } 32 | if game_layers.tune_raw.is_some() { 33 | stats.tune += 1; 34 | } 35 | Ok(()) 36 | } 37 | 38 | fn print_stats(stats: &Stats) { 39 | println!("game: {}", stats.game); 40 | println!("teleport: {}", stats.teleport); 41 | println!("speedup: {}", stats.speedup); 42 | println!("front: {}", stats.front); 43 | println!("switch: {}", stats.switch); 44 | println!("tune: {}", stats.tune); 45 | } 46 | 47 | fn main() { 48 | libtw2_tools::map_stats::stats(process, print_stats); 49 | } 50 | -------------------------------------------------------------------------------- /tools/src/bin/serverinfo.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | #[macro_use] 4 | extern crate log; 5 | 6 | use libtw2_serverbrowse::protocol as browse_protocol; 7 | use libtw2_serverbrowse::protocol::Response; 8 | use std::net::SocketAddr; 9 | use std::net::UdpSocket; 10 | 11 | const BUFSIZE: usize = 2048; 12 | 13 | fn do_(socket: UdpSocket, addr: SocketAddr) { 14 | let mut buf = [0; BUFSIZE]; 15 | 16 | socket 17 | .send_to(&browse_protocol::request_info_6(0), addr) 18 | .unwrap(); 19 | 20 | loop { 21 | let (len, from) = socket.recv_from(&mut buf).unwrap(); 22 | if from != addr { 23 | error!( 24 | "received response from non-peer, wanted={} got={}", 25 | addr, from 26 | ); 27 | continue; 28 | } 29 | 30 | match browse_protocol::parse_response(&buf[..len]) { 31 | Some(Response::Info6(x)) => { 32 | println!("{:?}", x.parse().unwrap()); 33 | break; 34 | } 35 | Some(Response::Info6Ddper(x)) => { 36 | println!("{:?}", x.parse().unwrap()); 37 | break; 38 | } 39 | _ => { 40 | error!("received non-info response from peer"); 41 | } 42 | } 43 | } 44 | } 45 | 46 | fn main() { 47 | libtw2_tools::client::client(do_); 48 | } 49 | -------------------------------------------------------------------------------- /stats-browser/src/hashmap_ext.rs: -------------------------------------------------------------------------------- 1 | use std::collections::hash_map; 2 | 3 | /// Convenience trait for getting the `occupied` or `vacant` variant out of a 4 | /// hashmap's entry. 5 | pub trait HashMapEntryIntoInner<'a> { 6 | type Key; 7 | type Value; 8 | fn into_occupied( 9 | self, 10 | ) -> Option< 11 | hash_map::OccupiedEntry< 12 | 'a, 13 | >::Key, 14 | >::Value, 15 | >, 16 | >; 17 | fn into_vacant( 18 | self, 19 | ) -> Option< 20 | hash_map::VacantEntry< 21 | 'a, 22 | >::Key, 23 | >::Value, 24 | >, 25 | >; 26 | } 27 | 28 | impl<'a, K, V> HashMapEntryIntoInner<'a> for hash_map::Entry<'a, K, V> { 29 | type Key = K; 30 | type Value = V; 31 | fn into_occupied(self) -> Option> { 32 | match self { 33 | hash_map::Entry::Occupied(o) => Some(o), 34 | hash_map::Entry::Vacant(_) => None, 35 | } 36 | } 37 | fn into_vacant(self) -> Option> { 38 | match self { 39 | hash_map::Entry::Occupied(_) => None, 40 | hash_map::Entry::Vacant(v) => Some(v), 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /_old/gamemap.c: -------------------------------------------------------------------------------- 1 | /*#include "gamemap.h" 2 | 3 | #include "datafile.h" 4 | 5 | struct tw_map 6 | { 7 | tw_datafile *df; 8 | }; 9 | 10 | typedef struct tw_map_fixed // fixed point - 22.10 11 | { 12 | int32_t value; 13 | } tw_map_fixed; 14 | 15 | // TW_MAP_ITEMTYPE_VERSION 16 | typedef struct tw_map_itemtype_version 17 | { 18 | int32_t version; 19 | } tw_map_itemtype_version; 20 | 21 | // TW_MAP_ITEMTYPE_INFO 22 | typedef struct tw_map_itemtype_info_v1 23 | { 24 | int32_t version; 25 | int32_t map_author; 26 | int32_t map_version; 27 | int32_t map_credits; 28 | int32_t map_license; 29 | } tw_map_itemtype_info_v1; 30 | 31 | typedef union tw_map_itemtype_info 32 | { 33 | int32_t version; 34 | tw_map_itemtype_info_v1 v1; 35 | } tw_map_itemtype_info; 36 | 37 | // TW_MAP_ITEMTYPE_IMAGE 38 | typedef struct tw_map_itemtype_image_v1 39 | { 40 | int32_t version; 41 | int32_t width; 42 | int32_t height; 43 | int32_t external; 44 | int32_t name; 45 | int32_t data; 46 | } tw_map_itemtype_image_v1; 47 | 48 | typedef struct tw_map_itemtype_image_v2 49 | { 50 | int32_t version; 51 | int32_t width; 52 | int32_t height; 53 | int32_t external; 54 | int32_t name; 55 | int32_t data; 56 | int32_t format; 57 | } tw_map_itemtype_image_v2; 58 | 59 | typedef union tw_map_itemtype_image 60 | { 61 | int32_t version; 62 | tw_map_itemtype_image_v1 v1; 63 | tw_map_itemtype_image_v2 v2; 64 | } tw_map_itemtype_image; 65 | */ 66 | -------------------------------------------------------------------------------- /tools/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtw2-tools" 3 | version = "0.0.1" 4 | authors = ["heinrich5991 "] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | 8 | [dependencies] 9 | arrayvec = "0.5.2" 10 | buffer = "0.1.9" 11 | chrono = { version = "0.4.0", features = ["serde"] } 12 | clap = "2.23.1" 13 | csv = "1.1.6" 14 | hexdump = "0.1.1" 15 | itertools = "0.7.4" 16 | libtw2-common = { path = "../common/" } 17 | libtw2-datafile = { path = "../datafile/" } 18 | libtw2-demo = { path = "../demo/" } 19 | libtw2-gamenet-ddnet = { path = "../gamenet/ddnet/" } 20 | libtw2-gamenet-spec = { path = "../gamenet/spec/" } 21 | libtw2-gamenet-teeworlds-0-6 = { path = "../gamenet/teeworlds-0.6/" } 22 | libtw2-gamenet-teeworlds-0-7 = { path = "../gamenet/teeworlds-0.7/" } 23 | libtw2-logger = { path = "../logger/" } 24 | libtw2-map = { path = "../map/" } 25 | libtw2-net = { path = "../net/" } 26 | libtw2-packer = { path = "../packer/", features = ["uuid"] } 27 | libtw2-serverbrowse = { path = "../serverbrowse/" } 28 | libtw2-snapshot = { path = "../snapshot/" } 29 | libtw2-teehistorian = { path = "../teehistorian/" } 30 | libtw2-world = { path = "../world/" } 31 | log = "0.3.1" 32 | rmp = "0.8.5" 33 | serde = "1.0.23" 34 | serde_derive = "1.0.27" 35 | serde_json = "1.0.7" 36 | uuid = { version = "0.8.1", features = ["serde"] } 37 | vec_map = "0.8.0" 38 | void = "1.0.2" 39 | walkdir = "2.0.1" 40 | warn = "0.2.2" 41 | -------------------------------------------------------------------------------- /tools/src/bin/entities.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | use libtw2_common::num::Cast; 4 | use libtw2_datafile as df; 5 | use libtw2_tools::map_stats::Entity; 6 | use std::path::Path; 7 | 8 | pub struct Stats { 9 | tiles: [u64; 256], 10 | } 11 | 12 | impl Default for Stats { 13 | fn default() -> Stats { 14 | Stats { tiles: [0; 256] } 15 | } 16 | } 17 | 18 | fn process(path: &Path, dfr: df::Reader, stats: &mut Stats) -> Result<(), libtw2_map::Error> { 19 | let mut map = libtw2_map::Reader::from_datafile(dfr); 20 | let game_layers = map.game_layers()?; 21 | let mut tiles_count = [0u64; 256]; 22 | 23 | let tiles = map.layer_tiles(game_layers.game())?; 24 | for tile in tiles.iter() { 25 | tiles_count[tile.index.usize()] += 1; 26 | stats.tiles[tile.index.usize()] += 1; 27 | } 28 | 29 | println!("{}", path.to_string_lossy()); 30 | for (i, &c) in tiles_count.iter().enumerate() { 31 | let entity = Entity(i.assert_u8()); 32 | if c != 0 { 33 | println!("{}: {:5}", entity, c); 34 | } 35 | } 36 | Ok(()) 37 | } 38 | 39 | fn print_stats(stats: &Stats) { 40 | for (i, &c) in stats.tiles.iter().enumerate() { 41 | let entity = Entity(i.assert_u8()); 42 | if c != 0 { 43 | println!("{}: {:5}", entity, c); 44 | } 45 | } 46 | } 47 | 48 | fn main() { 49 | libtw2_tools::map_stats::stats(process, print_stats); 50 | } 51 | -------------------------------------------------------------------------------- /tools/src/bin/jungle.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | use libtw2_datafile as df; 4 | use libtw2_map::reader; 5 | use std::collections::HashMap; 6 | use std::path::Path; 7 | 8 | fn process( 9 | _: &Path, 10 | dfr: df::Reader, 11 | tilesets: &mut HashMap, u64>, 12 | ) -> Result<(), libtw2_map::Error> { 13 | let mut map = libtw2_map::Reader::from_datafile(dfr); 14 | for i in map.group_indices() { 15 | let group = map.group(i)?; 16 | for k in group.layer_indices.clone() { 17 | let layer = map.layer(k)?; 18 | let image_index = if let Some(i) = match layer.t { 19 | reader::LayerType::Quads(q) => q.image, 20 | reader::LayerType::Tilemap(t) => t.type_.to_normal().and_then(|n| n.image), 21 | reader::LayerType::DdraceSounds(_) => continue, 22 | } { 23 | i 24 | } else { 25 | continue; 26 | }; 27 | let image = map.image(image_index)?; 28 | let name = map.image_name(image.name)?; 29 | *tilesets.entry(name).or_insert(0) += 1; 30 | } 31 | } 32 | Ok(()) 33 | } 34 | 35 | fn print_stats(tilesets: &HashMap, u64>) { 36 | for (name, &c) in tilesets.iter() { 37 | println!("{:14} {:5}", String::from_utf8_lossy(name), c); 38 | } 39 | } 40 | 41 | fn main() { 42 | libtw2_tools::map_stats::stats(process, print_stats); 43 | } 44 | -------------------------------------------------------------------------------- /common/src/str.rs: -------------------------------------------------------------------------------- 1 | use arrayvec::Array; 2 | use arrayvec::ArrayString; 3 | 4 | pub fn truncated_arraystring + Copy>(mut s: &str) -> ArrayString { 5 | let mut result = ArrayString::new(); 6 | if s.len() > result.capacity() { 7 | for n in (0..result.capacity() + 1).rev() { 8 | if s.is_char_boundary(n) { 9 | s = &s[..n]; 10 | break; 11 | } 12 | } 13 | } 14 | result.push_str(s); 15 | result 16 | } 17 | 18 | #[cfg(test)] 19 | mod test { 20 | use super::truncated_arraystring; 21 | use quickcheck::quickcheck; 22 | quickcheck! { 23 | fn truncated_arraystring0(v: String) -> bool { 24 | truncated_arraystring::<[u8; 0]>(&v); 25 | true 26 | } 27 | fn truncated_arraystring1(v: String) -> bool { 28 | truncated_arraystring::<[u8; 1]>(&v); 29 | true 30 | } 31 | fn truncated_arraystring2(v: String) -> bool { 32 | truncated_arraystring::<[u8; 2]>(&v); 33 | true 34 | } 35 | fn truncated_arraystring4(v: String) -> bool { 36 | truncated_arraystring::<[u8; 4]>(&v); 37 | true 38 | } 39 | fn truncated_arraystring8(v: String) -> bool { 40 | truncated_arraystring::<[u8; 8]>(&v); 41 | true 42 | } 43 | fn truncated_arraystring16(v: String) -> bool { 44 | truncated_arraystring::<[u8; 16]>(&v); 45 | true 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tools/src/bin/integer.rs: -------------------------------------------------------------------------------- 1 | use arrayvec::ArrayVec; 2 | use clap::App; 3 | use clap::Arg; 4 | use libtw2_packer::with_packer; 5 | use libtw2_packer::Unpacker; 6 | use libtw2_tools::unhexdump::Unhexdump; 7 | use libtw2_tools::warn_stdout::Stdout; 8 | 9 | fn main() { 10 | let matches = App::new("Teeworlds variable-length integer encoding") 11 | .about( 12 | "Takes an integer and writes out its Teeworlds variable-length\ 13 | or vice versa", 14 | ) 15 | .arg( 16 | Arg::with_name("decode").short("d").long("decode").help( 17 | "Decode a hexdump instead, hexdump must be enclosed in pipes,\ 18 | sorry :(", 19 | ), // TODO 20 | ) 21 | .arg( 22 | Arg::with_name("INTEGER") 23 | .help("The integer to de-/encode") 24 | .required(true), 25 | ) 26 | .get_matches(); 27 | let integer = matches.value_of("INTEGER").unwrap(); 28 | if !matches.is_present("decode") { 29 | let integer: i32 = integer.parse().expect("invalid input"); 30 | let mut out: ArrayVec<[u8; 32]> = ArrayVec::new(); 31 | with_packer(&mut out, |mut p| p.write_int(integer)).unwrap(); 32 | hexdump::hexdump(&out); 33 | } else { 34 | let mut un = Unhexdump::new(); 35 | un.feed(integer.as_bytes()).unwrap(); 36 | let encoded = un.into_inner().unwrap(); 37 | let mut up = Unpacker::new(&encoded); 38 | println!("{}", up.read_int(&mut Stdout).unwrap()); 39 | up.finish(&mut Stdout); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /stats-browser/src/config.rs: -------------------------------------------------------------------------------- 1 | use crate::time::Duration; 2 | 3 | /// Maximum number of malformed responses to report per server. 4 | pub const MAX_MALFORMED_RESP: u32 = 10; 5 | /// Maximum number of excess responses to report per server. 6 | pub const MAX_EXTRA_RESP: u32 = 10; 7 | /// Maximum number of responses with invalid token to report per server. 8 | pub const MAX_INVALID_RESP: u32 = 10; 9 | /// Maximum number of excess token responses to report per server. 10 | pub const MAX_EXTRA_TOKEN: u32 = 10; 11 | /// Maximum number of token responses with invalid token to report per server. 12 | pub const MAX_INVALID_TOKEN: u32 = 10; 13 | /// Maximum number of list requests per time span. 14 | pub const MAX_LISTS: u32 = 1; 15 | /// Maximum number of info requests per time span. 16 | pub const MAX_INFOS: u32 = 10; 17 | /// Time span for `MAX_LISTS`. 18 | pub const MAX_LISTS_MS: Duration = Duration(1_000); 19 | /// Time span for `MAX_INFOS`. 20 | pub const MAX_INFOS_MS: Duration = Duration(25); 21 | /// Time span in which info responses are expected. 22 | pub const INFO_EXPECT_MS: Duration = Duration(1_000); 23 | /// Time span after which a successful info request is repeated. 24 | pub const INFO_REPEAT_MS: Duration = Duration(5_000); 25 | /// Time span in which list responses are expected. 26 | pub const LIST_EXPECT_MS: Duration = Duration(5_000); 27 | /// Time span after which a successful list request is repeated. 28 | pub const LIST_REPEAT_MS: Duration = Duration(30_000); 29 | /// Time span for re-resolving master servers. 30 | pub const RESOLVE_REPEAT_MS: Duration = Duration(120_000); 31 | /// Sleep time in the main loop. 32 | pub const SLEEP_MS: Duration = Duration(5); 33 | -------------------------------------------------------------------------------- /common/benches/cast.rs: -------------------------------------------------------------------------------- 1 | use bencher::benchmark_group; 2 | use bencher::benchmark_main; 3 | use bencher::black_box; 4 | use bencher::Bencher; 5 | use libtw2_common::num::Cast; 6 | 7 | fn u32_assert_u8(bench: &mut Bencher) { 8 | fn helper() { 9 | black_box(black_box(0u32).assert_u8()); 10 | } 11 | bench.iter(helper); 12 | } 13 | 14 | fn u32_assert_u8_inline(bench: &mut Bencher) { 15 | fn helper() { 16 | let i = black_box(0u32); 17 | if i >= 256 { 18 | panic!(); 19 | } else { 20 | black_box(i as u8); 21 | } 22 | } 23 | bench.iter(helper); 24 | } 25 | 26 | fn u32_try_u8(bench: &mut Bencher) { 27 | fn helper() { 28 | match black_box(0u32).try_u8() { 29 | Some(x) => { 30 | black_box(x); 31 | } 32 | None => {} 33 | } 34 | } 35 | bench.iter(helper); 36 | } 37 | 38 | fn u32_try_u8_inline(bench: &mut Bencher) { 39 | fn helper() { 40 | let i = black_box(0u32); 41 | if i < 256 { 42 | black_box(i); 43 | } 44 | } 45 | bench.iter(helper); 46 | } 47 | 48 | fn u8_u32(bench: &mut Bencher) { 49 | fn helper() { 50 | black_box(black_box(0u8).u32()); 51 | } 52 | bench.iter(helper); 53 | } 54 | 55 | fn u8_u32_inline(bench: &mut Bencher) { 56 | fn helper() { 57 | black_box(black_box(0u8) as u32); 58 | } 59 | bench.iter(helper); 60 | } 61 | 62 | benchmark_group!( 63 | cast, 64 | u32_assert_u8, 65 | u32_assert_u8_inline, 66 | u32_try_u8, 67 | u32_try_u8_inline, 68 | u8_u32, 69 | u8_u32_inline, 70 | ); 71 | benchmark_main!(cast); 72 | -------------------------------------------------------------------------------- /httphook-ldpreload/src/leaky_vec.rs: -------------------------------------------------------------------------------- 1 | use std::ops; 2 | use std::ptr; 3 | use std::sync::atomic; 4 | use std::sync::atomic::AtomicPtr; 5 | use std::sync::Mutex; 6 | 7 | pub struct LeakyVec { 8 | write: Mutex>, 9 | read: AtomicPtr<&'static [T]>, 10 | } 11 | 12 | fn leak(elements: &[T]) -> &'static mut &'static [T] { 13 | Box::leak(Box::new(Box::leak(elements.to_vec().into_boxed_slice()))) 14 | } 15 | 16 | impl LeakyVec { 17 | pub const fn new() -> LeakyVec { 18 | LeakyVec { 19 | write: Mutex::new(Vec::new()), 20 | read: AtomicPtr::new(ptr::null_mut()), 21 | } 22 | } 23 | pub fn push_and_commit(&self, element: T) { 24 | let mut write = self.write.lock().unwrap(); 25 | write.push(element); 26 | self.read.store(leak(&write), atomic::Ordering::Release); 27 | } 28 | #[allow(dead_code)] // TODO (MSRV 1.63): Replace with #[expect(dead_code)]. 29 | pub fn push(&self, element: T) { 30 | let mut write = self.write.lock().unwrap(); 31 | write.push(element); 32 | } 33 | #[allow(dead_code)] // TODO (MSRV 1.63): Replace with #[expect(dead_code)]. 34 | pub fn commit(&self) { 35 | let write = self.write.lock().unwrap(); 36 | self.read.store(leak(&write), atomic::Ordering::Release); 37 | } 38 | } 39 | 40 | impl ops::Deref for LeakyVec { 41 | type Target = [T]; 42 | fn deref(&self) -> &[T] { 43 | let read = self.read.load(atomic::Ordering::Acquire); 44 | if read.is_null() { 45 | return &[]; 46 | } 47 | unsafe { *read } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tools/src/bin/author.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | use libtw2_datafile as df; 4 | use libtw2_map::format; 5 | use std::path::Path; 6 | 7 | #[derive(Default)] 8 | struct Stats { 9 | author: u64, 10 | version: u64, 11 | credits: u64, 12 | license: u64, 13 | settings: u64, 14 | info: u64, 15 | total: u64, 16 | } 17 | 18 | fn process(_: &Path, dfr: df::Reader, stats: &mut Stats) -> Result<(), libtw2_map::Error> { 19 | let map = libtw2_map::Reader::from_datafile(dfr); 20 | let info = match map.info() { 21 | Ok(i) => i, 22 | Err(format::Error::MissingInfo) => { 23 | stats.total += 1; 24 | return Ok(()); 25 | } 26 | Err(e) => return Err(e.into()), 27 | }; 28 | if info.author.is_some() { 29 | stats.author += 1; 30 | } 31 | if info.version.is_some() { 32 | stats.version += 1; 33 | } 34 | if info.credits.is_some() { 35 | stats.credits += 1; 36 | } 37 | if info.license.is_some() { 38 | stats.license += 1; 39 | } 40 | if info.settings.is_some() { 41 | stats.settings += 1; 42 | } 43 | stats.info += 1; 44 | stats.total += 1; 45 | Ok(()) 46 | } 47 | 48 | fn print_stats(stats: &Stats) { 49 | println!("author: {:5}", stats.author); 50 | println!("version: {:5}", stats.version); 51 | println!("credits: {:5}", stats.credits); 52 | println!("license: {:5}", stats.license); 53 | println!("settings: {:5}", stats.settings); 54 | println!("info: {:5}", stats.info); 55 | println!("total: {:5}", stats.total); 56 | } 57 | 58 | fn main() { 59 | libtw2_tools::map_stats::stats(process, print_stats); 60 | } 61 | -------------------------------------------------------------------------------- /common/src/bytes.rs: -------------------------------------------------------------------------------- 1 | use std::array::TryFromSliceError; 2 | use std::convert::TryInto; 3 | use std::mem; 4 | use zerocopy::byteorder::big_endian; 5 | 6 | pub trait TryFromByteSlice { 7 | fn try_from_byte_slice(bytes: &[u8]) -> Result<&Self, TryFromSliceError>; 8 | } 9 | 10 | impl TryFromByteSlice for [u8; N] { 11 | fn try_from_byte_slice(bytes: &[u8]) -> Result<&[u8; N], TryFromSliceError> { 12 | bytes.try_into() 13 | } 14 | } 15 | 16 | pub trait ByteArray { 17 | type ByteArray: AsRef<[u8]> + TryFromByteSlice; 18 | } 19 | 20 | pub trait AsBytesExt: ByteArray + zerocopy::AsBytes { 21 | fn as_byte_array(&self) -> &Self::ByteArray { 22 | TryFromByteSlice::try_from_byte_slice(self.as_bytes()).unwrap() 23 | } 24 | } 25 | 26 | pub trait FromBytesExt: ByteArray + zerocopy::FromBytes { 27 | fn ref_and_rest_from(bytes: &[u8]) -> Option<(&Self, &[u8])> 28 | where 29 | Self: Sized, 30 | { 31 | if bytes.len() < mem::size_of::() { 32 | return None; 33 | } 34 | let (result, rest) = bytes.split_at(mem::size_of::()); 35 | Some((Self::ref_from(result).unwrap(), rest)) 36 | } 37 | fn ref_from_array(bytes: &Self::ByteArray) -> &Self 38 | where 39 | Self: Sized, 40 | { 41 | Self::ref_from(bytes.as_ref()).unwrap() 42 | } 43 | fn from_array(bytes: Self::ByteArray) -> Self 44 | where 45 | Self: Copy + Sized, 46 | { 47 | *Self::ref_from_array(&bytes) 48 | } 49 | } 50 | 51 | impl AsBytesExt for T {} 52 | impl FromBytesExt for T {} 53 | 54 | boilerplate_packed_internal!(big_endian::U32, 4, test_be_u32_size); 55 | -------------------------------------------------------------------------------- /doc/packet7.md: -------------------------------------------------------------------------------- 1 | This document describes packet headers and chunk headers in Teeworlds 0.7.0+. 2 | 3 | All sizes in bits. 4 | 5 | packet7_header: 6 | [ 2] reserved 7 | [ 1] flag_connless 8 | [ 1] flag_compression 9 | [ 1] flag_request_resend 10 | [ 1] flag_control 11 | [10] ack 12 | [ 8] num_chunks 13 | [32] token 14 | 15 | RRff ffAA AAAA AAAA nnnn nnnn 16 | TTTT TTTT TTTT TTTT TTTT TTTT TTTT TTTT 17 | 18 | packet7_header_connless: 19 | [ 2] reserved 20 | [ 1] flag_connless 21 | [ 1] flag_compression 22 | [ 1] flag_request_resend 23 | [ 1] flag_control 24 | [ 2] version 25 | [32] token 26 | [32] response_token 27 | 28 | RRff ffVV 29 | TTTT TTTT TTTT TTTT TTTT TTTT TTTT TTTT 30 | rrrr rrrr rrrr rrrr rrrr rrrr rrrr rrrr 31 | 32 | NOTE: `padding` must be zeroed. If `flag_connless` is set, the other flags must 33 | not be set. `flag_control` implies `!flag_compression`. 34 | 35 | NOTE: In `packet7_header_connless`, `version` must be set to 1. 36 | 37 | chunk7_header_nonvital: 38 | [ 1] flag_resend 39 | [ 1] flag_vital 40 | [ 6] <---------- 41 | [ 2] padding |-- size 42 | [ 6] <---------- 43 | 44 | FFss ssss PPss ssss 45 | 46 | 47 | chunk7_header_vital: 48 | [ 1] flag_resend 49 | [ 1] flag_vital 50 | [ 6] <---------- 51 | [ 2] sequence |-- size 52 | [ 6] <---------- 53 | [ 8] sequence 54 | 55 | FFss ssss SSss ssss SSSS SSSS 56 | 57 | Unlike the `chunk_header_vital` in the Teeworlds 0.6/DDNet protocol, the 58 | sequence field does not have overlapping bits. 59 | -------------------------------------------------------------------------------- /doc/int.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | The Teeworlds protocol has several where 32-bit signed integers are encoded 5 | using a variable-length encoding. This documents describes this encoding. 6 | 7 | Format 8 | ====== 9 | 10 | All sizes are specified in bits, from the highest bit in a byte to the lowest. 11 | 12 | first_byte: 13 | [1] flag_extend 14 | [1] flag_sign 15 | [6] bits 16 | 17 | next_byte: 18 | [1] flag_extend 19 | [7] bits 20 | 21 | last_byte: 22 | [4] padding 23 | [4] bits 24 | 25 | int: 26 | first_byte [next_byte [next_byte [next_byte [last_byte]]]] 27 | 28 | As specified, an encoded `int` can have a size of 1 to 5 bytes. Some bytes have 29 | the `flag_extend` variable which specifies whether they're followed by another 30 | byte. The bits of the final integer are the `bits` fields combined, with a 31 | little-endian order. If we call our bits 0 to r, with 0 being the least 32 | significant bit, then it looks like this: 33 | 34 | ES54 3210 Ecba 9876 Ejih gfed Eqpo nmlk PPPP utsr 35 | ^^ ^^^^ ^^^ ^^^^ ^^^ ^^^^ ^^^ ^^^^ ^^^^ 36 | 37 | Always use the least amount of bytes possible to encode a number. The padding 38 | must always be zeroed. 39 | 40 | The `flag_sign` specifies that all the bits of the resulting number should be 41 | flipped, including the sign bit. 42 | 43 | NOTE: The reference implementation has no problem with accepting an overlong 44 | representation. The reference implementation also interprets the padding as 45 | part of the number, which leads to weird results. It should always be zeroed. 46 | 47 | Examples 48 | ======== 49 | 50 | 0 is encoded as `0000 0000`. 1 is encoded as `0000 0001`. -1 is encoded as 51 | `0100 0000` (note that the `bits` field is all zeros). 52 | 53 | 64 is encoded as `1000 0000 0000 0001`. 54 | -------------------------------------------------------------------------------- /datafile/src/writer.rs: -------------------------------------------------------------------------------- 1 | /* 2 | pub fn write_datafile(df: &T, writer: &mut W) -> Result,()> { 3 | let compressed_data: Vec> = try!(result::collect(df.data_iter().map(|maybe_x| maybe_x.map(|x| { 4 | zlib::compress_vec(x).unwrap() 5 | (item_type.start, item_type.num) 6 | } 7 | } 8 | 9 | /* 10 | pub fn write_datafile(df: &T, writer: &mut W) -> Result,()> { 11 | let compressed_data: Vec> = result::collect(df.data_iter().map(|maybe_x| maybe_x.map(|x| { 12 | zlib::compress_vec(x).unwrap() 13 | })))?; 14 | 15 | let size_items = df.items().fold(0, |s, i| { 16 | s + i.data.len() * mem::size_of::() + mem::size_of::() 17 | }); 18 | 19 | let size_data = compressed_data.iter().fold(0, |s, d| s + d.len()); 20 | 21 | DatafileHeaderVersion { 22 | magic: DATAFILE_MAGIC, 23 | version: 4, 24 | }.write(writer)?; 25 | 26 | DatafileHeader { 27 | _size: unimplemented!(), 28 | _swaplen: unimplemented!(), 29 | num_item_types: df.item_types().len(), 30 | num_items: df.items().len(), 31 | num_data: df.data_iter().len(), 32 | size_items: size_items, 33 | size_data: size_data, 34 | }.write(writer)?; 35 | 36 | for &type_id in df.item_types() { 37 | let (start, num) = df.item_type_indexes_start_num(type_id); 38 | DatafileItemType { 39 | type_id: type_id.as_i32().unwrap(), 40 | start: start.as_i32().unwrap(), 41 | num: num.as_i32().unwrap(), 42 | }.write(writer)?; 43 | } 44 | 45 | for DatafileItem { type_id, id, data } in df.items() { 46 | DatafileItemHeader::new(type_id, id, data.len()).write(writer)?; 47 | } 48 | unimplemented!(); 49 | Ok(Ok(())) 50 | } 51 | */ 52 | */ 53 | -------------------------------------------------------------------------------- /gamenet/ddnet/src/msg/mod.rs: -------------------------------------------------------------------------------- 1 | use libtw2_gamenet_common::error::Error; 2 | use libtw2_packer::Unpacker; 3 | use libtw2_packer::Warning; 4 | use warn::Warn; 5 | 6 | pub mod connless; 7 | pub mod game; 8 | pub mod system; 9 | 10 | pub use self::connless::Connless; 11 | pub use self::game::Game; 12 | pub use self::system::System; 13 | 14 | pub use libtw2_gamenet_common::msg::AddrPacked; 15 | pub use libtw2_gamenet_common::msg::CLIENTS_DATA_NONE; 16 | pub use libtw2_gamenet_common::msg::ClientsData; 17 | pub use libtw2_gamenet_common::msg::MessageId; 18 | pub use libtw2_gamenet_common::msg::SystemOrGame; 19 | 20 | struct Protocol; 21 | 22 | impl<'a> libtw2_gamenet_common::msg::Protocol<'a> for Protocol { 23 | type System = System<'a>; 24 | type Game = Game<'a>; 25 | 26 | fn decode_system(warn: &mut W, id: MessageId, p: &mut Unpacker<'a>) 27 | -> Result 28 | where W: Warn 29 | { 30 | System::decode_msg(warn, id, p) 31 | } 32 | fn decode_game(warn: &mut W, id: MessageId, p: &mut Unpacker<'a>) 33 | -> Result 34 | where W: Warn 35 | { 36 | Game::decode_msg(warn, id, p) 37 | } 38 | } 39 | 40 | pub fn decode<'a, W>(warn: &mut W, p: &mut Unpacker<'a>) 41 | -> Result, Game<'a>>, Error> 42 | where W: Warn 43 | { 44 | libtw2_gamenet_common::msg::decode(warn, Protocol, p) 45 | } 46 | 47 | pub fn decode_msg<'a, W>(warn: &mut W, id: SystemOrGame, p: &mut Unpacker<'a>) 48 | -> Result, Game<'a>>, Error> 49 | where W: Warn 50 | { 51 | Ok(match id { 52 | SystemOrGame::System(id) => SystemOrGame::System(System::decode_msg(warn, id, p)?), 53 | SystemOrGame::Game(id) => SystemOrGame::Game(Game::decode_msg(warn, id, p)?), 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /gamenet/teeworlds-0.5/src/msg/mod.rs: -------------------------------------------------------------------------------- 1 | use libtw2_gamenet_common::error::Error; 2 | use libtw2_packer::Unpacker; 3 | use libtw2_packer::Warning; 4 | use warn::Warn; 5 | 6 | pub mod connless; 7 | pub mod game; 8 | pub mod system; 9 | 10 | pub use self::connless::Connless; 11 | pub use self::game::Game; 12 | pub use self::system::System; 13 | 14 | pub use libtw2_gamenet_common::msg::AddrPacked; 15 | pub use libtw2_gamenet_common::msg::CLIENTS_DATA_NONE; 16 | pub use libtw2_gamenet_common::msg::ClientsData; 17 | pub use libtw2_gamenet_common::msg::MessageId; 18 | pub use libtw2_gamenet_common::msg::SystemOrGame; 19 | 20 | struct Protocol; 21 | 22 | impl<'a> libtw2_gamenet_common::msg::Protocol<'a> for Protocol { 23 | type System = System<'a>; 24 | type Game = Game<'a>; 25 | 26 | fn decode_system(warn: &mut W, id: MessageId, p: &mut Unpacker<'a>) 27 | -> Result 28 | where W: Warn 29 | { 30 | System::decode_msg(warn, id, p) 31 | } 32 | fn decode_game(warn: &mut W, id: MessageId, p: &mut Unpacker<'a>) 33 | -> Result 34 | where W: Warn 35 | { 36 | Game::decode_msg(warn, id, p) 37 | } 38 | } 39 | 40 | pub fn decode<'a, W>(warn: &mut W, p: &mut Unpacker<'a>) 41 | -> Result, Game<'a>>, Error> 42 | where W: Warn 43 | { 44 | libtw2_gamenet_common::msg::decode(warn, Protocol, p) 45 | } 46 | 47 | pub fn decode_msg<'a, W>(warn: &mut W, id: SystemOrGame, p: &mut Unpacker<'a>) 48 | -> Result, Game<'a>>, Error> 49 | where W: Warn 50 | { 51 | Ok(match id { 52 | SystemOrGame::System(id) => SystemOrGame::System(System::decode_msg(warn, id, p)?), 53 | SystemOrGame::Game(id) => SystemOrGame::Game(Game::decode_msg(warn, id, p)?), 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /gamenet/teeworlds-0.6/src/msg/mod.rs: -------------------------------------------------------------------------------- 1 | use libtw2_gamenet_common::error::Error; 2 | use libtw2_packer::Unpacker; 3 | use libtw2_packer::Warning; 4 | use warn::Warn; 5 | 6 | pub mod connless; 7 | pub mod game; 8 | pub mod system; 9 | 10 | pub use self::connless::Connless; 11 | pub use self::game::Game; 12 | pub use self::system::System; 13 | 14 | pub use libtw2_gamenet_common::msg::AddrPacked; 15 | pub use libtw2_gamenet_common::msg::CLIENTS_DATA_NONE; 16 | pub use libtw2_gamenet_common::msg::ClientsData; 17 | pub use libtw2_gamenet_common::msg::MessageId; 18 | pub use libtw2_gamenet_common::msg::SystemOrGame; 19 | 20 | struct Protocol; 21 | 22 | impl<'a> libtw2_gamenet_common::msg::Protocol<'a> for Protocol { 23 | type System = System<'a>; 24 | type Game = Game<'a>; 25 | 26 | fn decode_system(warn: &mut W, id: MessageId, p: &mut Unpacker<'a>) 27 | -> Result 28 | where W: Warn 29 | { 30 | System::decode_msg(warn, id, p) 31 | } 32 | fn decode_game(warn: &mut W, id: MessageId, p: &mut Unpacker<'a>) 33 | -> Result 34 | where W: Warn 35 | { 36 | Game::decode_msg(warn, id, p) 37 | } 38 | } 39 | 40 | pub fn decode<'a, W>(warn: &mut W, p: &mut Unpacker<'a>) 41 | -> Result, Game<'a>>, Error> 42 | where W: Warn 43 | { 44 | libtw2_gamenet_common::msg::decode(warn, Protocol, p) 45 | } 46 | 47 | pub fn decode_msg<'a, W>(warn: &mut W, id: SystemOrGame, p: &mut Unpacker<'a>) 48 | -> Result, Game<'a>>, Error> 49 | where W: Warn 50 | { 51 | Ok(match id { 52 | SystemOrGame::System(id) => SystemOrGame::System(System::decode_msg(warn, id, p)?), 53 | SystemOrGame::Game(id) => SystemOrGame::Game(Game::decode_msg(warn, id, p)?), 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /gamenet/teeworlds-0.7/src/msg/mod.rs: -------------------------------------------------------------------------------- 1 | use libtw2_gamenet_common::error::Error; 2 | use libtw2_packer::Unpacker; 3 | use libtw2_packer::Warning; 4 | use warn::Warn; 5 | 6 | pub mod connless; 7 | pub mod game; 8 | pub mod system; 9 | 10 | pub use self::connless::Connless; 11 | pub use self::game::Game; 12 | pub use self::system::System; 13 | 14 | pub use libtw2_gamenet_common::msg::AddrPacked; 15 | pub use libtw2_gamenet_common::msg::CLIENTS_DATA_NONE; 16 | pub use libtw2_gamenet_common::msg::ClientsData; 17 | pub use libtw2_gamenet_common::msg::MessageId; 18 | pub use libtw2_gamenet_common::msg::SystemOrGame; 19 | 20 | struct Protocol; 21 | 22 | impl<'a> libtw2_gamenet_common::msg::Protocol<'a> for Protocol { 23 | type System = System<'a>; 24 | type Game = Game<'a>; 25 | 26 | fn decode_system(warn: &mut W, id: MessageId, p: &mut Unpacker<'a>) 27 | -> Result 28 | where W: Warn 29 | { 30 | System::decode_msg(warn, id, p) 31 | } 32 | fn decode_game(warn: &mut W, id: MessageId, p: &mut Unpacker<'a>) 33 | -> Result 34 | where W: Warn 35 | { 36 | Game::decode_msg(warn, id, p) 37 | } 38 | } 39 | 40 | pub fn decode<'a, W>(warn: &mut W, p: &mut Unpacker<'a>) 41 | -> Result, Game<'a>>, Error> 42 | where W: Warn 43 | { 44 | libtw2_gamenet_common::msg::decode(warn, Protocol, p) 45 | } 46 | 47 | pub fn decode_msg<'a, W>(warn: &mut W, id: SystemOrGame, p: &mut Unpacker<'a>) 48 | -> Result, Game<'a>>, Error> 49 | where W: Warn 50 | { 51 | Ok(match id { 52 | SystemOrGame::System(id) => SystemOrGame::System(System::decode_msg(warn, id, p)?), 53 | SystemOrGame::Game(id) => SystemOrGame::Game(Game::decode_msg(warn, id, p)?), 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /doc/datafile_v4.ksy: -------------------------------------------------------------------------------- 1 | meta: 2 | id: tw_datafile_v4 3 | file-extension: map 4 | endian: le 5 | license: MIT/Apache-2.0 6 | doc-ref: https://github.com/heinrich5991/libtw2/blob/b510f20bc58ceb33f38ddc555b63989ccf5a90d7/doc/datafile.md 7 | seq: 8 | - id: header 9 | type: header 10 | - id: item_types 11 | type: item_type 12 | repeat: expr 13 | repeat-expr: header.num_item_types 14 | - id: item_offsets 15 | type: s4 16 | repeat: expr 17 | repeat-expr: header.num_items 18 | - id: data_offsets 19 | type: s4 20 | repeat: expr 21 | repeat-expr: header.num_data 22 | - id: data_sizes 23 | type: s4 24 | repeat: expr 25 | repeat-expr: header.num_data 26 | - id: items 27 | type: item 28 | repeat: expr 29 | repeat-expr: header.num_items 30 | - id: data 31 | size: '(_index == header.num_data - 1 ? header.data_size : data_offsets[_index + 1]) - data_offsets[_index]' 32 | repeat: expr 33 | repeat-expr: header.num_data 34 | types: 35 | header: 36 | seq: 37 | - id: magic 38 | contents: 'DATA' 39 | - id: version 40 | contents: "\x04\x00\x00\x00" 41 | - id: size 42 | type: s4 43 | - id: swaplen 44 | type: s4 45 | - id: num_item_types 46 | type: s4 47 | - id: num_items 48 | type: s4 49 | - id: num_data 50 | type: s4 51 | - id: item_size 52 | type: s4 53 | - id: data_size 54 | type: s4 55 | item_type: 56 | seq: 57 | - id: type_id 58 | type: s4 59 | - id: start 60 | type: s4 61 | - id: num 62 | type: s4 63 | item: 64 | seq: 65 | - id: id 66 | type: u2 67 | - id: type_id 68 | type: u2 69 | - id: size 70 | type: s4 71 | - id: item_data 72 | type: s4 73 | repeat: expr 74 | repeat-expr: size / 4 -------------------------------------------------------------------------------- /doc/tee_rendering.md: -------------------------------------------------------------------------------- 1 | This document describes how to render a tee like Teeworlds 0.6 or DDNet, given 2 | a tee skin image. 3 | 4 | A skin image must have an aspect ratio of 2:1 (width:height). We get the 5 | following body parts by splitting the tee skin image (3/8 means at 3/8 of the 6 | image's width, 1/4 means at 1/4 of the image's height, etc.): 7 | 8 | body: (0/8, 0/4) to (3/8, 3/4) 9 | body_outline: (3/8, 0/4) to (6/8, 3/4) 10 | hand: (6/8, 0/4) to (7/8, 1/4) 11 | hand_outline: (7/8, 0/4) to (8/8, 1/4) 12 | foot: (6/8, 1/4) to (8/8, 2/4) 13 | foot_outline: (6/8, 2/4) to (8/8, 3/4) 14 | eye_normal: (2/8, 3/4) to (3/8, 4/4) 15 | eye_angry: (3/8, 3/4) to (4/8, 4/4) 16 | eye_pain: (4/8, 3/4) to (5/8, 4/4) 17 | eye_happy: (5/8, 3/4) to (6/8, 4/4) 18 | eye_dead: (6/8, 3/4) to (7/8, 4/4) -- entirely unused 19 | eye_surprise: (7/8, 3/4) to (8/8, 4/4) 20 | 21 | This leaves an unused rectangle at `(0/8, 3/4) to (2/8, 4/4)`. 22 | 23 | For rendering, the segments need to be scaled like this (relative to body being 24 | 100%): 25 | 26 | body: 100% 27 | feet: 150% 28 | eyes: 120% 29 | hand: 93.75% 30 | 31 | The right eye needs to be mirrored horizontally (<->). 32 | The last eye shape `eye_blink` is achieved by scaling `eye_normal` 120% 33 | horizontally, but 45% vertically. 34 | 35 | Then, the images must be positioned like the following (hands or moving feet 36 | not handled), relative to 64/64 or 1 being the edge length of the body 37 | segment). 38 | 39 | body: 40 | x: 4/64 up 41 | feet: 42 | x: 7/64 left/right 43 | y: 10/64 down 44 | eyes: 45 | dir = angle of eyes (view angle), right = 0 46 | eyes_center: 47 | x: cos(dir) * 0.125 right 48 | y: body.y and then sin(dir) * 0.1 - 0.05 down 49 | eyes_offset: 50 | x: 0.075 - abs(cos(dir)) * 0.01 left/right 51 | -------------------------------------------------------------------------------- /gamenet/common/src/traits.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::msg::MessageId; 3 | use crate::msg::SystemOrGame; 4 | use crate::snap_obj; 5 | use buffer::CapacityError; 6 | use libtw2_packer::with_packer; 7 | use libtw2_packer::ExcessData; 8 | use libtw2_packer::IntUnpacker; 9 | use libtw2_packer::Packer; 10 | use libtw2_packer::Unpacker; 11 | use libtw2_packer::Warning; 12 | use warn::Warn; 13 | 14 | pub trait SnapObj: Sized { 15 | fn decode_obj>( 16 | warn: &mut W, 17 | obj_type_id: snap_obj::TypeId, 18 | p: &mut IntUnpacker, 19 | ) -> Result; 20 | fn obj_type_id(&self) -> snap_obj::TypeId; 21 | fn encode(&self) -> &[i32]; 22 | } 23 | 24 | pub trait Message<'a>: Sized { 25 | fn decode_msg>( 26 | warn: &mut W, 27 | msg_id: SystemOrGame, 28 | p: &mut Unpacker<'a>, 29 | ) -> Result; 30 | fn msg_id(&self) -> SystemOrGame; 31 | fn encode_msg<'d, 's>(&self, p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError>; 32 | } 33 | 34 | pub trait MessageExt<'a>: Message<'a> { 35 | fn decode>(warn: &mut W, p: &mut Unpacker<'a>) -> Result { 36 | let msg_id = SystemOrGame::decode_id(warn, p)?; 37 | Self::decode_msg(warn, msg_id, p) 38 | } 39 | fn encode<'d, 's>(&self, mut p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { 40 | with_packer(&mut p, |p| self.msg_id().encode_id(p))?; 41 | with_packer(&mut p, |p| self.encode_msg(p))?; 42 | Ok(p.written()) 43 | } 44 | } 45 | 46 | impl<'a, T: Message<'a>> MessageExt<'a> for T {} 47 | 48 | pub trait ProtocolStatic { 49 | type SnapObj: SnapObj + 'static; 50 | fn obj_size(type_id: u16) -> Option; 51 | } 52 | 53 | pub trait Protocol<'a>: ProtocolStatic { 54 | type Game: Message<'a>; 55 | type System: Message<'a>; 56 | } 57 | -------------------------------------------------------------------------------- /stats-browser/src/vec_map.rs: -------------------------------------------------------------------------------- 1 | use libtw2_common::unwrap_or_return; 2 | use std::default::Default; 3 | use std::iter; 4 | use std::marker::PhantomData; 5 | use std::ops; 6 | use std::slice; 7 | 8 | pub trait Index: Copy { 9 | fn to_usize(self) -> usize; 10 | fn from_usize(val: usize) -> Self; 11 | } 12 | 13 | impl Index for usize { 14 | fn to_usize(self) -> usize { 15 | self 16 | } 17 | fn from_usize(val: usize) -> usize { 18 | val 19 | } 20 | } 21 | 22 | #[derive(Clone)] 23 | pub struct VecMap { 24 | pub vec: Vec>, 25 | pub marker: PhantomData, 26 | } 27 | 28 | impl Default for VecMap { 29 | fn default() -> VecMap { 30 | VecMap { 31 | vec: vec![], 32 | marker: PhantomData, 33 | } 34 | } 35 | } 36 | 37 | impl ops::Index for VecMap { 38 | type Output = T; 39 | fn index(&self, index: I) -> &T { 40 | self.vec[index.to_usize()].as_ref().unwrap() 41 | } 42 | } 43 | 44 | impl ops::IndexMut for VecMap { 45 | fn index_mut(&mut self, index: I) -> &mut T { 46 | self.vec[index.to_usize()].as_mut().unwrap() 47 | } 48 | } 49 | 50 | pub type Iter<'a, I, T> = iter::FilterMap< 51 | iter::Enumerate>>, 52 | fn((usize, &Option)) -> Option<(I, &T)>, 53 | >; 54 | impl VecMap { 55 | pub fn push(&mut self, element: T) -> I { 56 | let index = self.vec.len(); 57 | self.vec.push(Some(element)); 58 | Index::from_usize(index) 59 | } 60 | pub fn iter(&self) -> Iter { 61 | fn indexify_filter((idx, elem): (usize, &Option)) -> Option<(I, &T)> { 62 | let elem = unwrap_or_return!(elem.as_ref(), None); 63 | Some((Index::from_usize(idx), elem)) 64 | } 65 | self.vec 66 | .iter() 67 | .enumerate() 68 | .filter_map(indexify_filter::) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tools/src/bin/tune_layer.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | use libtw2_common::num::Cast; 4 | use libtw2_common::unwrap_or_return; 5 | use libtw2_datafile as df; 6 | use std::fmt; 7 | use std::path::Path; 8 | 9 | #[derive(Clone, Copy)] 10 | struct Entity(u8); 11 | 12 | impl fmt::Debug for Entity { 13 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 14 | let Entity(inner) = *self; 15 | write!(f, "{:4x}", inner) 16 | } 17 | } 18 | 19 | impl fmt::Display for Entity { 20 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 21 | fmt::Debug::fmt(self, f) 22 | } 23 | } 24 | 25 | pub struct Stats { 26 | tune_layers: u64, 27 | tiles: [u64; 256], 28 | } 29 | 30 | impl Default for Stats { 31 | fn default() -> Stats { 32 | Stats { 33 | tune_layers: 0, 34 | tiles: [0; 256], 35 | } 36 | } 37 | } 38 | 39 | fn process(path: &Path, dfr: df::Reader, stats: &mut Stats) -> Result<(), libtw2_map::Error> { 40 | let mut map = libtw2_map::Reader::from_datafile(dfr); 41 | let game_layers = map.game_layers()?; 42 | let tune_layer = unwrap_or_return!(game_layers.tune(), Ok(())); 43 | let tiles = map.tune_layer_tiles(tune_layer)?; 44 | 45 | stats.tune_layers += 1; 46 | let mut tiles_count = [0u64; 256]; 47 | for tile in tiles.iter() { 48 | tiles_count[tile.index.usize()] += 1; 49 | stats.tiles[tile.index.usize()] += 1; 50 | } 51 | println!("{}", path.to_string_lossy()); 52 | for (i, &c) in tiles_count.iter().enumerate() { 53 | let entity = Entity(i.assert_u8()); 54 | if c != 0 { 55 | println!("{}: {:5}", entity, c); 56 | } 57 | } 58 | Ok(()) 59 | } 60 | 61 | fn print_stats(stats: &Stats) { 62 | for (i, &c) in stats.tiles.iter().enumerate() { 63 | let entity = Entity(i.assert_u8()); 64 | if c != 0 { 65 | println!("{}: {:5}", entity, c); 66 | } 67 | } 68 | println!("total: {}", stats.tune_layers); 69 | } 70 | 71 | fn main() { 72 | libtw2_tools::map_stats::stats(process, print_stats); 73 | } 74 | -------------------------------------------------------------------------------- /tools/src/bin/entities_flags.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | use libtw2_common::num::Cast; 4 | use libtw2_datafile as df; 5 | use libtw2_tools::map_stats::Entity; 6 | use std::path::Path; 7 | 8 | pub struct Stats { 9 | tiles: [[u64; 256]; 256], 10 | } 11 | 12 | impl Default for Stats { 13 | fn default() -> Stats { 14 | Stats { 15 | tiles: [[0; 256]; 256], 16 | } 17 | } 18 | } 19 | 20 | fn process(path: &Path, dfr: df::Reader, stats: &mut Stats) -> Result<(), libtw2_map::Error> { 21 | let mut map = libtw2_map::Reader::from_datafile(dfr); 22 | let game_layers = map.game_layers()?; 23 | let mut tiles_count = [[0u64; 256]; 256]; 24 | 25 | let tiles = map.layer_tiles(game_layers.game())?; 26 | for tile in tiles.iter() { 27 | let index = tile.index.usize(); 28 | let flags = tile.flags.usize(); 29 | tiles_count[index][flags] += 1; 30 | stats.tiles[index][flags] += 1; 31 | } 32 | 33 | if let Some(front) = game_layers.front() { 34 | let tiles = map.layer_tiles(front)?; 35 | for tile in tiles.iter() { 36 | let index = tile.index.usize(); 37 | let flags = tile.flags.usize(); 38 | tiles_count[index][flags] += 1; 39 | stats.tiles[index][flags] += 1; 40 | } 41 | } 42 | 43 | println!("{}", path.to_string_lossy()); 44 | for (i, inner) in tiles_count.iter().enumerate() { 45 | let entity = Entity(i.assert_u8()); 46 | for (flags, &c) in inner.iter().enumerate() { 47 | if c != 0 { 48 | println!("{}: {:4b}: {:5}", entity, flags.assert_u8(), c); 49 | } 50 | } 51 | } 52 | Ok(()) 53 | } 54 | 55 | fn print_stats(stats: &Stats) { 56 | for (i, inner) in stats.tiles.iter().enumerate() { 57 | let entity = Entity(i.assert_u8()); 58 | for (flags, &c) in inner.iter().enumerate() { 59 | if c != 0 { 60 | println!("{}: {:4b}: {:5}", entity, flags.assert_u8(), c); 61 | } 62 | } 63 | } 64 | } 65 | 66 | fn main() { 67 | libtw2_tools::map_stats::stats(process, print_stats); 68 | } 69 | -------------------------------------------------------------------------------- /_old/common_inlines.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static inline void tw_str_copy(char *dst, const char *src, size_t dst_size) 5 | { 6 | strncpy(dst, src, dst_size); 7 | dst[dst_size - 1] = 0; // null termination 8 | } 9 | 10 | static inline void tw_str_append(char *dst, const char *src, size_t dst_size) 11 | { 12 | unsigned len = strlen(dst); 13 | if(len < dst_size) 14 | strncpy(dst + len, src, dst_size - len); 15 | dst[dst_size - 1] = 0; 16 | } 17 | 18 | static inline int tw_str_comp(const char *str1, const char *str2) 19 | { 20 | return strcmp(str1, str2); 21 | } 22 | 23 | static inline size_t tw_str_length(const char *str) 24 | { 25 | return strlen(str); 26 | } 27 | 28 | static inline void tw_mem_copy(void *dst, const void *src, size_t size) 29 | { 30 | memcpy(dst, src, size); 31 | } 32 | 33 | static inline void tw_mem_move(void *dst, const void *src, size_t size) 34 | { 35 | memmove(dst, src, size); 36 | } 37 | 38 | static inline int tw_mem_comp(const void *mem1, const void *mem2, size_t size) 39 | { 40 | return memcmp(mem1, mem2, size); 41 | } 42 | 43 | static inline void tw_mem_zero(void *mem, size_t size) 44 | { 45 | memset(mem, 0, size); 46 | } 47 | 48 | static inline void tw_str_format(char *dst, size_t dst_size, const char *fmt, ...) 49 | { 50 | va_list va_args; 51 | va_start(va_args, fmt); 52 | tw_str_format_v(dst, dst_size, fmt, va_args); 53 | va_end(va_args); 54 | } 55 | 56 | static inline void tw_str_format_v(char *dst, size_t dst_size, const char *fmt, va_list va_args) 57 | { 58 | vsnprintf(dst, dst_size, fmt, va_args); 59 | dst[dst_size - 1] = 0; // null termination 60 | } 61 | 62 | static inline void tw_endian_tolittle(void *data, size_t size, int count) 63 | { 64 | (void)data; (void)size; (void)count; 65 | } 66 | 67 | static inline void tw_endian_tobig(void *data, size_t size, int count) 68 | { 69 | tw_endian_swap(data, size, count); 70 | } 71 | 72 | static inline void tw_endian_fromlittle(void *data, size_t size, int count) 73 | { 74 | (void)data; (void)size; (void)count; 75 | } 76 | 77 | static inline void tw_endian_frombig(void *data, size_t size, int count) 78 | { 79 | tw_endian_swap(data, size, count); 80 | } 81 | -------------------------------------------------------------------------------- /tools/src/unhexdump.rs: -------------------------------------------------------------------------------- 1 | use self::State::*; 2 | 3 | #[derive(Clone, Copy, Debug)] 4 | pub enum Error { 5 | UnknownCharacter, 6 | OddCharacterCount, 7 | } 8 | 9 | #[derive(Clone, Copy, Debug)] 10 | pub struct Unfinished; 11 | 12 | #[derive(Clone, Copy)] 13 | enum State { 14 | Before, 15 | InsideFirst, 16 | InsideSecond(u8), 17 | After, 18 | } 19 | 20 | impl Default for State { 21 | fn default() -> State { 22 | Before 23 | } 24 | } 25 | 26 | fn unhex(byte: u8) -> Result, Error> { 27 | match byte { 28 | b'0'..=b'9' => Ok(Some(byte - b'0')), 29 | b'a'..=b'f' => Ok(Some(byte - b'a' + 10)), 30 | b' ' => Ok(None), 31 | _ => Err(Error::UnknownCharacter), 32 | } 33 | } 34 | 35 | #[derive(Default)] 36 | pub struct Unhexdump { 37 | buf: Vec, 38 | state: State, 39 | } 40 | 41 | impl Unhexdump { 42 | pub fn new() -> Unhexdump { 43 | Default::default() 44 | } 45 | pub fn feed(&mut self, bytes: &[u8]) -> Result<(), Error> { 46 | for &b in bytes { 47 | match (self.state, b) { 48 | (Before, b'|') => self.state = InsideFirst, 49 | (Before, _) => {} 50 | (InsideFirst, b'|') => self.state = After, 51 | (InsideFirst, b) => { 52 | if let Some(n) = unhex(b)? { 53 | self.state = InsideSecond(n); 54 | } 55 | } 56 | (InsideSecond(_), b'|') => return Err(Error::OddCharacterCount), 57 | (InsideSecond(f), b) => { 58 | if let Some(n) = unhex(b)? { 59 | self.buf.push((f << 4) | n); 60 | self.state = InsideFirst; 61 | } 62 | } 63 | (After, b'\n') => self.state = Before, 64 | (After, _) => {} 65 | } 66 | } 67 | Ok(()) 68 | } 69 | pub fn into_inner(self) -> Result, Unfinished> { 70 | match self.state { 71 | After | Before => Ok(self.buf), 72 | _ => Err(Unfinished), 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /common/src/io.rs: -------------------------------------------------------------------------------- 1 | use std::error; 2 | use std::fmt; 3 | use std::io; 4 | use std::io::Read; 5 | 6 | struct SeekOverflow(()); 7 | 8 | pub fn seek_overflow() -> io::Error { 9 | io::Error::new(io::ErrorKind::InvalidData, SeekOverflow(())) 10 | } 11 | 12 | impl fmt::Debug for SeekOverflow { 13 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 14 | f.debug_struct("SeekOverflow").finish() 15 | } 16 | } 17 | 18 | impl error::Error for SeekOverflow {} 19 | 20 | impl fmt::Display for SeekOverflow { 21 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 22 | f.write_str("overflow while calculating seek offset") 23 | } 24 | } 25 | 26 | pub trait ReadExt: Read { 27 | fn read_retry(&mut self, buffer: &mut [u8]) -> io::Result { 28 | let mut read = 0; 29 | while read != buffer.len() { 30 | match self.read(&mut buffer[read..]) { 31 | Ok(0) => break, 32 | Ok(r) => read += r, 33 | Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} 34 | Err(e) => return Err(e), 35 | } 36 | } 37 | Ok(read) 38 | } 39 | } 40 | 41 | impl ReadExt for T {} 42 | 43 | pub trait FileExt { 44 | fn read_offset_retry(&self, buffer: &mut [u8], offset: u64) -> io::Result; 45 | } 46 | 47 | #[cfg(feature = "file_offset")] 48 | impl FileExt for std::fs::File { 49 | fn read_offset_retry(&self, buffer: &mut [u8], offset: u64) -> io::Result { 50 | use crate::num::Cast; 51 | 52 | // Make sure the additions in this function don't overflow 53 | let _end_offset = offset 54 | .checked_add(buffer.len().u64()) 55 | .ok_or_else(seek_overflow)?; 56 | 57 | let mut read = 0; 58 | while read != buffer.len() { 59 | match file_offset::FileExt::read_offset(self, &mut buffer[read..], offset + read.u64()) 60 | { 61 | Ok(0) => break, 62 | Ok(r) => read += r, 63 | Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} 64 | Err(e) => return Err(e), 65 | } 66 | } 67 | Ok(read) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tools/src/bin/ddnet_emptytele.rs: -------------------------------------------------------------------------------- 1 | use libtw2_map::Error; 2 | use std::path::Path; 3 | use std::process; 4 | 5 | fn tele_tile_name(index: u8) -> Option<&'static str> { 6 | Some(match index { 7 | 10 => "TELEINEVIL", 8 | 14 => "TELEINWEAPON", 9 | 15 => "TELEINHOOK", 10 | 26 => "TELEIN", 11 | 27 => "TELEOUT", 12 | 29 => "TELECHECK", 13 | 30 => "TELECHECKOUT", 14 | 31 => "TELECHECKIN", 15 | 63 => "TELECHECKINEVIL", 16 | _ => return None, 17 | }) 18 | } 19 | 20 | fn process(path: &Path) -> Result<(), Error> { 21 | let mut map = libtw2_map::Reader::open(path)?; 22 | let game_layers = map.game_layers()?; 23 | 24 | let tele = if let Some(t) = game_layers.teleport() { 25 | t 26 | } else { 27 | return Ok(()); 28 | }; 29 | let tele_tiles = map.tele_layer_tiles(tele)?; 30 | for ((y, x), &t) in tele_tiles.indexed_iter() { 31 | if t.index != 0 && t.number == 0 { 32 | if let Some(name) = tele_tile_name(t.index) { 33 | println!("{}: {}: ({}, {})", path.display(), name, x, y); 34 | } else { 35 | println!("{}: unknown ({}): ({}, {})", path.display(), t.index, x, y); 36 | } 37 | } 38 | } 39 | Ok(()) 40 | } 41 | 42 | fn main() { 43 | use clap::App; 44 | use clap::Arg; 45 | 46 | libtw2_logger::init(); 47 | 48 | let matches = App::new("DDNet teleporter scanner") 49 | .about("Scans map files for weird teleporters.") 50 | .arg( 51 | Arg::with_name("MAP") 52 | .help("Sets the map file to analyse") 53 | .multiple(true) 54 | .required(true), 55 | ) 56 | .get_matches(); 57 | 58 | let maps = matches.values_of_os("MAP").unwrap(); 59 | 60 | let mut error = false; 61 | for map in maps { 62 | let map = Path::new(map); 63 | match process(map) { 64 | Ok(()) => {} 65 | Err(err) => { 66 | eprintln!("{}: {:?}", map.display(), err); 67 | error = true; 68 | } 69 | } 70 | } 71 | if error { 72 | process::exit(1); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tools/src/bin/serverinfo7.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | #[macro_use] 4 | extern crate log; 5 | 6 | use libtw2_serverbrowse::protocol as browse_protocol; 7 | use libtw2_serverbrowse::protocol::Response; 8 | use libtw2_serverbrowse::protocol::Token7; 9 | use libtw2_serverbrowse::protocol::Token7Response; 10 | use std::net::SocketAddr; 11 | use std::net::UdpSocket; 12 | 13 | const BUFSIZE: usize = 2048; 14 | 15 | fn do_(socket: UdpSocket, addr: SocketAddr) { 16 | let mut buf = [0; BUFSIZE]; 17 | 18 | socket 19 | .send_to(&browse_protocol::request_token_7(Token7([0; 4])), addr) 20 | .unwrap(); 21 | 22 | loop { 23 | let (len, from) = socket.recv_from(&mut buf).unwrap(); 24 | if from != addr { 25 | error!( 26 | "received response from non-peer, wanted={} got={}", 27 | addr, from 28 | ); 29 | continue; 30 | } 31 | match browse_protocol::parse_response(&buf[..len]) { 32 | Some(Response::Token7(Token7Response(Token7([0, 0, 0, 0]), their_token))) => { 33 | info!("token={}", their_token); 34 | socket 35 | .send_to( 36 | &browse_protocol::request_info_7(Token7([0; 4]), their_token, 0), 37 | addr, 38 | ) 39 | .unwrap(); 40 | break; 41 | } 42 | _ => { 43 | error!("received non-token response from peer"); 44 | } 45 | } 46 | } 47 | loop { 48 | let (len, from) = socket.recv_from(&mut buf).unwrap(); 49 | if from != addr { 50 | error!( 51 | "received response from non-peer, wanted={} got={}", 52 | addr, from 53 | ); 54 | continue; 55 | } 56 | match browse_protocol::parse_response(&buf[..len]) { 57 | Some(Response::Info7(x)) => { 58 | println!("{:?}", x.parse().unwrap()); 59 | break; 60 | } 61 | _ => { 62 | error!("received non-info response from peer"); 63 | } 64 | } 65 | } 66 | } 67 | 68 | fn main() { 69 | libtw2_tools::client::client(do_); 70 | } 71 | -------------------------------------------------------------------------------- /snapshot/tests/correctness.rs: -------------------------------------------------------------------------------- 1 | use buffer::CapacityError; 2 | use libtw2_gamenet::snap_obj::obj_size; 3 | use libtw2_packer::with_packer; 4 | use libtw2_packer::Unpacker; 5 | use libtw2_snapshot::snap::Delta; 6 | use libtw2_snapshot::snap::Snap; 7 | use warn::Panic; 8 | 9 | #[rustfmt::skip] 10 | const FIRST_DATA: &'static [i32] = &[0,18,0,4,18,1744,1072,2,3,4,17,1840,912,1,0,4,16,880,880,0,0,4,15,1840,848,1,0,4,14,912,848,0,0,4,13,880,848,1,0,4,12,848,848,0,0,4,11,880,816,0,0,4,9,1264,656,0,0,4,8,1104,656,0,0,4,7,912,656,0,0,4,6,1712,624,2,2,4,5,1840,432,1,0,4,3,1840,336,1,0,9,0,292,1584,305,0,128,0,0,0,-1,0,0,1584,304,0,0,0,10,0,10,1,0,0,6,0,0,0,0,0,20,0,0,1,11,0,-287183387,-320474125,-1594563099,-2139062272,-2139062144,-2139062144,-2139062272,-1,-454695199,-169020288,-2139062144,-2139062144,-2139062144,-2139062272,0,65408,65408,10,0,1,0,0,0,0,]; 11 | const FIRST_CRC: i32 = 0x5b96263a; 12 | 13 | #[rustfmt::skip] 14 | const SECOND_DATA: &'static [i32] = &[0,1,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,]; 15 | const SECOND_CRC: i32 = 0x5b96263b; 16 | 17 | #[test] 18 | fn simple() { 19 | let mut buf = Vec::with_capacity(4096); 20 | let mut delta = Delta::new(); 21 | let mut prev = Snap::empty(); 22 | let mut snap = Snap::default(); 23 | 24 | with_packer(&mut buf, |mut p| -> Result<_, CapacityError> { 25 | for &d in &FIRST_DATA[..FIRST_DATA.len()] { 26 | p.write_int(d)?; 27 | } 28 | Ok(p.written()) 29 | }) 30 | .unwrap(); 31 | 32 | delta 33 | .read(&mut Panic, obj_size, &mut Unpacker::new(&buf)) 34 | .unwrap(); 35 | snap.read_with_delta(&mut Panic, &prev, &delta).unwrap(); 36 | println!("{:?}", snap); 37 | assert_eq!(snap.crc(), FIRST_CRC); 38 | 39 | prev = snap; 40 | snap = Snap::default(); 41 | 42 | buf.clear(); 43 | with_packer(&mut buf, |mut p| -> Result<_, CapacityError> { 44 | for &d in &SECOND_DATA[..SECOND_DATA.len()] { 45 | p.write_int(d)?; 46 | } 47 | Ok(p.written()) 48 | }) 49 | .unwrap(); 50 | 51 | delta 52 | .read(&mut Panic, obj_size, &mut Unpacker::new(&buf)) 53 | .unwrap(); 54 | snap.read_with_delta(&mut Panic, &prev, &delta).unwrap(); 55 | println!("{:?}", snap); 56 | assert_eq!(snap.crc(), SECOND_CRC); 57 | } 58 | -------------------------------------------------------------------------------- /doc/int.ksy: -------------------------------------------------------------------------------- 1 | meta: 2 | id: tw_int 3 | title: Variable length quantity, signed integer, at most 32 bit, little-endian 4 | ks-version: 0.7 5 | doc: | 6 | A variable-length signed integer. The highest bit of each byte specifies 7 | whether the number continues after this byte. The first byte also has the 8 | sign flag as the second-to-highest bit. The rest of the bits are used as 9 | binary digits of the represented number. 10 | 11 | The number can consist of 1 to 5 bytes, if it's five bytes long, the last 12 | byte's four upper bits (including the continuation bit) should be ignored. 13 | 14 | If the sign bit is set, the number's value should have all of its bits 15 | (including the sign bit) flipped, assuming two's complement. 16 | 17 | seq: 18 | - id: byte0 19 | type: first_byte 20 | - id: byte1 21 | type: continuation_byte 22 | if: byte0.has_continuation 23 | - id: byte2 24 | type: continuation_byte 25 | if: byte0.has_continuation and byte1.has_continuation 26 | - id: byte3 27 | type: continuation_byte 28 | if: byte0.has_continuation and byte1.has_continuation and byte2.has_continuation 29 | - id: byte4 30 | type: last_byte 31 | if: byte0.has_continuation and byte1.has_continuation and byte2.has_continuation and byte3.has_continuation 32 | types: 33 | first_byte: 34 | seq: 35 | - id: has_continuation 36 | type: b1 37 | - id: sign 38 | type: b1 39 | - id: bits 40 | type: b6 41 | continuation_byte: 42 | seq: 43 | - id: has_continuation 44 | type: b1 45 | - id: bits 46 | type: b7 47 | last_byte: 48 | seq: 49 | - id: padding 50 | type: b4 51 | - id: bits 52 | type: b4 53 | instances: 54 | len: 55 | value: >- 56 | (not byte0.has_continuation ? 1 : 57 | (not byte1.has_continuation ? 2 : 58 | (not byte2.has_continuation ? 3 : 59 | (not byte3.has_continuation ? 4 : 5)))) 60 | doc: Number of bytes this value occupies 61 | value: 62 | value: >- 63 | (byte0.bits 64 | | (len >= 2 ? byte1.bits << 6 : 0) 65 | | (len >= 3 ? byte2.bits << 13 : 0) 66 | | (len >= 4 ? byte3.bits << 20 : 0) 67 | | (len >= 5 ? byte4.bits << 27 : 0)) 68 | ^ (byte0.sign ? ~0 : 0) 69 | doc: Resulting value as normal integer 70 | -------------------------------------------------------------------------------- /_old/datafile_raw.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef struct tw_datafile_raw tw_datafile_raw; 5 | 6 | typedef int (*tw_dfr_callback_read)(void *buffer, size_t start, size_t buffer_size, size_t *read, void *userdata); 7 | typedef int (*tw_dfr_callback_filesize)(size_t *filesize, void *userdata); 8 | typedef int (*tw_dfr_callback_alloc)(void **result, size_t size, void *userdata); 9 | typedef void (*tw_dfr_callback_free)(void *ptr, void *userdata); 10 | 11 | typedef uint32_t tw_dfr_crc; 12 | 13 | 14 | typedef struct tw_dfr_error 15 | { 16 | int errno_; 17 | char string[256]; 18 | } tw_dfr_error; 19 | 20 | enum 21 | { 22 | TW_DFR_ERRNO_NONE=0, 23 | TW_DFR_ERRNO_FILETOOSHORT, 24 | TW_DFR_ERRNO_WRONGMAGIC, 25 | TW_DFR_ERRNO_UNSUPPORTEDVERSION, 26 | TW_DFR_ERRNO_MALFORMEDHEADER, 27 | TW_DFR_ERRNO_MALFORMED, 28 | TW_DFR_ERRNO_OUTOFRANGE, 29 | TW_DFR_ERRNO_DATAUNCOMPRESS, 30 | TW_DFR_ERRNO_NOTIMPLEMENTED, 31 | }; 32 | 33 | 34 | tw_datafile_raw *tw_dfr_create(void); 35 | void tw_dfr_free(tw_datafile_raw *dfr); 36 | 37 | void tw_dfr_callbacks_set(tw_datafile_raw *dfr, 38 | tw_dfr_callback_read read, 39 | tw_dfr_callback_filesize filesize, 40 | tw_dfr_callback_alloc alloc, 41 | tw_dfr_callback_free free 42 | ); 43 | 44 | int tw_dfr_open(tw_datafile_raw *dfr, tw_dfr_error *error, void *userdata); 45 | int tw_dfr_close(tw_datafile_raw *dfr, tw_dfr_error *error, void *userdata); 46 | 47 | int tw_dfr_data_read(tw_datafile_raw *dfr, void **data, size_t *data_size, int index, tw_dfr_error *error, void *userdata); 48 | int tw_dfr_num_data(tw_datafile_raw *dfr, int *num, tw_dfr_error *error, void *userdata); 49 | 50 | int tw_dfr_item_read(tw_datafile_raw *dfr, int32_t **item, size_t *item_count, int *type_id, int *id, int index, tw_dfr_error *error, void *userdata); 51 | int tw_dfr_item_find(tw_datafile_raw *dfr, int32_t **item, size_t *item_count, int type_id, int id, tw_dfr_error *error, void *userdata); 52 | int tw_dfr_num_items(tw_datafile_raw *dfr, int *num, tw_dfr_error *error, void *userdata); 53 | 54 | int tw_dfr_type_indexes(tw_datafile_raw *dfr, int *start, int *num, int type_id, tw_dfr_error *error, void *userdata); 55 | 56 | int tw_dfr_crc_calc(tw_datafile_raw *dfr, tw_dfr_crc *crc, tw_dfr_error *error, void *userdata); 57 | 58 | int tw_dfr_dump(tw_datafile_raw *dfr, tw_dfr_error *error, void *userdata); 59 | -------------------------------------------------------------------------------- /tools/src/bin/servercount7.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | #[macro_use] 4 | extern crate log; 5 | 6 | use libtw2_serverbrowse::protocol as browse_protocol; 7 | use libtw2_serverbrowse::protocol::Count7Response; 8 | use libtw2_serverbrowse::protocol::Response; 9 | use libtw2_serverbrowse::protocol::Token7; 10 | use libtw2_serverbrowse::protocol::Token7Response; 11 | use std::net::SocketAddr; 12 | use std::net::UdpSocket; 13 | 14 | const BUFSIZE: usize = 2048; 15 | 16 | fn do_(socket: UdpSocket, addr: SocketAddr) { 17 | let mut buf = [0; BUFSIZE]; 18 | 19 | socket 20 | .send_to(&browse_protocol::request_token_7(Token7([0; 4])), addr) 21 | .unwrap(); 22 | 23 | loop { 24 | let (len, from) = socket.recv_from(&mut buf).unwrap(); 25 | if from != addr { 26 | error!( 27 | "received response from non-peer, wanted={} got={}", 28 | addr, from 29 | ); 30 | continue; 31 | } 32 | match browse_protocol::parse_response(&buf[..len]) { 33 | Some(Response::Token7(Token7Response(Token7([0, 0, 0, 0]), their_token))) => { 34 | info!("token={}", their_token); 35 | socket 36 | .send_to( 37 | &browse_protocol::request_count_7(Token7([0; 4]), their_token), 38 | addr, 39 | ) 40 | .unwrap(); 41 | break; 42 | } 43 | _ => { 44 | error!("received non-token response from peer"); 45 | } 46 | } 47 | } 48 | loop { 49 | let (len, from) = socket.recv_from(&mut buf).unwrap(); 50 | if from != addr { 51 | error!( 52 | "received response from non-peer, wanted={} got={}", 53 | addr, from 54 | ); 55 | continue; 56 | } 57 | match browse_protocol::parse_response(&buf[..len]) { 58 | Some(Response::Count7(Count7Response(_, _, x))) => { 59 | println!("{}", x); 60 | break; 61 | } 62 | _ => { 63 | error!("received non-count response from peer"); 64 | } 65 | } 66 | } 67 | } 68 | 69 | fn main() { 70 | libtw2_tools::client::client(do_); 71 | } 72 | -------------------------------------------------------------------------------- /_old/datafile_raw.pxd: -------------------------------------------------------------------------------- 1 | 2 | from libc.stdint cimport int32_t, uint32_t 3 | 4 | cdef extern from "datafile_raw.h" nogil: 5 | ctypedef struct tw_datafile_raw: 6 | pass 7 | 8 | ctypedef int (*tw_dfr_callback_read)(void *buffer, size_t start, size_t buffer_size, size_t *read, void *userdata) 9 | ctypedef int (*tw_dfr_callback_filesize)(size_t *filesize, void *userdata) 10 | ctypedef int (*tw_dfr_callback_alloc)(void **result, size_t size, void *userdata) 11 | ctypedef void (*tw_dfr_callback_free)(void *ptr, void *userdata) 12 | 13 | ctypedef uint32_t tw_dfr_crc 14 | 15 | 16 | struct tw_dfr_error: 17 | int errno_ 18 | char string[256] 19 | 20 | enum: 21 | TW_DFR_ERRNO_NONE=0 22 | TW_DFR_ERRNO_FILETOOSHORT 23 | TW_DFR_ERRNO_WRONGMAGIC 24 | TW_DFR_ERRNO_UNSUPPORTEDVERSION 25 | TW_DFR_ERRNO_MALFORMEDHEADER 26 | TW_DFR_ERRNO_MALFORMED 27 | TW_DFR_ERRNO_OUTOFRANGE 28 | TW_DFR_ERRNO_DATAUNCOMPRESS 29 | TW_DFR_ERRNO_NOTIMPLEMENTED 30 | 31 | 32 | tw_datafile_raw *tw_dfr_create() 33 | void tw_dfr_free(tw_datafile_raw *dfr) 34 | 35 | void tw_dfr_callbacks_set(tw_datafile_raw *dfr, \ 36 | tw_dfr_callback_read read, \ 37 | tw_dfr_callback_filesize filesize, \ 38 | tw_dfr_callback_alloc alloc, \ 39 | tw_dfr_callback_free free \ 40 | ) 41 | 42 | int tw_dfr_open(tw_datafile_raw *dfr, tw_dfr_error *error, void *userdata) 43 | int tw_dfr_close(tw_datafile_raw *dfr, tw_dfr_error *error, void *userdata) 44 | 45 | int tw_dfr_data_read(tw_datafile_raw *dfr, void **data, size_t *data_size, int index, tw_dfr_error *error, void *userdata) 46 | int tw_dfr_num_data(tw_datafile_raw *dfr, int *num, tw_dfr_error *error, void *userdata) 47 | 48 | int tw_dfr_item_read(tw_datafile_raw *dfr, int32_t **item, size_t *item_count, int *type_id, int *id, int index, tw_dfr_error *error, void *userdata) 49 | int tw_dfr_item_find(tw_datafile_raw *dfr, int32_t **item, size_t *item_count, int type_id, int id, tw_dfr_error *error, void *userdata) 50 | int tw_dfr_num_items(tw_datafile_raw *dfr, int *num, tw_dfr_error *error, void *userdata) 51 | 52 | int tw_dfr_type_indexes(tw_datafile_raw *dfr, int *start, int *num, int type_id, tw_dfr_error *error, void *userdata) 53 | 54 | int tw_dfr_crc_calc(tw_datafile_raw *dfr, tw_dfr_crc *crc, tw_dfr_error *error, void *userdata) 55 | 56 | int tw_dfr_dump(tw_datafile_raw *dfr, tw_dfr_error *error, void *userdata) 57 | -------------------------------------------------------------------------------- /huffman/data/frequencies: -------------------------------------------------------------------------------- 1 | 1073741824 2 | 4545 3 | 2657 4 | 431 5 | 1950 6 | 919 7 | 444 8 | 482 9 | 2244 10 | 617 11 | 838 12 | 542 13 | 715 14 | 1814 15 | 304 16 | 240 17 | 754 18 | 212 19 | 647 20 | 186 21 | 283 22 | 131 23 | 146 24 | 166 25 | 543 26 | 164 27 | 167 28 | 136 29 | 179 30 | 859 31 | 363 32 | 113 33 | 157 34 | 154 35 | 204 36 | 108 37 | 137 38 | 180 39 | 202 40 | 176 41 | 872 42 | 404 43 | 168 44 | 134 45 | 151 46 | 111 47 | 113 48 | 109 49 | 120 50 | 126 51 | 129 52 | 100 53 | 41 54 | 20 55 | 16 56 | 22 57 | 18 58 | 18 59 | 17 60 | 19 61 | 16 62 | 37 63 | 13 64 | 21 65 | 362 66 | 166 67 | 99 68 | 78 69 | 95 70 | 88 71 | 81 72 | 70 73 | 83 74 | 284 75 | 91 76 | 187 77 | 77 78 | 68 79 | 52 80 | 68 81 | 59 82 | 66 83 | 61 84 | 638 85 | 71 86 | 157 87 | 50 88 | 46 89 | 69 90 | 43 91 | 11 92 | 24 93 | 13 94 | 19 95 | 10 96 | 12 97 | 12 98 | 20 99 | 14 100 | 9 101 | 20 102 | 20 103 | 10 104 | 10 105 | 15 106 | 15 107 | 12 108 | 12 109 | 7 110 | 19 111 | 15 112 | 14 113 | 13 114 | 18 115 | 35 116 | 19 117 | 17 118 | 14 119 | 8 120 | 5 121 | 15 122 | 17 123 | 9 124 | 15 125 | 14 126 | 18 127 | 8 128 | 10 129 | 2173 130 | 134 131 | 157 132 | 68 133 | 188 134 | 60 135 | 170 136 | 60 137 | 194 138 | 62 139 | 175 140 | 71 141 | 148 142 | 67 143 | 167 144 | 78 145 | 211 146 | 67 147 | 156 148 | 69 149 | 1674 150 | 90 151 | 174 152 | 53 153 | 147 154 | 89 155 | 181 156 | 51 157 | 174 158 | 63 159 | 163 160 | 80 161 | 167 162 | 94 163 | 128 164 | 122 165 | 223 166 | 153 167 | 218 168 | 77 169 | 200 170 | 110 171 | 190 172 | 73 173 | 174 174 | 69 175 | 145 176 | 66 177 | 277 178 | 143 179 | 141 180 | 60 181 | 136 182 | 53 183 | 180 184 | 57 185 | 142 186 | 57 187 | 158 188 | 61 189 | 166 190 | 112 191 | 152 192 | 92 193 | 26 194 | 22 195 | 21 196 | 28 197 | 20 198 | 26 199 | 30 200 | 21 201 | 32 202 | 27 203 | 20 204 | 17 205 | 23 206 | 21 207 | 30 208 | 22 209 | 22 210 | 21 211 | 27 212 | 25 213 | 17 214 | 27 215 | 23 216 | 18 217 | 39 218 | 26 219 | 15 220 | 21 221 | 12 222 | 18 223 | 18 224 | 27 225 | 20 226 | 18 227 | 15 228 | 19 229 | 11 230 | 17 231 | 33 232 | 12 233 | 18 234 | 15 235 | 19 236 | 18 237 | 16 238 | 26 239 | 17 240 | 18 241 | 9 242 | 10 243 | 25 244 | 22 245 | 22 246 | 17 247 | 20 248 | 16 249 | 6 250 | 16 251 | 15 252 | 20 253 | 14 254 | 18 255 | 24 256 | 335 257 | -------------------------------------------------------------------------------- /tools/src/bin/serverlist7.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(test))] 2 | 3 | #[macro_use] 4 | extern crate log; 5 | 6 | use libtw2_serverbrowse::protocol as browse_protocol; 7 | use libtw2_serverbrowse::protocol::List7Response; 8 | use libtw2_serverbrowse::protocol::Response; 9 | use libtw2_serverbrowse::protocol::Token7; 10 | use libtw2_serverbrowse::protocol::Token7Response; 11 | use std::net::SocketAddr; 12 | use std::net::UdpSocket; 13 | 14 | const BUFSIZE: usize = 2048; 15 | 16 | fn do_(socket: UdpSocket, addr: SocketAddr) { 17 | let mut buf = [0; BUFSIZE]; 18 | 19 | socket 20 | .send_to(&browse_protocol::request_token_7(Token7([0; 4])), addr) 21 | .unwrap(); 22 | 23 | loop { 24 | let (len, from) = socket.recv_from(&mut buf).unwrap(); 25 | if from != addr { 26 | error!( 27 | "received response from non-peer, wanted={} got={}", 28 | addr, from 29 | ); 30 | continue; 31 | } 32 | match browse_protocol::parse_response(&buf[..len]) { 33 | Some(Response::Token7(Token7Response(Token7([0, 0, 0, 0]), their_token))) => { 34 | info!("token={}", their_token); 35 | socket 36 | .send_to( 37 | &browse_protocol::request_list_7(Token7([0; 4]), their_token), 38 | addr, 39 | ) 40 | .unwrap(); 41 | break; 42 | } 43 | _ => { 44 | error!("received non-token response from peer"); 45 | } 46 | } 47 | } 48 | loop { 49 | let (len, from) = socket.recv_from(&mut buf).unwrap(); 50 | if from != addr { 51 | error!( 52 | "received response from non-peer, wanted={} got={}", 53 | addr, from 54 | ); 55 | continue; 56 | } 57 | match browse_protocol::parse_response(&buf[..len]) { 58 | Some(Response::List7(List7Response(_, _, list))) => { 59 | for &s in list.iter() { 60 | println!("{}", s.unpack()); 61 | } 62 | } 63 | _ => { 64 | error!("received non-list response from peer"); 65 | } 66 | } 67 | } 68 | } 69 | 70 | fn main() { 71 | libtw2_tools::client::client(do_); 72 | } 73 | -------------------------------------------------------------------------------- /_old/io.c: -------------------------------------------------------------------------------- 1 | #include "io.h" 2 | 3 | #include "error.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct tw_io 11 | { 12 | FILE *file; 13 | }; 14 | 15 | tw_io *tw_io_open(const char *filename, const char *mode) 16 | { 17 | tw_io io; 18 | 19 | errno = 0; 20 | io.file = fopen(filename, mode); 21 | if(io.file == NULL) 22 | { 23 | tw_error_set(TW_ERRNO_IO_ERROR, "could not open file, name=\"%s\" errno=%d msg=\"%s\"", filename, errno, strerror(errno)); 24 | return NULL; 25 | } 26 | 27 | tw_io *ret = malloc(sizeof(*ret)); 28 | *ret = io; 29 | return ret; 30 | } 31 | 32 | int tw_io_close(tw_io *io) 33 | { 34 | tw_io io_c = *io; 35 | free(io); 36 | io = NULL; 37 | 38 | if(fclose(io_c.file) != 0) 39 | { 40 | tw_error_set(TW_ERRNO_IO_ERROR, "could not close, errno=%d msg=\"%s\"", errno, strerror(errno)); 41 | return 1; 42 | } 43 | return 0; 44 | } 45 | 46 | size_t tw_io_read(tw_io *io, void *buffer, size_t size) 47 | { 48 | size_t read = fread(buffer, 1, size, io->file); 49 | if(read < size) 50 | { 51 | if(ferror(io->file) != 0) 52 | tw_error_set(TW_ERRNO_IO_ERROR, "read failed"); 53 | else if(feof(io->file) != 0) 54 | tw_error_set(TW_ERRNO_IO_EOF, "EOF reached"); 55 | } 56 | return read; 57 | } 58 | 59 | size_t tw_io_write(tw_io *io, const void *buffer, size_t size) 60 | { 61 | size_t wrote = fwrite(buffer, 1, size, io->file); 62 | if(wrote < size && ferror(io->file) != 0) 63 | tw_error_set(TW_ERRNO_IO_ERROR, "write failed"); 64 | return wrote; 65 | } 66 | 67 | long tw_io_tell(tw_io *io) 68 | { 69 | long tell; 70 | errno = 0; 71 | tell = ftell(io->file); 72 | if(tell == EOF) 73 | { 74 | tw_error_set(TW_ERRNO_IO_ERROR, "tell failed: %s", strerror(errno)); 75 | return -1; 76 | } 77 | return tell; 78 | } 79 | 80 | int tw_io_seek(tw_io *io, long offset) 81 | { 82 | errno = 0; 83 | if(fseek(io->file, offset, SEEK_SET) != 0) 84 | { 85 | tw_error_set(TW_ERRNO_IO_ERROR, "seek failed: %s", strerror(errno)); 86 | return 1; 87 | } 88 | return 0; 89 | } 90 | 91 | int tw_io_seek_end(tw_io *io) 92 | { 93 | errno = 0; 94 | if(fseek(io->file, 0, SEEK_END) != 0) 95 | { 96 | tw_error_set(TW_ERRNO_IO_ERROR, "seek failed: %s", strerror(errno)); 97 | return 1; 98 | } 99 | return 0; 100 | } 101 | 102 | int tw_io_flush(tw_io *io) 103 | { 104 | errno = 0; 105 | if(fflush(io->file) != 0) 106 | { 107 | tw_error_set(TW_ERRNO_IO_ERROR, "flush failed: %s", strerror(errno)); 108 | return 1; 109 | } 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: Build and test 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: 13 | - ubuntu-latest 14 | - macos-latest 15 | - windows-latest 16 | rust: 17 | - stable 18 | include: 19 | - os: ubuntu-latest 20 | rust: 1.63.0 21 | - os: ubuntu-latest 22 | rust: nightly 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions-rs/toolchain@v1 26 | with: 27 | profile: minimal 28 | toolchain: ${{ matrix.rust }} 29 | override: true 30 | - uses: Swatinem/rust-cache@v2 31 | # `demo` crate doesn't work with the MSRV. 32 | - name: Exclude `demo` crate from MSRV 33 | run: sed -i -e 's/"demo",//' -e 's/"tools",//' Cargo.toml; cargo update -p libtw2-common 34 | if: ${{ matrix.rust != 'stable' && matrix.rust != 'nightly' }} 35 | - run: cargo build --locked 36 | - run: cargo test --locked 37 | - run: cargo bench --locked 38 | - uses: actions/upload-artifact@v4 39 | if: ${{ matrix.rust == 'stable' }} 40 | with: 41 | path: | 42 | target 43 | !target/**/*.d 44 | !target/**/*.rlib 45 | !target/**/.cargo-lock 46 | !target/**/.fingerprint 47 | !target/**/CACHEDIR.TAG 48 | !target/**/build 49 | !target/**/deps 50 | !target/**/incremental 51 | name: libtw2-${{ matrix.os }} 52 | 53 | build-uniffi: 54 | name: Build uniffi 55 | runs-on: ubuntu-latest 56 | steps: 57 | - run: pip install build 58 | - uses: actions/checkout@v2 59 | - uses: Swatinem/rust-cache@v2 60 | with: 61 | workspaces: uniffi 62 | - run: cd uniffi; cargo build --locked 63 | - run: cd uniffi; cargo test --locked 64 | - run: cd uniffi; cargo bench --locked 65 | - run: cd uniffi/huffman; python -m build; pip install dist/libtw2_huffman-*.whl 66 | - run: cd uniffi/tests; python -m unittest *.py 67 | 68 | check-generated: 69 | name: Check that generated files match 70 | runs-on: ubuntu-latest 71 | steps: 72 | - uses: actions/checkout@v2 73 | - run: ./generate_all 74 | - run: test -z "$(git status --porcelain)" 75 | 76 | check-rustfmt: 77 | name: Check that everything is rustfmtted 78 | runs-on: ubuntu-latest 79 | steps: 80 | - uses: actions/checkout@v2 81 | - run: cargo fmt --check 82 | -------------------------------------------------------------------------------- /map/src/structs.rs: -------------------------------------------------------------------------------- 1 | #[deriving(Clone, Show, PartialEq, Eq)] 2 | pub struct Rectangle { 3 | x: i32, 4 | y: i32, 5 | w: i32, 6 | h: i32, 7 | } 8 | 9 | #[deriving(Clone, Show, PartialEq, Eq)] 10 | pub struct Color { 11 | r: i32, 12 | g: i32, 13 | b: i32, 14 | a: i32, 15 | } 16 | 17 | // TODO: express C strings through the type system 18 | // TODO: express cross-indexes through type system 19 | 20 | #[deriving(Clone, Show, PartialEq, Eq)] 21 | pub struct DataIndex(pub uint); 22 | 23 | 24 | #[deriving(Clone, Show)] 25 | pub struct MapItemVersion { 26 | version: i32, 27 | } 28 | 29 | #[deriving(Clone, Show)] 30 | pub struct MapItemInfo { 31 | author: Option, 32 | map_version: Option, 33 | credits: Option, 34 | license: Option, 35 | } 36 | 37 | #[deriving(Clone, Show)] 38 | enum ImageFormat { 39 | ImageFormatRgb, 40 | ImageFormatRgba, 41 | } 42 | 43 | #[deriving(Clone, Show)] 44 | pub struct MapItemImage { 45 | width: i32, 46 | height: i32, 47 | external: bool, 48 | name: Option, 49 | data: Option, 50 | format: ImageFormat, 51 | } 52 | 53 | #[deriving(Clone, Show)] 54 | pub struct MapItemGroup { 55 | offset_x: i32, 56 | offset_y: i32, 57 | parallax_x: i32, 58 | parallax_y: i32, 59 | 60 | clipping: Option, 61 | 62 | start_layer: i32, 63 | num_layers: u32, 64 | 65 | name: [u8, ..12], 66 | name_length: uint, 67 | } 68 | 69 | #[deriving(Clone, Show)] 70 | pub struct MapItemGroupClip { 71 | 72 | } 73 | 74 | #[deriving(Clone, Show)] 75 | pub struct MapItemLayer { 76 | flags: u32, 77 | kind: MapItemLayerKind, 78 | } 79 | 80 | #[deriving(Clone, Show)] 81 | pub enum MapItemLayerKind { 82 | LayerTilemap { 83 | flags: u32, 84 | width: i32, 85 | height: i32, 86 | 87 | color: Color, 88 | color_env: i32, 89 | color_env_offset: i32, 90 | 91 | image: i32, 92 | data: DataIndex, 93 | 94 | name: [u8, ..12], 95 | name_length: uint, 96 | } 97 | LayerQuads { 98 | flags: u32, 99 | 100 | num_quads: i32, 101 | data: DataIndex, 102 | image: i32, 103 | 104 | name: [u8, ..12], 105 | name_length: uint, 106 | } 107 | } 108 | 109 | #[deriving(Clone, Show)] 110 | pub struct MapItemEnvelope { 111 | channels: i32, 112 | start_points: i32, 113 | num_points: i32, 114 | synchronized: bool, 115 | 116 | name: [u8, ..32], 117 | name_length: uint, 118 | } 119 | -------------------------------------------------------------------------------- /gamenet/ddnet/src/traits.rs: -------------------------------------------------------------------------------- 1 | use crate::msg; 2 | use crate::snap_obj; 3 | use buffer::CapacityError; 4 | use libtw2_gamenet_common::error::Error; 5 | use libtw2_gamenet_common::msg::MessageId; 6 | use libtw2_gamenet_common::msg::SystemOrGame; 7 | use libtw2_gamenet_common::traits; 8 | use libtw2_packer::ExcessData; 9 | use libtw2_packer::IntUnpacker; 10 | use libtw2_packer::Packer; 11 | use libtw2_packer::Unpacker; 12 | use libtw2_packer::Warning; 13 | use warn::Warn; 14 | 15 | pub struct Protocol(()); 16 | 17 | impl traits::ProtocolStatic for Protocol { 18 | type SnapObj = snap_obj::SnapObj; 19 | fn obj_size(type_id: u16) -> Option { 20 | snap_obj::obj_size(type_id) 21 | } 22 | } 23 | 24 | impl<'a> traits::Protocol<'a> for Protocol { 25 | type Game = msg::Game<'a>; 26 | type System = msg::System<'a>; 27 | } 28 | 29 | impl traits::SnapObj for crate::SnapObj { 30 | fn decode_obj>( 31 | warn: &mut W, 32 | obj_type_id: snap_obj::TypeId, 33 | p: &mut IntUnpacker, 34 | ) -> Result { 35 | crate::SnapObj::decode_obj(warn, obj_type_id, p) 36 | } 37 | fn obj_type_id(&self) -> snap_obj::TypeId { 38 | self.obj_type_id() 39 | } 40 | fn encode(&self) -> &[i32] { 41 | self.encode() 42 | } 43 | } 44 | 45 | impl<'a> traits::Message<'a> for msg::Game<'a> { 46 | fn decode_msg>( 47 | warn: &mut W, 48 | id: SystemOrGame, 49 | p: &mut Unpacker<'a>, 50 | ) -> Result, Error> { 51 | if let SystemOrGame::Game(id) = id { 52 | msg::Game::decode_msg(warn, id, p) 53 | } else { 54 | Err(Error::UnknownId) 55 | } 56 | } 57 | fn msg_id(&self) -> SystemOrGame { 58 | SystemOrGame::Game(self.msg_id()) 59 | } 60 | fn encode_msg<'d, 's>(&self, p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { 61 | self.encode_msg(p) 62 | } 63 | } 64 | 65 | impl<'a> traits::Message<'a> for msg::System<'a> { 66 | fn decode_msg>( 67 | warn: &mut W, 68 | id: SystemOrGame, 69 | p: &mut Unpacker<'a>, 70 | ) -> Result, Error> { 71 | if let SystemOrGame::System(id) = id { 72 | msg::System::decode_msg(warn, id, p) 73 | } else { 74 | Err(Error::UnknownId) 75 | } 76 | } 77 | fn msg_id(&self) -> SystemOrGame { 78 | SystemOrGame::System(self.msg_id()) 79 | } 80 | fn encode_msg<'d, 's>(&self, p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { 81 | self.encode_msg(p) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /gamenet/teeworlds-0.5/src/traits.rs: -------------------------------------------------------------------------------- 1 | use crate::msg; 2 | use crate::snap_obj; 3 | use buffer::CapacityError; 4 | use libtw2_gamenet_common::error::Error; 5 | use libtw2_gamenet_common::msg::MessageId; 6 | use libtw2_gamenet_common::msg::SystemOrGame; 7 | use libtw2_gamenet_common::traits; 8 | use libtw2_packer::ExcessData; 9 | use libtw2_packer::IntUnpacker; 10 | use libtw2_packer::Packer; 11 | use libtw2_packer::Unpacker; 12 | use libtw2_packer::Warning; 13 | use warn::Warn; 14 | 15 | pub struct Protocol(()); 16 | 17 | impl traits::ProtocolStatic for Protocol { 18 | type SnapObj = snap_obj::SnapObj; 19 | fn obj_size(type_id: u16) -> Option { 20 | snap_obj::obj_size(type_id) 21 | } 22 | } 23 | 24 | impl<'a> traits::Protocol<'a> for Protocol { 25 | type Game = msg::Game<'a>; 26 | type System = msg::System<'a>; 27 | } 28 | 29 | impl traits::SnapObj for crate::SnapObj { 30 | fn decode_obj>( 31 | warn: &mut W, 32 | obj_type_id: snap_obj::TypeId, 33 | p: &mut IntUnpacker, 34 | ) -> Result { 35 | crate::SnapObj::decode_obj(warn, obj_type_id, p) 36 | } 37 | fn obj_type_id(&self) -> snap_obj::TypeId { 38 | self.obj_type_id() 39 | } 40 | fn encode(&self) -> &[i32] { 41 | self.encode() 42 | } 43 | } 44 | 45 | impl<'a> traits::Message<'a> for msg::Game<'a> { 46 | fn decode_msg>( 47 | warn: &mut W, 48 | id: SystemOrGame, 49 | p: &mut Unpacker<'a>, 50 | ) -> Result, Error> { 51 | if let SystemOrGame::Game(id) = id { 52 | msg::Game::decode_msg(warn, id, p) 53 | } else { 54 | Err(Error::UnknownId) 55 | } 56 | } 57 | fn msg_id(&self) -> SystemOrGame { 58 | SystemOrGame::Game(self.msg_id()) 59 | } 60 | fn encode_msg<'d, 's>(&self, p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { 61 | self.encode_msg(p) 62 | } 63 | } 64 | 65 | impl<'a> traits::Message<'a> for msg::System<'a> { 66 | fn decode_msg>( 67 | warn: &mut W, 68 | id: SystemOrGame, 69 | p: &mut Unpacker<'a>, 70 | ) -> Result, Error> { 71 | if let SystemOrGame::System(id) = id { 72 | msg::System::decode_msg(warn, id, p) 73 | } else { 74 | Err(Error::UnknownId) 75 | } 76 | } 77 | fn msg_id(&self) -> SystemOrGame { 78 | SystemOrGame::System(self.msg_id()) 79 | } 80 | fn encode_msg<'d, 's>(&self, p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { 81 | self.encode_msg(p) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /gamenet/teeworlds-0.6/src/traits.rs: -------------------------------------------------------------------------------- 1 | use crate::msg; 2 | use crate::snap_obj; 3 | use buffer::CapacityError; 4 | use libtw2_gamenet_common::error::Error; 5 | use libtw2_gamenet_common::msg::MessageId; 6 | use libtw2_gamenet_common::msg::SystemOrGame; 7 | use libtw2_gamenet_common::traits; 8 | use libtw2_packer::ExcessData; 9 | use libtw2_packer::IntUnpacker; 10 | use libtw2_packer::Packer; 11 | use libtw2_packer::Unpacker; 12 | use libtw2_packer::Warning; 13 | use warn::Warn; 14 | 15 | pub struct Protocol(()); 16 | 17 | impl traits::ProtocolStatic for Protocol { 18 | type SnapObj = snap_obj::SnapObj; 19 | fn obj_size(type_id: u16) -> Option { 20 | snap_obj::obj_size(type_id) 21 | } 22 | } 23 | 24 | impl<'a> traits::Protocol<'a> for Protocol { 25 | type Game = msg::Game<'a>; 26 | type System = msg::System<'a>; 27 | } 28 | 29 | impl traits::SnapObj for crate::SnapObj { 30 | fn decode_obj>( 31 | warn: &mut W, 32 | obj_type_id: snap_obj::TypeId, 33 | p: &mut IntUnpacker, 34 | ) -> Result { 35 | crate::SnapObj::decode_obj(warn, obj_type_id, p) 36 | } 37 | fn obj_type_id(&self) -> snap_obj::TypeId { 38 | self.obj_type_id() 39 | } 40 | fn encode(&self) -> &[i32] { 41 | self.encode() 42 | } 43 | } 44 | 45 | impl<'a> traits::Message<'a> for msg::Game<'a> { 46 | fn decode_msg>( 47 | warn: &mut W, 48 | id: SystemOrGame, 49 | p: &mut Unpacker<'a>, 50 | ) -> Result, Error> { 51 | if let SystemOrGame::Game(id) = id { 52 | msg::Game::decode_msg(warn, id, p) 53 | } else { 54 | Err(Error::UnknownId) 55 | } 56 | } 57 | fn msg_id(&self) -> SystemOrGame { 58 | SystemOrGame::Game(self.msg_id()) 59 | } 60 | fn encode_msg<'d, 's>(&self, p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { 61 | self.encode_msg(p) 62 | } 63 | } 64 | 65 | impl<'a> traits::Message<'a> for msg::System<'a> { 66 | fn decode_msg>( 67 | warn: &mut W, 68 | id: SystemOrGame, 69 | p: &mut Unpacker<'a>, 70 | ) -> Result, Error> { 71 | if let SystemOrGame::System(id) = id { 72 | msg::System::decode_msg(warn, id, p) 73 | } else { 74 | Err(Error::UnknownId) 75 | } 76 | } 77 | fn msg_id(&self) -> SystemOrGame { 78 | SystemOrGame::System(self.msg_id()) 79 | } 80 | fn encode_msg<'d, 's>(&self, p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { 81 | self.encode_msg(p) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /gamenet/teeworlds-0.7/src/traits.rs: -------------------------------------------------------------------------------- 1 | use crate::msg; 2 | use crate::snap_obj; 3 | use buffer::CapacityError; 4 | use libtw2_gamenet_common::error::Error; 5 | use libtw2_gamenet_common::msg::MessageId; 6 | use libtw2_gamenet_common::msg::SystemOrGame; 7 | use libtw2_gamenet_common::traits; 8 | use libtw2_packer::ExcessData; 9 | use libtw2_packer::IntUnpacker; 10 | use libtw2_packer::Packer; 11 | use libtw2_packer::Unpacker; 12 | use libtw2_packer::Warning; 13 | use warn::Warn; 14 | 15 | pub struct Protocol(()); 16 | 17 | impl traits::ProtocolStatic for Protocol { 18 | type SnapObj = snap_obj::SnapObj; 19 | fn obj_size(type_id: u16) -> Option { 20 | snap_obj::obj_size(type_id) 21 | } 22 | } 23 | 24 | impl<'a> traits::Protocol<'a> for Protocol { 25 | type Game = msg::Game<'a>; 26 | type System = msg::System<'a>; 27 | } 28 | 29 | impl traits::SnapObj for crate::SnapObj { 30 | fn decode_obj>( 31 | warn: &mut W, 32 | obj_type_id: snap_obj::TypeId, 33 | p: &mut IntUnpacker, 34 | ) -> Result { 35 | crate::SnapObj::decode_obj(warn, obj_type_id, p) 36 | } 37 | fn obj_type_id(&self) -> snap_obj::TypeId { 38 | self.obj_type_id() 39 | } 40 | fn encode(&self) -> &[i32] { 41 | self.encode() 42 | } 43 | } 44 | 45 | impl<'a> traits::Message<'a> for msg::Game<'a> { 46 | fn decode_msg>( 47 | warn: &mut W, 48 | id: SystemOrGame, 49 | p: &mut Unpacker<'a>, 50 | ) -> Result, Error> { 51 | if let SystemOrGame::Game(id) = id { 52 | msg::Game::decode_msg(warn, id, p) 53 | } else { 54 | Err(Error::UnknownId) 55 | } 56 | } 57 | fn msg_id(&self) -> SystemOrGame { 58 | SystemOrGame::Game(self.msg_id()) 59 | } 60 | fn encode_msg<'d, 's>(&self, p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { 61 | self.encode_msg(p) 62 | } 63 | } 64 | 65 | impl<'a> traits::Message<'a> for msg::System<'a> { 66 | fn decode_msg>( 67 | warn: &mut W, 68 | id: SystemOrGame, 69 | p: &mut Unpacker<'a>, 70 | ) -> Result, Error> { 71 | if let SystemOrGame::System(id) = id { 72 | msg::System::decode_msg(warn, id, p) 73 | } else { 74 | Err(Error::UnknownId) 75 | } 76 | } 77 | fn msg_id(&self) -> SystemOrGame { 78 | SystemOrGame::System(self.msg_id()) 79 | } 80 | fn encode_msg<'d, 's>(&self, p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> { 81 | self.encode_msg(p) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /huffman/reference/sys/src/teeworlds/huffman.h: -------------------------------------------------------------------------------- 1 | /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ 2 | /* If you are missing that file, acquire a complete release at teeworlds.com. */ 3 | #ifndef ENGINE_SHARED_HUFFMAN_H 4 | #define ENGINE_SHARED_HUFFMAN_H 5 | 6 | 7 | 8 | class CHuffman 9 | { 10 | enum 11 | { 12 | HUFFMAN_EOF_SYMBOL = 256, 13 | 14 | HUFFMAN_MAX_SYMBOLS=HUFFMAN_EOF_SYMBOL+1, 15 | HUFFMAN_MAX_NODES=HUFFMAN_MAX_SYMBOLS*2-1, 16 | 17 | HUFFMAN_LUTBITS = 10, 18 | HUFFMAN_LUTSIZE = (1< src/lib.rs 63 | python3 fix.py src/lib.rs /usr/lib wireshark wsutil 64 | cargo fmt 65 | 66 | # These makes the resulting source contain errors. 67 | #--opaque-type 'circuit_type' \ 68 | #--opaque-type 'port_type' \ 69 | -------------------------------------------------------------------------------- /gamenet/generate/generate.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | import os 4 | 5 | import datatypes 6 | 7 | def write_file(filename, data): 8 | with open(filename, "w") as f: 9 | f.write(data) 10 | 11 | def generate(spec, out_dir, name): 12 | protocol = datatypes.load_protocol_spec(spec) 13 | 14 | os.makedirs(os.path.join(out_dir, "src/msg"), exist_ok=True) 15 | 16 | emit = datatypes.Emit() 17 | with emit: 18 | datatypes.emit_header_enums() 19 | datatypes.emit_enum_module(protocol.constants, protocol.game_enumerations) 20 | write_file(os.path.join(out_dir, "src/enums.rs"), emit.get()) 21 | 22 | emit = datatypes.Emit() 23 | with emit: 24 | datatypes.emit_header_msg_connless(protocol.connless_messages) 25 | datatypes.emit_enum_connless_module("Connless", protocol.connless_messages) 26 | write_file(os.path.join(out_dir, "src/msg/connless.rs"), emit.get()) 27 | 28 | emit = datatypes.Emit() 29 | with emit: 30 | datatypes.emit_header_msg_game() 31 | datatypes.emit_enum_msg_module("Game", protocol.game_messages) 32 | write_file(os.path.join(out_dir, "src/msg/game.rs"), emit.get()) 33 | 34 | emit = datatypes.Emit() 35 | with emit: 36 | datatypes.emit_header_msg_system() 37 | datatypes.emit_enum_msg_module("System", protocol.system_messages) 38 | write_file(os.path.join(out_dir, "src/msg/system.rs"), emit.get()) 39 | 40 | emit = datatypes.Emit() 41 | with emit: 42 | datatypes.emit_header_snap_obj() 43 | datatypes.emit_enum_obj_module("SnapObj", protocol.snapshot_objects, protocol.game_flags) 44 | write_file(os.path.join(out_dir, "src/snap_obj.rs"), emit.get()) 45 | 46 | emit = datatypes.Emit() 47 | with emit: 48 | datatypes.emit_main_lib() 49 | write_file(os.path.join(out_dir, "src/lib.rs"), emit.get()) 50 | 51 | emit = datatypes.Emit() 52 | with emit: 53 | datatypes.emit_msg_module() 54 | write_file(os.path.join(out_dir, "src/msg/mod.rs"), emit.get()) 55 | 56 | emit = datatypes.Emit() 57 | with emit: 58 | datatypes.emit_traits_module() 59 | write_file(os.path.join(out_dir, "src/traits.rs"), emit.get()) 60 | 61 | emit = datatypes.Emit() 62 | with emit: 63 | datatypes.emit_cargo_toml(name) 64 | write_file(os.path.join(out_dir, "Cargo.toml"), emit.get()) 65 | 66 | def main(): 67 | p = argparse.ArgumentParser(description="Generate Rust protocol files for a protocol described in a JSON file") 68 | p.add_argument("spec", metavar="SPEC", help="JSON spec file") 69 | p.add_argument("out", metavar="OUT", help="Output directory") 70 | p.add_argument("name", metavar="NAME", help="Crate name") 71 | args = p.parse_args() 72 | with open(args.spec) as f: 73 | spec = json.load(f) 74 | generate(spec, args.out, args.name) 75 | 76 | if __name__ == "__main__": 77 | main() 78 | -------------------------------------------------------------------------------- /httphook-ldpreload/README.md: -------------------------------------------------------------------------------- 1 | httphook-ldpreload 2 | ================== 3 | 4 | Use this to register a Teeworlds 0.6/DDNet game server with the HTTPS 5 | mastersrv. This method only works on Linux. 6 | 7 | Building 8 | -------- 9 | 10 | You need at least Rust 1.63 installed. This should be available via your 11 | package manager or https://rustup.rs/. 12 | 13 | ```sh 14 | git clone https://github.com/heinrich5991/libtw2 15 | cd libtw2/httphook-ldpreload 16 | cargo build --release 17 | ls ../target/release/liblibtw2_httphook_ldpreload.so 18 | ``` 19 | 20 | You'll find the built library in `../target/release/liblibtw2_httphook_ldpreload.so`. 21 | 22 | Usage 23 | ----- 24 | 25 | Use the `LD_PRELOAD` variable to instruct the dynamic linker to load this 26 | library into the Teeworlds 0.6/DDNet game server process: 27 | 28 | ```sh 29 | LD_PRELOAD=/path/to/liblibtw2_httphook_ldpreload.so ./teeworlds_srv "sv_register 0" 30 | LD_PRELOAD=/path/to/liblibtw2_httphook_ldpreload.so ./DDNet-Server "sv_register 0" 31 | ``` 32 | 33 | You should disable its internal mastersrv registration using `"sv_register 0"` 34 | as it would conflict with the library's registration process. 35 | 36 | Configuration 37 | ------------- 38 | 39 | The library accepts a couple of optional environment variables to configure the 40 | registration process: 41 | 42 | - `LIBTW2_HTTPHOOK_COMMUNITY_TOKEN` (unset by default): Use this token to 43 | register with a community (server group) on the mastersrv. Example: 44 | `ddtc_6DnZq5Ix0J2kvDHbkPNtb6bsZxOVQg4ly2jw`. 45 | - `LIBTW2_HTTPHOOK_LOG` (default: `info`): Specify log level of the library. 46 | Examples: `debug`, `error`. [Documentation of the 47 | syntax](https://docs.rs/env_logger/0.3.5/env_logger/#enabling-logging). 48 | - `LIBTW2_HTTPHOOK_OVERRIDE_COUNTRY` (unset by default): Populate the 49 | `"country"` field in the server info with this. Possible values are drawn 50 | from [ISO 3166-1 numeric](https://en.wikipedia.org/wiki/ISO_3166-1_numeric), 51 | with DDNet's own additions, in 52 | [data/countryflags/index.txt](https://github.com/ddnet/ddnet/blob/c1ba856f321a36dc32ff9fc67cf3df1cbf285f12/data/countryflags/index.txt). 53 | Example: 276 for Germany or 905 for the EU. 54 | - `LIBTW2_HTTPHOOK_OVERRIDE_REQUIRES_LOGIN` (unset by default): Populate the 55 | `"requires_login"` field in the server info with this. Possible values: 56 | `true`, `false`. 57 | - `LIBTW2_HTTPHOOK_PROTOCOLS` (default: `all`): Which protocols to register the 58 | server for. Posible values: `none`, `ipv4`, `ipv6`, `all`. 59 | - `LIBTW2_HTTPHOOK_REGISTER_URL` (default: 60 | `https://master1.ddnet.org/ddnet/15/register`): Contact the mastersrv given 61 | by this URL. Example: `http://localhost:8080/ddnet/15/register` for local 62 | testing. 63 | 64 | Example: 65 | ``` 66 | LIBTW2_HTTPHOOK_COMMUNITY_TOKEN=ddtc_6DnZq5Ix0J2kvDHbkPNtb6bsZxOVQg4ly2jw LD_PRELOAD=/path/to/liblibtw2_httphook_ldpreload.so ./teeworlds_srv "sv_register 0" 67 | ``` 68 | -------------------------------------------------------------------------------- /net/src/collections/peer_set.rs: -------------------------------------------------------------------------------- 1 | use super::peer_map; 2 | use super::PeerMap; 3 | use crate::net::PeerId; 4 | use std::fmt; 5 | use std::iter::FromIterator; 6 | 7 | #[derive(Clone, Default)] 8 | pub struct PeerSet { 9 | set: PeerMap<()>, 10 | } 11 | 12 | impl fmt::Debug for PeerSet { 13 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 14 | f.debug_set().entries(self.iter()).finish() 15 | } 16 | } 17 | 18 | impl PeerSet { 19 | pub fn new() -> PeerSet { 20 | PeerSet { 21 | set: PeerMap::new(), 22 | } 23 | } 24 | pub fn with_capacity(cap: usize) -> PeerSet { 25 | PeerSet { 26 | set: PeerMap::with_capacity(cap), 27 | } 28 | } 29 | pub fn clear(&mut self) { 30 | self.set.clear() 31 | } 32 | pub fn len(&self) -> usize { 33 | self.set.len() 34 | } 35 | pub fn is_empty(&self) -> bool { 36 | self.set.is_empty() 37 | } 38 | pub fn iter(&self) -> Iter { 39 | Iter(self.set.iter()) 40 | } 41 | pub fn drain(&mut self) -> Drain { 42 | Drain(self.set.drain()) 43 | } 44 | pub fn insert(&mut self, pid: PeerId) -> bool { 45 | self.set.insert(pid, ()).is_none() 46 | } 47 | pub fn remove(&mut self, pid: PeerId) { 48 | self.set.remove(pid) 49 | } 50 | pub fn contains(&mut self, pid: PeerId) -> bool { 51 | self.set.contains_key(pid) 52 | } 53 | } 54 | 55 | impl FromIterator for PeerSet { 56 | fn from_iter(iter: I) -> PeerSet 57 | where 58 | I: IntoIterator, 59 | { 60 | PeerSet { 61 | set: FromIterator::from_iter(iter.into_iter().map(|pid| (pid, ()))), 62 | } 63 | } 64 | } 65 | 66 | impl Extend for PeerSet { 67 | fn extend(&mut self, iter: I) 68 | where 69 | I: IntoIterator, 70 | { 71 | self.set.extend(iter.into_iter().map(|pid| (pid, ()))) 72 | } 73 | } 74 | 75 | impl<'a> IntoIterator for &'a PeerSet { 76 | type Item = PeerId; 77 | type IntoIter = Iter<'a>; 78 | fn into_iter(self) -> Iter<'a> { 79 | self.iter() 80 | } 81 | } 82 | 83 | pub struct Iter<'a>(peer_map::Iter<'a, ()>); 84 | pub struct Drain<'a>(peer_map::Drain<'a, ()>); 85 | 86 | impl<'a> Iterator for Iter<'a> { 87 | type Item = PeerId; 88 | fn next(&mut self) -> Option { 89 | self.0.next().map(|(pid, &())| pid) 90 | } 91 | fn size_hint(&self) -> (usize, Option) { 92 | self.0.size_hint() 93 | } 94 | } 95 | 96 | impl<'a> Iterator for Drain<'a> { 97 | type Item = PeerId; 98 | fn next(&mut self) -> Option { 99 | self.0.next().map(|(pid, ())| pid) 100 | } 101 | fn size_hint(&self) -> (usize, Option) { 102 | self.0.size_hint() 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /_old/gamemap.h: -------------------------------------------------------------------------------- 1 | /*#include "common.h" 2 | 3 | #include 4 | 5 | typedef struct tw_map tw_map; 6 | 7 | // general struct "color" 8 | typedef struct tw_map_color 9 | { 10 | int red; 11 | int green; 12 | int blue; 13 | int alpha; 14 | } tw_map_color; 15 | 16 | typedef struct tw_map_point 17 | { 18 | float x; 19 | float y; 20 | } tw_map_point; 21 | 22 | 23 | typedef struct tw_map_info 24 | { 25 | const char *author; 26 | const char *version; 27 | const char *credits; 28 | const char *license; 29 | } tw_map_info; 30 | 31 | typedef struct tw_map_image 32 | { 33 | int width; 34 | int height; 35 | bool external; 36 | const char *name; 37 | 38 | size_t data_size; 39 | const tw_byte *data; 40 | } tw_map_image; 41 | 42 | typedef struct tw_map_envpoint 43 | { 44 | int time; 45 | int curvetype; 46 | float values[4]; 47 | } tw_map_envpoint; 48 | 49 | enum 50 | { 51 | TW_MAP_ENVPOINT_CURVETYPE_STEP=0, 52 | TW_MAP_ENVPOINT_CURVETYPE_LINEAR, 53 | TW_MAP_ENVPOINT_CURVETYPE_SLOW, 54 | TW_MAP_ENVPOINT_CURVETYPE_FAST, 55 | TW_MAP_ENVPOINT_CURVETYPE_SMOOTH, 56 | TW_MAP_ENVPOINT_NUM_CURVETYPES, 57 | }; 58 | 59 | typedef struct tw_map_envelope 60 | { 61 | int channels; 62 | int num_points; 63 | tw_map_envpoint *points; 64 | const char *name; 65 | bool synchronized; 66 | } tw_map_envelope; 67 | 68 | typedef struct tw_map_group 69 | { 70 | int offset_x; 71 | int offset_y; 72 | int parallax_x; 73 | int parallax_y; 74 | bool clipping; 75 | int clipping_x; 76 | int clipping_y; 77 | int clipping_width; 78 | int clipping_height; 79 | const char *name; 80 | 81 | int layers_start; // layer index 82 | int num_layers; 83 | } tw_map_group; 84 | 85 | typedef struct tw_map_layer 86 | { 87 | bool detail; 88 | } tw_map_layer; 89 | 90 | typedef struct tw_map_tile 91 | { 92 | tw_byte index; 93 | tw_byte flags; 94 | tw_byte unused[2]; 95 | } tw_map_tile; 96 | 97 | enum 98 | { 99 | TW_MAP_TILE_FLAG_VFLIP=1, 100 | TW_MAP_TILE_FLAG_HFLIP=2, 101 | TW_MAP_TILE_FLAG_OPAQUE=4, 102 | TW_MAP_TILE_FLAG_ROTATE=8, 103 | }; 104 | 105 | typedef struct tw_map_layer_tilemap 106 | { 107 | tw_map_layer layer; 108 | bool game; 109 | int width; 110 | int height; 111 | tw_map_color color; 112 | int color_env; // envelope index 113 | int color_env_offset; 114 | int image; // image index 115 | const tw_map_tile *data; 116 | const char *name; 117 | } tw_map_layer_tilemap; 118 | 119 | typedef struct tw_map_quad 120 | { 121 | tw_map_point points[5]; 122 | tw_map_color colors[4]; 123 | tw_map_point texcoords[4]; 124 | int pos_env; // envelope index 125 | int pos_env_offset; 126 | int color_env; // envelope index 127 | int color_env_offset; 128 | } tw_map_quad; 129 | 130 | typedef struct tw_map_layer_quads 131 | { 132 | int num_quads; 133 | tw_map_quad *data; 134 | } tw_map_layer_quads; 135 | 136 | tw_map *tw_map_open(const char *filename);*/ 137 | -------------------------------------------------------------------------------- /_old/error.c: -------------------------------------------------------------------------------- 1 | #include "error.h" 2 | 3 | #include "common.h" 4 | 5 | #include 6 | #include 7 | 8 | typedef struct tw_error 9 | { 10 | char file[64]; 11 | int line; 12 | char function[64]; 13 | char msg[128]; 14 | int errno; 15 | } tw_error; 16 | 17 | typedef struct tw_error_threadlocals 18 | { 19 | tw_error errors[32]; 20 | int num_errors; 21 | char msg[1024]; 22 | int msg_constructed; 23 | int errno; 24 | } tw_error_threadlocals; 25 | 26 | static __thread tw_error_threadlocals tls; 27 | 28 | 29 | void tw_error_msg_set_v(const char *file, int line, const char *function, int errno, const char *fmt, va_list va_args) 30 | { 31 | // just copy everything into our local storage 32 | tw_error error; 33 | if(file) 34 | tw_str_copy(error.file, file, sizeof(error.file)); 35 | else 36 | error.file[0] = 0; 37 | error.line = line; 38 | if(function) 39 | tw_str_copy(error.function, function, sizeof(error.function)); 40 | else 41 | error.function[0] = 0; 42 | error.errno = errno; 43 | if(fmt) 44 | tw_str_format_v(error.msg, sizeof(error.msg), fmt, va_args); 45 | else 46 | error.msg[0] = 0; 47 | tls.errors[tls.num_errors] = error; 48 | tls.num_errors++; 49 | } 50 | 51 | void tw_error_errno_set(int errno) 52 | { 53 | assert(errno != 0 && "can't set errno to 0"); 54 | tls.errno = errno; 55 | } 56 | 57 | int tw_error_errno(void) 58 | { 59 | return tls.errno; 60 | } 61 | 62 | void tw_error_errno_clear(void) 63 | { 64 | tls.errno = 0; 65 | } 66 | 67 | void tw_error_msg_clear(void) 68 | { 69 | tls.num_errors = 0; 70 | tls.msg_constructed = 0; 71 | } 72 | 73 | static void tw_error_msg_construct(void) 74 | { 75 | // no errors with string? return the stringified errno 76 | if(tls.num_errors == 0) 77 | { 78 | tw_str_format(tls.msg, sizeof(tls.msg), "unknown error (%d)", tls.errno); 79 | tls.msg_constructed = 1; 80 | return; 81 | } 82 | 83 | tls.msg[0] = 0; // make msg the empty string 84 | 85 | // construct the message the following way 86 | // "outer func error: inner func error: innermost func error" 87 | /*int i; 88 | for(i = tls.num_errors - 1; i >= 0; i--) 89 | { 90 | tw_error *error = &tls.errors[i]; 91 | if(error->msg[0] != 0) 92 | { 93 | // if we aren't at the beginning of the constructed 94 | // string, insert a ": " 95 | if(tls.msg[0] != 0) 96 | tw_str_append(tls.msg, ": ", sizeof(tls.msg)); 97 | tw_str_append(tls.msg, error->msg, sizeof(tls.msg)); 98 | } 99 | }*/ 100 | tw_str_copy(tls.msg, tls.errors[tls.num_errors - 1].msg, sizeof(tls.msg)); 101 | 102 | tls.msg_constructed = 1; 103 | } 104 | 105 | const char *tw_error_string_impl(void) 106 | { 107 | if(tls.errno == 0) 108 | return NULL; 109 | 110 | if(tls.msg_constructed == 0) 111 | tw_error_msg_construct(); 112 | assert(tls.msg_constructed != 0 && "msg construct didn't construct msg"); 113 | 114 | return tls.msg; 115 | } 116 | 117 | -------------------------------------------------------------------------------- /_old/datafile.py: -------------------------------------------------------------------------------- 1 | from datafile_py import * 2 | 3 | from weakref import WeakValueDictionary 4 | 5 | class Datafile: 6 | def __init__(self, file): 7 | self._file, self._file_owned = _get_file(file) 8 | self.name = self._file.name 9 | 10 | self._dfr = DatafileRaw() 11 | self._dfr.open(self._file) 12 | 13 | self._crc = None 14 | 15 | self._data = {} 16 | 17 | foo = {i: {item.id: item.data for item in self.types[i]} for i in range(7)} 18 | print(foo) 19 | raise NotImplementedError 20 | 21 | def __enter__(self): 22 | return self 23 | 24 | def __exit__(self, type, value, traceback): 25 | self.close() 26 | 27 | def __repr__(self): 28 | return ''.format(self.name) 29 | 30 | @property 31 | def crc(self): 32 | if self._crc is None: 33 | self._crc = self._dfr.crc_calc() 34 | return self._crc 35 | 36 | @property 37 | def data(self): 38 | return _DatafileData(self) 39 | 40 | @property 41 | def types(self): 42 | return _DatafileTypes(self) 43 | 44 | @property 45 | def items(self): 46 | return _DatafileItems(self) 47 | 48 | def close(self): 49 | if self._dfr is not None: 50 | self._dfr.close() 51 | if self._file is not None: 52 | if self._file_owned: 53 | self._file.close() 54 | self._dfr = None 55 | self._file = None 56 | self._data = None 57 | self._crc = None 58 | 59 | class _DatafileItems: 60 | def __init__(self, df): 61 | self._df = df 62 | def __getitem__(self, index): 63 | return self._df._dfr.item(index) 64 | def __iter__(self): 65 | return (self[i] for i in range(len(self))) 66 | def __len__(self): 67 | return self._df._dfr.num_items() 68 | 69 | class _DatafileTypeItems: 70 | def __init__(self, df, type_id): 71 | self._df = df 72 | self._type_id = type_id 73 | def __getitem__(self, id_): 74 | return self._df._dfr.item_find(self._type_id, id_) 75 | def __iter__(self): 76 | return (self._df._dfr.item(i) for i in self._df._dfr.type_indexes(self._type_id)) 77 | def __len__(self): 78 | return len(self._df._dfr.type_indexes(self._type_id)) 79 | 80 | class _DatafileTypes: 81 | def __init__(self, df): 82 | self._df = df 83 | def __getitem__(self, type_id): 84 | return _DatafileTypeItems(self._df, type_id) 85 | 86 | class _DatafileData: 87 | def __init__(self, df): 88 | self._df = df 89 | def __getitem__(self, index): 90 | try: 91 | return self._df._data[index] 92 | except KeyError: 93 | pass 94 | d = self._df._dfr.data(index) 95 | self._df._data[index] = d 96 | return d 97 | def drop(self, index): 98 | del self._df._data[index] 99 | def __iter__(self): 100 | return (self[i] for i in range(len(self))) 101 | def __len__(self): 102 | return self._df._dfr.num_data() 103 | 104 | def _get_file(file, mode='rb'): 105 | try: 106 | file.read 107 | file.seek 108 | file.tell 109 | except AttributeError: 110 | return open(file, mode), True 111 | else: 112 | return file, False 113 | -------------------------------------------------------------------------------- /tools/src/bin/teehistorian_dump.rs: -------------------------------------------------------------------------------- 1 | use libtw2_teehistorian::Buffer; 2 | use libtw2_teehistorian::Error; 3 | use libtw2_teehistorian::Item; 4 | use libtw2_teehistorian::Reader; 5 | use serde_derive::Serialize; 6 | use std::io; 7 | use std::path::Path; 8 | use std::process; 9 | 10 | #[derive(Serialize)] 11 | struct TickAndItem<'a> { 12 | tick: i32, 13 | item: Item<'a>, 14 | } 15 | 16 | fn process(path: &Path, json: bool) -> Result<(), Error> { 17 | let mut buffer = Buffer::new(); 18 | let (_, mut reader) = Reader::open(path, &mut buffer)?; 19 | let mut tick = None; 20 | if json { 21 | println!("["); 22 | } 23 | let mut first = true; 24 | while let Some(item) = reader.read(&mut buffer)? { 25 | match item { 26 | Item::TickStart(t) => { 27 | assert!(tick.is_none()); 28 | tick = Some(t); 29 | } 30 | Item::TickEnd(t) => { 31 | assert_eq!(tick, Some(t)); 32 | tick = None; 33 | } 34 | _ => { 35 | if !first { 36 | if json { 37 | println!(","); 38 | } 39 | } else { 40 | first = false; 41 | } 42 | if json { 43 | let stdout = io::stdout(); 44 | serde_json::to_writer( 45 | stdout.lock(), 46 | &TickAndItem { 47 | tick: tick.unwrap(), 48 | item: item, 49 | }, 50 | ) 51 | .unwrap(); 52 | } else { 53 | println!("{} {:?}", tick.expect("in tick"), item); 54 | } 55 | } 56 | } 57 | } 58 | assert!(tick.is_none()); 59 | if json { 60 | println!(); 61 | println!("]"); 62 | } 63 | Ok(()) 64 | } 65 | 66 | fn main() { 67 | use clap::App; 68 | use clap::Arg; 69 | 70 | libtw2_logger::init(); 71 | 72 | let matches = App::new("Teehistorian reader") 73 | .about( 74 | "Reads teehistorian file and dumps its contents in a human-readable\ 75 | text stream", 76 | ) 77 | .arg( 78 | Arg::with_name("TEEHISTORIAN") 79 | .help("Sets the teehistorian file to dump") 80 | .required(true), 81 | ) 82 | .arg( 83 | Arg::with_name("json") 84 | .long("json") 85 | .help("Output machine-readable JSON"), 86 | ) 87 | .get_matches(); 88 | 89 | let path = Path::new(matches.value_of_os("TEEHISTORIAN").unwrap()); 90 | let json = matches.is_present("json"); 91 | 92 | match process(path, json) { 93 | Ok(()) => {} 94 | Err(err) => { 95 | eprintln!("{}: {:?}", path.display(), err); 96 | process::exit(1); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /stats-browser/src/socket.rs: -------------------------------------------------------------------------------- 1 | use crate::addr::Addr; 2 | use mio::net::UdpSocket as MioUdpSocket; 3 | use std::fmt; 4 | use std::io; 5 | 6 | /// An unconnected non-blocking UDP socket. 7 | pub struct UdpSocket(MioUdpSocket); 8 | 9 | fn non_block(res: io::Result) -> io::Result> { 10 | match res { 11 | Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Ok(None), 12 | Err(e) => Err(e), 13 | Ok(x) => Ok(Some(x)), 14 | } 15 | } 16 | 17 | impl UdpSocket { 18 | /// Opens a UDP socket. 19 | pub fn open() -> SockResult { 20 | MioUdpSocket::bind(&"[::]:0".parse().unwrap()) 21 | .map(|s| UdpSocket(s)) 22 | .map_err(|e| SockError(e)) 23 | } 24 | /// Sends a UDP packet to the specified address. Non-blocking. 25 | pub fn send_to(&mut self, buf: &[u8], dst: Addr) -> SockResult> { 26 | let &mut UdpSocket(ref mut std_sock) = self; 27 | match non_block(std_sock.send_to(buf, &dst.to_socket_addr())) { 28 | Ok(Some(len)) => { 29 | assert!(len == buf.len(), "short send: {} out of {}", len, buf.len()); 30 | Ok(Ok(())) 31 | } 32 | Ok(None) => Ok(Err(WouldBlock)), 33 | Err(e) => Err(SockError(e)), 34 | } 35 | } 36 | /// Receives a UDP packet. Non-blocking. 37 | /// 38 | /// Returns number number of bytes read and the source address. 39 | pub fn recv_from(&mut self, buf: &mut [u8]) -> SockResult> { 40 | let &mut UdpSocket(ref mut std_sock) = self; 41 | match non_block(std_sock.recv_from(buf)) { 42 | Ok(Some((len, sockaddr))) => { 43 | let from = Addr::from_socket_addr(sockaddr); 44 | Ok(Ok((len, from))) 45 | } 46 | Ok(None) => Ok(Err(WouldBlock)), 47 | Err(x) => Err(SockError(x)), 48 | } 49 | } 50 | } 51 | 52 | /// Extension trait providing the `would_block` function for `NonBlock`. 53 | pub trait NonBlockExt { 54 | fn would_block(&self) -> bool; 55 | } 56 | impl NonBlockExt for NonBlock { 57 | /// Returns `true` if the operation would block. 58 | fn would_block(&self) -> bool { 59 | if let &Err(WouldBlock) = self { 60 | true 61 | } else { 62 | false 63 | } 64 | } 65 | } 66 | 67 | /// Socket error. Opaque struct. 68 | pub struct SockError(io::Error); 69 | 70 | /// Socket result alias. 71 | pub type SockResult = Result; 72 | /// Non-blocking result alias. 73 | pub type NonBlock = Result; 74 | 75 | /// Returned when an operation can't succeed without blocking. 76 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 77 | pub struct WouldBlock; 78 | 79 | // --------------------------------------- 80 | // Boilerplate trait implementations below 81 | // --------------------------------------- 82 | 83 | impl fmt::Debug for SockError { 84 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 85 | let SockError(ref inner) = *self; 86 | fmt::Debug::fmt(inner, f) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /_old/main.c: -------------------------------------------------------------------------------- 1 | #include "datafile.h" 2 | #include "error.h" 3 | #include "gamemap_constants.h" 4 | 5 | #include 6 | 7 | enum 8 | { 9 | COUNT_NOVERSION=0, 10 | COUNT_MULTIPLEVERSIONS, 11 | COUNT_SMALLVERSION, 12 | COUNT_BIGVERSION, 13 | COUNT_VERSIONNOT1, 14 | COUNT_VERSIONIDNOT0, 15 | NUM_COUNTS, 16 | 17 | MAX_VERSIONS=1024, 18 | }; 19 | 20 | static const char *const COUNT_NAMES[NUM_COUNTS] = { 21 | "no version", 22 | "multiple versions", 23 | "version too small", 24 | "version bigger than expected", 25 | "version not 1", 26 | "version ID not 0", 27 | }; 28 | 29 | int main(int argc, char **argv) 30 | { 31 | if(argc < 1 + 1) 32 | { 33 | fprintf(stderr, "USAGE: %s ...\n", argv[0]); 34 | return 1; 35 | } 36 | 37 | int counts[NUM_COUNTS] = { 0 }; 38 | 39 | int versions[MAX_VERSIONS][2]; 40 | int num_versions = 0; 41 | 42 | int i; 43 | for(i = 1; i < argc; i++) 44 | { 45 | const char *filename = argv[i]; 46 | 47 | #define COUNT(count) do { counts[count]++; printf("%s: %s\n", filename, COUNT_NAMES[count]); } while(0) 48 | 49 | tw_error_clear(); 50 | tw_datafile *df = tw_df_open(filename); 51 | if(!df) 52 | { 53 | fprintf(stderr, "%s: %d: %s\n", filename, tw_error_errno(), tw_error_string()); 54 | continue; 55 | } 56 | 57 | int start; 58 | int num; 59 | tw_df_type_indexes(df, TW_MAP_ITEMTYPE_VERSION, &start, &num); 60 | 61 | if(num == 0) 62 | COUNT(COUNT_NOVERSION); 63 | else if(num > 1) 64 | COUNT(COUNT_MULTIPLEVERSIONS); 65 | 66 | int k; 67 | for(k = start; k < start + num; k++) 68 | { 69 | size_t size; 70 | int id; 71 | void *item = tw_df_item_read(df, k, &size, NULL, &id); 72 | 73 | if(size < sizeof(tw_map_item_version) % sizeof(int32_t)) 74 | { 75 | COUNT(COUNT_SMALLVERSION); 76 | continue; 77 | } 78 | 79 | if(size > sizeof(tw_map_item_version) / sizeof(int32_t)) 80 | COUNT(COUNT_BIGVERSION); 81 | 82 | if(id != 0) 83 | COUNT(COUNT_VERSIONIDNOT0); 84 | 85 | tw_map_item_version *version = item; 86 | if(version->version != 1) 87 | COUNT(COUNT_VERSIONNOT1); 88 | 89 | int l; 90 | for(l = 0; l < num_versions; l++) 91 | { 92 | if(versions[l][0] == version->version) 93 | { 94 | versions[l][1]++; 95 | break; 96 | } 97 | } 98 | 99 | // nothing found 100 | if(l == num_versions) 101 | { 102 | if(l < MAX_VERSIONS) 103 | { 104 | versions[l][0] = version->version; 105 | versions[l][1] = 1; 106 | num_versions++; 107 | } 108 | else 109 | printf("too much versions\n"); 110 | } 111 | } 112 | 113 | //tw_df_dump(df);*/ 114 | 115 | tw_error_clear(); 116 | if(tw_df_close(df) != 0) 117 | { 118 | fprintf(stderr, "%s: %d: %s", filename, tw_error_errno(), tw_error_string()); 119 | continue; 120 | } 121 | } 122 | 123 | for(i = 0; i < NUM_COUNTS; i++) 124 | printf("%s: %d\n", COUNT_NAMES[i], counts[i]); 125 | 126 | for(i = 0; i < num_versions; i++) 127 | printf("version %d: %d\n", versions[i][0], versions[i][1]); 128 | 129 | return 0; 130 | } 131 | -------------------------------------------------------------------------------- /httphook/src/json.rs: -------------------------------------------------------------------------------- 1 | use libtw2_serverbrowse::protocol as browse_protocol; 2 | use serde_derive::Serialize; 3 | 4 | #[derive(Serialize)] 5 | pub struct Server { 6 | pub max_clients: i32, 7 | pub max_players: i32, 8 | pub passworded: bool, 9 | pub game_type: String, 10 | #[serde(skip_serializing_if = "Option::is_none")] 11 | pub country: Option, 12 | pub name: String, 13 | pub map: Map, 14 | pub version: String, 15 | #[serde(skip_serializing_if = "Option::is_none")] 16 | pub requires_login: Option, 17 | pub clients: Vec, 18 | } 19 | 20 | #[derive(Clone, Copy)] 21 | pub struct Iso3166_1Numeric(i32); 22 | 23 | #[derive(Serialize)] 24 | pub struct Map { 25 | pub name: String, 26 | #[serde(skip_serializing_if = "Option::is_none")] 27 | pub tw_crc: Option, 28 | #[serde(skip_serializing_if = "Option::is_none")] 29 | pub size: Option, 30 | } 31 | 32 | #[derive(Serialize)] 33 | pub struct Client { 34 | pub name: String, 35 | pub clan: String, 36 | pub country: i32, 37 | pub score: i32, 38 | pub is_player: bool, 39 | } 40 | 41 | impl<'de> serde::Deserialize<'de> for Iso3166_1Numeric { 42 | fn deserialize>( 43 | deserializer: D, 44 | ) -> Result { 45 | let inner = i32::deserialize(deserializer)?; 46 | if !(0 <= inner && inner < 1000) { 47 | return Err(serde::de::Error::invalid_value( 48 | serde::de::Unexpected::Signed(inner.into()), 49 | &"integer between 0 and 999", 50 | )); 51 | } 52 | Ok(Iso3166_1Numeric(inner)) 53 | } 54 | } 55 | 56 | impl serde::Serialize for Iso3166_1Numeric { 57 | fn serialize(&self, serializer: S) -> Result { 58 | self.0.serialize(serializer) 59 | } 60 | } 61 | 62 | impl From<&browse_protocol::ServerInfo> for Server { 63 | fn from(info: &browse_protocol::ServerInfo) -> Server { 64 | Server { 65 | max_clients: info.max_clients, 66 | max_players: info.max_players, 67 | passworded: info.flags & browse_protocol::SERVERINFO_FLAG_PASSWORDED != 0, 68 | game_type: (&*info.game_type).into(), 69 | country: None, 70 | name: (&*info.name).into(), 71 | map: Map { 72 | name: (&*info.map).into(), 73 | tw_crc: info.map_crc.map(|crc| format!("{:08x}", crc)), 74 | size: info.map_size, 75 | }, 76 | version: (&*info.version).into(), 77 | requires_login: None, 78 | clients: info.clients.iter().map(Client::from).collect(), 79 | } 80 | } 81 | } 82 | 83 | impl From<&browse_protocol::ClientInfo> for Client { 84 | fn from(info: &browse_protocol::ClientInfo) -> Client { 85 | Client { 86 | name: (&*info.name).into(), 87 | clan: (&*info.clan).into(), 88 | country: info.country, 89 | score: info.score, 90 | is_player: info.flags & browse_protocol::CLIENTINFO_FLAG_SPECTATOR == 0, 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /teehistorian/src/file.rs: -------------------------------------------------------------------------------- 1 | use crate::format; 2 | use crate::format::item::INPUT_LEN; 3 | use crate::format::Header; 4 | use crate::raw; 5 | use crate::raw::Callback; 6 | use std::fs::File; 7 | use std::io; 8 | use std::io::Read; 9 | use std::ops; 10 | use std::path::Path; 11 | 12 | pub use crate::raw::Buffer; 13 | pub use crate::raw::Item; 14 | pub use crate::raw::Pos; 15 | 16 | #[derive(Debug)] 17 | pub enum Error { 18 | Teehistorian(format::Error), 19 | Io(io::Error), 20 | } 21 | 22 | impl From for Error { 23 | fn from(err: format::Error) -> Error { 24 | Error::Teehistorian(err) 25 | } 26 | } 27 | 28 | impl From for Error { 29 | fn from(err: io::Error) -> Error { 30 | Error::Io(err) 31 | } 32 | } 33 | 34 | impl From> for Error { 35 | fn from(err: raw::Error) -> Error { 36 | match err { 37 | raw::Error::Teehistorian(e) => Error::Teehistorian(e), 38 | raw::Error::Cb(e) => Error::Io(e), 39 | } 40 | } 41 | } 42 | 43 | struct CallbackData { 44 | file: File, 45 | } 46 | 47 | pub struct Reader { 48 | callback_data: CallbackData, 49 | raw: raw::Reader, 50 | } 51 | 52 | impl Reader { 53 | fn new_impl<'a>(file: File, buffer: &'a mut Buffer) -> Result<(Header<'a>, Reader), Error> { 54 | let mut callback_data = CallbackData { file: file }; 55 | let (header, raw) = raw::Reader::new(&mut callback_data, buffer)?; 56 | Ok(( 57 | header, 58 | Reader { 59 | callback_data: callback_data, 60 | raw: raw, 61 | }, 62 | )) 63 | } 64 | pub fn new<'a>(file: File, buffer: &'a mut Buffer) -> Result<(Header<'a>, Reader), Error> { 65 | Reader::new_impl(file, buffer) 66 | } 67 | pub fn open<'a, P: AsRef>( 68 | path: P, 69 | buffer: &'a mut Buffer, 70 | ) -> Result<(Header, Reader), Error> { 71 | fn inner<'a>(path: &Path, buffer: &'a mut Buffer) -> Result<(Header<'a>, Reader), Error> { 72 | Reader::new_impl(File::open(path)?, buffer) 73 | } 74 | inner(path.as_ref(), buffer) 75 | } 76 | pub fn read<'a>(&mut self, buffer: &'a mut Buffer) -> Result>, Error> { 77 | Ok(self.raw.read(&mut self.callback_data, buffer)?) 78 | } 79 | pub fn player_pos(&self, cid: i32) -> Option { 80 | self.raw.player_pos(cid) 81 | } 82 | pub fn input(&self, cid: i32) -> Option<[i32; INPUT_LEN]> { 83 | self.raw.input(cid) 84 | } 85 | pub fn cids(&self) -> ops::Range { 86 | self.raw.cids() 87 | } 88 | } 89 | 90 | impl Callback for CallbackData { 91 | type Error = io::Error; 92 | fn read_at_most(&mut self, buffer: &mut [u8]) -> io::Result> { 93 | match self.file.read(buffer) { 94 | Ok(0) => Ok(None), 95 | Ok(read) => Ok(Some(read)), 96 | Err(ref e) if e.kind() == io::ErrorKind::Interrupted => Ok(Some(0)), 97 | Err(e) => Err(e), 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /common/src/pretty.rs: -------------------------------------------------------------------------------- 1 | use arrayvec::ArrayVec; 2 | use std::ascii; 3 | use std::fmt; 4 | use std::mem; 5 | use std::ops; 6 | use std::str; 7 | 8 | pub struct AlmostString([u8]); 9 | 10 | impl AlmostString { 11 | pub fn new(bytes: &[u8]) -> &AlmostString { 12 | unsafe { mem::transmute(bytes) } 13 | } 14 | } 15 | 16 | pub struct AlmostStringSlice<'a>([&'a [u8]]); 17 | 18 | impl<'a> AlmostStringSlice<'a> { 19 | pub fn new<'b>(bytes_slice: &'b [&'a [u8]]) -> &'b AlmostStringSlice<'a> { 20 | unsafe { mem::transmute(bytes_slice) } 21 | } 22 | } 23 | 24 | pub struct Bytes([u8]); 25 | 26 | impl Bytes { 27 | pub fn new(bytes: &[u8]) -> &Bytes { 28 | unsafe { mem::transmute(bytes) } 29 | } 30 | } 31 | 32 | pub struct BytesSlice<'a>([&'a [u8]]); 33 | 34 | impl<'a> BytesSlice<'a> { 35 | pub fn new<'b>(bytes_slice: &'b [&'a [u8]]) -> &'b BytesSlice<'a> { 36 | unsafe { mem::transmute(bytes_slice) } 37 | } 38 | } 39 | 40 | struct Byte { 41 | string: ArrayVec<[u8; 4]>, 42 | } 43 | 44 | impl Byte { 45 | fn new(byte: u8) -> Byte { 46 | let mut string = ArrayVec::new(); 47 | if byte == b'\\' || byte == b'\"' { 48 | string.push(b'\\'); 49 | string.push(byte); 50 | } else { 51 | string.extend(ascii::escape_default(byte)); 52 | } 53 | Byte { string: string } 54 | } 55 | } 56 | 57 | impl ops::Deref for Byte { 58 | type Target = str; 59 | fn deref(&self) -> &str { 60 | unsafe { str::from_utf8_unchecked(&self.string) } 61 | } 62 | } 63 | 64 | impl fmt::Debug for Bytes { 65 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 66 | f.write_str("b\"")?; 67 | for &byte in &self.0 { 68 | f.write_str(&Byte::new(byte))?; 69 | } 70 | f.write_str("\"")?; 71 | Ok(()) 72 | } 73 | } 74 | 75 | impl<'a> fmt::Debug for BytesSlice<'a> { 76 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 77 | f.debug_list() 78 | .entries(self.0.iter().cloned().map(Bytes::new)) 79 | .finish() 80 | } 81 | } 82 | 83 | impl fmt::Debug for AlmostString { 84 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 85 | // FIXME: Replace this with a UTF-8 decoder. 86 | let string = String::from_utf8_lossy(&self.0); 87 | fmt::Debug::fmt(&string, f) 88 | } 89 | } 90 | 91 | impl fmt::Display for AlmostString { 92 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 93 | // FIXME: Replace this with a UTF-8 decoder. 94 | let string = String::from_utf8_lossy(&self.0); 95 | if string.chars().any(|c| c < ' ' || c == '"') { 96 | fmt::Debug::fmt(&string, f) 97 | } else { 98 | fmt::Display::fmt(&string, f) 99 | } 100 | } 101 | } 102 | 103 | impl<'a> fmt::Debug for AlmostStringSlice<'a> { 104 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 105 | f.debug_list() 106 | .entries(self.0.iter().cloned().map(AlmostString::new)) 107 | .finish() 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /wireshark-dissector/src/format.rs: -------------------------------------------------------------------------------- 1 | use arrayvec::Array; 2 | use arrayvec::ArrayString; 3 | use std::fmt; 4 | use std::mem; 5 | 6 | pub struct Bitfield<'a> { 7 | bytes: &'a [u8], 8 | mask: u64, 9 | } 10 | 11 | impl<'a> Bitfield<'a> { 12 | pub fn new(bytes: &[u8], mask: u64) -> Bitfield { 13 | assert!(bytes.len() <= mem::size_of_val(&mask)); 14 | Bitfield { 15 | bytes: bytes, 16 | mask: mask, 17 | } 18 | } 19 | } 20 | 21 | impl<'a> fmt::Display for Bitfield<'a> { 22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 | let mut result: ArrayString<[u8; 256]> = ArrayString::new(); 24 | for (i, &b) in self.bytes.iter().enumerate() { 25 | if i != 0 { 26 | result.push_str(" "); 27 | } 28 | let mask_shift = (self.bytes.len() - i - 1) * 8; 29 | for j in (0..8).rev() { 30 | let bit = 1 << j; 31 | let mask_bit = 1 << (j + mask_shift); 32 | result.push_str(match (mask_bit & self.mask != 0, b & bit != 0) { 33 | (false, _) => ".", 34 | (true, false) => "0", 35 | (true, true) => "1", 36 | }); 37 | if j == 4 { 38 | result.push_str(" "); 39 | } 40 | } 41 | } 42 | f.write_str(&result) 43 | } 44 | } 45 | 46 | pub struct CommaSeparated + Copy> { 47 | empty: bool, 48 | string: ArrayString, 49 | } 50 | 51 | impl + Copy> CommaSeparated { 52 | pub fn new() -> CommaSeparated { 53 | CommaSeparated { 54 | empty: true, 55 | string: ArrayString::new(), 56 | } 57 | } 58 | pub fn add(&mut self, s: &str) { 59 | if !self.empty { 60 | self.string.push_str(", "); 61 | } 62 | self.empty = false; 63 | self.string.push_str(s); 64 | } 65 | pub fn or<'a>(&'a self, default: &'a str) -> &'a str { 66 | if !self.empty { 67 | &self.string 68 | } else { 69 | default 70 | } 71 | } 72 | } 73 | 74 | pub struct NumBytes { 75 | num: usize, 76 | } 77 | 78 | impl NumBytes { 79 | pub fn new(num: usize) -> NumBytes { 80 | NumBytes { num } 81 | } 82 | } 83 | 84 | impl fmt::Display for NumBytes { 85 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 86 | if self.num != 1 { 87 | write!(f, "{} bytes", self.num) 88 | } else { 89 | write!(f, "{} byte", self.num) 90 | } 91 | } 92 | } 93 | 94 | #[cfg(test)] 95 | mod test { 96 | use super::Bitfield; 97 | use std::fmt; 98 | 99 | fn assert_fmt(t: T, expected: &str) { 100 | assert_eq!(t.to_string(), expected); 101 | } 102 | 103 | #[test] 104 | fn format() { 105 | assert_fmt(Bitfield::new(&[], 0), ""); 106 | assert_fmt(Bitfield::new(&[0], 0), ".... ...."); 107 | assert_fmt(Bitfield::new(&[0], 0b1100_1100), "00.. 00.."); 108 | assert_fmt( 109 | Bitfield::new(&[0b10101010; 2], 0b0011_0011_1010_1010), 110 | "..10 ..10 1.1. 1.1.", 111 | ); 112 | } 113 | } 114 | --------------------------------------------------------------------------------