Add translation functionality

This commit is contained in:
NGnius (Graham) 2023-01-10 20:51:13 -05:00
parent 9755eb24b7
commit 4745f0ae7e
11 changed files with 428 additions and 108 deletions

227
Cargo.lock generated
View file

@ -17,7 +17,7 @@ version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"cipher", "cipher",
"cpufeatures", "cpufeatures",
"opaque-debug", "opaque-debug",
@ -87,6 +87,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.9.0" version = "0.9.0"
@ -133,12 +139,6 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -160,7 +160,7 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"wasm-bindgen", "wasm-bindgen",
] ]
@ -192,6 +192,18 @@ dependencies = [
"cipher", "cipher",
] ]
[[package]]
name = "current_locale"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0be8ddcccda8be68d8e31a421ceea7c79857404daa052434ae30ce2f402cd10"
dependencies = [
"libc",
"objc",
"objc-foundation",
"winapi",
]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.9.0" version = "0.9.0"
@ -211,6 +223,70 @@ dependencies = [
"crypto-common", "crypto-common",
] ]
[[package]]
name = "encoding"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec"
dependencies = [
"encoding-index-japanese",
"encoding-index-korean",
"encoding-index-simpchinese",
"encoding-index-singlebyte",
"encoding-index-tradchinese",
]
[[package]]
name = "encoding-index-japanese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-korean"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-simpchinese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-singlebyte"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-tradchinese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding_index_tests"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "1.8.0" version = "1.8.0"
@ -293,11 +369,21 @@ version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"libc", "libc",
"wasi", "wasi",
] ]
[[package]]
name = "gettext-ng"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2c86be871deb255ef65fc8395048a2505912c595f1eddc4da03aeb6fda5cf34"
dependencies = [
"byteorder",
"encoding",
]
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.14" version = "0.3.14"
@ -453,7 +539,7 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
] ]
[[package]] [[package]]
@ -483,7 +569,16 @@ version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
]
[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc",
] ]
[[package]] [[package]]
@ -492,12 +587,6 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memory_units"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.3.16" version = "0.3.16"
@ -569,6 +658,35 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2b2cbbfd8defa51ff24450a61d73b3ff3e158484ddd274a883e886e6fbaa78" checksum = "7b2b2cbbfd8defa51ff24450a61d73b3ff3e158484ddd274a883e886e6fbaa78"
[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
]
[[package]]
name = "objc-foundation"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
dependencies = [
"block",
"objc",
"objc_id",
]
[[package]]
name = "objc_id"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
dependencies = [
"objc",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.14.0" version = "1.14.0"
@ -625,7 +743,7 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"cpufeatures", "cpufeatures",
"opaque-debug", "opaque-debug",
"universal-hash", "universal-hash",
@ -763,7 +881,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
dependencies = [ dependencies = [
"block-buffer 0.9.0", "block-buffer 0.9.0",
"cfg-if 1.0.0", "cfg-if",
"cpufeatures", "cpufeatures",
"digest 0.9.0", "digest 0.9.0",
"opaque-debug", "opaque-debug",
@ -775,7 +893,7 @@ version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "006769ba83e921b3085caa8334186b00cf92b4cb1a6cf4632fbccc8eff5c7549" checksum = "006769ba83e921b3085caa8334186b00cf92b4cb1a6cf4632fbccc8eff5c7549"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"cpufeatures", "cpufeatures",
"digest 0.10.3", "digest 0.10.3",
] ]
@ -833,7 +951,7 @@ version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"fastrand", "fastrand",
"libc", "libc",
"redox_syscall", "redox_syscall",
@ -996,7 +1114,7 @@ version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"log", "log",
"pin-project-lite", "pin-project-lite",
"tracing-core", "tracing-core",
@ -1107,21 +1225,6 @@ name = "usdpl-back"
version = "0.7.2" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58928ed65332c30b9b9be5140fcdab97e45db679a5845d829aa26492765272e5" checksum = "58928ed65332c30b9b9be5140fcdab97e45db679a5845d829aa26492765272e5"
dependencies = [
"async-recursion",
"async-trait",
"bytes",
"hex",
"log",
"obfstr",
"tokio",
"usdpl-core 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"warp",
]
[[package]]
name = "usdpl-back"
version = "0.8.0"
dependencies = [ dependencies = [
"async-recursion", "async-recursion",
"async-trait", "async-trait",
@ -1135,12 +1238,20 @@ dependencies = [
] ]
[[package]] [[package]]
name = "usdpl-core" name = "usdpl-back"
version = "0.6.0" version = "0.9.0"
dependencies = [ dependencies = [
"aes-gcm-siv", "async-recursion",
"base64", "async-trait",
"hex-literal", "bytes",
"current_locale",
"gettext-ng",
"hex",
"log",
"obfstr",
"tokio",
"usdpl-core 0.9.0",
"warp",
] ]
[[package]] [[package]]
@ -1153,25 +1264,33 @@ dependencies = [
"base64", "base64",
] ]
[[package]]
name = "usdpl-core"
version = "0.9.0"
dependencies = [
"aes-gcm-siv",
"base64",
"hex-literal",
]
[[package]] [[package]]
name = "usdpl-front" name = "usdpl-front"
version = "0.7.0" version = "0.9.0"
dependencies = [ dependencies = [
"console_error_panic_hook", "console_error_panic_hook",
"hex", "hex",
"js-sys", "js-sys",
"obfstr", "obfstr",
"usdpl-core 0.6.0", "usdpl-core 0.9.0",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"wasm-bindgen-test", "wasm-bindgen-test",
"web-sys", "web-sys",
"wee_alloc",
] ]
[[package]] [[package]]
name = "usdpl-rs" name = "usdpl-rs"
version = "0.6.0" version = "0.9.0"
[[package]] [[package]]
name = "utf-8" name = "utf-8"
@ -1237,7 +1356,7 @@ version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"wasm-bindgen-macro", "wasm-bindgen-macro",
] ]
@ -1262,7 +1381,7 @@ version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
@ -1331,18 +1450,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "wee_alloc"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e"
dependencies = [
"cfg-if 0.1.10",
"libc",
"memory_units",
"winapi",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "usdpl-rs" name = "usdpl-rs"
version = "0.6.0" version = "0.9.0"
authors = ["NGnius (Graham) <ngniusness@gmail.com>"] authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
edition = "2021" edition = "2021"
license = "GPL-3.0-only" license = "GPL-3.0-only"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "usdpl-back" name = "usdpl-back"
version = "0.8.0" version = "0.9.0"
edition = "2021" edition = "2021"
license = "GPL-3.0-only" license = "GPL-3.0-only"
repository = "https://github.com/NGnius/usdpl-rs" repository = "https://github.com/NGnius/usdpl-rs"
@ -8,14 +8,15 @@ readme = "README.md"
description = "Universal Steam Deck Plugin Library back-end" description = "Universal Steam Deck Plugin Library back-end"
[features] [features]
default = ["blocking"] default = ["blocking", "translate"]
decky = ["usdpl-core/decky"] decky = ["usdpl-core/decky"]
crankshaft = ["usdpl-core/crankshaft"] crankshaft = ["usdpl-core/crankshaft"]
blocking = ["tokio", "tokio/rt", "tokio/rt-multi-thread"] # synchronous API for async functionality, using tokio blocking = ["tokio", "tokio/rt", "tokio/rt-multi-thread"] # synchronous API for async functionality, using tokio
encrypt = ["usdpl-core/encrypt", "obfstr", "hex"] encrypt = ["usdpl-core/encrypt", "obfstr", "hex"]
translate = ["usdpl-core/translate", "gettext-ng"]
[dependencies] [dependencies]
usdpl-core = { version = "0.6.0", path = "../usdpl-core"} usdpl-core = { version = "0.9.0", path = "../usdpl-core"}
log = "0.4" log = "0.4"
@ -31,3 +32,6 @@ async-recursion = "1.0.0"
# encryption helpers # encryption helpers
obfstr = { version = "0.3", optional = true } obfstr = { version = "0.3", optional = true }
hex = { version = "0.4", optional = true } hex = { version = "0.4", optional = true }
# translations
gettext-ng = { version = "0.4.1", optional = true }

View file

@ -118,14 +118,16 @@ impl Instance {
} else { } else {
socket::Packet::Invalid socket::Packet::Invalid
} }
} },
socket::Packet::Many(packets) => { socket::Packet::Many(packets) => {
let mut result = Vec::with_capacity(packets.len()); let mut result = Vec::with_capacity(packets.len());
for packet in packets { for packet in packets {
result.push(Self::handle_call(packet, handlers).await); result.push(Self::handle_call(packet, handlers).await);
} }
socket::Packet::Many(result) socket::Packet::Many(result)
} },
#[cfg(feature = "translate")]
socket::Packet::Language(lang) => socket::Packet::Translations(get_all_translations(lang)),
_ => socket::Packet::Invalid, _ => socket::Packet::Invalid,
} }
} }
@ -221,6 +223,32 @@ impl Instance {
} }
} }
#[cfg(feature = "translate")]
fn get_all_translations(language: String) -> Vec<(String, Vec<String>)> {
log::debug!("Loading translations for language `{}`...", language);
let result = load_locale(&language);
match result {
Ok(catalog) => {
let map = catalog.nalltext();
let mut result = Vec::with_capacity(map.len());
for (key, val) in map.iter() {
result.push((key.to_owned().into(), val.iter().map(|x| x.into()).collect()));
}
result
},
Err(e) => {
log::error!("Failed to load translations for language `{}`: {}", language, e);
vec![]
}
}
}
#[cfg(feature = "translate")]
fn load_locale(lang: &str) -> Result<gettext_ng::Catalog, gettext_ng::Error> {
let file = std::fs::File::open(lang).map_err(|e| gettext_ng::Error::Io(e))?;
gettext_ng::Catalog::parse(file)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[allow(unused_imports)] #[allow(unused_imports)]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "usdpl-core" name = "usdpl-core"
version = "0.6.0" version = "0.9.0"
edition = "2021" edition = "2021"
license = "GPL-3.0-only" license = "GPL-3.0-only"
repository = "https://github.com/NGnius/usdpl-rs" repository = "https://github.com/NGnius/usdpl-rs"
@ -12,6 +12,7 @@ default = []
decky = [] decky = []
crankshaft = [] crankshaft = []
encrypt = ["aes-gcm-siv"] encrypt = ["aes-gcm-siv"]
translate = []
[dependencies] [dependencies]
base64 = "0.13" base64 = "0.13"

View file

@ -24,6 +24,48 @@ impl<T: Dumpable> Dumpable for Vec<T> {
} }
} }
impl<T0: Dumpable, T1: Dumpable> Dumpable for (T0, T1) {
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
Ok(
self.0.dump(buffer)?
+ self.1.dump(buffer)?
)
}
}
impl<T0: Dumpable, T1: Dumpable, T2: Dumpable> Dumpable for (T0, T1, T2) {
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
Ok(
self.0.dump(buffer)?
+ self.1.dump(buffer)?
+ self.2.dump(buffer)?
)
}
}
impl<T0: Dumpable, T1: Dumpable, T2: Dumpable, T3: Dumpable> Dumpable for (T0, T1, T2, T3) {
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
Ok(
self.0.dump(buffer)?
+ self.1.dump(buffer)?
+ self.2.dump(buffer)?
+ self.3.dump(buffer)?
)
}
}
impl<T0: Dumpable, T1: Dumpable, T2: Dumpable, T3: Dumpable, T4: Dumpable> Dumpable for (T0, T1, T2, T3, T4) {
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
Ok(
self.0.dump(buffer)?
+ self.1.dump(buffer)?
+ self.2.dump(buffer)?
+ self.3.dump(buffer)?
+ self.4.dump(buffer)?
)
}
}
impl Dumpable for bool { impl Dumpable for bool {
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> { fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
buffer.write(&[*self as u8]).map_err(DumpError::Io) buffer.write(&[*self as u8]).map_err(DumpError::Io)
@ -97,6 +139,11 @@ mod tests {
&[3, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 5, 0, 0, 0, 116, 101, 115, 116, 50] &[3, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 5, 0, 0, 0, 116, 101, 115, 116, 50]
} }
test_impl! {tuple2_dump_test, (0u8, 1u8), 2, &[0, 1]}
test_impl! {tuple3_dump_test, (0u8, 1u8, 2u8), 3, &[0, 1, 2]}
test_impl! {tuple4_dump_test, (0u8, 1u8, 2u8, 3u8), 4, &[0, 1, 2, 3]}
test_impl! {tuple5_dump_test, (0u8, 1u8, 2u8, 3u8, 4u8), 5, &[0, 1, 2, 3, 4]}
test_impl! {bool_true_dump_test, true, 1, &[1]} test_impl! {bool_true_dump_test, true, 1, &[1]}
test_impl! {bool_false_dump_test, false, 1, &[0]} test_impl! {bool_false_dump_test, false, 1, &[0]}

View file

@ -38,6 +38,56 @@ impl<T: Loadable> Loadable for Vec<T> {
} }
} }
impl<T0: Loadable, T1: Loadable> Loadable for (T0, T1) {
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
let (t0, len0) = T0::load(buffer)?;
let (t1, len1) = T1::load(buffer)?;
Ok((
(t0, t1),
len0 + len1
))
}
}
impl<T0: Loadable, T1: Loadable, T2: Loadable> Loadable for (T0, T1, T2) {
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
let (t0, len0) = T0::load(buffer)?;
let (t1, len1) = T1::load(buffer)?;
let (t2, len2) = T2::load(buffer)?;
Ok((
(t0, t1, t2),
len0 + len1 + len2
))
}
}
impl<T0: Loadable, T1: Loadable, T2: Loadable, T3: Loadable> Loadable for (T0, T1, T2, T3) {
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
let (t0, len0) = T0::load(buffer)?;
let (t1, len1) = T1::load(buffer)?;
let (t2, len2) = T2::load(buffer)?;
let (t3, len3) = T3::load(buffer)?;
Ok((
(t0, t1, t2, t3),
len0 + len1 + len2 + len3
))
}
}
impl<T0: Loadable, T1: Loadable, T2: Loadable, T3: Loadable, T4: Loadable> Loadable for (T0, T1, T2, T3, T4) {
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
let (t0, len0) = T0::load(buffer)?;
let (t1, len1) = T1::load(buffer)?;
let (t2, len2) = T2::load(buffer)?;
let (t3, len3) = T3::load(buffer)?;
let (t4, len4) = T4::load(buffer)?;
Ok((
(t0, t1, t2, t3, t4),
len0 + len1 + len2 + len3 + len4
))
}
}
impl Loadable for bool { impl Loadable for bool {
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
let mut byte = [u8::MAX; 1]; let mut byte = [u8::MAX; 1];
@ -121,6 +171,8 @@ mod tests {
] ]
} }
test_impl! {tuple2_load_test, [0, 1], (u8, u8), 2, (0, 1)}
test_impl! {bool_true_load_test, [1], bool, 1, true} test_impl! {bool_true_load_test, [1], bool, 1, true}
test_impl! {bool_false_load_test, [0], bool, 1, false} test_impl! {bool_false_load_test, [0], bool, 1, false}

