diff --git a/2/3/4/5/6/7/ElvUI/Core/core.lua b/2/3/4/5/6/7/ElvUI/Core/core.lua index f086a2d..6fb40ad 100644 --- a/2/3/4/5/6/7/ElvUI/Core/core.lua +++ b/2/3/4/5/6/7/ElvUI/Core/core.lua @@ -304,8 +304,8 @@ function E:PLAYER_ENTERING_WORLD() if(not self.MediaUpdated) then self:UpdateMedia() self.MediaUpdated = true - else - self:ScheduleTimer("CheckRole", 0.01) + -- else + -- self:ScheduleTimer("CheckRole", 0.01) end local _, instanceType = IsInInstance() diff --git a/2/3/4/5/6/7/ElvUI/Core/distributor.lua b/2/3/4/5/6/7/ElvUI/Core/distributor.lua index e69de29..7d19dec 100644 --- a/2/3/4/5/6/7/ElvUI/Core/distributor.lua +++ b/2/3/4/5/6/7/ElvUI/Core/distributor.lua @@ -0,0 +1,546 @@ +local E, L, V, P, G = unpack(ElvUI) +local D = E:NewModule("Distributor", "AceEvent-3.0","AceTimer-3.0","AceComm-3.0","AceSerializer-3.0") +local LibCompress = LibStub:GetLibrary("LibCompress"); +local LibBase64 = LibStub("LibBase64-1.0"); + +local tonumber, type, pcall, loadstring = tonumber, type, pcall, loadstring; +local len, format, split, find = string.len, string.format, string.split, string.find; + +local CreateFrame = CreateFrame; +local GetNumRaidMembers, UnitInRaid = GetNumRaidMembers, UnitInRaid; +local GetNumPartyMembers, UnitInParty = GetNumPartyMembers, UnitInParty; +local ACCEPT, CANCEL, YES, NO = ACCEPT, CANCEL, YES, NO; + +---------------------------------- +-- CONSTANTS +---------------------------------- + +local REQUEST_PREFIX = "ELVUI_REQUEST" +local REPLY_PREFIX = "ELVUI_REPLY" +local TRANSFER_PREFIX = "ELVUI_TRANSFER" +local TRANSFER_COMPLETE_PREFIX = "ELVUI_COMPLETE" + +-- The active downloads +local Downloads = {} +local Uploads = {} + +function D:Initialize() + self:RegisterComm(REQUEST_PREFIX) + self:RegisterEvent("CHAT_MSG_ADDON") + + self.statusBar = CreateFrame("StatusBar", "ElvUI_Download", UIParent) + E:RegisterStatusBar(self.statusBar); + E:CreateBackdrop(self.statusBar, "Default") + self.statusBar:SetStatusBarTexture(E.media.normTex) + self.statusBar:SetStatusBarColor(0.95, 0.15, 0.15) + self.statusBar:SetWidth(250) + self.statusBar:SetWidth(18) + self.statusBar.text = self.statusBar:CreateFontString(nil, "OVERLAY") + E:FontTemplate(self.statusBar.text) + self.statusBar.text:SetPoint("CENTER", 0, 0) + self.statusBar:Hide() +end + +-- Used to start uploads +function D:Distribute(target, otherServer, isGlobal) + local profileKey, data; + if not isGlobal then + if ElvDB.profileKeys then + profileKey = ElvDB.profileKeys[E.myname.." - "..E.myrealm] + end + + data = ElvDB.profiles[profileKey] + else + profileKey = "global" + data = ElvDB.global + end + + if not data or not profileKey then return end + + local serialData = self:Serialize(data) + local length = len(serialData) + local message = format("%s:%d:%s", profileKey, length, target) + + Uploads[profileKey] = { + serialData = serialData, + target = target, + } + + if otherServer then + local numParty, numRaid = GetNumPartyMembers(), GetNumRaidMembers(); + if(numRaid > 0 and UnitInRaid("target")) then + self:SendCommMessage(REQUEST_PREFIX, message, "RAID"); + elseif(numParty > 0 and UnitInParty("target")) then + self:SendCommMessage(REQUEST_PREFIX, message, "PARTY"); + else + E:Print(L["Must be in group with the player if he isn't on the same server as you."]) + return + end + else + self:SendCommMessage(REQUEST_PREFIX, message, "WHISPER", target) + end + self:RegisterComm(REPLY_PREFIX) + E:StaticPopup_Show("DISTRIBUTOR_WAITING") +end + +function D:CHAT_MSG_ADDON(_, _, message, _, sender) + if not Downloads[sender] then return end + local cur = len(message) + local max = Downloads[sender].length + Downloads[sender].current = Downloads[sender].current + cur + + if Downloads[sender].current > max then + Downloads[sender].current = max + end + + self.statusBar:SetValue(Downloads[sender].current) +end + +function D:OnCommReceived(prefix, msg, dist, sender) + if prefix == REQUEST_PREFIX then + local profile, length, sendTo = split(":", msg) + + if dist ~= "WHISPER" and sendTo ~= E.myname then + return + end + + if self.statusBar:IsShown() then + self:SendCommMessage(REPLY_PREFIX, profile..":NO", dist, sender) + return + end + + local textString = format(L["%s is attempting to share the profile %s with you. Would you like to accept the request?"], sender, profile) + if profile == "global" then + textString = format(L["%s is attempting to share his filters with you. Would you like to accept the request?"], sender) + end + + E.PopupDialogs["DISTRIBUTOR_RESPONSE"] = { + text = textString, + OnAccept = function() + self.statusBar:SetMinMaxValues(0, length) + self.statusBar:SetValue(0) + self.statusBar.text:SetFormattedText(L["Data From: %s"], sender) + E:StaticPopupSpecial_Show(self.statusBar) + self:SendCommMessage(REPLY_PREFIX, profile..":YES", dist, sender) + end, + OnCancel = function() + self:SendCommMessage(REPLY_PREFIX, profile..":NO", dist, sender) + end, + button1 = ACCEPT, + button2 = CANCEL, + timeout = 32, + whileDead = 1, + hideOnEscape = 1, + } + E:StaticPopup_Show("DISTRIBUTOR_RESPONSE") + + Downloads[sender] = { + current = 0, + length = tonumber(length), + profile = profile, + } + + self:RegisterComm(TRANSFER_PREFIX) + elseif prefix == REPLY_PREFIX then + self:UnregisterComm(REPLY_PREFIX) + E:StaticPopup_Hide("DISTRIBUTOR_WAITING") + + local profileKey, response = split(":", msg) + if response == "YES" then + self:RegisterComm(TRANSFER_COMPLETE_PREFIX) + self:SendCommMessage(TRANSFER_PREFIX, Uploads[profileKey].serialData, dist, Uploads[profileKey].target) + Uploads[profileKey] = nil + else + E:StaticPopup_Show("DISTRIBUTOR_REQUEST_DENIED") + Uploads[profileKey] = nil + end + elseif prefix == TRANSFER_PREFIX then + self:UnregisterComm(TRANSFER_PREFIX) + E:StaticPopupSpecial_Hide(self.statusBar) + + local profileKey = Downloads[sender].profile + local success, data = self:Deserialize(msg) + + if success then + local textString = format(L["Profile download complete from %s, would you like to load the profile %s now?"], sender, profileKey) + + if profileKey == "global" then + textString = format(L["Filter download complete from %s, would you like to apply changes now?"], sender) + else + if not ElvDB.profiles[profileKey] then + ElvDB.profiles[profileKey] = data + else + textString = format(L["Profile download complete from %s, but the profile %s already exists. Change the name or else it will overwrite the existing profile."], sender, profileKey) + E.PopupDialogs["DISTRIBUTOR_CONFIRM"] = { + text = textString, + button1 = ACCEPT, + hasEditBox = 1, + editBoxWidth = 350, + maxLetters = 127, + OnAccept = function(self) + ElvDB.profiles[self.editBox:GetText()] = data + LibStub("AceAddon-3.0"):GetAddon("ElvUI").data:SetProfile(self.editBox:GetText()) + E:UpdateAll(true) + Downloads[sender] = nil + end, + OnShow = function(self) self.editBox:SetText(profileKey) self.editBox:SetFocus() end, + timeout = 0, + exclusive = 1, + whileDead = 1, + hideOnEscape = 1, + preferredIndex = 3 + } + + E:StaticPopup_Show("DISTRIBUTOR_CONFIRM") + self:SendCommMessage(TRANSFER_COMPLETE_PREFIX, "COMPLETE", dist, sender) + return + end + end + + E.PopupDialogs["DISTRIBUTOR_CONFIRM"] = { + text = textString, + OnAccept = function() + if profileKey == "global" then + E:CopyTable(ElvDB.global, data) + E:UpdateAll(true) + else + LibStub("AceAddon-3.0"):GetAddon("ElvUI").data:SetProfile(profileKey) + end + Downloads[sender] = nil + end, + OnCancel = function() + Downloads[sender] = nil + end, + button1 = YES, + button2 = NO, + whileDead = 1, + hideOnEscape = 1, + } + + E:StaticPopup_Show("DISTRIBUTOR_CONFIRM") + self:SendCommMessage(TRANSFER_COMPLETE_PREFIX, "COMPLETE", dist, sender) + else + E:StaticPopup_Show("DISTRIBUTOR_FAILED") + self:SendCommMessage(TRANSFER_COMPLETE_PREFIX, "FAILED", dist, sender) + end + elseif prefix == TRANSFER_COMPLETE_PREFIX then + self:UnregisterComm(TRANSFER_COMPLETE_PREFIX) + if msg == "COMPLETE" then + E:StaticPopup_Show("DISTRIBUTOR_SUCCESS") + else + E:StaticPopup_Show("DISTRIBUTOR_FAILED") + end + end +end + +local function GetProfileData(profileType) + if(not profileType or type(profileType) ~= "string") then + E:Print("Bad argument #1 to 'GetProfileData' (string expected)"); + return; + end + + local profileKey; + local profileData = {}; + + if(profileType == "profile") then + if(ElvDB.profileKeys) then + profileKey = ElvDB.profileKeys[E.myname.." - "..E.myrealm]; + end + + profileData = E:CopyTable(profileData , ElvDB.profiles[profileKey]) + profileData = E:RemoveTableDuplicates(profileData, P); + elseif(profileType == "private") then + local privateProfileKey = E.myname.." - "..E.myrealm; + profileKey = "private"; + + profileData = E:CopyTable(profileData, ElvPrivateDB.profiles[privateProfileKey]); + profileData = E:RemoveTableDuplicates(profileData, V); + elseif(profileType == "global") then + profileKey = "global"; + + profileData = E:CopyTable(profileData, ElvDB.global); + profileData = E:RemoveTableDuplicates(profileData, G); + elseif(profileType == "filtersNP") then + profileKey = "filtersNP"; + + profileData["nameplates"] = {}; + profileData["nameplates"]["filter"] = {}; + profileData["nameplates"]["filter"] = E:CopyTable(profileData["nameplates"]["filter"], ElvDB.global.nameplates.filter); + profileData = E:RemoveTableDuplicates(profileData, G); + elseif(profileType == "filtersUF") then + profileKey = "filtersUF"; + + profileData["unitframe"] = {}; + profileData["unitframe"]["aurafilters"] = {}; + profileData["unitframe"]["aurafilters"] = E:CopyTable(profileData["unitframe"]["aurafilters"], ElvDB.global.unitframe.aurafilters); + profileData["unitframe"]["buffwatch"] = {}; + profileData["unitframe"]["buffwatch"] = E:CopyTable(profileData["unitframe"]["buffwatch"], ElvDB.global.unitframe.buffwatch); + profileData = E:RemoveTableDuplicates(profileData, G); + elseif(profileType == "filtersAll") then + profileKey = "filtersAll"; + + profileData["nameplates"] = {}; + profileData["nameplates"]["filter"] = {}; + profileData["nameplates"]["filter"] = E:CopyTable(profileData["nameplates"]["filter"], ElvDB.global.nameplates.filter); + profileData["unitframe"] = {}; + profileData["unitframe"]["aurafilters"] = {}; + profileData["unitframe"]["aurafilters"] = E:CopyTable(profileData["unitframe"]["aurafilters"], ElvDB.global.unitframe.aurafilters); + profileData["unitframe"]["buffwatch"] = {}; + profileData["unitframe"]["buffwatch"] = E:CopyTable(profileData["unitframe"]["buffwatch"], ElvDB.global.unitframe.buffwatch); + profileData = E:RemoveTableDuplicates(profileData, G); + end + + return profileKey, profileData; +end + +local function GetProfileExport(profileType, exportFormat) + local profileExport, exportString; + local profileKey, profileData = GetProfileData(profileType); + + if(not profileKey or not profileData or (profileData and type(profileData) ~= "table")) then + E:Print("Error getting data from 'GetProfileData'"); + return; + end + + if(exportFormat == "text") then + local serialData = D:Serialize(profileData); + + exportString = D:CreateProfileExport(serialData, profileType, profileKey); + + local compressedData = LibCompress:Compress(exportString); + local encodedData = LibBase64:Encode(compressedData); + profileExport = encodedData; + elseif(exportFormat == "luaTable") then + exportString = E:TableToLuaString(profileData); + profileExport = D:CreateProfileExport(exportString, profileType, profileKey); + elseif(exportFormat == "luaPlugin") then + profileExport = E:ProfileTableToPluginFormat(profileData, profileType); + end + + return profileKey, profileExport; +end + +function D:CreateProfileExport(dataString, profileType, profileKey) + local returnString; + + if(profileType == "profile") then + returnString = format("%s::%s::%s", dataString, profileType, profileKey); + else + returnString = format("%s::%s", dataString, profileType); + end + + return returnString; +end + +function D:GetImportStringType(dataString) + local stringType = ""; + + if(LibBase64:IsBase64(dataString)) then + stringType = "Base64"; + elseif(find(dataString, "{")) then + stringType = "Table"; + end + + return stringType; +end + +function D:Decode(dataString) + local profileInfo, profileType, profileKey, profileData, message; + local stringType = self:GetImportStringType(dataString); + + if(stringType == "Base64") then + local decodedData = LibBase64:Decode(dataString); + local decompressedData, message = LibCompress:Decompress(decodedData); + + if(not decompressedData) then + E:Print("Error decompressing data:", message); + return; + end + + local serializedData, success; + serializedData, profileInfo = E:StringSplitMultiDelim(decompressedData, "^^::"); + + if not profileInfo then + E:Print("Error importing profile. String is invalid or corrupted!") + return + end + + serializedData = format("%s%s", serializedData, "^^"); + profileType, profileKey = E:StringSplitMultiDelim(profileInfo, "::"); + success, profileData = D:Deserialize(serializedData); + + if(not success) then + E:Print("Error deserializing:", profileData); + return; + end + elseif(stringType == "Table") then + local profileDataAsString; + profileDataAsString, profileInfo = E:StringSplitMultiDelim(dataString, "}::"); + + if not profileInfo then + E:Print("Error extracting profile info. Invalid import string!") + return + end + + if(not profileDataAsString) then + E:Print("Error extracting profile data. Invalid import string!"); + return; + end + + profileDataAsString = format("%s%s", profileDataAsString, "}"); + profileType, profileKey = E:StringSplitMultiDelim(profileInfo, "::"); + + local profileToTable = loadstring(format("%s %s", "return", profileDataAsString)); + if(profileToTable) then + message, profileData = pcall(profileToTable); + end + + if(not profileData or type(profileData) ~= "table") then + E:Print("Error converting lua string to table:", message); + return; + end + end + + return profileType, profileKey, profileData; +end + +local function SetImportedProfile(profileType, profileKey, profileData, force) + D.profileType = nil; + D.profileKey = nil; + D.profileData = nil; + + if(profileType == "profile") then + if(not ElvDB.profiles[profileKey] or force) then + if(force and E.data.keys.profile == profileKey) then + local tempKey = profileKey.."_Temp"; + E.data.keys.profile = tempKey; + end + ElvDB.profiles[profileKey] = profileData; + E.data:SetProfile(profileKey); + else + D.profileType = profileType; + D.profileKey = profileKey; + D.profileData = profileData; + E:StaticPopup_Show("IMPORT_PROFILE_EXISTS"); + + return; + end + elseif(profileType == "private") then + local profileKey = ElvPrivateDB.profileKeys[E.myname.." - "..E.myrealm]; + ElvPrivateDB.profiles[profileKey] = profileData; + E:StaticPopup_Show("IMPORT_RL"); + + elseif(profileType == "global") then + E:CopyTable(ElvDB.global, profileData); + E:StaticPopup_Show("IMPORT_RL"); + elseif(profileType == "filtersNP") then + E:CopyTable(ElvDB.global.nameplates, profileData.nameplates); + elseif(profileType == "filtersUF") then + E:CopyTable(ElvDB.global.unitframe, profileData.unitframe); + elseif(profileType == "filtersAll") then + E:CopyTable(ElvDB.global.nameplates, profileData.nameplates); + E:CopyTable(ElvDB.global.unitframe, profileData.unitframe); + end + + E:UpdateAll(true); +end + +function D:ExportProfile(profileType, exportFormat) + if(not profileType or not exportFormat) then + E:Print("Bad argument to 'ExportProfile' (string expected)"); + return; + end + + local profileKey, profileExport = GetProfileExport(profileType, exportFormat); + return profileKey, profileExport; +end + +function D:ImportProfile(dataString) + local profileType, profileKey, profileData = self:Decode(dataString); + + if(not profileData or type(profileData) ~= "table") then + E:Print("Error: something went wrong when converting string to table!"); + return; + end + + if(profileType and ((profileType == "profile" and profileKey) or profileType ~= "profile")) then + SetImportedProfile(profileType, profileKey, profileData); + end + + return true; +end + +E.PopupDialogs["DISTRIBUTOR_SUCCESS"] = { + text = L["Your profile was successfully recieved by the player."], + whileDead = 1, + hideOnEscape = 1, + button1 = OKAY, +} + +E.PopupDialogs["DISTRIBUTOR_WAITING"] = { + text = L["Profile request sent. Waiting for response from player."], + whileDead = 1, + hideOnEscape = 1, + timeout = 35, +} + +E.PopupDialogs["DISTRIBUTOR_REQUEST_DENIED"] = { + text = L["Request was denied by user."], + whileDead = 1, + hideOnEscape = 1, + button1 = OKAY, +} + +E.PopupDialogs["DISTRIBUTOR_FAILED"] = { + text = L["Lord! It's a miracle! The download up and vanished like a fart in the wind! Try Again!"], + whileDead = 1, + hideOnEscape = 1, + button1 = OKAY, +} + +E.PopupDialogs["DISTRIBUTOR_RESPONSE"] = {} +E.PopupDialogs["DISTRIBUTOR_CONFIRM"] = {} + +E.PopupDialogs["IMPORT_PROFILE_EXISTS"] = { + text = L["The profile you tried to import already exists. Choose a new name or accept to overwrite the existing profile."], + button1 = ACCEPT, + button2 = CANCEL, + hasEditBox = 1, + editBoxWidth = 350, + maxLetters = 127, + OnAccept = function(self) + local profileType = D.profileType; + local profileKey = self.editBox:GetText(); + local profileData = D.profileData; + SetImportedProfile(profileType, profileKey, profileData, true); + end, + EditBoxOnTextChanged = function(self) + if(self:GetText() == "") then + self:GetParent().button1:Disable(); + else + self:GetParent().button1:Enable(); + end + end, + OnShow = function(self) self.editBox:SetText(D.profileKey); self.editBox:SetFocus(); end, + timeout = 0, + whileDead = 1, + hideOnEscape = true, + preferredIndex = 3 +}; + +E.PopupDialogs["IMPORT_RL"] = { + text = L["You have imported settings which may require a UI reload to take effect. Reload now?"], + button1 = ACCEPT, + button2 = CANCEL, + OnAccept = ReloadUI, + timeout = 0, + whileDead = 1, + hideOnEscape = false, + preferredIndex = 3 +}; + +local function InitializeCallback() + D:Initialize() +end + +E:RegisterModule(D:GetName(), InitializeCallback) \ No newline at end of file diff --git a/2/3/4/5/6/7/ElvUI/Libraries/LibBase64-1.0/LibBase64-1.0.lua b/2/3/4/5/6/7/ElvUI/Libraries/LibBase64-1.0/LibBase64-1.0.lua index dcf5697..c5c08ed 100644 --- a/2/3/4/5/6/7/ElvUI/Libraries/LibBase64-1.0/LibBase64-1.0.lua +++ b/2/3/4/5/6/7/ElvUI/Libraries/LibBase64-1.0/LibBase64-1.0.lua @@ -6,34 +6,50 @@ Description: A library to encode and decode Base64 strings License: MIT ]] -local LibBase64 = LibStub:NewLibrary("LibBase64-1.0-ElvUI", 1) +local modf = math.modf +local sub = string.sub +local format = string.format +local getn = table.getn +local gsub = string.gsub +local strlen = string.len +local byte = string.byte + +local LibBase64 = LibStub:NewLibrary("LibBase64-1.0", 1) if not LibBase64 then return end -local _chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' +local chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" +local charTable = {} local byteToNum = {} local numToChar = {} -for i = 1, #_chars do - numToChar[i - 1] = _chars:sub(i, i) - byteToNum[_chars:byte(i)] = i - 1 + +for i = 1, strlen(chars) do + charTable[i] = sub(chars, i, i) end -_chars = nil -local A_byte = ("A"):byte() -local Z_byte = ("Z"):byte() -local a_byte = ("a"):byte() -local z_byte = ("z"):byte() -local zero_byte = ("0"):byte() -local nine_byte = ("9"):byte() -local plus_byte = ("+"):byte() -local slash_byte = ("/"):byte() -local equals_byte = ("="):byte() + +for i = 1, getn(charTable) do + numToChar[i - 1] = sub(chars, i, i) + byteToNum[byte(chars, i)] = i - 1 +end + +charTable = nil + +local A_byte = byte("A") +local Z_byte = byte("Z") +local a_byte = byte("a") +local z_byte = byte("z") +local zero_byte = byte("0") +local nine_byte = byte("9") +local plus_byte = byte("+") +local slash_byte = byte("/") +local equals_byte = byte("=") local whitespace = { - [(" "):byte()] = true, - [("\t"):byte()] = true, - [("\n"):byte()] = true, - [("\r"):byte()] = true, + [byte(" ")] = true, + [byte("\t")] = true, + [byte("\n")] = true, + [byte("\r")] = true, } local t = {} @@ -46,29 +62,29 @@ local t = {} -- @return a Base64-encoded string function LibBase64:Encode(text, maxLineLength, lineEnding) if type(text) ~= "string" then - error(("Bad argument #1 to `Encode'. Expected %q, got %q"):format("string", type(text)), 2) + error(format("Bad argument #1 to `Encode'. Expected %q, got %q", "string", type(text)), 2) end if maxLineLength == nil then -- do nothing elseif type(maxLineLength) ~= "number" then - error(("Bad argument #2 to `Encode'. Expected %q or %q, got %q"):format("number", "nil", type(maxLineLength)), 2) - elseif (maxLineLength % 4) ~= 0 then - error(("Bad argument #2 to `Encode'. Expected a multiple of 4, got %s"):format(maxLineLength), 2) + error(format("Bad argument #2 to `Encode'. Expected %q or %q, got %q", "number", "nil", type(maxLineLength)), 2) + elseif modf(maxLineLength, 4) ~= 0 then + error(format("Bad argument #2 to `Encode'. Expected a multiple of 4, got %s", maxLineLength), 2) elseif maxLineLength <= 0 then - error(("Bad argument #2 to `Encode'. Expected a number > 0, got %s"):format(maxLineLength), 2) + error(format("Bad argument #2 to `Encode'. Expected a number > 0, got %s", maxLineLength), 2) end if lineEnding == nil then lineEnding = "\r\n" elseif type(lineEnding) ~= "string" then - error(("Bad argument #3 to `Encode'. Expected %q, got %q"):format("string", type(lineEnding)), 2) + error(format("Bad argument #3 to `Encode'. Expected %q, got %q", "string", type(lineEnding)), 2) end local currentLength = 0 - for i = 1, #text, 3 do - local a, b, c = text:byte(i, i+2) + for i = 1, getn(text), 3 do + local a, b, c = byte(text, i, i+2) local nilNum = 0 if not b then nilNum = 2 @@ -80,33 +96,33 @@ function LibBase64:Encode(text, maxLineLength, lineEnding) end local num = a * 2^16 + b * 2^8 + c - local d = num % 2^6 + local d = modf(num, 2^6) num = (num - d) / 2^6 - local c = num % 2^6 + local c = modf(num, 2^6) num = (num - c) / 2^6 - local b = num % 2^6 + local b = modf(num, 2^6) num = (num - b) / 2^6 - local a = num % 2^6 + local a = modf(num, 2^6) - t[#t+1] = numToChar[a] + t[getn(t+1)] = numToChar[a] - t[#t+1] = numToChar[b] + t[getn(t+1)] = numToChar[b] - t[#t+1] = (nilNum >= 2) and "=" or numToChar[c] + t[getn(t+1)] = (nilNum >= 2) and "=" or numToChar[c] - t[#t+1] = (nilNum >= 1) and "=" or numToChar[d] + t[getn(t+1)] = (nilNum >= 1) and "=" or numToChar[d] currentLength = currentLength + 4 - if maxLineLength and (currentLength % maxLineLength) == 0 then - t[#t+1] = lineEnding + if maxLineLength and modf(currentLength, maxLineLength) == 0 then + t[getn(t+1)] = lineEnding end end local s = table.concat(t) - for i = 1, #t do + for i = 1, getn(t) do t[i] = nil end return s @@ -122,27 +138,27 @@ local t2 = {} -- @return a bytestring function LibBase64:Decode(text) if type(text) ~= "string" then - error(("Bad argument #1 to `Decode'. Expected %q, got %q"):format("string", type(text)), 2) + error(format("Bad argument #1 to `Decode'. Expected %q, got %q", "string", type(text)), 2) end - for i = 1, #text do - local byte = text:byte(i) + for i = 1, getn(text) do + local byte = byte(text, i) if whitespace[byte] or byte == equals_byte then -- do nothing else local num = byteToNum[byte] if not num then - for i = 1, #t2 do + for i = 1, getn(t2) do t2[k] = nil end - error(("Bad argument #1 to `Decode'. Received an invalid char: %q"):format(text:sub(i, i)), 2) + error(format("Bad argument #1 to `Decode'. Received an invalid char: %q", sub(text, i, i)), 2) end - t2[#t2+1] = num + t2[getn(t2+1)] = num end end - for i = 1, #t2, 4 do + for i = 1, getn(t2), 4 do local a, b, c, d = t2[i], t2[i+1], t2[i+2], t2[i+3] local nilNum = 0 @@ -157,30 +173,30 @@ function LibBase64:Decode(text) local num = a * 2^18 + b * 2^12 + c * 2^6 + d - local c = num % 2^8 + local c = modf(num, 2^8) num = (num - c) / 2^8 - local b = num % 2^8 + local b = modf(num, 2^8) num = (num - b) / 2^8 - local a = num % 2^8 + local a = modf(num, 2^8) - t[#t+1] = string.char(a) + t[getn(t+1)] = string.char(a) if nilNum < 2 then - t[#t+1] = string.char(b) + t[getn(t+1)] = string.char(b) end if nilNum < 1 then - t[#t+1] = string.char(c) + t[getn(t+1)] = string.char(c) end end - for i = 1, #t2 do + for i = 1, getn(t2) do t2[i] = nil end local s = table.concat(t) - for i = 1, #t do + for i = 1, getn(t) do t[i] = nil end @@ -189,15 +205,15 @@ end function LibBase64:IsBase64(text) if type(text) ~= "string" then - error(("Bad argument #1 to `IsBase64'. Expected %q, got %q"):format("string", type(text)), 2) + error(format("Bad argument #1 to `IsBase64'. Expected %q, got %q", "string", type(text)), 2) end - if #text % 4 ~= 0 then + if modf(strlen(text), 4 ~= 0) then return false end - for i = 1, #text do - local byte = text:byte(i) + for i = 1, getn(text) do + local byte = byte(text, i) if whitespace[byte] or byte == equals_byte then -- do nothing else diff --git a/2/3/4/5/6/7/ElvUI/Libraries/LibCompress/LibCompress.lua b/2/3/4/5/6/7/ElvUI/Libraries/LibCompress/LibCompress.lua index 79a46a6..5f14835 100644 --- a/2/3/4/5/6/7/ElvUI/Libraries/LibCompress/LibCompress.lua +++ b/2/3/4/5/6/7/ElvUI/Libraries/LibCompress/LibCompress.lua @@ -5,12 +5,12 @@ -- Authors: jjsheets and Galmok of European Stormrage (Horde) -- Email : sheets.jeff@gmail.com and galmok@gmail.com -- Licence: GPL version 2 (General Public License) --- Revision: $Revision: 77 $ --- Date: $Date: 2017-07-13 14:54:12 -0500 (Thu, 13 Jul 2017) $ ---------------------------------------------------------------------------------- -local LibCompress = LibStub:NewLibrary("LibCompress", 90000 + tonumber(("$Revision: 77 $"):match("%d+"))) +local MAJOR, MINOR = "LibCompress", 3 + +local LibCompress,oldminor = LibStub:NewLibrary(MAJOR, MINOR) if not LibCompress then return end @@ -22,15 +22,6 @@ if not LibCompress then return end -- local is faster than global -local CreateFrame = CreateFrame -local type = type -local tostring = tostring -local select = select -local next = next -local loadstring = loadstring -local setmetatable = setmetatable -local rawset = rawset -local assert = assert local table_insert = table.insert local table_remove = table.remove local table_concat = table.concat @@ -43,30 +34,35 @@ local pairs = pairs local math_modf = math.modf local bit_band = bit.band local bit_bor = bit.bor -local bit_bxor = bit.bxor -local bit_bnot = bit.bnot local bit_lshift = bit.lshift local bit_rshift = bit.rshift +local tostring = tostring +local next = next +local strlen = strlen +local strsplit = string.split +local strfind = string.find +local strgfind = string.gfind +local tonumber = tonumber -------------------------------------------------------------------------------- -- Cleanup local tables = {} -- tables that may be cleaned have to be kept here local tables_to_clean = {} -- list of tables by name (string) that may be reset to {} after a timeout +local timeout = -1 -- tables that may be erased local function cleanup() for k,v in pairs(tables_to_clean) do - tables[k] = {} - tables_to_clean[k] = nil + tables[k]={} + tables_to_clean[k]=nil end end -local timeout = -1 local function onUpdate(frame, elapsed) - frame:Hide() - timeout = timeout - elapsed - if timeout <= 0 then + timeout = timeout - arg1 + if timeout < 0 then + this:Hide() cleanup() end end @@ -75,20 +71,20 @@ LibCompress.frame = LibCompress.frame or CreateFrame("frame", nil, UIParent) -- LibCompress.frame:SetScript("OnUpdate", onUpdate) LibCompress.frame:Hide() -local function setCleanupTables(...) +local function setCleanupTables(s1, s2) timeout = 15 -- empty tables after 15 seconds if not LibCompress.frame:IsShown() then LibCompress.frame:Show() end - for i = 1, select("#",...) do - tables_to_clean[(select(i, ...))] = true + + if tables_to_clean[s1] then + tables_to_clean[s1] = true + end + if tables_to_clean[s2] then + tables_to_clean[s2] = true end end ----------------------------------------------------------------------- ----------------------------------------------------------------------- --- --- compression algorithms -------------------------------------------------------------------------------- -- LZW codec @@ -98,37 +94,35 @@ end -- the bytes returned by this do not contain "\000" local bytes = {} local function encode(x) - for k = 1, #bytes do - bytes[k] = nil - end - - bytes[#bytes + 1] = x % 255 - x=math.floor(x/255) - + local math_modf = math.modf + for k = 1, getn(bytes) do bytes[k] = nil end + local xmod + x, xmod = math_modf(x/255.0) + xmod = xmod * 255 + bytes[getn(bytes) + 1] = xmod while x > 0 do - bytes[#bytes + 1] = x % 255 - x=math.floor(x/255) + x, xmod = math_modf(x/255.0) + xmod = xmod * 255.0 + bytes[getn(bytes) + 1] = xmod end - if #bytes == 1 and bytes[1] > 0 and bytes[1] < 250 then + if getn(bytes) == 1 and bytes[1] > 0 and bytes[1] < 250 then return string_char(bytes[1]) else - for i = 1, #bytes do - bytes[i] = bytes[i] + 1 - end - return string_char(256 - #bytes, unpack(bytes)) + for i = 1, getn(bytes) do bytes[i] = bytes[i] + 1 end + return string_char(256 - getn(bytes), unpack(bytes)) end end --decode converts a unique character sequence into its equivalent number, from ss, beginning at the ith char. -- returns the decoded number and the count of characters used in the decode process. -local function decode(ss, i) +local function decode(ss,i) i = i or 1 - local a = string_byte(ss, i, i) + local a = string_byte(ss,i,i) if a > 249 then local r = 0 a = 256 - a - for n = i + a, i + 1, -1 do - r = r * 255 + string_byte(ss, n, n) - 1 + for n = i+a, i+1, -1 do + r = r * 255 + string_byte(ss,n,n) - 1 end return r, a + 1 else @@ -136,6 +130,116 @@ local function decode(ss, i) end end +-- +-- Added by Shino +-- +function LibCompress:TableToString(t) + if t == nil then return nil end + if type(t)=="string" then return t end + local cache={} + local function sub_mod(t) + if (type(t)=="table") then + for pos,val in pairs(t) do + if (type(val)=="table") then + table_insert(cache, pos..">".."{") + sub_mod(val) + table_insert(cache, "}") + elseif (type(val)=="string") then + table_insert(cache, pos..'>"'..val..'"') + else + table_insert(cache, pos..">"..tostring(val)) + end + end + else + table_insert(cache, tostring(t)) + end + end + sub_mod(t) + return table_concat(cache, ",") -- ? maybe use a different delimiter +end + +-- Adding a costum string.split function cause this one has a problem with too big tables +-- Credits: https://gist.github.com/jaredallard/ddb152179831dd23b230 +function LibCompress:StringSplit(delimiter, str) + local result = { } + local from = 1 + local delim_from, delim_to = strfind( str, delimiter, from ) + while delim_from do + table_insert( result, strsub( str, from , delim_from-1 ) ) + from = delim_to + 1 + delim_from, delim_to = strfind( str, delimiter, from ) + end + table.insert( result, strsub( str, from ) ) + return result +end + +function LibCompress:StringToTable(t) + if t=="" or t==nil then return nil end + if type(t)=="table" then return t end + local table = self:StringSplit(",",t) + local function fillSubTable(start) + local subT = {} + local cont = true + local po = nil + for k, v in pairs(table) do + if k>start then + cont = true + po = nil + for pos in strgfind(v, "(.+)>{") do -- opening sub table + po = tonumber(pos) + if po then + subT[po], start = fillSubTable(k) + else + subT[pos], start = fillSubTable(k) + end + end + if k>start then -- cause it could have changed if a new table was opened + for pos, val in strgfind(v, '(.+)%>"(.+)"') do + po = tonumber(pos) + if po then + subT[po] = ""..val + else + subT[pos] = ""..val + end + cont = false + end + if cont then + for pos, val in strgfind(v, "(.+)%>(.+)") do -- Do floats work too? + po = tonumber(pos) + if val == "false" then + if po then + subT[po] = false + else + subT[pos] = false + end + elseif val == "true" then + if po then + subT[po] = true + else + subT[pos] = true + end + else + if po then + subT[po] = tonumber(val) + else + subT[pos] = tonumber(val) + end + end + end + if v=="}" then -- closing sub table + return subT,k -- Returning the end + end + end + end + end + end + return subT, 0 + end + return fillSubTable(0) +end + +-- Shino end -- + -- Compresses the given uncompressed string. -- Unless the uncompressed string starts with "\002", this is guaranteed to return a string equal to or smaller than -- the passed string. @@ -145,43 +249,38 @@ local dict = {} function LibCompress:CompressLZW(uncompressed) if type(uncompressed) == "string" then local dict_size = 256 - for k in pairs(dict) do + for k, _ in pairs(dict) do dict[k] = nil end - local result = {"\002"} local w = '' local ressize = 1 - for i = 0, 255 do dict[string_char(i)] = i end - - for i = 1, #uncompressed do - local c = uncompressed:sub(i, i) + for i = 1, strlen(uncompressed) do + local c = string_sub(uncompressed, i,i) local wc = w..c if dict[wc] then w = wc else dict[wc] = dict_size - dict_size = dict_size + 1 + dict_size = dict_size +1 local r = encode(dict[w]) - ressize = ressize + #r - result[#result + 1] = r + ressize = ressize + strlen(r) + result[getn(result) + 1] = r w = c end end - if w then local r = encode(dict[w]) - ressize = ressize + #r - result[#result + 1] = r + ressize = ressize + strlen(r) + result[getn(result) + 1] = r end - - if (#uncompressed + 1) > ressize then - return table_concat(result) + if (strlen(uncompressed)+1) > ressize then + return table_concat(result, "") else - return string_char(1)..uncompressed + return "\002"..uncompressed end else return nil, "Can only compress strings" @@ -193,36 +292,31 @@ end -- compressed strings are marked by beginning with "\002" function LibCompress:DecompressLZW(compressed) if type(compressed) == "string" then - if compressed:sub(1, 1) ~= "\002" then - return nil, "Can only decompress LZW compressed data ("..tostring(compressed:sub(1, 1))..")" + if string_sub(compressed,1,1) ~= "\002" then + return nil, "Can only decompress LZW compressed data ("..tostring(string_sub(compressed,1,1))..")" end - - compressed = compressed:sub(2) + compressed = string_sub(compressed,2)-- ? local dict_size = 256 - - for k in pairs(dict) do + for k, _ in pairs(dict) do dict[k] = nil end - for i = 0, 255 do dict[i] = string_char(i) end - local result = {} local t = 1 local delta, k - k, delta = decode(compressed, t) + k, delta = decode(compressed,t) t = t + delta - result[#result + 1] = dict[k] - + result[getn(result)+1] = dict[k] local w = dict[k] local entry - while t <= #compressed do - k, delta = decode(compressed, t) + while t <= strlen(compressed) do + k, delta = decode(compressed,t) t = t + delta - entry = dict[k] or (w..w:sub(1, 1)) - result[#result + 1] = entry - dict[dict_size] = w..entry:sub(1, 1) + entry = dict[k] or (w..string_sub(w,1,1)) + result[getn(result)+1] = entry + dict[dict_size] = w..string_sub(entry,1,1) dict_size = dict_size + 1 w = entry end @@ -237,71 +331,44 @@ end -- Huffman codec -- implemented by Galmok of European Stormrage (Horde), galmok@gmail.com -local function addCode(tree, bcode, length) +local function addCode(tree, bcode,len) if tree then - tree.bcode = bcode - tree.blength = length + tree.bcode = bcode; + tree.blength = len; if tree.c1 then - addCode(tree.c1, bit_bor(bcode, bit_lshift(1, length)), length + 1) + addCode(tree.c1, bit_bor(bcode, bit_lshift(1,len)), len+1) end if tree.c2 then - addCode(tree.c2, bcode, length + 1) + addCode(tree.c2, bcode, len+1) end end end -local function escape_code(code, length) - local escaped_code = 0 - local b - local l = 0 - for i = length -1, 0, - 1 do - b = bit_band(code, bit_lshift(1, i)) == 0 and 0 or 1 - escaped_code = bit_lshift(escaped_code, 1 + b) + b - l = l + b +local function escape_code(code, len) + local escaped_code = 0; + local b; + local l = 0; + for i = len-1, 0,- 1 do + b = bit_band( code, bit_lshift(1,i))==0 and 0 or 1 + escaped_code = bit_lshift(escaped_code,1+b) + b + l = l + b; end - if length + l > 32 then - return nil, "escape overflow ("..(length + l)..")" - end - return escaped_code, length + l + return escaped_code, len+l end tables.Huffman_compressed = {} tables.Huffman_large_compressed = {} local compressed_size = 0 -local remainder -local remainder_length -local function addBits(tbl, code, length) - if remainder_length+length >= 32 then - -- we have at least 4 bytes to store; bulk it - remainder = remainder + bit_lshift(code, remainder_length) -- this overflows! Top part of code is lost (but we handle it below) - -- remainder now holds 4 full bytes to store. So lets do it. - compressed_size = compressed_size + 1 - tbl[compressed_size] = string_char(bit_band(remainder, 255)) .. - string_char(bit_band(bit_rshift(remainder, 8), 255)) .. - string_char(bit_band(bit_rshift(remainder, 16), 255)) .. - string_char(bit_band(bit_rshift(remainder, 24), 255)) - remainder = 0 - code = bit_rshift(code, 32 - remainder_length) - length = remainder_length + length - 32 - remainder_length = 0 - end - if remainder_length+length >= 16 then - -- we have at least 2 bytes to store; bulk it - remainder = remainder + bit_lshift(code, remainder_length) - remainder_length = length + remainder_length - -- remainder now holds at least 2 full bytes to store. So lets do it. - compressed_size = compressed_size + 1 - tbl[compressed_size] = string_char(bit_band(remainder, 255)) .. string_char(bit_band(bit_rshift(remainder, 8), 255)) - remainder = bit_rshift(remainder, 16) - code = remainder - length = remainder_length - 16 - remainder = 0 - remainder_length = 0 - end +local remainder; +local remainder_length; +local function addBits(tbl, code, len) remainder = remainder + bit_lshift(code, remainder_length) - remainder_length = length + remainder_length - if remainder_length >= 8 then + remainder_length = len + remainder_length + if remainder_length > 32 then + return true -- Bits lost due to too long code-words. + end + while remainder_length>=8 do compressed_size = compressed_size + 1 tbl[compressed_size] = string_char(bit_band(remainder, 255)) remainder = bit_rshift(remainder, 8) @@ -311,19 +378,16 @@ end -- word size for this huffman algorithm is 8 bits (1 byte). This means the best compression is representing 1 byte with 1 bit, i.e. compress to 0.125 of original size. function LibCompress:CompressHuffman(uncompressed) - if type(uncompressed) ~= "string" then + if not type(uncompressed)=="string" then return nil, "Can only compress strings" end - if #uncompressed == 0 then - return "\001" - end -- make histogram local hist = {} local n = 0 - -- don't have to use all data to make the histogram + -- dont have to use all datat to make the histogram local uncompressed_size = string_len(uncompressed) - local c + local c; for i = 1, uncompressed_size do c = string_byte(uncompressed, i) hist[c] = (hist[c] or 0) + 1 @@ -331,33 +395,24 @@ function LibCompress:CompressHuffman(uncompressed) --Start with as many leaves as there are symbols. local leafs = {} - local leaf + local leaf; local symbols = {} for symbol, weight in pairs(hist) do - leaf = { symbol=string_char(symbol), weight=weight } - symbols[symbol] = leaf + leaf = { symbol=string_char(symbol), weight=weight }; + symbols[symbol] = leaf; table_insert(leafs, leaf) end - --Enqueue all leaf nodes into the first queue (by probability in increasing order so that the least likely item is in the head of the queue). - sort(leafs, function(a, b) - if a.weight < b.weight then - return true - elseif a.weight > b.weight then - return false - else - return nil - end - end) + sort(leafs, function(a,b) if a.weightb.weight then return false else return nil end end) - local nLeafs = #leafs + local nLeafs = getn(leafs) -- create tree local huff = {} --While there is more than one node in the queues: - local length, height, li, hi, leaf1, leaf2 - local newNode - while (#leafs + #huff > 1) do + local l,h, li, hi, leaf1, leaf2 + local newNode; + while (getn(leafs)+getn(huff) > 1) do -- Dequeue the two nodes with the lowest weight. -- Dequeue first if not next(huff) then @@ -367,17 +422,16 @@ function LibCompress:CompressHuffman(uncompressed) hi, leaf1 = next(huff) table_remove(huff, hi) else - li, length = next(leafs) - hi, height = next(huff) - if length.weight <= height.weight then - leaf1 = length + li, l = next(leafs); + hi, h = next(huff); + if l.weight<=h.weight then + leaf1 = l; table_remove(leafs, li) else - leaf1 = height + leaf1 = h; table_remove(huff, hi) end end - -- Dequeue second if not next(huff) then li, leaf2 = next(leafs) @@ -386,39 +440,34 @@ function LibCompress:CompressHuffman(uncompressed) hi, leaf2 = next(huff) table_remove(huff, hi) else - li, length = next(leafs) - hi, height = next(huff) - if length.weight <= height.weight then - leaf2 = length + li, l = next(leafs); + hi, h = next(huff); + if l.weight<=h.weight then + leaf2 = l; table_remove(leafs, li) else - leaf2 = height + leaf2 = h; table_remove(huff, hi) end end --Create a new internal node, with the two just-removed nodes as children (either node can be either child) and the sum of their weights as the new weight. - newNode = { - c1 = leaf1, - c2 = leaf2, - weight = leaf1.weight + leaf2.weight - } + newNode = { c1 = leaf1, c2 = leaf2, weight = leaf1.weight+leaf2.weight } table_insert(huff,newNode) end - - if #leafs > 0 then - li, length = next(leafs) - table_insert(huff, length) + if getn(leafs)>0 then + li, l = next(leafs) + table_insert(huff, l) table_remove(leafs, li) end - huff = huff[1] + huff = huff[1]; -- assign codes to each symbol -- c1 = "0", c2 = "1" -- As a common convention, bit '0' represents following the left child and bit '1' represents following the right child. -- c1 = left, c2 = right - addCode(huff, 0, 0) + addCode(huff,0,0); if huff then huff.bcode = 0 huff.blength = 1 @@ -442,62 +491,58 @@ function LibCompress:CompressHuffman(uncompressed) -- bitfield_len = bitfield_len + 8 -- WRITING - remainder = 0 - remainder_length = 0 + remainder = 0; + remainder_length = 0; local compressed = tables.Huffman_compressed --compressed_size = 0 - -- first byte is version info. 0 = uncompressed, 1 = 8 - bit word huffman compressed + -- first byte is version info. 0 = uncompressed, 1 = 8-bit word huffman compressed compressed[1] = "\003" - -- Header: byte 0 = #leafs, bytes 1-3 = size of uncompressed data + -- Header: byte 0=#leafs, byte 1-3=size of uncompressed data -- max 2^24 bytes - local length = string_len(uncompressed) - compressed[2] = string_char(bit_band(nLeafs -1, 255)) -- number of leafs - compressed[3] = string_char(bit_band(length, 255)) -- bit 0-7 - compressed[4] = string_char(bit_band(bit_rshift(length, 8), 255)) -- bit 8-15 - compressed[5] = string_char(bit_band(bit_rshift(length, 16), 255)) -- bit 16-23 + local l = string_len(uncompressed) + compressed[2] = string_char(bit_band(nLeafs-1, 255)) -- number of leafs + compressed[3] = string_char(bit_band(l, 255)) -- bit 0-7 + compressed[4] = string_char(bit_band(bit_rshift(l, 8), 255)) -- bit 8-15 + compressed[5] = string_char(bit_band(bit_rshift(l, 16), 255)) -- bit 16-23 compressed_size = 5 -- create symbol/code map - local escaped_code, escaped_code_len, success, msg for symbol, leaf in pairs(symbols) do - addBits(compressed, symbol, 8) - escaped_code, escaped_code_len = escape_code(leaf.bcode, leaf.blength) - if not escaped_code then - return nil, escaped_code_len + addBits(compressed, symbol, 8); + if addBits(compressed, escape_code(leaf.bcode, leaf.blength)) then + -- code word too long. Needs new revision to be able to handle more than 32 bits + return string_char(0)..uncompressed end - addBits(compressed, escaped_code, escaped_code_len) - addBits(compressed, 3, 2) + addBits(compressed, 3, 2); end -- create huffman code local large_compressed = tables.Huffman_large_compressed local large_compressed_size = 0 local ulimit - for i = 1, length, 200 do - ulimit = length < (i + 199) and length or (i + 199) - + for i = 1, l, 200 do + ulimit = l<(i+199) and l or (i+199) for sub_i = i, ulimit do c = string_byte(uncompressed, sub_i) addBits(compressed, symbols[c].bcode, symbols[c].blength) end - large_compressed_size = large_compressed_size + 1 large_compressed[large_compressed_size] = table_concat(compressed, "", 1, compressed_size) compressed_size = 0 end - -- add remaining bits (if any) - if remainder_length > 0 then + -- add remainding bits (if any) + if remainder_length>0 then large_compressed_size = large_compressed_size + 1 large_compressed[large_compressed_size] = string_char(remainder) end local compressed_string = table_concat(large_compressed, "", 1, large_compressed_size) -- is compression worth it? If not, return uncompressed data. - if (#uncompressed + 1) <= #compressed_string then + if (strlen(uncompressed)+1) <= strlen(compressed_string) then return "\001"..uncompressed end @@ -519,87 +564,37 @@ setmetatable(lshiftMask, { local lshiftMinusOneMask = {} setmetatable(lshiftMinusOneMask, { __index = function (t, k) - local v = bit_lshift(1, k) - 1 + local v = bit_lshift(1, k)-1 rawset(t, k, v) return v end }) -local function bor64(valueA_high, valueA, valueB_high, valueB) - return bit_bor(valueA_high, valueB_high), - bit_bor(valueA, valueB) -end - -local function band64(valueA_high, valueA, valueB_high, valueB) - return bit_band(valueA_high, valueB_high), - bit_band(valueA, valueB) -end - -local function lshift64(value_high, value, lshift_amount) - if lshift_amount == 0 then - return value_high, value - end - if lshift_amount >= 64 then - return 0, 0 - end - if lshift_amount < 32 then - return bit_bor(bit_lshift(value_high, lshift_amount), bit_rshift(value, 32-lshift_amount)), - bit_lshift(value, lshift_amount) - end - -- 32-63 bit shift - return bit_lshift(value, lshift_amount), -- builtin modulus 32 on shift amount - 0 -end - -local function rshift64(value_high, value, rshift_amount) - if rshift_amount == 0 then - return value_high, value - end - if rshift_amount >= 64 then - return 0, 0 - end - if rshift_amount < 32 then - return bit_rshift(value_high, rshift_amount), - bit_bor(bit_lshift(value_high, 32-rshift_amount), bit_rshift(value, rshift_amount)) - end - -- 32-63 bit shift - return 0, - bit_rshift(value_high, rshift_amount) -end - -local function getCode2(bitfield_high, bitfield, field_len) - if field_len >= 2 then - -- [bitfield_high..bitfield]: bit 0 is right most in bitfield. bit is left most in bitfield_high - local b1, b2, remainder_high, remainder - for i = 0, field_len - 2 do - b1 = i <= 31 and bit_band(bitfield, bit_lshift(1, i)) or bit_band(bitfield_high, bit_lshift(1, i)) -- for shifts, 32 = 0 (5 bit used) - b2 = (i+1) <= 31 and bit_band(bitfield, bit_lshift(1, i+1)) or bit_band(bitfield_high, bit_lshift(1, i+1)) - if not (b1 == 0) and not (b2 == 0) then - -- found 2 bits set right after each other (stop bits) with i pointing at the first stop bit - -- return the two bitfields separated by the two stopbits (3 values for each: bitfield_high, bitfield, field_len) - -- bits left: field_len - (i+2) - remainder_high, remainder = rshift64(bitfield_high, bitfield, i+2) - -- first bitfield is the lower part - return (i-1) >= 32 and bit_band(bitfield_high, bit_lshift(1, i) - 1) or 0, - i >= 32 and bitfield or bit_band(bitfield, bit_lshift(1, i) - 1), - i, - remainder_high, - remainder, - field_len-(i+2) +local function getCode(bitfield, field_len) + if field_len>=2 then + local b; + local p = 0; + for i = 0, field_len-1 do + b = bit_band(bitfield, lshiftMask[i]) + if not (p==0) and not (b == 0) then + -- found 2 bits set right after each other (stop bits) + return bit_band( bitfield, lshiftMinusOneMask[i-1]), i-1, + bit_rshift(bitfield, i+1), field_len-i-1 end + p = b end end return nil end local function unescape_code(code, code_len) - local unescaped_code = 0 - local b - local l = 0 + local unescaped_code=0; + local b; + local l = 0; local i = 0 while i < code_len do b = bit_band( code, lshiftMask[i]) - if not (b == 0) then + if not (b==0) then unescaped_code = bit_bor(unescaped_code, lshiftMask[l]) i = i + 1 end @@ -610,21 +605,24 @@ local function unescape_code(code, code_len) end tables.Huffman_uncompressed = {} -tables.Huffman_large_uncompressed = {} -- will always be as big as the largest string ever decompressed. Bad, but clearing it every time takes precious time. +tables.Huffman_large_uncompressed = {} -- will always be as big as the larges string ever decompressed. Bad, but clearing i every timetakes precious time. function LibCompress:DecompressHuffman(compressed) - if not type(compressed) == "string" then - return nil, "Can only uncompress strings" + if type(compressed)~="string" or compressed == nil then + if type(compressed)=="table" then + return compressed + end + return nil end - local compressed_size = #compressed + local compressed_size = strlen(compressed) --decode header local info_byte = string_byte(compressed) -- is data compressed - if info_byte == 1 then - return compressed:sub(2) --return uncompressed data + if info_byte==1 then + return string_sub(compressed,2) --return uncompressed data end - if not (info_byte == 3) then + if not (info_byte==3) then return nil, "Can only decompress Huffman compressed data ("..tostring(info_byte)..")" end @@ -632,15 +630,14 @@ function LibCompress:DecompressHuffman(compressed) local c0 = string_byte(string_sub(compressed, 3, 3)) local c1 = string_byte(string_sub(compressed, 4, 4)) local c2 = string_byte(string_sub(compressed, 5, 5)) - local orig_size = c2 * 65536 + c1 * 256 + c0 - if orig_size == 0 then - return "" + local orig_size = c2*65536 + c1*256 + c0 + if orig_size==0 then + return ""; end - -- decode code -> symbol map - local bitfield = 0 - local bitfield_high = 0 - local bitfield_len = 0 + -- decode code->symbal map + local bitfield = 0; + local bitfield_len = 0; local map = {} -- only table not reused in Huffman decode. setmetatable(map, { __index = function (t, k) @@ -650,48 +647,44 @@ function LibCompress:DecompressHuffman(compressed) end }) - local i = 6 -- byte 1-5 are header bytes - local c, cl - local minCodeLen = 1000 - local maxCodeLen = 0 - local symbol, code_high, code, code_len, temp_high, temp, _bitfield_high, _bitfield, _bitfield_len - local n = 0 - local state = 0 -- 0 = get symbol (8 bits), 1 = get code (varying bits, ends with 2 bits set) - while n < num_symbols do - if i > compressed_size then + local i = 6; -- byte 1-5 are header bytes + local c, cl; + local minCodeLen = 1000; + local maxCodeLen = 0; + local symbol, code, code_len, _bitfield, _bitfield_len; + local n = 0; + local state = 0; -- 0 = get symbol (8 bits), 1 = get code (varying bits, ends with 2 bits set) + while ncompressed_size then return nil, "Cannot decode map" end c = string_byte(compressed, i) - temp_high, temp = lshift64(0, c, bitfield_len) - bitfield_high, bitfield = bor64(bitfield_high, bitfield, temp_high, temp) + bitfield = bit_bor(bitfield, bit_lshift(c, bitfield_len)) bitfield_len = bitfield_len + 8 if state == 0 then symbol = bit_band(bitfield, 255) - bitfield_high, bitfield = rshift64(bitfield_high, bitfield, 8) - bitfield_len = bitfield_len - 8 + bitfield = bit_rshift(bitfield, 8) + bitfield_len = bitfield_len -8 state = 1 -- search for code now else - code_high, code, code_len, _bitfield_high, _bitfield, _bitfield_len = getCode2(bitfield_high, bitfield, bitfield_len) - if code_high then - bitfield_high, bitfield, bitfield_len = _bitfield_high, _bitfield, _bitfield_len - if code_len > 32 then - return nil, "Unsupported symbol code length ("..code_len..")" - end + code, code_len, _bitfield, _bitfield_len = getCode(bitfield, bitfield_len) + if code then + bitfield, bitfield_len = _bitfield, _bitfield_len c, cl = unescape_code(code, code_len) - map[cl][c] = string_char(symbol) - minCodeLen = cl < minCodeLen and cl or minCodeLen - maxCodeLen = cl > maxCodeLen and cl or maxCodeLen + map[cl][c]=string_char(symbol) + minCodeLen = clmaxCodeLen and cl or maxCodeLen --print("symbol: "..string_char(symbol).." code: "..tobinary(c, cl)) n = n + 1 state = 0 -- search for next symbol (if any) end end - i = i + 1 + i=i+1 end - -- don't create new subtables for entries not in the map. Waste of space. + -- dont create new subtables for entries not in the map. Waste of space. -- But do return an empty table to prevent runtime errors. (instead of returning nil) local mt = {} setmetatable(map, { @@ -705,51 +698,49 @@ function LibCompress:DecompressHuffman(compressed) local uncompressed_size = 0 local large_uncompressed_size = 0 local test_code - local test_code_len = minCodeLen - local symbol - local dec_size = 0 + local test_code_len = minCodeLen; + local symbol; + local dec_size = 0; compressed_size = compressed_size + 1 - local temp_limit = 200 -- first limit of uncompressed data. large_uncompressed will hold strings of length 200 - temp_limit = temp_limit > orig_size and orig_size or temp_limit - + local temp_limit = 200; -- first limit of uncompressed data. large_uncompressed will hold strings of length 200 while true do - if test_code_len <= bitfield_len then - test_code = bit_band( bitfield, lshiftMinusOneMask[test_code_len]) + if test_code_len<=bitfield_len then + test_code=bit_band( bitfield, lshiftMinusOneMask[test_code_len]) symbol = map[test_code_len][test_code] - if symbol then uncompressed_size = uncompressed_size + 1 - uncompressed[uncompressed_size] = symbol + uncompressed[uncompressed_size]=symbol dec_size = dec_size + 1 - if dec_size >= temp_limit then - if dec_size >= orig_size then -- checked here for speed reasons - break - end - -- process compressed bytes in smaller chunks - large_uncompressed_size = large_uncompressed_size + 1 - large_uncompressed[large_uncompressed_size] = table_concat(uncompressed, "", 1, uncompressed_size) - uncompressed_size = 0 - temp_limit = temp_limit + 200 -- repeated chunk size is 200 uncompressed bytes - temp_limit = temp_limit > orig_size and orig_size or temp_limit + + if dec_size>=orig_size then -- checked here for speed reasons + break; end + -- process compressed bytes in smaller chunks + large_uncompressed_size = large_uncompressed_size + 1 + large_uncompressed[large_uncompressed_size] = table_concat(uncompressed, "", 1, uncompressed_size) + uncompressed_size = 0 + temp_limit = temp_limit + 200 -- repeated chunk size is 200 uncompressed bytes + temp_limit = temp_limit > orig_size and orig_size or temp_limit bitfield = bit_rshift(bitfield, test_code_len) bitfield_len = bitfield_len - test_code_len test_code_len = minCodeLen else test_code_len = test_code_len + 1 - if test_code_len > maxCodeLen then - return nil, "Decompression error at "..tostring(i).."/"..tostring(#compressed) + if test_code_len>maxCodeLen then + return nil, "Decompression error at "..tostring(i).."/"..tostring(strlen(compressed)) end end else c = string_byte(compressed, i) bitfield = bitfield + bit_lshift(c or 0, bitfield_len) bitfield_len = bitfield_len + 8 - if i > compressed_size then - break - end i = i + 1 + if i > temp_limit then + if i > compressed_size then + break; + end + end end end @@ -760,21 +751,14 @@ end -------------------------------------------------------------------------------- -- Generic codec interface -function LibCompress:Store(uncompressed) - if type(uncompressed) ~= "string" then - return nil, "Can only compress strings" - end - return "\001"..uncompressed -end - function LibCompress:DecompressUncompressed(data) - if type(data) ~= "string" then + if type(data)~="string" then return nil, "Can only handle strings" end - if string_byte(data) ~= 1 then + if string.byte(data) ~= 1 then return nil, "Can only handle uncompressed data" end - return data:sub(2) + return string_sub(data, 2) end local compression_methods = { @@ -792,12 +776,21 @@ local decompression_methods = { function LibCompress:Compress(data) local method = next(compression_methods) local result = compression_methods[method](self, data) + local nTable = {} + local rTable = {} local n method = next(compression_methods, method) while method do n = compression_methods[method](self, data) - if #n < #result then - result = n + nTable = {} + for i = 1, strlen(n) do + nTable[i] = string.sub(n, i, i) + end + for i = 1, strlen(result) do + rTable[i] = string.sub(result, i, i) + end + if getn(nTable) < getn(rTable) then + result = nTable end method = next(compression_methods, method) end @@ -805,455 +798,10 @@ function LibCompress:Compress(data) end function LibCompress:Decompress(data) - local header_info = string_byte(data) + local header_info = string.byte(data) if decompression_methods[header_info] then return decompression_methods[header_info](self, data) else return nil, "Unknown compression method ("..tostring(header_info)..")" end -end - ----------------------------------------------------------------------- ----------------------------------------------------------------------- --- --- Encoding algorithms - --------------------------------------------------------------------------------- --- Prefix encoding algorithm --- implemented by Galmok of European Stormrage (Horde), galmok@gmail.com - ---[[ - Howto: Encode and Decode: - - 3 functions are supplied, 2 of them are variants of the first. They return a table with functions to encode and decode text. - - table, msg = LibCompress:GetEncodeTable(reservedChars, escapeChars, mapChars) - - reservedChars: The characters in this string will not appear in the encoded data. - escapeChars: A string of characters used as escape-characters (don't supply more than needed). #escapeChars >= 1 - mapChars: First characters in reservedChars maps to first characters in mapChars. (#mapChars <= #reservedChars) - - return value: - table - if nil then msg holds an error message, otherwise use like this: - - encoded_message = table:Encode(message) - message = table:Decode(encoded_message) - - GetAddonEncodeTable: Sets up encoding for the addon channel (\000 is encoded) - GetChatEncodeTable: Sets up encoding for the chat channel (many bytes encoded, see the function for details) - - Except for the mapped characters, all encoding will be with 1 escape character followed by 1 suffix, i.e. 2 bytes. -]] --- to be able to match any requested byte value, the search string must be preprocessed --- characters to escape with %: --- ( ) . % + - * ? [ ] ^ $ --- "illegal" byte values: --- 0 is replaces %z -local gsub_escape_table = { - ['\000'] = "%z", - [('(')] = "%(", - [(')')] = "%)", - [('.')] = "%.", - [('%')] = "%%", - [('+')] = "%+", - [('-')] = "%-", - [('*')] = "%*", - [('?')] = "%?", - [('[')] = "%[", - [(']')] = "%]", - [('^')] = "%^", - [('$')] = "%$" -} - -local function escape_for_gsub(str) - return str:gsub("([%z%(%)%.%%%+%-%*%?%[%]%^%$])", gsub_escape_table) -end - -function LibCompress:GetEncodeTable(reservedChars, escapeChars, mapChars) - reservedChars = reservedChars or "" - escapeChars = escapeChars or "" - mapChars = mapChars or "" - - -- select a default escape character - if escapeChars == "" then - return nil, "No escape characters supplied" - end - - if #reservedChars < #mapChars then - return nil, "Number of reserved characters must be at least as many as the number of mapped chars" - end - - if reservedChars == "" then - return nil, "No characters to encode" - end - - -- list of characters that must be encoded - local encodeBytes = reservedChars..escapeChars..mapChars - - -- build list of bytes not available as a suffix to a prefix byte - local taken = {} - for i = 1, string_len(encodeBytes) do - taken[string_sub(encodeBytes, i, i)] = true - end - - -- allocate a table to hold encode/decode strings/functions - local codecTable = {} - - -- the encoding can be a single gsub, but the decoding can require multiple gsubs - local decode_func_string = {} - - local encode_search = {} - local encode_translate = {} - local encode_func - local decode_search = {} - local decode_translate = {} - local decode_func - local c, r, i, to, from - local escapeCharIndex, escapeChar = 0 - - -- map single byte to single byte - if #mapChars > 0 then - for i = 1, #mapChars do - from = string_sub(reservedChars, i, i) - to = string_sub(mapChars, i, i) - encode_translate[from] = to - table_insert(encode_search, from) - decode_translate[to] = from - table_insert(decode_search, to) - end - codecTable["decode_search"..tostring(escapeCharIndex)] = "([".. escape_for_gsub(table_concat(decode_search)).."])" - codecTable["decode_translate"..tostring(escapeCharIndex)] = decode_translate - table_insert(decode_func_string, "str = str:gsub(self.decode_search"..tostring(escapeCharIndex)..", self.decode_translate"..tostring(escapeCharIndex)..");") - - end - - -- map single byte to double-byte - escapeCharIndex = escapeCharIndex + 1 - escapeChar = string_sub(escapeChars, escapeCharIndex, escapeCharIndex) - r = 0 -- suffix char value to the escapeChar - decode_search = {} - decode_translate = {} - for i = 1, string_len(encodeBytes) do - c = string_sub(encodeBytes, i, i) - if not encode_translate[c] then - -- this loop will update escapeChar and r - while r < 256 and taken[string_char(r)] do - r = r + 1 - if r > 255 then -- switch to next escapeChar - if escapeChar == "" then -- we are out of escape chars and we need more! - return nil, "Out of escape characters" - end - - codecTable["decode_search"..tostring(escapeCharIndex)] = escape_for_gsub(escapeChar).."([".. escape_for_gsub(table_concat(decode_search)).."])" - codecTable["decode_translate"..tostring(escapeCharIndex)] = decode_translate - table_insert(decode_func_string, "str = str:gsub(self.decode_search"..tostring(escapeCharIndex)..", self.decode_translate"..tostring(escapeCharIndex)..");") - - escapeCharIndex = escapeCharIndex + 1 - escapeChar = string_sub(escapeChars, escapeCharIndex, escapeCharIndex) - - r = 0 - decode_search = {} - decode_translate = {} - end - end - encode_translate[c] = escapeChar..string_char(r) - table_insert(encode_search, c) - decode_translate[string_char(r)] = c - table_insert(decode_search, string_char(r)) - r = r + 1 - end - end - - if r > 0 then - codecTable["decode_search"..tostring(escapeCharIndex)] = escape_for_gsub(escapeChar).."([".. escape_for_gsub(table_concat(decode_search)).."])" - codecTable["decode_translate"..tostring(escapeCharIndex)] = decode_translate - table_insert(decode_func_string, "str = str:gsub(self.decode_search"..tostring(escapeCharIndex)..", self.decode_translate"..tostring(escapeCharIndex)..");") - end - - -- change last line from "str = ...;" to "return ...;"; - decode_func_string[#decode_func_string] = decode_func_string[#decode_func_string]:gsub("str = (.*);", "return %1;") - decode_func_string = "return function(self, str) "..table_concat(decode_func_string).." end" - - encode_search = "([".. escape_for_gsub(table_concat(encode_search)).."])" - decode_search = escape_for_gsub(escapeChars).."([".. escape_for_gsub(table_concat(decode_search)).."])" - - encode_func = assert(loadstring("return function(self, str) return str:gsub(self.encode_search, self.encode_translate); end"))() - decode_func = assert(loadstring(decode_func_string))() - - codecTable.encode_search = encode_search - codecTable.encode_translate = encode_translate - codecTable.Encode = encode_func - codecTable.decode_search = decode_search - codecTable.decode_translate = decode_translate - codecTable.Decode = decode_func - - codecTable.decode_func_string = decode_func_string -- to be deleted - return codecTable -end - --- Addons: Call this only once and reuse the returned table for all encodings/decodings. -function LibCompress:GetAddonEncodeTable(reservedChars, escapeChars, mapChars ) - reservedChars = reservedChars or "" - escapeChars = escapeChars or "" - mapChars = mapChars or "" - -- Following byte values are not allowed: - -- \000 - if escapeChars == "" then - escapeChars = "\001" - end - return self:GetEncodeTable( (reservedChars or "").."\000", escapeChars, mapChars) -end - --- Addons: Call this only once and reuse the returned table for all encodings/decodings. -function LibCompress:GetChatEncodeTable(reservedChars, escapeChars, mapChars) - reservedChars = reservedChars or "" - escapeChars = escapeChars or "" - mapChars = mapChars or "" - -- Following byte values are not allowed: - -- \000, s, S, \010, \013, \124, % - -- Because SendChatMessage will error if an UTF8 multibyte character is incomplete, - -- all character values above 127 have to be encoded to avoid this. This costs quite a bit of bandwidth (about 13-14%) - -- Also, because drunken status is unknown for the received, strings used with SendChatMessage should be terminated with - -- an identifying byte value, after which the server MAY add "...hic!" or as much as it can fit(!). - -- Pass the identifying byte as a reserved character to this function to ensure the encoding doesn't contain that value. - -- or use this: local message, match = arg1:gsub("^(.*)\029.-$", "%1") - -- arg1 is message from channel, \029 is the string terminator, but may be used in the encoded datastream as well. :-) - -- This encoding will expand data anywhere from: - -- 0% (average with pure ascii text) - -- 53.5% (average with random data valued zero to 255) - -- 100% (only encoding data that encodes to two bytes) - local i - local r = {} - - for i = 128, 255 do - table_insert(r, string_char(i)) - end - - reservedChars = "sS\000\010\013\124%"..table_concat(r)..(reservedChars or "") - if escapeChars == "" then - escapeChars = "\029\031" - end - - if mapChars == "" then - mapChars = "\015\020"; - end - return self:GetEncodeTable(reservedChars, escapeChars, mapChars) -end - --------------------------------------------------------------------------------- --- 7 bit encoding algorithm --- implemented by Galmok of European Stormrage (Horde), galmok@gmail.com - --- The encoded data holds values from 0 to 127 inclusive. Additional encoding may be necessary. --- This algorithm isn't exactly fast and be used with care and consideration - -tables.encode7bit = {} - -function LibCompress:Encode7bit(str) - local remainder = 0 - local remainder_length = 0 - local tbl = tables.encode7bit - local encoded_size = 0 - local length = #str - for i = 1, length do - local code = string_byte(str, i) - remainder = remainder + bit_lshift(code, remainder_length) - remainder_length = 8 + remainder_length - while remainder_length >= 7 do - encoded_size = encoded_size + 1 - tbl[encoded_size] = string_char(bit_band(remainder, 127)) - remainder = bit_rshift(remainder, 7) - remainder_length = remainder_length -7 - end - end - - if remainder_length > 0 then - encoded_size = encoded_size + 1 - tbl[encoded_size] = string_char(remainder) - end - setCleanupTables("encode7bit") - return table_concat(tbl, "", 1, encoded_size) -end - -tables.decode8bit = {} - -function LibCompress:Decode7bit(str) - local bit8 = tables.decode8bit - local decoded_size = 0 - local ch - local i = 1 - local bitfield_len = 0 - local bitfield = 0 - local length = #str - while true do - if bitfield_len >= 8 then - decoded_size = decoded_size + 1 - bit8[decoded_size] = string_char(bit_band(bitfield, 255)) - bitfield = bit_rshift(bitfield, 8) - bitfield_len = bitfield_len - 8 - end - ch = string_byte(str, i) - bitfield=bitfield + bit_lshift(ch or 0, bitfield_len) - bitfield_len = bitfield_len + 7 - if i > length then - break - end - i = i + 1 - end - setCleanupTables("decode8bit") - return table_concat(bit8, "", 1, decoded_size) -end - ----------------------------------------------------------------------- ----------------------------------------------------------------------- --- --- Checksum/hash algorithms - --------------------------------------------------------------------------------- --- FCS16/32 checksum algorithms --- converted from C by Galmok of European Stormrage (Horde), galmok@gmail.com --- usage: --- code = LibCompress:fcs16init() --- code = LibCompress:fcs16update(code, data1) --- code = LibCompress:fcs16update(code, data2) --- code = LibCompress:fcs16update(code, data...) --- code = LibCompress:fcs16final(code) --- --- data = string --- fcs16 provides a 16 bit checksum, fcs32 provides a 32 bit checksum. - - ---[[/* The following copyright notice concerns only the FCS hash algorithm ---------------------------------------------------------------------------- -Copyright (c) 2003, Dominik Reichl , Germany. -All rights reserved. - -Distributed under the terms of the GNU General Public License v2. - -This software is provided 'as is' with no explicit or implied warranties -in respect of its properties, including, but not limited to, correctness -and/or fitness for purpose. ---------------------------------------------------------------------------- -*/]] ---// FCS-16 algorithm implemented as described in RFC 1331 -local FCSINIT16 = 65535 ---// Fast 16 bit FCS lookup table -local fcs16tab = { [0]=0, 4489, 8978, 12955, 17956, 22445, 25910, 29887, - 35912, 40385, 44890, 48851, 51820, 56293, 59774, 63735, - 4225, 264, 13203, 8730, 22181, 18220, 30135, 25662, - 40137, 36160, 49115, 44626, 56045, 52068, 63999, 59510, - 8450, 12427, 528, 5017, 26406, 30383, 17460, 21949, - 44362, 48323, 36440, 40913, 60270, 64231, 51324, 55797, - 12675, 8202, 4753, 792, 30631, 26158, 21685, 17724, - 48587, 44098, 40665, 36688, 64495, 60006, 55549, 51572, - 16900, 21389, 24854, 28831, 1056, 5545, 10034, 14011, - 52812, 57285, 60766, 64727, 34920, 39393, 43898, 47859, - 21125, 17164, 29079, 24606, 5281, 1320, 14259, 9786, - 57037, 53060, 64991, 60502, 39145, 35168, 48123, 43634, - 25350, 29327, 16404, 20893, 9506, 13483, 1584, 6073, - 61262, 65223, 52316, 56789, 43370, 47331, 35448, 39921, - 29575, 25102, 20629, 16668, 13731, 9258, 5809, 1848, - 65487, 60998, 56541, 52564, 47595, 43106, 39673, 35696, - 33800, 38273, 42778, 46739, 49708, 54181, 57662, 61623, - 2112, 6601, 11090, 15067, 20068, 24557, 28022, 31999, - 38025, 34048, 47003, 42514, 53933, 49956, 61887, 57398, - 6337, 2376, 15315, 10842, 24293, 20332, 32247, 27774, - 42250, 46211, 34328, 38801, 58158, 62119, 49212, 53685, - 10562, 14539, 2640, 7129, 28518, 32495, 19572, 24061, - 46475, 41986, 38553, 34576, 62383, 57894, 53437, 49460, - 14787, 10314, 6865, 2904, 32743, 28270, 23797, 19836, - 50700, 55173, 58654, 62615, 32808, 37281, 41786, 45747, - 19012, 23501, 26966, 30943, 3168, 7657, 12146, 16123, - 54925, 50948, 62879, 58390, 37033, 33056, 46011, 41522, - 23237, 19276, 31191, 26718, 7393, 3432, 16371, 11898, - 59150, 63111, 50204, 54677, 41258, 45219, 33336, 37809, - 27462, 31439, 18516, 23005, 11618, 15595, 3696, 8185, - 63375, 58886, 54429, 50452, 45483, 40994, 37561, 33584, - 31687, 27214, 22741, 18780, 15843, 11370, 7921, 3960 } - -function LibCompress:fcs16init() - return FCSINIT16 -end - -function LibCompress:fcs16update(uFcs16, pBuffer) - local i - local length = string_len(pBuffer) - for i = 1, length do - uFcs16 = bit_bxor(bit_rshift(uFcs16,8), fcs16tab[bit_band(bit_bxor(uFcs16, string_byte(pBuffer, i)), 255)]) - end - return uFcs16 -end - -function LibCompress:fcs16final(uFcs16) - return bit_bxor(uFcs16,65535) -end --- END OF FCS16 - ---[[/* ---------------------------------------------------------------------------- -Copyright (c) 2003, Dominik Reichl , Germany. -All rights reserved. - -Distributed under the terms of the GNU General Public License v2. - -This software is provided 'as is' with no explicit or implied warranties -in respect of its properties, including, but not limited to, correctness -and/or fitness for purpose. ---------------------------------------------------------------------------- -*/]] - ---// FCS-32 algorithm implemented as described in RFC 1331 - -local FCSINIT32 = -1 - ---// Fast 32 bit FCS lookup table -local fcs32tab = { [0] = 0, 1996959894, -301047508, -1727442502, 124634137, 1886057615, -379345611, -1637575261, - 249268274, 2044508324, -522852066, -1747789432, 162941995, 2125561021, -407360249, -1866523247, - 498536548, 1789927666, -205950648, -2067906082, 450548861, 1843258603, -187386543, -2083289657, - 325883990, 1684777152, -43845254, -1973040660, 335633487, 1661365465, -99664541, -1928851979, - 997073096, 1281953886, -715111964, -1570279054, 1006888145, 1258607687, -770865667, -1526024853, - 901097722, 1119000684, -608450090, -1396901568, 853044451, 1172266101, -589951537, -1412350631, - 651767980, 1373503546, -925412992, -1076862698, 565507253, 1454621731, -809855591, -1195530993, - 671266974, 1594198024, -972236366, -1324619484, 795835527, 1483230225, -1050600021, -1234817731, - 1994146192, 31158534, -1731059524, -271249366, 1907459465, 112637215, -1614814043, -390540237, - 2013776290, 251722036, -1777751922, -519137256, 2137656763, 141376813, -1855689577, -429695999, - 1802195444, 476864866, -2056965928, -228458418, 1812370925, 453092731, -2113342271, -183516073, - 1706088902, 314042704, -1950435094, -54949764, 1658658271, 366619977, -1932296973, -69972891, - 1303535960, 984961486, -1547960204, -725929758, 1256170817, 1037604311, -1529756563, -740887301, - 1131014506, 879679996, -1385723834, -631195440, 1141124467, 855842277, -1442165665, -586318647, - 1342533948, 654459306, -1106571248, -921952122, 1466479909, 544179635, -1184443383, -832445281, - 1591671054, 702138776, -1328506846, -942167884, 1504918807, 783551873, -1212326853, -1061524307, - -306674912, -1698712650, 62317068, 1957810842, -355121351, -1647151185, 81470997, 1943803523, - -480048366, -1805370492, 225274430, 2053790376, -468791541, -1828061283, 167816743, 2097651377, - -267414716, -2029476910, 503444072, 1762050814, -144550051, -2140837941, 426522225, 1852507879, - -19653770, -1982649376, 282753626, 1742555852, -105259153, -1900089351, 397917763, 1622183637, - -690576408, -1580100738, 953729732, 1340076626, -776247311, -1497606297, 1068828381, 1219638859, - -670225446, -1358292148, 906185462, 1090812512, -547295293, -1469587627, 829329135, 1181335161, - -882789492, -1134132454, 628085408, 1382605366, -871598187, -1156888829, 570562233, 1426400815, - -977650754, -1296233688, 733239954, 1555261956, -1026031705, -1244606671, 752459403, 1541320221, - -1687895376, -328994266, 1969922972, 40735498, -1677130071, -351390145, 1913087877, 83908371, - -1782625662, -491226604, 2075208622, 213261112, -1831694693, -438977011, 2094854071, 198958881, - -2032938284, -237706686, 1759359992, 534414190, -2118248755, -155638181, 1873836001, 414664567, - -2012718362, -15766928, 1711684554, 285281116, -1889165569, -127750551, 1634467795, 376229701, - -1609899400, -686959890, 1308918612, 956543938, -1486412191, -799009033, 1231636301, 1047427035, - -1362007478, -640263460, 1088359270, 936918000, -1447252397, -558129467, 1202900863, 817233897, - -1111625188, -893730166, 1404277552, 615818150, -1160759803, -841546093, 1423857449, 601450431, - -1285129682, -1000256840, 1567103746, 711928724, -1274298825, -1022587231, 1510334235, 755167117 } - -function LibCompress:fcs32init() - return FCSINIT32 -end - -function LibCompress:fcs32update(uFcs32, pBuffer) - local i - local length = string_len(pBuffer) - for i = 1, length do - uFcs32 = bit_bxor(bit_rshift(uFcs32, 8), fcs32tab[bit_band(bit_bxor(uFcs32, string_byte(pBuffer, i)), 255)]) - end - return uFcs32 -end - -function LibCompress:fcs32final(uFcs32) - return bit_bnot(uFcs32) -end +end \ No newline at end of file diff --git a/2/3/4/5/6/7/ElvUI/Libraries/Load_Libraries.xml b/2/3/4/5/6/7/ElvUI/Libraries/Load_Libraries.xml index 911f35d..a3e83b8 100644 --- a/2/3/4/5/6/7/ElvUI/Libraries/Load_Libraries.xml +++ b/2/3/4/5/6/7/ElvUI/Libraries/Load_Libraries.xml @@ -16,11 +16,9 @@ - \ No newline at end of file diff --git a/2/3/4/5/6/7/ElvUI_Config/Core.lua b/2/3/4/5/6/7/ElvUI_Config/Core.lua index a36addc..7171f97 100644 --- a/2/3/4/5/6/7/ElvUI_Config/Core.lua +++ b/2/3/4/5/6/7/ElvUI_Config/Core.lua @@ -1,5 +1,5 @@ local E, L, V, P, G = unpack(ElvUI); ---local D = E:GetModule("Distributor"); +local D = E:GetModule("Distributor"); local AceGUI = LibStub("AceGUI-3.0"); local pairs = pairs; @@ -161,5 +161,303 @@ for _, testerName in pairs(TESTERS) do TESTER_STRING = TESTER_STRING .. LINE_BREAK .. testerName; end +E.Options.args.credits = { + type = "group", + name = L["Credits"], + order = -1, + args = { + text = { + order = 1, + type = "description", + name = L["ELVUI_CREDITS"] .. "\n\n" .. L["Coding:"] .. DEVELOPER_STRING .. "\n\n" .. L["Testing:"] .. TESTER_STRING .. "\n\n" .. L["Donations:"] .. DONATOR_STRING + } + } +} +local profileTypeItems = { + ["profile"] = L["Profile"], + ["private"] = L["Private (Character Settings)"], + ["global"] = L["Global (Account Settings)"], + ["filtersNP"] = L["Filters (NamePlates)"], + ["filtersUF"] = L["Filters (UnitFrames)"], + ["filtersAll"] = L["Filters (All)"] +} +local profileTypeListOrder = { + "profile", + "private", + "global", + "filtersNP", + "filtersUF", + "filtersAll" +} + +local exportTypeItems = { + ["text"] = L["Text"], + ["luaTable"] = L["Table"], + ["luaPlugin"] = L["Plugin"] +} + +local exportTypeListOrder = { + "text", + "luaTable", + "luaPlugin" +} + +local exportString = "" +local function ExportImport_Open(mode) + local frame = AceGUI:Create("Frame") + frame:SetTitle("") + frame:EnableResize(false) + frame:SetWidth(800) + frame:SetHeight(600) + frame.frame:SetFrameStrata("FULLSCREEN_DIALOG") + frame:SetLayout("flow") + + local box = AceGUI:Create("MultiLineEditBox") + box:SetNumLines(30) + box:DisableButton(true) + box:SetWidth(800) + box:SetLabel("") + frame:AddChild(box) + box.editBox.OnTextChangedOrig = box.editBox:GetScript("OnTextChanged") + box.editBox.OnCursorChangedOrig = box.editBox:GetScript("OnCursorChanged") + box.editBox:SetScript("OnCursorChanged", nil) + + local label1 = AceGUI:Create("Label") + local font = GameFontHighlightSmall:GetFont() + label1:SetFont(font, 14) + label1:SetText(" ") + label1:SetWidth(800) + frame:AddChild(label1) + + local label2 = AceGUI:Create("Label") + local font = GameFontHighlightSmall:GetFont() + label2:SetFont(font, 14) + label2:SetText(" \n ") + label2:SetWidth(800) + frame:AddChild(label2) + + if(mode == "export") then + frame:SetTitle(L["Export Profile"]) + + local profileTypeDropdown = AceGUI:Create("Dropdown") + profileTypeDropdown:SetMultiselect(false) + profileTypeDropdown:SetLabel(L["Choose What To Export"]) + profileTypeDropdown:SetList(profileTypeItems, profileTypeListOrder) + profileTypeDropdown:SetValue("profile") + frame:AddChild(profileTypeDropdown) + + local exportFormatDropdown = AceGUI:Create("Dropdown") + exportFormatDropdown:SetMultiselect(false) + exportFormatDropdown:SetLabel(L["Choose Export Format"]) + exportFormatDropdown:SetList(exportTypeItems, exportTypeListOrder) + exportFormatDropdown:SetValue("text") + exportFormatDropdown:SetWidth(150) + frame:AddChild(exportFormatDropdown) + + local exportButton = AceGUI:Create("Button") + exportButton:SetText(L["Export Now"]) + exportButton:SetAutoWidth(true) + local function OnClick(self) + label1:SetText(" ") + label2:SetText(" ") + + local profileType, exportFormat = profileTypeDropdown:GetValue(), exportFormatDropdown:GetValue() + local profileKey, profileExport = D:ExportProfile(profileType, exportFormat) + if(not profileKey or not profileExport) then + label1:SetText(L["Error exporting profile!"]) + else + label1:SetText(format("%s: %s%s|r", L["Exported"], E.media.hexvaluecolor, profileTypeItems[profileType])) + if(profileType == "profile") then + label2:SetText(format("%s: %s%s|r", L["Profile Name"], E.media.hexvaluecolor, profileKey)) + end + end + box:SetText(profileExport) + box.editBox:HighlightText() + box:SetFocus() + exportString = profileExport + end + exportButton:SetCallback("OnClick", OnClick) + frame:AddChild(exportButton) + + box.editBox:SetScript("OnChar", function() box:SetText(exportString) box.editBox:HighlightText() end) + box.editBox:SetScript("OnTextChanged", function(self) + box:SetText(exportString) + box.editBox:HighlightText() + end) + elseif(mode == "import") then + frame:SetTitle(L["Import Profile"]) + local importButton = AceGUI:Create("Button") + importButton:SetDisabled(true) + importButton:SetText(L["Import Now"]) + importButton:SetAutoWidth(true) + importButton:SetCallback("OnClick", function() + label1:SetText(" ") + label2:SetText(" ") + + local text + local success = D:ImportProfile(box:GetText()) + if(success) then + text = L["Profile imported successfully!"] + else + text = L["Error decoding data. Import string may be corrupted!"] + end + label1:SetText(text) + end) + frame:AddChild(importButton) + + local decodeButton = AceGUI:Create("Button") + decodeButton:SetDisabled(true) + decodeButton:SetText(L["Decode Text"]) + decodeButton:SetAutoWidth(true) + decodeButton:SetCallback("OnClick", function() + label1:SetText(" ") + label2:SetText(" ") + local decodedText + local profileType, profileKey, profileData = D:Decode(box:GetText()) + if(profileData) then + decodedText = E:TableToLuaString(profileData) + end + local importText = D:CreateProfileExport(decodedText, profileType, profileKey) + box:SetText(importText) + end) + frame:AddChild(decodeButton) + + local oldText = "" + local function OnTextChanged() + local text = box:GetText() + if(text == "") then + label1:SetText(" ") + label2:SetText(" ") + importButton:SetDisabled(true) + decodeButton:SetDisabled(true) + elseif(oldText ~= text) then + local stringType = D:GetImportStringType(text) + if(stringType == "Base64") then + decodeButton:SetDisabled(false) + else + decodeButton:SetDisabled(true) + end + + local profileType, profileKey = D:Decode(text) + if not profileType or (profileType and profileType == "profile" and not profileKey) then + label1:SetText(L["Error decoding data. Import string may be corrupted!"]) + label2:SetText(" ") + importButton:SetDisabled(true) + decodeButton:SetDisabled(true) + else + label1:SetText(format("%s: %s%s|r", L["Importing"], E.media.hexvaluecolor, profileTypeItems[profileType] or "")) + if(profileType == "profile") then + label2:SetText(format("%s: %s%s|r", L["Profile Name"], E.media.hexvaluecolor, profileKey)) + end + importButton:SetDisabled(false) + end + + box.scrollFrame:UpdateScrollChildRect() + box.scrollFrame:SetVerticalScroll(box.scrollFrame:GetVerticalScrollRange()) + box.scrollFrame:SetFrameLevel(box.scrollFrame:GetParent():GetFrameLevel() - 2) + + oldText = text + end + end + + box.editBox:SetFocus() + box.editBox:SetScript("OnChar", nil) + box.editBox:SetScript("OnTextChanged", OnTextChanged) + end + + frame:SetCallback("OnClose", function(widget) + box.editBox:SetScript("OnChar", nil) + box.editBox:SetScript("OnTextChanged", box.editBox.OnTextChangedOrig) + box.editBox:SetScript("OnCursorChanged", box.editBox.OnCursorChangedOrig) + box.editBox.OnTextChangedOrig = nil + box.editBox.OnCursorChangedOrig = nil + + exportString = "" + + AceGUI:Release(widget) + ACD:Open("ElvUI") + end) + + label1:SetText(" ") + label2:SetText(" ") + + ACD:Close("ElvUI") + + GameTooltip:Hide() +end + +E.Options.args.profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(E.data) +AC.RegisterOptionsTable(P, "ElvProfiles", E.Options.args.profiles) +E.Options.args.profiles.order = -10 + +if(not E.Options.args.profiles.plugins) then + E.Options.args.profiles.plugins = {} +end + +E.Options.args.profiles.plugins["ElvUI"] = { + spacer = { + order = 89, + type = "description", + name = "\n\n" + }, + desc = { + order = 90, + type = "description", + name = L["This feature will allow you to transfer settings to other characters."] + }, + distributeProfile = { + order = 91, + name = L["Share Current Profile"], + desc = L["Sends your current profile to your target."], + type = "execute", + func = function() + if(not UnitExists("target") or not UnitIsPlayer("target") or not UnitIsFriend("player", "target") or UnitIsUnit("player", "target")) then + E:Print(L["You must be targeting a player."]) + return + end + local name, server = UnitName("target") + if(name and (not server or server == "")) then + D:Distribute(name) + elseif(server) then + D:Distribute(name, true) + end + end + }, + distributeGlobal = { + order = 92, + type = "execute", + name = L["Share Filters"], + desc = L["Sends your filter settings to your target."], + func = function() + if(not UnitExists("target") or not UnitIsPlayer("target") or not UnitIsFriend("player", "target") or UnitIsUnit("player", "target")) then + E:Print(L["You must be targeting a player."]) + return + end + local name, server = UnitName("target") + if(name and (not server or server == "")) then + D:Distribute(name, false, true) + elseif(server) then + D:Distribute(name, true, true) + end + end, + }, + spacer2 = { + order = 93, + type = "description", + name = "" + }, + exportProfile = { + order = 94, + type = "execute", + name = L["Export Profile"], + func = function() ExportImport_Open("export") end + }, + importProfile = { + order = 95, + type = "execute", + name = L["Import Profile"], + func = function() ExportImport_Open("import") end + } +} \ No newline at end of file diff --git a/2/3/4/5/6/7/ElvUI_Config/Libraries/AceConfig-3.0/AceConfig-3.0.xml b/2/3/4/5/6/7/ElvUI_Config/Libraries/AceConfig-3.0/AceConfig-3.0.xml index 87972ad..6d7e3d5 100644 --- a/2/3/4/5/6/7/ElvUI_Config/Libraries/AceConfig-3.0/AceConfig-3.0.xml +++ b/2/3/4/5/6/7/ElvUI_Config/Libraries/AceConfig-3.0/AceConfig-3.0.xml @@ -3,6 +3,6 @@ - +