/// <summary>
        /// Parses the image from the wz filetod
        /// </summary>
        /// <param name="wzReader">The BinaryReader that is currently reading the wz file</param>
        /// <returns>bool Parse status</returns>
        public bool ParseImage(bool parseEverything)
        {
            if (Parsed)
            {
                return(true);
            }
            else if (Changed)
            {
                Parsed = true;
                return(true);
            }
            this.parseEverything = parseEverything;
            long originalPos = reader.BaseStream.Position;

            reader.BaseStream.Position = offset;

            byte   b    = reader.ReadByte();
            string prop = reader.ReadString();
            ushort val  = reader.ReadUInt16();

            if (b != WzImageHeaderByte || prop != "Property" || val != 0)
            {
                return(false);
            }

            properties.AddRange(WzImageProperty.ParsePropertyList(offset, reader, this, this));
            parsed = true;
            return(true);
        }
Exemple #2
0
        /// <summary>
        /// Parses the WzDirectory
        /// </summary>
        internal void ParseDirectory()
        {
            int entryCount = reader.ReadCompressedInt();

            for (int i = 0; i < entryCount; i++)
            {
                byte   type  = reader.ReadByte();
                string fname = null;
                int    fsize;
                int    checksum;
                uint   offset;

                long rememberPos = 0;
                if (type == 2)
                {
                    int stringOffset = reader.ReadInt32();
                    rememberPos = reader.BaseStream.Position;
                    reader.BaseStream.Position = reader.Header.FStart + stringOffset;
                    type  = reader.ReadByte();
                    fname = reader.ReadString();
                }
                else if (type == 3 || type == 4)
                {
                    fname       = reader.ReadString();
                    rememberPos = reader.BaseStream.Position;
                }
                reader.BaseStream.Position = rememberPos;
                fsize    = reader.ReadCompressedInt();
                checksum = reader.ReadCompressedInt();
                offset   = reader.ReadOffset();
                if (type == 3)
                {
                    WzDirectory subDir = new WzDirectory(reader, fname, hash, WzIv);
                    subDir.BlockSize = fsize;
                    subDir.Checksum  = checksum;
                    subDir.Offset    = offset;
                    subDir.Parent    = this;
                    subDirs.Add(subDir);
                }
                else
                {
                    WzImage img = new WzImage(fname, reader);
                    img.BlockSize = fsize;
                    img.Checksum  = checksum;
                    img.Offset    = offset;
                    img.Parent    = this;
                    images.Add(img);
                }
            }

            foreach (WzDirectory subdir in subDirs)
            {
                reader.BaseStream.Position = subdir.offset;
                subdir.ParseDirectory();
            }
        }
Exemple #3
0
        internal void DumpBlock(int endOfBlock, string name)
        {
            switch (reader.ReadByte())
            {
            case 0x1B:
                ExtractMore(endOfBlock, name, reader.ReadStringAtOffset(offset + reader.ReadInt32()));
                return;

            case 0x73:
                ExtractMore(endOfBlock, name, "");
                return;
            }
        }
        /// <summary>
        /// BPS of the mp3 file
        /// </summary>
        //public byte BPS { get { return bps; } set { bps = value; } }
        /// <summary>
        /// Creates a WzSoundProperty with the specified name
        /// </summary>
        /// <param name="name">The name of the property</param>
        /// <param name="reader">The wz reader</param>
        /// <param name="parseNow">Indicating whether to parse the property now</param>
        public WzSoundProperty(string name, WzBinaryReader reader, bool parseNow)
        {
            this.name = name;
            wzReader  = reader;
            reader.BaseStream.Position++;

            //note - soundDataLen does NOT include the length of the header.
            soundDataLen = reader.ReadCompressedInt();
            len_ms       = reader.ReadCompressedInt();

            long headerOff = reader.BaseStream.Position;

            reader.BaseStream.Position += soundHeader.Length; //skip GUIDs
            int wavFormatLen = reader.ReadByte();

            reader.BaseStream.Position = headerOff;

            header = reader.ReadBytes(soundHeader.Length + 1 + wavFormatLen);
            ParseHeader();

            //sound file offs
            offs = reader.BaseStream.Position;
            if (parseNow)
            {
                mp3bytes = reader.ReadBytes(soundDataLen);
            }
            else
            {
                reader.BaseStream.Position += soundDataLen;
            }
        }
Exemple #5
0
        internal WzPngProperty(WzBinaryReader reader, bool parseNow)
        {
            // Read compressed bytes
            width   = reader.ReadCompressedInt();
            height  = reader.ReadCompressedInt();
            format  = reader.ReadCompressedInt();
            format2 = reader.ReadByte();
            reader.BaseStream.Position += 4;
            offs = reader.BaseStream.Position;
            int len = reader.ReadInt32() - 1;

            reader.BaseStream.Position += 1;

            if (len > 0)
            {
                if (parseNow)
                {
                    compressedBytes = wzReader.ReadBytes(len);
                    ParsePng();
                }
                else
                {
                    reader.BaseStream.Position += len;
                }
            }
            wzReader = reader;
        }
        /// <summary>
        /// Creates a blank WzPngProperty
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="parseNow"></param>
        internal WzPngProperty(WzBinaryReader reader, bool parseNow)
        {
            // Read compressed bytes
            width   = reader.ReadCompressedInt();
            height  = reader.ReadCompressedInt();
            format  = reader.ReadCompressedInt();
            format2 = reader.ReadByte();
            reader.BaseStream.Position += 4;
            offs = reader.BaseStream.Position;
            int len = reader.ReadInt32() - 1;

            reader.BaseStream.Position += 1;

            if (len > 0)
            {
                if (parseNow)
                {
                    if (wzReader == null) // when saving the WZ file to a new encryption
                    {
                        compressedImageBytes = reader.ReadBytes(len);
                    }
                    else // when opening the Wz property
                    {
                        compressedImageBytes = wzReader.ReadBytes(len);
                    }
                    ParsePng();
                }
                else
                {
                    reader.BaseStream.Position += len;
                }
            }
            this.wzReader = reader;
        }
        //public byte BPS { get { return bps; } set { bps = value; } }
        /// <summary>
        /// BPS of the mp3 file
        /// Creates a WzSoundProperty with the specified name
        /// </summary>
        /// <param name="name">The name of the property</param>
        /// <param name="reader">The wz reader</param>
        /// <param name="parseNow">Indicating whether to parse the property now</param>
        public WzSoundProperty(string name, WzBinaryReader reader, bool parseNow)
        {
            this.Name = name;
            _wzReader = reader;
            reader.BaseStream.Position++;

            //note - soundDataLen does NOT include the length of the header.
            _soundDataLen = reader.ReadCompressedInt();
            // 时间
            _time = reader.ReadCompressedInt();

            var headerOff = reader.BaseStream.Position;

            reader.BaseStream.Position += SoundHeader.Length; //skip GUIDs
            int wavFormatLen = reader.ReadByte();

            reader.BaseStream.Position = headerOff;

            reader.ReadBytes(SoundHeader.Length + 1 + wavFormatLen);

            //sound file offs
            _offs = reader.BaseStream.Position;
            if (parseNow)
            {
                _mp3Bytes = reader.ReadBytes(_soundDataLen);
            }
            else
            {
                reader.BaseStream.Position += _soundDataLen;
            }
        }
