Welcome to NXPatcher!
Written by Fiel in the C/C++ programming languages
So what is NXPatcher? It's a program I developed in early June 2009. At the time I was extremely frustrated with Nexon's patching system, so I decided to write my own Maplestory pre-patcher and made it open source and free to redistribute. NXPatcher does more than just create pre-patchers, it can also read patch files directly, determine the version of maplestory you're using, split apart pre-patchers, and a lot more! I've spent more than 10 months writing this product and have examined every line of code. If you want a fast way to make Maplestory pre-patchers or if you're looking for freedom from the dreaded Nexon pre-patchers, you've come to the right place!
Since I wrote this program I have not used a Nexon Pre-patcher once, patched hundreds of times, and saved myself countless headaches. This is the program I trust and use for all of my patching needs.
Which version to choose? Lite or Advanced?
If you consider yourself not very computer savvy or just want to make pre-patchers in a very simple way, I recommend that you pick NXPatcher Lite. The Lite version only creates Nexon pre-patchers and does not contain any other features. Choosing NXPatcher Lite is also a good idea if you're interested in making pre-patchers for others. Please view the "Lite - Create Your Own Pre-patcher" spoiler below
If you are more technical minded, want fine-grained control over the patching process, or want to get away from Nexon pre-patchers, I recommend you pick NXPatcher Advanced. All of the documentation needed to run the Advanced version can be found in the spoilers marked "Advanced" below. Please view the "Advanced - Patch it yourself" spoiler below
Licensing:
Credit to "Fiel" for the source code isn't required, but appreciated.
You may use the source code and any binaries in this topic for any use that you require. You may also redistribute this code and all binaries.
Disclaimer:
This program is not affiliated with or endorsed by the Nexon Corporation or any of its subsidiaries in any way.
This program comes as is and with all faults. I, Fiel, disclaim any warranty of merchantability or fitness for a particular purpose. Use this program at your own risk.
Lite - Create your own Pre-patcher
Tutorial: How to make a Maplestory pre-patcher.
Time to completion: 2-3 minutes
Required files: NXPatcher.exe
PLEASE NOTE!: NXPatcherLite has recently updated to version 2.1. This version behaves differently from other versions.
How to do it:
1. Place NXPatcher.exe in your Maplestory directory.
2. Download the patch file from Nexon's FTP:
Here's what the patch file URL is for 0.72:
http://download2.nexon.net/Game/MapleStory/patch/patchdir/00072/00071to00072.patch
for 0.73:
http://download2.nexon.net/Game/MapleStory/patch/patchdir/00073/00072to00073.patch
Just look at the links to figure out how to get your patch files. It's really hard to describe, but very easy to comprehend.
When you download the patch file, download it to your maplestory directory.
3. After the patch file is fully downloaded, simply drag & drop the file onto NXPatcher.exe. The program creates the pre-patcher for you.
4. After the prepatcher is created, NXPatcher will ask you if you want to run the prepatcher.
Alternate Method 1:
3. After downloading the patch file, simply double click on NXPatcher.exe to run it.
4. A dialog window appears asking you to select the patch file. Select the patch file.
5. NXPatcher will create the patcher then ask you if you want to run it.
Alternate Method 2:
3. After the patch file is fully downloaded, right click on it, click on "Open With", then click on "Choose Default Program".
4. In the window that appears, select "Browse", and search for NXPatcher.exe. Once you've found NXPatcher.exe, double click on it.
5. Click on "OK" to make NXPatcher.exe the default program for patch files.
6. Now every time you want to create a pre-patcher, simply double-click on the patch file and NXPatcher will create a pre-patcher for you.
Enjoy!
Lite - Troubleshooting
Q: I created a pre-patcher with NXPatcher Lite, but it's stuck on Base.wz.
A: I did not write Nexon's horrible pre-patcher, only my own NXPatcher. I can't help you with this problem as I did not write their code.
Q: I created a pre-patcher with NXPatcher Lite, but it's stuck on "Checking the Files".
A: This likely means that your patch file that you downloaded from Nexon is corrupt. Redownload the file. Please consider trying a download manager.
Q: The program won't work and I can't find a solution in this troubleshooting section.
A: Post your problem in this thread. I'm around most days.
Lite - Errors
ERROR 1001 - BAD FILE NAME
This error is thrown if the patch file you selected does not exactly conform to the following naming scheme: \d\d\d\d\dto\d\d\d\d\d.patch (where "\d" means any number 0-9).
RESOLUTION: Please make sure that you are selecting the correct patch file. If you are sure of it, please rename the file to conform to the above naming scheme.
ERROR 1002 - BAD FILE NAME
This error is thrown if the patch file you selected does not exactly conform to the following naming scheme: \d\d\d\d\dto\d\d\d\d\d.patch (where "\d" means any number 0-9).
RESOLUTION: Please make sure that you are selecting the correct patch file. If you are sure of it, please rename the file to conform to the above naming scheme.
ERROR 1003 - BAD CRC
This error occurs because the patch file you provided has an incorrect CRC (Cyclic Redundancy Check). An incorrect CRC means that your patch file, even if created, will not run properly with the Nexon patcher. Because of this, NXPatcher will refuse to make the patch file.
RESOLUTION: Try downloading the patch file again at a later time and hopefully Nexon will have resolved the bad CRC.
ERROR 1004 - BAD HEADER
This error occurs because the file you provided to NXPatcher does not contain the correct file header. Because of this, NXPatcher cannot process the file to create a new prepatcher for you.
RESOLUTION: Please select a different file to use with NXPatcher.
Advanced - Patch it yourself
Tutorial: How to patch your game
Time to completion: 5-10 minutes
Required files: Windows 32-bit pre-compiled - NXPatcher.zip
If you're looking to compile NXPatcher yourself, you can download the source here. Note that the source code will only compile under a Windows environment. The source code is written in C, so you will need a C or C++ compiler. The only <windows.h> dependencies are creating directories, moving files, and showing error messages. I don't see any reason why it would not run under WINE, but I have not tested it myself.
How to do it:
1. Unpack the zip file to your Maplestory directory.
2. Download the desired patch file to your Maplestory directory.
Here's what the patch file URL is for 0.72:
http://download2.nexon.net/Game/MapleStory/patch/patchdir/00072/00071to00072.patch
for 0.73:
http://download2.nexon.net/Game/MapleStory/patch/patchdir/00073/00072to00073.patch
Just look at the links to figure out how to get your patch files. It's really hard to describe, but very easy to comprehend.
When you download the patch file, download it to your maplestory directory.
3. Edit the batch file so the third argument has the same name as the patch file you downloaded. Make sure the second argument is "read"
4. Run the batch file. All of the new files created by NXPatcher will be placed in the "Patcher" directory.
5. Copy the files from your "Patcher" directory to your Maplestory directory. You are now fully patched.
Advanced - Features & Documentation
Introduction:
There are five commands that NXPatcher accepts - read, write, hijack, version, and split. By using these five commands you can have full power over how you patch your Maplestory game.
Read:
The read command allows you to apply a patch file to your current directory. Use the following command:
NXPatcher read [Name of patch file]
ex. NXPatcher read 00075to00079.patch
NXPatcher will then create a directory called "Patcher" and place all of the newly updated files in that directory. Once NXPatcher is done reading the files, simply copy all of the new files from the Patcher directory to your maplestory directory. Your game is now fully patched and ready to go when the Nexon servers come back up. Just run Maplestory from the website, as you always would, to start the game again.
Write:
The write command allows you to create pre-patchers - possibly to use yourself or to share with your friends. Use the following command:
NXPatcher write [Name of patch file]
ex. NXPatcher write 00080to00085.patch
For example, if we were executing the example command above, NXPatcher will create a new file - 00080to00085.exe - which is the pre-patcher. Simply share that file with your friends.
WZ File Version Checking:
If you try to apply a patch file and it freezes on Base.wz (or if you use the "Patch it yourself" method above and it tells you that a checksum is bad), maybe you should try checking which version of wz file you're using as you may have downloaded the wrong patch file by mistake. Simply use this command:
NXPatcher.exe version Base.wz
And it will tell you what version of WZ file it is. The output looks something like this:
WZ File version is 79
This will work with any WZ file, so the following examples will work too:
NXPatcher.exe version Character.wz
NXPatcher.exe version Mob.wz
If you get the following error: "Version could not be determined", it's likely that your wz file is corrupt and you should reinstall the game from scratch.
If you get the error, "This is not a properly formatted WZ file", please make sure that you gave the name of a true WZ file (List.wz is not a true wz file in the strictest sense).
Splitting Patch EXE files:
If you have a pre-patcher EXE file, with NXPatcher you can break down the pre-patcher into its three parts: the base file, the patch file, and the notice file. Simply use the following command:
NXPatcher.exe split MyPrePatcher.exe
Pre-patcher EXE hijacking
Let's say you have a pre-patcher from Nexon. You don't want to use their pre-patcher, but you'd rather use the algorithms already in place with this NXPatcher. This is now easily possible. Simply use the following command:
NXPatcher.exe hijack MyPrePatcher.exe
The following will happen:
1. NXPatcher rips the patch file from the pre-patcher
2. NXPatcher then executes the patch file for itself
Menu:
Of course, you don't have to remember everything above to use NXPatcher correctly. Just type "NXPatcher.exe" to see a listing of everything this program can do for you.
Also, just as a reminder, NXPatcher never, ever removes any of your files! If your computer has to suddenly restart for anything or should there be an error with the patching program, none of your data is at risk.
Advanced - INI File Documentation
The INI file is split up into six different sections - one section for each of the five commands and a file associations section. There are a few basic guidelines that the NXPatcher INI file follows:
1. All variables which begin with the word if (ifbackupdir, ifusefastpatch, ifoutputbase) are boolean variables - the value of it must be 0 or 1, 0 for off and 1 for on.
2. All other variables are strings.
[fileassociations]
There are three default file associations with this section. This section is only used if the user is dragging & dropping a file on NXPatcher or if the user associated a file type with NXPatcher. When a file is executed with NXPatcher in these manners, the program checks the file suffix ("00080to00081.patch", "ManualPatch.exe") and searches for the file suffix in the fileassociations block. The command associated with the file suffix is then executed with that file.
As an example:
Code:[fileassociations] patch = write exe = split wz = versionFirst NXPatcher looks for the file suffix ("patch"), then NXPatcher looks up the file suffix in the fileassociations block which returns "write". The "write" command is then executed on the patch file.Code:C:\Nexon\Maplestory> NXPatcher 00080to00081.patch
For patch files, the write or read command must be used.
For exe files, the hijack or split command must be used.
For wz files, the version command must be used.
[read]
usefastpatchtype (Default value - 0)
This value determines how many copies of the currently patched file (ex. Mob.wz, Npc.wz) is stored in memory. Keep in mind for the purposes of calculating memory allocation that an unpacked version of the patch file (ex. "00085to00090.patch", "00100to00102.patch") is always stored in memory for the purposes of faster execution speed.
If set to 0:
-- While calculating the checksum of a rebuilt file, it is read in chunks from the hard drive.
-- While rebuilding the file, random accesses requested by the patch file are read from the hard drive
-- Uses fewer resources at the expense of slower execution (zero copies of the file is held in memory)
If set to 1:
-- The entire file is read into memory before calculating the checksum or performing any file rebuilding
-- The checksum is calculated while the file is in memory
-- The file is rebuilt from memory to the hard drive
-- Faster execution at the expense of higher resources (one copy of the entire file is held in memory)
If set to 2:
-- The entire file is read into memory before calculating the checksum or performing any file rebuilding
-- The checksum is calculated while the file is in memory
-- The entire file is rebuilt entirely inside memory
-- Very fast execution at the expense of a lot of resources (two copies of the entire file are held in memory at once)
outputfolder (Default value - Patcher)
This is the default folder which NXPatcher outputs all of the patch files. This can be an absolute or a relative reference. Never include a trailing slash. This folder is created automatically and does not have to exist prior to running NXPatcher.
ifbackup (Default value - 0)
If set to 0:
No backing up of current files to {read:backupdir} is performed.
If set to 1:
After the patch file is fully done with the patching procedure, NXPatcher creates a list of all of the new files being created with the new patch. It then takes all of the files in the current maplestory directory and moves them to wherever {read:backupdir} is. The backup procedure always occurs before {read:ifautoapply} happens.
backupdir (Default value - v{nxpatcher:version})
This is the directory in which all of the backup files are stored. This can be an absolute or a relative reference. Never include a trailing slash. This folder is created automatically and does not have to exist prior to running NXPatcher. The location of {read:backupdir} must be on the same volume as {read:outputfolder} or else the files will not be moved.
ifdeltxt (Default value - 1)
If set to 0:
The file {read:deltxt} is not created.
If set to 1:
During the patching procedure, the patch file will request that certain files will be deleted. For the sake of not corrupting perfectly good Maplestory files, these files are never deleted. Instead, a listing of all files requested by the patch file for deletion are placed in {read:deltxt} which is located in the {read:outputfolder} directory.
deltxt (Default value - delfiles.txt)
This file contains all of the files requested for deletion by the patch file. It is always located in the {read:outputfolder} directory.
ifautoapply (Default value - 0)
If set to 0:
The patch files are not automatically applied. They remain in the {read:outputfolder} directory.
If set to 1:
After the backup procedure is performed (see {read:ifbackup}), NXPatcher creates a list of files in the {read:outputfolder} directory and moves all of the files to the current working directory. The empty folders found in {read:outputfolder} are then deleted. Your maplestory directory and {read:outputfolder} must be on the same volume for this to work properly.
[hijack]
All of the variables for the hijack section are exactly the same as those for the read section and perform in exactly the same manner.
[split]
ifoutputbase (Default value - 1)
If set to 0:
The base file is not output during the split procedure
If set to 1:
The base file is output during the split procedure
outputbase (Default value - {nxpatcher:inputfileprefix}.base)
This is the place where the base file is written. This file is only written if {split:ifoutputbase} is set to 1. This can be an absolute or a relative reference.
ifoutputnotice (Default value - 1)
If set to 0:
The notice file is not output during the split procedure
If set to 1:
The notice file is output during the split procedure
outputnotice (Default value - {nxpatcher:inputfileprefix}.txt)
This is the place where the notice file is written. This file is only written if {split:ifoutputnotice} is set to 1. This can be an absolute or a relative reference.
ifoutputpatch (Default value - 1)
If set to 0:
The patch file is not output during the split procedure
If set to 1:
The patch file is output during the split procedure
outputpatch (Default value - {nxpatcher:inputfileprefix}.patch)
This is the place where the patch file is written. This file is only written if {split:ifoutputpatch} is set to 1. This can be an absolute or a relative reference.
[write]
ifinputbase (Default value - 0)
If set to 0:
The default base file is used.
If set to 1:
NXPatcher looks for the base file listed in {write:inputbase} to use instead of the default base file. This file must exist or NXPatcher will throw an exception.
inputbase (Default value - ManualPatch.base)
If the user does not want to use the default base file that NXPatcher provides, set {write:ifinputbase} to 1 and include the location of the base file in {write:inputbase}. NXPatcher will then use this alternate base file instead of its own.
ifinputnotice (Default value - 0)
If set to 0:
The default notice is used. The default notice is "Written by Fiel - http://www.southperry.net/"
If set to 1:
NXPatcher looks for the notice file listed in {write:inputnotice} to use instead of the default notice file. This file must exist or NXPatcher will throw an exception.
inputnotice (Default value - ManualPatch.base)
If the user does not want to use the default notice that NXPatcher provides, set {write:ifinputnotice} to 1 and include the location of the notice file in {write:inputnotice}. NXPatcher will then use this alternate notice instead of its own. Note that the only way the notice is shown during the patching is if the pre-patcher cannot access the internet during the patching procedure.
outputexe (Default value - {nxpatcher:inputfileprefix}.exe)
This is the location where the pre-patcher is written.
ifautorun (Default value - 0)
If set to 0:
The pre-patcher is not automatically started after its creation. It's a good idea to set this to 0 if multiple pre-patchers are being made for redistribution.
If set to 1:
The pre-patcher is automatically started after it's created. It's a good idea to set this to 1 for personal use.
[version]
ifallwzfiles (Default value - 0)
If set to 0:
The version is only determined for the wz file given to NXPatcher
If set to 1:
The version is determined for all of the standard wz files in the same directory as the wz file given to NXPatcher. The "standard files" is a list of the following wz files:
-- Base.wz
-- Character.wz
-- Effect.wz
-- Etc.wz
-- Item.wz
-- Map.wz
-- Mob.wz
-- Npc.wz
-- Quest.wz
-- Reactor.wz
-- Skill.wz
-- Sound.wz
-- String.wz
-- Tamingmob.wz
-- UI.wz
Dynamic variables
Dynamic variables are determined at runtime. Each dynamic variable looks like the following:
{nxpatcher:VAR_NAME}
where VAR_NAME is one of the variables listed below:
version
Determines the version of base.wz in the current directory. Base.wz is used because it is always the most up-to-date wz file.
version_to
Calculates the version of base.wz and adds 1 to it. So if the version of base.wz is 80, version_to will return 81. If 93, then 94.
inputfileprefix
The prefix of a file name is considered to be anything that is not the suffix - such as in the following:
00083to00084.patch
ManualPatch00296to00297.exe
Etc.wz
The prefix is taken from the file name currently being operated on by NXPatcher. As an example, if the write command is being used on "00100to00105.patch" and the {write:outputexe} variable is set to "{nxpatcher:inputfileprefix}.exe", the exe file will be written to "00100to00105.exe"
Example INI File
This is the one that I use every day for every patch:
Code:[fileassociations] patch = read exe = split wz = version [read] ifusefastpatch = 1 outputfolder = Patcher ifbackup = 0 backupdir = v{nxpatcher:version} ifdeltxt = 1 deltxt = {nxpatcher:inputfileprefix}.txt ifautoapply = 1 [hijack] ifusefastpatch = {read:ifusefastpatch} outputfolder = {read:outputfolder} ifbackup = {read:ifbackupfiles} backupdir = {read:backupdir} ifdeltxt = {read:ifdeltxt} deltxt = {read:deltxt} ifautoapply = {read:ifautoapply} [split] ifoutputbase = 0 outputbase = output/{nxpatcher:inputfileprefix}.base ifoutputnotice = 0 outputnotice = output/{nxpatcher:inputfileprefix}.txt ifoutputpatch = 1 outputpatch = output/{nxpatcher:inputfileprefix}.patch [write] ifinputbase = 0 inputbase = ManualPatchUnpack.base ifinputnotice = 0 inputnotice = ManualPatch.txt outputexe = output/{nxpatcher:inputfileprefix}.exe ifautorun = 1 [version] ifallwzfiles = 1
Advanced - Troubleshooting
Q: I tried to apply a patch, but it comes up with an error "Checksums do not match".
A: There are two possible places this can occur - while doing a checksum on the patch file or while doing a checksum on a maplestory file. If, by looking at the command prompt window, you can see that it hasn't tried patching any files yet, you can assume that the checksum failed for the patch file itself. Redownload the patch file, and try using a download manager. If the checksum fails on a specific file, then you either have the wrong patch file (redownload the correct one) or the wrong version of Maplestory (redownload Maplestory). Try using the version command to ensure you are using the correct version with the correct patch.
Q: I tried using read/write/split commands, but the command prompt open and closes.
A: Working as intended. NXPatcher shows no prompts or notifications that it has completed as this can be annoying for people creating multiple pre-patchers. Look in the same directory as NXPatcher for the EXE representing the patch file. For example, if your patch file is named "Weasels.patch", look for the "Weasels.exe" file.
Q: I get an error "Out of memory reallocation!"
A: This means NXPatcher ran out of memory in RAM. Open NXPatcher.ini. In the [read] section, change "usefastpatchtype" to 0. Try running the program again. If you run into the same error, then, in the [hijack] section, change "usefastpatchtype" to 0.
Q: The program won't work and I can't find a solution in this troubleshooting section.
A: Post your problem in this thread. I'm around most days.
Maplestory FTPs
Brazil:
-- Patch
-- Full Download
China:
-- Patch
-- Full Download
Europe:
-- Patch
-- Full Download
Global:
-- Old Patch FTP
-- New Patch FTP (Directory is forbidden)
-- [Versions 63-69] --> Direct Client Download (No Pando) (Remember to change URL for the version)
-- [Versions 70+] --> Direct Client Download (No Pando) (Remember to change URL for the version)
Japan:
-- Patch
-- Full Download
Korea:
-- Patch (Entire directory is forbidden. You have to know the exact link you want)
-- Full Download
Korea Tespia:
-- Patch (Entire directory is forbidden. You have to know the exact link you want)
-- Full Download
Southeast Asia:
-- Patch
-- Full Download (name of download changes with every patch)
-- Manual Patcher (Change URL for versions)
Taiwan:
-- Patch
-- Full Download
Thailand MS:
-- Patch
Patch File Format
This technical documentation demonstrates how the *.patch file format works.
FORMAT:
CHECKSUMS:Code:struct PatchFormat { char WzPatch[9]; //WzPatch\x1A - plus null terminator int version; //Always 0x02 - This is some sort of patch file version unsigned int checksum; unsigned char* zlibBlock; }
All checksums are a very simple CRC32 using the following algorithm.
Code://Official PKZIP CRC32 table generation /* Width: 32 Poly: 0x04C11DB7 Reflect-in: False XOR-in: 0x00000000 Reflect-out: False XOR-out: 0x00000000 */ void Generatesbox(int* sbox) { int remain; int dividend; int bit; for(dividend = 0; dividend < 256; dividend++) { remain = dividend << 24; for(bit = 0; bit < 8; bit++) { if(remain & 0x80000000) { remain = (remain << 1) ^ 0x04C11DB7; } else { remain = (remain << 1); } } sbox[dividend] = remain; } }Nexon's patcher just uses a static array. It's not computed. You can choose to do this however you want. Here is the final array just to make sure you have it correct:Code://This is the standard CRC32 implementation //"rollingChecksum" is used so the caller can maintain the current checksum between function calls unsigned int CalculateChecksum(unsigned char* eachBlock, int* sbox, long lengthOfBlock, unsigned int rollingChecksum) { int IndexLookup; int blockPos; for(blockPos = 0; blockPos < lengthOfBlock; blockPos++) { IndexLookup = (rollingChecksum >> 0x18) ^ eachBlock[blockPos]; rollingChecksum = (rollingChecksum << 0x08) ^ sbox[IndexLookup]; } return rollingChecksum; }
ZLIB BLOCK:Code:int sbox[256] = { 0, 0x4c11db7, 0x9823b6e, 0xd4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x18aeb13, 0x54bf6a4, 0x808d07d, 0xcc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x315d626, 0x7d4cb91, 0xa97ed48, 0xe56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x29f3d35, 0x65e2082, 0xb1d065b, 0xfdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 };
Once the CRC is checked in the patch format header, then read the zlib block and decompress it. Then you can start parsing the decompressed patch file.
Decompressed zlib block:
The decompressed zlib block is comprised of file blocks. Each file block contains a string terminated by 0x00, 0x01, or 0x02. The terminator tells the patcher what to do with the given file or folder.
The patch data immediately begins with a file block which is parsed like this:
0x00 FILE BLOCK:Code://unsigned char* zlibBlock = A pointer to the current place in the unpacked zlib stream //unsigned char* zlibBlockPtr = A pointer to the beginning of the unpacked zlib stream //int unpackedLengthZlib = The length of the decompressed zlib stream //char checkByte = The current byte being read from the zlib stream //char fileName[260] = The file name that we are working on now do { checkByte = GetByte(&zlibBlock); switch(checkByte) { case 0x00: //Create file OR folder break; case 0x01: //Rebuild the file break; case 0x02: //Delete the file OR folder break; default: //Add checkByte to the end of fileName } }while((zlibBlock - zlibBlockPtr) < unpackedLenZlibBlock); //Basically, continue parsing file blocks until the final offset is reached.
If the last byte in the fileName is '/' or '\\', then it's a folder. Simply create the folder.
Otherwise, we need to create a file.
Get the length and the checksum, calculate the checksum of the file and write it to disk. It's fairly straightforward.Code:struct NewFile { int length; unsigned int checksum; unsigned char* bytes; };
0x02 FILE BLOCK:
In this case, we're deleting a file or folder. Just delete it.
0x01 FILE BLOCK:
The 0x01 file block contains instructions for how to take the current file and rebuild it to the newest version.
There are many different ways that you can parse this block. It all depends on how much memory you want to consume. Nexon's patcher uses a 0x10000 byte cache for everything. This is why Nexon's patcher runs so terribly slow. The cache is way too small.
The header for this file block is quite simple:
So verify the checksum of the old file, then proceed to read the series of build blocks that give you directions on how to rebuild the file. The build blocks are parsed like this:Code:struct RebuildBlock { unsigned int oldFileChecksum; unsigned int newFileChecksum; };
As defined in the for loop, you stop reading blocks once a build block definition reads 0x00000000.Code:for(Command = GetInt(&zlibBlock); Command != 0x00000000; Command = GetInt(&zlibBlock)) { if((Command & 0xC0000000) == 0xC0000000) { //This is a repeat block. It's essentially run length encoding. repeatedByte = Command & 0x000000FF lengthOfBlock = (Command & 0x3FFFFF00) >> 8; //use memset in C to write to a buffer containing the repeatedByte for lengthOfBlock number of bytes, then write it to the file. } else if((Command & 0x80000000) == 0x80000000) { // This is a direct write block. The bytes to be written are contained directly in the zlib stream. Simply write these bytes out to the file. lengthOfBlock = Command & 0x7FFFFFFF; } else { //This means we take from the old file and write to the new file. lengthOfBlock = Command oldFileOffset = GetInt(&zlibBlock); //So open the old file, seek to the oldFileOffset, read lengthOfBlock number of bytes, then write those bytes to the new file. } }
It is STRONGLY recommended to calculate the new checksum while you have the bytes for the new file in memory. It's hundreds of times faster than writing to the hard disk and then calculating the new checksum from the disk.
MISTAKE:
There is a mistake in Nexon's algorithm for rebuilding files. The mistake can occur in the case where a 0x80000000 block contains more than 0x40000000 number of bytes. In this extremely unlikely event, the wrong kind of block will get parsed (0xC0000000 - a repeat block) and the resulting file will be wrong.
To fix this, the lengthOfBlock should be "Command & 0x3FFFFFFF" and Nexon should fix this in their patch builder.
The file rebuilding algorithm is stolen directly from the patcher.exe program.
CONCLUSION:
That's pretty much it. This is the Nexon patch format. It's overall extremely simple to parse and highly efficient. Nexon definitely deserves some praise for a good, custom file format.
Patch File Format - version 2
A new version was released along with the 64-bit client to go with the new WZ file structure.
The format of the .patch file is the same as version 1 except for the following differences:
Preamble
The decompressed patch file begins with a preamble block that looks like this:
The Preamble contains all files that should be in the Maplestory directory, not just the files that will be patched. Because of changes elsewhere in the format, it's critical that all files' CRCs are checked.Code:int numFiles = GetInt(); for(int i = 0; i < numFiles; i++) { int lenFileName = GetInt(); string fileName = File.Read(lenFileName); uint crc = GetInt(); }
File Block - Type 0x01
This is the Rebuild type of file block. The header to this file block is slightly changed. It only contains the CRC of the new completed file. The crc of the old file is removed because that CRC is contained in the preamble.
There is one change to the "Copy" part of the file block in the rebuild for-loop:
Before in the rebuild block, you were only taking the bytes from the old file and writing to the new one. The new rebuild block allows you to take bytes from any file in the Maplestory directory to add to the new file. Now it becomes obvious why the preamble is needed!Code:else { blockLength = Command offset = GetInt(); lenStr = GetInt(); fileName = File.Read(lenStr); // open fileName, go to offset, and read blockLength number of bytes and write those bytes to the new file }
This concludes the summary of changes.
Finding the difference between version 1 and version 2:
Although I previously defined there being a patch file version integer after the "WzPatch\x1A" in the header, Nexon didn't bother to update this number for this new version of patch file format. Therefore, you need to do a little bit more work to figure out which version it is.
To figure out which version it is, we'll use the fact that version 2 contains a preamble that starts with a 32-bit integer that contains numFiles. It is highly unlikely that the patcher will contain more than 0xFFFF number of files. So, decompress the first 16 bytes of the zlib block and check bytes 3 and 4 of the block. If those bytes are both zero, then you're working with version 2. If they are not both zero, then you are working with version 1.
Want quick and easy notification of just patches & updates? Follow PatchWatcher on twitter, or visit http://www.PatchWatcher.net/ and monitor just the patches you're interested in.
Bookmarks