This commit is contained in:
xeon 2024-07-20 15:13:04 +03:00
parent 2fa1067e96
commit 2de8ee290a
131 changed files with 72712 additions and 1 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
target/
Cargo.lock
/nap_proto/nap.proto
/nap_proto/bin.server.proto
/nap_sdk.toml
/nap_gameserver.toml

76
Cargo.toml Normal file
View file

@ -0,0 +1,76 @@
[workspace]
members = ["nap_common", "nap_sdk", "nap_proto", "nap_gameserver", "nap_data"]
resolver = "2"
[workspace.package]
version = "0.1.0"
[workspace.dependencies]
# Runtime
tokio = { version = "1.36.0", features = ["full"] }
tokio-util = { version = "0.7.10", features = ["io"] }
# Serialization
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.114"
toml = "0.8.4"
rbase64 = "2.0.3"
prost = "0.12.3"
prost-types = "0.12.3"
prost-build = "0.12.3"
# Cryptography
password-hash = { version = "0.5.0", features = ["alloc", "rand_core"] }
pbkdf2 = { version = "0.12.2", features = ["simple"] }
rand_mt = "4.2.2"
rsa = { version = "0.9.6", features = [
"sha1",
"nightly",
"pkcs5",
"serde",
"sha2",
] }
# Http
axum = "0.7.5"
# Database
sqlx = { version = "0.7.4", features = ["postgres", "runtime-tokio-rustls"] }
# Logging
env_logger = "0.11.3"
tracing = "0.1.40"
tracing-futures = "0.2.5"
tracing-log = { version = "0.2.0", features = ["std", "log-tracer"] }
tracing-subscriber = { version = "0.3.18", features = [
"env-filter",
"registry",
"std",
"tracing",
"tracing-log",
] }
tracing-bunyan-formatter = "0.3.9"
# Util
thiserror = "1.0.61"
paste = "1.0.15"
hex = "0.4.3"
rand = "0.8.5"
byteorder = "1.5.0"
atomic_enum = "0.3.0"
num_enum = "0.7.2"
dashmap = "6.0.1"
regex = "1.10.5"
ansi_term = "0.12.1"
# Internal
common = { path = "nap_common" }
data = { path = "nap_data" }
proto = { path = "nap_proto" }
proto_gen = { path = "nap_proto/proto_gen" }
[profile.release]
strip = true # Automatically strip symbols from the binary.
lto = true # Link-time optimization.
opt-level = 3 # Optimize for speed.
codegen-units = 1 # Maximum size reduction optimizations.

View file

