├── .gitattributes ├── .gitignore ├── LICENSE.md ├── README.md ├── RELEASENOTES.md ├── metadiff.sln ├── metadiff ├── meta_reader.cpp ├── meta_reader.h ├── meta_sig_parser.cpp ├── meta_sig_parser.h ├── meta_utils.cpp ├── meta_utils.h ├── metadiff.cpp ├── metadiff.vcxproj ├── metadiff.vcxproj.filters ├── stdafx.cpp ├── stdafx.h └── targetver.h └── witutils ├── cmdl_utils.cpp ├── cmdl_utils.h ├── diff_commons.cpp ├── diff_commons.h ├── diff_utils.h ├── file_utils.h ├── find_files.cpp ├── find_files.h ├── find_files_wcs.cpp ├── find_files_wcs.h ├── stdafx.cpp ├── stdafx.h ├── str_utils.h ├── targetver.h └── witutils.vcxproj /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 WalkingCat 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MetaDiff 2 | 3 | This is a diff tool to compare the CLI metadata in .NET assemblies or WinMD files. 4 | 5 | # Similiar tools 6 | 7 | LibCheck (Free) [https://www.microsoft.com/en-us/download/details.aspx?id=11287](https://www.microsoft.com/en-us/download/details.aspx?id=11287) 8 | Framework Design Studio (Free) [http://archive.msdn.microsoft.com/fds](http://archive.msdn.microsoft.com/fds) 9 | BitDiffer (Commercial) [http://www.bitwidgets.com/](http://www.bitwidgets.com/) 10 | NDepend (Commercial) [http://www.ndepend.com/](http://www.ndepend.com/) 11 | -------------------------------------------------------------------------------- /RELEASENOTES.md: -------------------------------------------------------------------------------- 1 | # 0.2 (2018.05.03) 2 | * Fixed an issue with incorrectly emitted parameters (#1) 3 | 4 | # 0.1 (2013.05.18) 5 | * Comparing: types, fields, properties, events, methods 6 | * NOT SUPPORTED YET: attributes, virtual methods, nested types, interfaces implemented by types 7 | -------------------------------------------------------------------------------- /metadiff.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "metadiff", "metadiff\metadiff.vcxproj", "{8930C3AB-40DD-4B3A-9F93-30A3032958FB}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "witutils", "witutils\witutils.vcxproj", "{6C290411-0E80-4EC5-BFAB-2A9206489474}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {8930C3AB-40DD-4B3A-9F93-30A3032958FB}.Debug|x64.ActiveCfg = Debug|x64 19 | {8930C3AB-40DD-4B3A-9F93-30A3032958FB}.Debug|x64.Build.0 = Debug|x64 20 | {8930C3AB-40DD-4B3A-9F93-30A3032958FB}.Debug|x86.ActiveCfg = Debug|Win32 21 | {8930C3AB-40DD-4B3A-9F93-30A3032958FB}.Debug|x86.Build.0 = Debug|Win32 22 | {8930C3AB-40DD-4B3A-9F93-30A3032958FB}.Release|x64.ActiveCfg = Release|x64 23 | {8930C3AB-40DD-4B3A-9F93-30A3032958FB}.Release|x64.Build.0 = Release|x64 24 | {8930C3AB-40DD-4B3A-9F93-30A3032958FB}.Release|x86.ActiveCfg = Release|Win32 25 | {8930C3AB-40DD-4B3A-9F93-30A3032958FB}.Release|x86.Build.0 = Release|Win32 26 | {6C290411-0E80-4EC5-BFAB-2A9206489474}.Debug|x64.ActiveCfg = Debug|x64 27 | {6C290411-0E80-4EC5-BFAB-2A9206489474}.Debug|x64.Build.0 = Debug|x64 28 | {6C290411-0E80-4EC5-BFAB-2A9206489474}.Debug|x86.ActiveCfg = Debug|Win32 29 | {6C290411-0E80-4EC5-BFAB-2A9206489474}.Debug|x86.Build.0 = Debug|Win32 30 | {6C290411-0E80-4EC5-BFAB-2A9206489474}.Release|x64.ActiveCfg = Release|x64 31 | {6C290411-0E80-4EC5-BFAB-2A9206489474}.Release|x64.Build.0 = Release|x64 32 | {6C290411-0E80-4EC5-BFAB-2A9206489474}.Release|x86.ActiveCfg = Release|Win32 33 | {6C290411-0E80-4EC5-BFAB-2A9206489474}.Release|x86.Build.0 = Release|Win32 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {463D6B08-A28A-4DC2-87EB-01C223729986} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /metadiff/meta_reader.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "meta_reader.h" 3 | #include "meta_sig_parser.h" 4 | #include "meta_utils.h" 5 | 6 | using namespace std; 7 | using namespace meta_utils; 8 | 9 | #pragma comment(lib, "mscoree.lib") 10 | 11 | meta_reader::meta_reader() {} 12 | 13 | // bool metadata_reader::init_alt(const wchar_t* filename) 14 | // { 15 | // // data from this dispenser has very occasional and subtle differences than MetaDataGetDispenser dispenser, wtf ? 16 | // 17 | // CComPtr meta_host; 18 | // CComPtr runtime_info; 19 | // CComPtr dispenser; 20 | // 21 | // HRESULT hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (void **)&meta_host); 22 | // if (SUCCEEDED(hr)) hr = meta_host->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (void **)&runtime_info); 23 | // if (SUCCEEDED(hr)) hr = runtime_info->GetInterface(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenserEx, (void **)&dispenser); 24 | // if (SUCCEEDED(hr)) hr = dispenser->OpenScope(filename, ofReadOnly, IID_IMetaDataImport2, (IUnknown**)&metadata_import); 25 | // return SUCCEEDED(hr) && metadata_import; 26 | // } 27 | 28 | bool meta_reader::init(const wchar_t* filename) 29 | { 30 | typedef HRESULT(__stdcall *MetaDataGetDispenser_func)(__in REFCLSID rclsid, __in REFIID riid, __deref_out LPVOID FAR * ppv); 31 | static const MetaDataGetDispenser_func _MetaDataGetDispenser = [] { 32 | MetaDataGetDispenser_func ret = nullptr; 33 | 34 | auto rometadata = LoadLibraryW(L"RoMetadata.dll"); 35 | if (rometadata != nullptr) ret = (MetaDataGetDispenser_func)GetProcAddress(rometadata, "MetaDataGetDispenser"); 36 | 37 | if (ret == nullptr) { 38 | auto mscoree = LoadLibraryW(L"MSCOREE.dll"); 39 | if (mscoree != NULL) ret = (MetaDataGetDispenser_func)GetProcAddress(mscoree, "MetaDataGetDispenser"); 40 | } 41 | 42 | return ret; 43 | }(); 44 | 45 | if (_MetaDataGetDispenser != nullptr) { 46 | _com_ptr_t<_com_IIID> dispenser; 47 | HRESULT hr = _MetaDataGetDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenserEx, (void **)&dispenser); 48 | if (SUCCEEDED(hr)) hr = dispenser->OpenScope(filename, ofReadOnly, IID_IMetaDataImport2, (IUnknown**)&metadata_import); 49 | return SUCCEEDED(hr) && metadata_import; 50 | } 51 | 52 | return false; 53 | } 54 | 55 | std::wstring meta_reader::get_name(mdToken token) 56 | { 57 | if (metadata_import) { 58 | MDUTF8CSTR name = nullptr; 59 | if (SUCCEEDED(metadata_import->GetNameFromToken(token, &name))) { 60 | return Utf8ToUtf16(name); 61 | } 62 | } 63 | 64 | // fall back 65 | std::wstringstream s; 66 | 67 | switch (TypeFromToken(token)) { 68 | case mdtModule: s << L"module"; break; 69 | case mdtTypeRef: s << L"type_ref"; break; 70 | case mdtTypeDef: s << L"type"; break; 71 | case mdtFieldDef: s << L"field"; break; 72 | case mdtMethodDef: s << L"method"; break; 73 | case mdtParamDef: s << L"param"; break; 74 | case mdtInterfaceImpl: s << L"if_impl"; break; 75 | case mdtMemberRef: s << L"member_ref"; break; 76 | case mdtModuleRef: s << L"module_ref"; break; 77 | case mdtTypeSpec: s << L"type_spec"; break; 78 | case mdtGenericParam: s << L"gen_param"; break; 79 | case mdtMethodSpec: s << L"method_spec"; break; 80 | default: s << std::uppercase << std::hex << std::setfill(L'0') << std::setw(2) << (TypeFromToken(token) >> 24); 81 | break; 82 | } 83 | s << L"_" << std::uppercase << std::hex << std::setfill(L'0') << std::setw(6) << RidFromToken(token); 84 | return s.str(); 85 | 86 | } 87 | 88 | std::wstring meta_reader::get_full_name(mdToken token) 89 | { 90 | if (metadata_import) { 91 | if (TypeFromToken(token) == mdtTypeDef) { 92 | wchar_t name[1024] = {}; 93 | if (SUCCEEDED(metadata_import->GetTypeDefProps(token, name, _countof(name), NULL, NULL, NULL))) { 94 | return std::wstring(name); 95 | } 96 | } 97 | 98 | if (TypeFromToken(token) == mdtTypeRef) { 99 | wchar_t name[1024] = {}; 100 | if (SUCCEEDED(metadata_import->GetTypeRefProps(token, NULL, name, _countof(name), NULL))) { 101 | return std::wstring(name); 102 | } 103 | } 104 | 105 | if (TypeFromToken(token) == mdtGenericParam) { 106 | wchar_t name[1024] = {}; 107 | if (SUCCEEDED(metadata_import->GetGenericParamProps(token, NULL, NULL, NULL, NULL, name, _countof(name), NULL))) { 108 | return std::wstring(name); 109 | } 110 | } 111 | } 112 | 113 | // fall back 114 | return get_name(token); 115 | } 116 | 117 | std::vector meta_reader::enum_tokens(std::function enum_func) const 118 | { 119 | std::vector ret; 120 | 121 | if (metadata_import) { 122 | HCORENUM e = NULL; mdTypeDef tokens[256] = {}; ULONG count = 0; 123 | while (enum_func(&e, tokens, _countof(tokens), &count) == S_OK) { 124 | for (ULONG i = 0; i < count; i++) { 125 | ret.push_back(tokens[i]); 126 | } 127 | } 128 | metadata_import->CloseEnum(e); 129 | } 130 | 131 | return ret; 132 | } 133 | 134 | std::vector meta_reader::enum_types() const 135 | { 136 | return enum_tokens([&](HCORENUM* en, mdToken* tokens, ULONG max, ULONG* count) { return metadata_import->EnumTypeDefs(en, tokens, max, count); }); 137 | } 138 | 139 | std::vector meta_reader::enum_fields(mdTypeDef type) 140 | { 141 | return enum_tokens([&](HCORENUM* en, mdToken* tokens, ULONG max, ULONG* count) { return metadata_import->EnumFields(en, type, tokens, max, count); }); 142 | } 143 | 144 | std::vector meta_reader::enum_properties(mdTypeDef type) 145 | { 146 | return enum_tokens([&](HCORENUM* en, mdToken* tokens, ULONG max, ULONG* count) { return metadata_import->EnumProperties(en, type, tokens, max, count); }); 147 | } 148 | 149 | std::vector meta_reader::enum_methods(mdTypeDef type) 150 | { 151 | return enum_tokens([&](HCORENUM* en, mdToken* tokens, ULONG max, ULONG* count) { return metadata_import->EnumMethods(en, type, tokens, max, count); }); 152 | } 153 | 154 | std::vector meta_reader::enum_events(mdTypeDef type) 155 | { 156 | return enum_tokens([&](HCORENUM* en, mdToken* tokens, ULONG max, ULONG* count) { return metadata_import->EnumEvents(en, type, tokens, max, count); }); 157 | } 158 | 159 | std::vector meta_reader::enum_generic_params(mdToken type_or_method) 160 | { 161 | return enum_tokens([&](HCORENUM* en, mdToken* tokens, ULONG max, ULONG* count) { return metadata_import->EnumGenericParams(en, type_or_method, tokens, max, count); }); 162 | } 163 | 164 | std::vector meta_reader::enum_params(mdMethodDef method) 165 | { 166 | return enum_tokens([&](HCORENUM* en, mdToken* tokens, ULONG max, ULONG* count) { return metadata_import->EnumParams(en, method, tokens, max, count); }); 167 | } 168 | 169 | std::shared_ptr meta_type_reader::get_field(mdFieldDef token) 170 | { 171 | auto ret = make_shared(); 172 | ret->token = token; 173 | 174 | wchar_t name[1024] = {}; PCCOR_SIGNATURE sig = nullptr; ULONG sig_size = 0; 175 | DWORD value_type = 0; UVCP_CONSTANT value = nullptr; ULONG value_length = 0; 176 | if (SUCCEEDED(metadata_import->GetFieldProps(token, nullptr, name, _countof(name), nullptr, &ret->attributes, &sig, &sig_size, &value_type, &value, &value_length))) { 177 | ret->name = name; 178 | 179 | if ((result->semantics == meta_type::type_semantics::enum_type) && IsFdStatic(ret->attributes) && IsFdPublic(ret->attributes)) { 180 | ret->visibility = meta_visibility::vis_public; 181 | ret->display_name = ret->name; 182 | } else { 183 | if ((result->semantics == meta_type::type_semantics::enum_type) && IsFdSpecialName(ret->attributes) && !wcscmp(name, COR_ENUM_FIELD_NAME_W)) { 184 | ret->set_special(meta_field::field_specials::enum_value); 185 | } 186 | 187 | ret->display_name = meta_sig_parser(metadata_import, sig, sig_size).parse_field(name); 188 | if (IsFdInitOnly(ret->attributes)) ret->display_name = L"readonly " + ret->display_name; 189 | if (IsFdStatic(ret->attributes)) ret->display_name = L"static " + ret->display_name; 190 | 191 | if (IsFdPrivate(ret->attributes)) ret->visibility = meta_visibility::vis_private; 192 | else if (IsFdFamily(ret->attributes)) ret->visibility = meta_visibility::vis_protected; 193 | else if (IsFdFamORAssem(ret->attributes)) ret->visibility = meta_visibility::vis_protected_internal; 194 | else if (IsFdPublic(ret->attributes)) ret->visibility = meta_visibility::vis_public; 195 | 196 | wstring access_modifier = format_visibility(ret->visibility); 197 | if (!access_modifier.empty()) ret->display_name = access_modifier + L" " + ret->display_name; 198 | } 199 | 200 | if (value != nullptr) { 201 | switch (value_type) { 202 | case ELEMENT_TYPE_I1: ret->display_name += L" = " + format_integer(*(char*)value); break; 203 | case ELEMENT_TYPE_I2: ret->display_name += L" = " + format_integer(*(short*)value); break; 204 | case ELEMENT_TYPE_I4: ret->display_name += L" = " + format_integer(*(int*)value); break; 205 | case ELEMENT_TYPE_I8: ret->display_name += L" = " + format_integer(*(__int64*)value); break; 206 | case ELEMENT_TYPE_U1: ret->display_name += L" = " + format_integer(*(unsigned char*)value); break; 207 | case ELEMENT_TYPE_U2: ret->display_name += L" = " + format_integer(*(unsigned short*)value); break; 208 | case ELEMENT_TYPE_U4: ret->display_name += L" = " + format_integer(*(unsigned int*)value); break; 209 | case ELEMENT_TYPE_U8: ret->display_name += L" = " + format_integer(*(unsigned __int64*)value); break; 210 | default: 211 | break; 212 | } 213 | } 214 | } 215 | return ret; 216 | } 217 | 218 | std::shared_ptr meta_type_reader::get_property(mdProperty token) 219 | { 220 | auto ret = make_shared(); 221 | ret->token = token; 222 | 223 | wchar_t name[1024] = {}; PCCOR_SIGNATURE sig = nullptr; ULONG sig_size = 0; 224 | mdMethodDef getter = mdMethodDefNil, setter = mdMethodDefNil; 225 | if (SUCCEEDED(metadata_import->GetPropertyProps(token, nullptr, name, _countof(name), nullptr, &ret->attributes, &sig, &sig_size, nullptr, nullptr, nullptr, &setter, &getter, nullptr, 0, nullptr))) { 226 | ret->name = name; 227 | 228 | ret->display_name = meta_sig_parser(metadata_import, sig, sig_size, type_generic_param_names).parse_property(name); 229 | 230 | meta_visibility setter_visibility = meta_visibility::vis_unknown, getter_visibility = meta_visibility::vis_unknown; 231 | for (const auto& m : result->methods) { 232 | if (m->token == getter) { 233 | m->set_semantic(meta_method::method_semantics::property_get); 234 | getter_visibility = m->visibility; 235 | } 236 | if (m->token == setter) { 237 | m->set_semantic(meta_method::method_semantics::property_set); 238 | setter_visibility = m->visibility; 239 | } 240 | } 241 | 242 | meta_visibility property_visibiliy = static_cast(max(static_cast(getter_visibility), static_cast(setter_visibility))); 243 | ret->visibility = property_visibiliy; 244 | 245 | ret->display_name += L" {"; 246 | if (!IsNilToken(getter)) { 247 | if (property_visibiliy != getter_visibility) { 248 | auto access_modifier = format_visibility(getter_visibility); 249 | if (!access_modifier.empty()) ret->display_name += L" " + access_modifier; 250 | } 251 | ret->display_name += L" get;"; 252 | } 253 | if (!IsNilToken(setter)) { 254 | if (property_visibiliy != setter_visibility) { 255 | auto access_modifier = format_visibility(setter_visibility); 256 | if (!access_modifier.empty()) ret->display_name += L" " + access_modifier; 257 | } 258 | ret->display_name += L" set;"; 259 | } 260 | ret->display_name += L" }"; 261 | 262 | auto access_modifier = format_visibility(getter_visibility); 263 | if (!access_modifier.empty()) ret->display_name = access_modifier + L" " + ret->display_name; 264 | } 265 | 266 | return ret; 267 | 268 | } 269 | 270 | std::shared_ptr meta_type_reader::get_method(mdMethodDef token) 271 | { 272 | auto ret = make_shared(); 273 | ret->token = token; 274 | 275 | wchar_t name[1024] = {}; PCCOR_SIGNATURE sig = nullptr; ULONG sig_size = 0; 276 | if (SUCCEEDED(metadata_import->GetMethodProps(token, nullptr, name, _countof(name), nullptr, &ret->attributes, &sig, &sig_size, nullptr, nullptr))) { 277 | ret->name = name; 278 | 279 | std::vector param_names; 280 | auto params = enum_params(token); 281 | for (const auto& p : params) param_names.push_back(get_name(p)); 282 | 283 | auto generic_params = enum_generic_params(token); 284 | if (!generic_params.empty()) { 285 | ret->display_name += L"<"; 286 | bool first = true; 287 | for (const auto& gp : generic_params) { 288 | if (!first) ret->display_name += L", "; else first = false; 289 | ret->display_name += get_full_name(gp); 290 | } 291 | ret->display_name += L">"; 292 | } 293 | 294 | bool is_ctor_cctor = false; 295 | if (IsMdInstanceInitializerW(ret->attributes, name)) { 296 | ret->set_special(meta_method::method_specials::constructor); 297 | is_ctor_cctor = true; 298 | } else if (IsMdClassConstructorW(ret->attributes, name)) { 299 | ret->set_special(meta_method::method_specials::class_constructor); 300 | is_ctor_cctor = true; 301 | } 302 | 303 | if (is_ctor_cctor) { 304 | ret->display_name = meta_sig_parser(metadata_import, sig, sig_size, type_generic_param_names).parse_method(get_name(result->token).c_str(), param_names, true); 305 | } else { 306 | ret->display_name = meta_sig_parser(metadata_import, sig, sig_size, type_generic_param_names).parse_method(name, param_names); 307 | } 308 | 309 | if (IsMdStatic(ret->attributes)) ret->display_name = L"static " + ret->display_name; 310 | 311 | if (IsMdPrivate(ret->attributes)) ret->visibility = meta_visibility::vis_private; 312 | else if (IsMdFamily(ret->attributes)) ret->visibility = meta_visibility::vis_protected; 313 | else if (IsMdFamORAssem(ret->attributes)) ret->visibility = meta_visibility::vis_protected_internal; 314 | else if (IsMdPublic(ret->attributes)) ret->visibility = meta_visibility::vis_public; 315 | 316 | if (IsMdPinvokeImpl(ret->attributes)) ret->display_name = L"extern " + ret->display_name; 317 | 318 | wstring access_modifier = format_visibility(ret->visibility); 319 | if (!access_modifier.empty()) ret->display_name = access_modifier + L" " + ret->display_name; 320 | 321 | if (IsMdSpecialName(ret->attributes) && !wcscmp(name, L"Invoke")) ret->set_special(meta_method::method_specials::delegate_invoke); 322 | } 323 | return ret; 324 | } 325 | 326 | std::shared_ptr meta_type_reader::get_event(mdEvent token) 327 | { 328 | auto ret = make_shared(); 329 | ret->token = token; 330 | 331 | wchar_t name[1024] = {}; PCCOR_SIGNATURE sig = nullptr; ULONG sig_size = 0; 332 | mdToken type = mdTokenNil; mdMethodDef add = mdMethodDefNil, remove = mdMethodDefNil, fire = mdMethodDefNil; 333 | if (SUCCEEDED(metadata_import->GetEventProps(token, nullptr, name, _countof(name), nullptr, &ret->attributes, &type, &add, &remove, &fire, nullptr, 0, nullptr))) { 334 | ret->name = name; 335 | ret->display_name = get_full_name(type) + L" " + ret->name; 336 | } 337 | 338 | for (const auto& m : result->methods) { 339 | if (m->token == add) m->set_semantic(meta_method::method_semantics::event_add); 340 | if (m->token == remove) m->set_semantic(meta_method::method_semantics::event_remove); 341 | if (m->token == fire) m->set_semantic(meta_method::method_semantics::event_fire); 342 | } 343 | 344 | return ret; 345 | } 346 | 347 | std::shared_ptr meta_type_reader::get_type() 348 | { 349 | wchar_t name[1024] = {}; PCCOR_SIGNATURE sig = nullptr; ULONG sig_size = 0; 350 | mdToken extends = mdTokenNil; 351 | if (SUCCEEDED(metadata_import->GetTypeDefProps(result->token, name, _countof(name), nullptr, &result->attributes, &extends))) { 352 | result->name = name; 353 | 354 | auto self_name = get_name(result->token); 355 | auto pos = result->name.rfind(self_name); 356 | if (pos > 1) result->namespace_name = result->name.substr(0, pos - 1); 357 | 358 | wstring semantic_name; 359 | wstring extends_name; 360 | 361 | if (!IsNilToken(extends)) extends_name = get_full_name(extends); 362 | 363 | if (IsTdClass(result->attributes)) { 364 | semantic_name = L"class"; 365 | result->set_semantic(meta_type::type_semantics::class_type); 366 | //TODO: this is dirty and stupid 367 | if (extends_name == L"System.Enum") { 368 | result->set_semantic(meta_type::type_semantics::enum_type); 369 | semantic_name = L"enum"; 370 | extends_name.clear(); 371 | } else if (extends_name == L"System.ValueType") { 372 | result->set_semantic(meta_type::type_semantics::value_type); 373 | semantic_name = L"struct"; 374 | extends_name.clear(); 375 | } else if (extends_name == L"System.MulticastDelegate") { 376 | result->set_semantic(meta_type::type_semantics::delegate_type); 377 | semantic_name = L"delegate"; 378 | extends_name.clear(); 379 | } else if (extends_name == L"System.Object") { 380 | // this type just implements some interfaces 381 | extends_name.clear(); 382 | } 383 | } else if (IsTdInterface(result->attributes)) { 384 | semantic_name = L"interface"; 385 | result->set_semantic(meta_type::type_semantics::interface_type); 386 | } 387 | 388 | auto generic_params = enum_generic_params(result->token); 389 | 390 | result->name = fix_generics_name(result->name, generic_params.size()); 391 | 392 | result->display_name = result->name; 393 | 394 | if (!semantic_name.empty()) result->display_name = semantic_name + L" " + result->display_name; 395 | 396 | if (IsTdPublic(result->attributes)) result->visibility = meta_visibility::vis_public; 397 | else if (IsTdNotPublic(result->attributes)) result->visibility = meta_visibility::vis_internal; 398 | 399 | for (const auto& gp : generic_params) { type_generic_param_names.push_back(get_full_name(gp)); } 400 | 401 | if (!type_generic_param_names.empty()) { 402 | result->display_name += L"<"; 403 | bool first = true; 404 | for (const auto& gp_name : type_generic_param_names) { 405 | if (!first) result->display_name += L", "; else first = false; 406 | result->display_name += gp_name; 407 | } 408 | result->display_name += L">"; 409 | } 410 | 411 | //TODO: this is stupid 412 | if (result->semantics == meta_type::type_semantics::delegate_type) { 413 | auto methods = enum_methods(result->token); 414 | for (const auto& m : methods) { 415 | wchar_t method_name[1024] = {}; PCCOR_SIGNATURE sig = nullptr; ULONG sig_size = 0; DWORD method_attr; 416 | if (SUCCEEDED(metadata_import->GetMethodProps(m, nullptr, method_name, _countof(method_name), nullptr, &method_attr, &sig, &sig_size, nullptr, nullptr))) { 417 | if (IsMdSpecialName(method_attr) && !wcscmp(method_name, L"Invoke")) { 418 | std::vector method_param_names; 419 | auto method_params = enum_params(m); 420 | for (const auto& p : method_params) method_param_names.push_back(get_name(p)); 421 | result->display_name = meta_sig_parser(metadata_import, sig, sig_size, type_generic_param_names).parse_method(result->display_name.c_str(), method_param_names); 422 | break; 423 | } 424 | } 425 | } 426 | } 427 | 428 | if (!extends_name.empty()) { 429 | result->display_name += L" : " + extends_name; 430 | } 431 | 432 | wstring access_modifier = format_visibility(result->visibility); 433 | if (!access_modifier.empty()) result->display_name = access_modifier + L" " + result->display_name; 434 | } 435 | 436 | auto fields = enum_fields(result->token); 437 | for (const auto& f : fields) result->fields.push_back(get_field(f)); 438 | 439 | auto methods = enum_methods(result->token); 440 | for (const auto& m : methods) result->methods.push_back(get_method(m)); 441 | 442 | auto properties = enum_properties(result->token); 443 | for (const auto& p : properties) result->properties.push_back(get_property(p)); 444 | 445 | auto events = enum_events(result->token); 446 | for (const auto e : events) result->events.push_back(get_event(e)); 447 | 448 | return result; 449 | } 450 | 451 | std::wstring meta_type_reader::format_visibility(meta_visibility visibility) 452 | { 453 | switch (visibility) { 454 | case meta_visibility::vis_unknown: return wstring(); 455 | case meta_visibility::vis_private: return L"private"; 456 | case meta_visibility::vis_protected: return L"protected"; 457 | case meta_visibility::vis_internal: return L"internal"; 458 | case meta_visibility::vis_protected_internal: return L"protected internal"; 459 | case meta_visibility::vis_public: return L"public"; 460 | default: return wstring(); 461 | } 462 | } 463 | 464 | std::wstring meta_type_reader::get_full_name(mdToken token) 465 | { 466 | if (metadata_import) { 467 | if (TypeFromToken(token) == mdtTypeSpec) { 468 | PCCOR_SIGNATURE sig = nullptr; ULONG sig_size = 0; 469 | if (SUCCEEDED(metadata_import->GetTypeSpecFromToken(token, &sig, &sig_size))) { 470 | return meta_sig_parser(metadata_import, sig, sig_size, type_generic_param_names).parse_element_type(); 471 | } 472 | } 473 | } 474 | 475 | return meta_reader::get_full_name(token); 476 | } 477 | -------------------------------------------------------------------------------- /metadiff/meta_reader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class meta_reader 6 | { 7 | protected: 8 | _com_ptr_t<_com_IIID> metadata_import; 9 | public: 10 | meta_reader(); 11 | meta_reader(const meta_reader& that) { metadata_import = that.metadata_import; } 12 | bool init_alt(const wchar_t* _filename); 13 | bool init(const wchar_t* _filename); 14 | std::wstring get_name(mdToken token); 15 | virtual std::wstring get_full_name(mdToken token); 16 | std::vector enum_tokens(std::function) const; 17 | std::vector enum_types() const; 18 | std::vector enum_fields(mdTypeDef type); 19 | std::vector enum_properties(mdTypeDef type); 20 | std::vector enum_methods(mdTypeDef type); 21 | std::vector enum_events(mdTypeDef type); 22 | std::vector enum_params(mdMethodDef method); 23 | std::vector enum_generic_params(mdToken type_or_method); 24 | }; 25 | 26 | enum class meta_visibility : int { 27 | // this is (roughly) ordered. some of them are not comparable (protected/internal), but here doesn't matter 28 | vis_unknown, vis_private, vis_protected, vis_internal, vis_protected_internal, vis_public 29 | }; 30 | 31 | struct meta_elem { 32 | mdToken token; 33 | DWORD attributes; 34 | std::wstring name; 35 | std::wstring display_name; 36 | meta_visibility visibility; 37 | meta_elem() : token(mdTokenNil), attributes(0), visibility(meta_visibility::vis_unknown) {} 38 | }; 39 | 40 | struct meta_field : public meta_elem { 41 | enum class field_specials : int { 42 | none = 0x0, 43 | enum_value = 0x1, 44 | } specials; 45 | void set_special(field_specials special) { specials = static_cast(static_cast(specials) | static_cast(special)); } 46 | meta_field() : specials(field_specials::none) {} 47 | }; 48 | 49 | struct meta_method : public meta_elem { 50 | enum class method_semantics : int { 51 | normal = 0x0, 52 | property_get = 0x1, 53 | property_set = 0x2, 54 | property_other = 0x4, 55 | event_add = 0x8, 56 | event_remove = 0x10, 57 | event_fire = 0x20, 58 | event_other = 0x40, 59 | } semantics; 60 | void set_semantic(method_semantics semantic) { semantics = static_cast(static_cast(semantics) | static_cast(semantic)); } 61 | enum class method_specials : int { 62 | none = 0x0, 63 | constructor = 0x1, 64 | class_constructor = 0x2, 65 | delegate_invoke = 0x4, 66 | } specials; 67 | void set_special(method_specials special) { specials = static_cast(static_cast(specials) | static_cast(special)); } 68 | meta_method() : semantics(method_semantics::normal), specials(method_specials::none) {} 69 | }; 70 | 71 | struct meta_property : public meta_elem {}; 72 | 73 | struct meta_event : public meta_elem {}; 74 | 75 | struct meta_type : public meta_elem { 76 | std::wstring namespace_name; 77 | 78 | enum class type_semantics : int { 79 | class_type = 0, 80 | interface_type = 1, 81 | value_type = 2, 82 | enum_type = 4, 83 | delegate_type = 8, 84 | } semantics; 85 | void set_semantic(type_semantics semantic) { semantics = static_cast(static_cast(semantics) | static_cast(semantic)); } 86 | meta_type() : semantics(type_semantics::class_type) {} 87 | 88 | std::vector> fields; 89 | std::vector> properties; 90 | std::vector> methods; 91 | std::vector> events; 92 | }; 93 | 94 | class meta_type_reader : meta_reader { 95 | std::shared_ptr result; 96 | std::vector type_generic_param_names; 97 | std::shared_ptr get_field(mdFieldDef token); 98 | std::shared_ptr get_property(mdProperty token); 99 | std::shared_ptr get_method(mdMethodDef token); 100 | std::shared_ptr get_event(mdEvent token); 101 | static std::wstring format_visibility(meta_visibility visibility); 102 | virtual std::wstring get_full_name(mdToken token) override; 103 | public: 104 | meta_type_reader(const meta_reader& reader, mdTypeDef token) : meta_reader(reader), result(std::make_shared()) { result->token = token; } 105 | std::shared_ptr get_type(); 106 | }; 107 | 108 | -------------------------------------------------------------------------------- /metadiff/meta_sig_parser.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "meta_sig_parser.h" 3 | #include "meta_utils.h" 4 | 5 | meta_sig_parser::meta_sig_parser(IMetaDataImport *import, PCCOR_SIGNATURE signature, ULONG sig_size) 6 | :m_import(import), m_signature(signature), m_sig_size(sig_size), m_sig_pos(0) {} 7 | 8 | meta_sig_parser::meta_sig_parser(IMetaDataImport *import, PCCOR_SIGNATURE signature, ULONG sig_size, const std::vector& var_names) 9 | :m_import(import), m_signature(signature), m_sig_size(sig_size), m_sig_pos(0), m_var_names(var_names) {} 10 | 11 | std::wstring meta_sig_parser::parse_field(wchar_t* name) 12 | { 13 | uncompress_data(); // field = 0x06 14 | return parse_element_type() + L" " + std::wstring(name); 15 | } 16 | 17 | std::wstring meta_sig_parser::parse_property(wchar_t* name) 18 | { 19 | uncompress_data(); // property = 0x08 or property | has_this = 0x28 20 | 21 | ULONG param_count = uncompress_data(); 22 | for (ULONG i = 0; i < param_count; ++i) { 23 | uncompress_data(); // custom_mod 24 | } 25 | 26 | return parse_element_type() + L" " + std::wstring(name); 27 | } 28 | 29 | std::wstring meta_sig_parser::parse_method(const wchar_t* name, const std::vector& raw_param_names, bool omit_return_type) 30 | { 31 | std::wstring ret = name; 32 | std::vector param_names = raw_param_names; 33 | CorCallingConvention calling_conv = (CorCallingConvention) uncompress_data(); // calling convention 34 | ULONG param_count = uncompress_data(); 35 | 36 | std::wstring return_type = parse_element_type(); 37 | if (return_type != L"void" && param_names.size() > 0) 38 | { 39 | param_names.erase(param_names.begin()); 40 | } 41 | 42 | if (!omit_return_type) ret = return_type + L" " + ret; 43 | 44 | if (param_count != 0) { 45 | ret += L"( "; 46 | for (UINT i = 0; i < param_count; i++) { 47 | ret += parse_element_type(); 48 | if (i < param_names.size()) ret += L" " + param_names[i]; 49 | if (i < param_count - 1) ret += L", "; 50 | } 51 | 52 | if (calling_conv == IMAGE_CEE_CS_CALLCONV_VARARG) { 53 | if (param_count) ret += L", "; 54 | ret += L"..."; 55 | } 56 | ret += L" )"; 57 | } else ret += L"()"; 58 | 59 | return ret; 60 | } 61 | 62 | std::wstring meta_sig_parser::parse_element_type() 63 | { 64 | CorElementType type = ELEMENT_TYPE_END; 65 | m_sig_pos += CorSigUncompressElementType(m_signature + m_sig_pos, &type); 66 | 67 | switch (type) { 68 | case ELEMENT_TYPE_VOID: return L"void"; 69 | case ELEMENT_TYPE_BOOLEAN: return L"bool"; 70 | case ELEMENT_TYPE_I1: return L"sbyte"; 71 | case ELEMENT_TYPE_U1: return L"byte"; 72 | case ELEMENT_TYPE_I2: return L"short"; 73 | case ELEMENT_TYPE_U2: return L"ushort"; 74 | case ELEMENT_TYPE_CHAR: return L"char"; 75 | case ELEMENT_TYPE_I4: return L"int"; 76 | case ELEMENT_TYPE_U4: return L"uint"; 77 | case ELEMENT_TYPE_I8: return L"long"; 78 | case ELEMENT_TYPE_U8: return L"ulong"; 79 | case ELEMENT_TYPE_R4: return L"float"; 80 | case ELEMENT_TYPE_R8: return L"double"; 81 | case ELEMENT_TYPE_OBJECT: return L"object"; 82 | case ELEMENT_TYPE_STRING: return L"string"; 83 | case ELEMENT_TYPE_I: return L"IntPtr"; 84 | case ELEMENT_TYPE_U: return L"UIntPtr"; 85 | case ELEMENT_TYPE_VALUETYPE: 86 | case ELEMENT_TYPE_CLASS: { 87 | mdToken token = uncompress_token(); 88 | MDUTF8CSTR name_utf8; 89 | if (SUCCEEDED(m_import->GetNameFromToken(token, &name_utf8))) { 90 | return meta_utils::Utf8ToUtf16(name_utf8); 91 | } else return L"**Unknown Type**"; 92 | } 93 | case ELEMENT_TYPE_TYPEDBYREF: return L"TypedReference"; 94 | case ELEMENT_TYPE_BYREF: return L"ref " + parse_element_type(); 95 | case ELEMENT_TYPE_SZARRAY: return parse_element_type() + L"[]"; 96 | case ELEMENT_TYPE_ARRAY: { 97 | std::wstring ret = parse_element_type(); 98 | ret += L"["; 99 | ULONG rank = uncompress_data(); 100 | if (rank > 0) { 101 | ULONG sizes = uncompress_data(); 102 | for (ULONG i = 0; i < sizes; i++) { 103 | ULONG dimSize = uncompress_data(); 104 | if (i > 0) ret += L","; 105 | } 106 | 107 | ULONG lowers = uncompress_data(); 108 | for (ULONG i = 0; i < lowers; i++) { 109 | int lowerBound = uncompress_int(); 110 | } 111 | } 112 | ret += L"]"; 113 | return ret; 114 | } 115 | case ELEMENT_TYPE_PTR: return parse_element_type() + L"*"; 116 | case ELEMENT_TYPE_CMOD_REQD:return L"CMOD_REQD" + parse_element_type(); 117 | case ELEMENT_TYPE_CMOD_OPT: return L"CMOD_OPT" + parse_element_type(); 118 | case ELEMENT_TYPE_MODIFIER: return parse_element_type(); 119 | case ELEMENT_TYPE_PINNED: return L"pinned " + parse_element_type(); 120 | case ELEMENT_TYPE_SENTINEL: return std::wstring(); 121 | case ELEMENT_TYPE_GENERICINST: { 122 | std::wstring ret = parse_element_type(); 123 | ULONG count = uncompress_data(); 124 | ret = meta_utils::fix_generics_name(ret, count); 125 | ret += L"<"; 126 | for (ULONG i = 0; i < count; i++) { 127 | ret += parse_element_type(); 128 | if (i < (count - 1)) ret += L", "; 129 | } 130 | ret += L">"; 131 | return ret; 132 | } 133 | case ELEMENT_TYPE_VAR: { 134 | ULONG num = uncompress_data(); 135 | if (num < m_var_names.size()) return m_var_names[num]; 136 | else { 137 | WCHAR num_str[64]; 138 | _itow_s(num, num_str, 10); 139 | return L"!" + std::wstring(num_str); 140 | } 141 | } 142 | case ELEMENT_TYPE_MVAR: { 143 | ULONG num = uncompress_data(); 144 | if (num < m_mvar_names.size()) return m_mvar_names[num]; 145 | else { 146 | WCHAR num_str[64]; 147 | _itow_s(num, num_str, 10); 148 | return L"!!" + std::wstring(num_str); 149 | } 150 | } 151 | case ELEMENT_TYPE_END: return std::wstring(); 152 | default: return L"***UNKNOWN TYPE***"; 153 | } 154 | } 155 | 156 | ULONG meta_sig_parser::uncompress_data() 157 | { 158 | ULONG ret = 0; 159 | m_sig_pos += CorSigUncompressData(m_signature + m_sig_pos, &ret); 160 | return ret; 161 | } 162 | 163 | int meta_sig_parser::uncompress_int() 164 | { 165 | int ret = 0; 166 | m_sig_pos += CorSigUncompressSignedInt(m_signature + m_sig_pos, &ret); 167 | return ret; 168 | } 169 | 170 | mdToken meta_sig_parser::uncompress_token() 171 | { 172 | mdToken ret = mdTokenNil; 173 | m_sig_pos += CorSigUncompressToken(&m_signature[m_sig_pos], &ret); 174 | return ret; 175 | } 176 | -------------------------------------------------------------------------------- /metadiff/meta_sig_parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class meta_sig_parser 6 | { 7 | private: 8 | PCCOR_SIGNATURE m_signature; 9 | const ULONG m_sig_size; 10 | ULONG m_sig_pos; 11 | IMetaDataImport*m_import; 12 | ULONG uncompress_data(); 13 | int uncompress_int(); 14 | mdToken uncompress_token(); 15 | const std::vector m_var_names; 16 | const std::vector m_mvar_names; 17 | public: 18 | meta_sig_parser(IMetaDataImport* import, PCCOR_SIGNATURE signature, ULONG sig_size); 19 | meta_sig_parser(IMetaDataImport* import, PCCOR_SIGNATURE signature, ULONG sig_size, const std::vector& var_names); 20 | std::wstring parse_element_type(); 21 | std::wstring parse_field(wchar_t* name); 22 | std::wstring parse_property(wchar_t* name); 23 | std::wstring parse_method(const wchar_t* name, const std::vector& param_names, bool omit_return_type = false); 24 | }; 25 | 26 | -------------------------------------------------------------------------------- /metadiff/meta_utils.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "meta_utils.h" 3 | 4 | std::wstring meta_utils::fix_generics_name(const std::wstring& name, int generics_count) 5 | { 6 | std::wstring ret = name; 7 | if (generics_count > 0) { 8 | std::wstring appendix = L"`" + format_integer(generics_count); 9 | auto pos = ret.rfind(appendix); 10 | if ((pos != std::wstring::npos) && (pos == (ret.length() - appendix.length()))) 11 | ret.erase(pos); 12 | } 13 | return ret; 14 | } 15 | 16 | std::wstring meta_utils::Utf8ToUtf16(const std::string& str) 17 | { 18 | std::wstring ret; 19 | 20 | int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.c_str(), -1, nullptr, 0); 21 | 22 | if (len > 0) { 23 | ret.resize(len - 1); 24 | MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.c_str(), len - 1, const_cast(ret.c_str()), len); 25 | } 26 | 27 | return ret; 28 | } 29 | -------------------------------------------------------------------------------- /metadiff/meta_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace meta_utils 6 | { 7 | template static std::wstring format_integer(T value) { 8 | std::wstringstream ss; 9 | ss << value; 10 | return ss.str(); 11 | } 12 | std::wstring Utf8ToUtf16(const std::string& str); 13 | std::wstring fix_generics_name(const std::wstring& name, int generics_count); 14 | }; 15 | 16 | -------------------------------------------------------------------------------- /metadiff/metadiff.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "meta_reader.h" 3 | 4 | using namespace std; 5 | 6 | enum class meta_diff : int { same, removed, changed, child_changed, added }; 7 | 8 | bool print_diff(meta_diff diff) { 9 | switch (diff) { 10 | case meta_diff::same: /* wprintf_s(L" = "); return true;*/ return false; 11 | case meta_diff::removed: wprintf_s(L" - "); return true; 12 | case meta_diff::changed: wprintf_s(L" * "); return true; 13 | case meta_diff::child_changed: wprintf_s(L" | "); return true; 14 | case meta_diff::added: wprintf_s(L" + "); return true; 15 | default: wprintf_s(L"? "); return true; 16 | } 17 | } 18 | 19 | template // T: meta_elem 20 | struct meta_diff_elem { 21 | meta_diff diff; 22 | shared_ptr elem; 23 | meta_diff_elem() : diff(meta_diff::same) {} 24 | meta_diff_elem(meta_diff _diff, const shared_ptr& _elem) : diff(_diff), elem(_elem) {} 25 | }; 26 | 27 | template 28 | bool find_changes(vector>& diffs, bool public_only, const vector>& old_list, const vector>& new_list, 29 | const function&, const shared_ptr&)>& filter = nullptr) 30 | { 31 | bool any_changes = false; 32 | 33 | for (const auto& new_elem : new_list) { 34 | shared_ptr old_elem; 35 | 36 | bool ignore = false; 37 | 38 | for (const auto& elem : old_list) { 39 | bool match = (elem->name == new_elem->name); 40 | 41 | if (match && (filter != nullptr)) { 42 | auto filter_res = filter(elem, new_elem); 43 | 44 | if (filter_res == 0) { // found match, proceed 45 | match = false; // ignore this old_elem 46 | } else if (filter_res < 0) { 47 | ignore = true; // ignore this new_elem; 48 | break; 49 | } 50 | } 51 | 52 | if (match) { 53 | old_elem = elem; 54 | break; 55 | } 56 | } 57 | 58 | if (ignore) continue; 59 | 60 | if (old_elem) { 61 | if (public_only && ((old_elem->visibility != meta_visibility::vis_public) && (new_elem->visibility != meta_visibility::vis_public))) 62 | continue; // both are not public, ignore. if ANY of them are public, should proceed. 63 | 64 | if (old_elem->display_name != new_elem->display_name) { 65 | diffs.push_back(meta_diff_elem(meta_diff::changed, new_elem)); 66 | any_changes = true; 67 | } 68 | } else { 69 | if (public_only && (new_elem->visibility != meta_visibility::vis_public)) 70 | continue; // not public, ignore. 71 | 72 | if (filter && (filter(old_elem, new_elem) < 0)) 73 | continue; // ignore 74 | 75 | diffs.push_back(meta_diff_elem(meta_diff::added, new_elem)); 76 | any_changes = true; 77 | } 78 | } 79 | 80 | for (const auto& old_elem : old_list) { 81 | if (find_if(begin(new_list), end(new_list), [&old_elem](const shared_ptr& elem) ->bool { return elem->name == old_elem->name; }) == end(new_list)) { 82 | if (public_only && (old_elem->visibility != meta_visibility::vis_public)) 83 | continue; // not public, ignore. 84 | diffs.push_back(meta_diff_elem(meta_diff::removed, old_elem)); 85 | any_changes = true; 86 | } 87 | } 88 | 89 | return any_changes; 90 | } 91 | 92 | enum diff_options { 93 | diffNone = 0x0, 94 | diffOld = 0x1, 95 | diffNew = 0x2, 96 | diffRec = 0x4, 97 | diffWcs = 0x8, 98 | diffNonPublic = 0x10, 99 | diffTypeInclude = 0x20, 100 | diffHelp = 0x80000000, 101 | }; 102 | 103 | const struct { const wchar_t* arg; const wchar_t* arg_alt; const wchar_t* params_desc; const wchar_t* description; const diff_options options; } cmd_options[] = { 104 | { L"?", L"help", nullptr, L"show this help", diffHelp }, 105 | { L"n", L"new", L"", L"specify new file(s)", diffNew }, 106 | { L"o", L"old", L"", L"specify old file(s)", diffOld }, 107 | { L"r", L"recursive", nullptr, L"search folder recursively", diffRec }, 108 | { nullptr, L"wcs", nullptr, L"folder is Windows Component Store", diffWcs }, 109 | { L"np", L"non-public", nullptr, L"show non-public members", diffNonPublic }, 110 | { L"t+", L"type-include", L"", L"show types when name match filter", diffTypeInclude }, 111 | }; 112 | 113 | void print_usage() { 114 | wprintf_s(L"\tUsage: metadiff [options]\n\n"); 115 | for (auto o = std::begin(cmd_options); o != std::end(cmd_options); ++o) { 116 | if (o->arg != nullptr) wprintf_s(L"\t-%ls", o->arg); else wprintf_s(L"\t"); 117 | 118 | int len = 0; 119 | if (o->arg_alt != nullptr) { 120 | len = wcslen(o->arg_alt); 121 | wprintf_s(L"\t--%ls", o->arg_alt); 122 | } else wprintf_s(L"\t"); 123 | 124 | if (len < 6) wprintf_s(L"\t"); 125 | 126 | if (o->params_desc != nullptr) len += wprintf_s(L" %ls", o->params_desc); 127 | 128 | if (len < 14) wprintf_s(L"\t"); 129 | 130 | wprintf_s(L"\t: %ls\n", o->description); 131 | } 132 | } 133 | 134 | struct meta_diff_type : meta_diff_elem { 135 | vector> fields; 136 | vector> properties; 137 | vector> methods; 138 | vector> events; 139 | meta_diff_type(meta_diff _diff, const shared_ptr& _elem) : meta_diff_elem(_diff, _elem) {} 140 | }; 141 | 142 | int _tmain(int argc, _TCHAR* argv[]) 143 | { 144 | auto out = stdout; 145 | 146 | int options = diffNone; 147 | const wchar_t* err_arg = nullptr; 148 | wstring new_files_pattern, old_files_pattern, type_filter; 149 | 150 | wprintf_s(L"\n MetaDiff v0.2 https://github.com/WalkingCat/MetaDiff\n\n"); 151 | 152 | for (int i = 1; i < argc; ++i) { 153 | const wchar_t* arg = argv[i]; 154 | if ((arg[0] == '-') || ((arg[0] == '/'))) { 155 | diff_options curent_option = diffNone; 156 | if ((arg[0] == '-') && (arg[1] == '-')) { 157 | for (auto o = std::begin(cmd_options); o != std::end(cmd_options); ++o) { 158 | if ((o->arg_alt != nullptr) && (_wcsicmp(arg + 2, o->arg_alt) == 0)) { curent_option = o->options; } 159 | } 160 | } else { 161 | for (auto o = std::begin(cmd_options); o != std::end(cmd_options); ++o) { 162 | if ((o->arg != nullptr) && (_wcsicmp(arg + 1, o->arg) == 0)) { curent_option = o->options; } 163 | } 164 | } 165 | 166 | bool valid = false; 167 | if (curent_option != diffNone) { 168 | valid = true; 169 | if (curent_option == diffNew) { 170 | if ((i + 1) < argc) new_files_pattern = argv[++i]; 171 | else valid = false; 172 | } else if (curent_option == diffOld) { 173 | if ((i + 1) < argc) old_files_pattern = argv[++i]; 174 | else valid = false; 175 | } else if (curent_option == diffTypeInclude) { 176 | if ((i + 1) < argc) type_filter = argv[++i]; 177 | else valid = false; 178 | } else options = (options | curent_option); 179 | } 180 | if (!valid && (err_arg == nullptr)) err_arg = arg; 181 | } else { if (new_files_pattern.empty()) new_files_pattern = arg; else err_arg = arg; } 182 | } 183 | 184 | if ((new_files_pattern.empty() && old_files_pattern.empty()) || (err_arg != nullptr) || (options & diffHelp)) { 185 | if (err_arg != nullptr) wprintf_s(L"\tError in option: %ls\n\n", err_arg); 186 | print_usage(); 187 | return 0; 188 | } 189 | 190 | const bool is_wcs = ((options & diffWcs) == diffWcs); 191 | const bool is_recursive = ((options & diffRec) == diffRec); 192 | const wchar_t def_file_pattern[] = L"*.winmd"; 193 | 194 | auto new_file_groups = is_wcs ? find_files_wcs_ex(new_files_pattern, def_file_pattern) : find_files_ex(new_files_pattern.c_str(), is_recursive, std::wstring_view()); 195 | auto old_file_groups = is_wcs ? find_files_wcs_ex(old_files_pattern, def_file_pattern) : find_files_ex(old_files_pattern.c_str(), is_recursive, std::wstring_view()); 196 | 197 | wprintf_s(L" new files: %ls%ls\n", new_files_pattern.c_str(), !new_file_groups.empty() ? L"" : L" (NOT EXISTS!)"); 198 | wprintf_s(L" old files: %ls%ls\n", old_files_pattern.c_str(), !old_file_groups.empty() ? L"" : L" (NOT EXISTS!)"); 199 | 200 | if (!type_filter.empty()) wprintf_s(L" type name filter: %ls\n", type_filter.c_str()); 201 | 202 | wprintf_s(L"\n"); 203 | 204 | wprintf_s(L" diff legends: +: added, -: removed, *: changed, |: type member changed\n"); 205 | 206 | auto get_meta_types = [](const wstring& file) -> unordered_map> { 207 | unordered_map> ret; 208 | meta_reader reader; 209 | if (reader.init(file.c_str())) { 210 | auto typedefs = reader.enum_types(); 211 | for (const auto& t : typedefs) { 212 | auto type = meta_type_reader(reader, t).get_type(); 213 | ret[type->name] = type; 214 | } 215 | } else { 216 | // wprintf_s(L"Can't read metadata from file %ls\n", file.c_str()); 217 | } 218 | return ret; 219 | }; 220 | 221 | const map empty_files; 222 | diff_maps(new_file_groups, old_file_groups, 223 | [&](const wstring& group_name, const map* new_files, const map* old_files) { 224 | bool printed_group_name = false; 225 | wchar_t printed_group_prefix = L' '; 226 | auto print_group_name = [&]() { 227 | if (is_wcs && (!printed_group_name)) { 228 | const auto prefix = new_files ? old_files ? L'*' : L'+' : L'-'; 229 | fwprintf_s(out, L"\n %lc %ls (\n", prefix, group_name.c_str()); 230 | printed_group_name = true; 231 | printed_group_prefix = prefix; 232 | } 233 | }; 234 | 235 | bool printed_previous_file_name = false; 236 | diff_maps(new_files ? *new_files : empty_files, old_files ? *old_files : empty_files, 237 | [&](const wstring& file_name, const wstring* new_file, const wstring* old_file) { 238 | bool printed_file_name = false; 239 | auto print_file_name = [&]() { 240 | if (!printed_file_name) { 241 | print_group_name(); 242 | if (printed_previous_file_name) { 243 | fwprintf_s(out, L"\n"); 244 | } 245 | fwprintf_s(out, L" %lc %ls\n", new_file ? old_file ? L'*' : L'+' : L'-', file_name.c_str()); 246 | printed_previous_file_name = printed_file_name = true; 247 | } 248 | }; 249 | 250 | if (old_file == nullptr) { 251 | print_file_name(); 252 | return; 253 | } 254 | 255 | const auto new_types = new_file ? get_meta_types(*new_file) : unordered_map>(); 256 | const auto old_types = old_file ? get_meta_types(*old_file) : unordered_map>(); 257 | 258 | vector diff_types; 259 | 260 | const bool public_only = (options & diff_options::diffNonPublic) == 0; 261 | 262 | for (const auto& new_type : new_types) { 263 | auto type_diff = meta_diff_type(meta_diff::same, new_type.second); 264 | 265 | auto old_type = old_types.find(new_type.first); 266 | if (old_type == old_types.end()) { // not found in old, so its new, and all members are new 267 | if (public_only && (new_type.second->visibility != meta_visibility::vis_public)) continue; // not public, ignore 268 | 269 | for (const auto& f : new_type.second->fields) type_diff.fields.push_back(meta_diff_elem(meta_diff::added, f)); 270 | for (const auto& p : new_type.second->properties) type_diff.properties.push_back(meta_diff_elem(meta_diff::added, p)); 271 | for (const auto& m : new_type.second->methods) type_diff.methods.push_back(meta_diff_elem(meta_diff::added, m)); 272 | for (const auto& e : new_type.second->events) type_diff.events.push_back(meta_diff_elem(meta_diff::added, e)); 273 | 274 | type_diff.diff = meta_diff::added; 275 | diff_types.push_back(type_diff); 276 | } else { 277 | if (public_only && ((new_type.second->visibility != meta_visibility::vis_public) && (old_type->second->visibility != meta_visibility::vis_public))) 278 | continue; // both are not public, ignore. if ANY of them are public, should proceed. 279 | 280 | auto method_filter = [](const shared_ptr& old_method, const shared_ptr& new_method) -> int { 281 | if (new_method) { 282 | if (old_method) { 283 | bool match = (old_method->display_name == new_method->display_name); // methods can be overloaded, so compare the full signature 284 | 285 | if (match) { 286 | if ((old_method->semantics != meta_method::method_semantics::normal) && (new_method->semantics != meta_method::method_semantics::normal)) 287 | return -1; // only care about normal methods, ignore others 288 | else 289 | return 1; 290 | } 291 | } else { 292 | if (new_method->semantics != meta_method::method_semantics::normal) 293 | return -1; // ignore 294 | } 295 | } 296 | return 0; // not match, try next 297 | }; 298 | 299 | bool member_changed = find_changes(type_diff.fields, public_only, old_type->second->fields, new_type.second->fields); 300 | member_changed = find_changes(type_diff.properties, public_only, old_type->second->properties, new_type.second->properties) || member_changed; 301 | member_changed = find_changes(type_diff.methods, public_only, old_type->second->methods, new_type.second->methods, method_filter) || member_changed; 302 | member_changed = find_changes(type_diff.events, public_only, old_type->second->events, new_type.second->events) || member_changed; 303 | 304 | if (member_changed) { 305 | type_diff.diff = meta_diff::child_changed; 306 | diff_types.push_back(type_diff); 307 | } else if (old_type->second->display_name != new_type.second->display_name) { 308 | type_diff.diff = meta_diff::changed; 309 | diff_types.push_back(type_diff); 310 | } 311 | } 312 | } 313 | 314 | for (const auto& old_type : old_types) { 315 | if (new_types.find(old_type.first) == new_types.end()) { 316 | if (public_only && (old_type.second->visibility != meta_visibility::vis_public)) 317 | continue; // not public, ignore 318 | 319 | diff_types.push_back(meta_diff_type(meta_diff::removed, old_type.second)); 320 | } 321 | } 322 | 323 | sort(begin(diff_types), end(diff_types), [](const meta_diff_type& left, const meta_diff_type& right) -> bool { 324 | const auto ns = left.elem->namespace_name.compare(right.elem->namespace_name); 325 | if (ns == 0) { 326 | return left.elem->name < right.elem->name; 327 | } else return ns < 0; 328 | }); 329 | 330 | if (!diff_types.empty()) { 331 | print_file_name(); 332 | 333 | for (const auto& type : diff_types) { 334 | if (!type_filter.empty() && (type.elem->namespace_name.find(type_filter) == wstring::npos)) continue; 335 | 336 | if (!print_diff(type.diff)) continue; 337 | 338 | wprintf_s(L"%ls", type.elem->display_name.c_str()); 339 | 340 | if ((type.diff == meta_diff::removed) || (type.diff == meta_diff::changed)) { 341 | wprintf_s(L";\n\n"); 342 | continue; 343 | } 344 | 345 | if (type.elem->semantics == meta_type::type_semantics::delegate_type) { 346 | wprintf_s(L";\n"); 347 | } else { 348 | wprintf_s(L" {\n"); 349 | for (const auto& f : type.fields) { 350 | if ((type.elem->semantics == meta_type::type_semantics::enum_type) && (f.elem->specials == meta_field::field_specials::enum_value)) { 351 | continue; // hide value__ for enums; 352 | } 353 | 354 | if (!print_diff(f.diff)) continue; 355 | 356 | wprintf_s(L" %ls", f.elem->display_name.c_str()); 357 | 358 | if (type.elem->semantics == meta_type::type_semantics::enum_type) { 359 | wprintf_s(L",\n"); 360 | } else { 361 | wprintf_s(L";\n"); 362 | } 363 | } 364 | 365 | for (const auto& p : type.properties) { 366 | if (!print_diff(p.diff)) continue; 367 | wprintf_s(L" %ls\n", p.elem->display_name.c_str()); 368 | } 369 | 370 | for (const auto& m : type.methods) { 371 | if (m.elem->semantics == meta_method::method_semantics::normal) { 372 | if (!print_diff(m.diff)) continue; 373 | wprintf_s(L" %ls;\n", m.elem->display_name.c_str()); 374 | } 375 | } 376 | 377 | for (const auto& e : type.events) { 378 | if (!print_diff(e.diff)) continue; 379 | wprintf_s(L" %ls;\n", e.elem->display_name.c_str()); 380 | } 381 | 382 | print_diff(type.diff); 383 | wprintf_s(L"}\n"); 384 | } 385 | } 386 | } 387 | } 388 | ); 389 | 390 | if (printed_group_name) 391 | fwprintf_s(out, L" %wc )\n", printed_group_prefix); 392 | } 393 | ); 394 | 395 | fwprintf_s(out, L"\n"); 396 | 397 | return 0; 398 | } 399 | -------------------------------------------------------------------------------- /metadiff/metadiff.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {8930C3AB-40DD-4B3A-9F93-30A3032958FB} 23 | Win32Proj 24 | metadiff 25 | SAK 26 | SAK 27 | SAK 28 | SAK 29 | 10.0 30 | 31 | 32 | 33 | Application 34 | true 35 | v142 36 | Unicode 37 | 38 | 39 | Application 40 | true 41 | v142 42 | Unicode 43 | 44 | 45 | Application 46 | false 47 | v142 48 | true 49 | Unicode 50 | 51 | 52 | Application 53 | false 54 | v142 55 | true 56 | Unicode 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | 77 | 78 | true 79 | 80 | 81 | false 82 | 83 | 84 | false 85 | 86 | 87 | 88 | Use 89 | Level3 90 | Disabled 91 | _SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | MultiThreadedDebug 94 | stdcpp17 95 | 96 | 97 | Console 98 | true 99 | 100 | 101 | 102 | 103 | Use 104 | Level3 105 | Disabled 106 | _SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 107 | true 108 | MultiThreadedDebug 109 | stdcpp17 110 | 111 | 112 | Console 113 | true 114 | 115 | 116 | 117 | 118 | Level3 119 | Use 120 | MaxSpeed 121 | true 122 | true 123 | _SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | true 125 | MultiThreaded 126 | stdcpp17 127 | 128 | 129 | Console 130 | true 131 | true 132 | true 133 | 134 | 135 | 136 | 137 | Level3 138 | Use 139 | MaxSpeed 140 | true 141 | true 142 | _SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 143 | true 144 | MultiThreaded 145 | stdcpp17 146 | 147 | 148 | Console 149 | true 150 | true 151 | true 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | Create 168 | Create 169 | Create 170 | Create 171 | 172 | 173 | 174 | 175 | {6c290411-0e80-4ec5-bfab-2a9206489474} 176 | 177 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /metadiff/metadiff.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /metadiff/stdafx.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | -------------------------------------------------------------------------------- /metadiff/stdafx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "targetver.h" 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "../witutils/find_files.h" 18 | #include "../witutils/find_files_wcs.h" 19 | #include "../witutils/diff_utils.h" 20 | 21 | #define VC_EXTRALEAN 22 | #include 23 | 24 | #include 25 | #pragma comment(lib, "comsuppwd.lib") 26 | 27 | //#include 28 | // already included in RoMetadataApi.h 29 | #include 30 | #include 31 | #include 32 | -------------------------------------------------------------------------------- /metadiff/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | -------------------------------------------------------------------------------- /witutils/cmdl_utils.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "cmdl_utils.h" 3 | 4 | using namespace std; 5 | 6 | options_data_t parse_cmdl(int argc, wchar_t * argv[], const cmdl_option * const options[], size_t count, const cmdl_option* const default_option) { 7 | unordered_map ret; 8 | 9 | for (int i = 1; i < argc; ++i) { 10 | const wchar_t* arg = argv[i]; 11 | const cmdl_option* option = nullptr; 12 | if ((arg[0] == '-') || ((arg[0] == '/'))) { 13 | const bool is_alt = ((arg[0] == '-') && (arg[1] == '-')); 14 | 15 | for (size_t i = 0; i < count; ++i) { 16 | auto o = options[i]; 17 | const bool match = is_alt ? 18 | ((o->arg_alt != nullptr) && (wcscmp(arg + 2, o->arg_alt) == 0)) : 19 | ((o->arg != nullptr) && (wcscmp(arg + 1, o->arg) == 0)); 20 | if (match) { 21 | option = o; 22 | } 23 | } 24 | 25 | bool err = false; 26 | if (option != nullptr) { 27 | if (option->data_desc != nullptr) { 28 | if ((i + 1) < argc) { 29 | ret[option] = argv[++i]; 30 | } else err = true; 31 | } else { 32 | ret[option] = wstring(); 33 | } 34 | } else err = true; 35 | 36 | if (err) { 37 | ret[nullptr] = arg; 38 | break; 39 | } 40 | 41 | } else if ((default_option != nullptr) && (default_option->data_desc != nullptr) && (ret.find(default_option) == ret.end())) { 42 | ret[default_option] = arg; 43 | } 44 | 45 | } 46 | 47 | return ret; 48 | } 49 | 50 | void print_cmdl_usage(const wchar_t * app, const cmdl_option* const options[], size_t count, const cmdl_option* const default_option) { 51 | wprintf_s(L" Usage: %ls [options]\n\n", app); 52 | for (size_t i = 0; i < count; ++i) { 53 | auto o = options[i]; 54 | if (o->arg != nullptr) { 55 | wprintf_s((o == default_option) ? L" [-%ls]" : L" -%ls", o->arg); 56 | } else { 57 | wprintf_s(L" "); 58 | } 59 | 60 | size_t len = 0; 61 | if (o->arg_alt != nullptr) { 62 | len = wcslen(o->arg_alt); 63 | wprintf_s(L"\t--%ls", o->arg_alt); 64 | } else wprintf_s(L"\t"); 65 | 66 | if (len < 6) wprintf_s(L"\t"); 67 | 68 | if (o->data_desc != nullptr) len += wprintf_s(L" %ls", o->data_desc); 69 | 70 | if (len < 14) wprintf_s(L"\t"); 71 | 72 | wprintf_s(L"\t: %ls\n", o->desc); 73 | } 74 | wprintf_s(L"\n"); 75 | } 76 | -------------------------------------------------------------------------------- /witutils/cmdl_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | struct cmdl_option { 6 | const wchar_t* arg; 7 | const wchar_t* arg_alt; 8 | const wchar_t* data_desc; 9 | const wchar_t* desc; 10 | }; 11 | 12 | using options_data_t = std::unordered_map; 13 | 14 | options_data_t parse_cmdl(int argc, wchar_t* argv[], const cmdl_option* const options[], size_t count, const cmdl_option* const default_option = nullptr); 15 | 16 | template 17 | options_data_t parse_cmdl(int argc, wchar_t* argv[], const cmdl_option* const (&options)[count], const cmdl_option* const default_option = nullptr) 18 | { 19 | return parse_cmdl(argc, argv, options, count, default_option); 20 | } 21 | 22 | void print_cmdl_usage(const wchar_t* app, const cmdl_option* const options[], size_t count, const cmdl_option* const default_option = nullptr); 23 | 24 | template 25 | void print_cmdl_usage(const wchar_t* app, const cmdl_option* const (&options)[count], const cmdl_option* const default_option = nullptr) { 26 | print_cmdl_usage(app, options, count, default_option); 27 | } 28 | -------------------------------------------------------------------------------- /witutils/diff_commons.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "diff_commons.h" 3 | #include "find_files.h" 4 | #include "find_files_wcs.h" 5 | #include "str_utils.h" 6 | 7 | using namespace std; 8 | 9 | constexpr cmdl_option diff_cmdl::show_help{ L"?", L"help", nullptr, L"show this help" }; 10 | constexpr cmdl_option diff_cmdl::new_files{ L"n", L"new", L"", L"specify new file(s)" }; 11 | constexpr cmdl_option diff_cmdl::old_files{ L"o", L"old", L"", L"specify old file(s)" }; 12 | constexpr cmdl_option diff_cmdl::filter{ L"f", L"filter", L"", L"path filter" }; 13 | constexpr cmdl_option diff_cmdl::recursive{ L"r", L"recursive", nullptr, L"search folder recursively" }; 14 | constexpr cmdl_option diff_cmdl::wcs_folder{ nullptr, L"wcs", nullptr, L"folder is Windows Component Store" }; 15 | constexpr cmdl_option diff_cmdl::out_file{ L"O", L"out", L"", L"output to file" }; 16 | constexpr const cmdl_option* diff_cmdl::options[7] = { &show_help, &new_files, &old_files, &filter, &recursive, &wcs_folder, &out_file }; 17 | constexpr const cmdl_option* diff_cmdl::default_option = &diff_cmdl::new_files; 18 | 19 | diff_params init_diff_params(int argc, wchar_t* argv[], const std::wstring_view default_file_pattern) 20 | { 21 | return init_diff_params(parse_cmdl(argc, argv, diff_cmdl::options, diff_cmdl::default_option), default_file_pattern); 22 | } 23 | 24 | diff_params init_diff_params(const options_data_t& options_data, const std::wstring_view default_file_pattern) 25 | { 26 | diff_params ret = {}; 27 | 28 | ret.show_help = options_data.find(&diff_cmdl::show_help) != options_data.end(); 29 | auto opt_it = options_data.find(nullptr); 30 | if (opt_it != options_data.end()) { 31 | ret.error = L"error in option: " + opt_it->second; 32 | ret.show_help = true; 33 | return ret; 34 | } 35 | opt_it = options_data.find(&diff_cmdl::new_files); 36 | if (opt_it != options_data.end()) { 37 | ret.new_files_pattern = opt_it->second; 38 | } 39 | opt_it = options_data.find(&diff_cmdl::old_files); 40 | if (opt_it != options_data.end()) { 41 | ret.old_files_pattern = opt_it->second; 42 | } 43 | opt_it = options_data.find(&diff_cmdl::filter); 44 | if (opt_it != options_data.end()) { 45 | ret.path_filter = tolower(opt_it->second); 46 | } 47 | opt_it = options_data.find(&diff_cmdl::out_file); 48 | if (opt_it != options_data.end()) { 49 | ret.output_file_name = opt_it->second; 50 | } 51 | 52 | ret.is_wcs = options_data.find(&diff_cmdl::wcs_folder) != options_data.end(); 53 | ret.is_rec = options_data.find(&diff_cmdl::recursive) != options_data.end(); 54 | 55 | if (!ret.output_file_name.empty()) { 56 | ret.out = nullptr; 57 | _wfopen_s(&ret.out, ret.output_file_name.c_str(), L"w, ccs=UTF-8"); 58 | 59 | if (ret.out == nullptr) { 60 | ret.error = L"can't open " + ret.output_file_name + L" for output"; 61 | } 62 | } else { 63 | ret.out = stdout; 64 | } 65 | 66 | auto search_files = [&](auto is_new) -> map> { 67 | map> file_groups; 68 | const auto& files_pattern = decltype(is_new)::value ? ret.new_files_pattern : ret.old_files_pattern; 69 | fwprintf_s(ret.out, L" %ls files: %ls", decltype(is_new)::value ? L"new" : L"old", files_pattern.c_str()); 70 | if (ret.is_wcs) { 71 | file_groups = find_files_wcs_ex(files_pattern, ret.path_filter, default_file_pattern); 72 | } else { 73 | file_groups = find_files_ex(files_pattern, ret.is_rec, ret.path_filter, default_file_pattern); 74 | } 75 | fwprintf_s(ret.out, L"%ls\n", !file_groups.empty() ? L"" : L" (EMPTY!)"); 76 | return file_groups; 77 | }; 78 | 79 | ret.new_file_groups = search_files(true_type()); 80 | ret.old_file_groups = search_files(false_type()); 81 | if (ret.new_file_groups.empty() && ret.old_file_groups.empty()) { 82 | ret.error = L"nothing to do"; 83 | ret.show_help = true; 84 | } else { 85 | if (!(ret.is_wcs || ret.is_rec)) { 86 | auto& new_files = ret.new_file_groups[wstring()], & old_files = ret.old_file_groups[wstring()]; 87 | if ((new_files.size() == 1) && (old_files.size() == 1)) { 88 | // allows diff single files with different names 89 | auto& new_file_name = new_files.begin()->first; 90 | auto& old_file_name = old_files.begin()->first; 91 | if (new_file_name != old_file_name) { 92 | auto diff_file_names = new_file_name + L" <=> " + old_file_name; 93 | auto new_file = new_files.begin()->second; 94 | new_files.clear(); 95 | new_files[diff_file_names] = new_file; 96 | auto old_file = old_files.begin()->second; 97 | old_files.clear(); 98 | old_files[diff_file_names] = old_file; 99 | } 100 | } 101 | } 102 | } 103 | 104 | return ret; 105 | } 106 | -------------------------------------------------------------------------------- /witutils/diff_commons.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "cmdl_utils.h" 3 | 4 | struct diff_cmdl { 5 | const static cmdl_option show_help; 6 | const static cmdl_option new_files; 7 | const static cmdl_option old_files; 8 | const static cmdl_option filter; 9 | const static cmdl_option recursive; 10 | const static cmdl_option wcs_folder; 11 | const static cmdl_option out_file; 12 | const static cmdl_option * const options[7]; 13 | const static cmdl_option * const default_option; 14 | }; 15 | 16 | struct diff_params { 17 | bool show_help; 18 | std::wstring error; 19 | std::wstring new_files_pattern; 20 | std::wstring old_files_pattern; 21 | std::wstring path_filter; 22 | std::wstring output_file_name; 23 | FILE* out; 24 | bool is_wcs; 25 | bool is_rec; 26 | std::map> new_file_groups; 27 | std::map> old_file_groups; 28 | }; 29 | 30 | diff_params init_diff_params(int argc, wchar_t* argv[], const std::wstring_view default_file_pattern = L"*"); 31 | diff_params init_diff_params(const options_data_t& options_data, const std::wstring_view default_file_pattern = L"*"); 32 | -------------------------------------------------------------------------------- /witutils/diff_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | void diff_maps(const std::map& new_map, const std::map& old_map, const TFunc& func) 9 | { 10 | auto new_it = new_map.begin(); 11 | auto old_it = old_map.begin(); 12 | 13 | while ((new_it != new_map.end()) || (old_it != old_map.end())) { 14 | int diff = 0; 15 | if (new_it != new_map.end()) { 16 | if (old_it != old_map.end()) { 17 | if (new_it->first > old_it->first) { 18 | diff = -1; 19 | } else if (new_it->first < old_it->first) { 20 | diff = 1; 21 | } 22 | } else diff = 1; 23 | } else { 24 | if (old_it != old_map.end()) { 25 | diff = -1; 26 | } 27 | } 28 | 29 | if (diff > 0) { 30 | func(new_it->first, &new_it->second, nullptr); 31 | ++new_it; 32 | } else if (diff < 0) { 33 | func(old_it->first, nullptr, &old_it->second); 34 | ++old_it; 35 | } else { 36 | func(old_it->first, &new_it->second, &old_it->second); 37 | ++new_it; 38 | ++old_it; 39 | } 40 | } 41 | } 42 | 43 | template 44 | void diff_sets(const std::set& new_set, const std::set& old_set, const TFunc& func) 45 | { 46 | auto new_it = new_set.begin(); 47 | auto old_it = old_set.begin(); 48 | 49 | while ((new_it != new_set.end()) || (old_it != old_set.end())) { 50 | int diff = 0; 51 | if (new_it != new_set.end()) { 52 | if (old_it != old_set.end()) { 53 | if (*new_it > *old_it) { 54 | diff = -1; 55 | } else if (*new_it < *old_it) { 56 | diff = 1; 57 | } 58 | } else diff = 1; 59 | } else { 60 | if (old_it != old_set.end()) 61 | diff = -1; 62 | } 63 | 64 | if (diff > 0) { 65 | func(&*new_it, nullptr); 66 | ++new_it; 67 | } else if (diff < 0) { 68 | func(nullptr, &*old_it); 69 | ++old_it; 70 | } else { 71 | ++new_it; 72 | ++old_it; 73 | } 74 | } 75 | } 76 | 77 | template 78 | void diff_sequences(const std::vector& new_seq, const std::vector& old_seq, TFunc& func) { 79 | // code derived from DTL (BSD License) https://github.com/cubicdaiya/dtl 80 | 81 | const bool swapped = (std::size(new_seq) > std::size(old_seq)); 82 | const std::vector &A = (swapped ? old_seq : new_seq), &B = (swapped ? new_seq : old_seq); 83 | const size_t M = std::size(A), N = std::size(B), delta = N - M, offset = M + 1; 84 | 85 | struct P { long long x; long long y; long long k; }; 86 | std::vector

