Skip to content

Commit

Permalink
[WzLib] Fixed support for 64-bit wz file without 2-byte encryption ve…
Browse files Browse the repository at this point in the history
…rsion

Tested for
-GMS v230, v83, v180, v188, v117
-MSEA v218  (64-bit client with encVer), v15, v160
-KMS v359 (64-bit client with encVer)

please report any errors you've faced, and with the version number.
  • Loading branch information
lastbattle committed Nov 29, 2022
1 parent 040c41e commit ebf107e
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 47 deletions.
17 changes: 17 additions & 0 deletions MapleLib/WzLib/Util/WzBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,23 @@ public WzBinaryReader(Stream input, byte[] WzIv)
#endregion

#region Methods
/// <summary>
/// Sets the base stream position to the header FStart + offset
/// </summary>
/// <param name="offset"></param>
public void SetOffsetFromFStartToPosition(int offset)
{
BaseStream.Position = Header.FStart + offset;
}

public void RollbackStreamPosition(int byOffset)
{
if (BaseStream.Position < byOffset)
throw new Exception("Cant rollback stream position below 0");

BaseStream.Position -= byOffset;
}

public string ReadStringAtOffset(long Offset)
{
return ReadStringAtOffset(Offset, false);
Expand Down
33 changes: 25 additions & 8 deletions MapleLib/WzLib/Util/WzBinaryWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.*/

using System;
using System.Collections;
using System.IO;
using MapleLib.MapleCryptoLib;
using MapleLib.WzLib.WzStructure.Enums;

namespace MapleLib.WzLib.Util
{
Expand Down Expand Up @@ -59,10 +61,12 @@ public WzBinaryWriter(Stream output, byte[] WzIv, bool leaveOpen)
/// ?InternalSerializeString@@YAHPAGPAUIWzArchive@@EE@Z
/// </summary>
/// <param name="s"></param>
/// <param name="withoutOffset">0x73</param>
/// <param name="withOffset">0x1B</param>
/// <param name="withoutOffset">bExistID_0x73 0x73</param>
/// <param name="withOffset">bNewID_0x1b 0x1B</param>
public void WriteStringValue(string s, int withoutOffset, int withOffset)
{
// if length is > 4 and the string cache contains the string
// writes the offset instead
if (s.Length > 4 && StringCache.ContainsKey(s))
{
Write((byte)withOffset);
Expand All @@ -80,24 +84,37 @@ public void WriteStringValue(string s, int withoutOffset, int withOffset)
}
}

public void WriteWzObjectValue(string s, byte type)
/// <summary>
/// Writes the Wz object value
/// </summary>
/// <param name="stringObjectValue"></param>
/// <param name="type"></param>
/// <param name="unk_GMS230"></param>
/// <returns>true if the Wz object value is written as an offset in the Wz file, else if not</returns>
public bool WriteWzObjectValue(string stringObjectValue, WzDirectoryType type)
{
string storeName = string.Format("{0}_{1}", type, s);
if (s.Length > 4 && StringCache.ContainsKey(storeName))
string storeName = string.Format("{0}_{1}", (byte) type, stringObjectValue);

// if length is > 4 and the string cache contains the string
// writes the offset instead
if (stringObjectValue.Length > 4 && StringCache.ContainsKey(storeName))
{
Write((byte)2);
Write((byte)WzDirectoryType.RetrieveStringFromOffset_2); // 2
Write((int)StringCache[storeName]);

return true;
}
else
{
int sOffset = (int)(this.BaseStream.Position - Header.FStart);
Write(type);
Write(s);
Write((byte)type);
Write(stringObjectValue);
if (!StringCache.ContainsKey(storeName))
{
StringCache[storeName] = sOffset;
}
}
return false;
}

public override void Write(string value)
Expand Down
1 change: 1 addition & 0 deletions MapleLib/WzLib/WzDirectory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ internal void ParseDirectory(bool lazyParse = false)
}
default:
{
reader.PrintHexBytes(20);
throw new Exception("[WzDirectory] Unknown directory. type = " + type);
}
}
Expand Down
58 changes: 27 additions & 31 deletions MapleLib/WzLib/WzFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,16 @@ public class WzFile : WzObject
internal WzDirectory wzDir;
internal WzHeader header;
internal string name = "";