Exemple #8
0
        private bool CalculateVersion(WzBinaryReader reader)
        {
            for (var j = 0; j < short.MaxValue; j++)
            {
                Version      = (short)j;
                _versionHash = GetVersionHash(_version, Version);
                if (_versionHash == 0)
                {
                    continue;
                }

                reader.Hash = _versionHash;
                var         position = reader.BaseStream.Position;
                WzDirectory testDirectory;
                try
                {
                    testDirectory = new WzDirectory(reader, Name, _versionHash, _wzIv, this);
                    testDirectory.ParseDirectory();
                }
                catch
                {
                    reader.BaseStream.Position = position;
                    continue;
                }

                var testImage = testDirectory.GetChildImages()[0];

                try
                {
                    reader.BaseStream.Position = testImage.Offset;
                    var checkByte = reader.ReadByte();
                    reader.BaseStream.Position = position;
                    testDirectory.Dispose();
                    switch (checkByte)
                    {
                    case 0x73:
                    case 0x1b:
                        var directory = new WzDirectory(reader, Name, _versionHash, _wzIv, this);
                        directory.ParseDirectory();
                        WzDirectory = directory;
                        return(true);

                    default:
                        reader.BaseStream.Position = position;
                        return(true);
                    }
                }
                catch
                {
                    reader.BaseStream.Position = position;
                }
            }

            return(false);
        }
Exemple #9
0
        /// <summary>
        /// Parses the image from the wz filetod
        /// </summary>
        /// <param name="wzReader">The BinaryReader that is currently reading the wz file</param>
        public void ParseImage(bool parseEverything)
        {
            if (Parsed)
            {
                return;
            }
            else if (Changed)
            {
                Parsed = true; return;
            }
            this.parseEverything = parseEverything;
            long originalPos = reader.BaseStream.Position;

            reader.BaseStream.Position = offset;
            byte b = reader.ReadByte();

            if (b != 0x73 || reader.ReadString() != "Property" || reader.ReadUInt16() != 0)
            {
                return;
            }
            properties.AddRange(WzImageProperty.ParsePropertyList(offset, reader, this, this));
            parsed = true;
        }
Exemple #10
0
        internal static IExtended ParseExtendedProp(WzBinaryReader reader, uint offset, int endOfBlock, string name, IWzObject parent, WzImage imgParent)
        {
            switch (reader.ReadByte())
            {
            case 0x1B:
                return(ExtractMore(reader, offset, endOfBlock, name, reader.ReadStringAtOffset(offset + reader.ReadInt32()), parent, imgParent));

            case 0x73:
                return(ExtractMore(reader, offset, endOfBlock, name, "", parent, imgParent));

            default:
                throw new System.Exception("Invlid byte read at ParseExtendedProp");
            }
        }
Exemple #11
0
        /// <summary>
        /// Parses the image from the wz filetod
        /// </summary>
        /// <param name="wzReader">The BinaryReader that is currently reading the wz file</param>
        public void ParseImage()
        {
            long originalPos = reader.BaseStream.Position;

            reader.BaseStream.Position = offset;
            byte   b    = reader.ReadByte();
            string tmp  = reader.ReadString();
            ushort tmp2 = reader.ReadUInt16();

            if (b != 0x73 || tmp != "Property" || tmp2 != 0)
            {
                return;
            }
            properties.AddRange(IWzImageProperty.ParsePropertyList(offset, reader, this, this));
            parsed = true;
        }
Exemple #12
0
        internal static AWzImageProperty ParseExtendedProp(WzBinaryReader pReader, uint pOffset, int pEndOfBlock, string pName, AWzObject pParent, WzImage pImgParent)
        {
            byte b = pReader.ReadByte();

            switch (b)
            {
            case 0x1B:
                return(ExtractMore(pReader, pOffset, pEndOfBlock, pName, pReader.ReadStringAtOffset(pOffset + pReader.ReadInt32()), pParent, pImgParent));

            case 0x73:
                return(ExtractMore(pReader, pOffset, pEndOfBlock, pName, pReader.ReadString(), pParent, pImgParent));

            default:
                return(null);
                //throw new Exception("Invlid type at ParseExtendedProp: " + b);
            }
        }
Exemple #13
0
        internal WzPngProperty(WzBinaryReader reader)
        {
            // Read compressed bytes
            width   = reader.ReadCompressedInt();
            height  = reader.ReadCompressedInt();
            format  = reader.ReadCompressedInt();
            format2 = reader.ReadByte();
            reader.BaseStream.Position += 4;
            int len = reader.ReadInt32() - 1;

            reader.BaseStream.Position += 1;

            if (len > 0)
            {
                compressedBytes = reader.ReadBytes(len);
            }
        }
Exemple #14
0
        /// <summary>
        /// Parses the image from the wz filetod
        /// </summary>
        public void ParseImage()
        {
            //long originalPos = mReader.BaseStream.Position;
            //byte[] keyCopy = mReader.WzKey;
            //Array.Copy(mReader.WzKey, keyCopy, mReader.WzKey.Length);
            mReader.BaseStream.Position = mOffset;
            byte b = mReader.ReadByte();

            if (b != 0x73 || mReader.ReadString() != "Property" || mReader.ReadUInt16() != 0)
            {
                return;
            }
            List <AWzImageProperty> properties = ParsePropertyList(mOffset, mReader, this, this);

            mProperties.AddRange(properties);
            properties.Clear();
            mParsed = true;
        }
Exemple #15
0
        internal WzPngProperty(WzBinaryReader pReader)
        {
            // Read compressed bytes
            mWidth   = pReader.ReadCompressedInt();
            mHeight  = pReader.ReadCompressedInt();
            mFormat  = pReader.ReadCompressedInt();
            mFormat2 = pReader.ReadByte();
            pReader.BaseStream.Position += 4;
            mOffsets = pReader.BaseStream.Position;
            int len = pReader.ReadInt32() - 1;

            pReader.BaseStream.Position += 1;

            if (len > 0)
            {
                pReader.BaseStream.Position += len;
            }
            mWzReader = pReader;
        }
        internal WzPngProperty(WzBinaryReader reader, bool parseNow)
        {
            this.wzReader = reader;

            // Width Height
            width  = reader.ReadCompressedInt();
            height = reader.ReadCompressedInt();
            if (this.width >= 0x10000 || this.height >= 0x10000) // copy pasta eric <3
            {
                throw new ArgumentException(string.Format("Invalid WzPngProperty in Wz. Width: {0}, Height: {1}", width, height));
            }

            // Image format
            nPixFormat = reader.ReadCompressedInt();
            nMagLevel  = reader.ReadByte();

            // Other crap
            reader.BaseStream.Position += 4;
            offs = reader.BaseStream.Position;
            int len = reader.ReadInt32() - 1;

            reader.BaseStream.Position += 1;

            if (len > 0)
            {
                if (parseNow)
                {
                    compressedBytes = wzReader.ReadBytes(len);
                    ParsePng();
                }
                else
                {
                    reader.BaseStream.Position += len;
                }
            }
            wzReader = reader;
        }
