- Added a new feature which allows syncing between multiple accounts (in beta) - Added new consumes to the itemlist - Added materials or objectives needed to make the consumes in the tooltips - Fixed some wrong ID's in the itemlist - Increased Performance in item scanning - Added class color codes to the preset lists and ordered the items better
436 lines
11 KiB
Lua
436 lines
11 KiB
Lua
--[[
|
|
|
|
* Copyright (c) 2006, Lucas Alonso & Ben Golus
|
|
* All rights reserved.
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * The names of the authors may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
luke@secretlevel.com
|
|
ben@secretlevel.com
|
|
|
|
]]--
|
|
|
|
--[[
|
|
|
|
A simple lua LZW compression library.
|
|
|
|
----
|
|
|
|
Syntax:
|
|
|
|
lzw:compress("string"[,chatsafe][,CompressTable,TableSize]);
|
|
returns compressed string
|
|
|
|
lzw:decompress("string"[,chatsafe][,CompressTable,TableSize]);
|
|
returns decompressed string
|
|
|
|
----
|
|
|
|
Typical usage:
|
|
|
|
----
|
|
|
|
local compressed = lzw:compress("Some text or arbitrary data in a string");
|
|
local decompressed = lzw:decompress(compressed);
|
|
|
|
----
|
|
|
|
Optimal compression can be achieved by creating code tables for specific input sets. For example:
|
|
|
|
----
|
|
|
|
local DecompressTable = {};
|
|
local CompressTable = {};
|
|
local TableSize;
|
|
|
|
lzw:begin_analyze();
|
|
|
|
for all input strings...
|
|
lzw:analyze_string( Str );
|
|
...
|
|
|
|
TableSize = lzw:end_analyze(DecompressTable, CompressTable);
|
|
|
|
local compressed = lzw:compress("Some data to compress", nil, CompressTable, TableSize);
|
|
local decompressed = lzw:decompress(compressed, nil, DecompressTable, TableSize);
|
|
|
|
----
|
|
|
|
Note that the decompression table and table size must be stored to be able to decompress data at a later point in time.
|
|
|
|
----
|
|
|
|
The resulting compressed string can optionally be output in a chat safe format.
|
|
|
|
----
|
|
|
|
local compressed = lzw:compress( Str, true );
|
|
local decompressed = lzw:decompress( Str, true );
|
|
|
|
----
|
|
|
|
Note that a string that has been compressed using the chat safe flag must be decompressed with the flag or the decompressed
|
|
string may become garbled.
|
|
|
|
]]--
|
|
local version = 1;
|
|
|
|
local lib = {
|
|
version = version;
|
|
RB_TempStream = "";
|
|
WB_TempStream = "";
|
|
WB_Used = 0;
|
|
WB_Curr = 0;
|
|
WB_Max = 8;
|
|
RB_Curr = 0;
|
|
RB_Loaded = 0;
|
|
RB_StreamPos = 0;
|
|
RB_StreamLen = 0;
|
|
RB_ByteTable = {};
|
|
AnalyzeCharacters = {};
|
|
};
|
|
|
|
local function CopyTable(var)
|
|
if ( not var ) then return; end
|
|
|
|
if ( type(var) == "table" ) then
|
|
local ret = {};
|
|
for k, v in var do
|
|
ret[k] = CopyTable(v);
|
|
end
|
|
return ret;
|
|
else
|
|
return var;
|
|
end
|
|
end
|
|
|
|
local function GetRequiredBitDepth( TableSize )
|
|
if( TableSize >= 256 ) then
|
|
return 9;
|
|
elseif( TableSize >= 128 ) then
|
|
return 8;
|
|
elseif( TableSize >= 64 ) then
|
|
return 7;
|
|
elseif( TableSize >= 32 ) then
|
|
return 6;
|
|
elseif( TableSize >= 16 ) then
|
|
return 5;
|
|
elseif( TableSize >= 8 ) then
|
|
return 4;
|
|
elseif( TableSize >= 4 ) then
|
|
return 3;
|
|
elseif( TableSize >= 2 ) then
|
|
return 2;
|
|
elseif( TableSize >= 1 ) then
|
|
return 1;
|
|
end
|
|
end
|
|
|
|
function lib:init()
|
|
if ( self.DefaultLZWTableSize ) then return; end
|
|
|
|
local QMask = bit.lshift( 1, 30 )-1;
|
|
local MaxSize = bit.lshift( 1, 30 );
|
|
local MaxMask = MaxSize-1;
|
|
|
|
self.MaskTable = {};
|
|
for i=0, 30 do
|
|
local TempMask = bit.band( bit.lshift( MaxSize-1, i ), QMask );
|
|
self.MaskTable[i] = bit.band( bit.bnot(TempMask), MaxMask );
|
|
end
|
|
|
|
self.DefaultLZWTable = {};
|
|
self.DefaultLZWReverseTable = {};
|
|
self.DefaultLZWTableSize = 1;
|
|
-- Initialize the reverse code table (Code Index -> String array).
|
|
for i=1, 255 do
|
|
tinsert( self.DefaultLZWReverseTable, string.char(i));
|
|
self.DefaultLZWTableSize = self.DefaultLZWTableSize + 1;
|
|
end
|
|
|
|
-- Our initial code table is the ASCII character set, codes 1-255.
|
|
-- String -> Code Index
|
|
for i, char in ipairs(self.DefaultLZWReverseTable) do
|
|
self.DefaultLZWTable[char] = i;
|
|
end
|
|
end
|
|
|
|
function lib:readbits( Bits, Stream )
|
|
if( Stream ) then
|
|
self.RB_TempStream = Stream;
|
|
self.RB_StreamPos = 1;
|
|
self.RB_StreamLen = string.len(Stream);
|
|
self.RB_Curr = 0;
|
|
self.RB_Loaded = 0;
|
|
for i=1, self.RB_StreamLen do
|
|
self.RB_ByteTable[i] = string.byte( string.sub( self.RB_TempStream, i, i) );
|
|
end
|
|
end
|
|
local Val = 0;
|
|
local BitsRead = 0;
|
|
|
|
while( BitsRead < Bits ) do
|
|
if( self.RB_Loaded <= 0 ) then
|
|
if( self.RB_StreamPos == self.RB_StreamLen+1 ) then
|
|
return -1;
|
|
end
|
|
self.RB_Curr = self.RB_ByteTable[self.RB_StreamPos];
|
|
self.RB_StreamPos = self.RB_StreamPos + 1;
|
|
self.RB_Loaded = self.WB_Max;
|
|
end
|
|
|
|
local Avail = self.RB_Loaded;
|
|
local NumRead = Bits - BitsRead;
|
|
|
|
if( NumRead > Avail ) then
|
|
NumRead = Avail;
|
|
end
|
|
|
|
local CurrMask = bit.band( self.RB_Curr, self.MaskTable[NumRead] );
|
|
if( BitsRead > 0 ) then
|
|
CurrMask = bit.lshift( CurrMask, BitsRead );
|
|
end
|
|
Val = bit.bor( Val, CurrMask );
|
|
|
|
self.RB_Loaded = self.RB_Loaded - NumRead;
|
|
BitsRead = BitsRead + NumRead;
|
|
if( self.RB_Loaded > 0 ) then
|
|
self.RB_Curr = bit.rshift( self.RB_Curr, NumRead );
|
|
end
|
|
end
|
|
return Val;
|
|
end
|
|
|
|
function lib:writebits( Val, Bits )
|
|
if( Val < 0 ) then
|
|
if( self.WB_Used > 0 ) then
|
|
self.WB_TempStream = self.WB_TempStream .. string.char( self.WB_Curr );
|
|
end
|
|
self.WB_Used = 0;
|
|
self.WB_Curr = 0;
|
|
local StreamQ = self.WB_TempStream;
|
|
self.WB_TempStream = "";
|
|
return( StreamQ );
|
|
end
|
|
while( Bits > 0 ) do
|
|
local NumWrite = self.WB_Max - self.WB_Used;
|
|
if( NumWrite > Bits ) then
|
|
NumWrite = Bits;
|
|
end
|
|
local Over = Bits - NumWrite;
|
|
self.WB_Curr = bit.bor( self.WB_Curr, bit.lshift( bit.band( Val, self.MaskTable[NumWrite] ), self.WB_Used ) );
|
|
self.WB_Used = self.WB_Used + NumWrite;
|
|
if( self.WB_Used == self.WB_Max ) then
|
|
self.WB_TempStream = self.WB_TempStream .. string.char( self.WB_Curr );
|
|
self.WB_Curr = 0;
|
|
self.WB_Used = 0;
|
|
end
|
|
Bits = Over;
|
|
Val = bit.rshift( Val, NumWrite );
|
|
end
|
|
end
|
|
|
|
local tab = string.char(9);
|
|
local newline = string.char(10);
|
|
local percent = string.char(37);
|
|
local percentpercent = percent..percent;
|
|
local pipe = string.char(124);
|
|
local special = string.char(255);
|
|
|
|
local specialtab = special..string.char(1);
|
|
local specialnewline = special..string.char(2);
|
|
local specialpercent = special..string.char(3);
|
|
local specialpipe = special..string.char(4);
|
|
local specials = special..string.char(5);
|
|
local specialS = special..string.char(6);
|
|
local specialspecial = special..special;
|
|
|
|
function lib:makesafe( Input )
|
|
Input = string.gsub(Input, "s", specials);
|
|
Input = string.gsub(Input, "S", specialS);
|
|
Input = string.gsub(Input, special, specialspecial);
|
|
Input = string.gsub(Input, tab, specialtab);
|
|
Input = string.gsub(Input, newline, specialnewline);
|
|
Input = string.gsub(Input, percentpercent, specialpercent);
|
|
Input = string.gsub(Input, pipe, specialpipe);
|
|
return Input;
|
|
end
|
|
|
|
function lib:unsafe( Input )
|
|
Input = string.gsub(Input, specials, "s");
|
|
Input = string.gsub(Input, specialS, "S");
|
|
Input = string.gsub(Input, specialspecial, special);
|
|
Input = string.gsub(Input, specialtab, tab);
|
|
Input = string.gsub(Input, specialnewline, newline);
|
|
Input = string.gsub(Input, specialpercent, percent);
|
|
Input = string.gsub(Input, specialpipe, pipe);
|
|
return Input;
|
|
end
|
|
|
|
function lib:compress( Input, Safe, LZWTable, LZWTableSize )
|
|
|
|
if( not LZWTable ) then
|
|
LZWTable = self.DefaultLZWTable;
|
|
LZWTableSize = self.DefaultLZWTableSize;
|
|
end
|
|
|
|
local TableSize = LZWTableSize;
|
|
local CodeTable = CopyTable( LZWTable );
|
|
local StreamPos = 1;
|
|
local StreamLen = string.len(Input);
|
|
local CodeBits = GetRequiredBitDepth( LZWTableSize );
|
|
local MaxCode = 2 ^ CodeBits;
|
|
|
|
-- Grab the first character out of Input into Work.
|
|
local Work = string.sub( Input, StreamPos, StreamPos );
|
|
StreamPos = StreamPos + 1;
|
|
|
|
-- Compress the input string.
|
|
while( StreamPos <= StreamLen ) do
|
|
-- Chop next character off input.
|
|
local Char = string.sub( Input, StreamPos, StreamPos );
|
|
StreamPos = StreamPos + 1;
|
|
-- Lookup string in the code table.
|
|
if( CodeTable[Work..Char] ) then
|
|
-- If it's in the code table, do nothing.
|
|
Work = Work .. Char;
|
|
else
|
|
if( TableSize == MaxCode ) then
|
|
CodeBits = CodeBits + 1;
|
|
MaxCode = MaxCode * 2;
|
|
end
|
|
-- If the string is NOT in the code table, output the previous
|
|
-- string, which WAS in the code table (see above).
|
|
self:writebits( CodeTable[Work], CodeBits );
|
|
|
|
-- Add this new string to the code table.
|
|
CodeTable[Work..Char] = TableSize;
|
|
TableSize = TableSize + 1;
|
|
|
|
-- Reset and start again.
|
|
Work = Char;
|
|
end
|
|
end
|
|
|
|
-- Ran out of new characters, so just output whatever we had at the end.
|
|
self:writebits( CodeTable[Work], CodeBits );
|
|
|
|
-- Finally get result stream.
|
|
if ( Safe ) then
|
|
-- Format safe for sending over chat
|
|
return self:makesafe(self:writebits( -1 ));
|
|
else
|
|
return self:writebits( -1 );
|
|
end
|
|
end
|
|
|
|
function lib:decompress( Input, Safe, LZWReverseTable, LZWTableSize )
|
|
|
|
if ( Safe ) then
|
|
-- Decode chat safe string
|
|
Input = self:unsafe(Input);
|
|
end
|
|
|
|
if( not LZWReverseTable ) then
|
|
LZWReverseTable = self.DefaultLZWReverseTable;
|
|
LZWTableSize = self.DefaultLZWTableSize;
|
|
end
|
|
|
|
local Table = CopyTable( LZWReverseTable );
|
|
local TableSize = LZWTableSize;
|
|
local CodeBits = GetRequiredBitDepth( TableSize );
|
|
local MaxCode = 2 ^ CodeBits;
|
|
|
|
if( TableSize == MaxCode ) then
|
|
CodeBits = CodeBits + 1;
|
|
MaxCode = MaxCode * 2;
|
|
end
|
|
|
|
-- Grab first code.
|
|
local LastCode = self:readbits( CodeBits, Input );
|
|
|
|
local Code = 0;
|
|
-- Result string, starts out with a simple ascii character from the first code.
|
|
local Result = Table[LastCode];
|
|
local LastChar = Result;
|
|
|
|
Code = self:readbits( CodeBits );
|
|
|
|
while( Code > 0 ) do
|
|
local CodeString;
|
|
if( Table[Code] ) then
|
|
CodeString = Table[Code];
|
|
else
|
|
CodeString = Table[LastCode] .. LastChar;
|
|
end
|
|
|
|
Result = Result .. CodeString;
|
|
LastChar = string.sub( CodeString, 1, 1 );
|
|
|
|
-- Create a new code based on the string from the last code, and the first character of this one.
|
|
Table[TableSize] = Table[LastCode] .. LastChar;
|
|
TableSize = TableSize + 1;
|
|
|
|
if( TableSize+1 == MaxCode ) then
|
|
CodeBits = CodeBits + 1;
|
|
MaxCode = MaxCode * 2;
|
|
end
|
|
|
|
LastCode = Code;
|
|
|
|
Code = self:readbits( CodeBits );
|
|
end
|
|
return Result;
|
|
end
|
|
|
|
function lib:begin_analyze()
|
|
self.AnalyzeCharacters = {};
|
|
end
|
|
|
|
function lib:end_analyze( OutTable ,OutReverseTable )
|
|
local OutTableSize = 1;
|
|
OutTable[0] = nil;
|
|
for k, v in self.AnalyzeCharacters do
|
|
OutReverseTable[k] = OutTableSize;
|
|
OutTableSize = OutTableSize + 1;
|
|
end
|
|
|
|
for k, v in OutReverseTable do
|
|
OutTable[v] = k;
|
|
end
|
|
self.AnalyzeCharacters = {};
|
|
return OutTableSize;
|
|
end
|
|
|
|
function lib:analyze_string( String )
|
|
local ClearLen = string.len( String );
|
|
for i=1, ClearLen do
|
|
local c = string.sub( String, i, i );
|
|
self.AnalyzeCharacters[c] = 1;
|
|
end
|
|
end
|
|
|
|
if ( not lzw or lzw.version < lzw.version ) then
|
|
lzw = lib;
|
|
lib:init();
|
|
end
|