internal ushort wzVersionHeader = 0;
internal const ushort wzVersionHeader64bit_start = 770; // 777 for KMS, GMS v230 uses 778.. wut

internal uint versionHash = 0;
internal short mapleStoryPatchVersion = 0;
internal WzMapleVersion maplepLocalVersion;
internal MapleStoryLocalisation mapleLocaleVersion = MapleStoryLocalisation.Not_Known;

internal bool b64BitClient = false; // KMS update after Q4 2021, ver 1.2.357
internal bool b64BitClient_withVerHeader = false; //
internal bool wz_withEncryptVersionHeader = true; // KMS update after Q4 2021, ver 1.2.357

internal byte[] WzIv;
#endregion
Expand Down Expand Up @@ -102,7 +103,7 @@ public override WzObjectType ObjectType
/// </summary>
public MapleStoryLocalisation MapleLocaleVersion { get { return mapleLocaleVersion; } private set { } }

public bool Is64BitWzFile { get { return b64BitClient; } private set { } }
public bool Is64BitWzFile { get { return !wz_withEncryptVersionHeader; } private set { } }

public override WzObject Parent { get { return null; } internal set { } }

Expand Down Expand Up @@ -166,9 +167,6 @@ public WzFile(string filePath, short gameVersion, WzMapleVersion version)
}
else
this.WzIv = WzTool.GetIvByMapleVersion(version);

string[] filePathSplit = Regex.Split(filePath, @"\\");
this.b64BitClient = filePathSplit.Contains("Data"); // TODO: Find a better way of identifying 64-bit client/ WZ from the WZ files instead. This might break if it isnt installed in the right path, esp. private servers
}

/// <summary>
Expand All @@ -183,8 +181,6 @@ public WzFile(string filePath, byte[] wzIv)
maplepLocalVersion = WzMapleVersion.CUSTOM;

this.WzIv = wzIv;

this.b64BitClient = false; // TODO
}

/// <summary>
Expand Down Expand Up @@ -235,15 +231,15 @@ internal WzFileParseStatus ParseMainWzDirectory(bool lazyParse = false)

