Skip to content

Commit

Permalink
[MapleLib] Fixed a parsing issue with the new KMS Base.wz without WzI…
Browse files Browse the repository at this point in the history
…mage
  • Loading branch information
lastbattle committed Sep 20, 2021
1 parent 1669125 commit 63e2d72
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 70 deletions.
4 changes: 2 additions & 2 deletions HaRepacker/WzNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public static bool CanNodeBeInserted(WzNode parentNode, string name)
else if (obj is WzDirectory directory)
return directory[name] == null;
else if (obj is WzFile file)
return file.WzDirectory[name] == null;
return file.WzDirectory?[name] == null;
else
return false;
}
Expand All @@ -127,7 +127,7 @@ private bool AddObjInternal(WzObject obj)
WzObject TaggedObject = (WzObject)Tag;
if (TaggedObject is WzFile file)
TaggedObject = file.WzDirectory;

if (TaggedObject is WzDirectory directory)
{
if (obj is WzDirectory wzDirectory)
Expand Down
9 changes: 9 additions & 0 deletions MapleLib/WzLib/Util/WzBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,15 @@ public long ReadLong()
return sb;
}

/// <summary>
/// The amount of bytes available remaining in the stream
/// </summary>
/// <returns></returns>
public long Available()
{
return BaseStream.Length - BaseStream.Position;
}

public uint ReadOffset()
{
uint offset = (uint)BaseStream.Position;
Expand Down
34 changes: 15 additions & 19 deletions MapleLib/WzLib/WzDirectory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace MapleLib.WzLib
public class WzDirectory : WzObject
{
#region Fields
internal List<WzImage> images = new List<WzImage>();
private List<WzImage> images = new List<WzImage>();
internal List<WzDirectory> subDirs = new List<WzDirectory>();
internal WzBinaryReader reader;
internal uint offset = 0;
Expand Down Expand Up @@ -90,7 +90,7 @@ public override void Dispose()
/// <summary>
/// The wz images contained in the directory
/// </summary>
public List<WzImage> WzImages { get { return images; } }
public List<WzImage> WzImages { get { return images; } private set { } }
/// <summary>
/// The sub directories contained in the directory
/// </summary>
Expand Down Expand Up @@ -182,6 +182,9 @@ 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.
Expand Down Expand Up @@ -223,7 +226,7 @@ internal void ParseDirectory(bool lazyParse = false)
}
default:
{
break;
throw new Exception("[WzDirectory] Unknown directory. type = " + type);
}
}
reader.BaseStream.Position = rememberPos;
Expand Down Expand Up @@ -261,8 +264,11 @@ internal void ParseDirectory(bool lazyParse = false)

foreach (WzDirectory subdir in subDirs)
{
reader.BaseStream.Position = subdir.offset;
subdir.ParseDirectory();
if (subdir.Checksum != 0)
{
reader.BaseStream.Position = subdir.offset;
subdir.ParseDirectory();
}
}
}

Expand Down Expand Up @@ -533,6 +539,7 @@ public void ClearImages()
foreach (WzImage img in images) img.Parent = null;
images.Clear();
}

/// <summary>
/// Clears the list of sub directories
/// </summary>
Expand All @@ -541,6 +548,7 @@ public void ClearDirectories()
foreach (WzDirectory dir in subDirs) dir.Parent = null;
subDirs.Clear();
}

/// <summary>
/// Gets an image in the list of images by it's name
/// </summary>
Expand All @@ -553,6 +561,7 @@ public WzImage GetImageByName(string name)
return wzI;
return null;
}

/// <summary>
/// Gets a sub directory in the list of directories by it's name
/// </summary>
Expand All @@ -565,20 +574,7 @@ public WzDirectory GetDirectoryByName(string name)
return dir;
return null;
}
/// <summary>
/// Gets all child images of a WzDirectory
/// </summary>
/// <returns></returns>
public List<WzImage> GetChildImages()
{
List<WzImage> imgFiles = new List<WzImage>();
imgFiles.AddRange(images);
foreach (WzDirectory subDir in subDirs)
{
imgFiles.AddRange(subDir.GetChildImages());
}
return imgFiles;
}

/// <summary>
/// Removes an image from the list
/// </summary>
Expand Down
118 changes: 69 additions & 49 deletions MapleLib/WzLib/WzFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class WzFile : WzObject
internal WzDirectory wzDir;
internal WzHeader header;
internal string name = "";
internal short version = 0;
internal short wzVersionHeader = 0;
internal uint versionHash = 0;
internal short mapleStoryPatchVersion = 0;
internal WzMapleVersion maplepLocalVersion;
Expand Down Expand Up @@ -141,9 +141,10 @@ public WzFile(string filePath, short gameVersion, WzMapleVersion version)

