|
System
Save/Load
[td]
JASS:
/*
struct SaveCode extends array
static method create takes nothing returns SaveCode
method destroy takes nothing returns nothing
method write takes integer num returns nothing
- writes the number to the code
Range: -2147483648, 2147483647
struct LoadCode extends array
readonly integer playerId
- player that owns the code
method read takes nothing returns integer
- returns integer written to code in order written
method destroy takes nothing returns nothing
function FormatBitInt takes BitInt data returns nothing
- Formats code to multiple of 128 bits
use this before applying hash, encryption, or saving
function SaveFile takes string mapName, string fileName, BitInt data returns nothing
- saves code to file
function LoadFile takes string mapName, string fileName returns LoadStream
- returns set of codes from file (1 per player)
function GetLoadProgress takes integer playerId returns real
- returns load progress of player (call while LoadFile is running)
struct LoadStream extends array
method read takes nothing returns LoadCode
- read code from stream (get a player's code)
0 if no codes left
method destroy takes nothing returns nothing
*/
library FormatBitInt uses BitInt
function FormatBitInt takes BitInt data returns nothing
local BitInt node = data
local integer count = 0
if (0 == data) then
return
endif
loop
set node = node.next
exitwhen node == data
if (node.bitSize < 8) then
set node.bits = node.bits*GetBitNumber(8 - node.bitSize + 1)
set node.bitSize = 8
endif
set count = count + 1
endloop
set data.bitCount = count*8
set count = 16 - count + count/16*16
set count = count - count/16*16
if (0 == data.bitCount) then
set count = 16
endif
loop
exitwhen 0 == count
set count = count - 1
call data.addNode()
set data.prev.bitSize = 8
set data.bitCount = data.bitCount + 8
endloop
endfunction
endlibrary
library SaveCode uses BitInt
struct SaveCode extends array
static method create takes nothing returns SaveCode
return BitInt.create()
endmethod
method write takes integer num returns nothing
local integer size = GetBitSize(num)
if (0 == size) then
set size = 1
endif
if (5 + size > 32) then
call BitInt(this).write(size - 1, 5)
call BitInt(this).write(num, size)
else
call BitInt(this).write((size - 1)*GetBitNumber(size + 1) + num, size + 5)
endif
endmethod
method destroy takes nothing returns nothing
call BitInt(this).destroy()
endmethod
endstruct
endlibrary
library LoadCode uses BitInt
struct LoadCode extends array
readonly integer playerId
static method create takes integer playerId returns LoadCode
local thistype this = BitInt.create()
set this.playerId = playerId
return this
endmethod
method read takes nothing returns integer
return BitInt(this).read(BitInt(this).read(5) + 1)
endmethod
method destroy takes nothing returns nothing
call BitInt(this).destroy()
endmethod
endstruct
endlibrary
library SaveFile uses SaveCode, FileIO, Thread
function SaveFile takes string mapName, string fileName, BitInt data returns nothing
local integer speed = 8
local integer count = data.bitCount
local BitInt node = data
local File file = 0
local integer rounds
local Thread thread = Thread.create()
local integer lineLength
local string line
local boolean doSync = true
if (data != 0) then
set file = File.open(mapName, fileName, File.Flag.WRITE)
endif
if (count - count/32*32 != 0) then
set count = count/32 + 1
else
set count = count/32
endif
if (0 != file) then
call file.write(I2S(count))
endif
loop
if (node != 0) then
set rounds = speed
loop
set lineLength = 128
set line = ""
loop
exitwhen node.next == data or 0 == lineLength
set node = node.next
set lineLength = lineLength - 2
set line = line + BitInt.charTable[node.bits/16] + BitInt.charTable[node.bits - node.bits/16*16]
endloop
if (line == "") then
set line = BitInt.charTable[0]
endif
call file.write(line)
set rounds = rounds - 1
exitwhen node.next == data or 0 == rounds
endloop
endif
if (doSync and (node == 0 or node.next == data)) then
set node = 0
call thread.sync()
set doSync = false
endif
call TriggerSyncReady()
exitwhen thread.synced
endloop
call thread.destroy()
if (0 != file) then
call file.close()
endif
endfunction
endlibrary
library LoadFile uses Network, LoadCode, FileIO, BitInt
globals
private real array progress
endglobals
function GetLoadProgress takes integer playerId returns real
return progress[playerId]
endfunction
private struct Loader extends array
private File file
private string buffer
private integer bufferPosition
private method getNextBroadcast takes nothing returns integer
set bufferPosition = bufferPosition + 8
if (bufferPosition == 128) then
set buffer = file.read()
set bufferPosition = 0
endif
return BitInt.char2Int(SubString(buffer, bufferPosition + 0, bufferPosition + 1))*0x10000000 + /*
*/ BitInt.char2Int(SubString(buffer, bufferPosition + 1, bufferPosition + 2))*0x1000000 + /*
*/ BitInt.char2Int(SubString(buffer, bufferPosition + 2, bufferPosition + 3))*0x100000 + /*
*/ BitInt.char2Int(SubString(buffer, bufferPosition + 3, bufferPosition + 4))*0x10000 + /*
*/ BitInt.char2Int(SubString(buffer, bufferPosition + 4, bufferPosition + 5))*0x1000 + /*
*/ BitInt.char2Int(SubString(buffer, bufferPosition + 5, bufferPosition + 6))*0x100 + /*
*/ BitInt.char2Int(SubString(buffer, bufferPosition + 6, bufferPosition + 7))*0x10 + /*
*/ BitInt.char2Int(SubString(buffer, bufferPosition + 7, bufferPosition + 8))
endmethod
private method broadcastPercentComplete takes integer playerId, real percent returns nothing
set progress[playerId] = percent
endmethod
implement StreamMod
static method loadFile takes string mapName, string fileName returns Loader
local File file = File.open(mapName, fileName, File.Flag.READ)
local thistype this = allocate(S2I(file.read()))
local integer playerId = 11
set this.file = file
set buffer = ""
set bufferPosition = 128 - 8
call this.synchronize()
call file.close()
loop
set progress[playerId] = -1
exitwhen 0 == playerId
set playerId = playerId - 1
endloop
return this
endmethod
private static method onInit takes nothing returns nothing
local integer playerId = 11
loop
set progress[playerId] = -1
exitwhen 0 == playerId
set playerId = playerId - 1
endloop
endmethod
endstruct
struct LoadStream extends array
private integer index
method read takes nothing returns LoadCode
local BitInt data
local integer playerId
local integer size
local integer rounds
local integer value
local integer pos
loop
exitwhen index == 12 or (Loader(this).size[index] != 0 and GetPlayerSlotState(Player(index)) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(index)) == MAP_CONTROL_USER)
set index = index + 1
endloop
if (index == 12) then
return 0
endif
set data = LoadCode.create(index)
set pos = 0
set playerId = index
set size = Loader(this).size[playerId]
set data.bitCount = size*32
loop
set rounds = 512
loop
set value = Loader(this).read(playerId, pos)
call data.addNode()
if (value < 0) then
set data.prev.bits = 0x80 + (-2147483648 + value)/0x1000000
else
set data.prev.bits = value/0x1000000
endif
set data.prev.bitSize = 8
set value = value - data.prev.bits*0x1000000
call data.addNode()
set data.prev.bits = value/0x10000
set data.prev.bitSize = 8
set value = value - data.prev.bits*0x10000
call data.addNode()
set data.prev.bitSize = 8
set data.prev.bits = value/0x100
call data.addNode()
set data.prev.bitSize = 8
set data.prev.bits = value - value/0x100*0x100
set pos = pos + 1
set rounds = rounds - 1
exitwhen pos == size or 0 == rounds
endloop
call TriggerSyncReady()
exitwhen pos == size
endloop
set index = index + 1
return data
endmethod
method destroy takes nothing returns nothing
set index = 0
call Loader(this).deallocate()
endmethod
endstruct
function LoadFile takes string mapName, string fileName returns LoadStream
return Loader.loadFile(mapName, fileName)
endfunction
endlibrary
____________________________________________________________________________________________________
| |
|
vJASS Demo
[td]
readme
core
save
load
architecture
compression
JASS:
/*
* This map includes
*
* 1. save/load system
* - basic save/load
* - does not actual implement save/load into a map, this is used
* - to do save/load***
*
* 2. encryption system (extra)
* - AES 128 bit
*
* Encryption = scrambling
*
* 3. cryptographic hash system (extra)
* - MD5
*
* Hash = tamper protection
*
* The 3 above systems can be used to create a map specific save/load system,
* either for vJASS or GUI.
*
* The included GUI Save/Load System is one example of such a system, created
* for GUI users and general maps. It does not support advanced features as
* you'll later learn about though.
*
* Besides the 3 systems, a collection of snippets are also included for
* saving units
*
* SaveLoad Unit (see trigger comment for API)
*/
____________________________________________________________________________________________________
JASS:
/*
* The save/load core is where to place information general to your save/load
* system. This is purely for organizational purposes.
*
* The core may include
*
* 1. map name
* 2. encoder array
* 2a. encryption strength (for encoders)
* 2b. encoder password (for encoders)
* 3. apply hash boolean
*/
/*
* Example
*/
scope SaveLoadCore
/*
* Settings
*/
globals
/*
* This is the name of the map
*/
constant string MAP_NAME = "MyFirstMap"
/*
* A more advanced save/load system may be used when saving perhaps
* multiple heroes for a given map.
*
* The naive approach to save multiple heroes is saving them all in
* one code, then loading the entire code and allowing the player
* to pick which hero they want to play as.
*
* The smart approach is making 1 code for each hero and then having
* a separate code that contains the list of heroes. This will make
* the save/load faster.
*
* Of course, alternatively, you could allow a player to pick a hero
* and then attempt to load that hero before generating a new hero
* (1 code per hero without the list).
*
* If you'd prefer players be able to create multiple of the same
* hero, you can have a list for each hero.
*
* Putting all of the heroes into one code does improve security,
* but it comes at the hefty cost of speed (possible FPS drop)
* and data limits (limit is ~65,000 bits of data per code).
*
* As such, the demonstration of save/load with vJASS provided
* within this map is only a demonstration of what can be done.
* It is not meant to be used as a system. This demonstration only
* supports the naive approach stated above.
*/
/*
* Encryption settings
*/
constant integer ENCRYPTION_STRENGTH = 2 //1 to 16
constant string ENCRYPTION_PASSWORD = "Hohoho"
/*
* Hash settings
*/
constant boolean APPLY_HASH = true
endglobals
globals
Encoder array encoder //these are used for encryption and decryption
endglobals
private struct CoreInit extends array
/*
* This method must be executed as encoders use synchronous natives
*/
private static method init takes nothing returns nothing
local integer playerId
/*
* Encoder creation
*/
if (ENCRYPTION_STRENGTH > 0) then
set playerId = 11
loop
if (/*
*/GetPlayerSlotState(Player(playerId)) == PLAYER_SLOT_STATE_PLAYING and /*
*/GetPlayerController(Player(playerId)) == MAP_CONTROL_USER /*
*/) then
/*
* The player name is used with the encryption password to make
* encryption both player unique and map unique
*
* Encoder creation generates a cipher by applying MD5 to the
* player name + password
*/
set encoder[playerId] = Encoder.create(GetPlayerName(Player(playerId)) + ENCRYPTION_PASSWORD)
endif
exitwhen 0 == playerId
set playerId = playerId - 1
endloop
endif
endmethod
private static method onInit takes nothing returns nothing
call init.execute()
endmethod
endstruct
endscope
____________________________________________________________________________________________________
JASS:
/*
* Saving may be accomplished with one of two techniques.
*
* Technique #1
*
* A -save command may be used just like in traditional save/load. When
* the player types -save, their information is saved.
*
* Technique #2
*
* A timer can be used to save a player's information whenever it
* expires. This will mean that the player will not have to save
* information themselves and can leave the game freely. This is great
* if a player disconnects as they won't loes their information.
*
* The danger in using a timer is that the player might leave the game
* while their code is being written to the file containing their
* information. When using periodic saving, you should alternate between
* two files so that if one file becomes corrupt due to a player leaving,
* the other file will still be ok. This problem can also occur with
* technique #1 when a player disconnects while their code is being written
* to a file.
*
* The chances of a corrupt file are not at all rare. Save/Load is not
* instantaneous and actually occurs over a period of time.
*
* The GUI version of save/load does not support alternating between two
* files for ultimate player safety. Nor will this vJASS demonstration.
* As such, someone may want to write up a GUI Save/Load system using
* the 3 systems of this map that does have file alternation.
*
* For file alternation, a third file must be used to determine the last
* correctly saved file.
*/
scope Save
private struct Save extends array
private static method save takes integer playerId, SaveCode data returns nothing
/*
* SaveCode essentially has 1 method for writing data
*
* call data.write(integer)
*
* So writing data to it is very simple. Any integer can be
* written, negative or positive.
*/
call data.write(15)
call data.write(-24298)
call data.write(38395)
endmethod
private static method saveComplete takes integer playerId returns nothing
call DisplayTimedTextToPlayer(Player(playerId),0,0,60000,"Save Complete")
endmethod
/*
* Remember that a save is not instant. This will ensure that
* a player does not save while a save is already in progress.
*/
private static boolean array isSaving
/*
* Notice that this is not a trigger condition, but rather a
* trigger action. This is because synchronus natives will be
* used.
*/
private static method onSave takes nothing returns nothing
local integer playerId
local SaveCode data = 0
local Thread thread
set playerId = GetPlayerId(GetTriggerPlayer())
if (isSaving[playerId]) then
return
endif
set isSaving[playerId] = true
/*
* Threads are used for synchronization
* In this case, the Thread is used to make the non saving players
* wait until the saving player is finished saving
*
* A thread will not be considered synchronized until all players in the game
* have sent synchronization requests. Every player except for the saving
* player will send a sync request with the following code.
*/
set thread = Thread.create()
if (GetPlayerId(GetLocalPlayer()) != playerId) then
call thread.sync()
endif
/*
* This loop will continue until the player is finished saving
* The loop exits when the thread is synchronized, and the saving player
* does not sync the thread until that player has finished saving.
*/
loop
if (GetPlayerId(GetLocalPlayer()) == playerId) then
/*
* A save code is only created for the saving player
*/
set data = SaveCode.create()
/*
* create a save method outside of this area for cleaner code :)
*/
call save(playerId, data)
/*
* The player id is set to -1 here so that the player does not enter
* this area again
*/
set playerId = -1
call thread.sync()
endif
/*
* TriggerSyncReady is used here to prevent op limit + for short wait
* It is a synchronous native
*/
call TriggerSyncReady()
exitwhen thread.synced
endloop
set playerId = GetPlayerId(GetTriggerPlayer())
/*
* FormatBitInt is a major operation, thus an evaluation is used
* It formats the save/load code to be in a multiple of 128 bits
* This is necessary because the LoadFile command reads out 128
* bits at a time. As such, it may end up reading out extra 0s, which
* will end up breaking the decryption process.
*/
call FormatBitInt.evaluate(data)
/*
* The hash is applied to the start of the code. Apply the hash before
* encryption so that players will not have access to the hash.
*
* The encryption goes from left to right (start to back). This means that
* the hash, if the encryption strength is >= 2, will affect the rest of the
* code. The hash is dependent on the rest of the code, so having a hash and
* encryption strenght >= 2 will end up making the entire code look
* completely different with tiny bit changes.
*
* encryption and hashing have synchronous native calls in them, so *ALL*
* players must call them or a desync will occur.
*/
if (APPLY_HASH) then
call ApplyHash(data)
endif
call encoder[playerId].encrypt(data, ENCRYPTION_STRENGTH)
/*
* The second argument is the file name. For this, can just use the
* playe's name. The file will only save for the saving player because
* data == 0 for all of the other players.
*
* SaveFile has synchronous native calls in it, so *ALL* players
* must call it or a desync will occur.
*/
call SaveFile(MAP_NAME, GetPlayerName(GetLocalPlayer()), data)
/*
* Destroy the save code
*/
if (0 != data) then
call data.destroy()
endif
/*
* Allow the saving player to save again
*/
set isSaving[playerId] = false
/*
* You may want to let the player know that the save was completed
* This can be done with a multiboard, text tags, or even a game message
*
* Remember that a code can become corrupt if a player leaves while they are
* still saving, so letting them know when their save is complete is a must.
* This is only true for the -save command. For periodic saving, it is not
* important to let a player know when their save is complete.
*/
call saveComplete(playerId)
endmethod
private static method onInit takes nothing returns nothing
local integer playerId = 11
local trigger t = CreateTrigger()
call TriggerAddAction(t, function thistype.onSave)
/*
* Registration of player -save command
*/
loop
if (GetPlayerSlotState(Player(playerId)) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(playerId)) == MAP_CONTROL_USER) then
call TriggerRegisterPlayerChatEvent(t, Player(playerId), "-save", true)
endif
exitwhen 0 == playerId
set playerId = playerId - 1
endloop
set t = null
endmethod
endstruct
endscope
____________________________________________________________________________________________________
JASS:
/*
* Loading may either be done when the map starts (load up all data for
* a player) or done in 2 stages.
*
* Stage 1
*
* Load up the file containing the list of heroes
*
* Or
*
* Wait until the player picks a hero
*
* Stage 2
*
* Load hero that player picked
*
* For this demonstration, it will simply load up all data for a player.
*
* If alternating files are used, any given hero will have three files
*
* File 1: last *fully* saved file (used to pick code #1 or code #2)
* File 2: code #1
* File 3: code #2
*
* For this demonstration, only 1 file will be used.
*/
scope Load
private struct Load extends array
private static method load takes LoadCode data returns nothing
/*
* LoadCode contains 2 useful things
*
* data.playerId
* - playerId refers to the loading player
*
* data.read()
* - reads integer out of code in order that they were written
* - in
*/
/*
* 15
*/
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"Loaded: "+I2S(data.read()))
/*
* -24298
*/
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"Loaded: "+I2S(data.read()))
/*
* 38395
*/
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"Loaded: "+I2S(data.read()))
/*
* 0 (no data left)
*
* BitInt(data).bitCount may or not be 0 though
*/
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"Loaded: "+I2S(data.read()))
endmethod
/*
* This method can be used to show the download progress for all players
*
* Use multiboard, text tag, or game message
*/
private static method updateProgress takes nothing returns nothing
local integer playerId = 11
call ClearTextMessages()
loop
/*
* If the player is not human, the load progress is -1, so only
* display human load progress.
*
* This can be useful for a progress multiboard as players with
* -1 can be shown in red
*
* Players < 100 can be shown in white
* Players == 100 can be shown in green
*/
if (GetLoadProgress(playerId) > -1) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"Download Progress For (" + GetPlayerName(Player(playerId)) + "): "+R2S(GetLoadProgress(playerId)) + "%")
endif
exitwhen 0 == playerId
set playerId = playerId - 1
endloop
endmethod
/*
* This method is executed as it has synchronous calls
*/
private static method onLoad takes nothing returns nothing
local LoadStream stream
local LoadCode data
local timer t
local boolean validated
/*
* Load the file
*
* A timer is used to display download progress
*/
set t = CreateTimer()
call TimerStart(t,.03125000,true,function thistype.updateProgress)
/*
* Notice that LoadFile returns a LoadStream. A LoadStream essentially
* contains all of the codes for all of the players for the given file.
*/
set stream = LoadFile(MAP_NAME, GetPlayerName(GetLocalPlayer()))
call PauseTimer(t)
call DestroyTimer(t)
set t = null
/*
* If File.enabled is false, then the player is currently not able
* to load files.
*
* Let them know somehow how to enable loading.
*
* Step 1. Go to C Drive
* Step 2. Go to !! AllowLocalFiles folder
* Step 3. Run AllowLocalFiles.bat
* Step 4. Restart Warcraft 3
*
* Even if a player doesn't have loading enabled, they will still have
* saving enabled, so they do not have to restart Warcraft 3 until
* they have finished their current game. Just let them know to restart
* before joining another game.
*
* A player will only ever have to run AllowLocalFiles.bat once per
* machine.
*
* I let them know via game messages here, but you might want to let
* them know via a multiboard or quest
*/
if (not File.enabled) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"Loading is currently disabled for you")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"To enable loading")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000," ")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"1. Go to C Drive")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"2. Go to !! AllowLocalFiles folder")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"3. Run AllowLocalFiles.bat")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"4. Restart Warcraft 3")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"You do not have to restart Warcraft 3 until after this game. Saving is Enabled.")
endif
/*
* There is no need for a Thread here because all of the players
* have the same data
*/
loop
/*
* Get the next code from the stream
*
* If the code was 0, then there are no codes left in the stream
*
* data.playerId gives the player that owns the code
*/
set data = stream.read()
exitwhen 0 == data
/*
* First decrypt the code and then remove the hash if hashing
* is used
*/
call encoder[data.playerId].decrypt(data, ENCRYPTION_STRENGTH)
set validated = not APPLY_HASH or ValidateHash(data)
if (validated) then
/*
* If the code was valid, load it
*/
call load(data)
else
/*
* Load Failed, code is corrupt
*/
endif
call data.destroy()
call TriggerSyncReady()
endloop
call stream.destroy()
endmethod
/*
* Initialization is done in 3 stages
*
* Stage 1
* run a timer
*
* Stage 2
* execute
*
* Stage 3
* wait for the game to start (synchronous)
*
* begin loading (loading must be done AFTER all players
* have finished loading the map)
*/
private static method init2 takes nothing returns nothing
call WaitForGameToStart()
call onLoad()
endmethod
private static method init takes nothing returns nothing
call DestroyTimer(GetExpiredTimer())
call init2.execute()
endmethod
private static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(),0,false,function thistype.init)
endmethod
endstruct
endscope
____________________________________________________________________________________________________
JASS:
/*
* So far, only single version save/load has been discussed. Let's consider
* a map that starts with this information
*
* hero
* xp
* strength
* agility
* intelligence
* life
* mana
* position (x, y, facing)
* inventory
* item 1
* charges
* item 2
* charges
* item 3
* charges
* item 4
* charges
* item 5
* charges
* item 6
* charges
*
* Later, in version 2 of the map, the author decides to add a pet. They
* also decide to add a bank that can hold gear.
*
* pet
* xp
* life
* mana
* position (x, y, facing)
*
* bank
* item 1
* charges
* item 2
* charges
* item 3
* charges
* item 4
* charges
* item 5
* charges
* item 6
* charges
*
* With single version save/load, this is impossible
*
* Muti-Version save/load means to adopt this architecture
*
* Saver
*
* Loader Version 1
* Loader Version 2
* Loader Version 3
* Loader Version X (4+ etc)
*
* Whenever the architecture of the code is changed, like new information
* is added, a new loader is added
*
* The version of the code can be stored at the start of the code
*
* call code.write(version)
*
* Later, the version is read out of the code
*
* version = code.read()
*
* And the correct loader is executed
*
* ExecuteFunc("Loader" + I2S(version)) //functions with
* //loader # on them
*
* or
*
* call TriggerExecute(loaders[version]) //trigger array
*
* This will allow a map to load up both old codes from previous map
* versions and new codes.
*
* Only 1 saver is needed because the map should always save using the
* latest version. There is no purpose to saving codes with old map versions.
*/
____________________________________________________________________________________________________
JASS:
/*
* To improve the speed of save/load and reduce size(allowing more data),
* traditional save/load compression techniques can still be utilized
*
* Catalogs
* Lossy Compression
* Partial Sets
* Range shifting (when the max data is 31 bits)
*
* See save/load with snippets for compression techniques
* and resources
*
* hiveworkshop.com/forums/spells-569/save-load-snippets-v2-1-0-5-a-202714/?prev=mmr%3D6
*
* When using these techniques, do not use SaveCode or LoadCode,
* use BitInt directly
*
* data = BitInt.create()
* data.write(value, bitSize)
* data.read(bitSize)
* bitSize = GetBitSize(max size)
*/
____________________________________________________________________________________________________
____________________________________________________________________________________________________
| |