Skip to content
Erwin Oegema edited this page Jul 30, 2017 · 5 revisions

WZ File

WZ files are the 'filesystem' files of the game MapleStory. They contain Directorys and Objects entries. Objects on the Directory level are always Propertys. Setting them to a different kind of Object is undefined.

History

Originally Wizet had plain .img files (serialized Objects) on the filesystem, built in a tree structure, with all having a main directory name (such as Character, Map, Etc and String).

At some point in time, Wizet decided to make files with the extension wz. These files contains the tree structure and its files.

An other thing they added was the support for encryption. They made List.wz (that is, ironically, no real wz file at all) with URI's to encrypted strings. If PCOM.dll finds this file, it'll initialize a list of these strings. This list will be checked everytime a string is being de/encoded. Each string that is 'inside' a URI (that is: a URI inside the list is the prefix of the URI of node of the string), it'll be en/decrypted. This cryptography is pretty weak, as it use a single xor key for all the strings. This key is calculated like the MapleStory packet AES, but with a predefined IV for SEA and GMS.

File format

WZ Header

50 4B 47 31 - "PKG1", file header/identifier
UINT64 - File size
INT32 - Absolute position of WZ content
NULL-TERMINATED STRING - File description, such as 'Package file v1.0 Copyright 2002 Wizet, ZMS'

Contents

Note: starts at Absolute position of WZ content

UINT16 - Encrypted version number
Directory - Root directory

Directory

Internal name: Package

Directories contain both sub Directory and Objects. Whenever an Object is found, treat is as one.

WZ-INT - Amount of entries
for i = 0; i < Amount of entries; i++ {
  UINT8 - Type (if & 1 then its a Directory, else its an Object)
  SERIALIZED-STRING - Name
  WZ-INT - Size
  WZ-INT - Checksum
  WZ-OFFSET - Entry data position in file
}

Object

An Object is a string (telling which datatype will come) and then the datatype data.

Values using a # will initialize the DLL before the # and tries to initialize the COM object with the name after the #.

These datatypes are supported:

  • Property
  • Canvas
  • Shape2D#Convex2D
  • Shape2D#Vector2D
  • UOL
  • Sound_DX8
SERIALIZED-STRING - Typename
... - The serialized datatype

Property

Contains a list of key-value pairs (map), where the key is a string, and the value is the Variant object.

UINT8 - Unknown, seems to be some identifier for PCOM.dll to read as a BMS config file (ascii), whenever not 0?
UINT8 - Unknown, seems to be unused
WZ-INT - Amount of variants
for i = 0; i < Amount of variants; i++ {
  SERIALIZED-STRING - Variant name (node name)
  Variant - The variant
}

Variant

An object that supports multiple types.

UINT8 - Type
if Type == 0 {
  // Object does not contain anything
} else if Type == 2 || Type == 11 {
  INT16 - Value
} else if Type == 3 || Type == 19 {
  WZ-INT - Value
} else if Type == 20 {
  WZ-LONG - Value
} else if Type == 4 {
  UINT8 - Flag for set float
  if Flag for set float == 0x80 {
    FLOAT32 - Value
  } else {
    // Default value is FLOAT32(0.0)
  }
} else if Type == 5 {
  FLOAT64 - Value
} else if Type == 8 {
  SERIALIZED-STRING - Value
} else if Type == 9 {
  // An Object
  INT32 - Size
  Object - The serialized object (= Value)
}

Other datatypes

WZ-OFFSET

Internal function name: LoadPos Note: Only used in Directorys

This value is pretty weird. It's a UINT32. To calculate the offset, you have to do this:

uint32 currentPos = (current position in wzfile)
uint32 offset = (currentPos - Absolute position of WZ content) ^ 0xFFFFFFFF
offset *= WZ File version hash
offset -= 0x581C3F6D
offset = rotl(offset, (byte)(offset & 0x1F))

uint32 encryptedOffset = ReadUINT32()
offset ^= encryptedOffset
offset += (Absolute position of WZ content) * 2
return offset

WZ-INT

Read a signed byte first. If its -128, read INT32. Else, return signed byte as INT32

sbyte possibleSize = ReadINT8()
if (possibleSize == -128) {
  return ReadINT32()
} else {
  return INT32(possibleSize)
}

WZ-LONG

Read a signed byte first. If its -128, read INT64. Else, return signed byte as INT64

sbyte possibleSize = ReadINT8()
if (possibleSize == -128) {
  return ReadINT64()
} else {
  return INT64(possibleSize)
}