if (version == WzMapleVersion.GETFROMZLZ)
{
FileStream zlzStream = File.OpenRead(Path.Combine(Path.GetDirectoryName(filePath), "ZLZ.dll"));
this.WzIv = Util.WzKeyGenerator.GetIvFromZlz(zlzStream);
zlzStream.Close();
using (FileStream zlzStream = File.OpenRead(Path.Combine(Path.GetDirectoryName(filePath), "ZLZ.dll")))
{
this.WzIv = Util.WzKeyGenerator.GetIvFromZlz(zlzStream);
}
}
else
this.WzIv = WzTool.GetIvByMapleVersion(version);
Expand Down Expand Up @@ -203,19 +204,21 @@ internal WzFileParseStatus ParseMainWzDirectory(bool lazyParse = false)
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));
byte unk1 = reader.ReadByte();
byte[] unk2 = reader.ReadBytes((int)(Header.FStart - (ulong)reader.BaseStream.Position));
reader.Header = this.Header;
this.version = reader.ReadInt16();
this.wzVersionHeader = reader.ReadInt16();

if (mapleStoryPatchVersion == -1)
{
// this step is actually not needed if we actually know the maplestory patch version (the client .exe), but since we dont..

This comment has been minimized.

Copy link
@mechpaul

mechpaul Sep 20, 2021

For some regions of Maplestory, the version is stored in the EXE file. You can get the version by using System.Diagnostics.FileVersionInfo.FileVersion on the EXE file. To see the version, right click on the Maplestory.exe file and click on "Details" then look at the FileVersion provided by the details. You could use this version as a potential first try.

I see it's used in KMST, KMS, and GMS.

This comment has been minimized.

Copy link
@lastbattle

lastbattle Sep 20, 2021

Author Owner

c2b34eb

Nice! That should speed things up when opening a WZ file.
Thanks!

// we'll need a bruteforce way around it.
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);
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;

Expand All @@ -235,61 +238,77 @@ internal WzFileParseStatus ParseMainWzDirectory(bool lazyParse = false)
continue;
}

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

switch (checkByte)
try
{
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 = 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
}
reader.BaseStream.Position = position; // reset
}
catch
catch
{
reader.BaseStream.Position = position; // reset
}
}
else // if there's no image in the WZ file (new KMST Base.wz), test the directory instead
{
reader.BaseStream.Position = position; // reset
// 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).
if (mapleStoryPatchVersion == 113)
{
// hack for now
reader.BaseStream.Position = position; // reset
continue;
}
else
{
this.wzDir = testDirectory;
bCloseTestDirectory = false;
}
}
return WzFileParseStatus.Success;
}
finally
{
testDirectory.Dispose();
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(version, mapleStoryPatchVersion);
this.versionHash = CheckAndGetVersionHash(wzVersionHeader, mapleStoryPatchVersion);
reader.Hash = this.versionHash;
WzDirectory directory = new WzDirectory(reader, this.name, this.versionHash, this.WzIv, this);
directory.ParseDirectory();
Expand Down Expand Up @@ -343,7 +362,7 @@ private void CreateWZVersionHash()
b = (versionHash >> 16) & 0xFF,
c = (versionHash >> 8) & 0xFF,
d = versionHash & 0xFF;
version = (byte)~(a ^ b ^ c ^ d);
wzVersionHeader = (byte)~(a ^ b ^ c ^ d);
}

/// <summary>
Expand Down Expand Up @@ -376,11 +395,12 @@ public void SaveToDisk(string path, WzMapleVersion savingToPreferredWzVer = WzMa
}

WzTool.StringCache.Clear();
uint totalLen = wzDir.GetImgOffsets(wzDir.GetOffsets(Header.FStart + 2));

using (WzBinaryWriter wzWriter = new WzBinaryWriter(File.Create(path), WzIv))
{
wzWriter.Hash = versionHash;

uint totalLen = wzDir.GetImgOffsets(wzDir.GetOffsets(Header.FStart + 2));
Header.FSize = totalLen - Header.FStart;
for (int i = 0; i < 4; i++)
{
Expand All @@ -395,7 +415,7 @@ public void SaveToDisk(string path, WzMapleVersion savingToPreferredWzVer = WzMa
{
wzWriter.Write(new byte[(int)extraHeaderLength]);
}
wzWriter.Write(version);
wzWriter.Write(wzVersionHeader);
wzWriter.Header = Header;
wzDir.SaveDirectory(wzWriter);
wzWriter.StringCache.Clear();
Expand Down
3 changes: 3 additions & 0 deletions MapleLib/WzLib/WzHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public string Ident
set { ident = value; }
}

/// <summary>
/// see: DEFAULT_WZ_HEADER_COPYRIGHT
/// </summary>
public string Copyright
{
get { return copyright; }
Expand Down

0 comments on commit 63e2d72

Please sign in to comment.