├── .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 ""; 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 | --------------------------------------------------------------------------------