Init commit
This commit is contained in:
parent
2d877cdf07
commit
61c0cf2eb0
24 changed files with 1407 additions and 2 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -5,7 +5,6 @@ target/
|
||||||
|
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||||
Cargo.lock
|
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
@ -18,4 +17,6 @@ Cargo.lock
|
||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
.idea/
|
||||||
|
|
||||||
|
releases
|
108
Cargo.lock
generated
Normal file
108
Cargo.lock
generated
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.22.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idlib"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "newestidmangler"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"idlib",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.210"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.210"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.128"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "newestidmangler"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
base64 = "0.22.1"
|
||||||
|
idlib = { path = "./idlib" }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
59
config.md
Normal file
59
config.md
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
# Values Guide
|
||||||
|
## Name
|
||||||
|
**Name** is stored as a String. It must be a valid Wynncraft item for it to display as intended.
|
||||||
|
## Shiny ID
|
||||||
|
not yet implemented
|
||||||
|
## Powders
|
||||||
|
|
||||||
|
### Powder Limit
|
||||||
|
Powder limit cannot be increased beyond 255. This is because the powder count is stored as a byte.
|
||||||
|
Potential value range: 0<->255.
|
||||||
|
|
||||||
|
### Format
|
||||||
|
Inside the "powders":[ ] array, set it up in the following structure.
|
||||||
|
**{ "type":"$TYPE", "tier":$TIER, "amount": $AMOUNT }**
|
||||||
|
**TYPE** is stored as a single character.
|
||||||
|
Potential Values: "E" "T" "W" "F" "A" . These represent Earth, Thunder, Wind, Fire, Air.
|
||||||
|
**TIER** is stored as a single digit integer.
|
||||||
|
Potential value range: 1<->6.
|
||||||
|
**AMOUNT** is stored as an integer. It is optional. If not provided it falls back to 1.
|
||||||
|
Potential value range: 1<->255.
|
||||||
|
#### Other things about powder format:
|
||||||
|
Each value in the array must have a comma at the end except the last.
|
||||||
|
The use of spaces is optional, as well as letter case for the $TYPE value.
|
||||||
|
The keys ("type" "tier" "amount") must all be lowercase.
|
||||||
|
If type is invalid, it will default back to being Thunder powder.
|
||||||
|
#### Powders Example
|
||||||
|
```js
|
||||||
|
"powders": [
|
||||||
|
{ "type":"T", "tier":6, "amount":5 },
|
||||||
|
{"type":"e","tier":1,"amount":5},
|
||||||
|
{"type":"F", "tier":3,"amount":1},
|
||||||
|
{ "type" : "w" , "tier":6 }
|
||||||
|
],
|
||||||
|
```
|
||||||
|
Note that the last powder block in array has no comma at the end.
|
||||||
|
### Which items can have powders?
|
||||||
|
Powders can only be encoded on an item that originally supported powders in the first place.
|
||||||
|
Unfortunately you can't add powders to an item that didn't originally have them.
|
||||||
|
e.g. can't put powder on depressing shears, as depressing weapons don't have powder slot.
|
||||||
|
|
||||||
|
## Identifications
|
||||||
|
### Format
|
||||||
|
Inside the "ids":[] array, set it up in the following structure.
|
||||||
|
**{"id": "$ID","base": $BASE,"roll": $ROLL}**
|
||||||
|
**ID** is stored as a string corresponding to the Wynntils internal ID string of any roll. See here for a list: https://raw.githubusercontent.com/Wynntils/Static-Storage/main/Reference/id_keys.json .
|
||||||
|
**BASE** is the base roll. The default base data is defined in (WARNING: EXTREMELY MASSIVE TEXT FILE) https://raw.githubusercontent.com/Wynntils/Static-Storage/main/Reference/gear.json .
|
||||||
|
Beautify it then look for `ITEMNAME > base > IDENTIFICATION > raw` for default value. This base value defines the listed ID ranges.
|
||||||
|
**ROLL** defines the actual rolled value for the Identification. The formula is `BASE * ROLL / 100`. Most values are in the range 30-130 due to how Wynncraft handles many Identifications as 30-130 percent of a base stat. Thus, if you are trying to find the ROLL value, try your desired roll **(NOT the roll percentage 1-100)** divided by the BASE then round it to the nearest integer. This value is optional, and you should only exclude it when it is a fixed value e.g. Skill Points.
|
||||||
|
|
||||||
|
## Rerolls
|
||||||
|
Optional single value, i8. Stores number of rerolls. If missing or is 0, rerolls are not encoded.
|
||||||
|
Potential range: 0<->255.
|
||||||
|
|
||||||
|
### Other things about this
|
||||||
|
The format obeys the same json rules as the powders.
|
||||||
|
If you are trying to get the 100% or 0% roll and the value you calculated is close to 130 or 30, change it to 130 or 30 because that is the real value.
|
||||||
|
|
||||||
|
# Current issues
|
||||||
|
Crafteds are not implemented
|
115
id_keys.json
Normal file
115
id_keys.json
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
{
|
||||||
|
"1stSpellCost": 0,
|
||||||
|
"2ndSpellCost": 1,
|
||||||
|
"3rdSpellCost": 2,
|
||||||
|
"4thSpellCost": 3,
|
||||||
|
"airDamage": 4,
|
||||||
|
"airDefence": 5,
|
||||||
|
"airMainAttackDamage": 6,
|
||||||
|
"airSpellDamage": 7,
|
||||||
|
"damageFromMobs": 8,
|
||||||
|
"earthDamage": 9,
|
||||||
|
"earthDefence": 10,
|
||||||
|
"earthMainAttackDamage": 11,
|
||||||
|
"earthSpellDamage": 12,
|
||||||
|
"elementalDamage": 13,
|
||||||
|
"elementalDefence": 14,
|
||||||
|
"elementalDefense": 15,
|
||||||
|
"elementalSpellDamage": 16,
|
||||||
|
"exploding": 17,
|
||||||
|
"fireDamage": 18,
|
||||||
|
"fireDefence": 19,
|
||||||
|
"fireSpellDamage": 20,
|
||||||
|
"gatherSpeed": 21,
|
||||||
|
"gatherXpBonus": 22,
|
||||||
|
"healingEfficiency": 23,
|
||||||
|
"healthRegen": 24,
|
||||||
|
"healthRegenRaw": 25,
|
||||||
|
"jumpHeight": 26,
|
||||||
|
"knockback": 27,
|
||||||
|
"leveledLootBonus": 28,
|
||||||
|
"leveledXpBonus": 29,
|
||||||
|
"lifeSteal": 30,
|
||||||
|
"lootBonus": 31,
|
||||||
|
"lootQuality": 32,
|
||||||
|
"mainAttackDamage": 33,
|
||||||
|
"manaRegen": 34,
|
||||||
|
"manaSteal": 35,
|
||||||
|
"poison": 36,
|
||||||
|
"raw1stSpellCost": 37,
|
||||||
|
"raw2ndSpellCost": 38,
|
||||||
|
"raw3rdSpellCost": 39,
|
||||||
|
"raw4thSpellCost": 40,
|
||||||
|
"rawAgility": 41,
|
||||||
|
"rawAirDamage": 42,
|
||||||
|
"rawAirMainAttackDamage": 43,
|
||||||
|
"rawAirSpellDamage": 44,
|
||||||
|
"rawAttackSpeed": 45,
|
||||||
|
"rawDefence": 46,
|
||||||
|
"rawDexterity": 47,
|
||||||
|
"rawEarthDamage": 48,
|
||||||
|
"rawEarthSpellDamage": 49,
|
||||||
|
"rawElementalDamage": 50,
|
||||||
|
"rawElementalMainAttackDamage": 51,
|
||||||
|
"rawElementalSpellDamage": 52,
|
||||||
|
"rawFireDamage": 53,
|
||||||
|
"rawFireMainAttackDamage": 54,
|
||||||
|
"rawFireSpellDamage": 55,
|
||||||
|
"rawHealth": 56,
|
||||||
|
"rawIntelligence": 57,
|
||||||
|
"rawMainAttackDamage": 58,
|
||||||
|
"rawNeutralDamage": 59,
|
||||||
|
"rawNeutralSpellDamage": 60,
|
||||||
|
"rawSpellDamage": 61,
|
||||||
|
"rawStrength": 62,
|
||||||
|
"rawThunderDamage": 63,
|
||||||
|
"rawThunderMainAttackDamage": 64,
|
||||||
|
"rawThunderSpellDamage": 65,
|
||||||
|
"rawWaterDamage": 66,
|
||||||
|
"rawWaterMainAttackDamage": 67,
|
||||||
|
"rawWaterSpellDamage": 68,
|
||||||
|
"reflection": 69,
|
||||||
|
"slowEnemy": 70,
|
||||||
|
"soulPointRegen": 71,
|
||||||
|
"spellDamage": 72,
|
||||||
|
"sprint": 73,
|
||||||
|
"sprintRegen": 74,
|
||||||
|
"stealing": 75,
|
||||||
|
"thorns": 76,
|
||||||
|
"thunderDamage": 77,
|
||||||
|
"thunderDefence": 78,
|
||||||
|
"thunderMainAttackDamage": 79,
|
||||||
|
"thunderSpellDamage": 80,
|
||||||
|
"walkSpeed": 81,
|
||||||
|
"waterDamage": 82,
|
||||||
|
"waterDefence": 83,
|
||||||
|
"waterSpellDamage": 84,
|
||||||
|
"weakenEnemy": 85,
|
||||||
|
"xpBonus": 86,
|
||||||
|
"healing": 87,
|
||||||
|
"rawEarthMainAttackDamage": 88,
|
||||||
|
"agility": 89,
|
||||||
|
"baseAirDamage": 90,
|
||||||
|
"baseAirDefence": 91,
|
||||||
|
"baseDamage": 92,
|
||||||
|
"baseEarthDamage": 93,
|
||||||
|
"baseEarthDefence": 94,
|
||||||
|
"baseFireDamage": 95,
|
||||||
|
"baseFireDefence": 96,
|
||||||
|
"baseHealth": 97,
|
||||||
|
"baseThunderDamage": 98,
|
||||||
|
"baseThunderDefence": 99,
|
||||||
|
"baseWaterDamage": 100,
|
||||||
|
"baseWaterDefence": 101,
|
||||||
|
"damage": 102,
|
||||||
|
"defence": 103,
|
||||||
|
"dexterity": 104,
|
||||||
|
"elementalMainAttackDamage": 105,
|
||||||
|
"fireMainAttackDamage": 106,
|
||||||
|
"intelligence": 107,
|
||||||
|
"neutralDamage": 108,
|
||||||
|
"neutralMainAttackDamage": 109,
|
||||||
|
"rawDamage": 110,
|
||||||
|
"rawNeutralMainAttackDamage": 111,
|
||||||
|
"strength": 112
|
||||||
|
}
|
6
idlib/Cargo.toml
Normal file
6
idlib/Cargo.toml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[package]
|
||||||
|
name = "idlib"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
36
idlib/src/data_transformer/enddata.rs
Normal file
36
idlib/src/data_transformer/enddata.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
use crate::types::transform::TransformVersion;
|
||||||
|
|
||||||
|
use super::{DataDecoder, DataEncoder, DataTransformerTypes, TransformId};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct EndData;
|
||||||
|
|
||||||
|
impl TransformId for EndData {
|
||||||
|
fn get_id() -> u8 {
|
||||||
|
DataTransformerTypes::EndDataTransformer as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataEncoder for EndData {
|
||||||
|
fn encode_data(
|
||||||
|
&self,
|
||||||
|
_ver: TransformVersion,
|
||||||
|
_out: &mut Vec<u8>,
|
||||||
|
) -> Result<(), super::DataTransformError> {
|
||||||
|
// end data is always empty
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: Iterator<Item = u8>> DataDecoder<B> for EndData {
|
||||||
|
fn decode_data(
|
||||||
|
_bytes: &mut B,
|
||||||
|
_ver: TransformVersion,
|
||||||
|
) -> Result<Self, super::DataTransformError>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
// end data is always empty
|
||||||
|
return Ok(Self);
|
||||||
|
}
|
||||||
|
}
|
175
idlib/src/data_transformer/identdata.rs
Normal file
175
idlib/src/data_transformer/identdata.rs
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
use crate::{
|
||||||
|
data_transformer::DataTransformError,
|
||||||
|
encoding::{decode_varint, encode_varint},
|
||||||
|
types::{
|
||||||
|
stat::{RollType, Stat, StatId},
|
||||||
|
transform::TransformVersion,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{DataDecoder, DataEncoder, DataTransformerTypes, TransformId};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct IdentificationData {
|
||||||
|
pub identifications: Vec<Stat>,
|
||||||
|
pub extended_encoding: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransformId for IdentificationData {
|
||||||
|
fn get_id() -> u8 {
|
||||||
|
DataTransformerTypes::IdentificationDataTransformer as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataEncoder for IdentificationData {
|
||||||
|
fn encode_data(
|
||||||
|
&self,
|
||||||
|
ver: TransformVersion,
|
||||||
|
out: &mut Vec<u8>,
|
||||||
|
) -> Result<(), super::DataTransformError> {
|
||||||
|
match ver {
|
||||||
|
TransformVersion::Version1 => {
|
||||||
|
if self.identifications.len() > 255 {
|
||||||
|
return Err(DataTransformError::TooManyIdentifications);
|
||||||
|
}
|
||||||
|
|
||||||
|
let encoded_id_count: u8 = self
|
||||||
|
.identifications
|
||||||
|
.iter()
|
||||||
|
.filter(|id| !id.pre_identified())
|
||||||
|
.count() as u8;
|
||||||
|
|
||||||
|
out.push(encoded_id_count);
|
||||||
|
out.push(self.extended_encoding as u8);
|
||||||
|
|
||||||
|
self.encode_individual_idents(out)?;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_encode_data(&self, ver: TransformVersion) -> bool {
|
||||||
|
match ver {
|
||||||
|
TransformVersion::Version1 => {
|
||||||
|
if self.extended_encoding {
|
||||||
|
return self.identifications.len() != 0;
|
||||||
|
} else {
|
||||||
|
return self
|
||||||
|
.identifications
|
||||||
|
.iter()
|
||||||
|
.any(|id: &Stat| !id.pre_identified());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdentificationData {
|
||||||
|
fn encode_individual_idents(&self, bytes: &mut Vec<u8>) -> Result<(), DataTransformError> {
|
||||||
|
// encode the static values if extended encoding is used
|
||||||
|
if self.extended_encoding {
|
||||||
|
let preid_stats: Vec<_> = self
|
||||||
|
.identifications
|
||||||
|
.iter()
|
||||||
|
.filter(|id| id.pre_identified())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
bytes.push(preid_stats.len() as u8);
|
||||||
|
|
||||||
|
for stat in preid_stats {
|
||||||
|
// first add the id of the ident
|
||||||
|
bytes.push(stat.kind.0);
|
||||||
|
|
||||||
|
// then add the basevalue
|
||||||
|
bytes.append(&mut encode_varint(
|
||||||
|
stat.base.ok_or(DataTransformError::NoBasevalueForIdent)? as i64,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ident in self.identifications.iter() {
|
||||||
|
// only handle non preids since preids are encoded using the earlier system
|
||||||
|
if let RollType::Value(roll_val) = ident.roll {
|
||||||
|
// add id of the ident
|
||||||
|
bytes.push(ident.kind.0);
|
||||||
|
|
||||||
|
if self.extended_encoding {
|
||||||
|
// push the baseval
|
||||||
|
bytes.append(&mut encode_varint(
|
||||||
|
ident.base.ok_or(DataTransformError::NoBasevalueForIdent)? as i64,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes.push(roll_val);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: Iterator<Item = u8>> DataDecoder<B> for IdentificationData {
|
||||||
|
fn decode_data(bytes: &mut B, ver: TransformVersion) -> Result<Self, super::DataTransformError>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
match ver {
|
||||||
|
TransformVersion::Version1 => {
|
||||||
|
let mut idents = Vec::new();
|
||||||
|
|
||||||
|
// first byte is the number of identifications
|
||||||
|
let ident_count = bytes.next().unwrap();
|
||||||
|
|
||||||
|
// second byte is whether or not extended coding is used
|
||||||
|
let extended_encoding = bytes.next().unwrap() == 1;
|
||||||
|
|
||||||
|
let mut preid_count = 0;
|
||||||
|
if extended_encoding {
|
||||||
|
// count of preid idents
|
||||||
|
preid_count = bytes.next().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..(ident_count + preid_count) {
|
||||||
|
// id of the ident
|
||||||
|
let id = bytes.next().unwrap();
|
||||||
|
|
||||||
|
let preid = i < preid_count;
|
||||||
|
|
||||||
|
// decode the possible baseval if using extended coding
|
||||||
|
let baseval = if extended_encoding {
|
||||||
|
Some(decode_varint(bytes) as i32)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// if preid skip decoding the value
|
||||||
|
if preid {
|
||||||
|
idents.push(Stat {
|
||||||
|
kind: StatId(id),
|
||||||
|
base: baseval,
|
||||||
|
roll: RollType::PreIdentified,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// decode the roll
|
||||||
|
let introll = bytes.next().unwrap();
|
||||||
|
|
||||||
|
idents.push(Stat {
|
||||||
|
kind: StatId(id),
|
||||||
|
base: baseval,
|
||||||
|
roll: RollType::Value(introll),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
identifications: idents,
|
||||||
|
extended_encoding,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
130
idlib/src/data_transformer/mod.rs
Normal file
130
idlib/src/data_transformer/mod.rs
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
use enddata::EndData;
|
||||||
|
use identdata::IdentificationData;
|
||||||
|
use namedata::NameData;
|
||||||
|
use powderdata::PowderData;
|
||||||
|
use rerolldata::RerollData;
|
||||||
|
use startdata::StartData;
|
||||||
|
use typedata::TypeData;
|
||||||
|
|
||||||
|
use crate::types::transform::TransformVersion;
|
||||||
|
|
||||||
|
pub mod enddata;
|
||||||
|
pub mod identdata;
|
||||||
|
pub mod namedata;
|
||||||
|
pub mod powderdata;
|
||||||
|
pub mod rerolldata;
|
||||||
|
pub mod shinydata;
|
||||||
|
pub mod startdata;
|
||||||
|
pub mod typedata;
|
||||||
|
|
||||||
|
pub trait TransformId {
|
||||||
|
fn get_id() -> u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait DataEncoder: TransformId {
|
||||||
|
fn encode(&self, ver: TransformVersion, out: &mut Vec<u8>) -> Result<(), DataTransformError> {
|
||||||
|
// skip encoding data which should not be encoded
|
||||||
|
if !self.should_encode_data(ver) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode the id
|
||||||
|
out.push(Self::get_id());
|
||||||
|
|
||||||
|
// encode the data
|
||||||
|
self.encode_data(ver, out)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_data(
|
||||||
|
&self,
|
||||||
|
ver: TransformVersion,
|
||||||
|
out: &mut Vec<u8>,
|
||||||
|
) -> Result<(), DataTransformError>;
|
||||||
|
|
||||||
|
fn should_encode_data(&self, _ver: TransformVersion) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait DataDecoder<B: Iterator<Item = u8>>: TransformId {
|
||||||
|
fn decode_data(bytes: &mut B, ver: TransformVersion) -> Result<Self, DataTransformError>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode<B: Iterator<Item = u8>>(bytes: &mut B) -> Result<Vec<AnyData>, DataTransformError> {
|
||||||
|
let mut out = Vec::new();
|
||||||
|
|
||||||
|
// decode the start byte and version
|
||||||
|
let ver = StartData::decode_start_bytes(bytes)?;
|
||||||
|
|
||||||
|
while let Some(id) = bytes.next() {
|
||||||
|
match id {
|
||||||
|
0 => return Err(DataTransformError::StartReparse),
|
||||||
|
1 => out.push(AnyData::TypeData(TypeData::decode_data(bytes, ver)?)),
|
||||||
|
2 => out.push(AnyData::NameData(NameData::decode_data(bytes, ver)?)),
|
||||||
|
3 => out.push(AnyData::IdentificationData(
|
||||||
|
IdentificationData::decode_data(bytes, ver)?,
|
||||||
|
)),
|
||||||
|
// TODO
|
||||||
|
255 => out.push(AnyData::EndData(EndData::decode_data(bytes, ver)?)),
|
||||||
|
_ => return Err(DataTransformError::UnknownTransformer(id)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DataTransformError {
|
||||||
|
NoStartBlock,
|
||||||
|
UnknownVersion(u8),
|
||||||
|
/// Attempt to parse start data. Start data is specially handled.
|
||||||
|
StartReparse,
|
||||||
|
|
||||||
|
InvalidTypeError,
|
||||||
|
|
||||||
|
BadString,
|
||||||
|
|
||||||
|
TooManyIdentifications,
|
||||||
|
NoBasevalueForIdent,
|
||||||
|
NoPotentialValuesForIdent,
|
||||||
|
InvalidIntRoll,
|
||||||
|
|
||||||
|
UnexpectedEndOfBytes,
|
||||||
|
UnknownTransformer(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum DataTransformerTypes {
|
||||||
|
StartDataTransformer = 0,
|
||||||
|
TypeDataTransformer = 1,
|
||||||
|
NameDataTransformer = 2,
|
||||||
|
IdentificationDataTransformer = 3,
|
||||||
|
PowderDataTransformer = 4,
|
||||||
|
RerollDataTransformer = 5,
|
||||||
|
ShinyDataTransformer = 6,
|
||||||
|
CustomGearTypeTransformer = 7,
|
||||||
|
DurabilityDataTransformer = 8,
|
||||||
|
RequirementsDataTransformer = 9,
|
||||||
|
DamageDataTransformer = 10,
|
||||||
|
DefenseDataTransformer = 11,
|
||||||
|
CustomIdentificationDataTransformer = 12,
|
||||||
|
CustomConsumableTypeDataTransformer = 13,
|
||||||
|
UsesDataTransformer = 14,
|
||||||
|
EffectsDataTransformer = 15,
|
||||||
|
EndDataTransformer = 255,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AnyData {
|
||||||
|
StartData(StartData),
|
||||||
|
TypeData(TypeData),
|
||||||
|
NameData(NameData),
|
||||||
|
IdentificationData(IdentificationData),
|
||||||
|
PowderData(PowderData),
|
||||||
|
RerollData(RerollData),
|
||||||
|
// TODO
|
||||||
|
EndData(EndData),
|
||||||
|
}
|
54
idlib/src/data_transformer/namedata.rs
Normal file
54
idlib/src/data_transformer/namedata.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
use crate::{data_transformer::DataTransformError, types::transform::TransformVersion};
|
||||||
|
|
||||||
|
use super::{DataDecoder, DataEncoder, DataTransformerTypes, TransformId};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NameData(pub String);
|
||||||
|
|
||||||
|
impl TransformId for NameData {
|
||||||
|
fn get_id() -> u8 {
|
||||||
|
DataTransformerTypes::NameDataTransformer as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataEncoder for NameData {
|
||||||
|
fn encode_data(
|
||||||
|
&self,
|
||||||
|
ver: TransformVersion,
|
||||||
|
out: &mut Vec<u8>,
|
||||||
|
) -> Result<(), super::DataTransformError> {
|
||||||
|
match ver {
|
||||||
|
TransformVersion::Version1 => {
|
||||||
|
// check that the string is valid ascii
|
||||||
|
if self.0.chars().any(|c| !c.is_ascii()) {
|
||||||
|
return Err(DataTransformError::BadString);
|
||||||
|
}
|
||||||
|
|
||||||
|
// push the bytes
|
||||||
|
out.extend_from_slice(self.0.as_bytes());
|
||||||
|
// push the null terminator
|
||||||
|
out.push(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: Iterator<Item = u8>> DataDecoder<B> for NameData {
|
||||||
|
fn decode_data(bytes: &mut B, ver: TransformVersion) -> Result<Self, super::DataTransformError>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
match ver {
|
||||||
|
TransformVersion::Version1 => {
|
||||||
|
let b: Vec<u8> = bytes.take_while(|b| *b != 0).collect();
|
||||||
|
|
||||||
|
// UTF-8 and ASCII share the same set of characters
|
||||||
|
Ok(NameData(
|
||||||
|
String::from_utf8(b).map_err(|_| DataTransformError::BadString)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
idlib/src/data_transformer/powderdata.rs
Normal file
59
idlib/src/data_transformer/powderdata.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use crate::types::{powder::Powders, transform::TransformVersion};
|
||||||
|
|
||||||
|
use super::{DataEncoder, DataTransformerTypes, TransformId};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PowderData {
|
||||||
|
pub powder_slots: u8,
|
||||||
|
pub powders: Vec<(Powders, u8)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransformId for PowderData {
|
||||||
|
fn get_id() -> u8 {
|
||||||
|
DataTransformerTypes::PowderDataTransformer as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataEncoder for PowderData {
|
||||||
|
fn encode_data(
|
||||||
|
&self,
|
||||||
|
ver: TransformVersion,
|
||||||
|
out: &mut Vec<u8>,
|
||||||
|
) -> Result<(), super::DataTransformError> {
|
||||||
|
match ver {
|
||||||
|
TransformVersion::Version1 => {
|
||||||
|
let bits_needed = self.powders.len() * 5;
|
||||||
|
let total_bits = (bits_needed + 7) / 8;
|
||||||
|
|
||||||
|
let mut powder_data = vec![0u8; total_bits];
|
||||||
|
|
||||||
|
for (i, pow) in self.powders.iter().enumerate() {
|
||||||
|
let elem = pow.0 as u8;
|
||||||
|
// TODO: figure out if wynntils fixes this and make the tier be encoded correctly
|
||||||
|
let tier = 0; //pow.1;
|
||||||
|
|
||||||
|
// calculate the 5 bit powder value
|
||||||
|
let powder_num = (elem * 6 + tier) & 0b00011111;
|
||||||
|
|
||||||
|
// bit position where this specific powder starts
|
||||||
|
let powder_idx = i * 5;
|
||||||
|
|
||||||
|
// set the values
|
||||||
|
for j in 0..5 {
|
||||||
|
// calculate the bit position of this bit
|
||||||
|
let idx = powder_idx + j;
|
||||||
|
let bit = (powder_num >> (4 - j)) & 0b1;
|
||||||
|
powder_data[idx / 8] |= bit << (7 - (idx % 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.push(self.powder_slots);
|
||||||
|
out.push(self.powders.len() as u8);
|
||||||
|
|
||||||
|
out.append(&mut powder_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
41
idlib/src/data_transformer/rerolldata.rs
Normal file
41
idlib/src/data_transformer/rerolldata.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
use crate::types::transform::TransformVersion;
|
||||||
|
|
||||||
|
use super::{DataDecoder, DataEncoder, DataTransformError, DataTransformerTypes, TransformId};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RerollData(pub u8);
|
||||||
|
|
||||||
|
impl TransformId for RerollData {
|
||||||
|
fn get_id() -> u8 {
|
||||||
|
DataTransformerTypes::RerollDataTransformer as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataEncoder for RerollData {
|
||||||
|
fn encode_data(
|
||||||
|
&self,
|
||||||
|
ver: crate::types::transform::TransformVersion,
|
||||||
|
out: &mut Vec<u8>,
|
||||||
|
) -> Result<(), super::DataTransformError> {
|
||||||
|
match ver {
|
||||||
|
TransformVersion::Version1 => out.push(self.0),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: Iterator<Item = u8>> DataDecoder<B> for RerollData {
|
||||||
|
fn decode_data(bytes: &mut B, ver: TransformVersion) -> Result<Self, super::DataTransformError>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
match ver {
|
||||||
|
TransformVersion::Version1 => Ok(Self(
|
||||||
|
bytes
|
||||||
|
.next()
|
||||||
|
.ok_or(DataTransformError::UnexpectedEndOfBytes)?,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
idlib/src/data_transformer/shinydata.rs
Normal file
31
idlib/src/data_transformer/shinydata.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
use crate::{encoding::encode_varint, types::transform::TransformVersion};
|
||||||
|
|
||||||
|
use super::{DataEncoder, DataTransformerTypes, TransformId};
|
||||||
|
|
||||||
|
pub struct ShinyData {
|
||||||
|
pub id: u8,
|
||||||
|
pub val: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransformId for ShinyData {
|
||||||
|
fn get_id() -> u8 {
|
||||||
|
DataTransformerTypes::ShinyDataTransformer as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataEncoder for ShinyData {
|
||||||
|
fn encode_data(
|
||||||
|
&self,
|
||||||
|
ver: crate::types::transform::TransformVersion,
|
||||||
|
out: &mut Vec<u8>,
|
||||||
|
) -> Result<(), super::DataTransformError> {
|
||||||
|
match ver {
|
||||||
|
TransformVersion::Version1 => {
|
||||||
|
out.push(self.id);
|
||||||
|
out.append(&mut encode_varint(self.val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
42
idlib/src/data_transformer/startdata.rs
Normal file
42
idlib/src/data_transformer/startdata.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
use crate::types::transform::TransformVersion;
|
||||||
|
|
||||||
|
use super::{DataEncoder, DataTransformError, DataTransformerTypes, TransformId};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct StartData(pub TransformVersion);
|
||||||
|
|
||||||
|
impl TransformId for StartData {
|
||||||
|
fn get_id() -> u8 {
|
||||||
|
DataTransformerTypes::StartDataTransformer as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataEncoder for StartData {
|
||||||
|
fn encode_data(
|
||||||
|
&self,
|
||||||
|
ver: TransformVersion,
|
||||||
|
out: &mut Vec<u8>,
|
||||||
|
) -> Result<(), DataTransformError> {
|
||||||
|
match ver {
|
||||||
|
TransformVersion::Version1 => out.push(self.0.version()),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StartData {
|
||||||
|
/// Special case function for parsing the start bytes
|
||||||
|
pub(crate) fn decode_start_bytes<B: Iterator<Item = u8>>(
|
||||||
|
bytes: &mut B,
|
||||||
|
) -> Result<TransformVersion, DataTransformError> {
|
||||||
|
let idbyte = bytes.next().unwrap();
|
||||||
|
if idbyte != DataTransformerTypes::StartDataTransformer as u8 {
|
||||||
|
return Err(DataTransformError::NoStartBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
let verbyte = bytes.next().unwrap();
|
||||||
|
|
||||||
|
TransformVersion::from_u8(verbyte).map_err(|_| DataTransformError::UnknownVersion(verbyte))
|
||||||
|
}
|
||||||
|
}
|
43
idlib/src/data_transformer/typedata.rs
Normal file
43
idlib/src/data_transformer/typedata.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
use crate::types::{itemtype::ItemType, transform::TransformVersion};
|
||||||
|
|
||||||
|
use super::{DataDecoder, DataEncoder, DataTransformError, DataTransformerTypes, TransformId};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TypeData(pub ItemType);
|
||||||
|
|
||||||
|
impl TransformId for TypeData {
|
||||||
|
fn get_id() -> u8 {
|
||||||
|
DataTransformerTypes::TypeDataTransformer as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataEncoder for TypeData {
|
||||||
|
fn encode_data(
|
||||||
|
&self,
|
||||||
|
ver: TransformVersion,
|
||||||
|
out: &mut Vec<u8>,
|
||||||
|
) -> Result<(), super::DataTransformError> {
|
||||||
|
match ver {
|
||||||
|
TransformVersion::Version1 => out.push(self.0.into()),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: Iterator<Item = u8>> DataDecoder<B> for TypeData {
|
||||||
|
fn decode_data(bytes: &mut B, ver: TransformVersion) -> Result<Self, super::DataTransformError>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
match ver {
|
||||||
|
TransformVersion::Version1 => {
|
||||||
|
let b = bytes.next().unwrap();
|
||||||
|
|
||||||
|
Ok(Self(
|
||||||
|
ItemType::try_from(b).map_err(|_| DataTransformError::InvalidTypeError)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
112
idlib/src/encoding.rs
Normal file
112
idlib/src/encoding.rs
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
/// Encode bytes into a string using the wynntils byte encoding scheme
|
||||||
|
///
|
||||||
|
/// https://github.com/Wynntils/Wynntils/blob/main/common/src/main/java/com/wynntils/utils/EncodedByteBuffer.java#L87
|
||||||
|
pub fn encode_string(data: &[u8]) -> String {
|
||||||
|
let mut out = String::new();
|
||||||
|
|
||||||
|
for d in data.chunks(2) {
|
||||||
|
if d.len() == 2 {
|
||||||
|
if d[0] == 255 && d[1] >= 254 {
|
||||||
|
out.push(char::from_u32(0x100000 + ((d[1] - 254) as u32)).unwrap());
|
||||||
|
} else {
|
||||||
|
out.push(char::from_u32(0xF0000 + ((d[0] as u32) << 8) + d[1] as u32).unwrap());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// encode leftover singular bits with the seperate encoding
|
||||||
|
out.push(char::from_u32(0x100000 + ((d[0] as u32) << 8) + 0xEE).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decodes the bytes of a wynntils private area encoded string
|
||||||
|
///
|
||||||
|
/// This function does not check whether or not the encoded data is valid
|
||||||
|
///
|
||||||
|
/// https://github.com/Wynntils/Wynntils/blob/main/common/src/main/java/com/wynntils/utils/EncodedByteBuffer.java#L33
|
||||||
|
pub fn decode_string(data: &str) -> Vec<u8> {
|
||||||
|
let mut out = Vec::new();
|
||||||
|
|
||||||
|
for c in data.chars() {
|
||||||
|
let n: u32 = c.into();
|
||||||
|
|
||||||
|
// special case Private use area B
|
||||||
|
if n > 0x100000 {
|
||||||
|
// single byte
|
||||||
|
if n & 0xFF == 0xEE {
|
||||||
|
out.push(((n & 0xFF00) >> 8) as u8);
|
||||||
|
|
||||||
|
assert!(((n & 0xFF00) >> 8) <= 255, "Invalid codepoint: {n:06X}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// two bytes
|
||||||
|
|
||||||
|
out.push(255);
|
||||||
|
out.push((254 + (n & 0xFF)) as u8);
|
||||||
|
|
||||||
|
// Only 0x100000-0x100001 are used
|
||||||
|
assert!(n < 0x100002, "Invalid codepoint: {n:06X}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.push(((n & 0xFF00) >> 8) as u8);
|
||||||
|
out.push((n & 0x00FF) as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode_varint(value: i64) -> Vec<u8> {
|
||||||
|
// zigzag encoding magic
|
||||||
|
// removes sign bit so values are only positive
|
||||||
|
let value = ((value << 1) ^ (value >> 63)) as u64;
|
||||||
|
|
||||||
|
// 7 bits per byte
|
||||||
|
// highest bit is used to indicate end of encoding
|
||||||
|
|
||||||
|
// calulate number of bytes needed
|
||||||
|
let mut numofbytes = 1;
|
||||||
|
let mut temp = value >> 7;
|
||||||
|
while temp != 0 {
|
||||||
|
// println!("{temp}");
|
||||||
|
numofbytes += 1;
|
||||||
|
temp >>= 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut outbytes = Vec::new();
|
||||||
|
for i in 0..numofbytes {
|
||||||
|
let mut next = (value >> (7 * i)) as u8 & 0x7F;
|
||||||
|
|
||||||
|
// indicate that we are **not** done by setting the highest bit
|
||||||
|
if i < numofbytes - 1 {
|
||||||
|
next |= 0b10000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
outbytes.push(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
outbytes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_varint<B: Iterator<Item = u8>>(bytes: &mut B) -> i64 {
|
||||||
|
let mut value = 0;
|
||||||
|
|
||||||
|
let mut data = Vec::new();
|
||||||
|
loop {
|
||||||
|
let b = bytes.next().unwrap();
|
||||||
|
|
||||||
|
data.push(b);
|
||||||
|
|
||||||
|
if (b & 0b10000000) == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, n) in data.into_iter().enumerate() {
|
||||||
|
value |= ((n & 0b01111111) as i64) << (7 * i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (value >> 1) ^ -(value & 1);
|
||||||
|
}
|
3
idlib/src/lib.rs
Normal file
3
idlib/src/lib.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod data_transformer;
|
||||||
|
pub mod encoding;
|
||||||
|
pub mod types;
|
31
idlib/src/types/itemtype.rs
Normal file
31
idlib/src/types/itemtype.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum ItemType {
|
||||||
|
Gear = 0,
|
||||||
|
Tome = 1,
|
||||||
|
Charm = 2,
|
||||||
|
CraftedGear = 3,
|
||||||
|
CraftedConsu = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<u8> for ItemType {
|
||||||
|
fn into(self) -> u8 {
|
||||||
|
self as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u8> for ItemType {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
0 => Ok(Self::Gear),
|
||||||
|
1 => Ok(Self::Tome),
|
||||||
|
2 => Ok(Self::Charm),
|
||||||
|
3 => Ok(Self::CraftedGear),
|
||||||
|
4 => Ok(Self::CraftedConsu),
|
||||||
|
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
idlib/src/types/mod.rs
Normal file
4
idlib/src/types/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
pub mod itemtype;
|
||||||
|
pub mod powder;
|
||||||
|
pub mod stat;
|
||||||
|
pub mod transform;
|
9
idlib/src/types/powder.rs
Normal file
9
idlib/src/types/powder.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/// Powder types
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
pub enum Powders {
|
||||||
|
EARTH = 1,
|
||||||
|
THUNDER = 2,
|
||||||
|
WATER = 3,
|
||||||
|
FIRE = 4,
|
||||||
|
AIR = 5,
|
||||||
|
}
|
24
idlib/src/types/stat.rs
Normal file
24
idlib/src/types/stat.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug)]
|
||||||
|
pub struct StatId(pub u8);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Stat {
|
||||||
|
pub kind: StatId,
|
||||||
|
pub base: Option<i32>,
|
||||||
|
pub roll: RollType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum RollType {
|
||||||
|
Value(u8),
|
||||||
|
PreIdentified,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stat {
|
||||||
|
pub fn pre_identified(&self) -> bool {
|
||||||
|
match self.roll {
|
||||||
|
RollType::Value(_) => false,
|
||||||
|
RollType::PreIdentified => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
idlib/src/types/transform.rs
Normal file
17
idlib/src/types/transform.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum TransformVersion {
|
||||||
|
Version1 = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransformVersion {
|
||||||
|
pub fn version(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_u8(byte: u8) -> Result<Self, ()> {
|
||||||
|
match byte {
|
||||||
|
0 => Ok(Self::Version1),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
269
src/main.rs
Normal file
269
src/main.rs
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
use idlib::{
|
||||||
|
data_transformer::{
|
||||||
|
decode, enddata::EndData, identdata::IdentificationData, namedata::NameData,
|
||||||
|
powderdata::PowderData, rerolldata::RerollData, shinydata::ShinyData, startdata::StartData,
|
||||||
|
typedata::TypeData, DataEncoder,
|
||||||
|
},
|
||||||
|
encoding::{decode_string, encode_string},
|
||||||
|
types::{
|
||||||
|
itemtype::ItemType,
|
||||||
|
powder::Powders,
|
||||||
|
stat::{RollType, Stat, StatId},
|
||||||
|
transform::TransformVersion,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
|
use std::panic;
|
||||||
|
use std::env;
|
||||||
|
use serde_json;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use base64::engine::{general_purpose, Engine};
|
||||||
|
|
||||||
|
// structs
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Powder {
|
||||||
|
r#type: char,
|
||||||
|
tier: u8,
|
||||||
|
amount: Option<u8>
|
||||||
|
}
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Identificationer {
|
||||||
|
id: String,
|
||||||
|
base: i32,
|
||||||
|
roll: Option<u8>
|
||||||
|
}
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct jsonconfig {
|
||||||
|
name: String,
|
||||||
|
ids: Vec<Identificationer>,
|
||||||
|
powder_limit: u8,
|
||||||
|
powders: Vec<Powder>,
|
||||||
|
rerolls:Option<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// enable fancypanic when building for release
|
||||||
|
fancypanic();
|
||||||
|
|
||||||
|
// newest json reading code
|
||||||
|
let json_config: jsonconfig = serde_json::from_reader(
|
||||||
|
fs::File::open("values.json").expect(ERROR[1]))
|
||||||
|
.expect(ERROR[2]);
|
||||||
|
let idsmap: HashMap<String, u8> = serde_json::from_reader(fs::File::open("id_keys.json").expect(ERROR[3]))
|
||||||
|
.expect(ERROR[4]);
|
||||||
|
// println!("{:?}",idsmap.get("airDamage"));
|
||||||
|
// below is no longer needed as ive merged it
|
||||||
|
//let imported2: jsoned = serde_json::from_reader(importedjson)
|
||||||
|
// .expect("this json sucks");
|
||||||
|
|
||||||
|
// read the file and stuff
|
||||||
|
// thanks to https://stackoverflow.com/a/52964674
|
||||||
|
// obselete do not use
|
||||||
|
//let file = fs::File::open("values.json")
|
||||||
|
// .expect("where file?");
|
||||||
|
//let thejson: serde_json::Value = serde_json::from_reader(file)
|
||||||
|
// .expect("where proper json format?");
|
||||||
|
//let powders = thejson.get("powders").expect("e").get("a");
|
||||||
|
//let powders2 = serde_json::json!(powders);
|
||||||
|
//println!("powders: {:?}",powders);
|
||||||
|
//println!("powders2: {}",powders2);
|
||||||
|
//println!("name is {}", thejson);
|
||||||
|
|
||||||
|
// let fuy = thejson.get("a");
|
||||||
|
// println!("{:#?}",fuy);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let mut out = Vec::new();
|
||||||
|
let ver = TransformVersion::Version1;
|
||||||
|
|
||||||
|
StartData(ver).encode(ver, &mut out).unwrap();
|
||||||
|
|
||||||
|
TypeData(ItemType::Gear).encode(ver, &mut out).unwrap();
|
||||||
|
|
||||||
|
NameData(String::from(format!("{}", json_config.name.trim()) ))
|
||||||
|
.encode(ver, &mut out)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let w1 = "aWRtYW5nbGVyLXJld3JpdGUgUHJlLVJlbGVhc2UgdjE=";
|
||||||
|
let w2 = "KEMpIFpBVFpPVSBhbmQgRU5ERVJOT04gMjAyNA==";
|
||||||
|
|
||||||
|
let l1 = String::from_utf8(general_purpose::STANDARD.decode(w1).unwrap()).unwrap();
|
||||||
|
let l2 = String::from_utf8(general_purpose::STANDARD.decode(w2).unwrap()).unwrap();
|
||||||
|
println!("{l1}");
|
||||||
|
println!("{l2}");
|
||||||
|
|
||||||
|
// json identification data handling
|
||||||
|
let mut idvec = Vec::new();
|
||||||
|
for eachid in json_config.ids {
|
||||||
|
let id_id = idsmap.get(eachid.id.trim());
|
||||||
|
let id_base = eachid.base as i32;
|
||||||
|
let id_roll = eachid.roll;
|
||||||
|
|
||||||
|
idvec.push(
|
||||||
|
(
|
||||||
|
Stat {
|
||||||
|
kind: StatId(match id_id {
|
||||||
|
Some(ide) => *ide,
|
||||||
|
None => panic!("There is a mismatched ID, and this message has replaced where the line is meant to be")
|
||||||
|
}),
|
||||||
|
base: Some(id_base),
|
||||||
|
roll: match id_roll{
|
||||||
|
Some(rolle) => RollType::Value(rolle),
|
||||||
|
None => RollType::PreIdentified
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// println!("{:?} {:?} {:?}",id_id,id_base,id_roll)
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentificationData {
|
||||||
|
identifications: idvec,
|
||||||
|
extended_encoding: true,
|
||||||
|
}
|
||||||
|
.encode(ver, &mut out)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// json powder data handling
|
||||||
|
let mut powdervec = Vec::new();
|
||||||
|
for eachpowder in json_config.powders {
|
||||||
|
let powdertier = eachpowder.tier; // get the powder tier
|
||||||
|
let powderamount:u8 = match eachpowder.amount { // get amount of powder if exists, otherwise 1
|
||||||
|
Some(amount) => {
|
||||||
|
amount
|
||||||
|
},// good,
|
||||||
|
None => {
|
||||||
|
1
|
||||||
|
}// bad,
|
||||||
|
};
|
||||||
|
// match for the powder type
|
||||||
|
// no need to return to variable or i'll need to rematch AGAIN
|
||||||
|
match eachpowder.r#type {
|
||||||
|
'E' | 'e' => {
|
||||||
|
for i in 0..powderamount {
|
||||||
|
powdervec.push((Powders::EARTH,powdertier))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'T' | 't' => {
|
||||||
|
for i in 0..powderamount {
|
||||||
|
powdervec.push((Powders::THUNDER,powdertier))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'W' | 'w' => {
|
||||||
|
for i in 0..powderamount {
|
||||||
|
powdervec.push((Powders::WATER,powdertier))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'F' | 'f' => {
|
||||||
|
for i in 0..powderamount {
|
||||||
|
powdervec.push((Powders::FIRE,powdertier))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'A' | 'a' => {
|
||||||
|
for i in 0..powderamount {
|
||||||
|
powdervec.push((Powders::AIR,powdertier))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
for i in 0..powderamount {
|
||||||
|
powdervec.push((Powders::THUNDER,powdertier))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// println!("tier {}",powdertier);
|
||||||
|
// println!("amount {}",powderamount);
|
||||||
|
}
|
||||||
|
// println!("{:?}",powdervec);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// old powder data encode kinda, takes data from new encode
|
||||||
|
PowderData {
|
||||||
|
powder_slots: json_config.powder_limit,
|
||||||
|
powders: powdervec,
|
||||||
|
}
|
||||||
|
.encode(ver, &mut out)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
match json_config.rerolls {
|
||||||
|
Some(i) => {
|
||||||
|
if i != 0 {
|
||||||
|
RerollData(i).encode(ver, &mut out).unwrap();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => pass()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ShinyData {
|
||||||
|
id: 2,
|
||||||
|
val: i64::MAX as i64, //- 0b0100_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000,
|
||||||
|
// u16::MAX is the max value of unsigned 16bit value
|
||||||
|
}
|
||||||
|
.encode(ver, &mut out)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
// prints (Water,6) 255 times
|
||||||
|
// println!("{:?}",vec![(Powders::WATER, 6); 255]);
|
||||||
|
|
||||||
|
EndData.encode(ver, &mut out).unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// final string print
|
||||||
|
println!("{}", encode_string(&out));
|
||||||
|
|
||||||
|
// I don't even know what the fuck this does
|
||||||
|
//for b in out {
|
||||||
|
// print!("{:02X}", b);
|
||||||
|
//}
|
||||||
|
|
||||||
|
// println!();
|
||||||
|
|
||||||
|
// decode test
|
||||||
|
let input = "";
|
||||||
|
let bytes = decode_string(&input);
|
||||||
|
let mut bytes_iter = bytes.into_iter();
|
||||||
|
|
||||||
|
let out = decode(&mut bytes_iter).unwrap();
|
||||||
|
|
||||||
|
// println!("{:#?}", out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn fancypanic() {
|
||||||
|
panic::set_hook(Box::new(|panic_info| {
|
||||||
|
let panic_msg = format!("{panic_info}");
|
||||||
|
println!("{}", panic_msg.lines().skip(1).next().unwrap_or("HOW DID YOU BREAK THE PANIC HANDLER???"));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pass() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const ERROR: [&'static str; 5] = [
|
||||||
|
"Error 0: what did you even do to get this? ",
|
||||||
|
"Error 1: json config file is missing, reobtain it from the values.json I have sent you. ",
|
||||||
|
"Error 2: json config is broken. Reread the example data or reobtain it from the values.json I have sent you. ",
|
||||||
|
"Error 3: Identifications hashmap not found. Get it from https://raw.githubusercontent.com/Wynntils/Static-Storage/main/Reference/id_keys.json and move it to this directory.",
|
||||||
|
"Error 4: Identifications hashhmap is corrupt. Reobtain it from https://raw.githubusercontent.com/Wynntils/Static-Storage/main/Reference/id_keys.json and move it to this directory."
|
||||||
|
];
|
||||||
|
const _BOIL: [&'static str; 3] = [
|
||||||
|
"0",
|
||||||
|
"reobtain it from the values.json I have sent you. ",
|
||||||
|
"Get it from https://raw.githubusercontent.com/Wynntils/Static-Storage/main/Reference/id_keys.json and move it to this directory."
|
||||||
|
];
|
26
values.json
Normal file
26
values.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"READMEFIRST": [
|
||||||
|
"Refer to config.md to understand how to apply the data shown here.",
|
||||||
|
"There are also some values you cannot increase beyond a limit."
|
||||||
|
],
|
||||||
|
"name":"Singularity",
|
||||||
|
|
||||||
|
"ids": [
|
||||||
|
{"id": "mainAttackDamage","base": 320,"roll": 69},
|
||||||
|
{"id": "healthRegenRaw", "base":250 , "roll":130 },
|
||||||
|
{"id": "rawDexterity", "base":35 },
|
||||||
|
{"id": "walkSpeed", "base":-40 , "roll":69},
|
||||||
|
{"id": "mainAttackDamage", "base":15, "roll":130 },
|
||||||
|
{"id": "rawMainAttackDamage", "base":444 , "roll":130 },
|
||||||
|
{"id": "rawSpellDamage", "base":222 , "roll":130 },
|
||||||
|
{"id": "spellDamage", "base":10 , "roll":130 }
|
||||||
|
],
|
||||||
|
"powder_limit": 255,
|
||||||
|
"powders": [
|
||||||
|
{ "type":"T", "tier":6, "amount":5 },
|
||||||
|
{"type":"e","tier":6,"amount":5},
|
||||||
|
{"type":"f", "tier":6,"amount":1},
|
||||||
|
{ "type" : "f" , "tier":6 }
|
||||||
|
],
|
||||||
|
"rerolls": 0
|
||||||
|
}
|
Loading…
Reference in a new issue