This commit is contained in:
Maple 2025-09-21 15:59:27 +02:00
commit aab4414904
71 changed files with 293 additions and 4316 deletions

View 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
View file

@ -0,0 +1,7 @@
pub mod message;
pub mod structures;
pub mod response;
pub mod protocols;

View 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
}

View 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>;
}

View 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>;
}

View 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>;
}

View 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{}
}

View 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);
}

View 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);
}

View file

@ -0,0 +1,5 @@
use macros::rmc_proto;
#[rmc_proto(112)]
pub trait Ranking{
}

View 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>;
}

View 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)
}
}

View 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
}
)
}
}

View 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())
}
}

View 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
}

View 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)
}
}

View 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;
}

View 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(())
}
}

View 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)?))
}
}

View 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)
}
}

View 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))
}
}

View 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)?)
}
}

View 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())
}
}

View 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)?)
}

View 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(())
}
}

View 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))
}
}
}