path_coordinates; 87 | std::vector path(M + N + 3, -1); 88 | 89 | auto snake = [&](const long long& k, const long long& above, const long long& below) -> long long { 90 | const long long r = above > below ? path[(size_t)k - 1 + offset] : path[(size_t)k + 1 + offset]; 91 | 92 | long long y = (above > below) ? above : below; 93 | long long x = y - k; 94 | while ((size_t)x < M && (size_t)y < N && (A[(size_t)x] == B[(size_t)y])) { 95 | ++x; ++y; 96 | } 97 | 98 | path[(size_t)k + offset] = static_cast(path_coordinates.size()); 99 | path_coordinates.push_back(P{ x, y, r }); 100 | 101 | return y; 102 | }; 103 | 104 | std::vector fp(M + N + 3, -1); 105 | for (long long p = 0; ; ++p) { 106 | for (long long k = -p; k <= static_cast(delta) - 1; ++k) { 107 | fp[size_t(k + offset)] = snake(k, fp[size_t(k - 1 + offset)] + 1, fp[size_t(k + 1 + offset)]); 108 | } 109 | for (long long k = static_cast(delta) + p; k >= static_cast(delta) + 1; --k) { 110 | fp[size_t(k + offset)] = snake(k, fp[size_t(k - 1 + offset)] + 1, fp[size_t(k + 1 + offset)]); 111 | } 112 | fp[delta + offset] = snake(static_cast(delta), fp[delta - 1 + offset] + 1, fp[delta + 1 + offset]); 113 | 114 | if (fp[delta + offset] == static_cast(N)) break; 115 | } 116 | 117 | std::vector

