diff --git a/.travis.yml b/.travis.yml index 41f69940..be4fdb89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,12 @@ +branches: + - develop + - master + osx_image: xcode8 language: objective-c xcode_project: SWCompression.xcodeproj xcode_scheme: SWCompression +script: +- xcodebuild clean build test -project SWCompression.xcodeproj -scheme SWCompression +notifications: + email: false diff --git a/CHANGELOG.md b/CHANGELOG.md index efe4a794..b1ce87f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +v1.0.1 +---------------- +Fixed several crashes caused by mistyping and shift 'overflow' (when it becomes greater than 15). + v1.0.0 ---------------- Initial release, which features support for Deflate, Gzip and Zlib decompression. diff --git a/README.md b/README.md index d83b2d2d..43ddf4c2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # SWCompression -[![GitHub license](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://raw.githubusercontent.com/tsolomko/SWCompression/master/LICENSE) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![GitHub license](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://raw.githubusercontent.com/tsolomko/SWCompression/master/LICENSE) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Build Status](https://travis-ci.org/tsolomko/SWCompression.svg?branch=develop)](https://travis-ci.org/tsolomko/SWCompression) A framework which contains native (*written in Swift*) implementations of some compression algorithms. diff --git a/SWCompression.podspec b/SWCompression.podspec index 4664e770..558de0f0 100644 --- a/SWCompression.podspec +++ b/SWCompression.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "SWCompression" - s.version = "1.0.0" + s.version = "1.0.1" s.summary = "Framework with implementations in Swift of different (de)compression algorithms" s.description = <<-DESC @@ -17,6 +17,11 @@ Pod::Spec.new do |s| s.author = { "Timofey Solomko" => "tsolomko@gmail.com" } + s.ios.deployment_target = "10.1" + s.osx.deployment_target = "10.12" + s.tvos.deployment_target = "10.0" + s.watchos.deployment_target = "3.1" + s.source = { :git => "https://github.com/tsolomko/SWCompression.git", :tag => "v#{s.version}" } s.source_files = "Sources" diff --git a/SWCompression.xcodeproj/project.pbxproj b/SWCompression.xcodeproj/project.pbxproj index 66362314..f68c0aec 100644 --- a/SWCompression.xcodeproj/project.pbxproj +++ b/SWCompression.xcodeproj/project.pbxproj @@ -42,6 +42,10 @@ 065569791DC65C2B00A47E66 /* HuffmanTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06E961C91DBD5FA1008C47F2 /* HuffmanTable.swift */; }; 0655697A1DC65C2B00A47E66 /* HuffmanLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06AC9B0C1DC1027900A5066B /* HuffmanLength.swift */; }; 0655697B1DC65C2B00A47E66 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 061FCE301DBCC8BF0052F7BE /* Extensions.swift */; }; + 0655697D1DC6963F00A47E66 /* secondtest.zlib in Resources */ = {isa = PBXBuildFile; fileRef = 0655697C1DC6963F00A47E66 /* secondtest.zlib */; }; + 0655697F1DC6966300A47E66 /* secondtest.zlib.answer in Resources */ = {isa = PBXBuildFile; fileRef = 0655697E1DC6966300A47E66 /* secondtest.zlib.answer */; }; + 065569811DC69DD500A47E66 /* thirdtest.zlib.answer in Resources */ = {isa = PBXBuildFile; fileRef = 065569801DC69DD500A47E66 /* thirdtest.zlib.answer */; }; + 065569831DC69DDE00A47E66 /* thirdtest.zlib in Resources */ = {isa = PBXBuildFile; fileRef = 065569821DC69DDE00A47E66 /* thirdtest.zlib */; }; 06AC9B0D1DC1027900A5066B /* HuffmanLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06AC9B0C1DC1027900A5066B /* HuffmanLength.swift */; }; 06BE1AD21DB410F100EE0F59 /* SWCompression.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06BE1AC81DB410F100EE0F59 /* SWCompression.framework */; }; 06BE1AD71DB410F100EE0F59 /* SWCompressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BE1AD61DB410F100EE0F59 /* SWCompressionTests.swift */; }; @@ -79,6 +83,10 @@ 065569611DC65B1B00A47E66 /* Info-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; 065569621DC65B1B00A47E66 /* Info-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-tvOS.plist"; sourceTree = ""; }; 065569631DC65B1B00A47E66 /* Info-watchOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-watchOS.plist"; sourceTree = ""; }; + 0655697C1DC6963F00A47E66 /* secondtest.zlib */ = {isa = PBXFileReference; lastKnownFileType = file; path = secondtest.zlib; sourceTree = ""; }; + 0655697E1DC6966300A47E66 /* secondtest.zlib.answer */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = secondtest.zlib.answer; sourceTree = ""; }; + 065569801DC69DD500A47E66 /* thirdtest.zlib.answer */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = thirdtest.zlib.answer; sourceTree = ""; }; + 065569821DC69DDE00A47E66 /* thirdtest.zlib */ = {isa = PBXFileReference; lastKnownFileType = file; path = thirdtest.zlib; sourceTree = ""; }; 06AC9B0C1DC1027900A5066B /* HuffmanLength.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HuffmanLength.swift; sourceTree = ""; }; 06BE1AC81DB410F100EE0F59 /* SWCompression.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SWCompression.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 06BE1ACB1DB410F100EE0F59 /* SWCompression.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SWCompression.h; sourceTree = ""; }; @@ -141,6 +149,10 @@ 0644925B1DC6121800F10981 /* helloworld.txt.zlib */, 06E63DB21DC3B8BD00D3A7C8 /* secondtest.txt */, 06E63DB31DC3B8BD00D3A7C8 /* secondtest.txt.gz */, + 0655697C1DC6963F00A47E66 /* secondtest.zlib */, + 0655697E1DC6966300A47E66 /* secondtest.zlib.answer */, + 065569821DC69DDE00A47E66 /* thirdtest.zlib */, + 065569801DC69DD500A47E66 /* thirdtest.zlib.answer */, ); path = "Test Files"; sourceTree = ""; @@ -418,10 +430,14 @@ files = ( 063364DF1DC51D33007E313F /* empty.txt.gz in Resources */, 061FCE2B1DBCC6070052F7BE /* helloworld.txt.gz in Resources */, + 0655697D1DC6963F00A47E66 /* secondtest.zlib in Resources */, 06E63DB41DC3B8BD00D3A7C8 /* secondtest.txt in Resources */, + 0655697F1DC6966300A47E66 /* secondtest.zlib.answer in Resources */, 0644925D1DC6121800F10981 /* helloworld.txt.zlib in Resources */, 06E63DB51DC3B8BD00D3A7C8 /* secondtest.txt.gz in Resources */, + 065569811DC69DD500A47E66 /* thirdtest.zlib.answer in Resources */, 061FCE2A1DBCC6070052F7BE /* helloworld.txt in Resources */, + 065569831DC69DDE00A47E66 /* thirdtest.zlib in Resources */, 063364DE1DC51D33007E313F /* empty.txt in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -831,6 +847,7 @@ 065569451DC659FF00A47E66 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; 065569511DC65A8000A47E66 /* Build configuration list for PBXNativeTarget "SWCompression watchOS" */ = { isa = XCConfigurationList; @@ -839,6 +856,7 @@ 065569531DC65A8000A47E66 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; 0655695E1DC65A9500A47E66 /* Build configuration list for PBXNativeTarget "SWCompression tvOS" */ = { isa = XCConfigurationList; @@ -847,6 +865,7 @@ 065569601DC65A9500A47E66 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; 06BE1AC21DB410F100EE0F59 /* Build configuration list for PBXProject "SWCompression" */ = { isa = XCConfigurationList; diff --git a/Sources/Deflate.swift b/Sources/Deflate.swift index 6891a7c2..3dae56ae 100644 --- a/Sources/Deflate.swift +++ b/Sources/Deflate.swift @@ -111,7 +111,7 @@ public class Deflate: DecompressionAlgorithm { var codeLengths: [Int] = [] var n = 0 while n < (literals + distances) { - let tuple = dynamicCodes.findNextSymbol(in: Data(data[index...index + 1]), + let tuple = dynamicCodes.findNextSymbol(in: Data(data[index...index + 2]), withShift: shift) let symbol = tuple.symbol guard symbol != -1 else { throw DeflateError.HuffmanTableError } @@ -145,7 +145,7 @@ public class Deflate: DecompressionAlgorithm { let end = (index, shift) count = 11 + convertToInt(reversedUint8Array: data.bits(from: start, to: end)) - what = codeLengths.last! + what = 0 } else { throw DeflateError.HuffmanTableError } @@ -159,7 +159,7 @@ public class Deflate: DecompressionAlgorithm { } while true { - let nextSymbol = mainLiterals.findNextSymbol(in: Data(data[index...index + 1]), + let nextSymbol = mainLiterals.findNextSymbol(in: Data(data[index...index + 2]), withShift: shift) let symbol = nextSymbol.symbol guard symbol != -1 else { throw DeflateError.HuffmanTableError } @@ -182,7 +182,7 @@ public class Deflate: DecompressionAlgorithm { convertToInt(reversedUint8Array: data.bits(from: start, to: end)) let newSymbolTuple = mainDistances.findNextSymbol(in: - Data(data[index...index + 1]), withShift: shift) + Data(data[index...index + 2]), withShift: shift) let newSymbol = newSymbolTuple.symbol guard newSymbol != -1 else { throw DeflateError.HuffmanTableError } index += newSymbolTuple.addToIndex @@ -192,9 +192,15 @@ public class Deflate: DecompressionAlgorithm { let start = (index, shift) let extraDistance = HuffmanTable.Constants.extraDistanceBits(n: newSymbol) guard extraDistance != -1 else { throw DeflateError.HuffmanTableError } - index += shift + extraDistance >= 8 ? 1 : 0 - shift = shift + extraDistance >= 8 ? - shift - (8 - extraDistance) : shift + extraDistance + if shift + extraDistance >= 16 { + index += 2 + shift = shift - (16 - extraDistance) + } else if shift + extraDistance >= 8 { + index += 1 + shift = shift - (8 - extraDistance) + } else { + shift += extraDistance + } let end = (index, shift) let distance = HuffmanTable.Constants.distanceBase[newSymbol] + convertToInt(reversedUint8Array: data.bits(from: start, to: end)) diff --git a/Sources/HuffmanTable.swift b/Sources/HuffmanTable.swift index 4da7a118..635eb236 100644 --- a/Sources/HuffmanTable.swift +++ b/Sources/HuffmanTable.swift @@ -25,7 +25,7 @@ class HuffmanTable: CustomStringConvertible { 8193, 12289, 16385, 24577] static func extraLengthBits(n: Int) -> Int { - if (n >= 257 && n <= 256) || n == 285 { + if (n >= 257 && n <= 260) || n == 285 { return 0 } else if n >= 261 && n <= 284 { return ((n - 257) >> 2) - 1 @@ -123,7 +123,7 @@ class HuffmanTable: CustomStringConvertible { func findNextSymbol(in data: Data, withShift shift: Int, reversed: Bool = true) -> (symbol: Int, addToIndex: Int, newShift: Int) { - let bitsArray = data.bits(from: (0, shift), to: (1, 8)) + let bitsArray = data.bits(from: (0, shift), to: (2, 8)) let bitsCount = bitsArray.count var cachedLength = -1 @@ -139,9 +139,19 @@ class HuffmanTable: CustomStringConvertible { } if (reversed && length.reversedSymbol == cached) || (!reversed && length.symbol == cached) { - return (length.code, - shift + cachedLength < 8 ? 0 : 1, - shift + cachedLength < 8 ? shift + cachedLength : shift + cachedLength - 8) + let addToIndex: Int + let newShift: Int + if shift + cachedLength >= 16 { + addToIndex = 2 + newShift = shift - (16 - cachedLength) + } else if shift + cachedLength >= 8 { + addToIndex = 1 + newShift = shift - (8 - cachedLength) + } else { + addToIndex = 0 + newShift = shift + cachedLength + } + return (length.code, addToIndex, newShift) } } return (-1, -1, -1) diff --git a/Tests/Constants.swift b/Tests/Constants.swift index b84aaf5d..37f35293 100644 --- a/Tests/Constants.swift +++ b/Tests/Constants.swift @@ -26,4 +26,14 @@ struct Constants { static let helloWorldZlibPath = testBundle.url(forResource: "helloworld.txt", withExtension: "zlib")! + static let secondZlibTestPath = testBundle.url(forResource: "secondtest", + withExtension: "zlib")! + static let secondZlibTestAnswerPath = testBundle.url(forResource: "secondtest.zlib", + withExtension: "answer")! + + static let thirdZlibTestPath = testBundle.url(forResource: "thirdtest", + withExtension: "zlib")! + static let thirdZlibTestAnswerPath = testBundle.url(forResource: "thirdtest.zlib", + withExtension: "answer")! + } diff --git a/Tests/SWCompressionTests.swift b/Tests/SWCompressionTests.swift index 926ced63..ce53cdd3 100644 --- a/Tests/SWCompressionTests.swift +++ b/Tests/SWCompressionTests.swift @@ -58,4 +58,32 @@ class SWCompressionTests: XCTestCase { XCTAssertEqual(decompressedString!, "Hello, World!\n", "Decompression was incorrect") } + func testSecondZlibFile() { + let testData = try? Data(contentsOf: Constants.secondZlibTestPath) + XCTAssertNotNil(testData, "Failed to load test archive") + let decompressedData = try? ZlibArchive.unarchive(archiveData: testData!) + XCTAssertNotNil(decompressedData, "Failed to decompress") + guard decompressedData != nil else { return } + let decompressedString = String(data: decompressedData!, encoding: .utf8) + XCTAssertNotNil(decompressedString, "Failed to convert decompressed data to string") + let answerString = try? String(contentsOf: Constants.secondZlibTestAnswerPath, encoding: .utf8) + XCTAssertNotNil(answerString, "Failed to get the answer") + guard answerString != nil else { return } + XCTAssertEqual(decompressedString!, answerString!, "Decompression was incorrect") + } + + func testThirdZlibFile() { + let testData = try? Data(contentsOf: Constants.thirdZlibTestPath) + XCTAssertNotNil(testData, "Failed to load test archive") + let decompressedData = try? ZlibArchive.unarchive(archiveData: testData!) + XCTAssertNotNil(decompressedData, "Failed to decompress") + guard decompressedData != nil else { return } + let decompressedString = String(data: decompressedData!, encoding: .utf8) + XCTAssertNotNil(decompressedString, "Failed to convert decompressed data to string") + let answerString = try? String(contentsOf: Constants.thirdZlibTestAnswerPath, encoding: .utf8) + XCTAssertNotNil(answerString, "Failed to get the answer") + guard answerString != nil else { return } + XCTAssertEqual(decompressedString!, answerString!, "Decompression was incorrect") + } + } diff --git a/Tests/Test Files/secondtest.zlib b/Tests/Test Files/secondtest.zlib new file mode 100644 index 00000000..480593bf Binary files /dev/null and b/Tests/Test Files/secondtest.zlib differ diff --git a/Tests/Test Files/secondtest.zlib.answer b/Tests/Test Files/secondtest.zlib.answer new file mode 100644 index 00000000..028c0b52 --- /dev/null +++ b/Tests/Test Files/secondtest.zlib.answer @@ -0,0 +1,79 @@ +A.mpq +Abilities\Spells\Demon\DarkConversion\ZombifyTarget.mdx +Abilities\Spells\Demon\DarkPortal\DarkPortalTarget.mdx +Abilities\Spells\Demon\DarkPortal\DemonRune1backup.blp +Abilities\Spells\Demon\DemonBoltImpact\DemonBoltImpact.mdx +Abilities\Spells\Demon\RainOfFire\RainOfFireLoop1.wav +Abilities\Spells\Demon\RainOfFire\RainOfFireTarget.mdx +Abilities\Spells\Demon\RainOfFire\RainOfFireTarget1.wav +Abilities\Spells\Demon\RainOfFire\RainOfFireTarget2.wav +Abilities\Spells\Demon\RainOfFire\RainOfFireTarget3.wav +Abilities\Spells\Demon\ReviveDemon\ReviveDemon.mdx +Abilities\Spells\Demon\SoulPreservation\SoulPreservation.wav +Abilities\Spells\Human\Avatar\Avatar.wav +Abilities\Spells\Human\Avatar\AvatarCaster.mdx +Abilities\Spells\Human\Blizzard\BlizzardLoop1.wav +Abilities\Spells\Human\Blizzard\BlizzardTarget.mdx +Abilities\Spells\Human\Blizzard\BlizzardTarget1.wav +Abilities\Spells\Human\Blizzard\BlizzardTarget2.wav +Abilities\Spells\Human\Blizzard\BlizzardTarget3.wav +Abilities\Spells\Human\Blizzard\Frost3test.blp +Abilities\Spells\Human\Brilliance\Brilliance.mdx +Abilities\Spells\Human\Brilliance\TjShockwave2.blp +Abilities\Spells\Human\CallToArms\HumanCallToArmsWhat1.wav +Abilities\Spells\Human\Defend\DefendCaster.mdx +Abilities\Spells\Human\Defend\DefendCaster.wav +Abilities\Spells\Human\DevotionAura\AuraRune10.blp +Abilities\Spells\Human\DevotionAura\AuraRuneArrow1.blp +Abilities\Spells\Human\DevotionAura\DevotionAura.mdx +Abilities\Spells\Human\DispelMagic\DispelMagicTarget.mdx +Abilities\Spells\Human\DispelMagic\DispelMagicTarget.wav +Abilities\Spells\Human\DivineShield\DivineShield.wav +Abilities\Spells\Human\DivineShield\DivineShieldTarget.mdx +Abilities\Spells\Human\DivineShield\PaladinDivineShieldDeath1.wav +Abilities\Spells\Human\Flare\FlareCaster.mdx +Abilities\Spells\Human\Flare\FlareTarget.mdx +Abilities\Spells\Human\Flare\FlareTarget1.wav +Abilities\Spells\Human\Flare\FlareTarget2.wav +Abilities\Spells\Human\Flare\FlareTarget3.wav +Abilities\Spells\Human\Flare\FlareTarget4.wav +Abilities\Spells\Human\Heal\HealTarget.mdx +Abilities\Spells\Human\Heal\HealTarget.wav +Abilities\Spells\Human\HolyBolt\HolyBolt.wav +Abilities\Spells\Human\HolyBolt\HolyBoltMissile.mdx +Abilities\Spells\Human\HolyBolt\HolyBoltSpecialArt.mdx +Abilities\Spells\Human\InnerFire\Crown1.blp +Abilities\Spells\Human\InnerFire\InnerFireBirth.wav +Abilities\Spells\Human\InnerFire\InnerFireTarget.mdx +Abilities\Spells\Human\InnerFire\PriestCastAttack1.wav +Abilities\Spells\Human\InnerFire\Rune7.blp +Abilities\Spells\Human\Invisibility\InvisibilityTarget.mdx +Abilities\Spells\Human\Invisibility\InvisibilityTarget.wav +Abilities\Spells\Human\MagicSentry\MagicSentryCaster.mdx +Abilities\Spells\Human\MassTeleport\MassTeleportCaster.mdx +Abilities\Spells\Human\MassTeleport\MassTeleportTarget.mdx +Abilities\Spells\Human\MassTeleport\MassTeleportTarget.wav +Abilities\Spells\Human\MassTeleport\MassTeleportTo.mdx +Abilities\Spells\Human\MassTeleport\Rune1.blp +Abilities\Spells\Human\Polymorph\PolymorphDone.wav +Abilities\Spells\Human\Polymorph\PolyMorphDoneGround.mdx +Abilities\Spells\Human\Polymorph\PolyMorphFallingSheepArt.mdx +Abilities\Spells\Human\Polymorph\PolyMorphTarget.mdx +Abilities\Spells\Human\Polymorph\PolymorphTarget1.wav +Abilities\Spells\Human\Polymorph\PolymorphTargetAir1.wav +Abilities\Spells\Human\Polymorph\Splat1.wav +Abilities\Spells\Human\Resurrect\Angel1.blp +Abilities\Spells\Human\Resurrect\DemonRune4backup.blp +Abilities\Spells\Human\Resurrect\Holyshaft.blp +Abilities\Spells\Human\Resurrect\Resurrectcaster.mdx +Abilities\Spells\Human\Resurrect\Resurrecttarget.mdx +Abilities\Spells\Human\Resurrect\ResurrectTarget.wav +Abilities\Spells\Human\Resurrect\Rune1b_64.blp +Abilities\Spells\Human\Resurrect\Rune9.blp +Abilities\Spells\Human\Resurrect\Star9.blp +Abilities\Spells\Human\Resurrect\sword1.blp +Abilities\Spells\Human\Resurrect\Yellow_Firering2b.blp +Abilities\Spells\Human\ReviveHuman\ReviveHuman.mdx +Abilities\Spells\Human\ReviveHuman\ReviveHuman.wav +Abilities\Spells\Human\Slow\Slowcaster.mdx +Abilities\Spells\Human\Slow\Slo \ No newline at end of file diff --git a/Tests/Test Files/thirdtest.zlib b/Tests/Test Files/thirdtest.zlib new file mode 100644 index 00000000..7623e24c Binary files /dev/null and b/Tests/Test Files/thirdtest.zlib differ diff --git a/Tests/Test Files/thirdtest.zlib.answer b/Tests/Test Files/thirdtest.zlib.answer new file mode 100644 index 00000000..2da144ae --- /dev/null +++ b/Tests/Test Files/thirdtest.zlib.answer @@ -0,0 +1,74 @@ +n\ImmolationDamage.mdx +Abilities\Spells\NightElf\Immolation\ImmolationDecay1.wav +Abilities\Spells\NightElf\Immolation\ImmolationTarget.mdx +Abilities\Spells\NightElf\Immolation\ImmolationTarget1.wav +Abilities\Spells\NightElf\ManaBurn\ManaBurnTarget.mdx +Abilities\Spells\NightElf\ManaBurn\ManaDrainTarget1.wav +Abilities\Spells\NightElf\MoonWell\CorruptedMoonWellTarget.mdx +Abilities\Spells\NightElf\MoonWell\MoonWellCasterArt.mdx +Abilities\Spells\NightElf\MoonWell\MoonWellTarget.mdx +Abilities\Spells\NightElf\Rejuvenation\RejuvenationTarget.mdx +Abilities\Spells\NightElf\Rejuvenation\RejuvenationTarget1.wav +Abilities\Spells\NightElf\Renew\CityScapeMagicRunesLoop1.wav +Abilities\Spells\NightElf\ReviveNightElf\ReviveNightElf.mdx +Abilities\Spells\NightElf\ReviveNightElf\ReviveNightElf.wav +Abilities\Spells\NightElf\Root\Root.wav +Abilities\Spells\NightElf\ShadowMeld\ShadowMeld1.wav +Abilities\Spells\NightElf\Starfall\Moon_Cresent.blp +Abilities\Spells\NightElf\Starfall\StarfallCaster.mdx +Abilities\Spells\NightElf\Starfall\StarfallCaster1.wav +Abilities\Spells\NightElf\Starfall\StarfallTarget.mdx +Abilities\Spells\NightElf\TargetArtLumber\GenericGlow2.blp +Abilities\Spells\NightElf\TargetArtLumber\TargetArtLumber.mdx +Abilities\Spells\NightElf\TargetArtLumber\WispHarvestLumber1.wav +Abilities\Spells\NightElf\ThornsAura\Thorns.blp +Abilities\Spells\NightElf\ThornsAura\ThornsAura.mdx +Abilities\Spells\NightElf\ThornsAura\ThornsAuraDamage.mdx +Abilities\Spells\NightElf\Tranquility\Tranquility.mdx +Abilities\Spells\NightElf\Tranquility\Tranquility.wav +Abilities\Spells\NightElf\Tranquility\TranquilityHealLoop1.wav +Abilities\Spells\NightElf\Tranquility\TranquilityTarget.mdx +Abilities\Spells\NightElf\Tranquility\TranquilityTarget1.wav +Abilities\Spells\NightElf\TreeofLifeUpgrade\TreeofLifeUpgradeTargetArt.mdx +Abilities\Spells\NightElf\TreeofLifeUpgrade\TreeofLifeUpgradeTargetArtHand.mdx +Abilities\Spells\NightElf\TrueshotAura\quartercircle2.blp +Abilities\Spells\NightElf\TrueshotAura\tjcrosshair.blp +Abilities\Spells\NightElf\TrueshotAura\TrueshotAura.mdx +Abilities\Spells\NightElf\Uproot\Uproot.wav +Abilities\Spells\Orc\BattleStations\OrcBurrowBattleStationsWhat1.wav +Abilities\Spells\Orc\Berserker\Berserkertarget.mdx +Abilities\Spells\Orc\Berserker\BerserkerTarget.wav +Abilities\Spells\Orc\Berserker\BerzerkerCaster.wav +Abilities\Spells\Orc\Berserker\Green_Glow2.blp +Abilities\Spells\Orc\Bloodlust\BloodLustSpecial.mdx +Abilities\Spells\Orc\Bloodlust\BloodLustTarget.mdx +Abilities\Spells\Orc\Bloodlust\BloodlustTarget.wav +Abilities\Spells\Orc\CommandAura\CommandAura.mdx +Abilities\Spells\Orc\CommandAura\CommandAuraTarget.mdx +Abilities\Spells\Orc\CommandAura\Flash.blp +Abilities\Spells\Orc\Devour\DevourEffectArt.mdx +Abilities\Spells\Orc\EarthQuake\EarthquakeLoop1.wav +Abilities\Spells\Orc\EarthQuake\EarthquakeRock.wav +Abilities\Spells\Orc\EarthQuake\EarthquakeTarget.mdx +Abilities\Spells\Orc\Ensnare\ensnare_AirTarget.mdx +Abilities\Spells\Orc\Ensnare\ensnaremissile.mdx +Abilities\Spells\Orc\Ensnare\EnsnareMissile.wav +Abilities\Spells\Orc\Ensnare\ensnareTarget.mdx +Abilities\Spells\Orc\Ensnare\EnsnareTarget.wav +Abilities\Spells\Orc\Ensnare\Tornado2.blp +Abilities\Spells\Orc\FeralSpirit\feralspiritdone.mdx +Abilities\Spells\Orc\FeralSpirit\feralspirittarget.mdx +Abilities\Spells\Orc\FeralSpirit\FeralSpiritTarget1.wav +Abilities\Spells\Orc\LightningBolt\LightningBolt.wav +Abilities\Spells\Orc\LightningBolt\LightningBoltMissile.mdx +Abilities\Spells\Orc\LightningShield\LightningShieldBuff.mdx +Abilities\Spells\Orc\LightningShield\LightningShieldTarget.mdx +Abilities\Spells\Orc\LightningShield\LightningShieldTarget.wav +Abilities\Spells\Orc\MirrorImage\MirrorImage.wav +Abilities\Spells\Orc\MirrorImage\MirrorImageCaster.mdx +Abilities\Spells\Orc\MirrorImage\MirrorImageDeath.wav +Abilities\Spells\Orc\MirrorImage\MirrorImageDeathCaster.mdx +Abilities\Spells\Orc\MirrorImage\MirrorImageMissile.mdx +Abilities\Spells\Orc\Purge\PurgeBuffTarget.mdx +Abilities\Spells\Orc\Purge\PurgeTarget1.wav +Abilities\Spells\Orc\Reincarnation\Gra \ No newline at end of file