• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Data Encryption and Save/Load codes

Level 31
Joined
Jul 10, 2007
Messages
6,306
This tutorial will go over the theory behind save/load codes.

Suggested Base Encryption Key: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*(){}[]:;<>?|+-"

Random Key Generator: ???? (I might actually write one since I can't seem to find one online)

When people work with compressing and protecting data, they convert that data from a set of numbers to a string.

These values
Code:
level = 5
strength = 22
agility = 15
intelligence = 44
unitTypeId = 'Hpal'

Might magically transform into something like Hlfn.


Data encryption works by merging a set of values into one value and then converting the base. The value Hlfn might seem pretty random, but it is really a number represented in another base.

Bases are the groupings of numbers. For example, base 10 will group 10 digits, 0-9, into each position. Base 8 will group 0-7 into each position. Base 16 groups 0-H into each position.

All of these values are the exact same things represented in different bases.
Code:
Base 10: 10

(00,01,02,03,04,05,06,07,08,09,10)
(00,01,02,03,04,05,06,07,10,11,12)
Base 8: 12 

(00,01,02,03,04,05,06,07,08,09,10)
(00,01,02,03,04,05,06,07,08,09,0A)
Base 16: A

(0,1,02,03,004,005,006,007,0008,0009,0010)
(0,1,10,11,100,101,110,111,1000,1001,1010)
Base 2: 1010

In JASS, different bases can be used to represent numbers as well
Code:
Base 8: 0281
Base 16: 0x29
Base 10: 1039

Because bases are simply a way to group numbers, in code one can create any base they want so long as they have a character to represent the value. The old and simple method used strings.

constant string BASE_10 = "0123456789"

To convert a value into a character
JASS:
string char0 = SubString(BASE_10, 0, 1)
string char1 = SubString(BASE_10, 1, 2)
string char2 = SubString(BASE_10, 2, 3)
string char3 = SubString(BASE_10, 3, 4)
string char4 = SubString(BASE_10, 4, 5)
string char5 = SubString(BASE_10, 5, 6)
string char6 = SubString(BASE_10, 6, 7)
string char7 = SubString(BASE_10, 7, 8)
string char8 = SubString(BASE_10, 8, 9)
string char9 = SubString(BASE_10, 9, 10)

Or a simple function
JASS:
globals
    constant string BASE_10 = "0123456789"
endglobals

function ConvertIntegerToCharacter takes integer i returns string
    return SubString(BASE_10, i, i+1)
endfunction

Notice string char9 = SubString(BASE_10, 9, 10)

The length BASE_10 is 10 (10 characters in it, which is why it's base 10). A base represents how many digits it has, so the size of a base is equal to the length of the string that represents it.

StringLength(BASE_10) == 10)

Also, using the idea that any old string can be used, the ConvertIntegerToCharacter function could be modified to take a string (the base).

JASS:
//retrieves how many digits the base has
//base 10 has 10 digits
//base 16 has 16 digits, etc
function GetBase takes string base returns integer
    return StringLength(base)
endfunction

function ConvertIntegerToCharacter takes string base, integer i returns string
    return SubString(base, i, i+1)
endfunction

So now data can be encrypted. If one were to use base "9876543210" and convert the number 29 to it, it'd change into 81. But what if the bases were of a different size? If the base were "01" and one were to plug in 29, how does it do it? This is where what is called modulo math comes into play.

Modulo math gets the remainder in division.
local integer remainder = numerator-numerator/divisor*divisor

If one were to plug 10 and 5 into the above, 10-10/5*5 would be 0.
Plugging 7 and 3 into the above, 7-7/3*3 = 1 (http://www.google.com/search?hl=en&safe=off&q=7%3&aq=f&aqi=g10&aql=&oq=). Keep in mind that this uses integer division.

If one were to convert the number 37 into base 16, it'd turn into 25.


Building from bottom up (always add in front)

Code:
37 mod 16 = (37-37/16*16) = 37-32 = 5
37 / 16 = 2 (2 < 16, so stop)

25

149 -> 95
Code:
149-144 = 5
149 / 16 = 9

95

29432 -> 72f8
Code:
29432-29424 = 8 (8)
29432/16 = 1839

1839-1824 = 15 (f8)
1839/16 = 114

114-112 = 2 (2f8)
114/16 = 7 (72f8)

Bases can also be converted back to base 10. This is done by converting the number digit by digit to base 10. To convert back to a base, it is digit*currentBase^placeHolder. After converting each digit, add them all up.

72f8 -> 29432
Code:
72f8
0123

Max placeholder: 3

7*16^3 = 28672
2*16^2 = 512
15*16^1 = 240
8*16^0 = 8

28672+512+240+8 = 29432

When dealing with data encryption in coding, it might end up dealing with a number that is something like 100 digits long, way larger than what can be stored into an integer. This is why most save/load codes encrypt integer by integer.

Converting 5 and 5 into base 16 would return 5 and 5, outputted as 55 (merge them in the string).

Another problem is determining when one integer starts. For example, 55 might be 55 or 5,5. Many save/load codes save a certain number of placeholders. Let's say that the first number has 2 placeholders reserved, giving it a max size of 16^2-1, and the second one has one placeholder reserved, giving it a max size of 16^1-1. This would output 055. Some dynamic tell how many placeholders the number in front of it has. 5 takes up one placeholder, which would output 1515. The code would read the one and know that the next 1 characters is 1 number, thus getting 5. It'd then read a 1 and know that the next 1 characters is 1 number, thus getting the second 5. The first method of predetermined placeholders may seem good, but what about large 9 digit numbers?

000000001 vs 11

This means that for numbers that use 1 or 2 placeholders should use reserved placeholders. Numbers that use more should use dynamic.

In code, given that you know how to parse the number, let's say that a set of numbers is ordered in this fashion:

Max: 9
Max: 3
Max: 1
Max: 1
Max: 2

storing 1,2,3,4,5 would output 11123405

Reading through, the first number has a max of 9, so it is using dynamic placeholders.

(1)

Next one characters is the number

1

Next number uses dynamic placeholders as its max is 3

(1)
2

Next number uses predetermined placeholders as its max is 1

3

Next number uses predetermined as its max is 1

4

Next uses predetermined as its max is 2

05

Thus giving back 1,2,3,4,5

With the method of dynamically generated and predetermined placeholders, data can be optimized to a maximum amount.

Sometimes a collection of numbers are merged into their max possible sizes. As long as the sum of the max placeholders is less than 2^32/2, then they can be merged.

Given two numbers each with a max placeholder of 2 and stored in base 16 would return a max number of 16^4-1 (16^(2+2)-1), which is 65536. This means that rather than storing 2,2 as 0202, they can be stored as 2020, or 7e4, thus saving 1 character. One can also maximize space by storing the total number into a string and perform all of the math on the string place by place, but that would be incredibly slow and be incredibly annoying to code and may only save 1 character in the entire string relative to maximizing each position.


Converting from characters to numbers can be rather difficult. Old encryption practices would loop through the base string to perform the conversion
JASS:
function ConvertCharacterToInteger takes string base, string char returns integer
    local integer curVal = 0

    loop
        set c = SubString(base, curVal, curVal+1)
        set curVal = curVal + 1
        exitwhen c == char or curVal == baseSize
    endloop

    if (c != char)
        return -1
    endif

    return curVal
endfunction

New practices use LoadInteger(table, baseId, StringHash(char). They also use arrays rather than strings to perform integer to character conversion.

values[baseId+integerValue]

Some save/load codes use random bases and save the base into the save/load code in another arbitrary base so that the random base can be retrieved. Others hash the player name into a base and continue to use that for the player, or may use some random base based off of the player name. Others will scramble up the code using a scramble key-

0123456789
9783461520

The above would swap position 7 with position 2, position 9 with position 0, and etc. Random scramble keys may be used.

Others may use a base combo, encrypting different segments of the code with different bases.

00|11|22|33|44|55

where the 6 segments, 0-5, are each encrypted with different bases. This could result in a code like ffffffffffff, which may actually be 001122334455 (group however).

Checksums, or multiple checksums (summing up each of the digits) is also used to ensure the code is valid.

Code:
111115
1+1+1+1+1 = 5

Code:
1112234
1+1+1 = 3
2+2 = 4

Code:
1116115

1+1+1+1+1 = 5
1+5 = 6 (second last + checksum)
 
Last edited:
Top