Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ flate2 = {version = "1.0.14", features = ["zlib"], default-features=false}
variant_partial_eq = { git = "https://github.com/stadust/variant-partial-eq" }
thiserror = "1.0.51"
dash-rs-derive = { path = "dash-rs-derive" }
quick-xml = "0.28.2"

[dev-dependencies]
# benchmark
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ pub mod request;
pub mod response;
pub(crate) mod serde;
pub mod util;
pub mod savefile;

pub use crate::serde::{Dash, DeError, GJFormat, IndexedDeserializer, IndexedSerializer, ProcessError, SerError, Thunk, ThunkProcessor};
38 changes: 38 additions & 0 deletions src/savefile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//! Module containing utilities for interacting with Geometry Dash's local save files

use std::{path::Path, io::Read};

use base64::{DecodeError, engine::general_purpose::URL_SAFE, Engine};
use flate2::read::GzDecoder;

use crate::util;

pub enum SavefileError {
Io(std::io::Error),
Base64(DecodeError)
}

/// Reads the given path to a CCGameManager.dat file and decodes it.
pub fn load_cc_game_manager(p: impl AsRef<Path>) -> Result<String, SavefileError> {
let mut cc_game_manager_bytes = std::fs::read_to_string(p)
.map_err(SavefileError::Io)?
.into_bytes();

util::cyclic_xor(&mut cc_game_manager_bytes, &[0xB]);

// Spurious nul-terminators at end of string, kinda scary
while cc_game_manager_bytes.last() == Some(&0) {
cc_game_manager_bytes.pop();
}

let decoded = URL_SAFE.decode(&cc_game_manager_bytes)
.map_err(SavefileError::Base64)?;
let mut decoder = GzDecoder::new(&decoded[..]);

let mut result = String::new();

decoder.read_to_string(&mut result)
.map_err(SavefileError::Io)?;

Ok(result)
}
8 changes: 8 additions & 0 deletions src/serde/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,13 @@
//!
//! All of these deserializers have the goal to use zero-allocations for maximum efficiency

use crate::serde::IndexedDeserializer;
use serde::{
de::{MapAccess, Visitor},
Deserializer,
};
use std::{collections::HashMap, fmt::Formatter};

pub mod error;
pub mod indexed;
pub mod plist;
317 changes: 317 additions & 0 deletions src/serde/de/plist.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
use std::fmt::{Debug, Display, Formatter, write};
use std::ops::Deref;
use quick_xml::events::Event;
use quick_xml::Reader;
use serde::{de::Visitor, Deserializer};
use std::borrow::Cow;

#[derive(Debug)]
pub enum Error<'de> {
Unexpected {
got: Event<'de>,
expected: String
},
Custom(String),
Xml(quick_xml::Error)
}

impl<'de> std::error::Error for Error<'de> {}

impl<'de> Display for Error<'de> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Error::Unexpected { got, expected } => write!(f, "expected {}, got {:?}", expected, got),
Error::Custom(msg) => write!(f, "{}", msg),
Error::Xml(inner) => write!(f, "{}", inner)
}
}
}

impl<'de> serde::de::Error for Error<'de> {
fn custom<T>(msg: T) -> Self where T: Display {
Error::Custom(msg.to_string())
}
}

impl From<quick_xml::Error> for Error<'_> {
fn from(value: quick_xml::Error) -> Self {
Error::Xml(value)
}
}

pub struct PlistDeserializer<'de> {
reader: Reader<&'de [u8]>,
}

impl<'de> PlistDeserializer<'de> {
pub fn new(input: &'de str) -> Result<Self, Error<'de>> {
let mut deserializer = PlistDeserializer {
reader: Reader::from_str(input)
};

match deserializer.reader.read_event()? {
Event::Decl(_) => (),
event => return Err(Error::Unexpected {got: event, expected: String::from("<?xml ...?>")})
}

deserializer.expect_tag_start("plist")?;
deserializer.expect_tag_start("dict")?;

Ok(deserializer)
}

fn expect_tag_start(&mut self, tag: &str) -> Result<(), Error<'de>> {
match self.reader.read_event()? {
Event::Start(bytes) if bytes.name().0 == tag.as_bytes() => Ok(()),
event => Err(Error::Unexpected { got: event, expected: format!("<{} ...>", tag) })
}
}

fn expect_text(&mut self) -> Result<Cow<'de, [u8]>, Error<'de>> {
match self.reader.read_event()? {
Event::Text(text) => Ok(text.into_inner()),
event => Err(Error::Unexpected {got: event, expected: String::from("text content")})
}
}

fn expect_tag_end(&mut self, tag: &str) -> Result<(), Error<'de>> {
match self.reader.read_event()? {
Event::End(bytes) if bytes.name().0 == tag.as_bytes() => Ok(()),
event => Err(Error::Unexpected { got: event, expected: format!("</{}>", tag) })
}
}

fn expect_simple_tag(&mut self, tag: &str) -> Result<Cow<'de, [u8]>, Error<'de>> {
self.expect_tag_start(tag)?;
let content = self.expect_text()?;
self.expect_tag_end(tag)?;
Ok(content)
}

fn expect_key(&mut self) -> Result<Cow<'de, [u8]>, Error<'de>> {
self.expect_simple_tag("k")
}
}

impl<'a, 'de> Deserializer<'de> for &'a mut PlistDeserializer<'de> {
type Error = Error<'de>;

fn deserialize_any<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_bool<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_i8<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_i16<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_i32<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_i64<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_u8<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_u16<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_u32<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_u64<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_f32<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_f64<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_char<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_str<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_string<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_bytes<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_byte_buf<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_option<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_unit<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_unit_struct<V>(self, name: &'static str, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_newtype_struct<V>(self, name: &'static str, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_seq<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_tuple_struct<V>(self, name: &'static str, len: usize, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_map<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_struct<V>(
self, name: &'static str, fields: &'static [&'static str], visitor: V,
) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_enum<V>(
self, name: &'static str, variants: &'static [&'static str], visitor: V,
) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_identifier<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}

fn deserialize_ignored_any<V>(self, visitor: V) -> Result<<V as Visitor<'de>>::Value, Self::Error>
where
V: Visitor<'de>,
{
todo!()
}
}

#[cfg(test)]
mod tests {
use crate::serde::de::plist::PlistDeserializer;

const INPUT: &str = r#"<?xml version="1.0"?><plist><dict><k>1</k><i>34</i></dict></plist>"#;

#[test]
fn test_deserialize() {
let mut deserializer = PlistDeserializer::new(INPUT).unwrap();
}
}