epc; 118 | for (long long r = path[delta + offset]; r != -1;) { 119 | const auto& p = path_coordinates[size_t(r)]; 120 | epc.push_back(P{ p.x, p.y, 0 }); 121 | r = p.k; 122 | } 123 | 124 | auto report_change = [&](long long x, long long y) { 125 | auto a = (x >= 0) ? &A[size_t(x)] : nullptr; 126 | auto b = (y >= 0) ? &B[size_t(y)] : nullptr; 127 | if (swapped) func(b, a); else func(a, b); 128 | }; 129 | 130 | std::deque xs, ys; 131 | auto report_changes = [&](bool all) { 132 | const auto xsize = xs.size(), ysize = ys.size(); 133 | const auto min_size = (xsize < ysize) ? xsize : ysize; 134 | for (size_t i = 0; i < min_size; ++i) { 135 | report_change(xs[i], ys[i]); 136 | } 137 | if (all) { 138 | for (size_t i = min_size; i < xsize; ++i) { 139 | report_change(xs[i], -1); 140 | } 141 | xs.clear(); 142 | for (size_t i = min_size; i < ysize; ++i) { 143 | report_change(-1, ys[i]); 144 | } 145 | ys.clear(); 146 | } else { 147 | xs.erase(xs.begin(), xs.begin() + min_size); 148 | ys.erase(ys.begin(), ys.begin() + min_size); 149 | } 150 | }; 151 | 152 | auto record_change = [&](long long x, long long y) { 153 | if ((x >= 0) && (y >=0)) { 154 | report_changes(true); 155 | report_change(x, y); 156 | } else if (x >= 0) { 157 | xs.push_back(x); 158 | report_changes(false); 159 | } else if (y >= 0) { 160 | ys.push_back(y); 161 | report_changes(false); 162 | } else { 163 | /// should not reach here 164 | } 165 | }; 166 | 167 | long long x = 0, y = 0; // coordinates 168 | for (size_t i = epc.size(); i != 0; --i) { 169 | const auto& p = epc[i - 1]; 170 | while ((x < p.x) || (y < p.y)) { 171 | if ((p.y - p.x) >(y - x)) { 172 | record_change(-1, y); 173 | ++y; 174 | } else if ((p.y - p.x) < (y - x)) { 175 | record_change(x, -1); 176 | ++x; 177 | } else { 178 | record_change(x, y); 179 | ++x; ++y; 180 | } 181 | } 182 | } 183 | report_changes(true); 184 | } 185 | 186 | -------------------------------------------------------------------------------- /witutils/file_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | inline std::vector read_text_file(const std::wstring_view path) { 8 | std::vector ret; 9 | FILE* f = nullptr; 10 | _wfopen_s(&f, path.data(), L"r, ccs=UTF-8"); 11 | if (f != nullptr) { 12 | std::wifstream fs(f); 13 | while (fs) { 14 | std::wstring line; 15 | std::getline(fs, line); 16 | ret.emplace_back(move(line)); 17 | } 18 | fclose(f); 19 | } 20 | return ret; 21 | } -------------------------------------------------------------------------------- /witutils/find_files.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "find_files.h" 3 | 4 | using namespace std; 5 | 6 | map find_files_impl(const wstring_view pattern, bool directories) 7 | { 8 | map ret; 9 | wchar_t path[MAX_PATH] = {}; 10 | wcscpy_s(path, pattern.data()); 11 | WIN32_FIND_DATA fd; 12 | HANDLE find = ::FindFirstFile(pattern.data(), &fd); 13 | if (find != INVALID_HANDLE_VALUE) { 14 | do { 15 | if ((wcscmp(fd.cFileName, L".") == 0) || (wcscmp(fd.cFileName, L"..") == 0)) 16 | continue; 17 | 18 | if (directories == ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) { 19 | PathRemoveFileSpec(path); 20 | PathCombine(path, path, fd.cFileName); 21 | wstring name = fd.cFileName; 22 | for (auto& c : name) c = towlower(c); 23 | ret[name] = path; 24 | } 25 | } while (::FindNextFile(find, &fd)); 26 | ::FindClose(find); 27 | } 28 | return ret; 29 | } 30 | 31 | map find_files(const wstring_view pattern) 32 | { 33 | return find_files_impl(pattern, false); 34 | } 35 | 36 | namespace { 37 | wstring path_combine(const wstring_view dir, const wstring_view file) { 38 | wchar_t path[MAX_PATH] = {}; 39 | wcscpy_s(path, dir.data()); 40 | PathCombine(path, path, file.data()); 41 | return path; 42 | } 43 | } 44 | 45 | std::map> 46 | find_files_ex(const wstring_view pattern, bool recursive, const wstring_view path_filter, const std::wstring_view default_file_pattern) 47 | { 48 | std::map> ret; 49 | 50 | if (pattern.empty()) 51 | return ret; 52 | 53 | wstring dir, file_pat{ pattern }; 54 | if (PathIsDirectory(pattern.data()) != FALSE) { 55 | dir = pattern; 56 | file_pat = default_file_pattern; 57 | } else if (PathIsFileSpec(pattern.data()) == FALSE) { 58 | const auto file_spec = PathFindFileName(pattern.data()); 59 | if (file_spec != pattern.data()) { 60 | dir = wstring(pattern.data(), file_spec - pattern.data()); 61 | file_pat = file_spec; 62 | } 63 | } 64 | 65 | if (path_filter.empty()) { 66 | auto files = find_files_impl(path_combine(dir, file_pat), false); 67 | if (!files.empty()) ret[wstring()] = move(files); 68 | } 69 | 70 | if (recursive) { 71 | static constexpr wstring_view asterisk = L"*"; 72 | const auto dirs = find_files_impl(path_combine(dir, asterisk), true); 73 | for (auto& dir_pair : dirs) { 74 | const bool include = (dir_pair.first.find(path_filter) != wstring::npos); 75 | auto files = find_files_ex(dir_pair.second, true, include ? wstring_view() : path_filter, file_pat); 76 | for (auto pair : files) { 77 | ret[path_combine(dir_pair.first, pair.first)] = move(pair.second); 78 | } 79 | } 80 | } 81 | 82 | return ret; 83 | } 84 | -------------------------------------------------------------------------------- /witutils/find_files.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | std::map find_files(const std::wstring_view pattern); 6 | std::map> find_files_ex(const std::wstring_view pattern, bool recursive, const std::wstring_view path_filter, const std::wstring_view default_file_pattern = L"*"); 7 | -------------------------------------------------------------------------------- /witutils/find_files_wcs.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "find_files_wcs.h" 3 | #include "find_files.h" 4 | 5 | using namespace std; 6 | 7 | wstring read_attribute_value(IXmlReader* xml_reader, const wchar_t* attribute) { 8 | wstring ret; 9 | const wchar_t* buf = nullptr; UINT len = 0; 10 | if (SUCCEEDED(xml_reader->MoveToAttributeByName(attribute, nullptr)) 11 | && SUCCEEDED(xml_reader->GetValue(&buf, &len)) && (buf != nullptr)) { 12 | ret = wstring(buf, len); 13 | } 14 | return ret; 15 | } 16 | 17 | struct manifest_source { 18 | static const unsigned char data[]; 19 | static const size_t size; 20 | }; 21 | 22 | vector read_manifest(const wchar_t* path) { 23 | vector ret; 24 | FILE* file = nullptr; 25 | _wfopen_s(&file, path, L"rb"); 26 | if (file == nullptr) return ret; 27 | 28 | fseek(file, 0, SEEK_END); 29 | const auto len = ftell(file); 30 | ret.resize(len); 31 | fseek(file, 0, SEEK_SET); 32 | fread_s(ret.data(), len, len, 1, file); 33 | fclose(file); 34 | 35 | constexpr size_t head_len = 4; 36 | if (len > head_len) { 37 | if ((ret[0] == 'D') && (ret[1] == 'C') && (ret[2] == 'M') && (ret[3] == 1)) { 38 | struct delta_input : DELTA_INPUT { 39 | delta_input(const unsigned char* buffer, size_t len) { 40 | lpcStart = buffer; uSize = len; Editable = FALSE; 41 | } 42 | }; 43 | 44 | DELTA_HEADER_INFO dhi = {}; 45 | BOOL res = GetDeltaInfoB(delta_input(ret.data() + head_len, ret.size() - head_len), &dhi); 46 | 47 | if ((res == TRUE) && (dhi.TargetSize > 0)) { 48 | vector target(dhi.TargetSize); 49 | 50 | res = ApplyDeltaProvidedB(DELTA_FLAG_NONE, 51 | delta_input(manifest_source::data, manifest_source::size), 52 | delta_input(ret.data() + head_len, ret.size() - head_len), 53 | target.data(), target.size()); 54 | 55 | if (res == TRUE) { 56 | ret = move(target); 57 | } 58 | } 59 | } 60 | } 61 | 62 | return ret; 63 | } 64 | 65 | wstring generate_key_from_manifest(const wstring_view parent, const wstring_view dir) { 66 | wstring ret; 67 | 68 | wchar_t manifest_name[MAX_PATH] = {}; 69 | wcscpy_s(manifest_name, parent.data()); 70 | PathAppendW(manifest_name, dir.data()); 71 | wcscat_s(manifest_name, L".manifest"); 72 | 73 | auto manifest = read_manifest(manifest_name); 74 | _com_ptr_t<_com_IIID> stream = SHCreateMemStream(manifest.data(), static_cast(manifest.size())); 75 | if (stream) { 76 | IXmlReader* xml_reader = nullptr; 77 | CreateXmlReader(__uuidof(IXmlReader), (void**)& xml_reader, nullptr); 78 | if (xml_reader) { 79 | xml_reader->SetProperty(XmlReaderProperty_DtdProcessing, DtdProcessing_Prohibit); 80 | IXmlReaderInput* xml_reader_input = nullptr; 81 | CreateXmlReaderInputWithEncodingCodePage(stream, nullptr, CP_UTF8, TRUE, nullptr, &xml_reader_input); 82 | if (xml_reader_input) { 83 | xml_reader->SetInput(xml_reader_input); 84 | XmlNodeType node_type = XmlNodeType::XmlNodeType_None; 85 | while (SUCCEEDED(xml_reader->Read(&node_type))) { 86 | if (node_type == XmlNodeType_Element) { 87 | const wchar_t* elem_name = nullptr; 88 | xml_reader->GetQualifiedName(&elem_name, nullptr); 89 | if (elem_name && wcscmp(elem_name, L"assemblyIdentity") == 0) { 90 | auto name = read_attribute_value(xml_reader, L"name"); 91 | auto arch = read_attribute_value(xml_reader, L"processorArchitecture"); 92 | auto lang = read_attribute_value(xml_reader, L"language"); 93 | if ((!name.empty()) && (!arch.empty())) { 94 | ret = arch + L"_" + name; 95 | if ((!lang.empty()) && (lang != L"neutral")) { 96 | ret += L"_" + lang; 97 | } 98 | } 99 | break; 100 | } 101 | } 102 | } 103 | xml_reader_input->Release(); 104 | } 105 | xml_reader->Release(); 106 | } 107 | } 108 | 109 | return ret; 110 | } 111 | 112 | map> 113 | find_files_wcs(const wstring_view directory, const std::wstring_view path_filter, const std::wstring_view file_pattern) 114 | { 115 | map> ret; 116 | 117 | // ex. amd64_wvms_vsft.inf.resources_31bf3856ad364e35_10.0.14393.0_en-us_f32e284e2439eb0bs 118 | wregex re(L"(.*)_[0-9a-f]+_[0-9\\.]+_([0-9a-z-]+)_[0-9a-f]+"); 119 | wsmatch match; 120 | 121 | wchar_t path[MAX_PATH] = {}; 122 | wcscpy_s(path, directory.data()); 123 | PathAppend(path, L"*"); 124 | WIN32_FIND_DATAW fd; 125 | HANDLE find = ::FindFirstFileW(path, &fd); 126 | if (find != INVALID_HANDLE_VALUE) { 127 | do { 128 | if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) { 129 | wstring file(fd.cFileName); 130 | if (regex_match(file, match, re)) { 131 | auto key = generate_key_from_manifest(directory, file); 132 | if (key.empty()) { // fallback 133 | key = match[1].str(); 134 | wstring lang = match[2].str(); 135 | if (lang != L"none") key += L"_" + lang; 136 | } 137 | for (auto& c : key) c = towlower(c); 138 | 139 | PathRemoveFileSpec(path); 140 | PathCombine(path, path, fd.cFileName); 141 | 142 | PathAppend(path, file_pattern.data()); 143 | auto& files = ret[key.c_str()]; 144 | const auto& found_files = find_files_ex(path, true, path_filter); 145 | for (auto& group_pair : found_files) { 146 | auto& group = group_pair.first; 147 | auto& group_files = group_pair.second; 148 | for (auto& file_pair : group_files) { 149 | auto key = file_pair.first; 150 | if (!group.empty()) key = group + L"\\" + key; 151 | files[key] = file_pair.second; 152 | } 153 | } 154 | PathRemoveFileSpec(path); 155 | } 156 | } 157 | } while (::FindNextFileW(find, &fd)); 158 | ::FindClose(find); 159 | } 160 | return ret; 161 | } 162 | 163 | std::map> 164 | find_files_wcs_ex(const std::wstring_view pattern, const std::wstring_view path_filter, const std::wstring_view default_file_pattern) 165 | { 166 | wstring directory, file_pattern{ default_file_pattern }; 167 | if (PathIsDirectoryW(pattern.data())) { 168 | directory = pattern; 169 | } else { 170 | file_pattern = PathFindFileName(pattern.data()); 171 | auto dir = pattern; 172 | PathRemoveFileSpec((LPWSTR)dir.data()); 173 | directory = dir; 174 | } 175 | 176 | return find_files_wcs(directory, path_filter, file_pattern); 177 | } 178 | 179 | 180 | constexpr unsigned char manifest_source::data[] = { 181 | 0x3C, 0x3F, 0x78, 0x6D, 0x6C, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x22, 0x31, 182 | 0x2E, 0x30, 0x22, 0x20, 0x65, 0x6E, 0x63, 0x6F, 0x64, 0x69, 0x6E, 0x67, 0x3D, 0x22, 0x55, 0x54, 183 | 0x46, 0x2D, 0x38, 0x22, 0x20, 0x73, 0x74, 0x61, 0x6E, 0x64, 0x61, 0x6C, 0x6F, 0x6E, 0x65, 0x3D, 184 | 0x22, 0x79, 0x65, 0x73, 0x22, 0x3F, 0x3E, 0x0D, 0x0A, 0x3C, 0x61, 0x73, 0x73, 0x65, 0x6D, 0x62, 185 | 0x6C, 0x79, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3D, 0x22, 0x75, 0x72, 0x6E, 0x3A, 0x73, 0x63, 186 | 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2D, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2D, 187 | 0x63, 0x6F, 0x6D, 0x3A, 0x61, 0x73, 0x6D, 0x2E, 0x76, 0x33, 0x22, 0x20, 0x6D, 0x61, 0x6E, 0x69, 188 | 0x66, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x22, 0x31, 0x2E, 0x30, 189 | 0x22, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x3D, 0x22, 0x44, 190 | 0x65, 0x70, 0x6C, 0x6F, 0x79, 0x6D, 0x65, 0x6E, 0x74, 0x22, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6C, 191 | 0x61, 0x79, 0x4E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x44, 0x65, 0x70, 0x6C, 0x6F, 0x79, 0x6D, 0x65, 192 | 0x6E, 0x74, 0x22, 0x20, 0x63, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x3D, 0x22, 0x43, 193 | 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x4D, 0x69, 0x63, 194 | 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x43, 0x6F, 0x72, 0x70, 0x6F, 0x72, 0x61, 0x74, 0x69, 195 | 0x6F, 0x6E, 0x2E, 0x20, 0x41, 0x6C, 0x6C, 0x20, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x52, 196 | 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x2E, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x61, 197 | 0x73, 0x73, 0x65, 0x6D, 0x62, 0x6C, 0x79, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x20, 198 | 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2D, 199 | 0x57, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x73, 0x2D, 0x22, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 200 | 0x6E, 0x3D, 0x22, 0x36, 0x2E, 0x22, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x6F, 0x72, 201 | 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x3D, 0x22, 0x78, 0x38, 202 | 0x36, 0x22, 0x20, 0x6C, 0x61, 0x6E, 0x67, 0x75, 0x61, 0x67, 0x65, 0x3D, 0x22, 0x6E, 0x65, 0x75, 203 | 0x74, 0x72, 0x61, 0x6C, 0x22, 0x20, 0x62, 0x75, 0x69, 0x6C, 0x64, 0x54, 0x79, 0x70, 0x65, 0x3D, 204 | 0x22, 0x72, 0x65, 0x6C, 0x65, 0x61, 0x73, 0x65, 0x22, 0x20, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 205 | 0x4B, 0x65, 0x79, 0x54, 0x6F, 0x6B, 0x65, 0x6E, 0x3D, 0x22, 0x33, 0x31, 0x62, 0x66, 0x33, 0x38, 206 | 0x35, 0x36, 0x61, 0x64, 0x33, 0x36, 0x34, 0x65, 0x33, 0x35, 0x22, 0x20, 0x76, 0x65, 0x72, 0x73, 207 | 0x69, 0x6F, 0x6E, 0x53, 0x63, 0x6F, 0x70, 0x65, 0x3D, 0x22, 0x6E, 0x6F, 0x6E, 0x53, 0x78, 0x53, 208 | 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x64, 0x65, 0x70, 0x65, 0x6E, 0x64, 0x65, 209 | 0x6E, 0x63, 0x79, 0x20, 0x64, 0x69, 0x73, 0x63, 0x6F, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6C, 0x65, 210 | 0x3D, 0x22, 0x6E, 0x6F, 0x22, 0x20, 0x72, 0x65, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 211 | 0x70, 0x65, 0x3D, 0x22, 0x52, 0x65, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, 0x3E, 0x0D, 212 | 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x64, 0x65, 0x70, 0x65, 0x6E, 0x64, 0x65, 0x6E, 0x74, 0x41, 213 | 0x73, 0x73, 0x65, 0x6D, 0x62, 0x6C, 0x79, 0x20, 0x64, 0x65, 0x70, 0x65, 0x6E, 0x64, 0x65, 0x6E, 214 | 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 215 | 0x73, 0x69, 0x74, 0x65, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x61, 216 | 0x73, 0x73, 0x65, 0x6D, 0x62, 0x6C, 0x79, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x20, 217 | 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x2E, 0x52, 0x65, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x73, 218 | 0x22, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x22, 0x36, 0x2E, 0x22, 0x20, 0x70, 219 | 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x6F, 0x72, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 220 | 0x74, 0x75, 0x72, 0x65, 0x3D, 0x22, 0x61, 0x6D, 0x64, 0x36, 0x34, 0x22, 0x20, 0x6C, 0x61, 0x6E, 221 | 0x67, 0x75, 0x61, 0x67, 0x65, 0x3D, 0x22, 0x2A, 0x22, 0x20, 0x62, 0x75, 0x69, 0x6C, 0x64, 0x54, 222 | 0x79, 0x70, 0x65, 0x3D, 0x22, 0x72, 0x65, 0x6C, 0x65, 0x61, 0x73, 0x65, 0x22, 0x20, 0x70, 0x75, 223 | 0x62, 0x6C, 0x69, 0x63, 0x4B, 0x65, 0x79, 0x54, 0x6F, 0x6B, 0x65, 0x6E, 0x3D, 0x22, 0x33, 0x31, 224 | 0x62, 0x66, 0x33, 0x38, 0x35, 0x36, 0x61, 0x64, 0x33, 0x36, 0x34, 0x65, 0x33, 0x35, 0x22, 0x20, 225 | 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x64, 0x65, 0x70, 0x65, 0x6E, 0x64, 226 | 0x65, 0x6E, 0x74, 0x41, 0x73, 0x73, 0x65, 0x6D, 0x62, 0x6C, 0x79, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 227 | 0x3C, 0x2F, 0x64, 0x65, 0x70, 0x65, 0x6E, 0x64, 0x65, 0x6E, 0x63, 0x79, 0x3E, 0x0D, 0x0A, 0x20, 228 | 0x20, 0x3C, 0x66, 0x69, 0x6C, 0x65, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x2E, 0x64, 0x6C, 229 | 0x6C, 0x22, 0x20, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6E, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x50, 0x61, 230 | 0x74, 0x68, 0x3D, 0x22, 0x24, 0x28, 0x72, 0x75, 0x6E, 0x74, 0x69, 0x6D, 0x65, 0x2E, 0x73, 0x79, 231 | 0x73, 0x74, 0x65, 0x6D, 0x33, 0x32, 0x29, 0x22, 0x20, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x4E, 232 | 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x2E, 0x64, 0x6C, 0x6C, 0x22, 0x20, 0x69, 0x6D, 0x70, 0x6F, 0x72, 233 | 0x74, 0x50, 0x61, 0x74, 0x68, 0x3D, 0x22, 0x24, 0x28, 0x62, 0x75, 0x69, 0x6C, 0x64, 0x2E, 0x6E, 234 | 0x74, 0x74, 0x72, 0x65, 0x65, 0x29, 0x5C, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 235 | 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 236 | 0x6F, 0x72, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x57, 0x52, 0x50, 0x5F, 0x46, 0x49, 0x4C, 237 | 0x45, 0x5F, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4C, 0x54, 0x5F, 0x53, 0x44, 0x44, 0x4C, 0x22, 0x20, 238 | 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 239 | 0x72, 0x65, 0x49, 0x6E, 0x66, 0x6F, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3D, 0x22, 0x75, 0x72, 240 | 0x6E, 0x3A, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2D, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 241 | 0x6F, 0x66, 0x74, 0x2D, 0x63, 0x6F, 0x6D, 0x3A, 0x61, 0x73, 0x6D, 0x2E, 0x76, 0x33, 0x22, 0x3E, 242 | 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 243 | 0x72, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6F, 0x72, 0x20, 0x44, 0x52, 0x4D, 244 | 0x4C, 0x65, 0x76, 0x65, 0x6C, 0x3D, 0x22, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 245 | 0x20, 0x3C, 0x2F, 0x73, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x49, 0x6E, 0x66, 0x6F, 246 | 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x61, 0x73, 0x6D, 0x76, 0x32, 0x3A, 0x68, 0x61, 247 | 0x73, 0x68, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 0x61, 0x73, 0x6D, 0x76, 0x32, 0x3D, 0x22, 248 | 0x75, 0x72, 0x6E, 0x3A, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2D, 0x6D, 0x69, 0x63, 0x72, 249 | 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2D, 0x63, 0x6F, 0x6D, 0x3A, 0x61, 0x73, 0x6D, 0x2E, 0x76, 0x32, 250 | 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x64, 0x73, 0x69, 0x67, 0x3A, 251 | 0x54, 0x72, 0x61, 0x6E, 0x73, 0x66, 0x6F, 0x72, 0x6D, 0x73, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 252 | 0x3A, 0x64, 0x73, 0x69, 0x67, 0x3D, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 253 | 0x77, 0x2E, 0x77, 0x33, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x32, 0x30, 0x30, 0x30, 0x2F, 0x30, 0x39, 254 | 0x2F, 0x78, 0x6D, 0x6C, 0x64, 0x73, 0x69, 0x67, 0x23, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 255 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x64, 0x73, 0x69, 0x67, 0x3A, 0x54, 0x72, 0x61, 0x6E, 0x73, 256 | 0x66, 0x6F, 0x72, 0x6D, 0x20, 0x41, 0x6C, 0x67, 0x6F, 0x72, 0x69, 0x74, 0x68, 0x6D, 0x3D, 0x22, 257 | 0x75, 0x72, 0x6E, 0x3A, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2D, 0x6D, 0x69, 0x63, 0x72, 258 | 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2D, 0x63, 0x6F, 0x6D, 0x3A, 0x48, 0x61, 0x73, 0x68, 0x54, 0x72, 259 | 0x61, 0x6E, 0x73, 0x66, 0x6F, 0x72, 0x6D, 0x73, 0x2E, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 260 | 0x79, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x64, 261 | 0x73, 0x69, 0x67, 0x3A, 0x54, 0x72, 0x61, 0x6E, 0x73, 0x66, 0x6F, 0x72, 0x6D, 0x73, 0x3E, 0x0D, 262 | 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x64, 0x73, 0x69, 0x67, 0x3A, 0x44, 0x69, 0x67, 263 | 0x65, 0x73, 0x74, 0x4D, 0x65, 0x74, 0x68, 0x6F, 0x64, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 264 | 0x64, 0x73, 0x69, 0x67, 0x3D, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 265 | 0x2E, 0x77, 0x33, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x32, 0x30, 0x30, 0x30, 0x2F, 0x30, 0x39, 0x2F, 266 | 0x78, 0x6D, 0x6C, 0x64, 0x73, 0x69, 0x67, 0x23, 0x22, 0x20, 0x41, 0x6C, 0x67, 0x6F, 0x72, 0x69, 267 | 0x74, 0x68, 0x6D, 0x3D, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 268 | 0x77, 0x33, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x32, 0x30, 0x30, 0x30, 0x2F, 0x30, 0x39, 0x2F, 0x78, 269 | 0x6D, 0x6C, 0x64, 0x73, 0x69, 0x67, 0x23, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x22, 0x20, 0x2F, 270 | 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x64, 0x73, 0x69, 0x67, 0x3A, 0x44, 271 | 0x69, 0x67, 0x65, 0x73, 0x74, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 272 | 0x3A, 0x64, 0x73, 0x69, 0x67, 0x3D, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 273 | 0x77, 0x2E, 0x77, 0x33, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x32, 0x30, 0x30, 0x30, 0x2F, 0x30, 0x39, 274 | 0x2F, 0x78, 0x6D, 0x6C, 0x64, 0x73, 0x69, 0x67, 0x23, 0x22, 0x3E, 0x3C, 0x2F, 0x64, 0x73, 0x69, 275 | 0x67, 0x3A, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3E, 0x0D, 0x0A, 276 | 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x61, 0x73, 0x6D, 0x76, 0x32, 0x3A, 0x68, 0x61, 0x73, 0x68, 277 | 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x2F, 0x66, 0x69, 0x6C, 0x65, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 278 | 0x3C, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x69, 0x65, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 279 | 0x20, 0x20, 0x20, 0x3C, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x79, 0x20, 0x64, 0x65, 280 | 0x73, 0x74, 0x69, 0x6E, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x50, 0x61, 0x74, 0x68, 0x3D, 0x22, 0x24, 281 | 0x28, 0x72, 0x75, 0x6E, 0x74, 0x69, 0x6D, 0x65, 0x2E, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x33, 282 | 0x32, 0x29, 0x5C, 0x22, 0x20, 0x6F, 0x77, 0x6E, 0x65, 0x72, 0x3D, 0x22, 0x74, 0x72, 0x75, 0x65, 283 | 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 0x65, 0x63, 0x75, 0x72, 284 | 0x69, 0x74, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6F, 0x72, 0x20, 0x6E, 0x61, 285 | 0x6D, 0x65, 0x3D, 0x22, 0x57, 0x52, 0x50, 0x5F, 0x44, 0x49, 0x52, 0x5F, 0x44, 0x45, 0x46, 0x41, 286 | 0x55, 0x4C, 0x54, 0x5F, 0x53, 0x44, 0x44, 0x4C, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 287 | 0x20, 0x20, 0x3C, 0x2F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x79, 0x3E, 0x0D, 0x0A, 288 | 0x20, 0x20, 0x20, 0x20, 0x3C, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x79, 0x20, 0x64, 289 | 0x65, 0x73, 0x74, 0x69, 0x6E, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x50, 0x61, 0x74, 0x68, 0x3D, 0x22, 290 | 0x24, 0x28, 0x72, 0x75, 0x6E, 0x74, 0x69, 0x6D, 0x65, 0x2E, 0x77, 0x69, 0x6E, 0x64, 0x6F, 0x77, 291 | 0x73, 0x29, 0x5C, 0x22, 0x20, 0x6F, 0x77, 0x6E, 0x65, 0x72, 0x3D, 0x22, 0x74, 0x72, 0x75, 0x65, 292 | 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x2F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 293 | 0x6F, 0x72, 0x69, 0x65, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x72, 0x65, 0x73, 0x63, 0x61, 294 | 0x63, 0x68, 0x65, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3D, 0x22, 0x75, 0x72, 0x6E, 0x3A, 0x73, 295 | 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2D, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 296 | 0x2D, 0x63, 0x6F, 0x6D, 0x3A, 0x72, 0x65, 0x73, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2E, 0x76, 0x31, 297 | 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x6D, 0x65, 0x6D, 0x62, 0x65, 0x72, 0x73, 298 | 0x68, 0x69, 0x70, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x63, 0x61, 0x74, 0x65, 299 | 0x67, 0x6F, 0x72, 0x79, 0x4D, 0x65, 0x6D, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x3E, 0x0D, 300 | 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x69, 0x64, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 301 | 0x22, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2E, 0x57, 0x69, 0x6E, 0x64, 0x6F, 302 | 0x77, 0x73, 0x2E, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6F, 0x72, 0x69, 0x65, 0x73, 0x22, 0x20, 0x76, 303 | 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x22, 0x31, 0x2E, 0x30, 0x2E, 0x30, 0x2E, 0x30, 0x22, 304 | 0x20, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x4B, 0x65, 0x79, 0x54, 0x6F, 0x6B, 0x65, 0x6E, 0x3D, 305 | 0x22, 0x33, 0x36, 0x35, 0x31, 0x34, 0x33, 0x62, 0x62, 0x32, 0x37, 0x65, 0x37, 0x61, 0x63, 0x38, 306 | 0x62, 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x4E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x53, 0x69, 0x6E, 307 | 0x67, 0x6C, 0x65, 0x49, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x46, 0x69, 0x6C, 0x65, 0x73, 308 | 0x42, 0x6F, 0x6F, 0x74, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6C, 0x22, 0x20, 0x2F, 0x3E, 309 | 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x69, 0x64, 0x20, 0x6E, 0x61, 0x6D, 0x65, 310 | 0x3D, 0x22, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2E, 0x57, 0x69, 0x6E, 0x64, 311 | 0x6F, 0x77, 0x73, 0x2E, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6F, 0x72, 0x69, 0x65, 0x73, 0x2E, 0x53, 312 | 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 313 | 0x3D, 0x22, 0x36, 0x2E, 0x22, 0x20, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x4B, 0x65, 0x79, 0x54, 314 | 0x6F, 0x6B, 0x65, 0x6E, 0x3D, 0x22, 0x33, 0x31, 0x62, 0x66, 0x33, 0x38, 0x35, 0x36, 0x61, 0x64, 315 | 0x33, 0x36, 0x34, 0x65, 0x33, 0x35, 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x4E, 0x61, 0x6D, 0x65, 316 | 0x3D, 0x22, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 317 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6F, 0x72, 0x79, 0x49, 0x6E, 318 | 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x20, 0x73, 0x75, 0x62, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6F, 319 | 0x72, 0x79, 0x3D, 0x22, 0x48, 0x54, 0x54, 0x50, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 320 | 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 321 | 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x48, 0x54, 0x54, 0x50, 0x22, 0x20, 0x64, 0x69, 0x73, 322 | 0x70, 0x6C, 0x61, 0x79, 0x4E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x40, 0x25, 0x53, 0x79, 0x73, 0x74, 323 | 0x65, 0x6D, 0x52, 0x6F, 0x6F, 0x74, 0x25, 0x5C, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x33, 0x32, 324 | 0x5C, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x73, 0x5C, 0x2E, 0x73, 0x79, 0x73, 0x2C, 0x2D, 0x31, 325 | 0x22, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x43, 0x6F, 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x3D, 0x22, 326 | 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x22, 0x20, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x50, 0x61, 0x74, 327 | 0x68, 0x3D, 0x22, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x33, 0x32, 0x5C, 0x64, 0x72, 0x69, 0x76, 328 | 0x65, 0x72, 0x73, 0x5C, 0x2E, 0x73, 0x79, 0x73, 0x22, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x3D, 329 | 0x22, 0x64, 0x65, 0x6D, 0x61, 0x6E, 0x64, 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x6B, 330 | 0x65, 0x72, 0x6E, 0x65, 0x6C, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x22, 0x20, 0x64, 0x65, 0x73, 331 | 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x3D, 0x22, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 332 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 333 | 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6F, 0x72, 0x20, 0x6E, 0x61, 0x6D, 0x65, 334 | 0x3D, 0x22, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 335 | 0x3C, 0x2F, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x3E, 0x0D, 0x0A, 336 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6F, 0x72, 0x79, 337 | 0x49, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 338 | 0x2F, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6F, 0x72, 0x79, 0x4D, 0x65, 0x6D, 0x62, 0x65, 0x72, 0x73, 339 | 0x68, 0x69, 0x70, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x2F, 0x6D, 0x65, 0x6D, 0x62, 0x65, 0x72, 340 | 0x73, 0x68, 0x69, 0x70, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x72, 0x65, 0x67, 0x69, 0x73, 341 | 0x74, 0x72, 0x79, 0x4B, 0x65, 0x79, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x72, 342 | 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4B, 0x65, 0x79, 0x20, 0x6B, 0x65, 0x79, 0x4E, 0x61, 343 | 0x6D, 0x65, 0x3D, 0x22, 0x48, 0x4B, 0x45, 0x59, 0x5F, 0x43, 0x4C, 0x41, 0x53, 0x53, 0x45, 0x53, 344 | 0x5F, 0x52, 0x4F, 0x4F, 0x54, 0x5C, 0x43, 0x4C, 0x53, 0x49, 0x44, 0x22, 0x20, 0x6F, 0x77, 0x6E, 345 | 0x65, 0x72, 0x3D, 0x22, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 346 | 0x20, 0x20, 0x20, 0x3C, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x56, 0x61, 0x6C, 0x75, 347 | 0x65, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x54, 348 | 0x79, 0x70, 0x65, 0x3D, 0x22, 0x52, 0x45, 0x47, 0x5F, 0x53, 0x5A, 0x22, 0x20, 0x76, 0x61, 0x6C, 349 | 0x75, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x6F, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 350 | 0x69, 0x6E, 0x74, 0x3D, 0x22, 0x72, 0x65, 0x70, 0x6C, 0x61, 0x63, 0x65, 0x22, 0x20, 0x6F, 0x77, 351 | 0x6E, 0x65, 0x72, 0x3D, 0x22, 0x74, 0x72, 0x75, 0x65, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 352 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 353 | 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6F, 0x72, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x57, 354 | 0x52, 0x50, 0x5F, 0x52, 0x45, 0x47, 0x4B, 0x45, 0x59, 0x5F, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4C, 355 | 0x54, 0x5F, 0x53, 0x44, 0x44, 0x4C, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 356 | 0x3C, 0x2F, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4B, 0x65, 0x79, 0x3E, 0x0D, 0x0A, 357 | 0x20, 0x20, 0x3C, 0x2F, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4B, 0x65, 0x79, 0x73, 358 | 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x74, 0x72, 0x75, 0x73, 0x74, 0x49, 0x6E, 0x66, 0x6F, 0x3E, 359 | 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x3E, 360 | 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 361 | 0x6F, 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 362 | 0x20, 0x3C, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 363 | 0x70, 0x74, 0x6F, 0x72, 0x44, 0x65, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x3E, 364 | 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 0x65, 0x63, 365 | 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6F, 0x72, 0x44, 366 | 0x65, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 367 | 0x57, 0x52, 0x50, 0x5F, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x4F, 0x52, 0x59, 0x5F, 0x44, 0x45, 368 | 0x46, 0x41, 0x55, 0x4C, 0x54, 0x5F, 0x53, 0x44, 0x44, 0x4C, 0x22, 0x20, 0x73, 0x64, 0x64, 0x6C, 369 | 0x3D, 0x22, 0x4F, 0x3A, 0x53, 0x2D, 0x31, 0x2D, 0x35, 0x2D, 0x38, 0x30, 0x2D, 0x39, 0x35, 0x36, 370 | 0x30, 0x30, 0x38, 0x38, 0x38, 0x35, 0x2D, 0x33, 0x34, 0x31, 0x38, 0x35, 0x32, 0x32, 0x36, 0x34, 371 | 0x39, 0x2D, 0x31, 0x38, 0x33, 0x31, 0x30, 0x33, 0x38, 0x30, 0x34, 0x34, 0x2D, 0x31, 0x38, 0x35, 372 | 0x33, 0x32, 0x39, 0x32, 0x36, 0x33, 0x31, 0x2D, 0x32, 0x32, 0x37, 0x31, 0x34, 0x37, 0x38, 0x34, 373 | 0x36, 0x34, 0x47, 0x3A, 0x53, 0x2D, 0x31, 0x2D, 0x35, 0x2D, 0x38, 0x30, 0x2D, 0x39, 0x35, 0x36, 374 | 0x30, 0x30, 0x38, 0x38, 0x38, 0x35, 0x2D, 0x33, 0x34, 0x31, 0x38, 0x35, 0x32, 0x32, 0x36, 0x34, 375 | 0x39, 0x2D, 0x31, 0x38, 0x33, 0x31, 0x30, 0x33, 0x38, 0x30, 0x34, 0x34, 0x2D, 0x31, 0x38, 0x35, 376 | 0x33, 0x32, 0x39, 0x32, 0x36, 0x33, 0x31, 0x2D, 0x32, 0x32, 0x37, 0x31, 0x34, 0x37, 0x38, 0x34, 377 | 0x36, 0x34, 0x44, 0x3A, 0x50, 0x28, 0x41, 0x3B, 0x43, 0x49, 0x3B, 0x47, 0x41, 0x3B, 0x3B, 0x3B, 378 | 0x53, 0x2D, 0x31, 0x2D, 0x35, 0x2D, 0x38, 0x30, 0x2D, 0x39, 0x35, 0x36, 0x30, 0x30, 0x38, 0x38, 379 | 0x38, 0x35, 0x2D, 0x33, 0x34, 0x31, 0x38, 0x35, 0x32, 0x32, 0x36, 0x34, 0x39, 0x2D, 0x31, 0x38, 380 | 0x33, 0x31, 0x30, 0x33, 0x38, 0x30, 0x34, 0x34, 0x2D, 0x31, 0x38, 0x35, 0x33, 0x32, 0x39, 0x32, 381 | 0x36, 0x33, 0x31, 0x2D, 0x32, 0x32, 0x37, 0x31, 0x34, 0x37, 0x38, 0x34, 0x36, 0x34, 0x29, 0x28, 382 | 0x41, 0x3B, 0x3B, 0x30, 0x78, 0x31, 0x33, 0x30, 0x31, 0x62, 0x66, 0x3B, 0x3B, 0x3B, 0x53, 0x59, 383 | 0x29, 0x28, 0x41, 0x3B, 0x49, 0x4F, 0x43, 0x49, 0x4F, 0x49, 0x3B, 0x47, 0x41, 0x3B, 0x3B, 0x3B, 384 | 0x53, 0x59, 0x29, 0x28, 0x41, 0x3B, 0x3B, 0x30, 0x78, 0x31, 0x33, 0x30, 0x31, 0x62, 0x66, 0x3B, 385 | 0x3B, 0x3B, 0x42, 0x41, 0x29, 0x28, 0x41, 0x3B, 0x49, 0x4F, 0x43, 0x49, 0x4F, 0x49, 0x3B, 0x47, 386 | 0x41, 0x3B, 0x3B, 0x3B, 0x42, 0x41, 0x29, 0x28, 0x41, 0x3B, 0x43, 0x49, 0x4F, 0x49, 0x3B, 0x47, 387 | 0x52, 0x47, 0x58, 0x3B, 0x3B, 0x3B, 0x42, 0x55, 0x29, 0x28, 0x41, 0x3B, 0x4F, 0x49, 0x43, 0x49, 388 | 0x49, 0x4F, 0x3B, 0x47, 0x41, 0x3B, 0x3B, 0x3B, 0x43, 0x4F, 0x29, 0x28, 0x41, 0x3B, 0x43, 0x49, 389 | 0x4F, 0x49, 0x3B, 0x47, 0x52, 0x47, 0x58, 0x3B, 0x3B, 0x3B, 0x53, 0x2D, 0x31, 0x2D, 0x31, 0x35, 390 | 0x2D, 0x32, 0x2D, 0x31, 0x29, 0x22, 0x20, 0x6F, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 391 | 0x48, 0x69, 0x6E, 0x74, 0x3D, 0x22, 0x72, 0x65, 0x70, 0x6C, 0x61, 0x63, 0x65, 0x22, 0x20, 0x2F, 392 | 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 0x65, 393 | 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6F, 0x72, 394 | 0x44, 0x65, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 395 | 0x22, 0x57, 0x52, 0x50, 0x5F, 0x46, 0x49, 0x4C, 0x45, 0x5F, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4C, 396 | 0x54, 0x5F, 0x53, 0x44, 0x44, 0x4C, 0x22, 0x20, 0x73, 0x64, 0x64, 0x6C, 0x3D, 0x22, 0x4F, 0x3A, 397 | 0x53, 0x2D, 0x31, 0x2D, 0x35, 0x2D, 0x38, 0x30, 0x2D, 0x39, 0x35, 0x36, 0x30, 0x30, 0x38, 0x38, 398 | 0x38, 0x35, 0x2D, 0x33, 0x34, 0x31, 0x38, 0x35, 0x32, 0x32, 0x36, 0x34, 0x39, 0x2D, 0x31, 0x38, 399 | 0x33, 0x31, 0x30, 0x33, 0x38, 0x30, 0x34, 0x34, 0x2D, 0x31, 0x38, 0x35, 0x33, 0x32, 0x39, 0x32, 400 | 0x36, 0x33, 0x31, 0x2D, 0x32, 0x32, 0x37, 0x31, 0x34, 0x37, 0x38, 0x34, 0x36, 0x34, 0x47, 0x3A, 401 | 0x53, 0x2D, 0x31, 0x2D, 0x35, 0x2D, 0x38, 0x30, 0x2D, 0x39, 0x35, 0x36, 0x30, 0x30, 0x38, 0x38, 402 | 0x38, 0x35, 0x2D, 0x33, 0x34, 0x31, 0x38, 0x35, 0x32, 0x32, 0x36, 0x34, 0x39, 0x2D, 0x31, 0x38, 403 | 0x33, 0x31, 0x30, 0x33, 0x38, 0x30, 0x34, 0x34, 0x2D, 0x31, 0x38, 0x35, 0x33, 0x32, 0x39, 0x32, 404 | 0x36, 0x33, 0x31, 0x2D, 0x32, 0x32, 0x37, 0x31, 0x34, 0x37, 0x38, 0x34, 0x36, 0x34, 0x44, 0x3A, 405 | 0x50, 0x28, 0x41, 0x3B, 0x3B, 0x46, 0x41, 0x3B, 0x3B, 0x3B, 0x53, 0x2D, 0x31, 0x2D, 0x35, 0x2D, 406 | 0x38, 0x30, 0x2D, 0x39, 0x35, 0x36, 0x30, 0x30, 0x38, 0x38, 0x38, 0x35, 0x2D, 0x33, 0x34, 0x31, 407 | 0x38, 0x35, 0x32, 0x32, 0x36, 0x34, 0x39, 0x2D, 0x31, 0x38, 0x33, 0x31, 0x30, 0x33, 0x38, 0x30, 408 | 0x34, 0x34, 0x2D, 0x31, 0x38, 0x35, 0x33, 0x32, 0x39, 0x32, 0x36, 0x33, 0x31, 0x2D, 0x32, 0x32, 409 | 0x37, 0x31, 0x34, 0x37, 0x38, 0x34, 0x36, 0x34, 0x29, 0x28, 0x41, 0x3B, 0x3B, 0x47, 0x52, 0x47, 410 | 0x58, 0x3B, 0x3B, 0x3B, 0x42, 0x41, 0x29, 0x28, 0x41, 0x3B, 0x3B, 0x47, 0x52, 0x47, 0x58, 0x3B, 411 | 0x3B, 0x3B, 0x53, 0x59, 0x29, 0x28, 0x41, 0x3B, 0x3B, 0x47, 0x52, 0x47, 0x58, 0x3B, 0x3B, 0x3B, 412 | 0x42, 0x55, 0x29, 0x28, 0x41, 0x3B, 0x3B, 0x47, 0x52, 0x47, 0x58, 0x3B, 0x3B, 0x3B, 0x53, 0x2D, 413 | 0x31, 0x2D, 0x31, 0x35, 0x2D, 0x32, 0x2D, 0x31, 0x29, 0x53, 0x3A, 0x28, 0x41, 0x55, 0x3B, 0x46, 414 | 0x41, 0x53, 0x41, 0x3B, 0x30, 0x78, 0x30, 0x30, 0x30, 0x44, 0x30, 0x31, 0x31, 0x36, 0x3B, 0x3B, 415 | 0x3B, 0x57, 0x44, 0x29, 0x22, 0x20, 0x6F, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 416 | 0x69, 0x6E, 0x74, 0x3D, 0x22, 0x72, 0x65, 0x70, 0x6C, 0x61, 0x63, 0x65, 0x22, 0x20, 0x2F, 0x3E, 417 | 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 0x65, 0x63, 418 | 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6F, 0x72, 0x44, 419 | 0x65, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 420 | 0x57, 0x52, 0x50, 0x5F, 0x52, 0x45, 0x47, 0x4B, 0x45, 0x59, 0x5F, 0x44, 0x45, 0x46, 0x41, 0x55, 421 | 0x4C, 0x54, 0x5F, 0x53, 0x44, 0x44, 0x4C, 0x22, 0x20, 0x73, 0x64, 0x64, 0x6C, 0x3D, 0x22, 0x4F, 422 | 0x3A, 0x53, 0x2D, 0x31, 0x2D, 0x35, 0x2D, 0x38, 0x30, 0x2D, 0x39, 0x35, 0x36, 0x30, 0x30, 0x38, 423 | 0x38, 0x38, 0x35, 0x2D, 0x33, 0x34, 0x31, 0x38, 0x35, 0x32, 0x32, 0x36, 0x34, 0x39, 0x2D, 0x31, 424 | 0x38, 0x33, 0x31, 0x30, 0x33, 0x38, 0x30, 0x34, 0x34, 0x2D, 0x31, 0x38, 0x35, 0x33, 0x32, 0x39, 425 | 0x32, 0x36, 0x33, 0x31, 0x2D, 0x32, 0x32, 0x37, 0x31, 0x34, 0x37, 0x38, 0x34, 0x36, 0x34, 0x47, 426 | 0x3A, 0x53, 0x2D, 0x31, 0x2D, 0x35, 0x2D, 0x38, 0x30, 0x2D, 0x39, 0x35, 0x36, 0x30, 0x30, 0x38, 427 | 0x38, 0x38, 0x35, 0x2D, 0x33, 0x34, 0x31, 0x38, 0x35, 0x32, 0x32, 0x36, 0x34, 0x39, 0x2D, 0x31, 428 | 0x38, 0x33, 0x31, 0x30, 0x33, 0x38, 0x30, 0x34, 0x34, 0x2D, 0x31, 0x38, 0x35, 0x33, 0x32, 0x39, 429 | 0x32, 0x36, 0x33, 0x31, 0x2D, 0x32, 0x32, 0x37, 0x31, 0x34, 0x37, 0x38, 0x34, 0x36, 0x34, 0x44, 430 | 0x3A, 0x50, 0x28, 0x41, 0x3B, 0x43, 0x49, 0x3B, 0x47, 0x41, 0x3B, 0x3B, 0x3B, 0x53, 0x2D, 0x31, 431 | 0x2D, 0x35, 0x2D, 0x38, 0x30, 0x2D, 0x39, 0x35, 0x36, 0x30, 0x30, 0x38, 0x38, 0x38, 0x35, 0x2D, 432 | 0x33, 0x34, 0x31, 0x38, 0x35, 0x32, 0x32, 0x36, 0x34, 0x39, 0x2D, 0x31, 0x38, 0x33, 0x31, 0x30, 433 | 0x33, 0x38, 0x30, 0x34, 0x34, 0x2D, 0x31, 0x38, 0x35, 0x33, 0x32, 0x39, 0x32, 0x36, 0x33, 0x31, 434 | 0x2D, 0x32, 0x32, 0x37, 0x31, 0x34, 0x37, 0x38, 0x34, 0x36, 0x34, 0x29, 0x28, 0x41, 0x3B, 0x43, 435 | 0x49, 0x3B, 0x47, 0x52, 0x3B, 0x3B, 0x3B, 0x53, 0x59, 0x29, 0x28, 0x41, 0x3B, 0x43, 0x49, 0x3B, 436 | 0x47, 0x52, 0x3B, 0x3B, 0x3B, 0x42, 0x41, 0x29, 0x28, 0x41, 0x3B, 0x43, 0x49, 0x3B, 0x47, 0x52, 437 | 0x3B, 0x3B, 0x3B, 0x42, 0x55, 0x29, 0x28, 0x41, 0x3B, 0x43, 0x49, 0x3B, 0x47, 0x52, 0x3B, 0x3B, 438 | 0x3B, 0x53, 0x2D, 0x31, 0x2D, 0x31, 0x35, 0x2D, 0x32, 0x2D, 0x31, 0x29, 0x22, 0x20, 0x6F, 0x70, 439 | 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x69, 0x6E, 0x74, 0x3D, 0x22, 0x72, 0x65, 0x70, 440 | 0x6C, 0x61, 0x63, 0x65, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 441 | 0x20, 0x20, 0x3C, 0x2F, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x73, 0x63, 442 | 0x72, 0x69, 0x70, 0x74, 0x6F, 0x72, 0x44, 0x65, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x6F, 0x6E, 443 | 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x61, 0x63, 0x63, 0x65, 444 | 0x73, 0x73, 0x43, 0x6F, 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 445 | 0x3C, 0x2F, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 446 | 0x2F, 0x74, 0x72, 0x75, 0x73, 0x74, 0x49, 0x6E, 0x66, 0x6F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 447 | 0x6C, 0x6F, 0x63, 0x61, 0x6C, 0x69, 0x7A, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3E, 0x0D, 0x0A, 0x20, 448 | 0x20, 0x20, 0x20, 0x3C, 0x72, 0x65, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x63, 0x75, 449 | 0x6C, 0x74, 0x75, 0x72, 0x65, 0x3D, 0x22, 0x65, 0x6E, 0x2D, 0x55, 0x53, 0x22, 0x3E, 0x0D, 0x0A, 450 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x54, 0x61, 0x62, 451 | 0x6C, 0x65, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 0x74, 452 | 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x64, 0x3D, 0x22, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 453 | 0x74, 0x69, 0x6F, 0x6E, 0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x22, 0x24, 0x28, 0x72, 454 | 0x75, 0x6E, 0x74, 0x69, 0x6D, 0x65, 0x2E, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x33, 0x32, 0x29, 455 | 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 456 | 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x64, 0x3D, 0x22, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 457 | 0x79, 0x4E, 0x61, 0x6D, 0x65, 0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x22, 0x22, 0x20, 458 | 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x73, 0x74, 0x72, 0x69, 459 | 0x6E, 0x67, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 460 | 0x72, 0x65, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x2F, 461 | 0x6C, 0x6F, 0x63, 0x61, 0x6C, 0x69, 0x7A, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3E, 0x0D, 0x0A, 0x20, 462 | 0x20, 0x3C, 0x64, 0x65, 0x70, 0x6C, 0x6F, 0x79, 0x6D, 0x65, 0x6E, 0x74, 0x20, 0x78, 0x6D, 0x6C, 463 | 0x6E, 0x73, 0x3D, 0x22, 0x75, 0x72, 0x6E, 0x3A, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2D, 464 | 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2D, 0x63, 0x6F, 0x6D, 0x3A, 0x61, 0x73, 465 | 0x6D, 0x2E, 0x76, 0x33, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x66, 0x65, 0x61, 466 | 0x74, 0x75, 0x72, 0x65, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3D, 0x22, 0x75, 0x72, 0x6E, 0x3A, 467 | 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2D, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 468 | 0x74, 0x2D, 0x63, 0x6F, 0x6D, 0x3A, 0x61, 0x73, 0x6D, 0x2E, 0x76, 0x33, 0x22, 0x20, 0x2F, 0x3E, 469 | 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x69, 0x6E, 0x73, 0x74, 0x72, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x61, 470 | 0x74, 0x69, 0x6F, 0x6E, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x65, 0x76, 0x65, 0x6E, 471 | 0x74, 0x73, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3D, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 472 | 0x2F, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2E, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 473 | 0x66, 0x74, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x77, 0x69, 0x6E, 0x2F, 0x32, 0x30, 0x30, 0x34, 0x2F, 474 | 0x30, 0x38, 0x2F, 0x65, 0x76, 0x65, 0x6E, 0x74, 0x73, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 475 | 0x20, 0x20, 0x20, 0x3C, 0x70, 0x72, 0x6F, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x67, 0x75, 0x69, 476 | 0x64, 0x3D, 0x22, 0x7B, 0x7D, 0x22, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x46, 0x69, 477 | 0x6C, 0x65, 0x4E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x25, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x52, 478 | 0x6F, 0x6F, 0x74, 0x25, 0x5C, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x33, 0x32, 0x22, 0x20, 0x6E, 479 | 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x72, 0x65, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x46, 480 | 0x69, 0x6C, 0x65, 0x4E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x73, 0x79, 0x6D, 0x62, 0x6F, 481 | 0x6C, 0x3D, 0x22, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 482 | 0x6F, 0x70, 0x63, 0x6F, 0x64, 0x65, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 483 | 0x20, 0x20, 0x20, 0x20, 0x3C, 0x6F, 0x70, 0x63, 0x6F, 0x64, 0x65, 0x20, 0x6D, 0x65, 0x73, 0x73, 484 | 0x61, 0x67, 0x65, 0x3D, 0x22, 0x24, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x2E, 0x48, 0x54, 485 | 0x54, 0x50, 0x5F, 0x4F, 0x50, 0x43, 0x4F, 0x44, 0x45, 0x5F, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 486 | 0x45, 0x5F, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x2E, 0x4F, 0x70, 0x63, 0x6F, 0x64, 0x65, 487 | 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x29, 0x22, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 488 | 0x48, 0x54, 0x54, 0x50, 0x5F, 0x4F, 0x50, 0x43, 0x4F, 0x44, 0x45, 0x5F, 0x52, 0x45, 0x43, 0x45, 489 | 0x49, 0x56, 0x45, 0x5F, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x22, 0x20, 0x73, 0x79, 0x6D, 490 | 0x62, 0x6F, 0x6C, 0x3D, 0x22, 0x48, 0x54, 0x54, 0x50, 0x5F, 0x4F, 0x50, 0x43, 0x4F, 0x44, 0x45, 491 | 0x5F, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x5F, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 492 | 0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x22, 0x31, 0x31, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 493 | 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x6F, 0x70, 0x63, 0x6F, 0x64, 494 | 0x65, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x74, 0x61, 495 | 0x73, 0x6B, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 496 | 0x3C, 0x74, 0x61, 0x73, 0x6B, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x3D, 0x22, 0x24, 497 | 0x28, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x2E, 0x48, 0x54, 0x54, 0x50, 0x5F, 0x54, 0x41, 0x53, 498 | 0x4B, 0x5F, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x2E, 0x54, 0x61, 0x73, 0x6B, 0x4D, 0x65, 499 | 0x73, 0x73, 0x61, 0x67, 0x65, 0x29, 0x22, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x48, 0x54, 500 | 0x54, 0x50, 0x5F, 0x54, 0x41, 0x53, 0x4B, 0x5F, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x22, 501 | 0x20, 0x73, 0x79, 0x6D, 0x62, 0x6F, 0x6C, 0x3D, 0x22, 0x48, 0x54, 0x54, 0x50, 0x5F, 0x54, 0x41, 502 | 0x53, 0x4B, 0x5F, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 503 | 0x65, 0x3D, 0x22, 0x31, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 504 | 0x20, 0x20, 0x3C, 0x2F, 0x74, 0x61, 0x73, 0x6B, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 505 | 0x20, 0x20, 0x20, 0x20, 0x3C, 0x63, 0x68, 0x61, 0x6E, 0x6E, 0x65, 0x6C, 0x73, 0x3E, 0x0D, 0x0A, 506 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x63, 0x68, 0x61, 0x6E, 0x6E, 507 | 0x65, 0x6C, 0x20, 0x63, 0x68, 0x69, 0x64, 0x3D, 0x22, 0x22, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 508 | 0x67, 0x65, 0x3D, 0x22, 0x24, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x2E, 0x29, 0x22, 0x20, 509 | 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x73, 0x79, 0x6D, 0x62, 0x6F, 0x6C, 0x3D, 0x22, 510 | 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x4F, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 511 | 0x6E, 0x61, 0x6C, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 512 | 0x20, 0x3C, 0x2F, 0x63, 0x68, 0x61, 0x6E, 0x6E, 0x65, 0x6C, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 513 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x6B, 0x65, 0x79, 0x77, 0x6F, 0x72, 0x64, 0x73, 0x3E, 514 | 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x6B, 0x65, 0x79, 515 | 0x77, 0x6F, 0x72, 0x64, 0x20, 0x6D, 0x61, 0x73, 0x6B, 0x3D, 0x22, 0x30, 0x78, 0x22, 0x20, 0x6D, 516 | 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x3D, 0x22, 0x24, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 517 | 0x2E, 0x29, 0x22, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x73, 0x79, 0x6D, 0x62, 518 | 0x6F, 0x6C, 0x3D, 0x22, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 519 | 0x20, 0x20, 0x3C, 0x2F, 0x6B, 0x65, 0x79, 0x77, 0x6F, 0x72, 0x64, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 520 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x6D, 0x61, 0x70, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 521 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x4D, 522 | 0x61, 0x70, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x4D, 0x61, 523 | 0x70, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 524 | 0x20, 0x3C, 0x6D, 0x61, 0x70, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x3D, 0x22, 0x24, 525 | 0x28, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x2E, 0x2E, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 526 | 0x29, 0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x22, 0x30, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 527 | 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x76, 0x61, 0x6C, 528 | 0x75, 0x65, 0x4D, 0x61, 0x70, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 529 | 0x3C, 0x2F, 0x6D, 0x61, 0x70, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 530 | 0x20, 0x3C, 0x74, 0x65, 0x6D, 0x70, 0x6C, 0x61, 0x74, 0x65, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 531 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x74, 0x65, 0x6D, 0x70, 0x6C, 0x61, 0x74, 532 | 0x65, 0x20, 0x74, 0x69, 0x64, 0x3D, 0x22, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 533 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x64, 0x61, 0x74, 0x61, 0x20, 0x69, 0x6E, 0x54, 534 | 0x79, 0x70, 0x65, 0x3D, 0x22, 0x77, 0x69, 0x6E, 0x3A, 0x55, 0x49, 0x6E, 0x74, 0x33, 0x32, 0x22, 535 | 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x6F, 0x75, 0x74, 0x54, 0x79, 0x70, 0x65, 536 | 0x3D, 0x22, 0x78, 0x73, 0x3A, 0x75, 0x6E, 0x73, 0x69, 0x67, 0x6E, 0x65, 0x64, 0x49, 0x6E, 0x74, 537 | 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 538 | 0x20, 0x20, 0x3C, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x3D, 539 | 0x22, 0x22, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 540 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x64, 0x61, 0x74, 0x61, 541 | 0x20, 0x69, 0x6E, 0x54, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x77, 0x69, 0x6E, 0x3A, 0x55, 0x6E, 0x69, 542 | 0x63, 0x6F, 0x64, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x22, 0x20, 0x6E, 0x61, 0x6D, 0x65, 543 | 0x3D, 0x22, 0x22, 0x20, 0x6F, 0x75, 0x74, 0x54, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x78, 0x73, 0x3A, 544 | 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 545 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 546 | 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x74, 547 | 0x65, 0x6D, 0x70, 0x6C, 0x61, 0x74, 0x65, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 548 | 0x20, 0x20, 0x3C, 0x2F, 0x74, 0x65, 0x6D, 0x70, 0x6C, 0x61, 0x74, 0x65, 0x73, 0x3E, 0x0D, 0x0A, 549 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x65, 0x76, 0x65, 0x6E, 0x74, 0x73, 0x3E, 550 | 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x65, 0x76, 0x65, 551 | 0x6E, 0x74, 0x20, 0x63, 0x68, 0x61, 0x6E, 0x6E, 0x65, 0x6C, 0x3D, 0x22, 0x22, 0x20, 0x6B, 0x65, 552 | 0x79, 0x77, 0x6F, 0x72, 0x64, 0x73, 0x3D, 0x22, 0x77, 0x69, 0x6E, 0x3A, 0x45, 0x76, 0x65, 0x6E, 553 | 0x74, 0x6C, 0x6F, 0x67, 0x43, 0x6C, 0x61, 0x73, 0x73, 0x69, 0x63, 0x22, 0x20, 0x6C, 0x65, 0x76, 554 | 0x65, 0x6C, 0x3D, 0x22, 0x77, 0x69, 0x6E, 0x3A, 0x49, 0x6E, 0x66, 0x6F, 0x72, 0x6D, 0x61, 0x74, 555 | 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x22, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x3D, 0x22, 556 | 0x24, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x2E, 0x65, 0x76, 0x65, 0x6E, 0x74, 0x29, 0x22, 557 | 0x20, 0x6F, 0x70, 0x63, 0x6F, 0x64, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x73, 0x79, 0x6D, 0x62, 0x6F, 558 | 0x6C, 0x3D, 0x22, 0x22, 0x20, 0x74, 0x61, 0x73, 0x6B, 0x3D, 0x22, 0x22, 0x20, 0x74, 0x65, 0x6D, 559 | 0x70, 0x6C, 0x61, 0x74, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x22, 560 | 0x22, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x22, 0x30, 0x22, 0x20, 0x2F, 0x3E, 561 | 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x65, 0x76, 0x65, 0x6E, 562 | 0x74, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x70, 0x72, 0x6F, 563 | 0x76, 0x69, 0x64, 0x65, 0x72, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x65, 0x76, 564 | 0x65, 0x6E, 0x74, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x63, 0x6F, 0x75, 0x6E, 565 | 0x74, 0x65, 0x72, 0x73, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3D, 0x22, 0x68, 0x74, 0x74, 0x70, 566 | 0x3A, 0x2F, 0x2F, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2E, 0x6D, 0x69, 0x63, 0x72, 0x6F, 567 | 0x73, 0x6F, 0x66, 0x74, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x77, 0x69, 0x6E, 0x2F, 0x32, 0x30, 0x30, 568 | 0x35, 0x2F, 0x31, 0x32, 0x2F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x65, 0x72, 0x73, 0x22, 0x20, 0x73, 569 | 0x63, 0x68, 0x65, 0x6D, 0x61, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x22, 0x32, 0x2E, 570 | 0x30, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x70, 0x72, 0x6F, 0x76, 571 | 0x69, 0x64, 0x65, 0x72, 0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 572 | 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x3D, 0x22, 0x2E, 0x65, 0x78, 0x65, 0x22, 0x20, 573 | 0x63, 0x61, 0x6C, 0x6C, 0x62, 0x61, 0x63, 0x6B, 0x3D, 0x22, 0x63, 0x75, 0x73, 0x74, 0x6F, 0x6D, 574 | 0x22, 0x20, 0x70, 0x72, 0x6F, 0x76, 0x69, 0x64, 0x65, 0x72, 0x47, 0x75, 0x69, 0x64, 0x3D, 0x22, 575 | 0x7B, 0x7D, 0x22, 0x20, 0x70, 0x72, 0x6F, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4E, 0x61, 0x6D, 0x65, 576 | 0x3D, 0x22, 0x48, 0x76, 0x53, 0x74, 0x61, 0x74, 0x73, 0x22, 0x20, 0x70, 0x72, 0x6F, 0x76, 0x69, 577 | 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x75, 0x73, 0x65, 0x72, 0x4D, 0x6F, 0x64, 578 | 0x65, 0x22, 0x20, 0x73, 0x79, 0x6D, 0x62, 0x6F, 0x6C, 0x3D, 0x22, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 579 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x65, 0x72, 0x53, 580 | 0x65, 0x74, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x3D, 0x22, 581 | 0x22, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x49, 0x44, 0x3D, 582 | 0x22, 0x22, 0x20, 0x67, 0x75, 0x69, 0x64, 0x3D, 0x22, 0x7B, 0x7D, 0x22, 0x20, 0x69, 0x6E, 0x73, 583 | 0x74, 0x61, 0x6E, 0x63, 0x65, 0x73, 0x3D, 0x22, 0x6D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x6C, 0x65, 584 | 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x22, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 585 | 0x22, 0x22, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x49, 0x44, 0x3D, 0x22, 0x22, 0x20, 0x73, 0x79, 0x6D, 586 | 0x62, 0x6F, 0x6C, 0x3D, 0x22, 0x22, 0x20, 0x75, 0x72, 0x69, 0x3D, 0x22, 0x4D, 0x69, 0x63, 0x72, 587 | 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2E, 0x57, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x73, 0x2E, 0x22, 0x3E, 588 | 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x63, 0x6F, 0x75, 589 | 0x6E, 0x74, 0x65, 0x72, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x53, 0x63, 0x61, 0x6C, 590 | 0x65, 0x3D, 0x22, 0x30, 0x22, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 591 | 0x6E, 0x3D, 0x22, 0x22, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 592 | 0x49, 0x44, 0x3D, 0x22, 0x22, 0x20, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6C, 0x4C, 0x65, 0x76, 0x65, 593 | 0x6C, 0x3D, 0x22, 0x73, 0x74, 0x61, 0x6E, 0x64, 0x61, 0x72, 0x64, 0x22, 0x20, 0x69, 0x64, 0x3D, 594 | 0x22, 0x22, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x49, 595 | 0x44, 0x3D, 0x22, 0x22, 0x20, 0x73, 0x79, 0x6D, 0x62, 0x6F, 0x6C, 0x3D, 0x22, 0x22, 0x20, 0x74, 596 | 0x79, 0x70, 0x65, 0x3D, 0x22, 0x70, 0x65, 0x72, 0x66, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x65, 597 | 0x72, 0x5F, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x5F, 0x72, 0x61, 0x77, 0x63, 0x6F, 0x75, 0x6E, 0x74, 598 | 0x22, 0x20, 0x75, 0x72, 0x69, 0x3D, 0x22, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 599 | 0x2E, 0x57, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x73, 0x2E, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 600 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x65, 601 | 0x72, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 602 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x63, 0x6F, 0x75, 603 | 0x6E, 0x74, 0x65, 0x72, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x6E, 0x61, 604 | 0x6D, 0x65, 0x3D, 0x22, 0x6E, 0x6F, 0x44, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x22, 0x20, 0x2F, 605 | 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 606 | 0x2F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x65, 0x72, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 607 | 0x65, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 608 | 0x2F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x65, 0x72, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 609 | 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x65, 0x72, 0x53, 0x65, 0x74, 0x3E, 610 | 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x70, 0x72, 0x6F, 0x76, 0x69, 0x64, 611 | 0x65, 0x72, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 612 | 0x65, 0x72, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x2F, 0x69, 0x6E, 0x73, 0x74, 0x72, 0x75, 613 | 0x6D, 0x65, 0x6E, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x6D, 614 | 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6E, 0x67, 615 | 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x22, 0x30, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 616 | 0x20, 0x20, 0x20, 0x3C, 0x73, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6F, 0x6D, 617 | 0x70, 0x6F, 0x6E, 0x65, 0x6E, 0x74, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 618 | 0x3C, 0x73, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6F, 0x6D, 0x70, 0x6F, 0x6E, 619 | 0x65, 0x6E, 0x74, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x61, 620 | 0x73, 0x73, 0x65, 0x6D, 0x62, 0x6C, 0x79, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x20, 621 | 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x5F, 0x22, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 622 | 0x3D, 0x22, 0x31, 0x2E, 0x30, 0x2E, 0x30, 0x2E, 0x30, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 623 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x73, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x65, 624 | 0x64, 0x43, 0x6F, 0x6D, 0x70, 0x6F, 0x6E, 0x65, 0x6E, 0x74, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 625 | 0x74, 0x79, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3D, 0x22, 0x75, 0x72, 0x6E, 0x3A, 0x73, 0x63, 626 | 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2D, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2D, 627 | 0x63, 0x6F, 0x6D, 0x3A, 0x61, 0x73, 0x6D, 0x2E, 0x76, 0x33, 0x22, 0x20, 0x62, 0x75, 0x69, 0x6C, 628 | 0x64, 0x54, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x72, 0x65, 0x6C, 0x65, 0x61, 0x73, 0x65, 0x22, 0x20, 629 | 0x6C, 0x61, 0x6E, 0x67, 0x75, 0x61, 0x67, 0x65, 0x3D, 0x22, 0x6E, 0x65, 0x75, 0x74, 0x72, 0x61, 630 | 0x6C, 0x22, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 631 | 0x73, 0x73, 0x6F, 0x72, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 632 | 0x3D, 0x22, 0x2A, 0x22, 0x20, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x4B, 0x65, 0x79, 0x54, 0x6F, 633 | 0x6B, 0x65, 0x6E, 0x3D, 0x22, 0x33, 0x31, 0x62, 0x66, 0x33, 0x38, 0x35, 0x36, 0x61, 0x64, 0x33, 634 | 0x36, 0x34, 0x65, 0x33, 0x35, 0x22, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6E, 0x67, 0x73, 0x56, 635 | 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x52, 0x61, 0x6E, 0x67, 0x65, 0x3D, 0x22, 0x30, 0x22, 0x20, 636 | 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x53, 0x63, 0x6F, 0x70, 0x65, 0x3D, 0x22, 0x6E, 0x6F, 637 | 0x6E, 0x53, 0x78, 0x53, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 638 | 0x3C, 0x2F, 0x73, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6F, 0x6D, 0x70, 0x6F, 639 | 0x6E, 0x65, 0x6E, 0x74, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x73, 0x75, 0x70, 640 | 0x70, 0x6F, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6F, 0x6D, 0x70, 0x6F, 0x6E, 0x65, 0x6E, 0x74, 0x73, 641 | 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x6D, 0x61, 0x63, 0x68, 0x69, 0x6E, 0x65, 0x53, 642 | 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3D, 0x22, 0x75, 643 | 0x72, 0x6E, 0x3A, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2D, 0x6D, 0x69, 0x63, 0x72, 0x6F, 644 | 0x73, 0x6F, 0x66, 0x74, 0x2D, 0x63, 0x6F, 0x6D, 0x3A, 0x61, 0x73, 0x6D, 0x2E, 0x76, 0x33, 0x22, 645 | 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x6D, 0x69, 0x67, 0x58, 0x6D, 0x6C, 646 | 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3D, 0x22, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 647 | 0x20, 0x20, 0x20, 0x20, 0x3C, 0x72, 0x75, 0x6C, 0x65, 0x73, 0x20, 0x63, 0x6F, 0x6E, 0x74, 0x65, 648 | 0x78, 0x74, 0x3D, 0x22, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 649 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 650 | 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 651 | 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x53, 0x65, 0x74, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 652 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x70, 0x61, 0x74, 0x74, 0x65, 653 | 0x72, 0x6E, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 654 | 0x79, 0x22, 0x3E, 0x48, 0x4B, 0x4C, 0x4D, 0x5C, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4D, 0x5C, 0x43, 655 | 0x75, 0x72, 0x72, 0x65, 0x6E, 0x74, 0x43, 0x6F, 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x53, 0x65, 0x74, 656 | 0x5C, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x5C, 0x2A, 0x20, 0x5B, 0x2A, 0x5D, 0x3C, 657 | 0x2F, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 658 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x53, 659 | 0x65, 0x74, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 660 | 0x2F, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 661 | 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x72, 0x75, 0x6C, 0x65, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 662 | 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x6D, 0x69, 0x67, 0x58, 0x6D, 0x6C, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 663 | 0x20, 0x20, 0x3C, 0x2F, 0x6D, 0x61, 0x63, 0x68, 0x69, 0x6E, 0x65, 0x53, 0x70, 0x65, 0x63, 0x69, 664 | 0x66, 0x69, 0x63, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x2F, 0x6D, 0x69, 0x67, 0x72, 0x61, 0x74, 665 | 0x69, 0x6F, 0x6E, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x75, 666 | 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3D, 0x22, 0x75, 0x72, 667 | 0x6E, 0x3A, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2D, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 668 | 0x6F, 0x66, 0x74, 0x2D, 0x63, 0x6F, 0x6D, 0x3A, 0x61, 0x73, 0x6D, 0x2E, 0x76, 0x33, 0x22, 0x20, 669 | 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 0x61, 0x73, 0x6D, 0x76, 0x33, 0x3D, 0x22, 0x75, 0x72, 0x6E, 670 | 0x3A, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2D, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 671 | 0x66, 0x74, 0x2D, 0x63, 0x6F, 0x6D, 0x3A, 0x61, 0x73, 0x6D, 0x2E, 0x76, 0x33, 0x22, 0x20, 0x78, 672 | 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 0x77, 0x63, 0x6D, 0x3D, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 673 | 0x2F, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2E, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 674 | 0x66, 0x74, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x57, 0x4D, 0x49, 0x43, 0x6F, 0x6E, 0x66, 0x69, 0x67, 675 | 0x2F, 0x32, 0x30, 0x30, 0x32, 0x2F, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 676 | 0x20, 0x20, 0x20, 0x3C, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6F, 677 | 0x6E, 0x53, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 678 | 0x3C, 0x78, 0x73, 0x64, 0x3A, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 679 | 0x73, 0x3A, 0x78, 0x73, 0x64, 0x3D, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 680 | 0x77, 0x2E, 0x77, 0x33, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x32, 0x30, 0x30, 0x31, 0x2F, 0x58, 0x4D, 681 | 0x4C, 0x53, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x22, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3D, 0x22, 682 | 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2D, 0x57, 0x69, 0x6E, 0x64, 0x6F, 0x77, 683 | 0x73, 0x2D, 0x48, 0x54, 0x54, 0x50, 0x22, 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4E, 0x61, 684 | 0x6D, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3D, 0x22, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 685 | 0x66, 0x74, 0x2D, 0x57, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x73, 0x2D, 0x48, 0x54, 0x54, 0x50, 0x22, 686 | 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x78, 0x73, 0x64, 0x3A, 687 | 0x73, 0x69, 0x6D, 0x70, 0x6C, 0x65, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 688 | 0x22, 0x22, 0x20, 0x77, 0x63, 0x6D, 0x3A, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 689 | 0x6F, 0x6E, 0x3D, 0x22, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 690 | 0x20, 0x20, 0x3C, 0x78, 0x73, 0x64, 0x3A, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x69, 691 | 0x6F, 0x6E, 0x20, 0x62, 0x61, 0x73, 0x65, 0x3D, 0x22, 0x78, 0x73, 0x64, 0x3A, 0x69, 0x6E, 0x74, 692 | 0x65, 0x67, 0x65, 0x72, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 693 | 0x20, 0x20, 0x20, 0x20, 0x3C, 0x78, 0x73, 0x64, 0x3A, 0x6D, 0x61, 0x78, 0x49, 0x6E, 0x63, 0x6C, 694 | 0x75, 0x73, 0x69, 0x76, 0x65, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x2F, 695 | 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 696 | 0x78, 0x73, 0x64, 0x3A, 0x6D, 0x69, 0x6E, 0x49, 0x6E, 0x63, 0x6C, 0x75, 0x73, 0x69, 0x76, 0x65, 697 | 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x22, 0x30, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 698 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x78, 0x73, 0x64, 0x3A, 0x72, 699 | 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 700 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x78, 0x73, 0x64, 0x3A, 0x73, 0x69, 0x6D, 0x70, 0x6C, 701 | 0x65, 0x54, 0x79, 0x70, 0x65, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 702 | 0x3C, 0x78, 0x73, 0x64, 0x3A, 0x63, 0x6F, 0x6D, 0x70, 0x6C, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 703 | 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x77, 0x63, 0x6D, 0x3A, 0x64, 0x65, 0x73, 704 | 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x3D, 0x22, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 705 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x78, 0x73, 0x64, 0x3A, 0x73, 0x65, 0x71, 706 | 0x75, 0x65, 0x6E, 0x63, 0x65, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 707 | 0x20, 0x20, 0x20, 0x20, 0x3C, 0x78, 0x73, 0x64, 0x3A, 0x65, 0x6C, 0x65, 0x6D, 0x65, 0x6E, 0x74, 708 | 0x20, 0x6D, 0x61, 0x78, 0x4F, 0x63, 0x63, 0x75, 0x72, 0x73, 0x3D, 0x22, 0x75, 0x6E, 0x62, 0x6F, 709 | 0x75, 0x6E, 0x64, 0x65, 0x64, 0x22, 0x20, 0x6D, 0x69, 0x6E, 0x4F, 0x63, 0x63, 0x75, 0x72, 0x73, 710 | 0x3D, 0x22, 0x30, 0x22, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x74, 0x79, 0x70, 711 | 0x65, 0x3D, 0x22, 0x78, 0x73, 0x64, 0x3A, 0x68, 0x65, 0x78, 0x42, 0x69, 0x6E, 0x61, 0x72, 0x79, 712 | 0x22, 0x20, 0x77, 0x63, 0x6D, 0x3A, 0x63, 0x68, 0x61, 0x6E, 0x67, 0x65, 0x49, 0x6D, 0x70, 0x61, 713 | 0x63, 0x74, 0x3D, 0x22, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x74, 0x61, 714 | 0x72, 0x74, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 715 | 0x20, 0x20, 0x3C, 0x2F, 0x78, 0x73, 0x64, 0x3A, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6E, 0x63, 0x65, 716 | 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x78, 0x73, 0x64, 717 | 0x3A, 0x63, 0x6F, 0x6D, 0x70, 0x6C, 0x65, 0x78, 0x54, 0x79, 0x70, 0x65, 0x3E, 0x0D, 0x0A, 0x20, 718 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x78, 0x73, 0x64, 0x3A, 0x65, 0x6C, 0x65, 0x6D, 719 | 0x65, 0x6E, 0x74, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 720 | 0x3D, 0x22, 0x78, 0x73, 0x64, 0x3A, 0x62, 0x6F, 0x6F, 0x6C, 0x65, 0x61, 0x6E, 0x22, 0x20, 0x77, 721 | 0x63, 0x6D, 0x3A, 0x63, 0x68, 0x61, 0x6E, 0x67, 0x65, 0x49, 0x6D, 0x70, 0x61, 0x63, 0x74, 0x3D, 722 | 0x22, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x22, 723 | 0x20, 0x77, 0x63, 0x6D, 0x3A, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 724 | 0x3D, 0x22, 0x22, 0x20, 0x77, 0x63, 0x6D, 0x3A, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x4E, 725 | 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x22, 0x20, 0x77, 0x63, 0x6D, 0x3A, 0x68, 0x61, 0x6E, 0x64, 0x6C, 726 | 0x65, 0x72, 0x3D, 0x22, 0x72, 0x65, 0x67, 0x6B, 0x65, 0x79, 0x28, 0x27, 0x48, 0x4B, 0x45, 0x59, 727 | 0x5F, 0x4C, 0x4F, 0x43, 0x41, 0x4C, 0x5F, 0x4D, 0x41, 0x43, 0x48, 0x49, 0x4E, 0x45, 0x5C, 0x53, 728 | 0x59, 0x53, 0x54, 0x45, 0x4D, 0x5C, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x74, 0x43, 0x6F, 0x6E, 729 | 0x74, 0x72, 0x6F, 0x6C, 0x53, 0x65, 0x74, 0x5C, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 730 | 0x5C, 0x27, 0x29, 0x22, 0x20, 0x77, 0x63, 0x6D, 0x3A, 0x73, 0x63, 0x6F, 0x70, 0x65, 0x3D, 0x22, 731 | 0x61, 0x6C, 0x6C, 0x55, 0x73, 0x65, 0x72, 0x73, 0x22, 0x20, 0x77, 0x63, 0x6D, 0x3A, 0x73, 0x75, 732 | 0x62, 0x53, 0x63, 0x6F, 0x70, 0x65, 0x3D, 0x22, 0x6D, 0x61, 0x63, 0x68, 0x69, 0x6E, 0x65, 0x49, 733 | 0x6E, 0x64, 0x65, 0x70, 0x65, 0x6E, 0x64, 0x65, 0x6E, 0x74, 0x22, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 734 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x78, 0x73, 0x64, 0x3A, 0x73, 0x63, 0x68, 0x65, 735 | 0x6D, 0x61, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x63, 0x6F, 0x6E, 0x66, 0x69, 736 | 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x53, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x3E, 0x0D, 737 | 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x6D, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x3E, 0x0D, 738 | 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x65, 0x6C, 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x73, 739 | 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x6D, 0x65, 0x74, 0x61, 0x64, 740 | 0x61, 0x74, 0x61, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x3C, 0x2F, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, 741 | 0x75, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3E, 0x0D, 0x0A, 0x3C, 0x2F, 0x61, 0x73, 0x73, 0x65, 742 | 0x6D, 0x62, 0x6C, 0x79, 0x3E, 0x0D, 0x0A, 0x61, 0x6D, 0x64, 0x36, 0x34, 0x61, 0x72, 0x6D, 0x52, 743 | 0x45, 0x47, 0x5F, 0x44, 0x57, 0x4F, 0x52, 0x44, 0x48, 0x4B, 0x45, 0x59, 0x5F, 0x4C, 0x4F, 0x43, 744 | 0x41, 0x4C, 0x5F, 0x4D, 0x41, 0x43, 0x48, 0x49, 0x4E, 0x45, 0x62, 0x6F, 0x6F, 0x6C, 0x65, 0x61, 745 | 0x6E, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x6D, 0x75, 0x6C, 0x74, 0x69, 0x53, 0x74, 0x72, 0x69, 746 | 0x6E, 0x67, 0x75, 0x6E, 0x73, 0x69, 0x67, 0x6E, 0x65, 0x64, 0x49, 0x6E, 0x74, 0x64, 0x69, 0x73, 747 | 0x70, 0x6C, 0x61, 0x79, 0x4E, 0x61, 0x6D, 0x65, 0x0D, 0x0A, 748 | }; 749 | constexpr size_t manifest_source::size = sizeof(manifest_source::data); -------------------------------------------------------------------------------- /witutils/find_files_wcs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | std::map> 7 | find_files_wcs(const std::wstring_view directory, const std::wstring_view path_filter, const std::wstring_view file_pattern = L"*"); 8 | 9 | std::map> 10 | find_files_wcs_ex(const std::wstring_view pattern, const std::wstring_view path_filter, const std::wstring_view default_file_pattern = L"*"); -------------------------------------------------------------------------------- /witutils/stdafx.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WalkingCat/MetaDiff/edd15ba9340f9330a4a52023f4f8b629c7720bf3/witutils/stdafx.cpp -------------------------------------------------------------------------------- /witutils/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WalkingCat/MetaDiff/edd15ba9340f9330a4a52023f4f8b629c7720bf3/witutils/stdafx.h -------------------------------------------------------------------------------- /witutils/str_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | inline std::wstring tolower(const std::wstring_view str) { 5 | std::wstring ret(str); 6 | for (auto& chr : ret) { 7 | chr = std::tolower(chr); 8 | } 9 | return std::move(ret); 10 | } 11 | -------------------------------------------------------------------------------- /witutils/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WalkingCat/MetaDiff/edd15ba9340f9330a4a52023f4f8b629c7720bf3/witutils/targetver.h -------------------------------------------------------------------------------- /witutils/witutils.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {6C290411-0E80-4EC5-BFAB-2A9206489474} 24 | Win32Proj 25 | witutils 26 | 10.0 27 | 28 | 29 | 30 | StaticLibrary 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | StaticLibrary 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | StaticLibrary 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | StaticLibrary 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | true 76 | NativeRecommendedRules.ruleset 77 | 78 | 79 | true 80 | true 81 | NativeRecommendedRules.ruleset 82 | 83 | 84 | false 85 | true 86 | NativeRecommendedRules.ruleset 87 | 88 | 89 | false 90 | true 91 | NativeRecommendedRules.ruleset 92 | 93 | 94 | 95 | Use 96 | Level3 97 | Disabled 98 | true 99 | WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) 100 | true 101 | MultiThreadedDebug 102 | stdcpp17 103 | true 104 | 105 | 106 | Windows 107 | true 108 | 109 | 110 | 111 | 112 | Use 113 | Level3 114 | Disabled 115 | true 116 | _DEBUG;_LIB;%(PreprocessorDefinitions) 117 | true 118 | MultiThreadedDebug 119 | stdcpp17 120 | true 121 | 122 | 123 | Windows 124 | true 125 | 126 | 127 | 128 | 129 | Use 130 | Level3 131 | MaxSpeed 132 | true 133 | true 134 | true 135 | WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) 136 | true 137 | MultiThreaded 138 | stdcpp17 139 | true 140 | 141 | 142 | Windows 143 | true 144 | true 145 | true 146 | 147 | 148 | 149 | 150 | Use 151 | Level3 152 | MaxSpeed 153 | true 154 | true 155 | true 156 | NDEBUG;_LIB;%(PreprocessorDefinitions) 157 | true 158 | MultiThreaded 159 | stdcpp17 160 | true 161 | 162 | 163 | Windows 164 | true 165 | true 166 | true 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | Create 187 | Create 188 | Create 189 | Create 190 | 191 | 192 | 193 | 194 | 195 | --------------------------------------------------------------------------------