View file

@ -39,6 +39,12 @@ pub enum Packet {
Bad, Bad,
/// Many packets merged into one /// Many packets merged into one
Many(Vec<Packet>), Many(Vec<Packet>),
/// Translation data dump
#[cfg(feature = "translate")]
Translations(Vec<(String, Vec<String>)>),
/// Request translations for language
#[cfg(feature = "translate")]
Language(String),
} }
impl Packet { impl Packet {
@ -53,6 +59,10 @@ impl Packet {
Self::Unsupported => 6, Self::Unsupported => 6,
Self::Bad => 7, Self::Bad => 7,
Self::Many(_) => 8, Self::Many(_) => 8,
#[cfg(feature = "translate")]
Self::Translations(_) => 9,
#[cfg(feature = "translate")]
Self::Language(_) => 10,
} }
} }
} }
@ -82,7 +92,17 @@ impl Loadable for Packet {
8 => { 8 => {
let (obj, len) = <_>::load(buf)?; let (obj, len) = <_>::load(buf)?;
(Self::Many(obj), len) (Self::Many(obj), len)
} },
#[cfg(feature = "translate")]
9 => {
let (obj, len) = <_>::load(buf)?;
(Self::Translations(obj), len)
},
#[cfg(feature = "translate")]
10 => {
let (obj, len) = <_>::load(buf)?;
(Self::Language(obj), len)
},
_ => return Err(LoadError::InvalidData), _ => return Err(LoadError::InvalidData),
}; };
result.1 += 1; result.1 += 1;
@ -102,6 +122,10 @@ impl Dumpable for Packet {
Self::Unsupported => Ok(0), Self::Unsupported => Ok(0),
Self::Bad => return Err(DumpError::Unsupported), Self::Bad => return Err(DumpError::Unsupported),
Self::Many(v) => v.dump(buf), Self::Many(v) => v.dump(buf),
#[cfg(feature = "translate")]
Self::Translations(tr) => tr.dump(buf),
#[cfg(feature = "translate")]
Self::Language(l) => l.dump(buf),
}?; }?;
Ok(size1 + result) Ok(size1 + result)
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "usdpl-front" name = "usdpl-front"
version = "0.7.0" version = "0.9.0"
authors = ["NGnius (Graham) <ngniusness@gmail.com>"] authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
edition = "2021" edition = "2021"
license = "GPL-3.0-only" license = "GPL-3.0-only"
@ -12,11 +12,12 @@ description = "Universal Steam Deck Plugin Library front-end designed for WASM"
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features] [features]
default = [] default = ["translate"]
decky = ["usdpl-core/decky"] decky = ["usdpl-core/decky"]
crankshaft = ["usdpl-core/crankshaft"] crankshaft = ["usdpl-core/crankshaft"]
debug = ["console_error_panic_hook"] debug = ["console_error_panic_hook"]
encrypt = ["usdpl-core/encrypt", "obfstr", "hex"] encrypt = ["usdpl-core/encrypt", "obfstr", "hex"]
translate = ["usdpl-core/translate"]
[dependencies] [dependencies]
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
@ -28,13 +29,6 @@ wasm-bindgen-futures = "0.4"
# code size when deploying. # code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true } console_error_panic_hook = { version = "0.1.6", optional = true }
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
#
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
wee_alloc = { version = "0.4.5", optional = true }
web-sys = { version = "0.3", features = [ web-sys = { version = "0.3", features = [
#'Headers', #'Headers',
'Request', 'Request',
@ -48,7 +42,7 @@ js-sys = { version = "0.3" }
obfstr = { version = "0.3", optional = true } obfstr = { version = "0.3", optional = true }
hex = { version = "0.4", optional = true } hex = { version = "0.4", optional = true }
usdpl-core = { version = "0.6.0", path = "../usdpl-core" } usdpl-core = { version = "0.9.0", path = "../usdpl-core" }
[dev-dependencies] [dev-dependencies]
wasm-bindgen-test = { version = "0.3.13" } wasm-bindgen-test = { version = "0.3.13" }

View file

@ -16,13 +16,14 @@ use usdpl_core::socket;
#[cfg(feature = "encrypt")] #[cfg(feature = "encrypt")]
const NONCE: [u8; socket::NONCE_SIZE]= [0u8; socket::NONCE_SIZE]; const NONCE: [u8; socket::NONCE_SIZE]= [0u8; socket::NONCE_SIZE];
pub async fn send_js( pub async fn send_recv_packet(
id: u64, id: u64,
packet: socket::Packet, packet: socket::Packet,
port: u16, port: u16,
#[cfg(feature = "encrypt")] #[cfg(feature = "encrypt")]
key: Vec<u8>, key: Vec<u8>,
) -> Result<Vec<Primitive>, JsValue> { ) -> Result<socket::Packet, JsValue> {
let mut opts = RequestInit::new(); let mut opts = RequestInit::new();
opts.method("POST"); opts.method("POST");
opts.mode(RequestMode::Cors); opts.mode(RequestMode::Cors);
@ -53,34 +54,31 @@ pub async fn send_js(
crate::imports::console_log(&format!("Received base64 `{}` len:{}", rust_str, rust_str.len())); crate::imports::console_log(&format!("Received base64 `{}` len:{}", rust_str, rust_str.len()));
#[cfg(not(feature = "encrypt"))] #[cfg(not(feature = "encrypt"))]
match socket::Packet::load_base64(rust_str.as_bytes()) {Ok(socket::Packet::load_base64(rust_str.as_bytes())
.map_err(super::convert::str_to_js)? .map_err(super::convert::str_to_js)?
.0 .0)}
{
socket::Packet::CallResponse(resp) => Ok(resp.response),
_ => {
//imports::console_warn(&format!("USDPL warning: Got non-call-response message from {}", resp.url()));
Err(format!(
"Expected call response message from {}, got something else",
resp.url()
)
.into())
}
}
#[cfg(feature = "encrypt")] #[cfg(feature = "encrypt")]
match socket::Packet::load_encrypted(rust_str.as_bytes(), key.as_slice(), &NONCE) {Ok(socket::Packet::load_encrypted(rust_str.as_bytes(), key.as_slice(), &NONCE)
.map_err(super::convert::str_to_js)? .map_err(super::convert::str_to_js)?
.0 .0)}
}
pub async fn send_call(
id: u64,
packet: socket::Packet,
port: u16,
#[cfg(feature = "encrypt")]
key: Vec<u8>,
) -> Result<Vec<Primitive>, JsValue> {
let packet = send_recv_packet(id, packet, port, #[cfg(feature = "encrypt")] key).await?;
match packet
{ {
socket::Packet::CallResponse(resp) => Ok(resp.response), socket::Packet::CallResponse(resp) => Ok(resp.response),
_ => { _ => {
//imports::console_warn(&format!("USDPL warning: Got non-call-response message from {}", resp.url())); //imports::console_warn(&format!("USDPL warning: Got non-call-response message from {}", resp.url()));
Err(format!( Err("Expected call response message, got something else".into())
"Expected call response message from {}, got something else",
resp.url()
)
.into())
} }
} }
} }

View file

@ -16,11 +16,18 @@ use usdpl_core::{socket::Packet, RemoteCall};
//const REMOTE_CALL_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); //const REMOTE_CALL_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
//const REMOTE_PORT: std::sync::atomic::AtomicU16 = std::sync::atomic::AtomicU16::new(31337); //const REMOTE_PORT: std::sync::atomic::AtomicU16 = std::sync::atomic::AtomicU16::new(31337);
static mut CTX: UsdplContext = UsdplContext { port: 31337, id: 1, static mut CTX: UsdplContext = UsdplContext {
#[cfg(feature = "encrypt")] key: Vec::new() }; port: 31337,
id: 1,
#[cfg(feature = "encrypt")]
key: Vec::new(),
};
static mut CACHE: Option<std::collections::HashMap<String, JsValue>> = None; static mut CACHE: Option<std::collections::HashMap<String, JsValue>> = None;
#[cfg(feature = "translate")]
static mut TRANSLATIONS: Option<std::collections::HashMap<String, Vec<String>>> = None;
#[cfg(feature = "encrypt")] #[cfg(feature = "encrypt")]
fn encryption_key() -> Vec<u8> { fn encryption_key() -> Vec<u8> {
hex::decode(obfstr::obfstr!(env!("USDPL_ENCRYPTION_KEY"))).unwrap() hex::decode(obfstr::obfstr!(env!("USDPL_ENCRYPTION_KEY"))).unwrap()
@ -52,11 +59,6 @@ fn increment_id() -> u64 {
current_id current_id
} }
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
/// Initialize the front-end library /// Initialize the front-end library
#[wasm_bindgen] #[wasm_bindgen]
pub fn init_usdpl(port: u16) { pub fn init_usdpl(port: u16) {
@ -123,7 +125,7 @@ pub async fn call_backend(name: String, parameters: Vec<JsValue>) -> JsValue {
let port = get_port(); let port = get_port();
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
imports::console_log(&format!("USDPL: Got port {}", port)); imports::console_log(&format!("USDPL: Got port {}", port));
let results = connection::send_js( let results = connection::send_call(
next_id, next_id,
Packet::Call(RemoteCall { Packet::Call(RemoteCall {
id: next_id, id: next_id,
@ -152,3 +154,66 @@ pub async fn call_backend(name: String, parameters: Vec<JsValue>) -> JsValue {
} }
results_js.into() results_js.into()
} }
/// Initialize translation strings for the front-end
#[wasm_bindgen]
pub async fn init_tr(locale: String) {
let next_id = increment_id();
match connection::send_recv_packet(
next_id,
Packet::Language(locale.clone()),
get_port(),
#[cfg(feature = "encrypt")]
get_key()
).await {
Ok(Packet::Translations(translations)) => {
#[cfg(feature = "debug")]
imports::console_log(&format!("USDPL: Got translations for {}", locale));
// convert translations into map
let mut tr_map = std::collections::HashMap::with_capacity(translations.len());
for (key, val) in translations {
tr_map.insert(key, val);
}
unsafe { TRANSLATIONS = Some(tr_map) }
},
Ok(_) => {
#[cfg(feature = "debug")]
imports::console_error(&format!("USDPL: Got wrong packet response for init_tr"));
unsafe { TRANSLATIONS = None }
},
#[allow(unused_variables)]
Err(e) => {
#[cfg(feature = "debug")]
imports::console_error(&format!("USDPL: Got wrong error for init_tr: {}", e));
unsafe { TRANSLATIONS = None }
}
}
}
/// Translate a phrase, equivalent to tr_n(msg_id, 0)
#[wasm_bindgen]
pub fn tr(msg_id: String) -> String {
if let Some(translations) = unsafe { TRANSLATIONS.as_ref().unwrap().get(&msg_id) } {
if let Some(translated) = translations.get(0) {
translated.to_owned()
} else {
msg_id
}
} else {
msg_id
}
}
/// Translate a phrase, retrieving the plural form for `n` items
#[wasm_bindgen]
pub fn tr_n(msg_id: String, n: usize) -> String {
if let Some(translations) = unsafe { TRANSLATIONS.as_ref().unwrap().get(&msg_id) } {
if let Some(translated) = translations.get(n) {
translated.to_owned()
} else {
msg_id
}
} else {
msg_id
}
}