Update 2.0
- 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
This commit is contained in:
+8
-3
@@ -1,11 +1,16 @@
|
||||
## Interface: 11200
|
||||
## Title: |cff00ff00Consumes Manager|r |cff808080[Horyoshi]|r
|
||||
## Notes: Tracks and manages consumables across your inventory, bank and mail through multiple characters. Keeps track of how much consumes you have left.
|
||||
## Notes: Easily track and manage your consumables, food buffs, and more across your inventory, bank, and mail, while supporting multiple characters and accounts.
|
||||
## Author: Horyoshi
|
||||
## Version: 1.8
|
||||
## Version: 2.0
|
||||
## SavedVariables: ConsumesManager_Options, ConsumesManager_SelectedItems, ConsumesManager_Data
|
||||
|
||||
libs\LibStub\LibStub.lua
|
||||
libs\LibCompress\LibCompress.lua
|
||||
libs\LibCompress\LibCompress.xml
|
||||
libs\LzwLib-1.0\LzwLib-1.0.lua
|
||||
|
||||
Main.lua
|
||||
Itemlist.lua
|
||||
Classpresets.lua
|
||||
minimap_icon.xml
|
||||
minimap_icon.xml
|
||||
|
||||
+103
-103
@@ -1,126 +1,126 @@
|
||||
|
||||
consumablesCategories = {
|
||||
["Elixirs"] = {
|
||||
{ id = 13452, name = "Elixir of the Mongoose", texture = "Interface\\Icons\\INV_Potion_32", description = "Increases Agility by 25 and critical strike chance by 2% for 1 hour." },
|
||||
{ id = 9187, name = "Elixir of Greater Agility", texture = "Interface\\Icons\\INV_Potion_94", description = "Increases Agility by 15 for 1 hour." },
|
||||
{ id = 13453, name = "Elixir of Brute Force", texture = "Interface\\Icons\\INV_Potion_40", description = "Increases Strength and Stamina by 18 for 1 hour." },
|
||||
{ id = 13447, name = "Elixir of the Sages", texture = "Interface\\Icons\\INV_Potion_29", description = "Increases Intellect and Spirit by 18 for 1 hour." },
|
||||
{ id = 13454, name = "Greater Arcane Elixir", texture = "Interface\\Icons\\INV_Potion_25", description = "Increases spell damage by up to 35 for 1 hour." },
|
||||
{ id = 13445, name = "Elixir of Superior Defense", texture = "Interface\\Icons\\INV_Potion_66", description = "Increases armor by 450 for 1 hour." },
|
||||
{ id = 17708, name = "Elixir of Frost Power", texture = "Interface\\Icons\\INV_Potion_03", description = "Increases Frost spell damage by up to 15 for 1 hour." },
|
||||
{ id = 22193, name = "Bloodkelp Elixir of Resistance", texture = "Interface\\Icons\\INV_Potion_21", description = "Increases all magical resistances by 15 for 30 minutes." },
|
||||
{ id = 3825, name = "Elixir of Fortitude", texture = "Interface\\Icons\\INV_Potion_43", description = "Increases the player's maximum health by 120 for 1 hour." },
|
||||
{ id = 9206, name = "Elixir of Giants", texture = "Interface\\Icons\\INV_Potion_61", description = "Increases your Strength by 25 for 1 hour." },
|
||||
{ id = 9179, name = "Elixir of Greater Intellect", texture = "Interface\\Icons\\INV_Potion_10", description = "Increases Intellect by 25 for 1 hour." },
|
||||
{ id = 50237, name = "Elixir of Greater Nature Power", texture = "Interface\\Icons\\INV_Potion_22", description = "Increases nature spell damage by up to 55 for 1 hour." },
|
||||
{ id = 21546, name = "Elixir of Greater Firepower", texture = "Interface\\Icons\\INV_Potion_60", description = "Increases spell fire damage by up to 40 for 1 hour." },
|
||||
{ id = 3386, name = "Elixir of Poison Resistance", texture = "Interface\\Icons\\INV_Potion_12", description = "Imbiber is cured of up to four poisons up to level 60." },
|
||||
{ id = 9264, name = "Elixir of Shadow Power", texture = "Interface\\Icons\\INV_Potion_46", description = "Increases spell shadow damage by up to 40 for 30 minutes." },
|
||||
{ id = 61224, name = "Dreamshard Elixir", texture = "Interface\\Icons\\INV_Potion_12", description = "Grants 2% spell critical and 15 spell power for 1 hour." }
|
||||
{ id = 13452, name = "Elixir of the Mongoose", mats = {"2x Mountain Silversage", "2x Plaguebloom", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_32", description = "Increases Agility by 25 and critical strike chance by 2% for 1 hour." },
|
||||
{ id = 9187, name = "Elixir of Greater Agility", mats = {"1x Sungrass", "1x Goldthorn", "1x Crystal Vial" }, texture = "Interface\\Icons\\INV_Potion_94", description = "Increases Agility by 15 for 1 hour." },
|
||||
{ id = 13453, name = "Elixir of Brute Force", mats = {"2x Gromsblood", "2x Plaguebloom", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_40", description = "Increases Strength and Stamina by 18 for 1 hour." },
|
||||
{ id = 13447, name = "Elixir of the Sages", mats = {"1x Dreamfoil", "2x Plaguebloom", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_29", description = "Increases Intellect and Spirit by 18 for 1 hour." },
|
||||
{ id = 13454, name = "Greater Arcane Elixir", mats = {"3x Dreamfoil", "1x Mountain Silversage", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_25", description = "Increases spell damage by up to 35 for 1 hour." },
|
||||
{ id = 13445, name = "Elixir of Superior Defense", mats = {"2x Stonescale Oil", "1x Sungrass", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_66", description = "Increases armor by 450 for 1 hour." },
|
||||
{ id = 17708, name = "Elixir of Frost Power", mats = {"2x Wintersbite", "1x Khadgar's Whisker", "1x Leaded Vial"}, texture = "Interface\\Icons\\INV_Potion_03", description = "Increases Frost spell damage by up to 15 for 1 hour." },
|
||||
{ id = 22193, name = "Bloodkelp Elixir of Resistance", mats = {"Quest: More Components of Importance"}, texture = "Interface\\Icons\\INV_Potion_21", description = "Increases all magical resistances by 15 for 30 minutes." },
|
||||
{ id = 3825, name = "Elixir of Fortitude", mats = {"1x Wild Steelbloom", "1x Goldthorn", "1x Leaded Vial"}, texture = "Interface\\Icons\\INV_Potion_43", description = "Increases the player's maximum health by 120 for 1 hour." },
|
||||
{ id = 9206, name = "Elixir of Giants", mats = {"1x Sungrass", "1x Gromsblood", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_61", description = "Increases your Strength by 25 for 1 hour." },
|
||||
{ id = 9179, name = "Elixir of Greater Intellect", mats = {"1x Blindweed", "1x Khadgar's Whisker", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_10", description = "Increases Intellect by 25 for 1 hour." },
|
||||
{ id = 50237, name = "Elixir of Greater Nature Power", mats = {"3x Heart of the Wild", "1x Golden Sansam", "1x Sungrass", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_22", description = "Increases nature spell damage by up to 55 for 1 hour." },
|
||||
{ id = 21546, name = "Elixir of Greater Firepower", mats = {"3x Fire Oil", "3x Firebloom", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_60", description = "Increases spell fire damage by up to 40 for 1 hour." },
|
||||
{ id = 3386, name = "Elixir of Poison Resistance", mats = {"1x Large Venom Sac", "1x Bruiseweed", "1x Leaded Vial"}, texture = "Interface\\Icons\\INV_Potion_12", description = "Imbiber is cured of up to four poisons up to level 60." },
|
||||
{ id = 9264, name = "Elixir of Shadow Power", mats = {"3x Ghost Mushroom", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_46", description = "Increases spell shadow damage by up to 40 for 30 minutes." },
|
||||
{ id = 61224, name = "Dreamshard Elixir", mats = {"1x Dream Dust", "1x Small Dream Shard", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_12", description = "Grants 2% spell critical and 15 spell power for 1 hour." }
|
||||
},
|
||||
["Flasks"] = {
|
||||
{ id = 13513, name = "Flask of Chromatic Resistance", texture = "Interface\\Icons\\INV_Potion_48", description = "Increases your resistance to all schools of magic by 25 for 2 hours." },
|
||||
{ id = 13511, name = "Flask of Distilled Wisdom", texture = "Interface\\Icons\\INV_Potion_97", description = "Increases the player's maximum mana by 2000 for 2 hours." },
|
||||
{ id = 13512, name = "Flask of Supreme Power", texture = "Interface\\Icons\\INV_Potion_41", description = "Increases damage done by magical spells and effects by up to 150 for 2 hours." },
|
||||
{ id = 13510, name = "Flask of the Titans", texture = "Interface\\Icons\\INV_Potion_62", description = "Increases the player's maximum health by 1200 for 2 hours." },
|
||||
{ id = 13506, name = "Flask of Petrification", texture = "Interface\\Icons\\INV_Potion_26", description = "You turn to stone, protecting you from all physical attacks and spells for 60 sec." }
|
||||
{ id = 13513, name = "Flask of Chromatic Resistance", mats = {"30x Icecap", "10x Mountain Silversage", "1x Black Lotus", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_48", description = "Increases your resistance to all schools of magic by 25 for 2 hours." },
|
||||
{ id = 13511, name = "Flask of Distilled Wisdom", mats = {"30x Dreamfoil", "10x Icecap", "1x Black Lotus", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_97", description = "Increases the player's maximum mana by 2000 for 2 hours." },
|
||||
{ id = 13512, name = "Flask of Supreme Power", mats = {"30x Dreamfoil", "10x Mountain Silversage", "1x Black Lotus", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_41", description = "Increases damage done by magical spells and effects by up to 150 for 2 hours." },
|
||||
{ id = 13510, name = "Flask of the Titans", mats = {"30x Gromsblood", "10x Stonescale Oil", "1x Black Lotus", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_62", description = "Increases the player's maximum health by 1200 for 2 hours." },
|
||||
{ id = 13506, name = "Flask of Petrification", mats = {"30x Stonescale Oil", "10x Mountain Silversage", "1x Black Lotus", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_26", description = "You turn to stone, protecting you from all physical attacks and spells for 60 sec." }
|
||||
},
|
||||
["Protection Potions"] = {
|
||||
{ id = 13457, name = "Greater Fire Protection Potion", texture = "Interface\\Icons\\INV_Potion_24", description = "Absorbs 1950 to 3250 Fire damage for 1 hour." },
|
||||
{ id = 13456, name = "Greater Frost Protection Potion", texture = "Interface\\Icons\\INV_Potion_20", description = "Absorbs 1950 to 3250 Frost damage for 1 hour." },
|
||||
{ id = 13458, name = "Greater Nature Protection Potion", texture = "Interface\\Icons\\INV_Potion_22", description = "Absorbs 1950 to 3250 Nature damage for 1 hour." },
|
||||
{ id = 13459, name = "Greater Shadow Protection Potion", texture = "Interface\\Icons\\INV_Potion_23", description = "Absorbs 1950 to 3250 Shadow damage for 1 hour." },
|
||||
{ id = 13461, name = "Greater Arcane Protection Potion", texture = "Interface\\Icons\\INV_Potion_83", description = "Absorbs 1950 to 3250 Arcane damage for 1 hour." },
|
||||
{ id = 13460, name = "Greater Holy Protection Potion", texture = "Interface\\Icons\\INV_Potion_09", description = "Absorbs 1950 to 3251 holy damage for 1 hour." }
|
||||
{ id = 13457, name = "Greater Fire Protection Potion", mats = {"1x Elemental Fire", "1x Firebloom", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_24", description = "Absorbs 1950 to 3250 Fire damage for 1 hour." },
|
||||
{ id = 13456, name = "Greater Frost Protection Potion", mats = {"1x Elemental Water", "1x Icecap", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_20", description = "Absorbs 1950 to 3250 Frost damage for 1 hour." },
|
||||
{ id = 13458, name = "Greater Nature Protection Potion", mats = {"1x Elemental Earth", "1x Dreamfoil", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_22", description = "Absorbs 1950 to 3250 Nature damage for 1 hour." },
|
||||
{ id = 13459, name = "Greater Shadow Protection Potion", mats = {"1x Shadow Oil", "2x Arthas' Tears", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_23", description = "Absorbs 1950 to 3250 Shadow damage for 1 hour." },
|
||||
{ id = 13461, name = "Greater Arcane Protection Potion", mats = {"1x Dream Dust", "1x Dreamfoil", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_83", description = "Absorbs 1950 to 3250 Arcane damage for 1 hour." },
|
||||
{ id = 13460, name = "Greater Holy Protection Potion", mats = {"1x Elemental Air", "1x Golden Sansam", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_09", description = "Absorbs 1950 to 3251 holy damage for 1 hour." }
|
||||
},
|
||||
["Weapon Enhancements"] = {
|
||||
{ id = 12404, name = "Dense Sharpening Stone", texture = "Interface\\Icons\\INV_Stone_SharpeningStone_05", description = "Increases weapon damage by 8 for 30 minutes." },
|
||||
{ id = 12645, name = "Thorium Shield Spike", texture = "Interface\\Icons\\INV_Misc_Armorkit_20", description = "Attaches a Thorium Spike to a shield, causing 20-30 damage when blocking." },
|
||||
{ id = 20748, name = "Brilliant Mana Oil", texture = "Interface\\Icons\\INV_Potion_100", description = "Increases mana regeneration by 12 and spell damage by up to 25 for 30 minutes." },
|
||||
{ id = 20749, name = "Brilliant Wizard Oil", texture = "Interface\\Icons\\INV_Potion_105", description = "Increases spell damage by up to 36 for 30 minutes." },
|
||||
{ id = 23123, name = "Blessed Wizard Oil", texture = "Interface\\Icons\\INV_Potion_26", description = "Increases spell damage against undead by up to 60. Lasts for 1 hour." },
|
||||
{ id = 3829, name = "Frost Oil", texture = "Interface\\Icons\\INV_Potion_20", description = "Adds a chance to inflict Frost damage on hit. Lasts 30 minutes." },
|
||||
{ id = 23122, name = "Consecrated Sharpening Stone", texture = "Interface\\Icons\\inv_stone_sharpeningstone_02", description = "Increases melee attack power by 100 against undead for 30 minutes." },
|
||||
{ id = 18262, name = "Elemental Sharpening Stone", texture = "Interface\\Icons\\INV_Stone_02", description = "Increases critical strike chance by 2% for 30 minutes." },
|
||||
{ id = 8928, name = "Instant Poison VI", texture = "Interface\\Icons\\Ability_Poisons", description = "20% chance to inflict 112 to 149 Nature damage." },
|
||||
{ id = 3776, name = "Crippling Poison II", texture = "Interface\\Icons\\INV_Potion_19", description = "30% chance to inflict the enemy, slowing their movement speed by 60% for 12 sec." },
|
||||
{ id = 20844, name = "Deadly Poison V", texture = "Interface\\Icons\\Ability_Rogue_DualWeild", description = "30% chance of poisoning the enemy for 136 Nature damage over 12 sec. Stacks up to 5 times on a single target." },
|
||||
{ id = 5763, name = "Mind-numbing Poison III", texture = "Interface\\Icons\\Spell_Nature_NullifyDisease", description = "20% chance of poisoning the enemy, increasing their casting time by 60% for 14 sec." },
|
||||
{ id = 10922, name = "Wound Poison IV", texture = "Interface\\Icons\\Ability_PoisonSting", description = "30% chance of poisoning the enemy, reducing all healing effects used on them by 135 for 15 sec. Stacks up to 5 times on a single target." },
|
||||
{ id = 12643, name = "Dense Weightstone", texture = "Interface\\Icons\\INV_stone_weightstone_05", description = "Increase the damage of a blunt weapon by 8 for 30 minutes." }
|
||||
{ id = 12404, name = "Dense Sharpening Stone", mats = {"1x Dense Stone"}, texture = "Interface\\Icons\\INV_Stone_SharpeningStone_05", description = "Increases weapon damage by 8 for 30 minutes." },
|
||||
{ id = 12645, name = "Thorium Shield Spike", mats = {"4x Thorium Bar", "4x Dense Grinding Stone", "2x Essence of Earth"}, texture = "Interface\\Icons\\INV_Misc_Armorkit_20", description = "Attaches a Thorium Spike to a shield, causing 20-30 damage when blocking." },
|
||||
{ id = 20748, name = "Brilliant Mana Oil", mats = {"2x Large Brilliant Shard", "3x Purple Lotus", "1x Imbued Vial"}, texture = "Interface\\Icons\\INV_Potion_100", description = "Increases mana regeneration by 12 and spell damage by up to 25 for 30 minutes." },
|
||||
{ id = 20749, name = "Brilliant Wizard Oil", mats = {"2x Large Brilliant Shard", "3x Firebloom", "1x Imbued Vial"}, texture = "Interface\\Icons\\INV_Potion_105", description = "Increases spell damage by up to 36 for 30 minutes." },
|
||||
{ id = 23123, name = "Blessed Wizard Oil", mats = {"Quest: 8x Necrotic Rune"}, texture = "Interface\\Icons\\INV_Potion_26", description = "Increases spell damage against undead by up to 60. Lasts for 1 hour." },
|
||||
{ id = 3829, name = "Frost Oil", mats = {"4x Khadgar's Whisker", "2x Wintersbite", "1x Leaded Vial"}, texture = "Interface\\Icons\\INV_Potion_20", description = "Adds a chance to inflict Frost damage on hit. Lasts 30 minutes." },
|
||||
{ id = 23122, name = "Consecrated Sharpening Stone", mats = {"Quest: 8x Necrotic Rune"}, texture = "Interface\\Icons\\inv_stone_sharpeningstone_02", description = "Increases melee attack power by 100 against undead for 30 minutes." },
|
||||
{ id = 18262, name = "Elemental Sharpening Stone", mats = {"2x Elemental Earth", "3x Dense Stone"}, texture = "Interface\\Icons\\INV_Stone_02", description = "Increases critical strike chance by 2% for 30 minutes." },
|
||||
{ id = 8928, name = "Instant Poison VI", mats = {"4x Dust of Deterioration", "1x Crystal Vial"}, texture = "Interface\\Icons\\Ability_Poisons", description = "20% chance to inflict 112 to 149 Nature damage." },
|
||||
{ id = 3776, name = "Crippling Poison II", mats = {"3x Essence of Agony", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_19", description = "30% chance to inflict the enemy, slowing their movement speed by 60% for 12 sec." },
|
||||
{ id = 20844, name = "Deadly Poison V", mats = {"7x Deathweed", "1x Crystal Vial"}, texture = "Interface\\Icons\\Ability_Rogue_DualWeild", description = "30% chance of poisoning the enemy for 136 Nature damage over 12 sec. Stacks up to 5 times on a single target." },
|
||||
{ id = 9186, name = "Mind-numbing Poison III", mats = {"2x Dust of Deterioration", "2x Essence of Agony", "1x Crystal Vial"}, texture = "Interface\\Icons\\Spell_Nature_NullifyDisease", description = "20% chance of poisoning the enemy, increasing their casting time by 60% for 14 sec." },
|
||||
{ id = 10922, name = "Wound Poison IV", mats = {"2x Essence of Agony", "2x Deathweed", "1x Crystal Vial"}, texture = "Interface\\Icons\\Ability_PoisonSting", description = "30% chance of poisoning the enemy, reducing all healing effects used on them by 135 for 15 sec. Stacks up to 5 times on a single target." },
|
||||
{ id = 12643, name = "Dense Weightstone", mats = {"1x Dense Stone", "1x Runecloth"}, texture = "Interface\\Icons\\INV_stone_weightstone_05", description = "Increase the damage of a blunt weapon by 8 for 30 minutes." }
|
||||
},
|
||||
["Combat Potions"] = {
|
||||
{ id = 13455, name = "Greater Stoneshield Potion", texture = "Interface\\Icons\\INV_Potion_69", description = "Increases armor by 2000 for 2 minutes." },
|
||||
{ id = 13442, name = "Mighty Rage Potion", texture = "Interface\\Icons\\INV_Potion_41", description = "Restores 45 to 75 rage and increases Strength by 60 for 20 seconds." },
|
||||
{ id = 20007, name = "Mageblood Potion", texture = "Interface\\Icons\\INV_Potion_45", description = "Regenerates 12 mana every 5 seconds for 1 hour." },
|
||||
{ id = 61423, name = "Dreamtonic", texture = "Interface\\Icons\\INV_Potion_10", description = "Increases spell damage by up to 35 for 20 minutes." }
|
||||
{ id = 13455, name = "Greater Stoneshield Potion", mats = {"3x Stonescale Oil", "1x Thorium Ore", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_69", description = "Increases armor by 2000 for 2 minutes." },
|
||||
{ id = 13442, name = "Mighty Rage Potion", mats = {"3x Gromsblood", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_41", description = "Restores 45 to 75 rage and increases Strength by 60 for 20 seconds." },
|
||||
{ id = 20007, name = "Mageblood Potion", mats = {"1x Dreamfoil", "2x Plaguebloom", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_45", description = "Regenerates 12 mana every 5 seconds for 1 hour." },
|
||||
{ id = 61423, name = "Dreamtonic", mats = {"Quest: 1x Small Dream Shard"}, texture = "Interface\\Icons\\INV_Potion_10", description = "Increases spell damage by up to 35 for 20 minutes." },
|
||||
{ id = 61181, name = "Potion of Quickness", mats = {"1x Gromsblood", "2x Mountain Silversage", "1x Swiftness Potion"}, texture = "Interface\\Icons\\inv_potion_08", description = "Increases haste by 5% for 30 sec." }
|
||||
},
|
||||
["Utility Items"] = {
|
||||
{ id = 20008, name = "Living Action Potion", texture = "Interface\\Icons\\INV_Potion_07", description = "Removes movement impairing effects and grants immunity to them for 5 seconds." },
|
||||
{ id = 3387, name = "Limited Invulnerability Potion", texture = "Interface\\Icons\\INV_Potion_62", description = "Makes you immune to physical attacks for 6 seconds." },
|
||||
{ id = 5634, name = "Free Action Potion", texture = "Interface\\Icons\\INV_Potion_04", description = "Grants immunity to stun and movement impairing effects for 30 seconds." },
|
||||
{ id = 61225, name = "Lucidity Potion", texture = "Interface\\Icons\\INV_Potion_36", description = "Become immune to Sleep, Polymorph, and Charm effects for 30 sec." },
|
||||
{ id = 9030, name = "Restorative Potion", texture = "Interface\\Icons\\INV_Potion_01", description = "Removes a magic, curse, poison, or disease effect every 5 seconds for 30 seconds." },
|
||||
{ id = 4390, name = "Iron Grenade", texture = "Interface\\Icons\\INV_Misc_Bomb_08", description = "Inflicts 132 to 219 Fire damage and stuns enemies in a 3-yard radius for 3 seconds." },
|
||||
{ id = 15993, name = "Thorium Grenade", texture = "Interface\\Icons\\INV_Misc_Bomb_08", description = "Inflicts 300 to 501 Fire damage and stuns enemies in a 3-yard radius for 3 seconds." },
|
||||
{ id = 10646, name = "Goblin Sapper Charge", texture = "Interface\\Icons\\spell_fire_selfdestruct", description = "Deals 450-751 Fire damage to enemies within 10 yards, also damages you." },
|
||||
{ id = 61675, name = "Nordanaar Herbal Tea", texture = "Interface\\Icons\\INV_Drink_Milk_05", description = "Restores 525 to 875 health and 810 to 1350 mana." },
|
||||
{ id = 13462, name = "Purification Potion", texture = "Interface\\Icons\\INV_Potion_31", description = "Attempts to remove one Curse, one Disease and one Poison from the Imbiber." },
|
||||
{ id = 13444, name = "Major Mana Potion", texture = "Interface\\Icons\\INV_Potion_76", description = "Restores 1350 to 2251 mana." },
|
||||
{ id = 13446, name = "Major Healing Potion", texture = "Interface\\Icons\\INV_Potion_54", description = "Restores 1050 to 1751 health." },
|
||||
{ id = 7676, name = "Thistle Tea", texture = "Interface\\Icons\\INV_Drink_Milk_05", description = "Instantly restores 100 energy." },
|
||||
{ id = 20004, name = "Major Troll's Blood Potion", texture = "Interface\\Icons\\inv_potion_80", description = "Regenerate 20 health every 5 sec for 1 hour." },
|
||||
{ id = 14530, name = "Heavy Runecloth Bandage", texture = "Interface\\Icons\\inv_misc_bandage_12", description = "Heals 2000 damage over 8 sec." },
|
||||
{ id = 13180, name = "Stratholme Holy Water", texture = "Interface\\Icons\\inv_potion_75", description = "Inflicts between 438 and 562 damage to Undead in a 10 yard radius." },
|
||||
{ id = 20008, name = "Living Action Potion", mats = {"2x Icecap", "2x Mountain Silversage", "2x Heart of the Wild", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_07", description = "Removes movement impairing effects and grants immunity to them for 5 seconds." },
|
||||
{ id = 3387, name = "Limited Invulnerability Potion", mats = {"2x Blindweed", "1x Ghost Mushroom", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_62", description = "Makes you immune to physical attacks for 6 seconds." },
|
||||
{ id = 5634, name = "Free Action Potion", mats = {"2x Blackmouth Oil", "1x Stranglekelp", "1x Leaded Vial"}, texture = "Interface\\Icons\\INV_Potion_04", description = "Grants immunity to stun and movement impairing effects for 30 seconds." },
|
||||
{ id = 61225, name = "Lucidity Potion", mats = {"1x Murloc Eye", "1x Dreamfoil", "1x Purple Lotus", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_36", description = "Become immune to Sleep, Polymorph, and Charm effects for 30 sec." },
|
||||
{ id = 9030, name = "Restorative Potion", mats = {"1x Elemental Earth", "1x Goldthorn", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_01", description = "Removes a magic, curse, poison, or disease effect every 5 seconds for 30 seconds." },
|
||||
{ id = 4390, name = "Iron Grenade", mats = {"1x Iron Bar", "1x Heavy Blasting Powder", "1x Silk Cloth"}, texture = "Interface\\Icons\\INV_Misc_Bomb_08", description = "Inflicts 132 to 219 Fire damage and stuns enemies in a 3-yard radius for 3 seconds." },
|
||||
{ id = 15993, name = "Thorium Grenade", mats = {"1x Thorium Widget", "3x Thorium Bar", "3x Dense Blasting Powder", "3x Runecloth"}, texture = "Interface\\Icons\\INV_Misc_Bomb_08", description = "Inflicts 300 to 501 Fire damage and stuns enemies in a 3-yard radius for 3 seconds." },
|
||||
{ id = 10646, name = "Goblin Sapper Charge", mats = {"1x Mageweave Cloth", "3x Solid Blasting Powder", "1x Unstable Trigger"}, texture = "Interface\\Icons\\spell_fire_selfdestruct", description = "Deals 450-751 Fire damage to enemies within 10 yards, also damages you." },
|
||||
{ id = 61675, name = "Nordanaar Herbal Tea", mats = {"Quest: 1x Small Dream Shard"}, texture = "Interface\\Icons\\INV_Drink_Milk_05", description = "Restores 525 to 875 health and 810 to 1350 mana." },
|
||||
{ id = 13462, name = "Purification Potion", mats = {"2x Icecap", "2x Plaguebloom", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_31", description = "Attempts to remove one Curse, one Disease and one Poison from the Imbiber." },
|
||||
{ id = 13444, name = "Major Mana Potion", mats = {"2x Dreamfoil", "2x Icecap", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_76", description = "Restores 1350 to 2251 mana." },
|
||||
{ id = 13446, name = "Major Healing Potion", mats = {"2x Golden Sansam", "1x Mountain Silversage", "1x Crystal Vial"}, texture = "Interface\\Icons\\INV_Potion_54", description = "Restores 1050 to 1751 health."},
|
||||
{ id = 7676, name = "Thistle Tea", mats = {"1x Swiftthistle", "1x Refreshing Spring Water"}, texture = "Interface\\Icons\\INV_Drink_Milk_05", description = "Instantly restores 100 energy." },
|
||||
{ id = 20004, name = "Major Troll's Blood Potion", mats = {"1x Gromsblood", "2x Plaguebloom", "1x Crystal Vial"}, texture = "Interface\\Icons\\inv_potion_80", description = "Regenerate 20 health every 5 sec for 1 hour." },
|
||||
{ id = 14530, name = "Heavy Runecloth Bandage", mats = {"2x Runecloth"}, texture = "Interface\\Icons\\inv_misc_bandage_12", description = "Heals 2000 damage over 8 sec." },
|
||||
{ id = 13180, name = "Stratholme Holy Water", mats = {"Looted from Crates in Stratholme"}, texture = "Interface\\Icons\\inv_potion_75", description = "Inflicts between 438 and 562 damage to Undead in a 10 yard radius." }
|
||||
|
||||
},
|
||||
["Food Buffs"] = {
|
||||
{ id = 21023, name = "Dirge's Kickin' Chimaerok Chops", texture = "Interface\\Icons\\INV_Misc_Food_65", description = "Increases Strength by 25 for 1 hour." },
|
||||
{ id = 18254, name = "Runn Tum Tuber Surprise", texture = "Interface\\Icons\\INV_Misc_Food_63", description = "Increases Intellect by 10 for 1 hour." },
|
||||
{ id = 13931, name = "Nightfin Soup", texture = "Interface\\Icons\\INV_Drink_17", description = "Restores 8 mana every 5 seconds for 1 hour." },
|
||||
{ id = 20452, name = "Smoked Desert Dumplings", texture = "Interface\\Icons\\INV_Misc_Food_64", description = "Increases Strength by 20 for 1 hour." },
|
||||
{ id = 51711, name = "Sweet Mountain Berry (Agility)", texture = "Interface\\Icons\\INV_Misc_Food_40", description = "Increases Agility by 10 for 1 hour." },
|
||||
{ id = 51714, name = "Sweet Mountain Berry (Stamina)", texture = "Interface\\Icons\\INV_Misc_Food_40", description = "Increases Stamina by 10 for 1 hour." },
|
||||
{ id = 51717, name = "Hardened Mushroom", texture = "Interface\\Icons\\INV_Mushroom_11", description = "Increases 25 Stamina for 15 minutes." },
|
||||
{ id = 51720, name = "Power Mushroom", texture = "Interface\\Icons\\INV_Mushroom_11", description = "Increases 20 Strength for 15 minutes." },
|
||||
{ id = 21151, name = "Rumsey Rum Black Label", texture = "Interface\\Icons\\INV_Drink_04", description = "Increases Stamina by 15 for 15 minutes." },
|
||||
{ id = 12218, name = "Monster Omelet", texture = "Interface\\Icons\\INV_Misc_Food_06", description = "Increases 12 Stamina and 12 Spirit for 15 minutes." },
|
||||
{ id = 60977, name = "Danonzo's Tel'Abim Delight", texture = "Interface\\Icons\\INV_Misc_Food_06", description = "Gain 22 spell damage for 15 minutes." },
|
||||
{ id = 60978, name = "Danonzo's Tel'Abim Medley", texture = "Interface\\Icons\\INV_Misc_Food_07", description = "Increase haste by 2% for 15 minutes." },
|
||||
{ id = 60976, name = "Danonzo's Tel'Abim Surprise", texture = "Interface\\Icons\\INV_Misc_Food_09", description = "Gain 45 Ranged Attack Power for 15 minutes." },
|
||||
{ id = 84041, name = "Gilneas Hot Stew", texture = "Interface\\Icons\\inv_drink_19", description = "Gain 12 spell damage for 15 minutes." },
|
||||
{ id = 21217, name = "Sagefish Delight", texture = "Interface\\Icons\\inv_misc_fish_21", description = "Gain 6 mana every 5 seconds for 15 minutes." }
|
||||
{ id = 21023, name = "Dirge's Kickin' Chimaerok Chops", mats = {"1x Hot Spices", "1x Goblin Rocket Fuel", "1x Deeprock Salt", "1x Chimaerok Tenderloin"}, texture = "Interface\\Icons\\INV_Misc_Food_65", description = "Increases Strength by 25 for 1 hour." },
|
||||
{ id = 18254, name = "Runn Tum Tuber Surprise", mats = {"1x Runn Tum Tuber", "1 Soothing Spices"}, texture = "Interface\\Icons\\INV_Misc_Food_63", description = "Increases Intellect by 10 for 1 hour." },
|
||||
{ id = 13931, name = "Nightfin Soup", mats = {"1x Raw Nightfin Snapper", "1x Refreshing Spring Water"}, texture = "Interface\\Icons\\INV_Drink_17", description = "Restores 8 mana every 5 seconds for 1 hour." },
|
||||
{ id = 20452, name = "Smoked Desert Dumplings", mats = {"1x Sandworm Meat", "1x Soothing Spices"}, texture = "Interface\\Icons\\INV_Misc_Food_64", description = "Increases Strength by 20 for 1 hour." },
|
||||
{ id = 51711, name = "Sweet Mountain Berry (Agility)", mats = {"Gardening: Mountain Berry Bush Seeds"}, texture = "Interface\\Icons\\INV_Misc_Food_40", description = "Increases Agility by 10 for 1 hour." },
|
||||
{ id = 51714, name = "Sweet Mountain Berry (Stamina)", mats = {"Gardening: Mountain Berry Bush Seeds"}, texture = "Interface\\Icons\\INV_Misc_Food_40", description = "Increases Stamina by 10 for 1 hour." },
|
||||
{ id = 51717, name = "Hardened Mushroom", mats = {"Gardening: Magic Mushroom Spores"}, texture = "Interface\\Icons\\INV_Mushroom_11", description = "Increases 25 Stamina for 15 minutes." },
|
||||
{ id = 51720, name = "Power Mushroom", mats = {"Gardening: Magic Mushroom Spores"}, texture = "Interface\\Icons\\INV_Mushroom_11", description = "Increases 20 Strength for 15 minutes." },
|
||||
{ id = 21151, name = "Rumsey Rum Black Label", mats = {"Diverse Attainable"}, texture = "Interface\\Icons\\INV_Drink_04", description = "Increases Stamina by 15 for 15 minutes." },
|
||||
{ id = 12218, name = "Monster Omelet", mats = {"1x Giant Egg", "2x Soothing Spices"}, texture = "Interface\\Icons\\INV_Misc_Food_06", description = "Increases 12 Stamina and 12 Spirit for 15 minutes." },
|
||||
{ id = 60977, name = "Danonzo's Tel'Abim Delight", mats = {"1x Gargantuan Tel'Abim Banana", "1x Soothing Spices", "1x Icecap"}, texture = "Interface\\Icons\\INV_Misc_Food_06", description = "Gain 22 spell damage for 15 minutes." },
|
||||
{ id = 60978, name = "Danonzo's Tel'Abim Medley", mats = {"1x Gargantuan Tel'Abim Banana", "1x Soothing Spices", "2x Golden Sansam"}, texture = "Interface\\Icons\\INV_Misc_Food_07", description = "Increase haste by 2% for 15 minutes." },
|
||||
{ id = 60976, name = "Danonzo's Tel'Abim Surprise", mats = {"1x Gargantuan Tel'Abim Banana", "1x Soothing Spices", "1x Heart of the Wild"}, texture = "Interface\\Icons\\INV_Misc_Food_09", description = "Gain 45 Ranged Attack Power for 15 minutes." },
|
||||
{ id = 84041, name = "Gilneas Hot Stew", mats = {"1x Red Wolf Meat", "1x White Spider Meat", "1x Refreshing Spring Water"}, texture = "Interface\\Icons\\inv_drink_19", description = "Gain 12 spell damage for 15 minutes." },
|
||||
{ id = 21217, name = "Sagefish Delight", mats = {"1x Raw Greater Sagefish", "1x Hot Spices"}, texture = "Interface\\Icons\\inv_misc_fish_21", description = "Gain 6 mana every 5 seconds for 15 minutes." }
|
||||
},
|
||||
["Special Buffs"] = {
|
||||
{ id = 12451, name = "Juju Power", texture = "Interface\\Icons\\INV_Misc_MonsterScales_11", description = "Increases Strength by 30 for 30 minutes." },
|
||||
{ id = 12460, name = "Juju Might", texture = "Interface\\Icons\\INV_Misc_MonsterScales_07", description = "Increases Attack Power by 40 for 30 minutes." },
|
||||
{ id = 12455, name = "Juju Ember", texture = "Interface\\Icons\\INV_Misc_MonsterScales_15", description = "Increases Fire Resistance by 15 for 30 minutes." },
|
||||
{ id = 12457, name = "Juju Chill", texture = "Interface\\Icons\\INV_Misc_MonsterScales_09", description = "Increase Frost resistance by 15 for 10 minutes." },
|
||||
{ id = 12820, name = "Winterfall Firewater", texture = "Interface\\Icons\\INV_Potion_92", description = "Increases Attack Power by 35 for 20 minutes." },
|
||||
{ id = 20079, name = "Spirit of Zanza", texture = "Interface\\Icons\\INV_Potion_30", description = "Increases the player's Spirit by 50 and Stamina by 50 for 2 hours." },
|
||||
{ id = 20081, name = "Swiftness of Zanza", texture = "Interface\\Icons\\inv_potion_31", description = "Increases the player's run speed by 20% for 2 hours." },
|
||||
{ id = 8412, name = "Ground Scorpok Assay", texture = "Interface\\Icons\\inv_misc_dust_02", description = "Increases Agility by 25 for 1 hour." },
|
||||
{ id = 8410, name = "R.O.I.D.S.", texture = "Interface\\Icons\\inv_stone_15", description = "Increases Strength by 25 for 1 hour." },
|
||||
{ id = 8423, name = "Cerebral Cortex Compound", texture = "Interface\\Icons\\inv_potion_32", description = " Increases Intellect by 25 for 1 hour." }
|
||||
{ id = 12451, name = "Juju Power", mats = {"Quest: 3x Winterfall E'ko (Kill Furbolgs)"}, texture = "Interface\\Icons\\INV_Misc_MonsterScales_11", description = "Increases Strength by 30 for 30 minutes." },
|
||||
{ id = 12460, name = "Juju Might", mats = {"Quest: 3x Frostmaul E'ko (Kill Frostmaul Giants)"}, texture = "Interface\\Icons\\INV_Misc_MonsterScales_07", description = "Increases Attack Power by 40 for 30 minutes." },
|
||||
{ id = 12455, name = "Juju Ember", mats = {"Quest: 3x Shardtooth E'ko (Kill Bears)"}, texture = "Interface\\Icons\\INV_Misc_MonsterScales_15", description = "Increases Fire Resistance by 15 for 30 minutes." },
|
||||
{ id = 12457, name = "Juju Chill", mats = {"Quest: 3x Chillwind E'ko (Kill Flying Chillwinds)"}, texture = "Interface\\Icons\\INV_Misc_MonsterScales_09", description = "Increase Frost resistance by 15 for 10 minutes." },
|
||||
{ id = 12820, name = "Winterfall Firewater", mats = {"Drops from Furbolgs in Winterspring"}, texture = "Interface\\Icons\\INV_Potion_92", description = "Increases Attack Power by 35 for 20 minutes." },
|
||||
{ id = 20079, name = "Spirit of Zanza", mats = {"Quest: 1x Zandalar Honor Token"}, texture = "Interface\\Icons\\INV_Potion_30", description = "Increases the player's Spirit by 50 and Stamina by 50 for 2 hours." },
|
||||
{ id = 20081, name = "Swiftness of Zanza", mats = {"Quest: 1x Zandalar Honor Token"}, texture = "Interface\\Icons\\inv_potion_31", description = "Increases the player's run speed by 20% for 2 hours." },
|
||||
{ id = 8412, name = "Ground Scorpok Assay", mats = {"Quest: 1x Blasted Boar Lung", "2x Vulture Gizzard", "3x Scorpok Pincer"}, texture = "Interface\\Icons\\inv_misc_dust_02", description = "Increases Agility by 25 for 1 hour." },
|
||||
{ id = 8410, name = "R.O.I.D.S.", mats = {"Quest: 1x Scorpok Pincer", "2x Blasted Boar Lung", "3x Snickerfang Jowl"}, texture = "Interface\\Icons\\inv_stone_15", description = "Increases Strength by 25 for 1 hour." },
|
||||
{ id = 8423, name = "Cerebral Cortex Compound", mats = {"Quest: 10x Basilisk Brain", "2x Vulture Gizzard"}, texture = "Interface\\Icons\\inv_potion_32", description = " Increases Intellect by 25 for 1 hour." }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-- Initialize tables
|
||||
consumablesList = {}
|
||||
consumablesNameToID = {}
|
||||
consumablesTexture = {}
|
||||
consumablesDescription = {}
|
||||
consumablesMats = {}
|
||||
|
||||
|
||||
-- Initialize tables
|
||||
consumablesList = {}
|
||||
consumablesNameToID = {}
|
||||
consumablesTexture = {}
|
||||
consumablesDescription = {}
|
||||
|
||||
-- Populate consumablesList and other lookup tables
|
||||
for categoryName, consumables in pairs(consumablesCategories) do
|
||||
for _, consumable in ipairs(consumables) do
|
||||
consumablesList[consumable.id] = consumable.name
|
||||
consumablesNameToID[consumable.name] = consumable.id
|
||||
consumablesTexture[consumable.id] = consumable.texture
|
||||
consumablesDescription[consumable.id] = consumable.description
|
||||
end
|
||||
end
|
||||
|
||||
-- Populate consumablesList and other lookup tables
|
||||
for categoryName, consumables in pairs(consumablesCategories) do
|
||||
for _, consumable in ipairs(consumables) do
|
||||
consumablesList[consumable.id] = consumable.name
|
||||
consumablesNameToID[consumable.name] = consumable.id
|
||||
consumablesTexture[consumable.id] = consumable.texture
|
||||
consumablesDescription[consumable.id] = consumable.description
|
||||
consumablesMats[consumable.id] = consumable.mats or {}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,798 @@
|
||||
----------------------------------------------------------------------------------
|
||||
--
|
||||
-- LibCompress.lua
|
||||
--
|
||||
-- Authors: jjsheets and Galmok of European Stormrage (Horde)
|
||||
-- Email : sheets.jeff@gmail.com and galmok@gmail.com
|
||||
-- Licence: GPL version 2 (General Public License)
|
||||
----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
local MAJOR, MINOR = "LibCompress", 3
|
||||
|
||||
local LibCompress,oldminor = LibStub:NewLibrary(MAJOR, MINOR)
|
||||
|
||||
if not LibCompress then return end
|
||||
|
||||
-- list of codecs in this file:
|
||||
-- \000 - Never used
|
||||
-- \001 - Uncompressed
|
||||
-- \002 - LZW
|
||||
-- \003 - Huffman
|
||||
|
||||
|
||||
-- local is faster than global
|
||||
local table_insert = table.insert
|
||||
local table_remove = table.remove
|
||||
local table_concat = table.concat
|
||||
local string_char = string.char
|
||||
local string_byte = string.byte
|
||||
local string_len = string.len
|
||||
local string_sub = string.sub
|
||||
local unpack = unpack
|
||||
local pairs = pairs
|
||||
local math_modf = math.modf
|
||||
local bit_band = bit.band
|
||||
local bit_bor = bit.bor
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
local function onUpdate(frame, elapsed)
|
||||
timeout = timeout - arg1
|
||||
if timeout < 0 then
|
||||
this:Hide()
|
||||
cleanup()
|
||||
end
|
||||
end
|
||||
|
||||
LibCompress.frame = LibCompress.frame or CreateFrame("frame", nil, UIParent) -- reuse the old frame
|
||||
LibCompress.frame:SetScript("OnUpdate", onUpdate)
|
||||
LibCompress.frame:Hide()
|
||||
|
||||
local function setCleanupTables(s1, s2)
|
||||
timeout = 15 -- empty tables after 15 seconds
|
||||
if not LibCompress.frame:IsShown() then
|
||||
LibCompress.frame:Show()
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- LZW codec
|
||||
-- implemented by sheets.jeff@gmail.com
|
||||
|
||||
-- encode is used to uniquely encode a number into a sequence of bytes that can be decoded using decode()
|
||||
-- the bytes returned by this do not contain "\000"
|
||||
local bytes = {}
|
||||
local function encode(x)
|
||||
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
|
||||
x, xmod = math_modf(x/255.0)
|
||||
xmod = xmod * 255.0
|
||||
bytes[getn(bytes) + 1] = xmod
|
||||
end
|
||||
if getn(bytes) == 1 and bytes[1] > 0 and bytes[1] < 250 then
|
||||
return string_char(bytes[1])
|
||||
else
|
||||
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)
|
||||
i = i or 1
|
||||
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
|
||||
end
|
||||
return r, a + 1
|
||||
else
|
||||
return a, 1
|
||||
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.
|
||||
-- the returned string will only contain "\000" characters in rare circumstances, and will contain none if the
|
||||
-- source string has none.
|
||||
local dict = {}
|
||||
function LibCompress:CompressLZW(uncompressed)
|
||||
if type(uncompressed) == "string" then
|
||||
local dict_size = 256
|
||||
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, 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
|
||||
local r = encode(dict[w])
|
||||
ressize = ressize + strlen(r)
|
||||
result[getn(result) + 1] = r
|
||||
w = c
|
||||
end
|
||||
end
|
||||
if w then
|
||||
local r = encode(dict[w])
|
||||
ressize = ressize + strlen(r)
|
||||
result[getn(result) + 1] = r
|
||||
end
|
||||
if (strlen(uncompressed)+1) > ressize then
|
||||
return table_concat(result, "")
|
||||
else
|
||||
return "\002"..uncompressed
|
||||
end
|
||||
else
|
||||
return nil, "Can only compress strings"
|
||||
end
|
||||
end
|
||||
|
||||
-- if the passed string is a compressed string, this will decompress it and return the decompressed string.
|
||||
-- Otherwise it return an error message
|
||||
-- compressed strings are marked by beginning with "\002"
|
||||
function LibCompress:DecompressLZW(compressed)
|
||||
if type(compressed) == "string" then
|
||||
if string_sub(compressed,1,1) ~= "\002" then
|
||||
return nil, "Can only decompress LZW compressed data ("..tostring(string_sub(compressed,1,1))..")"
|
||||
end
|
||||
compressed = string_sub(compressed,2)-- ?
|
||||
local dict_size = 256
|
||||
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)
|
||||
t = t + delta
|
||||
result[getn(result)+1] = dict[k]
|
||||
local w = dict[k]
|
||||
local entry
|
||||
while t <= strlen(compressed) do
|
||||
k, delta = decode(compressed,t)
|
||||
t = t + delta
|
||||
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
|
||||
return table_concat(result)
|
||||
else
|
||||
return nil, "Can only uncompress strings"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Huffman codec
|
||||
-- implemented by Galmok of European Stormrage (Horde), galmok@gmail.com
|
||||
|
||||
local function addCode(tree, bcode,len)
|
||||
if tree then
|
||||
tree.bcode = bcode;
|
||||
tree.blength = len;
|
||||
if tree.c1 then
|
||||
addCode(tree.c1, bit_bor(bcode, bit_lshift(1,len)), len+1)
|
||||
end
|
||||
if tree.c2 then
|
||||
addCode(tree.c2, bcode, len+1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
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, len)
|
||||
remainder = remainder + bit_lshift(code, remainder_length)
|
||||
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)
|
||||
remainder_length = remainder_length -8
|
||||
end
|
||||
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 not type(uncompressed)=="string" then
|
||||
return nil, "Can only compress strings"
|
||||
end
|
||||
|
||||
-- make histogram
|
||||
local hist = {}
|
||||
local n = 0
|
||||
-- dont have to use all datat to make the histogram
|
||||
local uncompressed_size = string_len(uncompressed)
|
||||
local c;
|
||||
for i = 1, uncompressed_size do
|
||||
c = string_byte(uncompressed, i)
|
||||
hist[c] = (hist[c] or 0) + 1
|
||||
end
|
||||
|
||||
--Start with as many leaves as there are symbols.
|
||||
local leafs = {}
|
||||
local leaf;
|
||||
local symbols = {}
|
||||
for symbol, weight in pairs(hist) do
|
||||
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)
|
||||
|
||||
local nLeafs = getn(leafs)
|
||||
|
||||
-- create tree
|
||||
local huff = {}
|
||||
--While there is more than one node in the queues:
|
||||
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
|
||||
li, leaf1 = next(leafs)
|
||||
table_remove(leafs, li)
|
||||
elseif not next(leafs) then
|
||||
hi, leaf1 = next(huff)
|
||||
table_remove(huff, hi)
|
||||
else
|
||||
li, l = next(leafs);
|
||||
hi, h = next(huff);
|
||||
if l.weight<=h.weight then
|
||||
leaf1 = l;
|
||||
table_remove(leafs, li)
|
||||
else
|
||||
leaf1 = h;
|
||||
table_remove(huff, hi)
|
||||
end
|
||||
end
|
||||
-- Dequeue second
|
||||
if not next(huff) then
|
||||
li, leaf2 = next(leafs)
|
||||
table_remove(leafs, li)
|
||||
elseif not next(leafs) then
|
||||
hi, leaf2 = next(huff)
|
||||
table_remove(huff, hi)
|
||||
else
|
||||
li, l = next(leafs);
|
||||
hi, h = next(huff);
|
||||
if l.weight<=h.weight then
|
||||
leaf2 = l;
|
||||
table_remove(leafs, li)
|
||||
else
|
||||
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 }
|
||||
table_insert(huff,newNode)
|
||||
end
|
||||
if getn(leafs)>0 then
|
||||
li, l = next(leafs)
|
||||
table_insert(huff, l)
|
||||
table_remove(leafs, li)
|
||||
end
|
||||
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);
|
||||
if huff then
|
||||
huff.bcode = 0
|
||||
huff.blength = 1
|
||||
end
|
||||
|
||||
-- READING
|
||||
-- bitfield = 0
|
||||
-- bitfield_len = 0
|
||||
-- read byte1
|
||||
-- bitfield = bitfield + bit_lshift(byte1, bitfield_len)
|
||||
-- bitfield_len = bitfield_len + 8
|
||||
-- read byte2
|
||||
-- bitfield = bitfield + bit_lshift(byte2, bitfield_len)
|
||||
-- bitfield_len = bitfield_len + 8
|
||||
-- (use 5 bits)
|
||||
-- word = bit_band( bitfield, bit_lshift(1,5)-1)
|
||||
-- bitfield = bit_rshift( bitfield, 5)
|
||||
-- bitfield_len = bitfield_len - 5
|
||||
-- read byte3
|
||||
-- bitfield = bitfield + bit_lshift(byte3, bitfield_len)
|
||||
-- bitfield_len = bitfield_len + 8
|
||||
|
||||
-- WRITING
|
||||
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
|
||||
compressed[1] = "\003"
|
||||
|
||||
-- Header: byte 0=#leafs, byte 1-3=size of uncompressed data
|
||||
-- max 2^24 bytes
|
||||
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
|
||||
for symbol, leaf in pairs(symbols) do
|
||||
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, 3, 2);
|
||||
end
|
||||
|
||||
-- create huffman code
|
||||
local large_compressed = tables.Huffman_large_compressed
|
||||
local large_compressed_size = 0
|
||||
local ulimit
|
||||
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 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 (strlen(uncompressed)+1) <= strlen(compressed_string) then
|
||||
return "\001"..uncompressed
|
||||
end
|
||||
|
||||
setCleanupTables("Huffman_compressed", "Huffman_large_compressed")
|
||||
return compressed_string
|
||||
end
|
||||
|
||||
-- lookuptable (cached between calls)
|
||||
local lshiftMask = {}
|
||||
setmetatable(lshiftMask, {
|
||||
__index = function (t, k)
|
||||
local v = bit_lshift(1, k)
|
||||
rawset(t, k, v)
|
||||
return v
|
||||
end
|
||||
})
|
||||
|
||||
-- lookuptable (cached between calls)
|
||||
local lshiftMinusOneMask = {}
|
||||
setmetatable(lshiftMinusOneMask, {
|
||||
__index = function (t, k)
|
||||
local v = bit_lshift(1, k)-1
|
||||
rawset(t, k, v)
|
||||
return v
|
||||
end
|
||||
})
|
||||
|
||||
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 i = 0
|
||||
while i < code_len do
|
||||
b = bit_band( code, lshiftMask[i])
|
||||
if not (b==0) then
|
||||
unescaped_code = bit_bor(unescaped_code, lshiftMask[l])
|
||||
i = i + 1
|
||||
end
|
||||
i = i + 1
|
||||
l = l + 1
|
||||
end
|
||||
return unescaped_code, l
|
||||
end
|
||||
|
||||
tables.Huffman_uncompressed = {}
|
||||
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 type(compressed)~="string" or compressed == nil then
|
||||
if type(compressed)=="table" then
|
||||
return compressed
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local compressed_size = strlen(compressed)
|
||||
--decode header
|
||||
local info_byte = string_byte(compressed)
|
||||
-- is data compressed
|
||||
if info_byte==1 then
|
||||
return string_sub(compressed,2) --return uncompressed data
|
||||
end
|
||||
if not (info_byte==3) then
|
||||
return nil, "Can only decompress Huffman compressed data ("..tostring(info_byte)..")"
|
||||
end
|
||||
|
||||
local num_symbols = string_byte(string_sub(compressed, 2, 2)) + 1
|
||||
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 "";
|
||||
end
|
||||
|
||||
-- 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)
|
||||
local v = {}
|
||||
rawset(t, k, v)
|
||||
return v
|
||||
end
|
||||
})
|
||||
|
||||
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 n<num_symbols do
|
||||
if i>compressed_size then
|
||||
return nil, "Cannot decode map"
|
||||
end
|
||||
|
||||
c = string_byte(compressed, i)
|
||||
bitfield = bit_bor(bitfield, bit_lshift(c, bitfield_len))
|
||||
bitfield_len = bitfield_len + 8
|
||||
|
||||
if state == 0 then
|
||||
symbol = bit_band(bitfield, 255)
|
||||
bitfield = bit_rshift(bitfield, 8)
|
||||
bitfield_len = bitfield_len -8
|
||||
state = 1 -- search for code now
|
||||
else
|
||||
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
|
||||
--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
|
||||
end
|
||||
|
||||
-- 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, {
|
||||
__index = function (t, k)
|
||||
return mt
|
||||
end
|
||||
})
|
||||
|
||||
local uncompressed = tables.Huffman_uncompressed
|
||||
local large_uncompressed = tables.Huffman_large_uncompressed
|
||||
local uncompressed_size = 0
|
||||
local large_uncompressed_size = 0
|
||||
local test_code
|
||||
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
|
||||
while true do
|
||||
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
|
||||
dec_size = dec_size + 1
|
||||
|
||||
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(strlen(compressed))
|
||||
end
|
||||
end
|
||||
else
|
||||
c = string_byte(compressed, i)
|
||||
bitfield = bitfield + bit_lshift(c or 0, bitfield_len)
|
||||
bitfield_len = bitfield_len + 8
|
||||
i = i + 1
|
||||
if i > temp_limit then
|
||||
if i > compressed_size then
|
||||
break;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
setCleanupTables("Huffman_uncompressed", "Huffman_large_uncompressed")
|
||||
return table_concat(large_uncompressed, "", 1, large_uncompressed_size)..table_concat(uncompressed, "", 1, uncompressed_size)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Generic codec interface
|
||||
|
||||
function LibCompress:DecompressUncompressed(data)
|
||||
if type(data)~="string" then
|
||||
return nil, "Can only handle strings"
|
||||
end
|
||||
if string.byte(data) ~= 1 then
|
||||
return nil, "Can only handle uncompressed data"
|
||||
end
|
||||
return string_sub(data, 2)
|
||||
end
|
||||
|
||||
local compression_methods = {
|
||||
[2] = LibCompress.CompressLZW,
|
||||
[3] = LibCompress.CompressHuffman
|
||||
}
|
||||
|
||||
local decompression_methods = {
|
||||
[1] = LibCompress.DecompressUncompressed,
|
||||
[2] = LibCompress.DecompressLZW,
|
||||
[3] = LibCompress.DecompressHuffman
|
||||
}
|
||||
|
||||
-- try all compression codecs and return best result
|
||||
function LibCompress:Compress(data)
|
||||
local method = next(compression_methods)
|
||||
local result = compression_methods[method](self, data);
|
||||
local n;
|
||||
method = next(compression_methods, method)
|
||||
while method do
|
||||
n = compression_methods[method](self, data)
|
||||
if getn(n) < getn(result) then
|
||||
result = n
|
||||
end
|
||||
method = next(compression_methods, method)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function LibCompress:Decompress(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
|
||||
@@ -0,0 +1,33 @@
|
||||
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
|
||||
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
|
||||
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
|
||||
local _G = getfenv()
|
||||
local strfind, strfmt = string.find, string.format
|
||||
local LibStub = _G[LIBSTUB_MAJOR]
|
||||
|
||||
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
|
||||
LibStub = LibStub or { libs = {}, minors = {} }
|
||||
_G[LIBSTUB_MAJOR] = LibStub
|
||||
LibStub.minor = LIBSTUB_MINOR
|
||||
|
||||
function LibStub:NewLibrary(major, minor)
|
||||
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
|
||||
local _,_,num = strfind(minor, "(%d+)")
|
||||
minor = assert(tonumber(num), "Minor version must either be a number or contain a number.")
|
||||
|
||||
local oldminor = self.minors[major]
|
||||
if oldminor and oldminor >= minor then return nil end
|
||||
self.minors[major], self.libs[major] = minor, self.libs[major] or {}
|
||||
return self.libs[major], oldminor
|
||||
end
|
||||
|
||||
function LibStub:GetLibrary(major, silent)
|
||||
if not self.libs[major] and not silent then
|
||||
error(strfmt("Cannot find a library instance of %q.", tostring(major)), 2)
|
||||
end
|
||||
return self.libs[major], self.minors[major]
|
||||
end
|
||||
|
||||
function LibStub:IterateLibraries() return pairs(self.libs) end
|
||||
setmetatable(LibStub, { __call = LibStub.GetLibrary })
|
||||
end
|
||||
@@ -0,0 +1,435 @@
|
||||
--[[
|
||||
|
||||
* 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
|
||||
Reference in New Issue
Block a user