// 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();
this.wzVersionHeader = this.wz_withEncryptVersionHeader ? reader.ReadUInt16() : wzVersionHeader64bit_start;

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)
if (!this.wz_withEncryptVersionHeader)
{
for (ushort maplestoryVerToDecode = wzVersionHeader64bit_start; maplestoryVerToDecode < wzVersionHeader64bit_start + 20; maplestoryVerToDecode++)
for (ushort maplestoryVerToDecode = wzVersionHeader64bit_start; maplestoryVerToDecode < wzVersionHeader64bit_start + 10; maplestoryVerToDecode++) // 770 ~ 780
{
if (TryDecodeWithWZVersionNumber(reader, wzVersionHeader, maplestoryVerToDecode, lazyParse))
{
Expand Down Expand Up @@ -290,40 +286,40 @@ private void Check64BitClient(WzBinaryReader reader)
{
if (this.Header.FSize >= 2)
{
this.wzVersionHeader = reader.ReadUInt16();
if (this.wzVersionHeader > 0xff)
reader.BaseStream.Position = this.header.FStart; // go back to 0x3C

int encver = reader.ReadUInt16();
if (encver > 0xff) // encver always less than 256
{
b64BitClient = true;
this.wz_withEncryptVersionHeader = false;
}
else if (this.wzVersionHeader == 0x80)
else if (encver == 0x80)
{
// there's an exceptional case that the first field of data part is a compressed int which determines the property count,
// there's an exceptional case that the first field of data part is a compressed int which determined property count,
// if the value greater than 127 and also to be a multiple of 256, the first 5 bytes will become to
// 80 00 xx xx xx
// so we additional check the int value, at most time the child node count in a WzFile won't greater than 65536 (0xFFFF).
// 80 00 xx xx xx
// so we additional check the int value, at most time the child node count in a wz won't greater than 65536.
if (this.Header.FSize >= 5)
{
reader.BaseStream.Position = this.header.FStart; // go back to 0x3C
int propCount = reader.ReadCompressedInt();
if (propCount > 0 && (propCount & 0xFF) == 0 && propCount <= 0xFFFF)
int propCount = reader.ReadInt32();
if (propCount > 0 && (propCount & 0xff) == 0 && propCount <= 0xffff)
{
b64BitClient = true;
this.wz_withEncryptVersionHeader = false;
}
}
} else if (this.wzVersionHeader == 0x21) // or 33
} else
{
b64BitClient = true;
// but read the header
// the latest KMS seems to include this back in again.. damn
this.b64BitClient_withVerHeader = true; // ugly hack, but until i've found a better way without breaking compatibility of old WZs.
// old wz file with header version
}
}
else
{
// Obviously, if data part have only 1 byte, encVer must be deleted.
b64BitClient = true;
// Obviously, if data part have only 1 byte, encver must be deleted.
this.wz_withEncryptVersionHeader = false;
}


// reset position
reader.BaseStream.Position = this.Header.FStart;
}
Expand Down Expand Up @@ -587,7 +583,7 @@ public void SaveToDisk(string path, bool? override_saveAs64BitWZ, WzMapleVersion
// MapleStory UserKey
bool bIsWzUserKeyDefault = MapleCryptoConstants.IsDefaultMapleStoryUserKey(); // check if its saving to the same UserKey.
// Save WZ as 64-bit wz format
bool bSaveAs64BitWZ = override_saveAs64BitWZ != null ? (bool)override_saveAs64BitWZ : b64BitClient;
bool bWZ_withEncryptVersionHeader = this.wz_withEncryptVersionHeader;

CreateWZVersionHash();
wzDir.SetVersionHash(versionHash);
Expand All @@ -605,7 +601,7 @@ public void SaveToDisk(string path, bool? override_saveAs64BitWZ, WzMapleVersion
{
wzWriter.Hash = versionHash;

uint totalLen = wzDir.GetImgOffsets(wzDir.GetOffsets(Header.FStart + (bSaveAs64BitWZ && !this.b64BitClient_withVerHeader ? 0 : 2u)));
uint totalLen = wzDir.GetImgOffsets(wzDir.GetOffsets(Header.FStart + (bWZ_withEncryptVersionHeader ? 2u: 0)));
Header.FSize = totalLen - Header.FStart;
for (int i = 0; i < 4; i++)
{
Expand All @@ -620,7 +616,7 @@ public void SaveToDisk(string path, bool? override_saveAs64BitWZ, WzMapleVersion
{
wzWriter.Write(new byte[(int)extraHeaderLength]);
}
if (!bSaveAs64BitWZ || this.b64BitClient_withVerHeader)
if (bWZ_withEncryptVersionHeader)
wzWriter.Write(wzVersionHeader);

wzWriter.Header = Header;
Expand Down
2 changes: 1 addition & 1 deletion MapleLib/WzLib/WzStructure/MapInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ public MapInfo(WzImage image, string strMapName, string strStreetName, string st
fs = InfoTool.GetFloat(prop);
break;
case "protectItem":
protectItem = InfoTool.GetInt(prop);
protectItem = InfoTool.GetInt(prop); // could also be a WzSubProperty in later versions. "Map002.wz\\Map\\Map2\\211000200.img\\info\\protectItem"
break;
case "createMobInterval":
createMobInterval = InfoTool.GetInt(prop);
Expand Down
7 changes: 0 additions & 7 deletions MapleLib/WzSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,11 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
along with this program. If not, see <http://www.gnu.org/licenses/>.*/

using System;
using MapleLib.WzLib.WzProperties;
using System.Reflection;
using MapleLib.WzLib.WzStructure;
using System.IO;
using Newtonsoft.Json.Linq;
using System.Drawing;
using System.Collections;
using Newtonsoft.Json;
using Spine;
using System.Security.Principal;
using Microsoft.Xna.Framework.Media;
using System.Windows.Controls;

namespace MapleLib.WzLib
{
Expand Down

0 comments on commit ebf107e

Please sign in to comment.