Exemple #17
0
        internal void ParseMainWzDirectory()
        {
            if (this.path == null)
            {
                Helpers.ErrorLogger.Log(Helpers.ErrorLevel.Critical, "[Error] Path is null");
                return;
            }

            WzBinaryReader reader = new WzBinaryReader(File.Open(this.path, FileMode.Open, FileAccess.Read, FileShare.Read), mapleVersion);

            this.Header           = new WzHeader();
            this.Header.Ident     = reader.ReadString(4);
            this.Header.FSize     = reader.ReadUInt64();
            this.Header.FStart    = reader.ReadUInt32();
            this.Header.Copyright = reader.ReadNullTerminatedString();
            reader.ReadBytes((int)(Header.FStart - reader.BaseStream.Position));
            reader.Header = this.Header;
            this.version  = reader.ReadInt16();
            if (fileVersion == -1)
            {
                for (int j = 0; j < short.MaxValue; j++)
                {
                    this.fileVersion = (short)j;
                    this.versionHash = GetVersionHash(version, fileVersion);
                    if (this.versionHash != 0)
                    {
                        reader.Hash = this.versionHash;
                        long        position      = reader.BaseStream.Position;
                        WzDirectory testDirectory = null;
                        try
                        {
                            testDirectory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                            testDirectory.ParseDirectory();
                        }
                        catch
                        {
                            reader.BaseStream.Position = position;
                            continue;
                        }
                        WzImage testImage = testDirectory.GetChildImages()[0];

                        try
                        {
                            reader.BaseStream.Position = testImage.Offset;
                            byte checkByte = reader.ReadByte();
                            reader.BaseStream.Position = position;
                            testDirectory.Dispose();
                            switch (checkByte)
                            {
                            case 0x73:
                            case 0x1b:
                            {
                                WzDirectory directory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                                directory.ParseDirectory();
                                this.wzDir = directory;
                                return;
                            }
                            }
                            reader.BaseStream.Position = position;
                        }
                        catch
                        {
                            reader.BaseStream.Position = position;
                        }
                    }
                }
                throw new Exception("Error with game version hash : The specified game version is incorrect and WzLib was unable to determine the version itself");
            }
            else
            {
                this.versionHash = GetVersionHash(version, fileVersion);
                reader.Hash      = this.versionHash;
                WzDirectory directory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                directory.ParseDirectory();
                this.wzDir = directory;
            }
        }
        /// <summary>
        /// Parse directories in the WZ file
        /// </summary>
        /// <param name="parseErrorMessage"></param>
        /// <param name="lazyParse">Only load the firt WzDirectory found if true</param>
        /// <returns></returns>
        internal bool ParseMainWzDirectory(out string parseErrorMessage, bool lazyParse = false)
        {
            if (this.path == null)
            {
                Helpers.ErrorLogger.Log(Helpers.ErrorLevel.Critical, "[Error] Path is null");
                parseErrorMessage = "[Error] Path is null";
                return(false);
            }
            WzBinaryReader reader = new WzBinaryReader(File.Open(this.path, FileMode.Open, FileAccess.Read, FileShare.Read), WzIv);

            this.Header           = new WzHeader();
            this.Header.Ident     = reader.ReadString(4);
            this.Header.FSize     = reader.ReadUInt64();
            this.Header.FStart    = reader.ReadUInt32();
            this.Header.Copyright = reader.ReadNullTerminatedString();
            reader.ReadBytes((int)(Header.FStart - reader.BaseStream.Position));
            reader.Header = this.Header;
            this.version  = reader.ReadInt16();

            if (mapleStoryPatchVersion == -1)
            {
                const short MAX_PATCH_VERSION = 10000; // wont be reached for the forseeable future.

                for (int j = 0; j < MAX_PATCH_VERSION; j++)
                {
                    this.mapleStoryPatchVersion = (short)j;
                    this.versionHash            = GetVersionHash(version, mapleStoryPatchVersion);
                    if (this.versionHash == 0)
                    {
                        continue;
                    }
                    reader.Hash = this.versionHash;
                    long        position = reader.BaseStream.Position; // save position to rollback to, if should parsing fail from here
                    WzDirectory testDirectory;
                    try
                    {
                        testDirectory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                        testDirectory.ParseDirectory(lazyParse);
                    }
                    catch
                    {
                        reader.BaseStream.Position = position;
                        continue;
                    }

                    try
                    {
                        List <WzImage> childImages = testDirectory.GetChildImages();
                        if (childImages.Count == 0)                // coincidentally in msea v194 Map001.wz, the hash matches exactly using mapleStoryPatchVersion of 113, and it fails to decrypt later on (probably 1 in a million chance).
                        {
                            reader.BaseStream.Position = position; // reset
                            continue;
                        }
                        WzImage testImage = childImages[0];

                        try
                        {
                            reader.BaseStream.Position = testImage.Offset;
                            byte checkByte = reader.ReadByte();
                            reader.BaseStream.Position = position;

                            switch (checkByte)
                            {
                            case 0x73:
                            case 0x1b:
                            {
                                WzDirectory directory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                                directory.ParseDirectory(lazyParse);
                                this.wzDir = directory;

                                parseErrorMessage = "Success";
                                return(true);
                            }

                            default:
                            {
                                Helpers.ErrorLogger.Log(Helpers.ErrorLevel.MissingFeature, "New Wz image header found. checkByte = " + checkByte);
                                // log or something
                                break;
                            }
                            }
                            reader.BaseStream.Position = position; // reset
                        }
                        catch
                        {
                            reader.BaseStream.Position = position; // reset
                        }
                    }
                    finally
                    {
                        testDirectory.Dispose();
                    }
                }
                parseErrorMessage = "Error with game version hash : The specified game version is incorrect and WzLib was unable to determine the version itself";
            }
            else
            {
                this.versionHash = GetVersionHash(version, mapleStoryPatchVersion);
                reader.Hash      = this.versionHash;
                WzDirectory directory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                directory.ParseDirectory();
                this.wzDir = directory;
            }

            parseErrorMessage = "Success";
            return(true);
        }
        /// <summary>
        /// Parse directories in the WZ file
        /// </summary>
        /// <param name="parseErrorMessage"></param>
        /// <param name="lazyParse">Only load the firt WzDirectory found if true</param>
        /// <returns></returns>
        internal WzFileParseStatus ParseMainWzDirectory(bool lazyParse = false)
        {
            if (this.path == null)
            {
                Helpers.ErrorLogger.Log(Helpers.ErrorLevel.Critical, "[Error] Path is null");
                return(WzFileParseStatus.Path_Is_Null);
            }
            WzBinaryReader reader = new WzBinaryReader(File.Open(this.path, FileMode.Open, FileAccess.Read, FileShare.Read), WzIv);

            this.Header           = new WzHeader();
            this.Header.Ident     = reader.ReadString(4);
            this.Header.FSize     = reader.ReadUInt64();
            this.Header.FStart    = reader.ReadUInt32();
            this.Header.Copyright = reader.ReadString((int)(Header.FStart - 17U));

            byte unk1 = reader.ReadByte();

            byte[] unk2 = reader.ReadBytes((int)(Header.FStart - (ulong)reader.BaseStream.Position));
            reader.Header = this.Header;

            Check64BitClient(reader);  // update b64BitClient flag

            // the value of wzVersionHeader is less important. It is used for reading/writing from/to WzFile Header, and calculating the versionHash.
            // it can be any number if the client is 64-bit. Assigning 777 is just for convenience when calculating the versionHash.
            this.wzVersionHeader = b64BitClient && !b64BitClient_withVerHeader ? wzVersionHeader64bit_start : reader.ReadUInt16();

            if (mapleStoryPatchVersion == -1)
            {
                // for 64-bit client, return immediately if version 777 works correctly.
                // -- the latest KMS update seems to have changed it to 778? 779?
                if (b64BitClient)
                {
                    for (ushort maplestoryVerToDecode = wzVersionHeader64bit_start; maplestoryVerToDecode < wzVersionHeader64bit_start + 10; maplestoryVerToDecode++)
                    {
                        if (TryDecodeWithWZVersionNumber(reader, wzVersionHeader, maplestoryVerToDecode, lazyParse))
                        {
                            return(WzFileParseStatus.Success);
                        }
                    }
                }
                // Attempt to get version from MapleStory.exe first
                short maplestoryVerDetectedFromClient = GetMapleStoryVerFromExe(this.path, out this.mapleLocaleVersion);

                // this step is actually not needed if we know the maplestory patch version (the client .exe), but since we dont..
                // we'll need a bruteforce way around it.
                const short MAX_PATCH_VERSION = 1000; // wont be reached for the forseeable future.

                for (int j = maplestoryVerDetectedFromClient; j < MAX_PATCH_VERSION; j++)
                {
                    if (TryDecodeWithWZVersionNumber(reader, wzVersionHeader, j, lazyParse))
                    {
                        return(WzFileParseStatus.Success);
                    }
                }
                //parseErrorMessage = "Error with game version hash : The specified game version is incorrect and WzLib was unable to determine the version itself";
                return(WzFileParseStatus.Error_Game_Ver_Hash);
            }
            else
            {
                this.versionHash = CheckAndGetVersionHash(wzVersionHeader, mapleStoryPatchVersion);
                reader.Hash      = this.versionHash;
                WzDirectory directory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                directory.ParseDirectory();
                this.wzDir = directory;
            }
            return(WzFileParseStatus.Success);
        }
Exemple #20
0
        internal void ParseMainWzDirectory(WzFile parentFile = null)
        {
            if (mPath == null)
            {
                Console.WriteLine("[Error] Path is null");
                return;
            }
            byte[] key = WzKeyGenerator.GenerateWzKey(mWzIv);
            mReader = new WzBinaryReader(File.Open(mPath, FileMode.Open, FileAccess.Read, FileShare.Read), key, true);
            Header  = new WzHeader {
                Ident = mReader.ReadString(4), FSize = mReader.ReadUInt64(), FStart = mReader.ReadUInt32(), Copyright = mReader.ReadNullTerminatedString()
            };
            int bytesToRead = (int)(Header.FStart - mReader.BaseStream.Position);

            if (bytesToRead < 0)
            {
                throw new Exception("Unable to parse WZ file header");
            }
            mReader.ReadBytes(bytesToRead);
            mReader.Header = Header;
            mVersion       = mReader.ReadInt16();
            if (mFileVersion == -1)
            {
                for (int j = 0; j < short.MaxValue; j++)
                {
                    mFileVersion = (short)j;
                    if (parentFile != null)
                    {
                        mFileVersion = parentFile.mFileVersion;
                    }
                    mVersionHash = GetVersionHash(mVersion, mFileVersion);
                    if (mVersionHash == 0)
                    {
                        continue;
                    }
                    mReader.Hash = mVersionHash;
                    long        position = mReader.BaseStream.Position;
                    WzDirectory testDirectory;
                    try {
                        testDirectory = new WzDirectory(mReader, mName, mVersionHash, mWzIv);
                        testDirectory.ParseDirectory();
                    } catch {
                        mReader.BaseStream.Position = position;
                        continue;
                    }
                    foreach (WzImage s in testDirectory.GetChildImages())
                    {
                        if (s.Name.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
                        {
                            testDirectory.Dispose();
                            throw new Exception("Invalid file names were detected. An invalid encryption may have been used.");
                        }
                    }
                    WzImage testImage = testDirectory.GetChildImages()[0];
                    try {
                        mReader.BaseStream.Position = testImage.Offset;
                        byte checkByte = mReader.ReadByte();
                        mReader.BaseStream.Position = position;
                        testDirectory.Dispose();
                        switch (checkByte)
                        {
                        case 0x73:
                        case 0x1b: {
                            mHash = mVersionHash;
                            ParseDirectory(parentFile);
                            return;
                        }
                        }
                        mReader.BaseStream.Position = position;
                    } catch {
                        mReader.BaseStream.Position = position;
                    }
                }
                throw new Exception("Error with game version hash : The specified game version is incorrect and WzLib was unable to determine the version itself");
            }
            mVersionHash = GetVersionHash(mVersion, mFileVersion);
            mReader.Hash = mVersionHash;
            mHash        = mVersionHash;
            ParseDirectory(parentFile);
        }
Exemple #21
0
        internal static List <IWzImageProperty> ParsePropertyList(uint offset, WzBinaryReader reader, IWzObject parent, WzImage parentImg)
        {
            int entryCount = reader.ReadCompressedInt();
            List <IWzImageProperty> properties = new List <IWzImageProperty>(entryCount);

            for (int i = 0; i < entryCount; i++)
            {
                string name = reader.ReadStringBlock(offset);
                switch (reader.ReadByte())
                {
                case 0:
                    properties.Add(new WzNullProperty(name)
                    {
                        Parent = parent                                                                  /*, ParentImage = parentImg*/
                    });
                    break;

                case 0x0B:
                case 2:
                    properties.Add(new WzUnsignedShortProperty(name, reader.ReadUInt16())
                    {
                        Parent = parent                                                                                                /*, ParentImage = parentImg*/
                    });
                    break;

                case 3:
                    properties.Add(new WzCompressedIntProperty(name, reader.ReadCompressedInt())
                    {
                        Parent = parent                                                                                                       /*, ParentImage = parentImg*/
                    });
                    break;

                case 4:
                    byte type = reader.ReadByte();
                    if (type == 0x80)
                    {
                        properties.Add(new WzByteFloatProperty(name, reader.ReadSingle())
                        {
                            Parent = parent                                                                                                /*, ParentImage = parentImg*/
                        });
                    }
                    else if (type == 0)
                    {
                        properties.Add(new WzByteFloatProperty(name, 0f)
                        {
                            Parent = parent                                                                               /*, ParentImage = parentImg*/
                        });
                    }
                    break;

                case 5:
                    properties.Add(new WzDoubleProperty(name, reader.ReadDouble())
                    {
                        Parent = parent                                                                                         /*, ParentImage = parentImg*/
                    });
                    break;

                case 8:
                    properties.Add(new WzStringProperty(name, reader.ReadStringBlock(offset))
                    {
                        Parent = parent
                    });
                    break;

                case 9:
                    int eob = (int)(reader.ReadUInt32() + reader.BaseStream.Position);
                    IWzImageProperty exProp = ParseExtendedProp(reader, offset, eob, name, parent, parentImg);
                    properties.Add(exProp);
                    if (reader.BaseStream.Position != eob)
                    {
                        reader.BaseStream.Position = eob;
                    }
                    break;

                default:
                    throw new Exception("Unknown property type at ParsePropertyList");
                }
            }
            return(properties);
        }
Exemple #22
0
        /// <summary>
        /// Parses the WzDirectory
        /// <paramref name="lazyParse">Only parses the first directory</paramref>
        /// </summary>
        internal void ParseDirectory(bool lazyParse = false)
        {
            //Debug.WriteLine(HexTool.ToString( reader.ReadBytes(20)));
            //reader.BaseStream.Position = reader.BaseStream.Position - 20;
            long available = reader.Available();

            if (available == 0)
            {
                return;
            }

            int entryCount = reader.ReadCompressedInt();

            if (entryCount < 0 || entryCount > 100000) // probably nothing > 100k folders for now.
            {
                throw new Exception("Invalid wz version used for decryption, try parsing other version numbers.");
            }

            for (int i = 0; i < entryCount; i++)
            {
                byte   type  = reader.ReadByte();
                string fname = null;
                int    fsize;
                int    checksum;
                uint   offset;

                long rememberPos = 0;
                switch (type)
                {
                case 1:      //01 XX 00 00 00 00 00 OFFSET (4 bytes)
                {
                    int unknown = reader.ReadInt32();
                    reader.ReadInt16();
                    uint offs = reader.ReadOffset();
                    continue;
                }

                case 2:
                {
                    int stringOffset = reader.ReadInt32();
                    rememberPos = reader.BaseStream.Position;
                    reader.BaseStream.Position = reader.Header.FStart + stringOffset;
                    type  = reader.ReadByte();
                    fname = reader.ReadString();
                    break;
                }

                case 3:
                case 4:
                {
                    fname       = reader.ReadString();
                    rememberPos = reader.BaseStream.Position;
                    break;
                }

                default:
                {
                    throw new Exception("[WzDirectory] Unknown directory. type = " + type);
                }
                }
                reader.BaseStream.Position = rememberPos;
                fsize    = reader.ReadCompressedInt();
                checksum = reader.ReadCompressedInt();
                offset   = reader.ReadOffset(); // IWzArchive::Getposition(pArchive)

                if (type == 3)
                {
                    WzDirectory subDir = new WzDirectory(reader, fname, hash, WzIv, wzFile)
                    {
                        BlockSize = fsize,
                        Checksum  = checksum,
                        Offset    = offset,
                        Parent    = this
                    };
                    subDirs.Add(subDir);

                    if (lazyParse)
                    {
                        break;
                    }
                }
                else
                {
                    WzImage img = new WzImage(fname, reader, checksum)
                    {
                        BlockSize = fsize,
                        Offset    = offset,
                        Parent    = this
                    };
                    images.Add(img);

                    if (lazyParse)
                    {
                        break;
                    }
                }
            }

            foreach (WzDirectory subdir in subDirs)
            {
                if (subdir.Checksum != 0)
                {
                    reader.BaseStream.Position = subdir.offset;
                    subdir.ParseDirectory();
                }
            }
        }
Exemple #23
0
        /// <summary>
        /// Parses the WzDirectory
        /// <paramref name="lazyParse">Only parses the first directory</paramref>
        /// </summary>
        internal void ParseDirectory(bool lazyParse = false)
        {
            //Debug.WriteLine(HexTool.ToString( reader.ReadBytes(20)));
            //reader.BaseStream.Position = reader.BaseStream.Position - 20;

            int entryCount = reader.ReadCompressedInt();

            for (int i = 0; i < entryCount; i++)
            {
                byte   type  = reader.ReadByte();
                string fname = null;
                int    fsize;
                int    checksum;
                uint   offset;

                long rememberPos = 0;
                switch (type)
                {
                case 1:      //01 XX 00 00 00 00 00 OFFSET (4 bytes)
                {
                    int unknown = reader.ReadInt32();
                    reader.ReadInt16();
                    uint offs = reader.ReadOffset();
                    continue;
                }

                case 2:
                {
                    int stringOffset = reader.ReadInt32();
                    rememberPos = reader.BaseStream.Position;
                    reader.BaseStream.Position = reader.Header.FStart + stringOffset;
                    type  = reader.ReadByte();
                    fname = reader.ReadString();
                    break;
                }

                case 3:
                case 4:
                {
                    fname       = reader.ReadString();
                    rememberPos = reader.BaseStream.Position;
                    break;
                }

                default:
                {
                    break;
                }
                }
                reader.BaseStream.Position = rememberPos;
                fsize    = reader.ReadCompressedInt();
                checksum = reader.ReadCompressedInt();
                offset   = reader.ReadOffset();
                if (type == 3)
                {
                    WzDirectory subDir = new WzDirectory(reader, fname, hash, WzIv, wzFile);
                    subDir.BlockSize = fsize;
                    subDir.Checksum  = checksum;
                    subDir.Offset    = offset;
                    subDir.Parent    = this;
                    subDirs.Add(subDir);

                    if (lazyParse)
                    {
                        break;
                    }
                }
                else
                {
                    WzImage img = new WzImage(fname, reader);
                    img.BlockSize = fsize;
                    img.Checksum  = checksum;
                    img.Offset    = offset;
                    img.Parent    = this;
                    images.Add(img);

                    if (lazyParse)
                    {
                        break;
                    }
                }
            }

            foreach (WzDirectory subdir in subDirs)
            {
                reader.BaseStream.Position = subdir.offset;
                subdir.ParseDirectory();
            }
        }
        /// <summary>
        /// Parse directories in the WZ file
        /// </summary>
        /// <param name="parseErrorMessage"></param>
        /// <param name="lazyParse">Only load the firt WzDirectory found if true</param>
        /// <returns></returns>
        internal WzFileParseStatus ParseMainWzDirectory(bool lazyParse = false)
        {
            if (this.path == null)
            {
                Helpers.ErrorLogger.Log(Helpers.ErrorLevel.Critical, "[Error] Path is null");
                return(WzFileParseStatus.Path_Is_Null);
            }
            WzBinaryReader reader = new WzBinaryReader(File.Open(this.path, FileMode.Open, FileAccess.Read, FileShare.Read), WzIv);

            this.Header           = new WzHeader();
            this.Header.Ident     = reader.ReadString(4);
            this.Header.FSize     = reader.ReadUInt64();
            this.Header.FStart    = reader.ReadUInt32();
            this.Header.Copyright = reader.ReadString((int)(Header.FStart - 17U));

            byte unk1 = reader.ReadByte();

            byte[] unk2 = reader.ReadBytes((int)(Header.FStart - (ulong)reader.BaseStream.Position));
            reader.Header        = this.Header;
            this.wzVersionHeader = reader.ReadInt16();

            if (mapleStoryPatchVersion == -1)
            {
                // Attempt to get version from MapleStory.exe first
                short maplestoryVerDetectedFromClient = GetMapleStoryVerFromExe(this.path, out this.mapleLocaleVersion);

                // this step is actually not needed if we know the maplestory patch version (the client .exe), but since we dont..
                // we'll need a bruteforce way around it.
                const short MAX_PATCH_VERSION = 10000; // wont be reached for the forseeable future.

                for (int j = maplestoryVerDetectedFromClient; j < MAX_PATCH_VERSION; j++)
                {
                    this.mapleStoryPatchVersion = (short)j;
                    this.versionHash            = CheckAndGetVersionHash(wzVersionHeader, mapleStoryPatchVersion);
                    if (this.versionHash == 0) // ugly hack, but that's the only way if the version number isnt known (nexon stores this in the .exe)
                    {
                        continue;
                    }

                    reader.Hash = this.versionHash;
                    long        position = reader.BaseStream.Position; // save position to rollback to, if should parsing fail from here
                    WzDirectory testDirectory;
                    try
                    {
                        testDirectory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                        testDirectory.ParseDirectory(lazyParse);
                    }
                    catch (Exception exp)
                    {
                        Debug.WriteLine(exp.ToString());

                        reader.BaseStream.Position = position;
                        continue;
                    }

                    // test the image and see if its correct by parsing it
                    bool bCloseTestDirectory = true;
                    try
                    {
                        WzImage testImage = testDirectory.WzImages.FirstOrDefault();
                        if (testImage != null)
                        {
                            try
                            {
                                reader.BaseStream.Position = testImage.Offset;
                                byte checkByte = reader.ReadByte();
                                reader.BaseStream.Position = position;

                                switch (checkByte)
                                {
                                case 0x73:
                                case 0x1b:
                                {
                                    WzDirectory directory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                                    directory.ParseDirectory(lazyParse);
                                    this.wzDir = directory;

                                    return(WzFileParseStatus.Success);
                                }

                                case 0x30:
                                case 0x6C:     // idk
                                case 0xBC:     // Map002.wz? KMST?
                                default:
                                {
                                    Helpers.ErrorLogger.Log(Helpers.ErrorLevel.MissingFeature,
                                                            string.Format("[WzFile.cs] New Wz image header found. checkByte = {0}. File Name = {1}", checkByte, Name));
                                    // log or something
                                    break;
                                }
                                }
                                reader.BaseStream.Position = position; // reset
                            }
                            catch
                            {
                                reader.BaseStream.Position = position; // reset
                            }
                        }
                        else // if there's no image in the WZ file (new KMST Base.wz), test the directory instead
                        {
                            // coincidentally in msea v194 Map001.wz, the hash matches exactly using mapleStoryPatchVersion of 113, and it fails to decrypt later on (probably 1 in a million chance? o_O).
                            // damn, technical debt accumulating here
                            if (mapleStoryPatchVersion == 113)
                            {
                                // hack for now
                                reader.BaseStream.Position = position; // reset
                                continue;
                            }
                            else
                            {
                                this.wzDir          = testDirectory;
                                bCloseTestDirectory = false;

                                return(WzFileParseStatus.Success);
                            }
                        }
                    }
                    finally
                    {
                        if (bCloseTestDirectory)
                        {
                            testDirectory.Dispose();
                        }
                    }
                }
                //parseErrorMessage = "Error with game version hash : The specified game version is incorrect and WzLib was unable to determine the version itself";
                return(WzFileParseStatus.Error_Game_Ver_Hash);
            }
            else
            {
                this.versionHash = CheckAndGetVersionHash(wzVersionHeader, mapleStoryPatchVersion);
                reader.Hash      = this.versionHash;
                WzDirectory directory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                directory.ParseDirectory();
                this.wzDir = directory;
            }
            return(WzFileParseStatus.Success);
        }
Exemple #25
0
        /// <summary>
        /// Parses the WzDirectory
        /// </summary>
        internal void ParseDirectory(WzFile parent = null)
        {
            //Array.Copy(mReader.WzKey, keyCopy, mReader.WzKey.Length);
            int entryCount = mReader.ReadCompressedInt();

            for (int i = 0; i < entryCount; i++)
            {
                byte   type  = mReader.ReadByte();
                string fname = null;
                int    fsize;
                int    checksum;
                uint   offset;
                long   rememberPos = 0;
                switch (type)
                {
                case 1: {
                    mReader.ReadInt32();
                    mReader.ReadInt16();
                    mReader.ReadOffset();
                    continue;
                }

                case 2: {
                    int stringOffset = mReader.ReadInt32();
                    rememberPos = mReader.BaseStream.Position;
                    mReader.BaseStream.Position = mReader.Header.FStart + stringOffset;
                    type  = mReader.ReadByte();
                    fname = mReader.ReadString().Trim();
                }
                break;

                case 3:
                case 4:
                    fname       = mReader.ReadString().Trim();
                    rememberPos = mReader.BaseStream.Position;
                    break;
                }
                mReader.BaseStream.Position = rememberPos;
                fsize    = mReader.ReadCompressedInt();
                checksum = mReader.ReadCompressedInt();
                offset   = mReader.ReadOffset();
                if (type == 3)
                {
                    WzDirectory subDir = new WzDirectory(mReader, fname, mHash, mWzIv)
                    {
                        BlockSize = fsize, Checksum = checksum, Offset = offset, Parent = parent ?? this
                    };
                    if (parent != null)
                    {
                        parent.mSubDirs.Add(subDir);
                    }
                    mSubDirs.Add(subDir);
                }
                else
                {
                    WzImage img = new WzImage(fname, mReader)
                    {
                        BlockSize = fsize, Checksum = checksum, Offset = offset, Parent = parent ?? this
                    };
                    if (parent != null)
                    {
                        parent.mImages.Add(img);
                    }
                    mImages.Add(img);
                }
            }
            foreach (WzDirectory subdir in mSubDirs)
            {
                mReader.BaseStream.Position = subdir.mOffset;
                subdir.ParseDirectory();
            }
        }
        /// <summary>
        /// Parses the image from the wz filetod
        /// </summary>
        /// <param name="wzReader">The BinaryReader that is currently reading the wz file</param>
        /// <returns>bool Parse status</returns>
        public bool ParseImage(bool forceReadFromData = false)
        {
            if (!forceReadFromData)   // only check if parsed or changed if its not false read
            {
                if (Parsed)
                {
                    return(true);
                }
                else if (Changed)
                {
                    Parsed = true;
                    return(true);
                }
            }

            lock (reader) // for multi threaded XMLWZ export.
            {
                long originalPos = reader.BaseStream.Position;
                reader.BaseStream.Position = offset;

                byte b = reader.ReadByte();
                switch (b)
                {
                case 0x1:     // .lua
                {
                    if (IsLuaWzImage)
                    {
                        WzLuaProperty lua = WzImageProperty.ParseLuaProperty(offset, reader, this, this);

                        List <WzImageProperty> luaImage = new List <WzImageProperty>();
                        luaImage.Add(lua);

                        properties.AddRange(luaImage);
                        parsed = true;         // test
                        return(true);
                    }

                    return(false);        // unhandled for now, if it isnt an .lua image
                }

                case WzImageHeaderByte:
                {
                    string prop = reader.ReadString();
                    ushort val  = reader.ReadUInt16();
                    if (prop != "Property" || val != 0)
                    {
                        return(false);
                    }
                    break;
                }

                default:
                {
                    // todo: log this or warn.
                    Helpers.ErrorLogger.Log(Helpers.ErrorLevel.MissingFeature, "[WzImage] New Wz image header found. b = " + b);
                    return(false);
                }
                }
                List <WzImageProperty> images = WzImageProperty.ParsePropertyList(offset, reader, this, this);
                properties.AddRange(images);

                parsed = true;
            }
            return(true);
        }
        private bool TryDecodeWithWZVersionNumber(WzBinaryReader reader, int useWzVersionHeader, int useMapleStoryPatchVersion, bool lazyParse)
        {
            this.mapleStoryPatchVersion = (short)useMapleStoryPatchVersion;

            this.versionHash = CheckAndGetVersionHash(useWzVersionHeader, mapleStoryPatchVersion);
            if (this.versionHash == 0) // ugly hack, but that's the only way if the version number isnt known (nexon stores this in the .exe)
            {
                return(false);
            }

            reader.Hash = this.versionHash;
            long        fallbackOffsetPosition = reader.BaseStream.Position; // save position to rollback to, if should parsing fail from here
            WzDirectory testDirectory;

            try
            {
                testDirectory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                testDirectory.ParseDirectory(lazyParse);
            }
            catch (Exception exp)
            {
                Debug.WriteLine(exp.ToString());

                reader.BaseStream.Position = fallbackOffsetPosition;
                return(false);
            }

            // test the image and see if its correct by parsing it
            bool bCloseTestDirectory = true;

            try
            {
                WzImage testImage = testDirectory.WzImages.FirstOrDefault();
                if (testImage != null)
                {
                    try
                    {
                        reader.BaseStream.Position = testImage.Offset;
                        byte checkByte = reader.ReadByte();
                        reader.BaseStream.Position = fallbackOffsetPosition;

                        switch (checkByte)
                        {
                        case 0x73:
                        case 0x1b:
                        {
                            WzDirectory directory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                            directory.ParseDirectory(lazyParse);
                            this.wzDir = directory;

                            return(true);
                        }

                        case 0x30:
                        case 0x6C:     // idk
                        case 0xBC:     // Map002.wz? KMST?
                        default:
                        {
                            Helpers.ErrorLogger.Log(Helpers.ErrorLevel.MissingFeature,
                                                    string.Format("[WzFile.cs] New Wz image header found. checkByte = {0}. File Name = {1}", checkByte, Name));
                            // log or something
                            break;
                        }
                        }
                        reader.BaseStream.Position = fallbackOffsetPosition; // reset
                    }
                    catch
                    {
                        reader.BaseStream.Position = fallbackOffsetPosition; // reset
                        return(false);
                    }
                    return(true);
                }
                else // if there's no image in the WZ file (new KMST Base.wz), test the directory instead
                {
                    // coincidentally in msea v194 Map001.wz, the hash matches exactly using mapleStoryPatchVersion of 113, and it fails to decrypt later on (probably 1 in a million chance? o_O).
                    // damn, technical debt accumulating here
                    if (mapleStoryPatchVersion == 113)
                    {
                        // hack for now
                        reader.BaseStream.Position = fallbackOffsetPosition; // reset
                        return(false);
                    }
                    else
                    {
                        this.wzDir          = testDirectory;
                        bCloseTestDirectory = false;

                        return(true);
                    }
                }
            }
            finally
            {
                if (bCloseTestDirectory)
                {
                    testDirectory.Dispose();
                }
            }
        }
Exemple #28
0
        internal static IExtended ExtractMore(WzBinaryReader reader, uint offset, int eob, string name, string iname, IWzObject parent, WzImage imgParent)
        {
            if (iname == "")
            {
                iname = reader.ReadString();
            }
            switch (iname)
            {
            case "Property":
                WzSubProperty subProp = new WzSubProperty(name)
                {
                    Parent = parent
                };
                reader.BaseStream.Position += 2;
                subProp.AddProperties(IWzImageProperty.ParsePropertyList(offset, reader, subProp, imgParent));
                return(subProp);

            case "Canvas":
                WzCanvasProperty canvasProp = new WzCanvasProperty(name)
                {
                    Parent = parent
                };
                reader.BaseStream.Position++;
                if (reader.ReadByte() == 1)
                {
                    reader.BaseStream.Position += 2;
                    canvasProp.AddProperties(IWzImageProperty.ParsePropertyList(offset, reader, canvasProp, imgParent));
                }
                canvasProp.PngProperty = new WzPngProperty(reader, imgParent.parseEverything)
                {
                    Parent = canvasProp
                };
                return(canvasProp);

            case "Shape2D#Vector2D":
                WzVectorProperty vecProp = new WzVectorProperty(name)
                {
                    Parent = parent
                };
                vecProp.X = new WzCompressedIntProperty("X", reader.ReadCompressedInt())
                {
                    Parent = vecProp
                };
                vecProp.Y = new WzCompressedIntProperty("Y", reader.ReadCompressedInt())
                {
                    Parent = vecProp
                };
                return(vecProp);

            case "Shape2D#Convex2D":
                WzConvexProperty convexProp = new WzConvexProperty(name)
                {
                    Parent = parent
                };
                int convexEntryCount = reader.ReadCompressedInt();
                convexProp.WzProperties.Capacity = convexEntryCount;     //performance thing
                for (int i = 0; i < convexEntryCount; i++)
                {
                    convexProp.AddProperty(ParseExtendedProp(reader, offset, 0, name, convexProp, imgParent));
                }
                return(convexProp);

            case "Sound_DX8":
                WzSoundProperty soundProp = new WzSoundProperty(name, reader, imgParent.parseEverything)
                {
                    Parent = parent
                };
                return(soundProp);

            case "UOL":
                reader.BaseStream.Position++;
                switch (reader.ReadByte())
                {
                case 0:
                    return(new WzUOLProperty(name, reader.ReadString())
                    {
                        Parent = parent
                    });

                case 1:
                    return(new WzUOLProperty(name, reader.ReadStringAtOffset(offset + reader.ReadInt32()))
                    {
                        Parent = parent
                    });
                }
                throw new Exception("Unsupported UOL type");

            default:
                throw new Exception("Unknown iname: " + iname);
            }
        }
        internal static IWzImageProperty[] ParsePropertyList(uint offset, WzBinaryReader reader, IWzObject parent, WzImage parentImg)
        {
            List <IWzImageProperty> properties = new List <IWzImageProperty>();
            int entryCount = reader.ReadCompressedInt();

            for (int i = 0; i < entryCount; i++)
            {
                string name  = reader.ReadStringBlock(offset);
                byte   ptype = reader.ReadByte();
                switch (ptype)
                {
                case 0:
                    properties.Add(new WzNullProperty(name, i)
                    {
                        Parent = parent, ParentImage = parentImg
                    });
                    break;

                case 0x0B:
                case 2:
                    properties.Add(new WzUnsignedShortProperty(name, reader.ReadUInt16())
                    {
                        Parent = parent, ParentImage = parentImg
                    });
                    break;

                case 3:
                    properties.Add(new WzCompressedIntProperty(name, reader.ReadCompressedInt())
                    {
                        Parent = parent, ParentImage = parentImg
                    });
                    break;

                case 4:
                    byte type = reader.ReadByte();
                    if (type == 0x80)
                    {
                        properties.Add(new WzByteFloatProperty(name, reader.ReadSingle())
                        {
                            Parent = parent, ParentImage = parentImg
                        });
                    }
                    else if (type == 0)
                    {
                        properties.Add(new WzByteFloatProperty(name, 0f)
                        {
                            Parent = parent, ParentImage = parentImg
                        });
                    }
                    break;

                case 5:
                    properties.Add(new WzDoubleProperty(name, reader.ReadDouble())
                    {
                        Parent = parent, ParentImage = parentImg
                    });
                    break;

                case 8:
                    properties.Add(new WzStringProperty(name, reader.ReadStringBlock(offset))
                    {
                        Parent = parent
                    });
                    break;

                case 9:
                    int eob = (int)(reader.ReadUInt32() + reader.BaseStream.Position);
                    WzExtendedProperty exProp = new WzExtendedProperty(offset, eob, name);
                    exProp.Parent      = parent;
                    exProp.ParentImage = parentImg;
                    exProp.ParseExtendedProperty(reader);
                    properties.Add(exProp);
                    if (reader.BaseStream.Position != eob)
                    {
                        reader.BaseStream.Position = eob;
                    }
                    break;

                default:
                {
                    Console.WriteLine("Unknown type: {0} | {1}", ptype, name);
                    break;
                }
                }
            }
            return(properties.ToArray());
        }
        /// <summary>
        /// Parse directories in the WZ file
        /// </summary>
        /// <param name="parseErrorMessage"></param>
        /// <param name="lazyParse">Only load the firt WzDirectory found if true</param>
        /// <returns></returns>
        internal WzFileParseStatus ParseMainWzDirectory(bool lazyParse = false)
        {
            if (this.path == null)
            {
                Helpers.ErrorLogger.Log(Helpers.ErrorLevel.Critical, "[Error] Path is null");
                return(WzFileParseStatus.Path_Is_Null);
            }
            WzBinaryReader reader = new WzBinaryReader(File.Open(this.path, FileMode.Open, FileAccess.Read, FileShare.Read), WzIv);

            this.Header           = new WzHeader();
            this.Header.Ident     = reader.ReadString(4);
            this.Header.FSize     = reader.ReadUInt64();
            this.Header.FStart    = reader.ReadUInt32();
            this.Header.Copyright = reader.ReadString((int)(Header.FStart - 17U));

            reader.ReadBytes(1);
            reader.ReadBytes((int)(Header.FStart - (ulong)reader.BaseStream.Position));
            reader.Header = this.Header;
            this.version  = reader.ReadInt16();

            if (mapleStoryPatchVersion == -1)
            {
                const short MAX_PATCH_VERSION = 10000; // wont be reached for the forseeable future.

                for (int j = 0; j < MAX_PATCH_VERSION; j++)
                {
                    this.mapleStoryPatchVersion = (short)j;
                    this.versionHash            = CheckAndGetVersionHash(version, mapleStoryPatchVersion);
                    if (this.versionHash == 0) // ugly hack, but that's the only way if the version number isnt known (nexon stores this in the .exe)
                    {
                        continue;
                    }

                    reader.Hash = this.versionHash;
                    long        position = reader.BaseStream.Position; // save position to rollback to, if should parsing fail from here
                    WzDirectory testDirectory;
                    try
                    {
                        testDirectory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                        testDirectory.ParseDirectory(lazyParse);
                    }
                    catch (Exception exp)
                    {
                        Debug.WriteLine(exp.ToString());

                        reader.BaseStream.Position = position;
                        continue;
                    }

                    try
                    {
                        List <WzImage> childImages = testDirectory.GetChildImages();
                        if (childImages.Count == 0)                // coincidentally in msea v194 Map001.wz, the hash matches exactly using mapleStoryPatchVersion of 113, and it fails to decrypt later on (probably 1 in a million chance).
                        {
                            reader.BaseStream.Position = position; // reset
                            continue;
                        }
                        WzImage testImage = childImages[0];

                        try
                        {
                            reader.BaseStream.Position = testImage.Offset;
                            byte checkByte = reader.ReadByte();
                            reader.BaseStream.Position = position;

                            switch (checkByte)
                            {
                            case 0x73:
                            case 0x1b:
                            {
                                WzDirectory directory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                                directory.ParseDirectory(lazyParse);
                                this.wzDir = directory;
                                return(WzFileParseStatus.Success);
                            }

                            case 0x30:
                            case 0x6C:     // idk
                            case 0xBC:     // Map002.wz? KMST?
                            default:
                            {
                                Helpers.ErrorLogger.Log(Helpers.ErrorLevel.MissingFeature,
                                                        string.Format("[WzFile.cs] New Wz image header found. checkByte = {0}. File Name = {1}", checkByte, Name));
                                // log or something
                                break;
                            }
                            }
                            reader.BaseStream.Position = position; // reset
                        }
                        catch
                        {
                            reader.BaseStream.Position = position; // reset
                        }
                    }
                    finally
                    {
                        testDirectory.Dispose();
                    }
                }
                //parseErrorMessage = "Error with game version hash : The specified game version is incorrect and WzLib was unable to determine the version itself";
                return(WzFileParseStatus.Error_Game_Ver_Hash);
            }
            else
            {
                this.versionHash = CheckAndGetVersionHash(version, mapleStoryPatchVersion);
                reader.Hash      = this.versionHash;
                WzDirectory directory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
                directory.ParseDirectory();
                this.wzDir = directory;
            }
            return(WzFileParseStatus.Success);
        }