/// <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); }
/// <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(); } }
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; } }
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; } }
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); }
/// <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; }
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"); } }
/// <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; }
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); } }
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); } }
/// <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; }
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; }
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); }
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); }
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); }
/// <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(); } } }
/// <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); }
/// <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(); } } }
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); }