Nestharus
o-o
- Reaction score
- 83
This and all save/load systems are Deprecated, do not use!
Quickstart Guide to Installing Lua Scripts (easy)
Saving and Loading Tutorial
Encoder Demo Map 2, a framework for Encoder
The best save/load system ever created for Warcraft 3. No other save/load system even compares to this.
Statistics
Security
Compression
Structure
Quickstart Guide to Installing Lua Scripts (easy)
Saving and Loading Tutorial
Encoder Demo Map 2, a framework for Encoder
The best save/load system ever created for Warcraft 3. No other save/load system even compares to this.
Statistics
min code size in demo: 21
average code size in demo: 60
max code size in demo: 63
kM6c-ypaF-UbKi-RWbb-nOqc-Z
average code size in demo: 60
4YMX-sYnp-Uiij-15vg-YcVv-Y4vz-fiZb-A0Ks-3w38-jZJm-zW3B-eJ3L-jcb3-VV0P-QmwU
max code size in demo: 63
JZae-DdjD-dWsQ-NKgU-sZCM-JLmt-XNmW-yMsA-eoND-aPor-qn8I-c4fO-tEjq-olO9-P3fW-eec
Security
1 bit value changes (from demo) (a bit is the tiniest change possible, like 0 to 1)
GCq3-4nVh-qtJc-AaJe-oFdh-N4
GTH2-JP2M-Vdc0-bU77-i6P1-rW
FoD8-kFq1-EO1B-3WSh-mGGf-jK6
xTEa-gBt2-4wPQ-wMuI-7jJK-V3
YDb6-LXrR-p1Ht-DnIZ-I6Mr-zia
Security Used in demo (prevents code tampering): 148194
Security Size (how big it is in the code): 3 chars (Ddy) (not too big ^^)
Where is the security? It's scrambled into the code at many different levels. This means that every single character in the code has a bit of every single security value in it + every single other value in the code. Changing 1 character in the code changes every single digit in the code by a fractional value. It's pretty much impossible to crack.
GCq3-4nVh-qtJc-AaJe-oFdh-N4
GTH2-JP2M-Vdc0-bU77-i6P1-rW
FoD8-kFq1-EO1B-3WSh-mGGf-jK6
xTEa-gBt2-4wPQ-wMuI-7jJK-V3
YDb6-LXrR-p1Ht-DnIZ-I6Mr-zia
Security Used in demo (prevents code tampering): 148194
Security Size (how big it is in the code): 3 chars (Ddy) (not too big ^^)
Where is the security? It's scrambled into the code at many different levels. This means that every single character in the code has a bit of every single security value in it + every single other value in the code. Changing 1 character in the code changes every single digit in the code by a fractional value. It's pretty much impossible to crack.
Compression
Encoder uses rather standard compression. It simply makes one gigantic number.
If there was this collection of numbers
For these ranges
It'd just merge them together
And then it would convert the base
Going back
If there was this collection of numbers
5,10,15
For these ranges
[0,6), [0,11), [0,16)
It'd just merge them together
(5*11+10)*16+15 = 1055
And then it would convert the base
h1
Going back
Code:
h1->1055
1055%16= 15; 1055/16=65
65%11= 10; 65/11=5
5%6= 5
5,10,15
Structure
Most save/load systems have a very simple data structure. Recall the ranges from before
They would be stored in a simple list for older save/load systems
For Encoder, they are stored in what I call a QueueQUeue, which is a tree structure formed
by a queues of queues.
Each link is a node specific to the tree and each non link is a node that may be in any
number of trees. The head represents the tree, in this case the Encoder.
Links are only entered if the value fits. For example, the head value is always going to be
nothing (0,0), so the links will always be entered. Values of nothing can fit into any link.
This is the basis that allows Encoder to serialize objects into a code rather than just
a collection of values.
Let's say that are were 200 items in a map. Items 1 through 100 have max item charges
of 0 and items 101 through 200 have max item charges of 16. Let's say that we want to
build a code that can store 6 of these items.
With a regular save/load system using a Queue, the struct would have to be this
A mid number of:
A min number of: 0
For Encoder
With that, only items fitting between 101 and 200 gets an item charge slot.
Max number: same as max for regular
Mid number:
Min number: 0
Comparisons
Max 1: uAv2c69GEgMu
Max 2: uAv2c69GEgMu
Mid 1: fmGT2JMqrYGU
Mid 2: 9muv6KXS
Min 1: 0
Min 2: 0
And again, this is assuming that the save/load system in question is using the same
compression technique as Encoder. Only 1 save/load system uses the same compression
technique, and that is Pipedream's save/load at wc3c.net. The others use a horrible
one that more than doubles the code size (7 chars -> 16+ chars).
Now, keep in mind that not every slot may be used in a tree. What if there were two
types of heroes, one with 4 abilities that had max levels of 3,3,3,1 and the other
with 5 abilities that had max levels of 4,4,4,2,4 (actual wc3 data). What this would mean
is that the first style of save/load would have to save 4,4,4,2,4 and the second style
could save 3,3,3,1 or 4,4,4,2,4. In practical use, the data structures can get very
complicated, which can result in some drastic differences.
Queue method:
Encoder method:
[0,6), [0,11), [0,16)
They would be stored in a simple list for older save/load systems
- [0,6)
- [0,11)
- [0,16)
For Encoder, they are stored in what I call a QueueQUeue, which is a tree structure formed
by a queues of queues.
- Head: (0,0)
- Link: (0,0)
- Value 1: [0,6)
- Link: (0,0)
- Value 2: [0,11)
- Value 2: [0,11)
- Link: (0,0)
- Value 3: [0,16)
- Link: (0,0)
Each link is a node specific to the tree and each non link is a node that may be in any
number of trees. The head represents the tree, in this case the Encoder.
Links are only entered if the value fits. For example, the head value is always going to be
nothing (0,0), so the links will always be entered. Values of nothing can fit into any link.
This is the basis that allows Encoder to serialize objects into a code rather than just
a collection of values.
Let's say that are were 200 items in a map. Items 1 through 100 have max item charges
of 0 and items 101 through 200 have max item charges of 16. Let's say that we want to
build a code that can store 6 of these items.
With a regular save/load system using a Queue, the struct would have to be this
- Item id: [0,201)
- Item charge: [0,17)
- Item id: [0,201)
- Item charge: [0,17)
- Item id: [0,201)
- Item charge: [0,17)
- Item id: [0,201)
- Item charge: [0,17)
- Item id: [0,201)
- Item charge: [0,17)
- Item id: [0,201)
- Item charge: [0,17)
Code:
((((((((((200*17+16)* 1
201+200)*17+16)* 2
201+200)*17+16)* 3
201+200)*17+16)* 4
201+200)*17+16)* 5
201+200)*17+16 6
= 1591731726658570620368
= uAv2c69GEgMu
Code:
((((((((((100*17+16)* 1
201+100)*17+16)* 2
201+100)*17+16)* 3
201+100)*17+16)* 4
201+100)*17+16)* 5
201+100)*17+16 6
= 799593572291014983768
= fmGT2JMqrYGU
For Encoder
- Head: (0,0)
- Link (0,0)
- Item id: [0,201)
- Link: [101, 200]
Item charge: [0,17)
- Link: [101, 200]
- Item id: [0,201)
- Link (0,0)
- Item id: [0,201)
- Link: [101, 200]
Item charge: [0,17)
- Link: [101, 200]
- Item id: [0,201)
- Link (0,0)
- Item id: [0,201)
- Link: [101, 200]
Item charge: [0,17)
- Link: [101, 200]
- Item id: [0,201)
- Link (0,0)
- Item id: [0,201)
- Link: [101, 200]
Item charge: [0,17)
- Link: [101, 200]
- Item id: [0,201)
- Link (0,0)
- Item id: [0,201)
- Link: [101, 200]
Item charge: [0,17)
- Link: [101, 200]
- Item id: [0,201)
- Link (0,0)
- Item id: [0,201)
- Link: [101, 200]
Item charge: [0,17)
- Link: [101, 200]
- Item id: [0,201)
- Link (0,0)
With that, only items fitting between 101 and 200 gets an item charge slot.
Max number: same as max for regular
Mid number:
Code:
((((100 1
*201+100)* 2
201+100)* 3
201+100)* 4
201+100)* 5
201+100 6
= 32972080300600
= 9muv6KXS
Comparisons
Max 1: uAv2c69GEgMu
Max 2: uAv2c69GEgMu
Mid 1: fmGT2JMqrYGU
Mid 2: 9muv6KXS
Min 1: 0
Min 2: 0
And again, this is assuming that the save/load system in question is using the same
compression technique as Encoder. Only 1 save/load system uses the same compression
technique, and that is Pipedream's save/load at wc3c.net. The others use a horrible
one that more than doubles the code size (7 chars -> 16+ chars).
Now, keep in mind that not every slot may be used in a tree. What if there were two
types of heroes, one with 4 abilities that had max levels of 3,3,3,1 and the other
with 5 abilities that had max levels of 4,4,4,2,4 (actual wc3 data). What this would mean
is that the first style of save/load would have to save 4,4,4,2,4 and the second style
could save 3,3,3,1 or 4,4,4,2,4. In practical use, the data structures can get very
complicated, which can result in some drastic differences.
Queue method:
- Hero id: [1,3)
- Ability Level: [0,5)
- Ability Level: [0,5)
- Ability Level: [0,5)
- Ability Level: [0,3)
- Ability Level: [0,5)
Encoder method:
- Head: (0,0)
- Link: (0,0)
- Hero id: [1,3)
- Link: [1,1]
- Ability Level: [0,4)
- Ability Level: [0,4)
- Ability Level: [0,4)
- Ability Level: [0,2)
- Link: [2,2]
- Ability Level: [0,5)
- Ability Level: [0,5)
- Ability Level: [0,5)
- Ability Level: [0,3)
- Ability Level: [0,5)
- Link: [1,1]
- Hero id: [1,3)
- Link: (0,0)
JASS:
library Encoder /* v3.0.1.2
*************************************************************************************
*
* Save/Load system
*
*************************************************************************************
* */uses/*
*
* */ BigInt /* hiveworkshop.com/forums/jass-functions-413/system-bigint-188973/
* */ QueueQueue /* hiveworkshop.com/forums/submissions-414/snippet-queuequeue-190890/
*
* These two can be changed (*Advanced*)
* */ KnuthChecksum /* hiveworkshop.com/forums/1846246-post343.html
* */ Scrambler /* hiveworkshop.com/forums/submissions-414/snippet-salt-189766/
*
* Used in settings functions:
* private function Checksum takes BigInt k, integer m returns integer
* private function ApplyScramble takes BigInt k, integer pid returns nothing
* private function UnapplyScramble takes BigInt k, integer pid returns nothing
*
************************************************************************************
*
* SETTINGS
*/
private keyword b10 //base 10
globals
/*************************************************************************************
*
* I suggest permutation of the following base for encoders
*
* 0123456789ABCDEFGHKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@#$%&?
*
*************************************************************************************/
/*************************************************************************************
*
* VER_BASE refers to the base used with encoder.toString()
*
*************************************************************************************/
private constant string VER_BASE="0123456789"
/*************************************************************************************
*
* Coloring Settings
*
*************************************************************************************/
private constant string NUM_COLOR="|cff40e0d0" //what to color numbers
private constant string LOWER_COLOR="|cffff69b4" //what to color lowercase characters
private constant string UPPER_COLOR="|cff00AA00" //what to color uppercase characters
private constant string SPEC_COLOR="|cffffff00" //what to color special characters
private constant string DELIMITER_COLOR="|cffffffff" //what to color DELIMITER characters
/*************************************************************************************
*
* Spacing Settings
*
*************************************************************************************/
private constant string DELIMITER="-" //DELIMITER to make code easier to read
private constant integer DELIMITER_COUNT=4 //how many characters per DELIMITER
/*************************************************************************************
*
* Encryption Settings
*
*************************************************************************************/
/*************************************************************************************
*
* SHUFFLES
*
* How many shuffles to perform in encoder
*
* Nust be greater than 0
*
*************************************************************************************/
private constant integer SHUFFLES=3
/*************************************************************************************
*
* CHECKSUM_VARIANCE
*
* Balanced value: .85
*
* The larger the variance value, the smaller the range of active checksums. A small range
* means that it is more likely for two players to have the same checksums.
*
* Smaller variance increases the range of active checksums, however it also increases the
* range of cehcksum strengths. This means that some checksums may be much weaker than others,
* which increases the chances for those players with weaker checksums to tamper with their
* code.
*
* Checksum strength should be about the same for each checksum and there should be enough
* active checksums that it is unlikely that two players will have te same checksum.
*
* .85 is a rather balanced value, but it can be increased for generally stronger checksums
* with smaller ranges or decreased for weaker checksums with wider ranges.
*
* Ex:
* .85 for a checksum of 238,609,294
*
* min: 202,817,899
* range: 35,791,395
*
* 1 in 35,791,395 checksums will work for a player and checksums will all be around same
* strength.
*
* .99 for a checksum of 238,609,294
*
* min: 236,223,201
* range: 2,386,093
*
* 1 in 2,386,093 checksums will work for a player and checksums will all be around same
* strength.
*
* .01 for a checksum of 238,609,294
*
* min: 2,386,092
* range: 236,223,202
*
* 1 in 236,223,202 will work for a player and checksums will have a wide range of strengths
* from weak to strong.
*
*************************************************************************************/
private constant real CHECKSUM_VARIANCE=.85
/*************************************************************************************
*
* PLAYER_CHECKSUM_SALT
*
* Player checksum salt refers to a value that is appended to a player's name when
* generating player hashes. A player's checksum salt helps determine the player's
* checksum for encoders.
*
* This value can be any string
*
* example: "29a\\~alf1!m~..."
*
*************************************************************************************/
private constant string PLAYER_CHECKSUM_SALT=""
endglobals
/*************************************************************************************
*
* Checksum
*
* This is the Checksum used for code security (makes modifications
* difficult). By default, this uses the Knuth Checksum, but
* that can be changed.
*
* BigInt k: number to get the checksum for
* integer m: dividend for modulos
*
* returns: nothing
*
*************************************************************************************/
private function Checksum takes BigInt k, integer m returns integer
return GetKnuthChecksum(k, m)
endfunction
/*************************************************************************************
*
* ApplyScramble
*
* This is essentially for scrambling the code given a player id.
* By default this uses my own scrambling algorithm.
* Because a player hash based on the player's username is used in my
* scrambling algorithm, this will make it so that a player can't load
* up the code of another player with 0 increase to the code size.
* This security alone is not enough to 100% guarantee the player unique
* codes.
*
* BigInt k: number to be scrambled
* integer pid: player id to scramble for
*
* returns: nothing
*
*************************************************************************************/
private function ApplyScramble takes BigInt k, integer pid returns nothing
call Shuffle(k, pid, SHUFFLES)
endfunction
/*************************************************************************************
*
* UnapplyScramble
*
* This is used to undo the scrambling on a number. This should
* revert the number back to what it was before it was scrambled.
*
* BigInt k: number to be unscrambled
* integer pid: player id to unscramble the number for
*
* returns: nothing
*
*************************************************************************************/
private function UnapplyScramble takes BigInt k, integer pid returns nothing
call Unshuffle(k, pid, SHUFFLES)
endfunction
/*
*******************************************************************
*
* struct CodeRange extends array
*
* - A slot that can store a value. These slots have a range of values they can store. The
* - range is from a low bound value to a high bound value. Slots are added to Encoder objects.
* - Specific ranges of slots can link to other slots (an item with 25 charges for example). Not
* - all slots can store values: some are purely pointers (an inventory slot for example which simply
* - points to 6 items).
*
* static method create takes integer lowBound, integer highBound returns CodeRange
* - Creates a new CodeRange that can be added to an Encoder and linked to. CodeRange objects
* - can be linked to multiple times and can link to as many other CodeRange objects as needed.
* - If the lowBound is equal to the highBound, then the CodeRange object returned is a pointer
* - object that can't store values. This can be useful for things like an inventory that simply
* - points to 6 item slots.
*
* - integer lowBound The minimum value that can be stored in the slot.
* - integer highBound The maximum value that can be stored in the slot.
* method link takes integer lowBound, integer highBound, CodeRange linkTo, integer customLinkId returns nothing
* - Links a CodeRange to another CodeRange. The link is only applied if the value that ends
* - up going into the CodeRange fits the link range. For example, if only heroes from 1 to 5 had
* - an inventory of 6, then the lowBound would be 1 and the highBound would be 5 for that link.
* - Passing in the minimal value and maximal values for a given slot does a link for all possible
* - values that can go into that slot.
*
* - integer lowBound The minimal value that can be in the slot to go into the link
* - integer highBound The maximal value that can be in the slot to go into the link
* - CodeRange linkTo The slot that is to be linked to
* - integer customLinkId A link id that can be used to infer current slot for save/load
* method linka takes CodeRange linkTo returns nothing
* - Links all possible values to a slot. Essentially just calls link with the minimum possible
* - value, maximum possible value, and a custom id of 0.
*
* - CodeRange linkTo The slot that is to be linked to
*
************************************************************************************
*
* struct DataBuffer extends array
*
* - The DataBuffer is used for reading and writing values.
* - When opening an Encoder, it is loaded into the DataBuffer
* - and then values can be read/written. An Encoder may be
* - opened for decompressing a code or for compressing a
* - collection of numbers into a code.
*
* readonly integer id
* - Returns the current custom link id (remember link and linka in CodeRange)
* - As slots inside of links may or may not exist (does value fit?), this is
* - a necessity so that a user can easily determine whether they are in a
* - questionable slot or not.
* - Can be used in read and write mode.
* readonly string code
* - Returns all of the values in the DataBuffer as a save/load code.
* - Can only be used when the DataBuffer is finalized.
* - Can be used in write mode.
*
* method write takes integer value returns nothing
* - Writes a value to the DataBuffer. Order of values is determined by the
* - loaded Encoder. For example, if a Hero, gold, and lumber slots were
* - added to the Encoder in that order, then the DataBuffer would expect
* - values fitting those ranges in that order. When all values are written,
* - the DataBuffer is finalized (meaning can't be written to) and the code
* - can be read.
* - Can be used in write mode.
*
* - integer value The value to write to the DataBuffer.
* method read takes nothing returns integer
* - Reads a value out of the DataBuffer. Value read order is determined by
* - the loaded Encoder.
* - Can be used in read mode.
*
************************************************************************************
*
* struct Encoder extends array
*
* - An Encoder is like a frame for compacting values. It is used
* - for storing base, checksum, player checksum, and CodeRange information.
* - the Encoder determines the order of values in a code for the DataBuffer
* - as well. DataBuffers can only be opened through an Encoder.
*
* static method create takes string base, integer minCodeLength, integer maxCodeLength, integer maxChecksum, integer encoderVersion returns Encoder
* - Creates a new Encoder.
*
* - string base The collection of possible characters that the Encoder can
* - use for its save/load codes. Bigger collection means smaller
* - codes.
* - integer minCodeLength Minimum length a code has to be to be loaded. Useful for blocking
* - small values like 1 or 5. Keeps load codes in bounds.
* - integer maxCodeLength Maximal length a code can be to be loaded. Useful for blocking
* - off random large values. Keeps load codes in bounds.
* - integer maxChecksum The maximum checksum value that can be put into the Encoder.
* - Checksums are used to validate codes (ensure they weren't
* - tampered with and that there are no typos in it). The bigger
* - the checksum value, the more secure the code is, but the longer
* - the code will be. I typically use a 6 digit number like 148292
* - or 559321.
* - Maximum checksum- 238609294
* -
* - integer encoderVersion Used for version control on encoders. toString returns this value and
* - convertString takes the toString value and converts it back into the encoder
* - toString() -> encoderVersion
* - convertString(encoderVersion)
* method toString takes nothing returns string
* - Converts the Encoder into a string that represents it. This can
* - be outputted to players to show the Encoder version that their
* - code was saved for. Players can then possibly type in the Encoder
* - version of older codes so that older codes can be loaded.
* static method convertString takes string encoderId returns Encoder
* - Converts an Encoder string into an Encoder. Used primarily for
* - older save/load codes (player might have inputted Encoder id
* - for their save/load code).
*
* - string encoderId The string that represents the Encoder (toString).
* method add takes CodeRange valueSlot returns nothing
* - Adds a new slot to the Encoder. Value order doesn't matter.
*
* - CodeRange valueSlot The CodeRange to be added to the Encoder.
* method read takes string codeString, integer loadingPlayerId returns DataBuffer
* - Opens a DataBuffer for reading and returns the opened DataBuffer. Loads
* - a code string into the DataBuffer. If this returns 0, the code was invalid.
*
* - string codeString The code to load into the DataBuffer
* - integer loadingPlayerId The player id to load the code for (must
* - be a valid human playing player).
* method write takes integer savingPlayerId returns DataBuffer
* - Opens a DataBuffer for writing and returns the opened DataBuffer. If
* - this returns 0, the Encoder or player id were invalid.
*
* - integer savingPlayerId The player id to save the code for (must
* - be a valid human playing player).
*
************************************************************************************/
/*************************************************************************************
*
* Code
*
*************************************************************************************/
globals
private keyword Link
/*************************************************************************************
*
* Encoder Variables
*
*************************************************************************************/
private Table array eb //encoder base
private string array er //encoder code string
private Table array eh //encoder max hash value
private Base es //encoder base for code string
private Link array el //last range added to encoder
private integer array ec //encoder ver to encoder
private integer array ml //minimum code length
private integer array mx //maximum code length
/*************************************************************************************
*
* Range Variables
*
*************************************************************************************/
private integer array rl //low bound
private integer array rh //high bound
private integer array rsh //shifted high bound
private integer array rf //flag
private constant integer AP=1 //always positive
private constant integer SN=2 //sometimes negative
private constant integer AN=3 //always negative
/*************************************************************************************
*
* Link Variables
*
*************************************************************************************/
private integer array li //link id
private boolean array lb //is link
/*************************************************************************************
*
* Player Variables
*
*************************************************************************************/
private integer array ph //hash of player name + salt
private integer array pn //next player
/*************************************************************************************
*
* Data Buffer Variables
*
*************************************************************************************/
//Base
private Base b10=0 //use for writing
//use encoder base for reading
private Link array dm //data buffer looper
private integer dc=0 //data buffer count
private integer array dn //data buffer next, recycler
private integer array dl //data buffer previous
private integer array dv //data buffer value
private Link array di //data buffer link node id
private integer array dd //data buffer node
private integer array dz //data buffer link id
private integer array de //data buffer encoder
private boolean array df //is data buffer finalized?
private boolean array dw //data buffer open for writing?
private integer array dp //data buffer player
endglobals
/*************************************************************************************
*
* Link
*
* Links values together to form dynamic objects.
*
*************************************************************************************/
private struct Link extends array
implement QueueQueue
implement QueueQueueLoop
endstruct
/*************************************************************************************
*
* CodeRange : Link
*
* Used for manipulating value slots in Encoders and CodeRanges.
*
* Methods
* static method create takes integer l, integer h returns CodeRange
* method link takes integer l, integer h, CodeRange p, integer i returns nothing
* method linka takes CodeRange p returns nothing
*
*************************************************************************************/
struct CodeRange extends array
/*************************************************************************************
*
* create
* Creates a code range given a maximum value and a minimum value. Ranges are slots
* that can be added to encoders and other Ranges.
*
* integer l: low bound
* integer h: high bound
*
* returns: CodeRange
*
*************************************************************************************/
static method create takes integer l,integer h returns CodeRange
local Link t
if (h>l) then
//first ensure that the high bound is greater than the low bound
// if the high bound is greater, then it is a valid range for
// storing actual values
//instantiate
set t=Link.allocate()
set t.skips=false
//store the low bound and the high bound into the properties
set rl[t]=l //low bound
set rh[t]=h //high bound
//now have to determine how to store the value
//the value could be negative, it could always be negative,
//or it could always be positive
if (0>h) then
/**********************************
*
* Flag: AN
* Shifted Low Bound: High Bound
* Shifted High Bound: -Low Bound + High Bound
*
**********************************/
set rf[t]=AN //flag to always negative
//the shifhted high bound is the low bound minus
//the high bound
set rsh[t]=-l+h+1
elseif (0>l) then
/**********************************
*
* Flag: SN
* Shifted Low Bound: low bound
* Shifted High Bound: high bound - low bound
*
**********************************/
set rf[t]=SN //flag to sometimes negative
set rsh[t]=h-l+1
else
/**********************************
*
* Flag: AP
* Shifted Low Bound: lowBound
* Shifted High Bound: highBound-lowBound
*
**********************************/
set rf[t]=AP
set rsh[t]=h-l+1
endif
return t
elseif (h==l) then
//if they are equal, then it is a valid pointer range.
// pointer ranges are used to just point to values. They don't go into the actual code,
// but values they point to do.
/**********************************
*
* Flag: 0
*
**********************************/
//simple instiate it as it is meant only for pointing
return Link.allocate()
debug else
//if the high bound is lower than the low bound, then the range isn't valid
// throw an error
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"CODE RANGE CREATE ERROR: HIGH BOUND LOWER THAN LOW BOUND")
debug return 0
endif
return 0
endmethod
/*************************************************************************************
*
* link
* Links a CodeRange to another CodeRange by adding the later range to the
* first range by a pointer.
*
* integer l: low bound
* integer h: high bound
* integer p: CodeRange to add to CodeRange this
* integer i: a user id for identifying the link
*
* returns: nothing
*
*************************************************************************************/
method link takes integer l,integer h,CodeRange p,integer i returns nothing
local Link t
debug if (h>=l and h<=rh[this] and l>=rl[this]) then
//first ensure that the high bound is greater than the low bound
// if the high bound is greater, then it is a valid range for
// storing actual values
//instantiate via point
set t=Link(this).point(p)
set t.skips=false
set rl[t]=l //link low bound
set rh[t]=h //link high bound
set li[t]=i //link id for identification
set lb[t]=true //is link
debug else
//if the high bound is lower than the low bound, then the range isn't valid
// throw an error
debug if (l>h) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"CODE RANGE CREATE ERROR: HIGH BOUND LOWER THAN LOW BOUND")
debug endif
//range was out of bounds
debug if (h>rh[this] or l<rl[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"CODE RANGE CREATE ERROR: LINK RANGE OUT OF BOUNDS")
debug endif
debug endif
endmethod
/*************************************************************************************
*
* linka
* A special link that links all values (lowBound to highBound).
*
* integer p: CodeRange to add to CodeRange this
*
* returns: nothing
*
*************************************************************************************/
method linka takes CodeRange p returns nothing
call link(rl[this],rh[this],p,0)
endmethod
endstruct
/*************************************************************************************
*
* DataBuffer : Queue, Link
*
* Used to read/write code.
*
* Properties
* readonly integer id
*
* Methods
* method write takes integer value returns nothing
* method operator code takes nothing returns string
* method read takes nothing returns integer
* internal method open takes nothing returns nothing
*
*************************************************************************************/
private keyword open
struct DataBuffer extends array
/*************************************************************************************
*
* id
* Retrieves the current open id on the DataBuffer
*
*************************************************************************************/
method operator id takes nothing returns integer
return dz[this]
endmethod
/*************************************************************************************
*
* internal open
* Prepares next slot in DataBuffer
*
* takes: nothing
*
* returns: nothing
*
*************************************************************************************/
method open takes nothing returns nothing
local Link n
local Link y=0
local Link l=0
//retrieve the next node
set n=dm[this].get //node
loop
//if the current node is a pointer, have to determine
// whether to go inside of the pointer or not.
exitwhen not lb[n]
loop
//keep looping until can either go inside of link or the
// node isn't a link
//link ranges of 0 to 0 are pointer links, all values fit into them
exitwhen not lb[n] or (0==rl[n] and 0==rh[n]) or 0==n
//if not a pointer link, then have to retrieve the parent
// node's value
set y=dm[this].depthPointer
loop
exitwhen not lb[y.depthNode]
set y=y.depthPointer
endloop
//the value is stored into the depth pointer's id, check for fit
exitwhen (y.id>=rl[n] and y.id<=rh[n])
set n=dm[this].skip //if value doesn't fit, skip link and all of its contents
endloop
//if the final found node was a pointer (meaning the value fit)
//go inside of it
if (lb[n]) then
//only store link for node ref if the link id isn't 0
if (0!=li[n]) then
set l=n
endif
set n=dm[this].get //go to next node
endif
endloop
//if there is no next node, finalize
if (0==n) then
set df[this]=true
return
endif
//add new node to data buffer
if (0==dn[0]) then
set dc=dc+1
set y=dc
else
set y=dn[0]
set dn[0]=dn[y]
endif
//this buffer is a list because it will eventually have to be looped
//over backwards. The buffer has to be read into the code backwards
//or it will be impossible to read it out because some values exist
//and some don't. Remember integers are written left to right but
//read right to left.
set dl[y]=dl[this]
set dn[y]=this
set dn[dl[y]]=y
set dl[this]=y
//set link id for current node to the node right above it, which
// is always going to be a link
set di[y]=li[l]
set dz[this]=li[l]
//set node for size reference and shifting
set dd[y]=n
endmethod
/*************************************************************************************
*
* write
* Writes a value to the DataBuffer
*
* integer value: value to write
*
* returns: nothing
*
*************************************************************************************/
method write takes integer v returns nothing
local Link y=dl[this]
local Link n=dd[y]
//make sure
// buffer isn't finalized
// buffer is open for writing
// buffer is valid
debug if (not df[this] and dw[this] and 0!=de[this] and v>=rl[n] and v<=rh[n]) then
//store shifted value as current depth pointer id
set dm[this].depthPointer.id=v
//shift the value so that it is smaller
if (rf[n]==AN) then
set v=-v+rh[n]
else
set v=v-rl[n]
endif
//store value
set dv[y]=v
//prepare next slot for writing
call open()
debug else
debug if (v<rl[n] or v>rh[n]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: VALUE OUT OF BOUNDS\n "+I2S(v)+"-> "+I2S(rl[n])+" ... "+I2S(rh[n]))
debug endif
debug if (df[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: DATA BUFFER FINALIZED")
debug endif
debug if (not dw[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: DATA BUFFER NOT OPEN FOR WRITING")
debug endif
debug if (0==de[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: INVALID ENCODER")
debug endif
debug endif
endmethod
/*************************************************************************************
*
* read
* Reads a value out of the DataBuffer
*
* returns: integer
*
*************************************************************************************/
method read takes nothing returns integer
local Link n
local Link o
//make sure
// buffer isn't finalized
// buffer is open for reading
// buffer is valid
debug if (not dw[this] and 0!=de[this]) then
//retrieve current node
set n=dp[this]
set o=dn[n]
//go to next node
set dp[this]=o
set dz[this]=di[o]
//if no more nodes, deallocate and close
if (o==this) then
set de[this]=0
set dn[dl[this]]=dn[0]
set dn[0]=this
endif
return dv[n]
debug else
debug if (dw[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: DATA BUFFER NOT OPEN FOR READING")
debug endif
debug if (0==de[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: INVALID ENCODER")
debug endif
debug endif
return 0
endmethod
/*************************************************************************************
*
* code
* Converts all values in a finalized DataBuffer that is open for writing
* into a save/load code and returns that code.
*
* returns: string
*
*************************************************************************************/
method operator code takes nothing returns string
local BigInt i //code as a number
local integer h //hash
local integer n //node
local string s="" //colorized code string
local string c //character
local boolean l //lowercase
local Base b
//make sure
// buffer is finalized
// buffer is open for writing
// buffer is valid
debug if (df[this] and dw[this] and 0!=de[this]) then
//create the code integer
set i=BigInt.create(b10)
//compress the values into one value
set n=dl[this]
loop
call i.multiply(rsh[dd[n]])
call i.add(dv[n],0)
set n=dl[n]
exitwhen n==this
endloop
//apply checksum
set h=Checksum(i,eh[de[this]][dp[this]])
//if checksum > last value
// range
if (integer(eh[de[this]][dp[this]])>rsh[dd[dl[this]]]) then
//add to front. Have to rebuild the entire number.
call i.destroy()
set i=BigInt.create(b10)
call i.add(h,0)
//compress the values into one value
set n=dl[this]
loop
call i.multiply(rsh[dd[n]])
call i.add(dv[n],0)
set n=dl[n]
exitwhen n==this
endloop
else
//multiply to back
call i.multiply(eh[de[this]][dp[this]])
call i.add(h,0)
endif
//scramble
call ApplyScramble(i,dp[this])
//colorize
set b=eb[de[this]][dp[this]]
set i.base=b
set h=DELIMITER_COUNT
loop
set i=i.previous
exitwhen i.end
set c=b.char(i.digit)
if (0==h) then
set h=DELIMITER_COUNT
set s=s+DELIMITER_COLOR+DELIMITER
endif
set l=StringCase(c,false)==c
if (c==StringCase(c,true) and l) then
if ("0"==c or 0!=S2I(c)) then
//number
set s=s+NUM_COLOR+c
else
//special
set s=s+SPEC_COLOR+c
endif
elseif (l) then
//lower
set s=s+LOWER_COLOR+c
else
//upper
set s=s+UPPER_COLOR+c
endif
set h=h-1
endloop
//close
call i.destroy()
set df[this]=false
set dw[this]=false
set de[this]=0
//deallocate
set dn[dl[this]]=dn[0]
set dn[0]=this
return s+"|r"
debug else
debug if (not df[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: ATTEMPT TO READ PARTIAL BUFFER")
debug endif
debug if (not dw[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: DATA BUFFER NOT OPEN FOR WRITING")
debug endif
debug if (0==de[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: INVALID ENCODER")
debug endif
debug endif
return null
endmethod
endstruct
Attachments
-
152.5 KB Views: 371