mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-05-06 07:17:27 +00:00
Decompression support
This commit is contained in:
parent
e621a9aedd
commit
3b4dd9d9e4
4 changed files with 61 additions and 24 deletions
|
|
@ -15,3 +15,5 @@ keywords = ["server", "verify", "game", "check", "status"]
|
|||
msrv = "1.58.1"
|
||||
|
||||
[dependencies]
|
||||
crc32fast = "1.3.2"
|
||||
bzip2-rs = "0.1.2"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
# Supported protocols:
|
||||
| Name | Documentation reference | Used by |
|
||||
|----------------|---------------------------------------------------------------------------|---------------------|
|
||||
| Valve Protocol | [Server Queries](https://developer.valvesoftware.com/wiki/Server_queries) | TF2, CSGO, The_ship |
|
||||
| Name | Documentation reference | Used by | Notes |
|
||||
|----------------|---------------------------------------------------------------------------|------------------------------------------------|----------------------------------------|
|
||||
| Valve Protocol | [Server Queries](https://developer.valvesoftware.com/wiki/Server_queries) | TF2, CSGO, TS, CSS, DODS, GM, HL2DM, L4D, L4D2 | Multi-packet decompression not tested. |
|
||||
|
||||
## Planned to add support:
|
||||
Minecraft protocol
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ pub enum GDError {
|
|||
PacketSend(String),
|
||||
/// Couldn't send the receive.
|
||||
PacketReceive(String),
|
||||
/// Unknown cast while translating a value to an enum
|
||||
/// Couldn't decompress data.
|
||||
Decompress(String),
|
||||
/// Unknown cast while translating a value to an enum.
|
||||
UnknownEnumCast,
|
||||
/// The server queried is not from the queried game.
|
||||
BadGame(String)
|
||||
|
|
@ -31,6 +33,7 @@ impl fmt::Display for GDError {
|
|||
GDError::PacketBad(details) => write!(f, "Packet bad: {details}"),
|
||||
GDError::PacketSend(details) => write!(f, "Couldn't send a packet: {details}"),
|
||||
GDError::PacketReceive(details) => write!(f, "Couldn't receive a packet: {details}"),
|
||||
GDError::Decompress(details) => write!(f, "Couldn't decompress data: {details}"),
|
||||
GDError::UnknownEnumCast => write!(f, "Unknown enum cast encountered."),
|
||||
GDError::BadGame(details) => write!(f, "Queried another game that the supposed one: {details}"),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use std::net::UdpSocket;
|
||||
use bzip2_rs::decoder::Decoder;
|
||||
use crate::{GDError, GDResult};
|
||||
use crate::utils::{buffer, complete_address};
|
||||
|
||||
|
|
@ -233,16 +234,19 @@ impl Packet {
|
|||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)] //remove this later on
|
||||
struct SplitPacketInfo {
|
||||
struct SplitPacket {
|
||||
pub header: u32,
|
||||
pub id: u32,
|
||||
pub total: u8,
|
||||
pub number: u8,
|
||||
pub size: u16,
|
||||
pub payload: Vec<u8>
|
||||
pub compressed: bool,
|
||||
pub decompressed_size: Option<u32>,
|
||||
pub uncompressed_crc32: Option<u32>,
|
||||
payload: Vec<u8>
|
||||
}
|
||||
|
||||
impl SplitPacketInfo {
|
||||
impl SplitPacket {
|
||||
fn new(_app: &App, buf: &[u8]) -> GDResult<Self> {
|
||||
let mut pos = 0;
|
||||
|
||||
|
|
@ -251,16 +255,10 @@ impl SplitPacketInfo {
|
|||
let total = buffer::get_u8(&buf, &mut pos)?;
|
||||
let number = buffer::get_u8(&buf, &mut pos)?;
|
||||
let size = buffer::get_u16_le(&buf, &mut pos)?;
|
||||
|
||||
let payload = match ((id >> 31) & 1) == 1 {
|
||||
false => buf[pos..].to_vec(),
|
||||
true => {
|
||||
let _decompressed_size = buffer::get_u32_le(&buf, &mut pos)?;
|
||||
let _uncompressed_crc32 = buffer::get_u32_le(&buf, &mut pos)?;
|
||||
|
||||
//decompress...
|
||||
vec![]
|
||||
}
|
||||
let compressed = ((id >> 31) & 1) == 1;
|
||||
let (decompressed_size, uncompressed_crc32) = match compressed {
|
||||
false => (None, None),
|
||||
true => (Some(buffer::get_u32_le(&buf, &mut pos)?), Some(buffer::get_u32_le(&buf, &mut pos)?))
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
|
|
@ -269,9 +267,44 @@ impl SplitPacketInfo {
|
|||
total,
|
||||
number,
|
||||
size,
|
||||
payload
|
||||
compressed,
|
||||
decompressed_size,
|
||||
uncompressed_crc32,
|
||||
payload: buf[pos..].to_vec()
|
||||
})
|
||||
}
|
||||
|
||||
fn decompress(&self) -> GDResult<Vec<u8>> {
|
||||
if !self.compressed {
|
||||
let mut decoder = Decoder::new();
|
||||
decoder.write(&self.payload).map_err(|e| GDError::Decompress(e.to_string()))?;
|
||||
|
||||
let decompressed_size = self.decompressed_size.unwrap() as usize;
|
||||
|
||||
let mut decompressed_payload = Vec::with_capacity(decompressed_size);
|
||||
decoder.read(&mut decompressed_payload).map_err(|e| GDError::Decompress(e.to_string()))?;
|
||||
|
||||
if decompressed_payload.len() != decompressed_size {
|
||||
Err(GDError::Decompress("Valve Protocol: The decompressed payload size doesn't match the expected one.".to_string()))
|
||||
}
|
||||
else if crc32fast::hash(&decompressed_payload) != self.uncompressed_crc32.unwrap() {
|
||||
Err(GDError::Decompress("Valve Protocol: The decompressed crc32 hash does not match the expected one.".to_string()))
|
||||
}
|
||||
else {
|
||||
Ok(decompressed_payload)
|
||||
}
|
||||
} else { //already decompressed
|
||||
Ok(self.payload.clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_payload(&self) -> GDResult<Vec<u8>> {
|
||||
if self.compressed {
|
||||
Ok(self.decompress()?)
|
||||
} else {
|
||||
Ok(self.payload.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValveProtocol {
|
||||
|
|
@ -302,16 +335,15 @@ impl ValveProtocol {
|
|||
let mut buf = self.receive_raw(buffer_size)?;
|
||||
|
||||
if buf[0] == 0xFE { //the packet is split
|
||||
let initial_split_packet_info = SplitPacketInfo::new(app, &buf)?;
|
||||
let mut final_packet = Packet::new(&initial_split_packet_info.payload)?;
|
||||
let mut main_packet = SplitPacket::new(app, &buf)?;
|
||||
|
||||
for _ in 1..initial_split_packet_info.total {
|
||||
for _ in 1..main_packet.total {
|
||||
buf = self.receive_raw(buffer_size)?;
|
||||
let split_packet_info = SplitPacketInfo::new(app, &buf)?;
|
||||
final_packet.payload.extend(split_packet_info.payload);
|
||||
let chunk_packet = SplitPacket::new(app, &buf)?;
|
||||
main_packet.payload.extend(chunk_packet.payload);
|
||||
}
|
||||
|
||||
Ok(final_packet)
|
||||
Ok(Packet::new(&main_packet.get_payload()?)?)
|
||||
}
|
||||
else {
|
||||
Packet::new(&buf)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue