├── .gitattributes
├── .gitignore
├── CREDITS.txt
├── LICENSE
├── MapleCryptoLib
├── AESEncryption.cs
├── CryptoConstants.cs
├── MapleCrypto.cs
└── MapleCustomEncryption.cs
├── MapleLib.csproj
├── MapleLib.sln
├── PacketLib
├── AbstractPacket.cs
├── Acceptor.cs
├── Connector.cs
├── HexEncoding.cs
├── Monitor.cs
├── PacketReader.cs
├── PacketWriter.cs
├── Session.cs
├── SessionType.cs
└── SocketInfo.cs
├── Properties
└── AssemblyInfo.cs
├── README.md
├── WzLib
├── APropertyContainer.cs
├── AWzImageProperty.cs
├── AWzObject.cs
├── IExtended.cs
├── Util
│ ├── MP3Header.cs
│ ├── WzBinaryReader.cs
│ ├── WzBinaryWriter.cs
│ ├── WzKeyGenerator.cs
│ ├── WzTool.cs
│ └── XmlUtil.cs
├── WzDirectory.cs
├── WzFile.cs
├── WzHeader.cs
├── WzImage.cs
├── WzListFile.cs
├── WzMapleVersion.cs
├── WzObjectType.cs
├── WzProperties
│ ├── WzByteFloatProperty.cs
│ ├── WzCanvasProperty.cs
│ ├── WzCompressedIntProperty.cs
│ ├── WzCompressedLongProperty.cs
│ ├── WzConvexProperty.cs
│ ├── WzDoubleProperty.cs
│ ├── WzNullProperty.cs
│ ├── WzPngProperty.cs
│ ├── WzRawDataProperty.cs
│ ├── WzShortProperty.cs
│ ├── WzSoundProperty.cs
│ ├── WzStringProperty.cs
│ ├── WzSubProperty.cs
│ ├── WzUOLProperty.cs
│ └── WzVectorProperty.cs
└── WzPropertyType.cs
├── app.config
└── packages.config
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
--------------------------------------------------------------------------------
/CREDITS.txt:
--------------------------------------------------------------------------------
1 | OWNER
2 | snow
3 |
4 | CONTRIBUTORS
5 | jonyleeson (Original WzLib)
6 | haha01haha01(HaRepacker)
7 | haha01haha01(Updates)
8 | Xterminator
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Johnny
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MapleCryptoLib/AESEncryption.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Security.Cryptography;
4 |
5 | namespace MapleLib.MapleCryptoLib {
6 | ///
7 | /// Class to handle the AES Encryption routines
8 | ///
9 | public class AESEncryption {
10 | ///
11 | /// Encrypt data using MapleStory's AES algorithm
12 | ///
13 | /// IV to use for encryption
14 | /// Data to encrypt
15 | /// Length of data
16 | /// Crypted data
17 | public static byte[] aesCrypt(byte[] pIV, byte[] pData, int pLength) {
18 | return aesCrypt(pIV, pData, pLength, CryptoConstants.TrimmedUserKey);
19 | }
20 |
21 | ///
22 | /// Encrypt data using MapleStory's AES method
23 | ///
24 | /// IV to use for encryption
25 | /// data to encrypt
26 | /// length of data
27 | /// the AES key to use
28 | /// Crypted data
29 | public static byte[] aesCrypt(byte[] pIV, byte[] pData, int pLength, byte[] pKey) {
30 | AesManaged crypto = new AesManaged { KeySize = 256, Key = pKey, Mode = CipherMode.ECB };
31 |
32 | MemoryStream memStream = new MemoryStream();
33 | CryptoStream cryptoStream = new CryptoStream(memStream, crypto.CreateEncryptor(), CryptoStreamMode.Write);
34 |
35 | int remaining = pLength;
36 | int llength = 0x5B0;
37 | int start = 0;
38 | while (remaining > 0) {
39 | byte[] myIV = MapleCrypto.multiplyBytes(pIV, 4, 4);
40 | if (remaining < llength) {
41 | llength = remaining;
42 | }
43 | for (int x = start; x < (start + llength); x++) {
44 | if ((x - start) % myIV.Length == 0) {
45 | cryptoStream.Write(myIV, 0, myIV.Length);
46 | byte[] newIV = memStream.ToArray();
47 | Array.Copy(newIV, myIV, myIV.Length);
48 | memStream.Position = 0;
49 | }
50 | pData[x] ^= myIV[(x - start) % myIV.Length];
51 | }
52 | start += llength;
53 | remaining -= llength;
54 | llength = 0x5B4;
55 | }
56 |
57 | try {
58 | cryptoStream.Dispose();
59 | memStream.Dispose();
60 | } catch (Exception e) {
61 | Console.WriteLine("Error disposing AES streams" + e);
62 | }
63 |
64 | return pData;
65 | }
66 | }
67 | }
--------------------------------------------------------------------------------
/MapleCryptoLib/CryptoConstants.cs:
--------------------------------------------------------------------------------
1 | namespace MapleLib.MapleCryptoLib {
2 | ///
3 | /// Contains all the constant values used for various functions
4 | ///
5 | public static class CryptoConstants {
6 | ///
7 | /// AES UserKey used by MapleStory
8 | ///
9 | public static readonly byte[] UserKey = new byte[] { //16 * 8
10 | 0x13, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00,
11 | 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
12 | 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
13 | 0xB4, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
14 | 0x1B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
15 | 0x0F, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,
16 | 0x33, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
17 | 0x52, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00
18 | };
19 |
20 | ///
21 | /// Trimmed AES UserKey used by MapleStory
22 | ///
23 | public static readonly byte[] TrimmedUserKey = new byte[] { //4 * 8
24 | 0x13, 0x00, 0x00, 0x00,
25 | 0x08, 0x00, 0x00, 0x00,
26 | 0x06, 0x00, 0x00, 0x00,
27 | 0xB4, 0x00, 0x00, 0x00,
28 | 0x1B, 0x00, 0x00, 0x00,
29 | 0x0F, 0x00, 0x00, 0x00,
30 | 0x33, 0x00, 0x00, 0x00,
31 | 0x52, 0x00, 0x00, 0x00,
32 | };
33 |
34 | ///
35 | /// ShuffleBytes used my MapleStory to generate a new IV
36 | ///
37 | public static readonly byte[] bShuffle = new byte[] {//16 * 16
38 | 0xEC, 0x3F, 0x77, 0xA4, 0x45, 0xD0, 0x71, 0xBF, 0xB7, 0x98, 0x20, 0xFC, 0x4B, 0xE9, 0xB3, 0xE1,
39 | 0x5C, 0x22, 0xF7, 0x0C, 0x44, 0x1B, 0x81, 0xBD, 0x63, 0x8D, 0xD4, 0xC3, 0xF2, 0x10, 0x19, 0xE0,
40 | 0xFB, 0xA1, 0x6E, 0x66, 0xEA, 0xAE, 0xD6, 0xCE, 0x06, 0x18, 0x4E, 0xEB, 0x78, 0x95, 0xDB, 0xBA,
41 | 0xB6, 0x42, 0x7A, 0x2A, 0x83, 0x0B, 0x54, 0x67, 0x6D, 0xE8, 0x65, 0xE7, 0x2F, 0x07, 0xF3, 0xAA,
42 | 0x27, 0x7B, 0x85, 0xB0, 0x26, 0xFD, 0x8B, 0xA9, 0xFA, 0xBE, 0xA8, 0xD7, 0xCB, 0xCC, 0x92, 0xDA,
43 | 0xF9, 0x93, 0x60, 0x2D, 0xDD, 0xD2, 0xA2, 0x9B, 0x39, 0x5F, 0x82, 0x21, 0x4C, 0x69, 0xF8, 0x31,
44 | 0x87, 0xEE, 0x8E, 0xAD, 0x8C, 0x6A, 0xBC, 0xB5, 0x6B, 0x59, 0x13, 0xF1, 0x04, 0x00, 0xF6, 0x5A,
45 | 0x35, 0x79, 0x48, 0x8F, 0x15, 0xCD, 0x97, 0x57, 0x12, 0x3E, 0x37, 0xFF, 0x9D, 0x4F, 0x51, 0xF5,
46 | 0xA3, 0x70, 0xBB, 0x14, 0x75, 0xC2, 0xB8, 0x72, 0xC0, 0xED, 0x7D, 0x68, 0xC9, 0x2E, 0x0D, 0x62,
47 | 0x46, 0x17, 0x11, 0x4D, 0x6C, 0xC4, 0x7E, 0x53, 0xC1, 0x25, 0xC7, 0x9A, 0x1C, 0x88, 0x58, 0x2C,
48 | 0x89, 0xDC, 0x02, 0x64, 0x40, 0x01, 0x5D, 0x38, 0xA5, 0xE2, 0xAF, 0x55, 0xD5, 0xEF, 0x1A, 0x7C,
49 | 0xA7, 0x5B, 0xA6, 0x6F, 0x86, 0x9F, 0x73, 0xE6, 0x0A, 0xDE, 0x2B, 0x99, 0x4A, 0x47, 0x9C, 0xDF,
50 | 0x09, 0x76, 0x9E, 0x30, 0x0E, 0xE4, 0xB2, 0x94, 0xA0, 0x3B, 0x34, 0x1D, 0x28, 0x0F, 0x36, 0xE3,
51 | 0x23, 0xB4, 0x03, 0xD8, 0x90, 0xC8, 0x3C, 0xFE, 0x5E, 0x32, 0x24, 0x50, 0x1F, 0x3A, 0x43, 0x8A,
52 | 0x96, 0x41, 0x74, 0xAC, 0x52, 0x33, 0xF0, 0xD9, 0x29, 0x80, 0xB1, 0x16, 0xD3, 0xAB, 0x91, 0xB9,
53 | 0x84, 0x7F, 0x61, 0x1E, 0xCF, 0xC5, 0xD1, 0x56, 0x3D, 0xCA, 0xF4, 0x05, 0xC6, 0xE5, 0x08, 0x49
54 | };
55 |
56 | ///
57 | /// Default AES Key used to generate a new IV
58 | ///
59 | public static byte[] bDefaultAESKeyValue = new byte[] { 0xC6, 0x50, 0x53, 0xF2, 0xA8, 0x42, 0x9D, 0x7F, 0x77, 0x09, 0x1D, 0x26, 0x42, 0x53, 0x88, 0x7C, };
60 |
61 | ///
62 | /// IV used to create the WzKey for GMS
63 | ///
64 | public static readonly byte[] WZ_GMSIV = new byte[] { 0x4D, 0x23, 0xC7, 0x2B };
65 |
66 | ///
67 | /// IV used to create the WzKey for MSEA
68 | ///
69 | public static readonly byte[] WZ_MSEAIV = new byte[] { 0xB9, 0x7D, 0x63, 0xE9 };
70 |
71 | ///
72 | /// Constant used in WZ offset encryption
73 | ///
74 | public const uint WZ_OffsetConstant = 0x581C3F6D;
75 |
76 | ///
77 | /// Trims the AES UserKey for use an AES cryptor
78 | ///
79 | public static byte[] createTrimmedUserKey() {
80 | byte[] key = new byte[32];
81 | for (int i = 0; i < 128; i += 16) {
82 | key[i / 4] = UserKey[i];
83 | }
84 | return key;
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/MapleCryptoLib/MapleCrypto.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace MapleLib.MapleCryptoLib {
4 | ///
5 | /// Class to manage Encryption and IV generation
6 | ///
7 | public class MapleCrypto {
8 | #region Properties
9 |
10 | ///
11 | /// (private) IV used in the packet encryption
12 | ///
13 | private byte[] mIV;
14 |
15 | ///
16 | /// Version of MapleStory used in encryption
17 | ///
18 | private readonly short mMapleVersion;
19 |
20 | ///
21 | /// (public) IV used in the packet encryption
22 | ///
23 | public byte[] IV { get { return mIV; } set { mIV = value; } }
24 |
25 | #endregion
26 |
27 | #region Methods
28 |
29 | ///
30 | /// Creates a new MapleCrypto class
31 | ///
32 | /// Intializing Vector
33 | /// Version of MapleStory
34 | public MapleCrypto(byte[] pIV, short pMapleVersion) {
35 | mIV = pIV;
36 | mMapleVersion = pMapleVersion;
37 | }
38 |
39 | ///
40 | /// Updates the current IV
41 | ///
42 | public void updateIV() {
43 | mIV = getNewIV(mIV);
44 | }
45 |
46 | ///
47 | /// Encrypts data with AES and updates the IV
48 | ///
49 | /// The data to crypt
50 | public void crypt(byte[] pData) {
51 | AESEncryption.aesCrypt(mIV, pData, pData.Length);
52 | updateIV();
53 | }
54 |
55 | ///
56 | /// Generates a new IV
57 | ///
58 | /// The Old IV used to generate the new IV
59 | /// A new IV
60 | public static byte[] getNewIV(byte[] pOldIV) {
61 | //byte[] start = CryptoConstants.bDefaultAESKeyValue;
62 | byte[] start = new byte[] { 0xf2, 0x53, 0x50, 0xc6 }; //TODO: ADD GLOBAL VAR BACK
63 | for (int i = 0; i < 4; i++) {
64 | shuffle(pOldIV[i], start);
65 | }
66 | return start;
67 | }
68 |
69 | ///
70 | /// Shuffle the bytes in the IV
71 | ///
72 | /// Byte of the old IV
73 | /// The Default AES Key
74 | /// The shuffled bytes
75 | public static byte[] shuffle(byte pInputByte, byte[] pStart) {
76 | byte a = pStart[1];
77 | byte b = a;
78 | uint c, d;
79 | b = CryptoConstants.bShuffle[b];
80 | b -= pInputByte;
81 | pStart[0] += b;
82 | b = pStart[2];
83 | b ^= CryptoConstants.bShuffle[pInputByte];
84 | a -= b;
85 | pStart[1] = a;
86 | a = pStart[3];
87 | b = a;
88 | a -= pStart[0];
89 | b = CryptoConstants.bShuffle[b];
90 | b += pInputByte;
91 | b ^= pStart[2];
92 | pStart[2] = b;
93 | a += CryptoConstants.bShuffle[pInputByte];
94 | pStart[3] = a;
95 |
96 | c = (uint)(pStart[0] + pStart[1] * 0x100 + pStart[2] * 0x10000 + pStart[3] * 0x1000000);
97 | d = c;
98 | c >>= 0x1D;
99 | d <<= 0x03;
100 | c |= d;
101 | pStart[0] = (byte)(c % 0x100);
102 | c /= 0x100;
103 | pStart[1] = (byte)(c % 0x100);
104 | c /= 0x100;
105 | pStart[2] = (byte)(c % 0x100);
106 | pStart[3] = (byte)(c / 0x100);
107 |
108 | return pStart;
109 | }
110 |
111 | ///
112 | /// Get a packet header for a packet being sent to the server
113 | ///
114 | /// Size of the packet
115 | /// The packet header
116 | public byte[] getHeaderToClient(int pSize) {
117 | byte[] header = new byte[4];
118 | int a = mIV[3] * 0x100 + mIV[2];
119 | a ^= -(mMapleVersion + 1);
120 | int b = a ^ pSize;
121 | header[0] = (byte)(a % 0x100);
122 | header[1] = (byte)((a - header[0]) / 0x100);
123 | header[2] = (byte)(b ^ 0x100);
124 | header[3] = (byte)((b - header[2]) / 0x100);
125 | return header;
126 | }
127 |
128 | ///
129 | /// Get a packet header for a packet being sent to the client
130 | ///
131 | /// Size of the packet
132 | /// The packet header
133 | public byte[] getHeaderToServer(int pSize) {
134 | byte[] header = new byte[4];
135 | int a = IV[3] * 0x100 + IV[2];
136 | a = a ^ (mMapleVersion);
137 | int b = a ^ pSize;
138 | header[0] = Convert.ToByte(a % 0x100);
139 | header[1] = Convert.ToByte(a / 0x100);
140 | header[2] = Convert.ToByte(b % 0x100);
141 | header[3] = Convert.ToByte(b / 0x100);
142 | return header;
143 | }
144 |
145 | ///
146 | /// Gets the length of a packet from the header
147 | ///
148 | /// Header of the packet
149 | /// The length of the packet
150 | public static int getPacketLength(int pPacketHeader) {
151 | return getPacketLength(BitConverter.GetBytes(pPacketHeader));
152 | }
153 |
154 | ///
155 | /// Gets the length of a packet from the header
156 | ///
157 | /// Header of the packet
158 | /// The length of the packet
159 | public static int getPacketLength(byte[] pPacketHeader) {
160 | if (pPacketHeader.Length < 4) {
161 | return -1;
162 | }
163 | return (pPacketHeader[0] + (pPacketHeader[1] << 8)) ^ (pPacketHeader[2] + (pPacketHeader[3] << 8));
164 | }
165 |
166 | ///
167 | /// Checks to make sure the packet is a valid MapleStory packet
168 | ///
169 | /// The header of the packet received
170 | /// The packet is valid
171 | public bool checkPacketToServer(byte[] pPacket) {
172 | int a = pPacket[0] ^ mIV[2];
173 | int b = mMapleVersion;
174 | int c = pPacket[1] ^ mIV[3];
175 | int d = mMapleVersion >> 8;
176 | return (a == b && c == d);
177 | }
178 |
179 | ///
180 | /// Multiplies bytes
181 | ///
182 | /// Bytes to multiply
183 | /// Amount of bytes to repeat
184 | /// Times to repeat the packet
185 | /// The multiplied bytes
186 | public static byte[] multiplyBytes(byte[] pInput, int pCount, int pMult) {
187 | byte[] ret = new byte[pCount * pMult];
188 | for (int x = 0; x < ret.Length; x++) {
189 | ret[x] = pInput[x % pCount];
190 | }
191 | return ret;
192 | }
193 |
194 | #endregion
195 | }
196 | }
--------------------------------------------------------------------------------
/MapleCryptoLib/MapleCustomEncryption.cs:
--------------------------------------------------------------------------------
1 | namespace MapleLib.MapleCryptoLib {
2 | ///
3 | /// Class to handle the MapleStory Custom Encryption routines
4 | ///
5 | public class MapleCustomEncryption {
6 | ///
7 | /// Encrypt data using MapleStory's Custom Encryption
8 | ///
9 | /// data to encrypt
10 | /// Encrypted data
11 | public static void Encrypt(byte[] pData) {
12 | int size = pData.Length;
13 | int j;
14 | byte a, c;
15 | for (int i = 0; i < 3; i++) {
16 | a = 0;
17 | for (j = size; j > 0; j--) {
18 | c = pData[size - j];
19 | c = rol(c, 3);
20 | c = (byte)(c + j);
21 | c ^= a;
22 | a = c;
23 | c = ror(a, j);
24 | c ^= 0xFF;
25 | c += 0x48;
26 | pData[size - j] = c;
27 | }
28 | a = 0;
29 | for (j = pData.Length; j > 0; j--) {
30 | c = pData[j - 1];
31 | c = rol(c, 4);
32 | c = (byte)(c + j);
33 | c ^= a;
34 | a = c;
35 | c ^= 0x13;
36 | c = ror(c, 3);
37 | pData[j - 1] = c;
38 | }
39 | }
40 | }
41 |
42 | ///
43 | /// Decrypt data using MapleStory's Custom Encryption
44 | ///
45 | /// data to decrypt
46 | /// Decrypted data
47 | public static void Decrypt(byte[] pData) {
48 | int size = pData.Length;
49 | int j;
50 | byte a, b, c;
51 | for (int i = 0; i < 3; i++) {
52 | b = 0;
53 | for (j = size; j > 0; j--) {
54 | c = pData[j - 1];
55 | c = rol(c, 3);
56 | c ^= 0x13;
57 | a = c;
58 | c ^= b;
59 | c = (byte)(c - j); // Guess this is supposed to be right?
60 | c = ror(c, 4);
61 | b = a;
62 | pData[j - 1] = c;
63 | }
64 | b = 0;
65 | for (j = size; j > 0; j--) {
66 | c = pData[size - j];
67 | c -= 0x48;
68 | c ^= 0xFF;
69 | c = rol(c, j);
70 | a = c;
71 | c ^= b;
72 | c = (byte)(c - j); // Guess this is supposed to be right?
73 | c = ror(c, 3);
74 | b = a;
75 | pData[size - j] = c;
76 | }
77 | }
78 | }
79 |
80 | ///
81 | /// Rolls a byte left
82 | ///
83 | /// input byte to roll
84 | /// amount of bits to roll
85 | /// The left rolled byte
86 | public static byte rol(byte pVal, int pNum) {
87 | int highbit;
88 | for (int i = 0; i < pNum; i++) {
89 | highbit = ((pVal & 0x80) != 0 ? 1 : 0);
90 | pVal <<= 1;
91 | pVal |= (byte)highbit;
92 | }
93 | return pVal;
94 | }
95 |
96 | ///
97 | /// Rolls a byte right
98 | ///
99 | /// input byte to roll
100 | /// amount of bits to roll
101 | /// The right rolled byte
102 | public static byte ror(byte pVal, int pNum) {
103 | int lowbit;
104 | for (int i = 0; i < pNum; i++) {
105 | lowbit = ((pVal & 1) != 0 ? 1 : 0);
106 | pVal >>= 1;
107 | pVal |= (byte)(lowbit << 7);
108 | }
109 | return pVal;
110 | }
111 | }
112 | }
--------------------------------------------------------------------------------
/MapleLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.30729
7 | 2.0
8 | {28AAB36D-942E-4476-A000-0E9DE380F390}
9 | Library
10 | Properties
11 | MapleLib
12 | MapleLib
13 | v4.7.2
14 | 512
15 |
16 |
17 |
18 |
19 | 3.5
20 |
21 |
22 |
23 |
24 |
25 | true
26 | full
27 | false
28 | bin\Debug\
29 | DEBUG;TRACE
30 | prompt
31 | 4
32 | AllRules.ruleset
33 | false
34 |
35 |
36 | pdbonly
37 | true
38 | bin\Release\
39 |
40 |
41 | prompt
42 | 4
43 | AllRules.ruleset
44 | false
45 |
46 |
47 | false
48 |
49 |
50 |
51 |
52 |
53 |
54 | ..\WZ-Dumper\packages\NAudio.Core.2.2.1\lib\netstandard2.0\NAudio.Core.dll
55 |
56 |
57 |
58 | 3.5
59 |
60 |
61 |
62 | 3.5
63 |
64 |
65 | 3.5
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
132 |
--------------------------------------------------------------------------------
/MapleLib.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28010.2003
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapleLib", "MapleLib.csproj", "{28AAB36D-942E-4476-A000-0E9DE380F390}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {28AAB36D-942E-4476-A000-0E9DE380F390}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {28AAB36D-942E-4476-A000-0E9DE380F390}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {28AAB36D-942E-4476-A000-0E9DE380F390}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {28AAB36D-942E-4476-A000-0E9DE380F390}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {95A5E99F-C013-446C-AEC6-87DF783CAED5}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/PacketLib/AbstractPacket.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace MapleLib.PacketLib {
4 | public abstract class AbstractPacket {
5 | protected MemoryStream mBuffer;
6 |
7 | public byte[] ToArray() {
8 | return mBuffer.ToArray();
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/PacketLib/Acceptor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Sockets;
4 |
5 | namespace MapleLib.PacketLib {
6 | ///
7 | /// A Nework Socket Acceptor (Listener)
8 | ///
9 | public class Acceptor {
10 | ///
11 | /// The listener socket
12 | ///
13 | private readonly Socket mListener;
14 |
15 | ///
16 | /// Method called when a client is connected
17 | ///
18 | public delegate void ClientConnectedHandler(Session pSession);
19 |
20 | ///
21 | /// Client connected event
22 | ///
23 | public event ClientConnectedHandler OnClientConnected;
24 |
25 | ///
26 | /// Creates a new instance of Acceptor
27 | ///
28 | public Acceptor() {
29 | mListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
30 | }
31 |
32 | ///
33 | /// Starts listening and accepting connections
34 | ///
35 | /// Port to listen to
36 | public void StartListening(int pPort) {
37 | mListener.Bind(new IPEndPoint(IPAddress.Any, pPort));
38 | mListener.Listen(15);
39 | mListener.BeginAccept(new AsyncCallback(OnClientConnect), null);
40 | }
41 |
42 | ///
43 | /// Stops listening for connections
44 | ///
45 | public void StopListening() {
46 | mListener.Disconnect(true);
47 | }
48 |
49 | ///
50 | /// Client connected handler
51 | ///
52 | /// The IAsyncResult
53 | private void OnClientConnect(IAsyncResult pIAR) {
54 | try {
55 | Socket socket = mListener.EndAccept(pIAR);
56 | Session session = new Session(socket, SessionType.SERVER_TO_CLIENT);
57 |
58 | if (OnClientConnected != null)
59 | OnClientConnected(session);
60 |
61 | session.WaitForData();
62 |
63 | mListener.BeginAccept(new AsyncCallback(OnClientConnect), null);
64 | } catch (ObjectDisposedException) {
65 | Console.WriteLine("[Error] OnClientConnect: Socket closed.");
66 | } catch (Exception se) {
67 | Console.WriteLine("[Error] OnClientConnect: " + se);
68 | }
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/PacketLib/Connector.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Net.Sockets;
3 |
4 | namespace MapleLib.PacketLib {
5 | ///
6 | /// Socket class to connect to a listener
7 | ///
8 | public class Connector {
9 | ///
10 | /// The connecting socket
11 | ///
12 | private readonly Socket mSocket;
13 |
14 | ///
15 | /// Method called when the client connects
16 | ///
17 | public delegate void ClientConnectedHandler(Session pSession);
18 |
19 | ///
20 | /// Client connected event
21 | ///
22 | public event ClientConnectedHandler OnClientConnected;
23 |
24 | ///
25 | /// Creates a new instance of Acceptor
26 | ///
27 | public Connector() {
28 | mSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
29 | }
30 |
31 | ///
32 | /// Connects to a listener
33 | ///
34 | /// IPEndPoint of listener
35 | /// Session connecting to
36 | public Session Connect(IPEndPoint pEP) {
37 | mSocket.Connect(pEP);
38 | return CreateSession();
39 | }
40 |
41 | ///
42 | /// Connects to a listener
43 | ///
44 | /// IPAdress of listener
45 | /// Port of listener
46 | /// Session connecting to
47 | public Session Connect(IPAddress pIP, int pPort) {
48 | mSocket.Connect(pIP, pPort);
49 | return CreateSession();
50 | }
51 |
52 | ///
53 | /// Connects to a listener
54 | ///
55 | /// IPAdress's of listener
56 | /// Port of listener
57 | /// Session connecting to
58 | public Session Connect(IPAddress[] pIP, int pPort) {
59 | mSocket.Connect(pIP, pPort);
60 | return CreateSession();
61 | }
62 |
63 | ///
64 | /// Connects to a listener
65 | ///
66 | /// IPAdress of listener
67 | /// Port of listener
68 | /// Session connecting to
69 | public Session Connect(string pIP, int pPort) {
70 | mSocket.Connect(pIP, pPort);
71 | return CreateSession();
72 | }
73 |
74 | ///
75 | /// Creates the session after connecting
76 | ///
77 | /// Session created with listener
78 | private Session CreateSession() {
79 | Session session = new Session(mSocket, SessionType.CLIENT_TO_SERVER);
80 |
81 | if (OnClientConnected != null)
82 | OnClientConnected(session);
83 |
84 | session.WaitForDataNoEncryption();
85 |
86 | return session;
87 | }
88 | }
89 | }
--------------------------------------------------------------------------------
/PacketLib/HexEncoding.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 |
4 | namespace MapleLib.PacketLib {
5 | ///
6 | /// Class to handle Hex Encoding and Hex Conversions
7 | ///
8 | public class HexEncoding {
9 | ///
10 | /// Checks if a character is a hex digit
11 | ///
12 | /// Char to check
13 | /// Char is a hex digit
14 | public static bool IsHexDigit(Char pChar) {
15 | int numChar;
16 | int numA = Convert.ToInt32('A');
17 | int num1 = Convert.ToInt32('0');
18 | pChar = Char.ToUpper(pChar);
19 | numChar = Convert.ToInt32(pChar);
20 | if (numChar >= numA && numChar < (numA + 6))
21 | return true;
22 | if (numChar >= num1 && numChar < (num1 + 10))
23 | return true;
24 | return false;
25 | }
26 |
27 | ///
28 | /// Convert a hex string to a byte
29 | ///
30 | /// Byte as a hex string
31 | /// Byte representation of the string
32 | private static byte HexToByte(string pHex) {
33 | if (pHex.Length > 2 || pHex.Length <= 0)
34 | throw new ArgumentException("hex must be 1 or 2 characters in length");
35 | byte newByte = byte.Parse(pHex, NumberStyles.HexNumber);
36 | return newByte;
37 | }
38 |
39 | ///
40 | /// Convert a hex string to a byte array
41 | ///
42 | /// byte array as a hex string
43 | /// Byte array representation of the string
44 | public static byte[] GetBytes(string pHexString) {
45 | string newString = string.Empty;
46 | char c;
47 | // remove all none A-F, 0-9, characters
48 | for (int i = 0; i < pHexString.Length; i++) {
49 | c = pHexString[i];
50 | if (IsHexDigit(c))
51 | newString += c;
52 | }
53 | // if odd number of characters, discard last character
54 | if (newString.Length % 2 != 0) {
55 | newString = newString.Substring(0, newString.Length - 1);
56 | }
57 |
58 | int byteLength = newString.Length / 2;
59 | byte[] bytes = new byte[byteLength];
60 | string hex;
61 | int j = 0;
62 | for (int i = 0; i < bytes.Length; i++) {
63 | hex = new String(new[] { newString[j], newString[j + 1] });
64 | bytes[i] = HexToByte(hex);
65 | j = j + 2;
66 | }
67 | return bytes;
68 | }
69 |
70 | ///
71 | /// Convert byte array to ASCII
72 | ///
73 | /// Bytes to convert to ASCII
74 | /// The byte array as an ASCII string
75 | public static String ToStringFromAscii(byte[] pBytes) {
76 | char[] ret = new char[pBytes.Length];
77 | for (int x = 0; x < pBytes.Length; x++) {
78 | if (pBytes[x] < 32 && pBytes[x] >= 0) {
79 | ret[x] = '.';
80 | } else {
81 | int chr = (pBytes[x]) & 0xFF;
82 | ret[x] = (char)chr;
83 | }
84 | }
85 | return new String(ret);
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/PacketLib/Monitor.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.MapleCryptoLib;
2 | using System;
3 | using System.Net;
4 | using System.Net.Sockets;
5 |
6 | namespace MapleLib.PacketLib {
7 | public class Monitor {
8 | ///
9 | /// The Monitor socket
10 | ///
11 | private readonly Socket mSocket;
12 |
13 | ///
14 | /// Method to handle packets received
15 | ///
16 | public delegate void PacketReceivedHandler(PacketReader pPacket);
17 |
18 | ///
19 | /// Packet received event
20 | ///
21 | //public event PacketReceivedHandler OnPacketReceived;//Unused
22 | ///
23 | /// Method to handle client disconnected
24 | ///
25 | public delegate void ClientDisconnectedHandler(Monitor pMonitor);
26 |
27 | ///
28 | /// Client disconnected event
29 | ///
30 | public event ClientDisconnectedHandler OnClientDisconnected;
31 |
32 | ///
33 | /// The Recieved packet crypto manager
34 | ///
35 | public MapleCrypto RIV { get; set; }
36 |
37 | ///
38 | /// The Sent packet crypto manager
39 | ///
40 | public MapleCrypto SIV { get; set; }
41 |
42 | ///
43 | /// The Monitor's socket
44 | ///
45 | public Socket Socket { get { return mSocket; } }
46 |
47 | ///
48 | /// Creates a new instance of Monitor
49 | ///
50 | public Monitor() {
51 | mSocket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
52 | }
53 |
54 | ///
55 | /// Starts listening and accepting connections
56 | ///
57 | /// Port to listen to
58 | public void StartMonitoring(IPAddress pIP) {
59 | mSocket.Bind(new IPEndPoint(pIP, 0));
60 |
61 | mSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, true); //??
62 |
63 | byte[] byIn = new byte[4] { 1, 0, 0, 0 };
64 | byte[] byOut = null;
65 |
66 | mSocket.IOControl(IOControlCode.ReceiveAll, byIn, byOut);
67 |
68 | WaitForData();
69 | }
70 |
71 | ///
72 | /// Waits for more data to arrive
73 | ///
74 | public void WaitForData() {
75 | WaitForData(new SocketInfo(mSocket, short.MaxValue));
76 | }
77 |
78 | ///
79 | /// Waits for more data to arrive
80 | ///
81 | /// Info about data to be received
82 | private void WaitForData(SocketInfo pSocketInfo) {
83 | try {
84 | mSocket.BeginReceive(pSocketInfo.DataBuffer, pSocketInfo.Index, pSocketInfo.DataBuffer.Length - pSocketInfo.Index, SocketFlags.None, new AsyncCallback(OnDataReceived), pSocketInfo);
85 | } catch (Exception se) {
86 | Console.WriteLine("[Error] Session.WaitForData: " + se);
87 | }
88 | }
89 |
90 | private void OnDataReceived(IAsyncResult pIAR) {
91 | SocketInfo socketInfo = (SocketInfo)pIAR.AsyncState;
92 | try {
93 | int received = socketInfo.Socket.EndReceive(pIAR);
94 | if (received == 0) {
95 | if (OnClientDisconnected != null) {
96 | OnClientDisconnected(this);
97 | }
98 | return;
99 | }
100 |
101 | socketInfo.Index += received;
102 |
103 |
104 | byte[] dataa = new byte[received];
105 | Buffer.BlockCopy(socketInfo.DataBuffer, 0, dataa, 0, received);
106 | Console.WriteLine(BitConverter.ToString(dataa));
107 | Console.WriteLine(HexEncoding.ToStringFromAscii(dataa));
108 | WaitForData();
109 | /*if (socketInfo.Index == socketInfo.DataBuffer.Length) {
110 | switch (socketInfo.State) {
111 | case SocketInfo.StateEnum.Header:
112 | PacketReader headerReader = new PacketReader(socketInfo.DataBuffer);
113 | byte[] packetHeaderB = headerReader.ToArray();
114 | int packetHeader = headerReader.ReadInt();
115 | short packetLength = (short)MapleCrypto.getPacketLength(packetHeader);
116 | if (!_RIV.checkPacket(packetHeader)) {
117 | Console.WriteLine("[Error] Packet check failed. Disconnecting client.");
118 | this.Socket.Close();
119 | }
120 | socketInfo.State = SocketInfo.StateEnum.Content;
121 | socketInfo.DataBuffer = new byte[packetLength];
122 | socketInfo.Index = 0;
123 | WaitForData(socketInfo);
124 | break;
125 | case SocketInfo.StateEnum.Content:
126 | byte[] data = socketInfo.DataBuffer;
127 |
128 | _RIV.crypt(data);
129 | MapleCustomEncryption.Decrypt(data);
130 |
131 | if (data.Length != 0 && OnPacketReceived != null) {
132 | OnPacketReceived(new PacketReader(data));
133 | }
134 | WaitForData();
135 | break;
136 | }
137 | } else {
138 | Console.WriteLine("[Warning] Not enough data");
139 | WaitForData(socketInfo);
140 | }*/
141 | } catch (ObjectDisposedException) {
142 | Console.WriteLine("[Error] Session.OnDataReceived: Socket has been closed");
143 | } catch (SocketException se) {
144 | if (se.ErrorCode != 10054) {
145 | Console.WriteLine("[Error] Session.OnDataReceived: " + se);
146 | }
147 | } catch (Exception e) {
148 | Console.WriteLine("[Error] Session.OnDataReceived: " + e);
149 | }
150 | }
151 | }
152 | }
--------------------------------------------------------------------------------
/PacketLib/PacketReader.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 |
4 | namespace MapleLib.PacketLib {
5 | ///
6 | /// Class to handle reading data from a packet
7 | ///
8 | public class PacketReader : AbstractPacket {
9 | ///
10 | /// The main reader tool
11 | ///
12 | private readonly BinaryReader mBinReader;
13 |
14 | ///
15 | /// Amount of data left in the reader
16 | ///
17 | public short Length { get { return (short)mBuffer.Length; } }
18 |
19 | ///
20 | /// Creates a new instance of PacketReader
21 | ///
22 | /// Starting byte array
23 | public PacketReader(byte[] pArrayOfBytes) {
24 | mBuffer = new MemoryStream(pArrayOfBytes, false);
25 | mBinReader = new BinaryReader(mBuffer, Encoding.ASCII);
26 | }
27 |
28 | ///
29 | /// Restart reading from the point specified.
30 | ///
31 | /// The point of the packet to start reading from.
32 | public void Reset(int pLength) {
33 | mBuffer.Seek(pLength, SeekOrigin.Begin);
34 | }
35 |
36 | public void Skip(int pLength) {
37 | mBuffer.Position += pLength;
38 | }
39 |
40 | ///
41 | /// Reads an unsigned byte from the stream
42 | ///
43 | /// an unsigned byte from the stream
44 | public byte ReadByte() {
45 | return mBinReader.ReadByte();
46 | }
47 |
48 | ///
49 | /// Reads a byte array from the stream
50 | ///
51 | /// Amount of bytes
52 | /// A byte array
53 | public byte[] ReadBytes(int pCount) {
54 | return mBinReader.ReadBytes(pCount);
55 | }
56 |
57 | ///
58 | /// Reads a bool from the stream
59 | ///
60 | /// A bool
61 | public bool ReadBool() {
62 | return mBinReader.ReadBoolean();
63 | }
64 |
65 | ///
66 | /// Reads a signed short from the stream
67 | ///
68 | /// A signed short
69 | public short ReadShort() {
70 | return mBinReader.ReadInt16();
71 | }
72 |
73 | ///
74 | /// Reads a signed int from the stream
75 | ///
76 | /// A signed int
77 | public int ReadInt() {
78 | return mBinReader.ReadInt32();
79 | }
80 |
81 | ///
82 | /// Reads a signed long from the stream
83 | ///
84 | /// A signed long
85 | public long ReadLong() {
86 | return mBinReader.ReadInt64();
87 | }
88 |
89 | ///
90 | /// Reads an ASCII string from the stream
91 | ///
92 | /// Amount of bytes
93 | /// An ASCII string
94 | public string ReadString(int pLength) {
95 | return Encoding.ASCII.GetString(ReadBytes(pLength));
96 | }
97 |
98 | ///
99 | /// Reads a maple string from the stream
100 | ///
101 | /// A maple string
102 | public string ReadMapleString() {
103 | return ReadString(ReadShort());
104 | }
105 | }
106 | }
--------------------------------------------------------------------------------
/PacketLib/PacketWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 |
5 | namespace MapleLib.PacketLib {
6 | ///
7 | /// Class to handle writing packets
8 | ///
9 | public class PacketWriter : AbstractPacket {
10 | ///
11 | /// The main writer tool
12 | ///
13 | private readonly BinaryWriter pBinWriter;
14 |
15 | ///
16 | /// Amount of data writen in the writer
17 | ///
18 | public short Length { get { return (short)mBuffer.Length; } }
19 |
20 | ///
21 | /// Creates a new instance of PacketWriter
22 | ///
23 | /// Starting size of the buffer
24 | public PacketWriter(int pSize = 0) {
25 | mBuffer = new MemoryStream(pSize);
26 | pBinWriter = new BinaryWriter(mBuffer, Encoding.ASCII);
27 | }
28 |
29 | public PacketWriter(byte[] pData) {
30 | mBuffer = new MemoryStream(pData);
31 | pBinWriter = new BinaryWriter(mBuffer, Encoding.ASCII);
32 | }
33 |
34 | ///
35 | /// Restart writing from the point specified. This will overwrite data in the packet.
36 | ///
37 | /// The point of the packet to start writing from.
38 | public void Reset(int pLength) {
39 | mBuffer.Seek(pLength, SeekOrigin.Begin);
40 | }
41 |
42 | ///
43 | /// Writes a byte to the stream
44 | ///
45 | /// The byte to write
46 | public void WriteByte(int pByte) {
47 | pBinWriter.Write((byte)pByte);
48 | }
49 |
50 | ///
51 | /// Writes a byte array to the stream
52 | ///
53 | /// The byte array to write
54 | public void WriteBytes(byte[] pBytes) {
55 | pBinWriter.Write(pBytes);
56 | }
57 |
58 | ///
59 | /// Writes a boolean to the stream
60 | ///
61 | /// The boolean to write
62 | public void WriteBool(bool pBool) {
63 | pBinWriter.Write(pBool);
64 | }
65 |
66 | ///
67 | /// Writes a short to the stream
68 | ///
69 | /// The short to write
70 | public void WriteShort(int pShort) {
71 | pBinWriter.Write((short)pShort);
72 | }
73 |
74 | ///
75 | /// Writes an int to the stream
76 | ///
77 | /// The int to write
78 | public void WriteInt(int pInt) {
79 | pBinWriter.Write(pInt);
80 | }
81 |
82 | ///
83 | /// Writes a long to the stream
84 | ///
85 | /// The long to write
86 | public void WriteLong(long pLong) {
87 | pBinWriter.Write(pLong);
88 | }
89 |
90 | ///
91 | /// Writes a string to the stream
92 | ///
93 | /// The string to write
94 | public void WriteString(String pString) {
95 | pBinWriter.Write(pString.ToCharArray());
96 | }
97 |
98 | ///
99 | /// Writes a string prefixed with a [short] length before it, to the stream
100 | ///
101 | /// The string to write
102 | public void WriteMapleString(String pString) {
103 | WriteShort((short)pString.Length);
104 | WriteString(pString);
105 | }
106 |
107 | ///
108 | /// Writes a hex-string to the stream
109 | ///
110 | /// The hex-string to write
111 | public void WriteHexString(String pHexString) {
112 | WriteBytes(HexEncoding.GetBytes(pHexString));
113 | }
114 |
115 | ///
116 | /// Sets a byte in the stream
117 | ///
118 | /// The index of the stream to set data at
119 | /// The byte to set
120 | public void SetByte(long pIndex, int pByte) {
121 | long oldIndex = mBuffer.Position;
122 | mBuffer.Position = pIndex;
123 | WriteByte((byte)pByte);
124 | mBuffer.Position = oldIndex;
125 | }
126 |
127 | ///
128 | /// Sets a byte array in the stream
129 | ///
130 | /// The index of the stream to set data at
131 | /// The bytes to set
132 | public void SetBytes(long pIndex, byte[] pBytes) {
133 | long oldIndex = mBuffer.Position;
134 | mBuffer.Position = pIndex;
135 | WriteBytes(pBytes);
136 | mBuffer.Position = oldIndex;
137 | }
138 |
139 | ///
140 | /// Sets a bool in the stream
141 | ///
142 | /// The index of the stream to set data at
143 | /// The bool to set
144 | public void SetBool(long pIndex, bool pBool) {
145 | long oldIndex = mBuffer.Position;
146 | mBuffer.Position = pIndex;
147 | WriteBool(pBool);
148 | mBuffer.Position = oldIndex;
149 | }
150 |
151 | ///
152 | /// Sets a short in the stream
153 | ///
154 | /// The index of the stream to set data at
155 | /// The short to set
156 | public void SetShort(long pIndex, int pShort) {
157 | long oldIndex = mBuffer.Position;
158 | mBuffer.Position = pIndex;
159 | WriteShort((short)pShort);
160 | mBuffer.Position = oldIndex;
161 | }
162 |
163 | ///
164 | /// Sets an int in the stream
165 | ///
166 | /// The index of the stream to set data at
167 | /// The int to set
168 | public void SetInt(long pIndex, int pInt) {
169 | long oldIndex = mBuffer.Position;
170 | mBuffer.Position = pIndex;
171 | WriteInt(pInt);
172 | mBuffer.Position = oldIndex;
173 | }
174 |
175 | ///
176 | /// Sets a long in the stream
177 | ///
178 | /// The index of the stream to set data at
179 | /// The long to set
180 | public void SetLong(long pIndex, long pLong) {
181 | long oldIndex = mBuffer.Position;
182 | mBuffer.Position = pIndex;
183 | WriteLong(pLong);
184 | mBuffer.Position = oldIndex;
185 | }
186 |
187 | ///
188 | /// Sets a long in the stream
189 | ///
190 | /// The index of the stream to set data at
191 | /// The long to set
192 | public void SetString(long pIndex, string pString) {
193 | long oldIndex = mBuffer.Position;
194 | mBuffer.Position = pIndex;
195 | WriteString(pString);
196 | mBuffer.Position = oldIndex;
197 | }
198 |
199 | ///
200 | /// Sets a string prefixed with a [short] length before it, in the stream
201 | ///
202 | /// The index of the stream to set data at
203 | /// The string to set
204 | public void SetMapleString(long pIndex, string pString) {
205 | long oldIndex = mBuffer.Position;
206 | mBuffer.Position = pIndex;
207 | WriteMapleString(pString);
208 | mBuffer.Position = oldIndex;
209 | }
210 |
211 | ///
212 | /// Sets a hex-string in the stream
213 | ///
214 | /// The index of the stream to set data at
215 | /// The hex-string to set
216 | public void SetHexString(long pIndex, string pString) {
217 | long oldIndex = mBuffer.Position;
218 | mBuffer.Position = pIndex;
219 | WriteHexString(pString);
220 | mBuffer.Position = oldIndex;
221 | }
222 | }
223 | }
--------------------------------------------------------------------------------
/PacketLib/Session.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.MapleCryptoLib;
2 | using System;
3 | using System.Net.Sockets;
4 |
5 | namespace MapleLib.PacketLib {
6 | ///
7 | /// Class to a network session socket
8 | ///
9 | public class Session {
10 | ///
11 | /// The Session's socket
12 | ///
13 | private readonly Socket mSocket;
14 |
15 | private readonly SessionType mType;
16 |
17 | ///
18 | /// The Recieved packet crypto manager
19 | ///
20 | private MapleCrypto mRIV;
21 |
22 | ///
23 | /// The Sent packet crypto manager
24 | ///
25 | private MapleCrypto mSIV;
26 |
27 | ///
28 | /// Method to handle packets received
29 | ///
30 | public delegate void PacketReceivedHandler(PacketReader pPacket, bool pIsInit);
31 |
32 | ///
33 | /// Packet received event
34 | ///
35 | public event PacketReceivedHandler OnPacketReceived;
36 |
37 | ///
38 | /// Method to handle client disconnected
39 | ///
40 | public delegate void ClientDisconnectedHandler(Session pSession);
41 |
42 | ///
43 | /// Client disconnected event
44 | ///
45 | public event ClientDisconnectedHandler OnClientDisconnected;
46 |
47 | public delegate void InitPacketReceived(short pVersion, byte pServerIdentifier);
48 |
49 | public event InitPacketReceived OnInitPacketReceived;
50 |
51 | ///
52 | /// The Recieved packet crypto manager
53 | ///
54 | public MapleCrypto RIV { get { return mRIV; } set { mRIV = value; } }
55 |
56 | ///
57 | /// The Sent packet crypto manager
58 | ///
59 | public MapleCrypto SIV { get { return mSIV; } set { mSIV = value; } }
60 |
61 | ///
62 | /// The Session's socket
63 | ///
64 | public Socket Socket { get { return mSocket; } }
65 |
66 | public SessionType Type { get { return mType; } }
67 |
68 | ///
69 | /// Creates a new instance of a Session
70 | ///
71 | /// Socket connection of the session
72 | public Session(Socket pSocket, SessionType pType) {
73 | mSocket = pSocket;
74 | mType = pType;
75 | }
76 |
77 | ///
78 | /// Waits for more data to arrive
79 | ///
80 | public void WaitForData() {
81 | WaitForData(new SocketInfo(mSocket, 4));
82 | }
83 |
84 | public void WaitForDataNoEncryption() {
85 | WaitForData(new SocketInfo(mSocket, 2, true));
86 | }
87 |
88 | ///
89 | /// Waits for more data to arrive
90 | ///
91 | /// Info about data to be received
92 | private void WaitForData(SocketInfo pSocketInfo) {
93 | try {
94 | mSocket.BeginReceive(pSocketInfo.DataBuffer, pSocketInfo.Index, pSocketInfo.DataBuffer.Length - pSocketInfo.Index, SocketFlags.None, new AsyncCallback(OnDataReceived), pSocketInfo);
95 | } catch (Exception se) {
96 | Console.WriteLine("[Error] Session.WaitForData: " + se);
97 | }
98 | }
99 |
100 | ///
101 | /// Data received event handler
102 | ///
103 | /// IAsyncResult of the data received event
104 | private void OnDataReceived(IAsyncResult pIAR) {
105 | SocketInfo socketInfo = (SocketInfo)pIAR.AsyncState;
106 | try {
107 | int received = socketInfo.Socket.EndReceive(pIAR);
108 | if (received == 0) {
109 | if (OnClientDisconnected != null) {
110 | OnClientDisconnected(this);
111 | }
112 | return;
113 | }
114 |
115 | socketInfo.Index += received;
116 |
117 | if (socketInfo.Index == socketInfo.DataBuffer.Length) {
118 | switch (socketInfo.State) {
119 | case SocketInfo.StateEnum.Header:
120 | if (socketInfo.NoEncryption) {
121 | PacketReader headerReader = new PacketReader(socketInfo.DataBuffer);
122 | short packetHeader = headerReader.ReadShort();
123 | socketInfo.State = SocketInfo.StateEnum.Content;
124 | socketInfo.DataBuffer = new byte[packetHeader];
125 | socketInfo.Index = 0;
126 | WaitForData(socketInfo);
127 | } else {
128 | PacketReader headerReader = new PacketReader(socketInfo.DataBuffer);
129 | byte[] packetHeaderB = headerReader.ToArray();
130 | int packetHeader = headerReader.ReadInt();
131 | short packetLength = (short)MapleCrypto.getPacketLength(packetHeader);
132 | if (mType == SessionType.SERVER_TO_CLIENT && !mRIV.checkPacketToServer(BitConverter.GetBytes(packetHeader))) {
133 | Console.WriteLine("[Error] Packet check failed. Disconnecting client.");
134 | //this.Socket.Close();
135 | }
136 | socketInfo.State = SocketInfo.StateEnum.Content;
137 | socketInfo.DataBuffer = new byte[packetLength];
138 | socketInfo.Index = 0;
139 | WaitForData(socketInfo);
140 | }
141 | break;
142 | case SocketInfo.StateEnum.Content:
143 | byte[] data = socketInfo.DataBuffer;
144 | if (socketInfo.NoEncryption) {
145 | socketInfo.NoEncryption = false;
146 | PacketReader reader = new PacketReader(data);
147 | short version = reader.ReadShort();
148 | string unknown = reader.ReadMapleString();
149 | mSIV = new MapleCrypto(reader.ReadBytes(4), version);
150 | mRIV = new MapleCrypto(reader.ReadBytes(4), version);
151 | byte serverType = reader.ReadByte();
152 | if (mType == SessionType.CLIENT_TO_SERVER) {
153 | OnInitPacketReceived(version, serverType);
154 | }
155 | OnPacketReceived(new PacketReader(data), true);
156 | WaitForData();
157 | } else {
158 | mRIV.crypt(data);
159 | MapleCustomEncryption.Decrypt(data);
160 | if (data.Length != 0 && OnPacketReceived != null) {
161 | OnPacketReceived(new PacketReader(data), false);
162 | }
163 | WaitForData();
164 | }
165 | break;
166 | }
167 | } else {
168 | Console.WriteLine("[Warning] Not enough data");
169 | WaitForData(socketInfo);
170 | }
171 | } catch (ObjectDisposedException) {
172 | Console.WriteLine("[Error] Session.OnDataReceived: Socket has been closed");
173 | } catch (SocketException se) {
174 | if (se.ErrorCode != 10054) {
175 | Console.WriteLine("[Error] Session.OnDataReceived: " + se);
176 | }
177 | } catch (Exception e) {
178 | Console.WriteLine("[Error] Session.OnDataReceived: " + e);
179 | }
180 | }
181 |
182 | public void SendInitialPacket(int pVersion, string pPatchLoc, byte[] pRIV, byte[] pSIV, byte pServerType) {
183 | PacketWriter writer = new PacketWriter();
184 | writer.WriteShort(string.IsNullOrEmpty(pPatchLoc) ? 0x0D : 0x0E);
185 | writer.WriteShort(pVersion);
186 | writer.WriteMapleString(pPatchLoc);
187 | writer.WriteBytes(pRIV);
188 | writer.WriteBytes(pSIV);
189 | writer.WriteByte(pServerType);
190 | SendRawPacket(writer);
191 | }
192 |
193 | ///
194 | /// Encrypts the packet then send it to the client.
195 | ///
196 | /// The PacketWrtier object to be sent.
197 | public void SendPacket(PacketWriter pPacket) {
198 | SendPacket(pPacket.ToArray());
199 | }
200 |
201 | ///
202 | /// Encrypts the packet then send it to the client.
203 | ///
204 | /// The byte array to be sent.
205 | public void SendPacket(byte[] pInput) {
206 | byte[] cryptData = pInput;
207 | byte[] sendData = new byte[cryptData.Length + 4];
208 | byte[] header = mType == SessionType.SERVER_TO_CLIENT ? mSIV.getHeaderToClient(cryptData.Length) : mSIV.getHeaderToServer(cryptData.Length);
209 |
210 | MapleCustomEncryption.Encrypt(cryptData);
211 | mSIV.crypt(cryptData);
212 |
213 | Buffer.BlockCopy(header, 0, sendData, 0, 4);
214 | Buffer.BlockCopy(cryptData, 0, sendData, 4, cryptData.Length);
215 | SendRawPacket(sendData);
216 | }
217 |
218 | ///
219 | /// Sends a raw packet to the client
220 | ///
221 | /// The PacketWriter
222 | public void SendRawPacket(PacketWriter pPacket) {
223 | SendRawPacket(pPacket.ToArray());
224 | }
225 |
226 | ///
227 | /// Sends a raw buffer to the client.
228 | ///
229 | /// The buffer to be sent.
230 | public void SendRawPacket(byte[] pBuffer) {
231 | //_socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, ar => _socket.EndSend(ar), null);//async
232 | mSocket.Send(pBuffer); //sync
233 | }
234 | }
235 | }
--------------------------------------------------------------------------------
/PacketLib/SessionType.cs:
--------------------------------------------------------------------------------
1 | namespace MapleLib.PacketLib {
2 | public enum SessionType {
3 | SERVER_TO_CLIENT,
4 | CLIENT_TO_SERVER
5 | }
6 | }
--------------------------------------------------------------------------------
/PacketLib/SocketInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Sockets;
2 |
3 | namespace MapleLib.PacketLib {
4 | ///
5 | /// Class to manage Socket and data to receive
6 | ///
7 | public class SocketInfo {
8 | ///
9 | /// Creates a new instance of a SocketInfo
10 | ///
11 | /// Socket connection of the session
12 | /// Length of the main packet's header (Usually 4)
13 | public SocketInfo(Socket pSocket, short pHeaderLength, bool pNoEncryption = false) {
14 | Socket = pSocket;
15 | State = StateEnum.Header;
16 | NoEncryption = pNoEncryption;
17 | DataBuffer = new byte[pHeaderLength];
18 | Index = 0;
19 | }
20 |
21 | ///
22 | /// The SocketInfo's socket
23 | ///
24 | public readonly Socket Socket;
25 |
26 | public bool NoEncryption;
27 |
28 | ///
29 | /// The Session's state of what data to receive
30 | ///
31 | public StateEnum State;
32 |
33 | ///
34 | /// The buffer of data to recieve
35 | ///
36 | public byte[] DataBuffer;
37 |
38 | ///
39 | /// The index of the current data
40 | ///
41 | public int Index;
42 |
43 | ///
44 | /// The SocketInfo's state of data
45 | ///
46 | public enum StateEnum {
47 | Header,
48 | Content
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 | using System.Resources;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 |
9 | [assembly: AssemblyTitle("MapleLib")]
10 | [assembly: AssemblyDescription("")]
11 | [assembly: AssemblyConfiguration("")]
12 | [assembly: AssemblyCompany("")]
13 | [assembly: AssemblyProduct("MapleLib")]
14 | [assembly: AssemblyCopyright("")]
15 | [assembly: AssemblyTrademark("")]
16 | [assembly: AssemblyCulture("")]
17 |
18 | // Setting ComVisible to false makes the types in this assembly not visible
19 | // to COM components. If you need to access a type in this assembly from
20 | // COM, set the ComVisible attribute to true on that type.
21 |
22 | [assembly: ComVisible(false)]
23 |
24 | // The following GUID is for the ID of the typelib if this project is exposed to COM
25 |
26 | [assembly: Guid("3acc67fa-b67a-46d1-af1f-b23c4a1cd93b")]
27 |
28 | // Version information for an assembly consists of the following four values:
29 | //
30 | // Major Version
31 | // Minor Version
32 | // Build Number
33 | // Revision
34 | //
35 | // You can specify all the values or you can default the Build and Revision Numbers
36 | // by using the '*' as shown below:
37 | // [assembly: AssemblyVersion("1.0.*")]
38 |
39 | [assembly: AssemblyVersion("1.5.0.0")]
40 | [assembly: AssemblyFileVersion("1.5.0.0")]
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MapleLib
2 | A WZ Library by Snow with changes made for use with WZ Dumper
--------------------------------------------------------------------------------
/WzLib/APropertyContainer.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.WzProperties;
2 | using System.Collections.Generic;
3 |
4 | namespace MapleLib.WzLib {
5 | public abstract class APropertyContainer : AWzImageProperty {
6 | public virtual void AddProperty(AWzImageProperty pProp) {
7 | pProp.Parent = this;
8 | pProp.ParentImage = ParentImage;
9 | WzProperties.Add(pProp);
10 | }
11 |
12 | public virtual void AddProperties(List pProps) {
13 | foreach (AWzImageProperty prop in pProps) {
14 | AddProperty(prop);
15 | }
16 | pProps.Clear();
17 | }
18 |
19 | public virtual void RemoveProperty(AWzImageProperty pProp) {
20 | WzProperties.Remove(pProp);
21 | }
22 |
23 | public virtual void ClearProperties() {
24 | WzProperties.Clear();
25 | }
26 |
27 | public override AWzImageProperty this[string pName] {
28 | get {
29 | foreach (AWzImageProperty prop in WzProperties)
30 | if (pName == "PNG" && prop is WzCanvasProperty) {
31 | return prop.ToPngProperty();
32 | } else if (prop.Name.ToLower() == pName.ToLower()) {
33 | return prop;
34 | }
35 | return null;
36 | }
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/WzLib/AWzImageProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using MapleLib.WzLib.WzProperties;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 |
8 | namespace MapleLib.WzLib {
9 | ///
10 | /// An interface for wz img properties
11 | ///
12 | public abstract class AWzImageProperty : AWzObject {
13 | public override WzObjectType ObjectType { get { return WzObjectType.Property; } }
14 |
15 | public abstract WzPropertyType PropertyType { get; }
16 |
17 | public abstract WzImage ParentImage { get; internal set; }
18 |
19 | public abstract void WriteValue(WzBinaryWriter pWriter);
20 |
21 | public virtual void ExportXml(StreamWriter pWriter, int pLevel) {
22 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.OpenNamedTag(PropertyType.ToString(), Name, true));
23 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.CloseTag(PropertyType.ToString()));
24 | }
25 |
26 | public virtual List WzProperties { get { return null; } }
27 |
28 | public virtual AWzImageProperty this[string pName] { get { return null; } }
29 |
30 | ///
31 | /// Gets a wz property by a path name
32 | ///
33 | /// path to property
34 | /// the wz property with the specified name
35 | public virtual AWzImageProperty GetFromPath(string pPath) {
36 | string[] segments = pPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
37 | if (segments[0] == "..") {
38 | return ((AWzImageProperty)Parent)[pPath.Substring(Name.IndexOf('/') + 1)];
39 | }
40 | AWzImageProperty ret = this;
41 | if (ret.WzProperties == null) {
42 | return null;
43 | }
44 | foreach (string t in segments) {
45 | bool foundChild = false;
46 | if (ret is WzPngProperty && t == "PNG") {
47 | return ret.ToPngProperty();
48 | }
49 | string t1 = t;
50 | foreach (AWzImageProperty iwp in ret.WzProperties.Where(iwp => iwp.Name == t1)) {
51 | ret = iwp;
52 | foundChild = true;
53 | break;
54 | }
55 | if (!foundChild) {
56 | return null;
57 | }
58 | }
59 | return ret;
60 | }
61 |
62 |
63 | internal static void WritePropertyList(WzBinaryWriter pWriter, List pProperties) {
64 | pWriter.Write((ushort)0);
65 | pWriter.WriteCompressedInt(pProperties.Count);
66 | foreach (AWzImageProperty prop in pProperties) {
67 | pWriter.WriteStringValue(prop.Name, 0x00, 0x01);
68 | if (prop is IExtended) {
69 | WriteExtendedProperty(pWriter, prop);
70 | } else {
71 | prop.WriteValue(pWriter);
72 | }
73 | }
74 | }
75 |
76 | internal static void WriteExtendedProperty(WzBinaryWriter pWriter, AWzImageProperty pProp) {
77 | pWriter.Write((byte)9);
78 | long beforePos = pWriter.BaseStream.Position;
79 | pWriter.Write(0); // Placeholder
80 | pProp.WriteValue(pWriter);
81 | int len = (int)(pWriter.BaseStream.Position - beforePos);
82 | long newPos = pWriter.BaseStream.Position;
83 | pWriter.BaseStream.Position = beforePos;
84 | pWriter.Write(len - 4);
85 | pWriter.BaseStream.Position = newPos;
86 | }
87 |
88 | internal static void DumpPropertyList(StreamWriter pWriter, int pLevel, List pProperties) {
89 | foreach (AWzImageProperty prop in pProperties) {
90 | prop.ExportXml(pWriter, pLevel + 1);
91 | }
92 | }
93 |
94 | internal static List ParsePropertyList(uint pOffset, WzBinaryReader pReader, AWzObject pParent, WzImage pParentImg) {
95 | List properties = new List();
96 | int entryCount = pReader.ReadCompressedInt();
97 | for (int i = 0; i < entryCount; i++) {
98 | string name = pReader.ReadStringBlock(pOffset).Trim();
99 | byte b = pReader.ReadByte();
100 | switch (b) {
101 | case 0:
102 | properties.Add(new WzNullProperty(name) { Parent = pParent, ParentImage = pParentImg });
103 | break;
104 | case 2:
105 | case 11: //UShort
106 | properties.Add(new WzShortProperty(name, pReader.ReadInt16()) { Parent = pParent, ParentImage = pParentImg });
107 | break;
108 | case 3:
109 | case 19: //UInt
110 | properties.Add(new WzCompressedIntProperty(name, pReader.ReadCompressedInt()) { Parent = pParent, ParentImage = pParentImg });
111 | break;
112 | case 4:
113 | byte type = pReader.ReadByte();
114 | if (type == 0x80)
115 | properties.Add(new WzByteFloatProperty(name, pReader.ReadSingle()) { Parent = pParent, ParentImage = pParentImg });
116 | else if (type == 0)
117 | properties.Add(new WzByteFloatProperty(name, 0f) { Parent = pParent, ParentImage = pParentImg });
118 | break;
119 | case 5:
120 | properties.Add(new WzDoubleProperty(name, pReader.ReadDouble()) { Parent = pParent, ParentImage = pParentImg });
121 | break;
122 | case 8:
123 | properties.Add(new WzStringProperty(name, pReader.ReadStringBlock(pOffset)) { Parent = pParent, ParentImage = pParentImg });
124 | break;
125 | case 9:
126 | int eob = (int)(pReader.ReadUInt32() + pReader.BaseStream.Position);
127 | AWzImageProperty exProp = ParseExtendedProp(pReader, pOffset, name, pParent, pParentImg);
128 | if (exProp != null)
129 | properties.Add(exProp);
130 | pReader.BaseStream.Position = eob;
131 | break;
132 | case 20:
133 | properties.Add(new WzCompressedLongProperty(name, pReader.ReadCompressedLong()) { Parent = pParent, ParentImage = pParentImg });
134 | break;
135 | default:
136 | throw new Exception("Unknown property type at ParsePropertyList: " + b + " name: " + name + " offset: " + pReader.GetCurrentOffset());
137 | }
138 | }
139 | return properties;
140 | }
141 |
142 | internal static AWzImageProperty ParseExtendedProp(WzBinaryReader pReader, uint pOffset, string pName, AWzObject pParent, WzImage pImgParent) {
143 | byte b = pReader.ReadByte();
144 | switch (b) {
145 | case 0x1B:
146 | return ExtractMore(pReader, pOffset, pName, pReader.ReadStringAtOffset(pOffset + pReader.ReadInt32()), pParent, pImgParent);
147 | case 0x73:
148 | return ExtractMore(pReader, pOffset, pName, pReader.ReadString(), pParent, pImgParent);
149 | default:
150 | return null;
151 | //throw new Exception("Invlid type at ParseExtendedProp: " + b);
152 | }
153 | }
154 |
155 | internal static AWzImageProperty ExtractMore(WzBinaryReader pReader, uint pOffset, string pName, string pImageName, AWzObject pParent, WzImage pImgParent) {
156 | switch (pImageName) {
157 | case "Property":
158 | WzSubProperty subProp = new WzSubProperty(pName) { Parent = pParent, ParentImage = pImgParent };
159 | pReader.BaseStream.Position += 2;
160 | subProp.AddProperties(ParsePropertyList(pOffset, pReader, subProp, pImgParent));
161 | return subProp;
162 | case "Canvas":
163 | WzCanvasProperty canvasProp = new WzCanvasProperty(pName) { Parent = pParent, ParentImage = pImgParent };
164 | pReader.BaseStream.Position++;
165 | if (pReader.ReadByte() == 1) {
166 | pReader.BaseStream.Position += 2;
167 | canvasProp.AddProperties(ParsePropertyList(pOffset, pReader, canvasProp, pImgParent));
168 | }
169 | canvasProp.PngProperty = new WzPngProperty(pReader) { Parent = canvasProp, ParentImage = pImgParent };
170 | return canvasProp;
171 | case "RawData":
172 | WzRawDataProperty rawData = new WzRawDataProperty(pName) { Parent = pParent, ParentImage = pImgParent };
173 | rawData.ParseRawData(pReader);
174 | return rawData;
175 | case "Shape2D#Vector2D":
176 | WzVectorProperty vecProp = new WzVectorProperty(pName) { Parent = pParent, ParentImage = pImgParent };
177 | vecProp.X = new WzCompressedIntProperty("X", pReader.ReadCompressedInt()) { Parent = vecProp, ParentImage = pImgParent };
178 | vecProp.Y = new WzCompressedIntProperty("Y", pReader.ReadCompressedInt()) { Parent = vecProp, ParentImage = pImgParent };
179 | return vecProp;
180 | case "Shape2D#Convex2D":
181 | WzConvexProperty convexProp = new WzConvexProperty(pName) { Parent = pParent, ParentImage = pImgParent };
182 | int convexEntryCount = pReader.ReadCompressedInt();
183 | for (int i = 0; i < convexEntryCount; i++) {
184 | AWzImageProperty imgProp = ParseExtendedProp(pReader, pOffset, pName, convexProp, pImgParent);
185 | if (imgProp != null)
186 | convexProp.AddProperty(imgProp);
187 | }
188 | return convexProp;
189 | case "Sound_DX8":
190 | WzSoundProperty soundProp = new WzSoundProperty(pName) { Parent = pParent, ParentImage = pImgParent };
191 | soundProp.ParseSound(pReader);
192 | return soundProp;
193 | case "UOL":
194 | pReader.BaseStream.Position++;
195 | byte b = pReader.ReadByte();
196 | switch (b) {
197 | case 0:
198 | return new WzUOLProperty(pName, pReader.ReadString()) { Parent = pParent, ParentImage = pImgParent };
199 | case 1:
200 | return new WzUOLProperty(pName, pReader.ReadStringAtOffset(pOffset + pReader.ReadInt32())) { Parent = pParent, ParentImage = pImgParent };
201 | default:
202 | throw new Exception("Unsupported UOL type: " + b);
203 | }
204 | default:
205 | throw new Exception("Unknown image name: " + pImageName);
206 | }
207 | }
208 | }
209 | }
--------------------------------------------------------------------------------
/WzLib/AWzObject.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.WzProperties;
2 | using System;
3 | using System.Drawing;
4 |
5 | namespace MapleLib.WzLib {
6 | ///
7 | /// An interface for wz objects
8 | ///
9 | public abstract class AWzObject : IDisposable {
10 | public abstract void Dispose();
11 |
12 | ///
13 | /// The name of the object
14 | ///
15 | public abstract string Name { get; set; }
16 |
17 | ///
18 | /// The WzObjectType of the object
19 | ///
20 | public abstract WzObjectType ObjectType { get; }
21 |
22 | ///
23 | /// Returns the parent object
24 | ///
25 | public abstract AWzObject Parent { get; internal set; }
26 |
27 | public virtual object WzValue { get; set; }
28 |
29 | public Object Tag { get; set; }
30 |
31 | public string FullPath {
32 | get {
33 | string result = Name;
34 | AWzObject currObj = this;
35 | while (currObj.Parent != null) {
36 | currObj = currObj.Parent;
37 | result = currObj.Name + @"\" + result;
38 | }
39 | return result;
40 | }
41 | }
42 |
43 | //public abstract void Remove();
44 |
45 | #region Cast Values
46 |
47 | public static explicit operator float(AWzObject obj) {
48 | return obj.ToFloat();
49 | }
50 |
51 | public static explicit operator int(AWzObject obj) {
52 | return obj.ToInt();
53 | }
54 |
55 | public static explicit operator double(AWzObject obj) {
56 | return obj.ToDouble();
57 | }
58 |
59 | public static explicit operator Bitmap(AWzObject obj) {
60 | return obj.ToBitmap();
61 | }
62 |
63 | public static explicit operator byte[] (AWzObject obj) {
64 | return obj.ToBytes();
65 | }
66 |
67 | public static explicit operator string(AWzObject obj) {
68 | return obj.ToString();
69 | }
70 |
71 | public static explicit operator short(AWzObject obj) {
72 | return obj.ToShort(0);
73 | }
74 |
75 | public static explicit operator Point(AWzObject obj) {
76 | return obj.ToPoint();
77 | }
78 |
79 | internal virtual float ToFloat(float def = 0) {
80 | return def;
81 | }
82 |
83 | internal virtual WzPngProperty ToPngProperty(WzPngProperty def = null) {
84 | return def;
85 | }
86 |
87 | internal virtual int ToInt(int def = 0) {
88 | return def;
89 | }
90 |
91 | internal virtual double ToDouble(double def = 0) {
92 | return def;
93 | }
94 |
95 | internal virtual Bitmap ToBitmap(Bitmap def = null) {
96 | return def;
97 | }
98 |
99 | internal virtual byte[] ToBytes(byte[] def = null) {
100 | return def;
101 | }
102 |
103 | public override string ToString() {
104 | return WzValue.ToString();
105 | }
106 |
107 | internal virtual short ToShort(short def = 0) {
108 | return def;
109 | }
110 |
111 | internal virtual Point ToPoint(int pXDef = 0, int pYDef = 0) {
112 | return new Point(pXDef, pYDef);
113 | }
114 |
115 | #endregion
116 | }
117 | }
--------------------------------------------------------------------------------
/WzLib/IExtended.cs:
--------------------------------------------------------------------------------
1 | namespace MapleLib.WzLib {
2 | public interface IExtended {
3 | }
4 | }
--------------------------------------------------------------------------------
/WzLib/Util/MP3Header.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace MapleLib.WzLib.Util {
4 | public class MP3Header {
5 | // Public variables for storing the information about the MP3
6 | public int intBitRate;
7 | public string strFileName;
8 | public long lngFileSize;
9 | public int intFrequency;
10 | public string strMode;
11 | public int intLength;
12 | public string strLengthFormatted;
13 |
14 | // Private variables used in the process of reading in the MP3 files
15 | private ulong bithdr;
16 | private bool boolVBitRate;
17 | private int intVFrames;
18 |
19 | public bool ReadMP3Information(string pFileName) {
20 | FileStream fs = new FileStream(pFileName, FileMode.Open, FileAccess.Read);
21 | // Set the filename not including the path information
22 | strFileName = @fs.Name;
23 | char[] chrSeparators = new[] { '\\', '/' };
24 | string[] strSeparator = strFileName.Split(chrSeparators);
25 | int intUpper = strSeparator.GetUpperBound(0);
26 | strFileName = strSeparator[intUpper];
27 |
28 | // Replace ' with '' for the SQL INSERT statement
29 | strFileName = strFileName.Replace("'", "''");
30 |
31 | // Set the file size
32 | lngFileSize = fs.Length;
33 |
34 | byte[] bytHeader = new byte[4];
35 | byte[] bytVBitRate = new byte[12];
36 | int intPos = 0;
37 |
38 | // Keep reading 4 bytes from the header until we know for sure that in
39 | // fact it's an MP3
40 | do {
41 | fs.Position = intPos;
42 | fs.Read(bytHeader, 0, 4);
43 | intPos++;
44 | LoadMP3Header(bytHeader);
45 | } while (!IsValidHeader() && (fs.Position != fs.Length));
46 |
47 | // If the current file stream position is equal to the length,
48 | // that means that we've read the entire file and it's not a valid MP3 file
49 | if (fs.Position != fs.Length) {
50 | intPos += 3;
51 |
52 | if (getVersionIndex() == 3) // MPEG Version 1
53 | {
54 | if (getModeIndex() == 3) // Single Channel
55 | {
56 | intPos += 17;
57 | } else {
58 | intPos += 32;
59 | }
60 | } else // MPEG Version 2.0 or 2.5
61 | {
62 | if (getModeIndex() == 3) // Single Channel
63 | {
64 | intPos += 9;
65 | } else {
66 | intPos += 17;
67 | }
68 | }
69 |
70 | // Check to see if the MP3 has a variable bitrate
71 | fs.Position = intPos;
72 | fs.Read(bytVBitRate, 0, 12);
73 | boolVBitRate = LoadVBRHeader(bytVBitRate);
74 |
75 | // Once the file's read in, then assign the properties of the file to the public variables
76 | intBitRate = getBitrate();
77 | intFrequency = getFrequency();
78 | strMode = getMode();
79 | intLength = getLengthInSeconds();
80 | strLengthFormatted = getFormattedLength();
81 | fs.Close();
82 | return true;
83 | }
84 | return false;
85 | }
86 |
87 | private void LoadMP3Header(byte[] pBuffer) {
88 | // this thing is quite interesting, it works like the following
89 | // c[0] = 00000011
90 | // c[1] = 00001100
91 | // c[2] = 00110000
92 | // c[3] = 11000000
93 | // the operator << means that we'll move the bits in that direction
94 | // 00000011 << 24 = 00000011000000000000000000000000
95 | // 00001100 << 16 = 000011000000000000000000
96 | // 00110000 << 24 = 0011000000000000
97 | // 11000000 = 11000000
98 | // +_________________________________
99 | // 00000011000011000011000011000000
100 | bithdr = (ulong)(((pBuffer[0] & 255) << 24) | ((pBuffer[1] & 255) << 16) | ((pBuffer[2] & 255) << 8) | ((pBuffer[3] & 255)));
101 | }
102 |
103 | private bool LoadVBRHeader(byte[] pInputheader) {
104 | // If it's a variable bitrate MP3, the first 4 bytes will read 'Xing'
105 | // since they're the ones who added variable bitrate-edness to MP3s
106 | if (pInputheader[0] == 88 && pInputheader[1] == 105 && pInputheader[2] == 110 && pInputheader[3] == 103) {
107 | int flags = (((pInputheader[4] & 255) << 24) | ((pInputheader[5] & 255) << 16) | ((pInputheader[6] & 255) << 8) | ((pInputheader[7] & 255)));
108 | if ((flags & 0x0001) == 1) {
109 | intVFrames = (((pInputheader[8] & 255) << 24) | ((pInputheader[9] & 255) << 16) | ((pInputheader[10] & 255) << 8) | ((pInputheader[11] & 255)));
110 | return true;
111 | }
112 | intVFrames = -1;
113 | return true;
114 | }
115 | return false;
116 | }
117 |
118 | private bool IsValidHeader() {
119 | return (((getFrameSync() & 2047) == 2047) && ((getVersionIndex() & 3) != 1) && ((getLayerIndex() & 3) != 0) && ((getBitrateIndex() & 15) != 0) && ((getBitrateIndex() & 15) != 15) && ((getFrequencyIndex() & 3) != 3) && ((getEmphasisIndex() & 3) != 2));
120 | }
121 |
122 | private int getFrameSync() {
123 | return (int)((bithdr >> 21) & 2047);
124 | }
125 |
126 | private int getVersionIndex() {
127 | return (int)((bithdr >> 19) & 3);
128 | }
129 |
130 | private int getLayerIndex() {
131 | return (int)((bithdr >> 17) & 3);
132 | }
133 |
134 | private int getProtectionBit() {
135 | return (int)((bithdr >> 16) & 1);
136 | }
137 |
138 | private int getBitrateIndex() {
139 | return (int)((bithdr >> 12) & 15);
140 | }
141 |
142 | private int getFrequencyIndex() {
143 | return (int)((bithdr >> 10) & 3);
144 | }
145 |
146 | private int getPaddingBit() {
147 | return (int)((bithdr >> 9) & 1);
148 | }
149 |
150 | private int getPrivateBit() {
151 | return (int)((bithdr >> 8) & 1);
152 | }
153 |
154 | private int getModeIndex() {
155 | return (int)((bithdr >> 6) & 3);
156 | }
157 |
158 | private int getModeExtIndex() {
159 | return (int)((bithdr >> 4) & 3);
160 | }
161 |
162 | private int getCoprightBit() {
163 | return (int)((bithdr >> 3) & 1);
164 | }
165 |
166 | private int getOrginalBit() {
167 | return (int)((bithdr >> 2) & 1);
168 | }
169 |
170 | private int getEmphasisIndex() {
171 | return (int)(bithdr & 3);
172 | }
173 |
174 | private double getVersion() {
175 | double[] table = { 2.5, 0.0, 2.0, 1.0 };
176 | return table[getVersionIndex()];
177 | }
178 |
179 | private int getLayer() {
180 | return (4 - getLayerIndex());
181 | }
182 |
183 | private int getBitrate() {
184 | // If the file has a variable bitrate, then we return an integer average bitrate,
185 | // otherwise, we use a lookup table to return the bitrate
186 | if (boolVBitRate) {
187 | double medFrameSize = lngFileSize / (double)getNumberOfFrames();
188 | return (int)((medFrameSize * getFrequency()) / (1000.0 * ((getLayerIndex() == 3) ? 12.0 : 144.0)));
189 | }
190 | int[,,] table = { { // MPEG 2 & 2.5
191 | { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer III
192 | { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer II
193 | { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer I
194 | }, { // MPEG 1
195 | { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }, // Layer III
196 | { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // Layer II
197 | { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 } // Layer I
198 | } };
199 |
200 | return table[getVersionIndex() & 1, getLayerIndex() - 1, getBitrateIndex()];
201 | }
202 |
203 | private int getFrequency() {
204 | int[,] table = { { 32000, 16000, 8000 }, // MPEG 2.5
205 | { 0, 0, 0 }, // reserved
206 | { 22050, 24000, 16000 }, // MPEG 2
207 | { 44100, 48000, 32000 } // MPEG 1
208 | };
209 |
210 | return table[getVersionIndex(), getFrequencyIndex()];
211 | }
212 |
213 | private string getMode() {
214 | switch (getModeIndex()) {
215 | default:
216 | return "Stereo";
217 | case 1:
218 | return "Joint Stereo";
219 | case 2:
220 | return "Dual Channel";
221 | case 3:
222 | return "Single Channel";
223 | }
224 | }
225 |
226 | private int getLengthInSeconds() {
227 | // "intKilBitFileSize" made by dividing by 1000 in order to match the "Kilobits/second"
228 | int intKiloBitFileSize = (int)((8 * lngFileSize) / 1000);
229 | return (intKiloBitFileSize / getBitrate());
230 | }
231 |
232 | private string getFormattedLength() {
233 | // Complete number of seconds
234 | int s = getLengthInSeconds();
235 |
236 | // Seconds to display
237 | int ss = s % 60;
238 |
239 | // Complete number of minutes
240 | int m = (s - ss) / 60;
241 |
242 | // Minutes to display
243 | int mm = m % 60;
244 |
245 | // Complete number of hours
246 | int h = (m - mm) / 60;
247 |
248 | // Make "hh:mm:ss"
249 | return h.ToString("D2") + ":" + mm.ToString("D2") + ":" + ss.ToString("D2");
250 | }
251 |
252 | private int getNumberOfFrames() {
253 | // Again, the number of MPEG frames is dependant on whether it's a variable bitrate MP3 or not
254 | if (!boolVBitRate) {
255 | double medFrameSize = (((getLayerIndex() == 3) ? 12 : 144) * ((1000.0 * getBitrate()) / getFrequency()));
256 | return (int)(lngFileSize / medFrameSize);
257 | }
258 | return intVFrames;
259 | }
260 | }
261 | }
--------------------------------------------------------------------------------
/WzLib/Util/WzBinaryReader.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.MapleCryptoLib;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace MapleLib.WzLib.Util {
7 | public class WzBinaryReader : BinaryReader {
8 | #region Properties
9 |
10 | public byte[] WzKey { get; private set; }
11 | public uint Hash { get; set; }
12 | public WzHeader Header { get; set; }
13 | private bool NoEncryption { get; set; }
14 |
15 | #endregion
16 |
17 | #region Constructors
18 |
19 | public WzBinaryReader(Stream pInput, byte[] pWzIv, bool setKey = false) : base(pInput) {
20 | WzKey = setKey ? pWzIv : WzKeyGenerator.GenerateWzKey(pWzIv);
21 | NoEncryption = WzKey.All(singleByte => singleByte == 0);
22 | }
23 |
24 | #endregion
25 |
26 | #region Methods
27 |
28 | public string ReadStringAtOffset(long pOffset, bool pReadByte = false) {
29 | long CurrentOffset = BaseStream.Position;
30 | BaseStream.Position = pOffset;
31 | if (pReadByte) {
32 | ReadByte();
33 | }
34 | string ReturnString = ReadString();
35 | BaseStream.Position = CurrentOffset;
36 | return ReturnString;
37 | }
38 |
39 | public override string ReadString() {
40 | sbyte smallLength = base.ReadSByte();
41 |
42 | if (smallLength == 0) {
43 | return string.Empty;
44 | }
45 |
46 | int length;
47 | StringBuilder retString = new StringBuilder();
48 | if (smallLength > 0) // Unicode
49 | {
50 | ushort mask = 0xAAAA;
51 | length = smallLength == sbyte.MaxValue ? ReadInt32() : smallLength;
52 | if (length <= 0) {
53 | return string.Empty;
54 | }
55 | if (NoEncryption) {
56 | for (int i = 0; i < length; i++) {
57 | ushort encryptedChar = ReadUInt16();
58 | encryptedChar ^= mask;
59 | retString.Append((char)encryptedChar);
60 | mask++;
61 | }
62 | } else {
63 | for (int i = 0; i < length; i++) {
64 | ushort encryptedChar = ReadUInt16();
65 | encryptedChar ^= mask;
66 | encryptedChar ^= (ushort)((WzKey[i * 2 + 1] << 8) + WzKey[i * 2]);
67 | retString.Append((char)encryptedChar);
68 | mask++;
69 | }
70 | }
71 | } else {
72 | // ASCII
73 | byte mask = 0xAA;
74 | if (smallLength == sbyte.MinValue) {
75 | length = ReadInt32();
76 | } else {
77 | length = -smallLength;
78 | }
79 | if (length <= 0) {
80 | return string.Empty;
81 | }
82 | if (NoEncryption) {
83 | for (int i = 0; i < length; i++) {
84 | byte encryptedChar = ReadByte();
85 | encryptedChar ^= mask;
86 | retString.Append((char)encryptedChar);
87 | mask++;
88 | }
89 | } else {
90 | for (int i = 0; i < length; i++) {
91 | byte encryptedChar = ReadByte();
92 | encryptedChar ^= mask;
93 | encryptedChar ^= WzKey[i];
94 | retString.Append((char)encryptedChar);
95 | mask++;
96 | }
97 | }
98 | }
99 | return retString.ToString();
100 | }
101 |
102 | public long GetCurrentOffset() {
103 | return base.BaseStream.Position;
104 | }
105 |
106 | /*public string ReadString(byte[] wzKey) {
107 | sbyte smallLength = base.ReadSByte();
108 |
109 | if (smallLength == 0) {
110 | return string.Empty;
111 | }
112 |
113 | int length;
114 | StringBuilder retString = new StringBuilder();
115 | if (smallLength > 0) // Unicode
116 | {
117 | ushort mask = 0xAAAA;
118 | length = smallLength == sbyte.MaxValue ? ReadInt32() : smallLength;
119 | if (length <= 0) {
120 | return string.Empty;
121 | }
122 | for (int i = 0; i < length; i++) {
123 | ushort encryptedChar = ReadUInt16();
124 | encryptedChar ^= mask;
125 | encryptedChar ^= (ushort) ((wzKey[i * 2 + 1] << 8) + wzKey[i * 2]);
126 | retString.Append((char) encryptedChar);
127 | mask++;
128 | }
129 | } else {
130 | // ASCII
131 | byte mask = 0xAA;
132 | if (smallLength == sbyte.MinValue) {
133 | length = ReadInt32();
134 | } else {
135 | length = -smallLength;
136 | }
137 | if (length <= 0) {
138 | return string.Empty;
139 | }
140 |
141 | for (int i = 0; i < length; i++) {
142 | byte encryptedChar = ReadByte();
143 | encryptedChar ^= mask;
144 | encryptedChar ^= wzKey[i];
145 | retString.Append((char) encryptedChar);
146 | mask++;
147 | }
148 | }
149 | return retString.ToString();
150 | }*/
151 |
152 | ///
153 | /// Reads an ASCII string, without decryption
154 | ///
155 | /// Length of bytes to read
156 | public string ReadString(int pLength) {
157 | return Encoding.ASCII.GetString(ReadBytes(pLength));
158 | }
159 |
160 | public string ReadNullTerminatedString() {
161 | StringBuilder retString = new StringBuilder();
162 | byte b = ReadByte();
163 | while (b != 0) {
164 | retString.Append((char)b);
165 | b = ReadByte();
166 | }
167 | return retString.ToString();
168 | }
169 |
170 | public int ReadCompressedInt() {
171 | sbyte sb = base.ReadSByte();
172 | return sb == sbyte.MinValue ? ReadInt32() : sb;
173 | }
174 |
175 | public long ReadCompressedLong() {
176 | sbyte sb = base.ReadSByte();
177 | return sb == sbyte.MinValue ? ReadInt64() : sb;
178 | }
179 |
180 | public uint ReadOffset() {
181 | uint offset = (uint)BaseStream.Position;
182 | offset = (offset - Header.FStart) ^ uint.MaxValue;
183 | offset *= Hash;
184 | offset -= CryptoConstants.WZ_OffsetConstant;
185 | offset = WzTool.RotateLeft(offset, (byte)(offset & 0x1F));
186 | uint encryptedOffset = ReadUInt32();
187 | offset ^= encryptedOffset;
188 | offset += Header.FStart * 2;
189 | return offset;
190 | }
191 |
192 | public string DecryptString(char[] pStringToDecrypt) {
193 | string outputString = "";
194 | for (int i = 0; i < pStringToDecrypt.Length; i++)
195 | outputString += (char)(pStringToDecrypt[i] ^ ((char)((WzKey[i * 2 + 1] << 8) + WzKey[i * 2])));
196 | return outputString;
197 | }
198 |
199 | public string DecryptNonUnicodeString(char[] pStringToDecrypt) {
200 | string outputString = "";
201 | for (int i = 0; i < pStringToDecrypt.Length; i++)
202 | outputString += (char)(pStringToDecrypt[i] ^ WzKey[i]);
203 | return outputString;
204 | }
205 |
206 | public string ReadStringBlock(uint pOffset) {
207 | switch (ReadByte()) {
208 | case 0:
209 | case 0x73:
210 | return ReadString();
211 | case 1:
212 | case 0x1B:
213 | return ReadStringAtOffset(pOffset + ReadInt32());
214 | default:
215 | return "";
216 | }
217 | }
218 |
219 | #endregion
220 | }
221 | }
--------------------------------------------------------------------------------
/WzLib/Util/WzBinaryWriter.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.MapleCryptoLib;
2 | using System.Collections;
3 | using System.IO;
4 | using System.Linq;
5 |
6 | namespace MapleLib.WzLib.Util {
7 | /*
8 | TODO : Maybe WzBinaryReader/Writer should read and contain the hash (this is probably what's going to happen)
9 | */
10 |
11 | public class WzBinaryWriter : BinaryWriter {
12 | #region Properties
13 |
14 | public byte[] WzKey { get; set; }
15 | public uint Hash { get; set; }
16 | public Hashtable StringCache { get; set; }
17 | public WzHeader Header { get; set; }
18 | public bool LeaveOpen { get; internal set; }
19 |
20 | #endregion
21 |
22 | #region Constructors
23 |
24 | public WzBinaryWriter(Stream pOutput, byte[] pWzIv, bool pLeaveOpen = false) : base(pOutput) {
25 | WzKey = WzKeyGenerator.GenerateWzKey(pWzIv);
26 | StringCache = new Hashtable();
27 | LeaveOpen = pLeaveOpen;
28 | }
29 |
30 | #endregion
31 |
32 | #region Methods
33 |
34 | public void WriteStringValue(string pString, int pWithoutOffset, int pWithOffset) {
35 | if (pString.Length > 4 && StringCache.ContainsKey(pString)) {
36 | Write((byte)pWithOffset);
37 | Write((int)StringCache[pString]);
38 | } else {
39 | Write((byte)pWithoutOffset);
40 | int sOffset = (int)BaseStream.Position;
41 | Write(pString);
42 | if (!StringCache.ContainsKey(pString)) {
43 | StringCache[pString] = sOffset;
44 | }
45 | }
46 | }
47 |
48 | public void WriteWzObjectValue(string pString, byte pType) {
49 | string storeName = pType + "_" + pString;
50 | if (pString.Length > 4 && StringCache.ContainsKey(storeName)) {
51 | Write((byte)2);
52 | Write((int)StringCache[storeName]);
53 | } else {
54 | int sOffset = (int)(BaseStream.Position - Header.FStart);
55 | Write(pType);
56 | Write(pString);
57 | if (!StringCache.ContainsKey(storeName)) {
58 | StringCache[storeName] = sOffset;
59 | }
60 | }
61 | }
62 |
63 | public void Write(string pValue, bool pEncryption = true, bool pUnicode = false) {
64 | if (!pEncryption) {
65 | base.Write(pValue);
66 | return;
67 | }
68 | if (pValue.Length == 0) {
69 | Write((byte)0);
70 | } else {
71 | foreach (char t in pValue.Where(t => t > sbyte.MaxValue)) {
72 | pUnicode = true;
73 | }
74 |
75 | if (pUnicode) {
76 | ushort mask = 0xAAAA;
77 |
78 | if (pValue.Length > sbyte.MaxValue) {
79 | Write(sbyte.MaxValue);
80 | Write(pValue.Length);
81 | } else {
82 | Write((sbyte)pValue.Length);
83 | }
84 |
85 | for (int i = 0; i < pValue.Length; i++) {
86 | ushort encryptedChar = pValue[i];
87 | encryptedChar ^= (ushort)((WzKey[i * 2 + 1] << 8) + WzKey[i * 2]);
88 | encryptedChar ^= mask;
89 | mask++;
90 | Write(encryptedChar);
91 | }
92 | } else { // ASCII
93 | byte mask = 0xAA;
94 |
95 | if (pValue.Length > sbyte.MaxValue) {
96 | Write(sbyte.MinValue);
97 | Write(pValue.Length);
98 | } else {
99 | Write((sbyte)(-pValue.Length));
100 | }
101 |
102 | for (int i = 0; i < pValue.Length; i++) {
103 | byte encryptedChar = (byte)pValue[i];
104 | encryptedChar ^= WzKey[i];
105 | encryptedChar ^= mask;
106 | mask++;
107 | Write(encryptedChar);
108 | }
109 | }
110 | }
111 | }
112 |
113 | ///
114 | /// No Encryption
115 | ///
116 | ///
117 | ///
118 | public void Write(string pValue, int pLength) {
119 | for (int i = 0; i < pLength; i++) {
120 | if (i < pValue.Length) {
121 | Write(pValue[i]);
122 | } else {
123 | Write((byte)0);
124 | }
125 | }
126 | }
127 |
128 | ///
129 | /// No Encryption
130 | ///
131 | ///
132 | public void WriteNullTerminatedString(string pValue) {
133 | foreach (char t in pValue) {
134 | Write((byte)t);
135 | }
136 | }
137 |
138 | public void WriteCompressedInt(int pValue) {
139 | if (pValue > sbyte.MaxValue || pValue <= sbyte.MinValue) {
140 | Write(sbyte.MinValue);
141 | Write(pValue);
142 | } else {
143 | Write((sbyte)pValue);
144 | }
145 | }
146 |
147 | public void WriteCompressedLong(long pValue) {
148 | if (pValue > sbyte.MaxValue || pValue <= sbyte.MinValue) {
149 | Write(sbyte.MinValue);
150 | Write(pValue);
151 | } else {
152 | Write((sbyte)pValue);
153 | }
154 | }
155 |
156 | public void WriteOffset(uint pValue) {
157 | uint encOffset = (uint)BaseStream.Position;
158 | encOffset = (encOffset - Header.FStart) ^ 0xFFFFFFFF;
159 | encOffset *= Hash;
160 | encOffset -= CryptoConstants.WZ_OffsetConstant;
161 | encOffset = WzTool.RotateLeft(encOffset, (byte)(encOffset & 0x1F));
162 | uint writeOffset = encOffset ^ (pValue - (Header.FStart * 2));
163 | Write(writeOffset);
164 | }
165 |
166 | public override void Close() {
167 | if (!LeaveOpen) {
168 | base.Close();
169 | }
170 | }
171 |
172 | #endregion
173 | }
174 | }
--------------------------------------------------------------------------------
/WzLib/Util/WzKeyGenerator.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.MapleCryptoLib;
2 | using System;
3 | using System.IO;
4 | using System.Security.Cryptography;
5 |
6 | namespace MapleLib.WzLib.Util {
7 | public class WzKeyGenerator {
8 | ///
9 | /// Generates the wz key used in the encryption from ZLZ.dll
10 | ///
11 | /// Path to ZLZ.dll
12 | /// The wz key
13 | public static byte[] GenerateKeyFromZlz(string pPathToZlz) {
14 | FileStream zlzStream = File.OpenRead(pPathToZlz);
15 | byte[] wzKey = GenerateWzKey(GetIvFromZlz(zlzStream), GetAesKeyFromZlz(zlzStream));
16 | zlzStream.Close();
17 | return wzKey;
18 | }
19 |
20 | public static byte[] GetIvFromZlz(FileStream pZLZStream) {
21 | byte[] iv = new byte[4];
22 |
23 | pZLZStream.Seek(0x10040, SeekOrigin.Begin);
24 | pZLZStream.Read(iv, 0, 4);
25 | return iv;
26 | }
27 |
28 | private static byte[] GetAesKeyFromZlz(FileStream pZLZStream) {
29 | byte[] aes = new byte[32];
30 |
31 | pZLZStream.Seek(0x10060, SeekOrigin.Begin);
32 | for (int i = 0; i < 8; i++) {
33 | pZLZStream.Read(aes, i * 4, 4);
34 | pZLZStream.Seek(12, SeekOrigin.Current);
35 | }
36 | return aes;
37 | }
38 |
39 | public static byte[] GenerateWzKey(byte[] pWzIv) {
40 | return GenerateWzKey(pWzIv, CryptoConstants.TrimmedUserKey);
41 | }
42 |
43 | public static byte[] GenerateWzKey(byte[] pWzIv, byte[] pAesKey) {
44 | if (BitConverter.ToInt32(pWzIv, 0) == 0) {
45 | return new byte[ushort.MaxValue];
46 | }
47 | AesManaged crypto = new AesManaged { KeySize = 256, Key = pAesKey, Mode = CipherMode.ECB };
48 |
49 | MemoryStream memStream = new MemoryStream();
50 | CryptoStream cryptoStream = new CryptoStream(memStream, crypto.CreateEncryptor(), CryptoStreamMode.Write);
51 |
52 | byte[] input = MapleCrypto.multiplyBytes(pWzIv, 4, 4);
53 | byte[] wzKey = new byte[ushort.MaxValue];
54 | for (int i = 0; i < (wzKey.Length / 16); i++) {
55 | cryptoStream.Write(input, 0, 16);
56 | input = memStream.ToArray();
57 | Array.Copy(memStream.ToArray(), 0, wzKey, (i * 16), 16);
58 | memStream.Position = 0;
59 | }
60 |
61 | try {
62 | cryptoStream.Dispose();
63 | memStream.Dispose();
64 | } catch (Exception e) {
65 | Console.WriteLine("Error disposing AES streams" + e);
66 | }
67 |
68 | return wzKey;
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/WzLib/Util/WzTool.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.MapleCryptoLib;
2 | using System;
3 | using System.Collections;
4 | using System.Linq;
5 |
6 | namespace MapleLib.WzLib.Util {
7 | public static class WzTool {
8 | public static readonly Hashtable StringCache = new Hashtable();
9 |
10 | public static uint RotateLeft(uint pValue, byte pBits) {
11 | return (((pValue) << (pBits)) | ((pValue) >> (32 - (pBits))));
12 | }
13 |
14 | public static uint RotateRight(uint pValue, byte pBits) {
15 | return (((pValue) >> (pBits)) | ((pValue) << (32 - (pBits))));
16 | }
17 |
18 | public static int GetCompressedIntLength(int pInt) {
19 | if (pInt > 127 || pInt < -127)
20 | return 5;
21 | return 1;
22 | }
23 |
24 | public static int GetEncodedStringLength(string pString) {
25 | int len = 0;
26 | if (string.IsNullOrEmpty(pString))
27 | return 1;
28 | bool unicode = false;
29 | foreach (char c in pString.Where(c => c > 255)) {
30 | unicode = true;
31 | }
32 | if (unicode) {
33 | if (pString.Length > 126)
34 | len += 5;
35 | else
36 | len += 1;
37 | len += pString.Length * 2;
38 | } else {
39 | if (pString.Length > 127)
40 | len += 5;
41 | else
42 | len += 1;
43 | len += pString.Length;
44 | }
45 | return len;
46 | }
47 |
48 | public static int GetWzObjectValueLength(string pString, byte pType) {
49 | string storeName = pType + "_" + pString;
50 | if (pString.Length > 4 && StringCache.ContainsKey(storeName)) {
51 | return 5;
52 | }
53 | StringCache[storeName] = 1;
54 | return 1 + GetEncodedStringLength(pString);
55 | }
56 |
57 | public static T StringToEnum(string pName) {
58 | try {
59 | return (T)Enum.Parse(typeof(T), pName);
60 | } catch {
61 | return default(T);
62 | }
63 | }
64 |
65 | public static byte[] GetIvByMapleVersion(WzMapleVersion pVersion) {
66 | switch (pVersion) {
67 | case WzMapleVersion.EMS:
68 | return CryptoConstants.WZ_MSEAIV;
69 | case WzMapleVersion.GMS:
70 | return CryptoConstants.WZ_GMSIV;
71 | //case WzMapleVersion.CLASSIC:
72 | //case WzMapleVersion.BMS:
73 | default:
74 | return new byte[4];
75 | }
76 | }
77 |
78 | public static byte[] Combine(byte[] pFirst, byte[] pSecond) {
79 | byte[] result = new byte[pFirst.Length + pSecond.Length];
80 | Array.Copy(pFirst, 0, result, 0, pFirst.Length);
81 | Array.Copy(pSecond, 0, result, pFirst.Length, pSecond.Length);
82 | return result;
83 | }
84 | }
85 | }
--------------------------------------------------------------------------------
/WzLib/Util/XmlUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace MapleLib.WzLib.Util {
4 | public class XmlUtil {
5 | private static readonly char[] specialCharacters = { '"', '\'', '&', '<', '>' };
6 | private static readonly string[] replacementStrings = { """, "'", "&", "<", ">" };
7 |
8 | public static string SanitizeText(string pText) {
9 | string fixedText = "";
10 | bool charFixed;
11 | foreach (char t in pText) {
12 | charFixed = false;
13 | for (int k = 0; k < specialCharacters.Length; k++) {
14 | if (t != specialCharacters[k])
15 | continue;
16 | fixedText += replacementStrings[k];
17 | charFixed = true;
18 | break;
19 | }
20 | if (!charFixed) {
21 | fixedText += t;
22 | }
23 | }
24 | return fixedText;
25 | }
26 |
27 | public static string OpenNamedTag(string pTag, string pName, bool pFinish) {
28 | return OpenNamedTag(pTag, pName, pFinish, false);
29 | }
30 |
31 | public static string EmptyNamedTag(string pTag, string pName) {
32 | return OpenNamedTag(pTag, pName, true, true);
33 | }
34 |
35 | public static string EmptyNamedValuePair(string pTag, string pName, string pValue) {
36 | return OpenNamedTag(pTag, pName, false, false) + Attrib("value", pValue, true, true);
37 | }
38 |
39 | public static string OpenNamedTag(string pTag, string pName, bool pFinish, bool pEmpty) {
40 | return "<" + pTag + " name=\"" + pName + "\"" + (pFinish ? (pEmpty ? "/>" : ">") : " ");
41 | }
42 |
43 | public static string Attrib(string pName, string pValue) {
44 | return Attrib(pName, pValue, false, false);
45 | }
46 |
47 | public static string Attrib(string pName, string pValue, bool pCloseTag, bool pEmpty) {
48 | return pName + "=\"" + SanitizeText(pValue) + "\"" + (pCloseTag ? (pEmpty ? "/>" : ">") : " ");
49 | }
50 |
51 | public static string CloseTag(string pTag) {
52 | return "" + pTag + ">";
53 | }
54 |
55 | public static string Indentation(int pLevel) {
56 | char[] indent = new char[pLevel];
57 | for (int i = 0; i < indent.Length; i++) {
58 | indent[i] = '\t';
59 | }
60 | return new String(indent);
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/WzLib/WzHeader.cs:
--------------------------------------------------------------------------------
1 | namespace MapleLib.WzLib {
2 | public class WzHeader {
3 | private string mIdent;
4 | private string mCopyright;
5 | private ulong mFSize;
6 | private uint mFStart;
7 | private uint mExtraBytes;
8 |
9 | public string Ident { get { return mIdent; } set { mIdent = value; } }
10 |
11 | public string Copyright { get { return mCopyright; } set { mCopyright = value; } }
12 |
13 | public ulong FSize { get { return mFSize; } set { mFSize = value; } }
14 |
15 | public uint FStart { get { return mFStart; } set { mFStart = value; } }
16 |
17 | public uint ExtraBytes { get { return mExtraBytes; } set { mExtraBytes = value; } }
18 |
19 | public void RecalculateFileStart() {
20 | mFStart = (uint)(mIdent.Length + sizeof(ulong) + sizeof(uint) + mCopyright.Length + 1) + mExtraBytes;
21 | }
22 |
23 | public static WzHeader GetDefault() {
24 | return new WzHeader { mIdent = "PKG1", mCopyright = "Package file v1.0 Copyright 2002 Wizet, ZMS", mFStart = 60, mFSize = 0, mExtraBytes = 0 };
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/WzLib/WzImage.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using MapleLib.WzLib.WzProperties;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 |
8 | namespace MapleLib.WzLib {
9 | ///
10 | /// A .img contained in a wz directory
11 | ///
12 | public class WzImage : WzSubProperty {
13 | #region Fields
14 |
15 | internal bool mParsed;
16 | internal bool initialParse;
17 | internal int mSize, checksum;
18 | internal uint mOffset;
19 | internal WzBinaryReader mReader;
20 | internal int mBlockStart;
21 | internal long mTempFileStart;
22 | internal long mTempFileEnd;
23 | internal HashSet referencedImgs = new HashSet();
24 |
25 | #endregion
26 |
27 | ///
28 | /// The parent of the object
29 | ///
30 | public override AWzObject Parent { get { return mParent; } internal set { mParent = value; } }
31 |
32 | ///
33 | /// The name of the image
34 | ///
35 | public override string Name { get { return mName; } set { mName = value; } }
36 |
37 | ///
38 | /// Is the object parsed
39 | ///
40 | public bool Parsed { get { return mParsed; } }
41 |
42 | ///
43 | /// Is this the initial parse
44 | ///
45 | public bool InitialParse { get { return initialParse; } }
46 |
47 | ///
48 | /// The size in the wz file of the image
49 | ///
50 | public int BlockSize { get { return mSize; } set { mSize = value; } }
51 |
52 | ///
53 | /// The checksum of the image
54 | ///
55 | public int Checksum { get { return checksum; } set { checksum = value; } }
56 |
57 | ///
58 | /// The offset of the image
59 | ///
60 | public uint Offset { get { return mOffset; } set { mOffset = value; } }
61 |
62 | public int BlockStart { get { return mBlockStart; } }
63 |
64 | ///
65 | /// The properties contained in the image
66 | ///
67 | public override List WzProperties {
68 | get {
69 | if (mReader != null && !mParsed) {
70 | ParseImage();
71 | }
72 | return mProperties;
73 | }
74 | }
75 |
76 | ///
77 | /// Gets a wz property by it's name
78 | ///
79 | /// The name of the property
80 | /// The wz property with the specified name
81 | public override AWzImageProperty this[string pName] {
82 | get {
83 | if (mReader != null && !mParsed)
84 | ParseImage();
85 | return mProperties.FirstOrDefault(iwp => iwp.Name.ToLower() == pName.ToLower());
86 | }
87 | }
88 |
89 | ///
90 | /// The WzObjectType of the image
91 | ///
92 | public override WzObjectType ObjectType {
93 | get {
94 | if (mReader != null && !mParsed)
95 | ParseImage();
96 | return WzObjectType.Image;
97 | }
98 | }
99 |
100 | ///
101 | /// Creates a blank WzImage
102 | ///
103 | public WzImage() {
104 | }
105 |
106 | ///
107 | /// Creates a WzImage with the given name
108 | ///
109 | /// The name of the image
110 | public WzImage(string pName) {
111 | mName = pName;
112 | initialParse = true;
113 | }
114 |
115 | public WzImage(string pName, Stream pDataStream, WzMapleVersion pMapleVersion) {
116 | mName = pName;
117 | mReader = new WzBinaryReader(pDataStream, WzTool.GetIvByMapleVersion(pMapleVersion));
118 | initialParse = true;
119 | }
120 |
121 | internal WzImage(string pName, WzBinaryReader pReader) {
122 | mName = pName;
123 | mReader = pReader;
124 | mBlockStart = (int)pReader.BaseStream.Position;
125 | initialParse = true;
126 | }
127 |
128 | /*internal WzImage(string pName, WzBinaryReader pReader, byte[] wzKey) {
129 | mName = pName;
130 | mReader = pReader;
131 | mBlockStart = (int) pReader.BaseStream.Position;
132 | WzKey = wzKey;
133 | }*/
134 |
135 | public void PartialDispose() {
136 | if (mProperties == null)
137 | return;
138 | foreach (AWzImageProperty prop in mProperties)
139 | prop.Dispose();
140 | mProperties.Clear();
141 | foreach (WzImage img in referencedImgs)
142 | img.PartialDispose();
143 | referencedImgs.Clear();
144 | UnparseImage();
145 | initialParse = false;
146 | }
147 |
148 | public override void Dispose() {
149 | mName = null;
150 | mReader = null;
151 | if (mProperties != null) {
152 | foreach (AWzImageProperty prop in mProperties)
153 | prop.Dispose();
154 | mProperties.Clear();
155 | mProperties = null;
156 | }
157 | if (referencedImgs != null) {
158 | foreach (WzImage img in referencedImgs)
159 | img.Dispose();
160 | referencedImgs.Clear();
161 | referencedImgs = null;
162 | }
163 | mParsed = false;
164 | }
165 |
166 | ///
167 | /// Parses the image from the wz filetod
168 | ///
169 | public void ParseImage() {
170 | //long originalPos = mReader.BaseStream.Position;
171 | //byte[] keyCopy = mReader.WzKey;
172 | //Array.Copy(mReader.WzKey, keyCopy, mReader.WzKey.Length);
173 | mReader.BaseStream.Position = mOffset;
174 | byte b = mReader.ReadByte();
175 | if (b != 0x73 || mReader.ReadString() != "Property" || mReader.ReadUInt16() != 0)
176 | return;
177 | List properties = ParsePropertyList(mOffset, mReader, this, this);
178 | mProperties.AddRange(properties);
179 | properties.Clear();
180 | mParsed = true;
181 | }
182 |
183 | public byte[] DataBlock {
184 | get {
185 | byte[] blockData = null;
186 | if (mReader != null && mSize > 0) {
187 | blockData = mReader.ReadBytes(mSize);
188 | mReader.BaseStream.Position = mBlockStart;
189 | }
190 | return blockData;
191 | }
192 | }
193 |
194 | public void UnparseImage() {
195 | mParsed = false;
196 | mProperties = new List();
197 | }
198 |
199 | public void AddReferencedImage(WzImage img) {
200 | if (!Equals(img) && !img.InitialParse)
201 | referencedImgs.Add(img);
202 | }
203 |
204 | internal void SaveImage(WzBinaryWriter pWriter) {
205 | if (mReader != null && !mParsed)
206 | ParseImage();
207 | long startPos = pWriter.BaseStream.Position;
208 | WriteValue(pWriter);
209 | pWriter.StringCache.Clear();
210 | mSize = (int)(pWriter.BaseStream.Position - startPos);
211 | }
212 |
213 | public void ExportXml(StreamWriter pWriter, bool pOneFile, int pLevel) {
214 | if (pOneFile) {
215 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.OpenNamedTag("WzImage", mName, true));
216 | DumpPropertyList(pWriter, pLevel, WzProperties);
217 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.CloseTag("WzImage"));
218 | } else {
219 | throw new Exception("Under Construction");
220 | }
221 | }
222 | }
223 | }
--------------------------------------------------------------------------------
/WzLib/WzListFile.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using System.Collections.Generic;
3 | using System.IO;
4 |
5 | namespace MapleLib.WzLib {
6 | ///
7 | /// A class that parses and contains the data of a wz list file
8 | ///
9 | public class WzListFile : AWzObject {
10 | #region Fields
11 |
12 | internal byte[] mWzFileBytes;
13 | internal List mListEntries = new List();
14 | internal string mName = "";
15 | internal byte[] mWzIv;
16 | internal WzMapleVersion mVersion;
17 |
18 | #endregion
19 |
20 | ///
21 | /// Name of the WzListFile
22 | ///
23 | public override string Name { get { return mName; } set { mName = value; } }
24 |
25 | ///
26 | /// The entries in the list wz file
27 | ///
28 | public string[] WzListEntries { get { return mListEntries.ToArray(); } }
29 |
30 | ///
31 | /// The WzObjectType of the file
32 | ///
33 | public override WzObjectType ObjectType { get { return WzObjectType.File; } }
34 |
35 | public override AWzObject Parent { get { return null; } internal set { } }
36 |
37 | public override void Dispose() {
38 | mWzFileBytes = null;
39 | mName = null;
40 | mListEntries.Clear();
41 | mListEntries = null;
42 | }
43 |
44 | ///
45 | /// Open a wz list file from a file on the disk
46 | ///
47 | /// Path to the wz file
48 | public WzListFile(string pFilePath, WzMapleVersion pVersion) {
49 | mName = Path.GetFileName(pFilePath);
50 | mWzIv = WzTool.GetIvByMapleVersion(pVersion);
51 | mVersion = pVersion;
52 | mWzFileBytes = File.ReadAllBytes(pFilePath);
53 | }
54 |
55 | ///
56 | /// Open a wz list file from an array of bytes in the memory
57 | ///
58 | /// The wz file in the memory
59 | public WzListFile(byte[] pFileBytes, byte[] pWzIv) {
60 | mWzFileBytes = pFileBytes;
61 | mWzIv = pWzIv;
62 | }
63 |
64 | public WzListFile(WzMapleVersion pVersion, string pName) {
65 | mName = pName;
66 | mVersion = pVersion;
67 | mWzIv = WzTool.GetIvByMapleVersion(pVersion);
68 | }
69 |
70 | ///
71 | /// Parses the wz list file
72 | ///
73 | public void ParseWzFile() {
74 | using (WzBinaryReader wzParser = new WzBinaryReader(new MemoryStream(mWzFileBytes), mWzIv)) {
75 | while (wzParser.PeekChar() != -1) {
76 | int Len = wzParser.ReadInt32();
77 | char[] List = new char[Len];
78 | for (int i = 0; i < Len; i++)
79 | List[i] = (char)wzParser.ReadInt16();
80 | wzParser.ReadUInt16();
81 | string Decrypted = wzParser.DecryptString(List);
82 | if (wzParser.PeekChar() == -1)
83 | if (Decrypted[Decrypted.Length - 1] == '/')
84 | Decrypted = Decrypted.TrimEnd("/".ToCharArray()) + "g"; // Last char should always be a g (.img)
85 | mListEntries.Add(Decrypted);
86 | }
87 | }
88 | }
89 |
90 | internal void SaveToDisk(string pPath) {
91 | WzBinaryWriter wzWriter = new WzBinaryWriter(File.Create(pPath), mWzIv);
92 | foreach (string entry in mListEntries) {
93 | string newEntry = entry + "\0";
94 | wzWriter.Write(newEntry.Length);
95 | wzWriter.Write(newEntry, true, true);
96 | }
97 | wzWriter.Close();
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/WzLib/WzMapleVersion.cs:
--------------------------------------------------------------------------------
1 | namespace MapleLib.WzLib {
2 | public enum WzMapleVersion {
3 | GMS,
4 | EMS,
5 | BMS,
6 | CLASSIC,
7 | GENERATE//,
8 | //LOAD_FROM_ZLZ
9 | }
10 | }
--------------------------------------------------------------------------------
/WzLib/WzObjectType.cs:
--------------------------------------------------------------------------------
1 | namespace MapleLib.WzLib {
2 | public enum WzObjectType {
3 | File,
4 | Image,
5 | Directory,
6 | Property,
7 | List
8 | }
9 | }
--------------------------------------------------------------------------------
/WzLib/WzProperties/WzByteFloatProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using System.IO;
3 |
4 | namespace MapleLib.WzLib.WzProperties {
5 | ///
6 | /// A property that is stored in the wz file with a byte and possibly followed by a float. If the
7 | /// byte is 0, the value is 0, else the value is the float that follows.
8 | ///
9 | public class WzByteFloatProperty : AWzImageProperty {
10 | #region Fields
11 |
12 | internal string mName;
13 | internal float mVal;
14 | internal AWzObject mParent;
15 | internal WzImage mImgParent;
16 |
17 | #endregion
18 |
19 | #region Inherited Members
20 |
21 | public override object WzValue { get { return mVal; } set { mVal = (float)value; } }
22 |
23 | ///
24 | /// The parent of the object
25 | ///
26 | public override AWzObject Parent { get { return mParent; } internal set { mParent = value; } }
27 |
28 | ///
29 | /// The image that this property is contained in
30 | ///
31 | public override WzImage ParentImage { get { return mImgParent; } internal set { mImgParent = value; } }
32 |
33 | ///
34 | /// The WzPropertyType of the property
35 | ///
36 | public override WzPropertyType PropertyType { get { return WzPropertyType.ByteFloat; } }
37 |
38 | ///
39 | /// The name of the property
40 | ///
41 | public override string Name { get { return mName; } set { mName = value; } }
42 |
43 | public override void WriteValue(WzBinaryWriter pWriter) {
44 | pWriter.Write((byte)4);
45 | if (Value == 0f) {
46 | pWriter.Write((byte)0);
47 | } else {
48 | pWriter.Write((byte)0x80);
49 | pWriter.Write(Value);
50 | }
51 | }
52 |
53 | public override void ExportXml(StreamWriter pWriter, int pLevel) {
54 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.EmptyNamedValuePair("WzByteFloat", Name, Value.ToString()));
55 | }
56 |
57 | ///
58 | /// Dispose the object
59 | ///
60 | public override void Dispose() {
61 | mName = null;
62 | }
63 |
64 | #endregion
65 |
66 | #region Custom Members
67 |
68 | ///
69 | /// The value of the property
70 | ///
71 | public float Value { get { return mVal; } set { mVal = Value; } }
72 |
73 | ///
74 | /// Creates a blank WzByteFloatProperty
75 | ///
76 | public WzByteFloatProperty() {
77 | }
78 |
79 | ///
80 | /// Creates a WzByteFloatProperty with the specified name
81 | ///
82 | /// The name of the property
83 | public WzByteFloatProperty(string pName) {
84 | mName = pName;
85 | }
86 |
87 | ///
88 | /// Creates a WzByteFloatProperty with the specified name and value
89 | ///
90 | /// The name of the property
91 | /// The value of the property
92 | public WzByteFloatProperty(string pName, float pValue) {
93 | mName = pName;
94 | mVal = pValue;
95 | }
96 |
97 | #endregion
98 |
99 | #region Cast Values
100 |
101 | internal override float ToFloat(float pDef = 0) {
102 | return mVal;
103 | }
104 |
105 | internal override double ToDouble(double pDef = 0) {
106 | return mVal;
107 | }
108 |
109 | internal override int ToInt(int pDef = 0) {
110 | return (int)mVal;
111 | }
112 |
113 | internal override short ToShort(short pDef = 0) {
114 | return (short)mVal;
115 | }
116 |
117 | #endregion
118 | }
119 | }
--------------------------------------------------------------------------------
/WzLib/WzProperties/WzCanvasProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.IO;
5 |
6 | namespace MapleLib.WzLib.WzProperties {
7 | ///
8 | /// A property that can contain sub properties and has one png image
9 | ///
10 | public class WzCanvasProperty : APropertyContainer {
11 | #region Fields
12 |
13 | internal List mProperties = new List();
14 | internal WzPngProperty mImageProp;
15 | internal string mName;
16 | internal AWzObject mParent;
17 | internal WzImage mImgParent;
18 | internal string _inlink;
19 | internal WzCanvasProperty _inlinkValue;
20 | internal string _outlink;
21 | internal WzCanvasProperty _outlinkValue;
22 |
23 | #endregion
24 |
25 | #region Inherited Members
26 |
27 | public override object WzValue { get { return PngProperty; } set { mImageProp.WzValue = value; } }
28 |
29 | ///
30 | /// The parent of the object
31 | ///
32 | public override AWzObject Parent { get { return mParent; } internal set { mParent = value; } }
33 |
34 | ///
35 | /// The image that this property is contained in
36 | ///
37 | public override WzImage ParentImage { get { return mImgParent; } internal set { mImgParent = value; } }
38 |
39 | ///
40 | /// The WzPropertyType of the property
41 | ///
42 | public override WzPropertyType PropertyType { get { return WzPropertyType.Canvas; } }
43 |
44 | ///
45 | /// The properties contained in this property
46 | ///
47 | public override List WzProperties { get { return mProperties; } }
48 |
49 | ///
50 | /// The inlink contained in this property
51 | ///
52 | public string Inlink { get { return _inlink; } }
53 |
54 | public WzCanvasProperty InlinkValue {
55 | get {
56 | if (_inlink == null) return null;
57 | if (_inlinkValue == null) {
58 | AWzObject curObj = mImgParent;
59 | string[] seperatedPath = _inlink.Split('/');
60 | foreach (string t in seperatedPath) {
61 | if (curObj == null)
62 | return null;
63 | string trimmedName = t.Trim();
64 | switch (curObj.ObjectType) {
65 | case WzObjectType.Image:
66 | curObj = ((WzImage)curObj)[trimmedName];
67 | continue;
68 | case WzObjectType.Property:
69 | switch (((AWzImageProperty)curObj).PropertyType) {
70 | case WzPropertyType.Canvas:
71 | curObj = ((WzCanvasProperty)curObj)[trimmedName];
72 | continue;
73 | case WzPropertyType.SubProperty:
74 | curObj = ((WzSubProperty)curObj)[trimmedName];
75 | continue;
76 | default:
77 | return null;
78 | }
79 | default:
80 | return null;
81 | }
82 | }
83 | _inlinkValue = (WzCanvasProperty)curObj;
84 | }
85 | return _inlinkValue;
86 | }
87 | }
88 |
89 | ///
90 | /// The outlink contained in this property
91 | ///
92 | public string Outlink { get { return _outlink; } }
93 |
94 | public WzCanvasProperty OutlinkValue {
95 | get {
96 | if (_outlink == null) return null;
97 | if (_outlinkValue == null || _outlinkValue != null && _outlinkValue.Name == null) { // Relocate if referenced value was disposed
98 | AWzObject curObj = mImgParent;
99 | while (curObj.Parent != null)
100 | curObj = curObj.Parent;
101 | string[] seperatedPath = _outlink.Substring(_outlink.IndexOf("/") + 1).Split('/');
102 | foreach (string t in seperatedPath) {
103 | if (curObj == null)
104 | return null;
105 | string trimmedName = t.Trim();
106 | switch (curObj.ObjectType) {
107 | case WzObjectType.File:
108 | case WzObjectType.Directory:
109 | curObj = ((WzDirectory)curObj)[trimmedName];
110 | continue;
111 | case WzObjectType.Image:
112 | curObj = ((WzImage)curObj)[trimmedName];
113 | continue;
114 | case WzObjectType.Property:
115 | switch (((AWzImageProperty)curObj).PropertyType) {
116 | case WzPropertyType.Canvas:
117 | curObj = ((WzCanvasProperty)curObj)[trimmedName];
118 | continue;
119 | case WzPropertyType.SubProperty:
120 | curObj = ((WzSubProperty)curObj)[trimmedName];
121 | continue;
122 | default:
123 | return null;
124 | }
125 | default:
126 | return null;
127 | }
128 | }
129 | if (curObj != null) {
130 | _outlinkValue = (WzCanvasProperty)curObj;
131 | mImgParent.AddReferencedImage(_outlinkValue.ParentImage);
132 | }
133 | }
134 | return _outlinkValue;
135 | }
136 | }
137 |
138 | ///
139 | /// The properties contained in this property
140 | ///
141 | public override void AddProperties(List pProps) {
142 | foreach (AWzImageProperty prop in pProps) {
143 | AddProperty(prop);
144 | if (prop.PropertyType.Equals(WzPropertyType.String)) {
145 | var stringProp = (WzStringProperty)prop;
146 | if (stringProp.Name.Contains("_inlink")) {
147 | _inlink = stringProp.Value;
148 | TrimLinkSpaces(ref _inlink);
149 | break;
150 | }
151 | if (stringProp.Name.Contains("_outlink")) {
152 | _outlink = stringProp.Value;
153 | TrimLinkSpaces(ref _outlink);
154 | break;
155 | }
156 | }
157 | }
158 | }
159 |
160 | private static void TrimLinkSpaces(ref string link) {
161 | string sanitizedLink = string.Empty;
162 | string[] seperatedPath = link.Split('/');
163 | foreach (string t in seperatedPath) {
164 | sanitizedLink += t.Trim() + "/";
165 | }
166 | link = sanitizedLink.Substring(0, sanitizedLink.Length - 1);
167 | }
168 |
169 | ///
170 | /// The name of the property
171 | ///
172 | public override string Name { get { return mName; } set { mName = value; } }
173 |
174 | ///
175 | /// Gets a wz property by it's name
176 | ///
177 | /// The name of the property
178 | /// The wz property with the specified name
179 | public override void WriteValue(WzBinaryWriter pWriter) {
180 | pWriter.WriteStringValue("Canvas", 0x73, 0x1B);
181 | pWriter.Write((byte)0);
182 | if (mProperties.Count > 0) {
183 | pWriter.Write((byte)1);
184 | WritePropertyList(pWriter, mProperties);
185 | } else {
186 | pWriter.Write((byte)0);
187 | }
188 | pWriter.WriteCompressedInt(PngProperty.Width);
189 | pWriter.WriteCompressedInt(PngProperty.Height);
190 | pWriter.WriteCompressedInt(PngProperty.mFormat);
191 | pWriter.Write((byte)PngProperty.mFormat2);
192 | pWriter.Write(0);
193 | byte[] bytes = PngProperty.GetCompressedBytes();
194 | pWriter.Write(bytes.Length + 1);
195 | pWriter.Write((byte)0);
196 | pWriter.Write(bytes);
197 | }
198 |
199 | public override void ExportXml(StreamWriter pWriter, int pLevel) {
200 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.OpenNamedTag("WzCanvas", Name, false, false) + XmlUtil.Attrib("width", PngProperty.Width.ToString()) + XmlUtil.Attrib("height", PngProperty.Height.ToString(), true, false));
201 | DumpPropertyList(pWriter, pLevel, WzProperties);
202 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.CloseTag("WzCanvas"));
203 | }
204 |
205 | ///
206 | /// Dispose the object
207 | ///
208 | public override void Dispose() {
209 | mName = null;
210 | _inlink = null;
211 | _inlinkValue = null;
212 | _outlink = null;
213 | if (_outlinkValue != null)
214 | mImgParent.AddReferencedImage(_outlinkValue.ParentImage);
215 | _outlinkValue = null;
216 | mImageProp.Dispose();
217 | mImageProp = null;
218 | foreach (AWzImageProperty prop in mProperties) {
219 | prop.Dispose();
220 | }
221 | mProperties.Clear();
222 | mProperties = null;
223 | }
224 |
225 | #endregion
226 |
227 | #region Custom Members
228 |
229 | ///
230 | /// The png image for this canvas property
231 | ///
232 | public WzPngProperty PngProperty { get { return mImageProp; } set { mImageProp = value; } }
233 |
234 | ///
235 | /// Creates a blank WzCanvasProperty
236 | ///
237 | public WzCanvasProperty() {
238 | }
239 |
240 | ///
241 | /// Creates a WzCanvasProperty with the specified name
242 | ///
243 | /// The name of the property
244 | public WzCanvasProperty(string pName) {
245 | mName = pName;
246 | }
247 |
248 | #endregion
249 |
250 | #region Cast Values
251 |
252 | internal override WzPngProperty ToPngProperty(WzPngProperty pDef = null) {
253 | return mImageProp;
254 | }
255 |
256 | internal override Bitmap ToBitmap(Bitmap pDef = null) {
257 | return mImageProp.GetPNG();
258 | }
259 |
260 | #endregion
261 | }
262 | }
--------------------------------------------------------------------------------
/WzLib/WzProperties/WzCompressedIntProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using System.IO;
3 |
4 | namespace MapleLib.WzLib.WzProperties {
5 | ///
6 | /// A property that is stored in the wz file with a signed byte and possibly followed by an int. If the
7 | /// signed byte is equal to -128, the value is is the int that follows, else the value is the byte.
8 | ///
9 | public class WzCompressedIntProperty : AWzImageProperty {
10 | #region Fields
11 |
12 | internal string mName;
13 | internal int mVal;
14 | internal AWzObject mParent;
15 | internal WzImage mImgParent;
16 |
17 | #endregion
18 |
19 | #region Inherited Members
20 |
21 | public override object WzValue { get { return mVal; } set { mVal = (int)value; } }
22 |
23 | ///
24 | /// The parent of the object
25 | ///
26 | public override AWzObject Parent { get { return mParent; } internal set { mParent = value; } }
27 |
28 | ///
29 | /// The image that this property is contained in
30 | ///
31 | public override WzImage ParentImage { get { return mImgParent; } internal set { mImgParent = value; } }
32 |
33 | ///
34 | /// The WzPropertyType of the property
35 | ///
36 | public override WzPropertyType PropertyType { get { return WzPropertyType.CompressedInt; } }
37 |
38 | ///
39 | /// The name of the property
40 | ///
41 | public override string Name { get { return mName; } set { mName = value; } }
42 |
43 | public override void WriteValue(WzBinaryWriter pWriter) {
44 | pWriter.Write((byte)3);
45 | pWriter.WriteCompressedInt(Value);
46 | }
47 |
48 | public override void ExportXml(StreamWriter pWriter, int pLevel) {
49 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.EmptyNamedValuePair("WzCompressedInt", Name, Value.ToString()));
50 | }
51 |
52 | ///
53 | /// Dispose the object
54 | ///
55 | public override void Dispose() {
56 | mName = null;
57 | }
58 |
59 | #endregion
60 |
61 | #region Custom Members
62 |
63 | ///
64 | /// The value of the property
65 | ///
66 | public int Value { get { return mVal; } set { mVal = value; } }
67 |
68 | ///
69 | /// Creates a blank WzCompressedIntProperty
70 | ///
71 | public WzCompressedIntProperty() {
72 | }
73 |
74 | ///
75 | /// Creates a WzCompressedIntProperty with the specified name
76 | ///
77 | /// The name of the property
78 | public WzCompressedIntProperty(string pName) {
79 | mName = pName;
80 | }
81 |
82 | ///
83 | /// Creates a WzCompressedIntProperty with the specified name and value
84 | ///
85 | /// The name of the property
86 | /// The value of the property
87 | public WzCompressedIntProperty(string pName, int pValue) {
88 | mName = pName;
89 | mVal = pValue;
90 | }
91 |
92 | #endregion
93 |
94 | #region Cast Values
95 |
96 | internal override float ToFloat(float pDef = 0) {
97 | return mVal;
98 | }
99 |
100 | internal override double ToDouble(double pDef = 0) {
101 | return mVal;
102 | }
103 |
104 | internal override int ToInt(int pDef = 0) {
105 | return mVal;
106 | }
107 |
108 | internal override short ToShort(short pDef = 0) {
109 | return (short)mVal;
110 | }
111 |
112 | #endregion
113 | }
114 | }
--------------------------------------------------------------------------------
/WzLib/WzProperties/WzCompressedLongProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using System.IO;
3 |
4 | namespace MapleLib.WzLib.WzProperties {
5 | ///
6 | /// A property that is stored in the wz file with a signed byte and possibly followed by an int. If the
7 | /// signed byte is equal to -128, the value is is the long that follows, else the value is the byte.
8 | ///
9 | public class WzCompressedLongProperty : AWzImageProperty {
10 | #region Fields
11 |
12 | internal string mName;
13 | internal long mVal;
14 | internal AWzObject mParent;
15 | internal WzImage mImgParent;
16 |
17 | #endregion
18 |
19 | #region Inherited Members
20 |
21 | public override object WzValue { get { return mVal; } set { mVal = (long)value; } }
22 |
23 | ///
24 | /// The parent of the object
25 | ///
26 | public override AWzObject Parent { get { return mParent; } internal set { mParent = value; } }
27 |
28 | ///
29 | /// The image that this property is contained in
30 | ///
31 | public override WzImage ParentImage { get { return mImgParent; } internal set { mImgParent = value; } }
32 |
33 | ///
34 | /// The WzPropertyType of the property
35 | ///
36 | public override WzPropertyType PropertyType { get { return WzPropertyType.CompressedLong; } }
37 |
38 | ///
39 | /// The name of the property
40 | ///
41 | public override string Name { get { return mName; } set { mName = value; } }
42 |
43 | public override void WriteValue(WzBinaryWriter pWriter) {
44 | pWriter.Write((byte)20);
45 | pWriter.WriteCompressedLong(Value);
46 | }
47 |
48 | public override void ExportXml(StreamWriter pWriter, int pLevel) {
49 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.EmptyNamedValuePair("WzCompressedLong", Name, Value.ToString()));
50 | }
51 |
52 | ///
53 | /// Dispose the object
54 | ///
55 | public override void Dispose() {
56 | mName = null;
57 | }
58 |
59 | #endregion
60 |
61 | #region Custom Members
62 |
63 | ///
64 | /// The value of the property
65 | ///
66 | public long Value { get { return mVal; } set { mVal = value; } }
67 |
68 | ///
69 | /// Creates a blank WzCompressedLongProperty
70 | ///
71 | public WzCompressedLongProperty() {
72 | }
73 |
74 | ///
75 | /// Creates a WzCompressedLongProperty with the specified name
76 | ///
77 | /// The name of the property
78 | public WzCompressedLongProperty(string pName) {
79 | mName = pName;
80 | }
81 |
82 | ///
83 | /// Creates a WzCompressedLongProperty with the specified name and value
84 | ///
85 | /// The name of the property
86 | /// The value of the property
87 | public WzCompressedLongProperty(string pName, long pValue) {
88 | mName = pName;
89 | mVal = pValue;
90 | }
91 |
92 | #endregion
93 |
94 | #region Cast Values
95 |
96 | internal override float ToFloat(float pDef = 0) {
97 | return mVal;
98 | }
99 |
100 | internal override double ToDouble(double pDef = 0) {
101 | return mVal;
102 | }
103 |
104 | internal override int ToInt(int pDef = 0) {
105 | return (int)mVal;
106 | }
107 |
108 | internal override short ToShort(short pDef = 0) {
109 | return (short)mVal;
110 | }
111 |
112 | #endregion
113 | }
114 | }
--------------------------------------------------------------------------------
/WzLib/WzProperties/WzConvexProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 |
6 | namespace MapleLib.WzLib.WzProperties {
7 | ///
8 | /// A property that contains several WzExtendedPropertys
9 | ///
10 | public class WzConvexProperty : APropertyContainer {
11 | #region Fields
12 |
13 | internal List mProperties = new List();
14 | internal string mName;
15 | internal AWzObject mParent;
16 | internal WzImage mImgParent;
17 |
18 | #endregion
19 |
20 | #region Inherited Members
21 |
22 | ///
23 | /// The parent of the object
24 | ///
25 | public override AWzObject Parent { get { return mParent; } internal set { mParent = value; } }
26 |
27 | ///
28 | /// The image that this property is contained in
29 | ///
30 | public override WzImage ParentImage { get { return mImgParent; } internal set { mImgParent = value; } }
31 |
32 | ///
33 | /// The WzPropertyType of the property
34 | ///
35 | public override WzPropertyType PropertyType { get { return WzPropertyType.Convex; } }
36 |
37 | ///
38 | /// The properties contained in the property
39 | ///
40 | public override List WzProperties { get { return mProperties; } }
41 |
42 | ///
43 | /// The name of this property
44 | ///
45 | public override string Name { get { return mName; } set { mName = value; } }
46 |
47 | public override object WzValue { get { return null; } set { } }
48 |
49 | public override void WriteValue(WzBinaryWriter pWriter) {
50 | pWriter.WriteStringValue("Shape2D#Convex2D", 0x73, 0x1B);
51 | pWriter.WriteCompressedInt(mProperties.Count);
52 | foreach (AWzImageProperty prop in mProperties) {
53 | prop.WriteValue(pWriter);
54 | }
55 | }
56 |
57 | public override void ExportXml(StreamWriter pWriter, int pLevel) {
58 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.OpenNamedTag("WzConvex", Name, true));
59 | DumpPropertyList(pWriter, pLevel, WzProperties);
60 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.CloseTag("WzConvex"));
61 | }
62 |
63 | public override void Dispose() {
64 | mName = null;
65 | foreach (AWzImageProperty prop in mProperties)
66 | prop.Dispose();
67 | mProperties.Clear();
68 | mProperties = null;
69 | }
70 |
71 | public override void AddProperty(AWzImageProperty pProp) {
72 | if (pProp is IExtended) {
73 | base.AddProperty(pProp);
74 | } else {
75 | throw new Exception("Convex can only hold extended properties");
76 | }
77 | }
78 |
79 | #endregion
80 |
81 | #region Custom Members
82 |
83 | ///
84 | /// Creates a blank WzConvexProperty
85 | ///
86 | public WzConvexProperty() {
87 | }
88 |
89 | ///
90 | /// Creates a WzConvexProperty with the specified name
91 | ///
92 | /// The name of the property
93 | public WzConvexProperty(string pName) {
94 | mName = pName;
95 | }
96 |
97 | #endregion
98 | }
99 | }
--------------------------------------------------------------------------------
/WzLib/WzProperties/WzDoubleProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using System.IO;
3 |
4 | namespace MapleLib.WzLib.WzProperties {
5 | ///
6 | /// A property that has the value of a double
7 | ///
8 | public class WzDoubleProperty : AWzImageProperty {
9 | #region Fields
10 |
11 | internal string mName;
12 | internal double mVal;
13 | internal AWzObject mParent;
14 | internal WzImage mImgParent;
15 |
16 | #endregion
17 |
18 | #region Inherited Members
19 |
20 | public override object WzValue { get { return mVal; } set { mVal = (double)value; } }
21 |
22 | ///
23 | /// The parent of the object
24 | ///
25 | public override AWzObject Parent { get { return mParent; } internal set { mParent = value; } }
26 |
27 | ///
28 | /// The image that this property is contained in
29 | ///
30 | public override WzImage ParentImage { get { return mImgParent; } internal set { mImgParent = value; } }
31 |
32 | ///
33 | /// The WzPropertyType of the property
34 | ///
35 | public override WzPropertyType PropertyType { get { return WzPropertyType.Double; } }
36 |
37 | ///
38 | /// The name of this property
39 | ///
40 | public override string Name { get { return mName; } set { mName = value; } }
41 |
42 | public override void WriteValue(WzBinaryWriter pWriter) {
43 | pWriter.Write((byte)5);
44 | pWriter.Write(Value);
45 | }
46 |
47 | public override void ExportXml(StreamWriter pWriter, int pLevel) {
48 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.EmptyNamedValuePair("WzDouble", Name, Value.ToString()));
49 | }
50 |
51 | public override void Dispose() {
52 | mName = null;
53 | }
54 |
55 | #endregion
56 |
57 | #region Custom Members
58 |
59 | ///
60 | /// The value of this property
61 | ///
62 | public double Value { get { return mVal; } set { mVal = value; } }
63 |
64 | ///
65 | /// Creates a blank WzDoubleProperty
66 | ///
67 | public WzDoubleProperty() {
68 | }
69 |
70 | ///
71 | /// Creates a WzDoubleProperty with the specified name
72 | ///
73 | /// The name of the property
74 | public WzDoubleProperty(string pName) {
75 | mName = pName;
76 | }
77 |
78 | ///
79 | /// Creates a WzDoubleProperty with the specified name and value
80 | ///
81 | /// The name of the property
82 | /// The value of the property
83 | public WzDoubleProperty(string pName, double pValue) {
84 | mName = pName;
85 | mVal = pValue;
86 | }
87 |
88 | #endregion
89 |
90 | #region Cast Values
91 |
92 | internal override float ToFloat(float pDef = 0) {
93 | return (float)mVal;
94 | }
95 |
96 | internal override double ToDouble(double pDef = 0) {
97 | return mVal;
98 | }
99 |
100 | internal override int ToInt(int pDef = 0) {
101 | return (int)mVal;
102 | }
103 |
104 | internal override short ToShort(short pDef = 0) {
105 | return (short)mVal;
106 | }
107 |
108 | #endregion
109 | }
110 | }
--------------------------------------------------------------------------------
/WzLib/WzProperties/WzNullProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using System.IO;
3 |
4 | namespace MapleLib.WzLib.WzProperties {
5 | ///
6 | /// A property that's value is null
7 | ///
8 | public class WzNullProperty : AWzImageProperty {
9 | #region Fields
10 |
11 | internal string mName;
12 | internal AWzObject mParent;
13 | internal WzImage mImgParent;
14 |
15 | #endregion
16 |
17 | #region Inherited Members
18 |
19 | ///
20 | /// The parent of the object
21 | ///
22 | public override AWzObject Parent { get { return mParent; } internal set { mParent = value; } }
23 |
24 | ///
25 | /// The image that this property is contained in
26 | ///
27 | public override WzImage ParentImage { get { return mImgParent; } internal set { mImgParent = value; } }
28 |
29 | ///
30 | /// The WzPropertyType of the property
31 | ///
32 | public override WzPropertyType PropertyType { get { return WzPropertyType.Null; } }
33 |
34 | ///
35 | /// The name of the property
36 | ///
37 | ///
38 | public override string Name { get { return mName; } set { mName = value; } }
39 |
40 | ///
41 | /// The WzObjectType of the property
42 | ///
43 | public override WzObjectType ObjectType { get { return WzObjectType.Property; } }
44 |
45 | public override object WzValue { get { return null; } set { } }
46 |
47 | public override void WriteValue(WzBinaryWriter pWriter) {
48 | pWriter.Write((byte)0);
49 | }
50 |
51 | public override void ExportXml(StreamWriter pWriter, int pLevel) {
52 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.EmptyNamedTag("WzNull", Name));
53 | }
54 |
55 | ///
56 | /// Disposes the object
57 | ///
58 | public override void Dispose() {
59 | mName = null;
60 | }
61 |
62 | #endregion
63 |
64 | #region Custom Members
65 |
66 | ///
67 | /// Creates a blank WzNullProperty
68 | ///
69 | public WzNullProperty() {
70 | }
71 |
72 | ///
73 | /// Creates a WzNullProperty with the specified name
74 | ///
75 | /// The name of the property
76 | public WzNullProperty(string pPropName) {
77 | mName = pPropName;
78 | }
79 |
80 | #endregion
81 | }
82 | }
--------------------------------------------------------------------------------
/WzLib/WzProperties/WzRawDataProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using NAudio.Wave;
3 | using System;
4 | using System.IO;
5 | using System.Runtime.InteropServices;
6 | using System.Runtime.InteropServices.ComTypes;
7 | using System.Runtime.Remoting.Messaging;
8 | using System.Runtime.Serialization;
9 |
10 | namespace MapleLib.WzLib.WzProperties
11 | {
12 | ///
13 | /// A property that contains raw data
14 | ///
15 | public class WzRawDataProperty : AWzImageProperty, IExtended
16 | {
17 | #region Fields
18 |
19 | internal string mName;
20 | internal byte[] mBytes;
21 | internal AWzObject mParent;
22 | internal WzImage mImgParent;
23 | internal WzBinaryReader mWzReader;
24 | internal long mOffsets;
25 | #endregion
26 |
27 | #region Inherited Members
28 |
29 | public override object WzValue {
30 | get { return GetBytes(); }
31 | set {
32 | if (value is byte[] v)
33 | SetDataUnsafe(v);
34 | }
35 | }
36 |
37 | ///
38 | /// The parent of the object
39 | ///
40 | public override AWzObject Parent { get { return mParent; } internal set { mParent = value; } }
41 |
42 | ///
43 | /// The image that this property is contained in
44 | ///
45 | public override WzImage ParentImage { get { return mImgParent; } internal set { mImgParent = value; } }
46 |
47 | ///
48 | /// The name of the property
49 | ///
50 | public override string Name { get { return mName; } set { mName = value; } }
51 |
52 | ///
53 | /// The WzPropertyType of the property
54 | ///
55 | public override WzPropertyType PropertyType { get { return WzPropertyType.RawData; } }
56 |
57 | public override void WriteValue(WzBinaryWriter pWriter) {
58 | byte[] data = GetBytes();
59 | pWriter.WriteStringValue("RawData", 0x73, 0x1B);
60 | pWriter.Write((byte)0);
61 | pWriter.WriteCompressedInt(data.Length);
62 | pWriter.Write(data);
63 | }
64 |
65 | public override void ExportXml(StreamWriter pWriter, int pLevel) {
66 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.EmptyNamedTag("RawData", Name));
67 | }
68 |
69 | ///
70 | /// Disposes the object
71 | ///
72 | public override void Dispose() {
73 | mName = null;
74 | mBytes = null;
75 | }
76 |
77 | #endregion
78 |
79 | #region Custom Members
80 |
81 | ///
82 | /// Creates a blank WzRawDataProperty
83 | ///
84 | public WzRawDataProperty() {
85 | }
86 |
87 | ///
88 | /// Creates a WzRawDataProperty with the specified name
89 | ///
90 | /// The name of the property
91 | public WzRawDataProperty(string pName) {
92 | mName = pName;
93 | }
94 |
95 | public void SetDataUnsafe(byte[] data) {
96 | mBytes = data;
97 | }
98 |
99 | #endregion
100 |
101 | #region Parsing Methods
102 |
103 |
104 | internal void ParseRawData(WzBinaryReader pReader) {
105 | pReader.BaseStream.Position++;
106 | mOffsets = pReader.BaseStream.Position;
107 | int dataLen = pReader.ReadCompressedInt();
108 | pReader.BaseStream.Position += dataLen;
109 | mWzReader = pReader;
110 | }
111 |
112 | public byte[] GetBytes(bool pSaveInMemory = false) {
113 | if (mBytes != null)
114 | return mBytes;
115 | if (mWzReader == null)
116 | return null;
117 | long currentPos = mWzReader.BaseStream.Position;
118 | mWzReader.BaseStream.Position = mOffsets;
119 | int dataLen = mWzReader.ReadCompressedInt();
120 | mBytes = mWzReader.ReadBytes(dataLen);
121 | mWzReader.BaseStream.Position = currentPos;
122 | if (pSaveInMemory)
123 | return mBytes;
124 | byte[] result = mBytes;
125 | mBytes = null;
126 | return result;
127 | }
128 |
129 | public void SaveToFile(string pFilePath) {
130 | File.WriteAllBytes(pFilePath, GetBytes());
131 | }
132 |
133 | #endregion
134 |
135 | #region Cast Values
136 |
137 | internal override byte[] ToBytes(byte[] pDef = null) {
138 | return GetBytes();
139 | }
140 |
141 | #endregion
142 | }
143 | }
--------------------------------------------------------------------------------
/WzLib/WzProperties/WzShortProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using System.IO;
3 |
4 | namespace MapleLib.WzLib.WzProperties {
5 | ///
6 | /// A wz property which has a value which is a ushort
7 | ///
8 | public class WzShortProperty : AWzImageProperty {
9 | #region Fields
10 |
11 | internal string mName;
12 | internal short mVal;
13 | internal AWzObject mParent;
14 | internal WzImage mImgParent;
15 |
16 | #endregion
17 |
18 | #region Inherited Members
19 |
20 | public override object WzValue { get { return mVal; } set { mVal = (short)value; } }
21 |
22 | ///
23 | /// The parent of the object
24 | ///
25 | public override AWzObject Parent { get { return mParent; } internal set { mParent = value; } }
26 |
27 | ///
28 | /// The image that this property is contained in
29 | ///
30 | public override WzImage ParentImage { get { return mImgParent; } internal set { mImgParent = value; } }
31 |
32 | ///
33 | /// The WzPropertyType of the property
34 | ///
35 | public override WzPropertyType PropertyType { get { return WzPropertyType.Short; } }
36 |
37 | ///
38 | /// The name of the property
39 | ///
40 | public override string Name { get { return mName; } set { mName = value; } }
41 |
42 | public override void WriteValue(WzBinaryWriter pWriter) {
43 | pWriter.Write((byte)2);
44 | pWriter.Write(Value);
45 | }
46 |
47 | public override void ExportXml(StreamWriter pWriter, int pLevel) {
48 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.EmptyNamedValuePair("WzShort", Name, Value.ToString()));
49 | }
50 |
51 | ///
52 | /// Disposes the object
53 | ///
54 | public override void Dispose() {
55 | mName = null;
56 | }
57 |
58 | #endregion
59 |
60 | #region Custom Members
61 |
62 | ///
63 | /// The value of the property
64 | ///
65 | public short Value { get { return mVal; } set { mVal = value; } }
66 |
67 | ///
68 | /// Creates a blank WzShortProperty
69 | ///
70 | public WzShortProperty() {
71 | }
72 |
73 | ///
74 | /// Creates a WzShortProperty with the specified name
75 | ///
76 | /// The name of the property
77 | public WzShortProperty(string pName) {
78 | mName = pName;
79 | }
80 |
81 | ///
82 | /// Creates a WzShortProperty with the specified name and value
83 | ///
84 | /// The name of the property
85 | /// The value of the property
86 | public WzShortProperty(string pName, short pValue) {
87 | mName = pName;
88 | mVal = pValue;
89 | }
90 |
91 | #endregion
92 |
93 | #region Cast Values
94 |
95 | internal override float ToFloat(float pDef = 0) {
96 | return mVal;
97 | }
98 |
99 | internal override double ToDouble(double pDef = 0) {
100 | return mVal;
101 | }
102 |
103 | internal override int ToInt(int pDef = 0) {
104 | return mVal;
105 | }
106 |
107 | internal override short ToShort(short pDef = 0) {
108 | return mVal;
109 | }
110 |
111 | #endregion
112 | }
113 | }
--------------------------------------------------------------------------------
/WzLib/WzProperties/WzSoundProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using NAudio.Wave;
3 | using System;
4 | using System.IO;
5 | using System.Runtime.InteropServices;
6 | using System.Runtime.InteropServices.ComTypes;
7 | using System.Runtime.Remoting.Messaging;
8 | using System.Runtime.Serialization;
9 |
10 | namespace MapleLib.WzLib.WzProperties {
11 | ///
12 | /// A property that contains data for an MP3 file
13 | ///
14 | public class WzSoundProperty : AWzImageProperty, IExtended {
15 | #region Fields
16 |
17 | internal string mName;
18 | internal byte[] mMp3bytes;
19 | internal AWzObject mParent;
20 | internal int mLenMs;
21 | internal WzImage mImgParent;
22 | internal WzBinaryReader mWzReader;
23 | internal long mOffsets;
24 | internal WaveFormat wavFormat;
25 | internal int headerLen;
26 | public static readonly byte[] SoundHeader = new byte[] { 0x02,
27 | 0x83, 0xEB, 0x36, 0xE4, 0x4F, 0x52, 0xCE, 0x11, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70,
28 | 0x8B, 0xEB, 0x36, 0xE4, 0x4F, 0x52, 0xCE, 0x11, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70,
29 | 0x00,
30 | 0x01,
31 | 0x81, 0x9F, 0x58, 0x05, 0x56, 0xC3, 0xCE, 0x11, 0xBF, 0x01, 0x00, 0xAA, 0x00, 0x55, 0x59, 0x5A
32 | };
33 | #endregion
34 |
35 | #region Inherited Members
36 |
37 | public override object WzValue {
38 | get { return GetBytes(); }
39 | set {
40 | if (value is byte[] v)
41 | SetDataUnsafe(v);
42 | else
43 | SetDataUnsafe(CreateCustomProperty("temp", (string)value).GetBytes());
44 | }
45 | }
46 |
47 | ///
48 | /// The parent of the object
49 | ///
50 | public override AWzObject Parent { get { return mParent; } internal set { mParent = value; } }
51 |
52 | ///
53 | /// The image that this property is contained in
54 | ///
55 | public override WzImage ParentImage { get { return mImgParent; } internal set { mImgParent = value; } }
56 |
57 | ///
58 | /// The name of the property
59 | ///
60 | public override string Name { get { return mName; } set { mName = value; } }
61 |
62 | ///
63 | /// The WzPropertyType of the property
64 | ///
65 | public override WzPropertyType PropertyType { get { return WzPropertyType.Sound; } }
66 |
67 | public override void WriteValue(WzBinaryWriter pWriter) {
68 | byte[] data = GetBytes();
69 | pWriter.WriteStringValue("Sound_DX8", 0x73, 0x1B);
70 | pWriter.Write((byte)0);
71 | pWriter.WriteCompressedInt(data.Length);
72 | pWriter.WriteCompressedInt(0);
73 | pWriter.Write(data);
74 | }
75 |
76 | public override void ExportXml(StreamWriter pWriter, int pLevel) {
77 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.EmptyNamedTag("WzSound", Name));
78 | }
79 |
80 | ///
81 | /// Disposes the object
82 | ///
83 | public override void Dispose() {
84 | mName = null;
85 | mMp3bytes = null;
86 | wavFormat = null;
87 | }
88 |
89 | #endregion
90 |
91 | #region Custom Members
92 |
93 | ///
94 | /// Length of the mp3 file in milliseconds
95 | ///
96 | public int Length { get { return mLenMs; } }
97 |
98 | ///
99 | /// Creates a blank WzSoundProperty
100 | ///
101 | public WzSoundProperty() {
102 | }
103 |
104 | ///
105 | /// Creates a WzSoundProperty with the specified name
106 | ///
107 | /// The name of the property
108 | public WzSoundProperty(string pName) {
109 | mName = pName;
110 | }
111 |
112 | public void SetDataUnsafe(byte[] data) {
113 | mMp3bytes = data;
114 | }
115 |
116 | public static WzSoundProperty CreateCustomProperty(string name, string file) {
117 | WzSoundProperty newProp = new WzSoundProperty(name);
118 | MP3Header header = new MP3Header();
119 | header.ReadMP3Information(file);
120 | newProp.mLenMs = header.intLength * 1000;
121 | byte[] frequencyBytes = BitConverter.GetBytes(header.intFrequency);
122 | byte[] headerBytes = new byte[SoundHeader.Length];
123 | Array.Copy(SoundHeader, headerBytes, headerBytes.Length);
124 | for (int i = 0; i < 4; i++) {
125 | headerBytes[56 + i] = frequencyBytes[i];
126 | }
127 | newProp.mMp3bytes = WzTool.Combine(headerBytes, File.ReadAllBytes(file));
128 | return newProp;
129 | }
130 |
131 | #endregion
132 |
133 | #region Parsing Methods
134 |
135 | private static T BytesToStruct(byte[] data) where T : new() {
136 | GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
137 | try {
138 | return Marshal.PtrToStructure(handle.AddrOfPinnedObject());
139 | } finally {
140 | handle.Free();
141 | }
142 | }
143 |
144 | private static T BytesToStructConstructorless(byte[] data) {
145 | GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
146 | try {
147 | T obj = (T)FormatterServices.GetUninitializedObject(typeof(T));
148 | Marshal.PtrToStructure(handle.AddrOfPinnedObject(), obj);
149 | return obj;
150 | } finally {
151 | handle.Free();
152 | }
153 | }
154 |
155 | private void ParseWzSoundPropertyHeader(byte[] wavHeader) {
156 | if (wavHeader.Length < Marshal.SizeOf())
157 | return;
158 |
159 | WaveFormat wavFmt = BytesToStruct(wavHeader);
160 | // parse to mp3 header
161 | if (wavFmt.Encoding == WaveFormatEncoding.MpegLayer3 && wavHeader.Length >= Marshal.SizeOf()) {
162 | this.wavFormat = BytesToStructConstructorless(wavHeader);
163 | } else if (wavFmt.Encoding == WaveFormatEncoding.Pcm) {
164 | this.wavFormat = wavFmt;
165 | }
166 | }
167 |
168 | internal void ParseSound(WzBinaryReader pReader) {
169 | pReader.BaseStream.Position++;
170 | mOffsets = pReader.BaseStream.Position;
171 | int soundDataLen = pReader.ReadCompressedInt();
172 | mLenMs = pReader.ReadCompressedInt();
173 |
174 | pReader.BaseStream.Position += SoundHeader.Length; // Skip GUIDs
175 | int wavHeaderLen = pReader.ReadByte();
176 | headerLen = SoundHeader.Length + 1 + wavHeaderLen;
177 | byte[] wavHeader = pReader.ReadBytes(wavHeaderLen);
178 | ParseWzSoundPropertyHeader(wavHeader);
179 | // mMp3bytes = pReader.ReadBytes(soundDataLen); // Save memory
180 |
181 | pReader.BaseStream.Position += soundDataLen;
182 | mWzReader = pReader;
183 | }
184 |
185 | public byte[] GetBytes(bool pSaveInMemory = false) {
186 | if (mMp3bytes != null)
187 | return mMp3bytes;
188 | if (mWzReader == null)
189 | return null;
190 | long currentPos = mWzReader.BaseStream.Position;
191 | mWzReader.BaseStream.Position = mOffsets;
192 | int soundDataLen = mWzReader.ReadCompressedInt();
193 | mWzReader.ReadCompressedInt();
194 | mWzReader.BaseStream.Position += headerLen;
195 | if (wavFormat != null && wavFormat.Encoding == WaveFormatEncoding.Pcm && wavFormat.SampleRate != soundDataLen && wavFormat.SampleRate != wavFormat.AverageBytesPerSecond) {
196 | byte[] soundData = mWzReader.ReadBytes(soundDataLen);
197 | using (MemoryStream ms = new MemoryStream()) {
198 | using (WaveFileWriter writer = new WaveFileWriter(ms, wavFormat)) {
199 | writer.Write(soundData, 0, soundDataLen);
200 | }
201 | mMp3bytes = ms.ToArray();
202 | }
203 | } else {
204 | mMp3bytes = mWzReader.ReadBytes(soundDataLen);
205 | }
206 | mWzReader.BaseStream.Position = currentPos;
207 | if (pSaveInMemory)
208 | return mMp3bytes;
209 | byte[] result = mMp3bytes;
210 | mMp3bytes = null;
211 | return result;
212 | }
213 |
214 | public void SaveToFile(string pFilePath) {
215 | File.WriteAllBytes(pFilePath, GetBytes());
216 | }
217 | public string GetExtension() {
218 | if (wavFormat != null && wavFormat.Encoding == WaveFormatEncoding.MpegLayer3)
219 | return ".mp3";
220 | if (mName.Equals("FONT_DATA"))
221 | return ".ttf";
222 | if (wavFormat != null && wavFormat.Encoding == WaveFormatEncoding.Pcm && wavFormat.SampleRate != wavFormat.AverageBytesPerSecond)
223 | return ".wav";
224 | return "";
225 | }
226 |
227 | #endregion
228 |
229 | #region Cast Values
230 |
231 | internal override byte[] ToBytes(byte[] pDef = null) {
232 | return GetBytes();
233 | }
234 |
235 | #endregion
236 | }
237 | }
--------------------------------------------------------------------------------
/WzLib/WzProperties/WzStringProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using System.IO;
3 |
4 | namespace MapleLib.WzLib.WzProperties {
5 | ///
6 | /// A property with a string as a value
7 | ///
8 | public class WzStringProperty : AWzImageProperty {
9 | #region Fields
10 |
11 | internal string mName, mVal;
12 | internal AWzObject mParent;
13 | internal WzImage mImgParent;
14 |
15 | #endregion
16 |
17 | #region Inherited Members
18 |
19 | public override object WzValue { get { return mVal; } set { mVal = (string)value; } }
20 |
21 | ///
22 | /// The parent of the object
23 | ///
24 | public override AWzObject Parent { get { return mParent; } internal set { mParent = value; } }
25 |
26 | ///
27 | /// The image that this property is contained in
28 | ///
29 | public override WzImage ParentImage { get { return mImgParent; } internal set { mImgParent = value; } }
30 |
31 | ///
32 | /// The WzPropertyType of the property
33 | ///
34 | public override WzPropertyType PropertyType { get { return WzPropertyType.String; } }
35 |
36 | ///
37 | /// The name of the property
38 | ///
39 | public override string Name { get { return mName; } set { mName = value; } }
40 |
41 | public override void WriteValue(WzBinaryWriter pWriter) {
42 | pWriter.Write((byte)8);
43 | pWriter.WriteStringValue(Value, 0, 1);
44 | }
45 |
46 | public override void ExportXml(StreamWriter pWriter, int pLevel) {
47 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.EmptyNamedValuePair("WzString", Name, Value));
48 | }
49 |
50 | ///
51 | /// Disposes the object
52 | ///
53 | public override void Dispose() {
54 | mName = null;
55 | mVal = null;
56 | }
57 |
58 | #endregion
59 |
60 | #region Custom Members
61 |
62 | ///
63 | /// The value of the property
64 | ///
65 | public string Value { get { return mVal; } set { mVal = value; } }
66 |
67 | ///
68 | /// Creates a blank WzStringProperty
69 | ///
70 | public WzStringProperty() {
71 | }
72 |
73 | ///
74 | /// Creates a WzStringProperty with the specified name
75 | ///
76 | /// The name of the property
77 | public WzStringProperty(string pName) {
78 | mName = pName;
79 | }
80 |
81 | ///
82 | /// Creates a WzStringProperty with the specified name and value
83 | ///
84 | /// The name of the property
85 | /// The value of the property
86 | public WzStringProperty(string pName, string pValue) {
87 | mName = pName;
88 | mVal = pValue;
89 | }
90 |
91 | #endregion
92 |
93 | #region Cast Values
94 |
95 | internal override float ToFloat(float pDef = 0) {
96 | return float.Parse(mVal);
97 | }
98 |
99 | internal override double ToDouble(double pDef = 0) {
100 | return double.Parse(mVal);
101 | }
102 |
103 | internal override int ToInt(int pDef = 0) {
104 | return int.Parse(mVal);
105 | }
106 |
107 | internal override short ToShort(short pDef = 0) {
108 | return short.Parse(mVal);
109 | }
110 |
111 | public override string ToString() {
112 | return mVal;
113 | }
114 |
115 | #endregion
116 | }
117 | }
--------------------------------------------------------------------------------
/WzLib/WzProperties/WzSubProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using System.Collections.Generic;
3 | using System.IO;
4 |
5 | namespace MapleLib.WzLib.WzProperties {
6 | ///
7 | /// A property that contains a set of properties
8 | ///
9 | public class WzSubProperty : APropertyContainer, IExtended {
10 | #region Fields
11 |
12 | internal List mProperties = new List();
13 | internal string mName;
14 | internal AWzObject mParent;
15 | internal WzImage mImgParent;
16 |
17 | #endregion
18 |
19 | #region Inherited Members
20 |
21 | ///
22 | /// The parent of the object
23 | ///
24 | public override AWzObject Parent { get { return mParent; } internal set { mParent = value; } }
25 |
26 | ///
27 | /// The image that this property is contained in
28 | ///
29 | public override WzImage ParentImage { get { return mImgParent; } internal set { mImgParent = value; } }
30 |
31 | ///
32 | /// The WzPropertyType of the property
33 | ///
34 | public override WzPropertyType PropertyType { get { return WzPropertyType.SubProperty; } }
35 |
36 | ///
37 | /// The wz properties contained in the property
38 | ///
39 | public override List WzProperties { get { return mProperties; } }
40 |
41 | ///
42 | /// The name of the property
43 | ///
44 | public override string Name { get { return mName; } set { mName = value; } }
45 |
46 | public override object WzValue { get { return null; } set { } }
47 |
48 | public override void WriteValue(WzBinaryWriter pWriter) {
49 | pWriter.WriteStringValue("Property", 0x73, 0x1B);
50 | WritePropertyList(pWriter, mProperties);
51 | }
52 |
53 | public override void ExportXml(StreamWriter pWriter, int pLevel) {
54 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.OpenNamedTag("WzSub", Name, true));
55 | DumpPropertyList(pWriter, pLevel, WzProperties);
56 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.CloseTag("WzSub"));
57 | }
58 |
59 | ///
60 | /// Disposes the object
61 | ///
62 | public override void Dispose() {
63 | mName = null;
64 | foreach (AWzImageProperty prop in mProperties)
65 | prop.Dispose();
66 | mProperties.Clear();
67 | mProperties = null;
68 | }
69 |
70 | #endregion
71 |
72 | #region Custom Members
73 |
74 | ///
75 | /// Creates a blank WzSubProperty
76 | ///
77 | public WzSubProperty() {
78 | }
79 |
80 | ///
81 | /// Creates a WzSubProperty with the specified name
82 | ///
83 | /// The name of the property
84 | public WzSubProperty(string pName) {
85 | mName = pName;
86 | }
87 |
88 | #endregion
89 | }
90 | }
--------------------------------------------------------------------------------
/WzLib/WzProperties/WzUOLProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.IO;
5 |
6 | namespace MapleLib.WzLib.WzProperties {
7 | ///
8 | /// A property that's value is a string
9 | ///
10 | public class WzUOLProperty : AWzImageProperty, IExtended {
11 | #region Fields
12 |
13 | internal string mName, mVal;
14 | internal AWzObject mParent;
15 | internal WzImage mImgParent;
16 | internal AWzImageProperty mLinkVal;
17 |
18 | #endregion
19 |
20 | #region Inherited Members
21 |
22 | public override object WzValue { get { return mVal; } set { mVal = (string)value; } }
23 |
24 | ///
25 | /// The parent of the object
26 | ///
27 | public override AWzObject Parent { get { return mParent; } internal set { mParent = value; } }
28 |
29 | ///
30 | /// The image that this property is contained in
31 | ///
32 | public override WzImage ParentImage { get { return mImgParent; } internal set { mImgParent = value; } }
33 |
34 | ///
35 | /// The name of the property
36 | ///
37 | public override string Name { get { return mName; } set { mName = value; } }
38 |
39 | public override List WzProperties { get { return LinkValue != null ? LinkValue.WzProperties : null; } }
40 |
41 | public override AWzImageProperty this[string pName] { get { return LinkValue[pName]; } }
42 |
43 | public override AWzImageProperty GetFromPath(string pPath) {
44 | return LinkValue.GetFromPath(pPath);
45 | }
46 |
47 | ///
48 | /// The WzPropertyType of the property
49 | ///
50 | public override WzPropertyType PropertyType { get { return WzPropertyType.UOL; } }
51 |
52 | public override void WriteValue(WzBinaryWriter pWriter) {
53 | pWriter.WriteStringValue("UOL", 0x73, 0x1B);
54 | pWriter.Write((byte)0);
55 | pWriter.WriteStringValue(Value, 0, 1);
56 | }
57 |
58 | public override void ExportXml(StreamWriter pWriter, int pLevel) {
59 | pWriter.WriteLine(XmlUtil.Indentation(pLevel) + XmlUtil.EmptyNamedValuePair("WzUOL", Name, Value));
60 | }
61 |
62 | ///
63 | /// Disposes the object
64 | ///
65 | public override void Dispose() {
66 | mName = null;
67 | mVal = null;
68 | mLinkVal = null;
69 | }
70 |
71 | #endregion
72 |
73 | #region Custom Members
74 |
75 | ///
76 | /// The value of the property
77 | ///
78 | public string Value { get { return mVal; } set { mVal = value; } }
79 |
80 | public AWzImageProperty LinkValue {
81 | get {
82 | if (mLinkVal == null) {
83 | AWzObject curObj = Parent;
84 | string[] seperatedPath = mVal.Split('/');
85 | foreach (string t in seperatedPath) {
86 | if (curObj == null)
87 | return null;
88 | string trimmedName = t.Trim();
89 | if (trimmedName.Equals("..")) {
90 | curObj = curObj.Parent;
91 | continue;
92 | }
93 | switch (curObj.ObjectType) {
94 | case WzObjectType.File:
95 | case WzObjectType.Directory:
96 | curObj = ((WzDirectory)curObj)[trimmedName];
97 | continue;
98 | case WzObjectType.Image:
99 | curObj = ((WzImage)curObj)[trimmedName];
100 | continue;
101 | case WzObjectType.Property:
102 | switch (((AWzImageProperty)curObj).PropertyType) {
103 | case WzPropertyType.Canvas:
104 | curObj = ((WzCanvasProperty)curObj)[trimmedName];
105 | continue;
106 | case WzPropertyType.Convex:
107 | curObj = ((WzConvexProperty)curObj)[trimmedName];
108 | continue;
109 | case WzPropertyType.SubProperty:
110 | curObj = ((WzSubProperty)curObj)[trimmedName];
111 | continue;
112 | case WzPropertyType.Vector:
113 | if (trimmedName == "X")
114 | return ((WzVectorProperty)curObj).X;
115 | return trimmedName == "Y" ? ((WzVectorProperty)curObj).Y : null;
116 | default:
117 | return null;
118 | }
119 | default:
120 | return null;
121 | }
122 | }
123 | mLinkVal = (AWzImageProperty)curObj;
124 | if (mLinkVal != null)
125 | mImgParent.AddReferencedImage(mLinkVal.ParentImage);
126 | }
127 | return mLinkVal;
128 | }
129 | }
130 |
131 | ///
132 | /// Creates a blank WzUOLProperty
133 | ///
134 | public WzUOLProperty() {
135 | }
136 |
137 | ///
138 | /// Creates a WzUOLProperty with the specified name
139 | ///
140 | /// The name of the property
141 | public WzUOLProperty(string pName) {
142 | mName = pName;
143 | }
144 |
145 | ///
146 | /// Creates a WzUOLProperty with the specified name and value
147 | ///
148 | /// The name of the property
149 | /// The value of the property
150 | public WzUOLProperty(string pName, string pValue) {
151 | mName = pName;
152 | mVal = pValue;
153 | }
154 |
155 | #endregion
156 |
157 | #region Cast Values
158 |
159 | internal override Bitmap ToBitmap(Bitmap pDef = null) {
160 | return LinkValue.ToBitmap(pDef);
161 | }
162 |
163 |
164 | internal override byte[] ToBytes(byte[] pDef = null) {
165 | return LinkValue.ToBytes(pDef);
166 | }
167 |
168 | internal override double ToDouble(double pDef = 0) {
169 | return LinkValue.ToDouble(pDef);
170 | }
171 |
172 | internal override float ToFloat(float pDef = 0) {
173 | return LinkValue.ToFloat(pDef);
174 | }
175 |
176 | internal override int ToInt(int pDef = 0) {
177 | return LinkValue.ToInt(pDef);
178 | }
179 |
180 | internal override WzPngProperty ToPngProperty(WzPngProperty pDef = null) {
181 | return LinkValue.ToPngProperty(pDef);
182 | }
183 |
184 | internal override Point ToPoint(int pXDef = 0, int pYDef = 0) {
185 | return LinkValue.ToPoint(pXDef, pYDef);
186 | }
187 |
188 | public override string ToString() {
189 | return LinkValue.ToString();
190 | }
191 |
192 | internal override short ToShort(short pDef = 0) {
193 | return LinkValue.ToShort(pDef);
194 | }
195 |
196 | #endregion
197 | }
198 | }
--------------------------------------------------------------------------------
/WzLib/WzProperties/WzVectorProperty.cs:
--------------------------------------------------------------------------------
1 | using MapleLib.WzLib.Util;
2 | using System.Drawing;
3 | using System.IO;
4 |
5 | namespace MapleLib.WzLib.WzProperties {
6 | ///
7 | /// A property that contains an x and a y value
8 | ///
9 | public class WzVectorProperty : AWzImageProperty, IExtended {
10 | #region Fields
11 |
12 | internal string name;
13 | internal WzCompressedIntProperty x, y;
14 | internal AWzObject parent;
15 | internal WzImage imgParent;
16 |
17 | #endregion
18 |
19 | #region Inherited Members
20 |
21 | public override object WzValue {
22 | get { return new Point(x.Value, y.Value); }
23 | set {
24 | if (value is Point) {
25 | x.mVal = ((Point)value).X;
26 | y.mVal = ((Point)value).Y;
27 | } else {
28 | x.mVal = ((Size)value).Width;
29 | y.mVal = ((Size)value).Height;
30 | }
31 | }
32 | }
33 |
34 | ///
35 | /// The parent of the object
36 | ///
37 | public override AWzObject Parent { get { return parent; } internal set { parent = value; } }
38 |
39 | ///
40 | /// The image that this property is contained in
41 | ///
42 | public override WzImage ParentImage { get { return imgParent; } internal set { imgParent = value; } }
43 |
44 | ///
45 | /// The name of the property
46 | ///
47 | public override string Name { get { return name; } set { name = value; } }
48 |
49 | ///
50 | /// The WzPropertyType of the property
51 | ///
52 | public override WzPropertyType PropertyType { get { return WzPropertyType.Vector; } }
53 |
54 | public override void WriteValue(WzBinaryWriter writer) {
55 | writer.WriteStringValue("Shape2D#Vector2D", 0x73, 0x1B);
56 | writer.WriteCompressedInt(X.Value);
57 | writer.WriteCompressedInt(Y.Value);
58 | }
59 |
60 | public override void ExportXml(StreamWriter writer, int level) {
61 | writer.WriteLine(XmlUtil.Indentation(level) + XmlUtil.OpenNamedTag("WzVector", Name, false, false) + XmlUtil.Attrib("X", X.Value.ToString()) + XmlUtil.Attrib("Y", Y.Value.ToString(), true, true));
62 | }
63 |
64 | ///
65 | /// Disposes the object
66 | ///
67 | public override void Dispose() {
68 | name = null;
69 | x.Dispose();
70 | x = null;
71 | y.Dispose();
72 | y = null;
73 | }
74 |
75 | #endregion
76 |
77 | #region Custom Members
78 |
79 | ///
80 | /// The X value of the Vector2D
81 | ///
82 | public WzCompressedIntProperty X { get { return x; } set { x = value; } }
83 |
84 | ///
85 | /// The Y value of the Vector2D
86 | ///
87 | public WzCompressedIntProperty Y { get { return y; } set { y = value; } }
88 |
89 | ///
90 | /// The Point of the Vector2D created from the X and Y
91 | ///
92 | public Point Pos { get { return new Point(X.Value, Y.Value); } }
93 |
94 | ///
95 | /// Creates a blank WzVectorProperty
96 | ///
97 | public WzVectorProperty() {
98 | }
99 |
100 | ///
101 | /// Creates a WzVectorProperty with the specified name
102 | ///
103 | /// The name of the property
104 | public WzVectorProperty(string name) {
105 | this.name = name;
106 | }
107 |
108 | ///
109 | /// Creates a WzVectorProperty with the specified name, x and y
110 | ///
111 | /// The name of the property
112 | /// The x value of the vector
113 | /// The y value of the vector
114 | public WzVectorProperty(string name, WzCompressedIntProperty x, WzCompressedIntProperty y) {
115 | this.name = name;
116 | this.x = x;
117 | this.y = y;
118 | }
119 |
120 | #endregion
121 |
122 | #region Cast Values
123 |
124 | internal override Point ToPoint(int pXDef = 0, int pYDef = 0) {
125 | return new Point(x.mVal, y.mVal);
126 | }
127 |
128 | public override string ToString() {
129 | return "X: " + x.mVal + ", Y: " + y.mVal;
130 | }
131 |
132 | #endregion
133 | }
134 | }
--------------------------------------------------------------------------------
/WzLib/WzPropertyType.cs:
--------------------------------------------------------------------------------
1 | namespace MapleLib.WzLib {
2 | public enum WzPropertyType {
3 | #region Regular
4 |
5 | Null = 0x1,
6 | Short = 0x2,
7 | CompressedInt = 0x4,
8 | ByteFloat = 0x8,
9 | Double = 0x10,
10 | String = 0x20,
11 | CompressedLong = 0x40,
12 |
13 | #endregion
14 |
15 | #region Extended
16 |
17 | SubProperty = 0x80,
18 | Canvas = 0x100,
19 | Vector = 0x200,
20 | Convex = 0x400,
21 | Sound = 0x800,
22 | UOL = 0x1000,
23 | RawData = 0x2000,
24 |
25 | #endregion
26 |
27 | #region Png
28 |
29 | PNG = 0x4000,
30 |
31 | #endregion
32 | }
33 | }
--------------------------------------------------------------------------------
/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------