@ -1,3 +1,59 @@
# JaneDoe-ZS
Server emulator for Zenless Zone Zero 1.1.1
#### Zenless Zone Zero 1.1.1 server emulator written in Rust.
![screenshot](https://git.xeondev.com/NewEriduPubSec/JaneDoe-ZS/raw/branch/master/screenshot.png)
### Current features
- Logging in
- Fully playable prologue
- Player progress saving with PostgreSQL
- Unlock all characters
- Training battle
### Requirements
- [Rust](https://www.rust-lang.org/tools/install)
- [PostgreSQL](https://www.postgresql.org/download/)
**NOTE**: Nightly Rust is required to build the project. To install it, first install
Rust itself, then run the following command:
```sh
rustup toolchain install nightly
rustup default nightly
```
### Setup
#### Server
##### a) building from sources
```
git clone https://git.xeondev.com/NewEriduPubSec/JaneDoe-ZS.git
cd JaneDoe-ZS
cargo run --bin nap-sdk
cargo run --bin nap-gameserver
```
##### b) using pre-built binaries
Navigate to the [Releases](https://git.xeondev.com/NewEriduPubSec/JaneDoe-ZS/releases)
page and download the latest release for your platform.<br>
Launch `nap-sdk` and `nap-gameserver`
#### Database
You have to put your PostgreSQL database credentials in both `nap_sdk.toml` and `nap_gameserver.toml` configuration files.
```
[database]
username = "root"
password = "root"
host = "localhost:5432"
name = "nap"
```
### Connecting
[Get ZZZ 1.1.1 beta client](https://nogatekeep.ing/assets/nap/pc/napcnbeta_1.1.1.7z),
apply [encryption patch](https://git.xeondev.com/NewEriduPubSec/JaneDoe-Patch) and follow its instructions.<br>
**NOTE**: You should create a game account. By default, you can do so at http://127.0.0.1:21000/account/register
### Troubleshooting
[Visit our discord](https://discord.xeondev.com/) if you have any questions/issues
### Support
If you want to support this project, feel free to [send a tip via boosty](https://boosty.to/xeondev/donate)

View file

@ -0,0 +1,394 @@
[
{
"ID": 1011,
"CodeName": "Avatar_Female_Size02_Anbi_En",
"Name": "Avatar_Female_Size02_Anbi",
"FullName": "Avatar_Female_Size02_Anbi_FullName",
"AudioEventReplaceParam": "anbi",
"AudioBank": "BK_Anbi|BK_CHR_Common|BK_Anbi_VO",
"HitTypes": [101],
"ElementTypes": [203],
"Unk1": 2,
"Gender": 2,
"Camp": 1,
"Unk2": ["1:1:2", "2:203:2"]
},
{
"ID": 1021,
"CodeName": "Avatar_Female_Size01_Tsubaki_En",
"Name": "Avatar_Female_Size01_Tsubaki",
"FullName": "Avatar_Female_Size01_Tsubaki_FullName",
"AudioEventReplaceParam": "tsubaki",
"AudioBank": "BK_Tsubaki|BK_CHR_Common|BK_Tsubaki_VO",
"HitTypes": [101],
"ElementTypes": [200],
"Unk1": 1,
"Gender": 2,
"Camp": 1,
"Unk2": ["1:1:2", "2:200:2"]
},
{
"ID": 1031,
"CodeName": "Avatar_Female_Size02_Nostradamus_En",
"Name": "Avatar_Female_Size02_Nostradamus",
"FullName": "Avatar_Female_Size02_Nostradamus_FullName",
"AudioEventReplaceParam": "nicole",
"AudioBank": "BK_Nicole|BK_CHR_Common|BK_Nicole_VO",
"HitTypes": [102],
"ElementTypes": [205],
"Unk1": 4,
"Gender": 2,
"Camp": 1,
"Unk2": ["1:1:2", "2:205:2"]
},
{
"ID": 1041,
"CodeName": "Avatar_Female_Size02_Longinus_En",
"Name": "Avatar_Female_Size02_Longinus",
"FullName": "Avatar_Female_Size02_Longinus_FullName",
"AudioEventReplaceParam": "longinus",
"AudioBank": "BK_Longinus|BK_CHR_Common|BK_Longinus_VO",
"HitTypes": [101],
"ElementTypes": [201],
"Unk1": 1,
"Gender": 2,
"Camp": 5,
"Unk2": ["1:5:2", "2:201:2"]
},
{
"ID": 1061,
"CodeName": "Avatar_Female_Size01_Corin_En",
"Name": "Avatar_Female_Size01_Corin",
"FullName": "Avatar_Female_Size01_Corin_FullName",
"AudioEventReplaceParam": "corin",
"AudioBank": "BK_Corin|BK_CHR_Common|BK_Corin_VO",
"HitTypes": [101],
"ElementTypes": [200],
"Unk1": 1,
"Gender": 2,
"Camp": 2,
"Unk2": ["1:2:2", "2:200:2"]
},
{
"ID": 1071,
"CodeName": "Avatar_Female_Size03_Caesar_En",
"Name": "Avatar_Female_Size03_Caesar",
"FullName": "Avatar_Female_Size03_Caesar_FullName",
"AudioEventReplaceParam": "caesar",
"AudioBank": "BK_Caesar|BK_CHR_Common|BK_Caesar_VO",
"HitTypes": [101, 102],
"ElementTypes": [200],
"Unk1": 5,
"Gender": 2,
"Camp": 4,
"Unk2": ["1:4:2", "2:200:2"]
},
{
"ID": 1081,
"CodeName": "Avatar_Male_Size03_Billy_En",
"Name": "Avatar_Male_Size03_Billy",
"FullName": "Avatar_Male_Size03_Billy_FullName",
"AudioEventReplaceParam": "billy",
"AudioBank": "BK_Billy|BK_CHR_Common|BK_Billy_VO",
"HitTypes": [103],
"ElementTypes": [200],
"Unk1": 1,
"Gender": 1,
"Camp": 1,
"Unk2": ["1:1:2", "2:200:2"]
},
{
"ID": 1091,
"CodeName": "Avatar_Female_Size02_Unagi_En",
"Name": "Avatar_Female_Size02_Unagi",
"FullName": "Avatar_Female_Size02_Unagi_FullName",
"AudioEventReplaceParam": "unagi",
"AudioBank": "BK_Unagi|BK_CHR_Common|BK_Unagi_VO",
"HitTypes": [101],
"ElementTypes": [202],
"Unk1": 1,
"Gender": 2,
"Camp": 6,
"Unk2": ["1:6:2", "2:202:2"]
},
{
"ID": 1101,
"CodeName": "Avatar_Female_Size01_OokumaMari_En",
"Name": "Avatar_Female_Size01_OokumaMari",
"FullName": "Avatar_Female_Size01_OokumaMari_FullName",
"AudioEventReplaceParam": "koleda",
"AudioBank": "BK_Koleda|BK_CHR_Common|BK_Koleda_VO",
"HitTypes": [102],
"ElementTypes": [201],
"Unk1": 2,
"Gender": 2,
"Camp": 3,
"Unk2": ["1:3:2", "2:201:2"]
},
{
"ID": 1111,
"CodeName": "Avatar_Male_Size03_Anton_En",
"Name": "Avatar_Male_Size03_Anton",
"FullName": "Avatar_Male_Size03_Anton_FullName",
"AudioEventReplaceParam": "anton",
"AudioBank": "BK_Anton|BK_CHR_Common|BK_Anton_VO",
"HitTypes": [103],
"ElementTypes": [203],
"Unk1": 1,
"Gender": 1,
"Camp": 3,
"Unk2": ["1:3:2", "2:203:2"]
},
{
"ID": 1121,
"CodeName": "Avatar_Male_Size03_Ben_En",
"Name": "Avatar_Male_Size03_Ben",
"FullName": "Avatar_Male_Size03_Ben_FullName",
"AudioEventReplaceParam": "ben",
"AudioBank": "BK_Ben|BK_CHR_Common|BK_Ben_VO",
"HitTypes": [102],
"ElementTypes": [201],
"Unk1": 5,
"Gender": 1,
"Camp": 3,
"Unk2": ["1:3:2", "2:201:2"]
},
{
"ID": 1131,
"CodeName": "Avatar_Female_Size01_Aokaku_En",
"Name": "Avatar_Female_Size01_Aokaku",
"FullName": "Avatar_Female_Size01_Aokaku_FullName",
"AudioEventReplaceParam": "aokaku",
"AudioBank": "BK_Aokaku|BK_CHR_Common|BK_Aokaku_VO",
"HitTypes": [101],
"ElementTypes": [202],
"Unk1": 4,
"Gender": 2,
"Camp": 6,
"Unk2": ["1:6:2", "2:202:2"]
},
{
"ID": 1141,
"CodeName": "Avatar_Male_Size03_Lycaon_En",
"Name": "Avatar_Male_Size03_Lycaon",
"FullName": "Avatar_Male_Size03_Lycaon_FullName",
"AudioEventReplaceParam": "lycaon",
"AudioBank": "BK_Lycaon|BK_CHR_Common|BK_Lycaon_VO",
"HitTypes": [102],
"ElementTypes": [202],
"Unk1": 2,
"Gender": 1,
"Camp": 2,
"Unk2": ["1:2:2", "2:202:2"]
},
{
"ID": 1151,
"CodeName": "Avatar_Female_Size01_Lucy_En",
"Name": "Avatar_Female_Size01_Lucy",
"FullName": "Avatar_Female_Size01_Lucy_FullName",
"AudioEventReplaceParam": "lucy",
"AudioBank": "BK_Lucy|BK_CHR_Common|BK_Lucy_VO",
"HitTypes": [102],
"ElementTypes": [201],
"Unk1": 4,
"Gender": 2,
"Camp": 4,
"Unk2": ["1:4:2", "2:201:2"]
},
{
"ID": 1161,
"CodeName": "Avatar_Male_Size02_Lighter_En",
"Name": "Avatar_Male_Size02_Lighter",
"FullName": "Avatar_Male_Size02_Lighter_FullName",
"AudioEventReplaceParam": "lighter",
"AudioBank": "BK_Lighter|BK_CHR_Common|BK_Lighter_VO",
"HitTypes": [102],
"ElementTypes": [201],
"Unk1": 1,
"Gender": 1,
"Camp": 4,
"Unk2": ["1:4:2", "2:201:2"]
},
{
"ID": 1171,
"CodeName": "Avatar_Female_Size02_Burnice_En",
"Name": "Avatar_Female_Size02_Burnice",
"FullName": "Avatar_Female_Size02_Burnice_FullName",
"AudioEventReplaceParam": "burnice",
"AudioBank": "BK_Burnice|BK_CHR_Common|BK_Burnice_VO",
"HitTypes": [103],
"ElementTypes": [201],
"Unk1": 3,
"Gender": 2,
"Camp": 4,
"Unk2": ["1:4:2", "2:201:2"]
},
{
"ID": 1181,
"CodeName": "Avatar_Female_Size03_Lisa_En",
"Name": "Avatar_Female_Size03_Lisa",
"FullName": "Avatar_Female_Size03_Lisa_FullName",
"AudioEventReplaceParam": "lisa",
"AudioBank": "BK_Lisa|BK_CHR_Common|BK_Lisa_VO",
"HitTypes": [103],
"ElementTypes": [203],
"Unk1": 3,
"Gender": 2,
"Camp": 3,
"Unk2": ["1:3:2", "2:203:2"]
},
{
"ID": 1191,
"CodeName": "Avatar_Female_Size02_Ellen_En",
"Name": "Avatar_Female_Size02_Ellen",
"FullName": "Avatar_Female_Size02_Ellen_FullName",
"AudioEventReplaceParam": "ellen",
"AudioBank": "BK_Ellen|BK_CHR_Common|BK_Ellen_VO",
"HitTypes": [101],
"ElementTypes": [202],
"Unk1": 1,
"Gender": 2,
"Camp": 2,
"Unk2": ["1:2:2", "2:202:2"]
},
{
"ID": 1201,
"CodeName": "Avatar_Male_Size01_Harumasa_En",
"Name": "Avatar_Male_Size01_Harumasa",
"FullName": "Avatar_Male_Size01_Harumasa_FullName",
"AudioEventReplaceParam": "harumasa",
"AudioBank": "BK_Harumasa|BK_CHR_Common|BK_Harumasa_VO",
"HitTypes": [103],
"ElementTypes": [203],
"Unk1": 1,
"Gender": 1,
"Camp": 6,
"Unk2": ["1:6:2", "2:200:2"]
},
{
"ID": 1211,
"CodeName": "Avatar_Female_Size03_Rina_En",
"Name": "Avatar_Female_Size03_Rina",
"FullName": "Avatar_Female_Size03_Rina_FullName",
"AudioEventReplaceParam": "rina",
"AudioBank": "BK_Rina|BK_CHR_Common|BK_Rina_VO",
"HitTypes": [102],
"ElementTypes": [203],
"Unk1": 4,
"Gender": 2,
"Camp": 2,
"Unk2": ["1:2:2", "2:203:2"]
},
{
"ID": 1221,
"CodeName": "Avatar_Female_Size03_Yanagi_En",
"Name": "Avatar_Female_Size03_Yanagi",
"FullName": "Avatar_Female_Size03_Yanagi_FullName",
"AudioEventReplaceParam": "yanagi",
"AudioBank": "BK_Yanagi|BK_CHR_Common|BK_Yanagi_VO",
"HitTypes": [101],
"ElementTypes": [203],
"Unk1": 3,
"Gender": 2,
"Camp": 6,
"Unk2": ["1:6:2", "2:203:2"]
},
{
"ID": 1241,
"CodeName": "Avatar_Female_Size03_ZhuYuan_En",
"Name": "Avatar_Female_Size03_ZhuYuan",
"FullName": "Avatar_Female_Size03_ZhuYuan_FullName",
"AudioEventReplaceParam": "ZhuYuan",
"AudioBank": "BK_ZhuYuan|BK_CHR_Common|BK_ZhuYuan_VO",
"HitTypes": [103],
"ElementTypes": [205],
"Unk1": 1,
"Gender": 2,
"Camp": 7,
"Unk2": ["1:7:2", "3:4:1"]
},
{
"ID": 1251,
"CodeName": "Avatar_Female_Size01_QingYi_En",
"Name": "Avatar_Female_Size01_QingYi",
"FullName": "Avatar_Female_Size01_QingYi_FullName",
"AudioEventReplaceParam": "qingyi",
"AudioBank": "BK_QingYi|BK_CHR_Common|BK_QingYi_VO",
"HitTypes": [102],
"ElementTypes": [203],
"Unk1": 2,
"Gender": 2,
"Camp": 7,
"Unk2": ["1:7:2", "3:1:1"]
},
{
"ID": 1261,
"CodeName": "Avatar_Female_Size03_JaneDoe_En",
"Name": "Avatar_Female_Size03_JaneDoe",
"FullName": "Avatar_Female_Size03_JaneDoe_FullName",
"AudioEventReplaceParam": "janedoe",
"AudioBank": "BK_JaneDoe|BK_CHR_Common|BK_JaneDoe_VO",
"HitTypes": [101],
"ElementTypes": [200],
"Unk1": 3,
"Gender": 2,
"Camp": 7,
"Unk2": ["1:7:2", "3:3:2"]
},
{
"ID": 1271,
"CodeName": "Avatar_Male_Size01_Seth_En",
"Name": "Avatar_Male_Size01_Seth",
"FullName": "Avatar_Male_Size01_Seth_FullName",
"AudioEventReplaceParam": "seth",
"AudioBank": "BK_Seth|BK_CHR_Common|BK_Seth_VO",
"HitTypes": [101],
"ElementTypes": [203],
"Unk1": 5,
"Gender": 1,
"Camp": 7,
"Unk2": ["1:7:2", "2:203:2"]
},
{
"ID": 1281,
"CodeName": "Avatar_Female_Size01_Clara_En",
"Name": "Avatar_Female_Size01_Clara",
"FullName": "Avatar_Female_Size01_Clara_FullName",
"AudioEventReplaceParam": "clara",
"AudioBank": "BK_Clara|BK_CHR_Common|BK_Clara_VO",
"HitTypes": [101],
"ElementTypes": [200],
"Unk1": 3,
"Gender": 2,
"Camp": 4,
"Unk2": ["1:4:2", "2:200:2"]
},
{
"ID": 2011,
"CodeName": "Avatar_Male_Size01_Wise_En",
"Name": "Avatar_Male_Size01_Wise",
"FullName": "Avatar_Male_Size01_Wise_FullName",
"AudioEventReplaceParam": "wise",
"AudioBank": "BK_Wise",
"HitTypes": [0],
"ElementTypes": [1],
"Unk1": 1,
"Gender": 1,
"Camp": 0,
"Unk2": ["1:0:2", "2:1:2"]
},
{
"ID": 2021,
"CodeName": "Avatar_Female_Size02_Belle_En",
"Name": "Avatar_Female_Size02_Belle",
"FullName": "Avatar_Female_Size02_Belle_FullName",
"AudioEventReplaceParam": "belle",
"AudioBank": "BK_Belle",
"HitTypes": [0],
"ElementTypes": [1],
"Unk1": 1,
"Gender": 2,
"Camp": 0,
"Unk2": ["1:0:2", "2:1:2"]
}
]

View file

@ -0,0 +1,194 @@
[
{
"ID": 3500000,
"Name": "Avatar_Male_Size01_Wise",
"MEPLHBONBFE": "2011",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "30070001",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole34",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 0
},
{
"ID": 3500001,
"Name": "Avatar_Female_Size02_Belle",
"MEPLHBONBFE": "2021",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "30070001",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole33",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 0
},
{
"ID": 3510000,
"Name": "Avatar_Female_Size02_Anbi",
"MEPLHBONBFE": "1011",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "1051211000",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole01",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 4
},
{
"ID": 3510001,
"Name": "Avatar_Female_Size01_Tsubaki",
"MEPLHBONBFE": "1021",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "1051211001",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole11",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 4
},
{
"ID": 3510002,
"Name": "Avatar_Female_Size02_Nostradamus",
"MEPLHBONBFE": "1031",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "1051211002",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole12",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 4
},
{
"ID": 3510003,
"Name": "Avatar_Male_Size03_Billy",
"MEPLHBONBFE": "1081",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "1051211003",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole10",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 4
},
{
"ID": 3510004,
"Name": "Avatar_Female_Size02_Longinus",
"MEPLHBONBFE": "1041",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "1051211004",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole05",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 4
},
{
"ID": 3510005,
"Name": "Avatar_Female_Size01_OokumaMari",
"MEPLHBONBFE": "1101",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "1051211005",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole14",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 4
},
{
"ID": 3510006,
"Name": "Avatar_Male_Size03_Ben",
"MEPLHBONBFE": "1121",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "1051211006",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole16",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 4
},
{
"ID": 3510007,
"Name": "Avatar_Female_Size03_Lisa",
"MEPLHBONBFE": "1181",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "1051211007",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole20",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 4
},
{
"ID": 3510008,
"Name": "Avatar_Male_Size03_Anton",
"MEPLHBONBFE": "1111",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "1051211008",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole15",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 4
},
{
"ID": 3510009,
"Name": "Avatar_Male_Size03_Lycaon",
"MEPLHBONBFE": "1141",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "1051211009",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole18",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 4
},
{
"ID": 3510010,
"Name": "Avatar_Female_Size02_Ellen",
"MEPLHBONBFE": "1191",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "1051211010",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole21",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 4
},
{
"ID": 3510011,
"Name": "Avatar_Female_Size03_Rina",
"MEPLHBONBFE": "1211",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "1051211011",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole22",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 4
},
{
"ID": 3510012,
"Name": "Avatar_Female_Size01_Corin",
"MEPLHBONBFE": "1061",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "1051211012",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole09",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 4
},
{
"ID": 3510013,
"Name": "Avatar_Female_Size03_ZhuYuan",
"MEPLHBONBFE": "1241",
"KEGJJDPAKOH": 0,
"NOCBKKCNNKA": "1051211013",
"LDAILPLNAEG": 0,
"ABMDGFGEHGF": "IconRole23",
"DGJEECKNEJH": [],
"FHNMJNDKHLN": "",
"NIENEBHOJNP": 4
}
]

View file

@ -0,0 +1,50 @@
[
{
"ProcedureID": 1,
"ProcedureType": 2,
"ContentID": "5000008",
"JumpTos": [2],
"ProcedureBanks": [],
"ProcedureEvent": ""
},
{
"ProcedureID": 2,
"ProcedureType": 4,
"ContentID": "CreateRole/LOGIN_P2_mux.mp4",
"JumpTos": [3],
"ProcedureBanks": ["BK_C00_CS01", "BK_C00_CS01_VO"],
"ProcedureEvent": "Play_C00_CS01_BGM"
},
{
"ProcedureID": 3,
"ProcedureType": 2,
"ContentID": "5000009",
"JumpTos": [4],
"ProcedureBanks": [],
"ProcedureEvent": ""
},
{
"ProcedureID": 4,
"ProcedureType": 1,
"ContentID": "107010011",
"JumpTos": [5],
"ProcedureBanks": [],
"ProcedureEvent": ""
},
{
"ProcedureID": 5,
"ProcedureType": 1,
"ContentID": "107010021",
"JumpTos": [6],
"ProcedureBanks": [],
"ProcedureEvent": ""
},
{
"ProcedureID": 6,
"ProcedureType": 1,
"ContentID": "107010031",
"JumpTos": [],
"ProcedureBanks": [],
"ProcedureEvent": ""
}
]

View file

@ -0,0 +1,92 @@
[
{
"SectionId": 1,
"PhotoName": "Photo_SectionName_01",
"Name": "MainCity_Street",
"PrimaryEntryName": "Street_PlayerPos_Default",
"SecondaryEntryName": "Street_PlayerPos_Default",
"ONEGOJKHNJL": 3,
"SectionName": "SectionName_Street"
},
{
"SectionId": 2,
"PhotoName": "Photo_SectionName_02",
"Name": "MainCity_Workshop",
"PrimaryEntryName": "Workshop_PlayerPos_Default",
"SecondaryEntryName": "Workshop_PlayerPos_Pos2",
"ONEGOJKHNJL": 3,
"SectionName": "SectionName_Workshop"
},
{
"SectionId": 3,
"PhotoName": "Photo_SectionName_03",
"Name": "MainCity_Garage",
"PrimaryEntryName": "Garage_PlayerPos_Default",
"SecondaryEntryName": "Garage_PlayerPos_Default",
"ONEGOJKHNJL": 3,
"SectionName": "SectionName_Garage"
},
{
"SectionId": 4,
"PhotoName": "Photo_SectionName_04",
"Name": "MainCity_ConstructionSite",
"PrimaryEntryName": "ConstructionSite_PlayerPos_Default",
"SecondaryEntryName": "ConstructionSite_PlayerPos_Default",
"ONEGOJKHNJL": 3,
"SectionName": "SectionName_ConstructionSite"
},
{
"SectionId": 5,
"PhotoName": "Photo_SectionName_05",
"Name": "MainCity_SkyScraper",
"PrimaryEntryName": "SkyScraper_PlayerPos_Default",
"SecondaryEntryName": "SkyScraper_PlayerPos_Default",
"ONEGOJKHNJL": 3,
"SectionName": "SectionName_SkyScraper"
},
{
"SectionId": 101,
"PhotoName": "Photo_SectionName_101",
"Name": "MainCity_FortuneSquare",
"PrimaryEntryName": "FortuneSquare_PlayerPos_Default",
"SecondaryEntryName": "FortuneSquare_PlayerPos_Default",
"ONEGOJKHNJL": 3,
"SectionName": "SectionName_FortuneSquare"
},
{
"SectionId": 103,
"PhotoName": "Photo_SectionName_103",
"Name": "MainCity_FortuneHIA",
"PrimaryEntryName": "FortuneHIA_PlayerPos_Default",
"SecondaryEntryName": "FortuneHIA_PlayerPos_Default",
"ONEGOJKHNJL": 3,
"SectionName": "SectionName_FortuneHIA"
},
{
"SectionId": 151,
"PhotoName": "Photo_SectionName_151",
"Name": "Maincity_Zero",
"PrimaryEntryName": "Zero_PlayerPos_Default",
"SecondaryEntryName": "Zero_PlayerPos_Default",
"ONEGOJKHNJL": 3,
"SectionName": "SectionName_Zero"
},
{
"SectionId": 153,
"PhotoName": "Photo_SectionName_153",
"Name": "Maincity_Subway_Street",
"PrimaryEntryName": "Subway_PlayerPos_Default",
"SecondaryEntryName": "Subway_PlayerPos_Default",
"ONEGOJKHNJL": 3,
"SectionName": "SectionName_Subway"
},
{
"SectionId": 154,
"PhotoName": "Photo_SectionName_154",
"Name": "Maincity_Subway_FortuneSquare",
"PrimaryEntryName": "SubwayFortuneSquare_PlayerPos_Default",
"SecondaryEntryName": "SubwayFortuneSquare_PlayerPos_Default",
"ONEGOJKHNJL": 3,
"SectionName": "SectionName_Subway"
}
]

View file

@ -0,0 +1,317 @@
[
{
"Id": 12254000,
"TrainingType": 1,
"BattleEventId": 19800014,
"CFFJLDMDEML": "",
"IIEMNPJFNJD": "",
"MEFHKNFOBCL": 0,
"PIIKKAIINOM": 0,
"CIPGDEMIEMF": 0,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "",
"DBIIAEFJHDA": ""
},
{
"Id": 12254001,
"TrainingType": 2,
"BattleEventId": 19800015,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_1",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconSkill_Evade.png",
"MEFHKNFOBCL": 122541011,
"PIIKKAIINOM": 122541081,
"CIPGDEMIEMF": -1,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "",
"DBIIAEFJHDA": ""
},
{
"Id": 12254002,
"TrainingType": 2,
"BattleEventId": 19800016,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_2",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconSkill_ParryAid.png",
"MEFHKNFOBCL": 122541011,
"PIIKKAIINOM": 122541081,
"CIPGDEMIEMF": 122541021,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "",
"DBIIAEFJHDA": ""
},
{
"Id": 12254003,
"TrainingType": 2,
"BattleEventId": 19800017,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_3",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconSkill_ElementAbnormal.png",
"MEFHKNFOBCL": 122541011,
"PIIKKAIINOM": 122541081,
"CIPGDEMIEMF": -1,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "",
"DBIIAEFJHDA": ""
},
{
"Id": 12254004,
"TrainingType": 2,
"BattleEventId": 19810007,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_4",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconStun.png",
"MEFHKNFOBCL": 122541011,
"PIIKKAIINOM": 122541081,
"CIPGDEMIEMF": 122541031,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle01.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254005,
"TrainingType": 2,
"BattleEventId": 19810008,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_5",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconAttack.png",
"MEFHKNFOBCL": 122541081,
"PIIKKAIINOM": 122541011,
"CIPGDEMIEMF": 122541031,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle10.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254006,
"TrainingType": 2,
"BattleEventId": 19810001,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_6",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconSupport.png",
"MEFHKNFOBCL": 122541031,
"PIIKKAIINOM": 122541081,
"CIPGDEMIEMF": 122541061,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle12.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254007,
"TrainingType": 2,
"BattleEventId": 19810009,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_7",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconAttack.png",
"MEFHKNFOBCL": 122541021,
"PIIKKAIINOM": 122541281,
"CIPGDEMIEMF": 122541011,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle11.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254008,
"TrainingType": 2,
"BattleEventId": 19810011,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_8",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconStun.png",
"MEFHKNFOBCL": 122541101,
"PIIKKAIINOM": 122541121,
"CIPGDEMIEMF": 122541111,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle14.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254009,
"TrainingType": 2,
"BattleEventId": 19810002,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_9",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconDefense.png",
"MEFHKNFOBCL": 122541121,
"PIIKKAIINOM": 122541111,
"CIPGDEMIEMF": 122541011,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle16.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254010,
"TrainingType": 2,
"BattleEventId": 19810010,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_10",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconAnomaly.png",
"MEFHKNFOBCL": 122541181,
"PIIKKAIINOM": 122541031,
"CIPGDEMIEMF": 122541111,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle20.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254011,
"TrainingType": 2,
"BattleEventId": 19810006,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_11",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconAttack.png",
"MEFHKNFOBCL": 122541111,
"PIIKKAIINOM": 122541011,
"CIPGDEMIEMF": 122541121,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle15.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254012,
"TrainingType": 2,
"BattleEventId": 19810012,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_12",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconStun.png",
"MEFHKNFOBCL": 122541141,
"PIIKKAIINOM": 122541061,
"CIPGDEMIEMF": 122541031,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle18.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254013,
"TrainingType": 2,
"BattleEventId": 19810003,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_13",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconAttack.png",
"MEFHKNFOBCL": 122541191,
"PIIKKAIINOM": 122541011,
"CIPGDEMIEMF": 122541131,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle21.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254014,
"TrainingType": 2,
"BattleEventId": 19810013,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_14",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconSupport.png",
"MEFHKNFOBCL": 122541211,
"PIIKKAIINOM": 122541011,
"CIPGDEMIEMF": 122541111,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle22.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254015,
"TrainingType": 2,
"BattleEventId": 19810014,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_15",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconAttack.png",
"MEFHKNFOBCL": 122541061,
"PIIKKAIINOM": 122541011,
"CIPGDEMIEMF": 122541081,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle09.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254016,
"TrainingType": 2,
"BattleEventId": 19810015,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_16",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconAttack.png",
"MEFHKNFOBCL": 122541041,
"PIIKKAIINOM": 122541011,
"CIPGDEMIEMF": 122541121,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle05.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254017,
"TrainingType": 2,
"BattleEventId": 19810004,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_17",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconAttack.png",
"MEFHKNFOBCL": 122541241,
"PIIKKAIINOM": 122541011,
"CIPGDEMIEMF": 122541031,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle23.png",
"DBIIAEFJHDA": "zhuyuan_release"
},
{
"Id": 12254018,
"TrainingType": 2,
"BattleEventId": 19810005,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_18",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconSupport.png",
"MEFHKNFOBCL": 122541131,
"PIIKKAIINOM": 122541081,
"CIPGDEMIEMF": 122541061,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle17.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254019,
"TrainingType": 2,
"BattleEventId": 19810016,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_19",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconSupport.png",
"MEFHKNFOBCL": 122541151,
"PIIKKAIINOM": 122541081,
"CIPGDEMIEMF": 122541121,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle27.png",
"DBIIAEFJHDA": ""
},
{
"Id": 12254021,
"TrainingType": 2,
"BattleEventId": 19810018,
"CFFJLDMDEML": "TrainingRoomTxt_SpecialTraining_Name_21",
"IIEMNPJFNJD": "UI/Sprite/A1DynamicLoad/TrainingCourse/UnPacker/TrainingSkill/IconAnomaly.png",
"MEFHKNFOBCL": 122541281,
"PIIKKAIINOM": 122541081,
"CIPGDEMIEMF": 122541151,
"LFLIIIIKBHJ": 0,
"HGJGIMKPHLH": false,
"OFALKONNAFN": false,
"LGGHKLFHOIL": "UI/Sprite/A1DynamicLoad/IconRoleCircle/UnPacker/IconRoleCircle28.png",
"DBIIAEFJHDA": ""
}
]

File diff suppressed because it is too large Load diff

1446
assets/VideoUSMEncKeys.json Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

33
nap_common/Cargo.toml Normal file
View file

@ -0,0 +1,33 @@
[package]
name = "common"
edition = "2021"
version.workspace = true
[dependencies]
# Serialization
serde.workspace = true
toml.workspace = true
# Cryptography
password-hash.workspace = true
pbkdf2.workspace = true
rsa.workspace = true
rand_mt.workspace = true
# Database
sqlx.workspace = true
# Util
thiserror.workspace = true
rand.workspace = true
byteorder.workspace = true
regex.workspace = true
ansi_term.workspace = true
# Logging
env_logger.workspace = true
tracing.workspace = true
tracing-futures.workspace = true
tracing-log.workspace = true
tracing-subscriber.workspace = true
tracing-bunyan-formatter.workspace = true

Binary file not shown.

View file

@ -0,0 +1,26 @@
CREATE TABLE t_sdk_account (
uid int primary key generated always as identity,
token varchar(64) NOT NULL,
username varchar(40) NOT NULL,
password varchar(256) NOT NULL,
UNIQUE(username)
);
CREATE TABLE t_combo_token (
account_uid varchar(32) primary key,
token varchar(64) NOT NULL,
device_id varchar(128) NOT NULL,
UNIQUE(account_uid, device_id)
);
CREATE TABLE t_user_uid (
uid int primary key generated always as identity,
account_uid varchar(64) NOT NULL,
ext varchar(512) NOT NULL DEFAULT '',
UNIQUE(account_uid)
);
CREATE TABLE t_player_data (
uid int primary key,
bin_data bytea NOT NULL
);

Binary file not shown.

View file

@ -0,0 +1,2 @@
pub const SERVER_VERSION: &str = "0.1.0";
pub const CLIENT_VERSION: &str = "CNBeta1.1.1";

View file

@ -0,0 +1,536 @@
#![allow(unused)]
pub const SHIFT_ROWS_TABLE: [u8; 16] = [0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11];
pub const SHIFT_ROWS_TABLE_INV: [u8; 16] = [0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3];
pub const LOOKUP_RCON: [u8; 16] = [
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
];
pub const LOOKUP_SBOX: [u8; 256] = [
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
];
pub const LOOKUP_SBOX_INV: [u8; 256] = [
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
];
pub const LOOKUP_G2: [u8; 256] = [
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25,
0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45,
0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85,
0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5,
0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5,
];
pub const LOOKUP_G3: [u8; 256] = [
0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71,
0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41,
0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1,
0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1,
0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1,
0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81,
0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a,
0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba,
0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea,
0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda,
0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a,
0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a,
0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a,
0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a,
];
pub const LOOKUP_G9: [u8; 256] = [
0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc,
0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01,
0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a,
0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa,
0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b,
0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0,
0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed,
0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d,
0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46,
];
pub const LOOKUP_G11: [u8; 256] = [
0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2,
0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f,
0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f,
0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4,
0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54,
0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e,
0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e,
0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5,
0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55,
0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68,
0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8,
0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13,
0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3,
];
pub const LOOKUP_G13: [u8; 256] = [
0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20,
0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26,
0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6,
0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d,
0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d,
0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91,
0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41,
0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a,
0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa,
0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc,
0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c,
0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47,
0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97,
];
pub const LOOKUP_G14: [u8; 256] = [
0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61,
0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7,
0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17,
0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c,
0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc,
0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b,
0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb,
0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0,
0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20,
0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6,
0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56,
0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d,
0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d,
];
pub const KEY_XORPAD_TABLE: [u8; 16] = [
0xA2, 0x25, 0x25, 0x99, 0xB7, 0x62, 0xF4, 0x39, 0x28, 0xE1, 0xB7, 0x73, 0x91, 0x05, 0x25, 0x87,
];
pub const AES_XORPAD_TABLE: [[u8; 2816]; 2] = [
[
0xDE, 0xAD, 0xCA, 0xFE, 0xFA, 0xCE, 0xB0, 0x0C, 0xDE, 0xAD, 0xCA, 0xFE, 0xFA, 0xCE, 0xB0,
0x0C, 0x3A, 0xE6, 0xDE, 0x9C, 0x81, 0xBA, 0x7C, 0xC6, 0x12, 0x1B, 0xAF, 0xD2, 0x8A, 0xBA,
0xF5, 0xE6, 0x41, 0xDF, 0x71, 0xBA, 0x37, 0x11, 0x50, 0xF3, 0xF3, 0x62, 0x6E, 0x04, 0xF1,
0x14, 0xFC, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x7B, 0x52, 0x7C,
0x19, 0x98, 0x35, 0x96, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0,
0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6,
0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81,
0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A,
0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A,
0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB,
0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A,
0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1,
0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA,
0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE,
0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0,
0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B,
0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC,
0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71,
0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25,
0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E,
0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76,
0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18,
0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E,
0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69,
0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47,
0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62,
0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8,
0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0,
0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3,
0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E,
0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48,
0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33,
0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78,
0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B,
0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E,
0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71,
0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53,
0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA,
0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18,
0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5,
0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0,
0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82,
0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73,
0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2,
0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE,
0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7,
0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F,
0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C,
0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2,
0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E,
0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF,
0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC,
0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E,
0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00,
0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8,
0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92,
0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35,
0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93,
0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67,
0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C,
0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC,
0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6,
0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2,
0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56,
0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37,
0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A,
0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA,
0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9,
0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0,
0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6,
0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81,
0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A,
0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A,
0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB,
0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A,
0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1,
0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA,
0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE,
0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0,
0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B,
0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC,
0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71,
0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25,
0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E,
0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76,
0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18,
0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E,
0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69,
0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47,
0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62,
0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8,
0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0,
0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3,
0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E,
0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48,
0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33,
0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78,
0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B,
0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E,
0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71,
0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53,
0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA,
0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18,
0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5,
0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0,
0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82,
0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73,
0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2,
0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE,
0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7,
0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F,
0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C,
0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2,
0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E,
0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF,
0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC,
0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E,
0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00,
0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8,
0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92,
0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35,
0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93,
0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67,
0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C,
0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC,
0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6,
0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2,
0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56,
0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37,
0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A,
0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA,
0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9,
0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0,
0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6,
0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81,
0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A,
0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A,
0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB,
0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A,
0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1,
0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA,
0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE,
0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0,
0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B,
0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC,
0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71,
0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25,
0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E,
0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76,
0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18,
0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E,
0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69,
0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47,
0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62,
0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8,
0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0,
0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3,
0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E,
0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48,
0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33,
0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78,
0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B,
0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E,
0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71,
0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53,
0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA,
0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18,
0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5,
0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0,
0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82,
0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73,
0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2,
0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE,
0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7,
0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F,
0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C,
0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2,
0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E,
0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF,
0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92, 0x18, 0x18, 0xE0, 0xAC,
0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35, 0xB5, 0x0E, 0xB6, 0x3E,
0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93, 0xD0, 0x69, 0x81, 0x00,
0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67, 0x82, 0x47, 0x6A, 0xB8,
0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56, 0x1F, 0x8E, 0xFA, 0x3C, 0x73, 0x62, 0x6A, 0x92,
0x18, 0x18, 0xE0, 0xAC, 0x9E, 0x71, 0x37, 0x3C, 0x48, 0xDE, 0xDC, 0xA2, 0xE8, 0xBB, 0x35,
0xB5, 0x0E, 0xB6, 0x3E, 0x71, 0x25, 0x8A, 0xF2, 0x33, 0xE0, 0xD6, 0xBE, 0xF0, 0x3A, 0x93,
0xD0, 0x69, 0x81, 0x00, 0x53, 0x7E, 0xCA, 0x4E, 0x78, 0x4B, 0xA2, 0xF7, 0xB3, 0xF1, 0x67,
0x82, 0x47, 0x6A, 0xB8, 0xAA, 0x76, 0xF9, 0xDF, 0x3B, 0xEC, 0x56,
],
[
0xC3, 0x20, 0x20, 0xB4, 0xAF, 0x0E, 0x82, 0x2E, 0xEF, 0x29, 0xFE, 0x75, 0x1D, 0xDB, 0x4B,
0x86, 0x86, 0x23, 0x28, 0x72, 0xA3, 0xF4, 0x1B, 0x4F, 0x5F, 0x0E, 0x02, 0xB1, 0xAC, 0x0D,
0xE6, 0x4F, 0x8B, 0x0B, 0x3F, 0xF3, 0x5F, 0xB5, 0x09, 0x7E, 0x3B, 0xE9, 0x93, 0x29, 0x55,
0xE1, 0xB4, 0x9B, 0xCC, 0xCE, 0x37, 0xFC, 0xAB, 0x6B, 0xA4, 0x05, 0xE6, 0xC7, 0x45, 0x34,
0xC0, 0xFF, 0x7C, 0x24, 0x89, 0x36, 0xBF, 0x17, 0xAB, 0x91, 0xCA, 0x49, 0xF2, 0x74, 0x80,
0xB6, 0x90, 0x60, 0xFF, 0xD2, 0xA9, 0xE5, 0xC9, 0x64, 0xBC, 0x38, 0x40, 0x98, 0xB3, 0xBA,
0x8F, 0x8B, 0xBA, 0x9D, 0xF3, 0xCF, 0x57, 0xBA, 0xAC, 0x18, 0xE7, 0xD3, 0x03, 0x01, 0x48,
0x29, 0x41, 0xF6, 0x2F, 0x89, 0xD4, 0x9F, 0xD7, 0xD3, 0x05, 0x71, 0x63, 0x30, 0x4E, 0xBB,
0xF7, 0xB0, 0x99, 0xFF, 0x43, 0xDA, 0x87, 0xCA, 0xA7, 0x48, 0x92, 0x9E, 0x76, 0xA6, 0xEE,
0x48, 0x1C, 0x96, 0x28, 0x8E, 0x54, 0x30, 0xD6, 0xA5, 0xD3, 0x22, 0xA2, 0x30, 0xCB, 0x6A,
0x85, 0x26, 0x69, 0xE1, 0x7C, 0xEC, 0xDC, 0xD4, 0x89, 0x2A, 0xB8, 0xAE, 0xDF, 0x12, 0x6E,
0x39, 0x8A, 0x9B, 0x48, 0x61, 0xF9, 0x4B, 0x34, 0xD0, 0xF1, 0x60, 0x87, 0xBA, 0x88, 0x86,
0x68, 0x8C, 0xBE, 0xC1, 0x9C, 0xAE, 0x30, 0xBC, 0xE6, 0x62, 0xFF, 0xEB, 0xBB, 0x88, 0x7C,
0xD2, 0xBB, 0x57, 0xB4, 0x02, 0x82, 0x06, 0x72, 0xD2, 0x94, 0x60, 0x86, 0x4A, 0x29, 0xF0,
0xEA, 0xD3, 0x88, 0x92, 0xF1, 0x22, 0xD1, 0x5C, 0x88, 0x65, 0xE6, 0xFB, 0xEE, 0x28, 0x79,
0x86, 0x68, 0x7D, 0xA6, 0x5A, 0xBF, 0xBD, 0x7D, 0x15, 0xEF, 0x05, 0xF6, 0xF9, 0xE0, 0x11,
0xD6, 0x30, 0x94, 0xF2, 0x6C, 0x3D, 0x0A, 0xDB, 0xC5, 0x0E, 0xDC, 0xF2, 0xFD, 0x1F, 0x61,
0x91, 0x5D, 0x80, 0x69, 0xA3, 0xDB, 0x35, 0x98, 0x4E, 0x4A, 0xC1, 0x49, 0x76, 0xAB, 0xC0,
0x67, 0x36, 0x3F, 0xA4, 0xC6, 0xE8, 0xCA, 0x25, 0x44, 0x63, 0x23, 0xB5, 0xC8, 0xBB, 0x3A,
0xAC, 0xA1, 0x09, 0xC3, 0x10, 0x57, 0xA5, 0x5B, 0x3B, 0x33, 0x21, 0xCD, 0x3C, 0x88, 0xAE,
0x1E, 0x8F, 0xC1, 0xD6, 0xFB, 0x94, 0x61, 0x38, 0xAB, 0xF1, 0x9C, 0x06, 0xCB, 0x89, 0x58,
0x9A, 0xF4, 0xF4, 0x33, 0x80, 0x66, 0x13, 0xC0, 0xFD, 0xE2, 0x16, 0xE0, 0x89, 0x65, 0xE2,
0xC1, 0xA6, 0xE3, 0x74, 0xD2, 0x5F, 0xA0, 0x76, 0xAD, 0xF5, 0x6B, 0x4F, 0xE0, 0xF7, 0x52,
0xB0, 0xB1, 0x48, 0xDD, 0xEE, 0xB6, 0x01, 0x9A, 0x90, 0x91, 0x18, 0xEC, 0xCB, 0xCB, 0xAD,
0x04, 0xB6, 0x73, 0xCF, 0x7F, 0xF3, 0xAC, 0xBE, 0xEC, 0x91, 0x44, 0x56, 0x81, 0xB8, 0x74,
0xAE, 0x28, 0x5D, 0xC7, 0x5C, 0xAB, 0x8B, 0x56, 0x21, 0x32, 0x91, 0xB9, 0x9E, 0x70, 0xF6,
0x9B, 0xAC, 0x50, 0x0B, 0x2E, 0x4B, 0x8B, 0xA2, 0xA5, 0x24, 0x5B, 0x91, 0xDF, 0x24, 0xA7,
0xB0, 0x79, 0xA7, 0x16, 0x54, 0x44, 0x2E, 0xBC, 0x48, 0xCD, 0x87, 0xBA, 0xAF, 0xD4, 0xB9,
0x1C, 0x0F, 0xAA, 0xFA, 0x3A, 0x3F, 0x3A, 0x3D, 0x68, 0x5A, 0xAE, 0xAC, 0xBA, 0xBE, 0xA3,
0x92, 0x6E, 0x38, 0x8E, 0x33, 0x3E, 0x0A, 0xCC, 0xF6, 0xE3, 0x26, 0x57, 0xEC, 0x8E, 0x63,
0x31, 0x27, 0xBA, 0x20, 0x4E, 0x7F, 0x34, 0xE5, 0x19, 0xFE, 0x7F, 0xA6, 0x97, 0x90, 0xD6,
0x29, 0x1C, 0x3F, 0x8C, 0x3F, 0x81, 0x62, 0x3D, 0xF5, 0x00, 0xD4, 0xC5, 0xE2, 0xE1, 0x42,
0x42, 0x8C, 0x65, 0x8F, 0x5A, 0x66, 0x59, 0xE1, 0xDD, 0xEC, 0xDC, 0x1B, 0x4E, 0x63, 0x82,
0xFF, 0x02, 0x9D, 0x53, 0xDE, 0xBD, 0xB4, 0x80, 0xCF, 0x2B, 0xB7, 0xDE, 0x69, 0x5D, 0x1B,
0xCA, 0xFB, 0xB3, 0xF9, 0xBE, 0xD0, 0xF5, 0x79, 0x86, 0x2F, 0x0E, 0xB6, 0xA9, 0x87, 0xF4,
0x68, 0xC1, 0xBF, 0x4F, 0xB8, 0xA6, 0x2D, 0x03, 0xA9, 0x72, 0x04, 0xCA, 0x37, 0x6D, 0x1B,
0x90, 0xDD, 0xBC, 0x52, 0xAE, 0xF3, 0xFF, 0x08, 0xDD, 0x4B, 0x46, 0xD0, 0xCD, 0xB1, 0x8A,
0x35, 0x9A, 0x02, 0x64, 0x64, 0x2F, 0x57, 0xA5, 0x7B, 0x9A, 0x0D, 0x2B, 0x55, 0x11, 0x3C,
0xC0, 0x35, 0x74, 0x69, 0xD9, 0x7B, 0x43, 0x1D, 0xAC, 0xB2, 0xC2, 0x8A, 0xBE, 0x22, 0x45,
0x46, 0x76, 0xA9, 0x8A, 0x49, 0xB2, 0x5F, 0xC0, 0xB8, 0xBC, 0xCD, 0x27, 0xF8, 0x14, 0xB2,
0xA9, 0x6D, 0x5A, 0x1F, 0xA4, 0x43, 0x1E, 0x0F, 0xDB, 0xA4, 0x9E, 0x2B, 0xCA, 0xFC, 0x98,
0x7F, 0xF1, 0x18, 0x87, 0x5B, 0x11, 0x2D, 0xC5, 0xE4, 0x91, 0x20, 0xA9, 0x6A, 0x2D, 0xAC,
0xA8, 0xFA, 0x94, 0x57, 0x7F, 0x30, 0x73, 0x08, 0xE8, 0x49, 0xF0, 0xC8, 0x63, 0xDA, 0x83,
0x87, 0x2A, 0xC3, 0x31, 0x1A, 0xFC, 0xB7, 0x57, 0xB2, 0x40, 0x46, 0x09, 0x6D, 0x84, 0xB4,
0x66, 0xF1, 0x13, 0x16, 0x3A, 0x3A, 0xFB, 0xC6, 0x6E, 0xB0, 0x71, 0xB8, 0x23, 0x74, 0x22,
0x89, 0xFC, 0xBE, 0x34, 0xB3, 0x17, 0xB6, 0xC9, 0x68, 0x53, 0x64, 0x47, 0xAF, 0xCA, 0x1D,
0x5F, 0xB4, 0x74, 0xA3, 0x77, 0xB5, 0xFB, 0x77, 0xD9, 0x69, 0x2B, 0x3A, 0xAA, 0xAE, 0xE4,
0x03, 0x81, 0x6B, 0x3A, 0x35, 0x9C, 0x45, 0x50, 0x9C, 0x76, 0xCE, 0xE3, 0x7F, 0x64, 0x4B,
0x9F, 0x83, 0x7B, 0x72, 0xBC, 0x02, 0x1E, 0x94, 0x99, 0xC1, 0x1C, 0x45, 0x19, 0x1D, 0x56,
0x74, 0x73, 0xE7, 0xFC, 0x58, 0x72, 0x2D, 0xE3, 0x50, 0xA4, 0x21, 0xBE, 0x81, 0xDF, 0x80,
0xDA, 0x40, 0xDB, 0x79, 0x67, 0x0E, 0x94, 0xA3, 0x05, 0xDD, 0xF7, 0x14, 0x28, 0xD6, 0xC4,
0x2B, 0xF3, 0xCF, 0x36, 0x08, 0x84, 0xF3, 0xC8, 0x8C, 0xAD, 0xCE, 0x7F, 0x7C, 0x0F, 0xC6,
0xFE, 0x05, 0x54, 0x4B, 0x17, 0xA1, 0x83, 0x65, 0x97, 0x29, 0x01, 0x70, 0xC1, 0x16, 0xAE,
0x69, 0xA4, 0x90, 0xB9, 0xBE, 0x17, 0x05, 0x50, 0xF1, 0x65, 0x07, 0x23, 0x76, 0x64, 0x84,
0x2D, 0x40, 0x34, 0xFD, 0xDF, 0x62, 0x7E, 0x4C, 0x85, 0xD2, 0x6D, 0x17, 0xE1, 0x41, 0x12,
0xC6, 0x3E, 0xD6, 0x14, 0xB8, 0x5F, 0x8F, 0x39, 0x65, 0xC2, 0x62, 0x21, 0x06, 0x5C, 0xC9,
0xB8, 0x99, 0xA5, 0x00, 0xE3, 0x9C, 0x73, 0xAB, 0xB9, 0x76, 0x12, 0xD2, 0xFA, 0x7F, 0x7D,
0x64, 0x63, 0x9E, 0x26, 0xAA, 0x89, 0x85, 0x3A, 0xC9, 0x94, 0x04, 0x97, 0xEC, 0xFD, 0xC5,
0xA3, 0xB1, 0x7D, 0xD6, 0x07, 0x9C, 0x47, 0x30, 0x9C, 0x64, 0x97, 0x0E, 0xC6, 0xFC, 0x0B,
0xFF, 0xA7, 0xF9, 0x46, 0x5B, 0x2B, 0xDB, 0x9E, 0x1C, 0x85, 0x3A, 0x75, 0xD6, 0xEB, 0x9B,
0x15, 0x36, 0xD7, 0x1A, 0x3D, 0xFC, 0x0B, 0x75, 0x08, 0x5E, 0x32, 0x23, 0xE0, 0xA5, 0xAD,
0x0F, 0x45, 0xB3, 0x78, 0x20, 0x22, 0x24, 0x64, 0x0C, 0xCF, 0xD6, 0x3C, 0xA4, 0x48, 0xC7,
0xB3, 0x6E, 0x02, 0xE2, 0x0A, 0xAB, 0x92, 0xFC, 0x40, 0x7D, 0xF5, 0x02, 0x61, 0x56, 0xAB,
0xC5, 0x68, 0x38, 0xE0, 0x01, 0xF1, 0x94, 0x73, 0xC6, 0xFE, 0xC2, 0x34, 0x67, 0x8E, 0xB1,
0x73, 0x72, 0xD4, 0x3B, 0xFD, 0x1F, 0xE2, 0xA8, 0xED, 0x20, 0x14, 0x0A, 0x60, 0x6D, 0xD1,
0x85, 0x14, 0x05, 0x54, 0x96, 0xC6, 0x3D, 0xB5, 0x1B, 0x37, 0x56, 0x24, 0xF7, 0x7C, 0x0F,
0x55, 0xC6, 0xAA, 0x7E, 0x33, 0x2D, 0xE1, 0x97, 0x74, 0xA8, 0xDC, 0xC5, 0xA1, 0xEC, 0x8C,
0xEF, 0x28, 0x3B, 0x49, 0x8B, 0x00, 0xED, 0x8B, 0xD9, 0xE9, 0x65, 0xD5, 0x05, 0x7B, 0x6D,
0x20, 0xCA, 0x8F, 0x93, 0xB4, 0xCA, 0x36, 0x34, 0x8E, 0x16, 0x46, 0xCE, 0x02, 0x23, 0x43,
0x22, 0xF6, 0xBD, 0x10, 0xCC, 0xD0, 0xA3, 0xB0, 0x42, 0xA5, 0xAF, 0x59, 0x72, 0x97, 0x0B,
0xAE, 0x80, 0x8D, 0x19, 0xD0, 0x1D, 0x7D, 0x30, 0x4E, 0x5B, 0x46, 0xC0, 0xC2, 0x5C, 0x40,
0xFC, 0xF3, 0xEF, 0x05, 0x84, 0xE8, 0x0C, 0x80, 0xD7, 0x37, 0xA1, 0x6F, 0xC1, 0x8C, 0xE0,
0xBA, 0xA1, 0x88, 0x7B, 0xE7, 0x20, 0xBF, 0x18, 0x02, 0x40, 0x9F, 0x6F, 0x23, 0x11, 0x78,
0x07, 0xD0, 0x92, 0x87, 0x2D, 0xB5, 0xE0, 0xE9, 0xE9, 0xAA, 0x32, 0x88, 0x57, 0xF8, 0x9B,
0x01, 0x93, 0x2D, 0x07, 0x77, 0x68, 0x86, 0xAD, 0x06, 0xDE, 0x57, 0xA9, 0xA4, 0x96, 0x33,
0x42, 0xF8, 0xFB, 0x23, 0x1F, 0x99, 0xB6, 0x62, 0x93, 0x6B, 0x12, 0xBE, 0x72, 0x9F, 0x96,
0x1A, 0xDA, 0x05, 0x60, 0xF1, 0xD5, 0x40, 0x9F, 0x75, 0xF3, 0x1D, 0xBE, 0xD7, 0x87, 0x5D,
0x3A, 0x55, 0xF0, 0x9B, 0xBF, 0xE8, 0xB9, 0x72, 0xC2, 0xDD, 0x4D, 0x27, 0xF6, 0xA9, 0x37,
0x96, 0x7E, 0x6E, 0x6E, 0x64, 0x37, 0x4E, 0x2E, 0x3F, 0xFD, 0x3C, 0xF4, 0xA6, 0xF5, 0x22,
0xA8, 0x43, 0xF4, 0x13, 0x21, 0xB5, 0x4E, 0xA8, 0x6D, 0x50, 0x0A, 0xB3, 0xFE, 0x9F, 0x5C,
0xE7, 0x1A, 0xCF, 0x36, 0x42, 0x30, 0x1C, 0x88, 0x7F, 0x29, 0xE9, 0xCD, 0x96, 0xF2, 0x6A,
0x52, 0xB2, 0x25, 0x87, 0x63, 0xDC, 0xFC, 0x72, 0xE4, 0xF8, 0x5E, 0xB1, 0x97, 0xB4, 0x1E,
0x08, 0x90, 0x68, 0x10, 0x73, 0x7F, 0x94, 0x61, 0x48, 0x49, 0x36, 0x9B, 0x7D, 0xBD, 0xDF,
0xCD, 0xB1, 0xA3, 0x7D, 0xFB, 0xDD, 0x97, 0x8A, 0x0D, 0xFC, 0x9A, 0xB8, 0xA9, 0x33, 0xB5,
0x4E, 0x50, 0x3D, 0x60, 0x90, 0xEB, 0xAB, 0xB8, 0xCB, 0x6E, 0x32, 0xE4, 0x6B, 0xB0, 0x3F,
0x57, 0xB8, 0xA4, 0x6A, 0x7C, 0x00, 0x66, 0x39, 0xB1, 0x22, 0xE2, 0x04, 0x26, 0xA1, 0x5A,
0x17, 0xAA, 0x80, 0xB6, 0xC0, 0xF6, 0xCF, 0x7A, 0xF8, 0x60, 0xE9, 0x52, 0xB8, 0x0E, 0x08,
0xC0, 0xD5, 0x1F, 0xAB, 0x61, 0x62, 0x1A, 0x83, 0xD1, 0x92, 0xE1, 0x4D, 0x6D, 0xDF, 0x27,
0x0E, 0xFF, 0xF9, 0xA3, 0x36, 0xFF, 0x73, 0xEF, 0x1D, 0xAB, 0xAC, 0xBF, 0xA7, 0xB3, 0x29,
0xD2, 0xB2, 0x37, 0xAB, 0x08, 0x7D, 0xB6, 0x7E, 0x0D, 0x25, 0xAA, 0x49, 0x29, 0x9F, 0x61,
0x52, 0x44, 0x19, 0x1C, 0x51, 0x95, 0x74, 0xB9, 0x3D, 0xDD, 0x95, 0x2C, 0x4F, 0x30, 0x56,
0xC9, 0xEF, 0x3D, 0x87, 0x90, 0x1E, 0xF8, 0x69, 0xFF, 0x37, 0x06, 0x27, 0xDB, 0x72, 0x82,
0x2C, 0xDE, 0xB8, 0x39, 0x0B, 0x78, 0xB1, 0x1F, 0x37, 0x54, 0xBF, 0x21, 0x32, 0x87, 0xB4,
0xD9, 0x49, 0x2D, 0x29, 0x19, 0x43, 0x01, 0xD4, 0xC0, 0xA3, 0xFF, 0x09, 0x6F, 0x69, 0xC8,
0x5D, 0x35, 0x1D, 0x10, 0x09, 0x91, 0xB6, 0x12, 0xEC, 0x04, 0xA6, 0x61, 0xEF, 0x73, 0xC7,
0x4C, 0x04, 0x8E, 0x3E, 0xAE, 0xD7, 0xC2, 0x84, 0x48, 0xAB, 0x99, 0x96, 0x75, 0xD8, 0xAD,
0xA7, 0x5B, 0xDE, 0x72, 0x44, 0x96, 0xC5, 0xB3, 0xEB, 0x8E, 0xED, 0xD6, 0x69, 0x81, 0xE6,
0x07, 0x3A, 0x15, 0x0D, 0x66, 0x5F, 0x36, 0xA9, 0xAB, 0x53, 0x82, 0x47, 0x98, 0x27, 0xF2,
0x16, 0x95, 0x05, 0x0B, 0xAE, 0xF1, 0x04, 0x92, 0x80, 0x20, 0xA4, 0x9B, 0x43, 0x66, 0x70,
0x7F, 0x45, 0x0B, 0x4B, 0x85, 0x95, 0x10, 0x09, 0xC8, 0xD9, 0xF9, 0x5D, 0x40, 0x6D, 0x07,
0x69, 0x18, 0xF3, 0xD6, 0x98, 0x61, 0x25, 0x8E, 0xA1, 0xE2, 0x24, 0xBD, 0xF0, 0xFA, 0x89,
0xD8, 0x68, 0xB2, 0x03, 0x81, 0x63, 0xF9, 0x42, 0xD4, 0x1A, 0xD9, 0x4D, 0xCD, 0x30, 0x36,
0x2D, 0xB1, 0x63, 0xFC, 0xA3, 0x2B, 0xA7, 0x07, 0x50, 0xBC, 0x67, 0xAB, 0x7D, 0x33, 0x1D,
0xEC, 0x62, 0xFE, 0xD2, 0x65, 0xAA, 0xBA, 0x37, 0xC9, 0x7F, 0x67, 0x26, 0x9D, 0x8A, 0x8B,
0x63, 0x0B, 0xE0, 0x30, 0x65, 0x07, 0x8C, 0xF3, 0xD1, 0xCF, 0x0D, 0xB4, 0x1E, 0xF3, 0x29,
0xBE, 0x43, 0x1F, 0x34, 0x1E, 0x52, 0x02, 0xA7, 0x8D, 0x30, 0x2E, 0x3E, 0x39, 0x00, 0xB6,
0x7B, 0x5C, 0x29, 0x39, 0xC0, 0x0D, 0xAB, 0xA0, 0x6D, 0x77, 0x3C, 0xB2, 0x18, 0x42, 0x57,
0x63, 0xDA, 0x9E, 0xF5, 0xE0, 0x42, 0x43, 0xF6, 0x50, 0xFD, 0x71, 0x9B, 0x30, 0xE0, 0x92,
0x8B, 0xD1, 0xE1, 0xC4, 0x96, 0xC9, 0xF5, 0x14, 0xB6, 0xF7, 0xA5, 0x10, 0x77, 0xF4, 0xF9,
0xAC, 0xDC, 0x45, 0xE1, 0x3C, 0xD6, 0x0B, 0xA5, 0xE2, 0x58, 0x01, 0x19, 0x39, 0x14, 0x68,
0x96, 0xC0, 0xCE, 0xA9, 0xDE, 0x84, 0x22, 0x59, 0x87, 0x70, 0xFD, 0x8A, 0x71, 0x64, 0x79,
0x16, 0x37, 0x80, 0x83, 0xFD, 0x9C, 0x73, 0xE6, 0x9C, 0x8B, 0xCD, 0xC0, 0x69, 0x66, 0x90,
0x45, 0x0A, 0xC9, 0x81, 0x4A, 0xDA, 0x26, 0xDA, 0xA1, 0x70, 0x03, 0x6C, 0x36, 0x9D, 0xAD,
0xD7, 0xE2, 0x1F, 0x27, 0xBE, 0xBB, 0xEC, 0x63, 0xD9, 0xC2, 0x2A, 0x56, 0x4D, 0x63, 0xCD,
0x92, 0xEE, 0xAF, 0xCA, 0xD0, 0x11, 0x35, 0x2F, 0x1D, 0xF1, 0x96, 0xD1, 0xAA, 0xDC, 0xF6,
0x14, 0x3F, 0xA0, 0xEE, 0x90, 0x83, 0x9F, 0x42, 0x40, 0xE6, 0x2C, 0x10, 0x23, 0x00, 0x23,
0x18, 0x8C, 0xA1, 0x26, 0x4B, 0x22, 0xE1, 0x36, 0x07, 0x55, 0xCB, 0xC3, 0xD2, 0xDD, 0x12,
0x58, 0x19, 0x75, 0x03, 0xC6, 0xD8, 0x2E, 0xCE, 0x87, 0x1C, 0xC3, 0x15, 0x44, 0x2A, 0x30,
0x00, 0x52, 0x39, 0x31, 0x13, 0xF4, 0x25, 0x75, 0x74, 0x15, 0x6C, 0xC5, 0xC1, 0xD2, 0x33,
0x75, 0xC2, 0x41, 0x22, 0x28, 0x95, 0xDF, 0x97, 0x6C, 0x31, 0xF8, 0x35, 0xA6, 0x54, 0x29,
0x5C, 0xF4, 0x20, 0x97, 0x69, 0xE5, 0x46, 0xFF, 0x34, 0x24, 0x73, 0x12, 0xB8, 0x61, 0x25,
0x46, 0xB3, 0x8F, 0xBA, 0x3C, 0xFA, 0x06, 0xFF, 0x3F, 0x66, 0x9D, 0x22, 0x55, 0x46, 0x2F,
0xFF, 0x44, 0xDB, 0x25, 0x29, 0xE0, 0x16, 0x6E, 0xEC, 0x87, 0x97, 0x92, 0x37, 0x23, 0x0E,
0x52, 0x4E, 0xBB, 0x10, 0xBB, 0x1C, 0x73, 0x75, 0xD1, 0x31, 0xC3, 0xAD, 0xFE, 0xB8, 0x12,
0x50, 0xA0, 0x69, 0x91, 0x36, 0xEA, 0x5F, 0x0D, 0xEC, 0x1A, 0x23, 0x4A, 0x7D, 0x94, 0x84,
0xC8, 0x4A, 0x58, 0x6A, 0xA1, 0xA3, 0x75, 0xCA, 0x85, 0xE7, 0x96, 0x91, 0x07, 0x05, 0x3A,
0x57, 0x61, 0x6A, 0x6F, 0xF1, 0xEF, 0xF7, 0xB3, 0xB1, 0x09, 0xB8, 0x91, 0xA8, 0xF9, 0x57,
0xB8, 0x63, 0x95, 0xFF, 0xB4, 0x1C, 0x96, 0xE7, 0xE5, 0xEC, 0x06, 0x3A, 0x11, 0xE6, 0x81,
0xAB, 0x23, 0xE4, 0x5E, 0x5A, 0xB6, 0x6B, 0x69, 0x62, 0x6F, 0x9D, 0xC4, 0x08, 0x6F, 0xA6,
0xBE, 0x4D, 0x09, 0x12, 0x77, 0xCA, 0xDD, 0xB5, 0x2D, 0x66, 0xCB, 0x4F, 0x4F, 0x11, 0xF2,
0x3A, 0x1A, 0x97, 0x1F, 0xFE, 0x50, 0x2F, 0x19, 0x32, 0x05, 0x45, 0xA0, 0x50, 0x60, 0x58,
0x40, 0x40, 0x3D, 0xF6, 0xC3, 0x6F, 0x07, 0xC8, 0x26, 0x26, 0x0E, 0x42, 0x22, 0x96, 0x6D,
0xFE, 0x95, 0x53, 0x70, 0xDC, 0x92, 0x12, 0x63, 0xFD, 0xA3, 0x7D, 0x6E, 0x44, 0xCD, 0x11,
0x2C, 0x51, 0x6F, 0xBC, 0x50, 0xFC, 0x1C, 0xC8, 0x3A, 0x28, 0xF5, 0x39, 0xF8, 0x8C, 0x60,
0x5D, 0xA5, 0x4A, 0xFA, 0xAB, 0x04, 0x7F, 0x34, 0x91, 0x53, 0xE7, 0x6C, 0x56, 0xC6, 0x14,
0xE4, 0xCC, 0xE4, 0xBB, 0x6E, 0x47, 0x7A, 0x46, 0x6B, 0xE2, 0x88, 0xA0, 0xBD, 0xBD, 0xCC,
0x51, 0xF3, 0x37, 0x4B, 0xB3, 0xA0, 0x19, 0x92, 0x48, 0x35, 0xBB, 0xBC, 0x79, 0x78, 0xFF,
0x49, 0xC1, 0x2B, 0x93, 0xDF, 0x75, 0xA7, 0xFB, 0x94, 0x89, 0xAF, 0x50, 0x5E, 0x2D, 0xE1,
0x78, 0x60, 0x0C, 0xDF, 0xF8, 0x7C, 0xFD, 0xCD, 0x2D, 0xE2, 0xFF, 0xD3, 0xA3, 0x4A, 0x48,
0x0D, 0x40, 0x8F, 0x03, 0x4F, 0x2C, 0xBD, 0xFA, 0x2E, 0x16, 0xC3, 0xD4, 0xFD, 0x0B, 0xB3,
0xBD, 0x4F, 0x30, 0xAD, 0xD0, 0xAE, 0xCA, 0x77, 0x9D, 0xDD, 0x3D, 0xA3, 0x66, 0xD0, 0xC1,
0x6D, 0xCC, 0x3B, 0x56, 0x81, 0x5D, 0x80, 0x07, 0xD8, 0x84, 0x46, 0x71, 0x40, 0x57, 0xB3,
0x44, 0x85, 0x63, 0x4E, 0x17, 0x2C, 0xB0, 0x21, 0x98, 0x43, 0x42, 0x04, 0x18, 0x84, 0xFA,
0xB1, 0xD7, 0xC5, 0x5C, 0xCA, 0x25, 0x8B, 0x1A, 0x7A, 0x50, 0x60, 0x68, 0x4A, 0x30, 0xEA,
0xE6, 0xDE, 0x19, 0xBB, 0x9F, 0x47, 0xEF, 0xDB, 0xC5, 0x81, 0x72, 0xF0, 0x8D, 0xBA, 0x74,
0x3A, 0xD1, 0xD5, 0xC6, 0xD1, 0xE0, 0xAE, 0x28, 0x2A, 0x65, 0xE5, 0x0B, 0x09, 0xFA, 0xEA,
0x5B, 0xA6, 0xDB, 0x38, 0xD8, 0x67, 0xC0, 0xBE, 0xA1, 0x12, 0x1C, 0x03, 0xB1, 0x81, 0xB8,
0x95, 0xDD, 0x78, 0xF8, 0x16, 0x6E, 0xAB, 0xBB, 0xAA, 0x33, 0x54, 0x0E, 0x39, 0x83, 0x24,
0x17, 0xB3, 0x0B, 0x3C, 0xA1, 0x62, 0x21, 0xB2, 0xA0, 0xF8, 0x49, 0xAB, 0x8B, 0x80, 0xC6,
0x3D, 0xF1, 0x2E, 0x18, 0x44, 0x74, 0x5F, 0x98, 0x92, 0x33, 0xFB, 0xB2, 0x52, 0x6B, 0x97,
0xE9, 0x48, 0x12, 0x91, 0x32, 0x50, 0x21, 0x75, 0x74, 0x69, 0x88, 0x54, 0xC6, 0xF3, 0xC9,
0x37, 0x3C, 0xB3, 0x89, 0xAB, 0x33, 0x1F, 0x79, 0x57, 0xF7, 0xE4, 0xB5, 0x87, 0x0C, 0xA4,
0x99, 0x48, 0x89, 0x63, 0x5F, 0x72, 0xA1, 0xBC, 0xFF, 0xFE, 0xF8, 0xB3, 0xF1, 0x00, 0xE4,
0xD4, 0x01, 0x9B, 0xB7, 0x2E, 0x4F, 0xA0, 0x90, 0xE4, 0x9B, 0x6A, 0xA8, 0xBA, 0xE1, 0xD3,
0xD5, 0xBC, 0xEB, 0xC5, 0xB2, 0x89, 0xB4, 0xE9, 0x4D, 0x3F, 0x4C, 0xFA, 0x8C, 0xCB, 0xCD,
0x22, 0x08, 0xB8, 0xC7, 0xB3, 0xA3, 0xED, 0x6B, 0xAC, 0xF3, 0x2D, 0x98, 0x70, 0x41, 0x47,
0x85, 0xE8, 0x6E, 0x31, 0x0A, 0xC2, 0x3E, 0x51, 0x39, 0x55, 0xF8, 0x4A, 0xE9, 0x48, 0x64,
0x01, 0xDB, 0x8D, 0xE3, 0xAF, 0xA4, 0xB9, 0xD8, 0x19, 0xCA, 0x86, 0xCA, 0xA1, 0x6C, 0x1C,
0x12, 0x3D, 0xA1, 0x02, 0x23, 0x1D, 0x29, 0x5D, 0x94, 0x04, 0xC6, 0x51, 0x01, 0x40, 0x0B,
0xB3, 0x69, 0x25, 0x45, 0xEF, 0x43, 0x81, 0x4F, 0x97, 0x57, 0x0D, 0xA1, 0xA5, 0xC9, 0x9D,
0xE6, 0x56, 0xB9, 0x38, 0x93, 0xA1, 0x78, 0xC5, 0xBF, 0x75, 0xFE, 0x81, 0x6A, 0x35, 0x64,
0x89, 0x64, 0x43, 0x75, 0xFD, 0x29, 0x63, 0xD1, 0x15, 0xAB, 0x43, 0x60, 0x65, 0xDC, 0x98,
0xD5, 0xC7, 0x6E, 0xF9, 0xB2, 0x38, 0xFB, 0x6E, 0xB0, 0x34, 0x9C, 0xA3, 0x73, 0x61, 0xF5,
0x51, 0xFF, 0x1F, 0xCE, 0xB0, 0x08, 0x83, 0x29, 0xB3, 0x82, 0x07, 0xFA, 0xC4, 0xE5, 0x21,
0xD3, 0xA0, 0xD4, 0xC0, 0xF8, 0x1A, 0x65, 0x9B, 0x35, 0x7A, 0xE3, 0x32, 0xA5, 0x4D, 0x77,
0x1F, 0x23, 0x19, 0xCC, 0xE1, 0xB3, 0x50, 0x0D, 0xE8, 0x2F, 0x8B, 0x18, 0x89, 0x61, 0xCB,
0x22, 0xBA, 0xE0, 0x4A, 0xA2, 0x7F, 0xA5, 0x1B, 0x45, 0x59, 0x33, 0xC4, 0x73, 0xDF, 0x42,
0xC6, 0x00, 0x11, 0x37, 0xF2, 0x3C, 0x1B, 0xF4, 0x26, 0xD1, 0x6D, 0x93, 0xC1, 0x94, 0xD2,
0x60, 0xE5, 0xF3, 0x91, 0x66, 0x92, 0x3C, 0x65, 0x27, 0xC1, 0x83, 0x13, 0x76, 0x5A, 0x88,
0xEC, 0xB2, 0x59, 0x95, 0x18, 0x81, 0x2E, 0x94, 0x96, 0x53, 0x17, 0xB6, 0xFD, 0x8C, 0xCC,
0xBE, 0x8D, 0x36, 0xB3, 0xC8, 0xF2, 0xB2, 0xBE, 0x0F, 0x12, 0x99, 0xFF, 0xFA, 0xF9, 0x18,
0xAB, 0x30, 0xFA, 0xB1, 0x5B, 0xF2, 0xEE, 0xCA, 0x6E, 0xA1, 0xD9, 0xCE, 0xCC, 0x60, 0xA0,
0x4D, 0xFD, 0x7C, 0xAD, 0x4D, 0x50, 0xB6, 0x88, 0x0D, 0x88, 0x3B, 0x28, 0x7F, 0xA1, 0x28,
0x41, 0x0A, 0x43, 0xAD, 0xCC, 0x08, 0x14, 0xF3, 0xF2, 0x43, 0xE7, 0xCF, 0x6A, 0x5C, 0x11,
0xD0, 0x6D, 0x99, 0xC8, 0x4F, 0xB1, 0x14, 0x06, 0xBC, 0x68, 0x6D, 0xBE, 0xCD, 0xD7, 0x58,
0xA2, 0x17, 0xF5, 0x9E, 0xFD, 0xDA, 0xFA, 0xBF, 0x73, 0x57, 0x4A, 0xF8, 0xF3, 0xA9, 0x94,
0xB3, 0x01, 0xE9, 0xA3, 0xDA, 0xEA, 0xC1, 0x40, 0x33, 0xAA, 0x3F, 0xE6, 0x0D, 0x6A, 0xE2,
0xF3, 0x74, 0xE8, 0x1B, 0x3C, 0x2B, 0x25, 0x44, 0x8E, 0x1C, 0x36, 0xBE, 0xA9, 0x27, 0x6E,
0x6A, 0x48, 0x8E, 0x2F, 0x2C, 0x9D, 0x71, 0x66, 0x23, 0x7C, 0x7A, 0x74, 0x93, 0x46, 0x2D,
0xCA, 0x6B, 0xC6, 0x33, 0xDA, 0x1E, 0x1E, 0x44, 0x07, 0xFD, 0x89, 0x5D, 0x30, 0x02, 0x4C,
0xB1, 0x73, 0xC0, 0x91, 0xEB, 0xA5, 0x61, 0x89, 0xA4, 0x04, 0xFD, 0xD5, 0x5F, 0x54, 0x59,
0x81, 0xC3, 0x2A, 0x13, 0x89, 0xDA, 0x68, 0xB6, 0x3A, 0x9C, 0x70, 0x6F, 0x48, 0xB4, 0x3C,
0xF8, 0x9B, 0xF8, 0xF2, 0x59, 0xBF, 0xF4, 0x8D, 0x06, 0x58, 0xEA, 0xA2, 0xA6, 0xB4, 0x70,
0x08, 0x80, 0x2B, 0x50, 0x13, 0x36, 0x79, 0x17, 0x0B, 0x94, 0x0E, 0x4D, 0xF5, 0xC8, 0x14,
0xB9, 0x02, 0x7D, 0xEE, 0x6B, 0xBD, 0x10, 0xB4, 0x85, 0x74, 0xA1, 0xB9, 0x84, 0x67, 0xC6,
0x2C, 0xDB, 0xDA, 0x55, 0x54, 0x16, 0xDA, 0x02, 0xB6, 0xDA, 0x2A, 0x9B, 0x51, 0xD6, 0xDC,
0x87, 0x80, 0xC1, 0xB8, 0x6F, 0x0C, 0xEF, 0x4B, 0xD1, 0x1A, 0x9F, 0x36, 0x2E, 0x9C, 0x7E,
0x5F, 0x17, 0xE2, 0xC1, 0x82, 0x0C, 0x42, 0x0D, 0x15, 0x18, 0xCA,
],
];

View file

@ -0,0 +1,175 @@
#![allow(unused)]
use super::magic::{LOOKUP_G11, LOOKUP_G13, LOOKUP_G14, LOOKUP_G2, LOOKUP_G3, LOOKUP_G9, LOOKUP_RCON, LOOKUP_SBOX, LOOKUP_SBOX_INV, SHIFT_ROWS_TABLE, SHIFT_ROWS_TABLE_INV};
fn xorr(a: &mut [u8], b: &[u8], n: usize) {
(0..n).for_each(|i| a[i] ^= b[i]);
}
fn xor_round_key(state: &mut [u8], keys: &[u8], round: usize) {
xorr(state, &keys[round * 16..], 16);
}
fn sub_bytes(a: &mut [u8], n: usize) {
(0..n).for_each(|i| a[i] = LOOKUP_SBOX[a[i] as usize]);
}
fn sub_bytes_inv(a: &mut [u8], n: usize) {
(0..n).for_each(|i| a[i] = LOOKUP_SBOX_INV[a[i] as usize]);
}
fn key_schedule_core(a: &mut [u8], i: usize) {
let temp = a[0];
a[0] = a[1];
a[1] = a[2];
a[2] = a[3];
a[3] = temp;
sub_bytes(a, 4);
a[0] ^= LOOKUP_RCON[i];
}
fn oqs_aes128_load_schedule_c(key: &[u8]) -> [u8; 176] {
let mut schedule = [0u8; 176];
let mut bytes = 16;
let mut i = 1;
let mut t = [0u8; 4];
schedule[0..16].copy_from_slice(key);
while bytes < 176 {
t.copy_from_slice(&schedule[bytes - 4..]);
key_schedule_core(&mut t, i);
i += 1;
xorr(&mut schedule[bytes..], &t, 4);
schedule[bytes..].copy_from_slice(&t);
bytes += 4;
for _ in 0..3 {
t.copy_from_slice(&schedule[bytes - 4..]);
xorr(&mut t, &schedule[bytes - 16..], 4);
schedule[bytes..].copy_from_slice(&t);
bytes += 4;
}
}
schedule
}
fn shift_rows(state: &mut [u8]) {
let temp = state.to_vec();
(0..16).for_each(|i| state[i] = temp[SHIFT_ROWS_TABLE[i] as usize]);
}
fn shift_rows_inv(state: &mut [u8]) {
let temp = state.to_vec();
(0..16).for_each(|i| state[i] = temp[SHIFT_ROWS_TABLE_INV[i] as usize]);
}
fn mix_col(state: &mut [u8]) {
let (a0, a1, a2, a3) = (state[0], state[1], state[2], state[3]);
state[0] = LOOKUP_G2[a0 as usize] ^ LOOKUP_G3[a1 as usize] ^ a2 ^ a3;
state[1] = LOOKUP_G2[a1 as usize] ^ LOOKUP_G3[a2 as usize] ^ a3 ^ a0;
state[2] = LOOKUP_G2[a2 as usize] ^ LOOKUP_G3[a3 as usize] ^ a0 ^ a1;
state[3] = LOOKUP_G2[a3 as usize] ^ LOOKUP_G3[a0 as usize] ^ a1 ^ a2;
}
fn mix_cols(state: &mut [u8]) {
mix_col(&mut state[0..4]);
mix_col(&mut state[4..8]);
mix_col(&mut state[8..12]);
mix_col(&mut state[12..16]);
}
fn mix_col_inv(state: &mut [u8]) {
let (a0, a1, a2, a3) = (state[0], state[1], state[2], state[3]);
state[0] = LOOKUP_G14[a0 as usize]
^ LOOKUP_G9[a3 as usize]
^ LOOKUP_G13[a2 as usize]
^ LOOKUP_G11[a1 as usize];
state[1] = LOOKUP_G14[a1 as usize]
^ LOOKUP_G9[a0 as usize]
^ LOOKUP_G13[a3 as usize]
^ LOOKUP_G11[a2 as usize];
state[2] = LOOKUP_G14[a2 as usize]
^ LOOKUP_G9[a1 as usize]
^ LOOKUP_G13[a0 as usize]
^ LOOKUP_G11[a3 as usize];
state[3] = LOOKUP_G14[a3 as usize]
^ LOOKUP_G9[a2 as usize]
^ LOOKUP_G13[a1 as usize]
^ LOOKUP_G11[a0 as usize];
}
fn mix_cols_inv(state: &mut [u8]) {
mix_col_inv(&mut state[0..4]);
mix_col_inv(&mut state[4..8]);
mix_col_inv(&mut state[8..12]);
mix_col_inv(&mut state[12..16]);
}
fn oqs_aes128_enc_c(plaintext: &[u8], schedule: &[u8], ciphertext: &mut [u8]) {
ciphertext.copy_from_slice(&plaintext[..16]);
xor_round_key(ciphertext, schedule, 0);
for i in 1..10 {
sub_bytes(ciphertext, 16);
shift_rows(ciphertext);
mix_cols(ciphertext);
xor_round_key(ciphertext, schedule, i);
}
sub_bytes(ciphertext, 16);
shift_rows(ciphertext);
xor_round_key(ciphertext, schedule, 10);
}
pub fn oqs_mhy128_enc_c(plaintext: &[u8], schedule: &[u8], ciphertext: &mut [u8]) {
ciphertext.copy_from_slice(&plaintext[..16]);
xor_round_key(ciphertext, schedule, 0);
for i in 1..10 {
sub_bytes_inv(ciphertext, 16);
shift_rows_inv(ciphertext);
mix_cols_inv(ciphertext);
xor_round_key(ciphertext, schedule, i);
}
sub_bytes_inv(ciphertext, 16);
shift_rows_inv(ciphertext);
xor_round_key(ciphertext, schedule, 10);
}
fn oqs_aes128_dec_c(ciphertext: &[u8], schedule: &[u8], plaintext: &mut [u8]) {
plaintext.copy_from_slice(&ciphertext[..16]);
xor_round_key(plaintext, schedule, 10);
shift_rows_inv(plaintext);
sub_bytes_inv(plaintext, 16);
for i in 0..9 {
xor_round_key(plaintext, schedule, 9 - i);
mix_cols_inv(plaintext);
shift_rows_inv(plaintext);
sub_bytes_inv(plaintext, 16);
}
xor_round_key(plaintext, schedule, 0);
}
fn oqs_mhy128_dec_c(ciphertext: &[u8], schedule: &[u8], plaintext: &mut [u8]) {
plaintext.copy_from_slice(&ciphertext[..16]);
xor_round_key(plaintext, schedule, 10);
shift_rows(plaintext);
sub_bytes(plaintext, 16);
for i in 0..9 {
xor_round_key(plaintext, schedule, 9 - i);
mix_cols(plaintext);
shift_rows(plaintext);
sub_bytes(plaintext, 16);
}
xor_round_key(plaintext, schedule, 0);
}

View file

@ -0,0 +1,92 @@
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
use magic::{AES_XORPAD_TABLE, KEY_XORPAD_TABLE};
use mhy_aes::oqs_mhy128_enc_c;
use std::io::Read;
use thiserror::Error;
mod magic;
mod mhy_aes;
const HEAD_MAGIC: u32 = 0x45633262; // "Ec2b"
const KEY_SIZE: usize = 16;
const DATA_SIZE: usize = 2048;
pub struct Ec2b {
key: [u8; KEY_SIZE],
data: [u8; DATA_SIZE],
}
#[derive(Error, Debug)]
pub enum DecodeError {
#[error("an I/O error occurred: {0}")]
IoError(#[from] std::io::Error),
#[error("magic mismatch, expected: {HEAD_MAGIC}, got: {0}")]
MagicMismatch(u32),
#[error("invalid key size, expected: {KEY_SIZE}, got: {0}")]
InvalidKeySize(usize),
#[error("invalid data size, expected: {DATA_SIZE}, got: {0}")]
InvalidDataSize(usize),
}
impl Ec2b {
const XOR_MAGIC: u64 = 0xCEAC3B5A867837AC;
pub fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let magic = r.read_u32::<BigEndian>()?;
matches!(magic, HEAD_MAGIC)
.then_some(())
.ok_or(DecodeError::MagicMismatch(magic))?;
let key_size = r.read_u32::<LittleEndian>()? as usize;
matches!(key_size, KEY_SIZE)
.then_some(())
.ok_or(DecodeError::InvalidKeySize(key_size))?;
let mut key = [0u8; KEY_SIZE];
r.read_exact(&mut key)?;
let data_size = r.read_u32::<LittleEndian>()? as usize;
matches!(data_size, DATA_SIZE)
.then_some(())
.ok_or(DecodeError::InvalidDataSize(data_size))?;
let mut data = [0u8; DATA_SIZE];
r.read_exact(&mut data)?;
Self::key_scramble(&mut key);
(0..16).for_each(|i| key[i] ^= KEY_XORPAD_TABLE[i]);
Ok(Self { key, data })
}
#[must_use] pub fn derive_seed(&self) -> u64 {
let val = self
.data
.chunks_exact(8)
.map(|chunk| u64::from_le_bytes(chunk.try_into().unwrap()))
.fold(0xFFFFFFFFFFFFFFFF, |val, i| val ^ i);
let key_qword_0 = u64::from_le_bytes(self.key[0..8].try_into().unwrap());
let key_qword_1 = u64::from_le_bytes(self.key[8..16].try_into().unwrap());
key_qword_1 ^ Self::XOR_MAGIC ^ val ^ key_qword_0
}
fn key_scramble(key: &mut [u8]) {
let mut round_keys = [0u8; 176];
for round in 0..11 {
for i in 0..16 {
for j in 0..16 {
let idx = (round << 8) + (i * 16) + j;
round_keys[round * 16 + i] ^=
AES_XORPAD_TABLE[1][idx] ^ AES_XORPAD_TABLE[0][idx];
}
}
}
let mut chip = [0u8; 16];
oqs_mhy128_enc_c(key, &round_keys, &mut chip);
key.copy_from_slice(&chip);
}
}

View file

@ -0,0 +1,7 @@
mod ec2b;
mod rsa_util;
mod xor;
pub use ec2b::Ec2b;
pub use rsa_util::*;
pub use xor::MhyXorpad;

View file

@ -0,0 +1,48 @@
use rsa::pkcs1v15::SigningKey;
use rsa::sha2::Sha256;
use rsa::signature::{RandomizedSigner, SignatureEncoding};
use rsa::{Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey};
const CLIENT_PUBLIC_KEY: &[u8] = include_bytes!("../../client_public_key.der");
const SERVER_PRIVATE_KEY: &[u8] = include_bytes!("../../server_private_key.der");
const RSA_CHUNK_SIZE: usize = 117;
#[must_use]
pub fn rsa_encrypt(data: &[u8]) -> Box<[u8]> {
let public_key: RsaPublicKey =
rsa::pkcs8::DecodePublicKey::from_public_key_der(CLIENT_PUBLIC_KEY)
.expect("Failed to read public key from der");
let mut rng = rand::thread_rng();
let mut result: Vec<u8> = Vec::new();
for chunk in data.chunks(RSA_CHUNK_SIZE) {
let encrypted_chunk = public_key
.encrypt(&mut rng, Pkcs1v15Encrypt, chunk)
.expect("Encryption failed");
result.extend(encrypted_chunk);
}
result.into()
}
#[must_use]
pub fn rsa_decrypt(cipher: &[u8]) -> Vec<u8> {
let private_key: RsaPrivateKey =
rsa::pkcs8::DecodePrivateKey::from_pkcs8_der(SERVER_PRIVATE_KEY)
.expect("Failed to read pkcs8 private key");
private_key
.decrypt(Pkcs1v15Encrypt, cipher)
.expect("Decryption failed")
}
#[must_use]
pub fn rsa_sign(data: &[u8]) -> Box<[u8]> {
let private_key = rsa::pkcs8::DecodePrivateKey::from_pkcs8_der(SERVER_PRIVATE_KEY)
.expect("Failed to read pkcs8 private key");
let signing_key = SigningKey::<Sha256>::new(private_key);
let mut rng = rand::thread_rng();
signing_key.sign_with_rng(&mut rng, data).to_bytes()
}

View file

@ -0,0 +1,22 @@
use byteorder::ByteOrder;
use rand_mt::Mt64;
const SIZE: usize = 4096;
pub struct MhyXorpad([u8; SIZE]);
impl MhyXorpad {
#[must_use]
pub fn new<E: ByteOrder>(seed: u64) -> Self {
let mut mt = Mt64::new(seed);
let mut buf = [0u8; 4096];
(0..(SIZE >> 3)).for_each(|i| E::write_u64(&mut buf[i * 8..(i + 1) * 8], mt.next_u64()));
Self(buf)
}
pub fn xor(&self, buf: &mut [u8]) {
buf.iter_mut()
.enumerate()
.for_each(|(i, v)| *v ^= self.0[i % SIZE]);
}
}

View file

@ -0,0 +1,40 @@
use serde::{Deserialize, Serialize};
use sqlx::{migrate, PgPool};
pub mod player_data;
pub mod sdk_data;
#[derive(Serialize, Deserialize)]
pub struct DatabaseCredentials {
pub username: String,
pub password: String,
pub host: String,
pub name: String,
}
impl ToString for DatabaseCredentials {
fn to_string(&self) -> String {
format!(
"postgres://{}:{}@{}/{}",
&self.username, &self.password, &self.host, &self.name
)
}
}
impl Default for DatabaseCredentials {
fn default() -> Self {
Self {
username: String::from("root"),
password: String::from("root"),
host: String::from("localhost:5432"),
name: String::from("nap"),
}
}
}
pub async fn init(credentials: &DatabaseCredentials) -> Result<PgPool, sqlx::Error> {
let pool = PgPool::connect(&credentials.to_string()).await?;
migrate!("./migrations").run(&pool).await?;
Ok(pool)
}

View file

@ -0,0 +1,20 @@
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
#[derive(FromRow, Debug)]
pub struct DbUserUidRow {
pub uid: i32,
pub account_uid: String,
pub ext: String,
}
#[derive(FromRow, Debug)]
pub struct DbPlayerDataRow {
pub uid: i32,
pub bin_data: Vec<u8>,
}
#[derive(Serialize, Deserialize)]
pub struct PlayerExtJsonData {
pub reg_platform: u32,
}

View file

@ -0,0 +1,97 @@
use std::sync::LazyLock;
use regex::Regex;
use sqlx::FromRow;
use thiserror::Error;
use crate::util;
#[derive(sqlx::Encode, sqlx::Decode)]
pub struct Username(String);
impl Username {
pub fn parse(username: String) -> Option<Self> {
static ALLOWED_USERNAME_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new("^[a-zA-Z0-9._@-]{6,25}$").unwrap());
ALLOWED_USERNAME_REGEX
.is_match(&username)
.then_some(Self(username))
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl sqlx::Type<sqlx::Postgres> for Username {
fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo {
<String as sqlx::Type<sqlx::Postgres>>::type_info()
}
fn compatible(ty: &<sqlx::Postgres as sqlx::Database>::TypeInfo) -> bool {
<String as sqlx::Type<sqlx::Postgres>>::compatible(ty)
}
}
#[derive(Error, Debug)]
pub enum PasswordError {
#[error("password pair mismatch")]
PairMismatch,
#[error("user input doesn't meet requirements")]
RequirementsMismatch,
#[error("failed to generate password hash: {0}")]
HashFailed(pbkdf2::password_hash::Error),
}
#[derive(sqlx::Encode, sqlx::Decode)]
pub struct Password(String);
impl Password {
pub fn new(password: String, password_v2: String) -> Result<Self, PasswordError> {
(password == password_v2)
.then_some(())
.ok_or(PasswordError::PairMismatch)?;
matches!(password.len(), 8..30)
.then_some(())
.ok_or(PasswordError::RequirementsMismatch)?;
let hash = util::hash_string(&password).map_err(|err| PasswordError::HashFailed(err))?;
Ok(Self(hash))
}
pub fn verify(&self, password: &str) -> bool {
util::verify_hash(password, &self.0).is_some()
}
pub fn as_hash_str(&self) -> &str {
self.0.as_str()
}
}
impl sqlx::Type<sqlx::Postgres> for Password {
fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo {
<String as sqlx::Type<sqlx::Postgres>>::type_info()
}
fn compatible(ty: &<sqlx::Postgres as sqlx::Database>::TypeInfo) -> bool {
<String as sqlx::Type<sqlx::Postgres>>::compatible(ty)
}
}
#[derive(FromRow)]
pub struct DbSdkAccountRow {
pub uid: i32,
pub token: String,
pub username: Username,
pub password: Password,
}
#[derive(FromRow)]
pub struct DbComboTokenRow {
pub account_uid: String,
pub token: String,
pub device_id: String,
}

8
nap_common/src/lib.rs Normal file
View file

@ -0,0 +1,8 @@
pub mod constants;
pub mod cryptography;
pub mod database;
mod logging;
pub mod splash;
pub mod util;
pub use logging::init_tracing;

View file

@ -0,0 +1,6 @@
pub fn init_tracing() {
#[cfg(target_os = "windows")]
ansi_term::enable_ansi_support().unwrap();
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
}

5
nap_common/src/splash.rs Normal file
View file

@ -0,0 +1,5 @@
use crate::constants::{CLIENT_VERSION, SERVER_VERSION};
pub fn print(service_type: &str) {
println!("\n ____. ________ __________ _________\n | |____ ____ ____ \\______ \\ ____ ____ \\____ / / _____/\n | \\__ \\ / \\_/ __ \\ | | \\ / _ \\_/ __ \\ / / \\_____ \\ \n/\\__| |/ __ \\| | \\ ___/ | ` ( <_> ) ___/ / /_ / \\\n\\________(______/___|__/\\_____>_________/\\____/ \\_____> /_______/\\/________ /\n v{SERVER_VERSION} (game: {CLIENT_VERSION}) {service_type}\n");
}

67
nap_common/src/util.rs Normal file
View file

@ -0,0 +1,67 @@
use std::{
fs::File,
time::{SystemTime, UNIX_EPOCH},
};
use password_hash::{PasswordHash, PasswordHasher, SaltString};
use pbkdf2::{Params, Pbkdf2};
use serde::{de::DeserializeOwned, Serialize};
#[must_use]
pub fn load_or_create_config<T>(path: &str) -> T
where
T: Default + Serialize + DeserializeOwned,
{
std::fs::read_to_string(path).map_or_else(
|_| {
let defaults = T::default();
std::fs::write(path, toml::to_string(&defaults).unwrap()).unwrap();
defaults
},
|data| toml::from_str(&data).unwrap(),
)
}
#[must_use]
pub fn open_secret_key() -> Result<File, std::io::Error> {
File::open("assets/security/client_secret_key.ec2b")
}
#[must_use]
pub fn cur_timestamp_ms() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u64
}
#[must_use]
pub fn cur_timestamp() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as u64
}
#[must_use]
pub fn hash_string(content: &str) -> Result<String, pbkdf2::password_hash::Error> {
let salt = SaltString::generate(rand::thread_rng());
let hash = Pbkdf2.hash_password_customized(
content.as_bytes(),
None,
None,
Params {
rounds: 10000,
output_length: 32,
},
&salt,
)?;
Ok(hash.serialize().to_string())
}
#[must_use]
pub fn verify_hash(content: &str, hash_str: &str) -> Option<()> {
let hash = PasswordHash::new(hash_str).ok()?;
hash.verify_password(&[&Pbkdf2], content).ok()
}

16
nap_data/Cargo.toml Normal file
View file

@ -0,0 +1,16 @@
[package]
name = "data"
edition = "2021"
version.workspace = true
[dependencies]
# Serialization
serde.workspace = true
serde_json.workspace = true
# Util
paste.workspace = true
thiserror.workspace = true
# Tracing
tracing.workspace = true

45
nap_data/src/lib.rs Normal file
View file

@ -0,0 +1,45 @@
pub mod tables;
use std::{collections::HashMap, sync::OnceLock};
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Serialize, Deserialize)]
pub struct AssetsConfig {
pub filecfg_path: String,
pub usm_keys_path: String,
}
#[derive(Error, Debug)]
pub enum DataLoadError {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("from_json failed for type {0}, error: {1}")]
FromJsonError(String, serde_json::Error),
}
static USM_KEY_MAP: OnceLock<HashMap<u32, u64>> = OnceLock::new();
pub fn init_data(config: &AssetsConfig) -> Result<(), DataLoadError> {
tables::load_tables(&config.filecfg_path)?;
if let Err(err) = load_usm_keys(&config.usm_keys_path) {
tracing::warn!("failed to load USM keys, in-game cutscenes will not work! Reason: {err}");
USM_KEY_MAP.set(HashMap::new()).unwrap();
}
Ok(())
}
pub fn usm_key_map() -> &'static HashMap<u32, u64> {
USM_KEY_MAP.get().unwrap()
}
fn load_usm_keys(path: &str) -> Result<(), DataLoadError> {
let data = std::fs::read_to_string(path).map_err(|err| DataLoadError::IoError(err))?;
let map = serde_json::from_str::<HashMap<u32, u64>>(&data)
.map_err(|err| DataLoadError::FromJsonError(String::from("USMKeyMap"), err))?;
USM_KEY_MAP.set(map).unwrap();
Ok(())
}

View file

@ -0,0 +1,17 @@
use serde::Deserialize;
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct AvatarBaseTemplate {
#[serde(rename = "ID")]
pub id: u32,
pub code_name: String,
pub name: String,
pub full_name: String,
pub audio_event_replace_param: String,
pub audio_bank: String,
pub hit_types: Box<[u32]>,
pub element_types: Box<[u32]>,
pub gender: u32,
pub camp: u32,
}

View file

@ -0,0 +1,46 @@
use paste::paste;
use std::sync::OnceLock;
use super::DataLoadError;
macro_rules! template_tables {
($($template_type:ident;)*) => {
$(paste! {
mod [<$template_type:snake>];
pub use [<$template_type:snake>]::$template_type;
})*
$(paste! {
static [<$template_type:snake:upper _TB>]: OnceLock<Vec<$template_type>> = OnceLock::new();
})*
pub(crate) fn load_tables(filecfg_path: &str) -> Result<(), DataLoadError> {
$(paste! {
let file_name = concat!(stringify!($template_type), "Tb.json");
let path = format!("{filecfg_path}/{file_name}");
let data = std::fs::read_to_string(path)?;
[<$template_type:snake:upper _TB>].set(serde_json::from_str(&data).map_err(|err| DataLoadError::FromJsonError(String::from(stringify!($template_type)), err))?).unwrap();
})*
Ok(())
}
$(paste! {
pub mod [<$template_type:snake _tb>] {
pub fn iter() -> ::std::slice::Iter<'static, super::$template_type> {
super::[<$template_type:snake:upper _TB>].get().unwrap().iter()
}
}
})*
};
}
template_tables! {
AvatarBaseTemplate;
UnlockConfigTemplate;
SectionConfigTemplate;
ProcedureConfigTemplate;
PostGirlConfigTemplate;
TrainingQuestTemplate;
}

View file

@ -0,0 +1,9 @@
use serde::Deserialize;
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct PostGirlConfigTemplate {
#[serde(rename = "ID")]
pub id: u32,
pub name: String,
}

View file

@ -0,0 +1,14 @@
use serde::Deserialize;
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct ProcedureConfigTemplate {
#[serde(rename = "ProcedureID")]
pub procedure_id: i32,
pub procedure_type: u32,
#[serde(rename = "ContentID")]
pub content_id: String,
pub jump_tos: Vec<i32>,
pub procedure_banks: Vec<String>,
pub procedure_event: String,
}

View file

@ -0,0 +1,12 @@
use serde::Deserialize;
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct SectionConfigTemplate {
pub section_id: u32,
pub photo_name: String,
pub name: String,
pub primary_entry_name: String,
pub secondary_entry_name: String,
pub section_name: String,
}

View file

@ -0,0 +1,9 @@
use serde::Deserialize;
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct TrainingQuestTemplate {
pub id: u32,
pub training_type: u32,
pub battle_event_id: u32,
}

View file

@ -0,0 +1,9 @@
use serde::Deserialize;
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct UnlockConfigTemplate {
#[serde(rename = "ID")]
pub id: i32,
pub name: String,
}

42
nap_gameserver/Cargo.toml Normal file
View file

@ -0,0 +1,42 @@
[package]
name = "nap-gameserver"
edition = "2021"
version.workspace = true
[dependencies]
# Runtime
tokio.workspace = true
tokio-util.workspace = true
# Serialization
prost.workspace = true
prost-types.workspace = true
rbase64.workspace = true
toml.workspace = true
serde.workspace = true
serde_json.workspace = true
# Database
sqlx.workspace = true
# Util
thiserror.workspace = true
paste.workspace = true
byteorder.workspace = true
hex.workspace = true
rand.workspace = true
atomic_enum.workspace = true
num_enum.workspace = true
dashmap.workspace = true
# Tracing
tracing.workspace = true
tracing-futures.workspace = true
tracing-log.workspace = true
tracing-subscriber.workspace = true
tracing-bunyan-formatter.workspace = true
# Internal
common.workspace = true
data.workspace = true
proto.workspace = true

View file

@ -0,0 +1,26 @@
use common::database::DatabaseCredentials;
use data::AssetsConfig;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct NapGSConfig {
pub tcp_listen_addr: String,
pub player_save_period_seconds: u64,
#[serde(rename = "database")]
pub database_credentials: DatabaseCredentials,
pub assets: AssetsConfig,
}
impl Default for NapGSConfig {
fn default() -> Self {
Self {
tcp_listen_addr: String::from("0.0.0.0:20501"),
player_save_period_seconds: 30,
database_credentials: DatabaseCredentials::default(),
assets: AssetsConfig {
filecfg_path: String::from("assets/FileCfg"),
usm_keys_path: String::from("assets/VideoUSMEncKeys.json"),
},
}
}
}

View file

@ -0,0 +1,94 @@
use common::database::{
player_data::{DbPlayerDataRow, DbUserUidRow, PlayerExtJsonData},
sdk_data::DbComboTokenRow,
};
use sqlx::{query, query_as, PgPool};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DbError {
#[error("SQL error: {0}")]
SqlxError(#[from] sqlx::Error),
#[error("entry not found")]
NotFound,
}
pub async fn select_combo_token_by_account(
pool: &PgPool,
account_uid: &str,
) -> Result<DbComboTokenRow, DbError> {
query_as("SELECT * from t_combo_token where account_uid = ($1)")
.bind(account_uid)
.fetch_optional(pool)
.await?
.ok_or(DbError::NotFound)
}
pub async fn select_player_uid_by_account(
pool: &PgPool,
account_uid: &str,
) -> Result<DbUserUidRow, DbError> {
query_as("SELECT * from t_user_uid where account_uid = ($1)")
.bind(account_uid)
.fetch_optional(pool)
.await?
.ok_or(DbError::NotFound)
}
pub async fn insert_player_uid_by_account(
pool: &PgPool,
account_uid: &str,
platform: u32,
) -> Result<DbUserUidRow, DbError> {
let ext = serde_json::to_string(&PlayerExtJsonData {
reg_platform: platform,
})
.unwrap();
Ok(
query_as("INSERT INTO t_user_uid (account_uid, ext) VALUES ($1, $2) RETURNING *")
.bind(account_uid)
.bind(ext)
.fetch_one(pool)
.await?,
)
}
pub async fn select_player_data_by_uid(
pool: &PgPool,
uid: i32,
) -> Result<DbPlayerDataRow, DbError> {
query_as("SELECT * from t_player_data where uid = ($1)")
.bind(uid)
.fetch_optional(pool)
.await?
.ok_or(DbError::NotFound)
}
pub async fn insert_player_data_by_uid(
pool: &PgPool,
uid: i32,
bin_data: Vec<u8>,
) -> Result<(), DbError> {
query("INSERT into t_player_data VALUES ($1, $2)")
.bind(uid)
.bind(bin_data)
.execute(pool)
.await?;
Ok(())
}
pub async fn update_player_data_by_uid(
pool: &PgPool,
uid: i32,
bin_data: Vec<u8>,
) -> Result<(), DbError> {
query("UPDATE t_player_data SET bin_data = ($1) WHERE uid = ($2)")
.bind(bin_data)
.bind(uid)
.execute(pool)
.await?;
Ok(())
}

View file

@ -0,0 +1,25 @@
use super::*;
pub async fn on_get_abyss_info(
_session: &NetSession,
_player: &mut Player,
_req: GetAbyssInfoCsReq,
) -> NetResult<GetAbyssInfoScRsp> {
Ok(GetAbyssInfoScRsp {
retcode: Retcode::RetSucc.into(),
abyss_info: Some(AbyssInfo::default()),
abyss_group_list: Vec::new(),
..Default::default()
})
}
pub async fn on_get_completed_abyss_group_list(
_session: &NetSession,
_player: &mut Player,
_req: GetCompletedAbyssGroupListCsReq,
) -> NetResult<GetCompletedAbyssGroupListScRsp> {
Ok(GetCompletedAbyssGroupListScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}

View file

@ -0,0 +1,23 @@
use super::*;
pub async fn on_get_activity_data(
_session: &NetSession,
_player: &mut Player,
_req: GetActivityDataCsReq,
) -> NetResult<GetActivityDataScRsp> {
Ok(GetActivityDataScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}
pub async fn on_get_web_activity_data(
_session: &NetSession,
_player: &mut Player,
_req: GetWebActivityDataCsReq,
) -> NetResult<GetWebActivityDataScRsp> {
Ok(GetWebActivityDataScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}

View file

@ -0,0 +1,12 @@
use super::*;
pub async fn on_get_arcade_data(
_session: &NetSession,
_player: &mut Player,
_req: GetArcadeDataCsReq,
) -> NetResult<GetArcadeDataScRsp> {
Ok(GetArcadeDataScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}

View file

@ -0,0 +1,30 @@
use crate::logic::role::Avatar;
use super::*;
pub async fn on_get_avatar_data(
_session: &NetSession,
player: &mut Player,
_req: GetAvatarDataCsReq,
) -> NetResult<GetAvatarDataScRsp> {
Ok(GetAvatarDataScRsp {
retcode: Retcode::RetSucc.into(),
avatar_list: player
.role_model
.avatar_list
.iter()
.map(Avatar::to_client)
.collect(),
})
}
pub async fn on_get_buddy_data(
_session: &NetSession,
_player: &mut Player,
_req: GetBuddyDataCsReq,
) -> NetResult<GetBuddyDataScRsp> {
Ok(GetBuddyDataScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}

View file

@ -0,0 +1,12 @@
use super::*;
pub async fn on_get_battle_pass_data(
_session: &NetSession,
_player: &mut Player,
_req: GetBattlePassDataCsReq,
) -> NetResult<GetBattlePassDataScRsp> {
Ok(GetBattlePassDataScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}

View file

@ -0,0 +1,23 @@
use super::*;
pub async fn on_get_cafe_data(
_session: &NetSession,
_player: &mut Player,
_req: GetCafeDataCsReq,
) -> NetResult<GetCafeDataScRsp> {
Ok(GetCafeDataScRsp {
retcode: Retcode::RetSucc.into(),
cafe_data: Some(CafeData::default()),
})
}
pub async fn on_get_reward_buff_data(
_session: &NetSession,
_player: &mut Player,
_req: GetRewardBuffDataCsReq,
) -> NetResult<GetRewardBuffDataScRsp> {
Ok(GetRewardBuffDataScRsp {
retcode: Retcode::RetSucc.into(),
info: Some(RewardBuffData::default()),
})
}

View file

@ -0,0 +1,23 @@
use super::*;
pub async fn on_get_character_quest_list(
_session: &NetSession,
_player: &mut Player,
_req: GetCharacterQuestListCsReq,
) -> NetResult<GetCharacterQuestListScRsp> {
Ok(GetCharacterQuestListScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}
pub async fn on_get_photo_wall_data(
_session: &NetSession,
_player: &mut Player,
_req: GetPhotoWallDataCsReq,
) -> NetResult<GetPhotoWallDataScRsp> {
Ok(GetPhotoWallDataScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}

View file

@ -0,0 +1,159 @@
use crate::logic::{EOperator, ESystem};
use super::*;
use data::tables;
pub async fn on_get_tips_info(
_session: &NetSession,
_player: &mut Player,
req: GetTipsInfoCsReq,
) -> NetResult<GetTipsInfoScRsp> {
Ok(GetTipsInfoScRsp {
retcode: Retcode::RetSucc.into(),
tips_info: Some(TipsInfo::default()),
ofolagfmcmo: req.ofolagfmcmo, // tips group type
})
}
pub async fn on_get_client_systems_info(
_session: &NetSession,
player: &mut Player,
_req: GetClientSystemsInfoCsReq,
) -> NetResult<GetClientSystemsInfoScRsp> {
Ok(GetClientSystemsInfoScRsp {
retcode: Retcode::RetSucc.into(),
info: Some(ClientSystemsInfo {
post_girl_data: Some(PostGirlData {
selected_post_girl_id_list: tables::post_girl_config_template_tb::iter()
.map(|template| template.id)
.collect(),
post_girl_list: tables::post_girl_config_template_tb::iter()
.map(|template| PostGirlItem {
template_id: template.id,
unlock_time: 1000,
})
.collect(),
..Default::default()
}),
unlock_data: Some(player.lock_model.to_client()),
hbhfjgbahgf: Some(Aboegnnepmi::default()),
..Default::default()
}),
})
}
pub async fn on_get_news_stand_data(
_session: &NetSession,
_player: &mut Player,
_req: GetNewsStandDataCsReq,
) -> NetResult<GetNewsStandDataScRsp> {
Ok(GetNewsStandDataScRsp {
retcode: Retcode::RetSucc.into(),
news_stand_data: Some(NewsStandData::default()),
})
}
pub async fn on_get_trashbin_hermit_data(
_session: &NetSession,
_player: &mut Player,
_req: GetTrashbinHermitDataCsReq,
) -> NetResult<GetTrashbinHermitDataScRsp> {
Ok(GetTrashbinHermitDataScRsp {
retcode: Retcode::RetSucc.into(),
trashbin_hermit_data: Some(TrashbinHermitData::default()),
})
}
pub async fn on_get_exploration_data(
_session: &NetSession,
_player: &mut Player,
_req: GetExplorationDataCsReq,
) -> NetResult<GetExplorationDataScRsp> {
Ok(GetExplorationDataScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}
pub async fn on_report_ui_layout_platform(
_session: &NetSession,
_player: &mut Player,
_req: ReportUiLayoutPlatformCsReq,
) -> NetResult<ReportUiLayoutPlatformScRsp> {
Ok(ReportUiLayoutPlatformScRsp {
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_unlock_newbie_group(
_session: &NetSession,
_player: &mut Player,
req: UnlockNewbieGroupCsReq,
) -> NetResult<UnlockNewbieGroupScRsp> {
Ok(UnlockNewbieGroupScRsp {
retcode: Retcode::RetSucc.into(),
group_id: req.group_id,
})
}
pub async fn on_battle_report(
_session: &NetSession,
_player: &mut Player,
_req: BattleReportCsReq,
) -> NetResult<BattleReportScRsp> {
Ok(BattleReportScRsp {
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_player_operation(
_session: &NetSession,
_player: &mut Player,
req: PlayerOperationCsReq,
) -> NetResult<PlayerOperationScRsp> {
tracing::info!(
"PlayerOperation(system={:?}, operator={:?}, param={})",
ESystem::from(req.system),
EOperator::from(req.operator),
req.param
);
Ok(PlayerOperationScRsp {
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_pop_up_window_seen(
_session: &NetSession,
_player: &mut Player,
req: PopUpWindowSeenCsReq,
) -> NetResult<PopUpWindowSeenScRsp> {
Ok(PopUpWindowSeenScRsp {
retcode: Retcode::RetSucc.into(),
popup_group_id_list: req.popup_group_id_list,
})
}
pub async fn on_report_system_settings_change(
_session: &NetSession,
_player: &mut Player,
req: ReportSystemSettingsChangeCsReq,
) -> NetResult<ReportSystemSettingsChangeScRsp> {
tracing::info!("system settings change {req:?}");
Ok(ReportSystemSettingsChangeScRsp {
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_interact_with_scene_object(
_session: &NetSession,
_player: &mut Player,
_req: InteractWithSceneObjectCsReq,
) -> NetResult<InteractWithSceneObjectScRsp> {
// this request is used for interaction with STATIC scene objects (not controlled by server, e.g. door in workshop)
Ok(InteractWithSceneObjectScRsp {
retcode: Retcode::RetSucc.into(),
})
}

View file

@ -0,0 +1,34 @@
use super::*;
pub async fn on_get_collect_map(
_session: &NetSession,
_player: &mut Player,
_req: GetCollectMapCsReq,
) -> NetResult<GetCollectMapScRsp> {
Ok(GetCollectMapScRsp {
retcode: Retcode::RetSucc.into(),
collect_map: Some(CollectMap::default()),
})
}
pub async fn on_get_workbench_info(
_session: &NetSession,
_player: &mut Player,
_req: GetWorkbenchInfoCsReq,
) -> NetResult<GetWorkbenchInfoScRsp> {
Ok(GetWorkbenchInfoScRsp {
retcode: Retcode::RetSucc.into(),
workbench_info: Some(WorkbenchInfo::default()),
})
}
pub async fn on_get_abyss_reward_data(
_session: &NetSession,
_player: &mut Player,
_req: GetAbyssRewardDataCsReq,
) -> NetResult<GetAbyssRewardDataScRsp> {
Ok(GetAbyssRewardDataScRsp {
retcode: Retcode::RetSucc.into(),
abyss_reward_data: Some(AbyssRewardData::default()),
})
}

View file

@ -0,0 +1,36 @@
use proto::Retcode;
use thiserror::Error;
use crate::logic::game::LogicError;
pub type NetResult<T> = Result<T, NetError>;
#[derive(Error, Debug)]
pub enum PacketHandlingError {
#[error("protobuf decode failed: {0}")]
DecodeFailed(#[from] prost::DecodeError),
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("net error: {0}")]
Net(#[from] NetError),
#[error("soft logout performed")]
Logout,
}
#[derive(Error, Debug)]
pub enum NetError {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("logic error: {0}")]
LogicError(#[from] LogicError),
#[error("retcode: {0:?}")]
ErrCode(Retcode),
#[error("soft logout performed")]
Logout,
}
impl From<Retcode> for NetError {
fn from(value: Retcode) -> Self {
Self::ErrCode(value)
}
}

View file

@ -0,0 +1,126 @@
#[macro_export]
macro_rules! auth_req_handlers {
($($module:ident::$name:ident;)*) => {
pub async fn handle_auth_request(session: &crate::net::NetSession, packet: &crate::net::NetPacket, state: &crate::ServerState) -> Result<bool, crate::handlers::core::PacketHandlingError> {
use ::prost::Message;
use crate::handlers::core::NetError;
use crate::handlers::core::PacketHandlingError;
::paste::paste! {
match packet.cmd_id {
$(
::proto::[<$name CsReq>]::CMD_ID => {
let mut req = ::proto::[<$name CsReq>]::decode(&*packet.body)?;
req.xor_fields();
let rsp = match crate::handlers::$module::[<on_ $name:snake>](&session, &state.player_mgr, req).await {
Ok(rsp) => {
::tracing::info!(concat!("[LOGIN] successfully handled request ", stringify!($name)));
rsp
},
Err(NetError::ErrCode(retcode)) => {
::tracing::info!("[LOGIN] request {} was handled, retcode: {retcode:?}", stringify!($name));
[<$name ScRsp>] {
retcode: retcode.into(),
..Default::default()
}
},
Err(err) => return Err(PacketHandlingError::Net(err)),
};
session.send_rsp(packet.head.packet_id, rsp).await?;
Ok(true)
}
)*
_ => Ok(false),
}
}
}
};
}
#[macro_export]
macro_rules! req_handlers {
($($module:ident::$name:ident;)*) => {
pub async fn handle_request(session: &crate::net::NetSession, packet: &crate::net::NetPacket, state: &crate::ServerState) -> Result<bool, crate::handlers::core::PacketHandlingError> {
use ::prost::Message;
use crate::handlers::core::NetError;
use crate::handlers::core::PacketHandlingError;
::paste::paste! {
match packet.cmd_id {
$(
::proto::[<$name CsReq>]::CMD_ID => {
let player_uid = *session.player_uid().unwrap(); // Should never be None here
let Some(player_lock) = state.player_mgr.get_player(player_uid).await else {
::tracing::error!("PlayerManager::get_player returned None, uid={player_uid}");
return Ok(true);
};
let mut req = ::proto::[<$name CsReq>]::decode(&*packet.body)?;
req.xor_fields();
let rsp = match crate::handlers::$module::[<on_ $name:snake>](&session, &mut *player_lock.lock().await, req).await {
Ok(rsp) => {
::tracing::info!(concat!("successfully handled request ", stringify!($name)));
rsp
}
Err(NetError::ErrCode(retcode)) => {
::tracing::info!("request {} was handled, retcode: {retcode:?}", stringify!($name));
[<$name ScRsp>] {
retcode: retcode.into(),
..Default::default()
}
},
Err(err) => return Err(PacketHandlingError::Net(err)),
};
session.send_rsp(packet.head.packet_id, rsp).await?;
Ok(true)
}
)*
_ => Ok(false),
}
}
}
};
}
#[macro_export]
macro_rules! notify_handlers {
($($module:ident::$name:ident;)*) => {
pub async fn handle_notify(session: &crate::net::NetSession, packet: &crate::net::NetPacket, state: &crate::ServerState) -> Result<bool, crate::handlers::core::PacketHandlingError> {
use ::prost::Message;
use crate::handlers::core::NetError;
use crate::handlers::core::PacketHandlingError;
::paste::paste! {
match packet.cmd_id {
$(
::proto::[<$name Notify>]::CMD_ID => {
let player_uid = *session.player_uid().unwrap(); // Should never be None here
let Some(player_lock) = state.player_mgr.get_player(player_uid).await else {
::tracing::error!("PlayerManager::get_player returned None, uid={player_uid}");
return Ok(true);
};
let mut ntf = ::proto::[<$name Notify>]::decode(&*packet.body)?;
ntf.xor_fields();
match crate::handlers::$module::[<on_ $name:snake>](session, &mut *player_lock.lock().await, ntf).await {
Ok(()) => (),
Err(NetError::Logout) => return Err(PacketHandlingError::Logout),
Err(err) => return Err(PacketHandlingError::Net(err)),
}
::tracing::info!("successfully handled notify {}", stringify!($name));
Ok(true)
}
)*
_ => Ok(false),
}
}
}
};
}
pub use auth_req_handlers;
pub use notify_handlers;
pub use req_handlers;

View file

@ -0,0 +1,5 @@
mod error;
mod macros;
pub use error::{NetError, NetResult, PacketHandlingError};
pub use macros::{auth_req_handlers, notify_handlers, req_handlers};

View file

@ -0,0 +1,12 @@
use super::*;
pub async fn on_get_daily_challenge_info(
_session: &NetSession,
_player: &mut Player,
_req: GetDailyChallengeInfoCsReq,
) -> NetResult<GetDailyChallengeInfoScRsp> {
Ok(GetDailyChallengeInfoScRsp {
retcode: Retcode::RetSucc.into(),
info: Some(DailyChallengeInfo::default()),
})
}

View file

@ -0,0 +1,23 @@
use super::*;
pub async fn on_get_embattles_data(
_session: &NetSession,
_player: &mut Player,
_req: GetEmbattlesDataCsReq,
) -> NetResult<GetEmbattlesDataScRsp> {
Ok(GetEmbattlesDataScRsp {
embattles_data: Some(EmbattlesData::default()),
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_report_battle_team(
_session: &NetSession,
_player: &mut Player,
_req: ReportBattleTeamCsReq,
) -> NetResult<ReportBattleTeamScRsp> {
Ok(ReportBattleTeamScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}

View file

@ -0,0 +1,12 @@
use super::*;
pub async fn on_get_fairy_info(
_session: &NetSession,
_player: &mut Player,
_req: GetFairyInfoCsReq,
) -> NetResult<GetFairyInfoScRsp> {
Ok(GetFairyInfoScRsp {
retcode: Retcode::RetSucc.into(),
info: Some(FairyInfo::default()),
})
}

View file

@ -0,0 +1,25 @@
use super::*;
pub async fn on_get_friend_list(
_session: &NetSession,
_player: &mut Player,
_req: GetFriendListCsReq,
) -> NetResult<GetFriendListScRsp> {
Ok(GetFriendListScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}
pub async fn on_get_friend_limit_info(
_session: &NetSession,
_player: &mut Player,
_req: GetFriendLimitInfoCsReq,
) -> NetResult<GetFriendLimitInfoScRsp> {
Ok(GetFriendLimitInfoScRsp {
retcode: Retcode::RetSucc.into(),
ngaiiafngbk: 30,
ajndiihdlfa: 300,
..Default::default()
})
}

View file

@ -0,0 +1,13 @@
use super::*;
pub async fn on_get_gacha_data(
_session: &NetSession,
_player: &mut Player,
req: GetGachaDataCsReq,
) -> NetResult<GetGachaDataScRsp> {
Ok(GetGachaDataScRsp {
retcode: Retcode::RetSucc.into(),
gacha_type: req.gacha_type,
gacha_data: Some(GachaData::default()),
})
}

View file

@ -0,0 +1,12 @@
use super::*;
pub async fn on_get_hadal_zone_data(
_session: &NetSession,
_player: &mut Player,
_req: GetHadalZoneDataCsReq,
) -> NetResult<GetHadalZoneDataScRsp> {
Ok(GetHadalZoneDataScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}

View file

@ -0,0 +1,15 @@
use super::*;
pub async fn on_get_item_data(
_session: &NetSession,
player: &mut Player,
_req: GetItemDataCsReq,
) -> NetResult<GetItemDataScRsp> {
let item_model = &player.item_model;
Ok(GetItemDataScRsp {
retcode: Retcode::RetSucc.into(),
resource_list: item_model.resources.iter().map(|i| i.to_client()).collect(),
..Default::default()
})
}

View file

@ -0,0 +1,59 @@
use common::cryptography::{rsa_decrypt, rsa_encrypt, rsa_sign};
use rand::RngCore;
use crate::{logic::player::PlayerManager, net::NetSessionState};
use super::*;
pub async fn on_player_get_token(
session: &NetSession,
player_mgr: &PlayerManager,
req: PlayerGetTokenCsReq,
) -> NetResult<PlayerGetTokenScRsp> {
session.set_state(NetSessionState::PlayerGetTokenCsReq);
let uid = player_mgr
.authorize(&req.account_uid, &req.token, req.platform_type)
.await?;
let client_rand_key = rbase64::decode(&req.client_rand_key).map_err(|_| Retcode::RetFail)?;
let client_rand_key = u64::from_le_bytes(rsa_decrypt(&client_rand_key).try_into().unwrap());
let server_rand_key = rand::thread_rng().next_u64();
session.set_session_key(client_rand_key ^ server_rand_key);
session.set_account_uid(req.account_uid);
session.set_player_uid(uid);
session.set_state(NetSessionState::PlayerGetTokenScRsp);
Ok(PlayerGetTokenScRsp {
retcode: Retcode::RetSucc.into(),
uid,
server_rand_key: rbase64::encode(&rsa_encrypt(&server_rand_key.to_le_bytes())),
sign: rbase64::encode(&rsa_sign(&server_rand_key.to_le_bytes())),
..Default::default()
})
}
pub async fn on_player_login(
session: &NetSession,
player_mgr: &PlayerManager,
_req: PlayerLoginCsReq,
) -> NetResult<PlayerLoginScRsp> {
session.set_state(NetSessionState::PlayerLoginCsReq);
let player_uid = *session.player_uid().ok_or(Retcode::RetFail)?;
let player_lock = player_mgr
.get_player(player_uid)
.await
.ok_or(Retcode::RetFail)?;
let mut player = player_lock.lock().await;
player.set_current_session(session.id());
player.on_login();
session.set_state(NetSessionState::StartBasicsReq);
Ok(PlayerLoginScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}

View file

@ -0,0 +1,12 @@
use super::*;
pub async fn on_get_player_mails(
_session: &NetSession,
_player: &mut Player,
_req: GetPlayerMailsCsReq,
) -> NetResult<GetPlayerMailsScRsp> {
Ok(GetPlayerMailsScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}

View file

@ -0,0 +1,12 @@
use super::*;
pub async fn on_get_main_city_revival_data(
_session: &NetSession,
_player: &mut Player,
_req: GetMainCityRevivalDataCsReq,
) -> NetResult<GetMainCityRevivalDataScRsp> {
Ok(GetMainCityRevivalDataScRsp {
retcode: Retcode::RetSucc.into(),
main_city_revival: Some(MainCityRevivalData::default()),
})
}

View file

@ -0,0 +1,34 @@
use common::util;
use super::*;
pub async fn on_get_server_timestamp(
_session: &NetSession,
_player: &mut Player,
_req: GetServerTimestampCsReq,
) -> NetResult<GetServerTimestampScRsp> {
Ok(GetServerTimestampScRsp {
retcode: Retcode::RetSucc.into(),
timestamp: util::cur_timestamp_ms(),
utc_offset: 3,
})
}
pub async fn on_get_authkey(
_session: &NetSession,
_player: &mut Player,
_req: GetAuthkeyCsReq,
) -> NetResult<GetAuthkeyScRsp> {
Ok(GetAuthkeyScRsp::default())
}
pub async fn on_get_cut_scene_key_info(
_session: &NetSession,
_player: &mut Player,
_req: GetCutSceneKeyInfoCsReq,
) -> NetResult<GetCutSceneKeyInfoScRsp> {
Ok(GetCutSceneKeyInfoScRsp {
key_map: data::usm_key_map().clone(),
retcode: Retcode::RetSucc.into(),
})
}

View file

@ -0,0 +1,117 @@
mod abyss;
mod activity;
mod arcade;
mod avatar;
mod battle_pass;
mod cafe;
mod character_quest;
mod client_systems;
mod collect;
mod daily_challenge;
mod embattles;
mod fairy;
mod friend;
mod gacha;
mod hadal_zone;
mod item;
mod login;
mod mail;
mod main_city_revival;
mod misc;
mod month_card;
mod perform;
mod player;
mod private_message;
mod quest;
mod ramen;
mod vhs_store;
mod world;
mod core;
use crate::logic::Player;
use core::NetResult;
use core::{auth_req_handlers, notify_handlers, req_handlers};
pub use core::PacketHandlingError;
use crate::net::NetSession;
use proto::*;
auth_req_handlers! {
login::PlayerGetToken;
login::PlayerLogin;
}
req_handlers! {
player::GetPlayerBasicInfo;
player::CreateRole;
player::GetPlayerTransaction;
item::GetItemData;
avatar::GetAvatarData;
avatar::GetBuddyData;
quest::GetQuestData;
quest::GetYorozuyaInfo;
quest::GetArchiveInfo;
abyss::GetAbyssInfo;
gacha::GetGachaData;
ramen::GetRamenData;
cafe::GetCafeData;
cafe::GetRewardBuffData;
mail::GetPlayerMails;
fairy::GetFairyInfo;
client_systems::GetTipsInfo;
client_systems::GetClientSystemsInfo;
private_message::GetPrivateMessageData;
collect::GetCollectMap;
collect::GetWorkbenchInfo;
collect::GetAbyssRewardData;
vhs_store::GetVhsStoreInfo;
activity::GetActivityData;
activity::GetWebActivityData;
embattles::GetEmbattlesData;
client_systems::GetNewsStandData;
client_systems::GetTrashbinHermitData;
client_systems::GetExplorationData;
main_city_revival::GetMainCityRevivalData;
arcade::GetArcadeData;
battle_pass::GetBattlePassData;
hadal_zone::GetHadalZoneData;
daily_challenge::GetDailyChallengeInfo;
abyss::GetCompletedAbyssGroupList;
friend::GetFriendList;
friend::GetFriendLimitInfo;
character_quest::GetCharacterQuestList;
character_quest::GetPhotoWallData;
month_card::GetMonthCardDayReward;
world::EnterWorld;
world::EnterSection;
world::SavePosInMainCity;
world::WorldInitFinish;
world::AdvanceBeginnerProcedure;
world::BeginnerBattleBegin;
world::BeginnerBattleEnd;
world::BeginnerBattleRebegin;
world::StartTrialFightingMission;
world::EndBattle;
world::LeaveCurDungeon;
client_systems::ReportUiLayoutPlatform;
client_systems::PlayerOperation;
client_systems::UnlockNewbieGroup;
client_systems::BattleReport;
client_systems::PopUpWindowSeen;
client_systems::ReportSystemSettingsChange;
client_systems::InteractWithSceneObject;
perform::PerformTrigger;
perform::PerformEnd;
perform::PerformJump;
misc::GetAuthkey;
misc::GetServerTimestamp;
misc::GetCutSceneKeyInfo;
embattles::ReportBattleTeam;
}
notify_handlers! {
player::KeepAlive;
player::PlayerLogout;
}

View file

@ -0,0 +1,12 @@
use super::*;
pub async fn on_get_month_card_day_reward(
_session: &NetSession,
_player: &mut Player,
_req: GetMonthCardDayRewardCsReq,
) -> NetResult<GetMonthCardDayRewardScRsp> {
Ok(GetMonthCardDayRewardScRsp {
retcode: Retcode::RetSucc.into(),
..Default::default()
})
}

View file

@ -0,0 +1,57 @@
use super::core::NetError;
use crate::logic::{
game::{GameInstance, LogicError},
procedure::ProcedureAction,
};
use super::*;
pub async fn on_perform_trigger(
_session: &NetSession,
player: &mut Player,
req: PerformTriggerCsReq,
) -> NetResult<PerformTriggerScRsp> {
let GameInstance::Fresh(fresh_game) = &mut player.game_instance else {
return Err(NetError::from(Retcode::RetFail));
};
fresh_game
.procedure_mgr
.on_action(ProcedureAction::PerformTrigger)
.map_err(LogicError::from)?;
Ok(PerformTriggerScRsp {
retcode: Retcode::RetSucc.into(),
perform_uid: ((req.perform_type as i64) << 32) | req.perform_id as i64,
})
}
pub async fn on_perform_end(
_session: &NetSession,
player: &mut Player,
_req: PerformEndCsReq,
) -> NetResult<PerformEndScRsp> {
let GameInstance::Fresh(fresh_game) = &mut player.game_instance else {
return Err(NetError::from(Retcode::RetFail));
};
fresh_game
.procedure_mgr
.on_action(ProcedureAction::PerformEnd)
.map_err(LogicError::from)?;
Ok(PerformEndScRsp {
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_perform_jump(
_session: &NetSession,
_player: &mut Player,
_req: PerformJumpCsReq,
) -> NetResult<PerformJumpScRsp> {
Ok(PerformJumpScRsp {
retcode: Retcode::RetSucc.into(),
})
}

View file

@ -0,0 +1,75 @@
use crate::logic::game::{GameInstance, LogicError};
use crate::logic::procedure::ProcedureAction;
use super::core::NetError;
use super::*;
pub async fn on_get_player_basic_info(
_session: &NetSession,
player: &mut Player,
_req: GetPlayerBasicInfoCsReq,
) -> NetResult<GetPlayerBasicInfoScRsp> {
Ok(GetPlayerBasicInfoScRsp {
retcode: Retcode::RetSucc.into(),
basic_info: Some(player.basic_data_model.player_basic_info()),
})
}
pub async fn on_create_role(
session: &NetSession,
player: &mut Player,
req: CreateRoleCsReq,
) -> NetResult<CreateRoleScRsp> {
let GameInstance::Fresh(fresh_game) = &mut player.game_instance else {
return Err(NetError::from(Retcode::RetFail));
};
fresh_game
.procedure_mgr
.on_action(ProcedureAction::SelectRole)
.map_err(LogicError::from)?;
player.set_frontend_avatar(req.avatar_id as i32)?;
session
.notify(PlayerSyncScNotify {
basic_info: Some(player.basic_data_model.player_basic_info()),
..Default::default()
})
.await?;
Ok(CreateRoleScRsp {
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_get_player_transaction(
_session: &NetSession,
_player: &mut Player,
_req: GetPlayerTransactionCsReq,
) -> NetResult<GetPlayerTransactionScRsp> {
Ok(GetPlayerTransactionScRsp {
retcode: Retcode::RetSucc.into(),
transaction: format!("{}-{}", 1337, 100),
})
}
pub async fn on_keep_alive(
_session: &NetSession,
_player: &mut Player,
_ntf: KeepAliveNotify,
) -> NetResult<()> {
Ok(())
}
pub async fn on_player_logout(
session: &NetSession,
_player: &mut Player,
_ntf: PlayerLogoutNotify,
) -> NetResult<()> {
tracing::info!(
"player logout requested (uid: {})",
session.player_uid().unwrap()
);
Err(NetError::Logout)
}

View file

@ -0,0 +1,12 @@
use super::*;
pub async fn on_get_private_message_data(
_session: &NetSession,
_player: &mut Player,
_req: GetPrivateMessageDataCsReq,
) -> NetResult<GetPrivateMessageDataScRsp> {
Ok(GetPrivateMessageDataScRsp {
retcode: Retcode::RetSucc.into(),
private_message_data: Some(PrivateMessageData::default()),
})
}

View file

@ -0,0 +1,35 @@
use super::*;
pub async fn on_get_quest_data(
_session: &NetSession,
_player: &mut Player,
req: GetQuestDataCsReq,
) -> NetResult<GetQuestDataScRsp> {
Ok(GetQuestDataScRsp {
retcode: Retcode::RetSucc.into(),
quest_type: req.quest_type,
quest_data: Some(QuestData::default()),
})
}
pub async fn on_get_yorozuya_info(
_session: &NetSession,
_player: &mut Player,
_req: GetYorozuyaInfoCsReq,
) -> NetResult<GetYorozuyaInfoScRsp> {
Ok(GetYorozuyaInfoScRsp {
retcode: Retcode::RetSucc.into(),
yorozuya_info: Some(YorozuyaInfo::default()),
})
}
pub async fn on_get_archive_info(
_session: &NetSession,
_player: &mut Player,
_req: GetArchiveInfoCsReq,
) -> NetResult<GetArchiveInfoScRsp> {
Ok(GetArchiveInfoScRsp {
retcode: Retcode::RetSucc.into(),
archive_info: Some(ArchiveInfo::default()),
})
}

View file

@ -0,0 +1,12 @@
use super::*;
pub async fn on_get_ramen_data(
_session: &NetSession,
_player: &mut Player,
_req: GetRamenDataCsReq,
) -> NetResult<GetRamenDataScRsp> {
Ok(GetRamenDataScRsp {
retcode: Retcode::RetSucc.into(),
ramen_data: Some(RamenData::default()),
})
}

View file

@ -0,0 +1,12 @@
use super::*;
pub async fn on_get_vhs_store_info(
_session: &NetSession,
_player: &mut Player,
_req: GetVhsStoreInfoCsReq,
) -> NetResult<GetVhsStoreInfoScRsp> {
Ok(GetVhsStoreInfoScRsp {
retcode: Retcode::RetSucc.into(),
info: Some(VhsStoreInfo::default()),
})
}

View file

@ -0,0 +1,200 @@
use super::core::NetError;
use crate::{
logic::{game::*, procedure::ProcedureAction, ELocalPlayType},
net::NetSessionState,
};
use super::*;
pub async fn on_enter_world(
session: &NetSession,
player: &mut Player,
_req: EnterWorldCsReq,
) -> NetResult<EnterWorldScRsp> {
session.set_state(NetSessionState::EndBasicsReq);
if player.basic_data_model.beginner_procedure_id != -1 {
player.game_instance = GameInstance::Fresh(FreshGame::new(
player.basic_data_model.beginner_procedure_id,
))
} else {
player.init_frontend_game()?;
}
let world_init_notify = player.game_instance.create_world_init_notify()?;
session.notify(world_init_notify).await?;
session.set_state(NetSessionState::EnterWorldScRsp);
Ok(EnterWorldScRsp {
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_advance_beginner_procedure(
session: &NetSession,
player: &mut Player,
req: AdvanceBeginnerProcedureCsReq,
) -> NetResult<AdvanceBeginnerProcedureScRsp> {
let is_end = {
let GameInstance::Fresh(fresh_game) = &mut player.game_instance else {
return Err(NetError::from(Retcode::RetFail));
};
fresh_game
.procedure_mgr
.try_complete_procedure(req.procedure_id)
.map_err(LogicError::from)?;
player.basic_data_model.beginner_procedure_id = fresh_game.procedure_mgr.procedure_id();
fresh_game.procedure_mgr.is_end()
};
if is_end {
player.init_frontend_game()?;
let world_init_notify = player.game_instance.create_world_init_notify()?;
session.notify(world_init_notify).await?;
}
Ok(AdvanceBeginnerProcedureScRsp {
retcode: Retcode::RetSucc.into(),
next_procedure_id: req.procedure_id,
})
}
pub async fn on_beginner_battle_begin(
_session: &NetSession,
player: &mut Player,
req: BeginnerBattleBeginCsReq,
) -> NetResult<BeginnerBattleBeginScRsp> {
let GameInstance::Fresh(fresh_game) = &mut player.game_instance else {
return Err(NetError::from(Retcode::RetFail));
};
fresh_game
.procedure_mgr
.on_action(ProcedureAction::BeginnerBattleBegin)
.map_err(LogicError::from)?;
Ok(BeginnerBattleBeginScRsp {
retcode: Retcode::RetSucc.into(),
battle_uid: req.battle_id as i64,
})
}
pub async fn on_beginner_battle_end(
_session: &NetSession,
player: &mut Player,
req: BeginnerBattleEndCsReq,
) -> NetResult<BeginnerBattleEndScRsp> {
let GameInstance::Fresh(fresh_game) = &mut player.game_instance else {
return Err(NetError::from(Retcode::RetFail));
};
tracing::info!(
"beginner battle end, id: {}, uid: {}, statistics: {:?}",
req.battle_id,
req.battle_uid,
req.battle_statistics
);
fresh_game
.procedure_mgr
.on_action(ProcedureAction::BeginnerBattleEnd)
.map_err(LogicError::from)?;
Ok(BeginnerBattleEndScRsp {
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_beginner_battle_rebegin(
_session: &NetSession,
_player: &mut Player,
_req: BeginnerBattleRebeginCsReq,
) -> NetResult<BeginnerBattleRebeginScRsp> {
Ok(BeginnerBattleRebeginScRsp {
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_enter_section(
_session: &NetSession,
_player: &mut Player,
_req: EnterSectionCsReq,
) -> NetResult<EnterSectionScRsp> {
Ok(EnterSectionScRsp {
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_save_pos_in_main_city(
_session: &NetSession,
player: &mut Player,
req: SavePosInMainCityCsReq,
) -> NetResult<SavePosInMainCityScRsp> {
if let Some(transform) = req.position {
player
.main_city_model
.update_position(transform.position, transform.rotation);
}
Ok(SavePosInMainCityScRsp {
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_world_init_finish(
_session: &NetSession,
_player: &mut Player,
_req: WorldInitFinishCsReq,
) -> NetResult<WorldInitFinishScRsp> {
Ok(WorldInitFinishScRsp {
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_start_trial_fighting_mission(
session: &NetSession,
player: &mut Player,
req: StartTrialFightingMissionCsReq,
) -> NetResult<StartTrialFightingMissionScRsp> {
player.game_instance = GameInstance::Hollow(
HollowGame::create_training_game(req.quest_id, ELocalPlayType::TrainingRoomFight)
.map_err(LogicError::from)?,
);
let world_init_notify = player.game_instance.create_world_init_notify()?;
session.notify(world_init_notify).await?;
Ok(StartTrialFightingMissionScRsp {
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_end_battle(
_session: &NetSession,
_player: &mut Player,
_req: EndBattleCsReq,
) -> NetResult<EndBattleScRsp> {
Ok(EndBattleScRsp {
battle_reward: Some(BattleRewardInfo::default()),
retcode: Retcode::RetSucc.into(),
})
}
pub async fn on_leave_cur_dungeon(
session: &NetSession,
player: &mut Player,
_req: LeaveCurDungeonCsReq,
) -> NetResult<LeaveCurDungeonScRsp> {
player.init_frontend_game()?;
let world_init_notify = player.game_instance.create_world_init_notify()?;
session.notify(world_init_notify).await?;
Ok(LeaveCurDungeonScRsp {
retcode: Retcode::RetSucc.into(),
})
}

View file

@ -0,0 +1,144 @@
use std::fmt::Display;
use num_enum::FromPrimitive;
#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive)]
#[repr(u32)]
pub enum ESceneType {
#[default]
Unknown = 0,
Hall = 1,
Hollow = 2,
Fight = 3,
Fresh = 4,
MultiFight = 5,
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy, FromPrimitive)]
#[repr(u32)]
pub enum ESystem {
#[default]
None = 0xFFFFFFFF,
VhsStore = 7,
Gacha = 21,
Hadal = 43,
MonsterCard = 40,
AbyssCollect = 45,
Vhs = 1,
ActivityPv = 48,
StoryModeSwitch = 12,
RamenStore = 8,
QteSwitch = 13,
DoubleElite = 36,
LandRevive = 35,
HadalShop = 47,
Role = 2,
Grocery = 10,
AvatarBase = 33,
Activity = 27,
BuddyInstall = 26,
AvatarEquip = 34,
TartarusHounds = 20,
BossBig = 38,
Buddy = 25,
Interknot = 6,
Workbench = 9,
HIA = 39,
Smithy = 3,
Trash = 23,
UseStoryMode = 15,
Photowall = 44,
AbyssShop = 46,
AbyssHeat = 29,
DailyQuest = 41,
Newsstand = 17,
Cafe = 22,
Rally = 42,
HollowQuest = 0,
Package = 4,
VideoShop = 11,
BattleDaily = 24,
Arcade = 19,
BossSmall = 37,
LineupSelect = 14,
ArcadeRoom = 30,
Teleport = 5,
Train = 32,
UseManualQteMode = 16,
ArcadeGame = 31,
Abyss = 28,
Toy = 18,
}
#[derive(Default, Debug, Clone, Copy, FromPrimitive)]
#[repr(u32)]
pub enum EOperator {
Enter = 0,
Log = 1,
Leave = 2,
#[default]
Undefined = 0xFFFFFFFF,
}
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum TimePeriodType {
Morning = 0,
Evening = 1,
Night = 2,
}
impl Display for TimePeriodType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
#[derive(Default, Debug, Clone, Copy)]
#[repr(u32)]
pub enum WeatherType {
SunShine = 0,
Fog = 1,
Cloudy = 2,
Rain = 3,
Thunder = 4,
ThickFog = 5,
ThickCloudy = 6,
#[default]
None = 0xFFFFFFFF,
}
impl Display for WeatherType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
#[derive(Default, Debug, Clone, Copy)]
pub enum ELocalPlayType {
TrainingRoom = 290,
BigBossBattle = 211,
BossLittleBattleLongfight = 215,
PureHollowBattle = 280,
DualElite = 208,
PureHollowBattleHardmode = 282,
OperationBetaDemo = 216,
AvatarDemoTrial = 213,
BossRushBattle = 218,
BossBattle = 210,
GuideSpecial = 203,
DailyChallenge = 206,
OperationTeamCoop = 219,
TrainingRoomFight = 291,
LevelZero = 205,
ArchiveLongFight = 212,
MpBigBossBattle = 214,
RallyLongFight = 207,
BigBossBattleLongfight = 217,
ArchiveBattle = 201,
PureHollowBattleLonghfight = 281,
HadalZone = 209,
ChessBoardBattle = 202,
#[default]
Unknown = 0,
}

View file

@ -0,0 +1,37 @@
use proto::{DungeonInfo, FreshSceneInfo, SceneInfo};
use crate::logic::{procedure::ProcedureManager, ESceneType};
use super::NapGameMode;
pub struct FreshGame {
pub procedure_mgr: ProcedureManager,
}
impl FreshGame {
pub fn new(start_procedure_id: i32) -> Self {
Self {
procedure_mgr: ProcedureManager::new(start_procedure_id),
}
}
}
impl NapGameMode for FreshGame {
fn scene_type(&self) -> ESceneType {
ESceneType::Fresh
}
fn scene_info(&self) -> Option<SceneInfo> {
Some(SceneInfo {
scene_type: self.scene_type() as u32,
fresh_scene_info: Some(FreshSceneInfo {
beginner_procedure_id: (self.procedure_mgr.procedure_id() - 1) as u32,
}),
..Default::default()
})
}
fn dungeon_info(&self) -> Option<DungeonInfo> {
None
}
}

View file

@ -0,0 +1,90 @@
use data::tables;
use proto::*;
use thiserror::Error;
use crate::logic::{math::Vector3f, time::MainCityTime, ESceneType};
use super::NapGameMode;
pub struct FrontendGame {
section_id: u32,
frontend_avatar_id: i32,
camera_x: u32,
camera_y: u32,
born_pos: String,
main_city_time: MainCityTime,
avatar_pos: Vector3f,
avatar_rot: Vector3f,
}
#[derive(Error, Debug)]
pub enum FrontendGameError {
#[error("section id is invalid ({0})")]
InvalidSection(u32),
}
impl FrontendGame {
pub fn new(
section_id: u32,
avatar_id: i32,
main_city_time: MainCityTime,
avatar_pos: Vector3f,
avatar_rot: Vector3f,
) -> Result<Self, FrontendGameError> {
let instance = Self {
section_id,
main_city_time,
frontend_avatar_id: avatar_id,
camera_x: 0xFFFFFFFF,
camera_y: 0xFFFFFFFF,
born_pos: Self::get_default_stage_entry_name(section_id)
.ok_or(FrontendGameError::InvalidSection(section_id))?,
avatar_pos,
avatar_rot,
};
tracing::info!("creating new frontend game (section={section_id}, avatar={avatar_id})");
Ok(instance)
}
fn get_default_stage_entry_name(section_id: u32) -> Option<String> {
tables::section_config_template_tb::iter()
.find(|tmpl| tmpl.section_id == section_id)
.map(|tmpl| tmpl.primary_entry_name.clone())
}
}
impl NapGameMode for FrontendGame {
fn scene_info(&self) -> Option<SceneInfo> {
Some(SceneInfo {
scene_type: self.scene_type() as u32,
hall_scene_info: Some(HallSceneInfo {
section_id: self.section_id,
frontend_avatar_id: self.frontend_avatar_id as u32,
camera_x: self.camera_x,
camera_y: self.camera_y,
born_pos: self
.avatar_pos
.is_zero()
.then_some(self.born_pos.clone())
.unwrap_or_default(),
day_of_week: self.main_city_time.day_of_week.value(),
time_of_day: self.main_city_time.time_of_day.value(),
position: (!self.avatar_pos.is_zero()).then_some(Transform {
position: self.avatar_pos.to_vec(),
rotation: self.avatar_rot.to_vec(),
}),
..Default::default()
}),
..Default::default()
})
}
fn dungeon_info(&self) -> Option<DungeonInfo> {
None
}
fn scene_type(&self) -> ESceneType {
ESceneType::Hall
}
}

View file

@ -0,0 +1,75 @@
use common::util;
use data::tables;
use proto::{DungeonInfo, DungeonItemData, FightSceneInfo, SceneInfo, WeatherPoolInfo};
use thiserror::Error;
use crate::logic::{ELocalPlayType, ESceneType, TimePeriodType, WeatherType};
use super::NapGameMode;
#[derive(Error, Debug)]
pub enum HollowGameError {
#[error("quest id is invalid: {0}")]
InvalidQuestId(u32),
}
pub struct HollowGame {
pub quest_id: u32,
pub battle_event_id: u32,
pub time_period: TimePeriodType,
pub weather: WeatherType,
pub play_type: ELocalPlayType,
pub start_timestamp: i64,
}
impl HollowGame {
pub fn create_training_game(
training_quest_id: u32,
play_type: ELocalPlayType,
) -> Result<Self, HollowGameError> {
let template = tables::training_quest_template_tb::iter()
.find(|tmpl| tmpl.id == training_quest_id)
.ok_or(HollowGameError::InvalidQuestId(training_quest_id))?;
Ok(Self {
quest_id: template.id,
battle_event_id: template.battle_event_id,
time_period: TimePeriodType::Morning,
weather: WeatherType::SunShine,
start_timestamp: util::cur_timestamp() as i64,
play_type,
})
}
}
impl NapGameMode for HollowGame {
fn scene_type(&self) -> ESceneType {
ESceneType::Fight
}
fn scene_info(&self) -> Option<SceneInfo> {
Some(SceneInfo {
scene_type: self.scene_type() as u32,
battle_event_id: self.battle_event_id,
play_type: self.play_type as u32,
fight_scene_info: Some(FightSceneInfo {
weather_pool: Some(WeatherPoolInfo {
time_period: self.time_period.to_string(),
weather: self.weather.to_string(),
..Default::default()
}),
..Default::default()
}),
..Default::default()
})
}
fn dungeon_info(&self) -> Option<DungeonInfo> {
Some(DungeonInfo {
quest_id: self.quest_id,
start_timestamp: self.start_timestamp,
dungeon_item_data: Some(DungeonItemData::default()),
..Default::default()
})
}
}

View file

@ -0,0 +1,60 @@
mod fresh;
mod frontend;
mod hollow;
pub use fresh::*;
pub use frontend::*;
pub use hollow::*;
use proto::{DungeonInfo, SceneInfo, WorldInitScNotify};
use thiserror::Error;
use super::{procedure::ProcedureError, ESceneType};
#[derive(Default)]
pub enum GameInstance {
Frontend(FrontendGame),
Fresh(FreshGame),
Hollow(HollowGame),
#[default]
Null,
}
#[derive(Error, Debug)]
pub enum LogicError {
#[error("game instance is NULL!")]
GameIsNull,
#[error("frontend error: {0}")]
Frontend(#[from] FrontendGameError),
#[error("procedure error: {0}")]
Procedure(#[from] ProcedureError),
#[error("hollow error: {0}")]
Hollow(#[from] HollowGameError),
}
impl GameInstance {
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
pub fn create_world_init_notify(&self) -> Result<WorldInitScNotify, LogicError> {
Ok(Self::world_init_sc_notify(match self {
Self::Frontend(game) => game,
Self::Fresh(game) => game,
Self::Hollow(game) => game,
Self::Null => return Err(LogicError::GameIsNull),
}))
}
fn world_init_sc_notify(game: &dyn NapGameMode) -> WorldInitScNotify {
WorldInitScNotify {
scene_info: game.scene_info(),
dungeon_info: game.dungeon_info(),
}
}
}
pub trait NapGameMode {
fn scene_type(&self) -> ESceneType;
fn scene_info(&self) -> Option<SceneInfo>;
fn dungeon_info(&self) -> Option<DungeonInfo>;
}

View file

@ -0,0 +1,47 @@
use super::ResourceItem;
use proto::ItemModelBin;
pub struct ItemModel {
pub resources: Vec<ResourceItem>,
}
impl ItemModel {
pub fn from_bin(bin: ItemModelBin) -> Self {
Self {
resources: bin
.resource_list
.into_iter()
.map(ResourceItem::from_bin)
.collect(),
}
}
pub fn to_bin(&self) -> ItemModelBin {
ItemModelBin {
resource_list: self.resources.iter().map(ResourceItem::to_bin).collect(),
}
}
pub fn add_resource(&mut self, template_id: u32, num: u32) {
if let Some(item) = self
.resources
.iter_mut()
.find(|item| item.template_id == template_id)
{
item.num += num as i32;
} else {
self.resources.push(ResourceItem {
template_id,
num: num as i32,
});
}
}
}
impl Default for ItemModel {
fn default() -> Self {
Self {
resources: Vec::new(),
}
}
}

View file

@ -0,0 +1,5 @@
mod item_model;
mod resource;
pub use item_model::ItemModel;
pub use resource::ResourceItem;

View file

@ -0,0 +1,29 @@
use proto::{Resource, ResourceItemBin};
pub struct ResourceItem {
pub template_id: u32,
pub num: i32,
}
impl ResourceItem {
pub fn from_bin(bin: ResourceItemBin) -> Self {
Self {
template_id: bin.template_id,
num: bin.num,
}
}
pub fn to_bin(&self) -> ResourceItemBin {
ResourceItemBin {
template_id: self.template_id,
num: self.num,
}
}
pub fn to_client(&self) -> Resource {
Resource {
template_id: self.template_id,
num: self.num,
}
}
}

View file

@ -0,0 +1,24 @@
#[derive(Default, Clone, Debug)]
pub struct Vector3f {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl Vector3f {
pub fn from_vec(v: Vec<f64>) -> Self {
Self {
x: v.get(0).cloned().unwrap_or_default(),
y: v.get(1).cloned().unwrap_or_default(),
z: v.get(2).cloned().unwrap_or_default(),
}
}
pub fn to_vec(&self) -> Vec<f64> {
vec![self.x, self.y, self.z]
}
pub fn is_zero(&self) -> bool {
self.x == 0.0 && self.y == 0.0 && self.z == 0.0
}
}

View file

@ -0,0 +1,11 @@
mod enums;
pub mod game;
pub mod item;
pub mod math;
pub mod player;
pub mod procedure;
pub mod role;
pub mod time;
pub use enums::*;
pub use player::Player;

View file

@ -0,0 +1,62 @@
use proto::{BasicDataModelBin, PlayerBasicInfo};
pub struct BasicDataModel {
pub level: u32,
pub exp: u32,
pub profile_icon: u32,
pub nick_name: Option<String>,
pub frontend_avatar_id: i32,
pub beginner_procedure_id: i32,
}
impl Default for BasicDataModel {
fn default() -> Self {
Self {
level: 1,
exp: 0,
profile_icon: 3200000,
nick_name: None,
frontend_avatar_id: 0,
beginner_procedure_id: 1,
}
}
}
impl BasicDataModel {
pub fn player_basic_info(&self) -> PlayerBasicInfo {
PlayerBasicInfo {
nick_name: self.nick_name.clone().unwrap_or_default(),
exp: self.exp,
level: self.level,
avatar_id: self.frontend_avatar_id as u32,
frontend_avatar_id: self.frontend_avatar_id as u32,
kbjleelonfe: self.profile_icon,
..Default::default()
}
}
pub fn from_bin(bin: BasicDataModelBin) -> Self {
Self {
level: bin.level,
exp: bin.exp,
profile_icon: bin.profile_icon,
frontend_avatar_id: bin.frontend_avatar_id,
beginner_procedure_id: bin.beginner_procedure_id,
nick_name: match bin.nick_name.is_empty() {
true => None,
false => Some(bin.nick_name),
},
}
}
pub fn to_bin(&self) -> BasicDataModelBin {
BasicDataModelBin {
level: self.level,
exp: self.exp,
profile_icon: self.profile_icon,
frontend_avatar_id: self.frontend_avatar_id,
nick_name: self.nick_name.clone().unwrap_or_default(),
beginner_procedure_id: self.beginner_procedure_id,
}
}
}

View file

@ -0,0 +1,37 @@
use std::collections::BTreeSet;
use proto::{LockModelBin, UnlockData};
#[derive(Default)]
pub struct LockModel {
unlock_list: BTreeSet<i32>,
}
impl LockModel {
pub fn from_bin(bin: LockModelBin) -> Self {
Self {
unlock_list: bin.unlock_list.into_iter().collect(),
}
}
pub fn to_bin(&self) -> LockModelBin {
LockModelBin {
unlock_list: self.unlock_list.clone().into_iter().collect(),
}
}
pub fn to_client(&self) -> UnlockData {
UnlockData {
unlock_id_list: self.unlock_list.clone().into_iter().collect(),
..Default::default()
}
}
pub fn add_unlock(&mut self, id: i32) {
self.unlock_list.insert(id);
}
pub fn is_unlock(&self, id: i32) -> bool {
self.unlock_list.contains(&id)
}
}

View file

@ -0,0 +1,50 @@
use proto::{MainCityModelBin, TransformBin};
use super::{math::Vector3f, time::MainCityTime};
pub struct MainCityModel {
pub position: Vector3f,
pub rotation: Vector3f,
pub main_city_time: MainCityTime,
pub section_id: u32,
}
impl MainCityModel {
pub fn update_position(&mut self, pos: Vec<f64>, rot: Vec<f64>) {
self.position = Vector3f::from_vec(pos);
self.rotation = Vector3f::from_vec(rot);
}
pub fn to_bin(&self) -> MainCityModelBin {
MainCityModelBin {
transform: Some(TransformBin {
position: self.position.to_vec(),
rotation: self.position.to_vec(),
}),
time: Some(self.main_city_time.to_bin()),
section_id: self.section_id,
}
}
pub fn from_bin(bin: MainCityModelBin) -> Self {
let transform = bin.transform.unwrap_or_default();
Self {
position: Vector3f::from_vec(transform.position),
rotation: Vector3f::from_vec(transform.rotation),
main_city_time: bin.time.map(MainCityTime::from_bin).unwrap_or_default(),
section_id: bin.section_id,
}
}
}
impl Default for MainCityModel {
fn default() -> Self {
Self {
position: Vector3f::default(),
rotation: Vector3f::default(),
main_city_time: MainCityTime::default(),
section_id: 1,
}
}
}

View file

@ -0,0 +1,12 @@
use super::*;
mod basic_data_model;
mod lock_model;
mod main_city_model;
mod player;
mod player_mgr;
pub use basic_data_model::BasicDataModel;
pub use lock_model::LockModel;
pub use player::Player;
pub use player_mgr::PlayerManager;

View file

@ -0,0 +1,100 @@
use data::tables;
use proto::{ItemStatic, PlayerDataBin, Retcode};
use super::game::{FrontendGame, GameInstance, LogicError};
use super::item::ItemModel;
use super::main_city_model::MainCityModel;
use super::role::RoleModel;
use super::{BasicDataModel, LockModel};
#[derive(Default)]
pub struct Player {
current_session_id: Option<u64>,
pub game_instance: GameInstance,
pub basic_data_model: BasicDataModel,
pub lock_model: LockModel,
pub role_model: RoleModel,
pub item_model: ItemModel,
pub main_city_model: MainCityModel,
}
impl Player {
const MAIN_AVATAR_IDS: [i32; 2] = [2011, 2021];
pub fn save_to_bin(&self) -> PlayerDataBin {
PlayerDataBin {
basic_data_model: Some(self.basic_data_model.to_bin()),
lock_model: Some(self.lock_model.to_bin()),
role_model: Some(self.role_model.to_bin()),
item_model: Some(self.item_model.to_bin()),
main_city_model: Some(self.main_city_model.to_bin()),
}
}
pub fn load_from_bin(bin: PlayerDataBin) -> Self {
Self {
basic_data_model: bin
.basic_data_model
.map(BasicDataModel::from_bin)
.unwrap_or_default(),
lock_model: bin.lock_model.map(LockModel::from_bin).unwrap_or_default(),
role_model: bin.role_model.map(RoleModel::from_bin).unwrap_or_default(),
item_model: bin.item_model.map(ItemModel::from_bin).unwrap_or_default(),
main_city_model: bin
.main_city_model
.map(MainCityModel::from_bin)
.unwrap_or_default(),
..Default::default()
}
}
pub fn on_login(&mut self) {
tables::unlock_config_template_tb::iter()
.for_each(|tmpl| self.lock_model.add_unlock(tmpl.id))
}
pub fn on_first_login(&mut self) {
self.item_model
.add_resource(ItemStatic::FrontendGold as u32, 1_000_000);
self.item_model
.add_resource(ItemStatic::GameDiamond as u32, 1_000_000);
self.item_model.add_resource(ItemStatic::Energy as u32, 240);
}
pub fn init_frontend_game(&mut self) -> Result<(), LogicError> {
let main_city = &self.main_city_model;
self.game_instance = GameInstance::Frontend(
FrontendGame::new(
main_city.section_id,
self.basic_data_model.frontend_avatar_id,
main_city.main_city_time.clone(),
main_city.position.clone(),
main_city.rotation.clone(),
)
.map_err(LogicError::from)?,
);
Ok(())
}
pub fn set_frontend_avatar(&mut self, id: i32) -> Result<(), Retcode> {
(self.basic_data_model.frontend_avatar_id == 0)
.then_some(())
.ok_or(Retcode::RetFail)?;
(Self::MAIN_AVATAR_IDS.contains(&id))
.then_some(())
.ok_or(Retcode::RetFail)?;
self.basic_data_model.frontend_avatar_id = id;
Ok(())
}
pub fn set_current_session(&mut self, id: u64) {
self.current_session_id = Some(id);
}
pub fn current_session_id(&self) -> Option<u64> {
self.current_session_id
}
}

View file

@ -0,0 +1,142 @@
use std::sync::Arc;
use dashmap::DashMap;
use prost::Message;
use proto::{PlayerDataBin, Retcode};
use sqlx::PgPool;
use tokio::sync::Mutex;
use crate::database::{self, DbError};
use super::Player;
pub type SharedPlayerLock = Arc<Mutex<Player>>;
pub struct PlayerManager {
players: DashMap<u32, SharedPlayerLock>,
pg_pool: PgPool,
}
impl PlayerManager {
pub fn new(pg_pool: PgPool) -> Self {
Self {
players: DashMap::new(),
pg_pool,
}
}
pub async fn authorize(
&self,
account_uid: &str,
token: &str,
platform_type: u32,
) -> Result<u32, Retcode> {
let combo_token =
match database::select_combo_token_by_account(&self.pg_pool, account_uid).await {
Ok(row) => row.token,
Err(_) => return Err(Retcode::RetFail),
};
(combo_token == token)
.then_some(())
.ok_or(Retcode::RetFail)?;
let player_uid = match database::select_player_uid_by_account(&self.pg_pool, account_uid)
.await
{
Ok(row) => row.uid,
Err(DbError::NotFound) => {
database::insert_player_uid_by_account(&self.pg_pool, account_uid, platform_type)
.await
.map_err(|err| {
tracing::error!("failed to insert player_uid: {err}");
Retcode::RetFail
})?
.uid
}
Err(_) => return Err(Retcode::RetFail),
} as u32;
Ok(player_uid)
}
pub async fn get_player(&self, uid: u32) -> Option<SharedPlayerLock> {
if let Some(player) = self.players.get(&uid).map(|a| (*a).clone()) {
return Some(player);
}
let player_bin_data =
match database::select_player_data_by_uid(&self.pg_pool, uid as i32).await {
Ok(row) => Some(row.bin_data),
Err(DbError::NotFound) => None,
Err(DbError::SqlxError(err)) => {
tracing::error!("SQL error: {err}");
return None;
}
};
let player = match player_bin_data {
Some(bin_data) => match PlayerDataBin::decode(&*bin_data) {
Ok(bin) => Player::load_from_bin(bin),
Err(err) => {
tracing::error!("failed to decode PlayerDataBin for uid {uid}, error: {err}");
return None;
}
},
None => {
let mut player = Player::default();
player.on_first_login();
match database::insert_player_data_by_uid(
&self.pg_pool,
uid as i32,
player.save_to_bin().encode_to_vec(),
)
.await
{
Ok(()) => player,
Err(err) => {
tracing::error!("failed to insert player data, uid: {uid}, error: {err}");
return None;
}
}
}
};
self.players.insert(uid, Arc::new(Mutex::new(player)));
self.players.get(&uid).map(|a| (*a).clone())
}
pub async fn save(&self, player_uid: u32) {
let Some(player_lock) = self.players.get(&player_uid) else {
tracing::warn!("save: player with uid {player_uid} not found");
return;
};
let player = player_lock.lock().await;
self.do_save(player_uid, &*player).await;
}
pub async fn save_and_remove(&self, player_uid: u32) {
let Some((_, player_lock)) = self.players.remove(&player_uid) else {
tracing::warn!("save_and_remove: player with uid {player_uid} not found");
return;
};
let player = player_lock.lock().await;
self.do_save(player_uid, &*player).await;
}
async fn do_save(&self, uid: u32, player: &Player) {
let bin = player.save_to_bin();
if let Err(err) =
database::update_player_data_by_uid(&self.pg_pool, uid as i32, bin.encode_to_vec())
.await
{
tracing::error!("failed to update player data: {err}");
}
tracing::info!("player data flushed to database, uid: {uid}");
}
}

View file

@ -0,0 +1,58 @@
use super::{ProcedureAction, ProcedureBase, ProcedureError, ProcedureState, ProcedureType};
pub struct ProcedureBattle {
id: i32,
state: ProcedureState,
}
impl ProcedureBattle {
pub fn new(id: i32) -> Self {
Self {
id,
state: ProcedureState::Init,
}
}
}
impl ProcedureBase for ProcedureBattle {
fn id(&self) -> i32 {
self.id
}
fn procedure_type(&self) -> ProcedureType {
ProcedureType::Battle
}
fn on_action(&mut self, action: ProcedureAction) -> Result<ProcedureState, ProcedureError> {
match action {
ProcedureAction::BeginnerBattleBegin => {
(self.state == ProcedureState::Init)
.then_some(())
.ok_or(ProcedureError::InvalidState(action, self.state))?;
self.state = ProcedureState::Running;
}
ProcedureAction::BeginnerBattleEnd => {
(self.state == ProcedureState::Running)
.then_some(())
.ok_or(ProcedureError::InvalidState(action, self.state))?;
self.state = ProcedureState::Finish;
}
ProcedureAction::PerformTrigger => (),
ProcedureAction::PerformEnd => (),
_ => {
return Err(ProcedureError::UnsupportedAction(
action,
self.procedure_type(),
))
}
}
Ok(self.state)
}
fn is_finished(&self) -> bool {
self.state == ProcedureState::Finish
}
}

View file

@ -0,0 +1,102 @@
mod battle;
mod plot_play;
mod procedure_mgr;
mod select_role;
pub use battle::ProcedureBattle;
pub use plot_play::ProcedurePlotPlay;
pub use procedure_mgr::ProcedureManager;
pub use select_role::ProcedureSelectRole;
use data::tables::{self, ProcedureConfigTemplate};
use num_enum::TryFromPrimitive;
use thiserror::Error;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum ProcedureState {
Init,
Running,
Finish,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, TryFromPrimitive)]
#[repr(u32)]
pub enum ProcedureType {
Battle = 1,
PlotPlay = 2,
SelectRole = 4,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ProcedureAction {
PerformTrigger,
PerformEnd,
SelectRole,
BeginnerBattleBegin,
BeginnerBattleEnd,
}
#[derive(Error, Debug)]
pub enum ProcedureError {
#[error("action {0:?} is not allowed for procedure of type {1:?}")]
UnsupportedAction(ProcedureAction, ProcedureType),
#[error("action {0:?} is not allowed in current state: {1:?}")]
InvalidState(ProcedureAction, ProcedureState),
#[error("can't advance procedure because it's not finished")]
NotFinished,
#[error("trying to complete procedure: {0}, current procedure: {1}")]
InvalidProcedureId(i32, i32),
#[error("current procedure is NULL!")]
ProcedureIsNull,
}
pub trait ProcedureBase {
fn id(&self) -> i32;
fn procedure_type(&self) -> ProcedureType;
fn get_next_id(&self) -> Option<&i32> {
let config = tables::procedure_config_template_tb::iter()
.find(|tmpl| tmpl.procedure_id == self.id())
.unwrap();
config.jump_tos.iter().next()
}
fn on_action(&mut self, action: ProcedureAction) -> Result<ProcedureState, ProcedureError>;
fn is_finished(&self) -> bool;
}
pub enum Procedure {
Battle(ProcedureBattle),
PlotPlay(ProcedurePlotPlay),
SelectRole(ProcedureSelectRole),
}
impl Procedure {
pub fn new(template: &ProcedureConfigTemplate) -> Self {
use ProcedureType::*;
let procedure_type = ProcedureType::try_from_primitive(template.procedure_type).unwrap();
match procedure_type {
Battle => Procedure::Battle(ProcedureBattle::new(template.procedure_id)),
PlotPlay => Procedure::PlotPlay(ProcedurePlotPlay::new(template.procedure_id)),
SelectRole => Procedure::SelectRole(ProcedureSelectRole::new(template.procedure_id)),
}
}
pub fn base(&self) -> &dyn ProcedureBase {
match self {
Self::Battle(proc) => proc,
Self::PlotPlay(proc) => proc,
Self::SelectRole(proc) => proc,
}
}
pub fn base_mut(&mut self) -> &mut dyn ProcedureBase {
match self {
Self::Battle(proc) => proc,
Self::PlotPlay(proc) => proc,
Self::SelectRole(proc) => proc,
}
}
}

View file

@ -0,0 +1,56 @@
use super::{ProcedureAction, ProcedureBase, ProcedureError, ProcedureState, ProcedureType};
pub struct ProcedurePlotPlay {
id: i32,
state: ProcedureState,
}
impl ProcedurePlotPlay {
pub fn new(id: i32) -> Self {
Self {
id,
state: ProcedureState::Init,
}
}
}
impl ProcedureBase for ProcedurePlotPlay {
fn id(&self) -> i32 {
self.id
}
fn procedure_type(&self) -> ProcedureType {
ProcedureType::PlotPlay
}
fn on_action(&mut self, action: ProcedureAction) -> Result<ProcedureState, ProcedureError> {
match action {
ProcedureAction::PerformTrigger => {
(self.state == ProcedureState::Init)
.then_some(())
.ok_or(ProcedureError::InvalidState(action, self.state))?;
self.state = ProcedureState::Running;
}
ProcedureAction::PerformEnd => {
(self.state == ProcedureState::Running)
.then_some(())
.ok_or(ProcedureError::InvalidState(action, self.state))?;
self.state = ProcedureState::Finish
}
_ => {
return Err(ProcedureError::UnsupportedAction(
action,
self.procedure_type(),
))
}
}
Ok(self.state)
}
fn is_finished(&self) -> bool {
self.state >= ProcedureState::Running
}
}

View file

@ -0,0 +1,66 @@
use data::tables;
use super::{Procedure, ProcedureAction, ProcedureError};
pub struct ProcedureManager {
cur_procedure_id: i32,
procedures: Vec<Procedure>,
}
impl ProcedureManager {
pub fn new(start_procedure_id: i32) -> Self {
Self {
cur_procedure_id: start_procedure_id,
procedures: tables::procedure_config_template_tb::iter()
.filter(|tmpl| tmpl.procedure_id >= start_procedure_id)
.map(Procedure::new)
.collect(),
}
}
pub fn try_complete_procedure(&mut self, procedure_id: i32) -> Result<(), ProcedureError> {
(self.cur_procedure_id == procedure_id)
.then_some(())
.ok_or(ProcedureError::InvalidProcedureId(
procedure_id,
self.cur_procedure_id,
))?;
let procedure = self
.procedures
.iter()
.find(|proc| proc.base().id() == procedure_id)
.ok_or(ProcedureError::ProcedureIsNull)?;
if procedure.base().is_finished() {
self.cur_procedure_id = procedure.base().get_next_id().cloned().unwrap_or(-1);
Ok(())
} else {
Err(ProcedureError::NotFinished)
}
}
pub fn on_action(&mut self, action: ProcedureAction) -> Result<(), ProcedureError> {
let procedure = self
.procedures
.iter_mut()
.find(|proc| proc.base().id() == self.cur_procedure_id)
.ok_or(ProcedureError::ProcedureIsNull)?;
let state = procedure.base_mut().on_action(action)?;
tracing::info!(
"procedure action {action:?} performed, state: {state:?}, procedure id: {}",
self.cur_procedure_id
);
Ok(())
}
pub fn procedure_id(&self) -> i32 {
self.cur_procedure_id
}
pub fn is_end(&self) -> bool {
self.cur_procedure_id == -1
}
}

View file

@ -0,0 +1,51 @@
use super::{ProcedureAction, ProcedureBase, ProcedureError, ProcedureState, ProcedureType};
pub struct ProcedureSelectRole {
id: i32,
state: ProcedureState,
}
impl ProcedureSelectRole {
pub fn new(id: i32) -> Self {
Self {
id,
state: ProcedureState::Init,
}
}
}
impl ProcedureBase for ProcedureSelectRole {
fn id(&self) -> i32 {
self.id
}
fn procedure_type(&self) -> ProcedureType {
ProcedureType::SelectRole
}
fn on_action(&mut self, action: ProcedureAction) -> Result<ProcedureState, ProcedureError> {
match action {
ProcedureAction::SelectRole => {
(self.state == ProcedureState::Init)
.then_some(())
.ok_or(ProcedureError::InvalidState(action, self.state))?;
self.state = ProcedureState::Finish;
}
ProcedureAction::PerformTrigger => (),
ProcedureAction::PerformEnd => (),
_ => {
return Err(ProcedureError::UnsupportedAction(
action,
self.procedure_type(),
))
}
}
Ok(self.state)
}
fn is_finished(&self) -> bool {
self.state == ProcedureState::Finish
}
}

View file

@ -0,0 +1,88 @@
use proto::{AvatarBin, AvatarInfo, AvatarSkillInfo};
use super::AvatarSkill;
pub const AVATAR_TALENT_COUNT: usize = 6;
pub struct Avatar {
pub template_id: u32,
pub level: u32,
pub exp: u32,
pub star: u32,
pub rank: u32,
pub unlocked_talent_num: u32,
pub skill_list: Vec<AvatarSkill>,
pub talent_switch: [bool; AVATAR_TALENT_COUNT],
}
impl Avatar {
pub fn new(template_id: u32) -> Self {
Self {
template_id,
level: 60,
exp: 0,
star: 0,
rank: 6,
unlocked_talent_num: 6,
skill_list: (0..=6)
.map(|st| AvatarSkill {
skill_type: st,
level: 1,
})
.collect(),
talent_switch: [true; AVATAR_TALENT_COUNT],
}
}
pub fn from_bin(bin: AvatarBin) -> Self {
Self {
template_id: bin.template_id,
level: bin.level,
exp: bin.exp,
star: bin.star,
rank: bin.rank,
unlocked_talent_num: bin.unlocked_talent_num,
skill_list: bin
.avatar_skill_list
.into_iter()
.map(AvatarSkill::from_bin)
.collect(),
talent_switch: bin
.talent_switch_list
.try_into()
.unwrap_or([false; AVATAR_TALENT_COUNT]),
}
}
pub fn to_bin(&self) -> AvatarBin {
AvatarBin {
template_id: self.template_id,
exp: self.exp,
level: self.level,
star: self.star,
rank: self.rank,
unlocked_talent_num: self.unlocked_talent_num,
avatar_skill_list: self.skill_list.iter().map(AvatarSkill::to_bin).collect(),
talent_switch_list: self.talent_switch.to_vec(),
}
}
pub fn to_client(&self) -> AvatarInfo {
AvatarInfo {
template_id: self.template_id,
level: self.level,
skill_list: self
.skill_list
.iter()
.map(|s| AvatarSkillInfo {
skill_type: s.skill_type,
level: s.level,
})
.collect(),
exp: self.exp, // exp?
rank: self.rank,
talent_switch_list: self.talent_switch.to_vec(),
unlocked_talent_num: self.unlocked_talent_num,
..Default::default()
}
}
}

View file

@ -0,0 +1,7 @@
mod avatar;
mod role_model;
mod skill;
pub use avatar::Avatar;
pub use role_model::RoleModel;
pub use skill::AvatarSkill;

View file

@ -0,0 +1,34 @@
use data::tables;
use proto::RoleModelBin;
use crate::logic::role::Avatar;
pub struct RoleModel {
pub avatar_list: Vec<Avatar>,
}
impl Default for RoleModel {
fn default() -> Self {
Self {
avatar_list: tables::avatar_base_template_tb::iter()
.map(|a| a.id)
.filter(|tmpl_id| *tmpl_id < 2000)
.map(|tmpl_id| Avatar::new(tmpl_id))
.collect(),
}
}
}
impl RoleModel {
pub fn from_bin(bin: RoleModelBin) -> Self {
Self {
avatar_list: bin.avatar_list.into_iter().map(Avatar::from_bin).collect(),
}
}
pub fn to_bin(&self) -> RoleModelBin {
RoleModelBin {
avatar_list: self.avatar_list.iter().map(Avatar::to_bin).collect(),
}
}
}

View file

@ -0,0 +1,22 @@
use proto::AvatarSkillBin;
pub struct AvatarSkill {
pub skill_type: u32,
pub level: u32,
}
impl AvatarSkill {
pub fn from_bin(bin: AvatarSkillBin) -> Self {
Self {
skill_type: bin.skill_type,
level: bin.level,
}
}
pub fn to_bin(&self) -> AvatarSkillBin {
AvatarSkillBin {
skill_type: self.skill_type,
level: self.level,
}
}
}

View file

@ -0,0 +1,102 @@
use proto::MainCityTimeBin;
#[derive(Clone)]
pub struct MainCityTime {
pub day_of_week: DayOfWeek,
pub time_of_day: TimeOfDay,
}
impl MainCityTime {
const DEFAULT_DAY: u32 = 1;
const DEFAULT_TIME: u32 = 1080;
pub fn tick(&mut self) {
if self.time_of_day.tick() {
self.day_of_week.next();
}
}
pub fn fast_forward(&mut self, minutes: u32) {
let days_passed = self.time_of_day.fast_forward(minutes);
self.day_of_week.fast_forward(days_passed)
}
pub fn to_bin(&self) -> MainCityTimeBin {
MainCityTimeBin {
minutes: self.time_of_day.value(),
day: self.day_of_week.value(),
}
}
pub fn from_bin(bin: MainCityTimeBin) -> Self {
Self {
time_of_day: TimeOfDay::new(bin.minutes),
day_of_week: DayOfWeek::new(bin.day),
}
}
}
impl Default for MainCityTime {
fn default() -> Self {
Self {
day_of_week: DayOfWeek::new(Self::DEFAULT_DAY),
time_of_day: TimeOfDay::new(Self::DEFAULT_TIME),
}
}
}
#[derive(Clone)]
pub struct DayOfWeek(u32);
#[derive(Clone)]
pub struct TimeOfDay(u32);
impl DayOfWeek {
const DAYS: u32 = 7;
pub fn new(day: u32) -> Self {
Self(day % Self::DAYS)
}
pub fn value(&self) -> u32 {
self.0
}
pub fn next(&mut self) {
self.0 = (self.0 + 1) % Self::DAYS
}
pub fn fast_forward(&mut self, days: u32) {
self.0 = (self.0 + days) % Self::DAYS
}
}
impl TimeOfDay {
const TIME_PERIOD: u32 = 1440;
pub fn new(minutes: u32) -> Self {
Self(minutes % Self::TIME_PERIOD)
}
pub fn value(&self) -> u32 {
self.0
}
pub fn is_zero(&self) -> bool {
(self.0 % Self::TIME_PERIOD) == 0
}
pub fn tick(&mut self) -> bool {
self.0 = (self.0 + 1) % Self::TIME_PERIOD;
self.is_zero()
}
pub fn fast_forward(&mut self, minutes: u32) -> u32 {
self.0 += minutes;
let days_passed = self.0 / Self::TIME_PERIOD;
self.0 %= Self::TIME_PERIOD;
days_passed
}
}

View file

@ -0,0 +1,45 @@
use std::{error::Error, sync::Arc};
use common::{init_tracing, splash, util::load_or_create_config};
use config::NapGSConfig;
use data::init_data;
use logic::player::PlayerManager;
use net::NetSessionManager;
mod config;
mod database;
mod handlers;
mod logic;
mod net;
pub struct ServerState {
pub session_mgr: NetSessionManager,
pub player_mgr: PlayerManager,
pub config: NapGSConfig,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
splash::print("GameServer");
init_tracing();
tracing::info!("don't forget to visit https://discord.xeondev.com/");
let config = load_or_create_config::<NapGSConfig>("nap_gameserver.toml");
init_data(&config.assets).expect("failed to init data");
let pg_pool = match common::database::init(&config.database_credentials).await {
Ok(pool) => pool,
Err(err) => {
tracing::error!("failed to connect to database: {err}");
std::process::exit(1);
}
};
let state = ServerState {
session_mgr: NetSessionManager::new(),
player_mgr: PlayerManager::new(pg_pool),
config,
};
net::listen(Arc::new(state)).await
}

Some files were not shown because too many files have changed in this diff Show more