refactor
This commit is contained in:
parent
a4ccc96ed0
commit
aab4414904
71 changed files with 293 additions and 4316 deletions
89
rnex-core/src/rmc/message.rs
Normal file
89
rnex-core/src/rmc/message.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
use std::io;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use bytemuck::bytes_of;
|
||||
use log::error;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use crate::rmc::response::{ErrorCode, RMCResponseResult};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RMCMessage{
|
||||
pub protocol_id: u16,
|
||||
pub call_id: u32,
|
||||
pub method_id: u32,
|
||||
|
||||
pub rest_of_data: Vec<u8>
|
||||
}
|
||||
|
||||
impl RMCMessage{
|
||||
pub fn new(stream: &mut (impl Seek + Read)) -> io::Result<Self>{
|
||||
let size: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let mut header_size = 1 + 4 + 4;
|
||||
|
||||
let protocol_id: u8 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
let protocol_id= protocol_id & (!0x80);
|
||||
|
||||
let protocol_id: u16 = match protocol_id{
|
||||
0x7F => {
|
||||
header_size += 2;
|
||||
stream.read_struct(IS_BIG_ENDIAN)?
|
||||
},
|
||||
_ => protocol_id as u16
|
||||
};
|
||||
|
||||
let call_id = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
let method_id = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let mut rest_of_data = Vec::new();
|
||||
|
||||
stream.read_to_end(&mut rest_of_data)?;
|
||||
|
||||
if header_size + rest_of_data.len() != size as usize {
|
||||
error!("received incorrect rmc packet: expected size {} but found {}", size, header_size + rest_of_data.len());
|
||||
}
|
||||
|
||||
|
||||
|
||||
//stream.
|
||||
Ok(Self{
|
||||
protocol_id,
|
||||
method_id,
|
||||
call_id,
|
||||
rest_of_data
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_data(&self) -> Vec<u8>{
|
||||
let size = (1 + 4 + 4 + self.rest_of_data.len()) as u32;
|
||||
|
||||
let mut output = Vec::new();
|
||||
|
||||
output.write_all(bytes_of(&size)).expect("unable to write size");
|
||||
|
||||
let proto_id = self.protocol_id as u8 | 0x80;
|
||||
|
||||
output.write_all(bytes_of(&proto_id)).expect("unable to write size");
|
||||
|
||||
output.write_all(bytes_of(&self.call_id)).expect("unable to write size");
|
||||
output.write_all(bytes_of(&self.method_id)).expect("unable to write size");
|
||||
|
||||
output.write_all(&self.rest_of_data).expect("unable to write data");
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
pub fn error_result_with_code(&self, error_code: ErrorCode) -> RMCResponseResult{
|
||||
RMCResponseResult::Error {
|
||||
call_id: self.call_id,
|
||||
error_code
|
||||
}
|
||||
}
|
||||
|
||||
pub fn success_with_data(&self, data: Vec<u8>) -> RMCResponseResult{
|
||||
RMCResponseResult::Success {
|
||||
call_id: self.call_id,
|
||||
method_id: self.method_id,
|
||||
data
|
||||
}
|
||||
}
|
||||
}
|
||||
7
rnex-core/src/rmc/mod.rs
Normal file
7
rnex-core/src/rmc/mod.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pub mod message;
|
||||
pub mod structures;
|
||||
pub mod response;
|
||||
pub mod protocols;
|
||||
|
||||
|
||||
|
||||
47
rnex-core/src/rmc/protocols/auth.rs
Normal file
47
rnex-core/src/rmc/protocols/auth.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
use rnex_core::rmc::response::ErrorCode;
|
||||
use rnex_core::rmc::structures::any::Any;
|
||||
use crate::rmc::structures::connection_data::ConnectionData;
|
||||
use rnex_core::rmc::structures::qresult::QResult;
|
||||
use macros::{method_id, rmc_proto};
|
||||
|
||||
|
||||
/// This is the representation for `Ticket Granting`(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[rmc_proto(10)]
|
||||
pub trait Auth {
|
||||
/// representation of the `Login` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(1)]
|
||||
async fn login(&self, name: String) -> Result<(), ErrorCode>;
|
||||
|
||||
/// representation of the `LoginEx` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(2)]
|
||||
async fn login_ex(
|
||||
&self,
|
||||
name: String,
|
||||
extra_data: Any,
|
||||
) -> Result<(QResult, u32, Vec<u8>, ConnectionData, String), ErrorCode>;
|
||||
|
||||
/// representation of the `RequestTicket` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(3)]
|
||||
async fn request_ticket(
|
||||
&self,
|
||||
source_pid: u32,
|
||||
destination_pid: u32,
|
||||
) -> Result<(QResult, Vec<u8>), ErrorCode>;
|
||||
|
||||
/// representation of the `GetPID` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(4)]
|
||||
async fn get_pid(&self, username: String) -> Result<u32, ErrorCode>;
|
||||
|
||||
/// representation of the `LoginWithContext` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(5)]
|
||||
async fn get_name(&self, pid: u32) -> Result<String, ErrorCode>;
|
||||
|
||||
// `LoginWithContext` is left out here because we don't need it right now and versioning still
|
||||
// needs to be figured out
|
||||
}
|
||||
17
rnex-core/src/rmc/protocols/matchmake.rs
Normal file
17
rnex-core/src/rmc/protocols/matchmake.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
use macros::{method_id, rmc_proto};
|
||||
use rnex_core::prudp::station_url::StationUrl;
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
|
||||
#[rmc_proto(21)]
|
||||
pub trait Matchmake{
|
||||
#[method_id(2)]
|
||||
async fn unregister_gathering(&self, gid: u32) -> Result<bool, ErrorCode>;
|
||||
#[method_id(41)]
|
||||
async fn get_session_urls(&self, gid: u32) -> Result<Vec<StationUrl>, ErrorCode>;
|
||||
|
||||
#[method_id(42)]
|
||||
async fn update_session_host(&self, gid: u32, change_owner: bool) -> Result<(), ErrorCode>;
|
||||
|
||||
#[method_id(44)]
|
||||
async fn migrate_gathering_ownership(&self, gid: u32, candidates: Vec<u32>, participants_only: bool) -> Result<(), ErrorCode>;
|
||||
}
|
||||
8
rnex-core/src/rmc/protocols/matchmake_ext.rs
Normal file
8
rnex-core/src/rmc/protocols/matchmake_ext.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
use macros::{method_id, rmc_proto};
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
|
||||
#[rmc_proto(50)]
|
||||
pub trait MatchmakeExt{
|
||||
#[method_id(1)]
|
||||
async fn end_participation(&self, gid: u32, message: String) -> Result<bool, ErrorCode>;
|
||||
}
|
||||
32
rnex-core/src/rmc/protocols/matchmake_extension.rs
Normal file
32
rnex-core/src/rmc/protocols/matchmake_extension.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
use macros::{method_id, rmc_proto};
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
use rnex_core::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession};
|
||||
|
||||
#[rmc_proto(109)]
|
||||
pub trait MatchmakeExtension{
|
||||
#[method_id(1)]
|
||||
async fn close_participation(&self, gid: u32) -> Result<(), ErrorCode>;
|
||||
|
||||
#[method_id(2)]
|
||||
async fn open_participation(&self, gid: u32) -> Result<(), ErrorCode>;
|
||||
|
||||
#[method_id(8)]
|
||||
async fn modify_current_game_attribute(&self, gid: u32, attrib_index: u32, attrib_val: u32) -> Result<(), ErrorCode>;
|
||||
|
||||
#[method_id(16)]
|
||||
async fn get_playing_session(&self, pids: Vec<u32>) -> Result<Vec<()>, ErrorCode>;
|
||||
|
||||
#[method_id(34)]
|
||||
async fn update_progress_score(&self, gid: u32, progress: u8) -> Result<(), ErrorCode>;
|
||||
#[method_id(38)]
|
||||
async fn create_matchmake_session_with_param(&self, session: CreateMatchmakeSessionParam) -> Result<MatchmakeSession, ErrorCode>;
|
||||
|
||||
#[method_id(39)]
|
||||
async fn join_matchmake_session_with_param(&self, session: JoinMatchmakeSessionParam) -> Result<MatchmakeSession, ErrorCode>;
|
||||
|
||||
#[method_id(40)]
|
||||
async fn auto_matchmake_with_param_postpone(&self, session: AutoMatchmakeParam) -> Result<MatchmakeSession, ErrorCode>;
|
||||
|
||||
#[method_id(41)]
|
||||
async fn find_matchmake_session_by_gathering_id_detail(&self, gid: u32) -> Result<MatchmakeSession, ErrorCode>;
|
||||
}
|
||||
342
rnex-core/src/rmc/protocols/mod.rs
Normal file
342
rnex-core/src/rmc/protocols/mod.rs
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
#![allow(async_fn_in_trait)]
|
||||
|
||||
pub mod auth;
|
||||
pub mod secure;
|
||||
pub mod notifications;
|
||||
pub mod matchmake;
|
||||
pub mod matchmake_extension;
|
||||
pub mod nat_traversal;
|
||||
pub mod matchmake_ext;
|
||||
pub mod ranking;
|
||||
|
||||
use crate::util::{SendingBufferConnection, SplittableBufferConnection};
|
||||
use crate::rmc::message::RMCMessage;
|
||||
use crate::rmc::protocols::RemoteCallError::ConnectionBroke;
|
||||
use crate::rmc::response::{ErrorCode, RMCResponse, RMCResponseResult};
|
||||
use crate::rmc::structures;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
use log::{error, info};
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
use std::io::Cursor;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::{Mutex, Notify};
|
||||
use tokio::time::{sleep, sleep_until, Instant};
|
||||
use crate::result::ResultExtension;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RemoteCallError {
|
||||
#[error("Call to remote timed out whilest waiting on response.")]
|
||||
Timeout,
|
||||
#[error("A server side rmc error occurred: {0:?}")]
|
||||
ServerError(ErrorCode),
|
||||
#[error("Connection broke")]
|
||||
ConnectionBroke,
|
||||
#[error("Error reading response data: {0}")]
|
||||
InvalidResponse(#[from] structures::Error),
|
||||
}
|
||||
|
||||
pub struct RmcConnection(pub SendingBufferConnection, pub RmcResponseReceiver);
|
||||
|
||||
pub struct RmcResponseReceiver(Arc<Notify>, Arc<Mutex<HashMap<u32, RMCResponse>>>);
|
||||
|
||||
impl RmcConnection {
|
||||
pub async fn make_raw_call<T: RmcSerialize>(
|
||||
&self,
|
||||
message: &RMCMessage,
|
||||
) -> Result<T, RemoteCallError> {
|
||||
self.make_raw_call_no_response(message).await?;
|
||||
|
||||
let data = self.1.get_response_data(message.call_id).await?;
|
||||
|
||||
let out = <T as RmcSerialize>::deserialize(&mut Cursor::new(data))?;
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub async fn make_raw_call_no_response(
|
||||
&self,
|
||||
message: &RMCMessage,
|
||||
) -> Result<(), RemoteCallError> {
|
||||
let message_data = message.to_data();
|
||||
|
||||
self.0.send(message_data).await.ok_or(ConnectionBroke)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn disconnect(&self){
|
||||
self.0.disconnect().await;
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcResponseReceiver {
|
||||
// returns none if timed out
|
||||
pub async fn get_response_data(&self, call_id: u32) -> Result<Vec<u8>, RemoteCallError> {
|
||||
let mut end_wait_time = Instant::now();
|
||||
end_wait_time += Duration::from_secs(5);
|
||||
|
||||
let sleep_fut = sleep_until(end_wait_time);
|
||||
tokio::pin!(sleep_fut);
|
||||
|
||||
let mut sleep_manual_unlock_fut = Instant::now();
|
||||
sleep_manual_unlock_fut += Duration::from_secs(4);
|
||||
|
||||
let sleep_manual_unlock_fut = sleep_until(sleep_manual_unlock_fut);
|
||||
tokio::pin!(sleep_manual_unlock_fut);
|
||||
|
||||
loop {
|
||||
let mut locked = self.1.lock().await;
|
||||
|
||||
if let Some(v) = locked.remove(&call_id) {
|
||||
match v.response_result{
|
||||
RMCResponseResult::Success {
|
||||
data,
|
||||
..
|
||||
} => return Ok(data),
|
||||
RMCResponseResult::Error {
|
||||
error_code,
|
||||
..
|
||||
} => return Err(RemoteCallError::ServerError(error_code))
|
||||
}
|
||||
}
|
||||
|
||||
drop(locked);
|
||||
|
||||
let notif_fut = self.0.notified();
|
||||
|
||||
tokio::select! {
|
||||
_ = &mut sleep_manual_unlock_fut => {
|
||||
continue;
|
||||
}
|
||||
_ = &mut sleep_fut => {
|
||||
return Err(RemoteCallError::Timeout);
|
||||
}
|
||||
_ = notif_fut => {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasRmcConnection {
|
||||
fn get_connection(&self) -> &RmcConnection;
|
||||
}
|
||||
|
||||
pub trait RemoteObject {
|
||||
fn new(conn: RmcConnection) -> Self;
|
||||
}
|
||||
|
||||
impl RemoteObject for () {
|
||||
fn new(_: RmcConnection) -> Self {}
|
||||
}
|
||||
|
||||
pub trait RmcCallable {
|
||||
//type Remote: RemoteObject;
|
||||
fn rmc_call(
|
||||
&self,
|
||||
responder: &SendingBufferConnection,
|
||||
protocol_id: u16,
|
||||
method_id: u32,
|
||||
call_id: u32,
|
||||
rest: Vec<u8>,
|
||||
) -> impl std::future::Future<Output = ()> + Send;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! define_rmc_proto {
|
||||
(proto $name:ident{
|
||||
$($protocol:path),*
|
||||
}) => {
|
||||
paste::paste!{
|
||||
pub trait [<Local $name>]: std::any::Any $( + [<Raw $protocol>] + $protocol)* {
|
||||
async fn rmc_call(&self, remote_response_connection: &rnex_core::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>){
|
||||
match protocol_id{
|
||||
$(
|
||||
[<Raw $protocol Info>]::PROTOCOL_ID => <Self as [<Raw $protocol>]>::rmc_call_proto(self, remote_response_connection, method_id, call_id, rest).await,
|
||||
)*
|
||||
v => log::error!("invalid protocol called on rmc object {}", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct [<Remote $name>](rnex_core::rmc::protocols::RmcConnection);
|
||||
|
||||
impl rnex_core::rmc::protocols::RemoteInstantiatable for [<Remote $name>]{
|
||||
fn new(conn: rnex_core::rmc::protocols::RmcConnection) -> Self{
|
||||
Self(conn)
|
||||
}
|
||||
async fn disconnect(&self){
|
||||
self.0.disconnect().await;
|
||||
}
|
||||
}
|
||||
|
||||
impl rnex_core::rmc::protocols::HasRmcConnection for [<Remote $name>]{
|
||||
fn get_connection(&self) -> &rnex_core::rmc::protocols::RmcConnection{
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl [<Remote $protocol>] for [<Remote $name>]{}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// This is a special case to allow unit to represent the fact that no object is represented.
|
||||
impl RmcCallable for () {
|
||||
async fn rmc_call(
|
||||
&self,
|
||||
_remote_response_connection: &SendingBufferConnection,
|
||||
_protocol_id: u16,
|
||||
_method_id: u32,
|
||||
_call_id: u32,
|
||||
_rest: Vec<u8>,
|
||||
) {
|
||||
//todo: maybe reply with not implemented(?)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RemoteInstantiatable{
|
||||
fn new(conn: RmcConnection) -> Self;
|
||||
async fn disconnect(&self);
|
||||
}
|
||||
|
||||
pub struct OnlyRemote<T: RemoteInstantiatable>(T);
|
||||
|
||||
impl<T: RemoteInstantiatable> Deref for OnlyRemote<T>{
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RemoteInstantiatable> OnlyRemote<T>{
|
||||
pub fn new(conn: RmcConnection) -> Self{
|
||||
Self(T::new(conn))
|
||||
}
|
||||
|
||||
pub async fn disconnect(&self) {
|
||||
self.0.disconnect().await;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RemoteInstantiatable> RmcCallable for OnlyRemote<T>{
|
||||
fn rmc_call(&self, _responder: &SendingBufferConnection, _protocol_id: u16, _method_id: u32, _call_id: u32, _rest: Vec<u8>) -> impl Future<Output = ()> + Send {
|
||||
// maybe respond with not implemented or something
|
||||
async{}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_incoming<T: RmcCallable + Send + Sync + 'static>(
|
||||
mut connection: SplittableBufferConnection,
|
||||
remote: Arc<T>,
|
||||
notify: Arc<Notify>,
|
||||
incoming: Arc<Mutex<HashMap<u32, RMCResponse>>>,
|
||||
) {
|
||||
let sending_conn = connection.duplicate_sender();
|
||||
|
||||
while let Some(v) = connection.recv().await{
|
||||
let Some(proto_id) = v.get(4) else {
|
||||
error!("received too small rmc message.");
|
||||
error!("ending rmc gateway.");
|
||||
return
|
||||
};
|
||||
|
||||
// protocol 0 is hardcoded to be the no protocol protocol aka keepalive protocol
|
||||
if *proto_id == 0{
|
||||
println!("got keepalive");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (proto_id & 0x80) == 0{
|
||||
let Some(response) = RMCResponse::new(&mut Cursor::new(v)).display_err_or_some() else {
|
||||
error!("ending rmc gateway.");
|
||||
return
|
||||
};
|
||||
|
||||
info!("got rmc response");
|
||||
|
||||
let mut locked = incoming.lock().await;
|
||||
|
||||
locked.insert(response.get_call_id(), response);
|
||||
notify.notify_waiters();
|
||||
} else {
|
||||
let Some(message) = RMCMessage::new(&mut Cursor::new(v)).display_err_or_some() else {
|
||||
error!("ending rmc gateway.");
|
||||
return
|
||||
};
|
||||
|
||||
let RMCMessage{
|
||||
protocol_id,
|
||||
method_id,
|
||||
call_id,
|
||||
rest_of_data
|
||||
} = message;
|
||||
|
||||
info!("RMC REQUEST: Proto: {}; Method: {};", protocol_id, method_id);
|
||||
|
||||
remote.rmc_call(&sending_conn, protocol_id, method_id, call_id, rest_of_data).await;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
info!("rmc disconnected")
|
||||
}
|
||||
|
||||
pub fn new_rmc_gateway_connection<T: RmcCallable + Sync + Send + 'static,F>(conn: SplittableBufferConnection, create_internal: F) -> Arc<T>
|
||||
where
|
||||
F: FnOnce(RmcConnection) -> Arc<T>,
|
||||
{
|
||||
let notify = Arc::new(Notify::new());
|
||||
let incoming: Arc<Mutex<HashMap<u32, RMCResponse>>> = Default::default();
|
||||
|
||||
let response_recv = RmcResponseReceiver(notify.clone(), incoming.clone());
|
||||
|
||||
let sending_conn = conn.duplicate_sender();
|
||||
|
||||
let rmc_conn = RmcConnection(sending_conn, response_recv);
|
||||
|
||||
let sending_conn = conn.duplicate_sender();
|
||||
|
||||
let exposed_object = (create_internal)(rmc_conn);
|
||||
|
||||
{
|
||||
let exposed_object = exposed_object.clone();
|
||||
tokio::spawn(async move {
|
||||
handle_incoming(
|
||||
conn,
|
||||
exposed_object,
|
||||
notify,
|
||||
incoming
|
||||
).await;
|
||||
});
|
||||
|
||||
|
||||
tokio::spawn(async move {
|
||||
while sending_conn.is_alive(){
|
||||
sending_conn.send([0,0,0,0,0].to_vec()).await;
|
||||
sleep(Duration::from_secs(10)).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
exposed_object
|
||||
}
|
||||
|
||||
impl<T: RmcCallable> RmcCallable for Arc<T>{
|
||||
fn rmc_call(&self, responder: &SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>) -> impl Future<Output=()> + Send {
|
||||
self.as_ref().rmc_call(responder, protocol_id, method_id, call_id, rest)
|
||||
}
|
||||
}
|
||||
|
||||
define_rmc_proto! {
|
||||
proto NoProto{}
|
||||
}
|
||||
23
rnex-core/src/rmc/protocols/nat_traversal.rs
Normal file
23
rnex-core/src/rmc/protocols/nat_traversal.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
use macros::{method_id, rmc_proto};
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
|
||||
#[rmc_proto(3)]
|
||||
pub trait NatTraversal{
|
||||
#[method_id(2)]
|
||||
async fn request_probe_initiation(&self, station_to_probe: String) -> Result<(),ErrorCode>;
|
||||
|
||||
#[method_id(3)]
|
||||
async fn request_probe_initialization_ext(&self, target_list: Vec<String>, station_to_probe: String) -> Result<(),ErrorCode>;
|
||||
|
||||
#[method_id(4)]
|
||||
async fn report_nat_traversal_result(&self, cid: u32, result: bool, rtt: u32) -> Result<(),ErrorCode>;
|
||||
|
||||
#[method_id(5)]
|
||||
async fn report_nat_properties(&self, nat_mapping: u32, nat_filtering: u32, rtt: u32) -> Result<(),ErrorCode>;
|
||||
}
|
||||
|
||||
#[rmc_proto(3, NoReturn)]
|
||||
pub trait NatTraversalConsole{
|
||||
#[method_id(2)]
|
||||
async fn request_probe_initiation(&self, station_to_probe: String);
|
||||
}
|
||||
24
rnex-core/src/rmc/protocols/notifications.rs
Normal file
24
rnex-core/src/rmc/protocols/notifications.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
use macros::{method_id, rmc_proto, rmc_struct, RmcSerialize};
|
||||
|
||||
pub mod notification_types{
|
||||
pub const OWNERSHIP_CHANGED: u32 = 4000;
|
||||
pub const HOST_CHANGED: u32 = 110000;
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize, Debug, Default, Clone)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct NotificationEvent{
|
||||
pub pid_source: u32,
|
||||
pub notif_type: u32,
|
||||
pub param_1: u32,
|
||||
pub param_2: u32,
|
||||
pub str_param: String,
|
||||
pub param_3: u32,
|
||||
}
|
||||
|
||||
#[rmc_proto(14, NoReturn)]
|
||||
pub trait Notification {
|
||||
#[method_id(1)]
|
||||
async fn process_notification_event(&self, event: NotificationEvent);
|
||||
}
|
||||
|
||||
5
rnex-core/src/rmc/protocols/ranking.rs
Normal file
5
rnex-core/src/rmc/protocols/ranking.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
use macros::rmc_proto;
|
||||
|
||||
#[rmc_proto(112)]
|
||||
pub trait Ranking{
|
||||
}
|
||||
12
rnex-core/src/rmc/protocols/secure.rs
Normal file
12
rnex-core/src/rmc/protocols/secure.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
use macros::{method_id, rmc_proto};
|
||||
use rnex_core::prudp::station_url::StationUrl;
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
use rnex_core::rmc::structures::qresult::QResult;
|
||||
|
||||
#[rmc_proto(11)]
|
||||
pub trait Secure {
|
||||
#[method_id(1)]
|
||||
async fn register(&self, station_urls: Vec<StationUrl>) -> Result<(QResult, u32, StationUrl), ErrorCode>;
|
||||
#[method_id(7)]
|
||||
async fn replace_url(&self, target: StationUrl, dest: StationUrl) -> Result<(), ErrorCode>;
|
||||
}
|
||||
497
rnex-core/src/rmc/response.rs
Normal file
497
rnex-core/src/rmc/response.rs
Normal file
|
|
@ -0,0 +1,497 @@
|
|||
// i seriously dont know why the compiler is complaining about unused parentheses in the repr
|
||||
// attributes but this gets it to not complain anymore
|
||||
#![allow(unused_parens)]
|
||||
|
||||
use std::io;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::mem::transmute;
|
||||
use bytemuck::bytes_of;
|
||||
use log::error;
|
||||
use v_byte_helpers::EnumTryInto;
|
||||
use v_byte_helpers::{ReadExtensions, IS_BIG_ENDIAN};
|
||||
use crate::rmc::response::ErrorCode::Core_Exception;
|
||||
use crate::rmc::structures::qresult::ERROR_MASK;
|
||||
use crate::util::SendingBufferConnection;
|
||||
|
||||
pub enum RMCResponseResult {
|
||||
Success {
|
||||
call_id: u32,
|
||||
method_id: u32,
|
||||
data: Vec<u8>,
|
||||
},
|
||||
Error {
|
||||
error_code: ErrorCode,
|
||||
call_id: u32,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct RMCResponse {
|
||||
pub protocol_id: u8,
|
||||
pub response_result: RMCResponseResult,
|
||||
}
|
||||
|
||||
impl RMCResponse {
|
||||
pub fn new(stream: &mut (impl Seek + Read)) -> io::Result<Self>{
|
||||
// ignore the size for now this will only be used for checking
|
||||
let size: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let protocol_id: u8 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
/*let protocol_id: u16 = match protocol_id{
|
||||
0x7F => {
|
||||
stream.read_struct(IS_BIG_ENDIAN)?
|
||||
},
|
||||
_ => protocol_id as u16
|
||||
};*/
|
||||
|
||||
let is_success: u8 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let response_result = if is_success == 0x01{
|
||||
let call_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
let method_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
let method_id = method_id & (!0x8000);
|
||||
|
||||
let mut data: Vec<u8> = vec![0u8; (size - 2 - 4 - 4) as _];
|
||||
|
||||
stream.read(&mut data)?;
|
||||
|
||||
|
||||
RMCResponseResult::Success {
|
||||
call_id,
|
||||
method_id,
|
||||
data
|
||||
}
|
||||
} else {
|
||||
let error_code: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
let error_code = error_code & (!0x80000000);
|
||||
let call_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
RMCResponseResult::Error {
|
||||
error_code: {
|
||||
match ErrorCode::try_from(error_code){
|
||||
Ok(v) => v,
|
||||
Err(()) => {
|
||||
error!("invalid error code {:#010x}", error_code);
|
||||
Core_Exception
|
||||
}
|
||||
}
|
||||
},
|
||||
call_id,
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self{
|
||||
protocol_id,
|
||||
response_result
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_call_id(&self) -> u32{
|
||||
match &self.response_result{
|
||||
RMCResponseResult::Success { call_id, ..} => *call_id,
|
||||
RMCResponseResult::Error { call_id, .. } => *call_id
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_data(self) -> Vec<u8> {
|
||||
generate_response(self.protocol_id, self.response_result).expect("failed to generate response")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Result<Vec<u8>> {
|
||||
let size = 1 + 1 + match &response {
|
||||
RMCResponseResult::Success {
|
||||
data,
|
||||
..
|
||||
} => 4 + 4 + data.len(),
|
||||
RMCResponseResult::Error { .. } => 4 + 4,
|
||||
};
|
||||
|
||||
let mut data_out = Vec::with_capacity(size + 4);
|
||||
|
||||
let u32_size: u32 = size as _;
|
||||
|
||||
data_out.write_all(bytes_of(&u32_size))?;
|
||||
data_out.push(protocol_id);
|
||||
|
||||
match response {
|
||||
RMCResponseResult::Success {
|
||||
call_id,
|
||||
method_id,
|
||||
data
|
||||
} => {
|
||||
data_out.push(1);
|
||||
data_out.write_all(bytes_of(&call_id))?;
|
||||
let ored_method_id = method_id | 0x8000;
|
||||
data_out.write_all(bytes_of(&ored_method_id))?;
|
||||
data_out.write_all(&data)?;
|
||||
}
|
||||
RMCResponseResult::Error {
|
||||
call_id,
|
||||
error_code
|
||||
} => {
|
||||
data_out.push(0);
|
||||
let error_code_val: u32 = error_code.into();
|
||||
let error_code_val = error_code_val | ERROR_MASK;
|
||||
data_out.write_all(bytes_of(&error_code_val))?;
|
||||
data_out.write_all(bytes_of(&call_id))?;
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(data_out.len(), size + 4);
|
||||
|
||||
Ok(data_out)
|
||||
}
|
||||
|
||||
pub async fn send_result(
|
||||
connection: &SendingBufferConnection,
|
||||
result: Result<Vec<u8>, ErrorCode>,
|
||||
protocol_id: u8,
|
||||
method_id: u32,
|
||||
call_id: u32,
|
||||
) {
|
||||
|
||||
let response_result = match result {
|
||||
Ok(v) => RMCResponseResult::Success {
|
||||
call_id,
|
||||
method_id,
|
||||
data: v
|
||||
},
|
||||
Err(e) =>
|
||||
RMCResponseResult::Error {
|
||||
call_id,
|
||||
error_code: e.into()
|
||||
}
|
||||
};
|
||||
|
||||
let response = RMCResponse{
|
||||
response_result,
|
||||
protocol_id
|
||||
};
|
||||
|
||||
send_response(connection, response).await
|
||||
}
|
||||
|
||||
pub async fn send_response(connection: &SendingBufferConnection, rmcresponse: RMCResponse) {
|
||||
connection.send(rmcresponse.to_data()).await;
|
||||
}
|
||||
|
||||
|
||||
//taken from kinnays error list directly
|
||||
#[allow(nonstandard_style)]
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, EnumTryInto, Clone, Copy)]
|
||||
pub enum ErrorCode {
|
||||
Core_Unknown = 0x00010001,
|
||||
Core_NotImplemented = 0x00010002,
|
||||
Core_InvalidPointer = 0x00010003,
|
||||
Core_OperationAborted = 0x00010004,
|
||||
Core_Exception = 0x00010005,
|
||||
Core_AccessDenied = 0x00010006,
|
||||
Core_InvalidHandle = 0x00010007,
|
||||
Core_InvalidIndex = 0x00010008,
|
||||
Core_OutOfMemory = 0x00010009,
|
||||
Core_InvalidArgument = 0x0001000A,
|
||||
Core_Timeout = 0x0001000B,
|
||||
Core_InitializationFailure = 0x0001000C,
|
||||
Core_CallInitiationFailure = 0x0001000D,
|
||||
Core_RegistrationError = 0x0001000E,
|
||||
Core_BufferOverflow = 0x0001000F,
|
||||
Core_InvalidLockState = 0x00010010,
|
||||
Core_InvalidSequence = 0x00010011,
|
||||
Core_SystemError = 0x00010012,
|
||||
Core_Cancelled = 0x00010013,
|
||||
DDL_InvalidSignature = 0x00020001,
|
||||
DDL_IncorrectVersion = 0x00020002,
|
||||
RendezVous_ConnectionFailure = 0x00030001,
|
||||
RendezVous_NotAuthenticated = 0x00030002,
|
||||
RendezVous_InvalidUsername = 0x00030064,
|
||||
RendezVous_InvalidPassword = 0x00030065,
|
||||
RendezVous_UsernameAlreadyExists = 0x00030066,
|
||||
RendezVous_AccountDisabled = 0x00030067,
|
||||
RendezVous_AccountExpired = 0x00030068,
|
||||
RendezVous_ConcurrentLoginDenied = 0x00030069,
|
||||
RendezVous_EncryptionFailure = 0x0003006A,
|
||||
RendezVous_InvalidPID = 0x0003006B,
|
||||
RendezVous_MaxConnectionsReached = 0x0003006C,
|
||||
RendezVous_InvalidGID = 0x0003006D,
|
||||
RendezVous_InvalidControlScriptID = 0x0003006E,
|
||||
RendezVous_InvalidOperationInLiveEnvironment = 0x0003006F,
|
||||
RendezVous_DuplicateEntry = 0x00030070,
|
||||
RendezVous_ControlScriptFailure = 0x00030071,
|
||||
RendezVous_ClassNotFound = 0x00030072,
|
||||
RendezVous_SessionVoid = 0x00030073,
|
||||
RendezVous_DDLMismatch = 0x00030075,
|
||||
RendezVous_InvalidConfiguration = 0x00030076,
|
||||
RendezVous_SessionFull = 0x000300C8,
|
||||
RendezVous_InvalidGatheringPassword = 0x000300C9,
|
||||
RendezVous_WithoutParticipationPeriod = 0x000300CA,
|
||||
RendezVous_PersistentGatheringCreationMax = 0x000300CB,
|
||||
RendezVous_PersistentGatheringParticipationMax = 0x000300CC,
|
||||
RendezVous_DeniedByParticipants = 0x000300CD,
|
||||
RendezVous_ParticipantInBlackList = 0x000300CE,
|
||||
RendezVous_GameServerMaintenance = 0x000300CF,
|
||||
RendezVous_OperationPostpone = 0x000300D0,
|
||||
RendezVous_OutOfRatingRange = 0x000300D1,
|
||||
RendezVous_ConnectionDisconnected = 0x000300D2,
|
||||
RendezVous_InvalidOperation = 0x000300D3,
|
||||
RendezVous_NotParticipatedGathering = 0x000300D4,
|
||||
RendezVous_MatchmakeSessionUserPasswordUnmatch = 0x000300D5,
|
||||
RendezVous_MatchmakeSessionSystemPasswordUnmatch = 0x000300D6,
|
||||
RendezVous_UserIsOffline = 0x000300D7,
|
||||
RendezVous_AlreadyParticipatedGathering = 0x000300D8,
|
||||
RendezVous_PermissionDenied = 0x000300D9,
|
||||
RendezVous_NotFriend = 0x000300DA,
|
||||
RendezVous_SessionClosed = 0x000300DB,
|
||||
RendezVous_DatabaseTemporarilyUnavailable = 0x000300DC,
|
||||
RendezVous_InvalidUniqueId = 0x000300DD,
|
||||
RendezVous_MatchmakingWithdrawn = 0x000300DE,
|
||||
RendezVous_LimitExceeded = 0x000300DF,
|
||||
RendezVous_AccountTemporarilyDisabled = 0x000300E0,
|
||||
RendezVous_PartiallyServiceClosed = 0x000300E1,
|
||||
RendezVous_ConnectionDisconnectedForConcurrentLogin = 0x000300E2,
|
||||
PythonCore_Exception = 0x00040001,
|
||||
PythonCore_TypeError = 0x00040002,
|
||||
PythonCore_IndexError = 0x00040003,
|
||||
PythonCore_InvalidReference = 0x00040004,
|
||||
PythonCore_CallFailure = 0x00040005,
|
||||
PythonCore_MemoryError = 0x00040006,
|
||||
PythonCore_KeyError = 0x00040007,
|
||||
PythonCore_OperationError = 0x00040008,
|
||||
PythonCore_ConversionError = 0x00040009,
|
||||
PythonCore_ValidationError = 0x0004000A,
|
||||
Transport_Unknown = 0x00050001,
|
||||
Transport_ConnectionFailure = 0x00050002,
|
||||
Transport_InvalidUrl = 0x00050003,
|
||||
Transport_InvalidKey = 0x00050004,
|
||||
Transport_InvalidURLType = 0x00050005,
|
||||
Transport_DuplicateEndpoint = 0x00050006,
|
||||
Transport_IOError = 0x00050007,
|
||||
Transport_Timeout = 0x00050008,
|
||||
Transport_ConnectionReset = 0x00050009,
|
||||
Transport_IncorrectRemoteAuthentication = 0x0005000A,
|
||||
Transport_ServerRequestError = 0x0005000B,
|
||||
Transport_DecompressionFailure = 0x0005000C,
|
||||
Transport_ReliableSendBufferFullFatal = 0x0005000D,
|
||||
Transport_UPnPCannotInit = 0x0005000E,
|
||||
Transport_UPnPCannotAddMapping = 0x0005000F,
|
||||
Transport_NatPMPCannotInit = 0x00050010,
|
||||
Transport_NatPMPCannotAddMapping = 0x00050011,
|
||||
Transport_UnsupportedNAT = 0x00050013,
|
||||
Transport_DnsError = 0x00050014,
|
||||
Transport_ProxyError = 0x00050015,
|
||||
Transport_DataRemaining = 0x00050016,
|
||||
Transport_NoBuffer = 0x00050017,
|
||||
Transport_NotFound = 0x00050018,
|
||||
Transport_TemporaryServerError = 0x00050019,
|
||||
Transport_PermanentServerError = 0x0005001A,
|
||||
Transport_ServiceUnavailable = 0x0005001B,
|
||||
Transport_ReliableSendBufferFull = 0x0005001C,
|
||||
Transport_InvalidStation = 0x0005001D,
|
||||
Transport_InvalidSubStreamID = 0x0005001E,
|
||||
Transport_PacketBufferFull = 0x0005001F,
|
||||
Transport_NatTraversalError = 0x00050020,
|
||||
Transport_NatCheckError = 0x00050021,
|
||||
DOCore_StationNotReached = 0x00060001,
|
||||
DOCore_TargetStationDisconnect = 0x00060002,
|
||||
DOCore_LocalStationLeaving = 0x00060003,
|
||||
DOCore_ObjectNotFound = 0x00060004,
|
||||
DOCore_InvalidRole = 0x00060005,
|
||||
DOCore_CallTimeout = 0x00060006,
|
||||
DOCore_RMCDispatchFailed = 0x00060007,
|
||||
DOCore_MigrationInProgress = 0x00060008,
|
||||
DOCore_NoAuthority = 0x00060009,
|
||||
DOCore_NoTargetStationSpecified = 0x0006000A,
|
||||
DOCore_JoinFailed = 0x0006000B,
|
||||
DOCore_JoinDenied = 0x0006000C,
|
||||
DOCore_ConnectivityTestFailed = 0x0006000D,
|
||||
DOCore_Unknown = 0x0006000E,
|
||||
DOCore_UnfreedReferences = 0x0006000F,
|
||||
DOCore_JobTerminationFailed = 0x00060010,
|
||||
DOCore_InvalidState = 0x00060011,
|
||||
DOCore_FaultRecoveryFatal = 0x00060012,
|
||||
DOCore_FaultRecoveryJobProcessFailed = 0x00060013,
|
||||
DOCore_StationInconsitency = 0x00060014,
|
||||
DOCore_AbnormalMasterState = 0x00060015,
|
||||
DOCore_VersionMismatch = 0x00060016,
|
||||
FPD_NotInitialized = 0x00650000,
|
||||
FPD_AlreadyInitialized = 0x00650001,
|
||||
FPD_NotConnected = 0x00650002,
|
||||
FPD_Connected = 0x00650003,
|
||||
FPD_InitializationFailure = 0x00650004,
|
||||
FPD_OutOfMemory = 0x00650005,
|
||||
FPD_RmcFailed = 0x00650006,
|
||||
FPD_InvalidArgument = 0x00650007,
|
||||
FPD_InvalidLocalAccountID = 0x00650008,
|
||||
FPD_InvalidPrincipalID = 0x00650009,
|
||||
FPD_InvalidLocalFriendCode = 0x0065000A,
|
||||
FPD_LocalAccountNotExists = 0x0065000B,
|
||||
FPD_LocalAccountNotLoaded = 0x0065000C,
|
||||
FPD_LocalAccountAlreadyLoaded = 0x0065000D,
|
||||
FPD_FriendAlreadyExists = 0x0065000E,
|
||||
FPD_FriendNotExists = 0x0065000F,
|
||||
FPD_FriendNumMax = 0x00650010,
|
||||
FPD_NotFriend = 0x00650011,
|
||||
FPD_FileIO = 0x00650012,
|
||||
FPD_P2PInternetProhibited = 0x00650013,
|
||||
FPD_Unknown = 0x00650014,
|
||||
FPD_InvalidState = 0x00650015,
|
||||
FPD_AddFriendProhibited = 0x00650017,
|
||||
FPD_InvalidAccount = 0x00650019,
|
||||
FPD_BlacklistedByMe = 0x0065001A,
|
||||
FPD_FriendAlreadyAdded = 0x0065001C,
|
||||
FPD_MyFriendListLimitExceed = 0x0065001D,
|
||||
FPD_RequestLimitExceed = 0x0065001E,
|
||||
FPD_InvalidMessageID = 0x0065001F,
|
||||
FPD_MessageIsNotMine = 0x00650020,
|
||||
FPD_MessageIsNotForMe = 0x00650021,
|
||||
FPD_FriendRequestBlocked = 0x00650022,
|
||||
FPD_NotInMyFriendList = 0x00650023,
|
||||
FPD_FriendListedByMe = 0x00650024,
|
||||
FPD_NotInMyBlacklist = 0x00650025,
|
||||
FPD_IncompatibleAccount = 0x00650026,
|
||||
FPD_BlockSettingChangeNotAllowed = 0x00650027,
|
||||
FPD_SizeLimitExceeded = 0x00650028,
|
||||
FPD_OperationNotAllowed = 0x00650029,
|
||||
FPD_NotNetworkAccount = 0x0065002A,
|
||||
FPD_NotificationNotFound = 0x0065002B,
|
||||
FPD_PreferenceNotInitialized = 0x0065002C,
|
||||
FPD_FriendRequestNotAllowed = 0x0065002D,
|
||||
Ranking_NotInitialized = 0x00670001,
|
||||
Ranking_InvalidArgument = 0x00670002,
|
||||
Ranking_RegistrationError = 0x00670003,
|
||||
Ranking_NotFound = 0x00670005,
|
||||
Ranking_InvalidScore = 0x00670006,
|
||||
Ranking_InvalidDataSize = 0x00670007,
|
||||
Ranking_PermissionDenied = 0x00670009,
|
||||
Ranking_Unknown = 0x0067000A,
|
||||
Ranking_NotImplemented = 0x0067000B,
|
||||
Authentication_NASAuthenticateError = 0x00680001,
|
||||
Authentication_TokenParseError = 0x00680002,
|
||||
Authentication_HttpConnectionError = 0x00680003,
|
||||
Authentication_HttpDNSError = 0x00680004,
|
||||
Authentication_HttpGetProxySetting = 0x00680005,
|
||||
Authentication_TokenExpired = 0x00680006,
|
||||
Authentication_ValidationFailed = 0x00680007,
|
||||
Authentication_InvalidParam = 0x00680008,
|
||||
Authentication_PrincipalIdUnmatched = 0x00680009,
|
||||
Authentication_MoveCountUnmatch = 0x0068000A,
|
||||
Authentication_UnderMaintenance = 0x0068000B,
|
||||
Authentication_UnsupportedVersion = 0x0068000C,
|
||||
Authentication_ServerVersionIsOld = 0x0068000D,
|
||||
Authentication_Unknown = 0x0068000E,
|
||||
Authentication_ClientVersionIsOld = 0x0068000F,
|
||||
Authentication_AccountLibraryError = 0x00680010,
|
||||
Authentication_ServiceNoLongerAvailable = 0x00680011,
|
||||
Authentication_UnknownApplication = 0x00680012,
|
||||
Authentication_ApplicationVersionIsOld = 0x00680013,
|
||||
Authentication_OutOfService = 0x00680014,
|
||||
Authentication_NetworkServiceLicenseRequired = 0x00680015,
|
||||
Authentication_NetworkServiceLicenseSystemError = 0x00680016,
|
||||
Authentication_NetworkServiceLicenseError3 = 0x00680017,
|
||||
Authentication_NetworkServiceLicenseError4 = 0x00680018,
|
||||
DataStore_Unknown = 0x00690001,
|
||||
DataStore_InvalidArgument = 0x00690002,
|
||||
DataStore_PermissionDenied = 0x00690003,
|
||||
DataStore_NotFound = 0x00690004,
|
||||
DataStore_AlreadyLocked = 0x00690005,
|
||||
DataStore_UnderReviewing = 0x00690006,
|
||||
DataStore_Expired = 0x00690007,
|
||||
DataStore_InvalidCheckToken = 0x00690008,
|
||||
DataStore_SystemFileError = 0x00690009,
|
||||
DataStore_OverCapacity = 0x0069000A,
|
||||
DataStore_OperationNotAllowed = 0x0069000B,
|
||||
DataStore_InvalidPassword = 0x0069000C,
|
||||
DataStore_ValueNotEqual = 0x0069000D,
|
||||
ServiceItem_Unknown = 0x006C0001,
|
||||
ServiceItem_InvalidArgument = 0x006C0002,
|
||||
ServiceItem_EShopUnknownHttpError = 0x006C0003,
|
||||
ServiceItem_EShopResponseParseError = 0x006C0004,
|
||||
ServiceItem_NotOwned = 0x006C0005,
|
||||
ServiceItem_InvalidLimitationType = 0x006C0006,
|
||||
ServiceItem_ConsumptionRightShortage = 0x006C0007,
|
||||
MatchmakeReferee_Unknown = 0x006F0001,
|
||||
MatchmakeReferee_InvalidArgument = 0x006F0002,
|
||||
MatchmakeReferee_AlreadyExists = 0x006F0003,
|
||||
MatchmakeReferee_NotParticipatedGathering = 0x006F0004,
|
||||
MatchmakeReferee_NotParticipatedRound = 0x006F0005,
|
||||
MatchmakeReferee_StatsNotFound = 0x006F0006,
|
||||
MatchmakeReferee_RoundNotFound = 0x006F0007,
|
||||
MatchmakeReferee_RoundArbitrated = 0x006F0008,
|
||||
MatchmakeReferee_RoundNotArbitrated = 0x006F0009,
|
||||
Subscriber_Unknown = 0x00700001,
|
||||
Subscriber_InvalidArgument = 0x00700002,
|
||||
Subscriber_OverLimit = 0x00700003,
|
||||
Subscriber_PermissionDenied = 0x00700004,
|
||||
Ranking2_Unknown = 0x00710001,
|
||||
Ranking2_InvalidArgument = 0x00710002,
|
||||
Ranking2_InvalidScore = 0x00710003,
|
||||
SmartDeviceVoiceChat_Unknown = 0x00720001,
|
||||
SmartDeviceVoiceChat_InvalidArgument = 0x00720002,
|
||||
SmartDeviceVoiceChat_InvalidResponse = 0x00720003,
|
||||
SmartDeviceVoiceChat_InvalidAccessToken = 0x00720004,
|
||||
SmartDeviceVoiceChat_Unauthorized = 0x00720005,
|
||||
SmartDeviceVoiceChat_AccessError = 0x00720006,
|
||||
SmartDeviceVoiceChat_UserNotFound = 0x00720007,
|
||||
SmartDeviceVoiceChat_RoomNotFound = 0x00720008,
|
||||
SmartDeviceVoiceChat_RoomNotActivated = 0x00720009,
|
||||
SmartDeviceVoiceChat_ApplicationNotSupported = 0x0072000A,
|
||||
SmartDeviceVoiceChat_InternalServerError = 0x0072000B,
|
||||
SmartDeviceVoiceChat_ServiceUnavailable = 0x0072000C,
|
||||
SmartDeviceVoiceChat_UnexpectedError = 0x0072000D,
|
||||
SmartDeviceVoiceChat_UnderMaintenance = 0x0072000E,
|
||||
SmartDeviceVoiceChat_ServiceNoLongerAvailable = 0x0072000F,
|
||||
SmartDeviceVoiceChat_AccountTemporarilyDisabled = 0x00720010,
|
||||
SmartDeviceVoiceChat_PermissionDenied = 0x00720011,
|
||||
SmartDeviceVoiceChat_NetworkServiceLicenseRequired = 0x00720012,
|
||||
SmartDeviceVoiceChat_AccountLibraryError = 0x00720013,
|
||||
SmartDeviceVoiceChat_GameModeNotFound = 0x00720014,
|
||||
Screening_Unknown = 0x00730001,
|
||||
Screening_InvalidArgument = 0x00730002,
|
||||
Screening_NotFound = 0x00730003,
|
||||
Custom_Unknown = 0x00740001,
|
||||
Ess_Unknown = 0x00750001,
|
||||
Ess_GameSessionError = 0x00750002,
|
||||
Ess_GameSessionMaintenance = 0x00750003,
|
||||
}
|
||||
|
||||
impl Into<u32> for ErrorCode {
|
||||
fn into(self) -> u32 {
|
||||
unsafe { transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use hmac::digest::consts::U5;
|
||||
use hmac::digest::KeyInit;
|
||||
use rc4::{Rc4, StreamCipher};
|
||||
use crate::rmc::response::ErrorCode;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut data_orig = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 69, 4, 20];
|
||||
let mut data = data_orig;
|
||||
|
||||
let mut rc4: Rc4<U5> =
|
||||
Rc4::new_from_slice("FUCKE".as_bytes().into()).expect("invalid key");
|
||||
|
||||
rc4.apply_keystream(&mut data);
|
||||
|
||||
assert_ne!(data_orig, data);
|
||||
|
||||
let mut rc4: Rc4<U5> =
|
||||
Rc4::new_from_slice("FUCKE".as_bytes().into()).expect("invalid key");
|
||||
|
||||
rc4.apply_keystream(&mut data);
|
||||
|
||||
assert_eq!(data_orig, data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enum_equivilance() {
|
||||
let val: u32 = ErrorCode::Core_Unknown.into();
|
||||
assert_eq!(val, 0x00010001)
|
||||
}
|
||||
}
|
||||
42
rnex-core/src/rmc/structures/any.rs
Normal file
42
rnex-core/src/rmc/structures/any.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
use std::io::{Read, Write};
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use super::{Result, RmcSerialize};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Any{
|
||||
pub name: String,
|
||||
pub data: Vec<u8>
|
||||
}
|
||||
|
||||
impl RmcSerialize for Any{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
self.name.serialize(writer)?;
|
||||
|
||||
let u32_len = self.data.len() as u32;
|
||||
|
||||
u32_len.serialize(writer)?;
|
||||
u32_len.serialize(writer)?;
|
||||
|
||||
self.data.serialize(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
|
||||
let name = String::deserialize(reader)?;
|
||||
|
||||
// also length ?
|
||||
let _len2: u32 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
let length: u32 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let mut data = vec![0; length as usize];
|
||||
|
||||
reader.read_exact(&mut data)?;
|
||||
|
||||
Ok(
|
||||
Any{
|
||||
name,
|
||||
data
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
29
rnex-core/src/rmc/structures/buffer.rs
Normal file
29
rnex-core/src/rmc/structures/buffer.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
use std::io::{Read, Write};
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
|
||||
|
||||
|
||||
impl<'a> RmcSerialize for &'a [u8]{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
let u32_size = self.len() as u32;
|
||||
writer.write(bytemuck::bytes_of(&u32_size))?;
|
||||
writer.write(self)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// DO NOT USE (also maybe split off the serialize and deserialize functions at some point)
|
||||
fn deserialize(_reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
panic!("cannot deserialize to a u8 slice reference (use this ONLY for writing)")
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for Box<[u8]>{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
(&self[..]).serialize(writer)
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Vec::deserialize(reader).map(|v| v.into_boxed_slice())
|
||||
}
|
||||
}
|
||||
13
rnex-core/src/rmc/structures/connection_data.rs
Normal file
13
rnex-core/src/rmc/structures/connection_data.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
use macros::RmcSerialize;
|
||||
use rnex_core::kerberos::KerberosDateTime;
|
||||
|
||||
#[derive(Debug, RmcSerialize)]
|
||||
#[rmc_struct(1)]
|
||||
pub struct ConnectionData{
|
||||
pub station_url: String,
|
||||
pub special_protocols: Vec<u8>,
|
||||
pub special_station_url: String,
|
||||
pub date_time: KerberosDateTime
|
||||
}
|
||||
|
||||
60
rnex-core/src/rmc/structures/list.rs
Normal file
60
rnex-core/src/rmc/structures/list.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
use std::array::from_fn;
|
||||
use std::io::{Read, Write};
|
||||
use std::mem::MaybeUninit;
|
||||
use bytemuck::bytes_of;
|
||||
use serde::Serialize;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
|
||||
|
||||
|
||||
// this is also for implementing `Buffer` this is tecnically not the same as its handled internaly
|
||||
// probably but as it has the same mapping it doesn't matter and simplifies things
|
||||
impl<T: RmcSerialize> RmcSerialize for Vec<T>{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
let u32_len = self.len() as u32;
|
||||
|
||||
writer.write_all(bytes_of(&u32_len))?;
|
||||
for e in self{
|
||||
e.serialize(writer)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let len: u32 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let mut vec = Vec::with_capacity(len as usize);
|
||||
|
||||
for _ in 0..len{
|
||||
vec.push(T::deserialize(reader)?);
|
||||
}
|
||||
|
||||
Ok(vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEN: usize, T: RmcSerialize> RmcSerialize for [T; LEN]{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
for i in 0..LEN{
|
||||
self[i].serialize(writer)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let mut arr = [const { MaybeUninit::<T>::uninit() }; LEN];
|
||||
|
||||
for i in 0..LEN{
|
||||
arr[i] = MaybeUninit::new(T::deserialize(reader)?);
|
||||
}
|
||||
|
||||
// all of the elements are now initialized so it is safe to assume they are initialized
|
||||
|
||||
let arr = arr.map(|v| unsafe{ v.assume_init() });
|
||||
|
||||
Ok(arr)
|
||||
}
|
||||
}
|
||||
126
rnex-core/src/rmc/structures/matchmake.rs
Normal file
126
rnex-core/src/rmc/structures/matchmake.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
use rnex_core::kerberos::KerberosDateTime;
|
||||
use rnex_core::rmc::structures::variant::Variant;
|
||||
use macros::RmcSerialize;
|
||||
|
||||
// rmc structure
|
||||
#[derive(RmcSerialize, Debug, Clone, Default)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct Gathering {
|
||||
pub self_gid: u32,
|
||||
pub owner_pid: u32,
|
||||
pub host_pid: u32,
|
||||
pub minimum_participants: u16,
|
||||
pub maximum_participants: u16,
|
||||
pub participant_policy: u32,
|
||||
pub policy_argument: u32,
|
||||
pub flags: u32,
|
||||
pub state: u32,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
// rmc structure
|
||||
#[derive(RmcSerialize, Debug, Clone, Default)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct MatchmakeParam {
|
||||
pub params: Vec<(String, Variant)>,
|
||||
}
|
||||
|
||||
// rmc structure
|
||||
#[derive(RmcSerialize, Debug, Clone, Default)]
|
||||
#[rmc_struct(3)]
|
||||
pub struct MatchmakeSession {
|
||||
//inherits from
|
||||
#[extends]
|
||||
pub gathering: Gathering,
|
||||
|
||||
pub gamemode: u32,
|
||||
pub attributes: Vec<u32>,
|
||||
pub open_participation: bool,
|
||||
pub matchmake_system_type: u32,
|
||||
pub application_buffer: Vec<u8>,
|
||||
pub participation_count: u32,
|
||||
pub progress_score: u8,
|
||||
pub session_key: Vec<u8>,
|
||||
pub option0: u32,
|
||||
pub matchmake_param: MatchmakeParam,
|
||||
pub datetime: KerberosDateTime,
|
||||
pub user_password: String,
|
||||
pub refer_gid: u32,
|
||||
pub user_password_enabled: bool,
|
||||
pub system_password_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize, Debug, Clone)]
|
||||
#[rmc_struct(3)]
|
||||
pub struct MatchmakeSessionSearchCriteria {
|
||||
pub attribs: Vec<String>,
|
||||
pub game_mode: String,
|
||||
pub minimum_participants: String,
|
||||
pub maximum_participants: String,
|
||||
pub matchmake_system_type: String,
|
||||
pub vacant_only: bool,
|
||||
pub exclude_locked: bool,
|
||||
pub exclude_non_host_pid: bool,
|
||||
pub selection_method: u32,
|
||||
pub vacant_participants: u16,
|
||||
pub matchmake_param: MatchmakeParam,
|
||||
pub exclude_user_password_set: bool,
|
||||
pub exclude_system_password_set: bool,
|
||||
pub refer_gid: u32,
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize, Debug, Clone)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct AutoMatchmakeParam {
|
||||
pub matchmake_session: MatchmakeSession,
|
||||
pub additional_participants: Vec<u32>,
|
||||
pub gid_for_participation_check: u32,
|
||||
pub auto_matchmake_option: u32,
|
||||
pub join_message: String,
|
||||
pub participation_count: u16,
|
||||
pub search_criteria: Vec<MatchmakeSessionSearchCriteria>,
|
||||
pub target_gids: Vec<u32>,
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize, Debug, Clone)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct CreateMatchmakeSessionParam {
|
||||
pub matchmake_session: MatchmakeSession,
|
||||
pub additional_participants: Vec<u32>,
|
||||
pub gid_for_participation_check: u32,
|
||||
pub create_matchmake_session_option: u32,
|
||||
pub join_message: String,
|
||||
pub participation_count: u16,
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize, Debug, Clone)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct MatchmakeBlockListParam {
|
||||
option_flag: u32,
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize, Debug, Clone)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct JoinMatchmakeSessionParam {
|
||||
pub gid: u32,
|
||||
pub additional_participants: Vec<u32>,
|
||||
pub gid_for_participation_check: u32,
|
||||
pub join_matchmake_session_open: u32,
|
||||
pub join_matchmake_session_behavior: u8,
|
||||
pub user_password: String,
|
||||
pub system_password: String,
|
||||
pub join_message: String,
|
||||
pub participation_count: u16,
|
||||
//pub extra_participant: u16,
|
||||
//pub block_list_param: MatchmakeBlockListParam
|
||||
}
|
||||
|
||||
pub mod gathering_flags {
|
||||
pub const PERSISTENT_GATHERING: u32 = 0x1;
|
||||
pub const DISCONNECT_CHANGE_OWNER: u32 = 0x10;
|
||||
pub const PERSISTENT_GATHERING_LEAVE_PARTICIPATION: u32 = 0x40;
|
||||
pub const PERSISTENT_GATHERING_ALLOW_ZERO_USERS: u32 = 0x80;
|
||||
pub const PARTICIPANTS_CHANGE_OWNER: u32 = 0x200;
|
||||
pub const VERBOSE_PARTICIPANTS: u32 = 0x400;
|
||||
pub const VERBOSE_PARTICIPANTS_EX: u32 = 0x800;
|
||||
}
|
||||
60
rnex-core/src/rmc/structures/mod.rs
Normal file
60
rnex-core/src/rmc/structures/mod.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use std::string::FromUtf8Error;
|
||||
use thiserror::Error;
|
||||
|
||||
//ideas for the future: make a proc macro library which allows generation of struct reads
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error{
|
||||
#[error("Io Error: {0}")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("UTF8 conversion Error: {0}")]
|
||||
Utf8(#[from] FromUtf8Error),
|
||||
#[error("unexpected value: {0}")]
|
||||
UnexpectedValue(u64),
|
||||
#[error("version mismatch: {0}")]
|
||||
VersionMismatch(u8),
|
||||
#[error("an error occurred reading the station url")]
|
||||
StationUrlInvalid
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub mod string;
|
||||
pub mod any;
|
||||
pub mod qresult;
|
||||
pub mod buffer;
|
||||
pub mod connection_data;
|
||||
pub mod rmc_struct;
|
||||
pub mod list;
|
||||
pub mod qbuffer;
|
||||
pub mod primitives;
|
||||
pub mod matchmake;
|
||||
pub mod variant;
|
||||
pub mod ranking;
|
||||
mod networking;
|
||||
|
||||
pub trait RmcSerialize{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()>;
|
||||
fn deserialize(reader: &mut dyn Read) -> Result<Self> where Self: Sized;
|
||||
|
||||
fn to_data(&self) -> Vec<u8>{
|
||||
let mut data = Vec::new();
|
||||
|
||||
self.serialize(&mut data).expect("out of memory or something");
|
||||
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for (){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn deserialize(reader: &mut dyn Read) -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
33
rnex-core/src/rmc/structures/networking.rs
Normal file
33
rnex-core/src/rmc/structures/networking.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
use std::io::{Read, Write};
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use rnex_core::prudp::virtual_port::VirtualPort;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
|
||||
impl RmcSerialize for SocketAddrV4{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.ip().to_bits().serialize(writer)?;
|
||||
self.port().serialize(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let ip = u32::deserialize(reader)?;
|
||||
let port = u16::deserialize(reader)?;
|
||||
|
||||
Ok(SocketAddrV4::new(Ipv4Addr::from_bits(ip), port))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl RmcSerialize for VirtualPort{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.0.serialize(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(Self(u8::deserialize(reader)?))
|
||||
}
|
||||
}
|
||||
241
rnex-core/src/rmc/structures/primitives.rs
Normal file
241
rnex-core/src/rmc/structures/primitives.rs
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
use std::io::{Read, Write};
|
||||
use bytemuck::bytes_of;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
|
||||
impl RmcSerialize for u8{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for i8{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for u16{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for i16{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for u32{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for i32{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for u64{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for i64{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for f64{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
Ok(writer.write_all(bytes_of(self))?)
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for bool{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
match self{
|
||||
true => writer.write_all(&[1])?,
|
||||
false => writer.write_all(&[0])?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
Ok(u8::deserialize(reader)? != 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T: RmcSerialize, U: RmcSerialize> RmcSerialize for (T, U){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.0.serialize(writer)?;
|
||||
self.1.serialize(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let first = T::deserialize(reader)?;
|
||||
let second = U::deserialize(reader)?;
|
||||
|
||||
Ok((first, second))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize> RmcSerialize for (T, U, V){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.0.serialize(writer)?;
|
||||
self.1.serialize(writer)?;
|
||||
self.2.serialize(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let first = T::deserialize(reader)?;
|
||||
let second = U::deserialize(reader)?;
|
||||
let third = V::deserialize(reader)?;
|
||||
|
||||
Ok((first, second, third))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize, W: RmcSerialize> RmcSerialize for (T, U, V, W){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.0.serialize(writer)?;
|
||||
self.1.serialize(writer)?;
|
||||
self.2.serialize(writer)?;
|
||||
self.3.serialize(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let first = T::deserialize(reader)?;
|
||||
let second = U::deserialize(reader)?;
|
||||
let third = V::deserialize(reader)?;
|
||||
let fourth = W::deserialize(reader)?;
|
||||
|
||||
Ok((first, second, third, fourth))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize, W: RmcSerialize, X: RmcSerialize> RmcSerialize for (T, U, V, W, X){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.0.serialize(writer)?;
|
||||
self.1.serialize(writer)?;
|
||||
self.2.serialize(writer)?;
|
||||
self.3.serialize(writer)?;
|
||||
self.4.serialize(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let first = T::deserialize(reader)?;
|
||||
let second = U::deserialize(reader)?;
|
||||
let third = V::deserialize(reader)?;
|
||||
let fourth = W::deserialize(reader)?;
|
||||
let fifth = X::deserialize(reader)?;
|
||||
|
||||
Ok((first, second, third, fourth, fifth))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize, W: RmcSerialize, X: RmcSerialize, Y: RmcSerialize> RmcSerialize for (T, U, V, W, X, Y){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.0.serialize(writer)?;
|
||||
self.1.serialize(writer)?;
|
||||
self.2.serialize(writer)?;
|
||||
self.3.serialize(writer)?;
|
||||
self.4.serialize(writer)?;
|
||||
self.5.serialize(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let first = T::deserialize(reader)?;
|
||||
let second = U::deserialize(reader)?;
|
||||
let third = V::deserialize(reader)?;
|
||||
let fourth = W::deserialize(reader)?;
|
||||
let fifth = X::deserialize(reader)?;
|
||||
let sixth = Y::deserialize(reader)?;
|
||||
|
||||
Ok((first, second, third, fourth, fifth, sixth))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize, W: RmcSerialize, X: RmcSerialize, Y: RmcSerialize, Z: RmcSerialize> RmcSerialize for (T, U, V, W, X, Y, Z){
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.0.serialize(writer)?;
|
||||
self.1.serialize(writer)?;
|
||||
self.2.serialize(writer)?;
|
||||
self.3.serialize(writer)?;
|
||||
self.4.serialize(writer)?;
|
||||
self.5.serialize(writer)?;
|
||||
self.6.serialize(writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
let first = T::deserialize(reader)?;
|
||||
let second = U::deserialize(reader)?;
|
||||
let third = V::deserialize(reader)?;
|
||||
let fourth = W::deserialize(reader)?;
|
||||
let fifth = X::deserialize(reader)?;
|
||||
let sixth = Y::deserialize(reader)?;
|
||||
let seventh = Z::deserialize(reader)?;
|
||||
|
||||
Ok((first, second, third, fourth, fifth, sixth, seventh))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RmcSerialize> RmcSerialize for Box<T>{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
self.as_ref().serialize(writer)
|
||||
}
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
T::deserialize(reader).map(Box::new)
|
||||
}
|
||||
}
|
||||
29
rnex-core/src/rmc/structures/qbuffer.rs
Normal file
29
rnex-core/src/rmc/structures/qbuffer.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
use std::io::{Read, Write};
|
||||
use bytemuck::bytes_of;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use crate::rmc::structures::{Result, RmcSerialize};
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct QBuffer(pub Vec<u8>);
|
||||
|
||||
impl RmcSerialize for QBuffer{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
let len_u16 = self.0.len() as u16;
|
||||
|
||||
writer.write(bytes_of(&len_u16))?;
|
||||
writer.write(&self.0)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
|
||||
let size: u16 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let mut vec = vec![0; size as usize];
|
||||
|
||||
reader.read_exact(&mut vec)?;
|
||||
|
||||
Ok(Self(vec))
|
||||
}
|
||||
}
|
||||
37
rnex-core/src/rmc/structures/qresult.rs
Normal file
37
rnex-core/src/rmc/structures/qresult.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use std::io::{Read, Write};
|
||||
use bytemuck::{bytes_of, Pod, Zeroable};
|
||||
use v_byte_helpers::SwapEndian;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use crate::rmc::response::ErrorCode;
|
||||
use crate::rmc::structures::{RmcSerialize, Result};
|
||||
|
||||
pub const ERROR_MASK: u32 = 1 << 31;
|
||||
|
||||
#[derive(Pod, Zeroable, Copy, Clone, SwapEndian, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct QResult(u32);
|
||||
|
||||
impl QResult{
|
||||
pub fn success(error_code: ErrorCode) -> Self{
|
||||
let val: u32 = error_code.into();
|
||||
|
||||
Self(val & (!ERROR_MASK))
|
||||
}
|
||||
|
||||
pub fn error(error_code: ErrorCode) -> Self{
|
||||
let val: u32 = error_code.into();
|
||||
|
||||
Self(val | ERROR_MASK)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for QResult{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
writer.write(bytes_of(self))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
|
||||
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||
}
|
||||
}
|
||||
69
rnex-core/src/rmc/structures/ranking.rs
Normal file
69
rnex-core/src/rmc/structures/ranking.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
use bytemuck::{Pod, Zeroable};
|
||||
use macros::RmcSerialize;
|
||||
use rnex_core::rmc::structures::qbuffer::QBuffer;
|
||||
|
||||
#[derive(RmcSerialize, Debug)]
|
||||
#[rmc_struct(0)]
|
||||
struct UploadCompetitionData{
|
||||
winning_team/*?*/: u32,
|
||||
splatfest_id/*?*/: u32,
|
||||
unk_2/*?*/: u32,
|
||||
unk_3: u32,
|
||||
team_id_1: u8,
|
||||
team_id_2: u8,
|
||||
unk_5: u32,
|
||||
player_data/*?*/: QBuffer,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
struct UserData{
|
||||
name: [u16; 0x10],
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test{
|
||||
use std::io::Cursor;
|
||||
use bytemuck::from_bytes;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use crate::rmc::structures::ranking::{UploadCompetitionData, UserData};
|
||||
use rnex_core::rmc::structures::RmcSerialize;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let data: [u8; 0xBD] = [
|
||||
0x00, 0xB8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00,
|
||||
0x00, 0x1F, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x49, 0x00,
|
||||
0x7A, 0x00, 0x7A, 0x00, 0x79, 0x00, 0x53, 0x00, 0x50, 0x00, 0x46, 0x00, 0x4E, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0xF2, 0x00, 0x00, 0x00,
|
||||
0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1F, 0x5E, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x90, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0A, 0x00, 0x00, 0x14, 0x87, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00,
|
||||
];
|
||||
|
||||
let mut cursor = Cursor::new(data);
|
||||
|
||||
let data = UploadCompetitionData::deserialize(&mut cursor).expect("unable to deserialize data");
|
||||
|
||||
let user_data: &UserData = from_bytes(&data.player_data.0[..size_of::<UserData>()]);
|
||||
|
||||
let pos = user_data.name.iter()
|
||||
.position(|v| *v == 0x0000)
|
||||
.unwrap_or(0x10);
|
||||
|
||||
let mut name = user_data.name[0..pos].to_vec();
|
||||
|
||||
name.iter_mut().for_each(|v| *v = v.swap_bytes());
|
||||
|
||||
let name = String::from_utf16(&name).expect("unable to get name");
|
||||
|
||||
println!("{:?}", name);
|
||||
|
||||
assert!(u8::deserialize(&mut cursor).is_err())
|
||||
}
|
||||
}
|
||||
44
rnex-core/src/rmc/structures/rmc_struct.rs
Normal file
44
rnex-core/src/rmc/structures/rmc_struct.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use std::io::{Cursor, Read, Write};
|
||||
use bytemuck::bytes_of;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use crate::rmc::structures::Error::VersionMismatch;
|
||||
use crate::rmc::structures::Result;
|
||||
|
||||
#[repr(C, packed)]
|
||||
struct StructureHeader{
|
||||
version: u8,
|
||||
length: u32
|
||||
}
|
||||
|
||||
pub fn write_struct(writer: &mut dyn Write, version: u8, pred: impl FnOnce(&mut Vec<u8>) -> Result<()> ) -> Result<()> {
|
||||
writer.write_all(&[version])?;
|
||||
|
||||
let mut scratch_space: Vec<u8> = Vec::new();
|
||||
|
||||
(pred)(&mut scratch_space)?;
|
||||
|
||||
let u32_size = scratch_space.len() as u32;
|
||||
|
||||
writer.write_all(bytes_of(&u32_size))?;
|
||||
writer.write_all(&scratch_space)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_struct<T: Sized>(mut reader: &mut dyn Read, version: u8, pred: impl FnOnce(&mut Cursor<Vec<u8>>) -> Result<T>) -> Result<T> {
|
||||
let ver: u8 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
if ver != version{
|
||||
return Err(VersionMismatch(ver));
|
||||
}
|
||||
|
||||
let size: u32 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
let mut vec = vec![0u8; size as usize];
|
||||
|
||||
reader.read_exact(&mut vec)?;
|
||||
|
||||
let mut cursor = Cursor::new(vec);
|
||||
|
||||
Ok(pred(&mut cursor)?)
|
||||
}
|
||||
39
rnex-core/src/rmc/structures/string.rs
Normal file
39
rnex-core/src/rmc/structures/string.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
use std::io::{Read, Write};
|
||||
use bytemuck::bytes_of;
|
||||
use log::error;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use super::{Result, RmcSerialize};
|
||||
|
||||
impl RmcSerialize for String{
|
||||
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
|
||||
let len: u16 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
let mut data = vec![0; len as usize - 1];
|
||||
reader.read_exact(&mut data)?;
|
||||
|
||||
let null: u8 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
if null != 0{
|
||||
error!("unable to find null terminator... continuing anyways");
|
||||
}
|
||||
|
||||
Ok(String::from_utf8(data)?)
|
||||
}
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
(&self[..]).serialize(writer)
|
||||
}
|
||||
}
|
||||
|
||||
impl RmcSerialize for &str{
|
||||
fn deserialize(_reader: &mut dyn Read) -> Result<Self> {
|
||||
panic!("cannot serialize to &str")
|
||||
}
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
let u16_len: u16 = (self.len() + 1) as u16;
|
||||
writer.write(bytes_of(&u16_len))?;
|
||||
|
||||
writer.write(self.as_bytes())?;
|
||||
writer.write(&[0])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
65
rnex-core/src/rmc/structures/variant.rs
Normal file
65
rnex-core/src/rmc/structures/variant.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
use std::io::{Read, Write};
|
||||
use crate::kerberos::KerberosDateTime;
|
||||
use crate::rmc::structures;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub enum Variant{
|
||||
#[default]
|
||||
None,
|
||||
SInt64(i64),
|
||||
Double(f64),
|
||||
Bool(bool),
|
||||
String(String),
|
||||
DateTime(KerberosDateTime),
|
||||
UInt64(u64),
|
||||
}
|
||||
|
||||
impl RmcSerialize for Variant{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
match self{
|
||||
Variant::None => {
|
||||
writer.write_all(&[0])?;
|
||||
}
|
||||
Variant::SInt64(v) => {
|
||||
writer.write_all(&[1])?;
|
||||
v.serialize(writer)?;
|
||||
}
|
||||
Variant::Double(v) => {
|
||||
writer.write_all(&[2])?;
|
||||
v.serialize(writer)?;
|
||||
}
|
||||
Variant::Bool(v) => {
|
||||
writer.write_all(&[3])?;
|
||||
v.serialize(writer)?;
|
||||
}
|
||||
Variant::String(v) => {
|
||||
writer.write_all(&[4])?;
|
||||
v.serialize(writer)?;
|
||||
}
|
||||
Variant::DateTime(v) => {
|
||||
writer.write_all(&[5])?;
|
||||
v.serialize(writer)?;
|
||||
}
|
||||
Variant::UInt64(v) => {
|
||||
writer.write_all(&[6])?;
|
||||
v.serialize(writer)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||
match u8::deserialize(reader)?{
|
||||
0 => Ok(Variant::None),
|
||||
1 => Ok(Variant::SInt64(i64::deserialize(reader)?)),
|
||||
2 => Ok(Variant::Double(f64::deserialize(reader)?)),
|
||||
3 => Ok(Variant::Bool(bool::deserialize(reader)?)),
|
||||
4 => Ok(Variant::String(String::deserialize(reader)?)),
|
||||
5 => Ok(Variant::DateTime(KerberosDateTime::deserialize(reader)?)),
|
||||
6 => Ok(Variant::UInt64(u64::deserialize(reader)?)),
|
||||
v => Err(structures::Error::UnexpectedValue(v as u64))
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue