From 933a225a127a401f84e95f6ffe0a1439dd9f4c21 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Fri, 20 Jul 2018 15:50:26 -0400 Subject: [PATCH 01/29] rename folder --- CMakeLists.txt | 58 +++++++++++++-------------- {gui => oldgui}/ITH.h | 0 {gui => oldgui}/ITHVNR.rc | 0 {gui => oldgui}/ProcessWindow.cpp | 0 {gui => oldgui}/ProcessWindow.h | 0 {gui => oldgui}/ProfileManager.cpp | 0 {gui => oldgui}/ProfileManager.h | 0 {gui => oldgui}/TextBuffer.cpp | 0 {gui => oldgui}/TextBuffer.h | 0 {gui => oldgui}/command.cpp | 0 {gui => oldgui}/icon1.ico | Bin {gui => oldgui}/language.cpp | 0 {gui => oldgui}/language.h | 0 {gui => oldgui}/main.cpp | 0 {gui => oldgui}/profile/Profile.cpp | 0 {gui => oldgui}/profile/Profile.h | 0 {gui => oldgui}/profile/misc.cpp | 0 {gui => oldgui}/profile/misc.h | 0 {gui => oldgui}/profile/pugiconfig.h | 0 {gui => oldgui}/profile/pugixml.cpp | 0 {gui => oldgui}/profile/pugixml.h | 0 {gui => oldgui}/resource.h | 0 {gui => oldgui}/utility.cpp | 0 {gui => oldgui}/utility.h | 0 {gui => oldgui}/version.h.in | 0 {gui => oldgui}/window.cpp | 0 {gui => oldgui}/window.h | 0 27 files changed, 29 insertions(+), 29 deletions(-) rename {gui => oldgui}/ITH.h (100%) rename {gui => oldgui}/ITHVNR.rc (100%) rename {gui => oldgui}/ProcessWindow.cpp (100%) rename {gui => oldgui}/ProcessWindow.h (100%) rename {gui => oldgui}/ProfileManager.cpp (100%) rename {gui => oldgui}/ProfileManager.h (100%) rename {gui => oldgui}/TextBuffer.cpp (100%) rename {gui => oldgui}/TextBuffer.h (100%) rename {gui => oldgui}/command.cpp (100%) rename {gui => oldgui}/icon1.ico (100%) rename {gui => oldgui}/language.cpp (100%) rename {gui => oldgui}/language.h (100%) rename {gui => oldgui}/main.cpp (100%) rename {gui => oldgui}/profile/Profile.cpp (100%) rename {gui => oldgui}/profile/Profile.h (100%) rename {gui => oldgui}/profile/misc.cpp (100%) rename {gui => oldgui}/profile/misc.h (100%) rename {gui => oldgui}/profile/pugiconfig.h (100%) rename {gui => oldgui}/profile/pugixml.cpp (100%) rename {gui => oldgui}/profile/pugixml.h (100%) rename {gui => oldgui}/resource.h (100%) rename {gui => oldgui}/utility.cpp (100%) rename {gui => oldgui}/utility.h (100%) rename {gui => oldgui}/version.h.in (100%) rename {gui => oldgui}/window.cpp (100%) rename {gui => oldgui}/window.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index d8650f8..8eb1b0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,40 +49,40 @@ include_directories( . texthook - ${CMAKE_BINARY_DIR}/gui + ${CMAKE_BINARY_DIR}/oldgui ) set(resource_src - gui/ITHVNR.rc - gui/icon1.ico + oldgui/ITHVNR.rc + oldgui/icon1.ico ) set(nexthooker_src - gui/command.cpp - gui/ITH.h - gui/language.cpp - gui/language.h - gui/main.cpp - gui/ProcessWindow.cpp - gui/ProcessWindow.h - gui/ProfileManager.cpp - gui/ProfileManager.h - gui/resource.h - gui/utility.cpp - gui/utility.h - ${CMAKE_BINARY_DIR}/gui/version.h - gui/version.h.in - gui/window.cpp - gui/window.h - gui/TextBuffer.cpp - gui/TextBuffer.h - gui/profile/Profile.h - gui/profile/Profile.cpp - gui/profile/pugiconfig.h - gui/profile/pugixml.cpp - gui/profile/pugixml.h - gui/profile/misc.h - gui/profile/misc.cpp + oldgui/command.cpp + oldgui/ITH.h + oldgui/language.cpp + oldgui/language.h + oldgui/main.cpp + oldgui/ProcessWindow.cpp + oldgui/ProcessWindow.h + oldgui/ProfileManager.cpp + oldgui/ProfileManager.h + oldgui/resource.h + oldgui/utility.cpp + oldgui/utility.h + ${CMAKE_BINARY_DIR}/oldgui/version.h + oldgui/version.h.in + oldgui/window.cpp + oldgui/window.h + oldgui/TextBuffer.cpp + oldgui/TextBuffer.h + oldgui/profile/Profile.h + oldgui/profile/Profile.cpp + oldgui/profile/pugiconfig.h + oldgui/profile/pugixml.cpp + oldgui/profile/pugixml.h + oldgui/profile/misc.h + oldgui/profile/misc.cpp ${resource_src} ) @@ -123,4 +123,4 @@ install(TARGETS ${PROJECT_NAME} CONFIGURATIONS Release ) -configure_file(gui/version.h.in gui/version.h) +configure_file(oldgui/version.h.in oldgui/version.h) diff --git a/gui/ITH.h b/oldgui/ITH.h similarity index 100% rename from gui/ITH.h rename to oldgui/ITH.h diff --git a/gui/ITHVNR.rc b/oldgui/ITHVNR.rc similarity index 100% rename from gui/ITHVNR.rc rename to oldgui/ITHVNR.rc diff --git a/gui/ProcessWindow.cpp b/oldgui/ProcessWindow.cpp similarity index 100% rename from gui/ProcessWindow.cpp rename to oldgui/ProcessWindow.cpp diff --git a/gui/ProcessWindow.h b/oldgui/ProcessWindow.h similarity index 100% rename from gui/ProcessWindow.h rename to oldgui/ProcessWindow.h diff --git a/gui/ProfileManager.cpp b/oldgui/ProfileManager.cpp similarity index 100% rename from gui/ProfileManager.cpp rename to oldgui/ProfileManager.cpp diff --git a/gui/ProfileManager.h b/oldgui/ProfileManager.h similarity index 100% rename from gui/ProfileManager.h rename to oldgui/ProfileManager.h diff --git a/gui/TextBuffer.cpp b/oldgui/TextBuffer.cpp similarity index 100% rename from gui/TextBuffer.cpp rename to oldgui/TextBuffer.cpp diff --git a/gui/TextBuffer.h b/oldgui/TextBuffer.h similarity index 100% rename from gui/TextBuffer.h rename to oldgui/TextBuffer.h diff --git a/gui/command.cpp b/oldgui/command.cpp similarity index 100% rename from gui/command.cpp rename to oldgui/command.cpp diff --git a/gui/icon1.ico b/oldgui/icon1.ico similarity index 100% rename from gui/icon1.ico rename to oldgui/icon1.ico diff --git a/gui/language.cpp b/oldgui/language.cpp similarity index 100% rename from gui/language.cpp rename to oldgui/language.cpp diff --git a/gui/language.h b/oldgui/language.h similarity index 100% rename from gui/language.h rename to oldgui/language.h diff --git a/gui/main.cpp b/oldgui/main.cpp similarity index 100% rename from gui/main.cpp rename to oldgui/main.cpp diff --git a/gui/profile/Profile.cpp b/oldgui/profile/Profile.cpp similarity index 100% rename from gui/profile/Profile.cpp rename to oldgui/profile/Profile.cpp diff --git a/gui/profile/Profile.h b/oldgui/profile/Profile.h similarity index 100% rename from gui/profile/Profile.h rename to oldgui/profile/Profile.h diff --git a/gui/profile/misc.cpp b/oldgui/profile/misc.cpp similarity index 100% rename from gui/profile/misc.cpp rename to oldgui/profile/misc.cpp diff --git a/gui/profile/misc.h b/oldgui/profile/misc.h similarity index 100% rename from gui/profile/misc.h rename to oldgui/profile/misc.h diff --git a/gui/profile/pugiconfig.h b/oldgui/profile/pugiconfig.h similarity index 100% rename from gui/profile/pugiconfig.h rename to oldgui/profile/pugiconfig.h diff --git a/gui/profile/pugixml.cpp b/oldgui/profile/pugixml.cpp similarity index 100% rename from gui/profile/pugixml.cpp rename to oldgui/profile/pugixml.cpp diff --git a/gui/profile/pugixml.h b/oldgui/profile/pugixml.h similarity index 100% rename from gui/profile/pugixml.h rename to oldgui/profile/pugixml.h diff --git a/gui/resource.h b/oldgui/resource.h similarity index 100% rename from gui/resource.h rename to oldgui/resource.h diff --git a/gui/utility.cpp b/oldgui/utility.cpp similarity index 100% rename from gui/utility.cpp rename to oldgui/utility.cpp diff --git a/gui/utility.h b/oldgui/utility.h similarity index 100% rename from gui/utility.h rename to oldgui/utility.h diff --git a/gui/version.h.in b/oldgui/version.h.in similarity index 100% rename from gui/version.h.in rename to oldgui/version.h.in diff --git a/gui/window.cpp b/oldgui/window.cpp similarity index 100% rename from gui/window.cpp rename to oldgui/window.cpp diff --git a/gui/window.h b/oldgui/window.h similarity index 100% rename from gui/window.h rename to oldgui/window.h From ebc1303e9af2368287c3f6c077065e368a118528 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Fri, 20 Jul 2018 16:08:30 -0400 Subject: [PATCH 02/29] add new gui --- .gitignore | 113 +++++++++++++++++++++++++- GUI.sln | 25 ++++++ GUI/App.config | 6 ++ GUI/Form1.Designer.cs | 40 +++++++++ GUI/Form1.cs | 20 +++++ GUI/GUI.csproj | 79 ++++++++++++++++++ GUI/Program.cs | 22 +++++ GUI/Properties/AssemblyInfo.cs | 36 +++++++++ GUI/Properties/Resources.Designer.cs | 71 ++++++++++++++++ GUI/Properties/Resources.resx | 117 +++++++++++++++++++++++++++ GUI/Properties/Settings.Designer.cs | 30 +++++++ GUI/Properties/Settings.settings | 7 ++ 12 files changed, 564 insertions(+), 2 deletions(-) create mode 100644 GUI.sln create mode 100644 GUI/App.config create mode 100644 GUI/Form1.Designer.cs create mode 100644 GUI/Form1.cs create mode 100644 GUI/GUI.csproj create mode 100644 GUI/Program.cs create mode 100644 GUI/Properties/AssemblyInfo.cs create mode 100644 GUI/Properties/Resources.Designer.cs create mode 100644 GUI/Properties/Resources.resx create mode 100644 GUI/Properties/Settings.Designer.cs create mode 100644 GUI/Properties/Settings.settings diff --git a/.gitignore b/.gitignore index fb4d675..1647eef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,111 @@ -.vs/ -CMakeBuilds/ \ No newline at end of file +# Build Folders (you can keep bin if you'd like, to store dlls and pdbs) +CMakeBuilds/ +[Bb]in/ +[Oo]bj/ + +# mstest test results +TestResults + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates +*.vs/ + +# Build results +[Dd]ebug/ +[Rr]elease/ +x64/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.log +*.vspscc +*.vssscc +.builds + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper* + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Publish Web Output +*.Publish.xml + +# NuGet Packages Directory +packages + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +[Bb]in +[Oo]bj +sql +TestResults +[Tt]est[Rr]esult* +*.Cache +ClientBin +[Ss]tyle[Cc]op.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +WinForms/AdamsLair.WinForms.xml diff --git a/GUI.sln b/GUI.sln new file mode 100644 index 0000000..7e2eba0 --- /dev/null +++ b/GUI.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2042 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUI", "GUI\GUI.csproj", "{D847D0DC-78F5-4722-8E06-1A124B957353}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F2661015-ED1B-45F8-8277-D2E3E26A75DA} + EndGlobalSection +EndGlobal diff --git a/GUI/App.config b/GUI/App.config new file mode 100644 index 0000000..8324aa6 --- /dev/null +++ b/GUI/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/GUI/Form1.Designer.cs b/GUI/Form1.Designer.cs new file mode 100644 index 0000000..1442a4c --- /dev/null +++ b/GUI/Form1.Designer.cs @@ -0,0 +1,40 @@ +namespace GUI +{ + partial class Form1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 450); + this.Text = "Form1"; + } + + #endregion + } +} + diff --git a/GUI/Form1.cs b/GUI/Form1.cs new file mode 100644 index 0000000..39c7c76 --- /dev/null +++ b/GUI/Form1.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace GUI +{ + public partial class Form1 : Form + { + public Form1() + { + InitializeComponent(); + } + } +} diff --git a/GUI/GUI.csproj b/GUI/GUI.csproj new file mode 100644 index 0000000..2acd95c --- /dev/null +++ b/GUI/GUI.csproj @@ -0,0 +1,79 @@ + + + + + Debug + AnyCPU + {D847D0DC-78F5-4722-8E06-1A124B957353} + WinExe + GUI + GUI + v4.6 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + Form + + + Form1.cs + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/GUI/Program.cs b/GUI/Program.cs new file mode 100644 index 0000000..17aaf57 --- /dev/null +++ b/GUI/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace GUI +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Form1()); + } + } +} diff --git a/GUI/Properties/AssemblyInfo.cs b/GUI/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8c56606 --- /dev/null +++ b/GUI/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("GUI")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GUI")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d847d0dc-78f5-4722-8e06-1a124b957353")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/GUI/Properties/Resources.Designer.cs b/GUI/Properties/Resources.Designer.cs new file mode 100644 index 0000000..e53846d --- /dev/null +++ b/GUI/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace GUI.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GUI.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/GUI/Properties/Resources.resx b/GUI/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/GUI/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GUI/Properties/Settings.Designer.cs b/GUI/Properties/Settings.Designer.cs new file mode 100644 index 0000000..578d709 --- /dev/null +++ b/GUI/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace GUI.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/GUI/Properties/Settings.settings b/GUI/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/GUI/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + From 1380f64cecdb8eec5e8f9e8e1ab3408d6fa30cb3 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Fri, 20 Jul 2018 16:26:27 -0400 Subject: [PATCH 03/29] minor fix --- texthook/CMakeLists.txt | 2 -- texthook/hookman.cc | 6 +++--- texthook/hookman.h | 2 +- texthook/host.cc | 6 +++--- texthook/host.h | 2 +- texthook/pipe.cc | 4 ++-- texthook/textthread.cc | 2 +- 7 files changed, 11 insertions(+), 13 deletions(-) diff --git a/texthook/CMakeLists.txt b/texthook/CMakeLists.txt index 0982464..aab5750 100644 --- a/texthook/CMakeLists.txt +++ b/texthook/CMakeLists.txt @@ -20,8 +20,6 @@ set(vnrhost_src textthread.cc ) -include_directories(../) - add_library(vnrhost SHARED ${vnrhost_src}) set_target_properties(vnrhost PROPERTIES LINK_FLAGS /SUBSYSTEM:WINDOWS) diff --git a/texthook/hookman.cc b/texthook/hookman.cc index 7566695..9d723ca 100644 --- a/texthook/hookman.cc +++ b/texthook/hookman.cc @@ -9,9 +9,9 @@ #endif // _MSC_VER #include "hookman.h" -#include "vnrhook/include/const.h" -#include "vnrhook/include/defs.h" -#include "vnrhook/include/types.h" +#include "../vnrhook/include/const.h" +#include "../vnrhook/include/defs.h" +#include "../vnrhook/include/types.h" #include "winmutex.h" #include diff --git a/texthook/hookman.h b/texthook/hookman.h index bdbdf28..fb5328a 100644 --- a/texthook/hookman.h +++ b/texthook/hookman.h @@ -8,7 +8,7 @@ #include "textthread.h" #include #include -#include "vnrhook/include/types.h" +#include "../vnrhook/include/types.h" struct ProcessRecord { diff --git a/texthook/host.cc b/texthook/host.cc index 74e6cc1..5e5393b 100644 --- a/texthook/host.cc +++ b/texthook/host.cc @@ -4,9 +4,9 @@ #include "host.h" #include "pipe.h" -#include "vnrhook/include/const.h" -#include "vnrhook/include/defs.h" -#include "vnrhook/include/types.h" +#include "../vnrhook/include/const.h" +#include "../vnrhook/include/defs.h" +#include "../vnrhook/include/types.h" HANDLE preventDuplicationMutex; diff --git a/texthook/host.h b/texthook/host.h index 36ed001..3919943 100644 --- a/texthook/host.h +++ b/texthook/host.h @@ -6,7 +6,7 @@ #define DLLEXPORT __declspec(dllexport) #include "hookman.h" -#include "vnrhook/include/types.h" +#include "../vnrhook/include/types.h" #include DLLEXPORT void OpenHost(); diff --git a/texthook/pipe.cc b/texthook/pipe.cc index fa69470..d03dd5d 100644 --- a/texthook/pipe.cc +++ b/texthook/pipe.cc @@ -4,8 +4,8 @@ #include "pipe.h" #include "host.h" -#include "vnrhook/include/defs.h" -#include "vnrhook/include/const.h" +#include "../vnrhook/include/defs.h" +#include "../vnrhook/include/const.h" #include extern HookManager* man; diff --git a/texthook/textthread.cc b/texthook/textthread.cc index 7481d5c..8f09409 100644 --- a/texthook/textthread.cc +++ b/texthook/textthread.cc @@ -7,7 +7,7 @@ #include "host.h" #include "textthread.h" -#include "vnrhook/include/const.h" +#include "../vnrhook/include/const.h" #include "winmutex.h" extern HookManager* man; From a8cdee67e8d06ef1265bbac1629a101aa63b344e Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Fri, 20 Jul 2018 16:54:51 -0400 Subject: [PATCH 04/29] add project for c#/c++ interoperability --- GUI.sln | 24 ++++ GUI/HostInterop/AssemblyInfo.cpp | Bin 0 -> 1522 bytes GUI/HostInterop/HostInterop.h | Bin 0 -> 468 bytes GUI/HostInterop/HostInterop.vcxproj | 151 ++++++++++++++++++++ GUI/HostInterop/HostInterop.vcxproj.filters | 29 ++++ GUI/HostInterop/stdafx.cpp | Bin 0 -> 44 bytes GUI/HostInterop/stdafx.h | Bin 0 -> 30 bytes 7 files changed, 204 insertions(+) create mode 100644 GUI/HostInterop/AssemblyInfo.cpp create mode 100644 GUI/HostInterop/HostInterop.h create mode 100644 GUI/HostInterop/HostInterop.vcxproj create mode 100644 GUI/HostInterop/HostInterop.vcxproj.filters create mode 100644 GUI/HostInterop/stdafx.cpp create mode 100644 GUI/HostInterop/stdafx.h diff --git a/GUI.sln b/GUI.sln index 7e2eba0..8f9287d 100644 --- a/GUI.sln +++ b/GUI.sln @@ -5,16 +5,40 @@ VisualStudioVersion = 15.0.27703.2042 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUI", "GUI\GUI.csproj", "{D847D0DC-78F5-4722-8E06-1A124B957353}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HostInterop", "GUI\HostInterop\HostInterop.vcxproj", "{C3534056-72B3-4651-B69C-6E428F675861}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x64.ActiveCfg = Debug|Any CPU + {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x64.Build.0 = Debug|Any CPU + {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.ActiveCfg = Debug|Any CPU + {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.Build.0 = Debug|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|Any CPU.ActiveCfg = Release|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|Any CPU.Build.0 = Release|Any CPU + {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|x64.ActiveCfg = Release|Any CPU + {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|x64.Build.0 = Release|Any CPU + {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|x86.ActiveCfg = Release|Any CPU + {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|x86.Build.0 = Release|Any CPU + {C3534056-72B3-4651-B69C-6E428F675861}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {C3534056-72B3-4651-B69C-6E428F675861}.Debug|x64.ActiveCfg = Debug|x64 + {C3534056-72B3-4651-B69C-6E428F675861}.Debug|x64.Build.0 = Debug|x64 + {C3534056-72B3-4651-B69C-6E428F675861}.Debug|x86.ActiveCfg = Debug|Win32 + {C3534056-72B3-4651-B69C-6E428F675861}.Debug|x86.Build.0 = Debug|Win32 + {C3534056-72B3-4651-B69C-6E428F675861}.Release|Any CPU.ActiveCfg = Release|Win32 + {C3534056-72B3-4651-B69C-6E428F675861}.Release|x64.ActiveCfg = Release|x64 + {C3534056-72B3-4651-B69C-6E428F675861}.Release|x64.Build.0 = Release|x64 + {C3534056-72B3-4651-B69C-6E428F675861}.Release|x86.ActiveCfg = Release|Win32 + {C3534056-72B3-4651-B69C-6E428F675861}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GUI/HostInterop/AssemblyInfo.cpp b/GUI/HostInterop/AssemblyInfo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c11d2a380dab0f8e6378793ffd7c02d711668133 GIT binary patch literal 1522 zcmcJPPfNp45X9#!_#H~UsE4ASq@GGWDD+Tl=|w!GF|EO-3Hc-T%d5YAQE1c$jZ{J* z-MpRIo!NcwZ%2x?R;ZOKJt|U1T@@;M546y$dU_&qr~~(__-5yqoLh4uA)`>nTj+8p z+PLh9FITNI`s(WroCVcHAeL~Y@cb32BG)jiO2Zz4mQZc%@N-Rw=X&A4RbT9l=hpEW zb_&~mSPUh^qnv6>RJMM`uKt8b!BcDX)rC2Bk1zJc+?dm|WevVXrVh{a9)Mdq{5d=& zkzFwg@$qus+;&_MWLT5VHC6m!^0;bLvyJHem z%=PT$Z;L!czZLzM^Zq&BM%{HgX5i(_t$$j(XwgHFo=zKz_!#OtXa=I8+E&`0El^}d bB-+x{k1*m+sc|y0VmFh&yJ`DeHBdhQ4(<<7 literal 0 HcmV?d00001 diff --git a/GUI/HostInterop/HostInterop.h b/GUI/HostInterop/HostInterop.h new file mode 100644 index 0000000000000000000000000000000000000000..ce127d23ed0c4e692501128e708d8b5f0d5e1df5 GIT binary patch literal 468 zcmbtQK@Ng25S*(f{-Ke(KEMy?*%y?8V1z;nBpAcj)!9uOFP=0_w%yt8%zJAPwI+`=BHW9i@@ zK5;hC>xV;pO~hozinDoYv{V|n5vw~eIODEeB`XJ=M7qBHJVyQ#tKvN{@3rHham8#x pj)r=UYBJM>w|EMyINk<{#n3?>YvM&}gKnW=EFxoKUZS@}@i%l + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {C3534056-72B3-4651-B69C-6E428F675861} + v4.6 + ManagedCProj + HostInterop + 10.0.17134.0 + + + + DynamicLibrary + true + v141 + true + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + DynamicLibrary + true + v141 + true + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Level3 + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + Use + + + + + + + + Level3 + Disabled + _DEBUG;%(PreprocessorDefinitions) + Use + + + + + + + + Level3 + WIN32;NDEBUG;%(PreprocessorDefinitions) + Use + + + + + + + + Level3 + NDEBUG;%(PreprocessorDefinitions) + Use + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/GUI/HostInterop/HostInterop.vcxproj.filters b/GUI/HostInterop/HostInterop.vcxproj.filters new file mode 100644 index 0000000..3e0ae9a --- /dev/null +++ b/GUI/HostInterop/HostInterop.vcxproj.filters @@ -0,0 +1,29 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/GUI/HostInterop/stdafx.cpp b/GUI/HostInterop/stdafx.cpp new file mode 100644 index 0000000000000000000000000000000000000000..08343afdf790eaa4524432ba7b0c3ddc395ee814 GIT binary patch literal 44 xcmezWPnjW;A&()MA%~%qA%!88L4iSup_rir$WCNPW2j)zW5@uC@G@{Q008!y2%rD} literal 0 HcmV?d00001 diff --git a/GUI/HostInterop/stdafx.h b/GUI/HostInterop/stdafx.h new file mode 100644 index 0000000000000000000000000000000000000000..866b7f5b647af399e28ddfc986dce977754dd671 GIT binary patch literal 30 kcmezWPnn^Bp@<=oA)O%?NGdSoGvqNOGo&)`GH@{f0E5H^bpQYW literal 0 HcmV?d00001 From 62b5f5fe8f788629c95cbd2375a0d9845decb4c1 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Fri, 20 Jul 2018 17:18:27 -0400 Subject: [PATCH 05/29] start implementing hostinterop --- GUI/HostInterop/HostInterop.h | Bin 468 -> 2098 bytes oldgui/command.cpp | 2 +- oldgui/main.cpp | 2 +- oldgui/window.cpp | 2 +- texthook/host.cc | 8 ++++---- texthook/host.h | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/GUI/HostInterop/HostInterop.h b/GUI/HostInterop/HostInterop.h index ce127d23ed0c4e692501128e708d8b5f0d5e1df5..617c60f162c2ba46a86fc6549acbfabf96fa4dfa 100644 GIT binary patch literal 2098 zcmb`I%}?7v5XI-}O8p;JvJpK>Kv4vg`Cx^jv@WgBh*9JqpR@ju|klbd(m=bA;B6-vx*rol7 zUISK+VkYux+xfZ1KVene3*Hye<2GJ1-d1*P6XNxVrkuWU7pH=i9`|655@2v$uZfG+ zmQjTX*M5!Y$eG;`ZHztG7vc=yuy1d9yG<;Pt(7C}cQr~zB-L+%?UG12e9FXMOiLIR zpr$)E<27tPf-2ny30`B{1wnd_iLbrtu~Ya-{~?$KsO|OuU-?zOu0(3<#MP=&%IiJ< zPcc(x)sDPPJ#qaGhU#?=yE#wlr=@+Mk4~u1z8%|FYIMOpP$|{S**(;`Yh64Z$8(HL z6+MnQsfR`{!f6-0$2ci3^@RG&Rc=$}5RH8OJocp~G?2-{w5HkVqf<9N6|th}ByU%C zok7K0$coUZ`{9hpCD=DGR`q>)s`DVTbbc<_Go|j@>&!Q0mUet=&(c^*Ph2mvNd@cg>YgPR@jP?@tod!t;JOsRyW<`nh^fXLyCS`s!RIUxt+5 lE*#r1UXGzF(Ddw6`K=@P>GW?mK1avq%esxJSE%}4`~|!bO)vle delta 22 acmdlaaD{opF-E>x25Sah1}=tLFbM!jg#>~C diff --git a/oldgui/command.cpp b/oldgui/command.cpp index 3b791f8..4fad94a 100644 --- a/oldgui/command.cpp +++ b/oldgui/command.cpp @@ -41,7 +41,7 @@ DWORD ProcessCommand(const std::wstring& cmd, DWORD pid) { HookParam hp = {}; if (Parse(m[1].str(), hp)) - InsertHook(pid, &hp); + InsertHook(pid, hp); } else if (regex_match(cmd, m, wregex(L":(?:h|help)", wregex::icase))) { diff --git a/oldgui/main.cpp b/oldgui/main.cpp index a884736..bc72605 100644 --- a/oldgui/main.cpp +++ b/oldgui/main.cpp @@ -149,7 +149,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine if (StartHost()) { SetUnhandledExceptionFilter(UnhandledExcept); - GetHostHookManager(&man); + man = GetHostHookManager(); pfman = new ProfileManager(); DefaultSettings(); LoadSettings(); diff --git a/oldgui/window.cpp b/oldgui/window.cpp index 81d9a5e..ba0abc1 100644 --- a/oldgui/window.cpp +++ b/oldgui/window.cpp @@ -488,7 +488,7 @@ void RegisterProcess(DWORD pid) { for (auto i = pf->Hooks().begin(); i != pf->Hooks().end(); ++i) { - InsertHook(pid, &i->get()->HP(), toMultiByteString(i->get()->Name())); + InsertHook(pid, i->get()->HP(), toMultiByteString(i->get()->Name())); } } } diff --git a/texthook/host.cc b/texthook/host.cc index 5e5393b..7391c47 100644 --- a/texthook/host.cc +++ b/texthook/host.cc @@ -117,19 +117,19 @@ DLLEXPORT bool DetachProcessById(DWORD processId) return WriteFile(man->GetHostPipe(processId), &command, sizeof(command), &unused, nullptr); } -DLLEXPORT void GetHostHookManager(HookManager** hookman) +DLLEXPORT HookManager* GetHostHookManager() { - *hookman = man; + return man; } -DLLEXPORT bool InsertHook(DWORD pid, const HookParam *hp, std::string name) +DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name) { HANDLE commandPipe = man->GetHostPipe(pid); if (commandPipe == nullptr) return false; BYTE buffer[PIPE_BUFFER_SIZE] = {}; *(DWORD*)buffer = HOST_COMMAND_NEW_HOOK; - *(HookParam*)(buffer + sizeof(DWORD)) = *hp; + *(HookParam*)(buffer + sizeof(DWORD)) = hp; if (name.size()) strcpy((char*)buffer + sizeof(DWORD) + sizeof(HookParam), name.c_str()); DWORD unused; return WriteFile(commandPipe, buffer, sizeof(DWORD) + sizeof(HookParam) + name.size(), &unused, nullptr); diff --git a/texthook/host.h b/texthook/host.h index 3919943..876d0b2 100644 --- a/texthook/host.h +++ b/texthook/host.h @@ -12,10 +12,10 @@ DLLEXPORT void OpenHost(); DLLEXPORT bool StartHost(); DLLEXPORT void CloseHost(); -DLLEXPORT void GetHostHookManager(HookManager **hookman); +DLLEXPORT HookManager* GetHostHookManager(); DLLEXPORT bool InjectProcessById(DWORD pid, DWORD timeout = 5000); DLLEXPORT bool DetachProcessById(DWORD pid); -DLLEXPORT bool InsertHook(DWORD pid, const HookParam *hp, std::string name = ""); +DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name = ""); DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr); // EOF From 314c17b4351c7676b92ec627f729fa0bc4157748 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Fri, 20 Jul 2018 22:10:03 -0400 Subject: [PATCH 06/29] fix? hostinterop --- GUI.sln | 4 +-- GUI/GUI.csproj | 26 ++++++++++++++++++++ GUI/HostInterop/HostInterop.cpp | 3 +++ GUI/HostInterop/HostInterop.h | Bin 2098 -> 2234 bytes GUI/HostInterop/HostInterop.vcxproj | 3 ++- GUI/HostInterop/HostInterop.vcxproj.filters | 3 +++ GUI/Program.cs | 2 ++ 7 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 GUI/HostInterop/HostInterop.cpp diff --git a/GUI.sln b/GUI.sln index 8f9287d..b1492e2 100644 --- a/GUI.sln +++ b/GUI.sln @@ -21,8 +21,8 @@ Global {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|Any CPU.Build.0 = Debug|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x64.ActiveCfg = Debug|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x64.Build.0 = Debug|Any CPU - {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.ActiveCfg = Debug|Any CPU - {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.Build.0 = Debug|Any CPU + {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.ActiveCfg = Debug|x86 + {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.Build.0 = Debug|x86 {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|Any CPU.ActiveCfg = Release|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|Any CPU.Build.0 = Release|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|x64.ActiveCfg = Release|Any CPU diff --git a/GUI/GUI.csproj b/GUI/GUI.csproj index 2acd95c..fdc7587 100644 --- a/GUI/GUI.csproj +++ b/GUI/GUI.csproj @@ -31,6 +31,26 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + true + @@ -75,5 +95,11 @@ + + + {c3534056-72b3-4651-b69c-6e428f675861} + HostInterop + + \ No newline at end of file diff --git a/GUI/HostInterop/HostInterop.cpp b/GUI/HostInterop/HostInterop.cpp new file mode 100644 index 0000000..e582dec --- /dev/null +++ b/GUI/HostInterop/HostInterop.cpp @@ -0,0 +1,3 @@ +#include "stdafx.h" + +#include "HostInterop.h" \ No newline at end of file diff --git a/GUI/HostInterop/HostInterop.h b/GUI/HostInterop/HostInterop.h index 617c60f162c2ba46a86fc6549acbfabf96fa4dfa..f244d87865daf8bafd5607454428dc462cdc66ee 100644 GIT binary patch delta 263 zcmdlauuE{m2}a&xh7yKEAkJh+W>A=XoY5Xlgq0}}O(c}bSx}!rp8-|pbf#49Jg7-! z45^dZnQhVZIWtd16S>Yj6^lvPEVt3rxw3v_2HH5el3jgr44V*&JqOusCvRg@0n6WH jGe?uNo;-n(WpWg|0#sg_9prK=2CK;nSwttVVQ&KfF~vX1 delta 133 zcmdlbxJh8c3C79S80{u2F$GL+U~-y#g(-zQlOd0xgdvroh#_yXGqdgFKISQt4Opgd zCNbm#VGe`BUse - + ..\..\CMakeBuilds\build\x86-Debug\texthook\vnrhost.lib;%(AdditionalDependencies) @@ -138,6 +138,7 @@ + Create Create diff --git a/GUI/HostInterop/HostInterop.vcxproj.filters b/GUI/HostInterop/HostInterop.vcxproj.filters index 3e0ae9a..de404d0 100644 --- a/GUI/HostInterop/HostInterop.vcxproj.filters +++ b/GUI/HostInterop/HostInterop.vcxproj.filters @@ -25,5 +25,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/GUI/Program.cs b/GUI/Program.cs index 17aaf57..7179701 100644 --- a/GUI/Program.cs +++ b/GUI/Program.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; +using HostInterop; namespace GUI { @@ -14,6 +15,7 @@ namespace GUI [STAThread] static void Main() { + if (!Host.Start()) return; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); From a8f2ca79a5d76958481f73101023b5d9018c0312 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Fri, 20 Jul 2018 23:43:25 -0400 Subject: [PATCH 07/29] Revert "fix? hostinterop" This reverts commit 314c17b4351c7676b92ec627f729fa0bc4157748. --- GUI.sln | 4 +-- GUI/GUI.csproj | 26 -------------------- GUI/HostInterop/HostInterop.cpp | 3 --- GUI/HostInterop/HostInterop.h | Bin 2234 -> 2098 bytes GUI/HostInterop/HostInterop.vcxproj | 3 +-- GUI/HostInterop/HostInterop.vcxproj.filters | 3 --- GUI/Program.cs | 2 -- 7 files changed, 3 insertions(+), 38 deletions(-) delete mode 100644 GUI/HostInterop/HostInterop.cpp diff --git a/GUI.sln b/GUI.sln index b1492e2..8f9287d 100644 --- a/GUI.sln +++ b/GUI.sln @@ -21,8 +21,8 @@ Global {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|Any CPU.Build.0 = Debug|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x64.ActiveCfg = Debug|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x64.Build.0 = Debug|Any CPU - {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.ActiveCfg = Debug|x86 - {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.Build.0 = Debug|x86 + {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.ActiveCfg = Debug|Any CPU + {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.Build.0 = Debug|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|Any CPU.ActiveCfg = Release|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|Any CPU.Build.0 = Release|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|x64.ActiveCfg = Release|Any CPU diff --git a/GUI/GUI.csproj b/GUI/GUI.csproj index fdc7587..2acd95c 100644 --- a/GUI/GUI.csproj +++ b/GUI/GUI.csproj @@ -31,26 +31,6 @@ prompt 4 - - true - bin\x86\Debug\ - DEBUG;TRACE - full - x86 - prompt - MinimumRecommendedRules.ruleset - true - - - bin\x86\Release\ - TRACE - true - pdbonly - x86 - prompt - MinimumRecommendedRules.ruleset - true - @@ -95,11 +75,5 @@ - - - {c3534056-72b3-4651-b69c-6e428f675861} - HostInterop - - \ No newline at end of file diff --git a/GUI/HostInterop/HostInterop.cpp b/GUI/HostInterop/HostInterop.cpp deleted file mode 100644 index e582dec..0000000 --- a/GUI/HostInterop/HostInterop.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "stdafx.h" - -#include "HostInterop.h" \ No newline at end of file diff --git a/GUI/HostInterop/HostInterop.h b/GUI/HostInterop/HostInterop.h index f244d87865daf8bafd5607454428dc462cdc66ee..617c60f162c2ba46a86fc6549acbfabf96fa4dfa 100644 GIT binary patch delta 133 zcmdlbxJh8c3C79S80{u2F$GL+U~-y#g(-zQlOd0xgdvroh#_yXGqdgFKISQt4Opgd zCNbm#VGe`BA=XoY5Xlgq0}}O(c}bSx}!rp8-|pbf#49Jg7-! z45^dZnQhVZIWtd16S>Yj6^lvPEVt3rxw3v_2HH5el3jgr44V*&JqOusCvRg@0n6WH jGe?uNo;-n(WpWg|0#sg_9prK=2CK;nSwttVVQ&KfF~vX1 diff --git a/GUI/HostInterop/HostInterop.vcxproj b/GUI/HostInterop/HostInterop.vcxproj index 63af8b4..288a16c 100644 --- a/GUI/HostInterop/HostInterop.vcxproj +++ b/GUI/HostInterop/HostInterop.vcxproj @@ -93,7 +93,7 @@ Use - ..\..\CMakeBuilds\build\x86-Debug\texthook\vnrhost.lib;%(AdditionalDependencies) + @@ -138,7 +138,6 @@ - Create Create diff --git a/GUI/HostInterop/HostInterop.vcxproj.filters b/GUI/HostInterop/HostInterop.vcxproj.filters index de404d0..3e0ae9a 100644 --- a/GUI/HostInterop/HostInterop.vcxproj.filters +++ b/GUI/HostInterop/HostInterop.vcxproj.filters @@ -25,8 +25,5 @@ Source Files - - Source Files - \ No newline at end of file diff --git a/GUI/Program.cs b/GUI/Program.cs index 7179701..17aaf57 100644 --- a/GUI/Program.cs +++ b/GUI/Program.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; -using HostInterop; namespace GUI { @@ -15,7 +14,6 @@ namespace GUI [STAThread] static void Main() { - if (!Host.Start()) return; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); From a055db4bf844ee088c2128467fd7c7a032acfbe0 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Fri, 20 Jul 2018 23:43:55 -0400 Subject: [PATCH 08/29] Revert "start implementing hostinterop" This reverts commit 62b5f5fe8f788629c95cbd2375a0d9845decb4c1. --- GUI/HostInterop/HostInterop.h | Bin 2098 -> 468 bytes oldgui/command.cpp | 2 +- oldgui/main.cpp | 2 +- oldgui/window.cpp | 2 +- texthook/host.cc | 8 ++++---- texthook/host.h | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/GUI/HostInterop/HostInterop.h b/GUI/HostInterop/HostInterop.h index 617c60f162c2ba46a86fc6549acbfabf96fa4dfa..ce127d23ed0c4e692501128e708d8b5f0d5e1df5 100644 GIT binary patch delta 22 acmdlaaD{opF-E>x25Sah1}=tLFbM!jg#>~C literal 2098 zcmb`I%}?7v5XI-}O8p;JvJpK>Kv4vg`Cx^jv@WgBh*9JqpR@ju|klbd(m=bA;B6-vx*rol7 zUISK+VkYux+xfZ1KVene3*Hye<2GJ1-d1*P6XNxVrkuWU7pH=i9`|655@2v$uZfG+ zmQjTX*M5!Y$eG;`ZHztG7vc=yuy1d9yG<;Pt(7C}cQr~zB-L+%?UG12e9FXMOiLIR zpr$)E<27tPf-2ny30`B{1wnd_iLbrtu~Ya-{~?$KsO|OuU-?zOu0(3<#MP=&%IiJ< zPcc(x)sDPPJ#qaGhU#?=yE#wlr=@+Mk4~u1z8%|FYIMOpP$|{S**(;`Yh64Z$8(HL z6+MnQsfR`{!f6-0$2ci3^@RG&Rc=$}5RH8OJocp~G?2-{w5HkVqf<9N6|th}ByU%C zok7K0$coUZ`{9hpCD=DGR`q>)s`DVTbbc<_Go|j@>&!Q0mUet=&(c^*Ph2mvNd@cg>YgPR@jP?@tod!t;JOsRyW<`nh^fXLyCS`s!RIUxt+5 lE*#r1UXGzF(Ddw6`K=@P>GW?mK1avq%esxJSE%}4`~|!bO)vle diff --git a/oldgui/command.cpp b/oldgui/command.cpp index 4fad94a..3b791f8 100644 --- a/oldgui/command.cpp +++ b/oldgui/command.cpp @@ -41,7 +41,7 @@ DWORD ProcessCommand(const std::wstring& cmd, DWORD pid) { HookParam hp = {}; if (Parse(m[1].str(), hp)) - InsertHook(pid, hp); + InsertHook(pid, &hp); } else if (regex_match(cmd, m, wregex(L":(?:h|help)", wregex::icase))) { diff --git a/oldgui/main.cpp b/oldgui/main.cpp index bc72605..a884736 100644 --- a/oldgui/main.cpp +++ b/oldgui/main.cpp @@ -149,7 +149,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine if (StartHost()) { SetUnhandledExceptionFilter(UnhandledExcept); - man = GetHostHookManager(); + GetHostHookManager(&man); pfman = new ProfileManager(); DefaultSettings(); LoadSettings(); diff --git a/oldgui/window.cpp b/oldgui/window.cpp index ba0abc1..81d9a5e 100644 --- a/oldgui/window.cpp +++ b/oldgui/window.cpp @@ -488,7 +488,7 @@ void RegisterProcess(DWORD pid) { for (auto i = pf->Hooks().begin(); i != pf->Hooks().end(); ++i) { - InsertHook(pid, i->get()->HP(), toMultiByteString(i->get()->Name())); + InsertHook(pid, &i->get()->HP(), toMultiByteString(i->get()->Name())); } } } diff --git a/texthook/host.cc b/texthook/host.cc index 7391c47..5e5393b 100644 --- a/texthook/host.cc +++ b/texthook/host.cc @@ -117,19 +117,19 @@ DLLEXPORT bool DetachProcessById(DWORD processId) return WriteFile(man->GetHostPipe(processId), &command, sizeof(command), &unused, nullptr); } -DLLEXPORT HookManager* GetHostHookManager() +DLLEXPORT void GetHostHookManager(HookManager** hookman) { - return man; + *hookman = man; } -DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name) +DLLEXPORT bool InsertHook(DWORD pid, const HookParam *hp, std::string name) { HANDLE commandPipe = man->GetHostPipe(pid); if (commandPipe == nullptr) return false; BYTE buffer[PIPE_BUFFER_SIZE] = {}; *(DWORD*)buffer = HOST_COMMAND_NEW_HOOK; - *(HookParam*)(buffer + sizeof(DWORD)) = hp; + *(HookParam*)(buffer + sizeof(DWORD)) = *hp; if (name.size()) strcpy((char*)buffer + sizeof(DWORD) + sizeof(HookParam), name.c_str()); DWORD unused; return WriteFile(commandPipe, buffer, sizeof(DWORD) + sizeof(HookParam) + name.size(), &unused, nullptr); diff --git a/texthook/host.h b/texthook/host.h index 876d0b2..3919943 100644 --- a/texthook/host.h +++ b/texthook/host.h @@ -12,10 +12,10 @@ DLLEXPORT void OpenHost(); DLLEXPORT bool StartHost(); DLLEXPORT void CloseHost(); -DLLEXPORT HookManager* GetHostHookManager(); +DLLEXPORT void GetHostHookManager(HookManager **hookman); DLLEXPORT bool InjectProcessById(DWORD pid, DWORD timeout = 5000); DLLEXPORT bool DetachProcessById(DWORD pid); -DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name = ""); +DLLEXPORT bool InsertHook(DWORD pid, const HookParam *hp, std::string name = ""); DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr); // EOF From dd6230c92de95cd818b1833652e2ce8e6976c879 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Fri, 20 Jul 2018 23:44:03 -0400 Subject: [PATCH 09/29] Revert "add project for c#/c++ interoperability" This reverts commit a8cdee67e8d06ef1265bbac1629a101aa63b344e. --- GUI.sln | 24 ---- GUI/HostInterop/AssemblyInfo.cpp | Bin 1522 -> 0 bytes GUI/HostInterop/HostInterop.h | Bin 468 -> 0 bytes GUI/HostInterop/HostInterop.vcxproj | 151 -------------------- GUI/HostInterop/HostInterop.vcxproj.filters | 29 ---- GUI/HostInterop/stdafx.cpp | Bin 44 -> 0 bytes GUI/HostInterop/stdafx.h | Bin 30 -> 0 bytes 7 files changed, 204 deletions(-) delete mode 100644 GUI/HostInterop/AssemblyInfo.cpp delete mode 100644 GUI/HostInterop/HostInterop.h delete mode 100644 GUI/HostInterop/HostInterop.vcxproj delete mode 100644 GUI/HostInterop/HostInterop.vcxproj.filters delete mode 100644 GUI/HostInterop/stdafx.cpp delete mode 100644 GUI/HostInterop/stdafx.h diff --git a/GUI.sln b/GUI.sln index 8f9287d..7e2eba0 100644 --- a/GUI.sln +++ b/GUI.sln @@ -5,40 +5,16 @@ VisualStudioVersion = 15.0.27703.2042 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUI", "GUI\GUI.csproj", "{D847D0DC-78F5-4722-8E06-1A124B957353}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HostInterop", "GUI\HostInterop\HostInterop.vcxproj", "{C3534056-72B3-4651-B69C-6E428F675861}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x64.ActiveCfg = Debug|Any CPU - {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x64.Build.0 = Debug|Any CPU - {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.ActiveCfg = Debug|Any CPU - {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.Build.0 = Debug|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|Any CPU.ActiveCfg = Release|Any CPU {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|Any CPU.Build.0 = Release|Any CPU - {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|x64.ActiveCfg = Release|Any CPU - {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|x64.Build.0 = Release|Any CPU - {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|x86.ActiveCfg = Release|Any CPU - {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|x86.Build.0 = Release|Any CPU - {C3534056-72B3-4651-B69C-6E428F675861}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {C3534056-72B3-4651-B69C-6E428F675861}.Debug|x64.ActiveCfg = Debug|x64 - {C3534056-72B3-4651-B69C-6E428F675861}.Debug|x64.Build.0 = Debug|x64 - {C3534056-72B3-4651-B69C-6E428F675861}.Debug|x86.ActiveCfg = Debug|Win32 - {C3534056-72B3-4651-B69C-6E428F675861}.Debug|x86.Build.0 = Debug|Win32 - {C3534056-72B3-4651-B69C-6E428F675861}.Release|Any CPU.ActiveCfg = Release|Win32 - {C3534056-72B3-4651-B69C-6E428F675861}.Release|x64.ActiveCfg = Release|x64 - {C3534056-72B3-4651-B69C-6E428F675861}.Release|x64.Build.0 = Release|x64 - {C3534056-72B3-4651-B69C-6E428F675861}.Release|x86.ActiveCfg = Release|Win32 - {C3534056-72B3-4651-B69C-6E428F675861}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GUI/HostInterop/AssemblyInfo.cpp b/GUI/HostInterop/AssemblyInfo.cpp deleted file mode 100644 index c11d2a380dab0f8e6378793ffd7c02d711668133..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1522 zcmcJPPfNp45X9#!_#H~UsE4ASq@GGWDD+Tl=|w!GF|EO-3Hc-T%d5YAQE1c$jZ{J* z-MpRIo!NcwZ%2x?R;ZOKJt|U1T@@;M546y$dU_&qr~~(__-5yqoLh4uA)`>nTj+8p z+PLh9FITNI`s(WroCVcHAeL~Y@cb32BG)jiO2Zz4mQZc%@N-Rw=X&A4RbT9l=hpEW zb_&~mSPUh^qnv6>RJMM`uKt8b!BcDX)rC2Bk1zJc+?dm|WevVXrVh{a9)Mdq{5d=& zkzFwg@$qus+;&_MWLT5VHC6m!^0;bLvyJHem z%=PT$Z;L!czZLzM^Zq&BM%{HgX5i(_t$$j(XwgHFo=zKz_!#OtXa=I8+E&`0El^}d bB-+x{k1*m+sc|y0VmFh&yJ`DeHBdhQ4(<<7 diff --git a/GUI/HostInterop/HostInterop.h b/GUI/HostInterop/HostInterop.h deleted file mode 100644 index ce127d23ed0c4e692501128e708d8b5f0d5e1df5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 468 zcmbtQK@Ng25S*(f{-Ke(KEMy?*%y?8V1z;nBpAcj)!9uOFP=0_w%yt8%zJAPwI+`=BHW9i@@ zK5;hC>xV;pO~hozinDoYv{V|n5vw~eIODEeB`XJ=M7qBHJVyQ#tKvN{@3rHham8#x pj)r=UYBJM>w|EMyINk<{#n3?>YvM&}gKnW=EFxoKUZS@}@i%l - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {C3534056-72B3-4651-B69C-6E428F675861} - v4.6 - ManagedCProj - HostInterop - 10.0.17134.0 - - - - DynamicLibrary - true - v141 - true - Unicode - - - DynamicLibrary - false - v141 - true - Unicode - - - DynamicLibrary - true - v141 - true - Unicode - - - DynamicLibrary - false - v141 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - - - true - - - false - - - false - - - - Level3 - Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) - Use - - - - - - - - Level3 - Disabled - _DEBUG;%(PreprocessorDefinitions) - Use - - - - - - - - Level3 - WIN32;NDEBUG;%(PreprocessorDefinitions) - Use - - - - - - - - Level3 - NDEBUG;%(PreprocessorDefinitions) - Use - - - - - - - - - - - - - - - - - - Create - Create - Create - Create - - - - - - \ No newline at end of file diff --git a/GUI/HostInterop/HostInterop.vcxproj.filters b/GUI/HostInterop/HostInterop.vcxproj.filters deleted file mode 100644 index 3e0ae9a..0000000 --- a/GUI/HostInterop/HostInterop.vcxproj.filters +++ /dev/null @@ -1,29 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;ipp;xsd - - - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/GUI/HostInterop/stdafx.cpp b/GUI/HostInterop/stdafx.cpp deleted file mode 100644 index 08343afdf790eaa4524432ba7b0c3ddc395ee814..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 xcmezWPnjW;A&()MA%~%qA%!88L4iSup_rir$WCNPW2j)zW5@uC@G@{Q008!y2%rD} diff --git a/GUI/HostInterop/stdafx.h b/GUI/HostInterop/stdafx.h deleted file mode 100644 index 866b7f5b647af399e28ddfc986dce977754dd671..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30 kcmezWPnn^Bp@<=oA)O%?NGdSoGvqNOGo&)`GH@{f0E5H^bpQYW From 224af9180660f86f6f9b0c5763be12a158405522 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 21 Jul 2018 00:51:27 -0400 Subject: [PATCH 10/29] add host interoperability project, and add everything to organized build system --- .gitignore | 2 +- CMakeSettings.json | 6 +- GUI.sln | 18 ++- GUI/GUI.csproj | 26 ++++ HostInterop/AssemblyInfo.cpp | Bin 0 -> 1522 bytes HostInterop/HostInterop.cpp | Bin 0 -> 104 bytes HostInterop/HostInterop.h | Bin 0 -> 398 bytes HostInterop/HostInterop.vcxproj | 154 ++++++++++++++++++++++++ HostInterop/HostInterop.vcxproj.filters | 32 +++++ HostInterop/stdafx.cpp | Bin 0 -> 44 bytes HostInterop/stdafx.h | Bin 0 -> 30 bytes 11 files changed, 228 insertions(+), 10 deletions(-) create mode 100644 HostInterop/AssemblyInfo.cpp create mode 100644 HostInterop/HostInterop.cpp create mode 100644 HostInterop/HostInterop.h create mode 100644 HostInterop/HostInterop.vcxproj create mode 100644 HostInterop/HostInterop.vcxproj.filters create mode 100644 HostInterop/stdafx.cpp create mode 100644 HostInterop/stdafx.h diff --git a/.gitignore b/.gitignore index 1647eef..44eddfa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) -CMakeBuilds/ +Builds/ [Bb]in/ [Oo]bj/ diff --git a/CMakeSettings.json b/CMakeSettings.json index ad46300..b7b4bdf 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -6,7 +6,7 @@ "generator": "Ninja", "configurationType": "Debug", "inheritEnvironments": [ "msvc_x86" ], - "buildRoot": "${workspaceRoot}\\CMakeBuilds\\build\\${name}", + "buildRoot": "${workspaceRoot}\\Builds\\${configurationType}", "installRoot": "${workspaceRoot}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", "cmakeCommandArgs": "", "buildCommandArgs": "-v", @@ -15,9 +15,9 @@ { "name": "x86-Release", "generator": "Ninja", - "configurationType": "MinSizeRel", + "configurationType": "Release", "inheritEnvironments": [ "msvc_x86" ], - "buildRoot": "${workspaceRoot}\\CMakeBuilds\\build\\${name}", + "buildRoot": "${workspaceRoot}\\Builds\\${configurationType}", "installRoot": "${workspaceRoot}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", "cmakeCommandArgs": "", "buildCommandArgs": "-v", diff --git a/GUI.sln b/GUI.sln index 7e2eba0..1a657a6 100644 --- a/GUI.sln +++ b/GUI.sln @@ -5,16 +5,22 @@ VisualStudioVersion = 15.0.27703.2042 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUI", "GUI\GUI.csproj", "{D847D0DC-78F5-4722-8E06-1A124B957353}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HostInterop", "HostInterop\HostInterop.vcxproj", "{0DF42D0B-0F87-475E-9E2C-9E32776B2AD5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|Any CPU.Build.0 = Release|Any CPU + {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.ActiveCfg = Debug|x86 + {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.Build.0 = Debug|x86 + {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|x86.ActiveCfg = Release|x86 + {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|x86.Build.0 = Release|x86 + {0DF42D0B-0F87-475E-9E2C-9E32776B2AD5}.Debug|x86.ActiveCfg = Debug|Win32 + {0DF42D0B-0F87-475E-9E2C-9E32776B2AD5}.Debug|x86.Build.0 = Debug|Win32 + {0DF42D0B-0F87-475E-9E2C-9E32776B2AD5}.Release|x86.ActiveCfg = Release|Win32 + {0DF42D0B-0F87-475E-9E2C-9E32776B2AD5}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GUI/GUI.csproj b/GUI/GUI.csproj index 2acd95c..3a00a0d 100644 --- a/GUI/GUI.csproj +++ b/GUI/GUI.csproj @@ -31,6 +31,26 @@ prompt 4 + + true + ..\Builds\Debug\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + ..\Builds\Release\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + true + @@ -75,5 +95,11 @@ + + + {0df42d0b-0f87-475e-9e2c-9e32776b2ad5} + HostInterop + + \ No newline at end of file diff --git a/HostInterop/AssemblyInfo.cpp b/HostInterop/AssemblyInfo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c11d2a380dab0f8e6378793ffd7c02d711668133 GIT binary patch literal 1522 zcmcJPPfNp45X9#!_#H~UsE4ASq@GGWDD+Tl=|w!GF|EO-3Hc-T%d5YAQE1c$jZ{J* z-MpRIo!NcwZ%2x?R;ZOKJt|U1T@@;M546y$dU_&qr~~(__-5yqoLh4uA)`>nTj+8p z+PLh9FITNI`s(WroCVcHAeL~Y@cb32BG)jiO2Zz4mQZc%@N-Rw=X&A4RbT9l=hpEW zb_&~mSPUh^qnv6>RJMM`uKt8b!BcDX)rC2Bk1zJc+?dm|WevVXrVh{a9)Mdq{5d=& zkzFwg@$qus+;&_MWLT5VHC6m!^0;bLvyJHem z%=PT$Z;L!czZLzM^Zq&BM%{HgX5i(_t$$j(XwgHFo=zKz_!#OtXa=I8+E&`0El^}d bB-+x{k1*m+sc|y0VmFh&yJ`DeHBdhQ4(<<7 literal 0 HcmV?d00001 diff --git a/HostInterop/HostInterop.cpp b/HostInterop/HostInterop.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bef4b4090aba63c40346f1a8bc7eae63d4081c00 GIT binary patch literal 104 zcmezWPnjW;A&()MA%~%qA%!88L4iSup_rir$WCNPW2j)zW5@uC@G@|LF*em64EbOk Qo?;WdHyG literal 0 HcmV?d00001 diff --git a/HostInterop/HostInterop.h b/HostInterop/HostInterop.h new file mode 100644 index 0000000000000000000000000000000000000000..57f3e8fd73c5e0ff1445ca581813218ec8687d92 GIT binary patch literal 398 zcma)&yAFat6hx=R#DCbtMoTTFw9#6BU?ECO_R9ECKmg+_s*PweZN+! zRLd$;&TdqyP^znzT7=cA(Vbq#tU!@7Yw*~hGCJ!??dax;C)b?v2-_=9aJ;^zuAus) zF6 + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {0DF42D0B-0F87-475E-9E2C-9E32776B2AD5} + v4.6 + ManagedCProj + HostInterop + 10.0.17134.0 + + + + DynamicLibrary + true + v141 + true + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + DynamicLibrary + true + v141 + true + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\Builds\$(Configuration)\$(Configuration)\ + + + true + + + false + $(SolutionDir)\Builds\$(Configuration)\$(Configuration)\ + + + false + + + + Level3 + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + Use + + + + + + + + Level3 + Disabled + _DEBUG;%(PreprocessorDefinitions) + Use + + + + + + + + Level3 + WIN32;NDEBUG;%(PreprocessorDefinitions) + Use + + + + + + + + Level3 + NDEBUG;%(PreprocessorDefinitions) + Use + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/HostInterop/HostInterop.vcxproj.filters b/HostInterop/HostInterop.vcxproj.filters new file mode 100644 index 0000000..a76d3b0 --- /dev/null +++ b/HostInterop/HostInterop.vcxproj.filters @@ -0,0 +1,32 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/HostInterop/stdafx.cpp b/HostInterop/stdafx.cpp new file mode 100644 index 0000000000000000000000000000000000000000..08343afdf790eaa4524432ba7b0c3ddc395ee814 GIT binary patch literal 44 xcmezWPnjW;A&()MA%~%qA%!88L4iSup_rir$WCNPW2j)zW5@uC@G@{Q008!y2%rD} literal 0 HcmV?d00001 diff --git a/HostInterop/stdafx.h b/HostInterop/stdafx.h new file mode 100644 index 0000000000000000000000000000000000000000..866b7f5b647af399e28ddfc986dce977754dd671 GIT binary patch literal 30 kcmezWPnn^Bp@<=oA)O%?NGdSoGvqNOGo&)`GH@{f0E5H^bpQYW literal 0 HcmV?d00001 From af19b4e48165d704d0f3e32c20270b2551846c20 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 21 Jul 2018 01:08:55 -0700 Subject: [PATCH 11/29] start implementing hostinterop --- GUI/Program.cs | 1 + HostInterop/HostInterop.h | Bin 398 -> 394 bytes HostInterop/HostInterop.vcxproj | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/GUI/Program.cs b/GUI/Program.cs index 17aaf57..c955516 100644 --- a/GUI/Program.cs +++ b/GUI/Program.cs @@ -14,6 +14,7 @@ namespace GUI [STAThread] static void Main() { + if (!HostInterop.Host.Start()) return; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); diff --git a/HostInterop/HostInterop.h b/HostInterop/HostInterop.h index 57f3e8fd73c5e0ff1445ca581813218ec8687d92..1f9aad4747c734005c2e880694027412bacb9d83 100644 GIT binary patch delta 138 zcmeBU?qZ&xWU0)M$&km8%#g!S%8l5E@SWZOaUse - + ..\Builds\Debug\texthook\vnrhost.lib;%(AdditionalDependencies) From a5232b605e52b647c047c1966ecaaed9277ac7f9 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 21 Jul 2018 08:22:32 -0700 Subject: [PATCH 12/29] update config/readme --- README.md | 3 ++- vnrhook/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 621af6b..087ea17 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,8 @@ GPL v3 ## Compiling -Before compiling *NextHooker*, You should get CMake, [Windows Driver Kit 7.1](http://www.microsoft.com/en-us/download/details.aspx?id=11800), and Visual Studio. +Before compiling *NextHooker*, you should get Visual Studio with CMake, ATL, and .NET 4.6.
+You should first compile all the CMake projects, then compile the *GUI* solution. ## Project Architecture diff --git a/vnrhook/CMakeLists.txt b/vnrhook/CMakeLists.txt index 9efcc1d..365a7ad 100644 --- a/vnrhook/CMakeLists.txt +++ b/vnrhook/CMakeLists.txt @@ -77,7 +77,7 @@ target_compile_options(vnrhook PRIVATE ) set(vnrhook_libs - "C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.17134.0\\um\\x86\\ntdll.lib" + ntdll.lib Version.lib ) From a2924f4a2cc56a075fe33aa95f08b5fa60ecf2d4 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 21 Jul 2018 11:09:07 -0700 Subject: [PATCH 13/29] continue implementing interop --- GUI.sln | 3 +++ GUI/GUI.csproj | 10 ++++------ HostInterop/AssemblyInfo.cpp | Bin 1522 -> 1476 bytes HostInterop/HostInterop.cpp | Bin 104 -> 4858 bytes HostInterop/HostInterop.h | Bin 394 -> 1718 bytes HostInterop/HostInterop.vcxproj | 15 ++++----------- HostInterop/HostInterop.vcxproj.filters | 18 ++++++------------ HostInterop/stdafx.cpp | Bin 44 -> 0 bytes HostInterop/stdafx.h | Bin 30 -> 0 bytes oldgui/ProcessWindow.cpp | 4 ++-- oldgui/command.cpp | 4 ++-- oldgui/main.cpp | 2 +- oldgui/window.cpp | 2 +- texthook/hookman.cc | 3 +-- texthook/hookman.h | 2 +- texthook/host.cc | 12 ++++++------ texthook/host.h | 8 ++++---- 17 files changed, 35 insertions(+), 48 deletions(-) delete mode 100644 HostInterop/stdafx.cpp delete mode 100644 HostInterop/stdafx.h diff --git a/GUI.sln b/GUI.sln index 1a657a6..22cde48 100644 --- a/GUI.sln +++ b/GUI.sln @@ -4,6 +4,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 VisualStudioVersion = 15.0.27703.2042 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUI", "GUI\GUI.csproj", "{D847D0DC-78F5-4722-8E06-1A124B957353}" + ProjectSection(ProjectDependencies) = postProject + {0DF42D0B-0F87-475E-9E2C-9E32776B2AD5} = {0DF42D0B-0F87-475E-9E2C-9E32776B2AD5} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HostInterop", "HostInterop\HostInterop.vcxproj", "{0DF42D0B-0F87-475E-9E2C-9E32776B2AD5}" EndProject diff --git a/GUI/GUI.csproj b/GUI/GUI.csproj index 3a00a0d..16eeb66 100644 --- a/GUI/GUI.csproj +++ b/GUI/GUI.csproj @@ -52,6 +52,10 @@ true + + False + ..\Builds\Debug\Debug\HostInterop.dll + @@ -95,11 +99,5 @@ - - - {0df42d0b-0f87-475e-9e2c-9e32776b2ad5} - HostInterop - - \ No newline at end of file diff --git a/HostInterop/AssemblyInfo.cpp b/HostInterop/AssemblyInfo.cpp index c11d2a380dab0f8e6378793ffd7c02d711668133..58c692ea021eb3efc86bd57283c4df1d12172ace 100644 GIT binary patch delta 11 ScmeyweT19o|G$j}hgbn2_62kR delta 55 zcmX@Y{fS%q|377hOolv$WQH7uQic?UR0ahGC5B>#5+FN~A&sGeL60E=D8kFYHSwY0 IM!jjQ0HSmav;Y7A diff --git a/HostInterop/HostInterop.cpp b/HostInterop/HostInterop.cpp index bef4b4090aba63c40346f1a8bc7eae63d4081c00..5667ebec5bbf01deff6c09cf4398b403c55d3e1c 100644 GIT binary patch literal 4858 zcmb_g+fExX5S`al>OZVNm6l%MovM{k=uK(?kw|@Ol_s#HXeoxnl|RyWf`G*jTKKmB=s-scLq$Rg%=?pt>)nlHb?Vjx8??%5*N*!(3IKt@}R<5BdhlUv}8yi0~ z1AHoEN44GQXQM_VBQ?Xhclh@eQQunNXu!%GJ7cVOU}3V58LPEI-pG3)nkJ&n{R5Th zz@HqpjV}#3hXhYD8(m1|p4L9riQDSm!8eQMmE!4_dd&J9`;<3N#>RaaHRglWbfS0| z>r4@b@k(cnv5wEbfClSt7|#&To!l>wIy9+lr)@LiHc!{E*GKL%MZ-EEvSU=t89ZlX zGstG;iq&Xho&g87HRPAZVo=e;T4Anco~F4smGF@C}#=Zd+ z<5l4NL|Dc41ik-+)mfRHP}W5x^rJwvjd1Q}p)0nMdwLn?G#2V-HLr>+Ip8fyI>rAP^2qGk2!)vGYn>i@b~TzqjhN}U$DpaxlR~oxCQnx${bF^n z_H3k6k$LUq%kSe0M2>sRlwn4Gv%KfHwLY>j0^jr(4=; zXvtQE@r+f@{%2*@d*nEcb$kN9$nc?0w!;kLhg{)%<6DirSZ2v;4?PIkJ)I5mI_LQW zv!4^0$mqA@V!t{V$+{D+S{vvZfBL1Zw_uI-$+T7baYfi#s3NMke2#7c3eQe8xb_p8IP<*M$AQPuTW6-kPpL$6ziuAO7+_W3$Eii-wN~v`H(E`Plu**3@6SRdcK3Y9f0e3zqy2NU-E-a zUb0bDE`#)WS;t*Zqj~q?uHR1&N9RxGs@2WVkx}g};wjMa-V;Y;b8JT6Cdk6u=mAji zjf8K8apXeThh~;lVs$FPSKkIeJLkdGGg93vRLoq`Mke$eo#!$ha$B+RO;bq{O7%MsP Gp9%njpA8HE diff --git a/HostInterop/HostInterop.h b/HostInterop/HostInterop.h index 1f9aad4747c734005c2e880694027412bacb9d83..e6daf99662990b5a134f5fb967925539a1ea48c2 100644 GIT binary patch literal 1718 zcma)6O>Y`85Iuv$f3Vccrb@+qqgINbZE|QqY^2_h%z6TDjJ>jFQKN|e-m$$Orm!@( z_002|@tb-3p|X6t=gjC5CgaJtc2aMd=6i4zFM$=W4q<=iQ+@EkIDFIy)>M=6+hjbl z@XSiZonsHiy!_yrZ^6=~B=H1)@M&j(i_^Hju;iBe8Yw(L+XSVmdZT2?(n<~)-#E0q zMCO1i5QOvXL~Kr9BZnO6+QCS{78U61#@A`X8)FnJkXt;B2lz}G2dDAmb!+w=Fgc$B z1M#E&4J9tEDZ#mH{%f(Cvr3Bd4(5Qo+zkMy)8-8grvn@6_Jp@V^`I5oRkzHc$aB)A zo#c7J73|FceD^6!?2rp#!;K>;)rol`d^ciBd%dOK1-7fcG@7ibl(+|m_Y7@m>KPif<=v0jad;!ux3cBA8}rdFsE*N!s*HoMlDIRZ9mDG z!*G&sG@XWqSFn|2T^l_&BjX{=xKbNlzGN}@^*}PTF;+g`us2o)Ap1KFHYdmz1-cVh8P4v=xc|iW3Fr!LCpEo929@`gTS41pT6`lBJ2-wG(!gM7nEwB`LoYvdEOuXb?14;n{%jS bx7e2|c1bGOeWyQzA=bF2!6K2j>EQSmQQIf~ literal 394 zcmZvXOAdlS5JYQ>iFY8d(GBk90=o7DfcT00GC-m+UR`}N0&zhm)6-quRn_x(hiYV$ z=&X|Ht5&5*DB-oZ zCeFv#K@Z2u>BJLjZgMy1@6oaDhPpx3?)r4mqwbZez%ip^m^$%hAPFul`KV%9-YjoB vXnAh01h1c3ieHVA-MK^05La4LKe^Vc!`-mm|HSBLCu2IVLH#xS_E&!a=>9(2 diff --git a/HostInterop/HostInterop.vcxproj b/HostInterop/HostInterop.vcxproj index 6ccee25..9bf83ac 100644 --- a/HostInterop/HostInterop.vcxproj +++ b/HostInterop/HostInterop.vcxproj @@ -92,7 +92,7 @@ Level3 Disabled WIN32;_DEBUG;%(PreprocessorDefinitions) - Use + NotUsing ..\Builds\Debug\texthook\vnrhost.lib;%(AdditionalDependencies) @@ -134,19 +134,12 @@ - - - - - - Create - Create - Create - Create - + + + diff --git a/HostInterop/HostInterop.vcxproj.filters b/HostInterop/HostInterop.vcxproj.filters index a76d3b0..68e44a6 100644 --- a/HostInterop/HostInterop.vcxproj.filters +++ b/HostInterop/HostInterop.vcxproj.filters @@ -6,18 +6,10 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - {93995380-89BD-4b04-88EB-625FBE52EBFB} + {344ee2d7-eda2-4003-8c18-092ff651deb3} h;hh;hpp;hxx;hm;inl;inc;ipp;xsd
- - - Header Files - - - Header Files - - Source Files @@ -25,8 +17,10 @@ Source Files - - Source Files - + + + + Header Files + \ No newline at end of file diff --git a/HostInterop/stdafx.cpp b/HostInterop/stdafx.cpp deleted file mode 100644 index 08343afdf790eaa4524432ba7b0c3ddc395ee814..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 xcmezWPnjW;A&()MA%~%qA%!88L4iSup_rir$WCNPW2j)zW5@uC@G@{Q008!y2%rD} diff --git a/HostInterop/stdafx.h b/HostInterop/stdafx.h deleted file mode 100644 index 866b7f5b647af399e28ddfc986dce977754dd671..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30 kcmezWPnn^Bp@<=oA)O%?NGdSoGvqNOGo&)`GH@{f0E5H^bpQYW diff --git a/oldgui/ProcessWindow.cpp b/oldgui/ProcessWindow.cpp index 91eccee..06e62f9 100644 --- a/oldgui/ProcessWindow.cpp +++ b/oldgui/ProcessWindow.cpp @@ -75,7 +75,7 @@ void ProcessWindow::RefreshProcess() void ProcessWindow::AttachProcess() { DWORD pid = GetSelectedPID(); - if (InjectProcessById(pid)) + if (InjectProcess(pid)) RefreshThreadWithPID(pid, true); else ConsoleOutput(L"NextHooker: could not inject"); @@ -84,7 +84,7 @@ void ProcessWindow::AttachProcess() void ProcessWindow::DetachProcess() { DWORD pid = GetSelectedPID(); - DetachProcessById(pid); + ::DetachProcess(pid); RefreshThreadWithPID(pid, false); } diff --git a/oldgui/command.cpp b/oldgui/command.cpp index 3b791f8..02197d4 100644 --- a/oldgui/command.cpp +++ b/oldgui/command.cpp @@ -35,13 +35,13 @@ DWORD ProcessCommand(const std::wstring& cmd, DWORD pid) if (regex_match(cmd, m, wregex(L"/p(\\d+)", wregex::icase))) { pid = std::stoul(m[1].str()); - InjectProcessById(pid); + InjectProcess(pid); } else if (regex_match(cmd, m, wregex(L"/h(.+)", wregex::icase))) { HookParam hp = {}; if (Parse(m[1].str(), hp)) - InsertHook(pid, &hp); + InsertHook(pid, hp); } else if (regex_match(cmd, m, wregex(L":(?:h|help)", wregex::icase))) { diff --git a/oldgui/main.cpp b/oldgui/main.cpp index a884736..bc72605 100644 --- a/oldgui/main.cpp +++ b/oldgui/main.cpp @@ -149,7 +149,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine if (StartHost()) { SetUnhandledExceptionFilter(UnhandledExcept); - GetHostHookManager(&man); + man = GetHostHookManager(); pfman = new ProfileManager(); DefaultSettings(); LoadSettings(); diff --git a/oldgui/window.cpp b/oldgui/window.cpp index 81d9a5e..ba0abc1 100644 --- a/oldgui/window.cpp +++ b/oldgui/window.cpp @@ -488,7 +488,7 @@ void RegisterProcess(DWORD pid) { for (auto i = pf->Hooks().begin(); i != pf->Hooks().end(); ++i) { - InsertHook(pid, &i->get()->HP(), toMultiByteString(i->get()->Name())); + InsertHook(pid, i->get()->HP(), toMultiByteString(i->get()->Name())); } } } diff --git a/texthook/hookman.cc b/texthook/hookman.cc index 9d723ca..ea0ec1b 100644 --- a/texthook/hookman.cc +++ b/texthook/hookman.cc @@ -141,10 +141,9 @@ void HookManager::DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD spl, con it->AddText(text, len); } -void HookManager::AddConsoleOutput(LPCWSTR text) +void HookManager::AddConsoleOutput(std::wstring text) { HM_LOCK; - int len = wcslen(text) * 2; TextThread *console = textThreadsByParams[{ 0, -1UL, -1UL, -1UL }]; console->AddSentence(std::wstring(text)); } diff --git a/texthook/hookman.h b/texthook/hookman.h index fb5328a..f2b1241 100644 --- a/texthook/hookman.h +++ b/texthook/hookman.h @@ -42,7 +42,7 @@ public: void ClearCurrent(); void SelectCurrent(DWORD num); void SetCurrent(TextThread *it); - void AddConsoleOutput(LPCWSTR text); + void AddConsoleOutput(std::wstring text); void DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD split, const BYTE *text, int len); void RemoveProcessContext(DWORD pid); // private void RemoveSingleHook(DWORD pid, DWORD addr); diff --git a/texthook/host.cc b/texthook/host.cc index 5e5393b..8a691e0 100644 --- a/texthook/host.cc +++ b/texthook/host.cc @@ -78,7 +78,7 @@ DLLEXPORT void CloseHost() } } -DLLEXPORT bool InjectProcessById(DWORD processId, DWORD timeout) +DLLEXPORT bool InjectProcess(DWORD processId, DWORD timeout) { if (processId == GetCurrentProcessId()) return false; @@ -110,26 +110,26 @@ DLLEXPORT bool InjectProcessById(DWORD processId, DWORD timeout) return false; } -DLLEXPORT bool DetachProcessById(DWORD processId) +DLLEXPORT bool DetachProcess(DWORD processId) { DWORD command = HOST_COMMAND_DETACH; DWORD unused; return WriteFile(man->GetHostPipe(processId), &command, sizeof(command), &unused, nullptr); } -DLLEXPORT void GetHostHookManager(HookManager** hookman) +DLLEXPORT HookManager* GetHostHookManager() { - *hookman = man; + return man; } -DLLEXPORT bool InsertHook(DWORD pid, const HookParam *hp, std::string name) +DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name) { HANDLE commandPipe = man->GetHostPipe(pid); if (commandPipe == nullptr) return false; BYTE buffer[PIPE_BUFFER_SIZE] = {}; *(DWORD*)buffer = HOST_COMMAND_NEW_HOOK; - *(HookParam*)(buffer + sizeof(DWORD)) = *hp; + *(HookParam*)(buffer + sizeof(DWORD)) = hp; if (name.size()) strcpy((char*)buffer + sizeof(DWORD) + sizeof(HookParam), name.c_str()); DWORD unused; return WriteFile(commandPipe, buffer, sizeof(DWORD) + sizeof(HookParam) + name.size(), &unused, nullptr); diff --git a/texthook/host.h b/texthook/host.h index 3919943..1166693 100644 --- a/texthook/host.h +++ b/texthook/host.h @@ -12,10 +12,10 @@ DLLEXPORT void OpenHost(); DLLEXPORT bool StartHost(); DLLEXPORT void CloseHost(); -DLLEXPORT void GetHostHookManager(HookManager **hookman); -DLLEXPORT bool InjectProcessById(DWORD pid, DWORD timeout = 5000); -DLLEXPORT bool DetachProcessById(DWORD pid); -DLLEXPORT bool InsertHook(DWORD pid, const HookParam *hp, std::string name = ""); +DLLEXPORT HookManager* GetHostHookManager(); +DLLEXPORT bool InjectProcess(DWORD pid, DWORD timeout = 5000); +DLLEXPORT bool DetachProcess(DWORD pid); +DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name = ""); DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr); // EOF From 4aef775a6a31ec7a0493d76a598f850e2bcab212 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 21 Jul 2018 14:01:38 -0700 Subject: [PATCH 14/29] prepare for use with c++/cli --- texthook/hookman.h | 4 ++-- texthook/textthread.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/texthook/hookman.h b/texthook/hookman.h index f2b1241..8b8dbfd 100644 --- a/texthook/hookman.h +++ b/texthook/hookman.h @@ -19,8 +19,8 @@ struct ProcessRecord HANDLE hostPipe; }; -typedef void(*ProcessEventCallback)(DWORD pid); -typedef void(*ThreadEventCallback)(TextThread*); +typedef void(__stdcall *ProcessEventCallback)(DWORD pid); +typedef void(__stdcall *ThreadEventCallback)(TextThread*); struct ThreadParameterHasher { diff --git a/texthook/textthread.h b/texthook/textthread.h index be7857e..321096f 100644 --- a/texthook/textthread.h +++ b/texthook/textthread.h @@ -25,7 +25,7 @@ struct ThreadParameter #define CURRENT_SELECT 0x1000 class TextThread; -typedef std::wstring(*ThreadOutputCallback)(TextThread*, std::wstring data); +typedef std::wstring(__stdcall *ThreadOutputCallback)(TextThread*, std::wstring data); //extern DWORD split_time,repeat_count,global_filter,cyclic_remove; From a4eede1b4edb52e9dbdfaa249087c340aad17c94 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 21 Jul 2018 14:16:26 -0700 Subject: [PATCH 15/29] remove unneeded logic from host --- texthook/hookman.cc | 33 ++------------------------------- texthook/hookman.h | 8 +------- 2 files changed, 3 insertions(+), 38 deletions(-) diff --git a/texthook/hookman.cc b/texthook/hookman.cc index ea0ec1b..2571940 100644 --- a/texthook/hookman.cc +++ b/texthook/hookman.cc @@ -20,7 +20,6 @@ HookManager::HookManager() : create(nullptr), remove(nullptr), - reset(nullptr), attach(nullptr), detach(nullptr), nextThreadNumber(0), @@ -31,8 +30,7 @@ HookManager::HookManager() : InitializeCriticalSection(&hmCs); // Console text thread - current = textThreadsByParams[{ 0, -1UL, -1UL, -1UL }] = new TextThread({ 0, -1UL, -1UL, -1UL }, nextThreadNumber++, splitDelay); - current->Status() |= USING_UNICODE | CURRENT_SELECT; + (textThreadsByParams[{ 0, -1UL, -1UL, -1UL }] = new TextThread({ 0, -1UL, -1UL, -1UL }, nextThreadNumber++, splitDelay))->Status() |= USING_UNICODE; } HookManager::~HookManager() @@ -50,25 +48,6 @@ TextThread *HookManager::FindSingle(DWORD number) return nullptr; } -void HookManager::SetCurrent(TextThread *it) -{ - HM_LOCK; - if (it == nullptr) return; - current->Status() &= ~CURRENT_SELECT; - current = it; - it->Status() |= CURRENT_SELECT; -} - -void HookManager::SelectCurrent(DWORD num) -{ - HM_LOCK; - if (TextThread *st = FindSingle(num)) - { - SetCurrent(st); - if (reset) reset(st); - } -} - void HookManager::RemoveSingleHook(DWORD pid, DWORD addr) { HM_LOCK; @@ -81,7 +60,6 @@ void HookManager::RemoveSingleHook(DWORD pid, DWORD addr) removedThreads.push_back(i.first); } for (auto i : removedThreads) textThreadsByParams.erase(i); - SelectCurrent(0); } void HookManager::RemoveProcessContext(DWORD pid) @@ -96,7 +74,6 @@ void HookManager::RemoveProcessContext(DWORD pid) removedThreads.push_back(i.first); } for (auto i : removedThreads) textThreadsByParams.erase(i); - SelectCurrent(0); } void HookManager::RegisterProcess(DWORD pid, HANDLE hostPipe) @@ -136,6 +113,7 @@ void HookManager::DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD spl, con if ((it = textThreadsByParams[tp]) == nullptr) { it = textThreadsByParams[tp] = new TextThread(tp, nextThreadNumber++, splitDelay); + if (GetHookParam(pid, hook).type & USING_UNICODE) it->Status() |= USING_UNICODE; if (create) create(it); } it->AddText(text, len); @@ -148,13 +126,6 @@ void HookManager::AddConsoleOutput(std::wstring text) console->AddSentence(std::wstring(text)); } -void HookManager::ClearCurrent() -{ - HM_LOCK; - current->Reset(); - if (reset) reset(current); -} - HANDLE HookManager::GetHostPipe(DWORD pid) { HM_LOCK; diff --git a/texthook/hookman.h b/texthook/hookman.h index 8b8dbfd..c5e939a 100644 --- a/texthook/hookman.h +++ b/texthook/hookman.h @@ -39,9 +39,6 @@ public: TextThread *FindSingle(DWORD number); HANDLE GetHostPipe(DWORD pid); - void ClearCurrent(); - void SelectCurrent(DWORD num); - void SetCurrent(TextThread *it); void AddConsoleOutput(std::wstring text); void DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD split, const BYTE *text, int len); void RemoveProcessContext(DWORD pid); // private @@ -53,7 +50,6 @@ public: void RegisterThreadCreateCallback(ThreadEventCallback cf) { create = cf; } void RegisterThreadRemoveCallback(ThreadEventCallback cf) { remove = cf; } - void RegisterThreadResetCallback(ThreadEventCallback cf) { reset = cf; } void RegisterProcessAttachCallback(ProcessEventCallback cf) { attach = cf; } void RegisterProcessDetachCallback(ProcessEventCallback cf) { detach = cf; } @@ -65,9 +61,7 @@ private: CRITICAL_SECTION hmCs; - TextThread *current; - - ThreadEventCallback create, remove, reset; + ThreadEventCallback create, remove; ProcessEventCallback attach, detach; WORD nextThreadNumber; From 3f6348e7d0a5c710cf891e38185acd6cea66103e Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 21 Jul 2018 14:19:25 -0700 Subject: [PATCH 16/29] remove old gui --- oldgui/ITH.h | 37 - oldgui/ITHVNR.rc | 70 - oldgui/ProcessWindow.cpp | 144 - oldgui/ProcessWindow.h | 23 - oldgui/ProfileManager.cpp | 166 - oldgui/ProfileManager.h | 34 - oldgui/TextBuffer.cpp | 49 - oldgui/TextBuffer.h | 21 - oldgui/command.cpp | 55 - oldgui/icon1.ico | Bin 766 -> 0 bytes oldgui/language.cpp | 128 - oldgui/language.h | 86 - oldgui/main.cpp | 176 - oldgui/profile/Profile.cpp | 293 - oldgui/profile/Profile.h | 171 - oldgui/profile/misc.cpp | 275 - oldgui/profile/misc.h | 22 - oldgui/profile/pugiconfig.h | 71 - oldgui/profile/pugixml.cpp | 11554 ---------------------------------- oldgui/profile/pugixml.h | 1366 ---- oldgui/resource.h | 14 - oldgui/utility.cpp | 302 - oldgui/utility.h | 107 - oldgui/version.h.in | 2 - oldgui/window.cpp | 646 -- oldgui/window.h | 20 - 26 files changed, 15832 deletions(-) delete mode 100644 oldgui/ITH.h delete mode 100644 oldgui/ITHVNR.rc delete mode 100644 oldgui/ProcessWindow.cpp delete mode 100644 oldgui/ProcessWindow.h delete mode 100644 oldgui/ProfileManager.cpp delete mode 100644 oldgui/ProfileManager.h delete mode 100644 oldgui/TextBuffer.cpp delete mode 100644 oldgui/TextBuffer.h delete mode 100644 oldgui/command.cpp delete mode 100644 oldgui/icon1.ico delete mode 100644 oldgui/language.cpp delete mode 100644 oldgui/language.h delete mode 100644 oldgui/main.cpp delete mode 100644 oldgui/profile/Profile.cpp delete mode 100644 oldgui/profile/Profile.h delete mode 100644 oldgui/profile/misc.cpp delete mode 100644 oldgui/profile/misc.h delete mode 100644 oldgui/profile/pugiconfig.h delete mode 100644 oldgui/profile/pugixml.cpp delete mode 100644 oldgui/profile/pugixml.h delete mode 100644 oldgui/resource.h delete mode 100644 oldgui/utility.cpp delete mode 100644 oldgui/utility.h delete mode 100644 oldgui/version.h.in delete mode 100644 oldgui/window.cpp delete mode 100644 oldgui/window.h diff --git a/oldgui/ITH.h b/oldgui/ITH.h deleted file mode 100644 index 097f8a9..0000000 --- a/oldgui/ITH.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) - * This file is part of the Interactive Text Hooker. - - * Interactive Text Hooker is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "profile/pugixml.h" -#pragma warning(disable: 4146) diff --git a/oldgui/ITHVNR.rc b/oldgui/ITHVNR.rc deleted file mode 100644 index 66c7081..0000000 --- a/oldgui/ITHVNR.rc +++ /dev/null @@ -1,70 +0,0 @@ -// Generated by ResEdit 1.6.6 -// Copyright (C) 2006-2015 -// http://www.resedit.net - -#include -#include -#include "resource.h" - - - - -// -// Dialog resources -// -LANGUAGE LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN -IDD_DIALOG2 DIALOGEX 100, 100, 341, 210 -STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU -CAPTION "Process Explorer" -FONT 8, "MS Shell Dlg", 400, 0, 1 -{ - DEFPUSHBUTTON "OK", IDOK, 281, 189, 53, 14, 0, WS_EX_LEFT - PUSHBUTTON "Remove Profile", IDC_BUTTON6, 226, 189, 53, 14, 0, WS_EX_LEFT - CONTROL "", IDC_LIST1, WC_LISTVIEW, WS_TABSTOP | WS_BORDER | LVS_ALIGNLEFT | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_REPORT, 7, 20, 327, 164, WS_EX_LEFT - LTEXT "Process", IDC_STATIC, 7, 7, 65, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT - PUSHBUTTON "Attach", IDC_BUTTON2, 61, 189, 53, 14, 0, WS_EX_LEFT - PUSHBUTTON "Detach", IDC_BUTTON3, 116, 189, 53, 14, 0, WS_EX_LEFT - PUSHBUTTON "Add Profile", IDC_BUTTON5, 171, 189, 53, 14, 0, WS_EX_LEFT - PUSHBUTTON "Refresh", IDC_BUTTON1, 7, 189, 53, 14, 0, WS_EX_LEFT -} - - - -LANGUAGE LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN -IDD_DIALOG4 DIALOGEX 150, 100, 123, 71 -STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU -CAPTION "Option" -FONT 8, "MS Shell Dlg", 400, 0, 1 -{ - LTEXT "More options coming soon?", 0, 18, 25, 88, 9, SS_LEFT, WS_EX_LEFT - DEFPUSHBUTTON "OK", IDOK, 5, 52, 50, 14, 0, WS_EX_LEFT - PUSHBUTTON "Cancel", IDCANCEL, 63, 52, 50, 14, 0, WS_EX_LEFT - EDITTEXT IDC_EDIT1, 60, 7, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT - LTEXT "Split time", IDC_STATIC, 7, 7, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT -} - - - -// -// Icon resources -// -LANGUAGE LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN -IDI_ICON1 ICON "icon1.ico" - - - -// -// Version Information resources -// -LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL -1 VERSIONINFO - FILEVERSION 0,0,0,0 - PRODUCTVERSION 0,0,0,0 - FILEOS VOS_UNKNOWN - FILETYPE VFT_UNKNOWN - FILESUBTYPE VFT2_UNKNOWN - FILEFLAGSMASK 0 - FILEFLAGS 0 -{ - -} diff --git a/oldgui/ProcessWindow.cpp b/oldgui/ProcessWindow.cpp deleted file mode 100644 index 06e62f9..0000000 --- a/oldgui/ProcessWindow.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include "ProcessWindow.h" -#include "resource.h" -#include "host.h" -#include "hookman.h" -#include "ProfileManager.h" -#include "profile/Profile.h" - -extern HookManager* man; // main.cpp -extern ProfileManager* pfman; // ProfileManager.cpp - -ProcessWindow::ProcessWindow(HWND hDialog) : hDlg(hDialog) -{ - hbRefresh = GetDlgItem(hDlg, IDC_BUTTON1); - hbAttach = GetDlgItem(hDlg, IDC_BUTTON2); - hbDetach = GetDlgItem(hDlg, IDC_BUTTON3); - hbAddProfile = GetDlgItem(hDlg, IDC_BUTTON5); - hbRemoveProfile = GetDlgItem(hDlg, IDC_BUTTON6); - EnableWindow(hbAddProfile, FALSE); - EnableWindow(hbRemoveProfile, FALSE); - hlProcess = GetDlgItem(hDlg, IDC_LIST1); - heOutput = GetDlgItem(hDlg, IDC_EDIT1); - ListView_SetExtendedListViewStyleEx(hlProcess, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); - InitProcessDlg(); - RefreshProcess(); - EnableWindow(hbDetach, FALSE); - EnableWindow(hbAttach, FALSE); -} - -void ProcessWindow::InitProcessDlg() -{ - LVCOLUMN lvc = {}; - lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH; - lvc.fmt = LVCFMT_RIGHT; // left-aligned column - lvc.cx = 40; - lvc.pszText = L"PID"; - ListView_InsertColumn(hlProcess, 0, &lvc); - lvc.cx = 100; - lvc.fmt = LVCFMT_LEFT; // left-aligned column - lvc.pszText = L"Name"; - ListView_InsertColumn(hlProcess, 1, &lvc); -} - -void ProcessWindow::RefreshProcess() -{ - ListView_DeleteAllItems(hlProcess); - LVITEM item = {}; - item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE; - DWORD idProcess[1024], cbNeeded; - WCHAR path[MAX_PATH]; - - if (EnumProcesses(idProcess, sizeof(idProcess), &cbNeeded)) - { - DWORD len = cbNeeded / sizeof(DWORD); - for (DWORD i = 0; i < len; ++i) - { - DWORD pid = idProcess[i]; - UniqueHandle hProcess(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)); - if (hProcess) - { - if (GetProcessImageFileName(hProcess.get(), path, MAX_PATH)) - { - WCHAR buffer[256]; - std::swprintf(buffer, L"%d", pid); - PWCHAR name = wcsrchr(path, L'\\') + 1; - item.pszText = buffer; - item.lParam = pid; - ListView_InsertItem(hlProcess, &item); - ListView_SetItemText(hlProcess, item.iItem, 1, name); - } - } - } - } -} - -void ProcessWindow::AttachProcess() -{ - DWORD pid = GetSelectedPID(); - if (InjectProcess(pid)) - RefreshThreadWithPID(pid, true); - else - ConsoleOutput(L"NextHooker: could not inject"); -} - -void ProcessWindow::DetachProcess() -{ - DWORD pid = GetSelectedPID(); - ::DetachProcess(pid); - RefreshThreadWithPID(pid, false); -} - -void ProcessWindow::CreateProfileForSelectedProcess() -{ - DWORD pid = GetSelectedPID(); - auto path = GetProcessPath(pid); - if (!path.empty()) - { - Profile* pf = pfman->CreateProfile(pid); - pfman->SaveProfiles(); - RefreshThread(ListView_GetSelectionMark(hlProcess)); - } -} - -void ProcessWindow::DeleteProfileForSelectedProcess() -{ - DWORD pid = GetSelectedPID(); - auto path = GetProcessPath(pid); - if (!path.empty()) - { - pfman->DeleteProfile(path); - RefreshThread(ListView_GetSelectionMark(hlProcess)); - } -} - -void ProcessWindow::RefreshThread(int index) -{ - LVITEM item = {}; - item.mask = LVIF_PARAM; - item.iItem = index; - ListView_GetItem(hlProcess, &item); - DWORD pid = item.lParam; - bool isAttached = man->GetHostPipe(pid) != NULL; - RefreshThreadWithPID(pid, isAttached); -} - -void ProcessWindow::RefreshThreadWithPID(DWORD pid, bool isAttached) -{ - EnableWindow(hbDetach, isAttached); - EnableWindow(hbAttach, !isAttached); - auto path = GetProcessPath(pid); - bool hasProfile = !path.empty() && pfman->HasProfile(path); - EnableWindow(hbAddProfile, isAttached && !hasProfile); - EnableWindow(hbRemoveProfile, hasProfile); - if (pid == GetCurrentProcessId()) - EnableWindow(hbAttach, FALSE); -} - -DWORD ProcessWindow::GetSelectedPID() -{ - LVITEM item = {}; - item.mask = LVIF_PARAM; - item.iItem = ListView_GetSelectionMark(hlProcess); - ListView_GetItem(hlProcess, &item); - return item.lParam; -} diff --git a/oldgui/ProcessWindow.h b/oldgui/ProcessWindow.h deleted file mode 100644 index ffa584e..0000000 --- a/oldgui/ProcessWindow.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "ITH.h" - -class ProcessWindow -{ -public: - ProcessWindow(HWND hDialog); - void InitProcessDlg(); - void RefreshProcess(); - void AttachProcess(); - void DetachProcess(); - void CreateProfileForSelectedProcess(); - void DeleteProfileForSelectedProcess(); - void RefreshThread(int index); -private: - void RefreshThreadWithPID(DWORD pid, bool isAttached); - DWORD GetSelectedPID(); - HWND hDlg; - HWND hlProcess; - HWND hbRefresh,hbAttach,hbDetach,hbAddProfile,hbRemoveProfile; - HWND heOutput; -}; diff --git a/oldgui/ProfileManager.cpp b/oldgui/ProfileManager.cpp deleted file mode 100644 index 191a153..0000000 --- a/oldgui/ProfileManager.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include "ProfileManager.h" -#include "profile/Profile.h" -#include "host.h" -#include "hookman.h" -#include "vnrhook/include/types.h" -#include "vnrhook/include/const.h" -#include "utility.h" -#include "profile/misc.h" - -extern HookManager* man; // main.cpp -ProfileManager* pfman; - -ProfileManager::ProfileManager() -{ - LoadProfiles(); -} - -ProfileManager::~ProfileManager() -{ - SaveProfiles(); -} - -Profile* ProfileManager::GetProfile(DWORD pid) -{ - std::wstring path = GetProcessPath(pid); - if (!path.empty()) - { - auto node = profile_tree.find(path); - if (node != profile_tree.end()) - return node->second.get(); - } - return NULL; -} - -bool ProfileManager::CreateProfile(pugi::xml_node game) -{ - auto file = game.child(L"File"); - auto profile = game.child(L"Profile"); - if (!file || !profile) - return false; - auto path = file.attribute(L"Path"); - if (!path) - return false; - auto profile_title = game.attribute(L"Title"); - auto title = profile_title ? profile_title.value() : L""; - auto pf = new Profile(title); - if (!pf->XmlReadProfile(profile)) - return false; - CSLock lock(cs); - auto& oldProfile = profile_tree[path.value()]; - if (!oldProfile) - oldProfile.swap(profile_ptr(pf)); - return true; -} - -Profile* ProfileManager::CreateProfile(DWORD pid) -{ - CSLock lock(cs); - auto path = GetProcessPath(pid); - auto& pf = profile_tree[path]; - if (!pf) - { - std::wstring title = GetProcessTitle(pid); - pf.reset(new Profile(title)); - } - return pf.get(); -} - -void ProfileManager::WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node root) -{ - auto game = root.append_child(L"Game"); - auto file_node = game.append_child(L"File"); - file_node.append_attribute(L"Path") = path.c_str(); - auto profile_node = game.append_child(L"Profile"); - pf.XmlWriteProfile(profile_node); - if (!pf.Title().empty()) - { - if (!game.attribute(L"Title")) - game.append_attribute(L"Title"); - game.attribute(L"Title") = pf.Title().c_str(); - } -} - -void ProfileManager::LoadProfiles() -{ - pugi::xml_document doc; - UniqueHandle hFile(IthCreateFile(L"NextHooker_Profile.xml", GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING)); - if (hFile.get() == INVALID_HANDLE_VALUE) - return; - DWORD size = GetFileSize(hFile.get(), NULL); - std::unique_ptr buffer(new char[size]); - ReadFile(hFile.get(), buffer.get(), size, &size, NULL); - auto result = doc.load_buffer(buffer.get(), size); - if (!result) - return; - auto root = doc.root().child(L"NextHookerProfile"); - if (!root) - return; - for (auto game = root.begin(); game != root.end(); ++game) - CreateProfile(*game); -} - -void ProfileManager::SaveProfiles() -{ - pugi::xml_document doc; - auto root = doc.append_child(L"NextHookerProfile"); - for (auto it = profile_tree.begin(); it != profile_tree.end(); ++it) { - auto& path = it->first; - auto& profile = it->second; - WriteProfileXml(path, *profile, root); - } - UniqueHandle hFile(IthCreateFile(L"NextHooker_Profile.xml", GENERIC_WRITE, 0, CREATE_ALWAYS)); - if (hFile.get() != INVALID_HANDLE_VALUE) - { - FileWriter fw(hFile.get()); - doc.save(fw); - } -} - -void ProfileManager::DeleteProfile(const std::wstring& path) -{ - CSLock lock(cs); - profile_tree.erase(profile_tree.find(path)); -} - -Profile* ProfileManager::GetProfile(const std::wstring& path) -{ - if (path.empty()) - return nullptr; - auto it = profile_tree.find(path); - if (it == profile_tree.end()) - return nullptr; - return it->second.get(); -} - -bool ProfileManager::HasProfile(const std::wstring& path) -{ - return profile_tree.find(path) != profile_tree.end(); -} - -DWORD ProfileManager::CountProfiles() -{ - return profile_tree.size(); -} - -DWORD SaveProcessProfile(TextThread* thread) -{ - ThreadParameter tp = thread->GetThreadParameter(); - std::wstring path = GetProcessPath(tp.pid); - if (path.empty()) - return 0; - pugi::xml_document doc; - pugi::xml_node profile_node = doc.append_child(L"Profile"); - Profile* pf = pfman->GetProfile(tp.pid); - if (pf != NULL) - pf->Clear(); - else - pf = pfman->CreateProfile(tp.pid); - - pf->AddHook(hook_ptr(new HookProfile(man->GetHookParam(tp.pid, tp.hook), man->GetHookName(tp.pid, tp.hook)))); - pf->AddThread(thread_ptr(new ThreadProfile(man->GetHookName(tp.pid, tp.hook), tp.retn, tp.spl, tp.hook, 0, THREAD_MASK_RETN | THREAD_MASK_SPLIT, L""))); - - pf->XmlReadProfile(profile_node); - - return 0; -} diff --git a/oldgui/ProfileManager.h b/oldgui/ProfileManager.h deleted file mode 100644 index 0f0966a..0000000 --- a/oldgui/ProfileManager.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "ITH.h" -#include "utility.h" // UniqueHandle, CriticalSection - -class Profile; - -class ProfileManager -{ -public: - ProfileManager(); - ~ProfileManager(); - Profile* CreateProfile(DWORD pid); - Profile* GetProfile(DWORD pid); - Profile* GetProfile(const std::wstring& path); - void LoadProfiles(); - void SaveProfiles(); - void DeleteProfile(const std::wstring& path); - void UpdateHookAddresses(DWORD pid); - bool HasProfile(const std::wstring& path); -private: - typedef std::unique_ptr profile_ptr; - typedef std::map profile_map; - - ProfileManager(const ProfileManager&); - ProfileManager operator=(const ProfileManager&); - - DWORD CountProfiles(); - bool CreateProfile(pugi::xml_node game); - void WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node doc); - // locate profile with executable path - profile_map profile_tree; - CriticalSection cs; -}; diff --git a/oldgui/TextBuffer.cpp b/oldgui/TextBuffer.cpp deleted file mode 100644 index f3e21d1..0000000 --- a/oldgui/TextBuffer.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "TextBuffer.h" - -DWORD WINAPI FlushThread(LPVOID lParam) -{ - TextBuffer* t = (TextBuffer*)lParam; - while (t->Running()) - { - t->Flush(); - Sleep(10); - } - return 0; -} - -TextBuffer::TextBuffer(HWND edit) : hThread(IthCreateThread(FlushThread, (DWORD)this)), -hEdit(edit), -running(true) -{ -} - -TextBuffer::~TextBuffer() -{ - running = false; - WaitForSingleObject(hThread.get(), 0); -} - -void TextBuffer::AddText(std::wstring text, bool line) -{ - CSLock lock(cs); - this->str.append(text); - line_break = line; -} - -void TextBuffer::Flush() -{ - CSLock lock(cs); - if (line_break || str.empty()) - return; - DWORD t = Edit_GetTextLength(hEdit); - Edit_SetSel(hEdit, t, -1); - Edit_ReplaceSel(hEdit, str.c_str()); - str.clear(); -} - -void TextBuffer::ClearBuffer() -{ - CSLock lock(cs); - str.clear(); - line_break = false; -} diff --git a/oldgui/TextBuffer.h b/oldgui/TextBuffer.h deleted file mode 100644 index cb15837..0000000 --- a/oldgui/TextBuffer.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "ITH.h" -#include "utility.h" // UniqueHandle, CriticalSection - -class TextBuffer -{ -public: - TextBuffer(HWND edit); - ~TextBuffer(); - void Flush(); - void AddText(std::wstring text, bool line); - void ClearBuffer(); - bool Running() { return running; } -private: - CriticalSection cs; - bool line_break, running; - UniqueHandle hThread; - HWND hEdit; - std::wstring str; -}; diff --git a/oldgui/command.cpp b/oldgui/command.cpp deleted file mode 100644 index 02197d4..0000000 --- a/oldgui/command.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) - * This file is part of the Interactive Text Hooker. - - * Interactive Text Hooker is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "ITH.h" -#include "host.h" -#include "vnrhook/include/const.h" -#include "vnrhook/include/types.h" -#include "language.h" -#include "utility.h" -#include "profile/misc.h" - -extern HookManager* man; -extern HWND hwndProcessComboBox; - -DWORD ProcessCommand(const std::wstring& cmd, DWORD pid) -{ - using std::wregex; - using std::regex_match; - std::match_results m; - - if (regex_match(cmd, m, wregex(L"/p(\\d+)", wregex::icase))) - { - pid = std::stoul(m[1].str()); - InjectProcess(pid); - } - else if (regex_match(cmd, m, wregex(L"/h(.+)", wregex::icase))) - { - HookParam hp = {}; - if (Parse(m[1].str(), hp)) - InsertHook(pid, hp); - } - else if (regex_match(cmd, m, wregex(L":(?:h|help)", wregex::icase))) - { - ConsoleOutput(Usage); - } - else - { - ConsoleOutput(L"Unknown command. Type :h or :help for help."); - } - return 0; -} diff --git a/oldgui/icon1.ico b/oldgui/icon1.ico deleted file mode 100644 index ea460c2d7468591126132eab499086a262429298..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 766 zcmbV|F-`+95Ji7bDvN?DRV22cproXTl-w+P18URYmN~`VfJ0C~PZLX4CV$qpSQ|mX z*w5qVvDRw?E4E>{0kFpB6|mv#o($wbR$QNhwHmhD$T0%!7{{DAa!N)_)5L<#9jZ1X zY_?=W37>OG!0+yYZS`<6Csdce-G#O;y`!l=OY0IkN8|3*d+`k9|B>EVSbX0}=b?46 z`+x7}Ra+0ObLX;dFVi?*Z0Wp9>-iz;c~-VCXSg4p*K57lKa=V{RiZwos1JoZ<~6x4 lY?*f{o`IMQ&QBzgFY-=4$QyYgFXWc&cj. - */ -const wchar_t* Warning=L"Warning!"; -//command.cpp -const wchar_t* ErrorSyntax=L"Syntax error"; -const wchar_t* Usage = L"Syntax:\r\n\ -\r\n\ -:H[ELP] - print help\r\n\ -Loader options:\r\n\ -/P[{process_id|Nprocess_name}] - attach to process\r\n\ -\r\n\ -Hook options:\r\n\ -/H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:module[:{name|#ordinal}]]\r\n\ -\r\n\ -All numbers in /H (except ordinal) are hexadecimal without any prefixes"; - -const wchar_t* ExtendedUsage = L"/H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:[module[:{name|#ordinal}]]]\r\n\ -\r\n\ -Set additional custom hook\r\n\ -\r\n\ -Hook types :\r\n\ -A - DBCS char\r\n\ -B - DBCS char(big-endian)\r\n\ -W - UCS2 char\r\n\ -S - MBCS string\r\n\ -Q - UTF-16 string\r\n\ -\r\n\ -Parameters:\r\n\ -X - use hardware breakpoints\r\n\ -N - don't use contexts\r\n\ -data_offset - stack offset to char / string pointer\r\n\ -drdo - add a level of indirection to data_offset\r\n\ -sub_offset - stack offset to subcontext\r\n\ -drso - add a level of indirection to sub_offset\r\n\ -addr - address of the hook\r\n\ -module - name of the module to use as base for 'addr'\r\n\ -name - name of the 'module' export to use as base for 'addr'\r\n\ -ordinal - number of the 'module' export ordinal to use as base for 'addr'\r\n\ -\r\n\ -Negative values of 'data_offset' and 'sub_offset' refer to registers: \r\n\ -- 4 for EAX, -8 for ECX, -C for EDX, -10 for EBX, -14 for ESP, -18 for EBP, -1C for ESI, -20 for EDI\r\n\ -\r\n\ -\"Add a level of indirection\" means in C/C++ style: (*(ESP+data_offset)+drdo) instead of (ESP+data_offset)\r\n\ -\r\n\ -All numbers except ordinal are hexadecimal without any prefixes"; - -//inject.cpp -const wchar_t* ErrorRemoteThread=L"Can't create remote thread."; -const wchar_t* ErrorOpenProcess=L"Can't open process."; -const wchar_t* ErrorNoProcess=L"Process not found"; -const wchar_t* SelfAttach=L"Please do not attach to ITH.exe"; -const wchar_t* AlreadyAttach=L"Process already attached."; -const wchar_t* FormatInject=L"Inject process %d. Module base %.8X"; -//main.cpp -const wchar_t* NotAdmin=L"Can't enable SeDebugPrevilege. ITH might malfunction.\r\n\ -Please run ITH as administrator or turn off UAC."; -//pipe.cpp -const wchar_t* ErrorCreatePipe=L"Can't create text pipe or too many instance."; -const wchar_t* FormatDetach=L"Process %d detached."; -const wchar_t* ErrorCmdQueueFull=L"Command queue full."; -const wchar_t* ErrorNoAttach=L"No process attached."; - -//profile.cpp -const wchar_t* ErrorMonitor=L"Can't monitor process."; -//utility.cpp -const wchar_t* InitMessage=L"Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com)\r\n\ -Copyright (C) 2015 Stomp (zorkzero@hotmail.com)\r\n\ -Copyright (C) 2016-2017 mireado (mireado@daum.net)\r\n\ -Copyright (C) 2018 Artikash (akashmozumdar@gmail.com)\r\n\ -Source code available at github.com/Artikash/NextHooker (GPLv3 License)"; -const wchar_t* BackgroundMsg=L"Type \":h\" or \":help\" for help."; -const wchar_t* ErrorLinkExist=L"Link exist."; -const wchar_t* ErrorCylicLink=L"Link failed. No cyclic link allowed."; -const wchar_t* FormatLink=L"Link from thread%.4x to thread%.4x."; -const wchar_t* ErrorLink=L"Link failed. Source or/and destination thread not found."; -const wchar_t* ErrorDeleteCombo=L"Error delete from combo."; - -//window.cpp -const wchar_t* ClassName=L"NextHooker"; -const wchar_t* ClassNameAdmin=L"NextHooker (Administrator)"; -const wchar_t* ErrorNotSplit=L"Need to enable split first!"; -const wchar_t* ErrorNotModule=L"Need to enable module first!"; -//Main window buttons -const wchar_t* ButtonTitleProcess=L"Process"; -const wchar_t* ButtonTitleThread=L"Thread"; -const wchar_t* ButtonTitleHook=L"Hook"; -const wchar_t* ButtonTitleProfile=L"Profile"; -const wchar_t* ButtonTitleOption=L"Option"; -const wchar_t* ButtonTitleClear=L"Clear"; -const wchar_t* ButtonTitleSave=L"Save"; -const wchar_t* ButtonTitleTop=L"Top"; -//Hook window -const wchar_t* SpecialHook=L"Special hook, no AGTH equivalent."; -//Process window -const wchar_t* TabTitlePID=L"PID"; -const wchar_t* TabTitleMemory=L"Memory"; -const wchar_t* TabTitleName=L"Name"; -const wchar_t* TabTitleTID=L"TID"; -const wchar_t* TabTitleStart=L"Start"; -const wchar_t* TabTitleModule=L"Module"; -const wchar_t* TabTitleState=L"State"; -const wchar_t* SuccessAttach=L"Attach ITH to process successfully."; -const wchar_t* FailAttach=L"Failed to attach ITH to process."; -const wchar_t* SuccessDetach=L"ITH detach from process."; -const wchar_t* FailDetach=L"Detach failed."; -//Profile window -const wchar_t* ProfileExist=L"Profile already exists."; -const wchar_t* SuccessAddProfile=L"Profile added."; -const wchar_t* FailAddProfile=L"Fail to add profile"; -const wchar_t* TabTitleNumber=L"No."; -const wchar_t* NoFile=L"Can't find file."; -const wchar_t* PathDismatch=L"Process name dismatch, continue?"; -const wchar_t* SuccessImportProfile=L"Import profile success"; -//const wchar_t* SuccessAddProfile=L"Profile added."; \ No newline at end of file diff --git a/oldgui/language.h b/oldgui/language.h deleted file mode 100644 index 141bf40..0000000 --- a/oldgui/language.h +++ /dev/null @@ -1,86 +0,0 @@ -/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) - * This file is part of the Interactive Text Hooker. - - * Interactive Text Hooker is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once - -extern const wchar_t* Warning; -//command.cpp -extern const wchar_t* ErrorSyntax; -extern const wchar_t* Usage; -extern const wchar_t* ExtendedUsage; -//inject.cpp -extern const wchar_t* ErrorRemoteThread; -extern const wchar_t* ErrorOpenProcess; -extern const wchar_t* ErrorNoProcess; -extern const wchar_t* SelfAttach; -extern const wchar_t* AlreadyAttach; -extern const wchar_t* FormatInject; -//main.cpp -extern const wchar_t* NotAdmin; -//pipe.cpp -extern const wchar_t* ErrorCreatePipe; -extern const wchar_t* FormatDetach; -extern const wchar_t* ErrorCmdQueueFull; -extern const wchar_t* ErrorNoAttach; - -//profile.cpp -extern const wchar_t* ErrorMonitor; - -//utility.cpp -extern const wchar_t* InitMessage; -extern const wchar_t* BackgroundMsg; -extern const wchar_t* ErrorLinkExist; -extern const wchar_t* ErrorCylicLink; -extern const wchar_t* FormatLink; -extern const wchar_t* ErrorLink; -extern const wchar_t* ErrorDeleteCombo; - -//window.cpp -extern const wchar_t* ClassName; -extern const wchar_t* ClassNameAdmin; -extern const wchar_t* ErrorNotSplit; -extern const wchar_t* ErrorNotModule; -//Main window buttons -extern const wchar_t* ButtonTitleProcess; -extern const wchar_t* ButtonTitleThread; -extern const wchar_t* ButtonTitleHook; -extern const wchar_t* ButtonTitleProfile; -extern const wchar_t* ButtonTitleOption; -extern const wchar_t* ButtonTitleClear; -extern const wchar_t* ButtonTitleSave; -extern const wchar_t* ButtonTitleTop; -//Hook window -extern const wchar_t* SpecialHook; -//Process window -extern const wchar_t* TabTitlePID; -extern const wchar_t* TabTitleMemory; -extern const wchar_t* TabTitleName; -extern const wchar_t* TabTitleTID; -extern const wchar_t* TabTitleStart; -extern const wchar_t* TabTitleModule; -extern const wchar_t* TabTitleState; -extern const wchar_t* SuccessAttach; -extern const wchar_t* FailAttach; -extern const wchar_t* SuccessDetach; -extern const wchar_t* FailDetach; -//Profile window -extern const wchar_t* ProfileExist; -extern const wchar_t* SuccessAddProfile; -extern const wchar_t* FailAddProfile; -extern const wchar_t* TabTitleNumber; -extern const wchar_t* NoFile; -extern const wchar_t* PathDismatch; -extern const wchar_t* SuccessImportProfile; \ No newline at end of file diff --git a/oldgui/main.cpp b/oldgui/main.cpp deleted file mode 100644 index bc72605..0000000 --- a/oldgui/main.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) - * This file is part of the Interactive Text Hooker. - - * Interactive Text Hooker is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "ITH.h" -#include "host.h" -#include "hookman.h" -#include "profile/Profile.h" -#include "ProfileManager.h" - -HINSTANCE hIns; -ATOM MyRegisterClass(HINSTANCE hInstance); -BOOL InitInstance(HINSTANCE hInstance, DWORD nCmdShow, RECT *rc); -RECT window; -extern HWND hMainWnd; // windows.cpp -extern ProfileManager* pfman; // ProfileManager.cpp - -HookManager* man; -LONG split_time; - -std::map setting; - -void SaveSettings() -{ - WINDOWPLACEMENT wndpl; - wndpl.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement(hMainWnd, &wndpl); - setting[L"window_left"] = wndpl.rcNormalPosition.left; - setting[L"window_right"] = wndpl.rcNormalPosition.right; - setting[L"window_top"] = wndpl.rcNormalPosition.top; - setting[L"window_bottom"] = wndpl.rcNormalPosition.bottom; - setting[L"split_time"] = split_time; - - UniqueHandle hFile(IthCreateFile(L"NextHooker.xml", GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS)); - if (hFile.get() != INVALID_HANDLE_VALUE) - { - FileWriter fw(hFile.get()); - pugi::xml_document doc; - auto root = doc.root().append_child(L"NextHookerSetting"); - for (auto it = setting.begin(); it != setting.end(); ++it) - root.append_attribute(it->first.c_str()).set_value(it->second); - doc.save(fw); - } -} - -void DefaultSettings() -{ - setting[L"split_time"] = 200; - setting[L"window_left"] = 100; - setting[L"window_right"] = 800; - setting[L"window_top"] = 100; - setting[L"window_bottom"] = 600; -} - -void InitializeSettings() -{ - split_time = setting[L"split_time"]; - window.left = setting[L"window_left"]; - window.right = setting[L"window_right"]; - window.top = setting[L"window_top"]; - window.bottom = setting[L"window_bottom"]; - - if (window.right < window.left || window.right - window.left < 600) - window.right = window.left + 600; - if (window.bottom < window.top || window.bottom - window.top < 200) - window.bottom = window.top + 200; -} - -void LoadSettings() -{ - UniqueHandle hFile(IthCreateFile(L"NextHooker.xml", GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING)); - if (hFile.get() != INVALID_HANDLE_VALUE) - { - DWORD size = GetFileSize(hFile.get(), NULL); - std::unique_ptr buffer(new char[size]); - ReadFile(hFile.get(), buffer.get(), size, &size, NULL); - pugi::xml_document doc; - auto result = doc.load_buffer_inplace(buffer.get(), size); - if (!result) - return; - auto root = doc.root().child(L"NextHookerSetting"); - for (auto attr = root.attributes_begin(); attr != root.attributes_end(); ++attr) - { - auto it = setting.find(attr->name()); - if (it != setting.end()) - it->second = std::stoul(attr->value()); - } - } -} - -extern LPCWSTR ClassName, ClassNameAdmin; -static WCHAR mutex[] = L"ITH_RUNNING"; -DWORD FindITH() -{ - HWND hwnd = FindWindow(ClassName, ClassName); - if (hwnd == NULL) - hwnd = FindWindow(ClassName, ClassNameAdmin); - if (hwnd) - { - ShowWindow(hwnd, SW_SHOWNORMAL); - SetForegroundWindow(hwnd); - return 0; - } - return 1; -} -LONG WINAPI UnhandledExcept(_EXCEPTION_POINTERS *ExceptionInfo) -{ - wchar_t path_name[512]; // fully qualified path name - WCHAR code[16]; - EXCEPTION_RECORD* rec = ExceptionInfo->ExceptionRecord; - std::swprintf(code, L"%08X", rec->ExceptionCode); - MEMORY_BASIC_INFORMATION info; - if (VirtualQuery(rec->ExceptionAddress, &info, sizeof(info))) - { - if (GetModuleFileName((HMODULE)info.AllocationBase, path_name, 512)) - { - LPWSTR name = wcsrchr(path_name, L'\\'); - if (name) - { - DWORD addr = (DWORD)rec->ExceptionAddress; - std::swprintf(name, L"%s:%08X", name + 1, addr - (DWORD)info.AllocationBase); - MessageBox(NULL, name, code, MB_OK); - TerminateProcess(GetCurrentProcess(), 0); - } - } - } - std::swprintf(path_name, L"%08X", rec->ExceptionAddress); - MessageBox(NULL, path_name, code, MB_OK); - TerminateProcess(GetCurrentProcess(), 0); - return 0; -} - -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) -{ - InitCommonControls(); - if (StartHost()) - { - SetUnhandledExceptionFilter(UnhandledExcept); - man = GetHostHookManager(); - pfman = new ProfileManager(); - DefaultSettings(); - LoadSettings(); - InitializeSettings(); - man->SetSplitInterval(split_time); - hIns = hInstance; - MyRegisterClass(hIns); - InitInstance(hIns, FALSE, &window); - MSG msg; - while (GetMessage(&msg, NULL, 0, 0)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - delete pfman; - man = NULL; - } - else - { - FindITH(); - } - CloseHost(); - TerminateProcess(GetCurrentProcess(), 0); -} diff --git a/oldgui/profile/Profile.cpp b/oldgui/profile/Profile.cpp deleted file mode 100644 index b973545..0000000 --- a/oldgui/profile/Profile.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) - * This file is part of the Interactive Text Hooker. - - * Interactive Text Hooker is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -#include "host.h" -#include "hookman.h" -#include "vnrhook/include/types.h" -#include "vnrhook/include/const.h" -#include "Profile.h" -#include "misc.h" -#include -#include "pugixml.h" - -extern HookManager* man; - -Profile::Profile(const std::wstring& title) : -select_index(-1), -title(title) -{} - -const std::unordered_set& Profile::Hooks() const -{ - return hooks; -} - -const std::vector& Profile::Threads() const -{ - return threads; -} - -const std::unordered_set& Profile::Links() const -{ - return links; -} - -bool Profile::XmlReadProfile(pugi::xml_node profile) -{ - auto hooks_node = profile.child(L"Hooks"); - auto threads_node = profile.child(L"Threads"); - auto links_node = profile.child(L"Links"); - if (hooks_node && !XmlReadProfileHook(hooks_node)) - return false; - if (threads_node && !XmlReadProfileThread(threads_node)) - return false; - if (links_node && !XmlReadProfileLink(links_node)) - return false; - auto select_node = profile.child(L"Select"); - if (select_node) - { - auto thread_index = select_node.attribute(L"ThreadIndex"); - if (!thread_index) - return false; - DWORD tmp_select = std::stoul(thread_index.value(), NULL, 16); - select_index = tmp_select & 0xFFFF; - } - return true; -} - -bool Profile::XmlReadProfileHook(pugi::xml_node hooks_node) -{ - for (auto hook = hooks_node.begin(); hook != hooks_node.end(); ++hook) - { - std::wstring name = hook->name(); - if (name.empty() || name.compare(L"Hook") != 0) - return false; - auto type = hook->attribute(L"Type"); - if (!type || type.empty()) - return false; - auto code = hook->attribute(L"Code"); - if (!code) - return false; - std::wstring code_value = code.value(); - HookParam hp = {}; - switch (type.value()[0]) - { - case L'H': - if (code_value[0] != L'/') - return false; - if (code_value[1] != L'H' && code_value[1] != L'h') - return false; - if (Parse(code_value.substr(2), hp)) - { - auto name = hook->attribute(L"Name"); - if (!name || name.empty()) - AddHook(hook_ptr(new HookProfile(hp, L""))); - else - AddHook(hook_ptr(new HookProfile(hp, name.value()))); - } - break; - default: - return false; - } - } - return true; -} - -bool Profile::XmlReadProfileThread(pugi::xml_node threads_node) -{ - std::wstring hook_name_buffer; - for (auto thread = threads_node.begin(); thread != threads_node.end(); ++thread) - { - std::wstring name = thread->name(); - if (name.empty() || name.compare(L"Thread") != 0) - return false; - auto hook_name = thread->attribute(L"HookName"); - if (!hook_name) - return false; - auto context = thread->attribute(L"Context"); - if (!context) - return false; - auto sub_context = thread->attribute(L"SubContext"); - if (!sub_context) - return false; - auto mask = thread->attribute(L"Mask"); - if (!mask) - return false; - DWORD mask_tmp = std::stoul(mask.value(), NULL, 16); - auto comment = thread->attribute(L"Comment"); - auto retn = std::stoul(context.value(), NULL, 16); - auto split = std::stoul(sub_context.value(), NULL, 16); - WORD flags = mask_tmp & 0xFFFF; - auto tp = new ThreadProfile(hook_name.value(), retn, split, 0, 0, flags, - comment.value()); - AddThread(thread_ptr(tp)); - } - return true; -} - -bool Profile::XmlReadProfileLink(pugi::xml_node links_node) -{ - for (auto link = links_node.begin(); link != links_node.end(); ++link) - { - std::wstring name = link->name(); - if (name.empty() || name.compare(L"Link") != 0) - return false; - auto from = link->attribute(L"From"); - if (!from) - return false; - DWORD link_from = std::stoul(from.value(), NULL, 16); - auto to = link->attribute(L"To"); - if (!to) - return false; - DWORD link_to = std::stoul(to.value(), NULL, 16); - auto lp = new LinkProfile(link_from & 0xFFFF, link_to & 0xFFFF); - AddLink(link_ptr(lp)); - } - return true; -} - -bool Profile::XmlWriteProfile(pugi::xml_node profile_node) -{ - if (!hooks.empty()) - { - auto node = profile_node.append_child(L"Hooks"); - XmlWriteProfileHook(node); - } - if (!threads.empty()) - { - auto node = profile_node.append_child(L"Threads"); - XmlWriteProfileThread(node); - } - if (!links.empty()) - { - auto node = profile_node.append_child(L"Links"); - XmlWriteProfileLink(node); - } - if (select_index != 0xFFFF) - { - auto node = profile_node.append_child(L"Select"); - node.append_attribute(L"ThreadIndex") = select_index; - } - return true; -} - -bool Profile::XmlWriteProfileHook(pugi::xml_node hooks_node) -{ - for (auto hook = hooks.begin(); hook != hooks.end(); ++hook) - { - auto hook_node = hooks_node.append_child(L"Hook"); - hook_node.append_attribute(L"Type") = L"H"; - hook_node.append_attribute(L"Code") = ParseCode((*hook)->HP()).c_str(); - if (!(*hook)->Name().empty()) - hook_node.append_attribute(L"Name") = (*hook)->Name().c_str(); - } - return true; -} - -bool Profile::XmlWriteProfileThread(pugi::xml_node threads_node) -{ - for (auto thread = threads.begin(); thread != threads.end(); ++thread) - { - const std::wstring& name = (*thread)->HookName(); - if (name.empty()) - return false; - auto node = threads_node.append_child(L"Thread"); - node.append_attribute(L"HookName") = name.c_str(); - std::wstring hex = ToHexString((*thread)->Flags() & (THREAD_MASK_RETN | THREAD_MASK_SPLIT)); - node.append_attribute(L"Mask") = hex.c_str(); - hex = ToHexString((*thread)->Split()); - node.append_attribute(L"SubContext") = hex.c_str(); - hex = ToHexString((*thread)->Return()); - node.append_attribute(L"Context") = hex.c_str(); - if (!(*thread)->Comment().empty()) - node.append_attribute(L"Comment") = (*thread)->Comment().c_str(); - } - return true; -} - -bool Profile::XmlWriteProfileLink(pugi::xml_node links_node) -{ - for (auto link = links.begin(); link != links.end(); ++link) - { - auto node = links_node.append_child(L"Link"); - node.append_attribute(L"From") = ToHexString((*link)->FromIndex()).c_str(); - node.append_attribute(L"To") = ToHexString((*link)->ToIndex()).c_str(); - } - return true; -} - -void Profile::Clear() -{ - title = L""; - select_index = -1; - hooks.clear(); - threads.clear(); - links.clear(); -} - -void Profile::AddHook(hook_ptr hook) -{ - hooks.insert(std::move(hook)); -} - -// add the thread profile and return its index -int Profile::AddThread(thread_ptr tp) -{ - auto it = std::find_if(threads.begin(), threads.end(), [&tp](thread_ptr& thread) - { - return thread->HookName().compare(tp->HookName()) == 0 && - thread->Return() == tp->Return() && - thread->Split() == tp->Split(); - }); - if (it != threads.end()) - return it - threads.begin(); - threads.push_back(std::move(tp)); - return threads.size() - 1; -} - -void Profile::AddLink(link_ptr link) -{ - links.insert(std::move(link)); -} - -const std::wstring& Profile::Title() const -{ - return title; -} - -bool Profile::IsThreadSelected(thread_ptr_iter thread_profile) -{ - if (thread_profile != threads.end()) - { - auto thread_index = thread_profile - threads.begin(); - return select_index == thread_index; - } - return false; -} - -thread_ptr_iter Profile::FindThread(const ThreadParameter* tp, const std::wstring& hook_name) const -{ - auto thread_profile = std::find_if(threads.begin(), threads.end(), - [&tp, &hook_name](const thread_ptr& thread_profile) -> bool - { - return thread_profile->HookName().compare(hook_name) == 0 - && thread_profile->Return() == tp->retn - && thread_profile->Split() == tp->spl; - }); - return thread_profile; -} diff --git a/oldgui/profile/Profile.h b/oldgui/profile/Profile.h deleted file mode 100644 index 72334e9..0000000 --- a/oldgui/profile/Profile.h +++ /dev/null @@ -1,171 +0,0 @@ -/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) - * This file is part of the Interactive Text Hooker. - - * Interactive Text Hooker is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "vnrhook/include/types.h" // HookParam -#include -#include -#include -#include - -struct ThreadParameter; -class TextThread; -class HookProfile; -class ThreadProfile; -class LinkProfile; -typedef std::unique_ptr hook_ptr; -typedef std::unique_ptr thread_ptr; -typedef std::unique_ptr link_ptr; -typedef std::vector::const_iterator thread_ptr_iter; -namespace pugi { - class xml_node; -} - -#define THREAD_MASK_RETN 1 -#define THREAD_MASK_SPLIT 2 - -class HookProfile -{ - HookParam hp; - std::wstring name; -public: - HookProfile(const HookParam& hp, const std::wstring& name) : - hp(hp), - name(name) - {} - const HookParam& HP() const { return hp; }; - const std::wstring& Name() const { return name; }; -}; - -class ThreadProfile -{ - std::wstring hook_name; - DWORD retn; - DWORD split; - DWORD hook_addr; - WORD hm_index, flags; - std::wstring comment; -public: - ThreadProfile(const std::wstring& hook_name, - DWORD retn, - DWORD split, - DWORD hook_addr, - WORD hm_index, - WORD flags, - const std::wstring& comment) : - hook_name(hook_name), - retn(retn), - split(split), - hook_addr(hook_addr), - hm_index(hm_index), - flags(flags), - comment(comment) - { - } - const std::wstring& HookName() const { return hook_name; } - const std::wstring& Comment() const { return comment; } - DWORD Return() const { return retn; } - DWORD Split() const { return split; } - DWORD& HookAddress() { return hook_addr; } - WORD& HookManagerIndex() { return hm_index; } - WORD Flags() const { return flags; } -}; - -class LinkProfile -{ - WORD from_index, to_index; -public: - LinkProfile(WORD from_index, WORD to_index) : - from_index(from_index), - to_index(to_index) - {} - WORD FromIndex() const { return from_index; } - WORD ToIndex() const { return to_index; } -}; - -namespace std { - template<> - struct hash { - size_t operator()(const hook_ptr &r) const - { - return hash{}(r->HP().address) - ^ hash{}(r->HP().module) - ^ hash{}(r->HP().function); - } - }; - template<> - struct equal_to { - bool operator()(const hook_ptr& r, const hook_ptr& r2) const - { - return r->HP().address == r2->HP().address - && r->HP().module == r2->HP().module - && r->HP().function == r2->HP().function; - } - }; - - template<> - struct hash { - size_t operator()(const link_ptr &r) const - { - return hash{}(r->FromIndex()) - ^ hash{}(r->ToIndex()); - } - }; - template<> - struct equal_to { - bool operator()(const link_ptr& r, const link_ptr& r2) const - { - return r->FromIndex() == r2->FromIndex() - && r->ToIndex() == r2->ToIndex(); - } - }; -} - -class Profile -{ -public: - Profile(const std::wstring& title); - bool XmlReadProfile(pugi::xml_node profile_node); - bool XmlWriteProfile(pugi::xml_node profile_node); - void AddHook(hook_ptr hook); - int AddThread(thread_ptr tp); - void AddLink(link_ptr lp); - void Clear(); - const std::unordered_set& Hooks() const; - const std::vector& Threads() const; - const std::unordered_set& Links() const; - const std::wstring& Title() const; - thread_ptr_iter FindThread(const ThreadParameter* tp, const std::wstring& hook_name) const; - WORD& SelectedIndex() { return select_index; } - bool IsThreadSelected(thread_ptr_iter thread_profile); - -private: - bool XmlReadProfileHook(pugi::xml_node hooks_node); - bool XmlReadProfileThread(pugi::xml_node threads_node); - bool XmlReadProfileLink(pugi::xml_node links_node); - bool XmlWriteProfileHook(pugi::xml_node hooks_node); - bool XmlWriteProfileThread(pugi::xml_node threads_node); - bool XmlWriteProfileLink(pugi::xml_node links_node); - - std::wstring title; - std::unordered_set hooks; - std::vector threads; - std::unordered_set links; - - WORD select_index; -}; diff --git a/oldgui/profile/misc.cpp b/oldgui/profile/misc.cpp deleted file mode 100644 index 6be4827..0000000 --- a/oldgui/profile/misc.cpp +++ /dev/null @@ -1,275 +0,0 @@ -#include "misc.h" -#include -#include -#include "host.h" -#include "vnrhook/include/const.h" -#include "vnrhook/include/types.h" - -DWORD Hash(const std::wstring& module, int length) -{ - DWORD hash = 0; - auto end = (length < 0 || static_cast(length) > module.length()) ? - module.end() : - module.begin() + length; - for (auto it = module.begin(); it != end; ++it) - hash = _rotr(hash, 7) + *it; - return hash; -} - -bool Parse(const std::wstring& cmd, HookParam& hp) -{ - using std::wregex; - using std::regex_search; - // /H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:[module[:{name|#ordinal}]]] - wregex rx(L"^X?([ABWSQ])(N)?", wregex::icase); - std::match_results m; - auto start = cmd.begin(); - auto end = cmd.end(); - bool result = regex_search(start, end, m, rx); - if (!result) - return result; - start = m[0].second; - if (m[2].matched) - hp.type |= NO_CONTEXT; - - switch (m[1].first[0]) - { - case L's': - case L'S': - hp.type |= USING_STRING; - break; - case L'e': - case L'E': - hp.type |= STRING_LAST_CHAR; - case L'a': - case L'A': - hp.type |= BIG_ENDIAN; - hp.length_offset = 1; - break; - case L'b': - case L'B': - hp.length_offset = 1; - break; - case L'h': - case L'H': - hp.type |= PRINT_DWORD; - case L'q': - case L'Q': - hp.type |= USING_STRING | USING_UNICODE; - break; - case L'l': - case L'L': - hp.type |= STRING_LAST_CHAR; - case L'w': - case L'W': - hp.type |= USING_UNICODE; - hp.length_offset = 1; - break; - default: - break; - } - - // [data_offset[*drdo]] - std::wstring data_offset(L"(-?[[:xdigit:]]+)"), drdo(L"(\\*-?[[:xdigit:]]+)?"); - rx = wregex(L"^" + data_offset + drdo, wregex::icase); - result = regex_search(start, end, m, rx); - if (result) - { - start = m[0].second; - hp.offset = std::stoul(m[1].str(), NULL, 16); - if (m[2].matched) - { - hp.type |= DATA_INDIRECT; - hp.index = std::stoul(m[2].str().substr(1), NULL, 16); - } - } - - // [:sub_offset[*drso]] - std::wstring sub_offset(L"(-?[[:xdigit:]]+)"), drso(L"(\\*-?[[:xdigit:]]+)?"); - rx = wregex(L"^:" + sub_offset + drso, wregex::icase); - result = regex_search(start, end, m, rx); - if (result) - { - start = m[0].second; - hp.type |= USING_SPLIT; - hp.split = std::stoul(m[1].str(), NULL, 16); - if (m[2].matched) - { - hp.type |= SPLIT_INDIRECT; - hp.split_index = std::stoul(m[2].str().substr(1), NULL, 16); - } - } - // @addr - rx = wregex(L"^@[[:xdigit:]]+", wregex::icase); - result = regex_search(start, end, m, rx); - if (!result) - return false; - start = m[0].second; - hp.address = std::stoul(m[0].str().substr(1), NULL, 16); - if (hp.offset & 0x80000000) - hp.offset -= 4; - if (hp.split & 0x80000000) - hp.split -= 4; - - // [:[module[:{name|#ordinal}]]] - // ":" -> module == NULL &% function == NULL - // "" -> MODULE_OFFSET && module == NULL && function == addr - // ":GDI.dll" -> MODULE_OFFSET && module != NULL - // ":GDI.dll:strlen" -> MODULE_OFFSET | FUNCTION_OFFSET && module != NULL && function != NULL - // ":GDI.dll:#123" -> MODULE_OFFSET | FUNCTION_OFFSET && module != NULL && function != NULL - std::wstring module(L"([^:]+)"), name(L"[^:[:space:]]+"), ordinal(L"\\d+"); - rx = wregex(L"^:(" + module + L"(:" + name + L"|#" + ordinal + L")?)?$", wregex::icase); - result = regex_search(start, end, m, rx); - if (result) // :[module[:{name|#ordinal}]] - { - if (m[1].matched) // module - { - hp.type |= MODULE_OFFSET; - std::wstring module = m[2]; - std::transform(module.begin(), module.end(), module.begin(), ::towlower); - hp.module = Hash(module); - if (m[3].matched) // :name|#ordinal - { - hp.type |= FUNCTION_OFFSET; - hp.function = Hash(m[3].str().substr(1)); - } - } - } - else - { - rx = wregex(L"^!([[:xdigit:]]+)(!([[:xdigit:]]+))?$", wregex::icase); - result = regex_search(start, end, m, rx); - if (result) - { - hp.type |= MODULE_OFFSET; - hp.module = std::stoul(m[1].str(), NULL, 16); - if (m[2].matched) - { - hp.type |= FUNCTION_OFFSET; - hp.function = std::stoul(m[2].str().substr(1), NULL, 16); - } - } - else - { - // Hack. Hook is relative to the executable. Store the original address in function. - // hp.module == NULL && hp.function != NULL - hp.type |= MODULE_OFFSET; - hp.function = hp.address; - } - } - return true; -} - -std::wstring ParseCode(const HookParam& hp) -{ - std::wstring code(L"/H"); - WCHAR c; - if (hp.type & PRINT_DWORD) - c = L'H'; - else if (hp.type & USING_UNICODE) - { - if (hp.type & USING_STRING) - c = L'Q'; - else if (hp.type & STRING_LAST_CHAR) - c = L'L'; - else - c = L'W'; - } - else - { - if (hp.type & USING_STRING) - c = L'S'; - else if (hp.type & BIG_ENDIAN) - c = L'A'; - else if (hp.type & STRING_LAST_CHAR) - c = L'E'; - else - c = L'B'; - } - code += c; - if (hp.type & NO_CONTEXT) - code += L'N'; - if (hp.offset >> 31) - code += L'-' + ToHexString(-(hp.offset + 4)); - else - code += ToHexString(hp.offset); - if (hp.type & DATA_INDIRECT) - { - if (hp.index >> 31) - code += L'*-' + ToHexString(-hp.index); - else - code += L'*' + ToHexString(hp.index); - } - if (hp.type & USING_SPLIT) - { - if (hp.split >> 31) - code += L":-" + ToHexString(-(4 + hp.split)); - else - code += L":" + ToHexString(hp.split); - } - if (hp.type & SPLIT_INDIRECT) - { - if (hp.split_index >> 31) - code += L"*-" + ToHexString(-hp.split_index); - else - code += L"*" + ToHexString(hp.split_index); - } - if (hp.module) - { - code += L"@" + ToHexString(hp.address) + L"!" + ToHexString(hp.module); - if (hp.function) - code += L"!" + ToHexString(hp.function); - } - else - { - // Hack. The original address is stored in the function field - // if (module == NULL && function != NULL). - // MODULE_OFFSET and FUNCTION_OFFSET are removed from HookParam.type in - // TextHook::UnsafeInsertHookCode() and can not be used here. - if (hp.function) - code += L"@" + ToHexString(hp.function); - else - code += L"@" + ToHexString(hp.address) + L":"; - } - return code; -} - - -std::string toMultiByteString(const std::wstring& unicodeString) -{ - int cbMultiByte = WideCharToMultiByte(932, 0, unicodeString.c_str(), unicodeString.length(), - NULL, 0, NULL, NULL); - auto lpMultiByteStr = std::make_unique(cbMultiByte); - WideCharToMultiByte(932, 0, unicodeString.c_str(), unicodeString.length(), - lpMultiByteStr.get(), cbMultiByte, NULL, NULL); - return std::string(lpMultiByteStr.get(), cbMultiByte); -} - -std::wstring toUnicodeString(const std::string& mbString) -{ - int cchWideChar = MultiByteToWideChar(932, 0, mbString.c_str(), mbString.length(), NULL, 0); - auto lpWideCharStr = std::make_unique(cchWideChar); - MultiByteToWideChar(932, 0, mbString.c_str(), mbString.length(), lpWideCharStr.get(), cchWideChar); - return std::wstring(lpWideCharStr.get(), cchWideChar); -} - -std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address) -{ - std::wstring hook_name; - WaitForSingleObject(pr.hookman_mutex, 0); - auto hooks = (const Hook*)pr.hookman_map; - for (int i = 0; i < MAX_HOOK; ++i) - { - auto& hook = hooks[i]; - if (hook.Address() == hook_address) - { - std::unique_ptr name(new CHAR[hook.NameLength()]); - // name is zero terminated - if (ReadProcessMemory(pr.process_handle, hooks[i].Name(), name.get(), hook.NameLength(), NULL)) - hook_name = toUnicodeString(name.get()); - break; - } - } - ReleaseMutex(pr.hookman_mutex); - return hook_name; -} diff --git a/oldgui/profile/misc.h b/oldgui/profile/misc.h deleted file mode 100644 index 5ad3d94..0000000 --- a/oldgui/profile/misc.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include -#include - -struct HookParam; -struct ProcessRecord; - -bool Parse(const std::wstring& cmd, HookParam& hp); -DWORD Hash(const std::wstring& module, int length = -1); -std::wstring ParseCode(const HookParam& hp); -std::string toMultiByteString(const std::wstring& unicodeString); -std::wstring toUnicodeString(const std::string& mbString); -std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address); - -template -std::wstring ToHexString(T i) { - std::wstringstream ss; - ss << std::uppercase << std::hex << i; - return ss.str(); -} diff --git a/oldgui/profile/pugiconfig.h b/oldgui/profile/pugiconfig.h deleted file mode 100644 index 4d431ab..0000000 --- a/oldgui/profile/pugiconfig.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * pugixml parser - version 1.6 - * -------------------------------------------------------- - * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ - * - * This library is distributed under the MIT License. See notice at the end - * of this file. - * - * This work is based on the pugxml parser, which is: - * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) - */ - -#ifndef HEADER_PUGICONFIG_HPP -#define HEADER_PUGICONFIG_HPP - -// Uncomment this to enable wchar_t mode -#define PUGIXML_WCHAR_MODE - -// Uncomment this to disable XPath -// #define PUGIXML_NO_XPATH - -// Uncomment this to disable STL -// #define PUGIXML_NO_STL - -// Uncomment this to disable exceptions -// #define PUGIXML_NO_EXCEPTIONS - -// Set this to control attributes for public classes/functions, i.e.: -// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL -// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL -// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall -// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead - -// Tune these constants to adjust memory-related behavior -// #define PUGIXML_MEMORY_PAGE_SIZE 32768 -// #define PUGIXML_MEMORY_OUTPUT_STACK 10240 -// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 - -// Uncomment this to switch to header-only version -// #define PUGIXML_HEADER_ONLY - -// Uncomment this to enable long long support -// #define PUGIXML_HAS_LONG_LONG - -#endif - -/** - * Copyright (c) 2006-2015 Arseny Kapoulkine - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ diff --git a/oldgui/profile/pugixml.cpp b/oldgui/profile/pugixml.cpp deleted file mode 100644 index 7880aae..0000000 --- a/oldgui/profile/pugixml.cpp +++ /dev/null @@ -1,11554 +0,0 @@ -/** - * pugixml parser - version 1.6 - * -------------------------------------------------------- - * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ - * - * This library is distributed under the MIT License. See notice at the end - * of this file. - * - * This work is based on the pugxml parser, which is: - * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) - */ - -#ifndef SOURCE_PUGIXML_CPP -#define SOURCE_PUGIXML_CPP - -#include "pugixml.h" - -#include -#include -#include -#include - -#ifdef PUGIXML_WCHAR_MODE -# include -#endif - -#ifndef PUGIXML_NO_XPATH -# include -# include -# ifdef PUGIXML_NO_EXCEPTIONS -# include -# endif -#endif - -#ifndef PUGIXML_NO_STL -# include -# include -# include -#endif - -// For placement new -#include - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4127) // conditional expression is constant -# pragma warning(disable: 4324) // structure was padded due to __declspec(align()) -# pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable -# pragma warning(disable: 4702) // unreachable code -# pragma warning(disable: 4996) // this function or variable may be unsafe -# pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged -#endif - -#ifdef __INTEL_COMPILER -# pragma warning(disable: 177) // function was declared but never referenced -# pragma warning(disable: 279) // controlling expression is constant -# pragma warning(disable: 1478 1786) // function was declared "deprecated" -# pragma warning(disable: 1684) // conversion from pointer to same-sized integral type -#endif - -#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY) -# pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away -#endif - -#ifdef __BORLANDC__ -# pragma option push -# pragma warn -8008 // condition is always false -# pragma warn -8066 // unreachable code -#endif - -#ifdef __SNC__ -// Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug -# pragma diag_suppress=178 // function was declared but never referenced -# pragma diag_suppress=237 // controlling expression is constant -#endif - -// Inlining controls -#if defined(_MSC_VER) && _MSC_VER >= 1300 -# define PUGI__NO_INLINE __declspec(noinline) -#elif defined(__GNUC__) -# define PUGI__NO_INLINE __attribute__((noinline)) -#else -# define PUGI__NO_INLINE -#endif - -// Branch weight controls -#if defined(__GNUC__) -# define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0) -#else -# define PUGI__UNLIKELY(cond) (cond) -#endif - -// Simple static assertion -#define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; } - -// Digital Mars C++ bug workaround for passing char loaded from memory via stack -#ifdef __DMC__ -# define PUGI__DMC_VOLATILE volatile -#else -# define PUGI__DMC_VOLATILE -#endif - -// Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all) -#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST) -using std::memcpy; -using std::memmove; -#endif - -// In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features -#if defined(_MSC_VER) && !defined(__S3E__) -# define PUGI__MSVC_CRT_VERSION _MSC_VER -#endif - -#ifdef PUGIXML_HEADER_ONLY -# define PUGI__NS_BEGIN namespace pugi { namespace impl { -# define PUGI__NS_END } } -# define PUGI__FN inline -# define PUGI__FN_NO_INLINE inline -#else -# if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces -# define PUGI__NS_BEGIN namespace pugi { namespace impl { -# define PUGI__NS_END } } -# else -# define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace { -# define PUGI__NS_END } } } -# endif -# define PUGI__FN -# define PUGI__FN_NO_INLINE PUGI__NO_INLINE -#endif - -// uintptr_t -#if !defined(_MSC_VER) || _MSC_VER >= 1600 -# include -#else -# ifndef _UINTPTR_T_DEFINED -// No native uintptr_t in MSVC6 and in some WinCE versions -typedef size_t uintptr_t; -#define _UINTPTR_T_DEFINED -# endif -PUGI__NS_BEGIN - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; -PUGI__NS_END -#endif - -// Memory allocation -PUGI__NS_BEGIN - PUGI__FN void* default_allocate(size_t size) - { - return malloc(size); - } - - PUGI__FN void default_deallocate(void* ptr) - { - free(ptr); - } - - template - struct xml_memory_management_function_storage - { - static allocation_function allocate; - static deallocation_function deallocate; - }; - - // Global allocation functions are stored in class statics so that in header mode linker deduplicates them - // Without a template<> we'll get multiple definitions of the same static - template allocation_function xml_memory_management_function_storage::allocate = default_allocate; - template deallocation_function xml_memory_management_function_storage::deallocate = default_deallocate; - - typedef xml_memory_management_function_storage xml_memory; -PUGI__NS_END - -// String utilities -PUGI__NS_BEGIN - // Get string length - PUGI__FN size_t strlength(const char_t* s) - { - assert(s); - - #ifdef PUGIXML_WCHAR_MODE - return wcslen(s); - #else - return strlen(s); - #endif - } - - // Compare two strings - PUGI__FN bool strequal(const char_t* src, const char_t* dst) - { - assert(src && dst); - - #ifdef PUGIXML_WCHAR_MODE - return wcscmp(src, dst) == 0; - #else - return strcmp(src, dst) == 0; - #endif - } - - // Compare lhs with [rhs_begin, rhs_end) - PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count) - { - for (size_t i = 0; i < count; ++i) - if (lhs[i] != rhs[i]) - return false; - - return lhs[count] == 0; - } - - // Get length of wide string, even if CRT lacks wide character support - PUGI__FN size_t strlength_wide(const wchar_t* s) - { - assert(s); - - #ifdef PUGIXML_WCHAR_MODE - return wcslen(s); - #else - const wchar_t* end = s; - while (*end) end++; - return static_cast(end - s); - #endif - } - -#ifdef PUGIXML_WCHAR_MODE - // Convert string to wide string, assuming all symbols are ASCII - PUGI__FN void widen_ascii(wchar_t* dest, const char* source) - { - for (const char* i = source; *i; ++i) *dest++ = *i; - *dest = 0; - } -#endif -PUGI__NS_END - -#if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH) -// auto_ptr-like buffer holder for exception recovery -PUGI__NS_BEGIN - struct buffer_holder - { - void* data; - void (*deleter)(void*); - - buffer_holder(void* data_, void (*deleter_)(void*)): data(data_), deleter(deleter_) - { - } - - ~buffer_holder() - { - if (data) deleter(data); - } - - void* release() - { - void* result = data; - data = 0; - return result; - } - }; -PUGI__NS_END -#endif - -PUGI__NS_BEGIN - static const size_t xml_memory_page_size = - #ifdef PUGIXML_MEMORY_PAGE_SIZE - PUGIXML_MEMORY_PAGE_SIZE - #else - 32768 - #endif - ; - - static const uintptr_t xml_memory_page_alignment = 64; - static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1); - static const uintptr_t xml_memory_page_contents_shared_mask = 32; - static const uintptr_t xml_memory_page_name_allocated_mask = 16; - static const uintptr_t xml_memory_page_value_allocated_mask = 8; - static const uintptr_t xml_memory_page_type_mask = 7; - static const uintptr_t xml_memory_page_name_allocated_or_shared_mask = xml_memory_page_name_allocated_mask | xml_memory_page_contents_shared_mask; - static const uintptr_t xml_memory_page_value_allocated_or_shared_mask = xml_memory_page_value_allocated_mask | xml_memory_page_contents_shared_mask; - - #define PUGI__NODETYPE(n) static_cast(((n)->header & impl::xml_memory_page_type_mask) + 1) - - struct xml_allocator; - - struct xml_memory_page - { - static xml_memory_page* construct(void* memory) - { - xml_memory_page* result = static_cast(memory); - - result->allocator = 0; - result->prev = 0; - result->next = 0; - result->busy_size = 0; - result->freed_size = 0; - - return result; - } - - xml_allocator* allocator; - - xml_memory_page* prev; - xml_memory_page* next; - - size_t busy_size; - size_t freed_size; - }; - - struct xml_memory_string_header - { - uint16_t page_offset; // offset from page->data - uint16_t full_size; // 0 if string occupies whole page - }; - - struct xml_allocator - { - xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size) - { - } - - xml_memory_page* allocate_page(size_t data_size) - { - size_t size = sizeof(xml_memory_page) + data_size; - - // allocate block with some alignment, leaving memory for worst-case padding - void* memory = xml_memory::allocate(size + xml_memory_page_alignment); - if (!memory) return 0; - - // align to next page boundary (note: this guarantees at least 1 usable byte before the page) - char* page_memory = reinterpret_cast((reinterpret_cast(memory) + xml_memory_page_alignment) & ~(xml_memory_page_alignment - 1)); - - // prepare page structure - xml_memory_page* page = xml_memory_page::construct(page_memory); - assert(page); - - page->allocator = _root->allocator; - - // record the offset for freeing the memory block - assert(page_memory > memory && page_memory - static_cast(memory) <= 127); - page_memory[-1] = static_cast(page_memory - static_cast(memory)); - - return page; - } - - static void deallocate_page(xml_memory_page* page) - { - char* page_memory = reinterpret_cast(page); - - xml_memory::deallocate(page_memory - page_memory[-1]); - } - - void* allocate_memory_oob(size_t size, xml_memory_page*& out_page); - - void* allocate_memory(size_t size, xml_memory_page*& out_page) - { - if (_busy_size + size > xml_memory_page_size) return allocate_memory_oob(size, out_page); - - void* buf = reinterpret_cast(_root) + sizeof(xml_memory_page) + _busy_size; - - _busy_size += size; - - out_page = _root; - - return buf; - } - - void deallocate_memory(void* ptr, size_t size, xml_memory_page* page) - { - if (page == _root) page->busy_size = _busy_size; - - assert(ptr >= reinterpret_cast(page) + sizeof(xml_memory_page) && ptr < reinterpret_cast(page) + sizeof(xml_memory_page) + page->busy_size); - (void)!ptr; - - page->freed_size += size; - assert(page->freed_size <= page->busy_size); - - if (page->freed_size == page->busy_size) - { - if (page->next == 0) - { - assert(_root == page); - - // top page freed, just reset sizes - page->busy_size = page->freed_size = 0; - _busy_size = 0; - } - else - { - assert(_root != page); - assert(page->prev); - - // remove from the list - page->prev->next = page->next; - page->next->prev = page->prev; - - // deallocate - deallocate_page(page); - } - } - } - - char_t* allocate_string(size_t length) - { - static const size_t max_encoded_offset = (1 << 16) * sizeof(void*); - - PUGI__STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset); - - // allocate memory for string and header block - size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t); - - // round size up to pointer alignment boundary - size_t full_size = (size + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1); - - xml_memory_page* page; - xml_memory_string_header* header = static_cast(allocate_memory(full_size, page)); - - if (!header) return 0; - - // setup header - ptrdiff_t page_offset = reinterpret_cast(header) - reinterpret_cast(page) - sizeof(xml_memory_page); - - assert(page_offset % sizeof(void*) == 0); - assert(page_offset >= 0 && static_cast(page_offset) < max_encoded_offset); - header->page_offset = static_cast(static_cast(page_offset) / sizeof(void*)); - - // full_size == 0 for large strings that occupy the whole page - assert(full_size % sizeof(void*) == 0); - assert(full_size < max_encoded_offset || (page->busy_size == full_size && page_offset == 0)); - header->full_size = static_cast(full_size < max_encoded_offset ? full_size / sizeof(void*) : 0); - - // round-trip through void* to avoid 'cast increases required alignment of target type' warning - // header is guaranteed a pointer-sized alignment, which should be enough for char_t - return static_cast(static_cast(header + 1)); - } - - void deallocate_string(char_t* string) - { - // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings - // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string - - // get header - xml_memory_string_header* header = static_cast(static_cast(string)) - 1; - assert(header); - - // deallocate - size_t page_offset = sizeof(xml_memory_page) + header->page_offset * sizeof(void*); - xml_memory_page* page = reinterpret_cast(static_cast(reinterpret_cast(header) - page_offset)); - - // if full_size == 0 then this string occupies the whole page - size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * sizeof(void*); - - deallocate_memory(header, full_size, page); - } - - xml_memory_page* _root; - size_t _busy_size; - }; - - PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page) - { - const size_t large_allocation_threshold = xml_memory_page_size / 4; - - xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size); - out_page = page; - - if (!page) return 0; - - if (size <= large_allocation_threshold) - { - _root->busy_size = _busy_size; - - // insert page at the end of linked list - page->prev = _root; - _root->next = page; - _root = page; - - _busy_size = size; - } - else - { - // insert page before the end of linked list, so that it is deleted as soon as possible - // the last page is not deleted even if it's empty (see deallocate_memory) - assert(_root->prev); - - page->prev = _root->prev; - page->next = _root; - - _root->prev->next = page; - _root->prev = page; - } - - // allocate inside page - page->busy_size = size; - - return reinterpret_cast(page) + sizeof(xml_memory_page); - } -PUGI__NS_END - -namespace pugi -{ - /// A 'name=value' XML attribute structure. - struct xml_attribute_struct - { - /// Default ctor - xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0) - { - } - - uintptr_t header; - - char_t* name; ///< Pointer to attribute name. - char_t* value; ///< Pointer to attribute value. - - xml_attribute_struct* prev_attribute_c; ///< Previous attribute (cyclic list) - xml_attribute_struct* next_attribute; ///< Next attribute - }; - - /// An XML document tree node. - struct xml_node_struct - { - /// Default ctor - /// \param type - node type - xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast(page) | (type - 1)), parent(0), name(0), value(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0) - { - } - - uintptr_t header; - - xml_node_struct* parent; ///< Pointer to parent - - char_t* name; ///< Pointer to element name. - char_t* value; ///< Pointer to any associated string data. - - xml_node_struct* first_child; ///< First child - - xml_node_struct* prev_sibling_c; ///< Left brother (cyclic list) - xml_node_struct* next_sibling; ///< Right brother - - xml_attribute_struct* first_attribute; ///< First attribute - }; -} - -PUGI__NS_BEGIN - struct xml_extra_buffer - { - char_t* buffer; - xml_extra_buffer* next; - }; - - struct xml_document_struct: public xml_node_struct, public xml_allocator - { - xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0) - { - } - - const char_t* buffer; - - xml_extra_buffer* extra_buffers; - }; - - inline xml_allocator& get_allocator(const xml_node_struct* node) - { - assert(node); - - return *reinterpret_cast(node->header & xml_memory_page_pointer_mask)->allocator; - } - - template inline xml_document_struct& get_document(const Object* object) - { - assert(object); - - return *static_cast(reinterpret_cast(object->header & xml_memory_page_pointer_mask)->allocator); - } -PUGI__NS_END - -// Low-level DOM operations -PUGI__NS_BEGIN - inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc) - { - xml_memory_page* page; - void* memory = alloc.allocate_memory(sizeof(xml_attribute_struct), page); - - return new (memory) xml_attribute_struct(page); - } - - inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type) - { - xml_memory_page* page; - void* memory = alloc.allocate_memory(sizeof(xml_node_struct), page); - - return new (memory) xml_node_struct(page, type); - } - - inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc) - { - uintptr_t header = a->header; - - if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name); - if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value); - - alloc.deallocate_memory(a, sizeof(xml_attribute_struct), reinterpret_cast(header & xml_memory_page_pointer_mask)); - } - - inline void destroy_node(xml_node_struct* n, xml_allocator& alloc) - { - uintptr_t header = n->header; - - if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name); - if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(n->value); - - for (xml_attribute_struct* attr = n->first_attribute; attr; ) - { - xml_attribute_struct* next = attr->next_attribute; - - destroy_attribute(attr, alloc); - - attr = next; - } - - for (xml_node_struct* child = n->first_child; child; ) - { - xml_node_struct* next = child->next_sibling; - - destroy_node(child, alloc); - - child = next; - } - - alloc.deallocate_memory(n, sizeof(xml_node_struct), reinterpret_cast(header & xml_memory_page_pointer_mask)); - } - - inline void append_node(xml_node_struct* child, xml_node_struct* node) - { - child->parent = node; - - xml_node_struct* head = node->first_child; - - if (head) - { - xml_node_struct* tail = head->prev_sibling_c; - - tail->next_sibling = child; - child->prev_sibling_c = tail; - head->prev_sibling_c = child; - } - else - { - node->first_child = child; - child->prev_sibling_c = child; - } - } - - inline void prepend_node(xml_node_struct* child, xml_node_struct* node) - { - child->parent = node; - - xml_node_struct* head = node->first_child; - - if (head) - { - child->prev_sibling_c = head->prev_sibling_c; - head->prev_sibling_c = child; - } - else - child->prev_sibling_c = child; - - child->next_sibling = head; - node->first_child = child; - } - - inline void insert_node_after(xml_node_struct* child, xml_node_struct* node) - { - xml_node_struct* parent = node->parent; - - child->parent = parent; - - if (node->next_sibling) - node->next_sibling->prev_sibling_c = child; - else - parent->first_child->prev_sibling_c = child; - - child->next_sibling = node->next_sibling; - child->prev_sibling_c = node; - - node->next_sibling = child; - } - - inline void insert_node_before(xml_node_struct* child, xml_node_struct* node) - { - xml_node_struct* parent = node->parent; - - child->parent = parent; - - if (node->prev_sibling_c->next_sibling) - node->prev_sibling_c->next_sibling = child; - else - parent->first_child = child; - - child->prev_sibling_c = node->prev_sibling_c; - child->next_sibling = node; - - node->prev_sibling_c = child; - } - - inline void remove_node(xml_node_struct* node) - { - xml_node_struct* parent = node->parent; - - if (node->next_sibling) - node->next_sibling->prev_sibling_c = node->prev_sibling_c; - else - parent->first_child->prev_sibling_c = node->prev_sibling_c; - - if (node->prev_sibling_c->next_sibling) - node->prev_sibling_c->next_sibling = node->next_sibling; - else - parent->first_child = node->next_sibling; - - node->parent = 0; - node->prev_sibling_c = 0; - node->next_sibling = 0; - } - - inline void append_attribute(xml_attribute_struct* attr, xml_node_struct* node) - { - xml_attribute_struct* head = node->first_attribute; - - if (head) - { - xml_attribute_struct* tail = head->prev_attribute_c; - - tail->next_attribute = attr; - attr->prev_attribute_c = tail; - head->prev_attribute_c = attr; - } - else - { - node->first_attribute = attr; - attr->prev_attribute_c = attr; - } - } - - inline void prepend_attribute(xml_attribute_struct* attr, xml_node_struct* node) - { - xml_attribute_struct* head = node->first_attribute; - - if (head) - { - attr->prev_attribute_c = head->prev_attribute_c; - head->prev_attribute_c = attr; - } - else - attr->prev_attribute_c = attr; - - attr->next_attribute = head; - node->first_attribute = attr; - } - - inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) - { - if (place->next_attribute) - place->next_attribute->prev_attribute_c = attr; - else - node->first_attribute->prev_attribute_c = attr; - - attr->next_attribute = place->next_attribute; - attr->prev_attribute_c = place; - place->next_attribute = attr; - } - - inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) - { - if (place->prev_attribute_c->next_attribute) - place->prev_attribute_c->next_attribute = attr; - else - node->first_attribute = attr; - - attr->prev_attribute_c = place->prev_attribute_c; - attr->next_attribute = place; - place->prev_attribute_c = attr; - } - - inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node) - { - if (attr->next_attribute) - attr->next_attribute->prev_attribute_c = attr->prev_attribute_c; - else - node->first_attribute->prev_attribute_c = attr->prev_attribute_c; - - if (attr->prev_attribute_c->next_attribute) - attr->prev_attribute_c->next_attribute = attr->next_attribute; - else - node->first_attribute = attr->next_attribute; - - attr->prev_attribute_c = 0; - attr->next_attribute = 0; - } - - PUGI__FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element) - { - xml_node_struct* child = allocate_node(alloc, type); - if (!child) return 0; - - append_node(child, node); - - return child; - } - - PUGI__FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc) - { - xml_attribute_struct* attr = allocate_attribute(alloc); - if (!attr) return 0; - - append_attribute(attr, node); - - return attr; - } -PUGI__NS_END - -// Helper classes for code generation -PUGI__NS_BEGIN - struct opt_false - { - enum { value = 0 }; - }; - - struct opt_true - { - enum { value = 1 }; - }; -PUGI__NS_END - -// Unicode utilities -PUGI__NS_BEGIN - inline uint16_t endian_swap(uint16_t value) - { - return static_cast(((value & 0xff) << 8) | (value >> 8)); - } - - inline uint32_t endian_swap(uint32_t value) - { - return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24); - } - - struct utf8_counter - { - typedef size_t value_type; - - static value_type low(value_type result, uint32_t ch) - { - // U+0000..U+007F - if (ch < 0x80) return result + 1; - // U+0080..U+07FF - else if (ch < 0x800) return result + 2; - // U+0800..U+FFFF - else return result + 3; - } - - static value_type high(value_type result, uint32_t) - { - // U+10000..U+10FFFF - return result + 4; - } - }; - - struct utf8_writer - { - typedef uint8_t* value_type; - - static value_type low(value_type result, uint32_t ch) - { - // U+0000..U+007F - if (ch < 0x80) - { - *result = static_cast(ch); - return result + 1; - } - // U+0080..U+07FF - else if (ch < 0x800) - { - result[0] = static_cast(0xC0 | (ch >> 6)); - result[1] = static_cast(0x80 | (ch & 0x3F)); - return result + 2; - } - // U+0800..U+FFFF - else - { - result[0] = static_cast(0xE0 | (ch >> 12)); - result[1] = static_cast(0x80 | ((ch >> 6) & 0x3F)); - result[2] = static_cast(0x80 | (ch & 0x3F)); - return result + 3; - } - } - - static value_type high(value_type result, uint32_t ch) - { - // U+10000..U+10FFFF - result[0] = static_cast(0xF0 | (ch >> 18)); - result[1] = static_cast(0x80 | ((ch >> 12) & 0x3F)); - result[2] = static_cast(0x80 | ((ch >> 6) & 0x3F)); - result[3] = static_cast(0x80 | (ch & 0x3F)); - return result + 4; - } - - static value_type any(value_type result, uint32_t ch) - { - return (ch < 0x10000) ? low(result, ch) : high(result, ch); - } - }; - - struct utf16_counter - { - typedef size_t value_type; - - static value_type low(value_type result, uint32_t) - { - return result + 1; - } - - static value_type high(value_type result, uint32_t) - { - return result + 2; - } - }; - - struct utf16_writer - { - typedef uint16_t* value_type; - - static value_type low(value_type result, uint32_t ch) - { - *result = static_cast(ch); - - return result + 1; - } - - static value_type high(value_type result, uint32_t ch) - { - uint32_t msh = static_cast(ch - 0x10000) >> 10; - uint32_t lsh = static_cast(ch - 0x10000) & 0x3ff; - - result[0] = static_cast(0xD800 + msh); - result[1] = static_cast(0xDC00 + lsh); - - return result + 2; - } - - static value_type any(value_type result, uint32_t ch) - { - return (ch < 0x10000) ? low(result, ch) : high(result, ch); - } - }; - - struct utf32_counter - { - typedef size_t value_type; - - static value_type low(value_type result, uint32_t) - { - return result + 1; - } - - static value_type high(value_type result, uint32_t) - { - return result + 1; - } - }; - - struct utf32_writer - { - typedef uint32_t* value_type; - - static value_type low(value_type result, uint32_t ch) - { - *result = ch; - - return result + 1; - } - - static value_type high(value_type result, uint32_t ch) - { - *result = ch; - - return result + 1; - } - - static value_type any(value_type result, uint32_t ch) - { - *result = ch; - - return result + 1; - } - }; - - struct latin1_writer - { - typedef uint8_t* value_type; - - static value_type low(value_type result, uint32_t ch) - { - *result = static_cast(ch > 255 ? '?' : ch); - - return result + 1; - } - - static value_type high(value_type result, uint32_t ch) - { - (void)ch; - - *result = '?'; - - return result + 1; - } - }; - - template struct wchar_selector; - - template <> struct wchar_selector<2> - { - typedef uint16_t type; - typedef utf16_counter counter; - typedef utf16_writer writer; - }; - - template <> struct wchar_selector<4> - { - typedef uint32_t type; - typedef utf32_counter counter; - typedef utf32_writer writer; - }; - - typedef wchar_selector::counter wchar_counter; - typedef wchar_selector::writer wchar_writer; - - template struct utf_decoder - { - static inline typename Traits::value_type decode_utf8_block(const uint8_t* data, size_t size, typename Traits::value_type result) - { - const uint8_t utf8_byte_mask = 0x3f; - - while (size) - { - uint8_t lead = *data; - - // 0xxxxxxx -> U+0000..U+007F - if (lead < 0x80) - { - result = Traits::low(result, lead); - data += 1; - size -= 1; - - // process aligned single-byte (ascii) blocks - if ((reinterpret_cast(data) & 3) == 0) - { - // round-trip through void* to silence 'cast increases required alignment of target type' warnings - while (size >= 4 && (*static_cast(static_cast(data)) & 0x80808080) == 0) - { - result = Traits::low(result, data[0]); - result = Traits::low(result, data[1]); - result = Traits::low(result, data[2]); - result = Traits::low(result, data[3]); - data += 4; - size -= 4; - } - } - } - // 110xxxxx -> U+0080..U+07FF - else if (static_cast(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80) - { - result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask)); - data += 2; - size -= 2; - } - // 1110xxxx -> U+0800-U+FFFF - else if (static_cast(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80) - { - result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask)); - data += 3; - size -= 3; - } - // 11110xxx -> U+10000..U+10FFFF - else if (static_cast(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80) - { - result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask)); - data += 4; - size -= 4; - } - // 10xxxxxx or 11111xxx -> invalid - else - { - data += 1; - size -= 1; - } - } - - return result; - } - - static inline typename Traits::value_type decode_utf16_block(const uint16_t* data, size_t size, typename Traits::value_type result) - { - const uint16_t* end = data + size; - - while (data < end) - { - unsigned int lead = opt_swap::value ? endian_swap(*data) : *data; - - // U+0000..U+D7FF - if (lead < 0xD800) - { - result = Traits::low(result, lead); - data += 1; - } - // U+E000..U+FFFF - else if (static_cast(lead - 0xE000) < 0x2000) - { - result = Traits::low(result, lead); - data += 1; - } - // surrogate pair lead - else if (static_cast(lead - 0xD800) < 0x400 && data + 1 < end) - { - uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; - - if (static_cast(next - 0xDC00) < 0x400) - { - result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); - data += 2; - } - else - { - data += 1; - } - } - else - { - data += 1; - } - } - - return result; - } - - static inline typename Traits::value_type decode_utf32_block(const uint32_t* data, size_t size, typename Traits::value_type result) - { - const uint32_t* end = data + size; - - while (data < end) - { - uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; - - // U+0000..U+FFFF - if (lead < 0x10000) - { - result = Traits::low(result, lead); - data += 1; - } - // U+10000..U+10FFFF - else - { - result = Traits::high(result, lead); - data += 1; - } - } - - return result; - } - - static inline typename Traits::value_type decode_latin1_block(const uint8_t* data, size_t size, typename Traits::value_type result) - { - for (size_t i = 0; i < size; ++i) - { - result = Traits::low(result, data[i]); - } - - return result; - } - - static inline typename Traits::value_type decode_wchar_block_impl(const uint16_t* data, size_t size, typename Traits::value_type result) - { - return decode_utf16_block(data, size, result); - } - - static inline typename Traits::value_type decode_wchar_block_impl(const uint32_t* data, size_t size, typename Traits::value_type result) - { - return decode_utf32_block(data, size, result); - } - - static inline typename Traits::value_type decode_wchar_block(const wchar_t* data, size_t size, typename Traits::value_type result) - { - return decode_wchar_block_impl(reinterpret_cast::type*>(data), size, result); - } - }; - - template PUGI__FN void convert_utf_endian_swap(T* result, const T* data, size_t length) - { - for (size_t i = 0; i < length; ++i) result[i] = endian_swap(data[i]); - } - -#ifdef PUGIXML_WCHAR_MODE - PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length) - { - for (size_t i = 0; i < length; ++i) result[i] = static_cast(endian_swap(static_cast::type>(data[i]))); - } -#endif -PUGI__NS_END - -PUGI__NS_BEGIN - enum chartype_t - { - ct_parse_pcdata = 1, // \0, &, \r, < - ct_parse_attr = 2, // \0, &, \r, ', " - ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab - ct_space = 8, // \r, \n, space, tab - ct_parse_cdata = 16, // \0, ], >, \r - ct_parse_comment = 32, // \0, -, >, \r - ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, . - ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, : - }; - - static const unsigned char chartype_table[256] = - { - 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 - 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47 - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63 - 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79 - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95 - 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111 - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127 - - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+ - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192 - }; - - enum chartypex_t - { - ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > - ctx_special_attr = 2, // Any symbol >= 0 and < 32 (except \t), &, <, >, " - ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _ - ctx_digit = 8, // 0-9 - ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . - }; - - static const unsigned char chartypex_table[256] = - { - 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15 - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31 - 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63 - - 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79 - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95 - 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 96-111 - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, // 112-127 - - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 128+ - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 - }; - -#ifdef PUGIXML_WCHAR_MODE - #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) -#else - #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) -#endif - - #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table) - #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table) - - PUGI__FN bool is_little_endian() - { - unsigned int ui = 1; - - return *reinterpret_cast(&ui) == 1; - } - - PUGI__FN xml_encoding get_wchar_encoding() - { - PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); - - if (sizeof(wchar_t) == 2) - return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - else - return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - } - - PUGI__FN xml_encoding guess_buffer_encoding(uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) - { - // look for BOM in first few bytes - if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be; - if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le; - if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be; - if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le; - if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8; - - // look for <, (contents); - - PUGI__DMC_VOLATILE uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; - - return guess_buffer_encoding(d0, d1, d2, d3); - } - - PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) - { - size_t length = size / sizeof(char_t); - - if (is_mutable) - { - out_buffer = static_cast(const_cast(contents)); - out_length = length; - } - else - { - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - if (contents) - memcpy(buffer, contents, length * sizeof(char_t)); - else - assert(length == 0); - - buffer[length] = 0; - - out_buffer = buffer; - out_length = length + 1; - } - - return true; - } - -#ifdef PUGIXML_WCHAR_MODE - PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re) - { - return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) || - (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be); - } - - PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) - { - const char_t* data = static_cast(contents); - size_t length = size / sizeof(char_t); - - if (is_mutable) - { - char_t* buffer = const_cast(data); - - convert_wchar_endian_swap(buffer, data, length); - - out_buffer = buffer; - out_length = length; - } - else - { - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - convert_wchar_endian_swap(buffer, data, length); - buffer[length] = 0; - - out_buffer = buffer; - out_length = length + 1; - } - - return true; - } - - PUGI__FN bool convert_buffer_utf8(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size) - { - const uint8_t* data = static_cast(contents); - size_t data_length = size; - - // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf8_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert utf8 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_utf8_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - template PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) - { - const uint16_t* data = static_cast(contents); - size_t data_length = size / sizeof(uint16_t); - - // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf16_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert utf16 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_utf16_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - template PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) - { - const uint32_t* data = static_cast(contents); - size_t data_length = size / sizeof(uint32_t); - - // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf32_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert utf32 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_utf32_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size) - { - const uint8_t* data = static_cast(contents); - size_t data_length = size; - - // get length in wchar_t units - size_t length = data_length; - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // convert latin1 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_latin1_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) - { - // get native encoding - xml_encoding wchar_encoding = get_wchar_encoding(); - - // fast path: no conversion required - if (encoding == wchar_encoding) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); - - // only endian-swapping is required - if (need_endian_swap_utf(encoding, wchar_encoding)) return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable); - - // source encoding is utf8 - if (encoding == encoding_utf8) return convert_buffer_utf8(out_buffer, out_length, contents, size); - - // source encoding is utf16 - if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) - { - xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - return (native_encoding == encoding) ? - convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) : - convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); - } - - // source encoding is utf32 - if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) - { - xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - return (native_encoding == encoding) ? - convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) : - convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); - } - - // source encoding is latin1 - if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size); - - assert(!"Invalid encoding"); - return false; - } -#else - template PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) - { - const uint16_t* data = static_cast(contents); - size_t data_length = size / sizeof(uint16_t); - - // first pass: get length in utf8 units - size_t length = utf_decoder::decode_utf16_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert utf16 input to utf8 - uint8_t* obegin = reinterpret_cast(buffer); - uint8_t* oend = utf_decoder::decode_utf16_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - template PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) - { - const uint32_t* data = static_cast(contents); - size_t data_length = size / sizeof(uint32_t); - - // first pass: get length in utf8 units - size_t length = utf_decoder::decode_utf32_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert utf32 input to utf8 - uint8_t* obegin = reinterpret_cast(buffer); - uint8_t* oend = utf_decoder::decode_utf32_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size) - { - for (size_t i = 0; i < size; ++i) - if (data[i] > 127) - return i; - - return size; - } - - PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) - { - const uint8_t* data = static_cast(contents); - size_t data_length = size; - - // get size of prefix that does not need utf8 conversion - size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length); - assert(prefix_length <= data_length); - - const uint8_t* postfix = data + prefix_length; - size_t postfix_length = data_length - prefix_length; - - // if no conversion is needed, just return the original buffer - if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); - - // first pass: get length in utf8 units - size_t length = prefix_length + utf_decoder::decode_latin1_block(postfix, postfix_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert latin1 input to utf8 - memcpy(buffer, data, prefix_length); - - uint8_t* obegin = reinterpret_cast(buffer); - uint8_t* oend = utf_decoder::decode_latin1_block(postfix, postfix_length, obegin + prefix_length); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) - { - // fast path: no conversion required - if (encoding == encoding_utf8) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); - - // source encoding is utf16 - if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) - { - xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - return (native_encoding == encoding) ? - convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) : - convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); - } - - // source encoding is utf32 - if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) - { - xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - return (native_encoding == encoding) ? - convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) : - convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); - } - - // source encoding is latin1 - if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable); - - assert(!"Invalid encoding"); - return false; - } -#endif - - PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length) - { - // get length in utf8 characters - return utf_decoder::decode_wchar_block(str, length, 0); - } - - PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) - { - // convert to utf8 - uint8_t* begin = reinterpret_cast(buffer); - uint8_t* end = utf_decoder::decode_wchar_block(str, length, begin); - - assert(begin + size == end); - (void)!end; - - // zero-terminate - buffer[size] = 0; - } - -#ifndef PUGIXML_NO_STL - PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length) - { - // first pass: get length in utf8 characters - size_t size = as_utf8_begin(str, length); - - // allocate resulting string - std::string result; - result.resize(size); - - // second pass: convert to utf8 - if (size > 0) as_utf8_end(&result[0], size, str, length); - - return result; - } - - PUGI__FN std::basic_string as_wide_impl(const char* str, size_t size) - { - const uint8_t* data = reinterpret_cast(str); - - // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf8_block(data, size, 0); - - // allocate resulting string - std::basic_string result; - result.resize(length); - - // second pass: convert to wchar_t - if (length > 0) - { - wchar_writer::value_type begin = reinterpret_cast(&result[0]); - wchar_writer::value_type end = utf_decoder::decode_utf8_block(data, size, begin); - - assert(begin + length == end); - (void)!end; - } - - return result; - } -#endif - - inline bool strcpy_insitu_allow(size_t length, uintptr_t header, uintptr_t header_mask, char_t* target) - { - // never reuse shared memory - if (header & xml_memory_page_contents_shared_mask) return false; - - size_t target_length = strlength(target); - - // always reuse document buffer memory if possible - if ((header & header_mask) == 0) return target_length >= length; - - // reuse heap memory if waste is not too great - const size_t reuse_threshold = 32; - - return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2); - } - - PUGI__FN bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source) - { - assert(header); - - size_t source_length = strlength(source); - - if (source_length == 0) - { - // empty string and null pointer are equivalent, so just deallocate old memory - xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator; - - if (header & header_mask) alloc->deallocate_string(dest); - - // mark the string as not allocated - dest = 0; - header &= ~header_mask; - - return true; - } - else if (dest && strcpy_insitu_allow(source_length, header, header_mask, dest)) - { - // we can reuse old buffer, so just copy the new data (including zero terminator) - memcpy(dest, source, (source_length + 1) * sizeof(char_t)); - - return true; - } - else - { - xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator; - - // allocate new buffer - char_t* buf = alloc->allocate_string(source_length + 1); - if (!buf) return false; - - // copy the string (including zero terminator) - memcpy(buf, source, (source_length + 1) * sizeof(char_t)); - - // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures) - if (header & header_mask) alloc->deallocate_string(dest); - - // the string is now allocated, so set the flag - dest = buf; - header |= header_mask; - - return true; - } - } - - struct gap - { - char_t* end; - size_t size; - - gap(): end(0), size(0) - { - } - - // Push new gap, move s count bytes further (skipping the gap). - // Collapse previous gap. - void push(char_t*& s, size_t count) - { - if (end) // there was a gap already; collapse it - { - // Move [old_gap_end, new_gap_start) to [old_gap_start, ...) - assert(s >= end); - memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); - } - - s += count; // end of current gap - - // "merge" two gaps - end = s; - size += count; - } - - // Collapse all gaps, return past-the-end pointer - char_t* flush(char_t* s) - { - if (end) - { - // Move [old_gap_end, current_pos) to [old_gap_start, ...) - assert(s >= end); - memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); - - return s - size; - } - else return s; - } - }; - - PUGI__FN char_t* strconv_escape(char_t* s, gap& g) - { - char_t* stre = s + 1; - - switch (*stre) - { - case '#': // &#... - { - unsigned int ucsc = 0; - - if (stre[1] == 'x') // &#x... (hex code) - { - stre += 2; - - char_t ch = *stre; - - if (ch == ';') return stre; - - for (;;) - { - if (static_cast(ch - '0') <= 9) - ucsc = 16 * ucsc + (ch - '0'); - else if (static_cast((ch | ' ') - 'a') <= 5) - ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10); - else if (ch == ';') - break; - else // cancel - return stre; - - ch = *++stre; - } - - ++stre; - } - else // &#... (dec code) - { - char_t ch = *++stre; - - if (ch == ';') return stre; - - for (;;) - { - if (static_cast(static_cast(ch) - '0') <= 9) - ucsc = 10 * ucsc + (ch - '0'); - else if (ch == ';') - break; - else // cancel - return stre; - - ch = *++stre; - } - - ++stre; - } - - #ifdef PUGIXML_WCHAR_MODE - s = reinterpret_cast(wchar_writer::any(reinterpret_cast(s), ucsc)); - #else - s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc)); - #endif - - g.push(s, stre - s); - return stre; - } - - case 'a': // &a - { - ++stre; - - if (*stre == 'm') // &am - { - if (*++stre == 'p' && *++stre == ';') // & - { - *s++ = '&'; - ++stre; - - g.push(s, stre - s); - return stre; - } - } - else if (*stre == 'p') // &ap - { - if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // ' - { - *s++ = '\''; - ++stre; - - g.push(s, stre - s); - return stre; - } - } - break; - } - - case 'g': // &g - { - if (*++stre == 't' && *++stre == ';') // > - { - *s++ = '>'; - ++stre; - - g.push(s, stre - s); - return stre; - } - break; - } - - case 'l': // &l - { - if (*++stre == 't' && *++stre == ';') // < - { - *s++ = '<'; - ++stre; - - g.push(s, stre - s); - return stre; - } - break; - } - - case 'q': // &q - { - if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // " - { - *s++ = '"'; - ++stre; - - g.push(s, stre - s); - return stre; - } - break; - } - - default: - break; - } - - return stre; - } - - // Parser utilities - #define PUGI__ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) - #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; } - #define PUGI__OPTSET(OPT) ( optmsk & (OPT) ) - #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); } - #define PUGI__POPNODE() { cursor = cursor->parent; } - #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; } - #define PUGI__SCANWHILE(X) { while (X) ++s; } - #define PUGI__SCANWHILE_UNROLL(X) { for (;;) { char_t ss = s[0]; if (PUGI__UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI__UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI__UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI__UNLIKELY(!(X))) { s += 3; break; } s += 4; } } - #define PUGI__ENDSEG() { ch = *s; *s = 0; ++s; } - #define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast(0) - #define PUGI__CHECK_ERROR(err, m) { if (*s == 0) PUGI__THROW_ERROR(err, m); } - - PUGI__FN char_t* strconv_comment(char_t* s, char_t endch) - { - gap g; - - while (true) - { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_comment)); - - if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair - { - *s++ = '\n'; // replace first one with 0x0a - - if (*s == '\n') g.push(s, 1); - } - else if (s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')) // comment ends here - { - *g.flush(s) = 0; - - return s + (s[2] == '>' ? 3 : 2); - } - else if (*s == 0) - { - return 0; - } - else ++s; - } - } - - PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch) - { - gap g; - - while (true) - { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_cdata)); - - if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair - { - *s++ = '\n'; // replace first one with 0x0a - - if (*s == '\n') g.push(s, 1); - } - else if (s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')) // CDATA ends here - { - *g.flush(s) = 0; - - return s + 1; - } - else if (*s == 0) - { - return 0; - } - else ++s; - } - } - - typedef char_t* (*strconv_pcdata_t)(char_t*); - - template struct strconv_pcdata_impl - { - static char_t* parse(char_t* s) - { - gap g; - - char_t* begin = s; - - while (true) - { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_pcdata)); - - if (*s == '<') // PCDATA ends here - { - char_t* end = g.flush(s); - - if (opt_trim::value) - while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) - --end; - - *end = 0; - - return s + 1; - } - else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair - { - *s++ = '\n'; // replace first one with 0x0a - - if (*s == '\n') g.push(s, 1); - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (*s == 0) - { - char_t* end = g.flush(s); - - if (opt_trim::value) - while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) - --end; - - *end = 0; - - return s; - } - else ++s; - } - } - }; - - PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) - { - PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); - - switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (eol escapes trim) - { - case 0: return strconv_pcdata_impl::parse; - case 1: return strconv_pcdata_impl::parse; - case 2: return strconv_pcdata_impl::parse; - case 3: return strconv_pcdata_impl::parse; - case 4: return strconv_pcdata_impl::parse; - case 5: return strconv_pcdata_impl::parse; - case 6: return strconv_pcdata_impl::parse; - case 7: return strconv_pcdata_impl::parse; - default: assert(false); return 0; // should not get here - } - } - - typedef char_t* (*strconv_attribute_t)(char_t*, char_t); - - template struct strconv_attribute_impl - { - static char_t* parse_wnorm(char_t* s, char_t end_quote) - { - gap g; - - // trim leading whitespaces - if (PUGI__IS_CHARTYPE(*s, ct_space)) - { - char_t* str = s; - - do ++str; - while (PUGI__IS_CHARTYPE(*str, ct_space)); - - g.push(s, str - s); - } - - while (true) - { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space)); - - if (*s == end_quote) - { - char_t* str = g.flush(s); - - do *str-- = 0; - while (PUGI__IS_CHARTYPE(*str, ct_space)); - - return s + 1; - } - else if (PUGI__IS_CHARTYPE(*s, ct_space)) - { - *s++ = ' '; - - if (PUGI__IS_CHARTYPE(*s, ct_space)) - { - char_t* str = s + 1; - while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str; - - g.push(s, str - s); - } - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (!*s) - { - return 0; - } - else ++s; - } - } - - static char_t* parse_wconv(char_t* s, char_t end_quote) - { - gap g; - - while (true) - { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws)); - - if (*s == end_quote) - { - *g.flush(s) = 0; - - return s + 1; - } - else if (PUGI__IS_CHARTYPE(*s, ct_space)) - { - if (*s == '\r') - { - *s++ = ' '; - - if (*s == '\n') g.push(s, 1); - } - else *s++ = ' '; - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (!*s) - { - return 0; - } - else ++s; - } - } - - static char_t* parse_eol(char_t* s, char_t end_quote) - { - gap g; - - while (true) - { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); - - if (*s == end_quote) - { - *g.flush(s) = 0; - - return s + 1; - } - else if (*s == '\r') - { - *s++ = '\n'; - - if (*s == '\n') g.push(s, 1); - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (!*s) - { - return 0; - } - else ++s; - } - } - - static char_t* parse_simple(char_t* s, char_t end_quote) - { - gap g; - - while (true) - { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); - - if (*s == end_quote) - { - *g.flush(s) = 0; - - return s + 1; - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (!*s) - { - return 0; - } - else ++s; - } - } - }; - - PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) - { - PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); - - switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes) - { - case 0: return strconv_attribute_impl::parse_simple; - case 1: return strconv_attribute_impl::parse_simple; - case 2: return strconv_attribute_impl::parse_eol; - case 3: return strconv_attribute_impl::parse_eol; - case 4: return strconv_attribute_impl::parse_wconv; - case 5: return strconv_attribute_impl::parse_wconv; - case 6: return strconv_attribute_impl::parse_wconv; - case 7: return strconv_attribute_impl::parse_wconv; - case 8: return strconv_attribute_impl::parse_wnorm; - case 9: return strconv_attribute_impl::parse_wnorm; - case 10: return strconv_attribute_impl::parse_wnorm; - case 11: return strconv_attribute_impl::parse_wnorm; - case 12: return strconv_attribute_impl::parse_wnorm; - case 13: return strconv_attribute_impl::parse_wnorm; - case 14: return strconv_attribute_impl::parse_wnorm; - case 15: return strconv_attribute_impl::parse_wnorm; - default: assert(false); return 0; // should not get here - } - } - - inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0) - { - xml_parse_result result; - result.status = status; - result.offset = offset; - - return result; - } - - struct xml_parser - { - xml_allocator alloc; - char_t* error_offset; - xml_parse_status error_status; - - xml_parser(const xml_allocator& alloc_): alloc(alloc_), error_offset(0), error_status(status_ok) - { - } - - // DOCTYPE consists of nested sections of the following possible types: - // , , "...", '...' - // - // - // First group can not contain nested groups - // Second group can contain nested groups of the same type - // Third group can contain all other groups - char_t* parse_doctype_primitive(char_t* s) - { - if (*s == '"' || *s == '\'') - { - // quoted string - char_t ch = *s++; - PUGI__SCANFOR(*s == ch); - if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); - - s++; - } - else if (s[0] == '<' && s[1] == '?') - { - // - s += 2; - PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype - if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); - - s += 2; - } - else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') - { - s += 4; - PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype - if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); - - s += 3; - } - else PUGI__THROW_ERROR(status_bad_doctype, s); - - return s; - } - - char_t* parse_doctype_ignore(char_t* s) - { - size_t depth = 0; - - assert(s[0] == '<' && s[1] == '!' && s[2] == '['); - s += 3; - - while (*s) - { - if (s[0] == '<' && s[1] == '!' && s[2] == '[') - { - // nested ignore section - s += 3; - depth++; - } - else if (s[0] == ']' && s[1] == ']' && s[2] == '>') - { - // ignore section end - s += 3; - - if (depth == 0) - return s; - - depth--; - } - else s++; - } - - PUGI__THROW_ERROR(status_bad_doctype, s); - } - - char_t* parse_doctype_group(char_t* s, char_t endch) - { - size_t depth = 0; - - assert((s[0] == '<' || s[0] == 0) && s[1] == '!'); - s += 2; - - while (*s) - { - if (s[0] == '<' && s[1] == '!' && s[2] != '-') - { - if (s[2] == '[') - { - // ignore - s = parse_doctype_ignore(s); - if (!s) return s; - } - else - { - // some control group - s += 2; - depth++; - } - } - else if (s[0] == '<' || s[0] == '"' || s[0] == '\'') - { - // unknown tag (forbidden), or some primitive group - s = parse_doctype_primitive(s); - if (!s) return s; - } - else if (*s == '>') - { - if (depth == 0) - return s; - - depth--; - s++; - } - else s++; - } - - if (depth != 0 || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s); - - return s; - } - - char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch) - { - // parse node contents, starting with exclamation mark - ++s; - - if (*s == '-') // 'value = s; // Save the offset. - } - - if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments)) - { - s = strconv_comment(s, endch); - - if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value); - } - else - { - // Scan for terminating '-->'. - PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')); - PUGI__CHECK_ERROR(status_bad_comment, s); - - if (PUGI__OPTSET(parse_comments)) - *s = 0; // Zero-terminate this segment at the first terminating '-'. - - s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. - } - } - else PUGI__THROW_ERROR(status_bad_comment, s); - } - else if (*s == '[') - { - // 'value = s; // Save the offset. - - if (PUGI__OPTSET(parse_eol)) - { - s = strconv_cdata(s, endch); - - if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value); - } - else - { - // Scan for terminating ']]>'. - PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); - PUGI__CHECK_ERROR(status_bad_cdata, s); - - *s++ = 0; // Zero-terminate this segment. - } - } - else // Flagged for discard, but we still have to scan for the terminator. - { - // Scan for terminating ']]>'. - PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); - PUGI__CHECK_ERROR(status_bad_cdata, s); - - ++s; - } - - s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. - } - else PUGI__THROW_ERROR(status_bad_cdata, s); - } - else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && PUGI__ENDSWITH(s[6], 'E')) - { - s -= 2; - - if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s); - - char_t* mark = s + 9; - - s = parse_doctype_group(s, endch); - if (!s) return s; - - assert((*s == 0 && endch == '>') || *s == '>'); - if (*s) *s++ = 0; - - if (PUGI__OPTSET(parse_doctype)) - { - while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark; - - PUGI__PUSHNODE(node_doctype); - - cursor->value = mark; - } - } - else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s); - else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s); - else PUGI__THROW_ERROR(status_unrecognized_tag, s); - - return s; - } - - char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch) - { - // load into registers - xml_node_struct* cursor = ref_cursor; - char_t ch = 0; - - // parse node contents, starting with question mark - ++s; - - // read PI target - char_t* target = s; - - if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s); - - PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); - PUGI__CHECK_ERROR(status_bad_pi, s); - - // determine node type; stricmp / strcasecmp is not portable - bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s; - - if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi)) - { - if (declaration) - { - // disallow non top-level declarations - if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s); - - PUGI__PUSHNODE(node_declaration); - } - else - { - PUGI__PUSHNODE(node_pi); - } - - cursor->name = target; - - PUGI__ENDSEG(); - - // parse value/attributes - if (ch == '?') - { - // empty node - if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s); - s += (*s == '>'); - - PUGI__POPNODE(); - } - else if (PUGI__IS_CHARTYPE(ch, ct_space)) - { - PUGI__SKIPWS(); - - // scan for tag end - char_t* value = s; - - PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); - PUGI__CHECK_ERROR(status_bad_pi, s); - - if (declaration) - { - // replace ending ? with / so that 'element' terminates properly - *s = '/'; - - // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES - s = value; - } - else - { - // store value and step over > - cursor->value = value; - PUGI__POPNODE(); - - PUGI__ENDSEG(); - - s += (*s == '>'); - } - } - else PUGI__THROW_ERROR(status_bad_pi, s); - } - else - { - // scan for tag end - PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); - PUGI__CHECK_ERROR(status_bad_pi, s); - - s += (s[1] == '>' ? 2 : 1); - } - - // store from registers - ref_cursor = cursor; - - return s; - } - - char_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch) - { - strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk); - strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk); - - char_t ch = 0; - xml_node_struct* cursor = root; - char_t* mark = s; - - while (*s != 0) - { - if (*s == '<') - { - ++s; - - LOC_TAG: - if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' - { - PUGI__PUSHNODE(node_element); // Append a new node to the tree. - - cursor->name = s; - - PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. - PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. - - if (ch == '>') - { - // end of tag - } - else if (PUGI__IS_CHARTYPE(ch, ct_space)) - { - LOC_ATTRIBUTES: - while (true) - { - PUGI__SKIPWS(); // Eat any whitespace. - - if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #... - { - xml_attribute_struct* a = append_new_attribute(cursor, alloc); // Make space for this attribute. - if (!a) PUGI__THROW_ERROR(status_out_of_memory, s); - - a->name = s; // Save the offset. - - PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. - PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. - - if (PUGI__IS_CHARTYPE(ch, ct_space)) - { - PUGI__SKIPWS(); // Eat any whitespace. - - ch = *s; - ++s; - } - - if (ch == '=') // '<... #=...' - { - PUGI__SKIPWS(); // Eat any whitespace. - - if (*s == '"' || *s == '\'') // '<... #="...' - { - ch = *s; // Save quote char to avoid breaking on "''" -or- '""'. - ++s; // Step over the quote. - a->value = s; // Save the offset. - - s = strconv_attribute(s, ch); - - if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value); - - // After this line the loop continues from the start; - // Whitespaces, / and > are ok, symbols and EOF are wrong, - // everything else will be detected - if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s); - } - else PUGI__THROW_ERROR(status_bad_attribute, s); - } - else PUGI__THROW_ERROR(status_bad_attribute, s); - } - else if (*s == '/') - { - ++s; - - if (*s == '>') - { - PUGI__POPNODE(); - s++; - break; - } - else if (*s == 0 && endch == '>') - { - PUGI__POPNODE(); - break; - } - else PUGI__THROW_ERROR(status_bad_start_element, s); - } - else if (*s == '>') - { - ++s; - - break; - } - else if (*s == 0 && endch == '>') - { - break; - } - else PUGI__THROW_ERROR(status_bad_start_element, s); - } - - // !!! - } - else if (ch == '/') // '<#.../' - { - if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s); - - PUGI__POPNODE(); // Pop. - - s += (*s == '>'); - } - else if (ch == 0) - { - // we stepped over null terminator, backtrack & handle closing tag - --s; - - if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s); - } - else PUGI__THROW_ERROR(status_bad_start_element, s); - } - else if (*s == '/') - { - ++s; - - char_t* name = cursor->name; - if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, s); - - while (PUGI__IS_CHARTYPE(*s, ct_symbol)) - { - if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, s); - } - - if (*name) - { - if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s); - else PUGI__THROW_ERROR(status_end_element_mismatch, s); - } - - PUGI__POPNODE(); // Pop. - - PUGI__SKIPWS(); - - if (*s == 0) - { - if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s); - } - else - { - if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s); - ++s; - } - } - else if (*s == '?') // 'first_child) continue; - } - } - - if (!PUGI__OPTSET(parse_trim_pcdata)) - s = mark; - - if (cursor->parent || PUGI__OPTSET(parse_fragment)) - { - PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree. - cursor->value = s; // Save the offset. - - s = strconv_pcdata(s); - - PUGI__POPNODE(); // Pop since this is a standalone. - - if (!*s) break; - } - else - { - PUGI__SCANFOR(*s == '<'); // '...<' - if (!*s) break; - - ++s; - } - - // We're after '<' - goto LOC_TAG; - } - } - - // check that last tag is closed - if (cursor != root) PUGI__THROW_ERROR(status_end_element_mismatch, s); - - return s; - } - - #ifdef PUGIXML_WCHAR_MODE - static char_t* parse_skip_bom(char_t* s) - { - unsigned int bom = 0xfeff; - return (s[0] == static_cast(bom)) ? s + 1 : s; - } - #else - static char_t* parse_skip_bom(char_t* s) - { - return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s; - } - #endif - - static bool has_element_node_siblings(xml_node_struct* node) - { - while (node) - { - if (PUGI__NODETYPE(node) == node_element) return true; - - node = node->next_sibling; - } - - return false; - } - - static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk) - { - // allocator object is a part of document object - xml_allocator& alloc_ = *static_cast(xmldoc); - - // early-out for empty documents - if (length == 0) - return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element); - - // get last child of the root before parsing - xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c : 0; - - // create parser on stack - xml_parser parser(alloc_); - - // save last character and make buffer zero-terminated (speeds up parsing) - char_t endch = buffer[length - 1]; - buffer[length - 1] = 0; - - // skip BOM to make sure it does not end up as part of parse output - char_t* buffer_data = parse_skip_bom(buffer); - - // perform actual parsing - parser.parse_tree(buffer_data, root, optmsk, endch); - - // update allocator state - alloc_ = parser.alloc; - - xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0); - assert(result.offset >= 0 && static_cast(result.offset) <= length); - - if (result) - { - // since we removed last character, we have to handle the only possible false positive (stray <) - if (endch == '<') - return make_parse_result(status_unrecognized_tag, length - 1); - - // check if there are any element nodes parsed - xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling : root->first_child; - - if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed)) - return make_parse_result(status_no_document_element, length - 1); - } - else - { - // roll back offset if it occurs on a null terminator in the source buffer - if (result.offset > 0 && static_cast(result.offset) == length - 1 && endch == 0) - result.offset--; - } - - return result; - } - }; - - // Output facilities - PUGI__FN xml_encoding get_write_native_encoding() - { - #ifdef PUGIXML_WCHAR_MODE - return get_wchar_encoding(); - #else - return encoding_utf8; - #endif - } - - PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding) - { - // replace wchar encoding with utf implementation - if (encoding == encoding_wchar) return get_wchar_encoding(); - - // replace utf16 encoding with utf16 with specific endianness - if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - // replace utf32 encoding with utf32 with specific endianness - if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - // only do autodetection if no explicit encoding is requested - if (encoding != encoding_auto) return encoding; - - // assume utf8 encoding - return encoding_utf8; - } - -#ifdef PUGIXML_WCHAR_MODE - PUGI__FN size_t get_valid_length(const char_t* data, size_t length) - { - if (length < 1) return 0; - - // discard last character if it's the lead of a surrogate pair - return (sizeof(wchar_t) == 2 && static_cast(static_cast(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length; - } - - PUGI__FN size_t convert_buffer_output(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) - { - // only endian-swapping is required - if (need_endian_swap_utf(encoding, get_wchar_encoding())) - { - convert_wchar_endian_swap(r_char, data, length); - - return length * sizeof(char_t); - } - - // convert to utf8 - if (encoding == encoding_utf8) - { - uint8_t* dest = r_u8; - uint8_t* end = utf_decoder::decode_wchar_block(data, length, dest); - - return static_cast(end - dest); - } - - // convert to utf16 - if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) - { - uint16_t* dest = r_u16; - - // convert to native utf16 - uint16_t* end = utf_decoder::decode_wchar_block(data, length, dest); - - // swap if necessary - xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint16_t); - } - - // convert to utf32 - if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) - { - uint32_t* dest = r_u32; - - // convert to native utf32 - uint32_t* end = utf_decoder::decode_wchar_block(data, length, dest); - - // swap if necessary - xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint32_t); - } - - // convert to latin1 - if (encoding == encoding_latin1) - { - uint8_t* dest = r_u8; - uint8_t* end = utf_decoder::decode_wchar_block(data, length, dest); - - return static_cast(end - dest); - } - - assert(!"Invalid encoding"); - return 0; - } -#else - PUGI__FN size_t get_valid_length(const char_t* data, size_t length) - { - if (length < 5) return 0; - - for (size_t i = 1; i <= 4; ++i) - { - uint8_t ch = static_cast(data[length - i]); - - // either a standalone character or a leading one - if ((ch & 0xc0) != 0x80) return length - i; - } - - // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk - return length; - } - - PUGI__FN size_t convert_buffer_output(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) - { - if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) - { - uint16_t* dest = r_u16; - - // convert to native utf16 - uint16_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); - - // swap if necessary - xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint16_t); - } - - if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) - { - uint32_t* dest = r_u32; - - // convert to native utf32 - uint32_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); - - // swap if necessary - xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint32_t); - } - - if (encoding == encoding_latin1) - { - uint8_t* dest = r_u8; - uint8_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); - - return static_cast(end - dest); - } - - assert(!"Invalid encoding"); - return 0; - } -#endif - - class xml_buffered_writer - { - xml_buffered_writer(const xml_buffered_writer&); - xml_buffered_writer& operator=(const xml_buffered_writer&); - - public: - xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding)) - { - PUGI__STATIC_ASSERT(bufcapacity >= 8); - } - - ~xml_buffered_writer() - { - flush(); - } - - size_t flush() - { - flush(buffer, bufsize); - bufsize = 0; - return 0; - } - - void flush(const char_t* data, size_t size) - { - if (size == 0) return; - - // fast path, just write data - if (encoding == get_write_native_encoding()) - writer.write(data, size * sizeof(char_t)); - else - { - // convert chunk - size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding); - assert(result <= sizeof(scratch)); - - // write data - writer.write(scratch.data_u8, result); - } - } - - void write_direct(const char_t* data, size_t length) - { - // flush the remaining buffer contents - flush(); - - // handle large chunks - if (length > bufcapacity) - { - if (encoding == get_write_native_encoding()) - { - // fast path, can just write data chunk - writer.write(data, length * sizeof(char_t)); - return; - } - - // need to convert in suitable chunks - while (length > bufcapacity) - { - // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer - // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary) - size_t chunk_size = get_valid_length(data, bufcapacity); - assert(chunk_size); - - // convert chunk and write - flush(data, chunk_size); - - // iterate - data += chunk_size; - length -= chunk_size; - } - - // small tail is copied below - bufsize = 0; - } - - memcpy(buffer + bufsize, data, length * sizeof(char_t)); - bufsize += length; - } - - void write_buffer(const char_t* data, size_t length) - { - size_t offset = bufsize; - - if (offset + length <= bufcapacity) - { - memcpy(buffer + offset, data, length * sizeof(char_t)); - bufsize = offset + length; - } - else - { - write_direct(data, length); - } - } - - void write_string(const char_t* data) - { - // write the part of the string that fits in the buffer - size_t offset = bufsize; - - while (*data && offset < bufcapacity) - buffer[offset++] = *data++; - - // write the rest - if (offset < bufcapacity) - { - bufsize = offset; - } - else - { - // backtrack a bit if we have split the codepoint - size_t length = offset - bufsize; - size_t extra = length - get_valid_length(data - length, length); - - bufsize = offset - extra; - - write_direct(data - extra, strlength(data) + extra); - } - } - - void write(char_t d0) - { - size_t offset = bufsize; - if (offset > bufcapacity - 1) offset = flush(); - - buffer[offset + 0] = d0; - bufsize = offset + 1; - } - - void write(char_t d0, char_t d1) - { - size_t offset = bufsize; - if (offset > bufcapacity - 2) offset = flush(); - - buffer[offset + 0] = d0; - buffer[offset + 1] = d1; - bufsize = offset + 2; - } - - void write(char_t d0, char_t d1, char_t d2) - { - size_t offset = bufsize; - if (offset > bufcapacity - 3) offset = flush(); - - buffer[offset + 0] = d0; - buffer[offset + 1] = d1; - buffer[offset + 2] = d2; - bufsize = offset + 3; - } - - void write(char_t d0, char_t d1, char_t d2, char_t d3) - { - size_t offset = bufsize; - if (offset > bufcapacity - 4) offset = flush(); - - buffer[offset + 0] = d0; - buffer[offset + 1] = d1; - buffer[offset + 2] = d2; - buffer[offset + 3] = d3; - bufsize = offset + 4; - } - - void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4) - { - size_t offset = bufsize; - if (offset > bufcapacity - 5) offset = flush(); - - buffer[offset + 0] = d0; - buffer[offset + 1] = d1; - buffer[offset + 2] = d2; - buffer[offset + 3] = d3; - buffer[offset + 4] = d4; - bufsize = offset + 5; - } - - void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5) - { - size_t offset = bufsize; - if (offset > bufcapacity - 6) offset = flush(); - - buffer[offset + 0] = d0; - buffer[offset + 1] = d1; - buffer[offset + 2] = d2; - buffer[offset + 3] = d3; - buffer[offset + 4] = d4; - buffer[offset + 5] = d5; - bufsize = offset + 6; - } - - // utf8 maximum expansion: x4 (-> utf32) - // utf16 maximum expansion: x2 (-> utf32) - // utf32 maximum expansion: x1 - enum - { - bufcapacitybytes = - #ifdef PUGIXML_MEMORY_OUTPUT_STACK - PUGIXML_MEMORY_OUTPUT_STACK - #else - 10240 - #endif - , - bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4) - }; - - char_t buffer[bufcapacity]; - - union - { - uint8_t data_u8[4 * bufcapacity]; - uint16_t data_u16[2 * bufcapacity]; - uint32_t data_u32[bufcapacity]; - char_t data_char[bufcapacity]; - } scratch; - - xml_writer& writer; - size_t bufsize; - xml_encoding encoding; - }; - - PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type) - { - while (*s) - { - const char_t* prev = s; - - // While *s is a usual symbol - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPEX(ss, type)); - - writer.write_buffer(prev, static_cast(s - prev)); - - switch (*s) - { - case 0: break; - case '&': - writer.write('&', 'a', 'm', 'p', ';'); - ++s; - break; - case '<': - writer.write('&', 'l', 't', ';'); - ++s; - break; - case '>': - writer.write('&', 'g', 't', ';'); - ++s; - break; - case '"': - writer.write('&', 'q', 'u', 'o', 't', ';'); - ++s; - break; - default: // s is not a usual symbol - { - unsigned int ch = static_cast(*s++); - assert(ch < 32); - - writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); - } - } - } - } - - PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) - { - if (flags & format_no_escapes) - writer.write_string(s); - else - text_output_escaped(writer, s, type); - } - - PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s) - { - do - { - writer.write('<', '!', '[', 'C', 'D'); - writer.write('A', 'T', 'A', '['); - - const char_t* prev = s; - - // look for ]]> sequence - we can't output it as is since it terminates CDATA - while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s; - - // skip ]] if we stopped at ]]>, > will go to the next CDATA section - if (*s) s += 2; - - writer.write_buffer(prev, static_cast(s - prev)); - - writer.write(']', ']', '>'); - } - while (*s); - } - - PUGI__FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth) - { - switch (indent_length) - { - case 1: - { - for (unsigned int i = 0; i < depth; ++i) - writer.write(indent[0]); - break; - } - - case 2: - { - for (unsigned int i = 0; i < depth; ++i) - writer.write(indent[0], indent[1]); - break; - } - - case 3: - { - for (unsigned int i = 0; i < depth; ++i) - writer.write(indent[0], indent[1], indent[2]); - break; - } - - case 4: - { - for (unsigned int i = 0; i < depth; ++i) - writer.write(indent[0], indent[1], indent[2], indent[3]); - break; - } - - default: - { - for (unsigned int i = 0; i < depth; ++i) - writer.write_buffer(indent, indent_length); - } - } - } - - PUGI__FN void node_output_comment(xml_buffered_writer& writer, const char_t* s) - { - writer.write('<', '!', '-', '-'); - - while (*s) - { - const char_t* prev = s; - - // look for -\0 or -- sequence - we can't output it since -- is illegal in comment body - while (*s && !(s[0] == '-' && (s[1] == '-' || s[1] == 0))) ++s; - - writer.write_buffer(prev, static_cast(s - prev)); - - if (*s) - { - assert(*s == '-'); - - writer.write('-', ' '); - ++s; - } - } - - writer.write('-', '-', '>'); - } - - PUGI__FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s) - { - while (*s) - { - const char_t* prev = s; - - // look for ?> sequence - we can't output it since ?> terminates PI - while (*s && !(s[0] == '?' && s[1] == '>')) ++s; - - writer.write_buffer(prev, static_cast(s - prev)); - - if (*s) - { - assert(s[0] == '?' && s[1] == '>'); - - writer.write('?', ' ', '>'); - s += 2; - } - } - } - - PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) - { - const char_t* default_name = PUGIXML_TEXT(":anonymous"); - - for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) - { - writer.write(' '); - writer.write_string(a->name ? a->name : default_name); - writer.write('=', '"'); - - if (a->value) - text_output(writer, a->value, ctx_special_attr, flags); - - writer.write('"'); - } - } - - PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) - { - const char_t* default_name = PUGIXML_TEXT(":anonymous"); - const char_t* name = node->name ? node->name : default_name; - - writer.write('<'); - writer.write_string(name); - - if (node->first_attribute) - node_output_attributes(writer, node, flags); - - if (!node->first_child) - { - writer.write(' ', '/', '>'); - - return false; - } - else - { - writer.write('>'); - - return true; - } - } - - PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node) - { - const char_t* default_name = PUGIXML_TEXT(":anonymous"); - const char_t* name = node->name ? node->name : default_name; - - writer.write('<', '/'); - writer.write_string(name); - writer.write('>'); - } - - PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) - { - const char_t* default_name = PUGIXML_TEXT(":anonymous"); - - switch (PUGI__NODETYPE(node)) - { - case node_pcdata: - text_output(writer, node->value ? node->value : PUGIXML_TEXT(""), ctx_special_pcdata, flags); - break; - - case node_cdata: - text_output_cdata(writer, node->value ? node->value : PUGIXML_TEXT("")); - break; - - case node_comment: - node_output_comment(writer, node->value ? node->value : PUGIXML_TEXT("")); - break; - - case node_pi: - writer.write('<', '?'); - writer.write_string(node->name ? node->name : default_name); - - if (node->value) - { - writer.write(' '); - node_output_pi_value(writer, node->value); - } - - writer.write('?', '>'); - break; - - case node_declaration: - writer.write('<', '?'); - writer.write_string(node->name ? node->name : default_name); - node_output_attributes(writer, node, flags); - writer.write('?', '>'); - break; - - case node_doctype: - writer.write('<', '!', 'D', 'O', 'C'); - writer.write('T', 'Y', 'P', 'E'); - - if (node->value) - { - writer.write(' '); - writer.write_string(node->value); - } - - writer.write('>'); - break; - - default: - assert(!"Invalid node type"); - } - } - - enum indent_flags_t - { - indent_newline = 1, - indent_indent = 2 - }; - - PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth) - { - size_t indent_length = ((flags & (format_indent | format_raw)) == format_indent) ? strlength(indent) : 0; - unsigned int indent_flags = indent_indent; - - xml_node_struct* node = root; - - do - { - assert(node); - - // begin writing current node - if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata) - { - node_output_simple(writer, node, flags); - - indent_flags = 0; - } - else - { - if ((indent_flags & indent_newline) && (flags & format_raw) == 0) - writer.write('\n'); - - if ((indent_flags & indent_indent) && indent_length) - text_output_indent(writer, indent, indent_length, depth); - - if (PUGI__NODETYPE(node) == node_element) - { - indent_flags = indent_newline | indent_indent; - - if (node_output_start(writer, node, flags)) - { - node = node->first_child; - depth++; - continue; - } - } - else if (PUGI__NODETYPE(node) == node_document) - { - indent_flags = indent_indent; - - if (node->first_child) - { - node = node->first_child; - continue; - } - } - else - { - node_output_simple(writer, node, flags); - - indent_flags = indent_newline | indent_indent; - } - } - - // continue to the next node - while (node != root) - { - if (node->next_sibling) - { - node = node->next_sibling; - break; - } - - node = node->parent; - - // write closing node - if (PUGI__NODETYPE(node) == node_element) - { - depth--; - - if ((indent_flags & indent_newline) && (flags & format_raw) == 0) - writer.write('\n'); - - if ((indent_flags & indent_indent) && indent_length) - text_output_indent(writer, indent, indent_length, depth); - - node_output_end(writer, node); - - indent_flags = indent_newline | indent_indent; - } - } - } - while (node != root); - - if ((indent_flags & indent_newline) && (flags & format_raw) == 0) - writer.write('\n'); - } - - PUGI__FN bool has_declaration(xml_node_struct* node) - { - for (xml_node_struct* child = node->first_child; child; child = child->next_sibling) - { - xml_node_type type = PUGI__NODETYPE(child); - - if (type == node_declaration) return true; - if (type == node_element) return false; - } - - return false; - } - - PUGI__FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node) - { - for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) - if (a == attr) - return true; - - return false; - } - - PUGI__FN bool allow_insert_attribute(xml_node_type parent) - { - return parent == node_element || parent == node_declaration; - } - - PUGI__FN bool allow_insert_child(xml_node_type parent, xml_node_type child) - { - if (parent != node_document && parent != node_element) return false; - if (child == node_document || child == node_null) return false; - if (parent != node_document && (child == node_declaration || child == node_doctype)) return false; - - return true; - } - - PUGI__FN bool allow_move(xml_node parent, xml_node child) - { - // check that child can be a child of parent - if (!allow_insert_child(parent.type(), child.type())) - return false; - - // check that node is not moved between documents - if (parent.root() != child.root()) - return false; - - // check that new parent is not in the child subtree - xml_node cur = parent; - - while (cur) - { - if (cur == child) - return false; - - cur = cur.parent(); - } - - return true; - } - - PUGI__FN void node_copy_string(char_t*& dest, uintptr_t& header, uintptr_t header_mask, char_t* source, uintptr_t& source_header, xml_allocator* alloc) - { - assert(!dest && (header & header_mask) == 0); - - if (source) - { - if (alloc && (source_header & header_mask) == 0) - { - dest = source; - - // since strcpy_insitu can reuse document buffer memory we need to mark both source and dest as shared - header |= xml_memory_page_contents_shared_mask; - source_header |= xml_memory_page_contents_shared_mask; - } - else - strcpy_insitu(dest, header, header_mask, source); - } - } - - PUGI__FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc) - { - node_copy_string(dn->name, dn->header, xml_memory_page_name_allocated_mask, sn->name, sn->header, shared_alloc); - node_copy_string(dn->value, dn->header, xml_memory_page_value_allocated_mask, sn->value, sn->header, shared_alloc); - - for (xml_attribute_struct* sa = sn->first_attribute; sa; sa = sa->next_attribute) - { - xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn)); - - if (da) - { - node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); - node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); - } - } - } - - PUGI__FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn) - { - xml_allocator& alloc = get_allocator(dn); - xml_allocator* shared_alloc = (&alloc == &get_allocator(sn)) ? &alloc : 0; - - node_copy_contents(dn, sn, shared_alloc); - - xml_node_struct* dit = dn; - xml_node_struct* sit = sn->first_child; - - while (sit && sit != sn) - { - if (sit != dn) - { - xml_node_struct* copy = append_new_node(dit, alloc, PUGI__NODETYPE(sit)); - - if (copy) - { - node_copy_contents(copy, sit, shared_alloc); - - if (sit->first_child) - { - dit = copy; - sit = sit->first_child; - continue; - } - } - } - - // continue to the next node - do - { - if (sit->next_sibling) - { - sit = sit->next_sibling; - break; - } - - sit = sit->parent; - dit = dit->parent; - } - while (sit != sn); - } - } - - inline bool is_text_node(xml_node_struct* node) - { - xml_node_type type = PUGI__NODETYPE(node); - - return type == node_pcdata || type == node_cdata; - } - - // get value with conversion functions - PUGI__FN int get_integer_base(const char_t* value) - { - const char_t* s = value; - - while (PUGI__IS_CHARTYPE(*s, ct_space)) - s++; - - if (*s == '-') - s++; - - return (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10; - } - - PUGI__FN int get_value_int(const char_t* value, int def) - { - if (!value) return def; - - int base = get_integer_base(value); - - #ifdef PUGIXML_WCHAR_MODE - return static_cast(wcstol(value, 0, base)); - #else - return static_cast(strtol(value, 0, base)); - #endif - } - - PUGI__FN unsigned int get_value_uint(const char_t* value, unsigned int def) - { - if (!value) return def; - - int base = get_integer_base(value); - - #ifdef PUGIXML_WCHAR_MODE - return static_cast(wcstoul(value, 0, base)); - #else - return static_cast(strtoul(value, 0, base)); - #endif - } - - PUGI__FN double get_value_double(const char_t* value, double def) - { - if (!value) return def; - - #ifdef PUGIXML_WCHAR_MODE - return wcstod(value, 0); - #else - return strtod(value, 0); - #endif - } - - PUGI__FN float get_value_float(const char_t* value, float def) - { - if (!value) return def; - - #ifdef PUGIXML_WCHAR_MODE - return static_cast(wcstod(value, 0)); - #else - return static_cast(strtod(value, 0)); - #endif - } - - PUGI__FN bool get_value_bool(const char_t* value, bool def) - { - if (!value) return def; - - // only look at first char - char_t first = *value; - - // 1*, t* (true), T* (True), y* (yes), Y* (YES) - return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y'); - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN long long get_value_llong(const char_t* value, long long def) - { - if (!value) return def; - - int base = get_integer_base(value); - - #ifdef PUGIXML_WCHAR_MODE - #ifdef PUGI__MSVC_CRT_VERSION - return _wcstoi64(value, 0, base); - #else - return wcstoll(value, 0, base); - #endif - #else - #ifdef PUGI__MSVC_CRT_VERSION - return _strtoi64(value, 0, base); - #else - return strtoll(value, 0, base); - #endif - #endif - } - - PUGI__FN unsigned long long get_value_ullong(const char_t* value, unsigned long long def) - { - if (!value) return def; - - int base = get_integer_base(value); - - #ifdef PUGIXML_WCHAR_MODE - #ifdef PUGI__MSVC_CRT_VERSION - return _wcstoui64(value, 0, base); - #else - return wcstoull(value, 0, base); - #endif - #else - #ifdef PUGI__MSVC_CRT_VERSION - return _strtoui64(value, 0, base); - #else - return strtoull(value, 0, base); - #endif - #endif - } -#endif - - // set value with conversion functions - PUGI__FN bool set_value_buffer(char_t*& dest, uintptr_t& header, uintptr_t header_mask, char (&buf)[128]) - { - #ifdef PUGIXML_WCHAR_MODE - char_t wbuf[128]; - impl::widen_ascii(wbuf, buf); - - return strcpy_insitu(dest, header, header_mask, wbuf); - #else - return strcpy_insitu(dest, header, header_mask, buf); - #endif - } - - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value) - { - char buf[128]; - sprintf(buf, "%d", value); - - return set_value_buffer(dest, header, header_mask, buf); - } - - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned int value) - { - char buf[128]; - sprintf(buf, "%u", value); - - return set_value_buffer(dest, header, header_mask, buf); - } - - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, float value) - { - char buf[128]; - sprintf(buf, "%.9g", value); - - return set_value_buffer(dest, header, header_mask, buf); - } - - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, double value) - { - char buf[128]; - sprintf(buf, "%.17g", value); - - return set_value_buffer(dest, header, header_mask, buf); - } - - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, bool value) - { - return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, long long value) - { - char buf[128]; - sprintf(buf, "%lld", value); - - return set_value_buffer(dest, header, header_mask, buf); - } - - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned long long value) - { - char buf[128]; - sprintf(buf, "%llu", value); - - return set_value_buffer(dest, header, header_mask, buf); - } -#endif - - // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick - PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result) - { - #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) - // there are 64-bit versions of fseek/ftell, let's use them - typedef __int64 length_type; - - _fseeki64(file, 0, SEEK_END); - length_type length = _ftelli64(file); - _fseeki64(file, 0, SEEK_SET); - #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)) - // there are 64-bit versions of fseek/ftell, let's use them - typedef off64_t length_type; - - fseeko64(file, 0, SEEK_END); - length_type length = ftello64(file); - fseeko64(file, 0, SEEK_SET); - #else - // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway. - typedef long length_type; - - fseek(file, 0, SEEK_END); - length_type length = ftell(file); - fseek(file, 0, SEEK_SET); - #endif - - // check for I/O errors - if (length < 0) return status_io_error; - - // check for overflow - size_t result = static_cast(length); - - if (static_cast(result) != length) return status_out_of_memory; - - // finalize - out_result = result; - - return status_ok; - } - - PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) - { - // We only need to zero-terminate if encoding conversion does not do it for us - #ifdef PUGIXML_WCHAR_MODE - xml_encoding wchar_encoding = get_wchar_encoding(); - - if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding)) - { - size_t length = size / sizeof(char_t); - - static_cast(buffer)[length] = 0; - return (length + 1) * sizeof(char_t); - } - #else - if (encoding == encoding_utf8) - { - static_cast(buffer)[size] = 0; - return size + 1; - } - #endif - - return size; - } - - PUGI__FN xml_parse_result load_file_impl(xml_document& doc, FILE* file, unsigned int options, xml_encoding encoding) - { - if (!file) return make_parse_result(status_file_not_found); - - // get file size (can result in I/O errors) - size_t size = 0; - xml_parse_status size_status = get_file_size(file, size); - - if (size_status != status_ok) - { - fclose(file); - return make_parse_result(size_status); - } - - size_t max_suffix_size = sizeof(char_t); - - // allocate buffer for the whole file - char* contents = static_cast(xml_memory::allocate(size + max_suffix_size)); - - if (!contents) - { - fclose(file); - return make_parse_result(status_out_of_memory); - } - - // read file in memory - size_t read_size = fread(contents, 1, size, file); - fclose(file); - - if (read_size != size) - { - xml_memory::deallocate(contents); - return make_parse_result(status_io_error); - } - - xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size); - - return doc.load_buffer_inplace_own(contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding); - } - -#ifndef PUGIXML_NO_STL - template struct xml_stream_chunk - { - static xml_stream_chunk* create() - { - void* memory = xml_memory::allocate(sizeof(xml_stream_chunk)); - - return new (memory) xml_stream_chunk(); - } - - static void destroy(void* ptr) - { - xml_stream_chunk* chunk = static_cast(ptr); - - // free chunk chain - while (chunk) - { - xml_stream_chunk* next_ = chunk->next; - - xml_memory::deallocate(chunk); - - chunk = next_; - } - } - - xml_stream_chunk(): next(0), size(0) - { - } - - xml_stream_chunk* next; - size_t size; - - T data[xml_memory_page_size / sizeof(T)]; - }; - - template PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream& stream, void** out_buffer, size_t* out_size) - { - buffer_holder chunks(0, xml_stream_chunk::destroy); - - // read file to a chunk list - size_t total = 0; - xml_stream_chunk* last = 0; - - while (!stream.eof()) - { - // allocate new chunk - xml_stream_chunk* chunk = xml_stream_chunk::create(); - if (!chunk) return status_out_of_memory; - - // append chunk to list - if (last) last = last->next = chunk; - else chunks.data = last = chunk; - - // read data to chunk - stream.read(chunk->data, static_cast(sizeof(chunk->data) / sizeof(T))); - chunk->size = static_cast(stream.gcount()) * sizeof(T); - - // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors - if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; - - // guard against huge files (chunk size is small enough to make this overflow check work) - if (total + chunk->size < total) return status_out_of_memory; - total += chunk->size; - } - - size_t max_suffix_size = sizeof(char_t); - - // copy chunk list to a contiguous buffer - char* buffer = static_cast(xml_memory::allocate(total + max_suffix_size)); - if (!buffer) return status_out_of_memory; - - char* write = buffer; - - for (xml_stream_chunk* chunk = static_cast*>(chunks.data); chunk; chunk = chunk->next) - { - assert(write + chunk->size <= buffer + total); - memcpy(write, chunk->data, chunk->size); - write += chunk->size; - } - - assert(write == buffer + total); - - // return buffer - *out_buffer = buffer; - *out_size = total; - - return status_ok; - } - - template PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream& stream, void** out_buffer, size_t* out_size) - { - // get length of remaining data in stream - typename std::basic_istream::pos_type pos = stream.tellg(); - stream.seekg(0, std::ios::end); - std::streamoff length = stream.tellg() - pos; - stream.seekg(pos); - - if (stream.fail() || pos < 0) return status_io_error; - - // guard against huge files - size_t read_length = static_cast(length); - - if (static_cast(read_length) != length || length < 0) return status_out_of_memory; - - size_t max_suffix_size = sizeof(char_t); - - // read stream data into memory (guard against stream exceptions with buffer holder) - buffer_holder buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate); - if (!buffer.data) return status_out_of_memory; - - stream.read(static_cast(buffer.data), static_cast(read_length)); - - // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors - if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; - - // return buffer - size_t actual_length = static_cast(stream.gcount()); - assert(actual_length <= read_length); - - *out_buffer = buffer.release(); - *out_size = actual_length * sizeof(T); - - return status_ok; - } - - template PUGI__FN xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding) - { - void* buffer = 0; - size_t size = 0; - xml_parse_status status = status_ok; - - // if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits) - if (stream.fail()) return make_parse_result(status_io_error); - - // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory) - if (stream.tellg() < 0) - { - stream.clear(); // clear error flags that could be set by a failing tellg - status = load_stream_data_noseek(stream, &buffer, &size); - } - else - status = load_stream_data_seek(stream, &buffer, &size); - - if (status != status_ok) return make_parse_result(status); - - xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size); - - return doc.load_buffer_inplace_own(buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding); - } -#endif - -#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))) - PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) - { - return _wfopen(path, mode); - } -#else - PUGI__FN char* convert_path_heap(const wchar_t* str) - { - assert(str); - - // first pass: get length in utf8 characters - size_t length = strlength_wide(str); - size_t size = as_utf8_begin(str, length); - - // allocate resulting string - char* result = static_cast(xml_memory::allocate(size + 1)); - if (!result) return 0; - - // second pass: convert to utf8 - as_utf8_end(result, size, str, length); - - return result; - } - - PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) - { - // there is no standard function to open wide paths, so our best bet is to try utf8 path - char* path_utf8 = convert_path_heap(path); - if (!path_utf8) return 0; - - // convert mode to ASCII (we mirror _wfopen interface) - char mode_ascii[4] = {0}; - for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast(mode[i]); - - // try to open the utf8 path - FILE* result = fopen(path_utf8, mode_ascii); - - // free dummy buffer - xml_memory::deallocate(path_utf8); - - return result; - } -#endif - - PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) - { - if (!file) return false; - - xml_writer_file writer(file); - doc.save(writer, indent, flags, encoding); - - int result = ferror(file); - - fclose(file); - - return result == 0; - } - - PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer) - { - // check input buffer - if (!contents && size) return make_parse_result(status_io_error); - - // get actual encoding - xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); - - // get private buffer - char_t* buffer = 0; - size_t length = 0; - - if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); - - // delete original buffer if we performed a conversion - if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); - - // store buffer for offset_debug - doc->buffer = buffer; - - // parse - xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options); - - // remember encoding - res.encoding = buffer_encoding; - - // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself - if (own || buffer != contents) *out_buffer = buffer; - - return res; - } -PUGI__NS_END - -namespace pugi -{ - PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_) - { - } - - PUGI__FN void xml_writer_file::write(const void* data, size_t size) - { - size_t result = fwrite(data, 1, size, static_cast(file)); - (void)!result; // unfortunately we can't do proper error handling here - } - -#ifndef PUGIXML_NO_STL - PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0) - { - } - - PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream) - { - } - - PUGI__FN void xml_writer_stream::write(const void* data, size_t size) - { - if (narrow_stream) - { - assert(!wide_stream); - narrow_stream->write(reinterpret_cast(data), static_cast(size)); - } - else - { - assert(wide_stream); - assert(size % sizeof(wchar_t) == 0); - - wide_stream->write(reinterpret_cast(data), static_cast(size / sizeof(wchar_t))); - } - } -#endif - - PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0) - { - } - - PUGI__FN xml_tree_walker::~xml_tree_walker() - { - } - - PUGI__FN int xml_tree_walker::depth() const - { - return _depth; - } - - PUGI__FN bool xml_tree_walker::begin(xml_node&) - { - return true; - } - - PUGI__FN bool xml_tree_walker::end(xml_node&) - { - return true; - } - - PUGI__FN xml_attribute::xml_attribute(): _attr(0) - { - } - - PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) - { - } - - PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***) - { - } - - PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const - { - return _attr ? unspecified_bool_xml_attribute : 0; - } - - PUGI__FN bool xml_attribute::operator!() const - { - return !_attr; - } - - PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const - { - return (_attr == r._attr); - } - - PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const - { - return (_attr != r._attr); - } - - PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const - { - return (_attr < r._attr); - } - - PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const - { - return (_attr > r._attr); - } - - PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const - { - return (_attr <= r._attr); - } - - PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const - { - return (_attr >= r._attr); - } - - PUGI__FN xml_attribute xml_attribute::next_attribute() const - { - return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute(); - } - - PUGI__FN xml_attribute xml_attribute::previous_attribute() const - { - return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute(); - } - - PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const - { - return (_attr && _attr->value) ? _attr->value : def; - } - - PUGI__FN int xml_attribute::as_int(int def) const - { - return impl::get_value_int(_attr ? _attr->value : 0, def); - } - - PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const - { - return impl::get_value_uint(_attr ? _attr->value : 0, def); - } - - PUGI__FN double xml_attribute::as_double(double def) const - { - return impl::get_value_double(_attr ? _attr->value : 0, def); - } - - PUGI__FN float xml_attribute::as_float(float def) const - { - return impl::get_value_float(_attr ? _attr->value : 0, def); - } - - PUGI__FN bool xml_attribute::as_bool(bool def) const - { - return impl::get_value_bool(_attr ? _attr->value : 0, def); - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN long long xml_attribute::as_llong(long long def) const - { - return impl::get_value_llong(_attr ? _attr->value : 0, def); - } - - PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const - { - return impl::get_value_ullong(_attr ? _attr->value : 0, def); - } -#endif - - PUGI__FN bool xml_attribute::empty() const - { - return !_attr; - } - - PUGI__FN const char_t* xml_attribute::name() const - { - return (_attr && _attr->name) ? _attr->name : PUGIXML_TEXT(""); - } - - PUGI__FN const char_t* xml_attribute::value() const - { - return (_attr && _attr->value) ? _attr->value : PUGIXML_TEXT(""); - } - - PUGI__FN size_t xml_attribute::hash_value() const - { - return static_cast(reinterpret_cast(_attr) / sizeof(xml_attribute_struct)); - } - - PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const - { - return _attr; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs) - { - set_value(rhs); - return *this; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(int rhs) - { - set_value(rhs); - return *this; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs) - { - set_value(rhs); - return *this; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(double rhs) - { - set_value(rhs); - return *this; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(float rhs) - { - set_value(rhs); - return *this; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs) - { - set_value(rhs); - return *this; - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN xml_attribute& xml_attribute::operator=(long long rhs) - { - set_value(rhs); - return *this; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs) - { - set_value(rhs); - return *this; - } -#endif - - PUGI__FN bool xml_attribute::set_name(const char_t* rhs) - { - if (!_attr) return false; - - return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(const char_t* rhs) - { - if (!_attr) return false; - - return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(int rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(unsigned int rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(double rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(float rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(bool rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN bool xml_attribute::set_value(long long rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(unsigned long long rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } -#endif - -#ifdef __BORLANDC__ - PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs) - { - return (bool)lhs && rhs; - } - - PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs) - { - return (bool)lhs || rhs; - } -#endif - - PUGI__FN xml_node::xml_node(): _root(0) - { - } - - PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p) - { - } - - PUGI__FN static void unspecified_bool_xml_node(xml_node***) - { - } - - PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const - { - return _root ? unspecified_bool_xml_node : 0; - } - - PUGI__FN bool xml_node::operator!() const - { - return !_root; - } - - PUGI__FN xml_node::iterator xml_node::begin() const - { - return iterator(_root ? _root->first_child : 0, _root); - } - - PUGI__FN xml_node::iterator xml_node::end() const - { - return iterator(0, _root); - } - - PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const - { - return attribute_iterator(_root ? _root->first_attribute : 0, _root); - } - - PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const - { - return attribute_iterator(0, _root); - } - - PUGI__FN xml_object_range xml_node::children() const - { - return xml_object_range(begin(), end()); - } - - PUGI__FN xml_object_range xml_node::children(const char_t* name_) const - { - return xml_object_range(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_)); - } - - PUGI__FN xml_object_range xml_node::attributes() const - { - return xml_object_range(attributes_begin(), attributes_end()); - } - - PUGI__FN bool xml_node::operator==(const xml_node& r) const - { - return (_root == r._root); - } - - PUGI__FN bool xml_node::operator!=(const xml_node& r) const - { - return (_root != r._root); - } - - PUGI__FN bool xml_node::operator<(const xml_node& r) const - { - return (_root < r._root); - } - - PUGI__FN bool xml_node::operator>(const xml_node& r) const - { - return (_root > r._root); - } - - PUGI__FN bool xml_node::operator<=(const xml_node& r) const - { - return (_root <= r._root); - } - - PUGI__FN bool xml_node::operator>=(const xml_node& r) const - { - return (_root >= r._root); - } - - PUGI__FN bool xml_node::empty() const - { - return !_root; - } - - PUGI__FN const char_t* xml_node::name() const - { - return (_root && _root->name) ? _root->name : PUGIXML_TEXT(""); - } - - PUGI__FN xml_node_type xml_node::type() const - { - return _root ? PUGI__NODETYPE(_root) : node_null; - } - - PUGI__FN const char_t* xml_node::value() const - { - return (_root && _root->value) ? _root->value : PUGIXML_TEXT(""); - } - - PUGI__FN xml_node xml_node::child(const char_t* name_) const - { - if (!_root) return xml_node(); - - for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) return xml_node(i); - - return xml_node(); - } - - PUGI__FN xml_attribute xml_node::attribute(const char_t* name_) const - { - if (!_root) return xml_attribute(); - - for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute) - if (i->name && impl::strequal(name_, i->name)) - return xml_attribute(i); - - return xml_attribute(); - } - - PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const - { - if (!_root) return xml_node(); - - for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) return xml_node(i); - - return xml_node(); - } - - PUGI__FN xml_node xml_node::next_sibling() const - { - return _root ? xml_node(_root->next_sibling) : xml_node(); - } - - PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const - { - if (!_root) return xml_node(); - - for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) - if (i->name && impl::strequal(name_, i->name)) return xml_node(i); - - return xml_node(); - } - - PUGI__FN xml_node xml_node::previous_sibling() const - { - if (!_root) return xml_node(); - - if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c); - else return xml_node(); - } - - PUGI__FN xml_node xml_node::parent() const - { - return _root ? xml_node(_root->parent) : xml_node(); - } - - PUGI__FN xml_node xml_node::root() const - { - return _root ? xml_node(&impl::get_document(_root)) : xml_node(); - } - - PUGI__FN xml_text xml_node::text() const - { - return xml_text(_root); - } - - PUGI__FN const char_t* xml_node::child_value() const - { - if (!_root) return PUGIXML_TEXT(""); - - for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (i->value && impl::is_text_node(i)) - return i->value; - - return PUGIXML_TEXT(""); - } - - PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const - { - return child(name_).child_value(); - } - - PUGI__FN xml_attribute xml_node::first_attribute() const - { - return _root ? xml_attribute(_root->first_attribute) : xml_attribute(); - } - - PUGI__FN xml_attribute xml_node::last_attribute() const - { - return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute(); - } - - PUGI__FN xml_node xml_node::first_child() const - { - return _root ? xml_node(_root->first_child) : xml_node(); - } - - PUGI__FN xml_node xml_node::last_child() const - { - return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node(); - } - - PUGI__FN bool xml_node::set_name(const char_t* rhs) - { - switch (type()) - { - case node_pi: - case node_declaration: - case node_element: - return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs); - - default: - return false; - } - } - - PUGI__FN bool xml_node::set_value(const char_t* rhs) - { - switch (type()) - { - case node_pi: - case node_cdata: - case node_pcdata: - case node_comment: - case node_doctype: - return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs); - - default: - return false; - } - } - - PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_) - { - if (!impl::allow_insert_attribute(type())) return xml_attribute(); - - xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); - if (!a) return xml_attribute(); - - impl::append_attribute(a._attr, _root); - - a.set_name(name_); - - return a; - } - - PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_) - { - if (!impl::allow_insert_attribute(type())) return xml_attribute(); - - xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); - if (!a) return xml_attribute(); - - impl::prepend_attribute(a._attr, _root); - - a.set_name(name_); - - return a; - } - - PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr) - { - if (!impl::allow_insert_attribute(type())) return xml_attribute(); - if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); - - xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); - if (!a) return xml_attribute(); - - impl::insert_attribute_after(a._attr, attr._attr, _root); - - a.set_name(name_); - - return a; - } - - PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr) - { - if (!impl::allow_insert_attribute(type())) return xml_attribute(); - if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); - - xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); - if (!a) return xml_attribute(); - - impl::insert_attribute_before(a._attr, attr._attr, _root); - - a.set_name(name_); - - return a; - } - - PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto) - { - if (!proto) return xml_attribute(); - - xml_attribute result = append_attribute(proto.name()); - result.set_value(proto.value()); - - return result; - } - - PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto) - { - if (!proto) return xml_attribute(); - - xml_attribute result = prepend_attribute(proto.name()); - result.set_value(proto.value()); - - return result; - } - - PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) - { - if (!proto) return xml_attribute(); - - xml_attribute result = insert_attribute_after(proto.name(), attr); - result.set_value(proto.value()); - - return result; - } - - PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) - { - if (!proto) return xml_attribute(); - - xml_attribute result = insert_attribute_before(proto.name(), attr); - result.set_value(proto.value()); - - return result; - } - - PUGI__FN xml_node xml_node::append_child(xml_node_type type_) - { - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::append_node(n._root, _root); - - if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); - - return n; - } - - PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_) - { - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::prepend_node(n._root, _root); - - if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); - - return n; - } - - PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node) - { - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - if (!node._root || node._root->parent != _root) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::insert_node_before(n._root, node._root); - - if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); - - return n; - } - - PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node) - { - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - if (!node._root || node._root->parent != _root) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::insert_node_after(n._root, node._root); - - if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); - - return n; - } - - PUGI__FN xml_node xml_node::append_child(const char_t* name_) - { - xml_node result = append_child(node_element); - - result.set_name(name_); - - return result; - } - - PUGI__FN xml_node xml_node::prepend_child(const char_t* name_) - { - xml_node result = prepend_child(node_element); - - result.set_name(name_); - - return result; - } - - PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node) - { - xml_node result = insert_child_after(node_element, node); - - result.set_name(name_); - - return result; - } - - PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node) - { - xml_node result = insert_child_before(node_element, node); - - result.set_name(name_); - - return result; - } - - PUGI__FN xml_node xml_node::append_copy(const xml_node& proto) - { - xml_node_type type_ = proto.type(); - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::append_node(n._root, _root); - impl::node_copy_tree(n._root, proto._root); - - return n; - } - - PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto) - { - xml_node_type type_ = proto.type(); - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::prepend_node(n._root, _root); - impl::node_copy_tree(n._root, proto._root); - - return n; - } - - PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node) - { - xml_node_type type_ = proto.type(); - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - if (!node._root || node._root->parent != _root) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::insert_node_after(n._root, node._root); - impl::node_copy_tree(n._root, proto._root); - - return n; - } - - PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node) - { - xml_node_type type_ = proto.type(); - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - if (!node._root || node._root->parent != _root) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::insert_node_before(n._root, node._root); - impl::node_copy_tree(n._root, proto._root); - - return n; - } - - PUGI__FN xml_node xml_node::append_move(const xml_node& moved) - { - if (!impl::allow_move(*this, moved)) return xml_node(); - - // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers - impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; - - impl::remove_node(moved._root); - impl::append_node(moved._root, _root); - - return moved; - } - - PUGI__FN xml_node xml_node::prepend_move(const xml_node& moved) - { - if (!impl::allow_move(*this, moved)) return xml_node(); - - // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers - impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; - - impl::remove_node(moved._root); - impl::prepend_node(moved._root, _root); - - return moved; - } - - PUGI__FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node) - { - if (!impl::allow_move(*this, moved)) return xml_node(); - if (!node._root || node._root->parent != _root) return xml_node(); - if (moved._root == node._root) return xml_node(); - - // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers - impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; - - impl::remove_node(moved._root); - impl::insert_node_after(moved._root, node._root); - - return moved; - } - - PUGI__FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node) - { - if (!impl::allow_move(*this, moved)) return xml_node(); - if (!node._root || node._root->parent != _root) return xml_node(); - if (moved._root == node._root) return xml_node(); - - // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers - impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; - - impl::remove_node(moved._root); - impl::insert_node_before(moved._root, node._root); - - return moved; - } - - PUGI__FN bool xml_node::remove_attribute(const char_t* name_) - { - return remove_attribute(attribute(name_)); - } - - PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a) - { - if (!_root || !a._attr) return false; - if (!impl::is_attribute_of(a._attr, _root)) return false; - - impl::remove_attribute(a._attr, _root); - impl::destroy_attribute(a._attr, impl::get_allocator(_root)); - - return true; - } - - PUGI__FN bool xml_node::remove_child(const char_t* name_) - { - return remove_child(child(name_)); - } - - PUGI__FN bool xml_node::remove_child(const xml_node& n) - { - if (!_root || !n._root || n._root->parent != _root) return false; - - impl::remove_node(n._root); - impl::destroy_node(n._root, impl::get_allocator(_root)); - - return true; - } - - PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) - { - // append_buffer is only valid for elements/documents - if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root); - - // get document node - impl::xml_document_struct* doc = &impl::get_document(_root); - - // disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense - doc->header |= impl::xml_memory_page_contents_shared_mask; - - // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later) - impl::xml_memory_page* page = 0; - impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page)); - (void)page; - - if (!extra) return impl::make_parse_result(status_out_of_memory); - - // save name; name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level - char_t* rootname = _root->name; - _root->name = 0; - - // parse - char_t* buffer = 0; - xml_parse_result res = impl::load_buffer_impl(doc, _root, const_cast(contents), size, options, encoding, false, false, &buffer); - - // restore name - _root->name = rootname; - - // add extra buffer to the list - extra->buffer = buffer; - extra->next = doc->extra_buffers; - doc->extra_buffers = extra; - - return res; - } - - PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const - { - if (!_root) return xml_node(); - - for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) - { - for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) - if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value : PUGIXML_TEXT(""))) - return xml_node(i); - } - - return xml_node(); - } - - PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const - { - if (!_root) return xml_node(); - - for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) - if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value : PUGIXML_TEXT(""))) - return xml_node(i); - - return xml_node(); - } - -#ifndef PUGIXML_NO_STL - PUGI__FN string_t xml_node::path(char_t delimiter) const - { - xml_node cursor = *this; // Make a copy. - - string_t result = cursor.name(); - - while (cursor.parent()) - { - cursor = cursor.parent(); - - string_t temp = cursor.name(); - temp += delimiter; - temp += result; - result.swap(temp); - } - - return result; - } -#endif - - PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const - { - xml_node found = *this; // Current search context. - - if (!_root || !path_ || !path_[0]) return found; - - if (path_[0] == delimiter) - { - // Absolute path; e.g. '/foo/bar' - found = found.root(); - ++path_; - } - - const char_t* path_segment = path_; - - while (*path_segment == delimiter) ++path_segment; - - const char_t* path_segment_end = path_segment; - - while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end; - - if (path_segment == path_segment_end) return found; - - const char_t* next_segment = path_segment_end; - - while (*next_segment == delimiter) ++next_segment; - - if (*path_segment == '.' && path_segment + 1 == path_segment_end) - return found.first_element_by_path(next_segment, delimiter); - else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end) - return found.parent().first_element_by_path(next_segment, delimiter); - else - { - for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling) - { - if (j->name && impl::strequalrange(j->name, path_segment, static_cast(path_segment_end - path_segment))) - { - xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter); - - if (subsearch) return subsearch; - } - } - - return xml_node(); - } - } - - PUGI__FN bool xml_node::traverse(xml_tree_walker& walker) - { - walker._depth = -1; - - xml_node arg_begin = *this; - if (!walker.begin(arg_begin)) return false; - - xml_node cur = first_child(); - - if (cur) - { - ++walker._depth; - - do - { - xml_node arg_for_each = cur; - if (!walker.for_each(arg_for_each)) - return false; - - if (cur.first_child()) - { - ++walker._depth; - cur = cur.first_child(); - } - else if (cur.next_sibling()) - cur = cur.next_sibling(); - else - { - // Borland C++ workaround - while (!cur.next_sibling() && cur != *this && !cur.parent().empty()) - { - --walker._depth; - cur = cur.parent(); - } - - if (cur != *this) - cur = cur.next_sibling(); - } - } - while (cur && cur != *this); - } - - assert(walker._depth == -1); - - xml_node arg_end = *this; - return walker.end(arg_end); - } - - PUGI__FN size_t xml_node::hash_value() const - { - return static_cast(reinterpret_cast(_root) / sizeof(xml_node_struct)); - } - - PUGI__FN xml_node_struct* xml_node::internal_object() const - { - return _root; - } - - PUGI__FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const - { - if (!_root) return; - - impl::xml_buffered_writer buffered_writer(writer, encoding); - - impl::node_output(buffered_writer, _root, indent, flags, depth); - } - -#ifndef PUGIXML_NO_STL - PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const - { - xml_writer_stream writer(stream); - - print(writer, indent, flags, encoding, depth); - } - - PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const - { - xml_writer_stream writer(stream); - - print(writer, indent, flags, encoding_wchar, depth); - } -#endif - - PUGI__FN ptrdiff_t xml_node::offset_debug() const - { - if (!_root) return -1; - - impl::xml_document_struct& doc = impl::get_document(_root); - - // we can determine the offset reliably only if there is exactly once parse buffer - if (!doc.buffer || doc.extra_buffers) return -1; - - switch (type()) - { - case node_document: - return 0; - - case node_element: - case node_declaration: - case node_pi: - return _root->name && (_root->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0 ? _root->name - doc.buffer : -1; - - case node_pcdata: - case node_cdata: - case node_comment: - case node_doctype: - return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1; - - default: - return -1; - } - } - -#ifdef __BORLANDC__ - PUGI__FN bool operator&&(const xml_node& lhs, bool rhs) - { - return (bool)lhs && rhs; - } - - PUGI__FN bool operator||(const xml_node& lhs, bool rhs) - { - return (bool)lhs || rhs; - } -#endif - - PUGI__FN xml_text::xml_text(xml_node_struct* root): _root(root) - { - } - - PUGI__FN xml_node_struct* xml_text::_data() const - { - if (!_root || impl::is_text_node(_root)) return _root; - - for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling) - if (impl::is_text_node(node)) - return node; - - return 0; - } - - PUGI__FN xml_node_struct* xml_text::_data_new() - { - xml_node_struct* d = _data(); - if (d) return d; - - return xml_node(_root).append_child(node_pcdata).internal_object(); - } - - PUGI__FN xml_text::xml_text(): _root(0) - { - } - - PUGI__FN static void unspecified_bool_xml_text(xml_text***) - { - } - - PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const - { - return _data() ? unspecified_bool_xml_text : 0; - } - - PUGI__FN bool xml_text::operator!() const - { - return !_data(); - } - - PUGI__FN bool xml_text::empty() const - { - return _data() == 0; - } - - PUGI__FN const char_t* xml_text::get() const - { - xml_node_struct* d = _data(); - - return (d && d->value) ? d->value : PUGIXML_TEXT(""); - } - - PUGI__FN const char_t* xml_text::as_string(const char_t* def) const - { - xml_node_struct* d = _data(); - - return (d && d->value) ? d->value : def; - } - - PUGI__FN int xml_text::as_int(int def) const - { - xml_node_struct* d = _data(); - - return impl::get_value_int(d ? d->value : 0, def); - } - - PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const - { - xml_node_struct* d = _data(); - - return impl::get_value_uint(d ? d->value : 0, def); - } - - PUGI__FN double xml_text::as_double(double def) const - { - xml_node_struct* d = _data(); - - return impl::get_value_double(d ? d->value : 0, def); - } - - PUGI__FN float xml_text::as_float(float def) const - { - xml_node_struct* d = _data(); - - return impl::get_value_float(d ? d->value : 0, def); - } - - PUGI__FN bool xml_text::as_bool(bool def) const - { - xml_node_struct* d = _data(); - - return impl::get_value_bool(d ? d->value : 0, def); - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN long long xml_text::as_llong(long long def) const - { - xml_node_struct* d = _data(); - - return impl::get_value_llong(d ? d->value : 0, def); - } - - PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const - { - xml_node_struct* d = _data(); - - return impl::get_value_ullong(d ? d->value : 0, def); - } -#endif - - PUGI__FN bool xml_text::set(const char_t* rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } - - PUGI__FN bool xml_text::set(int rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } - - PUGI__FN bool xml_text::set(unsigned int rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } - - PUGI__FN bool xml_text::set(float rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } - - PUGI__FN bool xml_text::set(double rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } - - PUGI__FN bool xml_text::set(bool rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN bool xml_text::set(long long rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } - - PUGI__FN bool xml_text::set(unsigned long long rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } -#endif - - PUGI__FN xml_text& xml_text::operator=(const char_t* rhs) - { - set(rhs); - return *this; - } - - PUGI__FN xml_text& xml_text::operator=(int rhs) - { - set(rhs); - return *this; - } - - PUGI__FN xml_text& xml_text::operator=(unsigned int rhs) - { - set(rhs); - return *this; - } - - PUGI__FN xml_text& xml_text::operator=(double rhs) - { - set(rhs); - return *this; - } - - PUGI__FN xml_text& xml_text::operator=(float rhs) - { - set(rhs); - return *this; - } - - PUGI__FN xml_text& xml_text::operator=(bool rhs) - { - set(rhs); - return *this; - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN xml_text& xml_text::operator=(long long rhs) - { - set(rhs); - return *this; - } - - PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs) - { - set(rhs); - return *this; - } -#endif - - PUGI__FN xml_node xml_text::data() const - { - return xml_node(_data()); - } - -#ifdef __BORLANDC__ - PUGI__FN bool operator&&(const xml_text& lhs, bool rhs) - { - return (bool)lhs && rhs; - } - - PUGI__FN bool operator||(const xml_text& lhs, bool rhs) - { - return (bool)lhs || rhs; - } -#endif - - PUGI__FN xml_node_iterator::xml_node_iterator() - { - } - - PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) - { - } - - PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) - { - } - - PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const - { - return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; - } - - PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const - { - return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; - } - - PUGI__FN xml_node& xml_node_iterator::operator*() const - { - assert(_wrap._root); - return _wrap; - } - - PUGI__FN xml_node* xml_node_iterator::operator->() const - { - assert(_wrap._root); - return const_cast(&_wrap); // BCC32 workaround - } - - PUGI__FN const xml_node_iterator& xml_node_iterator::operator++() - { - assert(_wrap._root); - _wrap._root = _wrap._root->next_sibling; - return *this; - } - - PUGI__FN xml_node_iterator xml_node_iterator::operator++(int) - { - xml_node_iterator temp = *this; - ++*this; - return temp; - } - - PUGI__FN const xml_node_iterator& xml_node_iterator::operator--() - { - _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); - return *this; - } - - PUGI__FN xml_node_iterator xml_node_iterator::operator--(int) - { - xml_node_iterator temp = *this; - --*this; - return temp; - } - - PUGI__FN xml_attribute_iterator::xml_attribute_iterator() - { - } - - PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) - { - } - - PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) - { - } - - PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const - { - return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; - } - - PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const - { - return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; - } - - PUGI__FN xml_attribute& xml_attribute_iterator::operator*() const - { - assert(_wrap._attr); - return _wrap; - } - - PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const - { - assert(_wrap._attr); - return const_cast(&_wrap); // BCC32 workaround - } - - PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++() - { - assert(_wrap._attr); - _wrap._attr = _wrap._attr->next_attribute; - return *this; - } - - PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int) - { - xml_attribute_iterator temp = *this; - ++*this; - return temp; - } - - PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator--() - { - _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); - return *this; - } - - PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int) - { - xml_attribute_iterator temp = *this; - --*this; - return temp; - } - - PUGI__FN xml_named_node_iterator::xml_named_node_iterator(): _name(0) - { - } - - PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name) - { - } - - PUGI__FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name): _wrap(ref), _parent(parent), _name(name) - { - } - - PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const - { - return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; - } - - PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const - { - return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; - } - - PUGI__FN xml_node& xml_named_node_iterator::operator*() const - { - assert(_wrap._root); - return _wrap; - } - - PUGI__FN xml_node* xml_named_node_iterator::operator->() const - { - assert(_wrap._root); - return const_cast(&_wrap); // BCC32 workaround - } - - PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++() - { - assert(_wrap._root); - _wrap = _wrap.next_sibling(_name); - return *this; - } - - PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int) - { - xml_named_node_iterator temp = *this; - ++*this; - return temp; - } - - PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator--() - { - if (_wrap._root) - _wrap = _wrap.previous_sibling(_name); - else - { - _wrap = _parent.last_child(); - - if (!impl::strequal(_wrap.name(), _name)) - _wrap = _wrap.previous_sibling(_name); - } - - return *this; - } - - PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator--(int) - { - xml_named_node_iterator temp = *this; - --*this; - return temp; - } - - PUGI__FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) - { - } - - PUGI__FN xml_parse_result::operator bool() const - { - return status == status_ok; - } - - PUGI__FN const char* xml_parse_result::description() const - { - switch (status) - { - case status_ok: return "No error"; - - case status_file_not_found: return "File was not found"; - case status_io_error: return "Error reading from file/stream"; - case status_out_of_memory: return "Could not allocate memory"; - case status_internal_error: return "Internal error occurred"; - - case status_unrecognized_tag: return "Could not determine tag type"; - - case status_bad_pi: return "Error parsing document declaration/processing instruction"; - case status_bad_comment: return "Error parsing comment"; - case status_bad_cdata: return "Error parsing CDATA section"; - case status_bad_doctype: return "Error parsing document type declaration"; - case status_bad_pcdata: return "Error parsing PCDATA section"; - case status_bad_start_element: return "Error parsing start element tag"; - case status_bad_attribute: return "Error parsing element attribute"; - case status_bad_end_element: return "Error parsing end element tag"; - case status_end_element_mismatch: return "Start-end tags mismatch"; - - case status_append_invalid_root: return "Unable to append nodes: root is not an element or document"; - - case status_no_document_element: return "No document element found"; - - default: return "Unknown error"; - } - } - - PUGI__FN xml_document::xml_document(): _buffer(0) - { - create(); - } - - PUGI__FN xml_document::~xml_document() - { - destroy(); - } - - PUGI__FN void xml_document::reset() - { - destroy(); - create(); - } - - PUGI__FN void xml_document::reset(const xml_document& proto) - { - reset(); - - for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling()) - append_copy(cur); - } - - PUGI__FN void xml_document::create() - { - assert(!_root); - - // initialize sentinel page - PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment - sizeof(void*) <= sizeof(_memory)); - - // align upwards to page boundary - void* page_memory = reinterpret_cast((reinterpret_cast(_memory) + (impl::xml_memory_page_alignment - 1)) & ~(impl::xml_memory_page_alignment - 1)); - - // prepare page structure - impl::xml_memory_page* page = impl::xml_memory_page::construct(page_memory); - assert(page); - - page->busy_size = impl::xml_memory_page_size; - - // allocate new root - _root = new (reinterpret_cast(page) + sizeof(impl::xml_memory_page)) impl::xml_document_struct(page); - _root->prev_sibling_c = _root; - - // setup sentinel page - page->allocator = static_cast(_root); - - // verify the document allocation - assert(reinterpret_cast(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory)); - } - - PUGI__FN void xml_document::destroy() - { - assert(_root); - - // destroy static storage - if (_buffer) - { - impl::xml_memory::deallocate(_buffer); - _buffer = 0; - } - - // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator) - for (impl::xml_extra_buffer* extra = static_cast(_root)->extra_buffers; extra; extra = extra->next) - { - if (extra->buffer) impl::xml_memory::deallocate(extra->buffer); - } - - // destroy dynamic storage, leave sentinel page (it's in static memory) - impl::xml_memory_page* root_page = reinterpret_cast(_root->header & impl::xml_memory_page_pointer_mask); - assert(root_page && !root_page->prev); - assert(reinterpret_cast(root_page) >= _memory && reinterpret_cast(root_page) < _memory + sizeof(_memory)); - - for (impl::xml_memory_page* page = root_page->next; page; ) - { - impl::xml_memory_page* next = page->next; - - impl::xml_allocator::deallocate_page(page); - - page = next; - } - - _root = 0; - } - -#ifndef PUGIXML_NO_STL - PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options, xml_encoding encoding) - { - reset(); - - return impl::load_stream_impl(*this, stream, options, encoding); - } - - PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options) - { - reset(); - - return impl::load_stream_impl(*this, stream, options, encoding_wchar); - } -#endif - - PUGI__FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options) - { - // Force native encoding (skip autodetection) - #ifdef PUGIXML_WCHAR_MODE - xml_encoding encoding = encoding_wchar; - #else - xml_encoding encoding = encoding_utf8; - #endif - - return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding); - } - - PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options) - { - return load_string(contents, options); - } - - PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding) - { - reset(); - - FILE* file = fopen(path_, "rb"); - - return impl::load_file_impl(*this, file, options, encoding); - } - - PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding) - { - reset(); - - FILE* file = impl::open_file_wide(path_, L"rb"); - - return impl::load_file_impl(*this, file, options, encoding); - } - - PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) - { - reset(); - - return impl::load_buffer_impl(static_cast(_root), _root, const_cast(contents), size, options, encoding, false, false, &_buffer); - } - - PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding) - { - reset(); - - return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, false, &_buffer); - } - - PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding) - { - reset(); - - return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, true, &_buffer); - } - - PUGI__FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const - { - impl::xml_buffered_writer buffered_writer(writer, encoding); - - if ((flags & format_write_bom) && encoding != encoding_latin1) - { - // BOM always represents the codepoint U+FEFF, so just write it in native encoding - #ifdef PUGIXML_WCHAR_MODE - unsigned int bom = 0xfeff; - buffered_writer.write(static_cast(bom)); - #else - buffered_writer.write('\xef', '\xbb', '\xbf'); - #endif - } - - if (!(flags & format_no_declaration) && !impl::has_declaration(_root)) - { - buffered_writer.write_string(PUGIXML_TEXT("'); - if (!(flags & format_raw)) buffered_writer.write('\n'); - } - - impl::node_output(buffered_writer, _root, indent, flags, 0); - } - -#ifndef PUGIXML_NO_STL - PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const - { - xml_writer_stream writer(stream); - - save(writer, indent, flags, encoding); - } - - PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags) const - { - xml_writer_stream writer(stream); - - save(writer, indent, flags, encoding_wchar); - } -#endif - - PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const - { - FILE* file = fopen(path_, (flags & format_save_file_text) ? "w" : "wb"); - return impl::save_file_impl(*this, file, indent, flags, encoding); - } - - PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const - { - FILE* file = impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"); - return impl::save_file_impl(*this, file, indent, flags, encoding); - } - - PUGI__FN xml_node xml_document::document_element() const - { - assert(_root); - - for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (PUGI__NODETYPE(i) == node_element) - return xml_node(i); - - return xml_node(); - } - -#ifndef PUGIXML_NO_STL - PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) - { - assert(str); - - return impl::as_utf8_impl(str, impl::strlength_wide(str)); - } - - PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string& str) - { - return impl::as_utf8_impl(str.c_str(), str.size()); - } - - PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str) - { - assert(str); - - return impl::as_wide_impl(str, strlen(str)); - } - - PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string& str) - { - return impl::as_wide_impl(str.c_str(), str.size()); - } -#endif - - PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate) - { - impl::xml_memory::allocate = allocate; - impl::xml_memory::deallocate = deallocate; - } - - PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function() - { - return impl::xml_memory::allocate; - } - - PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() - { - return impl::xml_memory::deallocate; - } -} - -#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) -namespace std -{ - // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) - PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&) - { - return std::bidirectional_iterator_tag(); - } - - PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&) - { - return std::bidirectional_iterator_tag(); - } - - PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&) - { - return std::bidirectional_iterator_tag(); - } -} -#endif - -#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) -namespace std -{ - // Workarounds for (non-standard) iterator category detection - PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&) - { - return std::bidirectional_iterator_tag(); - } - - PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&) - { - return std::bidirectional_iterator_tag(); - } - - PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&) - { - return std::bidirectional_iterator_tag(); - } -} -#endif - -#ifndef PUGIXML_NO_XPATH -// STL replacements -PUGI__NS_BEGIN - struct equal_to - { - template bool operator()(const T& lhs, const T& rhs) const - { - return lhs == rhs; - } - }; - - struct not_equal_to - { - template bool operator()(const T& lhs, const T& rhs) const - { - return lhs != rhs; - } - }; - - struct less - { - template bool operator()(const T& lhs, const T& rhs) const - { - return lhs < rhs; - } - }; - - struct less_equal - { - template bool operator()(const T& lhs, const T& rhs) const - { - return lhs <= rhs; - } - }; - - template void swap(T& lhs, T& rhs) - { - T temp = lhs; - lhs = rhs; - rhs = temp; - } - - template I min_element(I begin, I end, const Pred& pred) - { - I result = begin; - - for (I it = begin + 1; it != end; ++it) - if (pred(*it, *result)) - result = it; - - return result; - } - - template void reverse(I begin, I end) - { - while (end - begin > 1) swap(*begin++, *--end); - } - - template I unique(I begin, I end) - { - // fast skip head - while (end - begin > 1 && *begin != *(begin + 1)) begin++; - - if (begin == end) return begin; - - // last written element - I write = begin++; - - // merge unique elements - while (begin != end) - { - if (*begin != *write) - *++write = *begin++; - else - begin++; - } - - // past-the-end (write points to live element) - return write + 1; - } - - template void copy_backwards(I begin, I end, I target) - { - while (begin != end) *--target = *--end; - } - - template void insertion_sort(I begin, I end, const Pred& pred, T*) - { - assert(begin != end); - - for (I it = begin + 1; it != end; ++it) - { - T val = *it; - - if (pred(val, *begin)) - { - // move to front - copy_backwards(begin, it, it + 1); - *begin = val; - } - else - { - I hole = it; - - // move hole backwards - while (pred(val, *(hole - 1))) - { - *hole = *(hole - 1); - hole--; - } - - // fill hole with element - *hole = val; - } - } - } - - // std variant for elements with == - template void partition(I begin, I middle, I end, const Pred& pred, I* out_eqbeg, I* out_eqend) - { - I eqbeg = middle, eqend = middle + 1; - - // expand equal range - while (eqbeg != begin && *(eqbeg - 1) == *eqbeg) --eqbeg; - while (eqend != end && *eqend == *eqbeg) ++eqend; - - // process outer elements - I ltend = eqbeg, gtbeg = eqend; - - for (;;) - { - // find the element from the right side that belongs to the left one - for (; gtbeg != end; ++gtbeg) - if (!pred(*eqbeg, *gtbeg)) - { - if (*gtbeg == *eqbeg) swap(*gtbeg, *eqend++); - else break; - } - - // find the element from the left side that belongs to the right one - for (; ltend != begin; --ltend) - if (!pred(*(ltend - 1), *eqbeg)) - { - if (*eqbeg == *(ltend - 1)) swap(*(ltend - 1), *--eqbeg); - else break; - } - - // scanned all elements - if (gtbeg == end && ltend == begin) - { - *out_eqbeg = eqbeg; - *out_eqend = eqend; - return; - } - - // make room for elements by moving equal area - if (gtbeg == end) - { - if (--ltend != --eqbeg) swap(*ltend, *eqbeg); - swap(*eqbeg, *--eqend); - } - else if (ltend == begin) - { - if (eqend != gtbeg) swap(*eqbeg, *eqend); - ++eqend; - swap(*gtbeg++, *eqbeg++); - } - else swap(*gtbeg++, *--ltend); - } - } - - template void median3(I first, I middle, I last, const Pred& pred) - { - if (pred(*middle, *first)) swap(*middle, *first); - if (pred(*last, *middle)) swap(*last, *middle); - if (pred(*middle, *first)) swap(*middle, *first); - } - - template void median(I first, I middle, I last, const Pred& pred) - { - if (last - first <= 40) - { - // median of three for small chunks - median3(first, middle, last, pred); - } - else - { - // median of nine - size_t step = (last - first + 1) / 8; - - median3(first, first + step, first + 2 * step, pred); - median3(middle - step, middle, middle + step, pred); - median3(last - 2 * step, last - step, last, pred); - median3(first + step, middle, last - step, pred); - } - } - - template void sort(I begin, I end, const Pred& pred) - { - // sort large chunks - while (end - begin > 32) - { - // find median element - I middle = begin + (end - begin) / 2; - median(begin, middle, end - 1, pred); - - // partition in three chunks (< = >) - I eqbeg, eqend; - partition(begin, middle, end, pred, &eqbeg, &eqend); - - // loop on larger half - if (eqbeg - begin > end - eqend) - { - sort(eqend, end, pred); - end = eqbeg; - } - else - { - sort(begin, eqbeg, pred); - begin = eqend; - } - } - - // insertion sort small chunk - if (begin != end) insertion_sort(begin, end, pred, &*begin); - } -PUGI__NS_END - -// Allocator used for AST and evaluation stacks -PUGI__NS_BEGIN - struct xpath_memory_block - { - xpath_memory_block* next; - size_t capacity; - - char data[ - #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE - PUGIXML_MEMORY_XPATH_PAGE_SIZE - #else - 4096 - #endif - ]; - }; - - class xpath_allocator - { - xpath_memory_block* _root; - size_t _root_size; - - public: - #ifdef PUGIXML_NO_EXCEPTIONS - jmp_buf* error_handler; - #endif - - xpath_allocator(xpath_memory_block* root, size_t root_size = 0): _root(root), _root_size(root_size) - { - #ifdef PUGIXML_NO_EXCEPTIONS - error_handler = 0; - #endif - } - - void* allocate_nothrow(size_t size) - { - // align size so that we're able to store pointers in subsequent blocks - size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); - - if (_root_size + size <= _root->capacity) - { - void* buf = _root->data + _root_size; - _root_size += size; - return buf; - } - else - { - // make sure we have at least 1/4th of the page free after allocation to satisfy subsequent allocation requests - size_t block_capacity_base = sizeof(_root->data); - size_t block_capacity_req = size + block_capacity_base / 4; - size_t block_capacity = (block_capacity_base > block_capacity_req) ? block_capacity_base : block_capacity_req; - - size_t block_size = block_capacity + offsetof(xpath_memory_block, data); - - xpath_memory_block* block = static_cast(xml_memory::allocate(block_size)); - if (!block) return 0; - - block->next = _root; - block->capacity = block_capacity; - - _root = block; - _root_size = size; - - return block->data; - } - } - - void* allocate(size_t size) - { - void* result = allocate_nothrow(size); - - if (!result) - { - #ifdef PUGIXML_NO_EXCEPTIONS - assert(error_handler); - longjmp(*error_handler, 1); - #else - throw std::bad_alloc(); - #endif - } - - return result; - } - - void* reallocate(void* ptr, size_t old_size, size_t new_size) - { - // align size so that we're able to store pointers in subsequent blocks - old_size = (old_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); - new_size = (new_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); - - // we can only reallocate the last object - assert(ptr == 0 || static_cast(ptr) + old_size == _root->data + _root_size); - - // adjust root size so that we have not allocated the object at all - bool only_object = (_root_size == old_size); - - if (ptr) _root_size -= old_size; - - // allocate a new version (this will obviously reuse the memory if possible) - void* result = allocate(new_size); - assert(result); - - // we have a new block - if (result != ptr && ptr) - { - // copy old data - assert(new_size >= old_size); - memcpy(result, ptr, old_size); - - // free the previous page if it had no other objects - if (only_object) - { - assert(_root->data == result); - assert(_root->next); - - xpath_memory_block* next = _root->next->next; - - if (next) - { - // deallocate the whole page, unless it was the first one - xml_memory::deallocate(_root->next); - _root->next = next; - } - } - } - - return result; - } - - void revert(const xpath_allocator& state) - { - // free all new pages - xpath_memory_block* cur = _root; - - while (cur != state._root) - { - xpath_memory_block* next = cur->next; - - xml_memory::deallocate(cur); - - cur = next; - } - - // restore state - _root = state._root; - _root_size = state._root_size; - } - - void release() - { - xpath_memory_block* cur = _root; - assert(cur); - - while (cur->next) - { - xpath_memory_block* next = cur->next; - - xml_memory::deallocate(cur); - - cur = next; - } - } - }; - - struct xpath_allocator_capture - { - xpath_allocator_capture(xpath_allocator* alloc): _target(alloc), _state(*alloc) - { - } - - ~xpath_allocator_capture() - { - _target->revert(_state); - } - - xpath_allocator* _target; - xpath_allocator _state; - }; - - struct xpath_stack - { - xpath_allocator* result; - xpath_allocator* temp; - }; - - struct xpath_stack_data - { - xpath_memory_block blocks[2]; - xpath_allocator result; - xpath_allocator temp; - xpath_stack stack; - - #ifdef PUGIXML_NO_EXCEPTIONS - jmp_buf error_handler; - #endif - - xpath_stack_data(): result(blocks + 0), temp(blocks + 1) - { - blocks[0].next = blocks[1].next = 0; - blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data); - - stack.result = &result; - stack.temp = &temp; - - #ifdef PUGIXML_NO_EXCEPTIONS - result.error_handler = temp.error_handler = &error_handler; - #endif - } - - ~xpath_stack_data() - { - result.release(); - temp.release(); - } - }; -PUGI__NS_END - -// String class -PUGI__NS_BEGIN - class xpath_string - { - const char_t* _buffer; - bool _uses_heap; - size_t _length_heap; - - static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc) - { - char_t* result = static_cast(alloc->allocate((length + 1) * sizeof(char_t))); - assert(result); - - memcpy(result, string, length * sizeof(char_t)); - result[length] = 0; - - return result; - } - - xpath_string(const char_t* buffer, bool uses_heap_, size_t length_heap): _buffer(buffer), _uses_heap(uses_heap_), _length_heap(length_heap) - { - } - - public: - static xpath_string from_const(const char_t* str) - { - return xpath_string(str, false, 0); - } - - static xpath_string from_heap_preallocated(const char_t* begin, const char_t* end) - { - assert(begin <= end && *end == 0); - - return xpath_string(begin, true, static_cast(end - begin)); - } - - static xpath_string from_heap(const char_t* begin, const char_t* end, xpath_allocator* alloc) - { - assert(begin <= end); - - size_t length = static_cast(end - begin); - - return length == 0 ? xpath_string() : xpath_string(duplicate_string(begin, length, alloc), true, length); - } - - xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0) - { - } - - void append(const xpath_string& o, xpath_allocator* alloc) - { - // skip empty sources - if (!*o._buffer) return; - - // fast append for constant empty target and constant source - if (!*_buffer && !_uses_heap && !o._uses_heap) - { - _buffer = o._buffer; - } - else - { - // need to make heap copy - size_t target_length = length(); - size_t source_length = o.length(); - size_t result_length = target_length + source_length; - - // allocate new buffer - char_t* result = static_cast(alloc->reallocate(_uses_heap ? const_cast(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t))); - assert(result); - - // append first string to the new buffer in case there was no reallocation - if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t)); - - // append second string to the new buffer - memcpy(result + target_length, o._buffer, source_length * sizeof(char_t)); - result[result_length] = 0; - - // finalize - _buffer = result; - _uses_heap = true; - _length_heap = result_length; - } - } - - const char_t* c_str() const - { - return _buffer; - } - - size_t length() const - { - return _uses_heap ? _length_heap : strlength(_buffer); - } - - char_t* data(xpath_allocator* alloc) - { - // make private heap copy - if (!_uses_heap) - { - size_t length_ = strlength(_buffer); - - _buffer = duplicate_string(_buffer, length_, alloc); - _uses_heap = true; - _length_heap = length_; - } - - return const_cast(_buffer); - } - - bool empty() const - { - return *_buffer == 0; - } - - bool operator==(const xpath_string& o) const - { - return strequal(_buffer, o._buffer); - } - - bool operator!=(const xpath_string& o) const - { - return !strequal(_buffer, o._buffer); - } - - bool uses_heap() const - { - return _uses_heap; - } - }; -PUGI__NS_END - -PUGI__NS_BEGIN - PUGI__FN bool starts_with(const char_t* string, const char_t* pattern) - { - while (*pattern && *string == *pattern) - { - string++; - pattern++; - } - - return *pattern == 0; - } - - PUGI__FN const char_t* find_char(const char_t* s, char_t c) - { - #ifdef PUGIXML_WCHAR_MODE - return wcschr(s, c); - #else - return strchr(s, c); - #endif - } - - PUGI__FN const char_t* find_substring(const char_t* s, const char_t* p) - { - #ifdef PUGIXML_WCHAR_MODE - // MSVC6 wcsstr bug workaround (if s is empty it always returns 0) - return (*p == 0) ? s : wcsstr(s, p); - #else - return strstr(s, p); - #endif - } - - // Converts symbol to lower case, if it is an ASCII one - PUGI__FN char_t tolower_ascii(char_t ch) - { - return static_cast(ch - 'A') < 26 ? static_cast(ch | ' ') : ch; - } - - PUGI__FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc) - { - if (na.attribute()) - return xpath_string::from_const(na.attribute().value()); - else - { - xml_node n = na.node(); - - switch (n.type()) - { - case node_pcdata: - case node_cdata: - case node_comment: - case node_pi: - return xpath_string::from_const(n.value()); - - case node_document: - case node_element: - { - xpath_string result; - - xml_node cur = n.first_child(); - - while (cur && cur != n) - { - if (cur.type() == node_pcdata || cur.type() == node_cdata) - result.append(xpath_string::from_const(cur.value()), alloc); - - if (cur.first_child()) - cur = cur.first_child(); - else if (cur.next_sibling()) - cur = cur.next_sibling(); - else - { - while (!cur.next_sibling() && cur != n) - cur = cur.parent(); - - if (cur != n) cur = cur.next_sibling(); - } - } - - return result; - } - - default: - return xpath_string(); - } - } - } - - PUGI__FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn) - { - assert(ln->parent == rn->parent); - - // there is no common ancestor (the shared parent is null), nodes are from different documents - if (!ln->parent) return ln < rn; - - // determine sibling order - xml_node_struct* ls = ln; - xml_node_struct* rs = rn; - - while (ls && rs) - { - if (ls == rn) return true; - if (rs == ln) return false; - - ls = ls->next_sibling; - rs = rs->next_sibling; - } - - // if rn sibling chain ended ln must be before rn - return !rs; - } - - PUGI__FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn) - { - // find common ancestor at the same depth, if any - xml_node_struct* lp = ln; - xml_node_struct* rp = rn; - - while (lp && rp && lp->parent != rp->parent) - { - lp = lp->parent; - rp = rp->parent; - } - - // parents are the same! - if (lp && rp) return node_is_before_sibling(lp, rp); - - // nodes are at different depths, need to normalize heights - bool left_higher = !lp; - - while (lp) - { - lp = lp->parent; - ln = ln->parent; - } - - while (rp) - { - rp = rp->parent; - rn = rn->parent; - } - - // one node is the ancestor of the other - if (ln == rn) return left_higher; - - // find common ancestor... again - while (ln->parent != rn->parent) - { - ln = ln->parent; - rn = rn->parent; - } - - return node_is_before_sibling(ln, rn); - } - - PUGI__FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node) - { - while (node && node != parent) node = node->parent; - - return parent && node == parent; - } - - PUGI__FN const void* document_buffer_order(const xpath_node& xnode) - { - xml_node_struct* node = xnode.node().internal_object(); - - if (node) - { - if ((get_document(node).header & xml_memory_page_contents_shared_mask) == 0) - { - if (node->name && (node->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return node->name; - if (node->value && (node->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return node->value; - } - - return 0; - } - - xml_attribute_struct* attr = xnode.attribute().internal_object(); - - if (attr) - { - if ((get_document(attr).header & xml_memory_page_contents_shared_mask) == 0) - { - if ((attr->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return attr->name; - if ((attr->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return attr->value; - } - - return 0; - } - - return 0; - } - - struct document_order_comparator - { - bool operator()(const xpath_node& lhs, const xpath_node& rhs) const - { - // optimized document order based check - const void* lo = document_buffer_order(lhs); - const void* ro = document_buffer_order(rhs); - - if (lo && ro) return lo < ro; - - // slow comparison - xml_node ln = lhs.node(), rn = rhs.node(); - - // compare attributes - if (lhs.attribute() && rhs.attribute()) - { - // shared parent - if (lhs.parent() == rhs.parent()) - { - // determine sibling order - for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute()) - if (a == rhs.attribute()) - return true; - - return false; - } - - // compare attribute parents - ln = lhs.parent(); - rn = rhs.parent(); - } - else if (lhs.attribute()) - { - // attributes go after the parent element - if (lhs.parent() == rhs.node()) return false; - - ln = lhs.parent(); - } - else if (rhs.attribute()) - { - // attributes go after the parent element - if (rhs.parent() == lhs.node()) return true; - - rn = rhs.parent(); - } - - if (ln == rn) return false; - - if (!ln || !rn) return ln < rn; - - return node_is_before(ln.internal_object(), rn.internal_object()); - } - }; - - struct duplicate_comparator - { - bool operator()(const xpath_node& lhs, const xpath_node& rhs) const - { - if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true; - else return rhs.attribute() ? false : lhs.node() < rhs.node(); - } - }; - - PUGI__FN double gen_nan() - { - #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) - union { float f; uint32_t i; } u[sizeof(float) == sizeof(uint32_t) ? 1 : -1]; - u[0].i = 0x7fc00000; - return u[0].f; - #else - // fallback - const volatile double zero = 0.0; - return zero / zero; - #endif - } - - PUGI__FN bool is_nan(double value) - { - #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) - return !!_isnan(value); - #elif defined(fpclassify) && defined(FP_NAN) - return fpclassify(value) == FP_NAN; - #else - // fallback - const volatile double v = value; - return v != v; - #endif - } - - PUGI__FN const char_t* convert_number_to_string_special(double value) - { - #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) - if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0; - if (_isnan(value)) return PUGIXML_TEXT("NaN"); - return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); - #elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO) - switch (fpclassify(value)) - { - case FP_NAN: - return PUGIXML_TEXT("NaN"); - - case FP_INFINITE: - return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); - - case FP_ZERO: - return PUGIXML_TEXT("0"); - - default: - return 0; - } - #else - // fallback - const volatile double v = value; - - if (v == 0) return PUGIXML_TEXT("0"); - if (v != v) return PUGIXML_TEXT("NaN"); - if (v * 2 == v) return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); - return 0; - #endif - } - - PUGI__FN bool convert_number_to_boolean(double value) - { - return (value != 0 && !is_nan(value)); - } - - PUGI__FN void truncate_zeros(char* begin, char* end) - { - while (begin != end && end[-1] == '0') end--; - - *end = 0; - } - - // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent -#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) - PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) - { - // get base values - int sign, exponent; - _ecvt_s(buffer, buffer_size, value, DBL_DIG + 1, &exponent, &sign); - - // truncate redundant zeros - truncate_zeros(buffer, buffer + strlen(buffer)); - - // fill results - *out_mantissa = buffer; - *out_exponent = exponent; - } -#else - PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) - { - // get a scientific notation value with IEEE DBL_DIG decimals - sprintf(buffer, "%.*e", DBL_DIG, value); - assert(strlen(buffer) < buffer_size); - (void)!buffer_size; - - // get the exponent (possibly negative) - char* exponent_string = strchr(buffer, 'e'); - assert(exponent_string); - - int exponent = atoi(exponent_string + 1); - - // extract mantissa string: skip sign - char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer; - assert(mantissa[0] != '0' && mantissa[1] == '.'); - - // divide mantissa by 10 to eliminate integer part - mantissa[1] = mantissa[0]; - mantissa++; - exponent++; - - // remove extra mantissa digits and zero-terminate mantissa - truncate_zeros(mantissa, exponent_string); - - // fill results - *out_mantissa = mantissa; - *out_exponent = exponent; - } -#endif - - PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc) - { - // try special number conversion - const char_t* special = convert_number_to_string_special(value); - if (special) return xpath_string::from_const(special); - - // get mantissa + exponent form - char mantissa_buffer[32]; - - char* mantissa; - int exponent; - convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent); - - // allocate a buffer of suitable length for the number - size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4; - char_t* result = static_cast(alloc->allocate(sizeof(char_t) * result_size)); - assert(result); - - // make the number! - char_t* s = result; - - // sign - if (value < 0) *s++ = '-'; - - // integer part - if (exponent <= 0) - { - *s++ = '0'; - } - else - { - while (exponent > 0) - { - assert(*mantissa == 0 || static_cast(static_cast(*mantissa) - '0') <= 9); - *s++ = *mantissa ? *mantissa++ : '0'; - exponent--; - } - } - - // fractional part - if (*mantissa) - { - // decimal point - *s++ = '.'; - - // extra zeroes from negative exponent - while (exponent < 0) - { - *s++ = '0'; - exponent++; - } - - // extra mantissa digits - while (*mantissa) - { - assert(static_cast(*mantissa - '0') <= 9); - *s++ = *mantissa++; - } - } - - // zero-terminate - assert(s < result + result_size); - *s = 0; - - return xpath_string::from_heap_preallocated(result, s); - } - - PUGI__FN bool check_string_to_number_format(const char_t* string) - { - // parse leading whitespace - while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; - - // parse sign - if (*string == '-') ++string; - - if (!*string) return false; - - // if there is no integer part, there should be a decimal part with at least one digit - if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false; - - // parse integer part - while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; - - // parse decimal part - if (*string == '.') - { - ++string; - - while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; - } - - // parse trailing whitespace - while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; - - return *string == 0; - } - - PUGI__FN double convert_string_to_number(const char_t* string) - { - // check string format - if (!check_string_to_number_format(string)) return gen_nan(); - - // parse string - #ifdef PUGIXML_WCHAR_MODE - return wcstod(string, 0); - #else - return atof(string); - #endif - } - - PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result) - { - size_t length = static_cast(end - begin); - char_t* scratch = buffer; - - if (length >= sizeof(buffer) / sizeof(buffer[0])) - { - // need to make dummy on-heap copy - scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!scratch) return false; - } - - // copy string to zero-terminated buffer and perform conversion - memcpy(scratch, begin, length * sizeof(char_t)); - scratch[length] = 0; - - *out_result = convert_string_to_number(scratch); - - // free dummy buffer - if (scratch != buffer) xml_memory::deallocate(scratch); - - return true; - } - - PUGI__FN double round_nearest(double value) - { - return floor(value + 0.5); - } - - PUGI__FN double round_nearest_nzero(double value) - { - // same as round_nearest, but returns -0 for [-0.5, -0] - // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0) - return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5); - } - - PUGI__FN const char_t* qualified_name(const xpath_node& node) - { - return node.attribute() ? node.attribute().name() : node.node().name(); - } - - PUGI__FN const char_t* local_name(const xpath_node& node) - { - const char_t* name = qualified_name(node); - const char_t* p = find_char(name, ':'); - - return p ? p + 1 : name; - } - - struct namespace_uri_predicate - { - const char_t* prefix; - size_t prefix_length; - - namespace_uri_predicate(const char_t* name) - { - const char_t* pos = find_char(name, ':'); - - prefix = pos ? name : 0; - prefix_length = pos ? static_cast(pos - name) : 0; - } - - bool operator()(xml_attribute a) const - { - const char_t* name = a.name(); - - if (!starts_with(name, PUGIXML_TEXT("xmlns"))) return false; - - return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0; - } - }; - - PUGI__FN const char_t* namespace_uri(xml_node node) - { - namespace_uri_predicate pred = node.name(); - - xml_node p = node; - - while (p) - { - xml_attribute a = p.find_attribute(pred); - - if (a) return a.value(); - - p = p.parent(); - } - - return PUGIXML_TEXT(""); - } - - PUGI__FN const char_t* namespace_uri(xml_attribute attr, xml_node parent) - { - namespace_uri_predicate pred = attr.name(); - - // Default namespace does not apply to attributes - if (!pred.prefix) return PUGIXML_TEXT(""); - - xml_node p = parent; - - while (p) - { - xml_attribute a = p.find_attribute(pred); - - if (a) return a.value(); - - p = p.parent(); - } - - return PUGIXML_TEXT(""); - } - - PUGI__FN const char_t* namespace_uri(const xpath_node& node) - { - return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); - } - - PUGI__FN char_t* normalize_space(char_t* buffer) - { - char_t* write = buffer; - - for (char_t* it = buffer; *it; ) - { - char_t ch = *it++; - - if (PUGI__IS_CHARTYPE(ch, ct_space)) - { - // replace whitespace sequence with single space - while (PUGI__IS_CHARTYPE(*it, ct_space)) it++; - - // avoid leading spaces - if (write != buffer) *write++ = ' '; - } - else *write++ = ch; - } - - // remove trailing space - if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--; - - // zero-terminate - *write = 0; - - return write; - } - - PUGI__FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length) - { - char_t* write = buffer; - - while (*buffer) - { - PUGI__DMC_VOLATILE char_t ch = *buffer++; - - const char_t* pos = find_char(from, ch); - - if (!pos) - *write++ = ch; // do not process - else if (static_cast(pos - from) < to_length) - *write++ = to[pos - from]; // replace - } - - // zero-terminate - *write = 0; - - return write; - } - - PUGI__FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to) - { - unsigned char table[128] = {0}; - - while (*from) - { - unsigned int fc = static_cast(*from); - unsigned int tc = static_cast(*to); - - if (fc >= 128 || tc >= 128) - return 0; - - // code=128 means "skip character" - if (!table[fc]) - table[fc] = static_cast(tc ? tc : 128); - - from++; - if (tc) to++; - } - - for (int i = 0; i < 128; ++i) - if (!table[i]) - table[i] = static_cast(i); - - void* result = alloc->allocate_nothrow(sizeof(table)); - - if (result) - { - memcpy(result, table, sizeof(table)); - } - - return static_cast(result); - } - - PUGI__FN char_t* translate_table(char_t* buffer, const unsigned char* table) - { - char_t* write = buffer; - - while (*buffer) - { - char_t ch = *buffer++; - unsigned int index = static_cast(ch); - - if (index < 128) - { - unsigned char code = table[index]; - - // code=128 means "skip character" (table size is 128 so 128 can be a special value) - // this code skips these characters without extra branches - *write = static_cast(code); - write += 1 - (code >> 7); - } - else - { - *write++ = ch; - } - } - - // zero-terminate - *write = 0; - - return write; - } - - inline bool is_xpath_attribute(const char_t* name) - { - return !(starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')); - } - - struct xpath_variable_boolean: xpath_variable - { - xpath_variable_boolean(): value(false) - { - } - - bool value; - char_t name[1]; - }; - - struct xpath_variable_number: xpath_variable - { - xpath_variable_number(): value(0) - { - } - - double value; - char_t name[1]; - }; - - struct xpath_variable_string: xpath_variable - { - xpath_variable_string(): value(0) - { - } - - ~xpath_variable_string() - { - if (value) xml_memory::deallocate(value); - } - - char_t* value; - char_t name[1]; - }; - - struct xpath_variable_node_set: xpath_variable - { - xpath_node_set value; - char_t name[1]; - }; - - static const xpath_node_set dummy_node_set; - - PUGI__FN unsigned int hash_string(const char_t* str) - { - // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time) - unsigned int result = 0; - - while (*str) - { - result += static_cast(*str++); - result += result << 10; - result ^= result >> 6; - } - - result += result << 3; - result ^= result >> 11; - result += result << 15; - - return result; - } - - template PUGI__FN T* new_xpath_variable(const char_t* name) - { - size_t length = strlength(name); - if (length == 0) return 0; // empty variable names are invalid - - // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters - void* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t)); - if (!memory) return 0; - - T* result = new (memory) T(); - - memcpy(result->name, name, (length + 1) * sizeof(char_t)); - - return result; - } - - PUGI__FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name) - { - switch (type) - { - case xpath_type_node_set: - return new_xpath_variable(name); - - case xpath_type_number: - return new_xpath_variable(name); - - case xpath_type_string: - return new_xpath_variable(name); - - case xpath_type_boolean: - return new_xpath_variable(name); - - default: - return 0; - } - } - - template PUGI__FN void delete_xpath_variable(T* var) - { - var->~T(); - xml_memory::deallocate(var); - } - - PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var) - { - switch (type) - { - case xpath_type_node_set: - delete_xpath_variable(static_cast(var)); - break; - - case xpath_type_number: - delete_xpath_variable(static_cast(var)); - break; - - case xpath_type_string: - delete_xpath_variable(static_cast(var)); - break; - - case xpath_type_boolean: - delete_xpath_variable(static_cast(var)); - break; - - default: - assert(!"Invalid variable type"); - } - } - - PUGI__FN xpath_variable* get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end) - { - size_t length = static_cast(end - begin); - char_t* scratch = buffer; - - if (length >= sizeof(buffer) / sizeof(buffer[0])) - { - // need to make dummy on-heap copy - scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!scratch) return 0; - } - - // copy string to zero-terminated buffer and perform lookup - memcpy(scratch, begin, length * sizeof(char_t)); - scratch[length] = 0; - - xpath_variable* result = set->get(scratch); - - // free dummy buffer - if (scratch != buffer) xml_memory::deallocate(scratch); - - return result; - } -PUGI__NS_END - -// Internal node set class -PUGI__NS_BEGIN - PUGI__FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end) - { - if (end - begin < 2) - return xpath_node_set::type_sorted; - - document_order_comparator cmp; - - bool first = cmp(begin[0], begin[1]); - - for (const xpath_node* it = begin + 1; it + 1 < end; ++it) - if (cmp(it[0], it[1]) != first) - return xpath_node_set::type_unsorted; - - return first ? xpath_node_set::type_sorted : xpath_node_set::type_sorted_reverse; - } - - PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev) - { - xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; - - if (type == xpath_node_set::type_unsorted) - { - xpath_node_set::type_t sorted = xpath_get_order(begin, end); - - if (sorted == xpath_node_set::type_unsorted) - { - sort(begin, end, document_order_comparator()); - - type = xpath_node_set::type_sorted; - } - else - type = sorted; - } - - if (type != order) reverse(begin, end); - - return order; - } - - PUGI__FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type) - { - if (begin == end) return xpath_node(); - - switch (type) - { - case xpath_node_set::type_sorted: - return *begin; - - case xpath_node_set::type_sorted_reverse: - return *(end - 1); - - case xpath_node_set::type_unsorted: - return *min_element(begin, end, document_order_comparator()); - - default: - assert(!"Invalid node set type"); - return xpath_node(); - } - } - - class xpath_node_set_raw - { - xpath_node_set::type_t _type; - - xpath_node* _begin; - xpath_node* _end; - xpath_node* _eos; - - public: - xpath_node_set_raw(): _type(xpath_node_set::type_unsorted), _begin(0), _end(0), _eos(0) - { - } - - xpath_node* begin() const - { - return _begin; - } - - xpath_node* end() const - { - return _end; - } - - bool empty() const - { - return _begin == _end; - } - - size_t size() const - { - return static_cast(_end - _begin); - } - - xpath_node first() const - { - return xpath_first(_begin, _end, _type); - } - - void push_back_grow(const xpath_node& node, xpath_allocator* alloc); - - void push_back(const xpath_node& node, xpath_allocator* alloc) - { - if (_end != _eos) - *_end++ = node; - else - push_back_grow(node, alloc); - } - - void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc) - { - if (begin_ == end_) return; - - size_t size_ = static_cast(_end - _begin); - size_t capacity = static_cast(_eos - _begin); - size_t count = static_cast(end_ - begin_); - - if (size_ + count > capacity) - { - // reallocate the old array or allocate a new one - xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node))); - assert(data); - - // finalize - _begin = data; - _end = data + size_; - _eos = data + size_ + count; - } - - memcpy(_end, begin_, count * sizeof(xpath_node)); - _end += count; - } - - void sort_do() - { - _type = xpath_sort(_begin, _end, _type, false); - } - - void truncate(xpath_node* pos) - { - assert(_begin <= pos && pos <= _end); - - _end = pos; - } - - void remove_duplicates() - { - if (_type == xpath_node_set::type_unsorted) - sort(_begin, _end, duplicate_comparator()); - - _end = unique(_begin, _end); - } - - xpath_node_set::type_t type() const - { - return _type; - } - - void set_type(xpath_node_set::type_t value) - { - _type = value; - } - }; - - PUGI__FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc) - { - size_t capacity = static_cast(_eos - _begin); - - // get new capacity (1.5x rule) - size_t new_capacity = capacity + capacity / 2 + 1; - - // reallocate the old array or allocate a new one - xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node))); - assert(data); - - // finalize - _begin = data; - _end = data + capacity; - _eos = data + new_capacity; - - // push - *_end++ = node; - } -PUGI__NS_END - -PUGI__NS_BEGIN - struct xpath_context - { - xpath_node n; - size_t position, size; - - xpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_) - { - } - }; - - enum lexeme_t - { - lex_none = 0, - lex_equal, - lex_not_equal, - lex_less, - lex_greater, - lex_less_or_equal, - lex_greater_or_equal, - lex_plus, - lex_minus, - lex_multiply, - lex_union, - lex_var_ref, - lex_open_brace, - lex_close_brace, - lex_quoted_string, - lex_number, - lex_slash, - lex_double_slash, - lex_open_square_brace, - lex_close_square_brace, - lex_string, - lex_comma, - lex_axis_attribute, - lex_dot, - lex_double_dot, - lex_double_colon, - lex_eof - }; - - struct xpath_lexer_string - { - const char_t* begin; - const char_t* end; - - xpath_lexer_string(): begin(0), end(0) - { - } - - bool operator==(const char_t* other) const - { - size_t length = static_cast(end - begin); - - return strequalrange(other, begin, length); - } - }; - - class xpath_lexer - { - const char_t* _cur; - const char_t* _cur_lexeme_pos; - xpath_lexer_string _cur_lexeme_contents; - - lexeme_t _cur_lexeme; - - public: - explicit xpath_lexer(const char_t* query): _cur(query) - { - next(); - } - - const char_t* state() const - { - return _cur; - } - - void next() - { - const char_t* cur = _cur; - - while (PUGI__IS_CHARTYPE(*cur, ct_space)) ++cur; - - // save lexeme position for error reporting - _cur_lexeme_pos = cur; - - switch (*cur) - { - case 0: - _cur_lexeme = lex_eof; - break; - - case '>': - if (*(cur+1) == '=') - { - cur += 2; - _cur_lexeme = lex_greater_or_equal; - } - else - { - cur += 1; - _cur_lexeme = lex_greater; - } - break; - - case '<': - if (*(cur+1) == '=') - { - cur += 2; - _cur_lexeme = lex_less_or_equal; - } - else - { - cur += 1; - _cur_lexeme = lex_less; - } - break; - - case '!': - if (*(cur+1) == '=') - { - cur += 2; - _cur_lexeme = lex_not_equal; - } - else - { - _cur_lexeme = lex_none; - } - break; - - case '=': - cur += 1; - _cur_lexeme = lex_equal; - - break; - - case '+': - cur += 1; - _cur_lexeme = lex_plus; - - break; - - case '-': - cur += 1; - _cur_lexeme = lex_minus; - - break; - - case '*': - cur += 1; - _cur_lexeme = lex_multiply; - - break; - - case '|': - cur += 1; - _cur_lexeme = lex_union; - - break; - - case '$': - cur += 1; - - if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) - { - _cur_lexeme_contents.begin = cur; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; - - if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname - { - cur++; // : - - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; - } - - _cur_lexeme_contents.end = cur; - - _cur_lexeme = lex_var_ref; - } - else - { - _cur_lexeme = lex_none; - } - - break; - - case '(': - cur += 1; - _cur_lexeme = lex_open_brace; - - break; - - case ')': - cur += 1; - _cur_lexeme = lex_close_brace; - - break; - - case '[': - cur += 1; - _cur_lexeme = lex_open_square_brace; - - break; - - case ']': - cur += 1; - _cur_lexeme = lex_close_square_brace; - - break; - - case ',': - cur += 1; - _cur_lexeme = lex_comma; - - break; - - case '/': - if (*(cur+1) == '/') - { - cur += 2; - _cur_lexeme = lex_double_slash; - } - else - { - cur += 1; - _cur_lexeme = lex_slash; - } - break; - - case '.': - if (*(cur+1) == '.') - { - cur += 2; - _cur_lexeme = lex_double_dot; - } - else if (PUGI__IS_CHARTYPEX(*(cur+1), ctx_digit)) - { - _cur_lexeme_contents.begin = cur; // . - - ++cur; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; - - _cur_lexeme_contents.end = cur; - - _cur_lexeme = lex_number; - } - else - { - cur += 1; - _cur_lexeme = lex_dot; - } - break; - - case '@': - cur += 1; - _cur_lexeme = lex_axis_attribute; - - break; - - case '"': - case '\'': - { - char_t terminator = *cur; - - ++cur; - - _cur_lexeme_contents.begin = cur; - while (*cur && *cur != terminator) cur++; - _cur_lexeme_contents.end = cur; - - if (!*cur) - _cur_lexeme = lex_none; - else - { - cur += 1; - _cur_lexeme = lex_quoted_string; - } - - break; - } - - case ':': - if (*(cur+1) == ':') - { - cur += 2; - _cur_lexeme = lex_double_colon; - } - else - { - _cur_lexeme = lex_none; - } - break; - - default: - if (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) - { - _cur_lexeme_contents.begin = cur; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; - - if (*cur == '.') - { - cur++; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; - } - - _cur_lexeme_contents.end = cur; - - _cur_lexeme = lex_number; - } - else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) - { - _cur_lexeme_contents.begin = cur; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; - - if (cur[0] == ':') - { - if (cur[1] == '*') // namespace test ncname:* - { - cur += 2; // :* - } - else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname - { - cur++; // : - - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; - } - } - - _cur_lexeme_contents.end = cur; - - _cur_lexeme = lex_string; - } - else - { - _cur_lexeme = lex_none; - } - } - - _cur = cur; - } - - lexeme_t current() const - { - return _cur_lexeme; - } - - const char_t* current_pos() const - { - return _cur_lexeme_pos; - } - - const xpath_lexer_string& contents() const - { - assert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || _cur_lexeme == lex_quoted_string); - - return _cur_lexeme_contents; - } - }; - - enum ast_type_t - { - ast_unknown, - ast_op_or, // left or right - ast_op_and, // left and right - ast_op_equal, // left = right - ast_op_not_equal, // left != right - ast_op_less, // left < right - ast_op_greater, // left > right - ast_op_less_or_equal, // left <= right - ast_op_greater_or_equal, // left >= right - ast_op_add, // left + right - ast_op_subtract, // left - right - ast_op_multiply, // left * right - ast_op_divide, // left / right - ast_op_mod, // left % right - ast_op_negate, // left - right - ast_op_union, // left | right - ast_predicate, // apply predicate to set; next points to next predicate - ast_filter, // select * from left where right - ast_string_constant, // string constant - ast_number_constant, // number constant - ast_variable, // variable - ast_func_last, // last() - ast_func_position, // position() - ast_func_count, // count(left) - ast_func_id, // id(left) - ast_func_local_name_0, // local-name() - ast_func_local_name_1, // local-name(left) - ast_func_namespace_uri_0, // namespace-uri() - ast_func_namespace_uri_1, // namespace-uri(left) - ast_func_name_0, // name() - ast_func_name_1, // name(left) - ast_func_string_0, // string() - ast_func_string_1, // string(left) - ast_func_concat, // concat(left, right, siblings) - ast_func_starts_with, // starts_with(left, right) - ast_func_contains, // contains(left, right) - ast_func_substring_before, // substring-before(left, right) - ast_func_substring_after, // substring-after(left, right) - ast_func_substring_2, // substring(left, right) - ast_func_substring_3, // substring(left, right, third) - ast_func_string_length_0, // string-length() - ast_func_string_length_1, // string-length(left) - ast_func_normalize_space_0, // normalize-space() - ast_func_normalize_space_1, // normalize-space(left) - ast_func_translate, // translate(left, right, third) - ast_func_boolean, // boolean(left) - ast_func_not, // not(left) - ast_func_true, // true() - ast_func_false, // false() - ast_func_lang, // lang(left) - ast_func_number_0, // number() - ast_func_number_1, // number(left) - ast_func_sum, // sum(left) - ast_func_floor, // floor(left) - ast_func_ceiling, // ceiling(left) - ast_func_round, // round(left) - ast_step, // process set left with step - ast_step_root, // select root node - - ast_opt_translate_table, // translate(left, right, third) where right/third are constants - ast_opt_compare_attribute // @name = 'string' - }; - - enum axis_t - { - axis_ancestor, - axis_ancestor_or_self, - axis_attribute, - axis_child, - axis_descendant, - axis_descendant_or_self, - axis_following, - axis_following_sibling, - axis_namespace, - axis_parent, - axis_preceding, - axis_preceding_sibling, - axis_self - }; - - enum nodetest_t - { - nodetest_none, - nodetest_name, - nodetest_type_node, - nodetest_type_comment, - nodetest_type_pi, - nodetest_type_text, - nodetest_pi, - nodetest_all, - nodetest_all_in_namespace - }; - - enum predicate_t - { - predicate_default, - predicate_posinv, - predicate_constant, - predicate_constant_one - }; - - enum nodeset_eval_t - { - nodeset_eval_all, - nodeset_eval_any, - nodeset_eval_first - }; - - template struct axis_to_type - { - static const axis_t axis; - }; - - template const axis_t axis_to_type::axis = N; - - class xpath_ast_node - { - private: - // node type - char _type; - char _rettype; - - // for ast_step - char _axis; - - // for ast_step/ast_predicate/ast_filter - char _test; - - // tree node structure - xpath_ast_node* _left; - xpath_ast_node* _right; - xpath_ast_node* _next; - - union - { - // value for ast_string_constant - const char_t* string; - // value for ast_number_constant - double number; - // variable for ast_variable - xpath_variable* variable; - // node test for ast_step (node name/namespace/node type/pi target) - const char_t* nodetest; - // table for ast_opt_translate_table - const unsigned char* table; - } _data; - - xpath_ast_node(const xpath_ast_node&); - xpath_ast_node& operator=(const xpath_ast_node&); - - template static bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) - { - xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); - - if (lt != xpath_type_node_set && rt != xpath_type_node_set) - { - if (lt == xpath_type_boolean || rt == xpath_type_boolean) - return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); - else if (lt == xpath_type_number || rt == xpath_type_number) - return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); - else if (lt == xpath_type_string || rt == xpath_type_string) - { - xpath_allocator_capture cr(stack.result); - - xpath_string ls = lhs->eval_string(c, stack); - xpath_string rs = rhs->eval_string(c, stack); - - return comp(ls, rs); - } - } - else if (lt == xpath_type_node_set && rt == xpath_type_node_set) - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); - - for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) - for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture cri(stack.result); - - if (comp(string_value(*li, stack.result), string_value(*ri, stack.result))) - return true; - } - - return false; - } - else - { - if (lt == xpath_type_node_set) - { - swap(lhs, rhs); - swap(lt, rt); - } - - if (lt == xpath_type_boolean) - return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); - else if (lt == xpath_type_number) - { - xpath_allocator_capture cr(stack.result); - - double l = lhs->eval_number(c, stack); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); - - for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture cri(stack.result); - - if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) - return true; - } - - return false; - } - else if (lt == xpath_type_string) - { - xpath_allocator_capture cr(stack.result); - - xpath_string l = lhs->eval_string(c, stack); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); - - for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture cri(stack.result); - - if (comp(l, string_value(*ri, stack.result))) - return true; - } - - return false; - } - } - - assert(!"Wrong types"); - return false; - } - - static bool eval_once(xpath_node_set::type_t type, nodeset_eval_t eval) - { - return type == xpath_node_set::type_sorted ? eval != nodeset_eval_all : eval == nodeset_eval_any; - } - - template static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) - { - xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); - - if (lt != xpath_type_node_set && rt != xpath_type_node_set) - return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); - else if (lt == xpath_type_node_set && rt == xpath_type_node_set) - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); - - for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) - { - xpath_allocator_capture cri(stack.result); - - double l = convert_string_to_number(string_value(*li, stack.result).c_str()); - - for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture crii(stack.result); - - if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) - return true; - } - } - - return false; - } - else if (lt != xpath_type_node_set && rt == xpath_type_node_set) - { - xpath_allocator_capture cr(stack.result); - - double l = lhs->eval_number(c, stack); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); - - for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture cri(stack.result); - - if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) - return true; - } - - return false; - } - else if (lt == xpath_type_node_set && rt != xpath_type_node_set) - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); - double r = rhs->eval_number(c, stack); - - for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) - { - xpath_allocator_capture cri(stack.result); - - if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r)) - return true; - } - - return false; - } - else - { - assert(!"Wrong types"); - return false; - } - } - - static void apply_predicate_boolean(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) - { - assert(ns.size() >= first); - assert(expr->rettype() != xpath_type_number); - - size_t i = 1; - size_t size = ns.size() - first; - - xpath_node* last = ns.begin() + first; - - // remove_if... or well, sort of - for (xpath_node* it = last; it != ns.end(); ++it, ++i) - { - xpath_context c(*it, i, size); - - if (expr->eval_boolean(c, stack)) - { - *last++ = *it; - - if (once) break; - } - } - - ns.truncate(last); - } - - static void apply_predicate_number(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) - { - assert(ns.size() >= first); - assert(expr->rettype() == xpath_type_number); - - size_t i = 1; - size_t size = ns.size() - first; - - xpath_node* last = ns.begin() + first; - - // remove_if... or well, sort of - for (xpath_node* it = last; it != ns.end(); ++it, ++i) - { - xpath_context c(*it, i, size); - - if (expr->eval_number(c, stack) == i) - { - *last++ = *it; - - if (once) break; - } - } - - ns.truncate(last); - } - - static void apply_predicate_number_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) - { - assert(ns.size() >= first); - assert(expr->rettype() == xpath_type_number); - - size_t size = ns.size() - first; - - xpath_node* last = ns.begin() + first; - - xpath_context c(xpath_node(), 1, size); - - double er = expr->eval_number(c, stack); - - if (er >= 1.0 && er <= size) - { - size_t eri = static_cast(er); - - if (er == eri) - { - xpath_node r = last[eri - 1]; - - *last++ = r; - } - } - - ns.truncate(last); - } - - void apply_predicate(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, bool once) - { - if (ns.size() == first) return; - - assert(_type == ast_filter || _type == ast_predicate); - - if (_test == predicate_constant || _test == predicate_constant_one) - apply_predicate_number_const(ns, first, _right, stack); - else if (_right->rettype() == xpath_type_number) - apply_predicate_number(ns, first, _right, stack, once); - else - apply_predicate_boolean(ns, first, _right, stack, once); - } - - void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval) - { - if (ns.size() == first) return; - - bool last_once = eval_once(ns.type(), eval); - - for (xpath_ast_node* pred = _right; pred; pred = pred->_next) - pred->apply_predicate(ns, first, stack, !pred->_next && last_once); - } - - bool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc) - { - assert(a); - - const char_t* name = a->name ? a->name : PUGIXML_TEXT(""); - - switch (_test) - { - case nodetest_name: - if (strequal(name, _data.nodetest) && is_xpath_attribute(name)) - { - ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); - return true; - } - break; - - case nodetest_type_node: - case nodetest_all: - if (is_xpath_attribute(name)) - { - ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); - return true; - } - break; - - case nodetest_all_in_namespace: - if (starts_with(name, _data.nodetest) && is_xpath_attribute(name)) - { - ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); - return true; - } - break; - - default: - ; - } - - return false; - } - - bool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc) - { - assert(n); - - xml_node_type type = PUGI__NODETYPE(n); - - switch (_test) - { - case nodetest_name: - if (type == node_element && n->name && strequal(n->name, _data.nodetest)) - { - ns.push_back(xml_node(n), alloc); - return true; - } - break; - - case nodetest_type_node: - ns.push_back(xml_node(n), alloc); - return true; - - case nodetest_type_comment: - if (type == node_comment) - { - ns.push_back(xml_node(n), alloc); - return true; - } - break; - - case nodetest_type_text: - if (type == node_pcdata || type == node_cdata) - { - ns.push_back(xml_node(n), alloc); - return true; - } - break; - - case nodetest_type_pi: - if (type == node_pi) - { - ns.push_back(xml_node(n), alloc); - return true; - } - break; - - case nodetest_pi: - if (type == node_pi && n->name && strequal(n->name, _data.nodetest)) - { - ns.push_back(xml_node(n), alloc); - return true; - } - break; - - case nodetest_all: - if (type == node_element) - { - ns.push_back(xml_node(n), alloc); - return true; - } - break; - - case nodetest_all_in_namespace: - if (type == node_element && n->name && starts_with(n->name, _data.nodetest)) - { - ns.push_back(xml_node(n), alloc); - return true; - } - break; - - default: - assert(!"Unknown axis"); - } - - return false; - } - - template void step_fill(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc, bool once, T) - { - const axis_t axis = T::axis; - - switch (axis) - { - case axis_attribute: - { - for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute) - if (step_push(ns, a, n, alloc) & once) - return; - - break; - } - - case axis_child: - { - for (xml_node_struct* c = n->first_child; c; c = c->next_sibling) - if (step_push(ns, c, alloc) & once) - return; - - break; - } - - case axis_descendant: - case axis_descendant_or_self: - { - if (axis == axis_descendant_or_self) - if (step_push(ns, n, alloc) & once) - return; - - xml_node_struct* cur = n->first_child; - - while (cur) - { - if (step_push(ns, cur, alloc) & once) - return; - - if (cur->first_child) - cur = cur->first_child; - else - { - while (!cur->next_sibling) - { - cur = cur->parent; - - if (cur == n) return; - } - - cur = cur->next_sibling; - } - } - - break; - } - - case axis_following_sibling: - { - for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling) - if (step_push(ns, c, alloc) & once) - return; - - break; - } - - case axis_preceding_sibling: - { - for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c) - if (step_push(ns, c, alloc) & once) - return; - - break; - } - - case axis_following: - { - xml_node_struct* cur = n; - - // exit from this node so that we don't include descendants - while (!cur->next_sibling) - { - cur = cur->parent; - - if (!cur) return; - } - - cur = cur->next_sibling; - - while (cur) - { - if (step_push(ns, cur, alloc) & once) - return; - - if (cur->first_child) - cur = cur->first_child; - else - { - while (!cur->next_sibling) - { - cur = cur->parent; - - if (!cur) return; - } - - cur = cur->next_sibling; - } - } - - break; - } - - case axis_preceding: - { - xml_node_struct* cur = n; - - // exit from this node so that we don't include descendants - while (!cur->prev_sibling_c->next_sibling) - { - cur = cur->parent; - - if (!cur) return; - } - - cur = cur->prev_sibling_c; - - while (cur) - { - if (cur->first_child) - cur = cur->first_child->prev_sibling_c; - else - { - // leaf node, can't be ancestor - if (step_push(ns, cur, alloc) & once) - return; - - while (!cur->prev_sibling_c->next_sibling) - { - cur = cur->parent; - - if (!cur) return; - - if (!node_is_ancestor(cur, n)) - if (step_push(ns, cur, alloc) & once) - return; - } - - cur = cur->prev_sibling_c; - } - } - - break; - } - - case axis_ancestor: - case axis_ancestor_or_self: - { - if (axis == axis_ancestor_or_self) - if (step_push(ns, n, alloc) & once) - return; - - xml_node_struct* cur = n->parent; - - while (cur) - { - if (step_push(ns, cur, alloc) & once) - return; - - cur = cur->parent; - } - - break; - } - - case axis_self: - { - step_push(ns, n, alloc); - - break; - } - - case axis_parent: - { - if (n->parent) - step_push(ns, n->parent, alloc); - - break; - } - - default: - assert(!"Unimplemented axis"); - } - } - - template void step_fill(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* p, xpath_allocator* alloc, bool once, T v) - { - const axis_t axis = T::axis; - - switch (axis) - { - case axis_ancestor: - case axis_ancestor_or_self: - { - if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test - if (step_push(ns, a, p, alloc) & once) - return; - - xml_node_struct* cur = p; - - while (cur) - { - if (step_push(ns, cur, alloc) & once) - return; - - cur = cur->parent; - } - - break; - } - - case axis_descendant_or_self: - case axis_self: - { - if (_test == nodetest_type_node) // reject attributes based on principal node type test - step_push(ns, a, p, alloc); - - break; - } - - case axis_following: - { - xml_node_struct* cur = p; - - while (cur) - { - if (cur->first_child) - cur = cur->first_child; - else - { - while (!cur->next_sibling) - { - cur = cur->parent; - - if (!cur) return; - } - - cur = cur->next_sibling; - } - - if (step_push(ns, cur, alloc) & once) - return; - } - - break; - } - - case axis_parent: - { - step_push(ns, p, alloc); - - break; - } - - case axis_preceding: - { - // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding - step_fill(ns, p, alloc, once, v); - break; - } - - default: - assert(!"Unimplemented axis"); - } - } - - template void step_fill(xpath_node_set_raw& ns, const xpath_node& xn, xpath_allocator* alloc, bool once, T v) - { - const axis_t axis = T::axis; - const bool axis_has_attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self); - - if (xn.node()) - step_fill(ns, xn.node().internal_object(), alloc, once, v); - else if (axis_has_attributes && xn.attribute() && xn.parent()) - step_fill(ns, xn.attribute().internal_object(), xn.parent().internal_object(), alloc, once, v); - } - - template xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v) - { - const axis_t axis = T::axis; - const bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling); - const xpath_node_set::type_t axis_type = axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; - - bool once = - (axis == axis_attribute && _test == nodetest_name) || - (!_right && eval_once(axis_type, eval)) || - (_right && !_right->_next && _right->_test == predicate_constant_one); - - xpath_node_set_raw ns; - ns.set_type(axis_type); - - if (_left) - { - xpath_node_set_raw s = _left->eval_node_set(c, stack, nodeset_eval_all); - - // self axis preserves the original order - if (axis == axis_self) ns.set_type(s.type()); - - for (const xpath_node* it = s.begin(); it != s.end(); ++it) - { - size_t size = ns.size(); - - // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes - if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted); - - step_fill(ns, *it, stack.result, once, v); - if (_right) apply_predicates(ns, size, stack, eval); - } - } - else - { - step_fill(ns, c.n, stack.result, once, v); - if (_right) apply_predicates(ns, 0, stack, eval); - } - - // child, attribute and self axes always generate unique set of nodes - // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice - if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted) - ns.remove_duplicates(); - - return ns; - } - - public: - xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value): - _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) - { - assert(type == ast_string_constant); - _data.string = value; - } - - xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value): - _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) - { - assert(type == ast_number_constant); - _data.number = value; - } - - xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value): - _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) - { - assert(type == ast_variable); - _data.variable = value; - } - - xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0): - _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0) - { - } - - xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents): - _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(static_cast(axis)), _test(static_cast(test)), _left(left), _right(0), _next(0) - { - assert(type == ast_step); - _data.nodetest = contents; - } - - xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test): - _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast(test)), _left(left), _right(right), _next(0) - { - assert(type == ast_filter || type == ast_predicate); - } - - void set_next(xpath_ast_node* value) - { - _next = value; - } - - void set_right(xpath_ast_node* value) - { - _right = value; - } - - bool eval_boolean(const xpath_context& c, const xpath_stack& stack) - { - switch (_type) - { - case ast_op_or: - return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack); - - case ast_op_and: - return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack); - - case ast_op_equal: - return compare_eq(_left, _right, c, stack, equal_to()); - - case ast_op_not_equal: - return compare_eq(_left, _right, c, stack, not_equal_to()); - - case ast_op_less: - return compare_rel(_left, _right, c, stack, less()); - - case ast_op_greater: - return compare_rel(_right, _left, c, stack, less()); - - case ast_op_less_or_equal: - return compare_rel(_left, _right, c, stack, less_equal()); - - case ast_op_greater_or_equal: - return compare_rel(_right, _left, c, stack, less_equal()); - - case ast_func_starts_with: - { - xpath_allocator_capture cr(stack.result); - - xpath_string lr = _left->eval_string(c, stack); - xpath_string rr = _right->eval_string(c, stack); - - return starts_with(lr.c_str(), rr.c_str()); - } - - case ast_func_contains: - { - xpath_allocator_capture cr(stack.result); - - xpath_string lr = _left->eval_string(c, stack); - xpath_string rr = _right->eval_string(c, stack); - - return find_substring(lr.c_str(), rr.c_str()) != 0; - } - - case ast_func_boolean: - return _left->eval_boolean(c, stack); - - case ast_func_not: - return !_left->eval_boolean(c, stack); - - case ast_func_true: - return true; - - case ast_func_false: - return false; - - case ast_func_lang: - { - if (c.n.attribute()) return false; - - xpath_allocator_capture cr(stack.result); - - xpath_string lang = _left->eval_string(c, stack); - - for (xml_node n = c.n.node(); n; n = n.parent()) - { - xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang")); - - if (a) - { - const char_t* value = a.value(); - - // strnicmp / strncasecmp is not portable - for (const char_t* lit = lang.c_str(); *lit; ++lit) - { - if (tolower_ascii(*lit) != tolower_ascii(*value)) return false; - ++value; - } - - return *value == 0 || *value == '-'; - } - } - - return false; - } - - case ast_opt_compare_attribute: - { - const char_t* value = (_right->_type == ast_string_constant) ? _right->_data.string : _right->_data.variable->get_string(); - - xml_attribute attr = c.n.node().attribute(_left->_data.nodetest); - - return attr && strequal(attr.value(), value) && is_xpath_attribute(attr.name()); - } - - case ast_variable: - { - assert(_rettype == _data.variable->type()); - - if (_rettype == xpath_type_boolean) - return _data.variable->get_boolean(); - - // fallthrough to type conversion - } - - default: - { - switch (_rettype) - { - case xpath_type_number: - return convert_number_to_boolean(eval_number(c, stack)); - - case xpath_type_string: - { - xpath_allocator_capture cr(stack.result); - - return !eval_string(c, stack).empty(); - } - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.result); - - return !eval_node_set(c, stack, nodeset_eval_any).empty(); - } - - default: - assert(!"Wrong expression for return type boolean"); - return false; - } - } - } - } - - double eval_number(const xpath_context& c, const xpath_stack& stack) - { - switch (_type) - { - case ast_op_add: - return _left->eval_number(c, stack) + _right->eval_number(c, stack); - - case ast_op_subtract: - return _left->eval_number(c, stack) - _right->eval_number(c, stack); - - case ast_op_multiply: - return _left->eval_number(c, stack) * _right->eval_number(c, stack); - - case ast_op_divide: - return _left->eval_number(c, stack) / _right->eval_number(c, stack); - - case ast_op_mod: - return fmod(_left->eval_number(c, stack), _right->eval_number(c, stack)); - - case ast_op_negate: - return -_left->eval_number(c, stack); - - case ast_number_constant: - return _data.number; - - case ast_func_last: - return static_cast(c.size); - - case ast_func_position: - return static_cast(c.position); - - case ast_func_count: - { - xpath_allocator_capture cr(stack.result); - - return static_cast(_left->eval_node_set(c, stack, nodeset_eval_all).size()); - } - - case ast_func_string_length_0: - { - xpath_allocator_capture cr(stack.result); - - return static_cast(string_value(c.n, stack.result).length()); - } - - case ast_func_string_length_1: - { - xpath_allocator_capture cr(stack.result); - - return static_cast(_left->eval_string(c, stack).length()); - } - - case ast_func_number_0: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(string_value(c.n, stack.result).c_str()); - } - - case ast_func_number_1: - return _left->eval_number(c, stack); - - case ast_func_sum: - { - xpath_allocator_capture cr(stack.result); - - double r = 0; - - xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all); - - for (const xpath_node* it = ns.begin(); it != ns.end(); ++it) - { - xpath_allocator_capture cri(stack.result); - - r += convert_string_to_number(string_value(*it, stack.result).c_str()); - } - - return r; - } - - case ast_func_floor: - { - double r = _left->eval_number(c, stack); - - return r == r ? floor(r) : r; - } - - case ast_func_ceiling: - { - double r = _left->eval_number(c, stack); - - return r == r ? ceil(r) : r; - } - - case ast_func_round: - return round_nearest_nzero(_left->eval_number(c, stack)); - - case ast_variable: - { - assert(_rettype == _data.variable->type()); - - if (_rettype == xpath_type_number) - return _data.variable->get_number(); - - // fallthrough to type conversion - } - - default: - { - switch (_rettype) - { - case xpath_type_boolean: - return eval_boolean(c, stack) ? 1 : 0; - - case xpath_type_string: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(eval_string(c, stack).c_str()); - } - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(eval_string(c, stack).c_str()); - } - - default: - assert(!"Wrong expression for return type number"); - return 0; - } - - } - } - } - - xpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack) - { - assert(_type == ast_func_concat); - - xpath_allocator_capture ct(stack.temp); - - // count the string number - size_t count = 1; - for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++; - - // gather all strings - xpath_string static_buffer[4]; - xpath_string* buffer = static_buffer; - - // allocate on-heap for large concats - if (count > sizeof(static_buffer) / sizeof(static_buffer[0])) - { - buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string))); - assert(buffer); - } - - // evaluate all strings to temporary stack - xpath_stack swapped_stack = {stack.temp, stack.result}; - - buffer[0] = _left->eval_string(c, swapped_stack); - - size_t pos = 1; - for (xpath_ast_node* n = _right; n; n = n->_next, ++pos) buffer[pos] = n->eval_string(c, swapped_stack); - assert(pos == count); - - // get total length - size_t length = 0; - for (size_t i = 0; i < count; ++i) length += buffer[i].length(); - - // create final string - char_t* result = static_cast(stack.result->allocate((length + 1) * sizeof(char_t))); - assert(result); - - char_t* ri = result; - - for (size_t j = 0; j < count; ++j) - for (const char_t* bi = buffer[j].c_str(); *bi; ++bi) - *ri++ = *bi; - - *ri = 0; - - return xpath_string::from_heap_preallocated(result, ri); - } - - xpath_string eval_string(const xpath_context& c, const xpath_stack& stack) - { - switch (_type) - { - case ast_string_constant: - return xpath_string::from_const(_data.string); - - case ast_func_local_name_0: - { - xpath_node na = c.n; - - return xpath_string::from_const(local_name(na)); - } - - case ast_func_local_name_1: - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); - xpath_node na = ns.first(); - - return xpath_string::from_const(local_name(na)); - } - - case ast_func_name_0: - { - xpath_node na = c.n; - - return xpath_string::from_const(qualified_name(na)); - } - - case ast_func_name_1: - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); - xpath_node na = ns.first(); - - return xpath_string::from_const(qualified_name(na)); - } - - case ast_func_namespace_uri_0: - { - xpath_node na = c.n; - - return xpath_string::from_const(namespace_uri(na)); - } - - case ast_func_namespace_uri_1: - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); - xpath_node na = ns.first(); - - return xpath_string::from_const(namespace_uri(na)); - } - - case ast_func_string_0: - return string_value(c.n, stack.result); - - case ast_func_string_1: - return _left->eval_string(c, stack); - - case ast_func_concat: - return eval_string_concat(c, stack); - - case ast_func_substring_before: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_string s = _left->eval_string(c, swapped_stack); - xpath_string p = _right->eval_string(c, swapped_stack); - - const char_t* pos = find_substring(s.c_str(), p.c_str()); - - return pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string(); - } - - case ast_func_substring_after: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_string s = _left->eval_string(c, swapped_stack); - xpath_string p = _right->eval_string(c, swapped_stack); - - const char_t* pos = find_substring(s.c_str(), p.c_str()); - if (!pos) return xpath_string(); - - const char_t* rbegin = pos + p.length(); - const char_t* rend = s.c_str() + s.length(); - - return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); - } - - case ast_func_substring_2: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_string s = _left->eval_string(c, swapped_stack); - size_t s_length = s.length(); - - double first = round_nearest(_right->eval_number(c, stack)); - - if (is_nan(first)) return xpath_string(); // NaN - else if (first >= s_length + 1) return xpath_string(); - - size_t pos = first < 1 ? 1 : static_cast(first); - assert(1 <= pos && pos <= s_length + 1); - - const char_t* rbegin = s.c_str() + (pos - 1); - const char_t* rend = s.c_str() + s.length(); - - return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); - } - - case ast_func_substring_3: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_string s = _left->eval_string(c, swapped_stack); - size_t s_length = s.length(); - - double first = round_nearest(_right->eval_number(c, stack)); - double last = first + round_nearest(_right->_next->eval_number(c, stack)); - - if (is_nan(first) || is_nan(last)) return xpath_string(); - else if (first >= s_length + 1) return xpath_string(); - else if (first >= last) return xpath_string(); - else if (last < 1) return xpath_string(); - - size_t pos = first < 1 ? 1 : static_cast(first); - size_t end = last >= s_length + 1 ? s_length + 1 : static_cast(last); - - assert(1 <= pos && pos <= end && end <= s_length + 1); - const char_t* rbegin = s.c_str() + (pos - 1); - const char_t* rend = s.c_str() + (end - 1); - - return (end == s_length + 1 && !s.uses_heap()) ? xpath_string::from_const(rbegin) : xpath_string::from_heap(rbegin, rend, stack.result); - } - - case ast_func_normalize_space_0: - { - xpath_string s = string_value(c.n, stack.result); - - char_t* begin = s.data(stack.result); - char_t* end = normalize_space(begin); - - return xpath_string::from_heap_preallocated(begin, end); - } - - case ast_func_normalize_space_1: - { - xpath_string s = _left->eval_string(c, stack); - - char_t* begin = s.data(stack.result); - char_t* end = normalize_space(begin); - - return xpath_string::from_heap_preallocated(begin, end); - } - - case ast_func_translate: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_string s = _left->eval_string(c, stack); - xpath_string from = _right->eval_string(c, swapped_stack); - xpath_string to = _right->_next->eval_string(c, swapped_stack); - - char_t* begin = s.data(stack.result); - char_t* end = translate(begin, from.c_str(), to.c_str(), to.length()); - - return xpath_string::from_heap_preallocated(begin, end); - } - - case ast_opt_translate_table: - { - xpath_string s = _left->eval_string(c, stack); - - char_t* begin = s.data(stack.result); - char_t* end = translate_table(begin, _data.table); - - return xpath_string::from_heap_preallocated(begin, end); - } - - case ast_variable: - { - assert(_rettype == _data.variable->type()); - - if (_rettype == xpath_type_string) - return xpath_string::from_const(_data.variable->get_string()); - - // fallthrough to type conversion - } - - default: - { - switch (_rettype) - { - case xpath_type_boolean: - return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); - - case xpath_type_number: - return convert_number_to_string(eval_number(c, stack), stack.result); - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); - return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); - } - - default: - assert(!"Wrong expression for return type string"); - return xpath_string(); - } - } - } - } - - xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval) - { - switch (_type) - { - case ast_op_union: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval); - xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval); - - // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother - rs.set_type(xpath_node_set::type_unsorted); - - rs.append(ls.begin(), ls.end(), stack.result); - rs.remove_duplicates(); - - return rs; - } - - case ast_filter: - { - xpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all); - - // either expression is a number or it contains position() call; sort by document order - if (_test != predicate_posinv) set.sort_do(); - - bool once = eval_once(set.type(), eval); - - apply_predicate(set, 0, stack, once); - - return set; - } - - case ast_func_id: - return xpath_node_set_raw(); - - case ast_step: - { - switch (_axis) - { - case axis_ancestor: - return step_do(c, stack, eval, axis_to_type()); - - case axis_ancestor_or_self: - return step_do(c, stack, eval, axis_to_type()); - - case axis_attribute: - return step_do(c, stack, eval, axis_to_type()); - - case axis_child: - return step_do(c, stack, eval, axis_to_type()); - - case axis_descendant: - return step_do(c, stack, eval, axis_to_type()); - - case axis_descendant_or_self: - return step_do(c, stack, eval, axis_to_type()); - - case axis_following: - return step_do(c, stack, eval, axis_to_type()); - - case axis_following_sibling: - return step_do(c, stack, eval, axis_to_type()); - - case axis_namespace: - // namespaced axis is not supported - return xpath_node_set_raw(); - - case axis_parent: - return step_do(c, stack, eval, axis_to_type()); - - case axis_preceding: - return step_do(c, stack, eval, axis_to_type()); - - case axis_preceding_sibling: - return step_do(c, stack, eval, axis_to_type()); - - case axis_self: - return step_do(c, stack, eval, axis_to_type()); - - default: - assert(!"Unknown axis"); - return xpath_node_set_raw(); - } - } - - case ast_step_root: - { - assert(!_right); // root step can't have any predicates - - xpath_node_set_raw ns; - - ns.set_type(xpath_node_set::type_sorted); - - if (c.n.node()) ns.push_back(c.n.node().root(), stack.result); - else if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result); - - return ns; - } - - case ast_variable: - { - assert(_rettype == _data.variable->type()); - - if (_rettype == xpath_type_node_set) - { - const xpath_node_set& s = _data.variable->get_node_set(); - - xpath_node_set_raw ns; - - ns.set_type(s.type()); - ns.append(s.begin(), s.end(), stack.result); - - return ns; - } - - // fallthrough to type conversion - } - - default: - assert(!"Wrong expression for return type node set"); - return xpath_node_set_raw(); - } - } - - void optimize(xpath_allocator* alloc) - { - if (_left) _left->optimize(alloc); - if (_right) _right->optimize(alloc); - if (_next) _next->optimize(alloc); - - // Rewrite [position()=expr] with [expr] - // Note that this step has to go before classification to recognize [position()=1] - if ((_type == ast_filter || _type == ast_predicate) && - _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number) - { - _right = _right->_right; - } - - // Classify filter/predicate ops to perform various optimizations during evaluation - if (_type == ast_filter || _type == ast_predicate) - { - assert(_test == predicate_default); - - if (_right->_type == ast_number_constant && _right->_data.number == 1.0) - _test = predicate_constant_one; - else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last)) - _test = predicate_constant; - else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr()) - _test = predicate_posinv; - } - - // Rewrite descendant-or-self::node()/child::foo with descendant::foo - // The former is a full form of //foo, the latter is much faster since it executes the node test immediately - // Do a similar kind of rewrite for self/descendant/descendant-or-self axes - // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1]) - if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left && - _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && - is_posinv_step()) - { - if (_axis == axis_child || _axis == axis_descendant) - _axis = axis_descendant; - else - _axis = axis_descendant_or_self; - - _left = _left->_left; - } - - // Use optimized lookup table implementation for translate() with constant arguments - if (_type == ast_func_translate && _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) - { - unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string); - - if (table) - { - _type = ast_opt_translate_table; - _data.table = table; - } - } - - // Use optimized path for @attr = 'value' or @attr = $value - if (_type == ast_op_equal && - _left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right && - (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string))) - { - _type = ast_opt_compare_attribute; - } - } - - bool is_posinv_expr() const - { - switch (_type) - { - case ast_func_position: - case ast_func_last: - return false; - - case ast_string_constant: - case ast_number_constant: - case ast_variable: - return true; - - case ast_step: - case ast_step_root: - return true; - - case ast_predicate: - case ast_filter: - return true; - - default: - if (_left && !_left->is_posinv_expr()) return false; - - for (xpath_ast_node* n = _right; n; n = n->_next) - if (!n->is_posinv_expr()) return false; - - return true; - } - } - - bool is_posinv_step() const - { - assert(_type == ast_step); - - for (xpath_ast_node* n = _right; n; n = n->_next) - { - assert(n->_type == ast_predicate); - - if (n->_test != predicate_posinv) - return false; - } - - return true; - } - - xpath_value_type rettype() const - { - return static_cast(_rettype); - } - }; - - struct xpath_parser - { - xpath_allocator* _alloc; - xpath_lexer _lexer; - - const char_t* _query; - xpath_variable_set* _variables; - - xpath_parse_result* _result; - - char_t _scratch[32]; - - #ifdef PUGIXML_NO_EXCEPTIONS - jmp_buf _error_handler; - #endif - - void throw_error(const char* message) - { - _result->error = message; - _result->offset = _lexer.current_pos() - _query; - - #ifdef PUGIXML_NO_EXCEPTIONS - longjmp(_error_handler, 1); - #else - throw xpath_exception(*_result); - #endif - } - - void throw_error_oom() - { - #ifdef PUGIXML_NO_EXCEPTIONS - throw_error("Out of memory"); - #else - throw std::bad_alloc(); - #endif - } - - void* alloc_node() - { - void* result = _alloc->allocate_nothrow(sizeof(xpath_ast_node)); - - if (!result) throw_error_oom(); - - return result; - } - - const char_t* alloc_string(const xpath_lexer_string& value) - { - if (value.begin) - { - size_t length = static_cast(value.end - value.begin); - - char_t* c = static_cast(_alloc->allocate_nothrow((length + 1) * sizeof(char_t))); - if (!c) throw_error_oom(); - assert(c); // workaround for clang static analysis - - memcpy(c, value.begin, length * sizeof(char_t)); - c[length] = 0; - - return c; - } - else return 0; - } - - xpath_ast_node* parse_function_helper(ast_type_t type0, ast_type_t type1, size_t argc, xpath_ast_node* args[2]) - { - assert(argc <= 1); - - if (argc == 1 && args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); - - return new (alloc_node()) xpath_ast_node(argc == 0 ? type0 : type1, xpath_type_string, args[0]); - } - - xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2]) - { - switch (name.begin[0]) - { - case 'b': - if (name == PUGIXML_TEXT("boolean") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_boolean, xpath_type_boolean, args[0]); - - break; - - case 'c': - if (name == PUGIXML_TEXT("count") && argc == 1) - { - if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); - return new (alloc_node()) xpath_ast_node(ast_func_count, xpath_type_number, args[0]); - } - else if (name == PUGIXML_TEXT("contains") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_boolean, args[0], args[1]); - else if (name == PUGIXML_TEXT("concat") && argc >= 2) - return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("ceiling") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_ceiling, xpath_type_number, args[0]); - - break; - - case 'f': - if (name == PUGIXML_TEXT("false") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_false, xpath_type_boolean); - else if (name == PUGIXML_TEXT("floor") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_floor, xpath_type_number, args[0]); - - break; - - case 'i': - if (name == PUGIXML_TEXT("id") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_id, xpath_type_node_set, args[0]); - - break; - - case 'l': - if (name == PUGIXML_TEXT("last") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_last, xpath_type_number); - else if (name == PUGIXML_TEXT("lang") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_lang, xpath_type_boolean, args[0]); - else if (name == PUGIXML_TEXT("local-name") && argc <= 1) - return parse_function_helper(ast_func_local_name_0, ast_func_local_name_1, argc, args); - - break; - - case 'n': - if (name == PUGIXML_TEXT("name") && argc <= 1) - return parse_function_helper(ast_func_name_0, ast_func_name_1, argc, args); - else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) - return parse_function_helper(ast_func_namespace_uri_0, ast_func_namespace_uri_1, argc, args); - else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) - return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("not") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_not, xpath_type_boolean, args[0]); - else if (name == PUGIXML_TEXT("number") && argc <= 1) - return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]); - - break; - - case 'p': - if (name == PUGIXML_TEXT("position") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_position, xpath_type_number); - - break; - - case 'r': - if (name == PUGIXML_TEXT("round") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_round, xpath_type_number, args[0]); - - break; - - case 's': - if (name == PUGIXML_TEXT("string") && argc <= 1) - return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]); - else if (name == PUGIXML_TEXT("string-length") && argc <= 1) - return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]); - else if (name == PUGIXML_TEXT("starts-with") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); - else if (name == PUGIXML_TEXT("substring-before") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("substring-after") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) - return new (alloc_node()) xpath_ast_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("sum") && argc == 1) - { - if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); - return new (alloc_node()) xpath_ast_node(ast_func_sum, xpath_type_number, args[0]); - } - - break; - - case 't': - if (name == PUGIXML_TEXT("translate") && argc == 3) - return new (alloc_node()) xpath_ast_node(ast_func_translate, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("true") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_true, xpath_type_boolean); - - break; - - default: - break; - } - - throw_error("Unrecognized function or wrong parameter count"); - - return 0; - } - - axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified) - { - specified = true; - - switch (name.begin[0]) - { - case 'a': - if (name == PUGIXML_TEXT("ancestor")) - return axis_ancestor; - else if (name == PUGIXML_TEXT("ancestor-or-self")) - return axis_ancestor_or_self; - else if (name == PUGIXML_TEXT("attribute")) - return axis_attribute; - - break; - - case 'c': - if (name == PUGIXML_TEXT("child")) - return axis_child; - - break; - - case 'd': - if (name == PUGIXML_TEXT("descendant")) - return axis_descendant; - else if (name == PUGIXML_TEXT("descendant-or-self")) - return axis_descendant_or_self; - - break; - - case 'f': - if (name == PUGIXML_TEXT("following")) - return axis_following; - else if (name == PUGIXML_TEXT("following-sibling")) - return axis_following_sibling; - - break; - - case 'n': - if (name == PUGIXML_TEXT("namespace")) - return axis_namespace; - - break; - - case 'p': - if (name == PUGIXML_TEXT("parent")) - return axis_parent; - else if (name == PUGIXML_TEXT("preceding")) - return axis_preceding; - else if (name == PUGIXML_TEXT("preceding-sibling")) - return axis_preceding_sibling; - - break; - - case 's': - if (name == PUGIXML_TEXT("self")) - return axis_self; - - break; - - default: - break; - } - - specified = false; - return axis_child; - } - - nodetest_t parse_node_test_type(const xpath_lexer_string& name) - { - switch (name.begin[0]) - { - case 'c': - if (name == PUGIXML_TEXT("comment")) - return nodetest_type_comment; - - break; - - case 'n': - if (name == PUGIXML_TEXT("node")) - return nodetest_type_node; - - break; - - case 'p': - if (name == PUGIXML_TEXT("processing-instruction")) - return nodetest_type_pi; - - break; - - case 't': - if (name == PUGIXML_TEXT("text")) - return nodetest_type_text; - - break; - - default: - break; - } - - return nodetest_none; - } - - // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall - xpath_ast_node* parse_primary_expression() - { - switch (_lexer.current()) - { - case lex_var_ref: - { - xpath_lexer_string name = _lexer.contents(); - - if (!_variables) - throw_error("Unknown variable: variable set is not provided"); - - xpath_variable* var = get_variable_scratch(_scratch, _variables, name.begin, name.end); - - if (!var) - throw_error("Unknown variable: variable set does not contain the given name"); - - _lexer.next(); - - return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var); - } - - case lex_open_brace: - { - _lexer.next(); - - xpath_ast_node* n = parse_expression(); - - if (_lexer.current() != lex_close_brace) - throw_error("Unmatched braces"); - - _lexer.next(); - - return n; - } - - case lex_quoted_string: - { - const char_t* value = alloc_string(_lexer.contents()); - - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_string_constant, xpath_type_string, value); - _lexer.next(); - - return n; - } - - case lex_number: - { - double value = 0; - - if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value)) - throw_error_oom(); - - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_number_constant, xpath_type_number, value); - _lexer.next(); - - return n; - } - - case lex_string: - { - xpath_ast_node* args[2] = {0}; - size_t argc = 0; - - xpath_lexer_string function = _lexer.contents(); - _lexer.next(); - - xpath_ast_node* last_arg = 0; - - if (_lexer.current() != lex_open_brace) - throw_error("Unrecognized function call"); - _lexer.next(); - - if (_lexer.current() != lex_close_brace) - args[argc++] = parse_expression(); - - while (_lexer.current() != lex_close_brace) - { - if (_lexer.current() != lex_comma) - throw_error("No comma between function arguments"); - _lexer.next(); - - xpath_ast_node* n = parse_expression(); - - if (argc < 2) args[argc] = n; - else last_arg->set_next(n); - - argc++; - last_arg = n; - } - - _lexer.next(); - - return parse_function(function, argc, args); - } - - default: - throw_error("Unrecognizable primary expression"); - - return 0; - } - } - - // FilterExpr ::= PrimaryExpr | FilterExpr Predicate - // Predicate ::= '[' PredicateExpr ']' - // PredicateExpr ::= Expr - xpath_ast_node* parse_filter_expression() - { - xpath_ast_node* n = parse_primary_expression(); - - while (_lexer.current() == lex_open_square_brace) - { - _lexer.next(); - - xpath_ast_node* expr = parse_expression(); - - if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set"); - - n = new (alloc_node()) xpath_ast_node(ast_filter, n, expr, predicate_default); - - if (_lexer.current() != lex_close_square_brace) - throw_error("Unmatched square brace"); - - _lexer.next(); - } - - return n; - } - - // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep - // AxisSpecifier ::= AxisName '::' | '@'? - // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' - // NameTest ::= '*' | NCName ':' '*' | QName - // AbbreviatedStep ::= '.' | '..' - xpath_ast_node* parse_step(xpath_ast_node* set) - { - if (set && set->rettype() != xpath_type_node_set) - throw_error("Step has to be applied to node set"); - - bool axis_specified = false; - axis_t axis = axis_child; // implied child axis - - if (_lexer.current() == lex_axis_attribute) - { - axis = axis_attribute; - axis_specified = true; - - _lexer.next(); - } - else if (_lexer.current() == lex_dot) - { - _lexer.next(); - - return new (alloc_node()) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0); - } - else if (_lexer.current() == lex_double_dot) - { - _lexer.next(); - - return new (alloc_node()) xpath_ast_node(ast_step, set, axis_parent, nodetest_type_node, 0); - } - - nodetest_t nt_type = nodetest_none; - xpath_lexer_string nt_name; - - if (_lexer.current() == lex_string) - { - // node name test - nt_name = _lexer.contents(); - _lexer.next(); - - // was it an axis name? - if (_lexer.current() == lex_double_colon) - { - // parse axis name - if (axis_specified) throw_error("Two axis specifiers in one step"); - - axis = parse_axis_name(nt_name, axis_specified); - - if (!axis_specified) throw_error("Unknown axis"); - - // read actual node test - _lexer.next(); - - if (_lexer.current() == lex_multiply) - { - nt_type = nodetest_all; - nt_name = xpath_lexer_string(); - _lexer.next(); - } - else if (_lexer.current() == lex_string) - { - nt_name = _lexer.contents(); - _lexer.next(); - } - else throw_error("Unrecognized node test"); - } - - if (nt_type == nodetest_none) - { - // node type test or processing-instruction - if (_lexer.current() == lex_open_brace) - { - _lexer.next(); - - if (_lexer.current() == lex_close_brace) - { - _lexer.next(); - - nt_type = parse_node_test_type(nt_name); - - if (nt_type == nodetest_none) throw_error("Unrecognized node type"); - - nt_name = xpath_lexer_string(); - } - else if (nt_name == PUGIXML_TEXT("processing-instruction")) - { - if (_lexer.current() != lex_quoted_string) - throw_error("Only literals are allowed as arguments to processing-instruction()"); - - nt_type = nodetest_pi; - nt_name = _lexer.contents(); - _lexer.next(); - - if (_lexer.current() != lex_close_brace) - throw_error("Unmatched brace near processing-instruction()"); - _lexer.next(); - } - else - throw_error("Unmatched brace near node type test"); - - } - // QName or NCName:* - else - { - if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:* - { - nt_name.end--; // erase * - - nt_type = nodetest_all_in_namespace; - } - else nt_type = nodetest_name; - } - } - } - else if (_lexer.current() == lex_multiply) - { - nt_type = nodetest_all; - _lexer.next(); - } - else throw_error("Unrecognized node test"); - - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, alloc_string(nt_name)); - - xpath_ast_node* last = 0; - - while (_lexer.current() == lex_open_square_brace) - { - _lexer.next(); - - xpath_ast_node* expr = parse_expression(); - - xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, 0, expr, predicate_default); - - if (_lexer.current() != lex_close_square_brace) - throw_error("Unmatched square brace"); - _lexer.next(); - - if (last) last->set_next(pred); - else n->set_right(pred); - - last = pred; - } - - return n; - } - - // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step - xpath_ast_node* parse_relative_location_path(xpath_ast_node* set) - { - xpath_ast_node* n = parse_step(set); - - while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) - { - lexeme_t l = _lexer.current(); - _lexer.next(); - - if (l == lex_double_slash) - n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); - - n = parse_step(n); - } - - return n; - } - - // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath - // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath - xpath_ast_node* parse_location_path() - { - if (_lexer.current() == lex_slash) - { - _lexer.next(); - - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); - - // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path - lexeme_t l = _lexer.current(); - - if (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply) - return parse_relative_location_path(n); - else - return n; - } - else if (_lexer.current() == lex_double_slash) - { - _lexer.next(); - - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); - n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); - - return parse_relative_location_path(n); - } - - // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1 - return parse_relative_location_path(0); - } - - // PathExpr ::= LocationPath - // | FilterExpr - // | FilterExpr '/' RelativeLocationPath - // | FilterExpr '//' RelativeLocationPath - // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr - // UnaryExpr ::= UnionExpr | '-' UnaryExpr - xpath_ast_node* parse_path_or_unary_expression() - { - // Clarification. - // PathExpr begins with either LocationPath or FilterExpr. - // FilterExpr begins with PrimaryExpr - // PrimaryExpr begins with '$' in case of it being a variable reference, - // '(' in case of it being an expression, string literal, number constant or - // function call. - - if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || - _lexer.current() == lex_quoted_string || _lexer.current() == lex_number || - _lexer.current() == lex_string) - { - if (_lexer.current() == lex_string) - { - // This is either a function call, or not - if not, we shall proceed with location path - const char_t* state = _lexer.state(); - - while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state; - - if (*state != '(') return parse_location_path(); - - // This looks like a function call; however this still can be a node-test. Check it. - if (parse_node_test_type(_lexer.contents()) != nodetest_none) return parse_location_path(); - } - - xpath_ast_node* n = parse_filter_expression(); - - if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) - { - lexeme_t l = _lexer.current(); - _lexer.next(); - - if (l == lex_double_slash) - { - if (n->rettype() != xpath_type_node_set) throw_error("Step has to be applied to node set"); - - n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); - } - - // select from location path - return parse_relative_location_path(n); - } - - return n; - } - else if (_lexer.current() == lex_minus) - { - _lexer.next(); - - // precedence 7+ - only parses union expressions - xpath_ast_node* expr = parse_expression_rec(parse_path_or_unary_expression(), 7); - - return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr); - } - else - return parse_location_path(); - } - - struct binary_op_t - { - ast_type_t asttype; - xpath_value_type rettype; - int precedence; - - binary_op_t(): asttype(ast_unknown), rettype(xpath_type_none), precedence(0) - { - } - - binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_) - { - } - - static binary_op_t parse(xpath_lexer& lexer) - { - switch (lexer.current()) - { - case lex_string: - if (lexer.contents() == PUGIXML_TEXT("or")) - return binary_op_t(ast_op_or, xpath_type_boolean, 1); - else if (lexer.contents() == PUGIXML_TEXT("and")) - return binary_op_t(ast_op_and, xpath_type_boolean, 2); - else if (lexer.contents() == PUGIXML_TEXT("div")) - return binary_op_t(ast_op_divide, xpath_type_number, 6); - else if (lexer.contents() == PUGIXML_TEXT("mod")) - return binary_op_t(ast_op_mod, xpath_type_number, 6); - else - return binary_op_t(); - - case lex_equal: - return binary_op_t(ast_op_equal, xpath_type_boolean, 3); - - case lex_not_equal: - return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3); - - case lex_less: - return binary_op_t(ast_op_less, xpath_type_boolean, 4); - - case lex_greater: - return binary_op_t(ast_op_greater, xpath_type_boolean, 4); - - case lex_less_or_equal: - return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4); - - case lex_greater_or_equal: - return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4); - - case lex_plus: - return binary_op_t(ast_op_add, xpath_type_number, 5); - - case lex_minus: - return binary_op_t(ast_op_subtract, xpath_type_number, 5); - - case lex_multiply: - return binary_op_t(ast_op_multiply, xpath_type_number, 6); - - case lex_union: - return binary_op_t(ast_op_union, xpath_type_node_set, 7); - - default: - return binary_op_t(); - } - } - }; - - xpath_ast_node* parse_expression_rec(xpath_ast_node* lhs, int limit) - { - binary_op_t op = binary_op_t::parse(_lexer); - - while (op.asttype != ast_unknown && op.precedence >= limit) - { - _lexer.next(); - - xpath_ast_node* rhs = parse_path_or_unary_expression(); - - binary_op_t nextop = binary_op_t::parse(_lexer); - - while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence) - { - rhs = parse_expression_rec(rhs, nextop.precedence); - - nextop = binary_op_t::parse(_lexer); - } - - if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set)) - throw_error("Union operator has to be applied to node sets"); - - lhs = new (alloc_node()) xpath_ast_node(op.asttype, op.rettype, lhs, rhs); - - op = binary_op_t::parse(_lexer); - } - - return lhs; - } - - // Expr ::= OrExpr - // OrExpr ::= AndExpr | OrExpr 'or' AndExpr - // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr - // EqualityExpr ::= RelationalExpr - // | EqualityExpr '=' RelationalExpr - // | EqualityExpr '!=' RelationalExpr - // RelationalExpr ::= AdditiveExpr - // | RelationalExpr '<' AdditiveExpr - // | RelationalExpr '>' AdditiveExpr - // | RelationalExpr '<=' AdditiveExpr - // | RelationalExpr '>=' AdditiveExpr - // AdditiveExpr ::= MultiplicativeExpr - // | AdditiveExpr '+' MultiplicativeExpr - // | AdditiveExpr '-' MultiplicativeExpr - // MultiplicativeExpr ::= UnaryExpr - // | MultiplicativeExpr '*' UnaryExpr - // | MultiplicativeExpr 'div' UnaryExpr - // | MultiplicativeExpr 'mod' UnaryExpr - xpath_ast_node* parse_expression() - { - return parse_expression_rec(parse_path_or_unary_expression(), 0); - } - - xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result) - { - } - - xpath_ast_node* parse() - { - xpath_ast_node* result = parse_expression(); - - if (_lexer.current() != lex_eof) - { - // there are still unparsed tokens left, error - throw_error("Incorrect query"); - } - - return result; - } - - static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result) - { - xpath_parser parser(query, variables, alloc, result); - - #ifdef PUGIXML_NO_EXCEPTIONS - int error = setjmp(parser._error_handler); - - return (error == 0) ? parser.parse() : 0; - #else - return parser.parse(); - #endif - } - }; - - struct xpath_query_impl - { - static xpath_query_impl* create() - { - void* memory = xml_memory::allocate(sizeof(xpath_query_impl)); - - return new (memory) xpath_query_impl(); - } - - static void destroy(void* ptr) - { - if (!ptr) return; - - // free all allocated pages - static_cast(ptr)->alloc.release(); - - // free allocator memory (with the first page) - xml_memory::deallocate(ptr); - } - - xpath_query_impl(): root(0), alloc(&block) - { - block.next = 0; - block.capacity = sizeof(block.data); - } - - xpath_ast_node* root; - xpath_allocator alloc; - xpath_memory_block block; - }; - - PUGI__FN xpath_string evaluate_string_impl(xpath_query_impl* impl, const xpath_node& n, xpath_stack_data& sd) - { - if (!impl) return xpath_string(); - - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return xpath_string(); - #endif - - xpath_context c(n, 1, 1); - - return impl->root->eval_string(c, sd.stack); - } - - PUGI__FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl) - { - if (!impl) return 0; - - if (impl->root->rettype() != xpath_type_node_set) - { - #ifdef PUGIXML_NO_EXCEPTIONS - return 0; - #else - xpath_parse_result res; - res.error = "Expression does not evaluate to node set"; - - throw xpath_exception(res); - #endif - } - - return impl->root; - } -PUGI__NS_END - -namespace pugi -{ -#ifndef PUGIXML_NO_EXCEPTIONS - PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_) - { - assert(_result.error); - } - - PUGI__FN const char* xpath_exception::what() const throw() - { - return _result.error; - } - - PUGI__FN const xpath_parse_result& xpath_exception::result() const - { - return _result; - } -#endif - - PUGI__FN xpath_node::xpath_node() - { - } - - PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_) - { - } - - PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) - { - } - - PUGI__FN xml_node xpath_node::node() const - { - return _attribute ? xml_node() : _node; - } - - PUGI__FN xml_attribute xpath_node::attribute() const - { - return _attribute; - } - - PUGI__FN xml_node xpath_node::parent() const - { - return _attribute ? _node : _node.parent(); - } - - PUGI__FN static void unspecified_bool_xpath_node(xpath_node***) - { - } - - PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const - { - return (_node || _attribute) ? unspecified_bool_xpath_node : 0; - } - - PUGI__FN bool xpath_node::operator!() const - { - return !(_node || _attribute); - } - - PUGI__FN bool xpath_node::operator==(const xpath_node& n) const - { - return _node == n._node && _attribute == n._attribute; - } - - PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const - { - return _node != n._node || _attribute != n._attribute; - } - -#ifdef __BORLANDC__ - PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs) - { - return (bool)lhs && rhs; - } - - PUGI__FN bool operator||(const xpath_node& lhs, bool rhs) - { - return (bool)lhs || rhs; - } -#endif - - PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_) - { - assert(begin_ <= end_); - - size_t size_ = static_cast(end_ - begin_); - - if (size_ <= 1) - { - // deallocate old buffer - if (_begin != &_storage) impl::xml_memory::deallocate(_begin); - - // use internal buffer - if (begin_ != end_) _storage = *begin_; - - _begin = &_storage; - _end = &_storage + size_; - } - else - { - // make heap copy - xpath_node* storage = static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); - - if (!storage) - { - #ifdef PUGIXML_NO_EXCEPTIONS - return; - #else - throw std::bad_alloc(); - #endif - } - - memcpy(storage, begin_, size_ * sizeof(xpath_node)); - - // deallocate old buffer - if (_begin != &_storage) impl::xml_memory::deallocate(_begin); - - // finalize - _begin = storage; - _end = storage + size_; - } - } - - PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage) - { - } - - PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_), _begin(&_storage), _end(&_storage) - { - _assign(begin_, end_); - } - - PUGI__FN xpath_node_set::~xpath_node_set() - { - if (_begin != &_storage) impl::xml_memory::deallocate(_begin); - } - - PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(ns._type), _begin(&_storage), _end(&_storage) - { - _assign(ns._begin, ns._end); - } - - PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) - { - if (this == &ns) return *this; - - _type = ns._type; - _assign(ns._begin, ns._end); - - return *this; - } - - PUGI__FN xpath_node_set::type_t xpath_node_set::type() const - { - return _type; - } - - PUGI__FN size_t xpath_node_set::size() const - { - return _end - _begin; - } - - PUGI__FN bool xpath_node_set::empty() const - { - return _begin == _end; - } - - PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const - { - assert(index < size()); - return _begin[index]; - } - - PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const - { - return _begin; - } - - PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const - { - return _end; - } - - PUGI__FN void xpath_node_set::sort(bool reverse) - { - _type = impl::xpath_sort(_begin, _end, _type, reverse); - } - - PUGI__FN xpath_node xpath_node_set::first() const - { - return impl::xpath_first(_begin, _end, _type); - } - - PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) - { - } - - PUGI__FN xpath_parse_result::operator bool() const - { - return error == 0; - } - - PUGI__FN const char* xpath_parse_result::description() const - { - return error ? error : "No error"; - } - - PUGI__FN xpath_variable::xpath_variable(): _type(xpath_type_none), _next(0) - { - } - - PUGI__FN const char_t* xpath_variable::name() const - { - switch (_type) - { - case xpath_type_node_set: - return static_cast(this)->name; - - case xpath_type_number: - return static_cast(this)->name; - - case xpath_type_string: - return static_cast(this)->name; - - case xpath_type_boolean: - return static_cast(this)->name; - - default: - assert(!"Invalid variable type"); - return 0; - } - } - - PUGI__FN xpath_value_type xpath_variable::type() const - { - return _type; - } - - PUGI__FN bool xpath_variable::get_boolean() const - { - return (_type == xpath_type_boolean) ? static_cast(this)->value : false; - } - - PUGI__FN double xpath_variable::get_number() const - { - return (_type == xpath_type_number) ? static_cast(this)->value : impl::gen_nan(); - } - - PUGI__FN const char_t* xpath_variable::get_string() const - { - const char_t* value = (_type == xpath_type_string) ? static_cast(this)->value : 0; - return value ? value : PUGIXML_TEXT(""); - } - - PUGI__FN const xpath_node_set& xpath_variable::get_node_set() const - { - return (_type == xpath_type_node_set) ? static_cast(this)->value : impl::dummy_node_set; - } - - PUGI__FN bool xpath_variable::set(bool value) - { - if (_type != xpath_type_boolean) return false; - - static_cast(this)->value = value; - return true; - } - - PUGI__FN bool xpath_variable::set(double value) - { - if (_type != xpath_type_number) return false; - - static_cast(this)->value = value; - return true; - } - - PUGI__FN bool xpath_variable::set(const char_t* value) - { - if (_type != xpath_type_string) return false; - - impl::xpath_variable_string* var = static_cast(this); - - // duplicate string - size_t size = (impl::strlength(value) + 1) * sizeof(char_t); - - char_t* copy = static_cast(impl::xml_memory::allocate(size)); - if (!copy) return false; - - memcpy(copy, value, size); - - // replace old string - if (var->value) impl::xml_memory::deallocate(var->value); - var->value = copy; - - return true; - } - - PUGI__FN bool xpath_variable::set(const xpath_node_set& value) - { - if (_type != xpath_type_node_set) return false; - - static_cast(this)->value = value; - return true; - } - - PUGI__FN xpath_variable_set::xpath_variable_set() - { - for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; - } - - PUGI__FN xpath_variable_set::~xpath_variable_set() - { - for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) - { - xpath_variable* var = _data[i]; - - while (var) - { - xpath_variable* next = var->_next; - - impl::delete_xpath_variable(var->_type, var); - - var = next; - } - } - } - - PUGI__FN xpath_variable* xpath_variable_set::find(const char_t* name) const - { - const size_t hash_size = sizeof(_data) / sizeof(_data[0]); - size_t hash = impl::hash_string(name) % hash_size; - - // look for existing variable - for (xpath_variable* var = _data[hash]; var; var = var->_next) - if (impl::strequal(var->name(), name)) - return var; - - return 0; - } - - PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) - { - const size_t hash_size = sizeof(_data) / sizeof(_data[0]); - size_t hash = impl::hash_string(name) % hash_size; - - // look for existing variable - for (xpath_variable* var = _data[hash]; var; var = var->_next) - if (impl::strequal(var->name(), name)) - return var->type() == type ? var : 0; - - // add new variable - xpath_variable* result = impl::new_xpath_variable(type, name); - - if (result) - { - result->_type = type; - result->_next = _data[hash]; - - _data[hash] = result; - } - - return result; - } - - PUGI__FN bool xpath_variable_set::set(const char_t* name, bool value) - { - xpath_variable* var = add(name, xpath_type_boolean); - return var ? var->set(value) : false; - } - - PUGI__FN bool xpath_variable_set::set(const char_t* name, double value) - { - xpath_variable* var = add(name, xpath_type_number); - return var ? var->set(value) : false; - } - - PUGI__FN bool xpath_variable_set::set(const char_t* name, const char_t* value) - { - xpath_variable* var = add(name, xpath_type_string); - return var ? var->set(value) : false; - } - - PUGI__FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value) - { - xpath_variable* var = add(name, xpath_type_node_set); - return var ? var->set(value) : false; - } - - PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name) - { - return find(name); - } - - PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const - { - return find(name); - } - - PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) - { - impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create(); - - if (!qimpl) - { - #ifdef PUGIXML_NO_EXCEPTIONS - _result.error = "Out of memory"; - #else - throw std::bad_alloc(); - #endif - } - else - { - impl::buffer_holder impl_holder(qimpl, impl::xpath_query_impl::destroy); - - qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result); - - if (qimpl->root) - { - qimpl->root->optimize(&qimpl->alloc); - - _impl = static_cast(impl_holder.release()); - _result.error = 0; - } - } - } - - PUGI__FN xpath_query::~xpath_query() - { - impl::xpath_query_impl::destroy(_impl); - } - - PUGI__FN xpath_value_type xpath_query::return_type() const - { - if (!_impl) return xpath_type_none; - - return static_cast(_impl)->root->rettype(); - } - - PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node& n) const - { - if (!_impl) return false; - - impl::xpath_context c(n, 1, 1); - impl::xpath_stack_data sd; - - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return false; - #endif - - return static_cast(_impl)->root->eval_boolean(c, sd.stack); - } - - PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const - { - if (!_impl) return impl::gen_nan(); - - impl::xpath_context c(n, 1, 1); - impl::xpath_stack_data sd; - - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return impl::gen_nan(); - #endif - - return static_cast(_impl)->root->eval_number(c, sd.stack); - } - -#ifndef PUGIXML_NO_STL - PUGI__FN string_t xpath_query::evaluate_string(const xpath_node& n) const - { - impl::xpath_stack_data sd; - - impl::xpath_string r = impl::evaluate_string_impl(static_cast(_impl), n, sd); - - return string_t(r.c_str(), r.length()); - } -#endif - - PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const - { - impl::xpath_stack_data sd; - - impl::xpath_string r = impl::evaluate_string_impl(static_cast(_impl), n, sd); - - size_t full_size = r.length() + 1; - - if (capacity > 0) - { - size_t size = (full_size < capacity) ? full_size : capacity; - assert(size > 0); - - memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t)); - buffer[size - 1] = 0; - } - - return full_size; - } - - PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const - { - impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); - if (!root) return xpath_node_set(); - - impl::xpath_context c(n, 1, 1); - impl::xpath_stack_data sd; - - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return xpath_node_set(); - #endif - - impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all); - - return xpath_node_set(r.begin(), r.end(), r.type()); - } - - PUGI__FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const - { - impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); - if (!root) return xpath_node(); - - impl::xpath_context c(n, 1, 1); - impl::xpath_stack_data sd; - - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return xpath_node(); - #endif - - impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first); - - return r.first(); - } - - PUGI__FN const xpath_parse_result& xpath_query::result() const - { - return _result; - } - - PUGI__FN static void unspecified_bool_xpath_query(xpath_query***) - { - } - - PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const - { - return _impl ? unspecified_bool_xpath_query : 0; - } - - PUGI__FN bool xpath_query::operator!() const - { - return !_impl; - } - - PUGI__FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const - { - xpath_query q(query, variables); - return select_node(q); - } - - PUGI__FN xpath_node xml_node::select_node(const xpath_query& query) const - { - return query.evaluate_node(*this); - } - - PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const - { - xpath_query q(query, variables); - return select_nodes(q); - } - - PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const - { - return query.evaluate_node_set(*this); - } - - PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const - { - xpath_query q(query, variables); - return select_single_node(q); - } - - PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const - { - return query.evaluate_node(*this); - } -} - -#endif - -#ifdef __BORLANDC__ -# pragma option pop -#endif - -// Intel C++ does not properly keep warning state for function templates, -// so popping warning state at the end of translation unit leads to warnings in the middle. -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) -# pragma warning(pop) -#endif - -// Undefine all local macros (makes sure we're not leaking macros in header-only mode) -#undef PUGI__NO_INLINE -#undef PUGI__UNLIKELY -#undef PUGI__STATIC_ASSERT -#undef PUGI__DMC_VOLATILE -#undef PUGI__MSVC_CRT_VERSION -#undef PUGI__NS_BEGIN -#undef PUGI__NS_END -#undef PUGI__FN -#undef PUGI__FN_NO_INLINE -#undef PUGI__NODETYPE -#undef PUGI__IS_CHARTYPE_IMPL -#undef PUGI__IS_CHARTYPE -#undef PUGI__IS_CHARTYPEX -#undef PUGI__ENDSWITH -#undef PUGI__SKIPWS -#undef PUGI__OPTSET -#undef PUGI__PUSHNODE -#undef PUGI__POPNODE -#undef PUGI__SCANFOR -#undef PUGI__SCANWHILE -#undef PUGI__SCANWHILE_UNROLL -#undef PUGI__ENDSEG -#undef PUGI__THROW_ERROR -#undef PUGI__CHECK_ERROR - -#endif - -/** - * Copyright (c) 2006-2015 Arseny Kapoulkine - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ diff --git a/oldgui/profile/pugixml.h b/oldgui/profile/pugixml.h deleted file mode 100644 index d9c27f6..0000000 --- a/oldgui/profile/pugixml.h +++ /dev/null @@ -1,1366 +0,0 @@ -/** - * pugixml parser - version 1.6 - * -------------------------------------------------------- - * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ - * - * This library is distributed under the MIT License. See notice at the end - * of this file. - * - * This work is based on the pugxml parser, which is: - * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) - */ - -#ifndef PUGIXML_VERSION -// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons -# define PUGIXML_VERSION 160 -#endif - -// Include user configuration file (this can define various configuration macros) -#include "pugiconfig.h" - -#ifndef HEADER_PUGIXML_HPP -#define HEADER_PUGIXML_HPP - -// Include stddef.h for size_t and ptrdiff_t -#include - -// Include exception header for XPath -#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) -# include -#endif - -// Include STL headers -#ifndef PUGIXML_NO_STL -# include -# include -# include -#endif - -// Macro for deprecated features -#ifndef PUGIXML_DEPRECATED -# if defined(__GNUC__) -# define PUGIXML_DEPRECATED __attribute__((deprecated)) -# elif defined(_MSC_VER) && _MSC_VER >= 1300 -# define PUGIXML_DEPRECATED __declspec(deprecated) -# else -# define PUGIXML_DEPRECATED -# endif -#endif - -// If no API is defined, assume default -#ifndef PUGIXML_API -# define PUGIXML_API -#endif - -// If no API for classes is defined, assume default -#ifndef PUGIXML_CLASS -# define PUGIXML_CLASS PUGIXML_API -#endif - -// If no API for functions is defined, assume default -#ifndef PUGIXML_FUNCTION -# define PUGIXML_FUNCTION PUGIXML_API -#endif - -// If the platform is known to have long long support, enable long long functions -#ifndef PUGIXML_HAS_LONG_LONG -# if defined(__cplusplus) && __cplusplus >= 201103 -# define PUGIXML_HAS_LONG_LONG -# elif defined(_MSC_VER) && _MSC_VER >= 1400 -# define PUGIXML_HAS_LONG_LONG -# endif -#endif - -// Character interface macros -#ifdef PUGIXML_WCHAR_MODE -# define PUGIXML_TEXT(t) L ## t -# define PUGIXML_CHAR wchar_t -#else -# define PUGIXML_TEXT(t) t -# define PUGIXML_CHAR char -#endif - -namespace pugi -{ - // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE - typedef PUGIXML_CHAR char_t; - -#ifndef PUGIXML_NO_STL - // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE - typedef std::basic_string, std::allocator > string_t; -#endif -} - -// The PugiXML namespace -namespace pugi -{ - // Tree node types - enum xml_node_type - { - node_null, // Empty (null) node handle - node_document, // A document tree's absolute root - node_element, // Element tag, i.e. '' - node_pcdata, // Plain character data, i.e. 'text' - node_cdata, // Character data, i.e. '' - node_comment, // Comment tag, i.e. '' - node_pi, // Processing instruction, i.e. '' - node_declaration, // Document declaration, i.e. '' - node_doctype // Document type declaration, i.e. '' - }; - - // Parsing options - - // Minimal parsing mode (equivalent to turning all other flags off). - // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. - const unsigned int parse_minimal = 0x0000; - - // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. - const unsigned int parse_pi = 0x0001; - - // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. - const unsigned int parse_comments = 0x0002; - - // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. - const unsigned int parse_cdata = 0x0004; - - // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. - // This flag is off by default; turning it on usually results in slower parsing and more memory consumption. - const unsigned int parse_ws_pcdata = 0x0008; - - // This flag determines if character and entity references are expanded during parsing. This flag is on by default. - const unsigned int parse_escapes = 0x0010; - - // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. - const unsigned int parse_eol = 0x0020; - - // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. - const unsigned int parse_wconv_attribute = 0x0040; - - // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. - const unsigned int parse_wnorm_attribute = 0x0080; - - // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. - const unsigned int parse_declaration = 0x0100; - - // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default. - const unsigned int parse_doctype = 0x0200; - - // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only - // of whitespace is added to the DOM tree. - // This flag is off by default; turning it on may result in slower parsing and more memory consumption. - const unsigned int parse_ws_pcdata_single = 0x0400; - - // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default. - const unsigned int parse_trim_pcdata = 0x0800; - - // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document - // is a valid document. This flag is off by default. - const unsigned int parse_fragment = 0x1000; - - // The default parsing mode. - // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, - // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. - const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; - - // The full parsing mode. - // Nodes of all types are added to the DOM tree, character/reference entities are expanded, - // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. - const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; - - // These flags determine the encoding of input data for XML document - enum xml_encoding - { - encoding_auto, // Auto-detect input encoding using BOM or < / class xml_object_range - { - public: - typedef It const_iterator; - typedef It iterator; - - xml_object_range(It b, It e): _begin(b), _end(e) - { - } - - It begin() const { return _begin; } - It end() const { return _end; } - - private: - It _begin, _end; - }; - - // Writer interface for node printing (see xml_node::print) - class PUGIXML_CLASS xml_writer - { - public: - virtual ~xml_writer() {} - - // Write memory chunk into stream/file/whatever - virtual void write(const void* data, size_t size) = 0; - }; - - // xml_writer implementation for FILE* - class PUGIXML_CLASS xml_writer_file: public xml_writer - { - public: - // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio - xml_writer_file(void* file); - - virtual void write(const void* data, size_t size); - - private: - void* file; - }; - - #ifndef PUGIXML_NO_STL - // xml_writer implementation for streams - class PUGIXML_CLASS xml_writer_stream: public xml_writer - { - public: - // Construct writer from an output stream object - xml_writer_stream(std::basic_ostream >& stream); - xml_writer_stream(std::basic_ostream >& stream); - - virtual void write(const void* data, size_t size); - - private: - std::basic_ostream >* narrow_stream; - std::basic_ostream >* wide_stream; - }; - #endif - - // A light-weight handle for manipulating attributes in DOM tree - class PUGIXML_CLASS xml_attribute - { - friend class xml_attribute_iterator; - friend class xml_node; - - private: - xml_attribute_struct* _attr; - - typedef void (*unspecified_bool_type)(xml_attribute***); - - public: - // Default constructor. Constructs an empty attribute. - xml_attribute(); - - // Constructs attribute from internal pointer - explicit xml_attribute(xml_attribute_struct* attr); - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Comparison operators (compares wrapped attribute pointers) - bool operator==(const xml_attribute& r) const; - bool operator!=(const xml_attribute& r) const; - bool operator<(const xml_attribute& r) const; - bool operator>(const xml_attribute& r) const; - bool operator<=(const xml_attribute& r) const; - bool operator>=(const xml_attribute& r) const; - - // Check if attribute is empty - bool empty() const; - - // Get attribute name/value, or "" if attribute is empty - const char_t* name() const; - const char_t* value() const; - - // Get attribute value, or the default value if attribute is empty - const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; - - // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty - int as_int(int def = 0) const; - unsigned int as_uint(unsigned int def = 0) const; - double as_double(double def = 0) const; - float as_float(float def = 0) const; - - #ifdef PUGIXML_HAS_LONG_LONG - long long as_llong(long long def = 0) const; - unsigned long long as_ullong(unsigned long long def = 0) const; - #endif - - // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty - bool as_bool(bool def = false) const; - - // Set attribute name/value (returns false if attribute is empty or there is not enough memory) - bool set_name(const char_t* rhs); - bool set_value(const char_t* rhs); - - // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") - bool set_value(int rhs); - bool set_value(unsigned int rhs); - bool set_value(double rhs); - bool set_value(float rhs); - bool set_value(bool rhs); - - #ifdef PUGIXML_HAS_LONG_LONG - bool set_value(long long rhs); - bool set_value(unsigned long long rhs); - #endif - - // Set attribute value (equivalent to set_value without error checking) - xml_attribute& operator=(const char_t* rhs); - xml_attribute& operator=(int rhs); - xml_attribute& operator=(unsigned int rhs); - xml_attribute& operator=(double rhs); - xml_attribute& operator=(float rhs); - xml_attribute& operator=(bool rhs); - - #ifdef PUGIXML_HAS_LONG_LONG - xml_attribute& operator=(long long rhs); - xml_attribute& operator=(unsigned long long rhs); - #endif - - // Get next/previous attribute in the attribute list of the parent node - xml_attribute next_attribute() const; - xml_attribute previous_attribute() const; - - // Get hash value (unique for handles to the same object) - size_t hash_value() const; - - // Get internal pointer - xml_attribute_struct* internal_object() const; - }; - -#ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); -#endif - - // A light-weight handle for manipulating nodes in DOM tree - class PUGIXML_CLASS xml_node - { - friend class xml_attribute_iterator; - friend class xml_node_iterator; - friend class xml_named_node_iterator; - - protected: - xml_node_struct* _root; - - typedef void (*unspecified_bool_type)(xml_node***); - - public: - // Default constructor. Constructs an empty node. - xml_node(); - - // Constructs node from internal pointer - explicit xml_node(xml_node_struct* p); - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Comparison operators (compares wrapped node pointers) - bool operator==(const xml_node& r) const; - bool operator!=(const xml_node& r) const; - bool operator<(const xml_node& r) const; - bool operator>(const xml_node& r) const; - bool operator<=(const xml_node& r) const; - bool operator>=(const xml_node& r) const; - - // Check if node is empty. - bool empty() const; - - // Get node type - xml_node_type type() const; - - // Get node name, or "" if node is empty or it has no name - const char_t* name() const; - - // Get node value, or "" if node is empty or it has no value - // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. - const char_t* value() const; - - // Get attribute list - xml_attribute first_attribute() const; - xml_attribute last_attribute() const; - - // Get children list - xml_node first_child() const; - xml_node last_child() const; - - // Get next/previous sibling in the children list of the parent node - xml_node next_sibling() const; - xml_node previous_sibling() const; - - // Get parent node - xml_node parent() const; - - // Get root of DOM tree this node belongs to - xml_node root() const; - - // Get text object for the current node - xml_text text() const; - - // Get child, attribute or next/previous sibling with the specified name - xml_node child(const char_t* name) const; - xml_attribute attribute(const char_t* name) const; - xml_node next_sibling(const char_t* name) const; - xml_node previous_sibling(const char_t* name) const; - - // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA - const char_t* child_value() const; - - // Get child value of child with specified name. Equivalent to child(name).child_value(). - const char_t* child_value(const char_t* name) const; - - // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) - bool set_name(const char_t* rhs); - bool set_value(const char_t* rhs); - - // Add attribute with specified name. Returns added attribute, or empty attribute on errors. - xml_attribute append_attribute(const char_t* name); - xml_attribute prepend_attribute(const char_t* name); - xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); - xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); - - // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. - xml_attribute append_copy(const xml_attribute& proto); - xml_attribute prepend_copy(const xml_attribute& proto); - xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); - xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); - - // Add child node with specified type. Returns added node, or empty node on errors. - xml_node append_child(xml_node_type type = node_element); - xml_node prepend_child(xml_node_type type = node_element); - xml_node insert_child_after(xml_node_type type, const xml_node& node); - xml_node insert_child_before(xml_node_type type, const xml_node& node); - - // Add child element with specified name. Returns added node, or empty node on errors. - xml_node append_child(const char_t* name); - xml_node prepend_child(const char_t* name); - xml_node insert_child_after(const char_t* name, const xml_node& node); - xml_node insert_child_before(const char_t* name, const xml_node& node); - - // Add a copy of the specified node as a child. Returns added node, or empty node on errors. - xml_node append_copy(const xml_node& proto); - xml_node prepend_copy(const xml_node& proto); - xml_node insert_copy_after(const xml_node& proto, const xml_node& node); - xml_node insert_copy_before(const xml_node& proto, const xml_node& node); - - // Move the specified node to become a child of this node. Returns moved node, or empty node on errors. - xml_node append_move(const xml_node& moved); - xml_node prepend_move(const xml_node& moved); - xml_node insert_move_after(const xml_node& moved, const xml_node& node); - xml_node insert_move_before(const xml_node& moved, const xml_node& node); - - // Remove specified attribute - bool remove_attribute(const xml_attribute& a); - bool remove_attribute(const char_t* name); - - // Remove specified child - bool remove_child(const xml_node& n); - bool remove_child(const char_t* name); - - // Parses buffer as an XML document fragment and appends all nodes as children of the current node. - // Copies/converts the buffer, so it may be deleted or changed after the function returns. - // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. - xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Find attribute using predicate. Returns first attribute for which predicate returned true. - template xml_attribute find_attribute(Predicate pred) const - { - if (!_root) return xml_attribute(); - - for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) - if (pred(attrib)) - return attrib; - - return xml_attribute(); - } - - // Find child node using predicate. Returns first child for which predicate returned true. - template xml_node find_child(Predicate pred) const - { - if (!_root) return xml_node(); - - for (xml_node node = first_child(); node; node = node.next_sibling()) - if (pred(node)) - return node; - - return xml_node(); - } - - // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. - template xml_node find_node(Predicate pred) const - { - if (!_root) return xml_node(); - - xml_node cur = first_child(); - - while (cur._root && cur._root != _root) - { - if (pred(cur)) return cur; - - if (cur.first_child()) cur = cur.first_child(); - else if (cur.next_sibling()) cur = cur.next_sibling(); - else - { - while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); - - if (cur._root != _root) cur = cur.next_sibling(); - } - } - - return xml_node(); - } - - // Find child node by attribute name/value - xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; - xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; - - #ifndef PUGIXML_NO_STL - // Get the absolute node path from root as a text string. - string_t path(char_t delimiter = '/') const; - #endif - - // Search for a node by path consisting of node names and . or .. elements. - xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; - - // Recursively traverse subtree with xml_tree_walker - bool traverse(xml_tree_walker& walker); - - #ifndef PUGIXML_NO_XPATH - // Select single node by evaluating XPath query. Returns first node from the resulting node set. - xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const; - xpath_node select_node(const xpath_query& query) const; - - // Select node set by evaluating XPath query - xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; - xpath_node_set select_nodes(const xpath_query& query) const; - - // (deprecated: use select_node instead) Select single node by evaluating XPath query. - xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; - xpath_node select_single_node(const xpath_query& query) const; - - #endif - - // Print subtree using a writer object - void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; - - #ifndef PUGIXML_NO_STL - // Print subtree to stream - void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; - void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; - #endif - - // Child nodes iterators - typedef xml_node_iterator iterator; - - iterator begin() const; - iterator end() const; - - // Attribute iterators - typedef xml_attribute_iterator attribute_iterator; - - attribute_iterator attributes_begin() const; - attribute_iterator attributes_end() const; - - // Range-based for support - xml_object_range children() const; - xml_object_range children(const char_t* name) const; - xml_object_range attributes() const; - - // Get node offset in parsed file/string (in char_t units) for debugging purposes - ptrdiff_t offset_debug() const; - - // Get hash value (unique for handles to the same object) - size_t hash_value() const; - - // Get internal pointer - xml_node_struct* internal_object() const; - }; - -#ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); -#endif - - // A helper for working with text inside PCDATA nodes - class PUGIXML_CLASS xml_text - { - friend class xml_node; - - xml_node_struct* _root; - - typedef void (*unspecified_bool_type)(xml_text***); - - explicit xml_text(xml_node_struct* root); - - xml_node_struct* _data_new(); - xml_node_struct* _data() const; - - public: - // Default constructor. Constructs an empty object. - xml_text(); - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Check if text object is empty - bool empty() const; - - // Get text, or "" if object is empty - const char_t* get() const; - - // Get text, or the default value if object is empty - const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; - - // Get text as a number, or the default value if conversion did not succeed or object is empty - int as_int(int def = 0) const; - unsigned int as_uint(unsigned int def = 0) const; - double as_double(double def = 0) const; - float as_float(float def = 0) const; - - #ifdef PUGIXML_HAS_LONG_LONG - long long as_llong(long long def = 0) const; - unsigned long long as_ullong(unsigned long long def = 0) const; - #endif - - // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty - bool as_bool(bool def = false) const; - - // Set text (returns false if object is empty or there is not enough memory) - bool set(const char_t* rhs); - - // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") - bool set(int rhs); - bool set(unsigned int rhs); - bool set(double rhs); - bool set(float rhs); - bool set(bool rhs); - - #ifdef PUGIXML_HAS_LONG_LONG - bool set(long long rhs); - bool set(unsigned long long rhs); - #endif - - // Set text (equivalent to set without error checking) - xml_text& operator=(const char_t* rhs); - xml_text& operator=(int rhs); - xml_text& operator=(unsigned int rhs); - xml_text& operator=(double rhs); - xml_text& operator=(float rhs); - xml_text& operator=(bool rhs); - - #ifdef PUGIXML_HAS_LONG_LONG - xml_text& operator=(long long rhs); - xml_text& operator=(unsigned long long rhs); - #endif - - // Get the data node (node_pcdata or node_cdata) for this object - xml_node data() const; - }; - -#ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); -#endif - - // Child node iterator (a bidirectional iterator over a collection of xml_node) - class PUGIXML_CLASS xml_node_iterator - { - friend class xml_node; - - private: - mutable xml_node _wrap; - xml_node _parent; - - xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); - - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_node value_type; - typedef xml_node* pointer; - typedef xml_node& reference; - - #ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; - #endif - - // Default constructor - xml_node_iterator(); - - // Construct an iterator which points to the specified node - xml_node_iterator(const xml_node& node); - - // Iterator operators - bool operator==(const xml_node_iterator& rhs) const; - bool operator!=(const xml_node_iterator& rhs) const; - - xml_node& operator*() const; - xml_node* operator->() const; - - const xml_node_iterator& operator++(); - xml_node_iterator operator++(int); - - const xml_node_iterator& operator--(); - xml_node_iterator operator--(int); - }; - - // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) - class PUGIXML_CLASS xml_attribute_iterator - { - friend class xml_node; - - private: - mutable xml_attribute _wrap; - xml_node _parent; - - xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); - - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_attribute value_type; - typedef xml_attribute* pointer; - typedef xml_attribute& reference; - - #ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; - #endif - - // Default constructor - xml_attribute_iterator(); - - // Construct an iterator which points to the specified attribute - xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); - - // Iterator operators - bool operator==(const xml_attribute_iterator& rhs) const; - bool operator!=(const xml_attribute_iterator& rhs) const; - - xml_attribute& operator*() const; - xml_attribute* operator->() const; - - const xml_attribute_iterator& operator++(); - xml_attribute_iterator operator++(int); - - const xml_attribute_iterator& operator--(); - xml_attribute_iterator operator--(int); - }; - - // Named node range helper - class PUGIXML_CLASS xml_named_node_iterator - { - friend class xml_node; - - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_node value_type; - typedef xml_node* pointer; - typedef xml_node& reference; - - #ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; - #endif - - // Default constructor - xml_named_node_iterator(); - - // Construct an iterator which points to the specified node - xml_named_node_iterator(const xml_node& node, const char_t* name); - - // Iterator operators - bool operator==(const xml_named_node_iterator& rhs) const; - bool operator!=(const xml_named_node_iterator& rhs) const; - - xml_node& operator*() const; - xml_node* operator->() const; - - const xml_named_node_iterator& operator++(); - xml_named_node_iterator operator++(int); - - const xml_named_node_iterator& operator--(); - xml_named_node_iterator operator--(int); - - private: - mutable xml_node _wrap; - xml_node _parent; - const char_t* _name; - - xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name); - }; - - // Abstract tree walker class (see xml_node::traverse) - class PUGIXML_CLASS xml_tree_walker - { - friend class xml_node; - - private: - int _depth; - - protected: - // Get current traversal depth - int depth() const; - - public: - xml_tree_walker(); - virtual ~xml_tree_walker(); - - // Callback that is called when traversal begins - virtual bool begin(xml_node& node); - - // Callback that is called for each node traversed - virtual bool for_each(xml_node& node) = 0; - - // Callback that is called when traversal ends - virtual bool end(xml_node& node); - }; - - // Parsing status, returned as part of xml_parse_result object - enum xml_parse_status - { - status_ok = 0, // No error - - status_file_not_found, // File was not found during load_file() - status_io_error, // Error reading from file/stream - status_out_of_memory, // Could not allocate memory - status_internal_error, // Internal error occurred - - status_unrecognized_tag, // Parser could not determine tag type - - status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction - status_bad_comment, // Parsing error occurred while parsing comment - status_bad_cdata, // Parsing error occurred while parsing CDATA section - status_bad_doctype, // Parsing error occurred while parsing document type declaration - status_bad_pcdata, // Parsing error occurred while parsing PCDATA section - status_bad_start_element, // Parsing error occurred while parsing start element tag - status_bad_attribute, // Parsing error occurred while parsing element attribute - status_bad_end_element, // Parsing error occurred while parsing end element tag - status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) - - status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) - - status_no_document_element // Parsing resulted in a document without element nodes - }; - - // Parsing result - struct PUGIXML_CLASS xml_parse_result - { - // Parsing status (see xml_parse_status) - xml_parse_status status; - - // Last parsed offset (in char_t units from start of input data) - ptrdiff_t offset; - - // Source document encoding - xml_encoding encoding; - - // Default constructor, initializes object to failed state - xml_parse_result(); - - // Cast to bool operator - operator bool() const; - - // Get error description - const char* description() const; - }; - - // Document class (DOM tree root) - class PUGIXML_CLASS xml_document: public xml_node - { - private: - char_t* _buffer; - - char _memory[192]; - - // Non-copyable semantics - xml_document(const xml_document&); - const xml_document& operator=(const xml_document&); - - void create(); - void destroy(); - - public: - // Default constructor, makes empty document - xml_document(); - - // Destructor, invalidates all node/attribute handles to this document - ~xml_document(); - - // Removes all nodes, leaving the empty document - void reset(); - - // Removes all nodes, then copies the entire contents of the specified document - void reset(const xml_document& proto); - - #ifndef PUGIXML_NO_STL - // Load document from stream. - xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default); - #endif - - // (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied. - xml_parse_result load(const char_t* contents, unsigned int options = parse_default); - - // Load document from zero-terminated string. No encoding conversions are applied. - xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default); - - // Load document from file - xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. - xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). - // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. - xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). - // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). - xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). - void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - - #ifndef PUGIXML_NO_STL - // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). - void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; - #endif - - // Save XML to file - bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - - // Get document element - xml_node document_element() const; - }; - -#ifndef PUGIXML_NO_XPATH - // XPath query return type - enum xpath_value_type - { - xpath_type_none, // Unknown type (query failed to compile) - xpath_type_node_set, // Node set (xpath_node_set) - xpath_type_number, // Number - xpath_type_string, // String - xpath_type_boolean // Boolean - }; - - // XPath parsing result - struct PUGIXML_CLASS xpath_parse_result - { - // Error message (0 if no error) - const char* error; - - // Last parsed offset (in char_t units from string start) - ptrdiff_t offset; - - // Default constructor, initializes object to failed state - xpath_parse_result(); - - // Cast to bool operator - operator bool() const; - - // Get error description - const char* description() const; - }; - - // A single XPath variable - class PUGIXML_CLASS xpath_variable - { - friend class xpath_variable_set; - - protected: - xpath_value_type _type; - xpath_variable* _next; - - xpath_variable(); - - // Non-copyable semantics - xpath_variable(const xpath_variable&); - xpath_variable& operator=(const xpath_variable&); - - public: - // Get variable name - const char_t* name() const; - - // Get variable type - xpath_value_type type() const; - - // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error - bool get_boolean() const; - double get_number() const; - const char_t* get_string() const; - const xpath_node_set& get_node_set() const; - - // Set variable value; no type conversion is performed, false is returned on type mismatch error - bool set(bool value); - bool set(double value); - bool set(const char_t* value); - bool set(const xpath_node_set& value); - }; - - // A set of XPath variables - class PUGIXML_CLASS xpath_variable_set - { - private: - xpath_variable* _data[64]; - - // Non-copyable semantics - xpath_variable_set(const xpath_variable_set&); - xpath_variable_set& operator=(const xpath_variable_set&); - - xpath_variable* find(const char_t* name) const; - - public: - // Default constructor/destructor - xpath_variable_set(); - ~xpath_variable_set(); - - // Add a new variable or get the existing one, if the types match - xpath_variable* add(const char_t* name, xpath_value_type type); - - // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch - bool set(const char_t* name, bool value); - bool set(const char_t* name, double value); - bool set(const char_t* name, const char_t* value); - bool set(const char_t* name, const xpath_node_set& value); - - // Get existing variable by name - xpath_variable* get(const char_t* name); - const xpath_variable* get(const char_t* name) const; - }; - - // A compiled XPath query object - class PUGIXML_CLASS xpath_query - { - private: - void* _impl; - xpath_parse_result _result; - - typedef void (*unspecified_bool_type)(xpath_query***); - - // Non-copyable semantics - xpath_query(const xpath_query&); - xpath_query& operator=(const xpath_query&); - - public: - // Construct a compiled object from XPath expression. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. - explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); - - // Destructor - ~xpath_query(); - - // Get query expression return type - xpath_value_type return_type() const; - - // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - bool evaluate_boolean(const xpath_node& n) const; - - // Evaluate expression as double value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - double evaluate_number(const xpath_node& n) const; - - #ifndef PUGIXML_NO_STL - // Evaluate expression as string value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - string_t evaluate_string(const xpath_node& n) const; - #endif - - // Evaluate expression as string value in the specified context; performs type conversion if necessary. - // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. - size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; - - // Evaluate expression as node set in the specified context. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. - // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. - xpath_node_set evaluate_node_set(const xpath_node& n) const; - - // Evaluate expression as node set in the specified context. - // Return first node in document order, or empty node if node set is empty. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. - // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead. - xpath_node evaluate_node(const xpath_node& n) const; - - // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) - const xpath_parse_result& result() const; - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - }; - - #ifndef PUGIXML_NO_EXCEPTIONS - // XPath exception class - class PUGIXML_CLASS xpath_exception: public std::exception - { - private: - xpath_parse_result _result; - - public: - // Construct exception from parse result - explicit xpath_exception(const xpath_parse_result& result); - - // Get error message - virtual const char* what() const throw(); - - // Get parse result - const xpath_parse_result& result() const; - }; - #endif - - // XPath node class (either xml_node or xml_attribute) - class PUGIXML_CLASS xpath_node - { - private: - xml_node _node; - xml_attribute _attribute; - - typedef void (*unspecified_bool_type)(xpath_node***); - - public: - // Default constructor; constructs empty XPath node - xpath_node(); - - // Construct XPath node from XML node/attribute - xpath_node(const xml_node& node); - xpath_node(const xml_attribute& attribute, const xml_node& parent); - - // Get node/attribute, if any - xml_node node() const; - xml_attribute attribute() const; - - // Get parent of contained node/attribute - xml_node parent() const; - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Comparison operators - bool operator==(const xpath_node& n) const; - bool operator!=(const xpath_node& n) const; - }; - -#ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); -#endif - - // A fixed-size collection of XPath nodes - class PUGIXML_CLASS xpath_node_set - { - public: - // Collection type - enum type_t - { - type_unsorted, // Not ordered - type_sorted, // Sorted by document order (ascending) - type_sorted_reverse // Sorted by document order (descending) - }; - - // Constant iterator type - typedef const xpath_node* const_iterator; - - // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work - typedef const xpath_node* iterator; - - // Default constructor. Constructs empty set. - xpath_node_set(); - - // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful - xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); - - // Destructor - ~xpath_node_set(); - - // Copy constructor/assignment operator - xpath_node_set(const xpath_node_set& ns); - xpath_node_set& operator=(const xpath_node_set& ns); - - // Get collection type - type_t type() const; - - // Get collection size - size_t size() const; - - // Indexing operator - const xpath_node& operator[](size_t index) const; - - // Collection iterators - const_iterator begin() const; - const_iterator end() const; - - // Sort the collection in ascending/descending order by document order - void sort(bool reverse = false); - - // Get first node in the collection by document order - xpath_node first() const; - - // Check if collection is empty - bool empty() const; - - private: - type_t _type; - - xpath_node _storage; - - xpath_node* _begin; - xpath_node* _end; - - void _assign(const_iterator begin, const_iterator end); - }; -#endif - -#ifndef PUGIXML_NO_STL - // Convert wide string to UTF8 - std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); - std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); - - // Convert UTF8 to wide string - std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); - std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); -#endif - - // Memory allocation function interface; returns pointer to allocated memory or NULL on failure - typedef void* (*allocation_function)(size_t size); - - // Memory deallocation function interface - typedef void (*deallocation_function)(void* ptr); - - // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. - void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); - - // Get current memory management functions - allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); - deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); -} - -#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) -namespace std -{ - // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) - std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&); -} -#endif - -#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) -namespace std -{ - // Workarounds for (non-standard) iterator category detection - std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&); -} -#endif - -#endif - -// Make sure implementation is included in header-only mode -// Use macro expansion in #include to work around QMake (QTBUG-11923) -#if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE) -# define PUGIXML_SOURCE "pugixml.cpp" -# include PUGIXML_SOURCE -#endif - -/** - * Copyright (c) 2006-2015 Arseny Kapoulkine - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ diff --git a/oldgui/resource.h b/oldgui/resource.h deleted file mode 100644 index ccc2c3f..0000000 --- a/oldgui/resource.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef IDC_STATIC -#define IDC_STATIC (-1) -#endif - -#define IDD_DIALOG2 102 -#define IDD_DIALOG4 104 -#define IDI_ICON1 110 -#define IDC_EDIT1 1011 -#define IDC_BUTTON1 1020 -#define IDC_BUTTON2 1021 -#define IDC_BUTTON3 1022 -#define IDC_BUTTON5 1024 -#define IDC_LIST1 1028 -#define IDC_BUTTON6 40000 diff --git a/oldgui/utility.cpp b/oldgui/utility.cpp deleted file mode 100644 index 2cc43b3..0000000 --- a/oldgui/utility.cpp +++ /dev/null @@ -1,302 +0,0 @@ -/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) - * This file is part of the Interactive Text Hooker. - - * Interactive Text Hooker is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "utility.h" -#include "host.h" -#include "hookman.h" -#include "vnrhook/include/types.h" -#include "vnrhook/include/const.h" -#include "profile/misc.h" - -extern HookManager* man; // main.cpp - -std::wstring GetDriveLetter(const std::wstring& devicePath); -std::wstring GetWindowsPath(const std::wstring& fileObjectPath); -PVOID GetAllocationBase(DWORD pid, LPCVOID); -std::wstring GetModuleFileNameAsString(DWORD pid, PVOID allocationBase); -std::wstring GetModuleFileNameAsString(); -std::wstring GetProcessPath(HANDLE hProc); - -void ConsoleOutput(LPCWSTR text) -{ - man->AddConsoleOutput(text); -} - -std::wstring GetProcessPath(DWORD pid) -{ - UniqueHandle hProc(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)); - if (hProc) - return GetProcessPath(hProc.get()); - else - return L""; -} - -std::wstring GetProcessPath(HANDLE hProc) -{ - wchar_t path[MAX_PATH]; - GetProcessImageFileName(hProc, path, MAX_PATH); - return GetWindowsPath(path); -} - -std::wstring GetWindowsPath(const std::wstring& path) -{ - // path is in device form - // \Device\HarddiskVolume2\Windows\System32\taskhost.exe - auto pathOffset = path.find(L'\\', 1) + 1; - pathOffset = path.find(L'\\', pathOffset); - std::wstring devicePath = path.substr(0, pathOffset); // \Device\HarddiskVolume2 - std::wstring dosDrive = GetDriveLetter(devicePath); // C: - if (dosDrive.empty()) - return path; - std::wstring dosPath = dosDrive; // C: - dosPath += path.substr(pathOffset); // C:\Windows\System32\taskhost.exe - return dosPath; -} - -std::wstring GetDriveLetter(const std::wstring& devicePath) -{ - for (wchar_t drive = L'A'; drive <= L'Z'; drive++) - { - wchar_t szDriveName[3] = { drive, L':', L'\0' }; - wchar_t szTarget[512]; - if (QueryDosDevice(szDriveName, szTarget, 512)) - if (devicePath.compare(szTarget) == 0) - return szDriveName; - } - return L""; -} - -std::wstring GetCode(const HookParam& hp, DWORD pid) -{ - std::wstring code(L"/H"); - WCHAR c; - if (hp.type & PRINT_DWORD) - c = L'H'; - else if (hp.type & USING_UNICODE) - { - if (hp.type & USING_STRING) - c = L'Q'; - else if (hp.type & STRING_LAST_CHAR) - c = L'L'; - else - c = L'W'; - } - else - { - if (hp.type & USING_STRING) - c = L'S'; - else if (hp.type & BIG_ENDIAN) - c = L'A'; - else if (hp.type & STRING_LAST_CHAR) - c = L'E'; - else - c = L'B'; - } - code += c; - if (hp.type & NO_CONTEXT) - code += L'N'; - if (hp.offset >> 31) - code += L"-" + ToHexString(-(hp.offset + 4)); - else - code += ToHexString(hp.offset); - if (hp.type & DATA_INDIRECT) - { - if (hp.index >> 31) - code += L"*-" + ToHexString(-hp.index); - else - code += L"*" + ToHexString(hp.index); - } - if (hp.type & USING_SPLIT) - { - if (hp.split >> 31) - code += L":-" + ToHexString(-(4 + hp.split)); - else - code += L":" + ToHexString(hp.split); - } - if (hp.type & SPLIT_INDIRECT) - { - if (hp.split_index >> 31) - code += L"*-" + ToHexString(-hp.split_index); - else - code += L"*" + ToHexString(hp.split_index); - } - if (pid) - { - PVOID allocationBase = GetAllocationBase(pid, (LPCVOID)hp.address); - if (allocationBase) - { - std::wstring path = GetModuleFileNameAsString(pid, allocationBase); - if (!path.empty()) - { - auto fileName = path.substr(path.rfind(L'\\') + 1); - DWORD relativeHookAddress = hp.address - (DWORD)allocationBase; - code += L"@" + ToHexString(relativeHookAddress) + L":" + fileName; - return code; - } - } - } - if (hp.module) - { - code += L"@" + ToHexString(hp.address) + L"!" + ToHexString(hp.module); - if (hp.function) - code += L"!" + ToHexString(hp.function); - } - else - { - // Hack. The original address is stored in the function field - // if (module == NULL && function != NULL). - // MODULE_OFFSET and FUNCTION_OFFSET are removed from HookParam.type in - // TextHook::UnsafeInsertHookCode() and can not be used here. - if (hp.function) - code += L"@" + ToHexString(hp.function); - else - code += L"@" + ToHexString(hp.address) + L":"; - } - return code; -} - -std::wstring GetModuleFileNameAsString(DWORD pid, PVOID allocationBase) -{ - UniqueHandle hProc(OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)); - if (hProc.get()) - { - WCHAR path[MAX_PATH]; - if (GetModuleFileNameEx(hProc.get(), (HMODULE)allocationBase, path, MAX_PATH)) - return path; - } - return L""; -} - -PVOID GetAllocationBase(DWORD pid, LPCVOID addr) -{ - UniqueHandle hProc(OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)); - if (hProc.get()) - { - MEMORY_BASIC_INFORMATION info; - if (VirtualQueryEx(hProc.get(), addr, &info, sizeof(info))) - { - if (info.Type & MEM_IMAGE) - return info.AllocationBase; - } - } - return NULL; -} - -struct TitleParam -{ - DWORD pid, buffer_len, retn_len; - std::wstring buffer; -}; - -BOOL CALLBACK EnumProc(HWND hwnd, LPARAM lParam) -{ - TitleParam* p = (TitleParam*)lParam; - DWORD pid; - GetWindowThreadProcessId(hwnd, &pid); - if (pid == p->pid) - { - if (GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE) - { - int len = GetWindowTextLength(hwnd); - std::unique_ptr result(new wchar_t[len + 1]); - GetWindowText(hwnd, result.get(), len + 1); - p->buffer = result.get(); - p->retn_len = p->buffer.size(); - if (!p->buffer.empty()) - return FALSE; - } - } - return TRUE; -} - -std::wstring GetProcessTitle(DWORD pid) -{ - TitleParam p; - p.pid = pid; - p.buffer_len = 0; - p.retn_len = 0; - EnumWindows(EnumProc, (LPARAM)&p); - return p.buffer; -} - -WindowsError::WindowsError(DWORD error_code) : error_code(error_code), msg("") -{ - CHAR str[512]; - std::sprintf(str, "error code 0x%8x", error_code); - msg = str; -} - -const char *WindowsError::what() const -{ - return msg.c_str(); -} - -HANDLE IthCreateThread(LPVOID start_addr, DWORD param) -{ - return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_addr, (LPVOID)param, 0, NULL); -} - -std::wstring GetModuleFileNameAsString() -{ - WCHAR path[MAX_PATH]; - GetModuleFileName(NULL, path, MAX_PATH); - return path; -} - -bool IthCreateDirectory(LPCWSTR name) -{ - std::wstring path = GetModuleFileNameAsString(); - path = path.substr(0, path.rfind(L'\\') + 1) + name; - BOOL error_code = CreateDirectory(path.c_str(), NULL); - return error_code != 0 || GetLastError() == ERROR_ALREADY_EXISTS; -} - -HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition) -{ - std::wstring path = GetModuleFileNameAsString(); - path = path.substr(0, path.rfind(L'\\') + 1) + name; - return CreateFile(path.c_str(), option, share, NULL, disposition, FILE_ATTRIBUTE_NORMAL, NULL); -} - -//SJIS->Unicode. mb must be null-terminated. wc_length is the length of wc in characters. -int MB_WC(const char* mb, wchar_t* wc, int wc_length) -{ - return MultiByteToWideChar(932, 0, mb, -1, wc, wc_length); -} - -// Count characters in wide string. mb_length is the number of bytes from mb to convert or -// -1 if the string is null terminated. -int MB_WC_count(const char* mb, int mb_length) -{ - return MultiByteToWideChar(932, 0, mb, mb_length, NULL, 0); -} - -// Unicode->SJIS. Analogous to MB_WC. -int WC_MB(const wchar_t *wc, char* mb, int mb_length) -{ - return WideCharToMultiByte(932, 0, wc, -1, mb, mb_length, NULL, NULL); -} - -std::wstring GetEntryString(TextThread* thread) -{ - ThreadParameter tp = thread->GetThreadParameter(); - std::wstring buffer; - buffer.resize(200); - buffer.resize(swprintf(&buffer[0], L"%.4X:%.4d:0x%08X:0x%08X:0x%08X:", thread->Number(), tp.pid, tp.hook, tp.retn, tp.spl)); - buffer += man->GetHookName(tp.pid, tp.hook); - return buffer; -} diff --git a/oldgui/utility.h b/oldgui/utility.h deleted file mode 100644 index 47c3c13..0000000 --- a/oldgui/utility.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "ITH.h" -#include "textthread.h" - -struct HookParam; -struct ProcessRecord; - -DWORD ProcessCommand(const std::wstring& cmd, DWORD pid); -std::wstring GetProcessPath(DWORD pid); -void ConsoleOutput(LPCWSTR); -void ConsoleOutput(LPCSTR text); -std::wstring GetProcessTitle(DWORD pid); -std::wstring GetCode(const HookParam& hp, DWORD pid = 0); - -// http://codesequoia.wordpress.com/2012/08/26/stdunique_ptr-for-windows-handles/ -struct HandleDeleter -{ - typedef HANDLE pointer; - void operator() (HANDLE h) - { - if (h != INVALID_HANDLE_VALUE) { - CloseHandle(h); - } - } -}; - -typedef std::unique_ptr UniqueHandle; - -class FileWriter : public pugi::xml_writer -{ - HANDLE hFile; -public: - FileWriter(HANDLE hFile) : hFile(hFile) {}; - ~FileWriter() {}; - - virtual void write(const void* data, size_t size) - { - DWORD dwNumberOfBytesWritten; - WriteFile(hFile, data, size, &dwNumberOfBytesWritten, NULL); - } -}; - -class WindowsError : public std::exception -{ -private: - std::string msg; - DWORD error_code; -public: - WindowsError(DWORD error_code); - virtual const char *what() const; -}; - -HANDLE IthCreateThread(LPVOID start_addr, DWORD param); -bool IthCreateDirectory(LPCWSTR name); -HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition); -int MB_WC(const char* mb, wchar_t* wc, int wc_length); -int MB_WC_count(const char* mb, int mb_length); -int WC_MB(const wchar_t *wc, char* mb, int mb_length); -std::wstring GetEntryString(TextThread * thread); -bool Parse(const std::wstring& cmd, HookParam& hp); - -// http://jrdodds.blogs.com/blog/2004/08/raii_in_c.html -class CriticalSection -{ -public: - CriticalSection() - { - ::InitializeCriticalSection(&m_rep); - } - ~CriticalSection() - { - ::DeleteCriticalSection(&m_rep); - } - void Enter() - { - ::EnterCriticalSection(&m_rep); - } - void Leave() - { - ::LeaveCriticalSection(&m_rep); - } -private: - CriticalSection(const CriticalSection&); - CriticalSection& operator=(const CriticalSection&); - - CRITICAL_SECTION m_rep; -}; - -class CSLock -{ -public: - CSLock(CriticalSection& a_section) - : m_section(a_section) - { - m_section.Enter(); - } - ~CSLock() - { - m_section.Leave(); - } -private: - CSLock(const CSLock&); - CSLock& operator=(const CSLock&); - - CriticalSection& m_section; -}; diff --git a/oldgui/version.h.in b/oldgui/version.h.in deleted file mode 100644 index fe68a00..0000000 --- a/oldgui/version.h.in +++ /dev/null @@ -1,2 +0,0 @@ -const wchar_t* build_date=L"@BUILD_DATE@"; -const WCHAR program_version[] = L"@CPACK_PACKAGE_VERSION_MAJOR@.@CPACK_PACKAGE_VERSION_MINOR@.@CPACK_PACKAGE_VERSION_PATCH@"; diff --git a/oldgui/window.cpp b/oldgui/window.cpp deleted file mode 100644 index ba0abc1..0000000 --- a/oldgui/window.cpp +++ /dev/null @@ -1,646 +0,0 @@ -/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) - * This file is part of the Interactive Text Hooker. - - * Interactive Text Hooker is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include "window.h" -#include "ProcessWindow.h" -#include "resource.h" -#include "language.h" -#include "host.h" -#include "hookman.h" -#include "vnrhook/include/const.h" -#include "version.h" -#include "ProfileManager.h" -#include "profile/Profile.h" -#include "TextBuffer.h" -#include "profile/misc.h" - -#define CMD_SIZE 512 - -static WNDPROC proc, proccmd, procChar; -static WCHAR last_cmd[CMD_SIZE]; -extern HINSTANCE hIns; // main.cpp - -HWND hMainWnd, hwndCombo, hwndProcessComboBox, hwndEdit, hwndCmd; -HWND hwndProcess; -HWND hwndOption, hwndTop, hwndClear, hwndSave, hwndExtensions, hwndRemoveHook; -HWND hProcDlg, hOptionDlg; -HBRUSH hWhiteBrush; -DWORD background; -ProcessWindow* pswnd; -TextBuffer* texts; -extern ProfileManager* pfman; // ProfileManager.cpp -extern HookManager* man; // main.cpp -#define COMMENT_BUFFER_LENGTH 512 -static WCHAR comment_buffer[COMMENT_BUFFER_LENGTH]; - -LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); -void SaveSettings(); // main.cpp -extern LONG split_time, process_time, inject_delay, insert_delay, -auto_inject, auto_insert, clipboard_flag, cyclic_remove, global_filter; //main.cpp -static int last_select, last_edit; - -ATOM MyRegisterClass(HINSTANCE hInstance) -{ - WNDCLASSEX wcex; - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = CS_HREDRAW | CS_VREDRAW; - wcex.lpfnWndProc = WndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = hInstance; - wcex.hIcon = NULL; - wcex.hCursor = NULL; - wcex.hbrBackground = GetStockBrush(WHITE_BRUSH); - wcex.lpszMenuName = NULL; - wcex.lpszClassName = ClassName; - wcex.hIconSm = LoadIcon(hInstance, (LPWSTR)IDI_ICON1); - return RegisterClassEx(&wcex); -} - -BOOL InitInstance(HINSTANCE hInstance, DWORD nAdmin, RECT* rc) -{ - hIns = hInstance; - LPCWSTR name = (nAdmin) ? ClassNameAdmin : ClassName; - hMainWnd = CreateWindow(ClassName, name, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, - rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, NULL, NULL, hInstance, 0); - if (!hMainWnd) - return FALSE; - ShowWindow(hMainWnd, SW_SHOWNORMAL); - UpdateWindow(hMainWnd); - return TRUE; -} - -DWORD SaveProcessProfile(TextThread* thread); // ProfileManager.cpp - -BOOL CALLBACK OptionDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - SetWindowText(GetDlgItem(hDlg, IDC_EDIT1), std::to_wstring((long long)split_time).c_str()); - } - return TRUE; - case WM_COMMAND: - { - DWORD wmId = LOWORD(wParam); - DWORD wmEvent = HIWORD(wParam); - switch (wmId) - { - case IDOK: - { - WCHAR str[128]; - GetWindowText(GetDlgItem(hDlg, IDC_EDIT1), str, 0x80); - DWORD st = std::stoul(str); - split_time = st > 100 ? st : 100; - man->SetSplitInterval(split_time); - } - case IDCANCEL: - EndDialog(hDlg, 0); - hOptionDlg = NULL; - break; - } - return TRUE; - } - default: - return FALSE; - } - return FALSE; -} - -BOOL CALLBACK ProcessDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - pswnd = new ProcessWindow(hDlg); - return TRUE; - } - case WM_COMMAND: - { - DWORD wmId, wmEvent; - wmId = LOWORD(wParam); - wmEvent = HIWORD(wParam); - switch (wmId) - { - case WM_DESTROY: - case IDOK: - EndDialog(hDlg, NULL); - hProcDlg = NULL; - delete pswnd; - pswnd = NULL; - break; - case IDC_BUTTON1: - pswnd->RefreshProcess(); - break; - case IDC_BUTTON2: - pswnd->AttachProcess(); - break; - case IDC_BUTTON3: - pswnd->DetachProcess(); - break; - case IDC_BUTTON5: - pswnd->CreateProfileForSelectedProcess(); - break; - case IDC_BUTTON6: - pswnd->DeleteProfileForSelectedProcess(); - break; - } - } - return TRUE; - - case WM_NOTIFY: - { - LPNMHDR dr = (LPNMHDR)lParam; - switch (dr->code) - { - case LVN_ITEMCHANGED: - if (dr->idFrom == IDC_LIST1) - { - NMLISTVIEW *nmlv = (LPNMLISTVIEW)lParam; - if (nmlv->uNewState & LVIS_SELECTED) - pswnd->RefreshThread(nmlv->iItem); - } - break; - } - } - return TRUE; - default: - return FALSE; - } -} - -LRESULT CALLBACK EditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - - switch (message) - { - case WM_CHAR: //Filter user input. - if (GetKeyState(VK_CONTROL) & 0x8000) - { - if (wParam == 1) - { - Edit_SetSel(hwndEdit, 0, -1); - SendMessage(hwndEdit, WM_COPY, 0, 0); - } - } - return 0; - case WM_LBUTTONUP: - if (hwndEdit) - SendMessage(hwndEdit, WM_COPY, 0, 0); - default: - { - return proc(hWnd, message, wParam, lParam); - } - - } -} - -LRESULT CALLBACK EditCmdProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) - { - case WM_KEYDOWN: - if (wParam == VK_UP) - { - SetWindowText(hWnd, last_cmd); - SetFocus(hWnd); - return 0; - } - break; - case WM_CHAR: - if (wParam == VK_RETURN) - { - DWORD s = 0, pid = 0; - WCHAR str[32]; - if (GetWindowTextLength(hWnd) == 0) - break; - GetWindowText(hWnd, last_cmd, CMD_SIZE); - //IthBreak(); - if (GetWindowText(hwndProcessComboBox, str, 32)) - pid = std::stoul(str); - ProcessCommand(last_cmd, pid); - Edit_SetSel(hWnd, 0, -1); - Edit_ReplaceSel(hWnd, &s); - SetFocus(hWnd); - return 0; - } - default: - break; - } - return CallWindowProc(proccmd, hWnd, message, wParam, lParam); -} - -void CreateButtons(HWND hWnd) -{ - hwndProcess = CreateWindow(L"Button", L"Process", WS_CHILD | WS_VISIBLE, - 0, 0, 0, 0, hWnd, 0, hIns, NULL); - hwndOption = CreateWindow(L"Button", L"Option", WS_CHILD | WS_VISIBLE, - 0, 0, 0, 0, hWnd, 0, hIns, NULL); - hwndClear = CreateWindow(L"Button", L"Clear", WS_CHILD | WS_VISIBLE, - 0, 0, 0, 0, hWnd, 0, hIns, NULL); - hwndSave = CreateWindow(L"Button", L"Save", WS_CHILD | WS_VISIBLE, - 0, 0, 0, 0, hWnd, 0, hIns, NULL); - hwndExtensions = CreateWindow(L"Button", L"Extensions", WS_CHILD | WS_VISIBLE, - 0, 0, 0, 0, hWnd, 0, hIns, NULL); - hwndRemoveHook = CreateWindow(L"Button", L"Unhook", WS_CHILD | WS_VISIBLE, - 0, 0, 0, 0, hWnd, 0, hIns, NULL); - hwndTop = CreateWindow(L"Button", L"Top", WS_CHILD | WS_VISIBLE | BS_PUSHLIKE | BS_CHECKBOX, - 0, 0, 0, 0, hWnd, 0, hIns, NULL); - hwndProcessComboBox = CreateWindow(L"ComboBox", NULL, - WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | - CBS_SORT | WS_VSCROLL | WS_TABSTOP, - 0, 0, 0, 0, hWnd, 0, hIns, NULL); - hwndCmd = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", NULL, - WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_AUTOHSCROLL, - 0, 0, 0, 0, hWnd, 0, hIns, NULL); - hwndEdit = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", NULL, - WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | WS_VSCROLL | - ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, - 0, 0, 0, 0, hWnd, 0, hIns, NULL); -} - -void ClickButton(HWND hWnd, HWND h) -{ - if (h == hwndProcess) - { - if (hProcDlg) - SetForegroundWindow(hProcDlg); - else - hProcDlg = CreateDialog(hIns, (LPWSTR)IDD_DIALOG2, 0, ProcessDlgProc); - } - else if (h == hwndOption) - { - if (hOptionDlg) - SetForegroundWindow(hOptionDlg); - else - hOptionDlg = CreateDialog(hIns, (LPWSTR)IDD_DIALOG4, 0, OptionDlgProc); - } - else if (h == hwndClear) - { - man->ClearCurrent(); - } - else if (h == hwndTop) - { - if (Button_GetCheck(h) == BST_CHECKED) - { - Button_SetCheck(h, BST_UNCHECKED); - SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); - if (hProcDlg) - SetWindowPos(hProcDlg, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); - if (hOptionDlg) - SetWindowPos(hOptionDlg, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); - } - else - { - Button_SetCheck(h, BST_CHECKED); - SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); - if (hProcDlg) - SetWindowPos(hProcDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); - if (hOptionDlg) - SetWindowPos(hOptionDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); - } - } - else if (h == hwndSave) - { - WCHAR str[32]; - if (GetWindowText(hwndCombo, str, 32)) - { - TextThread* current = man->FindSingle(std::stoul(str, nullptr, 16)); - SaveProcessProfile(current); - } - pfman->SaveProfiles(); - } - else if (h == hwndExtensions) - { - man->AddConsoleOutput(L"GUI for managing your extensions coming soon. Please do it manually for now."); - } - else if (h == hwndRemoveHook) - { - WCHAR str[32]; - if (GetWindowText(hwndCombo, str, 32)) - { - std::wstring entry(str); - std::size_t i; - DWORD threadNumber = std::stoul(entry, &i, 16); - entry = entry.substr(i + 1); - DWORD pid = std::stoul(entry, &i); - entry = entry.substr(i + 1); - DWORD addr = std::stoul(entry, NULL, 16); - if (threadNumber != 0) - RemoveHook(pid, addr); - } - } -} - -std::wstring ThreadOutput(TextThread* thread, std::wstring output) -{ - if (thread->Status() & CURRENT_SELECT) texts->AddText(output, false); - return output; -} - -bool GetHookParam(DWORD pid, DWORD hook_addr, HookParam& hp) -{ - if (!pid) - return false; - hp = man->GetHookParam(pid, hook_addr); - return true; -} - -std::wstring CreateEntryWithLink(ThreadParameter tp, std::wstring& entry) -{ - std::wstring entryWithLink = entry; - if (tp.pid == 0) - entryWithLink += L"ConsoleOutput"; - HookParam hp = {}; - if (GetHookParam(tp.pid, tp.hook, hp)) - entryWithLink += L" (" + GetCode(hp, tp.pid) + L")"; - return entryWithLink; -} - -void AddToCombo(TextThread& thread, bool replace) -{ - std::wstring entry = GetEntryString(&thread); - std::wstring entryWithLink = CreateEntryWithLink(thread.GetThreadParameter(), entry); - int i = ComboBox_FindString(hwndCombo, -1, entry.c_str()); - if (replace) - { - int sel = ComboBox_GetCurSel(hwndCombo); - if (i != CB_ERR) - ComboBox_DeleteString(hwndCombo, i); - ComboBox_AddString(hwndCombo, entryWithLink.c_str()); - ComboBox_SetCurSel(hwndCombo, sel); - } - else - { - if (i == CB_ERR) - ComboBox_AddString(hwndCombo, entryWithLink.c_str()); - // Why set current selection to 0 when the new thread is selected? - if (thread.Status() & CURRENT_SELECT) - ComboBox_SetCurSel(hwndCombo, 0); - } -} - -void ThreadRemove(TextThread* thread) -{ - std::wstring entry = GetEntryString(thread); - if (thread->GetThreadParameter().pid == 0) - entry += L"ConsoleOutput"; - int i = ComboBox_FindString(hwndCombo, 0, entry.c_str()); - if (i != CB_ERR) - { - if (ComboBox_DeleteString(hwndCombo, i) == CB_ERR) - ConsoleOutput(ErrorDeleteCombo); - } -} - -void SetEditText(LPCWSTR wc) -{ - DWORD line; - Edit_SetText(hwndEdit, wc); - line = Edit_GetLineCount(hwndEdit); - SendMessage(hwndEdit, EM_LINESCROLL, 0, line); -} - -void ThreadReset(TextThread* thread) -{ - texts->ClearBuffer(); - man->SetCurrent(thread);; - - std::wstring text = thread->GetStore(); - SetEditText(text.c_str()); - - WCHAR buffer[16]; - std::swprintf(buffer, L"%04X", thread->Number()); - DWORD tmp = ComboBox_FindString(hwndCombo, 0, buffer); - if (tmp != CB_ERR) - ComboBox_SetCurSel(hwndCombo, tmp); -} - -bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook); - -void ThreadCreate(TextThread* thread) -{ - thread->RegisterOutputCallBack(ThreadOutput); - //thread->RegisterFilterCallBack(ThreadFilter, 0); - AddToCombo(*thread, false); - auto tp = thread->GetThreadParameter(); - auto hook = man->GetHookParam(tp.pid, tp.hook); - if (hook.type & USING_UNICODE) thread->Status() |= USING_UNICODE; - auto pf = pfman->GetProfile(tp.pid); - if (!pf) - return; - const std::wstring& hook_name = man->GetHookName(tp.pid, tp.hook); - auto thread_profile = pf->FindThread(&thread->GetThreadParameter(), hook_name); - if (thread_profile != pf->Threads().end()) - { - (*thread_profile)->HookManagerIndex() = thread->Number(); - auto thread_index = thread_profile - pf->Threads().begin(); - if (pf->IsThreadSelected(thread_profile)) - ThreadReset(thread); - } -} - -bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook) -{ - bool res = false; - WaitForSingleObject(pr.hookman_mutex, 0); - auto hooks = (const Hook*)pr.hookman_map; - for (DWORD i = 0; i < MAX_HOOK; i++) - { - if (hooks[i].Address() == hook) - { - res = hooks[i].Type() & USING_UNICODE; - break; - } - } - ReleaseMutex(pr.hookman_mutex); - return res; -} - -void RegisterProcess(DWORD pid) -{ - auto path = GetProcessPath(pid); - if (!path.empty()) - { - WCHAR str[MAX_PATH]; - std::swprintf(str, L"%04d:%s", pid, path.substr(path.rfind(L'\\') + 1).c_str()); - ComboBox_AddString(hwndProcessComboBox, str); - if (ComboBox_GetCount(hwndProcessComboBox) == 1) - ComboBox_SetCurSel(hwndProcessComboBox, 0); - } - Profile* pf = pfman->GetProfile(pid); - if (pf) - { - for (auto i = pf->Hooks().begin(); i != pf->Hooks().end(); ++i) - { - InsertHook(pid, i->get()->HP(), toMultiByteString(i->get()->Name())); - } - } -} - -void RemoveProcessList(DWORD pid) -{ - WCHAR str[MAX_PATH]; - std::swprintf(str, L"%04d", pid); - DWORD i = ComboBox_FindString(hwndProcessComboBox, 0, str); - DWORD j = ComboBox_GetCurSel(hwndProcessComboBox); - if (i != CB_ERR) - { - DWORD k = ComboBox_DeleteString(hwndProcessComboBox, i); - if (i == j) - ComboBox_SetCurSel(hwndProcessComboBox, 0); - } -} - -LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) - { - case WM_CREATE: - CreateButtons(hWnd); - // Add text to the window. - Edit_LimitText(hwndEdit, -1); - SendMessage(hwndEdit, WM_INPUTLANGCHANGEREQUEST, 0, 0x411); - proc = (WNDPROC)SetWindowLong(hwndEdit, GWL_WNDPROC, (LONG)EditProc); - proccmd = (WNDPROC)SetWindowLong(hwndCmd, GWL_WNDPROC, (LONG)EditCmdProc); - hwndCombo = CreateWindow(L"ComboBox", NULL, - WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | - CBS_SORT | WS_VSCROLL | WS_TABSTOP, - 0, 0, 0, 0, hWnd, 0, hIns, NULL); - { - HDC hDC = GetDC(hWnd); - int nHeight = -MulDiv(12, GetDeviceCaps(hDC, LOGPIXELSY), 72); - ReleaseDC(hWnd, hDC); - HFONT hf = CreateFont(nHeight, 0, 0, 0, FW_LIGHT, FALSE, FALSE, FALSE, SHIFTJIS_CHARSET, - OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, - L"MS Gothic"); - hWhiteBrush = GetStockBrush(WHITE_BRUSH); - SendMessage(hwndCmd, WM_SETFONT, (WPARAM)hf, 0); - SendMessage(hwndEdit, WM_SETFONT, (WPARAM)hf, 0); - SendMessage(hwndCombo, WM_SETFONT, (WPARAM)hf, 0); - SendMessage(hwndProcessComboBox, WM_SETFONT, (WPARAM)hf, 0); - texts = new TextBuffer(hwndEdit); - man->RegisterThreadCreateCallback(ThreadCreate); - man->RegisterThreadRemoveCallback(ThreadRemove); - man->RegisterThreadResetCallback(ThreadReset); - TextThread* console = man->FindSingle(0); - console->RegisterOutputCallBack(ThreadOutput); - AddToCombo(*console, false); - man->RegisterProcessAttachCallback(RegisterProcess); - man->RegisterProcessDetachCallback(RemoveProcessList); - OpenHost(); - { - static const WCHAR program_name[] = L"NextHooker beta v"; - //static const WCHAR program_version[] = L"3.0"; - static WCHAR version_info[256]; - std::swprintf(version_info, L"%s%s (%s)", program_name, program_version, build_date); - man->AddConsoleOutput(version_info); - man->AddConsoleOutput(InitMessage); - } - - if (background == 0) - man->AddConsoleOutput(BackgroundMsg); - } - - return 0; - case WM_COMMAND: - { - DWORD wmId, wmEvent, dwId; - wmId = LOWORD(wParam); - wmEvent = HIWORD(wParam); - switch (wmEvent) - { - case EN_VSCROLL: - { - SCROLLBARINFO info = { sizeof(info) }; - GetScrollBarInfo(hwndEdit, OBJID_VSCROLL, &info); - InvalidateRect(hwndEdit, 0, 1); - ValidateRect(hwndEdit, &info.rcScrollBar); - RedrawWindow(hwndEdit, 0, 0, RDW_ERASE); - } - break; - case CBN_SELENDOK: - { - if ((HWND)lParam == hwndProcessComboBox) - return 0; - dwId = ComboBox_GetCurSel(hwndCombo); - int len = ComboBox_GetLBTextLen(hwndCombo, dwId); - if (len > 0) - { - LPWSTR pwcEntry = new WCHAR[len + 1]; - len = ComboBox_GetLBText(hwndCombo, dwId, pwcEntry); - DWORD num = std::stoul(pwcEntry, NULL, 16); - man->SelectCurrent(num); - delete[] pwcEntry; - } - } - return 0; - case BN_CLICKED: - ClickButton(hWnd, (HWND)lParam); - break; - default: - break; - } - } - break; - case WM_SETFOCUS: - SetFocus(hwndEdit); - return 0; - case WM_SIZE: - { - WORD width = LOWORD(lParam); - WORD height = HIWORD(lParam); - DWORD l = width / 7; - WORD h = HIWORD(GetDialogBaseUnits()); // height of the system font - h = h + (h / 2); - HDC hDC = GetDC(hWnd); - RECT rc; - GetClientRect(hWnd, &rc); - FillRect(hDC, &rc, hWhiteBrush); - ReleaseDC(hWnd, hDC); - MoveWindow(hwndProcess, 0, 0, l, h, TRUE); - MoveWindow(hwndOption, l * 1, 0, l, h, TRUE); - MoveWindow(hwndTop, l * 2, 0, l, h, TRUE); - MoveWindow(hwndClear, l * 3, 0, l, h, TRUE); - MoveWindow(hwndExtensions, l * 4, 0, l, h, TRUE); - MoveWindow(hwndRemoveHook, l * 5, 0, l, h, TRUE); - MoveWindow(hwndSave, l * 6, 0, width - 6 * l, h, TRUE); - l *= 2; - MoveWindow(hwndProcessComboBox, 0, h, l, 200, TRUE); - MoveWindow(hwndCmd, l, h, width - l, h, TRUE); - MoveWindow(hwndCombo, 0, h * 2, width, 200, TRUE); - h *= 3; - MoveWindow(hwndEdit, 0, h, width, height - h, TRUE); - } - return 0; - case WM_DESTROY: - man->RegisterThreadCreateCallback(0); - man->RegisterThreadRemoveCallback(0); - man->RegisterThreadResetCallback(0); - man->RegisterProcessAttachCallback(0); - man->RegisterProcessDetachCallback(0); - //delete texts; - SaveSettings(); - PostQuitMessage(0); - return 0; - default: - return DefWindowProc(hWnd, message, wParam, lParam); - } - return 0; -} - diff --git a/oldgui/window.h b/oldgui/window.h deleted file mode 100644 index 68fc565..0000000 --- a/oldgui/window.h +++ /dev/null @@ -1,20 +0,0 @@ -/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) - * This file is part of the Interactive Text Hooker. - - * Interactive Text Hooker is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "ITH.h" From 2905ed4f9c743131f3eafd68bec930833684df88 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 21 Jul 2018 14:23:59 -0700 Subject: [PATCH 17/29] update config --- CMakeLists.txt | 71 -------------------------------------------------- 1 file changed, 71 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8eb1b0a..13d1736 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,80 +47,9 @@ add_definitions( include_directories( . - texthook - ${CMAKE_BINARY_DIR}/oldgui ) -set(resource_src - oldgui/ITHVNR.rc - oldgui/icon1.ico -) - -set(nexthooker_src - oldgui/command.cpp - oldgui/ITH.h - oldgui/language.cpp - oldgui/language.h - oldgui/main.cpp - oldgui/ProcessWindow.cpp - oldgui/ProcessWindow.h - oldgui/ProfileManager.cpp - oldgui/ProfileManager.h - oldgui/resource.h - oldgui/utility.cpp - oldgui/utility.h - ${CMAKE_BINARY_DIR}/oldgui/version.h - oldgui/version.h.in - oldgui/window.cpp - oldgui/window.h - oldgui/TextBuffer.cpp - oldgui/TextBuffer.h - oldgui/profile/Profile.h - oldgui/profile/Profile.cpp - oldgui/profile/pugiconfig.h - oldgui/profile/pugixml.cpp - oldgui/profile/pugixml.h - oldgui/profile/misc.h - oldgui/profile/misc.cpp - ${resource_src} -) - -source_group("Resource Files" FILES ${resource_src}) - -add_executable(${PROJECT_NAME} ${nexthooker_src}) - add_subdirectory(texthook) add_subdirectory(vnrhook) # add_subdirectory(profile) - -set_target_properties(${PROJECT_NAME} PROPERTIES - LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFESTDEPENDENCY:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"" -) - -target_compile_definitions(${PROJECT_NAME} - PRIVATE - PSAPI_VERSION=1 - DEFAULT_MM -) - -target_link_libraries(${PROJECT_NAME} - #profile - vnrhost - #ithsys - #${WDK_HOME}/lib/wxp/i386/ntdll.lib - comctl32.lib - psapi.lib -) - -target_compile_options(${PROJECT_NAME} - PRIVATE - /EHsc -) - -install(TARGETS ${PROJECT_NAME} - DESTINATION . - CONFIGURATIONS Release -) - -configure_file(oldgui/version.h.in oldgui/version.h) From 4543a86e486240a00c4395f99606ee14f9d06cf9 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 21 Jul 2018 15:11:28 -0700 Subject: [PATCH 18/29] give up on c# interop (callbacks too difficult). trying qt instead --- .gitignore | 109 ------------------ GUI.sln | 34 ------ GUI/App.config | 6 - GUI/Form1.Designer.cs | 40 ------- GUI/Form1.cs | 20 ---- GUI/GUI.csproj | 103 ----------------- GUI/Program.cs | 23 ---- GUI/Properties/AssemblyInfo.cs | 36 ------ GUI/Properties/Resources.Designer.cs | 71 ------------ GUI/Properties/Resources.resx | 117 ------------------- GUI/Properties/Settings.Designer.cs | 30 ----- GUI/Properties/Settings.settings | 7 -- HostInterop/AssemblyInfo.cpp | Bin 1476 -> 0 bytes HostInterop/HostInterop.cpp | Bin 4858 -> 0 bytes HostInterop/HostInterop.h | 66 ----------- HostInterop/HostInterop.vcxproj | 147 ------------------------ HostInterop/HostInterop.vcxproj.filters | 26 ----- 17 files changed, 835 deletions(-) delete mode 100644 GUI.sln delete mode 100644 GUI/App.config delete mode 100644 GUI/Form1.Designer.cs delete mode 100644 GUI/Form1.cs delete mode 100644 GUI/GUI.csproj delete mode 100644 GUI/Program.cs delete mode 100644 GUI/Properties/AssemblyInfo.cs delete mode 100644 GUI/Properties/Resources.Designer.cs delete mode 100644 GUI/Properties/Resources.resx delete mode 100644 GUI/Properties/Settings.Designer.cs delete mode 100644 GUI/Properties/Settings.settings delete mode 100644 HostInterop/AssemblyInfo.cpp delete mode 100644 HostInterop/HostInterop.cpp delete mode 100644 HostInterop/HostInterop.h delete mode 100644 HostInterop/HostInterop.vcxproj delete mode 100644 HostInterop/HostInterop.vcxproj.filters diff --git a/.gitignore b/.gitignore index 44eddfa..92576db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,111 +1,2 @@ -# Build Folders (you can keep bin if you'd like, to store dlls and pdbs) Builds/ -[Bb]in/ -[Oo]bj/ - -# mstest test results -TestResults - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.sln.docstates *.vs/ - -# Build results -[Dd]ebug/ -[Rr]elease/ -x64/ -*_i.c -*_p.c -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.log -*.vspscc -*.vssscc -.builds - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opensdf -*.sdf - -# Visual Studio profiler -*.psess -*.vsp -*.vspx - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper* - -# NCrunch -*.ncrunch* -.*crunch*.local.xml - -# Installshield output folder -[Ee]xpress - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish - -# Publish Web Output -*.Publish.xml - -# NuGet Packages Directory -packages - -# Windows Azure Build Output -csx -*.build.csdef - -# Windows Store app package directory -AppPackages/ - -# Others -[Bb]in -[Oo]bj -sql -TestResults -[Tt]est[Rr]esult* -*.Cache -ClientBin -[Ss]tyle[Cc]op.* -~$* -*.dbmdl -Generated_Code #added for RIA/Silverlight projects - -# Backup & report files from converting an old project file to a newer -# Visual Studio version. Backup files are not needed, because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -WinForms/AdamsLair.WinForms.xml diff --git a/GUI.sln b/GUI.sln deleted file mode 100644 index 22cde48..0000000 --- a/GUI.sln +++ /dev/null @@ -1,34 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.2042 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUI", "GUI\GUI.csproj", "{D847D0DC-78F5-4722-8E06-1A124B957353}" - ProjectSection(ProjectDependencies) = postProject - {0DF42D0B-0F87-475E-9E2C-9E32776B2AD5} = {0DF42D0B-0F87-475E-9E2C-9E32776B2AD5} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HostInterop", "HostInterop\HostInterop.vcxproj", "{0DF42D0B-0F87-475E-9E2C-9E32776B2AD5}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.ActiveCfg = Debug|x86 - {D847D0DC-78F5-4722-8E06-1A124B957353}.Debug|x86.Build.0 = Debug|x86 - {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|x86.ActiveCfg = Release|x86 - {D847D0DC-78F5-4722-8E06-1A124B957353}.Release|x86.Build.0 = Release|x86 - {0DF42D0B-0F87-475E-9E2C-9E32776B2AD5}.Debug|x86.ActiveCfg = Debug|Win32 - {0DF42D0B-0F87-475E-9E2C-9E32776B2AD5}.Debug|x86.Build.0 = Debug|Win32 - {0DF42D0B-0F87-475E-9E2C-9E32776B2AD5}.Release|x86.ActiveCfg = Release|Win32 - {0DF42D0B-0F87-475E-9E2C-9E32776B2AD5}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {F2661015-ED1B-45F8-8277-D2E3E26A75DA} - EndGlobalSection -EndGlobal diff --git a/GUI/App.config b/GUI/App.config deleted file mode 100644 index 8324aa6..0000000 --- a/GUI/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/GUI/Form1.Designer.cs b/GUI/Form1.Designer.cs deleted file mode 100644 index 1442a4c..0000000 --- a/GUI/Form1.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace GUI -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} - diff --git a/GUI/Form1.cs b/GUI/Form1.cs deleted file mode 100644 index 39c7c76..0000000 --- a/GUI/Form1.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace GUI -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} diff --git a/GUI/GUI.csproj b/GUI/GUI.csproj deleted file mode 100644 index 16eeb66..0000000 --- a/GUI/GUI.csproj +++ /dev/null @@ -1,103 +0,0 @@ - - - - - Debug - AnyCPU - {D847D0DC-78F5-4722-8E06-1A124B957353} - WinExe - GUI - GUI - v4.6 - 512 - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - true - ..\Builds\Debug\Debug\ - DEBUG;TRACE - full - x86 - prompt - MinimumRecommendedRules.ruleset - true - - - ..\Builds\Release\Release\ - TRACE - true - pdbonly - x86 - prompt - MinimumRecommendedRules.ruleset - true - - - - False - ..\Builds\Debug\Debug\HostInterop.dll - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - \ No newline at end of file diff --git a/GUI/Program.cs b/GUI/Program.cs deleted file mode 100644 index c955516..0000000 --- a/GUI/Program.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace GUI -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - if (!HostInterop.Host.Start()) return; - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new Form1()); - } - } -} diff --git a/GUI/Properties/AssemblyInfo.cs b/GUI/Properties/AssemblyInfo.cs deleted file mode 100644 index 8c56606..0000000 --- a/GUI/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("GUI")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("GUI")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("d847d0dc-78f5-4722-8e06-1a124b957353")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/GUI/Properties/Resources.Designer.cs b/GUI/Properties/Resources.Designer.cs deleted file mode 100644 index e53846d..0000000 --- a/GUI/Properties/Resources.Designer.cs +++ /dev/null @@ -1,71 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace GUI.Properties -{ - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GUI.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; - } - set - { - resourceCulture = value; - } - } - } -} diff --git a/GUI/Properties/Resources.resx b/GUI/Properties/Resources.resx deleted file mode 100644 index af7dbeb..0000000 --- a/GUI/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/GUI/Properties/Settings.Designer.cs b/GUI/Properties/Settings.Designer.cs deleted file mode 100644 index 578d709..0000000 --- a/GUI/Properties/Settings.Designer.cs +++ /dev/null @@ -1,30 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace GUI.Properties -{ - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { - return defaultInstance; - } - } - } -} diff --git a/GUI/Properties/Settings.settings b/GUI/Properties/Settings.settings deleted file mode 100644 index 3964565..0000000 --- a/GUI/Properties/Settings.settings +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/HostInterop/AssemblyInfo.cpp b/HostInterop/AssemblyInfo.cpp deleted file mode 100644 index 58c692ea021eb3efc86bd57283c4df1d12172ace..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1476 zcmcJPPfNo<6vXE&_#Hw|>Y=D7si#s83Oy7py@-c2{bMj`LedKT^6Kwx6dH9)BauMZ zZ1QI2&6~~p+eW2gt+Z6YJyfbWJ2|U}RiFoLd0w+lb**C^sYyjoJ>65gP{KU|Csqc} zUy(M{8iuJf>;Y&gYGa3Apmwf8uRNRjvNfJN$7|RbZ0)cZN`#vQYS*}I{fwOcl%0~R z^6IM#^Zt>!*c+S1l%B1s|7~aN@XYT%xNC<$g~w)iqMZaKF+4-HrW-qiAkn;6#ff?WhaDZ>-DOs?h#ru&CqOV_qox|1k9rx?KXj> zp7-$m{4yX55fcx%E_JR8U6G7t2^hP%%v>5n)-T?En<5YJFQFfsyg$dgsE3}833vs$ z^{2Ii7hN3b>a6C7kD>YpO+YkMJC%0M7UU{nXTIa9A7RKprNYUnid`muPmi7FO%3$} DK>!Gs diff --git a/HostInterop/HostInterop.cpp b/HostInterop/HostInterop.cpp deleted file mode 100644 index 5667ebec5bbf01deff6c09cf4398b403c55d3e1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4858 zcmb_g+fExX5S`al>OZVNm6l%MovM{k=uK(?kw|@Ol_s#HXeoxnl|RyWf`G*jTKKmB=s-scLq$Rg%=?pt>)nlHb?Vjx8??%5*N*!(3IKt@}R<5BdhlUv}8yi0~ z1AHoEN44GQXQM_VBQ?Xhclh@eQQunNXu!%GJ7cVOU}3V58LPEI-pG3)nkJ&n{R5Th zz@HqpjV}#3hXhYD8(m1|p4L9riQDSm!8eQMmE!4_dd&J9`;<3N#>RaaHRglWbfS0| z>r4@b@k(cnv5wEbfClSt7|#&To!l>wIy9+lr)@LiHc!{E*GKL%MZ-EEvSU=t89ZlX zGstG;iq&Xho&g87HRPAZVo=e;T4Anco~F4smGF@C}#=Zd+ z<5l4NL|Dc41ik-+)mfRHP}W5x^rJwvjd1Q}p)0nMdwLn?G#2V-HLr>+Ip8fyI>rAP^2qGk2!)vGYn>i@b~TzqjhN}U$DpaxlR~oxCQnx${bF^n z_H3k6k$LUq%kSe0M2>sRlwn4Gv%KfHwLY>j0^jr(4=; zXvtQE@r+f@{%2*@d*nEcb$kN9$nc?0w!;kLhg{)%<6DirSZ2v;4?PIkJ)I5mI_LQW zv!4^0$mqA@V!t{V$+{D+S{vvZfBL1Zw_uI-$+T7baYfi#s3NMke2#7c3eQe8xb_p8IP<*M$AQPuTW6-kPpL$6ziuAO7+_W3$Eii-wN~v`H(E`Plu**3@6SRdcK3Y9f0e3zqy2NU-E-a zUb0bDE`#)WS;t*Zqj~q?uHR1&N9RxGs@2WVkx}g};wjMa-V;Y;b8JT6Cdk6u=mAji zjf8K8apXeThh~;lVs$FPSKkIsplitDelay = splitDelay; } - }; - - public ref class TextThread - { - internal: - TextThread(::TextThread* native); - ::TextThread* native; - }; - - public ref class HookParam - { - internal: - HookParam(::HookParam native); - }; - - public ref class ThreadParam - { - - }; -} \ No newline at end of file diff --git a/HostInterop/HostInterop.vcxproj b/HostInterop/HostInterop.vcxproj deleted file mode 100644 index 9bf83ac..0000000 --- a/HostInterop/HostInterop.vcxproj +++ /dev/null @@ -1,147 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {0DF42D0B-0F87-475E-9E2C-9E32776B2AD5} - v4.6 - ManagedCProj - HostInterop - 10.0.17134.0 - - - - DynamicLibrary - true - v141 - true - Unicode - - - DynamicLibrary - false - v141 - true - Unicode - - - DynamicLibrary - true - v141 - true - Unicode - - - DynamicLibrary - false - v141 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - $(SolutionDir)\Builds\$(Configuration)\$(Configuration)\ - - - true - - - false - $(SolutionDir)\Builds\$(Configuration)\$(Configuration)\ - - - false - - - - Level3 - Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) - NotUsing - - - ..\Builds\Debug\texthook\vnrhost.lib;%(AdditionalDependencies) - - - - - Level3 - Disabled - _DEBUG;%(PreprocessorDefinitions) - Use - - - - - - - - Level3 - WIN32;NDEBUG;%(PreprocessorDefinitions) - Use - - - - - - - - Level3 - NDEBUG;%(PreprocessorDefinitions) - Use - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/HostInterop/HostInterop.vcxproj.filters b/HostInterop/HostInterop.vcxproj.filters deleted file mode 100644 index 68e44a6..0000000 --- a/HostInterop/HostInterop.vcxproj.filters +++ /dev/null @@ -1,26 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {344ee2d7-eda2-4003-8c18-092ff651deb3} - h;hh;hpp;hxx;hm;inl;inc;ipp;xsd - - - - - Source Files - - - Source Files - - - - - Header Files - - - \ No newline at end of file From fd824713036516a70e9c250b6279e779708ee5e1 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 21 Jul 2018 15:30:42 -0700 Subject: [PATCH 19/29] remove unneeded stuff --- texthook/hookman.h | 4 ++-- texthook/textthread.h | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/texthook/hookman.h b/texthook/hookman.h index c5e939a..f8c3c02 100644 --- a/texthook/hookman.h +++ b/texthook/hookman.h @@ -19,8 +19,8 @@ struct ProcessRecord HANDLE hostPipe; }; -typedef void(__stdcall *ProcessEventCallback)(DWORD pid); -typedef void(__stdcall *ThreadEventCallback)(TextThread*); +typedef void(*ProcessEventCallback)(DWORD pid); +typedef void(*ThreadEventCallback)(TextThread*); struct ThreadParameterHasher { diff --git a/texthook/textthread.h b/texthook/textthread.h index 321096f..a541971 100644 --- a/texthook/textthread.h +++ b/texthook/textthread.h @@ -22,10 +22,8 @@ struct ThreadParameter } }; -#define CURRENT_SELECT 0x1000 - class TextThread; -typedef std::wstring(__stdcall *ThreadOutputCallback)(TextThread*, std::wstring data); +typedef std::wstring(*ThreadOutputCallback)(TextThread*, std::wstring data); //extern DWORD split_time,repeat_count,global_filter,cyclic_remove; From a01e7c70a8e0f5e70dfa76cd461c3b6cc9cd351a Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 21 Jul 2018 15:40:16 -0700 Subject: [PATCH 20/29] add qt gui --- GUI/GUI.pro | 38 ++++++ GUI/GUI.pro.user | 322 +++++++++++++++++++++++++++++++++++++++++++++ GUI/main.cpp | 11 ++ GUI/mainwindow.cpp | 14 ++ GUI/mainwindow.h | 22 ++++ GUI/mainwindow.ui | 49 +++++++ README.md | 3 +- 7 files changed, 457 insertions(+), 2 deletions(-) create mode 100644 GUI/GUI.pro create mode 100644 GUI/GUI.pro.user create mode 100644 GUI/main.cpp create mode 100644 GUI/mainwindow.cpp create mode 100644 GUI/mainwindow.h create mode 100644 GUI/mainwindow.ui diff --git a/GUI/GUI.pro b/GUI/GUI.pro new file mode 100644 index 0000000..4dede0a --- /dev/null +++ b/GUI/GUI.pro @@ -0,0 +1,38 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2018-07-21T15:14:19 +# +#------------------------------------------------- + +QT += core gui widgets + +TARGET = GUI +TEMPLATE = app + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +CONFIG += c++11 + +SOURCES += \ + main.cpp \ + mainwindow.cpp + +HEADERS += \ + mainwindow.h + +FORMS += \ + mainwindow.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/GUI/GUI.pro.user b/GUI/GUI.pro.user new file mode 100644 index 0000000..faa1193 --- /dev/null +++ b/GUI/GUI.pro.user @@ -0,0 +1,322 @@ + + + + + + EnvironmentId + {55d86bc4-98d6-4dfb-871d-bc86f1163818} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + -fno-delayed-template-parsing + + true + + + + ProjectExplorer.Project.Target.0 + + Desktop Qt 5.11.0 MSVC2015 32bit + Desktop Qt 5.11.0 MSVC2015 32bit + qt.qt5.5110.win32_msvc2015_kit + 0 + 0 + 0 + + C:/Users/Akash/Documents/Storage/Code/NextHooker/Builds/Debug/ + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + true + + + C:/Users/Akash/Documents/Storage/Code/NextHooker/Builds/Release + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + + false + false + true + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + + C:/Users/Akash/Documents/Storage/Code/NextHooker/build-GUI-Desktop_Qt_5_11_0_MSVC2015_32bit-Profile + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + true + true + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Profile + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + 3 + + + 0 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy Configuration + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + GUI + + Qt4ProjectManager.Qt4RunConfiguration:C:/Users/Akash/Documents/Storage/Code/NextHooker/GUI/GUI.pro + true + + GUI.pro + + C:/Users/Akash/Documents/Storage/Code/NextHooker/Builds/Debug + 3768 + false + true + false + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 18 + + + Version + 18 + + diff --git a/GUI/main.cpp b/GUI/main.cpp new file mode 100644 index 0000000..b48f94e --- /dev/null +++ b/GUI/main.cpp @@ -0,0 +1,11 @@ +#include "mainwindow.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp new file mode 100644 index 0000000..49d64fc --- /dev/null +++ b/GUI/mainwindow.cpp @@ -0,0 +1,14 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/GUI/mainwindow.h b/GUI/mainwindow.h new file mode 100644 index 0000000..9353441 --- /dev/null +++ b/GUI/mainwindow.h @@ -0,0 +1,22 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow *ui; +}; + +#endif // MAINWINDOW_H diff --git a/GUI/mainwindow.ui b/GUI/mainwindow.ui new file mode 100644 index 0000000..50dc006 --- /dev/null +++ b/GUI/mainwindow.ui @@ -0,0 +1,49 @@ + + + MainWindow + + + + 0 + 0 + 496 + 376 + + + + NextHooker + + + + + + 0 + 0 + 496 + 21 + + + + + NextHooker + + + + + + + NextHooker + + + TopToolBarArea + + + false + + + + + + + + diff --git a/README.md b/README.md index 087ea17..49e60d9 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,7 @@ GPL v3 ## Compiling -Before compiling *NextHooker*, you should get Visual Studio with CMake, ATL, and .NET 4.6.
-You should first compile all the CMake projects, then compile the *GUI* solution. +Before compiling *NextHooker*, you should get Visual Studio with CMake and ATL support, as well as Qt version 5.11
## Project Architecture From ff6dd7c7d8939aece61a40f7d3ded1643949d2f2 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sun, 22 Jul 2018 16:53:51 -0700 Subject: [PATCH 21/29] link qt with vnrhost --- .gitignore | 1 + GUI/GUI.pro | 4 ++++ GUI/mainwindow.cpp | 12 ++++++++++++ GUI/mainwindow.h | 2 ++ GUI/mainwindow.ui | 40 ++++++++++++++++++++++------------------ 5 files changed, 41 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 92576db..5900057 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ Builds/ *.vs/ +*.pro.user \ No newline at end of file diff --git a/GUI/GUI.pro b/GUI/GUI.pro index 4dede0a..dcccb00 100644 --- a/GUI/GUI.pro +++ b/GUI/GUI.pro @@ -32,7 +32,11 @@ HEADERS += \ FORMS += \ mainwindow.ui +win32: LIBS += \ + -L$$PWD/../Builds/Debug/texthook/ -lvnrhost + # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target + diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index 49d64fc..2917b96 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -1,14 +1,26 @@ #include "mainwindow.h" #include "ui_mainwindow.h" +#include "QMessageBox" +#include "qlineedit.h" +#include +#include "../texthook/host.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); + connect(ui->centralWidget->children().at(0), SIGNAL(returnPressed()), this, SLOT(onCommand())); + StartHost(); } MainWindow::~MainWindow() { delete ui; } + +void MainWindow::onCommand() +{ + QLineEdit* lineEdit = (QLineEdit*)sender(); + QMessageBox::information(this, "called", lineEdit->text()); +} diff --git a/GUI/mainwindow.h b/GUI/mainwindow.h index 9353441..a4af74b 100644 --- a/GUI/mainwindow.h +++ b/GUI/mainwindow.h @@ -15,6 +15,8 @@ public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); +public slots: + void onCommand(); private: Ui::MainWindow *ui; }; diff --git a/GUI/mainwindow.ui b/GUI/mainwindow.ui index 50dc006..d260328 100644 --- a/GUI/mainwindow.ui +++ b/GUI/mainwindow.ui @@ -13,7 +13,28 @@ NextHooker - + + + + + 252 + 0 + 241 + 20 + + + + + + + 3 + 40 + 491 + 291 + + + + @@ -23,23 +44,6 @@ 21 - - - NextHooker - - - - - - - NextHooker - - - TopToolBarArea - - - false - From 89f8cb9d9b57901c0a408acb669a187f8d06115e Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sun, 22 Jul 2018 16:56:33 -0700 Subject: [PATCH 22/29] Delete GUI.pro.user --- GUI/GUI.pro.user | 322 ----------------------------------------------- 1 file changed, 322 deletions(-) delete mode 100644 GUI/GUI.pro.user diff --git a/GUI/GUI.pro.user b/GUI/GUI.pro.user deleted file mode 100644 index faa1193..0000000 --- a/GUI/GUI.pro.user +++ /dev/null @@ -1,322 +0,0 @@ - - - - - - EnvironmentId - {55d86bc4-98d6-4dfb-871d-bc86f1163818} - - - ProjectExplorer.Project.ActiveTarget - 0 - - - ProjectExplorer.Project.EditorSettings - - true - false - true - - Cpp - - CppGlobal - - - - QmlJS - - QmlJSGlobal - - - 2 - UTF-8 - false - 4 - false - 80 - true - true - 1 - true - false - 0 - true - true - 0 - 8 - true - 1 - true - true - true - false - - - - ProjectExplorer.Project.PluginSettings - - - -fno-delayed-template-parsing - - true - - - - ProjectExplorer.Project.Target.0 - - Desktop Qt 5.11.0 MSVC2015 32bit - Desktop Qt 5.11.0 MSVC2015 32bit - qt.qt5.5110.win32_msvc2015_kit - 0 - 0 - 0 - - C:/Users/Akash/Documents/Storage/Code/NextHooker/Builds/Debug/ - - - true - qmake - - QtProjectManager.QMakeBuildStep - true - - false - false - false - - - true - Make - - Qt4ProjectManager.MakeStep - - false - - - - 2 - Build - - ProjectExplorer.BuildSteps.Build - - - - true - Make - - Qt4ProjectManager.MakeStep - - true - clean - - - 1 - Clean - - ProjectExplorer.BuildSteps.Clean - - 2 - false - - Debug - Debug - Qt4ProjectManager.Qt4BuildConfiguration - 2 - true - - - C:/Users/Akash/Documents/Storage/Code/NextHooker/Builds/Release - - - true - qmake - - QtProjectManager.QMakeBuildStep - false - - false - false - true - - - true - Make - - Qt4ProjectManager.MakeStep - - false - - - - 2 - Build - - ProjectExplorer.BuildSteps.Build - - - - true - Make - - Qt4ProjectManager.MakeStep - - true - clean - - - 1 - Clean - - ProjectExplorer.BuildSteps.Clean - - 2 - false - - Release - Release - Qt4ProjectManager.Qt4BuildConfiguration - 0 - true - - - C:/Users/Akash/Documents/Storage/Code/NextHooker/build-GUI-Desktop_Qt_5_11_0_MSVC2015_32bit-Profile - - - true - qmake - - QtProjectManager.QMakeBuildStep - true - - false - true - true - - - true - Make - - Qt4ProjectManager.MakeStep - - false - - - - 2 - Build - - ProjectExplorer.BuildSteps.Build - - - - true - Make - - Qt4ProjectManager.MakeStep - - true - clean - - - 1 - Clean - - ProjectExplorer.BuildSteps.Clean - - 2 - false - - Profile - Profile - Qt4ProjectManager.Qt4BuildConfiguration - 0 - true - - 3 - - - 0 - Deploy - - ProjectExplorer.BuildSteps.Deploy - - 1 - Deploy Configuration - - ProjectExplorer.DefaultDeployConfiguration - - 1 - - - false - false - 1000 - - true - - false - false - false - false - true - 0.01 - 10 - true - 1 - 25 - - 1 - true - false - true - valgrind - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - - 2 - - GUI - - Qt4ProjectManager.Qt4RunConfiguration:C:/Users/Akash/Documents/Storage/Code/NextHooker/GUI/GUI.pro - true - - GUI.pro - - C:/Users/Akash/Documents/Storage/Code/NextHooker/Builds/Debug - 3768 - false - true - false - false - true - - 1 - - - - ProjectExplorer.Project.TargetCount - 1 - - - ProjectExplorer.Project.Updater.FileVersion - 18 - - - Version - 18 - - From 3d4ee3d6988a08440a0fe16e1248f6156557e2fc Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sun, 22 Jul 2018 18:10:00 -0700 Subject: [PATCH 23/29] refactoring and bugfixes --- texthook/hookman.cc | 29 +++++++++-------------------- texthook/hookman.h | 7 +++---- texthook/host.cc | 2 +- texthook/textthread.cc | 3 ++- 4 files changed, 15 insertions(+), 26 deletions(-) diff --git a/texthook/hookman.cc b/texthook/hookman.cc index 2571940..61e230b 100644 --- a/texthook/hookman.cc +++ b/texthook/hookman.cc @@ -35,11 +35,14 @@ HookManager::HookManager() : HookManager::~HookManager() { - HM_LOCK; + EnterCriticalSection(&hmCs); + RemoveThreads([](auto one, auto two) { return true; }, {}); + for (auto i : processRecordsByIds) UnRegisterProcess(i.first); + LeaveCriticalSection(&hmCs); DeleteCriticalSection(&hmCs); } -TextThread *HookManager::FindSingle(DWORD number) +TextThread* HookManager::FindSingle(DWORD number) { HM_LOCK; for (auto i : textThreadsByParams) @@ -48,26 +51,12 @@ TextThread *HookManager::FindSingle(DWORD number) return nullptr; } -void HookManager::RemoveSingleHook(DWORD pid, DWORD addr) +void HookManager::RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadParameter cmp) { HM_LOCK; std::vector removedThreads; for (auto i : textThreadsByParams) - if (i.first.pid == pid && i.first.hook == addr) - { - if (remove) remove(i.second); - delete i.second; - removedThreads.push_back(i.first); - } - for (auto i : removedThreads) textThreadsByParams.erase(i); -} - -void HookManager::RemoveProcessContext(DWORD pid) -{ - HM_LOCK; - std::vector removedThreads; - for (auto i : textThreadsByParams) - if (i.first.pid == pid) + if (RemoveIf(i.first, cmp)) { if (remove) remove(i.second); delete i.second; @@ -98,7 +87,7 @@ void HookManager::UnRegisterProcess(DWORD pid) CloseHandle(pr.process_handle); CloseHandle(pr.hookman_section); processRecordsByIds.erase(pid); - RemoveProcessContext(pid); + RemoveThreads([](auto one, auto two) { return one.pid == two.pid; }, { pid, 0, 0, 0 }); if (detach) detach(pid); } @@ -149,7 +138,7 @@ HookParam HookManager::GetHookParam(DWORD pid, DWORD addr) std::wstring HookManager::GetHookName(DWORD pid, DWORD addr) { HM_LOCK; - std::string buffer; + std::string buffer = ""; ProcessRecord pr = processRecordsByIds[pid]; if (pr.hookman_map == nullptr) return L""; MutexLocker locker(pr.hookman_mutex); diff --git a/texthook/hookman.h b/texthook/hookman.h index f8c3c02..4d3037f 100644 --- a/texthook/hookman.h +++ b/texthook/hookman.h @@ -37,14 +37,13 @@ public: HookManager(); ~HookManager(); - TextThread *FindSingle(DWORD number); - HANDLE GetHostPipe(DWORD pid); + TextThread* FindSingle(DWORD number); void AddConsoleOutput(std::wstring text); void DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD split, const BYTE *text, int len); - void RemoveProcessContext(DWORD pid); // private - void RemoveSingleHook(DWORD pid, DWORD addr); + void RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadParameter cmp); void RegisterProcess(DWORD pid, HANDLE hostPipe); void UnRegisterProcess(DWORD pid); + HANDLE GetHostPipe(DWORD pid); HookParam GetHookParam(DWORD pid, DWORD addr); std::wstring GetHookName(DWORD pid, DWORD addr); diff --git a/texthook/host.cc b/texthook/host.cc index 8a691e0..e6667dc 100644 --- a/texthook/host.cc +++ b/texthook/host.cc @@ -151,7 +151,7 @@ DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr) WaitForSingleObject(hookRemovalEvent, 1000); CloseHandle(hookRemovalEvent); - man->RemoveSingleHook(pid, addr); + man->RemoveThreads([](auto one, auto two) { return one.pid == two.pid && one.hook == two.hook; }, { pid, addr, 0, 0 }); return true; } diff --git a/texthook/textthread.cc b/texthook/textthread.cc index 8f09409..d7ca42a 100644 --- a/texthook/textthread.cc +++ b/texthook/textthread.cc @@ -29,7 +29,8 @@ TextThread::TextThread(ThreadParameter tp, unsigned int threadNumber, unsigned i TextThread::~TextThread() { - TT_LOCK; + EnterCriticalSection(&ttCs); + LeaveCriticalSection(&ttCs); DeleteCriticalSection(&ttCs); } From e500c9ceedc6163936884e8dfc2838882ea99232 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sun, 22 Jul 2018 18:15:43 -0700 Subject: [PATCH 24/29] refactor --- texthook/hookman.cc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/texthook/hookman.cc b/texthook/hookman.cc index 61e230b..2df0b11 100644 --- a/texthook/hookman.cc +++ b/texthook/hookman.cc @@ -28,7 +28,6 @@ HookManager::HookManager() : processRecordsByIds() { InitializeCriticalSection(&hmCs); - // Console text thread (textThreadsByParams[{ 0, -1UL, -1UL, -1UL }] = new TextThread({ 0, -1UL, -1UL, -1UL }, nextThreadNumber++, splitDelay))->Status() |= USING_UNICODE; } @@ -94,8 +93,7 @@ void HookManager::UnRegisterProcess(DWORD pid) void HookManager::DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD spl, const BYTE *text, int len) { // jichi 20/27/2013: When PID is zero, the text comes from console, which I don't need - if (!text || !pid || len <= 0) - return; + if (!text || !pid || len <= 0) return; HM_LOCK; ThreadParameter tp = { pid, hook, retn, spl }; TextThread *it; @@ -111,8 +109,7 @@ void HookManager::DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD spl, con void HookManager::AddConsoleOutput(std::wstring text) { HM_LOCK; - TextThread *console = textThreadsByParams[{ 0, -1UL, -1UL, -1UL }]; - console->AddSentence(std::wstring(text)); + textThreadsByParams[{ 0, -1UL, -1UL, -1UL }]->AddSentence(std::wstring(text)); } HANDLE HookManager::GetHostPipe(DWORD pid) @@ -142,7 +139,6 @@ std::wstring HookManager::GetHookName(DWORD pid, DWORD addr) ProcessRecord pr = processRecordsByIds[pid]; if (pr.hookman_map == nullptr) return L""; MutexLocker locker(pr.hookman_mutex); - USES_CONVERSION; const Hook* hooks = (const Hook*)pr.hookman_map; for (int i = 0; i < MAX_HOOK; ++i) { @@ -152,6 +148,7 @@ std::wstring HookManager::GetHookName(DWORD pid, DWORD addr) ReadProcessMemory(pr.process_handle, hooks[i].Name(), &buffer[0], hooks[i].NameLength(), nullptr); } } + USES_CONVERSION; return std::wstring(A2W(buffer.c_str())); } From e12a35a609f9b17720442728ea8f76c16b962d48 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sun, 22 Jul 2018 18:33:45 -0700 Subject: [PATCH 25/29] update config --- CMakeLists.txt | 22 ---------------------- texthook/CMakeLists.txt | 19 +------------------ vnrhook/CMakeLists.txt | 28 ++-------------------------- 3 files changed, 3 insertions(+), 66 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 13d1736..eac5ce9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,35 +4,14 @@ set(CMAKE_CONFIGURATION_TYPES Debug Release) project(NextHooker) -set(CMAKE_INSTALL_PREFIX "" CACHE FILEPATH "installation path") - -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/Debug") -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/Debug") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release") -set(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION ON) - -execute_process(COMMAND "cmd" " /C date /T" OUTPUT_VARIABLE FULLDATE) -# set(DATE "07/13/2018") -string(SUBSTRING ${FULLDATE} 4 10 BUILD_DATE) -set(BUILD_DATE ${BUILD_DATE}) - -set(CPACK_GENERATOR "ZIP") -set(CPACK_PACKAGE_VERSION_MAJOR 1) -set(CPACK_PACKAGE_VERSION_MINOR 0) -set(CPACK_PACKAGE_VERSION_PATCH 4) -set(CPACK_SOURCE_GENERATOR "ZIP") -set(CPACK_SOURCE_IGNORE_FILES "/CVS/;/\\\\.svn/;/\\\\.bzr/;/\\\\.hg/;/\\\\.git/;\\\\.swp$;\\\\.#;/#" ".*\\\\.user$" "\\\\.gitignore$" "\\\\.gitmodules$" "\\\\.git$") -include(CPack) - add_compile_options( #/Zc:auto # config.pri /wd4819 # config.pri /MP /GS- - $<$:/MT> - #$<$:/MTd> ) add_definitions( @@ -52,4 +31,3 @@ include_directories( add_subdirectory(texthook) add_subdirectory(vnrhook) -# add_subdirectory(profile) diff --git a/texthook/CMakeLists.txt b/texthook/CMakeLists.txt index aab5750..5a51e0b 100644 --- a/texthook/CMakeLists.txt +++ b/texthook/CMakeLists.txt @@ -1,11 +1,3 @@ -# texthook.pro -# CONFIG += noqtgui dll #eha # eha will catch all exceptions, but does not work on Windows XP - -# DEFINES += ITH_HAS_CRT # Use native CRT - -# # TODO: Get rid of dependence on msvc's swprintf -# DEFINES += _CRT_NON_CONFORMING_SWPRINTFS - project(host) set(vnrhost_src @@ -26,21 +18,12 @@ set_target_properties(vnrhost PROPERTIES LINK_FLAGS /SUBSYSTEM:WINDOWS) target_compile_options(vnrhost PRIVATE # /GR- - $<$:/MT> + $<$:/MT> $<$:> ) -#STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) - -target_link_libraries(vnrhost) - target_compile_definitions(vnrhost PRIVATE ITH_HAS_CRT _CRT_NON_CONFORMING_SWPRINTFS ) - -install(TARGETS vnrhost RUNTIME - DESTINATION . - CONFIGURATIONS Release -) diff --git a/vnrhook/CMakeLists.txt b/vnrhook/CMakeLists.txt index 365a7ad..e2a9c5c 100644 --- a/vnrhook/CMakeLists.txt +++ b/vnrhook/CMakeLists.txt @@ -1,22 +1,3 @@ -# # hook.pro -# # Exception handler to catch all exceptions -# CONFIG += dll noqt eh eha # noeh nosafeseh - -# DEFINES += ITH_HAS_CRT ITH_HAS_SEH -# DEFINES += MEMDBG_NO_STL NTINSPECT_NO_STL # disabled as not used - -# # jichi 11/13/2011: disable swprinf warning -# DEFINES += _CRT_NON_CONFORMING_SWPRINTFS - -# config.pri -# CONFIG(eha) { -# message(CONFIG eha) -# QMAKE_CXXFLAGS_STL_ON -= /EHsc -# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc -# QMAKE_CXXFLAGS_STL_ON += /EHa -# QMAKE_CXXFLAGS_EXCEPTIONS_ON += /EHa -# } - project(engine) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) @@ -72,7 +53,7 @@ set_target_properties(vnrhook PROPERTIES target_compile_options(vnrhook PRIVATE /EHa - $<$:/MT> + $<$:/MT> $<$:> ) @@ -88,9 +69,4 @@ target_compile_definitions(vnrhook ITH_HAS_CRT ITH_HAS_SEH _CRT_NON_CONFORMING_SWPRINTFS -) - -install(TARGETS vnrhook RUNTIME - DESTINATION . - CONFIGURATIONS Release -) +) \ No newline at end of file From ffeb4e2dad2a4d5ab8336327bfe5d4f3ba61bd3c Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sun, 22 Jul 2018 23:27:12 -0700 Subject: [PATCH 26/29] final fix for build pipeline --- CMakeLists.txt | 14 +++++++++----- GUI/GUI.pro | 2 +- README.md | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eac5ce9..050bff0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,7 @@ cmake_minimum_required(VERSION 2.8) -set(CMAKE_CONFIGURATION_TYPES Debug Release) - project(NextHooker) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/Debug") -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release") - add_compile_options( #/Zc:auto # config.pri /wd4819 # config.pri @@ -29,5 +24,14 @@ include_directories( texthook ) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/Debug) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/Debug) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/Debug) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/Release) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/Release) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/Release) + +set(CMAKE_CONFIGURATION_TYPES Debug Release) + add_subdirectory(texthook) add_subdirectory(vnrhook) diff --git a/GUI/GUI.pro b/GUI/GUI.pro index dcccb00..765e480 100644 --- a/GUI/GUI.pro +++ b/GUI/GUI.pro @@ -33,7 +33,7 @@ FORMS += \ mainwindow.ui win32: LIBS += \ - -L$$PWD/../Builds/Debug/texthook/ -lvnrhost + -L$$PWD/../Builds/Debug/Debug/ -lvnrhost # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin diff --git a/README.md b/README.md index 49e60d9..a9422ee 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ GPL v3 ## Compiling Before compiling *NextHooker*, you should get Visual Studio with CMake and ATL support, as well as Qt version 5.11
+Use Visual Studio to compile all the CMake projects, then use Qt Creator to compile/run the GUI. ## Project Architecture From fe30b77a44f627de3dd6172dde0df32c7d4239c3 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Mon, 23 Jul 2018 12:25:02 -0700 Subject: [PATCH 27/29] start implementing new GUI --- GUI/GUI.pro | 3 + GUI/mainwindow.cpp | 40 +++++- GUI/mainwindow.h | 11 +- GUI/mainwindow.ui | 112 +++++++++++---- texthook/CMakeLists.txt | 2 - texthook/hookman.cc | 155 -------------------- texthook/hookman.h | 70 --------- texthook/host.cc | 310 +++++++++++++++++++++++++++------------- texthook/host.h | 62 ++++++-- texthook/pipe.cc | 10 +- texthook/textthread.cc | 2 - texthook/textthread.h | 3 +- 12 files changed, 403 insertions(+), 377 deletions(-) delete mode 100644 texthook/hookman.cc delete mode 100644 texthook/hookman.h diff --git a/GUI/GUI.pro b/GUI/GUI.pro index 765e480..83dd4d2 100644 --- a/GUI/GUI.pro +++ b/GUI/GUI.pro @@ -35,6 +35,9 @@ FORMS += \ win32: LIBS += \ -L$$PWD/../Builds/Debug/Debug/ -lvnrhost +QMAKE_CXXFLAGS_RELEASE += \ + /MT + # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index 2917b96..ee5157c 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -1,17 +1,44 @@ #include "mainwindow.h" #include "ui_mainwindow.h" #include "QMessageBox" -#include "qlineedit.h" +#include "QLineEdit" +#include "QTableWidget" +#include "QInputDialog" #include +#include +#include #include "../texthook/host.h" +QTableWidget* processList; + +QString GetModuleName(DWORD processId, HMODULE module = NULL) +{ + HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId); + wchar_t buffer[MAX_PATH]; + GetModuleFileNameExW(handle, module, buffer, MAX_PATH); + return QString::fromWCharArray(wcsrchr(buffer, L'\\') + 1); +} + +void OnProcessAttach(DWORD processId) +{ + processList->setItem(processList->rowCount(), 0, new QTableWidgetItem(QString::number(processId))); +} + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { + Host::Start(); ui->setupUi(this); - connect(ui->centralWidget->children().at(0), SIGNAL(returnPressed()), this, SLOT(onCommand())); - StartHost(); + + processList = this->findChild("processList"); + Host::RegisterProcessAttachCallback([](DWORD processId) + { + processList->insertRow(processList->rowCount()); + processList->setItem(processList->rowCount() - 1, 0, new QTableWidgetItem(QString::number(processId))); + processList->setItem(processList->rowCount() - 1, 1, new QTableWidgetItem(GetModuleName(processId))); + }); + Host::Open(); } MainWindow::~MainWindow() @@ -19,8 +46,9 @@ MainWindow::~MainWindow() delete ui; } -void MainWindow::onCommand() +void MainWindow::on_attachButton_clicked() { - QLineEdit* lineEdit = (QLineEdit*)sender(); - QMessageBox::information(this, "called", lineEdit->text()); + //processList->insertRow(processList->rowCount()); + //processList->setItem(processList->rowCount() - 1, 0, new QTableWidgetItem(QString::number(6000))); + Host::InjectProcess(QInputDialog::getInt(this, "Process ID?", "")); } diff --git a/GUI/mainwindow.h b/GUI/mainwindow.h index a4af74b..d1084ea 100644 --- a/GUI/mainwindow.h +++ b/GUI/mainwindow.h @@ -3,8 +3,9 @@ #include -namespace Ui { -class MainWindow; +namespace Ui +{ + class MainWindow; } class MainWindow : public QMainWindow @@ -15,8 +16,10 @@ public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); -public slots: - void onCommand(); +private slots: + + void on_attachButton_clicked(); + private: Ui::MainWindow *ui; }; diff --git a/GUI/mainwindow.ui b/GUI/mainwindow.ui index d260328..62fc05d 100644 --- a/GUI/mainwindow.ui +++ b/GUI/mainwindow.ui @@ -6,46 +6,110 @@ 0 0 - 496 - 376 + 800 + 600 NextHooker - - - - 252 - 0 - 241 - 20 - - - - - - - 3 - 40 - 491 - 291 - - - + + + + + + 5 + 0 + + + + + + + + + 2 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Attach to game + + + + + + + + 0 + 0 + + + + Currently attached to: + + + Qt::AlignCenter + + + + + + + 2 + + + 50 + + + true + + + false + + + + ID + + + AlignCenter + + + + + Name + + + AlignCenter + + + + + + + + 0 0 - 496 + 800 21 - diff --git a/texthook/CMakeLists.txt b/texthook/CMakeLists.txt index 5a51e0b..d0bcc40 100644 --- a/texthook/CMakeLists.txt +++ b/texthook/CMakeLists.txt @@ -1,12 +1,10 @@ project(host) set(vnrhost_src - hookman.h host.h pipe.h textthread.h winmutex.h - hookman.cc host.cc pipe.cc textthread.cc diff --git a/texthook/hookman.cc b/texthook/hookman.cc deleted file mode 100644 index 2df0b11..0000000 --- a/texthook/hookman.cc +++ /dev/null @@ -1,155 +0,0 @@ -// hookman.cc -// 8/24/2013 jichi -// Branch IHF/HookManager.cpp, rev 133 -// 8/24/2013 TODO: Clean up this file - -#ifdef _MSC_VER -# pragma warning (disable:4100) // C4100: unreference formal parameter -# pragma warning (disable:4146) // C4146: unary minus operator applied to unsigned type -#endif // _MSC_VER - -#include "hookman.h" -#include "../vnrhook/include/const.h" -#include "../vnrhook/include/defs.h" -#include "../vnrhook/include/types.h" -#include "winmutex.h" -#include - -#define HM_LOCK CriticalSectionLocker hmLocker(hmCs) // Synchronized scope for accessing private data - -HookManager::HookManager() : - create(nullptr), - remove(nullptr), - attach(nullptr), - detach(nullptr), - nextThreadNumber(0), - splitDelay(250), - textThreadsByParams(), - processRecordsByIds() -{ - InitializeCriticalSection(&hmCs); - // Console text thread - (textThreadsByParams[{ 0, -1UL, -1UL, -1UL }] = new TextThread({ 0, -1UL, -1UL, -1UL }, nextThreadNumber++, splitDelay))->Status() |= USING_UNICODE; -} - -HookManager::~HookManager() -{ - EnterCriticalSection(&hmCs); - RemoveThreads([](auto one, auto two) { return true; }, {}); - for (auto i : processRecordsByIds) UnRegisterProcess(i.first); - LeaveCriticalSection(&hmCs); - DeleteCriticalSection(&hmCs); -} - -TextThread* HookManager::FindSingle(DWORD number) -{ - HM_LOCK; - for (auto i : textThreadsByParams) - if (i.second->Number() == number) - return i.second; - return nullptr; -} - -void HookManager::RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadParameter cmp) -{ - HM_LOCK; - std::vector removedThreads; - for (auto i : textThreadsByParams) - if (RemoveIf(i.first, cmp)) - { - if (remove) remove(i.second); - delete i.second; - removedThreads.push_back(i.first); - } - for (auto i : removedThreads) textThreadsByParams.erase(i); -} - -void HookManager::RegisterProcess(DWORD pid, HANDLE hostPipe) -{ - HM_LOCK; - ProcessRecord record; - record.hostPipe = hostPipe; - record.hookman_section = OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(pid)).c_str()); - record.hookman_map = MapViewOfFile(record.hookman_section, FILE_MAP_READ, 0, 0, HOOK_SECTION_SIZE / 2); // jichi 1/16/2015: Changed to half to hook section size - record.process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); - record.hookman_mutex = OpenMutexW(MUTEX_ALL_ACCESS, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(pid)).c_str()); - processRecordsByIds[pid] = record; - if (attach) attach(pid); -} - -void HookManager::UnRegisterProcess(DWORD pid) -{ - HM_LOCK; - ProcessRecord pr = processRecordsByIds[pid]; - CloseHandle(pr.hookman_mutex); - UnmapViewOfFile(pr.hookman_map); - CloseHandle(pr.process_handle); - CloseHandle(pr.hookman_section); - processRecordsByIds.erase(pid); - RemoveThreads([](auto one, auto two) { return one.pid == two.pid; }, { pid, 0, 0, 0 }); - if (detach) detach(pid); -} - -void HookManager::DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD spl, const BYTE *text, int len) -{ - // jichi 20/27/2013: When PID is zero, the text comes from console, which I don't need - if (!text || !pid || len <= 0) return; - HM_LOCK; - ThreadParameter tp = { pid, hook, retn, spl }; - TextThread *it; - if ((it = textThreadsByParams[tp]) == nullptr) - { - it = textThreadsByParams[tp] = new TextThread(tp, nextThreadNumber++, splitDelay); - if (GetHookParam(pid, hook).type & USING_UNICODE) it->Status() |= USING_UNICODE; - if (create) create(it); - } - it->AddText(text, len); -} - -void HookManager::AddConsoleOutput(std::wstring text) -{ - HM_LOCK; - textThreadsByParams[{ 0, -1UL, -1UL, -1UL }]->AddSentence(std::wstring(text)); -} - -HANDLE HookManager::GetHostPipe(DWORD pid) -{ - HM_LOCK; - return processRecordsByIds[pid].hostPipe; -} - -HookParam HookManager::GetHookParam(DWORD pid, DWORD addr) -{ - HM_LOCK; - HookParam ret = {}; - ProcessRecord pr = processRecordsByIds[pid]; - if (pr.hookman_map == nullptr) return ret; - MutexLocker locker(pr.hookman_mutex); - const Hook* hooks = (const Hook*)pr.hookman_map; - for (int i = 0; i < MAX_HOOK; ++i) - if (hooks[i].Address() == addr) - ret = hooks[i].hp; - return ret; -} - -std::wstring HookManager::GetHookName(DWORD pid, DWORD addr) -{ - HM_LOCK; - std::string buffer = ""; - ProcessRecord pr = processRecordsByIds[pid]; - if (pr.hookman_map == nullptr) return L""; - MutexLocker locker(pr.hookman_mutex); - const Hook* hooks = (const Hook*)pr.hookman_map; - for (int i = 0; i < MAX_HOOK; ++i) - { - if (hooks[i].Address() == addr) - { - buffer.resize(hooks[i].NameLength()); - ReadProcessMemory(pr.process_handle, hooks[i].Name(), &buffer[0], hooks[i].NameLength(), nullptr); - } - } - USES_CONVERSION; - return std::wstring(A2W(buffer.c_str())); -} - -// EOF diff --git a/texthook/hookman.h b/texthook/hookman.h deleted file mode 100644 index 4d3037f..0000000 --- a/texthook/hookman.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -// hookman.h -// 8/23/2013 jichi -// Branch: ITH/HookManager.h, rev 133 - -#include -#include "textthread.h" -#include -#include -#include "../vnrhook/include/types.h" - -struct ProcessRecord -{ - HANDLE process_handle; - HANDLE hookman_mutex; - HANDLE hookman_section; - LPVOID hookman_map; - HANDLE hostPipe; -}; - -typedef void(*ProcessEventCallback)(DWORD pid); -typedef void(*ThreadEventCallback)(TextThread*); - -struct ThreadParameterHasher -{ - size_t operator()(const ThreadParameter& tp) const - { - return std::hash()(tp.pid << 6) + std::hash()(tp.hook) + std::hash()(tp.retn) + std::hash()(tp.spl); - } -}; - -// Artikash 7/19/2018: This should probably be broken up into 2-4 classes... -class __declspec(dllexport) HookManager -{ -public: - HookManager(); - ~HookManager(); - - TextThread* FindSingle(DWORD number); - void AddConsoleOutput(std::wstring text); - void DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD split, const BYTE *text, int len); - void RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadParameter cmp); - void RegisterProcess(DWORD pid, HANDLE hostPipe); - void UnRegisterProcess(DWORD pid); - HANDLE GetHostPipe(DWORD pid); - HookParam GetHookParam(DWORD pid, DWORD addr); - std::wstring GetHookName(DWORD pid, DWORD addr); - - void RegisterThreadCreateCallback(ThreadEventCallback cf) { create = cf; } - void RegisterThreadRemoveCallback(ThreadEventCallback cf) { remove = cf; } - void RegisterProcessAttachCallback(ProcessEventCallback cf) { attach = cf; } - void RegisterProcessDetachCallback(ProcessEventCallback cf) { detach = cf; } - - void SetSplitInterval(unsigned int splitDelay) { this->splitDelay = splitDelay; } - -private: - std::unordered_map textThreadsByParams; - std::unordered_map processRecordsByIds; - - CRITICAL_SECTION hmCs; - - ThreadEventCallback create, remove; - ProcessEventCallback attach, detach; - - WORD nextThreadNumber; - unsigned int splitDelay; -}; - -// EOF diff --git a/texthook/host.cc b/texthook/host.cc index e6667dc..bdf62cd 100644 --- a/texthook/host.cc +++ b/texthook/host.cc @@ -4,28 +4,37 @@ #include "host.h" #include "pipe.h" +#include "winmutex.h" +#include #include "../vnrhook/include/const.h" #include "../vnrhook/include/defs.h" #include "../vnrhook/include/types.h" +#include HANDLE preventDuplicationMutex; -HookManager* man; +std::unordered_map textThreadsByParams; +std::unordered_map processRecordsByIds; + +CRITICAL_SECTION hostCs; + +ThreadEventCallback onCreate, onRemove; +ProcessEventCallback onAttach, onDetach; + +WORD nextThreadNumber; HWND dummyWindow; bool running; -namespace -{ // unnamed - void GetDebugPrivileges() // Artikash 5/19/2018: Is it just me or is this function 100% superfluous? - { - HANDLE processToken; - TOKEN_PRIVILEGES Privileges = { 1, {0x14, 0, SE_PRIVILEGE_ENABLED} }; +#define HOST_LOCK CriticalSectionLocker hostLocker(hostCs) // Synchronized scope for accessing private data - OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &processToken); - AdjustTokenPrivileges(processToken, FALSE, &Privileges, 0, nullptr, nullptr); - CloseHandle(processToken); - } -} // unnamed namespace +void GetDebugPrivileges() // Artikash 5/19/2018: Is it just me or is this function 100% superfluous? +{ + HANDLE processToken; + TOKEN_PRIVILEGES Privileges = { 1, {0x14, 0, SE_PRIVILEGE_ENABLED} }; + OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &processToken); + AdjustTokenPrivileges(processToken, FALSE, &Privileges, 0, nullptr, nullptr); + CloseHandle(processToken); +} BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID unused) { @@ -33,12 +42,11 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID unused) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(hinstDLL); - GetDebugPrivileges(); // jichi 8/24/2013: Create hidden window so that ITH can access timer and events dummyWindow = CreateWindowW(L"Button", L"InternalWindow", 0, 0, 0, 0, 0, 0, 0, hinstDLL, 0); break; case DLL_PROCESS_DETACH: - CloseHost(); + Host::Close(); DestroyWindow(dummyWindow); break; default: @@ -47,112 +55,222 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID unused) return true; } -DLLEXPORT bool StartHost() +namespace Host { - preventDuplicationMutex = CreateMutexW(nullptr, TRUE, ITH_SERVER_MUTEX); - if (GetLastError() == ERROR_ALREADY_EXISTS || ::running) + DLLEXPORT bool Start() { - MessageBoxW(nullptr, L"I am sorry that this game is attached by some other VNR ><\nPlease restart the game and try again!", L"Error", MB_ICONERROR); + preventDuplicationMutex = CreateMutexW(nullptr, TRUE, ITH_SERVER_MUTEX); + if (GetLastError() == ERROR_ALREADY_EXISTS || running) + { + MessageBoxW(nullptr, L"I am sorry that this game is attached by some other VNR ><\nPlease restart the game and try again!", L"Error", MB_ICONERROR); + return false; + } + else + { + running = true; + GetDebugPrivileges(); + InitializeCriticalSection(&hostCs); + onAttach = onDetach = nullptr; + onCreate = onRemove = nullptr; + nextThreadNumber = 0; + // Console text thread + (textThreadsByParams[{ 0, -1UL, -1UL, -1UL }] = new TextThread({ 0, -1UL, -1UL, -1UL }, nextThreadNumber++))->Status() |= USING_UNICODE; + return true; + } + } + + DLLEXPORT void Open() + { + CreateNewPipe(); + } + + DLLEXPORT void Close() + { + if (running) + { + EnterCriticalSection(&hostCs); + running = false; + RemoveThreads([](auto one, auto two) { return true; }, {}); + for (auto i : processRecordsByIds) UnregisterProcess(i.first); + LeaveCriticalSection(&hostCs); + DeleteCriticalSection(&hostCs); + CloseHandle(preventDuplicationMutex); + } + } + + DLLEXPORT bool InjectProcess(DWORD processId, DWORD timeout) + { + if (processId == GetCurrentProcessId()) return false; + + CloseHandle(CreateMutexW(nullptr, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId)).c_str())); + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + AddConsoleOutput(L"already locked"); + return false; + } + + HMODULE textHooker = LoadLibraryExW(ITH_DLL, nullptr, DONT_RESOLVE_DLL_REFERENCES); + wchar_t textHookerPath[MAX_PATH]; + unsigned int textHookerPathSize = GetModuleFileNameW(textHooker, textHookerPath, MAX_PATH) * 2 + 2; + FreeLibrary(textHooker); + + if (HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId)) + if (LPVOID remoteData = VirtualAllocEx(processHandle, nullptr, textHookerPathSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) + if (WriteProcessMemory(processHandle, remoteData, textHookerPath, textHookerPathSize, nullptr)) + if (HANDLE thread = CreateRemoteThread(processHandle, nullptr, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, remoteData, 0, nullptr)) + { + WaitForSingleObject(thread, timeout); + CloseHandle(thread); + VirtualFreeEx(processHandle, remoteData, 0, MEM_RELEASE); + CloseHandle(processHandle); + return true; + } + + AddConsoleOutput(L"couldn't inject dll"); return false; } - else + + DLLEXPORT bool DetachProcess(DWORD processId) { - ::running = true; - ::man = new HookManager; + DWORD command = HOST_COMMAND_DETACH; + DWORD unused; + return WriteFile(processRecordsByIds[processId].hostPipe, &command, sizeof(command), &unused, nullptr); + } + + DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name) + { + BYTE buffer[PIPE_BUFFER_SIZE] = {}; + *(DWORD*)buffer = HOST_COMMAND_NEW_HOOK; + *(HookParam*)(buffer + sizeof(DWORD)) = hp; + if (name.size()) strcpy((char*)buffer + sizeof(DWORD) + sizeof(HookParam), name.c_str()); + DWORD unused; + return WriteFile(processRecordsByIds[pid].hostPipe, buffer, sizeof(DWORD) + sizeof(HookParam) + name.size(), &unused, nullptr); + } + + DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr) + { + HANDLE hostPipe = processRecordsByIds[pid].hostPipe; + if (hostPipe == nullptr) return false; + HANDLE hookRemovalEvent = CreateEventW(nullptr, TRUE, FALSE, ITH_REMOVEHOOK_EVENT); + BYTE buffer[sizeof(DWORD) * 2] = {}; + *(DWORD*)buffer = HOST_COMMAND_REMOVE_HOOK; + *(DWORD*)(buffer + sizeof(DWORD)) = addr; + DWORD unused; + WriteFile(hostPipe, buffer, sizeof(DWORD) * 2, &unused, nullptr); + WaitForSingleObject(hookRemovalEvent, 1000); + CloseHandle(hookRemovalEvent); + RemoveThreads([](auto one, auto two) { return one.pid == two.pid && one.hook == two.hook; }, { pid, addr, 0, 0 }); return true; } -} -DLLEXPORT void OpenHost() -{ - CreateNewPipe(); -} - -DLLEXPORT void CloseHost() -{ - if (::running) + DLLEXPORT HookParam GetHookParam(DWORD pid, DWORD addr) { - ::running = false; - delete man; - CloseHandle(preventDuplicationMutex); - } -} - -DLLEXPORT bool InjectProcess(DWORD processId, DWORD timeout) -{ - if (processId == GetCurrentProcessId()) return false; - - CloseHandle(CreateMutexW(nullptr, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId)).c_str())); - if (GetLastError() == ERROR_ALREADY_EXISTS) - { - man->AddConsoleOutput(L"already locked"); - return false; + HOST_LOCK; + HookParam ret = {}; + ProcessRecord pr = processRecordsByIds[pid]; + if (pr.hookman_map == nullptr) return ret; + MutexLocker locker(pr.hookman_mutex); + const Hook* hooks = (const Hook*)pr.hookman_map; + for (int i = 0; i < MAX_HOOK; ++i) + if (hooks[i].Address() == addr) + ret = hooks[i].hp; + return ret; } - HMODULE textHooker = LoadLibraryExW(ITH_DLL, nullptr, DONT_RESOLVE_DLL_REFERENCES); - wchar_t textHookerPath[MAX_PATH]; - unsigned int textHookerPathSize = GetModuleFileNameW(textHooker, textHookerPath, MAX_PATH) * 2 + 2; - FreeLibrary(textHooker); + DLLEXPORT std::wstring GetHookName(DWORD pid, DWORD addr) + { + HOST_LOCK; + std::string buffer = ""; + ProcessRecord pr = processRecordsByIds[pid]; + if (pr.hookman_map == nullptr) return L""; + MutexLocker locker(pr.hookman_mutex); + const Hook* hooks = (const Hook*)pr.hookman_map; + for (int i = 0; i < MAX_HOOK; ++i) + if (hooks[i].Address() == addr) + { + buffer.resize(hooks[i].NameLength()); + ReadProcessMemory(pr.process_handle, hooks[i].Name(), &buffer[0], hooks[i].NameLength(), nullptr); + } + USES_CONVERSION; + return std::wstring(A2W(buffer.c_str())); + } - if (HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId)) - if (LPVOID remoteData = VirtualAllocEx(processHandle, nullptr, textHookerPathSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) - if (WriteProcessMemory(processHandle, remoteData, textHookerPath, textHookerPathSize, nullptr)) - if (HANDLE thread = CreateRemoteThread(processHandle, nullptr, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, remoteData, 0, nullptr)) - { - WaitForSingleObject(thread, timeout); - CloseHandle(thread); - VirtualFreeEx(processHandle, remoteData, 0, MEM_RELEASE); - CloseHandle(processHandle); - return true; - } + DLLEXPORT TextThread* GetThread(DWORD number) + { + HOST_LOCK; + for (auto i : textThreadsByParams) + if (i.second->Number() == number) + return i.second; + return nullptr; + } - man->AddConsoleOutput(L"couldn't inject dll"); - return false; + DLLEXPORT void AddConsoleOutput(std::wstring text) + { + HOST_LOCK; + textThreadsByParams[{ 0, -1UL, -1UL, -1UL }]->AddSentence(std::wstring(text)); + } + + DLLEXPORT void RegisterThreadCreateCallback(ThreadEventCallback cf) { onCreate = cf; } + DLLEXPORT void RegisterThreadRemoveCallback(ThreadEventCallback cf) { onRemove = cf; } + DLLEXPORT void RegisterProcessAttachCallback(ProcessEventCallback cf) { onAttach = cf; } + DLLEXPORT void RegisterProcessDetachCallback(ProcessEventCallback cf) { onDetach = cf; } } -DLLEXPORT bool DetachProcess(DWORD processId) +void DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD split, const BYTE * text, int len) { - DWORD command = HOST_COMMAND_DETACH; - DWORD unused; - return WriteFile(man->GetHostPipe(processId), &command, sizeof(command), &unused, nullptr); + // jichi 20/27/2013: When PID is zero, the text comes from console, which I don't need + if (!text || !pid || len <= 0) return; + HOST_LOCK; + ThreadParameter tp = { pid, hook, retn, split }; + TextThread *it; + if ((it = textThreadsByParams[tp]) == nullptr) + { + it = textThreadsByParams[tp] = new TextThread(tp, nextThreadNumber++); + if (Host::GetHookParam(pid, hook).type & USING_UNICODE) it->Status() |= USING_UNICODE; + if (onCreate) onCreate(it); + } + it->AddText(text, len); } -DLLEXPORT HookManager* GetHostHookManager() +void RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadParameter cmp) { - return man; + HOST_LOCK; + std::vector removedThreads; + for (auto i : textThreadsByParams) + if (RemoveIf(i.first, cmp)) + { + if (onRemove) onRemove(i.second); + delete i.second; + removedThreads.push_back(i.first); + } + for (auto i : removedThreads) textThreadsByParams.erase(i); } -DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name) +void RegisterProcess(DWORD pid, HANDLE hostPipe) { - HANDLE commandPipe = man->GetHostPipe(pid); - if (commandPipe == nullptr) return false; - - BYTE buffer[PIPE_BUFFER_SIZE] = {}; - *(DWORD*)buffer = HOST_COMMAND_NEW_HOOK; - *(HookParam*)(buffer + sizeof(DWORD)) = hp; - if (name.size()) strcpy((char*)buffer + sizeof(DWORD) + sizeof(HookParam), name.c_str()); - DWORD unused; - return WriteFile(commandPipe, buffer, sizeof(DWORD) + sizeof(HookParam) + name.size(), &unused, nullptr); + HOST_LOCK; + ProcessRecord record; + record.hostPipe = hostPipe; + record.hookman_section = OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(pid)).c_str()); + record.hookman_map = MapViewOfFile(record.hookman_section, FILE_MAP_READ, 0, 0, HOOK_SECTION_SIZE / 2); // jichi 1/16/2015: Changed to half to hook section size + record.process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + record.hookman_mutex = OpenMutexW(MUTEX_ALL_ACCESS, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(pid)).c_str()); + processRecordsByIds[pid] = record; + if (onAttach) onAttach(pid); } -DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr) +void UnregisterProcess(DWORD pid) { - HANDLE commandPipe = man->GetHostPipe(pid); - if (commandPipe == nullptr) return false; - - HANDLE hookRemovalEvent = CreateEventW(nullptr, TRUE, FALSE, ITH_REMOVEHOOK_EVENT); - - BYTE buffer[sizeof(DWORD) * 2] = {}; - *(DWORD*)buffer = HOST_COMMAND_REMOVE_HOOK; - *(DWORD*)(buffer + sizeof(DWORD)) = addr; - DWORD unused; - WriteFile(commandPipe, buffer, sizeof(DWORD) * 2, &unused, nullptr); - - WaitForSingleObject(hookRemovalEvent, 1000); - CloseHandle(hookRemovalEvent); - - man->RemoveThreads([](auto one, auto two) { return one.pid == two.pid && one.hook == two.hook; }, { pid, addr, 0, 0 }); - return true; + HOST_LOCK; + ProcessRecord pr = processRecordsByIds[pid]; + if (!pr.hostPipe) return; + CloseHandle(pr.hookman_mutex); + UnmapViewOfFile(pr.hookman_map); + CloseHandle(pr.process_handle); + CloseHandle(pr.hookman_section); + processRecordsByIds.erase(pid); + RemoveThreads([](auto one, auto two) { return one.pid == two.pid; }, { pid, 0, 0, 0 }); + if (onDetach) onDetach(pid); } -// EOF +// EOF \ No newline at end of file diff --git a/texthook/host.h b/texthook/host.h index 1166693..9154786 100644 --- a/texthook/host.h +++ b/texthook/host.h @@ -5,17 +5,57 @@ // Branch: ITH/IHF.h, rev 105 #define DLLEXPORT __declspec(dllexport) -#include "hookman.h" -#include "../vnrhook/include/types.h" -#include -DLLEXPORT void OpenHost(); -DLLEXPORT bool StartHost(); -DLLEXPORT void CloseHost(); -DLLEXPORT HookManager* GetHostHookManager(); -DLLEXPORT bool InjectProcess(DWORD pid, DWORD timeout = 5000); -DLLEXPORT bool DetachProcess(DWORD pid); -DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name = ""); -DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr); +#include +#include "textthread.h" +#include +#include "../vnrhook/include/types.h" + +struct ProcessRecord +{ + HANDLE process_handle; + HANDLE hookman_mutex; + HANDLE hookman_section; + LPVOID hookman_map; + HANDLE hostPipe; +}; + +typedef void(*ProcessEventCallback)(DWORD pid); +typedef void(*ThreadEventCallback)(TextThread*); + +struct ThreadParameterHasher +{ + size_t operator()(const ThreadParameter& tp) const + { + return std::hash()(tp.pid << 6) + std::hash()(tp.hook) + std::hash()(tp.retn) + std::hash()(tp.spl); + } +}; + +namespace Host +{ + DLLEXPORT void Open(); + DLLEXPORT bool Start(); + DLLEXPORT void Close(); + DLLEXPORT bool InjectProcess(DWORD pid, DWORD timeout = 5000); + DLLEXPORT bool DetachProcess(DWORD pid); + + DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name = ""); + DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr); + DLLEXPORT HookParam GetHookParam(DWORD pid, DWORD addr); + DLLEXPORT std::wstring GetHookName(DWORD pid, DWORD addr); + + DLLEXPORT TextThread* GetThread(DWORD number); + DLLEXPORT void AddConsoleOutput(std::wstring text); + + DLLEXPORT void RegisterThreadCreateCallback(ThreadEventCallback cf); + DLLEXPORT void RegisterThreadRemoveCallback(ThreadEventCallback cf); + DLLEXPORT void RegisterProcessAttachCallback(ProcessEventCallback cf); + DLLEXPORT void RegisterProcessDetachCallback(ProcessEventCallback cf); +} + +void DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD split, const BYTE *text, int len); +void RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadParameter cmp); +void RegisterProcess(DWORD pid, HANDLE hostPipe); +void UnregisterProcess(DWORD pid); // EOF diff --git a/texthook/pipe.cc b/texthook/pipe.cc index d03dd5d..304ae21 100644 --- a/texthook/pipe.cc +++ b/texthook/pipe.cc @@ -8,8 +8,6 @@ #include "../vnrhook/include/const.h" #include -extern HookManager* man; - struct Pipes { HANDLE hookPipe; @@ -34,7 +32,7 @@ DWORD WINAPI TextReceiver(LPVOID lpThreadParameter) BYTE buffer[PIPE_BUFFER_SIZE] = {}; DWORD bytesRead, processId; ReadFile(pipes->hookPipe, &processId, sizeof(processId), &bytesRead, nullptr); - man->RegisterProcess(processId, pipes->hostPipe); + RegisterProcess(processId, pipes->hostPipe); // jichi 9/27/2013: why recursion? // Artikash 5/20/2018: To create a new pipe for another process @@ -55,13 +53,13 @@ DWORD WINAPI TextReceiver(LPVOID lpThreadParameter) case HOST_NOTIFICATION_NEWHOOK: // Artikash 7/18/2018: Useless for now, but could be used to implement smth later break; case HOST_NOTIFICATION_TEXT: - man->AddConsoleOutput(A2W((LPCSTR)(buffer + sizeof(DWORD) * 2))); // Text + Host::AddConsoleOutput(A2W((LPCSTR)(buffer + sizeof(DWORD) * 2))); // Text break; } } else { - man->DispatchText(processId, + DispatchText(processId, *(DWORD*)buffer, // Hook address *(DWORD*)(buffer + sizeof(DWORD)), // Return address *(DWORD*)(buffer + sizeof(DWORD) * 2), // Split @@ -73,7 +71,7 @@ DWORD WINAPI TextReceiver(LPVOID lpThreadParameter) DisconnectNamedPipe(pipes->hookPipe); DisconnectNamedPipe(pipes->hostPipe); - man->UnRegisterProcess(processId); + UnregisterProcess(processId); CloseHandle(pipes->hookPipe); CloseHandle(pipes->hostPipe); delete pipes; diff --git a/texthook/textthread.cc b/texthook/textthread.cc index d7ca42a..1b41ddf 100644 --- a/texthook/textthread.cc +++ b/texthook/textthread.cc @@ -5,12 +5,10 @@ # pragma warning (disable:4100) // C4100: unreference formal parameter #endif // _MSC_VER -#include "host.h" #include "textthread.h" #include "../vnrhook/include/const.h" #include "winmutex.h" -extern HookManager* man; extern HWND dummyWindow; #define TT_LOCK CriticalSectionLocker ttLocker(ttCs) // Synchronized scope for accessing private data diff --git a/texthook/textthread.h b/texthook/textthread.h index a541971..b67d016 100644 --- a/texthook/textthread.h +++ b/texthook/textthread.h @@ -30,7 +30,7 @@ typedef std::wstring(*ThreadOutputCallback)(TextThread*, std::wstring data); class TextThread { public: - TextThread(ThreadParameter tp, unsigned int threadNumber, unsigned int splitDelay); + TextThread(ThreadParameter tp, unsigned int threadNumber, unsigned int splitDelay = 250); ~TextThread(); void Reset(); @@ -42,6 +42,7 @@ public: DWORD &Status() { return status; } WORD Number() const { return threadNumber; } ThreadParameter GetThreadParameter() { return tp; } + void SetSplitDelay(unsigned int splitDelay) { this->splitDelay = splitDelay; } void RegisterOutputCallBack(ThreadOutputCallback cb) { output = cb; } From 97fe9800a6283429690ce6ed8fc3d04513884dab Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Mon, 23 Jul 2018 22:57:54 -0700 Subject: [PATCH 28/29] implement basic gui --- GUI/mainwindow.cpp | 82 +++++++++++++++++++++++++++++++++++-------- GUI/mainwindow.h | 10 +++++- GUI/mainwindow.ui | 86 +++++++++++++++++++--------------------------- texthook/host.cc | 5 ++- 4 files changed, 117 insertions(+), 66 deletions(-) diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index ee5157c..09369d7 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -1,6 +1,8 @@ #include "mainwindow.h" #include "ui_mainwindow.h" +#include "QTextBrowser" #include "QMessageBox" +#include "QComboBox" #include "QLineEdit" #include "QTableWidget" #include "QInputDialog" @@ -9,46 +11,98 @@ #include #include "../texthook/host.h" -QTableWidget* processList; +QMainWindow* mainWindow; +QComboBox* processCombo; +QComboBox* ttCombo; +QTextBrowser* textOutput; QString GetModuleName(DWORD processId, HMODULE module = NULL) { HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId); wchar_t buffer[MAX_PATH]; GetModuleFileNameExW(handle, module, buffer, MAX_PATH); + CloseHandle(handle); return QString::fromWCharArray(wcsrchr(buffer, L'\\') + 1); } -void OnProcessAttach(DWORD processId) +QString ProcessString(DWORD processId) { - processList->setItem(processList->rowCount(), 0, new QTableWidgetItem(QString::number(processId))); + return QString("%1: %2").arg(QString::number(processId), GetModuleName(processId)); +} + +QString TextThreadString(TextThread* thread) +{ + ThreadParameter tp = thread->GetThreadParameter(); + return QString("%1:%2:%3:%4:%5:%6").arg( + QString::number(thread->Number()), + QString::number(tp.pid), + QString::number(tp.hook, 16), + QString::number(tp.retn, 16), + QString::number(tp.spl, 16), + QString::fromWCharArray(Host::GetHookName(tp.pid, tp.hook).c_str()) + ); } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { - Host::Start(); ui->setupUi(this); + mainWindow = this; + processCombo = mainWindow->findChild("processCombo"); + ttCombo = mainWindow->findChild("ttCombo"); + textOutput = this->findChild("textOutput"); - processList = this->findChild("processList"); - Host::RegisterProcessAttachCallback([](DWORD processId) - { - processList->insertRow(processList->rowCount()); - processList->setItem(processList->rowCount() - 1, 0, new QTableWidgetItem(QString::number(processId))); - processList->setItem(processList->rowCount() - 1, 1, new QTableWidgetItem(GetModuleName(processId))); - }); + Host::Start(); + Host::RegisterProcessAttachCallback(AddProcess); + Host::RegisterProcessDetachCallback(RemoveProcess); + Host::RegisterThreadCreateCallback(AddThread); + Host::RegisterThreadRemoveCallback(RemoveThread); Host::Open(); } MainWindow::~MainWindow() { + Host::Close(); delete ui; } +void AddProcess(DWORD processId) +{ + processCombo->addItem(ProcessString(processId)); +} + +void RemoveProcess(DWORD processId) +{ + processCombo->removeItem(processCombo->findText(ProcessString(processId))); +} + +void AddThread(TextThread* thread) +{ + ttCombo->addItem(TextThreadString(thread)); + thread->RegisterOutputCallBack([](auto thread, auto data) + { + if (ttCombo->currentText() == TextThreadString(thread)) textOutput->append(QString::fromWCharArray(data.c_str())); + return data + L"\r\n"; + }); +} + +void RemoveThread(TextThread* thread) +{ + ttCombo->removeItem(ttCombo->findText(TextThreadString(thread))); +} + void MainWindow::on_attachButton_clicked() { - //processList->insertRow(processList->rowCount()); - //processList->setItem(processList->rowCount() - 1, 0, new QTableWidgetItem(QString::number(6000))); - Host::InjectProcess(QInputDialog::getInt(this, "Process ID?", "")); + Host::InjectProcess(QInputDialog::getInt(this, "Process ID?", "You can find this under Task Manager -> Details")); +} + +void MainWindow::on_detachButton_clicked() +{ + Host::DetachProcess(processCombo->currentText().split(":")[0].toInt()); +} + +void MainWindow::on_ttCombo_activated(int index) +{ + textOutput->setText(QString::fromWCharArray(Host::GetThread(index)->GetStore().c_str())); } diff --git a/GUI/mainwindow.h b/GUI/mainwindow.h index d1084ea..2d4781c 100644 --- a/GUI/mainwindow.h +++ b/GUI/mainwindow.h @@ -2,6 +2,8 @@ #define MAINWINDOW_H #include +#include +#include "../texthook/textthread.h" namespace Ui { @@ -17,11 +19,17 @@ public: ~MainWindow(); private slots: - void on_attachButton_clicked(); + void on_detachButton_clicked(); + void on_ttCombo_activated(int index); private: Ui::MainWindow *ui; }; +void AddProcess(DWORD processId); +void RemoveProcess(DWORD processId); +void AddThread(TextThread* thread); +void RemoveThread(TextThread* thread); + #endif // MAINWINDOW_H diff --git a/GUI/mainwindow.ui b/GUI/mainwindow.ui index 62fc05d..b8e4cad 100644 --- a/GUI/mainwindow.ui +++ b/GUI/mainwindow.ui @@ -15,20 +15,10 @@ - - - - - 5 - 0 - - - - - + 2 0 @@ -40,6 +30,9 @@ QFrame::Raised + + + @@ -48,51 +41,44 @@ - - - - 0 - 0 - - + + + + - Currently attached to: - - - Qt::AlignCenter + Detach from game + + + + + + + + 6 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + - - - 2 + + + + + + + 5 + 0 + - - 50 - - - true - - - false - - - - ID - - - AlignCenter - - - - - Name - - - AlignCenter - - diff --git a/texthook/host.cc b/texthook/host.cc index bdf62cd..78c4fb9 100644 --- a/texthook/host.cc +++ b/texthook/host.cc @@ -74,13 +74,15 @@ namespace Host onCreate = onRemove = nullptr; nextThreadNumber = 0; // Console text thread - (textThreadsByParams[{ 0, -1UL, -1UL, -1UL }] = new TextThread({ 0, -1UL, -1UL, -1UL }, nextThreadNumber++))->Status() |= USING_UNICODE; return true; } } DLLEXPORT void Open() { + TextThread* console = textThreadsByParams[{ 0, -1UL, -1UL, -1UL }] = new TextThread({ 0, -1UL, -1UL, -1UL }, nextThreadNumber++); + console->Status() |= USING_UNICODE; + if (onCreate) onCreate(console); CreateNewPipe(); } @@ -179,6 +181,7 @@ namespace Host DLLEXPORT std::wstring GetHookName(DWORD pid, DWORD addr) { + if (pid == 0) return L"Console"; HOST_LOCK; std::string buffer = ""; ProcessRecord pr = processRecordsByIds[pid]; From 541e3cc4e70701425ac2765480d18b35e2fc7361 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Tue, 24 Jul 2018 10:39:02 -0700 Subject: [PATCH 29/29] implement callbacks properly and update gui some more --- GUI/GUI.pro | 6 ++-- GUI/hostsignaller.cpp | 19 ++++++++++++ GUI/hostsignaller.h | 24 +++++++++++++++ GUI/mainwindow.cpp | 69 ++++++++++++++++++++++++++++--------------- GUI/mainwindow.h | 13 ++++---- GUI/mainwindow.ui | 22 +++++++++++--- texthook/host.cc | 13 ++++---- texthook/host.h | 5 ++-- texthook/textthread.h | 3 +- 9 files changed, 128 insertions(+), 46 deletions(-) create mode 100644 GUI/hostsignaller.cpp create mode 100644 GUI/hostsignaller.h diff --git a/GUI/GUI.pro b/GUI/GUI.pro index 83dd4d2..fac6fea 100644 --- a/GUI/GUI.pro +++ b/GUI/GUI.pro @@ -24,10 +24,12 @@ CONFIG += c++11 SOURCES += \ main.cpp \ - mainwindow.cpp + mainwindow.cpp \ + hostsignaller.cpp HEADERS += \ - mainwindow.h + mainwindow.h \ + hostsignaller.h FORMS += \ mainwindow.ui diff --git a/GUI/hostsignaller.cpp b/GUI/hostsignaller.cpp new file mode 100644 index 0000000..fb5a9a2 --- /dev/null +++ b/GUI/hostsignaller.cpp @@ -0,0 +1,19 @@ +#include "hostsignaller.h" +#include "../texthook/host.h" + +void HostSignaller::Initialize() +{ + Host::RegisterProcessAttachCallback([&](DWORD pid){ emit AddProcess(pid); }); + Host::RegisterProcessDetachCallback([&](DWORD pid){ emit RemoveProcess(pid); }); + Host::RegisterThreadCreateCallback([&](TextThread* thread) + { + emit AddThread(thread); + thread->RegisterOutputCallBack([&](TextThread* thread, std::wstring output) + { + //output = DispatchToExtensions(output); + emit ThreadOutput(thread, QString::fromWCharArray(output.c_str())); + return output; + }); + }); + Host::RegisterThreadRemoveCallback([&](TextThread* thread){ emit RemoveThread(thread); }); +} diff --git a/GUI/hostsignaller.h b/GUI/hostsignaller.h new file mode 100644 index 0000000..537d3a5 --- /dev/null +++ b/GUI/hostsignaller.h @@ -0,0 +1,24 @@ +#ifndef HOSTSIGNALLER_H +#define HOSTSIGNALLER_H + +#include +#include +#include "../texthook/host.h" + +// Artikash 7/24/2018: This class is a workaround for the fact that Qt only lets me manipulate the GUI in the main thread. +class HostSignaller : public QObject +{ + Q_OBJECT + +public: + void Initialize(); + +signals: + void AddProcess(unsigned int processId); + void RemoveProcess(unsigned int processId); + void AddThread(TextThread* thread); + void RemoveThread(TextThread* thread); + void ThreadOutput(TextThread* thread, QString output); +}; + +#endif // HOSTSIGNALLER_H diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index 09369d7..306e6f7 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -1,11 +1,13 @@ #include "mainwindow.h" #include "ui_mainwindow.h" +#include "QCoreApplication" #include "QTextBrowser" #include "QMessageBox" #include "QComboBox" #include "QLineEdit" -#include "QTableWidget" #include "QInputDialog" +#include +#include #include #include #include @@ -33,63 +35,82 @@ QString ProcessString(DWORD processId) QString TextThreadString(TextThread* thread) { ThreadParameter tp = thread->GetThreadParameter(); - return QString("%1:%2:%3:%4:%5:%6").arg( + return QString("%1:%2:%3:%4:%5: ").arg( QString::number(thread->Number()), QString::number(tp.pid), QString::number(tp.hook, 16), QString::number(tp.retn, 16), - QString::number(tp.spl, 16), - QString::fromWCharArray(Host::GetHookName(tp.pid, tp.hook).c_str()) - ); + QString::number(tp.spl, 16) + ).toUpper() + QString::fromWCharArray(Host::GetHookName(tp.pid, tp.hook).c_str()); } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), - ui(new Ui::MainWindow) + ui(new Ui::MainWindow), + hostSignaller(new HostSignaller) { ui->setupUi(this); mainWindow = this; processCombo = mainWindow->findChild("processCombo"); ttCombo = mainWindow->findChild("ttCombo"); - textOutput = this->findChild("textOutput"); + textOutput = mainWindow->findChild("textOutput"); Host::Start(); - Host::RegisterProcessAttachCallback(AddProcess); - Host::RegisterProcessDetachCallback(RemoveProcess); - Host::RegisterThreadCreateCallback(AddThread); - Host::RegisterThreadRemoveCallback(RemoveThread); + hostSignaller->Initialize(); + connect(hostSignaller, &HostSignaller::AddProcess, this, &MainWindow::AddProcess); + connect(hostSignaller, &HostSignaller::RemoveProcess, this, &MainWindow::RemoveProcess); + connect(hostSignaller, &HostSignaller::AddThread, this, &MainWindow::AddThread); + connect(hostSignaller, &HostSignaller::RemoveThread, this, &MainWindow::RemoveThread); + connect(hostSignaller, &HostSignaller::ThreadOutput, this, &MainWindow::ThreadOutput); Host::Open(); } MainWindow::~MainWindow() { Host::Close(); + delete hostSignaller; delete ui; } -void AddProcess(DWORD processId) +void MainWindow::AddProcess(unsigned int processId) { - processCombo->addItem(ProcessString(processId)); + processCombo->addItem(ProcessString(processId), Qt::AlignHCenter); } -void RemoveProcess(DWORD processId) +void MainWindow::RemoveProcess(unsigned int processId) { - processCombo->removeItem(processCombo->findText(ProcessString(processId))); + for (int i = 0; i < processCombo->count(); ++i) + if (processCombo->itemText(i).split(":")[0] == QString::number(processId)) + processCombo->removeItem(i); } -void AddThread(TextThread* thread) +void MainWindow::AddThread(TextThread* thread) { ttCombo->addItem(TextThreadString(thread)); - thread->RegisterOutputCallBack([](auto thread, auto data) - { - if (ttCombo->currentText() == TextThreadString(thread)) textOutput->append(QString::fromWCharArray(data.c_str())); - return data + L"\r\n"; - }); } -void RemoveThread(TextThread* thread) +void MainWindow::RemoveThread(TextThread* thread) { - ttCombo->removeItem(ttCombo->findText(TextThreadString(thread))); + for (int i = 0; i < ttCombo->count(); ++i) + if (ttCombo->itemText(i).split(":")[0] == QString::number(thread->Number())) + { + ttCombo->removeItem(i); + if (i == ttCombo->currentIndex()) + { + ttCombo->setCurrentIndex(0); + on_ttCombo_activated(0); + } + } +} + +void MainWindow::ThreadOutput(TextThread* thread, QString output) +{ + if (TextThreadString(thread) == ttCombo->currentText()) + { + textOutput->moveCursor(QTextCursor::End); + textOutput->insertPlainText(output); + textOutput->moveCursor(QTextCursor::End); + } } void MainWindow::on_attachButton_clicked() @@ -104,5 +125,5 @@ void MainWindow::on_detachButton_clicked() void MainWindow::on_ttCombo_activated(int index) { - textOutput->setText(QString::fromWCharArray(Host::GetThread(index)->GetStore().c_str())); + textOutput->setText(QString::fromWCharArray(Host::GetThread(ttCombo->itemText(index).split(":")[0].toInt())->GetStore().c_str())); } diff --git a/GUI/mainwindow.h b/GUI/mainwindow.h index 2d4781c..6169cbd 100644 --- a/GUI/mainwindow.h +++ b/GUI/mainwindow.h @@ -4,6 +4,7 @@ #include #include #include "../texthook/textthread.h" +#include "hostsignaller.h" namespace Ui { @@ -18,18 +19,20 @@ public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); + QString ProcessOutput(TextThread *thread, QString output); private slots: void on_attachButton_clicked(); void on_detachButton_clicked(); void on_ttCombo_activated(int index); + void AddProcess(unsigned int processId); + void RemoveProcess(unsigned int processId); + void AddThread(TextThread* thread); + void RemoveThread(TextThread* thread); + void ThreadOutput(TextThread* thread, QString output); private: Ui::MainWindow *ui; + HostSignaller* hostSignaller; }; -void AddProcess(DWORD processId); -void RemoveProcess(DWORD processId); -void AddThread(TextThread* thread); -void RemoveThread(TextThread* thread); - #endif // MAINWINDOW_H diff --git a/GUI/mainwindow.ui b/GUI/mainwindow.ui index b8e4cad..d9b5878 100644 --- a/GUI/mainwindow.ui +++ b/GUI/mainwindow.ui @@ -10,6 +10,11 @@ 600 + + + 10 + + NextHooker @@ -33,6 +38,13 @@ + + + + QComboBox::InsertAtBottom + + + @@ -40,9 +52,6 @@ - - - @@ -79,6 +88,11 @@ 0 + + + 12 + + @@ -92,7 +106,7 @@ 0 0 800 - 21 + 23 diff --git a/texthook/host.cc b/texthook/host.cc index 78c4fb9..668aee8 100644 --- a/texthook/host.cc +++ b/texthook/host.cc @@ -45,10 +45,6 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID unused) // jichi 8/24/2013: Create hidden window so that ITH can access timer and events dummyWindow = CreateWindowW(L"Button", L"InternalWindow", 0, 0, 0, 0, 0, 0, 0, hinstDLL, 0); break; - case DLL_PROCESS_DETACH: - Host::Close(); - DestroyWindow(dummyWindow); - break; default: break; } @@ -73,7 +69,6 @@ namespace Host onAttach = onDetach = nullptr; onCreate = onRemove = nullptr; nextThreadNumber = 0; - // Console text thread return true; } } @@ -92,8 +87,9 @@ namespace Host { EnterCriticalSection(&hostCs); running = false; + DestroyWindow(dummyWindow); RemoveThreads([](auto one, auto two) { return true; }, {}); - for (auto i : processRecordsByIds) UnregisterProcess(i.first); + //for (auto i : processRecordsByIds) UnregisterProcess(i.first); // Artikash 7/24/2018 FIXME: This segfaults since UnregisterProcess invalidates the iterator LeaveCriticalSection(&hostCs); DeleteCriticalSection(&hostCs); CloseHandle(preventDuplicationMutex); @@ -243,7 +239,8 @@ void RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadPara if (RemoveIf(i.first, cmp)) { if (onRemove) onRemove(i.second); - delete i.second; + //delete i.second; // Artikash 7/24/2018: FIXME: Qt GUI updates on another thread, so I can't delete this yet. + i.second->Reset(); // Temp workaround to free some memory. removedThreads.push_back(i.first); } for (auto i : removedThreads) textThreadsByParams.erase(i); @@ -276,4 +273,4 @@ void UnregisterProcess(DWORD pid) if (onDetach) onDetach(pid); } -// EOF \ No newline at end of file +// EOF diff --git a/texthook/host.h b/texthook/host.h index 9154786..48b8667 100644 --- a/texthook/host.h +++ b/texthook/host.h @@ -9,6 +9,7 @@ #include #include "textthread.h" #include +#include #include "../vnrhook/include/types.h" struct ProcessRecord @@ -20,8 +21,8 @@ struct ProcessRecord HANDLE hostPipe; }; -typedef void(*ProcessEventCallback)(DWORD pid); -typedef void(*ThreadEventCallback)(TextThread*); +typedef std::function ProcessEventCallback; +typedef std::function ThreadEventCallback; struct ThreadParameterHasher { diff --git a/texthook/textthread.h b/texthook/textthread.h index b67d016..219e1b7 100644 --- a/texthook/textthread.h +++ b/texthook/textthread.h @@ -7,6 +7,7 @@ #include #include #include +#include struct ThreadParameter { @@ -23,7 +24,7 @@ struct ThreadParameter }; class TextThread; -typedef std::wstring(*ThreadOutputCallback)(TextThread*, std::wstring data); +typedef std::function ThreadOutputCallback; //extern DWORD split_time,repeat_count,global_filter,cyclic_remove;