aboutsummaryrefslogtreecommitdiff
path: root/src/opcode.rs
blob: 1985605ee6a43fa1d5c625e20d624fa6cdcb319c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use std::io::ErrorKind;

use tokio_util::{
    bytes::{Buf, BufMut},
    codec::{Decoder, Encoder},
};

use crate::{jobs::JobOpcode, player::FetchUpdateStatus};

#[derive(Copy, Clone)]
pub struct OpcodeDecoder {}

pub struct Opcode {
    pub opcode: JobOpcode,
    pub request_id: u32,

    pub signature: String,
}

pub struct OpcodeResponse {
    pub opcode: JobOpcode,
    pub request_id: u32,

    pub update_status: Result<(), FetchUpdateStatus>,
    pub signature: String,
    pub signature_timestamp: u64,

    pub has_player: u8,
    pub player_id: u32,
    pub last_player_update: u64,
}

impl Default for OpcodeResponse {
    fn default() -> Self {
        OpcodeResponse {
            opcode: JobOpcode::ForceUpdate,
            request_id: 0,
            update_status: Ok(()),
            signature: String::new(),
            signature_timestamp: 0,
            has_player: 0,
            player_id: 0,
            last_player_update: 0,
        }
    }
}
impl Decoder for OpcodeDecoder {
    type Item = Opcode;
    type Error = std::io::Error;

    fn decode(
        &mut self,
        src: &mut tokio_util::bytes::BytesMut,
    ) -> Result<Option<Self::Item>, Self::Error> {
        //println!("Decoder length: {}", src.len());
        if 5 > src.len() {
            return Ok(None);
        }

        let opcode_byte: u8 = src[0];
        let opcode: JobOpcode = opcode_byte.into();
        let request_id: u32 = u32::from_be_bytes(src[1..5].try_into().unwrap());

        match opcode {
            JobOpcode::ForceUpdate
            | JobOpcode::GetSignatureTimestamp
            | JobOpcode::PlayerStatus
            | JobOpcode::PlayerUpdateTimestamp => {
                src.advance(5);
                Ok(Some(Opcode {
                    opcode,
                    request_id,
                    signature: Default::default(),
                }))
            }
            JobOpcode::DecryptSignature | JobOpcode::DecryptNSignature => {
                if 7 > src.len() {
                    return Ok(None);
                }

                let sig_size: u16 = ((src[5] as u16) << 8) | src[6] as u16;

                if (usize::from(sig_size) + 7) > src.len() {
                    return Ok(None);
                }

                let sig: String =
                    match String::from_utf8(src[7..(usize::from(sig_size) + 7)].to_vec()) {
                        Ok(x) => x,
                        Err(x) => {
                            return Err(std::io::Error::new(
                                ErrorKind::InvalidData,
                                x.utf8_error(),
                            ));
                        }
                    };

                src.advance(7 + sig.len());

                Ok(Some(Opcode {
                    opcode,
                    request_id,
                    signature: sig,
                }))
            }
            _ => Err(std::io::Error::new(ErrorKind::InvalidInput, "")),
        }
    }
}

impl Encoder<OpcodeResponse> for OpcodeDecoder {
    type Error = std::io::Error;
    fn encode(
        &mut self,
        item: OpcodeResponse,
        dst: &mut tokio_util::bytes::BytesMut,
    ) -> Result<(), Self::Error> {
        dst.put_u32(item.request_id);
        match item.opcode {
            JobOpcode::ForceUpdate => {
                dst.put_u32(2);
                match item.update_status {
                    Ok(_x) => dst.put_u16(0xF44F),
                    Err(FetchUpdateStatus::PlayerAlreadyUpdated) => dst.put_u16(0xFFFF),
                    Err(_x) => dst.put_u16(0x0000),
                }
            }
            JobOpcode::DecryptSignature | JobOpcode::DecryptNSignature => {
                dst.put_u32(2 + u32::try_from(item.signature.len()).unwrap());
                dst.put_u16(u16::try_from(item.signature.len()).unwrap());
                if !item.signature.is_empty() {
                    dst.put_slice(item.signature.as_bytes());
                }
            }
            JobOpcode::GetSignatureTimestamp => {
                dst.put_u32(8);
                dst.put_u64(item.signature_timestamp);
            }
            JobOpcode::PlayerStatus => {
                dst.put_u32(5);
                dst.put_u8(item.has_player);
                dst.put_u32(item.player_id);
            }
            JobOpcode::PlayerUpdateTimestamp => {
                dst.put_u32(8);
                dst.put_u64(item.last_player_update);
            }
            _ => {}
        }
        Ok(())
    }
}