diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..c12c6e7f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +# Normalize line endings to CRLF on checkout +* text eol=crlf + +# Git kept corrupting binary files +# by trying to convert them to CRLF +*.iwi binary +*.lib binary +*.ttf binary \ No newline at end of file diff --git a/.gitignore b/.gitignore index dbaa467f..88b36f82 100644 --- a/.gitignore +++ b/.gitignore @@ -14,13 +14,21 @@ /build /ipch +/.vs + +/Debug +/Release components/sdk +components/game_mod/steam components/*/Debug components/*/Release components/shared/*/Debug components/shared/*/Release components/D3DBSP_Lib/*/Release components/D3DBSP_Lib/*/Debug +components/launcher/imgui.ini + +components/gsc_parser/src/cpp/parser !*.lib \ No newline at end of file diff --git a/LinkerMod.sln b/LinkerMod.sln index 5f93a931..004332a5 100644 --- a/LinkerMod.sln +++ b/LinkerMod.sln @@ -6,32 +6,57 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "launcher_ldr", "components\launcher_ldr\launcher_ldr.vcxproj", "{E543772A-4051-499E-8DC6-B5501043DA7B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "linker_pc", "components\linker_pc\linker_pc.vcxproj", "{3A1FA19B-CC11-4725-AC86-6B3CF539E1B2}" + ProjectSection(ProjectDependencies) = postProject + {E543772A-4051-499E-8DC6-B5501043DA7B} = {E543772A-4051-499E-8DC6-B5501043DA7B} + {06E30C65-D79A-4FEC-8A60-B36D907E6601} = {06E30C65-D79A-4FEC-8A60-B36D907E6601} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "game_mod", "components\game_mod\game_mod.vcxproj", "{69B41EFB-E992-4351-AA29-FDC61F4DBDE0}" + ProjectSection(ProjectDependencies) = postProject + {E543772A-4051-499E-8DC6-B5501043DA7B} = {E543772A-4051-499E-8DC6-B5501043DA7B} + {06E30C65-D79A-4FEC-8A60-B36D907E6601} = {06E30C65-D79A-4FEC-8A60-B36D907E6601} + {86C18FD7-1144-440C-B5E3-265194BBC934} = {86C18FD7-1144-440C-B5E3-265194BBC934} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "radiant_mod", "components\radiant_mod\radiant_mod.vcxproj", "{6A3FB8CD-BEA3-42F1-B551-A0F56ED9B266}" + ProjectSection(ProjectDependencies) = postProject + {E543772A-4051-499E-8DC6-B5501043DA7B} = {E543772A-4051-499E-8DC6-B5501043DA7B} + {06E30C65-D79A-4FEC-8A60-B36D907E6601} = {06E30C65-D79A-4FEC-8A60-B36D907E6601} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cod2rad", "components\cod2rad\cod2rad.vcxproj", "{B19C7605-5258-457E-883A-1A7896E7BD6C}" ProjectSection(ProjectDependencies) = postProject {F9BA1B1B-9BF9-4365-8A18-531F80C8847F} = {F9BA1B1B-9BF9-4365-8A18-531F80C8847F} + {E543772A-4051-499E-8DC6-B5501043DA7B} = {E543772A-4051-499E-8DC6-B5501043DA7B} + {06E30C65-D79A-4FEC-8A60-B36D907E6601} = {06E30C65-D79A-4FEC-8A60-B36D907E6601} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cod2map", "components\cod2map\cod2map.vcxproj", "{37CF9A1C-7E15-44FA-BA81-A389E617F2D9}" ProjectSection(ProjectDependencies) = postProject {F9BA1B1B-9BF9-4365-8A18-531F80C8847F} = {F9BA1B1B-9BF9-4365-8A18-531F80C8847F} + {E543772A-4051-499E-8DC6-B5501043DA7B} = {E543772A-4051-499E-8DC6-B5501043DA7B} + {06E30C65-D79A-4FEC-8A60-B36D907E6601} = {06E30C65-D79A-4FEC-8A60-B36D907E6601} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "path_mod", "components\path_mod\path_mod.vcxproj", "{B6D3B040-AC50-4BA9-BC91-CE5F5DAAF877}" ProjectSection(ProjectDependencies) = postProject {F9BA1B1B-9BF9-4365-8A18-531F80C8847F} = {F9BA1B1B-9BF9-4365-8A18-531F80C8847F} + {E543772A-4051-499E-8DC6-B5501043DA7B} = {E543772A-4051-499E-8DC6-B5501043DA7B} + {06E30C65-D79A-4FEC-8A60-B36D907E6601} = {06E30C65-D79A-4FEC-8A60-B36D907E6601} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "D3DBSP_Lib", "components\D3DBSP_Lib\D3DBSP_Lib\D3DBSP_Lib.vcxproj", "{F9BA1B1B-9BF9-4365-8A18-531F80C8847F}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "asset_viewer", "components\asset_viewer\asset_viewer.vcxproj", "{9934C743-9987-4D7C-B847-0E77DDA520C0}" + ProjectSection(ProjectDependencies) = postProject + {E543772A-4051-499E-8DC6-B5501043DA7B} = {E543772A-4051-499E-8DC6-B5501043DA7B} + {06E30C65-D79A-4FEC-8A60-B36D907E6601} = {06E30C65-D79A-4FEC-8A60-B36D907E6601} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "asset_util", "components\asset_util\asset_util.vcxproj", "{0045F885-7DD4-4898-B760-ACB4DA32DA6B}" ProjectSection(ProjectDependencies) = postProject + {F9BA1B1B-9BF9-4365-8A18-531F80C8847F} = {F9BA1B1B-9BF9-4365-8A18-531F80C8847F} + {C61A8481-11F8-4A98-9776-C5A4E1F13D98} = {C61A8481-11F8-4A98-9776-C5A4E1F13D98} {D8721F95-955F-4931-9EBA-5C7BA28E6875} = {D8721F95-955F-4931-9EBA-5C7BA28E6875} {612D8EF6-74FD-4E5E-A5C9-F4E8BF20195A} = {612D8EF6-74FD-4E5E-A5C9-F4E8BF20195A} EndProjectSection @@ -40,58 +65,137 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "components\shared\z EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniz", "components\shared\miniz\miniz.vcxproj", "{D8721F95-955F-4931-9EBA-5C7BA28E6875}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "launcher", "components\launcher\launcher.vcxproj", "{99C1298E-362E-455B-B938-04CB5B5510BA}" + ProjectSection(ProjectDependencies) = postProject + {86C18FD7-1144-440C-B5E3-265194BBC934} = {86C18FD7-1144-440C-B5E3-265194BBC934} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imgui", "components\shared\imgui\imgui.vcxproj", "{86C18FD7-1144-440C-B5E3-265194BBC934}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "detours", "components\shared\detours\detours.vcxproj", "{06E30C65-D79A-4FEC-8A60-B36D907E6601}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gsc_parser", "components\gsc_parser\gsc_parser.vcxproj", "{C61A8481-11F8-4A98-9776-C5A4E1F13D98}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "semver", "components\shared\semver\semver.vcxproj", "{5FB372DD-A7E1-4A4B-9A28-3AC02543E9E8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_json", "components\shared\jsoncpp\makefiles\msvc\lib_json.vcxproj", "{1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E543772A-4051-499E-8DC6-B5501043DA7B}.Debug|Win32.ActiveCfg = Debug|Win32 {E543772A-4051-499E-8DC6-B5501043DA7B}.Debug|Win32.Build.0 = Debug|Win32 + {E543772A-4051-499E-8DC6-B5501043DA7B}.Debug|x64.ActiveCfg = Debug|Win32 {E543772A-4051-499E-8DC6-B5501043DA7B}.Release|Win32.ActiveCfg = Release|Win32 {E543772A-4051-499E-8DC6-B5501043DA7B}.Release|Win32.Build.0 = Release|Win32 + {E543772A-4051-499E-8DC6-B5501043DA7B}.Release|x64.ActiveCfg = Release|Win32 {3A1FA19B-CC11-4725-AC86-6B3CF539E1B2}.Debug|Win32.ActiveCfg = Debug|Win32 {3A1FA19B-CC11-4725-AC86-6B3CF539E1B2}.Debug|Win32.Build.0 = Debug|Win32 + {3A1FA19B-CC11-4725-AC86-6B3CF539E1B2}.Debug|x64.ActiveCfg = Debug|Win32 {3A1FA19B-CC11-4725-AC86-6B3CF539E1B2}.Release|Win32.ActiveCfg = Release|Win32 {3A1FA19B-CC11-4725-AC86-6B3CF539E1B2}.Release|Win32.Build.0 = Release|Win32 + {3A1FA19B-CC11-4725-AC86-6B3CF539E1B2}.Release|x64.ActiveCfg = Release|Win32 {69B41EFB-E992-4351-AA29-FDC61F4DBDE0}.Debug|Win32.ActiveCfg = Debug|Win32 {69B41EFB-E992-4351-AA29-FDC61F4DBDE0}.Debug|Win32.Build.0 = Debug|Win32 + {69B41EFB-E992-4351-AA29-FDC61F4DBDE0}.Debug|x64.ActiveCfg = Debug|Win32 {69B41EFB-E992-4351-AA29-FDC61F4DBDE0}.Release|Win32.ActiveCfg = Release|Win32 {69B41EFB-E992-4351-AA29-FDC61F4DBDE0}.Release|Win32.Build.0 = Release|Win32 + {69B41EFB-E992-4351-AA29-FDC61F4DBDE0}.Release|x64.ActiveCfg = Release|Win32 {6A3FB8CD-BEA3-42F1-B551-A0F56ED9B266}.Debug|Win32.ActiveCfg = Debug|Win32 {6A3FB8CD-BEA3-42F1-B551-A0F56ED9B266}.Debug|Win32.Build.0 = Debug|Win32 + {6A3FB8CD-BEA3-42F1-B551-A0F56ED9B266}.Debug|x64.ActiveCfg = Debug|Win32 {6A3FB8CD-BEA3-42F1-B551-A0F56ED9B266}.Release|Win32.ActiveCfg = Release|Win32 {6A3FB8CD-BEA3-42F1-B551-A0F56ED9B266}.Release|Win32.Build.0 = Release|Win32 + {6A3FB8CD-BEA3-42F1-B551-A0F56ED9B266}.Release|x64.ActiveCfg = Release|Win32 {B19C7605-5258-457E-883A-1A7896E7BD6C}.Debug|Win32.ActiveCfg = Debug|Win32 {B19C7605-5258-457E-883A-1A7896E7BD6C}.Debug|Win32.Build.0 = Debug|Win32 + {B19C7605-5258-457E-883A-1A7896E7BD6C}.Debug|x64.ActiveCfg = Debug|Win32 {B19C7605-5258-457E-883A-1A7896E7BD6C}.Release|Win32.ActiveCfg = Release|Win32 {B19C7605-5258-457E-883A-1A7896E7BD6C}.Release|Win32.Build.0 = Release|Win32 + {B19C7605-5258-457E-883A-1A7896E7BD6C}.Release|x64.ActiveCfg = Release|Win32 {37CF9A1C-7E15-44FA-BA81-A389E617F2D9}.Debug|Win32.ActiveCfg = Debug|Win32 {37CF9A1C-7E15-44FA-BA81-A389E617F2D9}.Debug|Win32.Build.0 = Debug|Win32 + {37CF9A1C-7E15-44FA-BA81-A389E617F2D9}.Debug|x64.ActiveCfg = Debug|Win32 {37CF9A1C-7E15-44FA-BA81-A389E617F2D9}.Release|Win32.ActiveCfg = Release|Win32 {37CF9A1C-7E15-44FA-BA81-A389E617F2D9}.Release|Win32.Build.0 = Release|Win32 + {37CF9A1C-7E15-44FA-BA81-A389E617F2D9}.Release|x64.ActiveCfg = Release|Win32 {B6D3B040-AC50-4BA9-BC91-CE5F5DAAF877}.Debug|Win32.ActiveCfg = Debug|Win32 + {B6D3B040-AC50-4BA9-BC91-CE5F5DAAF877}.Debug|x64.ActiveCfg = Debug|Win32 {B6D3B040-AC50-4BA9-BC91-CE5F5DAAF877}.Release|Win32.ActiveCfg = Release|Win32 + {B6D3B040-AC50-4BA9-BC91-CE5F5DAAF877}.Release|x64.ActiveCfg = Release|Win32 {F9BA1B1B-9BF9-4365-8A18-531F80C8847F}.Debug|Win32.ActiveCfg = Debug|Win32 {F9BA1B1B-9BF9-4365-8A18-531F80C8847F}.Debug|Win32.Build.0 = Debug|Win32 + {F9BA1B1B-9BF9-4365-8A18-531F80C8847F}.Debug|x64.ActiveCfg = Debug|Win32 {F9BA1B1B-9BF9-4365-8A18-531F80C8847F}.Release|Win32.ActiveCfg = Release|Win32 {F9BA1B1B-9BF9-4365-8A18-531F80C8847F}.Release|Win32.Build.0 = Release|Win32 + {F9BA1B1B-9BF9-4365-8A18-531F80C8847F}.Release|x64.ActiveCfg = Release|Win32 {9934C743-9987-4D7C-B847-0E77DDA520C0}.Debug|Win32.ActiveCfg = Debug|Win32 {9934C743-9987-4D7C-B847-0E77DDA520C0}.Debug|Win32.Build.0 = Debug|Win32 + {9934C743-9987-4D7C-B847-0E77DDA520C0}.Debug|x64.ActiveCfg = Debug|Win32 {9934C743-9987-4D7C-B847-0E77DDA520C0}.Release|Win32.ActiveCfg = Release|Win32 {9934C743-9987-4D7C-B847-0E77DDA520C0}.Release|Win32.Build.0 = Release|Win32 + {9934C743-9987-4D7C-B847-0E77DDA520C0}.Release|x64.ActiveCfg = Release|Win32 {0045F885-7DD4-4898-B760-ACB4DA32DA6B}.Debug|Win32.ActiveCfg = Debug|Win32 {0045F885-7DD4-4898-B760-ACB4DA32DA6B}.Debug|Win32.Build.0 = Debug|Win32 + {0045F885-7DD4-4898-B760-ACB4DA32DA6B}.Debug|x64.ActiveCfg = Debug|Win32 {0045F885-7DD4-4898-B760-ACB4DA32DA6B}.Release|Win32.ActiveCfg = Release|Win32 {0045F885-7DD4-4898-B760-ACB4DA32DA6B}.Release|Win32.Build.0 = Release|Win32 + {0045F885-7DD4-4898-B760-ACB4DA32DA6B}.Release|x64.ActiveCfg = Release|Win32 {612D8EF6-74FD-4E5E-A5C9-F4E8BF20195A}.Debug|Win32.ActiveCfg = Debug|Win32 {612D8EF6-74FD-4E5E-A5C9-F4E8BF20195A}.Debug|Win32.Build.0 = Debug|Win32 + {612D8EF6-74FD-4E5E-A5C9-F4E8BF20195A}.Debug|x64.ActiveCfg = Debug|Win32 {612D8EF6-74FD-4E5E-A5C9-F4E8BF20195A}.Release|Win32.ActiveCfg = Release|Win32 {612D8EF6-74FD-4E5E-A5C9-F4E8BF20195A}.Release|Win32.Build.0 = Release|Win32 + {612D8EF6-74FD-4E5E-A5C9-F4E8BF20195A}.Release|x64.ActiveCfg = Release|Win32 {D8721F95-955F-4931-9EBA-5C7BA28E6875}.Debug|Win32.ActiveCfg = Debug|Win32 {D8721F95-955F-4931-9EBA-5C7BA28E6875}.Debug|Win32.Build.0 = Debug|Win32 + {D8721F95-955F-4931-9EBA-5C7BA28E6875}.Debug|x64.ActiveCfg = Debug|Win32 {D8721F95-955F-4931-9EBA-5C7BA28E6875}.Release|Win32.ActiveCfg = Release|Win32 {D8721F95-955F-4931-9EBA-5C7BA28E6875}.Release|Win32.Build.0 = Release|Win32 + {D8721F95-955F-4931-9EBA-5C7BA28E6875}.Release|x64.ActiveCfg = Release|Win32 + {99C1298E-362E-455B-B938-04CB5B5510BA}.Debug|Win32.ActiveCfg = Debug|Win32 + {99C1298E-362E-455B-B938-04CB5B5510BA}.Debug|Win32.Build.0 = Debug|Win32 + {99C1298E-362E-455B-B938-04CB5B5510BA}.Debug|x64.ActiveCfg = Debug|Win32 + {99C1298E-362E-455B-B938-04CB5B5510BA}.Release|Win32.ActiveCfg = Release|Win32 + {99C1298E-362E-455B-B938-04CB5B5510BA}.Release|Win32.Build.0 = Release|Win32 + {99C1298E-362E-455B-B938-04CB5B5510BA}.Release|x64.ActiveCfg = Release|Win32 + {86C18FD7-1144-440C-B5E3-265194BBC934}.Debug|Win32.ActiveCfg = Debug|Win32 + {86C18FD7-1144-440C-B5E3-265194BBC934}.Debug|Win32.Build.0 = Debug|Win32 + {86C18FD7-1144-440C-B5E3-265194BBC934}.Debug|x64.ActiveCfg = Debug|Win32 + {86C18FD7-1144-440C-B5E3-265194BBC934}.Release|Win32.ActiveCfg = Release|Win32 + {86C18FD7-1144-440C-B5E3-265194BBC934}.Release|Win32.Build.0 = Release|Win32 + {86C18FD7-1144-440C-B5E3-265194BBC934}.Release|x64.ActiveCfg = Release|Win32 + {06E30C65-D79A-4FEC-8A60-B36D907E6601}.Debug|Win32.ActiveCfg = Debug|Win32 + {06E30C65-D79A-4FEC-8A60-B36D907E6601}.Debug|Win32.Build.0 = Debug|Win32 + {06E30C65-D79A-4FEC-8A60-B36D907E6601}.Debug|x64.ActiveCfg = Debug|Win32 + {06E30C65-D79A-4FEC-8A60-B36D907E6601}.Release|Win32.ActiveCfg = Release|Win32 + {06E30C65-D79A-4FEC-8A60-B36D907E6601}.Release|Win32.Build.0 = Release|Win32 + {06E30C65-D79A-4FEC-8A60-B36D907E6601}.Release|x64.ActiveCfg = Release|Win32 + {C61A8481-11F8-4A98-9776-C5A4E1F13D98}.Debug|Win32.ActiveCfg = Debug|Win32 + {C61A8481-11F8-4A98-9776-C5A4E1F13D98}.Debug|Win32.Build.0 = Debug|Win32 + {C61A8481-11F8-4A98-9776-C5A4E1F13D98}.Debug|x64.ActiveCfg = Debug|Win32 + {C61A8481-11F8-4A98-9776-C5A4E1F13D98}.Release|Win32.ActiveCfg = Release|Win32 + {C61A8481-11F8-4A98-9776-C5A4E1F13D98}.Release|Win32.Build.0 = Release|Win32 + {C61A8481-11F8-4A98-9776-C5A4E1F13D98}.Release|x64.ActiveCfg = Release|Win32 + {5FB372DD-A7E1-4A4B-9A28-3AC02543E9E8}.Debug|Win32.ActiveCfg = Debug|Win32 + {5FB372DD-A7E1-4A4B-9A28-3AC02543E9E8}.Debug|Win32.Build.0 = Debug|Win32 + {5FB372DD-A7E1-4A4B-9A28-3AC02543E9E8}.Debug|x64.ActiveCfg = Debug|Win32 + {5FB372DD-A7E1-4A4B-9A28-3AC02543E9E8}.Release|Win32.ActiveCfg = Release|Win32 + {5FB372DD-A7E1-4A4B-9A28-3AC02543E9E8}.Release|Win32.Build.0 = Release|Win32 + {5FB372DD-A7E1-4A4B-9A28-3AC02543E9E8}.Release|x64.ActiveCfg = Release|Win32 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Debug|Win32.ActiveCfg = Debug|Win32 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Debug|Win32.Build.0 = Debug|Win32 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Debug|x64.ActiveCfg = Debug|x64 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Debug|x64.Build.0 = Debug|x64 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Release|Win32.ActiveCfg = Release|Win32 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Release|Win32.Build.0 = Release|Win32 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Release|x64.ActiveCfg = Release|x64 + {1E6C2C1C-6453-4129-AE3F-0EE8E6599C89}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/README.md b/README.md index 34436f40..69e4d4ea 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # LinkerMod ###### Enhancements for Call of Duty: Black Ops' Mod Tools +#### Join the [Discord Server](https://discord.gg/nukNNsP)! ## Features - Mod Support diff --git a/build.bat b/build.bat new file mode 100644 index 00000000..9404fb6b --- /dev/null +++ b/build.bat @@ -0,0 +1,69 @@ +@echo off + +rem +rem ------- Find Visual Studio's install path ------- +rem +:find2015 +if "%VS140COMNTOOLS%"=="" ( + goto find2013 +) else ( + echo Found: Visual Studio 2015 + set TMP_VSPATH="%VS140COMNTOOLS%\..\..\vc\vcvarsall.bat" + goto compile +) + +:find2013 +if "%VS120COMNTOOLS%"=="" ( + goto find2012 +) else ( + echo Found: Visual Studio 2013 + set TMP_VSPATH="%VS120COMNTOOLS%\..\..\vc\vcvarsall.bat" + goto compile +) + +:find2012 +if "%VS110COMNTOOLS%"=="" ( + goto notfound +) else ( + echo Found: Visual Studio 2012 + set TMP_VSPATH="%VS110COMNTOOLS%\..\..\vc\vcvarsall.bat" + goto compile +) + +:notfound +echo Visual Studio (2012/2013/2015) wasn't found on your computer. +exit 1 + +rem +rem ------ Compile ------- +rem +:compile +call %TMP_VSPATH% amd64_x86 +set type=Configuration=Release;Platform=Win32 + +msbuild.exe LinkerMod.sln /maxcpucount /verbosity:minimal /t:Rebuild /p:%type% + +rem +rem ------ Copy Files ------- +rem +:docopy +setlocal ENABLEEXTENSIONS +set KEY_NAME=HKLM\SOFTWARE\activision\call of duty black ops +set VALUE_NAME=InstallPath + +FOR /F "tokens=2*" %%A IN ('REG.exe query "%KEY_NAME%" /v "%VALUE_NAME%" /reg:32 ') DO (set pInstallDir=%%B) + +if NOT "%pInstallDir%" == "" ( + copy .\build\Release\asset_viewer.dll "%pInstallDir%"\bin\asset_viewer.dll + copy .\build\Release\cod2map.dll "%pInstallDir%"\bin\cod2map.dll + copy .\build\Release\cod2rad.dll "%pInstallDir%"\bin\cod2rad.dll + copy .\build\Release\game_mod.dll "%pInstallDir%"\bin\game_mod.dll + copy .\build\Release\linker_pc.dll "%pInstallDir%"\bin\linker_pc.dll + copy .\build\Release\path_mod.dll "%pInstallDir%"\bin\path_mod.dll + copy .\build\Release\radiant_mod.dll "%pInstallDir%"\bin\radiant_mod.dll + copy .\build\Release\launcher_ldr.exe "%pInstallDir%"\bin\launcher_ldr.exe + exit 0 +) else ( + echo InstallPath registry key wasn't found. + exit 1 +) \ No newline at end of file diff --git a/components/D3DBSP_Lib/D3DBSP_Lib/ConsoleLog.cpp b/components/D3DBSP_Lib/D3DBSP_Lib/ConsoleLog.cpp index daa1a319..4c3953da 100644 --- a/components/D3DBSP_Lib/D3DBSP_Lib/ConsoleLog.cpp +++ b/components/D3DBSP_Lib/D3DBSP_Lib/ConsoleLog.cpp @@ -17,118 +17,120 @@ WORD GetConsoleTextAttribute (HANDLE hCon) return con_info.wAttributes; } - -int Con_Init() +namespace D3DBSP_Lib { - if(!hConsole) + int Con_Init() { - hConsole = GetStdHandle(STD_OUTPUT_HANDLE); - defaultConsoleAttributes = GetConsoleTextAttribute(hConsole); - - hLogfile = nullptr; + if(!hConsole) + { + hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + defaultConsoleAttributes = GetConsoleTextAttribute(hConsole); + + hLogfile = nullptr; + } + + return 0; } - - return 0; -} - -int Con_Init(const char* logfilePath, LOGFILE_MODE mode) -{ - if(!hConsole) + + int Con_Init(const char* logfilePath, LOGFILE_MODE mode) { - hConsole = GetStdHandle(STD_OUTPUT_HANDLE); - defaultConsoleAttributes = GetConsoleTextAttribute(hConsole); + if(!hConsole) + { + hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + defaultConsoleAttributes = GetConsoleTextAttribute(hConsole); + } + + return Log_Init(logfilePath, mode); } - - return Log_Init(logfilePath, mode); -} - -int Log_Init(const char* logfilePath, LOGFILE_MODE mode) -{ - if(!hLogfile) + + int Log_Init(const char* logfilePath, LOGFILE_MODE mode) { - int result = fopen_s(&hLogfile,logfilePath,FILEMODE_STRINGS[mode]); - Log_Printf("Logfile Initialized!\n"); - return result; + if(!hLogfile) + { + int result = fopen_s(&hLogfile,logfilePath,FILEMODE_STRINGS[mode]); + Log_Printf("Logfile Initialized!\n"); + return result; + } + + return 0; + } + + int Con_Printf(const char* fmt, ...) + { + SetConsoleTextAttribute(hConsole, defaultConsoleAttributes); + va_list ap; + va_start(ap, fmt); + int out = 1; + if(hConsole) + out = vprintf(fmt, ap); + if(hLogfile) + vfprintf(hLogfile,fmt,ap); + va_end(ap); + return out; + } + + int Con_Warning(const char* fmt, ...) + { + SetConsoleTextAttribute(hConsole, 0xE); + printf("WARNING: "); + va_list ap; + va_start(ap, fmt); + int out = 1; + if(hConsole) + out = vprintf(fmt, ap); + if(hLogfile) + vfprintf(hLogfile,fmt,ap); + va_end(ap); + SetConsoleTextAttribute(hConsole, defaultConsoleAttributes); + dwWarningCount++; + return out; + } + + int Con_Error(const char* fmt, ...) + { + SetConsoleTextAttribute(hConsole, 0xC); + printf("ERROR: "); + va_list ap; + va_start(ap, fmt); + int out = 1; + if(hConsole) + out = vprintf(fmt, ap); + if(hLogfile) + vfprintf(hLogfile,fmt,ap); + va_end(ap); + SetConsoleTextAttribute(hConsole, defaultConsoleAttributes); + dwErrorCount++; + return out; + } + + int Log_Printf(const char* fmt, ...) + { + int out = 1; + va_list ap; + va_start(ap, fmt); + if(hLogfile) + out = vfprintf(hLogfile,fmt,ap); + va_end(ap); + return out; + } + + DWORD Con_ErrorCount(void) + { + return dwErrorCount; + } + + DWORD Con_WarningCount(void) + { + return dwWarningCount; + } + + int Con_Restore(void) + { + if(hLogfile) + fclose(hLogfile); + if(hConsole) + return SetConsoleTextAttribute(hConsole, defaultConsoleAttributes); + + return 0; } - - return 0; -} - -int Con_Printf(const char* fmt, ...) -{ - SetConsoleTextAttribute(hConsole, defaultConsoleAttributes); - va_list ap; - va_start(ap, fmt); - int out = 1; - if(hConsole) - out = vprintf(fmt, ap); - if(hLogfile) - vfprintf(hLogfile,fmt,ap); - va_end(ap); - return out; -} - -int Con_Warning(const char* fmt, ...) -{ - SetConsoleTextAttribute(hConsole, 0xE); - printf("WARNING: "); - va_list ap; - va_start(ap, fmt); - int out = 1; - if(hConsole) - out = vprintf(fmt, ap); - if(hLogfile) - vfprintf(hLogfile,fmt,ap); - va_end(ap); - SetConsoleTextAttribute(hConsole, defaultConsoleAttributes); - dwWarningCount++; - return out; -} - -int Con_Error(const char* fmt, ...) -{ - SetConsoleTextAttribute(hConsole, 0xC); - printf("ERROR: "); - va_list ap; - va_start(ap, fmt); - int out = 1; - if(hConsole) - out = vprintf(fmt, ap); - if(hLogfile) - vfprintf(hLogfile,fmt,ap); - va_end(ap); - SetConsoleTextAttribute(hConsole, defaultConsoleAttributes); - dwErrorCount++; - return out; -} - -int Log_Printf(const char* fmt, ...) -{ - int out = 1; - va_list ap; - va_start(ap, fmt); - if(hLogfile) - out = vfprintf(hLogfile,fmt,ap); - va_end(ap); - return out; -} - -DWORD Con_ErrorCount(void) -{ - return dwErrorCount; -} - -DWORD Con_WarningCount(void) -{ - return dwWarningCount; } - -int Con_Restore(void) -{ - if(hLogfile) - fclose(hLogfile); - if(hConsole) - return SetConsoleTextAttribute(hConsole, defaultConsoleAttributes); - - return 0; -} \ No newline at end of file diff --git a/components/D3DBSP_Lib/D3DBSP_Lib/D3DBSP.cpp b/components/D3DBSP_Lib/D3DBSP_Lib/D3DBSP.cpp index 10f6834c..174fdd56 100644 --- a/components/D3DBSP_Lib/D3DBSP_Lib/D3DBSP.cpp +++ b/components/D3DBSP_Lib/D3DBSP_Lib/D3DBSP.cpp @@ -28,8 +28,8 @@ int D3DBSP::Load(const char* filepath) struct stat* results = new struct stat; if (stat(filepath, results) != 0) { - Con_Printf("\n"); - Con_Error("File not found\n"); + D3DBSP_Lib::Con_Printf("\n"); + D3DBSP_Lib::Con_Error("File not found\n"); delete results; return 1;// ERR_FILE_NOT_FOUND; } @@ -85,7 +85,7 @@ int D3DBSP::Load(BYTE* pBuf) memcpy(&magicValue,pBuf,sizeof(DWORD)); if(magicValue != 'PSBI') { - Con_Error("Buffer does not contain D3DBSP data\n"); + D3DBSP_Lib::Con_Error("Buffer does not contain D3DBSP data\n"); return 2; //ERR_NOT_A_D3DBSP_FILE } @@ -159,8 +159,8 @@ int D3DBSP::Write(const char* filepath) if(!pLump->isEmpty) { #if _DEBUG - Log_Printf("Writing Lump [0x%X] %s\n", this->diskLumpOrder[i], LUMP_NAMES[this->diskLumpOrder[i]]); - Log_Printf(" Start: 0x%X (Size: 0x%X)\n", (DWORD)ofile.tellp(), pLump->size); + D3DBSP_Lib::Log_Printf("Writing Lump [0x%X] %s\n", this->diskLumpOrder[i], LUMP_NAMES[this->diskLumpOrder[i]]); + D3DBSP_Lib::Log_Printf(" Start: 0x%X (Size: 0x%X)\n", (DWORD)ofile.tellp(), pLump->size); #endif ofile.write((char*)pLump->content, pLump->size); if(padding_size(pLump->size)) @@ -169,7 +169,7 @@ int D3DBSP::Write(const char* filepath) ofile.write((char*)&pad, padding_size(pLump->size)); } #if _DEBUG - Log_Printf(" End: 0x%X\n", (DWORD)ofile.tellp()); + D3DBSP_Lib::Log_Printf(" End: 0x%X\n", (DWORD)ofile.tellp()); #endif } } diff --git a/components/D3DBSP_Lib/D3DBSP_Lib/D3DBSP_Lib.h b/components/D3DBSP_Lib/D3DBSP_Lib/D3DBSP_Lib.h index b81c2f54..bb4ee2e1 100644 --- a/components/D3DBSP_Lib/D3DBSP_Lib/D3DBSP_Lib.h +++ b/components/D3DBSP_Lib/D3DBSP_Lib/D3DBSP_Lib.h @@ -11,16 +11,20 @@ enum LOGFILE_MODE LOGFILE_UPDATE_APPEND }; -int Con_Init(void); -int Con_Init(const char* logfilePath, LOGFILE_MODE mode); -int Log_Init(const char* logfilePath, LOGFILE_MODE mode); -int Con_Printf(const char* fmt, ...); -int Con_Warning(const char* fmt, ...); -int Con_Error(const char* fmt, ...); -int Log_Printf(const char* fmt, ...); -DWORD Con_ErrorCount(void); -DWORD Con_WarningCount(void); -int Con_Restore(void); +namespace D3DBSP_Lib +{ + + int Con_Init(void); + int Con_Init(const char* logfilePath, LOGFILE_MODE mode); + int Log_Init(const char* logfilePath, LOGFILE_MODE mode); + int Con_Printf(const char* fmt, ...); + int Con_Warning(const char* fmt, ...); + int Con_Error(const char* fmt, ...); + int Log_Printf(const char* fmt, ...); + DWORD Con_ErrorCount(void); + DWORD Con_WarningCount(void); + int Con_Restore(void); +} class Lump { diff --git a/components/D3DBSP_Lib/D3DBSP_Lib/D3DBSP_Lib.vcxproj b/components/D3DBSP_Lib/D3DBSP_Lib/D3DBSP_Lib.vcxproj index 5e216643..452d16e9 100644 --- a/components/D3DBSP_Lib/D3DBSP_Lib/D3DBSP_Lib.vcxproj +++ b/components/D3DBSP_Lib/D3DBSP_Lib/D3DBSP_Lib.vcxproj @@ -80,6 +80,7 @@ Disabled WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) false + false Windows @@ -95,6 +96,7 @@ true true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true Windows diff --git a/components/asset_util/arg.cpp b/components/asset_util/arg.cpp index 040e4817..f7039374 100644 --- a/components/asset_util/arg.cpp +++ b/components/asset_util/arg.cpp @@ -5,7 +5,8 @@ #include "cmd.h" #include "common/llist.h" #include "platform.h" -#include "common\io.h" +#include "common/io.h" +#include "shared_assert.h" Argument* g_shortcut[255] = { NULL }; diff --git a/components/asset_util/asset_util.vcxproj b/components/asset_util/asset_util.vcxproj index 29074159..478a2ea4 100644 --- a/components/asset_util/asset_util.vcxproj +++ b/components/asset_util/asset_util.vcxproj @@ -13,9 +13,17 @@ + + + + + + + + @@ -37,6 +45,14 @@ + + + + + + + + @@ -66,15 +82,15 @@ Application true - v120_xp Unicode + v120_xp Application false - v120_xp true Unicode + v120_xp @@ -101,6 +117,7 @@ Level3 Disabled WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS; + false Console @@ -121,6 +138,7 @@ true true WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS; + true Console diff --git a/components/asset_util/asset_util.vcxproj.filters b/components/asset_util/asset_util.vcxproj.filters index 2cedabe4..6cfaa9cd 100644 --- a/components/asset_util/asset_util.vcxproj.filters +++ b/components/asset_util/asset_util.vcxproj.filters @@ -54,6 +54,30 @@ cmds + + cmds + + + cmds + + + cmds + + + cmds\ripper + + + cmds\ripper + + + cmds\ripper + + + cmds\ripper + + + cmds + @@ -106,6 +130,30 @@ common + + cmds\ripper + + + cmds\ripper + + + cmds\ripper + + + cmds\ripper + + + cmds\ripper + + + cmds\ripper + + + cmds\ripper + + + cmds\search + @@ -123,5 +171,11 @@ {f2eaf143-9cec-4b52-b528-477c8590ac00} + + {0b5340fe-2268-4426-9c05-d7a26685c469} + + + {83e72f22-768f-431b-bdf5-d614ad8a8b54} + \ No newline at end of file diff --git a/components/asset_util/cmd.cpp b/components/asset_util/cmd.cpp index 3c10fcac..6629ecad 100644 --- a/components/asset_util/cmd.cpp +++ b/components/asset_util/cmd.cpp @@ -15,9 +15,16 @@ Command* Command::g_cmds = NULL; REGISTER_GLOBAL_COMMAND(g_cmd_test, "test", "An empty test command", Cmd_Test_f, CMD_CVARS(&g_var)); #endif REGISTER_GLOBAL_COMMAND(g_cmd_help, "help", "Print usage information", Cmd_Help_f, CMD_GLOBALCVARS); -REGISTER_GLOBAL_COMMAND(g_cmd_ents, "ents", "Extract the entity string from a fastfile", Cmd_Ents_f, CMD_GLOBALCVARS); -REGISTER_GLOBAL_COMMAND(g_cmd_extract_ff, "extract-ff", "Extract assets from *.ff files", Cmd_Extract_FF_f, CMD_CVARS(&g_extractAll, &g_extractSounds,&g_useLocalized)); -REGISTER_GLOBAL_COMMAND(g_cmd_extract_iwd, "extract-iwd", "Extract assets from *.iwd files", Cmd_Extract_IWD_f, CMD_CVARS(&g_extractAll, &g_extractImages, &g_extractSounds)); + +REGISTER_GLOBAL_COMMAND(g_cmd_extract_ff, "extract-ff", "Extract assets from *.ff files", Cmd_Extract_FF_f, CMD_CVARS(&g_extractAll, &g_extractSounds,&g_useLocalized, &fs_outdir)); +REGISTER_GLOBAL_COMMAND(g_cmd_extract_iwd, "extract-iwd", "Extract assets from *.iwd files", Cmd_Extract_IWD_f, CMD_CVARS(&g_extractAll, &g_extractImages, &g_extractSounds, &fs_outdir)); +REGISTER_GLOBAL_COMMAND(g_cmd_search, "search", "Search IWD's and / or FastFiles for a pattern", Cmd_Search_f, CMD_CVARS(&g_useLocalized)); +REGISTER_GLOBAL_COMMAND(g_cmd_rip, "rip", "Rip assets from the running game process", Cmd_Rip_f, CMD_CVARS(&fs_outdir, &rip_waitForProcess, &rip_waitForMap, &rip_killProcess)); + +REGISTER_GLOBAL_COMMAND(g_cmd_ents, "ents", "Extract the entity string from a fastfile", Cmd_Ents_f, CMD_CVARS(&ents_useLabels, &ents_genBrushes)); +REGISTER_GLOBAL_COMMAND(g_cmd_csvgen, "csvgen", "Experimental CSV regeneration for some assets", Cmd_CSVGen_f, CMD_CVARS(&csvgen_aitypes, &csvgen_characters, &csvgen_xmodelaliases)); + +REGISTER_GLOBAL_COMMAND(g_cmd_bsp_info, "bsp_info", "Print lump offsets and sizes for a given bsp", Cmd_BspInfo_f, CMD_GLOBALCVARS); #undef CMD_GLOBALCVARS #undef CMD_CVARS diff --git a/components/asset_util/cmd.h b/components/asset_util/cmd.h index ee43250d..336e18e1 100644 --- a/components/asset_util/cmd.h +++ b/components/asset_util/cmd.h @@ -31,8 +31,15 @@ class Command : public Argument, public LList #define REGISTER_GLOBAL_COMMAND(IDENTIFIER) extern Command IDENTIFIER; REGISTER_GLOBAL_COMMAND(g_cmd_help); -REGISTER_GLOBAL_COMMAND(g_cmd_ents); + REGISTER_GLOBAL_COMMAND(g_cmd_extract_ff); REGISTER_GLOBAL_COMMAND(g_cmd_extract_iwd); +REGISTER_GLOBAL_COMMAND(g_cmd_search); +REGISTER_GLOBAL_COMMAND(g_cmd_rip); + +REGISTER_GLOBAL_COMMAND(g_cmd_ents); +REGISTER_GLOBAL_COMMAND(g_cmd_csvgen); + +REGISTER_GLOBAL_COMMAND(g_cmd_bsp_info); #undef REGISTER_GLOBAL_COMMAND diff --git a/components/asset_util/cmds/cmd_bsp.cpp b/components/asset_util/cmds/cmd_bsp.cpp new file mode 100644 index 00000000..fab9ac9d --- /dev/null +++ b/components/asset_util/cmds/cmd_bsp.cpp @@ -0,0 +1,129 @@ +#include "../sys/AppInfo.h" +#include "../common/io.h" +#include "../common/fs.h" + +#include "../cvar.h" +#include "cmd_common.h" + +#include "../D3DBSP_Lib/D3DBSP_Lib/D3DBSP_Lib.h" +#pragma comment(lib, "D3DBSP_Lib.lib") + +static const char *LumpNames[] = +{ + "LUMP_MATERIALS", + "LUMP_LIGHTBYTES", + "LUMP_LIGHTGRIDENTRIES", + "LUMP_LIGHTGRIDCOLORS", + "LUMP_PLANES", + "LUMP_BRUSHSIDES", + "LUMP_BRUSHSIDEEDGECOUNTS", + "LUMP_BRUSHEDGES", + "LUMP_BRUSHES", + "LUMP_TRIANGLES", + "LUMP_DRAWVERTS", + "LUMP_DRAWINDICES", + "LUMP_CULLGROUPS", + "LUMP_CULLGROUPINDICES", + "LUMP_OBSOLETE_1", + "LUMP_OBSOLETE_2", + "LUMP_OBSOLETE_3", + "LUMP_OBSOLETE_4", + "LUMP_OBSOLETE_5", + "LUMP_PORTALVERTS", + "LUMP_OBSOLETE_6", + "LUMP_UINDS", + "LUMP_BRUSHVERTSCOUNTS", + "LUMP_BRUSHVERTS", + "LUMP_AABBTREES", + "LUMP_CELLS", + "LUMP_PORTALS", + "LUMP_NODES", + "LUMP_LEAFS", + "LUMP_LEAFBRUSHES", + "LUMP_LEAFSURFACES", + "LUMP_COLLISIONVERTS", + "LUMP_COLLISIONTRIS", + "LUMP_COLLISIONEDGEWALKABLE", + "LUMP_COLLISIONBORDERS", + "LUMP_COLLISIONPARTITIONS", + "LUMP_COLLISIONAABBS", + "LUMP_MODELS", + "LUMP_VISIBILITY", + "LUMP_ENTITIES", + "LUMP_PATHCONNECTIONS", + "LUMP_REFLECTION_PROBES", + "LUMP_VERTEX_LAYER_DATA", + "LUMP_PRIMARY_LIGHTS", + "LUMP_LIGHTGRIDHEADER", + "LUMP_LIGHTGRIDROWS", + "LUMP_OBSOLETE_10", + "LUMP_UNLAYERED_TRIANGLES", + "LUMP_UNLAYERED_DRAWVERTS", + "LUMP_UNLAYERED_DRAWINDICES", + "LUMP_UNLAYERED_CULLGROUPS", + "LUMP_UNLAYERED_AABBTREES", + "LUMP_WATERHEADER", + "LUMP_WATERCELLS", + "LUMP_WATERCELLDATA", + "LUMP_BURNABLEHEADER", + "LUMP_BURNABLECELLS", + "LUMP_BURNABLECELLDATA", + "LUMP_SIMPLELIGHTMAPBYTES", + "LUMP_LODCHAINS", + "LUMP_LODINFOS", + "LUMP_LODSURFACES", + "LUMP_LIGHTREGIONS", + "LUMP_LIGHTREGION_HULLS", + "LUMP_LIGHTREGION_AXES", + "LUMP_WIILIGHTGRID", + "LUMP_LIGHTGRID2D_LIGHTS", + "LUMP_LIGHTGRID2D_INDICES", + "LUMP_LIGHTGRID2D_POINTS", + "LUMP_LIGHTGRID2D_CELLS", + "LUMP_LIGHT_CORONAS", + "LUMP_SHADOWMAP_VOLUMES", + "LUMP_SHADOWMAP_VOLUME_PLANES", + "LUMP_EXPOSURE_VOLUMES", + "LUMP_EXPOSURE_VOLUME_PLANES", + "LUMP_OCCLUDERS", + "LUMP_OUTDOORBOUNDS", + "LUMP_HERO_ONLY_LIGHTS" +}; + +extern unsigned int padded(unsigned int i); + +int Cmd_BspInfo_f(int argc, char** argv) +{ + for (int i = 1; i < argc; i++) + { + char* filepath = argv[i]; + + D3DBSP iBSP; + if (iBSP.Load(argv[i]) != 0) + continue; + + if (argc > 2) + Con_Print("Info for: '%s'\n\n", FS_GetFilenameSubString(argv[i])); + + Con_Print("%-5s %-28s %-10s %s\n\n", "LUMP", "NAME", "OFFSET", "SIZE"); + + unsigned int lumpOffset = sizeof(DWORD) * 3 + 8 * iBSP.diskLumpCount; + for (unsigned int j = 0; j < iBSP.diskLumpCount; j++) + { + LUMP_TYPE type = iBSP.diskLumpOrder[j]; + unsigned int size = iBSP.lumps[type].size; + + Con_Print("[%2d]: %-28s 0x%08X ( 0x%X bytes )\n", j, LumpNames[j], lumpOffset, size); + + lumpOffset += padded(size); + } + } + + if (argc < 2) + { + char* _argv[] = { NULL, "bsp_info" }; + Cmd_Help_f(ARRAYSIZE(_argv), _argv); + return -1; + } + return 0; +} diff --git a/components/asset_util/cmds/cmd_common.h b/components/asset_util/cmds/cmd_common.h index c14e37b4..f0239ff5 100644 --- a/components/asset_util/cmds/cmd_common.h +++ b/components/asset_util/cmds/cmd_common.h @@ -1,12 +1,18 @@ #pragma once #include "../common/io.h" +#include "../cvar.h" int Cmd_Help_f(int argc, char** argv); int Cmd_Ents_f(int argc, char** argv); int Cmd_Extract_FF_f(int argc, char** argv); int Cmd_Extract_IWD_f(int argc, char** argv); +int Cmd_Search_f(int argc, char** argv); +int Cmd_Rip_f(int argc, char** argv); + +int Cmd_BspInfo_f(int argc, char** argv); +int Cmd_CSVGen_f(int argc, char** argv); // // An empty test command used for debugging diff --git a/components/asset_util/cmds/cmd_csvgen.cpp b/components/asset_util/cmds/cmd_csvgen.cpp new file mode 100644 index 00000000..9762d2b0 --- /dev/null +++ b/components/asset_util/cmds/cmd_csvgen.cpp @@ -0,0 +1,694 @@ +#include "../sys/AppInfo.h" +#include "../common/io.h" +#include "../common/fs.h" + +#include "../cvar.h" +#include "cmd_common.h" + +#include "../gsc_parser/gsc_parser.h" +#pragma comment(lib, "gsc_parser.lib") + +#include +#include + +// +// Enumerate over the children of a symbol, cancels if the callback returns false +// +void Symbol_EnumChildren(Symbol* symbol, std::function callback) +{ + for (Symbol* c = symbol->Children(); c; c = c->NextElem()) + { + if (!callback(c)) + { + return; + } + } +} + +// +// Get the main function from the global AST group +// +Function* AST_FindFunction(Symbol* AST, const char* name) +{ + for (Symbol* c = AST->Children(); c; c = c->NextElem()) + { + if (c->Type() == S_TYPE_FUNCTION_DECL) + { + Function* func = (Function*)c; + if (strcmp(func->identifier->value, name) == 0) + { + return func; + } + } + } + + return NULL; +} + +const char* XMA_Expression_ExtractModelName(Expression* expr) +{ + if (expr->Operator() != OP_TYPE_ASSIGN) + return NULL; + + Group* op1_group = (Group*)expr->Children()->HeadNode()->Owner(); + Group* op2_group = (Group*)expr->Children()->HeadNode()->PrevNode()->Owner(); + if (op1_group->Type() != S_TYPE_GROUP || op2_group->Type() != S_TYPE_GROUP) + return NULL; + + Literal* op2_val = (Literal*)op2_group->Children()->HeadNode()->Owner(); + if (op2_val->Type() != S_TYPE_LITERAL_STRING) + return NULL; + + // + // These checks aren't really necessary as all xmodelaliases scripts follow the same pattern + // + Member* array_expr = (Member*)op1_group->Children()->HeadNode()->Owner(); + if (array_expr->Type() != S_TYPE_MEMBER_ARRAY_ELEMENT) + return NULL; + + Identifier* identifier = (Identifier*)array_expr->Children()->HeadNode()->Owner(); + if (identifier->Type() != S_TYPE_IDENTIFIER) + return NULL; + + // the model names are always assigned to an array element of a[] + if (strcmp(identifier->value, "a") != 0) + return NULL; + + return op2_val->value; +} + +void AST_GenCSV_XModelAlias(Symbol* AST, const char* rawfile, FILE* csv) +{ + Function* main = AST_FindFunction(AST, "main"); + if (!main) + { + Con_Warning("script does not contain main()\n"); + return; + } + + Symbol* statements = main->Children()->HeadNode()->PrevNode()->Owner(); + if (statements->Size() < 1) + { + Con_Warning("main() function doesn't contain any statments\n"); + return; + } + + std::set models; + Symbol_EnumChildren(statements, [&models](Symbol* statement) -> bool + { + // + // Skip anything that isnt an expression + // + if (statement->Type() != S_TYPE_EXPRESSION) + return true; + + const char* model = XMA_Expression_ExtractModelName((Expression*)statement); + + // + // Skip any statements that don't contain valid data for this + // + if (!model) + return true; + + std::string modelname = model + 1; + modelname[modelname.size() - 1] = '\0'; + + models.insert(modelname); + return true; + }); + + fprintf(csv, "rawfile,xmodelalias/%s\n", rawfile); + for (const std::string& model : models) + { + fprintf(csv, "xmodel,%s\n", model.c_str()); + } +} + +void AST_GenCSV_AIType(Symbol* AST, const char* rawfile, FILE* csv) +{ + Function* main = AST_FindFunction(AST, "main"); + if (!main) + { + Con_Warning("script does not contain main()\n"); + return; + } + + Symbol* main_statements = main->Children()->HeadNode()->PrevNode()->Owner(); + if (main_statements->Size() < 1) + { + Con_Warning("main() function doesn't contain any statments\n"); + return; + } + + Function* precache = AST_FindFunction(AST, "precache"); + if (!precache) + { + Con_Warning("script does not contain precache()\n"); + return; + } + + Symbol* precache_statements = precache->Children()->HeadNode()->PrevNode()->Owner(); + if (precache_statements->Size() < 1) + { + Con_Warning("precache() function doesn't contain any statments\n"); + return; + } + + std::set weapons; + std::set includes; + std::set characters; + + Symbol_EnumChildren(main_statements, [&includes](Symbol* statement) -> bool + { + Expression* expr = (Expression*)statement; + if (expr->Type() != S_TYPE_EXPRESSION) + return true; + + if (expr->Operator() != OP_TYPE_ASSIGN) + return true; + + Group* op1_group = (Group*)expr->Children()->HeadNode()->Owner(); + Group* op2_group = (Group*)expr->Children()->HeadNode()->PrevNode()->Owner(); + if (op1_group->Type() != S_TYPE_GROUP || op2_group->Type() != S_TYPE_GROUP) + return true; + + // + // Skip if the assigned value isn't a string + // + Literal* op2_val = (Literal*)op2_group->Children()->HeadNode()->Owner(); + if (op2_val->Type() != S_TYPE_LITERAL_STRING) + return true; + + // + // Skip if this expression isn't assigning to a struct property + // + Member* prop_expr = (Member*)op1_group->Children()->HeadNode()->Owner(); + if (prop_expr->Type() != S_TYPE_MEMBER_OBJECT_PROPERTY) + return true; + + Identifier* prop_expr_args = (Identifier*)prop_expr->Children(); + + if (prop_expr_args->Type() != S_TYPE_IDENTIFIER || prop_expr_args->NextElem()->Type() != S_TYPE_IDENTIFIER) + return true; + + // + // Skip if the we're not assigning to self + // + if (strcmp(prop_expr_args->value, "self") != 0) + return true; + + // + // Skip if this expression isn't assigning to the csvInclude property + // + if (strcmp(static_cast(prop_expr_args->NextElem())->value, "csvInclude") != 0) + return true; + + std::string str = op2_val->value + 1; + + // + // Skip if the string is empty (The only thing that would be in the string here would be a quote char) + // + if (str.size() <= 1) + return true; + + // + // Skip if its not a csv file + // + auto offset = str.find_last_of("."); + if (offset != std::string::npos) + str[offset] = '\0'; // Strip the extension and trailing quote + else + str[str.size() - 1] = '\0'; // Otherwise strip the just trailing quote + + includes.insert(str); + return true; + }); + + Symbol_EnumChildren(precache_statements, [&characters, &weapons](Symbol* statement) -> bool + { + // + // Function calls reside inside groups + // + if (statement->Type() != S_TYPE_GROUP) + return true; + + if (statement->Size() < 1) + return true; + + Function* call = (Function*)statement->Children()->HeadNode()->Owner(); + + // + // Skip anything that isnt an expression + // + if (call->Type() != S_TYPE_FUNCTION_CALL) + return true; + + // + // Cheap way to get the weapons + // + if (call->Children()->HeadNode()->Owner()->Type() == S_TYPE_IDENTIFIER) + { + Identifier* identifier = (Identifier*)call->Children()->HeadNode()->Owner(); + Group* arg_group = (Group*)call->Children()->NextElem()->Children(); + + if (strcmp(identifier->value, "precacheItem") != 0) + return true; + + if (arg_group->Children() == NULL) + return true; + + Symbol* args = arg_group->Children(); + + int argc = args->Size() + 1; // The size of a linked list never includes the head node + if (argc != 1) + { + Con_Warning("Wrong number of args for precacheItem()\n"); + return true; + } + + if (args->Type() != S_TYPE_LITERAL_STRING) + { + Con_Warning("Wrong type of args for precacheItem() %d\n", args->Size()); + return true; + } + + Literal* string = (Literal*)args; + std::string weapon = string->value + 1; + weapon[weapon.size() - 1] = '\0'; + + weapons.insert(weapon); + return true; + } + + if (call->Children()->HeadNode()->Owner()->Type() == S_TYPE_REFERENCE) + { + Reference* ref = (Reference*)call->Children()->HeadNode()->Owner(); + + if (strcmp(ref->identifier->value, "precache") != 0) + return true; + + const char* substr = "character\\"; + if (strstr(ref->file->value, substr) != ref->file->value) + { + if (strstr(ref->file->value, "character/") != ref->file->value) + return true; + } + + std::string character = ref->file->value + strlen(substr); + characters.insert(character); + return true; + } + + return true; + }); + + fprintf(csv, "rawfile,aitype/%s\n", rawfile); + for (const std::string& character : characters) + { + fprintf(csv, "character,%s\n", character.c_str()); + } + + for (const std::string& weapon : weapons) + { + fprintf(csv, "weapon,sp/%s\n", weapon.c_str()); + } + + for (const std::string& include : includes) + { + fprintf(csv, "include,%s\n", include.c_str()); + } +} + +void AST_GenCSV_Character(Symbol* AST, const char* rawfile, FILE* csv) +{ + std::string csc_path = AppInfo_RawDir(); + csc_path += "\\character\\clientscripts\\"; + csc_path += rawfile; + + if (csc_path[csc_path.size() - 3] == 'g') + csc_path[csc_path.size() - 3] = 'c'; + else if (csc_path[csc_path.size() - 3] == 'G') + csc_path[csc_path.size() - 3] = 'C'; + + if (!FS_FileExists(csc_path.c_str())) + csc_path.clear(); + + Function* precache = AST_FindFunction(AST, "precache"); + if (!precache) + { + Con_Warning("script does not contain precache()\n"); + return; + } + + Symbol* precache_statements = precache->Children()->HeadNode()->PrevNode()->Owner(); + if (precache_statements->Size() < 1) + { + Con_Warning("precache() function doesn't contain any statments\n"); + return; + } + + std::set models; + std::set xmodelaliases; + Symbol_EnumChildren(precache_statements, [&models, &xmodelaliases](Symbol* statement) -> bool + { + // + // Function calls reside inside groups + // + if (statement->Type() != S_TYPE_GROUP) + return true; + + if (!statement->Children() || statement->Children()->Size() + 1 < 1) + return true; + + Function* call = (Function*)statement->Children()->HeadNode()->Owner(); + + // + // Skip anything that isnt an expression + // + if (call->Type() != S_TYPE_FUNCTION_CALL) + return true; + + // + // Cheap way to get the models + // + if (call->Children()->HeadNode()->Owner()->Type() == S_TYPE_IDENTIFIER) + { + Identifier* identifier = (Identifier*)call->Children()->HeadNode()->Owner(); + Group* arg_group = (Group*)call->Children()->NextElem()->Children(); + + if (strcmp(identifier->value, "precacheModel") != 0) + return true; + + if (arg_group->Children() == NULL) + return true; + + Symbol* args = arg_group->Children(); + + int argc = args->Size() + 1; // The size of a linked list never includes the head node + if (argc != 1) + { + Con_Warning("Wrong number of args for precacheItem()\n"); + return true; + } + + if (args->Type() != S_TYPE_LITERAL_STRING) + { + Con_Warning("Wrong type of args for precacheItem()\n"); + return true; + } + + Literal* string = (Literal*)args; + std::string model = string->value + 1; + model[model.size() - 1] = '\0'; + + models.insert(model); + return true; + } + + if (call->Children()->HeadNode()->Owner()->Type() == S_TYPE_REFERENCE) + { + Reference* ref = (Reference*)call->Children()->HeadNode()->Owner(); + + if (strcmp(ref->file->value, "codescripts\\character") != 0) + return true; + + if (strcmp(ref->identifier->value, "precacheModelArray") != 0) + return true; + + Group* arg_group = (Group*)call->Children()->NextElem()->Children(); + + if (arg_group->Children() == NULL) + return true; + + Symbol* args = arg_group->Children(); + + int argc = args->Size() + 1; // The size of a linked list never includes the head node + if (argc != 1) + { + Con_Warning("Wrong number of args for precacheModelArray()\n"); + return true; + } + + // + // The secondary call to the xmodelalias::main() + // + call = (Function*)args; + if (call->Type() != S_TYPE_FUNCTION_CALL) + { + Con_Warning("Wrong type of args for precacheModelArray()\n"); + return true; + } + + ref = (Reference*)call->Children()->HeadNode()->Owner(); + if (ref->Type() != S_TYPE_REFERENCE) + return true; + + if (strcmp(ref->identifier->value, "main") != 0) + return true; + + std::string alias = ref->file->value; + if (alias.find("xmodelalias\\") == 0) + alias = alias.c_str() + strlen("xmodelalias\\"); + + xmodelaliases.insert(alias); + return true; + } + + return true; + }); + + fprintf(csv, "rawfile,character/%s\n", rawfile); + for (const std::string& model : models) + { + fprintf(csv, "xmodel,%s\n", model.c_str()); + } + + for (const std::string& xmodelalias : xmodelaliases) + { + fprintf(csv, "xmodelalias,%s\n", xmodelalias.c_str()); + } + + if (csc_path.length() > 0) + { + fprintf(csv, "rawfile,character/clientscripts/%s", FS_GetFilenameSubString(csc_path.c_str())); + } +} + +int CSVGen_Callback(const char* filePath, const char* fileName, void(*ast_callback)(Symbol* AST, const char* rawfile, FILE* csv)) +{ + std::string csv_path = filePath; + csv_path[csv_path.length() - 3] = 'c'; + csv_path[csv_path.length() - 2] = 's'; + csv_path[csv_path.length() - 1] = 'v'; + + if (!fs_overwrite.ValueBool() && FS_FileExists(csv_path.c_str())) + { + Con_Print("File '%s' already exists - skipping...\n", FS_GetFilenameSubString(csv_path.c_str())); + return 1; + } + + FILE* h; + fopen_s(&h, filePath, "rb"); + + if (!h) + { + Con_Warning("File '%s' could not be opened - skipping...\n", fileName); + return -1; + } + + const char* typeString = ""; + if (ast_callback == AST_GenCSV_AIType) + typeString = "AIType"; + else if (ast_callback == AST_GenCSV_Character) + typeString = "Character"; + else if (ast_callback == AST_GenCSV_XModelAlias) + typeString = "XModelAlias"; + + Con_Print("%s (%s)\n", fileName, typeString); + + int fileSize = FS_FileSize(filePath); + char* buf = new char[fileSize]; + + fread(buf, 1, fileSize, h); + fclose(h); + + yyscan_t scanner = NULL; + yylex_init(&scanner); + + yy_scan_bytes(buf, fileSize, scanner); + delete[] buf; + + Symbol* AST = NULL; + int err = yyparse(&AST, scanner); + yylex_destroy(scanner); + + FILE* csv = fopen(csv_path.c_str(), "w"); + if (csv) + { + ast_callback(AST, FS_GetFilenameSubString(filePath), csv); + fclose(csv); + } + else + { + Con_Error("Could not open '%s' for writing...\n", FS_GetFilenameSubString(csv_path.c_str())); + } + + delete AST; + + return 0; +} + +int CSVGen_AIType_Callback(const char* filePath, const char* fileName) +{ + return CSVGen_Callback(filePath, fileName, AST_GenCSV_AIType); +} + +int CSVGen_Character_Callback(const char* filePath, const char* fileName) +{ + return CSVGen_Callback(filePath, fileName, AST_GenCSV_Character); +} + +int CSVGen_XModelAlias_Callback(const char* filePath, const char* fileName) +{ + return CSVGen_Callback(filePath, fileName, AST_GenCSV_XModelAlias); +} + +int Cmd_CSVGen_f(int argc, char** argv) +{ + // Automatically iterate over all files for the enabled types + bool autoMode = false; + + // True if the user manually defined specific types to handle + bool explicitType = (csvgen_aitypes.ValueBool() | csvgen_characters.ValueBool() | csvgen_xmodelaliases.ValueBool()); + + // + // If no explicit types were given - or an asterisk was the only arg - enable automode for all types + // If the user doesnt explicitly provide files to be parsed or + // the user provides only an asterisk as the file + // we automatically assume that we need to enter autoMode to automatically parse all files for the given types + // + if (argc == 1 || (argc == 2 && strcmp(argv[1], "*") == 0)) + { + autoMode = true; + + // If no explicit types were given - we enable all types + if (!explicitType) + { + csvgen_aitypes.Enable(); + csvgen_characters.Enable(); + csvgen_xmodelaliases.Enable(); + } + } + + // + // Automatic file mode logic + // + if (autoMode) + { + if (argc < 1 || argc > 2) + { + char* _argv[] = { NULL, "csvgen" }; + Cmd_Help_f(ARRAYSIZE(_argv), _argv); + return -1; + } + + char dir_path[MAX_PATH]; + if (csvgen_aitypes.ValueBool()) + { + sprintf_s(dir_path, "%s\\aitype", AppInfo_RawDir()); + FS_FileIterator(dir_path, "*.gsc", CSVGen_AIType_Callback); + } + + if (csvgen_characters.ValueBool()) + { + sprintf_s(dir_path, "%s\\character", AppInfo_RawDir()); + FS_FileIterator(dir_path, "*.gsc", CSVGen_Character_Callback); + } + + if (csvgen_xmodelaliases.ValueBool()) + { + sprintf_s(dir_path, "%s\\xmodelalias", AppInfo_RawDir()); + FS_FileIterator(dir_path, "*.gsc", CSVGen_XModelAlias_Callback); + } + + return 0; + } + + // + // Explicit file mode logic + // + // We only want to run csv gen for a specific files + // If only 1 type is given via args, use that type, otherwise print a warning and attempt to resolve the type automatically + // + int typeFlags = 0; + typeFlags |= csvgen_aitypes.ValueBool() ? 1 << 0 : 0; + typeFlags |= csvgen_characters.ValueBool() ? 1 << 1 : 0; + typeFlags |= csvgen_xmodelaliases.ValueBool() ? 1 << 2 : 0; + + for (int i = 0, bitCount = 0; i < sizeof(typeFlags) * 8; i++) + { + bitCount += (typeFlags >> i) & 1; + if (bitCount > 1) + { + Con_Warning("Warning: Multiple type arguments given. Falling back to automatic type resolution mode.\n"); + explicitType = false; + break; + } + } + + for (int i = 1; i < argc; i++) + { + const char* filepath = argv[i]; + const char* filename = FS_GetFilenameSubString(argv[i]); + + // + // Automatic Type Resolution + // + if (!explicitType) + { + if (!FS_FileExists(filepath)) + Con_Warning("File '%s' does not exist... skipping\n", filename); + else if (csvgen_aitypes.ValueBool() && strstr(argv[i], "aitype") != 0) + CSVGen_AIType_Callback(filepath, filename); + else if (csvgen_characters.ValueBool() && strstr(argv[i], "character") != 0) + CSVGen_Character_Callback(filepath, filename); + else if (csvgen_xmodelaliases.ValueBool() && strstr(argv[i], "xmodelalias") != 0) + CSVGen_XModelAlias_Callback(filepath, filename); + else + Con_Warning("Unable to resolve type for file '%s'... skipping\n", filename); + + continue; + } + + // + // Explicit type logic + // + switch (typeFlags) + { + case 1 << 0: + CSVGen_AIType_Callback(filepath, filename); + break; + case 1 << 1: + CSVGen_Character_Callback(filepath, filename); + break; + case 1 << 2: + CSVGen_XModelAlias_Callback(filepath, filename); + break; + default: + Con_Warning("Warning: Invalid type flags..."); + } + } + + // + // This should never be reached in automatic file mode + // Thus - there should always be at least 1 file provided + // + if (argc < 2) + { + char* _argv[] = { NULL, "csvgen" }; + Cmd_Help_f(ARRAYSIZE(_argv), _argv); + return -1; + } + + return 0; +} diff --git a/components/asset_util/cmds/cmd_ents.cpp b/components/asset_util/cmds/cmd_ents.cpp index 8798c8ed..74fc5364 100644 --- a/components/asset_util/cmds/cmd_ents.cpp +++ b/components/asset_util/cmds/cmd_ents.cpp @@ -1,6 +1,203 @@ #include "../common/ff.h" #include "zlib\zlib.h" #include "../common/io.h" +#include "cmd_common.h" + +#include "../cvar.h" + +#include +#include + +enum class ParseEnts_StrType +{ + KEY, + VALUE, + + INFO, // used for the header - we just drop these for now +}; + +struct KeyValuePair +{ + std::string key; + std::string value; +}; + +struct EntityTable +{ + std::string header; + std::vector> ents; +}; + +int ParseEnts(const char* ents_str, EntityTable* ents_table) +{ + std::vector stack; + + const char* str_start = NULL; + const char* ent_start = NULL; + int ent_level = 0; // Its like ESP but for ents! + + ParseEnts_StrType strType = ParseEnts_StrType::INFO; + + std::vector ent; + KeyValuePair kv; + + for (const char* c = ents_str; *c; c++) + { + if (*c == '"') + { + if (str_start == NULL) + { + str_start = c + 1; + continue; + } + + if (strType != ParseEnts_StrType::INFO) + { + std::string str(str_start, c); + if (strType == ParseEnts_StrType::KEY) + { + kv.key = str; + strType = ParseEnts_StrType::VALUE; + } + else if (strType == ParseEnts_StrType::VALUE) + { + kv.value = str; + ent.push_back(kv); + kv.key.clear(); + kv.value.clear(); + strType = ParseEnts_StrType::KEY; + } + } + str_start = NULL; + } + else if (str_start == NULL) + { + if (*c == '{') + { + if (ent_level++ == 0) + { + ent_start = c + 1; + + // Add the header to the ents table as the first entry + if (strType == ParseEnts_StrType::INFO) + { + ents_table->header = std::string(ents_str, c); + } + + strType = ParseEnts_StrType::KEY; // The next string we should see is a key + } + } + else if (*c == '}') + { + if (--ent_level == 0) + { + ents_table->ents.push_back(ent); + ent.clear(); + } + } + + if (ent_level < 0) + { + return ent_level; + } + } + } + + return ent_level; +} + +// +// Attempts to get the value for the given key and place it in +// +int Ent_GetValueForKey(std::vector* ent, const char* _key, std::string** out) +{ + std::string key = _key; + for (unsigned int i = 0; i < key.length(); i++) + { + key[i] = tolower(key[i]); + } + + for (unsigned int i = 0; i < ent->size(); i++) + { + if ((*ent)[i].key == key) + { + *out = &(*ent)[i].value; + return 1; + } + } + + return 0; +} + +std::string FormatBrush(float* offset, int size) +{ + char buf[2048]; + + int r = size / 2; + const char* mtl = "trigger"; + + std::string out = ""; + + if (ents_useLabels.ValueBool()) + out += "// brush 0\n"; + + out += "{\n"; + sprintf_s(buf, " (%.2f %.2f %.2f) (%.2f %.2f %.2f) (%.2f %.2f %.2f) %s 64 64 -304 48 0 0 lightmap_gray 16383.969 16384 -304 48 0 0\n", offset[0], offset[1] + r, offset[2] - r, offset[0] - r, offset[1] + r, offset[2] - r, offset[0] - r, offset[1], offset[2] - r, mtl); + out += buf; + sprintf_s(buf, " (%.2f %.2f %.2f) (%.2f %.2f %.2f) (%.2f %.2f %.2f) %s 64 64 -304 48 0 0 lightmap_gray 16383.953 16384 -304 48 0 0\n", offset[0] - r, offset[1], offset[2] + r, offset[0] - r, offset[1] + r, offset[2] + r, offset[0], offset[1] + r, offset[2] + r, mtl); + out += buf; + sprintf_s(buf, " (%.2f %.2f %.2f) (%.2f %.2f %.2f) (%.2f %.2f %.2f) %s 64 64 -304 0 0 0 lightmap_gray 16383.984 16384 -304 0 0 0\n", offset[0], offset[1] - r, offset[2] + r, offset[0] + r, offset[1] - r, offset[2] + r, offset[0] + r, offset[1] - r, offset[2] - r, mtl); + out += buf; + sprintf_s(buf, " (%.2f %.2f %.2f) (%.2f %.2f %.2f) (%.2f %.2f %.2f) %s 64 64 -48 0 0 0 lightmap_gray 16384 16384 -48 0 0 0\n", offset[0] + r, offset[1] - r, offset[2] + r, offset[0] + r, offset[1], offset[2] + r, offset[0] + r, offset[1], offset[2] - r, mtl); + out += buf; + sprintf_s(buf, " (%.2f %.2f %.2f) (%.2f %.2f %.2f) (%.2f %.2f %.2f) %s 64 64 -304 0 0 0 lightmap_gray 16384 16384 -304 0 0 0\n", offset[0], offset[1] + r, offset[2] + r, offset[0] - r, offset[1] + r, offset[2] + r, offset[0] - r, offset[1] + r, offset[2] - r, mtl); + out += buf; + sprintf_s(buf, " (%.2f %.2f %.2f) (%.2f %.2f %.2f) (%.2f %.2f %.2f) %s 64 64 -48 0 0 0 lightmap_gray 16384 16384 -48 0 0 0\n", offset[0]-r, offset[1] + r, offset[2] + r, offset[0] - r, offset[1], offset[2] + r, offset[0] - r, offset[1], offset[2] - r, mtl); + out += buf; + out += "}"; + + return out; +} + +int AddBrushes(EntityTable* ents_table) +{ + std::vector>& ents = ents_table->ents; + for (unsigned int i = 0; i < ents.size(); i++) + { + std::vector& ent = ents[i]; + + std::string* classname; + if (!Ent_GetValueForKey(&ent, "classname", &classname)) + continue; + + std::string* model; + if (!Ent_GetValueForKey(&ent, "model", &model)) + continue; + + // Skip any thing that uses a non-builtin brushmodel + if ((*model)[0] != '*') + continue; + + std::string* origin; + if (!Ent_GetValueForKey(&ent, "origin", &origin)) + continue; + + float offset[3]; + int r = sscanf_s(origin->c_str(), "%f %f %f", offset, offset + 1, offset + 2); + if (r != 3) + continue; + + KeyValuePair kv; + kv.key = ""; + std::string value = FormatBrush(offset, 64); + + kv.value = value; + + ent.push_back(kv); + } + + return 0; +} char* FindEntsString(BYTE* start, BYTE* end) { @@ -13,7 +210,7 @@ char* FindEntsString(BYTE* start, BYTE* end) if (strncmp(pattern, (char*)c, len) == NULL) { char* p = (char*)c; - + // // Automatically retry until the real start of the ents string is found // @@ -48,7 +245,7 @@ char* FindEntsString(BYTE* start, BYTE* end) return p; } - Con_Print("Trying different offset... (%d attempts remaining)\n", retryCount - 1); + Con_Print_v("Trying different offset... (%d attempts remaining)\n", retryCount - 1); p--; } } @@ -64,59 +261,96 @@ char* FindEntsString(BYTE* start, BYTE* end) // int Cmd_Ents_f(int argc, char** argv) { - // - // Temp arg handler - // - _ASSERT(argc > 1); - char* filepath = argv[1]; + for (int i = 1; i < argc; i++) + { + char* filepath = argv[i]; - Con_Print("Extracting ents from \"%s\"...\n", filepath); + Con_Print_v("Extracting ents from \"%s\"...\n", filepath); - FILE* h = nullptr; - if (fopen_s(&h, filepath, "r+b") != 0) - { - Con_Error("ERROR: Fastfile '%s' could not be found\n\n", filepath); - return FALSE; - } - rewind(h); + FILE* h = nullptr; + if (fopen_s(&h, filepath, "r+b") != 0) + { + Con_Error("ERROR: Fastfile '%s' could not be found\n", filepath); + return FALSE; + } + rewind(h); - fseek(h, 0, SEEK_END); - size_t fileSize = ftell(h); + fseek(h, 0, SEEK_END); + size_t fileSize = ftell(h); - // Get Compressed FileSize and Allocate a Storage Buffer for Compressed Data - size_t cSize = fileSize - 12; - BYTE* cBuf = new BYTE[cSize | 0x8000]; + // Get Compressed FileSize and Allocate a Storage Buffer for Compressed Data + size_t cSize = fileSize - 12; + BYTE* cBuf = new BYTE[cSize | 0x8000]; - fseek(h, 12, SEEK_SET); - fread(cBuf, 1, cSize, h); + fseek(h, 12, SEEK_SET); + fread(cBuf, 1, cSize, h); - XFile ffInfo; - unsigned long dSize = sizeof(XFile); - uncompress((BYTE*)&ffInfo, &dSize, cBuf, 0x8000); + XFile ffInfo; + unsigned long dSize = sizeof(XFile); + uncompress((BYTE*)&ffInfo, &dSize, cBuf, 0x8000); - dSize = ffInfo.size + 36; - if (dSize >= 1073741824) - { - //Any fastfiles that claim they decompress to a file >= 1GB - //are either corrupt or do not belong to the vanilla game - Con_Error("ERROR: Skipping %s\n", filepath); - return 1; - } + dSize = ffInfo.size + 36; + if (dSize >= 1073741824) + { + //Any fastfiles that claim they decompress to a file >= 1GB + //are either corrupt or do not belong to the vanilla game + Con_Error("ERROR: Skipping %s\n", filepath); + return 1; + } - BYTE* dBuf = new BYTE[dSize]; - uncompress(dBuf, &dSize, cBuf, cSize); - delete[] cBuf; + BYTE* dBuf = new BYTE[dSize]; + uncompress(dBuf, &dSize, cBuf, cSize); + delete[] cBuf; - char* result = FindEntsString((BYTE*)dBuf, dBuf + ffInfo.size + 36); - if (result == NULL) - { - Con_Error("Error: Could not find entity string\n"); + char* ents_str = FindEntsString((BYTE*)dBuf, dBuf + ffInfo.size + 36); + if (ents_str == NULL) + { + Con_Error("Error: Could not find entity string\n"); + delete[] dBuf; + return -1; + } + + EntityTable ents_table; + int l = ParseEnts(ents_str, &ents_table); + delete[] dBuf; // Free the fastfile buffer as we don't need it anymore + + if (ents_genBrushes.ValueBool()) + AddBrushes(&ents_table); + + Con_Print_nv( "iwmap 4\n" + "\"000_Global\" flags active\n" + "\"The Map\" flags\n" ); + + Con_Print("%s", ents_table.header.c_str()); + +#if 1 + std::vector>& ents = ents_table.ents; + for (unsigned int i = 0; i < ents.size(); i++) + { + std::vector& ent = ents[i]; + + if (ents_useLabels.ValueBool()) + Con_Print("// Entity %d\n", i); + Con_Print("{\n"); + for (unsigned int k = 0; k < ents[i].size(); k++) + { + KeyValuePair& kv = ent[k]; + if (kv.key.length() > 0) + Con_Print("\"%s\" \"%s\"\n", kv.key.c_str(), kv.value.c_str()); + else + Con_Print("%s\n", kv.value.c_str()); + } + Con_Print("}\n"); + } +#endif } - else + + if (argc < 2) { - Con_Print("%s\n", result); + char* _argv[] = { NULL, "ents" }; + Cmd_Help_f(ARRAYSIZE(_argv), _argv); + return -1; } - delete[] dBuf; return 0; } \ No newline at end of file diff --git a/components/asset_util/cmds/cmd_extract.cpp b/components/asset_util/cmds/cmd_extract.cpp index add0eeb1..52b8baaa 100644 --- a/components/asset_util/cmds/cmd_extract.cpp +++ b/components/asset_util/cmds/cmd_extract.cpp @@ -3,18 +3,126 @@ #include "../sys/AppInfo.h" #include "../common/io.h" +#include "../common/fs.h" #include "../cvar.h" +#include +#include + +enum PriorityFlags +{ + PATCH = 1 << 0, + MP = 1 << 1, + SO = 1 << 2, + ZM = 1 << 3, + + CODE_PRE_GFX = 1 << 4, + CODE_POST_GFX = 1 << 5, + UI = 1 << 6, + + COMMON = 1 << 7, + TERMINAL = 1 << 8, + LEVEL = 1 << 9, + + LOCALIZED = 1 << 10 +}; + +// Determine the extraction order priority for a given fastfile +// Lower numbers get higher priority (are extracted first) +int FF_GetPriority(const char* name, const char* path) +{ + int flags = (PriorityFlags)NULL; + + if (strstr(path, AppInfo_FFDir()) != NULL) + flags |= LOCALIZED; + + if (strstr(name, "_patch") != NULL || strstr(name, "patch") == name) + flags |= PATCH; + + if (strstr(name, "mp_") != NULL || strstr(name, "_mp") != NULL) + flags |= MP; + + if (strstr(name, "so_") != NULL) + flags |= SO; + + if (strstr(name, "zombie")) + flags |= ZM; + + if (strstr(name, "code_pre_gfx") != NULL) + flags |= CODE_PRE_GFX; + else if (strstr(name, "code_post_gfx") != NULL) + flags |= CODE_POST_GFX; + else if (strstr(name, "common") != NULL) + flags |= COMMON; + else if (strstr(name, "ui") != NULL) + flags |= UI; + else if (strstr(name, "terminal") != NULL) + flags |= TERMINAL; + else + flags |= LEVEL; + + return flags; +} + +struct FastFileEntry +{ + int priority; + std::string name; + std::string path; +}; + int Cmd_Extract_FF_f(int argc, char** argv) { - if (g_useLocalized.ValueBool()) + if (g_extractAll.ValueBool()) { - FS_FileIterator(AppInfo_ZoneDir(), FS_SEARCHPATTERN_FF, FF_FFExtract); + std::vector entries; + auto FF_ExtractDefered = [&entries](const char* filePath, const char* fileName) -> int + { + FastFileEntry entry; + entry.priority = FF_GetPriority(fileName, filePath); + entry.path = filePath; + entry.name = fileName; + + entries.push_back(entry); + return 0; + }; + + // Handle iterating over the fastfiles for each fastfile directory + // Used to ensure localized fastfiles are retrieved as well + auto FF_IterateFastFileDirectory = [&FF_ExtractDefered](const char* path) -> int + { + return FS_FileIterator(path, FS_SEARCHPATTERN_FF, FF_ExtractDefered); + }; + + // + // Extract normal fastfiles while deferring patch fastfiles + // + if (g_useLocalized.ValueBool()) + FS_DirectoryIterator(AppInfo_ZoneDir(), FF_IterateFastFileDirectory); + else + FS_FileIterator(AppInfo_FFDir(), FS_SEARCHPATTERN_FF, FF_ExtractDefered); + + // + // Sort the fastfiles by priority weight + // + std::sort(entries.begin(), entries.end(), [](FastFileEntry& a, FastFileEntry& b)->bool + { + return a.priority < b.priority; + }); + + for (auto& entry : entries) + { + FF_FFExtract(entry.path.c_str(), entry.name.c_str()); + } } else { - FS_FileIterator(AppInfo_FFDir(), FS_SEARCHPATTERN_FF, FF_FFExtract); + for (int i = 1; i < argc; i++) + { + const char* filename = FS_GetFilenameSubString(argv[i]); + FF_FFExtract(argv[i], filename); + } } return 0; } diff --git a/components/asset_util/cmds/cmd_help.cpp b/components/asset_util/cmds/cmd_help.cpp index 40f9e0c6..1a6fda7d 100644 --- a/components/asset_util/cmds/cmd_help.cpp +++ b/components/asset_util/cmds/cmd_help.cpp @@ -18,5 +18,20 @@ int Cmd_Help_f(int argc, char** argv) } Arg_PrintUsage(cmd); + + if (cmd == &g_cmd_csvgen) + { + Con_Print("Info:\n" + " csvgen has two 'modes' which control how it runs.\n\n" + " auto mode: csvgen [options] [*]\n" + " Options determine which types csvgen tries to regenerate csvs for\n" + " by default all types are enabled\n\n" + " explicit mode: csvgen [option] file1 [file2 ...]\n" + " Command specific options define the type of file(s) being passed to csvgen\n" + " If no type option is given, or more than one is given csvgen will attempt\n" + " to automatically determine the type for each file\n" + "\n"); + } + return 0; } \ No newline at end of file diff --git a/components/asset_util/cmds/cmd_rip.cpp b/components/asset_util/cmds/cmd_rip.cpp new file mode 100644 index 00000000..40e4cf0e --- /dev/null +++ b/components/asset_util/cmds/cmd_rip.cpp @@ -0,0 +1,68 @@ +#include "cmd_common.h" + +#include "../sys/process.h" +#include "ripper\process_info.h" + +#include "ripper\snd_ripper.h" + +bool kill_process = true; +bool wait = true; + +int Cmd_Rip_f(int argc, char** argv) +{ + unsigned int timeoutDelay = rip_waitForProcess.ValueBool() ? UINT_MAX : 0; + if (processId_t pid = Process_FindSupportedProcess(timeoutDelay)) + { + if (int err = ProcessInfo_Init(pid)) + { + Con_Error("Couldn't initialize process info\n"); + return err; + } + + if (rip_waitForMap.ValueBool()) + { + DB_WaitForMapToLoad(); + } + + Process_SuspendThreads(pid); + + snapshots_map_t snapshots_map(0); + DB_EnumAssetPoolEx(ASSET_TYPE_SOUND, Rip_SoundBank_GatherSnapshots_Callback_f, NULL, &snapshots_map); + DB_EnumAssetPoolEx(ASSET_TYPE_SOUND, Rip_SoundBank_Callback_f, NULL, &snapshots_map); + + for (auto& set : snapshots_map) + { + std::string name = set.first; + Rip_Snapshot_Callback_f(name, set.second); + } + + radverbs_map_t radverbs_map(0); + DB_EnumAssetPoolEx(ASSET_TYPE_SOUND, Rip_SoundBank_GatherRadverbs_Callback_f, NULL, &radverbs_map); + + for (auto& set : radverbs_map) + { + std::string name = set.first; + Rip_Radverb_Callback_f(name, set.second); + } + + Process_ResumeThreads(pid); + + ProcessInfo_Free(); + + if (rip_killProcess.ValueBool()) + { + Process_KillProcess(pid); + } + + return 0; + } + + if (argc < 2) + { + char* _argv[] = { NULL, "rip" }; + Cmd_Help_f(ARRAYSIZE(_argv), _argv); + return -1; + } + + return 1; +} \ No newline at end of file diff --git a/components/asset_util/cmds/cmd_search.cpp b/components/asset_util/cmds/cmd_search.cpp new file mode 100644 index 00000000..f9464505 --- /dev/null +++ b/components/asset_util/cmds/cmd_search.cpp @@ -0,0 +1,228 @@ +#include "../sys/AppInfo.h" +#include "cmd_common.h" + +#include "../common/fs.h" +#include "../common/ff.h" + +#include "zlib\zlib.h" +#include "shared_assert.h" + +#include "search/handler.h" + +enum class FileType +{ + IWD, + FF, + + ERR, +}; + +struct FileEntry +{ + FileType type; + + std::string name; + std::string path; + + void Clear(void) + { + type = FileType::ERR; + path = ""; + name = ""; + } +}; + +class CachedFile +{ +private: + FileEntry entry; + + BYTE* data; + size_t size; + +private: + void Clear(void) + { + this->entry.Clear(); + delete[] this->data; + data = NULL; + size = NULL; + } + +public: + std::string test; + + CachedFile(void) : data(NULL), size(0) + { + } + + CachedFile(CachedFile&& arg) + { + this->entry = arg.entry; + this->data = arg.data; + this->size = arg.size; + arg.data = NULL; + arg.size = 0; + } + + ~CachedFile(void) + { + this->Clear(); + } + + bool isValid() + { + return data != NULL; + } + + void Load(FileEntry& _entry) + { + Con_Print_v("Load: %s\n", _entry.name.c_str()); + + this->entry = _entry; + + FILE* h = nullptr; + if (fopen_s(&h, entry.path.c_str(), "r+b") != 0) + { + Con_Error("ERROR: Fastfile '%s' could not be found\n", entry.path.c_str()); + entry.Clear(); + return; + } + rewind(h); + + fseek(h, 0, SEEK_END); + size_t fileSize = ftell(h); + + if (entry.type == FileType::FF) + { + // Get Compressed FileSize and Allocate a Storage Buffer for Compressed Data + size_t cSize = fileSize - 12; + BYTE* cBuf = new BYTE[cSize | 0x8000]; + + fseek(h, 12, SEEK_SET); + fread(cBuf, 1, cSize, h); + + this->data = cBuf; + this->size = cSize; + } + else if (entry.type == FileType::IWD) + { + this->size = fileSize; + this->data = new BYTE[size]; + fread(data, 1, size, h); + } + + fclose(h); + } + + void Decompress(void) + { + Con_Print_v("Decompress: %s\n", entry.name.c_str()); + + if (!this->isValid()) + return; + + if (this->entry.type == FileType::FF) + { + XFile ffInfo; + unsigned long dSize = sizeof(XFile); + uncompress((BYTE*)&ffInfo, &dSize, this->data, 0x8000); + + dSize = ffInfo.size + 36; + if (dSize >= 1073741824) + { + //Any fastfiles that claim they decompress to a file >= 1GB + //are either corrupt or do not belong to the vanilla game + Con_Error("ERROR: Skipping %s\n", entry.name.c_str()); + this->Clear(); + return; + } + + BYTE* dBuf = new BYTE[dSize]; + uncompress(dBuf, &dSize, this->data, this->size); + + BYTE* cBuf = this->data; + this->data = dBuf; + this->size = dSize; + delete[] cBuf; + } + } + + void Search(char* pattern) + { + Con_Print_v("Search: %s\n", this->entry.name.c_str()); + + if (!this->isValid()) + return; + + unsigned int pattern_len = strlen(pattern); + for (unsigned int i = 0; i < this->size - pattern_len + 1; i++) + { + if (memcmp(&data[i], pattern, pattern_len) == 0) + { + Con_Print("%s\n", entry.name.c_str()); + return; + } + } + + return; + } +}; + +void FF_Cache(FileEntry& fileEntry, CachedFile& outData) +{ + outData.Load(fileEntry); +} + +void FF_Decompress(CachedFile& fileData) +{ + fileData.Decompress(); +} + +int Cmd_Search_f(int argc, char** argv) +{ + if (argc != 2) + { + char* _argv[] = { NULL, "search" }; + Cmd_Help_f(ARRAYSIZE(_argv), _argv); + return -1; + } + + const unsigned int MAX_CACHED_FILES = 2; + const unsigned int THREAD_COUNT = 3; + + std::vector entries; + auto FF_ExtractDefered = [&entries](const char* filePath, const char* fileName) -> int + { + FileEntry entry = { FileType::FF, fileName, filePath }; + entries.push_back(entry); + return 0; + }; + + // Handle iterating over the fastfiles for each fastfile directory + // Used to ensure localized fastfiles are retrieved as well + auto FF_IterateFastFileDirectory = [&FF_ExtractDefered](const char* path) -> int + { + return FS_FileIterator(path, FS_SEARCHPATTERN_FF, FF_ExtractDefered); + }; + + // + // Extract normal fastfiles while deferring patch fastfiles + // + if (g_useLocalized.ValueBool()) + FS_DirectoryIterator(AppInfo_ZoneDir(), FF_IterateFastFileDirectory); + else + FS_FileIterator(AppInfo_FFDir(), FS_SEARCHPATTERN_FF, FF_ExtractDefered); + + char* pattern = argv[1]; + auto FF_Search = [pattern](CachedFile& fileData) + { + fileData.Search(pattern); + }; + + Handler ffHandler(entries, MAX_CACHED_FILES, FF_Search, FF_Cache, FF_Decompress); + + ffHandler.RunHandler(THREAD_COUNT); + + return 0; +} diff --git a/components/asset_util/cmds/ripper/db_registry.cpp b/components/asset_util/cmds/ripper/db_registry.cpp new file mode 100644 index 00000000..4475b95a --- /dev/null +++ b/components/asset_util/cmds/ripper/db_registry.cpp @@ -0,0 +1,256 @@ +#include "db_registry.h" +#include "foreign_ptr.h" +#include "../../common/io.h" + +#include "dvar.h" + +static const char* g_assetNames[] = +{ + "xmodelpieces", + "physpreset", + "physconstraints", + "destructibledef", + "xanim", + "xmodel", + "material", + "techset", + "image", + "sound", + "sound_patch", + "col_map_sp", + "col_map_mp", + "com_map", + "game_map_sp", + "game_map_mp", + "map_ents", + "gfx_map", + "lightdef", + "ui_map", + "font", + "menufile", + "menu", + "localize", + "weapon", + "weapondef", + "weaponvariant", + "snddriverglobals", + "fx", + "impactfx", + "aitype", + "mptype", + "mpbody", + "mphead", + "character", + "xmodelalias", + "rawfile", + "stringtable", + "packindex", + "xGlobals", + "ddl", + "glasses", + "emblemset" +}; + +const char * DB_GetXAssetTypeName(int type) +{ + return g_assetNames[type]; +} + +unsigned int DB_HashForName(const char *name, XAssetType type) +{ + unsigned int hash = type; + for (const char * pos = name; *pos; ++pos) + { + int c = tolower(*pos); + if (c == '\\') + c = '/'; + hash = (hash << 16) + c + (hash << 6) - hash; + } + return hash; +} + +const char* DB_GetXAssetName(XAsset *asset) +{ + XAssetHeader header = ForeignPointer(asset)->header; + SIZE_T numofbytesread = 0; + + static char str[256]; + + //switch (ForeignPointer(asset)->type) + //{ + //default: + strcpy_s(str, "unsupported asset type"); + return str; + //}; +} + +int DB_EnumAssetPool(XAssetType type, asset_callback_t assetCallback_f, asset_callback_t assetOverrideCallback_f, const char* zone) +{ + ForeignPointer& db_hashTable = g_process.db_hashTable; + ForeignPointer& db_assetEntryPool = g_process.db_assetEntryPool; + ForeignPointer& db_zoneNames = g_process.db_zoneNames; + + SIZE_T numofbytesread = 0; + + unsigned int assetCount = 0; + for (unsigned int hashIndex = 0; hashIndex < 0x8000; hashIndex++) + { + __int16 nextAssetEntryIndex = 0; + for (int assetEntryIndex = db_hashTable[hashIndex]; assetEntryIndex; assetEntryIndex = nextAssetEntryIndex) + { + XAssetEntry assetEntry = db_assetEntryPool[assetEntryIndex].entry; + + nextAssetEntryIndex = assetEntry.nextHash; + if (assetEntry.asset.type != type) + continue; + + if (assetCallback_f) + { + XAsset* assetOffset = (XAsset*)(db_assetEntryPool.pAddress + assetEntryIndex); + ForeignPointer zoneName(db_zoneNames.pAddress + assetEntry.zoneIndex); + if (zone == NULL || (zone != NULL && _stricmp(zoneName->name, zone) == 0)) + { + ForeignPointer asset(assetOffset); + if (assetCallback_f(asset, zoneName) == 0) + assetCount++; + } + } + + if (!assetOverrideCallback_f) + continue; + + XAssetEntry overrideAssetEntry; + for (int overrideAssetEntryIndex = assetEntry.nextOverride; overrideAssetEntryIndex; overrideAssetEntryIndex = overrideAssetEntry.nextOverride) + { + overrideAssetEntry = db_assetEntryPool[overrideAssetEntryIndex].entry; + + XAsset* overrideAssetOffset = (XAsset*)(db_assetEntryPool.pAddress + overrideAssetEntryIndex); + ForeignPointer overrideZoneName(db_zoneNames.pAddress + overrideAssetEntry.zoneIndex); + if (zone == NULL || (zone != NULL && _stricmp(overrideZoneName->name, zone) == 0)) + { + ForeignPointer overrideAsset(overrideAssetOffset); + if (assetOverrideCallback_f(overrideAsset, overrideZoneName) == 0) + assetCount++; + } + } + } + } + + return assetCount; +} + +int DB_EnumAssetPoolEx(XAssetType type, asset_callback_ex_t assetCallback_f, asset_callback_ex_t assetOverrideCallback_f, void* data, const char* zone) +{ + ForeignPointer& db_hashTable = g_process.db_hashTable; + ForeignPointer& db_assetEntryPool = g_process.db_assetEntryPool; + ForeignPointer& db_zoneNames = g_process.db_zoneNames; + + SIZE_T numofbytesread = 0; + + unsigned int assetCount = 0; + for (unsigned int hashIndex = 0; hashIndex < 0x8000; hashIndex++) + { + __int16 nextAssetEntryIndex = 0; + for (int assetEntryIndex = db_hashTable[hashIndex]; assetEntryIndex; assetEntryIndex = nextAssetEntryIndex) + { + XAssetEntry assetEntry = db_assetEntryPool[assetEntryIndex].entry; + + nextAssetEntryIndex = assetEntry.nextHash; + if (assetEntry.asset.type != type) + continue; + + if (assetCallback_f) + { + XAsset* assetOffset = (XAsset*)(db_assetEntryPool.pAddress + assetEntryIndex); + ForeignPointer zoneName(db_zoneNames.pAddress + assetEntry.zoneIndex); + if (zone == NULL || (zone != NULL && _stricmp(zoneName->name, zone) == 0)) + { + ForeignPointer asset(assetOffset); + if (assetCallback_f(asset, zoneName, data) == 0) + assetCount++; + } + } + + if (!assetOverrideCallback_f) + continue; + + XAssetEntry overrideAssetEntry; + for (int overrideAssetEntryIndex = assetEntry.nextOverride; overrideAssetEntryIndex; overrideAssetEntryIndex = overrideAssetEntry.nextOverride) + { + overrideAssetEntry = db_assetEntryPool[overrideAssetEntryIndex].entry; + + XAsset* overrideAssetOffset = (XAsset*)(db_assetEntryPool.pAddress + overrideAssetEntryIndex); + ForeignPointer overrideZoneName(db_zoneNames.pAddress + overrideAssetEntry.zoneIndex); + if (zone == NULL || (zone != NULL && _stricmp(overrideZoneName->name, zone) == 0)) + { + ForeignPointer overrideAsset(overrideAssetOffset); + if (assetOverrideCallback_f(overrideAsset, overrideZoneName, data) == 0) + assetCount++; + } + } + } + } + + return assetCount; +} + +void* DB_FindSingletonAssetForType(XAssetType type) +{ + ForeignPointer& db_hashTable = g_process.db_hashTable; + ForeignPointer& db_assetEntryPool = g_process.db_assetEntryPool; + ForeignPointer& db_zoneNames = g_process.db_zoneNames; + + SIZE_T numofbytesread = 0; + + unsigned int assetCount = 0; + for (unsigned int hashIndex = 0; hashIndex < 0x8000; hashIndex++) + { + __int16 nextAssetEntryIndex = 0; + for (int assetEntryIndex = db_hashTable[hashIndex]; assetEntryIndex; assetEntryIndex = nextAssetEntryIndex) + { + XAssetEntry assetEntry = db_assetEntryPool[assetEntryIndex].entry; + + nextAssetEntryIndex = assetEntry.nextHash; + if (assetEntry.asset.type != type) + continue; + + return assetEntry.asset.header.data; + } + } + + return NULL; +} + +int DB_ListAssetPool_AssetCallback(ForeignPointer& asset, ForeignPointer& zoneName) +{ + const char* assetName = DB_GetXAssetName(asset.pAddress); + Con_Print("%s,%s\n", assetName, zoneName->name); + return 0; +} + +void DB_ListAssetPool(XAssetType type, const char* zone) +{ + int count = DB_EnumAssetPool(type, DB_ListAssetPool_AssetCallback, DB_ListAssetPool_AssetCallback, zone); + printf("Total of %d assets in %s pool\n", count, DB_GetXAssetTypeName(type)); +} + +void DB_WaitForMapToLoad(void) +{ + // Delay (ms) to prevent the checks from causing the game process to slow down + const unsigned int sleep_delay = 100; + + Con_Print("Waiting for map to load... "); + + while (*g_process.cl_ingame == NULL) + { + Sleep(sleep_delay); + } + + ForeignPointer cl_ingame_val = *g_process.cl_ingame; + while (!cl_ingame_val->current.enabled) + { + Sleep(sleep_delay); + } + + Con_Print("Map Loaded!\n"); +} \ No newline at end of file diff --git a/components/asset_util/cmds/ripper/db_registry.h b/components/asset_util/cmds/ripper/db_registry.h new file mode 100644 index 00000000..c17e779f --- /dev/null +++ b/components/asset_util/cmds/ripper/db_registry.h @@ -0,0 +1,125 @@ +#pragma once +#include "../../sys/process.h" +#include +#include + +enum XAssetType +{ + ASSET_TYPE_XMODELPIECES = 0x0, + ASSET_TYPE_PHYSPRESET = 0x1, + ASSET_TYPE_PHYSCONSTRAINTS = 0x2, + ASSET_TYPE_DESTRUCTIBLEDEF = 0x3, + ASSET_TYPE_XANIMPARTS = 0x4, + ASSET_TYPE_XMODEL = 0x5, + ASSET_TYPE_MATERIAL = 0x6, + ASSET_TYPE_TECHNIQUE_SET = 0x7, + ASSET_TYPE_IMAGE = 0x8, + ASSET_TYPE_SOUND = 0x9, + ASSET_TYPE_SOUND_PATCH = 0xA, + ASSET_TYPE_CLIPMAP = 0xB, + ASSET_TYPE_CLIPMAP_PVS = 0xC, + ASSET_TYPE_COMWORLD = 0xD, + ASSET_TYPE_GAMEWORLD_SP = 0xE, + ASSET_TYPE_GAMEWORLD_MP = 0xF, + ASSET_TYPE_MAP_ENTS = 0x10, + ASSET_TYPE_GFXWORLD = 0x11, + ASSET_TYPE_LIGHT_DEF = 0x12, + ASSET_TYPE_UI_MAP = 0x13, + ASSET_TYPE_FONT = 0x14, + ASSET_TYPE_MENULIST = 0x15, + ASSET_TYPE_MENU = 0x16, + ASSET_TYPE_LOCALIZE_ENTRY = 0x17, + ASSET_TYPE_WEAPON = 0x18, + ASSET_TYPE_WEAPONDEF = 0x19, + ASSET_TYPE_WEAPON_VARIANT = 0x1A, + ASSET_TYPE_SNDDRIVER_GLOBALS = 0x1B, + ASSET_TYPE_FX = 0x1C, + ASSET_TYPE_IMPACT_FX = 0x1D, + ASSET_TYPE_AITYPE = 0x1E, + ASSET_TYPE_MPTYPE = 0x1F, + ASSET_TYPE_MPBODY = 0x20, + ASSET_TYPE_MPHEAD = 0x21, + ASSET_TYPE_CHARACTER = 0x22, + ASSET_TYPE_XMODELALIAS = 0x23, + ASSET_TYPE_RAWFILE = 0x24, + ASSET_TYPE_STRINGTABLE = 0x25, + ASSET_TYPE_PACK_INDEX = 0x26, + ASSET_TYPE_XGLOBALS = 0x27, + ASSET_TYPE_DDL = 0x28, + ASSET_TYPE_GLASSES = 0x29, + ASSET_TYPE_EMBLEMSET = 0x2A, + ASSET_TYPE_COUNT = 0x2B, + ASSET_TYPE_STRING = 0x2B, + ASSET_TYPE_ASSETLIST = 0x2C, +}; + +union XAssetHeader +{ + struct SndBank *sound; + void *data; +}; + +struct XAsset +{ + XAssetType type; + XAssetHeader header; +}; + +struct XAssetEntry +{ + XAsset asset; + char zoneIndex; + bool inuse; + unsigned __int16 nextHash; + unsigned __int16 nextOverride; + unsigned __int16 usageFrame; +}; + +union XAssetEntryPoolEntry +{ + XAssetEntry entry; + XAssetEntryPoolEntry *next; +}; + +enum FF_DIR +{ + FFD_DEFAULT = 0x0, + FFD_MOD_DIR = 0x1, + FFD_USER_MAP = 0x2, +}; + +struct XZoneName +{ + char name[64]; + int flags; + int fileSize; + FF_DIR dir; + bool loaded; +}; + +#include "foreign_ptr.h" + +const char* DB_GetXAssetTypeName(int type); +const char* DB_GetXAssetName(XAsset *asset); + +// +// Enumerate over all assets of a given type in the asset pool +// if assetOverrideCallback_f is NULL, override assets will be ignored +// the zone argument can be used to filter by a specific zone name +// +// The return value is the number of assets that were successfully enumerated (where the callback succeeded) +// +typedef int(__cdecl* asset_callback_t)(ForeignPointer& asset, ForeignPointer& zoneName); +typedef int(__cdecl* asset_callback_ex_t)(ForeignPointer& asset, ForeignPointer& zoneName, void* data); + +int DB_EnumAssetPool(XAssetType type, asset_callback_t assetCallback_f, asset_callback_t assetOverrideCallback_f, const char* zone = NULL); +int DB_EnumAssetPoolEx(XAssetType type, asset_callback_ex_t assetCallback_f, asset_callback_ex_t assetOverrideCallback_f, void* data, const char* zone = NULL); + +void* DB_FindSingletonAssetForType(XAssetType type); + +// +// List all assets of a given type in the asset pool +// the zone argument can be used to filter by a specific zone name +// +void DB_ListAssetPool(XAssetType type, const char* zone = NULL); +void DB_WaitForMapToLoad(void); diff --git a/components/asset_util/cmds/ripper/dvar.h b/components/asset_util/cmds/ripper/dvar.h new file mode 100644 index 00000000..957d1ba3 --- /dev/null +++ b/components/asset_util/cmds/ripper/dvar.h @@ -0,0 +1,89 @@ +#pragma once + +enum dvarType_t +{ + DVAR_TYPE_BOOL = 0x0, + DVAR_TYPE_FLOAT = 0x1, + DVAR_TYPE_FLOAT_2 = 0x2, + DVAR_TYPE_FLOAT_3 = 0x3, + DVAR_TYPE_FLOAT_4 = 0x4, + DVAR_TYPE_INT = 0x5, + DVAR_TYPE_ENUM = 0x6, + DVAR_TYPE_STRING = 0x7, + DVAR_TYPE_COLOR = 0x8, + DVAR_TYPE_INT64 = 0x9, + DVAR_TYPE_LINEAR_COLOR_RGB = 0xA, + DVAR_TYPE_COLOR_XYZ = 0xB, + DVAR_TYPE_COUNT = 0xC, +}; + +enum DvarSetSource +{ + DVAR_SOURCE_INTERNAL = 0x0, + DVAR_SOURCE_EXTERNAL = 0x1, + DVAR_SOURCE_SCRIPT = 0x2, + DVAR_SOURCE_DEVGUI = 0x3, +}; + +union DvarValue +{ + bool enabled; + int integer; + unsigned int unsignedInt; + __int64 integer64; + unsigned __int64 unsignedInt64; + float value; + float vector[4]; + const char *string; + char color[4]; +}; + +union DvarLimits +{ + struct + { + int stringCount; + const char **strings; + } enumeration; + + struct + { + int min; + int max; + } integer; + + struct + { + __int64 min; + __int64 max; + } integer64; + + struct + { + float min; + float max; + } value; + + struct + { + float min; + float max; + } vector; +}; + +struct dvar_s +{ + const char *name; + const char *description; + int hash; + unsigned int flags; + dvarType_t type; + bool modified; + bool loadedFromSaveGame; + DvarValue current; + DvarValue latched; + DvarValue reset; + DvarValue saved; + DvarLimits domain; + dvar_s *hashNext; +}; \ No newline at end of file diff --git a/components/asset_util/cmds/ripper/foreign_ptr.h b/components/asset_util/cmds/ripper/foreign_ptr.h new file mode 100644 index 00000000..87cc99db --- /dev/null +++ b/components/asset_util/cmds/ripper/foreign_ptr.h @@ -0,0 +1,52 @@ +#pragma once +#include +#include + +template +class ForeignPointer +{ +private: + T content; +public: + T* pAddress; + + bool operator==(void* address) {return pAddress == address}; + + T operator*(void) const; + T operator[](int index) const; + T* operator->(void) const; + + ForeignPointer(void) {}; + ForeignPointer(T* address) : pAddress(address) {}; + //ForeignPointer(ForeignPointer& arg) : pAddress(arg.pAddress) {}; + //ForeignPointer(void* address) : pAddress(address) {}; + ~ForeignPointer(void) {}; +}; + +#include "process_info.h" + +template +T ForeignPointer::operator*(void) const +{ + T out; + SIZE_T numofbytesread = 0; + ReadProcessMemory(g_process.handle, pAddress, &out, sizeof(T), &numofbytesread); + return out; +}; + +template +T ForeignPointer::operator[](int index) const +{ + T out; + SIZE_T numofbytesread = 0; + ReadProcessMemory(g_process.handle, pAddress + index, &out, sizeof(T), &numofbytesread); + return out; +}; + +template +T* ForeignPointer::operator->(void) const +{ + SIZE_T numofbytesread; + ReadProcessMemory(g_process.handle, (void*)pAddress, (void*)&content, sizeof(T), &numofbytesread); + return (T*)&content; +} diff --git a/components/asset_util/cmds/ripper/process_info.cpp b/components/asset_util/cmds/ripper/process_info.cpp new file mode 100644 index 00000000..3a0c207f --- /dev/null +++ b/components/asset_util/cmds/ripper/process_info.cpp @@ -0,0 +1,42 @@ +#pragma once +#include "process_info.h" + +ProcessInfo g_process = { 0 }; + +int ProcessInfo_Init(processId_t pid) +{ + g_process.handle = OpenProcess(PROCESS_VM_READ, FALSE, pid); + g_process.pid = pid; + + switch (PROCESS_TYPE type = Process_GetProcessType(pid)) + { + case PROCESS_BLACK_OPS: + g_process.db_hashTable = ForeignPointer((unsigned __int16*)0x00CD81F8); + g_process.db_assetEntryPool = ForeignPointer((XAssetEntryPoolEntry*)0x00DE85D8); + g_process.db_zoneNames = ForeignPointer((XZoneName*)0x010C6608); + g_process.cl_ingame = ForeignPointer((dvar_s**)0x02910158); + break; + case PROCESS_BLACK_OPS_MP: + g_process.db_hashTable = ForeignPointer((unsigned __int16*)0x24365D8); + g_process.db_assetEntryPool = ForeignPointer((XAssetEntryPoolEntry*)0x252FC18); + g_process.db_zoneNames = ForeignPointer((XZoneName*)0x028C9B08); + g_process.cl_ingame = ForeignPointer((dvar_s**)0x00E67B30); + break; + default: + ProcessInfo_Free(); + return -1; + } + + return 0; +} + +void ProcessInfo_Free(void) +{ + CloseHandle(g_process.handle); + g_process.handle = NULL; + g_process.pid = NULL; + + g_process.db_hashTable = nullptr; + g_process.db_zoneNames = nullptr; + g_process.db_assetEntryPool = nullptr; +} diff --git a/components/asset_util/cmds/ripper/process_info.h b/components/asset_util/cmds/ripper/process_info.h new file mode 100644 index 00000000..879d08ea --- /dev/null +++ b/components/asset_util/cmds/ripper/process_info.h @@ -0,0 +1,27 @@ +/* + This header file contains the structure offsets struct, system initializers / destructors, and other process info that is relevent to only the ripper command and not asset_util as a whole +*/ + +#pragma once +#include "../../sys/process.h" +#include "db_registry.h" +#include "foreign_ptr.h" + +#include "dvar.h" + +struct ProcessInfo +{ + HANDLE handle; + processId_t pid; + + ForeignPointer db_hashTable; + ForeignPointer db_assetEntryPool; + ForeignPointer db_zoneNames; + + ForeignPointer cl_ingame; +}; + +extern ProcessInfo g_process; + +int ProcessInfo_Init(processId_t pid); +void ProcessInfo_Free(void); diff --git a/components/asset_util/cmds/ripper/snd_alias_db.h b/components/asset_util/cmds/ripper/snd_alias_db.h new file mode 100644 index 00000000..d14cb8b2 --- /dev/null +++ b/components/asset_util/cmds/ripper/snd_alias_db.h @@ -0,0 +1,190 @@ +#pragma once + +enum snd_asset_format +{ + SND_ASSET_FORMAT_PCMS16 = 0x0, + SND_ASSET_FORMAT_PCMS24 = 0x1, + SND_ASSET_FORMAT_PCMS32 = 0x2, + SND_ASSET_FORMAT_IEEE = 0x3, + SND_ASSET_FORMAT_XMA4 = 0x4, + SND_ASSET_FORMAT_MP3 = 0x5, + SND_ASSET_FORMAT_MSADPCM = 0x6, + SND_ASSET_FORMAT_WMA = 0x7, +}; + +enum snd_asset_channel +{ + SND_ASSET_CHANNEL_L = 0x1, + SND_ASSET_CHANNEL_R = 0x2, + SND_ASSET_CHANNEL_C = 0x4, + SND_ASSET_CHANNEL_LFE = 0x8, + SND_ASSET_CHANNEL_LS = 0x10, + SND_ASSET_CHANNEL_RS = 0x20, + SND_ASSET_CHANNEL_LB = 0x40, + SND_ASSET_CHANNEL_RB = 0x80, +}; + +enum snd_asset_flags +{ + SND_ASSET_FLAG_DEFAULT = 0x0, + SND_ASSET_FLAG_LOOPING = 0x1, + SND_ASSET_FLAG_PAD_LOOP_BUFFER = 0x2, +}; + +struct snd_radverb +{ + char name[32]; + unsigned int id; + float smoothing; + float earlyTime; + float lateTime; + float earlyGain; + float lateGain; + float returnGain; + float earlyLpf; + float lateLpf; + float inputLpf; + float dampLpf; + float wallReflect; + float dryGain; + float earlySize; + float lateSize; + float diffusion; +}; + +struct snd_snapshot +{ + char name[32]; + unsigned int id; + char occlusionName[32]; + unsigned int occlusionId; + float fadeIn; + float fadeOut; + float distance; + unsigned int fadeInCurve; + unsigned int fadeOutCurve; + float attenuation[64]; +}; + +struct snd_asset +{ + unsigned int version; + unsigned int frame_count; + unsigned int frame_rate; + unsigned int channel_count; + unsigned int header_size; + unsigned int block_size; + unsigned int buffer_size; + snd_asset_format format; + snd_asset_channel channel_flags; + snd_asset_flags flags; + unsigned int seek_table_count; + unsigned int *seek_table; + unsigned int data_size; + char *data; +}; + +struct snd_alias_t +{ + const char *name; + unsigned int id; + const char *subtitle; + const char *secondaryname; + struct SoundFile *soundFile; + unsigned int flags; + unsigned int duck; + unsigned int contextType; + unsigned int contextValue; + unsigned __int16 fluxTime; + unsigned __int16 startDelay; + unsigned __int16 reverbSend; + unsigned __int16 centerSend; + unsigned __int16 volMin; + unsigned __int16 volMax; + unsigned __int16 teamVolMod; + unsigned __int16 pitchMin; + unsigned __int16 pitchMax; + unsigned __int16 teamPitchMod; + unsigned __int16 distMin; + unsigned __int16 distMax; + unsigned __int16 distReverbMax; + unsigned __int16 envelopMin; + unsigned __int16 envelopMax; + unsigned __int16 envelopPercentage; + char minPriorityThreshold; + char maxPriorityThreshold; + char probability; + char occlusionLevel; + char occlusionWetDry; + char minPriority; + char maxPriority; + char pan; + char dryCurve; + char wetCurve; + char dryMinCurve; + char wetMinCurve; + char limitCount; + char entityLimitCount; + char snapshotGroup; +}; + +struct snd_alias_list_t +{ + const char *name; + unsigned int id; + snd_alias_t *head; + int count; + int sequence; +}; + +struct LoadedSound +{ + const char *name; + snd_asset sound; +}; + +struct PrimedSound +{ + const char *name; + char *buffer; + unsigned int size; +}; + +struct StreamedSound +{ + char *filename; + PrimedSound *primeSnd; +}; + +union SoundFileRef +{ + LoadedSound *loadSnd; + StreamedSound *streamSnd; +}; + +struct SoundFile +{ + SoundFileRef u; + char type; + char exists; +}; + +struct SndIndexEntry +{ + unsigned __int16 value; + unsigned __int16 next; +}; + +struct SndBank +{ + const char *name; + unsigned int aliasCount; + snd_alias_list_t *alias; + SndIndexEntry *aliasIndex; + unsigned int packHash; + unsigned int packLocation; + unsigned int radverbCount; + snd_radverb *radverbs; + unsigned int snapshotCount; + snd_snapshot *snapshots; +}; diff --git a/components/asset_util/cmds/ripper/snd_csv_enum.cpp b/components/asset_util/cmds/ripper/snd_csv_enum.cpp new file mode 100644 index 00000000..e7f1a2f1 --- /dev/null +++ b/components/asset_util/cmds/ripper/snd_csv_enum.cpp @@ -0,0 +1,58 @@ +#include "snd_csv_enum.h" +#include "snd_alias_db.h" +#include "foreign_ptr.h" + +// +// Encode an enum's in a larger bitflags variable +// size is the number of bits to use for the input value +// shift is the number of bits to shift the masked input value +// +// For encoding enums from a snd_csv_entry_t +// 'size' is (int)entry->minimum +// 'shift' is (int)entry->maximum +// +void Snd_CSV_EncodeEnumBits(unsigned int* bits, unsigned int value, int size, int shift) +{ +#if 0 + * bits = ((value & ((1 << size) - 1)) << shift) | ((*bits) & ~(((1 << size) - 1) << shift)); + return; +#endif + + unsigned int mask = (1 << size) - 1; + unsigned int encoded_value = (value & mask) << shift; + + *bits = ((*bits) & ~(mask)) | encoded_value; +} + +// +// Extract an encoded enum's value from a larger bitflags variable +// size is the number of bits to use for the input value +// shift is the number of bits to shift the masked input value +// +unsigned int Snd_CSV_DecodeEnumBits(unsigned int bits, int size, int shift) +{ + unsigned int mask = (1 << size) - 1; + return (bits >> shift) & mask; +} + +const char* SND_CSV_ResolveEnumBitsString(ForeignPointer& alias, snd_csv_enum_bits_entry_t& enum_bits_entry) +{ + _ASSERT(enum_bits_entry.enum_stringtable); + + unsigned int bits = alias->flags; + unsigned int enum_val = Snd_CSV_DecodeEnumBits(bits, enum_bits_entry.size, enum_bits_entry.shift); + + unsigned int enum_stringCount = 0; + for (; enum_bits_entry.enum_stringtable[enum_stringCount]; enum_stringCount++); + + if (enum_val > enum_stringCount) + return NULL; + + return enum_bits_entry.enum_stringtable[enum_val]; +} + +unsigned int SND_CSV_ResolveEnumBitsValue(ForeignPointer& alias, snd_csv_enum_bits_entry_t& enum_bits_entry) +{ + unsigned int bits = alias->flags; + return Snd_CSV_DecodeEnumBits(bits, enum_bits_entry.size, enum_bits_entry.shift); +} diff --git a/components/asset_util/cmds/ripper/snd_csv_enum.h b/components/asset_util/cmds/ripper/snd_csv_enum.h new file mode 100644 index 00000000..08d6cd29 --- /dev/null +++ b/components/asset_util/cmds/ripper/snd_csv_enum.h @@ -0,0 +1,147 @@ +#pragma once +#include +#include "foreign_ptr.h" + +static const char* fields_soundalias[] = +{ + "name", + "file", + "template", + "loadspec", + "secondary", + "group", + "vol_min", + "vol_max", + "team_vol_mod", + "dist_min", + "dist_max", + "dist_reverb_max", + "volume_falloff_curve", + "reverb_falloff_curve", + "volume_min_falloff_curve", + "reverb_min_falloff_curve", + "limit_count", + "limit_type", + "entity_limit_count", + "entity_limit_type", + "pitch_min", + "pitch_max", + "team_pitch_mod", + "min_priority", + "max_priority", + "min_priority_threshold", + "max_priority_threshold", + "spatialized", + "type", + "loop", + "randomize_type", + "probability", + "start_delay", + "reverb_send", + "duck", + "pan", + "center_send", + "envelop_min", + "envelop_max", + "envelop_percentage", + "occlusion_level", + "occlusion_wet_dry", + "is_big", + "distance_lpf", + "move_type", + "move_time", + "real_delay", + "subtitle", + "mature", + "doppler", + "futz", + "context_type", + "context_value", + "compression", + "timescale", + "music", + "fade_in", + "fade_out", + "pc_format", + "pause", + "stop_on_death", + "bus", + "snapshot", + "voice_limit", + "file_xenon", + "file_size_xenon", + "file_ps3", + "file_size_ps3", + "file_pc", + "file_size_pc", + "file_wii", + "file_size_wii", + "source_csv", + "language", +}; + +static const char* fields_snapshot[] = +{ + "name", + "occlusion", + "loadspec", + "fadeIn", + "fadeInCurve", + "fadeOut", + "fadeOutCurve", + "distance", + // ... (Everything after this point is auto generated from snapshotGroups) +}; + +static const char* fields_radverb[] = +{ + "name", + "loadspec", + "smoothing", + "earlyTime", + "lateTime", + "earlyGain", + "lateGain", + "returnGain", + "earlyLpf", + "lateLpf", + "inputLpf", + "dampLpf", + "wallReflect", + "dryGain", + "earlySize", + "lateSize", + "diffusion", +}; + +static const char *enum_bus[3] = { "world", "game", "voice" }; +static const char *enum_type[5] = { "unknown", "loaded", "streamed", "primed", NULL }; +static const char *enum_priority[5] = { "none", "oldest", "reject", "priority", NULL }; + +static const char *enum_move_type[9] = +{ + "none", + "left_player", + "center_player", + "right_player", + "random_player", + "left_shot", + "center_shot", + "right_shot", + NULL +}; + +static const char *enum_randomize_type[4] = { "volume", "pitch", "variant", NULL }; +static const char *enum_spatialized[4] = { "2d", "3d", "2.5d", NULL }; +static const char *enum_looping[3] = { "nonlooping", "looping", NULL }; +static const char *enum_yes_no[3] = { "no", "yes", NULL }; + +struct snd_csv_enum_bits_entry_t +{ + int size; + int shift; + const char** enum_stringtable; +}; + +const char* SND_CSV_ResolveEnumBitsString(ForeignPointer& alias, snd_csv_enum_bits_entry_t& enum_bits_entry); +unsigned int SND_CSV_ResolveEnumBitsValue(ForeignPointer& alias, snd_csv_enum_bits_entry_t& enum_bits_entry); diff --git a/components/asset_util/cmds/ripper/snd_driver_db.h b/components/asset_util/cmds/ripper/snd_driver_db.h new file mode 100644 index 00000000..f6b6b5f6 --- /dev/null +++ b/components/asset_util/cmds/ripper/snd_driver_db.h @@ -0,0 +1,110 @@ +#pragma once + +enum snd_category_t +{ + SND_CATEGORY_SFX = 0x0, + SND_CATEGORY_MUSIC = 0x1, + SND_CATEGORY_VOICE = 0x2, + SND_CATEGORY_UI = 0x3, + SND_CATEGORY_COUNT = 0x4, +}; + +struct snd_group +{ + char name[32]; + char parentName[32]; + unsigned int id; + int parentIndex; + snd_category_t category; + unsigned __int16 attenuationSp; + unsigned __int16 attenuationMp; +}; + +struct snd_curve +{ + char name[32]; + unsigned int id; + float points[8][2]; +}; + +struct snd_pan +{ + char name[32]; + unsigned int id; + float front; + float back; + float center; + float lfe; + float left; + float right; +}; + +struct snd_snapshot_group +{ + char name[32]; +}; + +struct snd_context +{ + unsigned int type; + unsigned int valueCount; + unsigned int values[8]; +}; + +struct snd_master +{ + char name[32]; + unsigned int id; + float notchE; + float notchG; + float notchF; + float notchQ; + float lowE; + float lowG; + float lowF; + float lowQ; + float peak1E; + float peak1G; + float peak1F; + float peak1Q; + float peak2E; + float peak2G; + float peak2F; + float peak2Q; + float hiE; + float hiG; + float hiF; + float hiQ; + float eqG; + float compE; + float compPG; + float compMG; + float compT; + float compR; + float compTA; + float compTR; + float limitE; + float limitPG; + float limitMG; + float limitT; + float limitR; + float limitTA; + float limitTR; +}; + +struct SndDriverGlobals +{ + const char *name; + unsigned int groupCount; + snd_group *groups; + unsigned int curveCount; + snd_curve *curves; + unsigned int panCount; + snd_pan *pans; + unsigned int snapshotGroupCount; + snd_snapshot_group *snapshotGroups; + unsigned int contextCount; + snd_context *contexts; + unsigned int masterCount; + snd_master *masters; +}; diff --git a/components/asset_util/cmds/ripper/snd_ripper.cpp b/components/asset_util/cmds/ripper/snd_ripper.cpp new file mode 100644 index 00000000..7319f5ad --- /dev/null +++ b/components/asset_util/cmds/ripper/snd_ripper.cpp @@ -0,0 +1,621 @@ +#include "snd_ripper.h" +#include "../../common/fs.h" +#include "../../common/io.h" + +#include "../../sys/AppInfo.h" +#include "../../cvar.h" + +static snd_csv_enum_bits_entry_t ee_limit_type = { 2, 25, enum_priority }; +static snd_csv_enum_bits_entry_t ee_entity_limit_type = { 2, 27, enum_priority }; +static snd_csv_enum_bits_entry_t ee_randomize_type = { 3, 29, enum_randomize_type }; +static snd_csv_enum_bits_entry_t ee_move_type = { 3, 22, enum_move_type }; +static snd_csv_enum_bits_entry_t ee_type = { 2, 14, enum_type }; +static snd_csv_enum_bits_entry_t ee_group = { 6, 16, NULL }; +static snd_csv_enum_bits_entry_t ee_real_delay = { 1, 2, enum_yes_no }; +static snd_csv_enum_bits_entry_t ee_distance_lpf = { 1, 3, enum_yes_no }; +static snd_csv_enum_bits_entry_t ee_doppler = { 1, 4, enum_yes_no }; +static snd_csv_enum_bits_entry_t ee_is_big = { 1, 5, enum_yes_no }; +static snd_csv_enum_bits_entry_t ee_loop = { 1, 0, enum_looping }; +static snd_csv_enum_bits_entry_t ee_spatialized = { 1, 1, enum_spatialized }; +static snd_csv_enum_bits_entry_t ee_futz = { 1, 6, enum_yes_no }; +static snd_csv_enum_bits_entry_t ee_music = { 1, 8, enum_yes_no }; +static snd_csv_enum_bits_entry_t ee_timescale = { 1, 10, enum_yes_no }; +static snd_csv_enum_bits_entry_t ee_pause = { 1, 7, enum_yes_no }; +static snd_csv_enum_bits_entry_t ee_stop_on_death = { 1, 9, enum_yes_no }; +static snd_csv_enum_bits_entry_t ee_bus = { 2, 12, enum_bus }; +static snd_csv_enum_bits_entry_t ee_voice_limit = { 1, 11, enum_yes_no }; + +struct snd_csv_context_entry_t +{ + const char* type; + const char* value[8]; +}; + +static snd_csv_context_entry_t contexts[] = +{ + {"ringoff_plr", {"indoor", "outdoor", NULL, NULL, NULL, NULL, NULL, NULL} }, + {"mature", {"explicit", "safe", NULL, NULL, NULL, NULL, NULL, NULL} }, + {"test", {"high", "low", NULL, NULL, NULL, NULL, NULL, NULL} }, + {"hazmat", {"mask", NULL, NULL, NULL, NULL, NULL, NULL, NULL} }, +}; + +// +// Read an external string with max length (len) +// if len == 0 - the string can be any length +// +std::string ReadString(const char* ptr, int len = 0) +{ + std::string out = "\0"; + SIZE_T numofbytesread; + + for (int i = 0; len == 0 || i < len; i++) + { + char c = 0; + ReadProcessMemory(g_process.handle, (void*)ptr, (void*)&c, sizeof(c), &numofbytesread); + + if (c == '\0' || numofbytesread == 0) + return out; + + out += c; + ptr++; + } + + return out; +} + +double __cdecl SND_CSV_CENTS_Encode(double val) +{ + double v = val / 1200.0; + return pow(2.0, v); +} + +double SND_CSV_CENTS_Decode(double val) +{ + double v = log(val) / log(2); + return v * 1200; +} + +unsigned int __cdecl SND_HashName(const char *name) +{ + int hash = 0; + + if (name && *name) + { + unsigned int len = strlen(name); + hash = 5381; + for (unsigned i = 0; i < len; ++i) + hash = (hash << 16) + (hash << 6) + tolower(name[i]) - hash; + if (!hash) + hash = 1; + } + + return hash; +} + +void Snd_CSV_PrintHeader_Soundalias(FILE* f) +{ + for (int i = 0; i < ARRAYSIZE(fields_soundalias); i++) + { + fprintf(f, "%s,", fields_soundalias[i]); + } + fprintf(f, "\n"); +} + +void Snd_CSV_PrintHeader_Snapshot(FILE* f, ForeignPointer& globals) +{ + for (int i = 0; i < ARRAYSIZE(fields_snapshot); i++) + { + fprintf(f, "%s,", fields_snapshot[i]); + } + + ForeignPointer snapshotGroups(globals->snapshotGroups); + for (unsigned int i = 0; i < globals->snapshotGroupCount; i++) + { + fprintf(f, "%s,", snapshotGroups[i].name); + } + + fprintf(f, "\n"); +} + +void Snd_CSV_PrintHeader_Radverb(FILE* f) +{ + for (int i = 0; i < ARRAYSIZE(fields_radverb); i++) + { + fprintf(f, "%s,", fields_radverb[i]); + } + fprintf(f, "\n"); +} + +int Rip_Sound_Alias_Callback_f(ForeignPointer& alias, snd_ripper_instance_info_t instance) +{ + std::string name = ReadString(alias->name, 256); + std::string file = ""; + + const char* _template = ""; //template + const char* loadspec = ""; // unknown + std::string secondaryname = ReadString(alias->secondaryname, 256); //secondary + + unsigned int group_index = SND_CSV_ResolveEnumBitsValue(alias, ee_group); + _ASSERT(group_index < instance.globals->groupCount); + + ForeignPointer group((snd_group*)instance.globals->groups + group_index); + + unsigned short vol_min = alias->volMin; + unsigned short vol_max = alias->volMax; + unsigned short team_vol_mod = alias->teamVolMod; + unsigned short dist_min = alias->distMin; + unsigned short dist_max = alias->distMax; + unsigned short dist_reverb_max = alias->distReverbMax; + + _ASSERT(alias->dryCurve < (int)instance.globals->curveCount); + _ASSERT(alias->wetCurve < (int)instance.globals->curveCount); + _ASSERT(alias->dryMinCurve < (int)instance.globals->curveCount); + _ASSERT(alias->wetMinCurve < (int)instance.globals->curveCount); + + ForeignPointer curves((snd_curve*)instance.globals->curves); + + const char* volume_falloff_curve = curves[alias->dryCurve].name; + const char* reverb_falloff_curve = curves[alias->wetCurve].name; + const char* volume_min_falloff_curve = curves[alias->dryMinCurve].name; + const char* reverb_min_falloff_curve = curves[alias->wetMinCurve].name; + + char limit_count = alias->limitCount; + const char* limit_type = SND_CSV_ResolveEnumBitsString(alias, ee_limit_type); + char entity_limit_count = alias->entityLimitCount; + const char* entity_limit_type = SND_CSV_ResolveEnumBitsString(alias, ee_entity_limit_type); + unsigned short pitch_min = alias->pitchMin; + unsigned short pitch_max = alias->pitchMax; + unsigned short team_pitch_mod = alias->teamPitchMod; + char min_priority = alias->minPriority; + char max_priority = alias->maxPriority; + unsigned char min_priority_threshold = alias->minPriorityThreshold; + unsigned char max_priority_threshold = alias->maxPriorityThreshold; + const char* spatialized = SND_CSV_ResolveEnumBitsString(alias, ee_spatialized); + const char* type = SND_CSV_ResolveEnumBitsString(alias, ee_type); + const char* loop = SND_CSV_ResolveEnumBitsString(alias, ee_loop); + const char* randomize_type = SND_CSV_ResolveEnumBitsString(alias, ee_randomize_type); + unsigned char probability = alias->probability; + unsigned short start_delay = alias->startDelay; + unsigned short reverb_send = alias->reverbSend; + + float fade_in = 0; + float fade_out = 0; + + unsigned int duck_hash = alias->duck; + std::string duck = (duck_hash) ? "" : ""; + + ForeignPointer snapshots((snd_snapshot*)instance.bank->snapshots); + if (duck_hash != 0) + { + for (auto& set : *instance.snapshots_map) + { + bool done = false; + + for (auto& snapshot : set.second) + { + if (duck_hash == SND_HashName(snapshot->name)) + { + duck = snapshot->name; + fade_in = snapshot->fadeIn; + fade_out = snapshot->fadeOut; + done = true; + break; + } + } + + if (done) + break; + } + } + + _ASSERT(duck != ""); + + unsigned char pan_index = alias->pan; + _ASSERT(pan_index < instance.globals->panCount); + + ForeignPointer pan = ((snd_pan*)instance.globals->pans + pan_index); + + unsigned short center_send = alias->centerSend; + unsigned short envelop_min = alias->envelopMin; + unsigned short envelop_max = alias->envelopMax; + unsigned short envelop_percentage = alias->envelopPercentage; + unsigned char occlusion_level = alias->occlusionLevel; + unsigned char occlusion_wet_dry = alias->occlusionWetDry; + const char* is_big = SND_CSV_ResolveEnumBitsString(alias, ee_is_big); + const char* distance_lpf = SND_CSV_ResolveEnumBitsString(alias, ee_distance_lpf); + const char* move_type = SND_CSV_ResolveEnumBitsString(alias, ee_move_type); + unsigned short move_time = alias->fluxTime; + const char* real_delay = SND_CSV_ResolveEnumBitsString(alias, ee_real_delay); + std::string subtitle = ReadString(alias->subtitle, 1024); + const char* mature = "both"; //mature + const char* doppler = SND_CSV_ResolveEnumBitsString(alias, ee_doppler); + const char* futz = SND_CSV_ResolveEnumBitsString(alias, ee_futz); + + unsigned int contextType_hash = alias->contextType; // context_type + snd_csv_context_entry_t* context = NULL; + + for (int i = 0; i < ARRAYSIZE(contexts); i++) + { + if (contextType_hash == SND_HashName(contexts[i].type)) + { + context = &contexts[i]; + break; + } + } + + _ASSERT(!contextType_hash || (contextType_hash && context)); + + unsigned int contextValue_hash = alias->contextValue; //context_value + const char* context_value = NULL; + + if (context != NULL) + { + for (int i = 0; i < 8; i++) + { + if (context->value[i] == NULL) + break; + + if (contextValue_hash = SND_HashName(context->value[i])) + { + context_value = (*context).value[i]; + break; + } + } + + _ASSERT(context_value); + } + + unsigned short compression = 0; //compression + + const char* timescale = SND_CSV_ResolveEnumBitsString(alias, ee_timescale); + const char* music = SND_CSV_ResolveEnumBitsString(alias, ee_music); + + const char* pc_format = "xwma"; //pc_format - (This appears to be xwma in every existing alias - and theres no way to get it for non-loaded files) + + const char* pause = SND_CSV_ResolveEnumBitsString(alias, ee_pause); + const char* stop_on_death = SND_CSV_ResolveEnumBitsString(alias, ee_stop_on_death); + const char* bus = SND_CSV_ResolveEnumBitsString(alias, ee_bus); + + unsigned char snapshotGroup_index = alias->snapshotGroup; + _ASSERT(snapshotGroup_index < instance.globals->snapshotGroupCount); + + ForeignPointer snapshotgroup = ((snd_snapshot_group*)instance.globals->snapshotGroups + snapshotGroup_index); + std::string snapshot = snapshotgroup->name; + + const char* voice_limit = SND_CSV_ResolveEnumBitsString(alias, ee_voice_limit); + const char* file_xenon = ""; + int file_size_xenon = 0; + const char* file_ps3 = ""; + int file_size_ps3 = 0; + const char* file_pc = ""; + int file_size_pc = 0; + const char* file_wii = ""; + int file_size_wii = 0; + const char* source_csv = ""; + + ForeignPointer soundFile((SoundFile*)alias->soundFile); + if (strcmp(type, "loaded") == 0) + { + ForeignPointer snd((LoadedSound*)soundFile->u.loadSnd); + file = ReadString(snd->name); + } + else if (strcmp(type, "streamed") == 0) + { + ForeignPointer snd((StreamedSound*)soundFile->u.streamSnd); + file = ReadString(snd->filename); + } + else if (strcmp(type, "primed") == 0) + { + ForeignPointer streamed_snd((StreamedSound*)soundFile->u.streamSnd); + ForeignPointer primed_snd((PrimedSound*)streamed_snd->primeSnd); + file = ReadString(primed_snd->name); + } + else // Try to use loaded since the name is always first + { + ForeignPointer snd((LoadedSound*)soundFile->u.loadSnd); + file = ReadString(snd->name); + } + + fprintf(instance.outputFile, "%s,", name.c_str()); + fprintf(instance.outputFile, "%s,", file.c_str()); + fprintf(instance.outputFile, "%s,", _template); + fprintf(instance.outputFile, "%s,", loadspec); + fprintf(instance.outputFile, "%s,", secondaryname.c_str()); + fprintf(instance.outputFile, "%s,", group->name); + fprintf(instance.outputFile, "%.3g,", 100.0 * (double)vol_min / (double)USHRT_MAX); + fprintf(instance.outputFile, "%.3g,", 100.0 * (double)vol_max / (double)USHRT_MAX); + fprintf(instance.outputFile, "%.3g,", 100.0 * (double)team_vol_mod / (double)USHRT_MAX); + fprintf(instance.outputFile, "%d,", dist_min); + fprintf(instance.outputFile, "%d,", dist_max); + fprintf(instance.outputFile, "%d,", dist_reverb_max); + fprintf(instance.outputFile, "%s,", volume_falloff_curve); + fprintf(instance.outputFile, "%s,", reverb_falloff_curve); + fprintf(instance.outputFile, "%s,", volume_min_falloff_curve); + fprintf(instance.outputFile, "%s,", reverb_min_falloff_curve); + fprintf(instance.outputFile, "%d,", limit_count); + fprintf(instance.outputFile, "%s,", limit_type); + fprintf(instance.outputFile, "%d,", entity_limit_count); + fprintf(instance.outputFile, "%s,", entity_limit_type); + fprintf(instance.outputFile, "%.3g,", SND_CSV_CENTS_Decode((double)pitch_min / (double)SHRT_MAX)); + fprintf(instance.outputFile, "%.3g,", SND_CSV_CENTS_Decode((double)pitch_max / (double)SHRT_MAX)); + fprintf(instance.outputFile, "%.3g,", SND_CSV_CENTS_Decode((double)team_pitch_mod / (double)SHRT_MAX)); + fprintf(instance.outputFile, "%d,", min_priority); + fprintf(instance.outputFile, "%d,", max_priority); + fprintf(instance.outputFile, "%.3g,", (double)min_priority_threshold / (double)UCHAR_MAX); + fprintf(instance.outputFile, "%.3g,", (double)max_priority_threshold / (double)UCHAR_MAX); + fprintf(instance.outputFile, "%s,", spatialized); + fprintf(instance.outputFile, "%s,", type); + fprintf(instance.outputFile, "%s,", loop); + fprintf(instance.outputFile, "%s,", randomize_type); + fprintf(instance.outputFile, "%.3g,", (double)probability / (double)UCHAR_MAX); + fprintf(instance.outputFile, "%d,", start_delay); + fprintf(instance.outputFile, "%.3g,", 100.0 * (double)reverb_send / (double)USHRT_MAX); + fprintf(instance.outputFile, "%s,", duck.c_str()); + fprintf(instance.outputFile, "%s,", pan->name); + fprintf(instance.outputFile, "%.3g,", 100.0 * (double)center_send / (double)USHRT_MAX); + fprintf(instance.outputFile, "%d,", envelop_min); + fprintf(instance.outputFile, "%d,", envelop_max); + fprintf(instance.outputFile, "%.3g,", 100 * (double)envelop_percentage / (double)USHRT_MAX); + fprintf(instance.outputFile, "%.3g,", (double)occlusion_level / (double)UCHAR_MAX); + fprintf(instance.outputFile, "%.3g,", (double)occlusion_wet_dry / (double)UCHAR_MAX); + fprintf(instance.outputFile, "%s,", is_big); + fprintf(instance.outputFile, "%s,", distance_lpf); + fprintf(instance.outputFile, "%s,", move_type); + fprintf(instance.outputFile, "%d,", move_time); + fprintf(instance.outputFile, "%s,", real_delay); + fprintf(instance.outputFile, "%s,", subtitle.c_str()); + fprintf(instance.outputFile, "%s,", mature); + fprintf(instance.outputFile, "%s,", doppler); + fprintf(instance.outputFile, "%s,", futz); + fprintf(instance.outputFile, "%s,", context ? context->type : ""); // , context_type); + fprintf(instance.outputFile, "%s,", context_value ? context_value : ""); // , context_value); + fprintf(instance.outputFile, "%d,", compression); + fprintf(instance.outputFile, "%s,", timescale); + fprintf(instance.outputFile, "%s,", music); + fprintf(instance.outputFile, "%f,", fade_in); + fprintf(instance.outputFile, "%f,", fade_out); + fprintf(instance.outputFile, "%s,", pc_format); + fprintf(instance.outputFile, "%s,", pause); + fprintf(instance.outputFile, "%s,", stop_on_death); + fprintf(instance.outputFile, "%s,", bus); + fprintf(instance.outputFile, "%s,", snapshot.c_str()); + fprintf(instance.outputFile, "%s,", voice_limit); + fprintf(instance.outputFile, "%s,", file_xenon); + fprintf(instance.outputFile, "%d,", file_size_xenon); + fprintf(instance.outputFile, "%s,", file_ps3); + fprintf(instance.outputFile, "%d,", file_size_ps3); + fprintf(instance.outputFile, "%s,", file_pc); + fprintf(instance.outputFile, "%d,", file_size_pc); + fprintf(instance.outputFile, "%s,", file_wii); + fprintf(instance.outputFile, "%d,", file_size_wii); + fprintf(instance.outputFile, "%s,", source_csv); + fprintf(instance.outputFile, "%s,\n", instance.language.c_str()); + + return 0; +} + +std::string Snd_ResolveBaseName(std::string& str) +{ + auto offset = str.find("."); + + if (offset != std::string::npos) + { + return std::string(str, 0, offset); + } + + return str; +} + +int Rip_SoundBank_GatherSnapshots_Callback_f(ForeignPointer& asset, ForeignPointer& zoneName, void* data) +{ + _ASSERT(data); + + snapshots_map_t* snapshots_map = (snapshots_map_t*)data; + + ForeignPointer bank((SndBank*)asset->header.sound); + auto& snapshots = (*snapshots_map)[Snd_ResolveBaseName(ReadString(bank->name))]; + + if (bank->snapshots == nullptr || bank->snapshotCount == 0) + return 0; + + for (unsigned int i = 0; i < bank->snapshotCount; i++) + { + snapshots.push_back(ForeignPointer((snd_snapshot*)bank->snapshots + i)); + } + + return 0; +} + +int Rip_SoundBank_GatherRadverbs_Callback_f(ForeignPointer& asset, ForeignPointer& zoneName, void* data) +{ + _ASSERT(data); + + radverbs_map_t* radverbs_map = (radverbs_map_t*)data; + + ForeignPointer bank((SndBank*)asset->header.sound); + auto& radverbs = (*radverbs_map)[Snd_ResolveBaseName(ReadString(bank->name))]; + + if (bank->radverbs == nullptr || bank->radverbCount == 0) + return 0; + + for (unsigned int i = 0; i < bank->radverbCount; i++) + { + radverbs.push_back(ForeignPointer(bank->radverbs + i)); + } + + return 0; +} + +int Rip_SoundBank_Soundalias_Callback_f(ForeignPointer& bank, ForeignPointer& globals, snapshots_map_t* snapshots_map) +{ + std::string name = ReadString(bank->name, 128); + + Con_Print("Exporting soundalias %s.csv...\n", name.c_str()); + + int err = FS_CreatePath("soundaliases\\zones\\"); + if (err) + { + return Con_Error("Error: Unable to create output path (0x%X)\n", err); + } + + char path[MAX_PATH]; + sprintf_s(path, "%s/soundaliases\\zones\\%s.csv", AppInfo_OutDir(), name.c_str()); + + FS_SanitizePath(path); + + if (FS_FileExists(path) && !fs_overwrite.ValueBool()) + { + Con_Print(" ...skipping (file already exists)\n"); + return 1; + } + + FILE* h = fopen(path, "w"); + + if (!h) + { + return Con_Error("Couldnt open '%s'\n", path); + } + + Snd_CSV_PrintHeader_Soundalias(h); + + std::string language = ""; + + auto _offset = name.find_last_of("."); + if (_offset != std::string::npos && _offset + 1 < name.size()) + language = std::string(name, _offset + 1); + else + Con_Warning("Unable to determine language for '%s'\n", name.c_str()); + + snd_ripper_instance_info_t instance = { h, language, globals, bank, snapshots_map }; + + ForeignPointer alias((snd_alias_list_t*)bank->alias); + for (unsigned int i = 0; i < bank->aliasCount; i++) + { + ForeignPointer entry((snd_alias_t*)alias[i].head); + Rip_Sound_Alias_Callback_f(entry, instance); + } + + fclose(h); + return 0; +} + +int Rip_SoundBank_Callback_f(ForeignPointer& asset, ForeignPointer& zoneName, void* data) +{ + ForeignPointer globals((SndDriverGlobals*)DB_FindSingletonAssetForType(ASSET_TYPE_SNDDRIVER_GLOBALS)); + ForeignPointer bank((SndBank*)asset->header.sound); + + return Rip_SoundBank_Soundalias_Callback_f(bank, globals, (snapshots_map_t*)data); +} + + +int Rip_Snapshot_Callback_f(std::string name, std::vector>& snapshots) +{ + Con_Print("Exporting snapshot %s.snapshot.csv...\n", name.c_str()); + + int err = FS_CreatePath("soundaliases\\zones\\snapshots\\"); + if (err) + { + return Con_Error("Error: Unable to create output path (0x%X)\n", err); + } + + char path[MAX_PATH]; + sprintf_s(path, "%s/soundaliases\\zones\\snapshots\\%s.csv", AppInfo_OutDir(), name.c_str()); + + FS_SanitizePath(path); + + if (FS_FileExists(path) && !fs_overwrite.ValueBool()) + { + Con_Print(" ...skipping (file already exists)\n"); + return 1; + } + + FILE* h = fopen(path, "w"); + + if (!h) + { + return Con_Error("Couldnt open '%s'\n", path); + } + + ForeignPointer globals((SndDriverGlobals*)DB_FindSingletonAssetForType(ASSET_TYPE_SNDDRIVER_GLOBALS)); + Snd_CSV_PrintHeader_Snapshot(h, globals); + + for (auto& snapshot : snapshots) + { + fprintf(h, "%s,", snapshot->name); + fprintf(h, "%s,", snapshot->occlusionName); + fprintf(h, ","); // loadspec + fprintf(h, "%.3g,", snapshot->fadeIn); + fprintf(h, ","); // fadeInCurve + fprintf(h, "%.3g,", snapshot->fadeOut); + fprintf(h, ","); // fadeOutCurve + fprintf(h, "%d,", (int)snapshot->distance); + + for (unsigned int i = 0; i < globals->snapshotGroupCount; i++) + { + fprintf(h, "%.3g,", snapshot->attenuation[i]); + } + + fprintf(h, "\n"); + } + + fclose(h); + return 0; +} + +int Rip_Radverb_Callback_f(std::string name, std::vector>& radverbs) +{ + Con_Print("Exporting snapshot %s.radverb.csv...\n", name.c_str()); + + int err = FS_CreatePath("soundaliases\\zones\\radverb\\"); + if (err) + { + return Con_Error("Error: Unable to create output path (0x%X)\n", err); + } + + char path[MAX_PATH]; + sprintf_s(path, "%s/soundaliases\\zones\\radverb\\%s.csv", AppInfo_OutDir(), name.c_str()); + + FS_SanitizePath(path); + + if (FS_FileExists(path) && !fs_overwrite.ValueBool()) + { + Con_Print(" ...skipping (file already exists)\n"); + return 1; + } + + FILE* h = fopen(path, "w"); + + if (!h) + { + return Con_Error("Couldnt open '%s'\n", path); + } + + Snd_CSV_PrintHeader_Radverb(h); + + for (auto& radverb : radverbs) + { + fprintf(h, "%s,", radverb->name); + fprintf(h, ","); //loadspec + fprintf(h, "%.7g,", radverb->smoothing); + fprintf(h, "%.7g,", radverb->earlyTime); + fprintf(h, "%.7g,", radverb->lateTime); + fprintf(h, "%.7g,", radverb->earlyGain); + fprintf(h, "%.7g,", radverb->lateGain); + fprintf(h, "%.7g,", radverb->returnGain); + fprintf(h, "%.7g,", radverb->earlyLpf); + fprintf(h, "%.7g,", radverb->lateLpf); + fprintf(h, "%.7g,", radverb->inputLpf); + fprintf(h, "%.7g,", radverb->dampLpf); + fprintf(h, "%.7g,", radverb->wallReflect); + fprintf(h, "%.7g,", radverb->dryGain); + fprintf(h, "%.7g,", radverb->earlySize); + fprintf(h, "%.7g,", radverb->lateSize); + fprintf(h, "%.7g,", radverb->diffusion); + + fprintf(h, "\n"); + } + + fclose(h); + return 0; +} + diff --git a/components/asset_util/cmds/ripper/snd_ripper.h b/components/asset_util/cmds/ripper/snd_ripper.h new file mode 100644 index 00000000..bdfd910e --- /dev/null +++ b/components/asset_util/cmds/ripper/snd_ripper.h @@ -0,0 +1,40 @@ +#pragma once +#include "process_info.h" +#include "snd_alias_db.h" +#include "snd_csv_enum.h" +#include "snd_driver_db.h" + +#include +#include + +typedef std::unordered_map>> snapshots_map_t; +typedef std::unordered_map>> radverbs_map_t; + +struct snd_ripper_instance_info_t +{ + FILE* outputFile; + std::string& language; + ForeignPointer& globals; + ForeignPointer& bank; + + snapshots_map_t* snapshots_map; +}; + +struct snd_csv_duck_entry_t +{ + unsigned int hash; + const char* string; +}; + +double __cdecl SND_CSV_CENTS_Encode(double val); +double SND_CSV_CENTS_Decode(double val); +unsigned int __cdecl SND_HashName(const char *name); + +void Snd_CSV_PrintHeader(FILE* f); + +int Rip_SoundBank_GatherSnapshots_Callback_f(ForeignPointer& asset, ForeignPointer& zoneName, void* data); +int Rip_SoundBank_GatherRadverbs_Callback_f(ForeignPointer& asset, ForeignPointer& zoneName, void* data); + +int Rip_SoundBank_Callback_f(ForeignPointer& asset, ForeignPointer& zoneName, void* data); +int Rip_Snapshot_Callback_f(std::string name, std::vector>& snapshots); +int Rip_Radverb_Callback_f(std::string name, std::vector>& radverbs); diff --git a/components/asset_util/cmds/search/handler.h b/components/asset_util/cmds/search/handler.h new file mode 100644 index 00000000..a669875e --- /dev/null +++ b/components/asset_util/cmds/search/handler.h @@ -0,0 +1,262 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +#define UNUSED(x) (void)(x) + +template +class Handler +{ + private: + enum SlotState + { + EMPTY = 0, + INUSE = -1 + }; + + std::mutex mtx_slots; + std::mutex mtx_fs; + + std::mutex mtx_busy; + std::condition_variable cv_data; + + _SLOT_T **slots; + unsigned int numSlots; + + std::vector<_SRC_T> input_data; + unsigned int input_index; + + // Always lock mtx_slots before calling this + void FreeSlot(_SLOT_T **slot) + { + if (*slot != (_SLOT_T *)SlotState::INUSE) + delete[] * slot; + *slot = (_SLOT_T *)SlotState::EMPTY; + } + + typedef std::function _SlotInit_f; // Initializes an empty slot with data + + typedef std::function _SlotPostInit_f; // Executes any post slot init code + // returns a pointer to the (new) _SLOT_T data + // Useful for decompression, etc. + + typedef std::function _SlotHandleData_f; // Handle loaded slot data + + _SlotInit_f _SlotInit_Callback; + _SlotPostInit_f _SlotPostInit_Callback; + _SlotHandleData_f _SlotHandleData_Callback; + + static void _SlotInit_DefaultCallback(_SRC_T &_src, _SLOT_T &_outData) + { + UNUSED(_src); + UNUSED(_outData); + }; + static void _SlotPostInit_DefaultCallback(_SLOT_T &_slotData) { UNUSED(_slotData); }; + static void _SlotHandleData_DefaultCallback(_SLOT_T &_slotData) { UNUSED(_slotData); }; + + // Run a single handler iteration + // Either loads data, handles loaded data, or waits for data to be loaded (if all slots are busy) + int ExecuteLoopIteration() + { + _SLOT_T **slot = (_SLOT_T **)SlotState::INUSE; + + // Data handler lambda - lock mtx_slots before calling this + auto Handle_Data = [&slot, this](void) -> int { + _SLOT_T *data = *slot; + *slot = (_SLOT_T *)SlotState::INUSE; + mtx_slots.unlock(); + + // Run the handler callback on the slot data + _SlotHandleData_Callback(*data); + + //printf("FREEING DATA 0x%" PRIx64 "\n", (long unsigned int)data); + delete data; + + // Free the slot & mark it as empty + mtx_slots.lock(); + FreeSlot(slot); + mtx_slots.unlock(); + + return 0; + }; + + mtx_slots.lock(); + + // + // Attempt to get an EMPTY or Unhandled slot + // Otherwise we just get an INUSE handle + // + for (unsigned int i = 0; i < numSlots; i++) + { + if (slots[i] == (_SLOT_T *)SlotState::INUSE) + continue; + + slot = &slots[i]; + break; + } + + if (slot == (_SLOT_T **)SlotState::INUSE || *slot == (_SLOT_T *)SlotState::INUSE) + { + // There are no usable slots + mtx_slots.unlock(); + + //printf("All slots are in-use!\n"); + + // Wait for cv_data to be notified before checking for data again + std::unique_lock lock(mtx_busy); + cv_data.wait(lock); + + return 0; + } + else if (*slot == (_SLOT_T *)SlotState::EMPTY) + { + // Slot is empty - needs to be populated with data + //printf("Got an empty slot\n"); + + // If we've already loaded all source data + // We just exit current handle loop + // (might be better for the thread to try handling any data thats left?) + if (input_index >= input_data.size()) + { + mtx_slots.unlock(); + + // Notify any threads that are waiting for data + // They'll either find an empty slot - realize theres no data and exit + // or they'll handle any unhandled data first + cv_data.notify_all(); + + return -1; + } + + // If another thread is already using the filesytem + // we try to find some loaded data that hasn't been handled + // and handle it + if (!mtx_fs.try_lock()) + { + // printf("Filesystem busy... looking for unhandled data\n"); + for (unsigned int i = 0; i < numSlots; i++) + { + if (slots[i] == (_SLOT_T *)SlotState::INUSE || slots[i] == (_SLOT_T *)SlotState::EMPTY) + continue; + + slot = &slots[i]; + break; + } + + if (*slot != (_SLOT_T *)SlotState::INUSE && *slot != (_SLOT_T *)SlotState::EMPTY) + return Handle_Data(); + + mtx_slots.unlock(); + return 0; + } + + // Prevent any other threads from taking the slot while we populate the data + *slot = (_SLOT_T *)SlotState::INUSE; + + // Update the active input data index, but grab the current one first + unsigned int src_index = input_index++; + + mtx_slots.unlock(); + + // Allocate the data for the slot + // and call the _SlotInit callback to run the init code for the new data + _SLOT_T *data = new _SLOT_T; + _SlotInit_Callback(input_data[src_index], *data); + + mtx_fs.unlock(); + + // Run any PostInit code after the filesystem mutex has been freed + _SlotPostInit_Callback(*data); + + // Set slot to point to the data that we initialized + mtx_slots.lock(); + *slot = data; // Assign the data pointer to the slot + // printf("ALLOC DATA: 0x%" PRIx64 "\n", (long unsigned int)data); + mtx_slots.unlock(); + + // Notify any threads that were waiting for data + cv_data.notify_one(); + + return 0; + } + else + { + // We got a normal slot with data in it + // printf("Found data to handle!\n"); + return Handle_Data(); + } + + return 1; + } + + static int WorkerFunc(Handler* handler) + { + int result; + do + { + result = handler->ExecuteLoopIteration(); + } while (result == 0); + + return result; + } + + public: + Handler(std::vector<_SRC_T> &source_data, unsigned int _slotCount, + _SlotHandleData_f HandlerCallback=Handler::_SlotHandleData_DefaultCallback, + _SlotInit_f InitCallback = Handler::_SlotInit_DefaultCallback, + _SlotPostInit_f PostInitCallback = Handler::_SlotPostInit_DefaultCallback) + { + numSlots = _slotCount; + slots = new _SLOT_T *[numSlots]; + memset(slots, 0, sizeof(_SLOT_T *) * numSlots); + + input_data = source_data; + input_index = 0; + + _SlotInit_Callback = InitCallback; + _SlotPostInit_Callback = PostInitCallback; + _SlotHandleData_Callback = HandlerCallback; + } + + ~Handler() + { + mtx_slots.lock(); + + for (unsigned int i = 0; i < numSlots; i++) + { + FreeSlot(&slots[i]); + } + + _SLOT_T **p = slots; + slots = NULL; + delete[] p; + + mtx_slots.unlock(); + } + + void RunHandler(unsigned int workerCount = 1) + { + workerCount -= 1; + + std::thread* threads = new std::thread[workerCount]; + for(unsigned int i = 0; i < workerCount; i++) + { + threads[i] = std::thread(Handler::WorkerFunc, this); + } + + Handler::WorkerFunc(this); + + for(unsigned int i = 0; i < workerCount; i++) + { + threads[i].join(); + } + + delete[] threads; + } +}; \ No newline at end of file diff --git a/components/asset_util/common/ff.cpp b/components/asset_util/common/ff.cpp index b807c3a8..122a8faa 100644 --- a/components/asset_util/common/ff.cpp +++ b/components/asset_util/common/ff.cpp @@ -6,24 +6,56 @@ #include #include "../sys/AppInfo.h" #include "zlib\zlib.h" +#include +#include +#include -char* FindRawfileString(BYTE* start, BYTE* end) +enum class RAWFILE_TYPE { + COMPRESSED, + UNCOMPRESSED, + SOUND, + + NONE = -1, +}; + +RAWFILE_TYPE FindRawfileString(BYTE* start, BYTE* end, char** result) +{ + RAWFILE_TYPE type = RAWFILE_TYPE::NONE; + while (start < end - 5) { - if (strncmp(".atr", (char*)start, 4) == 0 || - strncmp(".gsc", (char*)start, 4) == 0 || - strncmp(".csc", (char*)start, 4) == 0 || - strncmp(".vision", (char*)start, 4) == 0 || - strncmp(".wav", (char*)start, 4) == 0) + if (strncmp(".gsc", (char*)start, 4) == 0 || + strncmp(".csc", (char*)start, 4) == 0) + { + type = RAWFILE_TYPE::COMPRESSED; + } + else if (strncmp(".atr", (char*)start, 4) == 0 || + strncmp(".sun", (char*)start, 4) == 0 || + strncmp(".xpo", (char*)start, 4) == 0 ) + { + type = RAWFILE_TYPE::UNCOMPRESSED; + } + else if (strncmp(".wav", (char*)start, 4) == 0) + { + type = RAWFILE_TYPE::SOUND; + } + else if (start < end - 8 && strncmp(".vision", (char*)start, 7) == 0) { - return (char*)start; + type = RAWFILE_TYPE::UNCOMPRESSED; + } + + if (type != RAWFILE_TYPE::NONE) + { + *result = (char*)start; + return type; } start++; } - return nullptr; + *result = nullptr; + return RAWFILE_TYPE::NONE; } char* FindRawfileStringReverseLookup(BYTE* start) @@ -49,7 +81,7 @@ int FF_FFExtractCompressedRawfile(XAssetRawfileHeader* rawfileHeader, const char Con_Print_v("Extracting file: \"%s\"... ", rawfilePath); char qpath[1024] = ""; - sprintf_s(qpath, "%s/%s", AppInfo_RawDir(), rawfilePath); + sprintf_s(qpath, "%s/%s", AppInfo_OutDir(), rawfilePath); // // If not in overwrite mode AND the file exists @@ -57,11 +89,9 @@ int FF_FFExtractCompressedRawfile(XAssetRawfileHeader* rawfileHeader, const char // if (!fs_overwrite.ValueBool()) { - if (FILE* h = fopen(qpath, "r")) + if (FS_FileExists(qpath)) { Con_Print_v("SKIPPED\n"); - - fclose(h); return 0; } } @@ -112,7 +142,19 @@ int FF_FFExtractUncompressedRawfile(char* rawfileData, const char* rawfilePath) Con_Print_v("Extracting file: \"%s\"... ", rawfilePath); char qpath[1024] = ""; - sprintf_s(qpath, "%s/%s", AppInfo_RawDir(), rawfilePath); + sprintf_s(qpath, "%s/%s", AppInfo_OutDir(), rawfilePath); + + int* pHeader = (int*)rawfilePath; + int len = pHeader[-2]; + + // + // Catch incorrect rawfile data to prevent massive allocations + // + if (len > 1024 * 1024 * 16) + { + Con_Print_v("IGNORED\n"); + return 0; + } // // If not in overwrite mode AND the file exists @@ -131,26 +173,18 @@ int FF_FFExtractUncompressedRawfile(char* rawfileData, const char* rawfilePath) if (FS_CreatePath(rawfilePath) != 0) { + printf("%s\n", rawfilePath); Con_Error_v("PATH ERROR\n"); return 0; } - - // - // Catch incorrect rawfile data to prevent massive allocations - // - if (strlen(rawfileData) > 1024 * 1024 * 16) - { - Con_Print_v("IGNORED\n"); - return 0; - } - + if (FILE* h = fopen(qpath, "wb")) { - fwrite(rawfileData, 1, strlen(rawfileData), h); + fwrite(rawfileData, 1, len, h); fclose(h); Con_Print_v("SUCCESS\n"); - return strlen(rawfileData); + return len; } Con_Error_v("ERROR\n"); @@ -169,7 +203,7 @@ int FF_FFExtractSoundFile(Snd_Header* snd_header, const char* sndfilePath) #if _DEBUG sprintf_s(qpath, "%s", sndfilePath); #else - sprintf_s(qpath, "%s/%s", AppInfo_RawDir(), sndfilePath); + sprintf_s(qpath, "%s/%s", AppInfo_OutDir(), sndfilePath); #endif @@ -200,7 +234,7 @@ int FF_FFExtractSoundFile(Snd_Header* snd_header, const char* sndfilePath) // if (snd_header->format != 6 && snd_header->format != 7) { - Con_Print_v("IGNORED\n"); + Con_Print_v("IGNORED (fmt %d)\n", snd_header->format); return 0; } @@ -244,91 +278,105 @@ int FF_FFExtractSoundFile(Snd_Header* snd_header, const char* sndfilePath) return 0; } -int FF_FFExtractFiles(BYTE* searchData, DWORD searchSize) +void PerformLookup(BYTE *Buffer, std::vector& Data, std::vector& Signature) { - int extractedFileCount = 0; - - BYTE* endofBuffer = searchData + searchSize; - - BYTE* lastSearchLoc = 0; - while (searchData < searchData + searchSize) + for (auto scanStart = Data.begin();;) { - char* rawfileString = FindRawfileString(searchData, endofBuffer); + // Search for the pattern + auto ret = std::search(scanStart, Data.end(), Signature.begin(), Signature.end()); - if (!rawfileString) - { - return extractedFileCount; - } + // If we didn't find a match, exit + if (ret == Data.end()) + break; + + // Convert match index to a real address + BYTE *asset = std::distance(Data.begin(), ret) + Buffer; + // Parse the asset (reverse string scan for the name) + char *rawfileString = (char *)asset; char* tmpString = FindRawfileStringReverseLookup((BYTE*)rawfileString); + int moveLen = 0; if (!tmpString) - { - return extractedFileCount; - } + return; - if ((BYTE*)tmpString < searchData || !IsCharAlphaNumericA(*tmpString)) + if ((BYTE*)tmpString < Buffer || !IsCharAlphaNumericA(*tmpString)) { - searchData += strlen(rawfileString) + 1; + scanStart = ret + strlen(rawfileString) + 1; continue; } rawfileString = tmpString; - if (Str_EndsWith(rawfileString, ".wav")) - { - if (!g_extractSounds.ValueBool()) - { - searchData = (BYTE*)rawfileString + strlen(rawfileString) + 1; - continue; - } + char assetExt[32]; + memset(assetExt, 0, sizeof(assetExt)); + memcpy(assetExt, asset, Signature.size()); - Snd_Header* snd_info = (Snd_Header*)(rawfileString - sizeof(Snd_Header)); - FF_FFExtractSoundFile(snd_info, rawfileString); - searchData = (BYTE*)rawfileString + strlen(rawfileString) + 1; - } - - // - // ARG_FLAG_FF Should never been false if this function is running - // - /* - if (!ARG_FLAG_FF) + // Dump + if (strstr(assetExt, ".gsc") || + strstr(assetExt, ".csc")) { - searchData = (BYTE*)rawfileString + strlen(rawfileString) + 1; - continue; + // GameScripts are compressed + XAssetRawfileHeader* rawfileHeader = (XAssetRawfileHeader*)(rawfileString + strlen(rawfileString) + 1); + if (FF_FFExtractCompressedRawfile(rawfileHeader, rawfileString)) + moveLen = rawfileHeader->compressedSize; } - */ - - if (Str_EndsWith(rawfileString, ".vision")) + else if (strstr(assetExt, ".atr") || + strstr(assetExt, ".sun") || + strstr(assetExt, ".xpo") || + strstr(assetExt, ".txt") || + strstr(assetExt, ".cfg") || + strstr(assetExt, ".vision")) { - char* rawfileData = rawfileString + strlen(rawfileString) + 1; - int fileLen = FF_FFExtractUncompressedRawfile(rawfileData, rawfileString); - - if (!fileLen) + // Ignore menu .txt files stored in /ui/ or /ui_mp/ + if (!(strstr(assetExt, ".txt") && (!_strnicmp(rawfileString, "ui/", 3) || !_strnicmp(rawfileString, "ui_mp/", 6)))) { - searchData = (BYTE*)rawfileString + strlen(rawfileString) + 1; - continue; + // Random uncompressed types + char* rawfileData = rawfileString + strlen(rawfileString) + 1; + moveLen = FF_FFExtractUncompressedRawfile(rawfileData, rawfileString); } - - searchData = (BYTE*)rawfileData + fileLen + 1; } - else + else if (strstr(assetExt, ".wav")) { - XAssetRawfileHeader* rawfileHeader = (XAssetRawfileHeader*)(rawfileString + strlen(rawfileString) + 1); - if (!FF_FFExtractCompressedRawfile(rawfileHeader, rawfileString)) - { - searchData = (BYTE*)rawfileString + strlen(rawfileString) + 1; - continue; - } - - - searchData = (BYTE*)rawfileHeader + rawfileHeader->compressedSize; + // Audio files + Snd_Header* snd_info = (Snd_Header*)(rawfileString - sizeof(Snd_Header)); + moveLen = FF_FFExtractSoundFile(snd_info, rawfileString); + } + else if (strstr(assetExt, ".hlsl")) + { + // Shaders (ignored for now) } - extractedFileCount++; + scanStart = (ret + moveLen + 1); } +} - return extractedFileCount; +int FF_FFExtractFiles(BYTE* searchData, DWORD searchSize) +{ + auto data = std::vector(searchData, searchData + searchSize); + auto scanList = std::vector>(); + + scanList.push_back(std::vector({ '.', 'g', 's', 'c' })); + scanList.push_back(std::vector({ '.', 'c', 's', 'c' })); + scanList.push_back(std::vector({ '.', 'a', 't', 'r' })); + scanList.push_back(std::vector({ '.', 's', 'u', 'n' })); + scanList.push_back(std::vector({ '.', 'x', 'p', 'o' })); + scanList.push_back(std::vector({ '.', 'w', 'a', 'v' })); + scanList.push_back(std::vector({ '.', 'c', 'f', 'g' })); + scanList.push_back(std::vector({ '.', 't', 'x', 't' })); + scanList.push_back(std::vector({ '.', 'v', 'i', 's', 'i', 'o', 'n' })); + // scanList.push_back(std::vector({ '.', 'h', 'l', 's', 'l' })); + + if (g_extractSounds.ValueBool()) + scanList.push_back(std::vector({ '.', 'w', 'a', 'v' })); + + concurrency::parallel_for(size_t(0), scanList.size(), [&](size_t i) + { + PerformLookup(searchData, data, scanList[i]); + }); + + // Return value is never used as of this time + return 0; } int FF_FFExtract(const char* filepath, const char* filename) @@ -338,7 +386,7 @@ int FF_FFExtract(const char* filepath, const char* filename) FILE* h = nullptr; if (fopen_s(&h, filepath, "r+b") != 0) { - Con_Error("ERROR: Fastfile '%s' could not be found\n\n", filepath); + Con_Error("ERROR: Fastfile '%s' could not be found\n", filepath); return FALSE; } rewind(h); diff --git a/components/asset_util/common/fs.cpp b/components/asset_util/common/fs.cpp index e42af46c..ac618bf7 100644 --- a/components/asset_util/common/fs.cpp +++ b/components/asset_util/common/fs.cpp @@ -28,12 +28,16 @@ bool FS_FileExists(const char* qpath) return false; } -size_t FS_FileSize(const char* qpath) +/* +// This is now defined in gsc_lib + +long int FS_FileSize(const char* qpath) { struct stat st_buf; int r = stat(qpath, &st_buf); return r == 0 ? st_buf.st_size : -1; } +*/ const char* FS_GetExtensionSubString(const char* filename) { @@ -76,6 +80,26 @@ const char* FS_GetFilenameSubString(const char* pathname) return last; } +const wchar_t* FS_GetFilenameSubStringW(wchar_t* pathname) +{ + wchar_t* last = pathname; + while (*pathname) + { + if (*pathname == '/' || *pathname == '\\') + last = pathname + 1; + ++pathname; + } + return last; +} + +void FS_StripFilename(const char* in, char* out) +{ + const char* extension = FS_GetFilenameSubString(in); + while (in != extension) + *out++ = *in++; + *out = 0; +} + int FS_FileCount(const char* path, const char* pattern) { HANDLE dir; @@ -83,7 +107,6 @@ int FS_FileCount(const char* path, const char* pattern) char spath[MAX_PATH]; sprintf_s(spath, "%s/%s", path, pattern); - Con_Print("%s\n", spath); if ((dir = FindFirstFileA(spath, &file_data)) == INVALID_HANDLE_VALUE) { @@ -105,7 +128,7 @@ int FS_FileCount(const char* path, const char* pattern) return count; } -int FS_FileIterator(const char* path, const char* pattern, int(__cdecl* FS_FileHandlerCallback)(const char* filePath, const char* fileName)) +int FS_FileIterator(const char* path, const char* pattern, FS_FileHandlerCallback_t FS_FileHandlerCallback) { HANDLE dir; WIN32_FIND_DATAA file_data; @@ -137,7 +160,7 @@ int FS_FileIterator(const char* path, const char* pattern, int(__cdecl* FS_FileH return count; } -int FS_DirectoryIterator(const char* path, int(__cdecl* FS_DirectoryHandlerCallback)(const char* path)) +int FS_DirectoryIterator(const char* path, FS_DirectoryHandlerCallback_t FS_DirectoryHandlerCallback) { HANDLE dir; WIN32_FIND_DATAA file_data; @@ -172,25 +195,38 @@ int FS_DirectoryIterator(const char* path, int(__cdecl* FS_DirectoryHandlerCallb return count; } -int FS_CreatePath(const char* targetPath) +int FS_CreatePath(const char* _targetPath) { + char targetPath[1024]; + if (strchr(_targetPath, ':') != NULL) + strcpy_s(targetPath, _targetPath); + else + sprintf_s(targetPath, "%s/%s", AppInfo_OutDir(), _targetPath); + int len = strlen(targetPath); for (int i = 0; i < len; i++) { - if (targetPath[i] == '/' || targetPath[i] == '\\') + bool skip = false; + if (i > 0 && targetPath[i-1] == ':') + skip = true; + + if (!skip && (targetPath[i] == '/' || targetPath[i] == '\\')) { char buf[1024] = ""; strncpy(buf + strlen(buf), targetPath, i); - char qpath[1024] = ""; - sprintf_s(qpath, "%s/%s/", AppInfo_RawDir(), buf); + char* qpath = buf; + if (strlen(qpath) == 0) + continue; + FS_SanitizePath(qpath); #if _DEBUG if (!CreateDirectoryA(buf, 0) && GetLastError() != ERROR_ALREADY_EXISTS) #else if (!CreateDirectoryA(qpath, 0) && GetLastError() != ERROR_ALREADY_EXISTS) #endif { + return GetLastError(); } } @@ -245,3 +281,28 @@ int FS_CopyDirectory(char* srcPath, char* destPath, bool overwriteFiles) return 0; } +void FS_SanitizePath(char* path) +{ + if (path[0] == '/') + path[0] = '\\'; + + // + // Cleanup double / or \ + // + int len = strlen(path); + for (int i = 1; i < len; i++) + { + if (path[i] == '/') + path[i] = '\\'; + + if (path[i] == '\\' && path[i - 1] == '\\') + strcpy(&path[i - 1], &path[i]); + } + + // + // Trim any leading / or \ + // + const char* c = path; + for (; *c == '/' || *c == '\\'; c++); + strcpy(path, c); +} diff --git a/components/asset_util/common/fs.h b/components/asset_util/common/fs.h index 26716e89..17ccc255 100644 --- a/components/asset_util/common/fs.h +++ b/components/asset_util/common/fs.h @@ -1,5 +1,6 @@ #pragma once #include +#include #define FS_SEARCHPATTERN_IWD "*.IWD" #define FS_SEARCHPATTERN_FF "*.FF" @@ -8,17 +9,24 @@ const char* FS_Cwd(void); bool FS_FileExists(const char* qpath); -size_t FS_FileSize(const char* qpath); +//size_t FS_FileSize(const char* qpath); +long int FS_FileSize(const char* qpath); const char* FS_GetExtensionSubString(const char* filename); void FS_StripExtension(const char* in, char* out); const char* FS_GetFilenameSubString(const char* pathname); +const wchar_t* FS_GetFilenameSubStringW(wchar_t* pathname); +void FS_StripFilename(const char* in, char* out); + +typedef std::function FS_FileHandlerCallback_t; +typedef std::function FS_DirectoryHandlerCallback_t; int FS_FileCount(const char* path, const char* pattern); -int FS_FileIterator(const char* path, const char* pattern, int(__cdecl* FS_FileHandlerCallback)(const char* filePath, const char* fileName)); -int FS_DirectoryIterator(const char* path, int(__cdecl* FS_DirectoryHandlerCallback)(const char* path)); +int FS_FileIterator(const char* path, const char* pattern, FS_FileHandlerCallback_t FS_FileHandlerCallback); +int FS_DirectoryIterator(const char* path, FS_DirectoryHandlerCallback_t FS_DirectoryHandlerCallback); int FS_CreatePath(const char* targetPath); int FS_CopyDirectory(char* srcPath, char* destPath, bool overwriteFiles = false); +void FS_SanitizePath(char* path); diff --git a/components/asset_util/common/iwd.cpp b/components/asset_util/common/iwd.cpp index 7ea359df..7e4f0a6f 100644 --- a/components/asset_util/common/iwd.cpp +++ b/components/asset_util/common/iwd.cpp @@ -44,7 +44,8 @@ int __cdecl IWD_IWDHandler(const char* iwdPath, const char* iwdName) int __cdecl IWD_IWDExtractFile(mz_zip_archive* iwd, const char* filepath) { char outPath[MAX_PATH]; - sprintf_s(outPath, "%s/%s", AppInfo_RawDir(), filepath); + sprintf_s(outPath, "%s/%s", AppInfo_OutDir(), filepath); + FS_SanitizePath(outPath); if (fs_overwrite.ValueBool()) { @@ -53,19 +54,19 @@ int __cdecl IWD_IWDExtractFile(mz_zip_archive* iwd, const char* filepath) return 0; } - FILE* h = NULL; - if (fopen_s(&h, outPath, "r")) + if (FS_FileExists(outPath)) { Con_Print_v("Skipping file: \"%s\"\n", filepath); - fclose(h); return 1; } else { Con_Print_v("Extracting file: \"%s\"... ", filepath); - if (FS_CreatePath(filepath) != 0) + + int err = FS_CreatePath(filepath); + if (err != 0) { - Con_Error_v("DIR ERROR\n"); + Con_Error_v("DIR ERROR (%d)\n", err); return 1; } @@ -75,7 +76,7 @@ int __cdecl IWD_IWDExtractFile(mz_zip_archive* iwd, const char* filepath) return 1; } - Con_Print("SUCCESS\n"); + Con_Print_v("SUCCESS\n"); return 0; } diff --git a/components/asset_util/common/llist.h b/components/asset_util/common/llist.h index 7c34fce6..ca0f420b 100644 --- a/components/asset_util/common/llist.h +++ b/components/asset_util/common/llist.h @@ -1,5 +1,8 @@ #pragma once +#include "../gsc_parser/src/cpp/util/llist.h" + +#if 0 template class LList { @@ -192,3 +195,4 @@ LList* LList::NextNode(void) const { return (this->next == head) ? NULL : this->next; } +#endif \ No newline at end of file diff --git a/components/asset_util/cvar.cpp b/components/asset_util/cvar.cpp index 3b87267a..95052ce3 100644 --- a/components/asset_util/cvar.cpp +++ b/components/asset_util/cvar.cpp @@ -36,7 +36,6 @@ REGISTER_GLOBAL_CVAR(fs_overwrite, "overwrite", 'o', "Overwrite existing files", #if _DEBUG REGISTER_GLOBAL_CVAR(g_dumpCVars, "dumpCVars", 'd', "Print all cvar values to the console", false); #endif -REGISTER_GLOBAL_CVAR(g_outPath, "outPath", NULL, "Target directory for file output", "/"); // // Register Standard CVars @@ -47,6 +46,19 @@ REGISTER_CVAR(g_extractImages, "images", NULL, "Extract image files", false); REGISTER_CVAR(g_extractSounds, "sounds", NULL, "Extract audio files", false); REGISTER_CVAR(g_useLocalized, "includeLocalized", NULL, "Extract from localized files as well", false); +REGISTER_CVAR(ents_useLabels, "labels", NULL, "Write entity number in a comment above each entity", false); +REGISTER_CVAR(ents_genBrushes, "dummyBrushes", NULL, "Generate dummy brushes for script/trigger brush models", true); + +REGISTER_CVAR(csvgen_aitypes, "aitype", NULL, "Regenerate all aitype CSVs", false); +REGISTER_CVAR(csvgen_characters, "character", NULL, "Regenerate all character CSVs", false); +REGISTER_CVAR(csvgen_xmodelaliases, "xmodelalias", NULL, "Regenerate all xmodelalias CSVs", false); + +REGISTER_CVAR(rip_waitForProcess, "waitForProcess", NULL, "Wait for a supported process to launch", false); +REGISTER_CVAR(rip_waitForMap, "waitForMap", NULL, "Wait for a map to load before continuing", false); +REGISTER_CVAR(rip_killProcess, "killProcess", NULL, "Terminate the game process when ripping is compeleted", false); + +REGISTER_CVAR(fs_outdir, "outdir", NULL, "Target directory for file output (default is the game's raw directory)", ""); + #undef REGISTER_GLOBAL_CVAR #undef REGISTER_CVAR diff --git a/components/asset_util/cvar.h b/components/asset_util/cvar.h index d346bf6d..176eafb6 100644 --- a/components/asset_util/cvar.h +++ b/components/asset_util/cvar.h @@ -92,6 +92,7 @@ class CVar : public Argument REGISTER_GLOBAL_CVAR(g_verbose); REGISTER_GLOBAL_CVAR(g_logfile); REGISTER_GLOBAL_CVAR(fs_overwrite); + #if _DEBUG REGISTER_GLOBAL_CVAR(g_dumpCVars); #endif @@ -105,5 +106,18 @@ REGISTER_CVAR(g_extractImages); REGISTER_CVAR(g_extractSounds); REGISTER_CVAR(g_useLocalized); +REGISTER_CVAR(ents_useLabels); +REGISTER_CVAR(ents_genBrushes); + +REGISTER_CVAR(csvgen_aitypes); +REGISTER_CVAR(csvgen_characters); +REGISTER_CVAR(csvgen_xmodelaliases); + +REGISTER_CVAR(rip_waitForProcess); +REGISTER_CVAR(rip_waitForMap); +REGISTER_CVAR(rip_killProcess); + +REGISTER_CVAR(fs_outdir); + #undef REGISTER_GLOBAL_CVAR #undef REGISTER_CVAR diff --git a/components/asset_util/gdt/gsc.cpp b/components/asset_util/gdt/gsc.cpp index 10bd74a6..87e20d05 100644 --- a/components/asset_util/gdt/gsc.cpp +++ b/components/asset_util/gdt/gsc.cpp @@ -1,6 +1,6 @@ -#include "../shared/utility.h" +#include "../shared/shared_utility.h" #include "gsc.h" -#include "assettype\character.h" +#include "assettype/character.h" #include "../common/io.h" #include diff --git a/components/asset_util/main.cpp b/components/asset_util/main.cpp index 9033391e..5cfefe02 100644 --- a/components/asset_util/main.cpp +++ b/components/asset_util/main.cpp @@ -68,7 +68,6 @@ int app_main(int argc, char** argv) } else #endif - Con_Print("\n"); AppInfo_Init(); return cmd_info.Cmd()->Exec(cmd_info.Argc(), cmd_info.Argv()); diff --git a/components/asset_util/setup.h b/components/asset_util/setup.h index f64bb10b..0b60693d 100644 --- a/components/asset_util/setup.h +++ b/components/asset_util/setup.h @@ -1,6 +1,6 @@ #pragma once -#include "../shared/utility.h" +#include "../shared/shared_utility.h" int Setup_Init(void); int Setup_Execute(void); \ No newline at end of file diff --git a/components/asset_util/sys/AppInfo.cpp b/components/asset_util/sys/AppInfo.cpp index d7f29bd9..7b35c5ac 100644 --- a/components/asset_util/sys/AppInfo.cpp +++ b/components/asset_util/sys/AppInfo.cpp @@ -2,6 +2,9 @@ #include "../setup.h" #include "AppInfo.h" #include "../common/io.h" +#include "../common/fs.h" + +#include "../cvar.h" char g_GameDirectory[MAX_PATH]; @@ -65,4 +68,18 @@ const char* AppInfo_RawDir() return rawDir; #endif +} + +const char* AppInfo_OutDir() +{ + if (strlen(fs_outdir.ValueString()) == 0) + return AppInfo_RawDir(); + + if (strstr(fs_outdir.ValueString(), ":") != NULL && strstr(fs_outdir.ValueString(), ":") != fs_outdir.ValueString() + 1) + { + Con_Warning("Invalid output directory - falling back to default\n"); + fs_outdir.AssignRawString(""); + } + + return fs_outdir.ValueString(); } \ No newline at end of file diff --git a/components/asset_util/sys/AppInfo.h b/components/asset_util/sys/AppInfo.h index e653e6f8..63759761 100644 --- a/components/asset_util/sys/AppInfo.h +++ b/components/asset_util/sys/AppInfo.h @@ -5,4 +5,5 @@ const char* AppInfo_AppDir(); const char* AppInfo_FFDir(); const char* AppInfo_ZoneDir(); const char* AppInfo_IWDDir(); -const char* AppInfo_RawDir(); \ No newline at end of file +const char* AppInfo_RawDir(); +const char* AppInfo_OutDir(); \ No newline at end of file diff --git a/components/asset_util/sys/process.cpp b/components/asset_util/sys/process.cpp index 82278b51..310541df 100644 --- a/components/asset_util/sys/process.cpp +++ b/components/asset_util/sys/process.cpp @@ -1,7 +1,14 @@ #include +#include #include #include "process.h" #include "AppInfo.h" +#include "../common/io.h" + +#include "../common/fs.h" + +#include +#pragma comment(lib, "psapi.lib") int Process_ExecuteConverter(void) { @@ -26,4 +33,172 @@ int Process_ExecuteConverter(void) } return 1; -} \ No newline at end of file +} + +LPCWSTR processStringTable[] = +{ + L"BlackOps.exe", + //L"BlackOpsMP.exe" - Disabled for VAC potential +}; + +PROCESS_TYPE Process_GetProcessType(WCHAR* processString) +{ + DWORD supportedProcessCount = sizeof(processStringTable) / sizeof(LPCWSTR); + + for (DWORD i = 0; i < supportedProcessCount; i++) + { + if (_wcsicmp(processString, processStringTable[i]) == 0) + { + return (PROCESS_TYPE)(i+1); + } + } + + return PROCESS_UNKNOWN; +}; + +PROCESS_TYPE Process_GetProcessType(processId_t pid) +{ + PROCESS_TYPE type = PROCESS_UNKNOWN; + + HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (!handle) + return type; + + WCHAR buf[MAX_PATH]; + if (GetModuleFileNameExW(handle, 0, buf, MAX_PATH)) + { + type = Process_GetProcessType((WCHAR*)FS_GetFilenameSubStringW(buf)); + } + + CloseHandle(handle); + return type; +} + +processId_t Process_FindSupportedProcess_Launched(void) +{ + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + + if (Process32First(snapshot, &entry) == TRUE) + { + while (Process32Next(snapshot, &entry) == TRUE) + { + if (PROCESS_TYPE processType = Process_GetProcessType(entry.szExeFile)) + { + CloseHandle(snapshot); + + switch (processType) + { + case PROCESS_BLACK_OPS: + case PROCESS_BLACK_OPS_MP: + wprintf(L"Supported process found! ('%s')\n", entry.szExeFile); + return entry.th32ProcessID; + default: + return NULL; + } + } + } + } + + CloseHandle(snapshot); + return NULL; +} + +processId_t Process_FindSupportedProcess(unsigned int timeoutDelay) +{ + _ASSERT(timeoutDelay < UINT_MAX); + + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + + LARGE_INTEGER start; + QueryPerformanceCounter(&start); + + if (timeoutDelay > 0) + Con_Print("Waiting for supported process to launch...\n"); + + for (bool had_to_wait = false;; had_to_wait = true) + { + LARGE_INTEGER split; + QueryPerformanceCounter(&split); + + if (processId_t pid = Process_FindSupportedProcess_Launched()) + { + // + // If we had to wait for the process to launch - and *just* found the process + // that means the process probably *just* launched + // So we wait a bit for the process to initialize before returning it + // + if (timeoutDelay && had_to_wait) + Sleep(100); + + return pid; + } + + LARGE_INTEGER elapsed_ms; + elapsed_ms.QuadPart = split.QuadPart - start.QuadPart; + elapsed_ms.QuadPart *= 1000; + elapsed_ms.QuadPart /= freq.QuadPart; + + if (elapsed_ms.QuadPart > timeoutDelay || elapsed_ms.QuadPart > UINT_MAX) + { + const char* reason = ""; + if (timeoutDelay) + reason = " - timeout reached"; + Con_Error("Could not find supported running process%s.\n", reason); + return NULL; + } + } +} + +void Process_SuspendThreads(processId_t pid) +{ + HANDLE threadSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL); + + THREADENTRY32 threadEntry; + threadEntry.dwSize = sizeof(THREADENTRY32); + + Thread32First(threadSnapshot, &threadEntry); + do + { + if (threadEntry.th32OwnerProcessID == pid) + { + HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, threadEntry.th32ThreadID); + SuspendThread(hThread); + CloseHandle(hThread); + } + } while (Thread32Next(threadSnapshot, &threadEntry)); + + CloseHandle(threadSnapshot); +} + +void Process_ResumeThreads(processId_t pid) +{ + HANDLE threadSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL); + + THREADENTRY32 threadEntry; + threadEntry.dwSize = sizeof(THREADENTRY32); + + Thread32First(threadSnapshot, &threadEntry); + do + { + if (threadEntry.th32OwnerProcessID == pid) + { + HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, threadEntry.th32ThreadID); + ResumeThread(hThread); + CloseHandle(hThread); + } + } while (Thread32Next(threadSnapshot, &threadEntry)); + + CloseHandle(threadSnapshot); +} + +bool Process_KillProcess(processId_t pid) +{ + HANDLE hProc = OpenProcess(PROCESS_TERMINATE, FALSE, pid); + if (!hProc) + return false; + + return TerminateProcess(hProc, 0) ? true : false; +} diff --git a/components/asset_util/sys/process.h b/components/asset_util/sys/process.h index ff618f83..902f7b04 100644 --- a/components/asset_util/sys/process.h +++ b/components/asset_util/sys/process.h @@ -1,3 +1,38 @@ #pragma once +enum PROCESS_TYPE +{ + PROCESS_UNKNOWN, + PROCESS_BLACK_OPS, + PROCESS_BLACK_OPS_MP, +}; + +typedef unsigned int processId_t; + int Process_ExecuteConverter(void); + +// +// Resolve a process' PROCESS_TYPE from the pid +// +PROCESS_TYPE Process_GetProcessType(processId_t pid); + +// +// Searches the active process list for a supported process, and returns the processId +// Returns NULL if no supported process is found withing the given number of ms +// +processId_t Process_FindSupportedProcess(unsigned int timeoutDelay=0); + +// +// Suspend all threads for a given processId +// +void Process_SuspendThreads(processId_t pid); + +// +// Resume all threads for a given processId +// +void Process_ResumeThreads(processId_t pid); + +// +// Kill a given process +// +bool Process_KillProcess(processId_t pid); diff --git a/components/asset_viewer/asset_viewer.vcxproj b/components/asset_viewer/asset_viewer.vcxproj index 03a90ba1..a8cdbaa8 100644 --- a/components/asset_viewer/asset_viewer.vcxproj +++ b/components/asset_viewer/asset_viewer.vcxproj @@ -21,15 +21,15 @@ DynamicLibrary true - v120_xp Unicode + v120_xp DynamicLibrary false - v120_xp true Unicode + v120_xp @@ -56,6 +56,7 @@ Level3 Disabled WIN32;_DEBUG;_WINDOWS;_USRDLL;ASSET_VIEWER_EXPORTS;%(PreprocessorDefinitions) + false Windows @@ -83,10 +84,12 @@ + + diff --git a/components/asset_viewer/common.cpp b/components/asset_viewer/common.cpp new file mode 100644 index 00000000..d9c08ff6 --- /dev/null +++ b/components/asset_viewer/common.cpp @@ -0,0 +1,10 @@ +#include "stdafx.h" +#include "common.h" + +void Com_SuppressNoModelSpam(int channel, const char* fmt, const char* name) +{ + if (*name) + { + Com_PrintError(channel, fmt, name); + } +} diff --git a/components/asset_viewer/common.h b/components/asset_viewer/common.h new file mode 100644 index 00000000..9301038d --- /dev/null +++ b/components/asset_viewer/common.h @@ -0,0 +1,6 @@ +#pragma once + +typedef void(__cdecl *Com_PrintError_t)(int channel, const char *fmt, ...); +static Com_PrintError_t Com_PrintError = (Com_PrintError_t)0x0040A0B0; + +void Com_SuppressNoModelSpam(int channel, const char* fmt, const char* name); diff --git a/components/asset_viewer/dllmain.cpp b/components/asset_viewer/dllmain.cpp index 25777aec..93edc2aa 100644 --- a/components/asset_viewer/dllmain.cpp +++ b/components/asset_viewer/dllmain.cpp @@ -13,6 +13,19 @@ void strcpy_safe(char *Dest, const char *Src) VirtualProtect(Dest, srcLen, d, &d); } +void DEJA_Printf(LPCSTR fmt, ...) +{ + char str[256]; + va_list va; + + va_start(va, fmt); + wvsprintfA(str, fmt, va); + printf("DEJA \\\\\\\nDEJA >>> "); + printf(str); + printf("\nDEJA ///\n"); + va_end(va); +} + bool g_Initted = false; BOOL AssetViewerMod_Init() @@ -72,6 +85,18 @@ BOOL AssetViewerMod_Init() PatchMemory_WithNOP(0x0080481B, 1); #endif + // + // Prevent "ERROR: xmodel '' not found" from being shown when theres no model loaded + // +#if ASSET_VIEWER_DISABLE_NO_MODEL_SPAM + PatchCall(0x00433FA2, (PBYTE)&Com_SuppressNoModelSpam); +#endif + + // + // Print DEJA output to console + // + Detours::X86::DetourFunction((PBYTE)0x008907B0, (PBYTE)DEJA_Printf); + g_Initted = true; return TRUE; diff --git a/components/asset_viewer/stdafx.h b/components/asset_viewer/stdafx.h index e8f29361..b4afc079 100644 --- a/components/asset_viewer/stdafx.h +++ b/components/asset_viewer/stdafx.h @@ -2,6 +2,9 @@ #define _CRT_SECURE_NO_WARNINGS 1 +#pragma comment(lib, "detours.lib") +#include "../shared/detours/Detours.h" + #define WIN32_LEAN_AND_MEAN #include #include @@ -12,7 +15,7 @@ // // Shared files // -#include "../shared/utility.h" +#include "../shared/shared_utility.h" #include "../shared/minidx9/Include/d3dx9.h" #pragma comment(lib, "../shared/minidx9/Lib/x86/d3dx9.lib") @@ -25,9 +28,11 @@ #define SRCLINE(x) #define CHECK_SIZE(Type, Size) static_assert(sizeof(Type) == Size, "Invalid type size!"); +#include "common.h" + // // Asset Viewer Mod Options // Can be used to easily toggle certain tweaks // #define ASSET_VIEWER_DISABLE_MATERIAL_ASSERT 1 - +#define ASSET_VIEWER_DISABLE_NO_MODEL_SPAM 1 diff --git a/components/cod2map/arg.cpp b/components/cod2map/arg.cpp new file mode 100644 index 00000000..222a0c34 --- /dev/null +++ b/components/cod2map/arg.cpp @@ -0,0 +1,73 @@ +#include "stdafx.h" + +#define VANILLA_ARG_COUNT 29 + +struct Argument +{ + const char *arg; + const char *description; + + // Returns the number of args it consumes + int(__cdecl *func)(int argc, const char **argv); +}; + +Argument* g_args = (Argument*)0x004F4390; + +Argument g_customArgs[] = +{ + { "-debugProbes", "Add a reflective debug sphere on top of each reflection probe", &Probe_EnableDebugSpheres_f } +}; + +void __declspec(naked) Arg_Handle_hk(void) +{ + _asm + { + push ebx // argv + push [esp+8] // argc + call Arg_Handle + add esp, 8 + retn + } +} + +int Arg_CheckForMatch(const char* name, int index) +{ + if (index < VANILLA_ARG_COUNT) + return _stricmp(name, g_args[index].arg); + + return _stricmp(name, g_customArgs[index - VANILLA_ARG_COUNT].arg); +} + +int Arg_Handle(int argc, const char** argv) +{ + int argIndex = 0; + while (Arg_CheckForMatch(*argv, argIndex)) + { + if (++argIndex < VANILLA_ARG_COUNT + ARRAYSIZE(g_customArgs)) + continue; + + // There was an unknown arg - print usage + Com_Printf("\n\nUnknown argument '%s'\n\n", *argv); + Com_Printf("USAGE: cod2map [options] mapname, where options are 0 or more of the following.\n"); + Com_Printf("Options ignore capitalization; it is only present in the list for clarity.\n"); + + // Print vanilla args + for (int i = 0; i < VANILLA_ARG_COUNT; i++) + { + Com_Printf("%-20s %s\n", g_args[i].arg, g_args[i].description); + } + + // Print custom args + for (int i = 0; i < ARRAYSIZE(g_customArgs); i++) + { + Com_Printf("%-20s %s\n", g_customArgs[i].arg, g_customArgs[i].description); + } + + exit(-1); + } + + if (argIndex < VANILLA_ARG_COUNT) + return g_args[argIndex].func(argc, argv); + else + return g_customArgs[argIndex - VANILLA_ARG_COUNT].func(argc, argv); +} diff --git a/components/cod2map/arg.h b/components/cod2map/arg.h new file mode 100644 index 00000000..c55d667f --- /dev/null +++ b/components/cod2map/arg.h @@ -0,0 +1,4 @@ +#pragma once + +void Arg_Handle_hk(void); +int Arg_Handle(int argc, const char** argv); diff --git a/components/cod2map/cod2map.vcxproj b/components/cod2map/cod2map.vcxproj index 6cef4704..850ee755 100644 --- a/components/cod2map/cod2map.vcxproj +++ b/components/cod2map/cod2map.vcxproj @@ -87,22 +87,32 @@ + + + + + + + + {5fb372dd-a7e1-4a4b-9a28-3ac02543e9e8} + + diff --git a/components/cod2map/common.h b/components/cod2map/common.h new file mode 100644 index 00000000..cb991483 --- /dev/null +++ b/components/cod2map/common.h @@ -0,0 +1,4 @@ +#pragma once + +typedef int (__cdecl* Com_Printf_t)(char *fmt, ...); +static Com_Printf_t Com_Printf = (Com_Printf_t)0x00418F60; diff --git a/components/cod2map/dllmain.cpp b/components/cod2map/dllmain.cpp index 8314668d..1e8faac2 100644 --- a/components/cod2map/dllmain.cpp +++ b/components/cod2map/dllmain.cpp @@ -1,3 +1,4 @@ +#define G_VERSION 1, 0, 0 #include "stdafx.h" LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) @@ -43,6 +44,9 @@ int __cdecl ConvertBSP_Post(FILE* h) iBSP->Convert(BSPVERSION_COD_BO); Light_FixPrimaryLightInfo(&iBSP->lumps[LUMP_PRIMARY_LIGHTS]); + + if (Probe_UseDebugSpheres()) + Probe_AddDebugSpheres(iBSP); len = iBSP->PotentialFileSize(); buf = new BYTE[len]; @@ -136,11 +140,21 @@ void Init_MapMod() // Detours::X86::DetourFunction((PBYTE)0x0043D649, (PBYTE)&mfh_PrimaryLightHandler); + // + // Add -debugReflectionProbes launch arg + // + Detours::X86::DetourFunction((PBYTE)0x00406D20, (PBYTE)&Arg_Handle_hk); + + // + // (Deprecated): Replacement for the pointfile extension + // + /* void* stringPatch = ".pts"; PatchMemory(0x0042626F, (PBYTE)&stringPatch, 4); PatchMemory(0x00426514, (PBYTE)&stringPatch, 4); stringPatch = "%s.pts"; PatchMemory(0x00406F4E, (PBYTE)&stringPatch, 4); + */ // // Increase (double) the max amount of curvenn/terrain collision verts diff --git a/components/cod2map/probe.cpp b/components/cod2map/probe.cpp new file mode 100644 index 00000000..f9db6384 --- /dev/null +++ b/components/cod2map/probe.cpp @@ -0,0 +1,53 @@ +#include "stdafx.h" +#include "../D3DBSP_Lib/D3DBSP_Lib/ReflectionProbes.h" + +typedef DiskGfxReflectionProbe_BO Probe; + +static bool g_shouldAddDebugSpheres = false; + +int Probe_EnableDebugSpheres_f(int argc, const char **argv) +{ + g_shouldAddDebugSpheres = true; + return 1; +} + +bool Probe_UseDebugSpheres(void) +{ + return g_shouldAddDebugSpheres; +} + +int Probe_AddDebugSpheres(D3DBSP* bsp) +{ + if (bsp == NULL) + return -1; + + Lump& lump_probes = bsp->lumps[LUMP_REFLECTION_PROBES]; + Lump& lump_ents = bsp->lumps[LUMP_ENTITIES]; + + if (lump_probes.isEmpty || lump_ents.isEmpty) + return 0; + + std::string str = (char*)lump_ents.content; + lump_ents.FreeMemory(); + + char buf[1024]; + const char* fmt = + "{\n" + "\"classname\" \"misc_model\"\n" + "\"model\" \"%s\"\n" + "\"origin\" \"%f %f %f\"\n" + "}\n"; + + Probe* probe = (Probe*)lump_probes.content; + for (unsigned int i = 0; i < lump_probes.size / sizeof(Probe); i++) + { + buf[0] = '\0'; + sprintf_s(buf, fmt, "test_sphere_silver", probe[i].origin[0], probe[i].origin[1], probe[i].origin[2]); + str += buf; + } + + lump_ents.AllocateMemory(str.size()); + memcpy(lump_ents.content, str.c_str(), str.size()); + + return 0; +} diff --git a/components/cod2map/probe.h b/components/cod2map/probe.h new file mode 100644 index 00000000..2fadabe2 --- /dev/null +++ b/components/cod2map/probe.h @@ -0,0 +1,6 @@ +#pragma once + +int Probe_EnableDebugSpheres_f(int argc, const char **argv); + +bool Probe_UseDebugSpheres(void); +int Probe_AddDebugSpheres(D3DBSP* bsp); diff --git a/components/cod2map/stdafx.h b/components/cod2map/stdafx.h index 88427e91..4ebd030f 100644 --- a/components/cod2map/stdafx.h +++ b/components/cod2map/stdafx.h @@ -2,7 +2,11 @@ #define _CRT_SECURE_NO_WARNINGS 1 +#pragma comment(lib, "detours.lib") +#include "../shared/detours/Detours.h" + #define WIN32_LEAN_AND_MEAN + #include #include #include @@ -11,7 +15,8 @@ // // Shared files // -#include "../shared/utility.h" +#include "../shared/shared_utility.h" +#include "../shared/shared_version.h" #include "PageGuard.h" @@ -24,9 +29,13 @@ #pragma comment(lib, "../../build/Release/D3DBSP_Lib.lib") #endif +using namespace D3DBSP_Lib; +#include "arg.h" #include "vec.h" #include "kvs.h" +#include "probe.h" #include "lights.h" +#include "common.h" #include "libqhull_geom.h" #include "com_files.h" \ No newline at end of file diff --git a/components/cod2rad/cod2rad.vcxproj b/components/cod2rad/cod2rad.vcxproj index 14bc8364..be181e25 100644 --- a/components/cod2rad/cod2rad.vcxproj +++ b/components/cod2rad/cod2rad.vcxproj @@ -21,15 +21,15 @@ DynamicLibrary true - v120_xp Unicode + v120_xp DynamicLibrary false - v120_xp true Unicode + v120_xp @@ -57,6 +57,8 @@ WIN32;_DEBUG;_WINDOWS;_USRDLL;COD2RAD_EXPORTS;%(PreprocessorDefinitions) MultiThreadedDebugDLL false + true + Speed Windows @@ -75,6 +77,7 @@ WIN32;NDEBUG;_WINDOWS;_USRDLL;COD2RAD_EXPORTS;%(PreprocessorDefinitions) MultiThreadedDLL true + Speed Windows @@ -90,12 +93,17 @@ + - + + + + + @@ -106,8 +114,8 @@ + - false @@ -117,9 +125,14 @@ + + + + + @@ -127,6 +140,12 @@ Create + + + + + {5fb372dd-a7e1-4a4b-9a28-3ac02543e9e8} + diff --git a/components/cod2rad/com_bsp_load_obj.cpp b/components/cod2rad/com_bsp_load_obj.cpp index 90c76411..850d570c 100644 --- a/components/cod2rad/com_bsp_load_obj.cpp +++ b/components/cod2rad/com_bsp_load_obj.cpp @@ -54,22 +54,24 @@ void Com_SaveLightmaps_HDR(Lump* lump) void Com_SaveLightgrid_HDR(Lump* lump) { - DWORD* lightgridColorCount = (DWORD*)0x112BAAB4; - DiskGfxLightGridColors_BO* lightgridColors = (DiskGfxLightGridColors_BO*)lump->content; - for (DWORD i = 0; i < *lightgridColorCount; i++) + for (DWORD i = 0; i < lightGridColorCount; i++) { for (int y = 0; y < 56; y++) { for (int x = 0; x < 3; x++) { +#if USE_LEGACY_HDR lightgridColors[i].rgb[y][x] = DiskLightGridSampleColors_HDR[i][y][x]; +#endif + lightgridColors[i].rgb[y][x] = disk_lightGridColorsHDR[i].rgb[y][x] * 4; // Multiplying the lighting by 4 (so its 0 - 1024 range) seems to look ok } } } - +#if USE_LEGACY_HDR delete[] DiskLightGridSampleColors_HDR; +#endif } int __cdecl Com_SaveBsp_EnforceVersion(FILE* h) @@ -93,7 +95,9 @@ int __cdecl Com_SaveBsp_EnforceVersion(FILE* h) if (g_HDR) { +#if USE_LEGACY_HDR delete[] LightGridSampleColors_HDR; +#endif Com_SaveLightmaps_HDR(&iBSP->lumps[LUMP_LIGHTBYTES]); Com_SaveLightgrid_HDR(&iBSP->lumps[LUMP_LIGHTGRIDCOLORS]); diff --git a/components/cod2rad/com_files.cpp b/components/cod2rad/com_files.cpp index c8377ff8..e6ce5113 100644 --- a/components/cod2rad/com_files.cpp +++ b/components/cod2rad/com_files.cpp @@ -52,6 +52,35 @@ void __declspec(naked) hk_FS_FOpenFileRead() } } +int __cdecl FS_ReadFile(const char *qpath, void **buffer) +{ + int result = 0; + + _asm + { + pushad + push buffer + mov eax, qpath + mov ebx, 0x00423E80 + call ebx + add esp, 4 + mov result, eax + popad + } +} + +void __cdecl FS_FreeFile(void *buffer) +{ + _asm + { + pushad + mov eax, buffer + mov ebx, 0x004232D0 + call ebx + popad + } +} + const char* strlastof(const char* str, const char* delims) { int delimCount = strlen(delims); diff --git a/components/cod2rad/com_files.h b/components/cod2rad/com_files.h index 912f1175..8c91f36d 100644 --- a/components/cod2rad/com_files.h +++ b/components/cod2rad/com_files.h @@ -24,6 +24,8 @@ static FS_FileRead_t FS_FileRead = (FS_FileRead_t)0x00403F10; typedef size_t(__cdecl* FS_FileWrite_t)(const void *DstBuf, size_t Size, size_t Count, FILE *File); static FS_FileWrite_t FS_FileWrite = (FS_FileWrite_t)0x0040408A; +int __cdecl FS_ReadFile(const char *qpath, void **buffer); +void __cdecl FS_FreeFile(void *buffer); size_t FS_FileGetFileSize(FILE* h); diff --git a/components/cod2rad/com_math.cpp b/components/cod2rad/com_math.cpp new file mode 100644 index 00000000..caa15003 --- /dev/null +++ b/components/cod2rad/com_math.cpp @@ -0,0 +1,37 @@ +#include "stdafx.h" + +int ClampByte(int v) +{ + if (v < 0) + return 0; + if (v > 255) + return 255; + return v; +} + +BYTE __cdecl EncodeFloatInByte(float flt) +{ + double f = flt; + if (f < 0.0) + { + return 0; + } + + if (f > 1.0) + { + return 255; + } + + return (BYTE)(f * 255.0); +} + +WORD __cdecl EncodeFloatInWord(float flt) +{ + double f = flt; + if (f < 0.0) + { + return 0; + } + + return (WORD)(f * 255.0); +} diff --git a/components/cod2rad/com_math.h b/components/cod2rad/com_math.h new file mode 100644 index 00000000..5614a8a1 --- /dev/null +++ b/components/cod2rad/com_math.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include "vector.h" + +// +// Clamp an integer value to the 0 - 255 range +// +int ClampByte(int v); + +// +// Encode a float ( 0.0 - 1.0 ) to a BYTE ( 0 - 255 ) +// +BYTE __cdecl EncodeFloatInByte(float flt); + +WORD __cdecl EncodeFloatInWord(float flt); diff --git a/components/cod2rad/com_memory.cpp b/components/cod2rad/com_memory.cpp index 04de3a9b..62c41ea6 100644 --- a/components/cod2rad/com_memory.cpp +++ b/components/cod2rad/com_memory.cpp @@ -1,5 +1,29 @@ #include "stdafx.h" +struct hunkHeader_t +{ + unsigned int magic; + int size; + const char *name; + int dummy; +}; +STATIC_ASSERT_SIZE(hunkHeader_t, 16); + +struct hunkUsed_t +{ + int permanent; + int temp; +}; +STATIC_ASSERT_SIZE(hunkUsed_t, 8); + +VANILLA_VALUE(s_hunkData, char*, 0x174EFFC0); +VANILLA_VALUE(hunk_low, hunkUsed_t, 0x1748F158); + +namespace vanilla +{ + auto free = (void(__cdecl*)(void*lpMem))0x00402FD7; +} + void* FS_AllocMem(size_t size) { if (size % 4096) @@ -10,3 +34,35 @@ void* FS_AllocMem(size_t size) return buf; } + +void* _Hunk_AllocTempMemoryInternal(size_t _size) +{ + static DWORD dwFunc = 0x004219A0; + + void* result = NULL; + _asm + { + mov eax, _size + call dwFunc + mov result, eax + } + return result; +} + +void _Hunk_FreeTempMemory(void *buf) +{ + if (s_hunkData) + { + ASSERT(buf); + hunkHeader_t* hdr = reinterpret_cast(buf)-1; + if (hdr->magic != 0x89537892) + Com_Error("Hunk_FreeTempMemory: bad magic"); + hdr->magic = 0x89537893; + ASSERT(hdr == (void *)(s_hunkData + ((hunk_low.temp - hdr->size + 15) & ~15))); + hunk_low.temp -= hdr->size; + } + else + { + vanilla::free(buf); + } +} diff --git a/components/cod2rad/com_memory.h b/components/cod2rad/com_memory.h index 98dbd3a2..014ee192 100644 --- a/components/cod2rad/com_memory.h +++ b/components/cod2rad/com_memory.h @@ -7,3 +7,6 @@ typedef void (__cdecl* Z_Free_t)(void* ptr); static Z_Free_t Z_Free = (Z_Free_t)0x00402FD7; void* FS_AllocMem(size_t size); + +void* _Hunk_AllocTempMemoryInternal(size_t size); +void _Hunk_FreeTempMemory(void *buf); diff --git a/components/cod2rad/common.h b/components/cod2rad/common.h index 3dba7e70..57416381 100644 --- a/components/cod2rad/common.h +++ b/components/cod2rad/common.h @@ -1,4 +1,10 @@ #pragma once -typedef int(__cdecl* Com_FatalError_t)(char* format, ...); -static Com_FatalError_t Com_FatalError = (Com_FatalError_t)0x004294B0; +// ... + +typedef int(__cdecl* Com_Print_t)(const char* fmt, ...); +static Com_Print_t Com_Printf = (Com_Print_t)0x00441010; +static Com_Print_t Com_Error = (Com_Print_t)0x00440250; + +typedef void(__cdecl* Com_AssembleImageFilepath_t)(const char* image, char* buf); +static Com_AssembleImageFilepath_t Com_AssembleImageFilepath = (Com_AssembleImageFilepath_t)0x0041E1A0; diff --git a/components/cod2rad/console.cpp b/components/cod2rad/console.cpp deleted file mode 100644 index 6c32c697..00000000 --- a/components/cod2rad/console.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "stdafx.h" - -void UpdateProgressPrint() -{ - ((void(__cdecl *)())0x00429500)(); -} - -void SetProgress(int a1, int a2) -{ - *(DWORD *)0x1730241C = a1; - *(DWORD *)0x17302420 = a2; - UpdateProgressPrint(); -} \ No newline at end of file diff --git a/components/cod2rad/console.h b/components/cod2rad/console.h deleted file mode 100644 index 41258591..00000000 --- a/components/cod2rad/console.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -void UpdateProgressPrint(); -void SetProgress(int a1, int a2); \ No newline at end of file diff --git a/components/cod2rad/dllmain.cpp b/components/cod2rad/dllmain.cpp index 44f9cbba..e1d0af9c 100644 --- a/components/cod2rad/dllmain.cpp +++ b/components/cod2rad/dllmain.cpp @@ -1,3 +1,4 @@ +#define G_VERSION 1, 3, 0 #include "stdafx.h" static char* techsetPath = "pimp/techsets/%s%s.techset"; @@ -15,6 +16,27 @@ LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) //return PageGuard_Check(ExceptionInfo); } +#if MINIMAL_MATERIALS +int __cdecl MaterialRedirect(char* dst, const char* fmt, const char* name) +{ + printf("MTL: %s\n", name); + + std::string n = name; + if (n == "$default" || n.find("sky") != std::string::npos) + return sprintf(dst, fmt, name); + else + return sprintf(dst, fmt, "blockout_test_asphalt"); +} +#endif + +#if CUSTOM_RAND +int getRandomNumber() +{ + return 4; // chosen by fair dice roll. + // guaranteed to be random. +} +#endif + const int MAX_MAP_COLLISIONVERTS = 65536 * 2; const int MAX_MAP_COLLISIONVERTS_SIZE = MAX_MAP_COLLISIONVERTS * 12; BYTE collVertData[MAX_MAP_COLLISIONVERTS_SIZE]; @@ -49,6 +71,18 @@ BOOL cod2rad_Init() // PatchArguments(); + // + // Reenable Improved Quantization in Extra Mode + // Disabled - Currently Corrupts LightGrid Colors + // + // PatchMemory(0x00440870, (PBYTE)"\x01", 1); + + // + // Patch Default Gamma 2.2 -> 2.0 + // + float gamma_default = 2.0f; + PatchMemory(0x00462ABC, (PBYTE)&gamma_default, 4); + // // Increase limits for LUMP_COLLISIONVERTS // @@ -61,8 +95,8 @@ BOOL cod2rad_Init() // // Enable Techset / Technique Path Redirection // - PatchMemory(0x0042CA85, (PBYTE)&techiquePath, 4); - PatchMemory(0x0042CB4C, (PBYTE)&techsetPath, 4); + //PatchMemory(0x0042CA85, (PBYTE)&techiquePath, 4); + //PatchMemory(0x0042CB4C, (PBYTE)&techsetPath, 4); // // Patch the requested IWI version to match BO1 @@ -70,6 +104,7 @@ BOOL cod2rad_Init() PatchMemory(0x00417AA7, (PBYTE)"\xEB", 1); PatchMemory(0x00417B41, (PBYTE)"\x30", 1); FS_FileOpen = (FS_FileOpen_t)Detours::X86::DetourFunction((PBYTE)0x004034E8, (PBYTE)&FS_ImageRedirect); + Detours::X86::DetourFunction((PBYTE)0x00417A50, (PBYTE)&hk_Image_GetRawPixels); // // Patch Xmodel loading functions to support Black Ops @@ -94,6 +129,20 @@ BOOL cod2rad_Init() Detours::X86::DetourFunction((PBYTE)0x004413D0, (PBYTE)&hk_FS_FOpenFileRead); Detours::X86::DetourFunction((PBYTE)0x00442E97, (PBYTE)&mfh_Com_SaveBsp); +#if !USE_LEGACY_HDR + Detours::X86::DetourFunction((PBYTE)0x00432830, (PBYTE)&hk_StoreLightBytes); +#endif + + Detours::X86::DetourFunction((PBYTE)0x0042B450, (PBYTE)&RegisterLightDef); + +#if MINIMAL_MATERIALS + PatchCall(0x0042CD4A, (PBYTE)&MaterialRedirect); +#endif + +#if CUSTOM_RAND + Detours::X86::DetourFunction((PBYTE)0x004047C3, (PBYTE)&getRandomNumber); +#endif + g_initted = true; return TRUE; } @@ -107,6 +156,15 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv cod2rad_Init(); break; case DLL_PROCESS_DETACH: +#if VARIANCE_TRACKER + VARIANCE_LOG(vt_GetInitialLightingHighlightDir); + VARIANCE_LOG(vt_GetColorsForHighlightDir_1); + VARIANCE_LOG(vt_GetColorsForHighlightDir_2); + VARIANCE_LOG(vt_GetLightingApproximationError); + VARIANCE_LOG(vt_GetGradientOfLightingErrorFunctionWithRespectToDir); + VARIANCE_LOG(vt_ImproveLightingApproximation_1); + VARIANCE_LOG(vt_ImproveLightingApproximation_2); +#endif Con_Restore(); break; } diff --git a/components/cod2rad/hdr.cpp b/components/cod2rad/hdr.cpp index 47dba5a8..d838d894 100644 --- a/components/cod2rad/hdr.cpp +++ b/components/cod2rad/hdr.cpp @@ -3,8 +3,10 @@ vec4* LightmapBytes_HDR = nullptr; vec4* Lightmap2Bytes_HDR = nullptr; +#if USE_LEGACY_HDR SampleColorHDR* LightGridSampleColors_HDR = nullptr; DiskSampleColorHDR* DiskLightGridSampleColors_HDR = nullptr; +#endif void R_Init_LightmapsHDR() { @@ -12,6 +14,7 @@ void R_Init_LightmapsHDR() Lightmap2Bytes_HDR = new vec4[512 * 512 * *g_LightmapCount]; } +#if USE_LEGACY_HDR void R_Init_LightgridHDR() { LightGridSampleColors_HDR = new SampleColorHDR[*g_lightgridSampleCount + 1]; @@ -22,11 +25,13 @@ void R_Init_DiskLightgridHDR() { DiskLightGridSampleColors_HDR = new DiskSampleColorHDR[*g_diskLightgridSampleCount]; } +#endif void PatchHDR_Lightmaps() { +#if USE_LEGACY_HDR // - // Remove HDR Clamping when getting lightmap pixels + // Remove HDR Clamping when getting lightmap pixels (GetColorsForHighlightDir) // PatchMemory(0x00431186, (PBYTE)"\xEB", 1); PatchMemory(0x004311AB, (PBYTE)"\xEB", 1); @@ -39,7 +44,7 @@ void PatchHDR_Lightmaps() PatchMemory(0x00431388, (PBYTE)"\xEB", 1); // - // Remove HDR Pixel Clamping Before Converting to RGB8 + // Remove HDR Pixel Clamping Before Converting to RGB8 (StoreLightBytes) // PatchMemory(0x004328AA, (PBYTE)"\xEB", 1); PatchMemory(0x004328EC, (PBYTE)"\xEB", 1); @@ -48,25 +53,56 @@ void PatchHDR_Lightmaps() PatchMemory(0x004329C8, (PBYTE)"\xEB", 1); PatchMemory(0x00432A0B, (PBYTE)"\xEB", 1); PatchMemory(0x00432A86, (PBYTE)"\xEB", 1); +#endif // // Enable HDR Lighmap Allocation and Storage // o_R_BuildFinalLightmaps = (R_BuildFinalLightmaps_t)Detours::X86::DetourFunction((PBYTE)0x00432D70, (PBYTE)&hk_R_BuildFinalLightmaps); + +#if USE_LEGACY_HDR Detours::X86::DetourFunction((PBYTE)0x00432830, (PBYTE)&hk_R_StoreLightmapPixel); Detours::X86::DetourFunction((PBYTE)0x00432885, (PBYTE)&mfh_R_StoreLightmapPixel); +#endif } void PatchHDR_Lightgrid() { +#if USE_LEGACY_HDR // // Remove HDR Clamping // PatchMemory(0x00434E87, (PBYTE)"\xEB", 1); + // Allocates the HDR sample color buffer, runs just before SetupLightRegions() + // Detours::X86::DetourFunction((PBYTE)0x00436847, (PBYTE)&mfh_R_Init_Lightgrid); - Detours::X86::DetourFunction((PBYTE)0x00434EA7, (PBYTE)&mfh_R_Store_LightgridSample); - Detours::X86::DetourFunction((PBYTE)0x00435C8E, (PBYTE)&mfh_R_Alloc_DiskLightGridColors); - o_R_Store_QuantizedLightGridSample = (R_Store_QuantizedLightGridSample_t)Detours::X86::DetourFunction((PBYTE)0x00433890, (PBYTE)&hk_R_Store_QuantizedLightGridSample); + Detours::X86::DetourFunction((PBYTE)0x00434EA7, (PBYTE)&mfh_R_Store_LightgridSample); // StoreLightingForDir - Just rewrite the whole func + + Detours::X86::DetourFunction((PBYTE)0x00435C8E, (PBYTE)&mfh_R_Alloc_DiskLightGridColors); // ClusterLightGridValues + o_R_Store_QuantizedLightGridSample = (R_Store_QuantizedLightGridSample_t)Detours::X86::DetourFunction((PBYTE)0x00433890, (PBYTE)&hk_R_Store_QuantizedLightGridSample); // SetLightGridColorsForCluster +#endif + + // + // Patch the struct size when allocating lightGridGlob->colors in CalculateLightGrid() + // Makes it twice as large to accommodate for the HDR colors + // + // Note - lightgridglob->colors pointers in essentially everywhere its used + // + int size = sizeof(GfxLightGridColorsHDR); //GfxLightGridColors + PatchMemory(0x004367C0, (PBYTE)&size, 4); + + // + // Adjust the multiplication used to generate a pointer to a specific entry to pass to StoreLightingForDir + // Accomodates the new struct type + // + PatchMemory(0x004364F3, (PBYTE)&size, 4); + PatchMemory(0x0043561B, (PBYTE)&size, 4); + + // + // Automatically adjust the GfxLightGridColors pointer passed to StoreLightingForDir for our HDR struct + // + Detours::X86::DetourFunction((PBYTE)0x00434E20, (PBYTE)&StoreLightingForDir); + Detours::X86::DetourFunction((PBYTE)0x00435B70, (PBYTE)&ClusterLightGridValues); } \ No newline at end of file diff --git a/components/cod2rad/hdr.h b/components/cod2rad/hdr.h index a04538fd..eefdf97d 100644 --- a/components/cod2rad/hdr.h +++ b/components/cod2rad/hdr.h @@ -5,14 +5,17 @@ extern vec4* LightmapBytes_HDR; extern vec4* Lightmap2Bytes_HDR; +#if USE_LEGACY_HDR extern SampleColorHDR* LightGridSampleColors_HDR; extern DiskSampleColorHDR* DiskLightGridSampleColors_HDR; +#endif void PatchHDR_Lightmaps(); void PatchHDR_Lightgrid(); void R_Init_LightmapsHDR(); -void R_Init_LightgridHDR(); +#if USE_LEGACY_HDR void R_Init_DiskLightgridHDR(); +#endif void __cdecl R_StoreLightmapPixelHDR(vec4* pel1, vec4* pel2, int lightmap, int row, int pel); \ No newline at end of file diff --git a/components/cod2rad/lighting.cpp b/components/cod2rad/lighting.cpp new file mode 100644 index 00000000..3576c7e5 --- /dev/null +++ b/components/cod2rad/lighting.cpp @@ -0,0 +1,146 @@ +#include "stdafx.h" + +double __cdecl GammaCorrect(float color) +{ + long double _color = color; + if (_color > 0.0) + { + return pow(_color, 1.0 / g_Gamma); + } + + return 0.0; +} + +void __cdecl GammaCorrectColor(float *rgb) +{ + rgb[0] = (float)GammaCorrect(rgb[0]); + rgb[1] = (float)GammaCorrect(rgb[1]); + rgb[2] = (float)GammaCorrect(rgb[2]); +} + +void __cdecl GammaCorrectColor(vec3* rgb) +{ + rgb->r = (float)GammaCorrect(rgb->r); + rgb->g = (float)GammaCorrect(rgb->g); + rgb->b = (float)GammaCorrect(rgb->b); +} + +// +// Clamp src to 0.0 - 'max' range, returns true if the value needed to be clamped +// +bool ClampColor(vec3 *dst, vec3 *src, float max) +{ + bool clamped = false; + + if (src->r >= 0.0f) + { + if (src->r <= max) + { + dst->r = src->r; + } + else + { + dst->r = max; + clamped = true; + } + } + else + { + dst->r = 0.0f; + clamped = true; + } + + if (src->g >= 0.0f) + { + if (src->g <= max) + { + dst->g = src->g; + } + else + { + dst->g = max; + clamped = true; + } + } + else + { + dst->g = 0.0f; + clamped = true; + } + + if (src->b >= 0.0f) + { + if (src->b <= max) + { + dst->b = src->b; + } + else + { + dst->b = max; + clamped = true; + } + } + else + { + dst->b = 0.0f; + clamped = true; + } + + return clamped; +} + +vec3 ColorSRGBtoLinear(vec3* color) +{ + vec3 tmp = ((*color + 0.055f) / 1.055f); + tmp.r = pow(color->r, 2.4f); + tmp.g = pow(color->g, 2.4f); + tmp.b = pow(color->b, 2.4f); + + vec3 linear; + linear.r = (color->r > 0.04045f) ? tmp.r : color->r / 12.92f; + linear.g = (color->g > 0.04045f) ? tmp.g : color->g / 12.92f; + linear.b = (color->b > 0.04045f) ? tmp.b : color->b / 12.92f; + + return linear; +} + +void EncodeNormalToFloats(vec3* normal, vec2* out) +{ + out->x = normal->x / normal->z * 0.25f + 0.5f; + out->y = normal->y / normal->z * 0.25f + 0.5f; +} + +void EncodeNormalToBytes(vec3* normal, BYTE(&out)[2]) +{ + out[0] = EncodeFloatInByte(normal->x / normal->z * 0.25f + 0.5f); + out[1] = EncodeFloatInByte(normal->y / normal->z * 0.25f + 0.5f); +} + +void DecodeNormalFromBytes(int packed_x, int packed_y, vec3* out) +{ + /* + if a = b * 0.25 + 0.5 + then b = 4*a-2 + but since we're also converting from byte to float + we need b = (4*a)/255 + 0.5 + which simplifies to the formula below + */ + out->x = (float)packed_x / 63.75f - 2.0f; + out->y = (float)packed_y / 63.75f - 2.0f; + out->z = 1.0f; + + // This only works if the original normal's z axis was > 0.0f (which is how all normals should be) + Vec3Normalize(out); +} + +void DecodeNormalFromFloats(vec2* packed, vec3* out) +{ + /* + if a = b * 0.25 + 0.5 + then b = 4*a-2 + */ + *out = vec3((*packed * 4.0f) - 2.0f, 1.0f); + + // This only works if the original normal's z axis was > 0.0f (which is how all normals should be) + Vec3Normalize(out); +} \ No newline at end of file diff --git a/components/cod2rad/lighting.h b/components/cod2rad/lighting.h new file mode 100644 index 00000000..77e5eb49 --- /dev/null +++ b/components/cod2rad/lighting.h @@ -0,0 +1,25 @@ +#pragma once + +static int& g_basisDirectionsCount = *(int*)0x16E99F70; +static vec3*& g_basisDirections = *(vec3**)0x16E99F74; + +static float& g_Gamma = *(float*)0x153C907C; + +double __cdecl GammaCorrect(float color); +void __cdecl GammaCorrectColor(float *rgb); +void __cdecl GammaCorrectColor(vec3* rgb); + +bool ClampColor(vec3 *dst, vec3 *src, float max = 1.0f); + +vec3 ColorSRGBtoLinear(vec3* color); + +void EncodeNormalToFloats(vec3* normal, vec2* out); +void EncodeNormalToBytes(vec3* normal, BYTE(&out)[2]); + +/* + Generally the incoming packed values are really: + packed_xz = normal.x / normal.z * 0.25 + 0.5 + packed_yz = normal.y / normal.z * 0.25 + 0.5 +*/ +void DecodeNormalFromBytes(int packed_xz, int packed_yz, vec3* out); +void DecodeNormalFromFloats(vec2* packed, vec3* out); diff --git a/components/cod2rad/print.cpp b/components/cod2rad/print.cpp new file mode 100644 index 00000000..a60c4c83 --- /dev/null +++ b/components/cod2rad/print.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" + +static int& g_progressCounter_current = *(int*)0x1730241C; +static int& g_progressCounter_max = *(int*)0x17302420; + +void __cdecl SetProgress(int current, int total) +{ + g_progressCounter_current = current; + g_progressCounter_max = total; + + UpdateProgressPrint(); +} + +void __cdecl UpdateProgress(int amount) +{ + if (g_progressCounter_max) + { + g_progressCounter_current += amount; + UpdateProgressPrint(); + } +} diff --git a/components/cod2rad/print.h b/components/cod2rad/print.h new file mode 100644 index 00000000..d44ee5d3 --- /dev/null +++ b/components/cod2rad/print.h @@ -0,0 +1,16 @@ +#pragma once + +typedef int(__cdecl* Com_FatalError_t)(char* format, ...); +static Com_FatalError_t Com_FatalError = (Com_FatalError_t)0x004294B0; + +typedef void(__cdecl* BeginProgress_t)(const char* msg); +static BeginProgress_t BeginProgress = (BeginProgress_t)0x00429650; + +typedef void(__cdecl* EndProgress_t)(void); +static EndProgress_t EndProgress = (EndProgress_t)0x004294C0; + +typedef void(__cdecl* UpdateProgressPrint_t)(); +static UpdateProgressPrint_t UpdateProgressPrint = (UpdateProgressPrint_t)0x00429500; + +void __cdecl SetProgress(int current, int total); +void __cdecl UpdateProgress(int amount); diff --git a/components/cod2rad/r_image_wavelet.cpp b/components/cod2rad/r_image_wavelet.cpp new file mode 100644 index 00000000..6f66b117 --- /dev/null +++ b/components/cod2rad/r_image_wavelet.cpp @@ -0,0 +1,306 @@ +#include "stdafx.h" +#include "r_imagedecode.h" + +struct WaveletDecode +{ + unsigned __int16 value; + unsigned __int16 bit; + const char *data; + int width; + int height; + int channels; + int bpp; + int mipLevel; + bool dataInitialized; +}; + +struct WaveletHuffmanDecode +{ + __int16 value; + __int16 bits; +}; + +static WaveletHuffmanDecode* waveletDecodeBlue = (WaveletHuffmanDecode*)0x0044E370; +static WaveletHuffmanDecode* waveletDecodeRedGreen = (WaveletHuffmanDecode*)0x00452370; +static WaveletHuffmanDecode* waveletDecodeAlpha = (WaveletHuffmanDecode*)0x00456370; + +static void Wavelet_ConsumeBits(unsigned __int16 bitCount, WaveletDecode *decode) +{ + ASSERT(bitCount > 0 && bitCount <= 16); + ASSERT(decode->bit < 8); + + decode->value >>= bitCount; + + decode->value |= (((BYTE)decode->data[0] << 0) | + ((BYTE)decode->data[1] << 8) | + ((BYTE)decode->data[2] << 16) | + ((BYTE)decode->data[3] << 24)) >> decode->bit << (16 - bitCount); + + decode->bit += bitCount; + decode->data += decode->bit >> 3; + decode->bit &= 7u; +} + +static int Wavelet_DecodeValue(WaveletHuffmanDecode *decodeTable, unsigned __int16 bitCount, int bias, WaveletDecode *decode) +{ + ASSERT((1 << bitCount) - 1 >= bias * 2 - 1); + ASSERT((1 << (bitCount - 1)) - 1 < bias * 2 - 1); + + int index = decode->value & 0xFFF; + Wavelet_ConsumeBits(decodeTable[index].bits, decode); + int value = decodeTable[index].value; + + if (value == 0xFFFF8000) + { + value = (((1 << bitCount) - 1) & decode->value) - bias; + Wavelet_ConsumeBits(bitCount, decode); + } + + return value; +} + +static void Wavelet_AddDeltaToMipmap(char *inout, int size, WaveletDecode *decode, const int *dstChanOffset) +{ + for (int i = 0; i < size; i++) + { + for (int chanIndex = 0; chanIndex < decode->channels; chanIndex++) + { + char* value = &inout[dstChanOffset[chanIndex]]; + char old = *value; + *value = Wavelet_DecodeValue(waveletDecodeAlpha, 9u, 255, decode) + old; + } + + inout += decode->bpp; + } +} + +static void Wavelet_DecompressLevel(char *src, char *dst, WaveletDecode *decode) +{ + ASSERT_MSG_VA(decode->bpp >= 1 && decode->bpp <= 4, "decode->bpp not in [1, 4]\n\t%i not in [%i, %i]", decode->bpp, 1, 4); + + ASSERT(decode->bpp == decode->channels || (decode->bpp == 4 && decode->channels == 3)); + ASSERT(decode->mipLevel >= 0); + + int dstChanOffset[4]; + int dstBpp = decode->bpp; + + switch (dstBpp) + { + case 1: + dstChanOffset[0] = 0; + break; + case 2: + dstChanOffset[0] = 0; + dstChanOffset[1] = 1; + break; + case 3: + case 4: + dstChanOffset[0] = 0; + dstChanOffset[1] = 1; + dstChanOffset[2] = 2; + dstChanOffset[3] = 3; + break; + default: + break; + } + + int w = decode->width >> decode->mipLevel; + int h = decode->height >> decode->mipLevel; + + if (w > 1 && h > 1) + { + if (!decode->dataInitialized) + { + decode->value = (((BYTE)decode->data[1]) << 8) | (BYTE)decode->data[0]; + decode->bit = 0; + decode->data += 2; + decode->dataInitialized = 1; + } + + bool needsMipDelta = (decode->value & 1) != 0; + Wavelet_ConsumeBits(1, decode); + + if (needsMipDelta) + Wavelet_AddDeltaToMipmap(src, h * w / 4, decode, dstChanOffset); + + int stride = dstBpp * w; + + int coeff[4][3]; + for (int y = 0; y < h; y += 2) + { + for (int x = 0; x < w; x += 2) + { + ASSERT(dst + stride + dstBpp <= src || dst > src); + + if (decode->channels != 1) + { + int evenOddParity = decode->value & 1; + Wavelet_ConsumeBits(1, decode); + + coeff[0][0] = Wavelet_DecodeValue(waveletDecodeBlue, 9, 255, decode); + coeff[0][1] = Wavelet_DecodeValue(waveletDecodeBlue, 9, 255, decode); + coeff[0][2] = Wavelet_DecodeValue(waveletDecodeBlue, 9, 255, decode); + + int base = 2 * (BYTE)src[dstChanOffset[0]]; + char* dstChan = &dst[dstChanOffset[0]]; + + dst[dstChanOffset[0]] = evenOddParity + ((coeff[0][2] + coeff[0][1] + coeff[0][0] + base) >> 1); + + dstChan[dstBpp] = (coeff[0][0] + base - (coeff[0][2] + coeff[0][1])) >> 1; + dstChan[stride] = (coeff[0][1] - coeff[0][2] + base - coeff[0][0]) >> 1; + dstChan[dstBpp + stride] = (base - coeff[0][0] - (coeff[0][1] - coeff[0][2])) >> 1; + + if (decode->channels >= 3) + { + evenOddParity = decode->value & 1; + Wavelet_ConsumeBits(1, decode); + + coeff[1][0] = coeff[0][0] + Wavelet_DecodeValue(waveletDecodeRedGreen, 10, 510, decode); + coeff[1][1] = coeff[0][1] + Wavelet_DecodeValue(waveletDecodeRedGreen, 10, 510, decode); + coeff[1][2] = coeff[0][2] + Wavelet_DecodeValue(waveletDecodeRedGreen, 10, 510, decode); + + base = 2 * (BYTE)src[dstChanOffset[1]]; + dstChan = &dst[dstChanOffset[1]]; + + dst[dstChanOffset[1]] = evenOddParity + ((coeff[1][2] + coeff[1][1] + coeff[1][0] + base) >> 1); + + dstChan[dstBpp] = (coeff[1][0] + base - (coeff[1][2] + coeff[1][1])) >> 1; + dstChan[stride] = (coeff[1][1] - coeff[1][2] + base - coeff[1][0]) >> 1; + dstChan[dstBpp + stride] = (base - coeff[1][0] - (coeff[1][1] - coeff[1][2])) >> 1; + + evenOddParity = decode->value & 1; + Wavelet_ConsumeBits(1, decode); + + coeff[2][0] = coeff[0][0] + Wavelet_DecodeValue(waveletDecodeRedGreen, 10, 510, decode); + coeff[2][1] = coeff[0][1] + Wavelet_DecodeValue(waveletDecodeRedGreen, 10, 510, decode); + coeff[2][2] = coeff[0][2] + Wavelet_DecodeValue(waveletDecodeRedGreen, 10, 510, decode); + + base = 2 * (BYTE)src[dstChanOffset[2]]; + dstChan = &dst[dstChanOffset[2]]; + + dst[dstChanOffset[2]] = evenOddParity + ((coeff[2][2] + coeff[2][1] + coeff[2][0] + base) >> 1); + + dstChan[dstBpp] = (coeff[2][0] + base - (coeff[2][2] + coeff[2][1])) >> 1; + dstChan[stride] = (coeff[2][1] - coeff[2][2] + base - coeff[2][0]) >> 1; + dstChan[dstBpp + stride] = (base - coeff[2][0] - (coeff[2][1] - coeff[2][2])) >> 1; + } + } + + if (decode->channels == 3) + { + if (decode->channels != decode->bpp) + { + char* dstChan = &dst[dstChanOffset[3]]; + + dst[dstChanOffset[3]] = -1; + + dstChan[dstBpp] = -1; + dstChan[stride] = -1; + dstChan[dstBpp + stride] = -1; + } + } + else + { + int evenOddParity = decode->value & 1; + Wavelet_ConsumeBits(1, decode); + + coeff[3][0] = Wavelet_DecodeValue(waveletDecodeAlpha, 9, 255, decode); + coeff[3][1] = Wavelet_DecodeValue(waveletDecodeAlpha, 9, 255, decode); + coeff[3][2] = Wavelet_DecodeValue(waveletDecodeAlpha, 9, 255, decode); + + int base = 2 * src[dstChanOffset[decode->channels - 1]]; + char* dstChan = &dst[dstChanOffset[decode->channels - 1]]; + + dstChan[0] = evenOddParity + ((coeff[3][2] + coeff[3][1] + coeff[3][0] + base) >> 1); + + dstChan[dstBpp] = (coeff[3][0] + base - (coeff[3][2] + coeff[3][1])) >> 1; + dstChan[stride] = (coeff[3][1] - coeff[3][2] + base - coeff[3][0]) >> 1; + dstChan[dstBpp + stride] = (base - coeff[3][0] - (coeff[3][1] - coeff[3][2])) >> 1; + } + + src += dstBpp; + dst += 2 * dstBpp; + } + + dst += stride; + } + } + else + { + w = max(w, 1); + h = max(h, 1); + + int size = h * w; + ASSERT(size >= 1); + + for (; size > 0; size--) + { + for (int chanIndex = 0; chanIndex < decode->channels; ++chanIndex) + dst[dstChanOffset[chanIndex]] = *decode->data++; + + if (decode->bpp != decode->channels) + dst[dstChanOffset[3]] = -1; + + dst += dstBpp; + } + } +} + +void __cdecl Image_DecodeWavelet(GfxRawImage *image, t5::GfxImageFileHeader *imageFile, unsigned __int8 *data, int bytesPerPixel) +{ + PBYTE to[6]; + PBYTE from[6]; + + PBYTE pixels[6]; + + ASSERT(image); + ASSERT(imageFile); + + WaveletDecode decode; + decode.width = imageFile->dimensions[0]; + decode.height = imageFile->dimensions[1]; + decode.value = 0; + decode.bit = 0; + + int mipCount = Image_CountMipmapsForFile(imageFile); + int height = imageFile->dimensions[1]; + int width = imageFile->dimensions[0]; + + decode.mipLevel = mipCount - 1; + decode.channels = bytesPerPixel; + decode.bpp = bytesPerPixel; + decode.dataInitialized = 0; + + unsigned int totalSize = bytesPerPixel * width * height; + int faceCount = ((imageFile->flags & t5::IMG_FLAG_CUBEMAP) != 0) ? 6 : 1; + + memset(to, 0, sizeof(PBYTE) * faceCount); + for (int faceIndex = 0; faceIndex < faceCount; faceIndex++) + { + pixels[faceIndex++] = (PBYTE)_Hunk_AllocTempMemoryInternal(totalSize); + } + + unsigned __int8 mipLevel = decode.mipLevel; + for (decode.data = (const char *)data; decode.mipLevel >= 0; mipLevel = --decode.mipLevel) + { + int mipWidth = max(decode.width >> mipLevel, 1); + int mipHeight = max(decode.height >> mipLevel, 1); + + unsigned int mipSize = bytesPerPixel * mipWidth * mipHeight; + + for (int faceIndex = 0; faceIndex < faceCount; faceIndex++) + { + from[faceIndex] = to[faceIndex]; + to[faceIndex] = &pixels[faceIndex][totalSize] - mipSize; + + Wavelet_DecompressLevel((char*)from[faceIndex], (char*)to[faceIndex], &decode); + if (faceIndex == 0 && decode.mipLevel == 0) + Image_CopyBitmapData(to[faceIndex], image, imageFile); + } + } + + for (int i = faceCount - 1; i >= 0; --i) + { + _Hunk_FreeTempMemory(pixels[i]); + } +} diff --git a/components/cod2rad/r_image_wavelet.h b/components/cod2rad/r_image_wavelet.h new file mode 100644 index 00000000..ac76b841 --- /dev/null +++ b/components/cod2rad/r_image_wavelet.h @@ -0,0 +1,4 @@ +#pragma once +#include "r_imagedecode.h" + +void __cdecl Image_DecodeWavelet(GfxRawImage *image, t5::GfxImageFileHeader *imageFile, unsigned __int8 *data, int bytesPerPixel); diff --git a/components/cod2rad/r_imagedecode.cpp b/components/cod2rad/r_imagedecode.cpp new file mode 100644 index 00000000..029c3c38 --- /dev/null +++ b/components/cod2rad/r_imagedecode.cpp @@ -0,0 +1,629 @@ +#include "stdafx.h" +#include "r_image_wavelet.h" + +#define IMAGE_DUMP 0 +#define IMAGE_LOG 0 + +union ddscolor_t +{ + struct + { + unsigned __int16 b : 5; + unsigned __int16 g : 6; + unsigned __int16 r : 5; + }; + + unsigned __int16 rgb; +}; +STATIC_ASSERT_SIZE(ddscolor_t, 2); + +struct DdsBlock_Dxt1_t +{ + ddscolor_t color0; + ddscolor_t color1; + char bits[4]; +}; +STATIC_ASSERT_SIZE(DdsBlock_Dxt1_t, 8); + +struct DdsBlock_Dxt3_t +{ + char alpha[8]; + DdsBlock_Dxt1_t color; +}; +STATIC_ASSERT_SIZE(DdsBlock_Dxt3_t, 16); + +struct DdsBlock_Dxt5_t +{ + char alpha0; + char alpha1; + char alpha[6]; + DdsBlock_Dxt1_t color; +}; +STATIC_ASSERT_SIZE(DdsBlock_Dxt5_t, 16); + +GfxRawPixel::GfxRawPixel(void) +{ +} + +GfxRawPixel::GfxRawPixel(vec3& vec) +{ + this->r = (BYTE)(vec.r * 255.0f); + this->g = (BYTE)(vec.g * 255.0f); + this->b = (BYTE)(vec.b * 255.0f); + this->a = -1; +} + +GfxRawPixel::GfxRawPixel(vec4& vec) +{ + this->r = (BYTE)(vec.r * 255.0f); + this->g = (BYTE)(vec.g * 255.0f); + this->b = (BYTE)(vec.b * 255.0f); + this->a = (BYTE)(vec.a * 255.0f); +} + +bool SaveBitmap(char* filepath, BYTE* pixels, int width, int height, int bytesPerPixel) { + FILE* f = NULL; + fopen_s(&f, filepath, "wb"); + if (f == NULL) + { + Com_Printf("ERROR: Could not save image %s\n", filepath); + return false; + } + + BITMAPV4HEADER info; + ZeroMemory(&info, sizeof(info)); + info.bV4Size = sizeof(BITMAPV4HEADER); + info.bV4Width = width; + info.bV4Height = height; + info.bV4Planes = 1; + info.bV4BitCount = bytesPerPixel * 8; + info.bV4V4Compression = BI_BITFIELDS; + info.bV4SizeImage = width * height * bytesPerPixel; + + info.bV4RedMask = 0x000000FF; + info.bV4GreenMask = 0x0000FF00; + info.bV4BlueMask = 0x00FF0000; + if (bytesPerPixel == 4) + info.bV4AlphaMask = 0xFF000000; + + BITMAPFILEHEADER header; + ZeroMemory(&header, sizeof(header)); + header.bfType = 'B' + ('M' << 8); + header.bfOffBits = sizeof(BITMAPFILEHEADER) + info.bV4Size; + header.bfSize = header.bfOffBits + info.bV4SizeImage; + header.bfReserved1 = 0; + header.bfReserved2 = 0; + + fwrite(&header, 1, sizeof(BITMAPFILEHEADER), f); + fwrite(&info, 1, sizeof(BITMAPV4HEADER), f); + fwrite(pixels, 1, info.bV4SizeImage, f); + fclose(f); + + return true; +} + +void __declspec(naked) hk_Image_GetRawPixels(void) +{ + _asm + { + push[esp+4] // imageName + push esi // image + call Image_GetRawPixels + add esp, 8 + retn + } +} + +t4::GfxImageFileHeader::GfxImageFileHeader(t5::GfxImageFileHeader& header) +{ + tag[0] = header.tag[0]; + tag[1] = header.tag[1]; + tag[3] = header.tag[2]; + + version = header.version; + format = header.format; + flags = header.flags; + + dimensions[0] = header.dimensions[0]; + dimensions[1] = header.dimensions[1]; + dimensions[2] = header.dimensions[2]; + + fileSizeForPicmip[0] = header.fileSizeForPicmip[0]; + fileSizeForPicmip[1] = header.fileSizeForPicmip[1]; + fileSizeForPicmip[2] = header.fileSizeForPicmip[2]; + fileSizeForPicmip[3] = header.fileSizeForPicmip[3]; +} + +unsigned int Image_CountMipmaps(unsigned int imageFlags, unsigned int width, unsigned int height, unsigned int depth) +{ + if (imageFlags & 2) + return 1; + + unsigned int mipCount = 1; + unsigned int mipRes = 1; + + while (mipRes < width || mipRes < height || mipRes < depth) + { + mipRes *= 2; + ++mipCount; + } + + return mipCount; +} + +int Image_CountMipmapsForFile(t5::GfxImageFileHeader *fileHeader) +{ + return Image_CountMipmaps( + fileHeader->flags, + fileHeader->dimensions[0], + fileHeader->dimensions[1], + fileHeader->dimensions[2]); +} + +bool __cdecl Image_ValidateHeader(t5::GfxImageFileHeader *imageFile, const char *filepath) +{ + if (imageFile->tag[0] == 'I' && imageFile->tag[1] == 'W' && imageFile->tag[2] == 'i') + { + if (imageFile->version == 13) + return true; + + Com_Printf("ERROR: image '%s' is version %i but should be version %i\n", filepath, imageFile->version, 13); + return false; + } + + Com_Printf("ERROR: image '%s' is not an IW image\n", filepath); + return false; +} + +void __cdecl Image_GetRawPixels(GfxRawImage *image, const char *imageName) +{ + ASSERT(imageName != NULL); + + char path[MAX_PATH]; + Com_AssembleImageFilepath(imageName, path); + + t5::GfxImageFileHeader* header = NULL; + if (FS_ReadFile(path, (void**)&header) < 0) + Com_Error("image '%s' is missing", path); + + if (!Image_ValidateHeader(header, path)) + Com_Error("image '%s' is not valid", path); + + strcpy_s(image->name, imageName); + + int width = header->dimensions[0]; + image->width = width; + int height = header->dimensions[1]; + image->height = height; + image->pixels = (GfxRawPixel *)Z_Malloc(4 * height * width); + + BYTE* pixels = (PBYTE)&header[1]; + + t4::GfxImageFileHeader dummyHeader(*header); + +#if IMAGE_DUMP || IMAGE_LOG + sprintf_s(path, "dump/%s.bmp", imageName); + Com_Printf("Loading image %dx%d (%d fmt) %s\n", width, height, header->format, path); +#endif + + switch (header->format) + { + case IMG_FORMAT_BITMAP_RGBA: + image->hasAlpha = 1; + Image_DecodeBitmap(image, header, pixels, 4); + break; + case IMG_FORMAT_BITMAP_RGB: + image->hasAlpha = 0; + Image_DecodeBitmap(image, header, pixels, 3); + break; + case IMG_FORMAT_BITMAP_LUMINANCE: + image->hasAlpha = 0; + Image_DecodeBitmap(image, header, pixels, 1); + break; + case IMG_FORMAT_BITMAP_ALPHA: + image->hasAlpha = 1; + Image_DecodeBitmap(image, header, pixels, 1); + break; + case IMG_FORMAT_WAVELET_RGBA: + image->hasAlpha = 1; + Image_DecodeWavelet(image, header, pixels, 4); + break; + case IMG_FORMAT_WAVELET_RGB: + image->hasAlpha = 0; + Image_DecodeWavelet(image, header, pixels, 3); + break; + case IMG_FORMAT_WAVELET_LUMINANCE_ALPHA: + image->hasAlpha = 1; + Image_DecodeWavelet(image, header, pixels, 2); + break; + case IMG_FORMAT_WAVELET_LUMINANCE: + image->hasAlpha = 0; + Image_DecodeWavelet(image, header, pixels, 1); + break; + case IMG_FORMAT_WAVELET_ALPHA: + image->hasAlpha = 1; + Image_DecodeWavelet(image, header, pixels, 1); + break; + case IMG_FORMAT_DXT1: + image->hasAlpha = 0; + Image_DecodeDxtc(image, header, pixels, 8); + break; + case IMG_FORMAT_DXT3: + case IMG_FORMAT_DXT5: + image->hasAlpha = 1; + Image_DecodeDxtc(image, header, pixels, 16); + break; + case IMG_FORMAT_BITMAP_RGB565: + image->hasAlpha = 0; + Image_DecodeBitmap(image, header, pixels, 2); + break; + case IMG_FORMAT_BITMAP_LUMINANCE_ALPHA: + case IMG_FORMAT_BITMAP_RGB5A3: + image->hasAlpha = 1; + Image_DecodeBitmap(image, header, pixels, 2); + break; + default: + Con_Error("Unhandled image type: %d (%s)\n", header->format, imageName); + break; + } + +#if IMAGE_DUMP + SaveBitmap(path, (BYTE*)image->pixels, image->width, image->height, 4); +#endif + FS_FreeFile(header); +} + +void Image_CopyBitmapData(BYTE *pixels, GfxRawImage *image, t5::GfxImageFileHeader *imageFile) +{ + PBYTE src = pixels; + GfxRawPixel* dst = image->pixels; + + int i = imageFile->dimensions[0] * imageFile->dimensions[1]; + + switch (imageFile->format) + { + case IMG_FORMAT_BITMAP_RGBA: + case IMG_FORMAT_WAVELET_RGBA: + for (; i; --i) + { + dst->r = src[2]; + dst->g = src[1]; + dst->b = src[0]; + dst->a = src[3]; + src += 4; + ++dst; + } + break; + case IMG_FORMAT_BITMAP_RGB: + case IMG_FORMAT_WAVELET_RGB: + for (; i; --i) + { + dst->r = src[2]; + dst->g = src[1]; + dst->b = src[0]; + dst->a = -1; + src += 3; + ++dst; + } + break; + case IMG_FORMAT_BITMAP_LUMINANCE_ALPHA: + case IMG_FORMAT_WAVELET_LUMINANCE_ALPHA: + for (; i; --i) + { + dst->r = src[0]; + dst->g = src[0]; + dst->b = src[0]; + dst->a = src[1]; + src += 2; + ++dst; + } + break; + case IMG_FORMAT_BITMAP_LUMINANCE: + case IMG_FORMAT_WAVELET_LUMINANCE: + for (; i; --i) + { + dst->r = src[0]; + dst->g = src[0]; + dst->b = src[0]; + dst->a = -1; + ++src; + ++dst; + } + break; + case IMG_FORMAT_BITMAP_ALPHA: + case IMG_FORMAT_WAVELET_ALPHA: + for (; i; --i) + { + dst->r = 0; + dst->g = 0; + dst->b = 0; + dst->a = *src++; + ++dst; + } + break; + case IMG_FORMAT_BITMAP_RGB565: + for (; i; --i) + { + dst->r = (src[0] >> 5) | src[0] & 0xF8; + dst->g = 32 * src[0] | ((BYTE)(src[0] & 6 | (src[1] >> 2) & 0x38) >> 1); + dst->b = 8 * src[1] | (src[1] >> 2) & 7; + dst->a = 0; + ++dst; + } + break; + case IMG_FORMAT_BITMAP_RGB5A3: + for (; i; --i) + { + BYTE r, g, b1, b2, a, v11, v12; + if ((*src & 0x80u) == 0) + { + v11 = 16 * src[0]; + v12 = src[1]; + a = ((BYTE)(2 * (src[0] & 0xF0) | ((BYTE)(2 * (src[0] & 0xF0)) >> 3)) >> 3) | 2 * (src[0] & 0xF0); + r = (v11 >> 4) | v11; + g = ((BYTE)(v12 & 0xF0) >> 4) | v12 & 0xF0; + b1 = 16 * v12; + b2 = b1 >> 4; + } + else + { + BYTE _b = src[1]; + r = ((BYTE)(2 * (src[0] & 0xFC)) >> 5) | 2 * (src[0] & 0xFC); + g = ((BYTE)((src[0] << 6) | (_b >> 2) & 0x38) >> 5) | (src[0] << 6) | (_b >> 2) & 0x38; + b1 = 8 * _b; + a = -1; + b2 = (BYTE)(8 * _b) >> 5; + } + + dst->g = g; + dst->r = r; + dst->b = b2 | b1; + dst->a = a; + ++dst; + } + break; + default: + ASSERT_MSG(false, "unhandled case"); + break; + } +} + +void __cdecl Image_DecodeBitmap(struct GfxRawImage *image, struct t5::GfxImageFileHeader *imageFile, BYTE *data, int bytesPerPixel) +{ + ASSERT(image); + ASSERT(imageFile); + + int faceCount = (imageFile->flags & t5::IMG_FLAG_CUBEMAP) != 0 ? 6 : 1; + for (int mipLevel = Image_CountMipmapsForFile(imageFile) - 1; mipLevel >= 0; mipLevel--) + { + int width = max(imageFile->dimensions[0] >> mipLevel, 1); + int height = max(imageFile->dimensions[1] >> mipLevel, 1); + + int mipSize = bytesPerPixel * width * height; + + for (int faceIndex = 0; faceIndex < faceCount; faceIndex++) + { + if (faceIndex == 0 && mipLevel == 0) + Image_CopyBitmapData(data, image, imageFile); + + data += mipSize; + } + } +} + +inline vec3 Pixel_Unpack_RGB565(unsigned __int16 packed) +{ + vec3 dst; + dst.r = (packed >> 11) / 32.0f; + dst.g = ((packed >> 5) & 0x3F) / 64.0f; + dst.b = (packed & 0x1F) / 32.0f; + return dst; +} + +void Image_DecompressDxt1_Internal(GfxRawImage *image, DdsBlock_Dxt1_t *data, int x, int y, bool hasAlpha) +{ + vec3 color0 = Pixel_Unpack_RGB565(data->color0.rgb); + vec3 color1 = Pixel_Unpack_RGB565(data->color1.rgb); + + GfxRawPixel rgba[4]; + + if (hasAlpha || data->color0.rgb > data->color1.rgb) + { + rgba[0] = color0; + rgba[1] = color1; + + rgba[2] = (color1 + (color0 * 2.0f)) / 3.0f; + rgba[3] = (color0 + (color1 * 2.0f)) / 3.0f; + } + else + { + rgba[0] = color0; + rgba[1] = color1; + + rgba[2] = (color0 + color1) / 2.0f; + + rgba[3].r = 0; + rgba[3].g = 0; + rgba[3].b = 0; + rgba[3].a = 0; + } + + image->Pixel(x + 0, y + 0) = rgba[(data->bits[0] >> 0) & 3]; + image->Pixel(x + 1, y + 0) = rgba[(data->bits[0] >> 2) & 3]; + image->Pixel(x + 2, y + 0) = rgba[(data->bits[0] >> 4) & 3]; + image->Pixel(x + 3, y + 0) = rgba[(data->bits[0] >> 6) & 3]; + + image->Pixel(x + 0, y + 1) = rgba[(data->bits[1] >> 0) & 3]; + image->Pixel(x + 1, y + 1) = rgba[(data->bits[1] >> 2) & 3]; + image->Pixel(x + 2, y + 1) = rgba[(data->bits[1] >> 4) & 3]; + image->Pixel(x + 3, y + 1) = rgba[(data->bits[1] >> 6) & 3]; + + image->Pixel(x + 0, y + 2) = rgba[(data->bits[2] >> 0) & 3]; + image->Pixel(x + 1, y + 2) = rgba[(data->bits[2] >> 2) & 3]; + image->Pixel(x + 2, y + 2) = rgba[(data->bits[2] >> 4) & 3]; + image->Pixel(x + 3, y + 2) = rgba[(data->bits[2] >> 6) & 3]; + + image->Pixel(x + 0, y + 3) = rgba[(data->bits[3] >> 0) & 3]; + image->Pixel(x + 1, y + 3) = rgba[(data->bits[3] >> 2) & 3]; + image->Pixel(x + 2, y + 3) = rgba[(data->bits[3] >> 4) & 3]; + image->Pixel(x + 3, y + 3) = rgba[(data->bits[3] >> 6) & 3]; +} + +void Image_DecompressDxt1(DdsBlock_Dxt1_t* data, GfxRawImage *image, int x, int y) +{ + Image_DecompressDxt1_Internal(image, data, x, y, false); +} + +void Image_DecompressDxt3(DdsBlock_Dxt3_t* data, GfxRawImage *image, int x, int y) +{ + Image_DecompressDxt1_Internal(image, &data->color, x, y, true); + + image->Pixel(x + 0, y + 0).a = 17 * (data->alpha[0] & 0xF); + image->Pixel(x + 1, y + 0).a = 17 * (data->alpha[0] >> 4); + image->Pixel(x + 2, y + 0).a = 17 * (data->alpha[1] & 0xF); + image->Pixel(x + 3, y + 0).a = 17 * (data->alpha[1] >> 4); + + image->Pixel(x + 0, y + 1).a = 17 * (data->alpha[2] & 0xF); + image->Pixel(x + 1, y + 1).a = 17 * (data->alpha[2] >> 4); + image->Pixel(x + 2, y + 1).a = 17 * (data->alpha[3] & 0xF); + image->Pixel(x + 3, y + 1).a = 17 * (data->alpha[3] >> 4); + + image->Pixel(x + 0, y + 2).a = 17 * (data->alpha[4] & 0xF); + image->Pixel(x + 1, y + 2).a = 17 * (data->alpha[4] >> 4); + image->Pixel(x + 2, y + 2).a = 17 * (data->alpha[5] & 0xF); + image->Pixel(x + 3, y + 2).a = 17 * (data->alpha[5] >> 4); + + image->Pixel(x + 0, y + 3).a = 17 * (data->alpha[6] & 0xF); + image->Pixel(x + 1, y + 3).a = 17 * (data->alpha[6] >> 4); + image->Pixel(x + 2, y + 3).a = 17 * (data->alpha[7] & 0xF); + image->Pixel(x + 3, y + 3).a = 17 * (data->alpha[7] >> 4); +} + +void __cdecl Image_DecompressDxt5(DdsBlock_Dxt5_t *data, GfxRawImage *image, int x, int y) +{ + Image_DecompressDxt1_Internal(image, &data->color, x, y, true); + + BYTE alpha[8]; + alpha[0] = data->alpha0; + alpha[1] = data->alpha1; + + int alpha0 = (BYTE)data->alpha0; + int alpha1 = (BYTE)data->alpha1; + + // Calculate the remaining alpha values + if (alpha[0] > alpha[1]) + { + alpha[2] = (BYTE)((6 * alpha0 + 1 * alpha1) / 7); + alpha[3] = (BYTE)((5 * alpha0 + 2 * alpha1) / 7); + alpha[4] = (BYTE)((4 * alpha0 + 3 * alpha1) / 7); + alpha[5] = (BYTE)((3 * alpha0 + 4 * alpha1) / 7); + alpha[6] = (BYTE)((2 * alpha0 + 5 * alpha1) / 7); + alpha[7] = (BYTE)((1 * alpha0 + 6 * alpha1) / 7); + } + else + { + alpha[2] = (BYTE)((4 * alpha0 + 1 * alpha1) / 5); + alpha[3] = (BYTE)((3 * alpha0 + 2 * alpha1) / 5); + alpha[4] = (BYTE)((2 * alpha0 + 3 * alpha1) / 5); + alpha[5] = (BYTE)((1 * alpha0 + 4 * alpha1) / 5); + alpha[6] = 0; + alpha[7] = 255; + } + + // Calculate the alpha lookup indices + BYTE indices[16]; + for (int i = 0; i < 2; i++) + { + int value = 0; + // Load 3 bytes into 'value' + for (int j = 0; j < 3; j++) + value |= ((BYTE)data->alpha[i * 3 + j] << 8 * j); + + // Unpack 8 3-bit indices from the loaded bytes + for (int j = 0; j < 8; j++) + indices[i * 8 + j] = (BYTE)((value >> 3 * j) & 7); + } + + // Apply the alpha values to their respective pixels + image->Pixel(x + 0, y + 0).a = alpha[indices[0]]; + image->Pixel(x + 1, y + 0).a = alpha[indices[1]]; + image->Pixel(x + 2, y + 0).a = alpha[indices[2]]; + image->Pixel(x + 3, y + 0).a = alpha[indices[3]]; + + image->Pixel(x + 0, y + 1).a = alpha[indices[4]]; + image->Pixel(x + 1, y + 1).a = alpha[indices[5]]; + image->Pixel(x + 2, y + 1).a = alpha[indices[6]]; + image->Pixel(x + 3, y + 1).a = alpha[indices[7]]; + + image->Pixel(x + 0, y + 2).a = alpha[indices[8]]; + image->Pixel(x + 1, y + 2).a = alpha[indices[9]]; + image->Pixel(x + 2, y + 2).a = alpha[indices[10]]; + image->Pixel(x + 3, y + 2).a = alpha[indices[11]]; + + image->Pixel(x + 0, y + 3).a = alpha[indices[12]]; + image->Pixel(x + 1, y + 3).a = alpha[indices[13]]; + image->Pixel(x + 2, y + 3).a = alpha[indices[14]]; + image->Pixel(x + 3, y + 3).a = alpha[indices[15]]; +} + +void Image_CopyDxtcData(BYTE *data, GfxRawImage *image, t5::GfxImageFileHeader *imageFile) +{ + typedef void(*Image_DecompressDxtcBlock_t)(void* data, GfxRawImage *image, int x, int y); + Image_DecompressDxtcBlock_t Image_DecompressDxtcBlock = NULL; + int bytesPerPixel = 1; + + switch (imageFile->format) + { + case IMG_FORMAT_DXT1: + Image_DecompressDxtcBlock = (Image_DecompressDxtcBlock_t)Image_DecompressDxt1; + bytesPerPixel = 8; + break; + case IMG_FORMAT_DXT3: + Image_DecompressDxtcBlock = (Image_DecompressDxtcBlock_t)Image_DecompressDxt3; + bytesPerPixel = 16; + break; + case IMG_FORMAT_DXT5: + Image_DecompressDxtcBlock = (Image_DecompressDxtcBlock_t)Image_DecompressDxt5; + bytesPerPixel = 16; + break; + default: + Com_Error("unhandled case - unsupported image format for file '%s'", image->name); + return; + } + + // Size of DXT compressed block in pixels + const int DXT_BLOCK_WIDTH = 4; + const int DXT_BLOCK_HEIGHT = 4; + + for (int y = 0; y < image->height; y += DXT_BLOCK_WIDTH) + { + for (int x = 0; x < image->width; x += DXT_BLOCK_HEIGHT) + { + Image_DecompressDxtcBlock(data, image, x, y); + data += bytesPerPixel; + } + } +} + +void __cdecl Image_DecodeDxtc(struct GfxRawImage *image, struct t5::GfxImageFileHeader *imageFile, BYTE *data, int bytesPerPixel) +{ + ASSERT(image); + ASSERT(imageFile); + + int faceCount = (imageFile->flags & t5::IMG_FLAG_CUBEMAP) != 0 ? 6 : 1; + for (int mipLevel = Image_CountMipmapsForFile(imageFile) - 1; mipLevel >= 0; mipLevel--) + { + int width = max(imageFile->dimensions[0] >> mipLevel, 1); + int height = max(imageFile->dimensions[1] >> mipLevel, 1); + + int mipSize = bytesPerPixel * ((width + 3) >> 2) * ((height + 3) >> 2); + + for (int faceIndex = 0; faceIndex < faceCount; faceIndex++) + { + if (faceIndex == 0 && mipLevel == 0) + Image_CopyDxtcData(data, image, imageFile); + + data += mipSize; + } + } +} \ No newline at end of file diff --git a/components/cod2rad/r_imagedecode.h b/components/cod2rad/r_imagedecode.h new file mode 100644 index 00000000..d194b5bc --- /dev/null +++ b/components/cod2rad/r_imagedecode.h @@ -0,0 +1,159 @@ +#pragma once + +union GfxTexture +{ + int *ptr; +}; + +struct Picmip +{ + char platform[2]; +}; + +struct CardMemory +{ + int platform[2]; +}; + +struct GfxImage +{ + int mapType; + GfxTexture texture; + Picmip picmip; + bool noPicmip; + char semantic; + char track; + char unk1[3]; + CardMemory cardMemory; + unsigned __int16 width; + unsigned __int16 height; + unsigned __int16 depth; + char category; + char unk2[1]; + const char *name; +}; + +enum GfxRefBlendMode : DWORD +{ + BLENDMODE_OPAQUE = 0x0, + BLENDMODE_BLEND = 0x1, + BLENDMODE_GT0 = 0x2, + BLENDMODE_GE128 = 0x3, + BLENDMODE_LT128 = 0x4, + BLENDMODE_ADD = 0x5, +}; + +struct GfxRawPixel +{ + char r; + char g; + char b; + char a; + + GfxRawPixel(void); + GfxRawPixel(vec3& vec); + GfxRawPixel(vec4& vec); +}; + +struct GfxRawImage +{ + char name[64]; + GfxRefBlendMode blendMode; + bool hasAlpha; + int width; + int height; + GfxRawPixel *pixels; + + inline GfxRawPixel& Pixel(int x, int y) + { + return this->pixels[y * this->width + x]; + } +}; +STATIC_ASSERT_SIZE(GfxRawImage, 0x54); + +namespace t5 +{ + // Black Ops image file header + struct GfxImageFileHeader + { + char tag[3]; + char version; + char format; + char flags; + __int16 dimensions[3]; + float gamma; + int fileSizeForPicmip[8]; + }; + + enum file_image_flags_t + { + IMG_FLAG_NOPICMIP = 0x1, + IMG_FLAG_NOMIPMAPS = 0x2, + IMG_FLAG_CUBEMAP = 0x4, + IMG_FLAG_VOLMAP = 0x8, + IMG_FLAG_STREAMING = 0x10, + IMG_FLAG_LEGACY_NORMALS = 0x20, + IMG_FLAG_CLAMP_U = 0x40, + IMG_FLAG_CLAMP_V = 0x80, + IMG_FLAG_FORCE_SYSTEM = 0x100, + IMG_FLAG_DYNAMIC = 0x10000, + IMG_FLAG_RENDER_TARGET = 0x20000, + IMG_FLAG_SYSTEMMEM = 0x40000, + }; + +} + +namespace t4 +{ + // World at War image file header + struct GfxImageFileHeader + { + char tag[3]; + char version; + char format; + char flags; + __int16 dimensions[3]; + int fileSizeForPicmip[4]; + + GfxImageFileHeader(t5::GfxImageFileHeader& header); + }; +} + +enum GfxImageFileFormat +{ + IMG_FORMAT_INVALID = 0x0, + IMG_FORMAT_BITMAP_RGBA = 0x1, + IMG_FORMAT_BITMAP_RGB = 0x2, + IMG_FORMAT_BITMAP_LUMINANCE_ALPHA = 0x3, + IMG_FORMAT_BITMAP_LUMINANCE = 0x4, + IMG_FORMAT_BITMAP_ALPHA = 0x5, + IMG_FORMAT_WAVELET_RGBA = 0x6, + IMG_FORMAT_WAVELET_RGB = 0x7, + IMG_FORMAT_WAVELET_LUMINANCE_ALPHA = 0x8, + IMG_FORMAT_WAVELET_LUMINANCE = 0x9, + IMG_FORMAT_WAVELET_ALPHA = 0xA, + IMG_FORMAT_DXT1 = 0xB, + IMG_FORMAT_DXT3 = 0xC, + IMG_FORMAT_DXT5 = 0xD, + IMG_FORMAT_DXN = 0xE, + IMG_FORMAT_BITMAP_RGB565 = 0xF, + IMG_FORMAT_BITMAP_RGB5A3 = 0x10, + IMG_FORMAT_BITMAP_C8 = 0x11, + IMG_FORMAT_BITMAP_RGBA8 = 0x12, + IMG_FORMAT_A16B16G16R16F = 0x13, + IMG_FORMAT_COUNT = 0x14, +}; + +typedef void (__cdecl* Image_LoadPixels_t)(struct GfxRawImage * image, struct t4::GfxImageFileHeader * header, unsigned __int8 * pixels, int bitsPerPixel); +//static Image_LoadPixels_t Image_DecodeBitmap = (Image_LoadPixels_t)0x004176B0; +//static Image_LoadPixels_t Image_DecodeWavelet = (Image_LoadPixels_t)0x004178A0; +//static Image_LoadPixels_t Image_DecodeDxtc = (Image_LoadPixels_t)0x004177F0; + +int Image_CountMipmapsForFile(t5::GfxImageFileHeader *fileHeader); + +void hk_Image_GetRawPixels(void); +void __cdecl Image_GetRawPixels(GfxRawImage *image, const char *imageName); + +void Image_CopyBitmapData(BYTE *data, GfxRawImage *image, t5::GfxImageFileHeader *imageFile); +void __cdecl Image_DecodeBitmap(struct GfxRawImage *image, struct t5::GfxImageFileHeader *imageFile, BYTE *data, int bytesPerPixel); +void __cdecl Image_DecodeDxtc(struct GfxRawImage *image, struct t5::GfxImageFileHeader *imageFile, BYTE *data, int bytesPerPixel); \ No newline at end of file diff --git a/components/cod2rad/r_light_load_obj.cpp b/components/cod2rad/r_light_load_obj.cpp new file mode 100644 index 00000000..ad9719d9 --- /dev/null +++ b/components/cod2rad/r_light_load_obj.cpp @@ -0,0 +1,83 @@ +#include "stdafx.h" + +#define LIGHTDEF_DEFAULT "light_dynamic" + +void GetDegammadImage(GfxRawImage* image, GfxLightImage* lightImage) +{ + _asm + { + pushad + push lightImage + mov edi, image + mov ebx, 0x0042AAE0 + call ebx + add esp, 4 + popad + } +} + +GfxLightDef *__cdecl RegisterLightDef(char *lightDefName) +{ + for (int i = 0; i < pointLightGlob.defCount; i++) + { + GfxLightDef* def = &pointLightGlob.lightDefs[i]; + if (_stricmp(def->name, lightDefName) == 0) + { + return def; + } + } + + if (pointLightGlob.defCount >= 64) + { + Com_FatalError("More than %i lightDefs used by all lights combined; can't load '%s'\n", 64, lightDefName); + return NULL; + } + + GfxLightDef* def = &pointLightGlob.lightDefs[pointLightGlob.defCount]; + + char lightDefPath[MAX_PATH]; + sprintf_s(lightDefPath, "lights/%s", lightDefName); + + const char* file = NULL; + int fileSize = FS_ReadFile(lightDefPath, (void**)&file); + + // A lightdef file needs both the samplerState byte plus a null terminated string for the def image name (meaning it must be >= 2 bytes) + if (fileSize < 3) + { + Com_FatalError("Couldn't get light def images for '%s'\n", lightDefName); + return NULL; + } + + GfxRawImage imageHeader; + if (strlen(file + 1) == 0) + { + memset(&imageHeader, 0, sizeof(GfxRawImage)); + FS_FreeFile((void*)file); + } + else + { + Image_GetRawPixels(&imageHeader, file + 1); + FS_FreeFile((void*)file); + } + + if (imageHeader.height != 1) + { + if (_stricmp(lightDefName, LIGHTDEF_DEFAULT) == 0) + { + Com_FatalError("Falloff image %s in light def %s has dimensions %ix%i; height should be 1\n", imageHeader.name, lightDefName, imageHeader.width, imageHeader.height); + return NULL; + } + + printf("Falloff image %s in light def %s has dimensions %ix%i; height should be 1; Overriding with default light def %s", + imageHeader.name, lightDefName, imageHeader.width, imageHeader.height, LIGHTDEF_DEFAULT); + return RegisterLightDef(LIGHTDEF_DEFAULT); + } + + def->name = _strdup(lightDefName); + GetDegammadImage(&imageHeader, &def->attenuation); + + ++pointLightGlob.defCount; + printf("Registered lightDef '%s'\n", lightDefName); + + return def; +} diff --git a/components/cod2rad/r_light_load_obj.h b/components/cod2rad/r_light_load_obj.h new file mode 100644 index 00000000..340ee215 --- /dev/null +++ b/components/cod2rad/r_light_load_obj.h @@ -0,0 +1,26 @@ +#pragma once + +#include "r_imagedecode.h" + +struct __declspec(align(4)) GfxLightImage +{ + GfxImage *image; + char samplerState; +}; + +struct GfxLightDef +{ + const char *name; + GfxLightImage attenuation; + int lmapLookupStart; +}; + +struct LightGlobals +{ + int defCount; + GfxLightDef lightDefs[64]; +}; + +static LightGlobals& pointLightGlob = *(LightGlobals*)0x172DE010; + +GfxLightDef *__cdecl RegisterLightDef(char *lightDefName); diff --git a/components/cod2rad/r_lightgrid.cpp b/components/cod2rad/r_lightgrid.cpp index 7c5610bf..63ec687e 100644 --- a/components/cod2rad/r_lightgrid.cpp +++ b/components/cod2rad/r_lightgrid.cpp @@ -1,5 +1,7 @@ #include "stdafx.h" +#if USE_LEGACY_HDR + R_Init_Lightgrid_t o_R_Init_Lightgrid = (R_Init_Lightgrid_t)0x00433440; R_Store_QuantizedLightGridSample_t o_R_Store_QuantizedLightGridSample = (R_Store_QuantizedLightGridSample_t)0x00433890; @@ -11,7 +13,11 @@ struct GfxLightGridColor_Internal float f0; float f1; }; +#endif + +GfxLightGridColorsHDR disk_lightGridColorsHDR[0xFFFF]; +#if USE_LEGACY_HDR void* rtn_R_Init_Lightgrid = (void*)0x0043684C; void __declspec(naked) mfh_R_Init_Lightgrid() { @@ -147,4 +153,460 @@ void __declspec(naked) hk_R_Store_QuantizedLightGridSample() call o_R_Store_QuantizedLightGridSample retn } -} \ No newline at end of file +} + +#endif + +void __cdecl AdjustLightingContrast(int sampleCount, int baseIndex, vec3* colors) +{ + _asm + { + push colors + push baseIndex + mov edi, sampleCount + mov ebx, 0x00430410 + call ebx + add esp, 8 + } +} + +// 0x00434E20 +void __cdecl StoreLightingForDir(vec3* lighting, GfxLightGridColorsHDR* dst) +{ + for (int i = 0; i < 56; i++) + { + GammaCorrectColor(&lighting[i]); + } + + AdjustLightingContrast(56, -1, lighting); + + for (int sampleIndex = 0; sampleIndex < 56; sampleIndex++) + { + vec3 v = lighting[sampleIndex] * 0.5; + dst->rgb[sampleIndex][0] = EncodeFloatInByte(v.r); + dst->rgb[sampleIndex][1] = EncodeFloatInByte(v.g); + dst->rgb[sampleIndex][2] = EncodeFloatInByte(v.b); + } +} + +void CalculateClusterMean_o(GridColorsCluster *cluster, float *means) +{ + static DWORD dwCall = 0x004331A0; + + __asm + { + mov ecx, cluster + mov eax, means + call dwCall + } +} + +void CalculateClusterMean(GridColorsCluster *cluster, float *means) +{ + float sums[168]; + memset(sums, 0, sizeof(float) * 168); + + for (unsigned int clusterIndex = 0; clusterIndex < cluster->count; clusterIndex++) + { + GfxLightGridColorsHDR* colors = &lightGridGlob->colors[lightGridGlob->mapping[cluster->first + clusterIndex]]; + for (int i = 0; i < 168; i++) + { + sums[i] += colors->all[i]; + } + } + + for (int i = 0; i < 168; i++) + { + means[i] = sums[i] / cluster->count; + } +} + +void CalculateClusterMeanAndVariance_o(GridColorsCluster *cluster) +{ + ASSERT(cluster); + ASSERT(cluster->count); + + static DWORD dwCall = 0x00434320; + + __asm + { + mov edi, cluster + call dwCall + } +} + +void CalculateClusterMeanAndVariance(GridColorsCluster *cluster) +{ + ASSERT(cluster); + ASSERT(cluster->count); + + float means[168]; + CalculateClusterMean(cluster, means); + + float totals[168]; + memset(totals, 0, sizeof(float) * 168); + + for (unsigned int clusterIndex = 0; clusterIndex < cluster->count; clusterIndex++) + { + GfxLightGridColorsHDR* colors = &lightGridGlob->colors[lightGridGlob->mapping[cluster->first + clusterIndex]]; + for (int i = 0; i < 168; i++) + { + double v = colors->all[i] - means[i]; + totals[i] += (float)(v * v); + } + } + + cluster->unknown3 = -FLT_MAX; + for (int i = 0; i < 168; i++) + { + float unk = sqrt(totals[i] / cluster->count); + if (cluster->unknown3 < unk) + { + cluster->unknown3 = unk; + cluster->unknown1 = i; + cluster->unknown2 = means[i]; + } + } +} + +GridColorsCluster *ChooseClusterToSplit() +{ + static DWORD dwCall = 0x00433090; + + __asm + { + call dwCall + } +} + +void SplitCluster_o(GridColorsCluster *cluster) +{ + ASSERT(cluster); + ASSERT(cluster->count >= 2); + + static DWORD dwCall = 0x00434B50; + + __asm + { + mov esi, cluster + call dwCall + } +} + +void SplitCluster(GridColorsCluster *cluster) +{ + unsigned int head = 0; + unsigned int tail = 0; + + unsigned int* mapping = lightGridGlob->mapping; + + ASSERT(cluster); + ASSERT(cluster->count >= 2); + + if (cluster->unknown3 <= 0.0) + { + head = cluster->first + ((cluster->count + 1) >> 1); + tail = head - 1; + } + else + { + head = cluster->first; + tail = cluster->first + cluster->count - 1; + + while (1) + { + if (lightGridGlob->colors[mapping[head]].all[cluster->unknown1] <= cluster->unknown2) + break; + + LABEL_10: + if (lightGridGlob->colors[mapping[tail]].all[cluster->unknown1] >= cluster->unknown2) + { + while (head <= --tail) + { + if (lightGridGlob->colors[mapping[tail]].all[cluster->unknown1] < cluster->unknown2) + goto LABEL_13; + } + + goto LABEL_18; + } + + LABEL_13: + ASSERT(head < tail); + + int oldHead = mapping[head]; + mapping[head] = mapping[tail]; + mapping[tail] = oldHead; + + if (++head > --tail) + goto LABEL_18; + } + + while (++head <= tail) + { + if (lightGridGlob->colors[mapping[head]].all[cluster->unknown1] > cluster->unknown2) + goto LABEL_10; + } + } + +LABEL_18: + ASSERT(head == tail + 1); + ASSERT(head != 0); + ASSERT(head != cluster->first + cluster->count); + + GridColorsCluster* newCluster = &lightGridGlob->clusters[lightGridGlob->clusterCount++]; + newCluster->first = head; + newCluster->count = cluster->first + cluster->count - head; + + cluster->count = head - cluster->first; + + CalculateClusterMeanAndVariance(newCluster); + CalculateClusterMeanAndVariance(cluster); +} + +void __cdecl SwapClusters(int fromIndex, int toIndex) +{ + // Vanilla: std::swap(disk_lightGridColors[toIndex], disk_lightGridColors[fromIndex]); + std::swap(disk_lightGridColorsHDR[toIndex], disk_lightGridColorsHDR[fromIndex]); + std::swap(lightGridGlob->clusters[fromIndex], lightGridGlob->clusters[toIndex]); + + for (unsigned int i = 0; i < lightGridGlob->pointCount; i++) + { + int index = lightGridGlob->points[i].entry.colorsIndex; + if (index == fromIndex) + lightGridGlob->points[i].entry.colorsIndex = toIndex; + else if (index == toIndex) + lightGridGlob->points[i].entry.colorsIndex = fromIndex; + } +} + +int GetClusterDefaultScore(GridColorsCluster *cluster) +{ + static DWORD dwCall = 0x00432E90; + + __asm + { + mov ecx, cluster + call dwCall + } +} + +void SetLightGridColorsForCluster2(GridColorsCluster *cluster, GfxLightGridColors *colors, GfxLightGridColorsHDR* colorsHDR=NULL) +{ + float means[168]; + CalculateClusterMean(cluster, means); + + for (int i = 0; i < 168; i++) + { + colors->all[i] = (BYTE)means[i]; + if (colorsHDR) + colorsHDR->all[i] = (short)means[i]; + } +} + +void SetLightGridColorsForCluster(GridColorsCluster *cluster, GfxLightGridColorsHDR* colorsHDR) +{ + float means[168]; + CalculateClusterMean(cluster, means); + + for (int i = 0; i < 168; i++) + { + colorsHDR->all[i] = (short)means[i]; + } +} + +/* +void __cdecl ImproveLightGridValues(int threadCount) +{ + _asm + { + mov eax, threadCount + mov ebx, 0x00435830 + call ebx + } +} +*/ + +void __cdecl ClusterLightGridValues(int threadCount) +{ + lightGridGlob->mapping = new unsigned int[lightGridGlob->pointCount]; + if (!lightGridGlob->mapping) + Com_FatalError("Couldn't allocate %i bytes for light grid color mapping", sizeof(int) * lightGridGlob->pointCount); + + lightGridGlob->clusters = new GridColorsCluster[LIGHTGRID_MAX_COLORCOUNT]; + if (!lightGridGlob->clusters) + Com_FatalError("Couldn't allocate %i bytes for light grid colors", sizeof(GridColorsCluster) * LIGHTGRID_MAX_COLORCOUNT); + + for (unsigned int i = 0; i < lightGridGlob->pointCount; i++) + { + lightGridGlob->mapping[i] = i; + } + + lightGridGlob->clusterCount = 1; + lightGridGlob->clusters[0].first = 0; + lightGridGlob->clusters[0].count = lightGridGlob->pointCount; + + CalculateClusterMeanAndVariance(&lightGridGlob->clusters[0]); + + unsigned int clusterCount = lightGridGlob->clusterCount; + for (; lightGridGlob->clusterCount < LIGHTGRID_MAX_COLORCOUNT; clusterCount = lightGridGlob->clusterCount) + { + GridColorsCluster *cluster = ChooseClusterToSplit(); + + if (cluster->unknown3 < options_clusterThreshold && clusterCount >= 2) + break; + + SplitCluster(cluster); + } + + unsigned int maxScore = 0; + unsigned int maxScoreIndex = 0; + + if (clusterCount > 0) + { + for (unsigned int colorIndex = 0; colorIndex < lightGridGlob->clusterCount; colorIndex++) + { + GridColorsCluster *cluster = &lightGridGlob->clusters[colorIndex]; + for (unsigned int firstIndex = cluster->first; firstIndex < (cluster->first + cluster->count); firstIndex++) + { + lightGridGlob->points[lightGridGlob->mapping[firstIndex]].entry.colorsIndex = colorIndex; + } + + SetLightGridColorsForCluster(cluster, &disk_lightGridColorsHDR[colorIndex]); + + unsigned int score = GetClusterDefaultScore(cluster); + if (score > maxScore) + { + maxScore = score; + maxScoreIndex = colorIndex; + } + } + } + + SwapClusters(0, maxScoreIndex); + SwapClusters(1, lightGridGlob->points[lightGridGlob->pointCount - 1].entry.colorsIndex); + + lightGridColorCount = lightGridGlob->clusterCount; + + if (options_ImproveLights) + ImproveLightGridValues(threadCount); + + delete[] lightGridGlob->clusters; + delete[] lightGridGlob->mapping; + lightGridGlob->clusters = nullptr; + lightGridGlob->mapping = nullptr; + + --lightGridGlob->pointCount; +} + +bool CompareLightGridColors(GfxLightGridColorsHDR* a, GfxLightGridColorsHDR* b, unsigned int* out) +{ + unsigned int totalDifference = 0; + + for (int i = 0; i < 56; i++) + { + for (int c = 0; c < 3; c++) + { + totalDifference += b->rgb[i][c] - a->rgb[i][c]; + if (totalDifference >= *out) + return false; + } + } + + *out = totalDifference; + return true; +} + +unsigned short AssignLightGridColors(unsigned short colorIndex, GfxLightGridColorsHDR* colors) +{ + unsigned int unk = INT_MAX; + CompareLightGridColors(&lightGridGlob->colors[colorIndex], colors, &unk); + if (!unk) + { + return colorIndex; + } + + for (unsigned int i = 0; i < lightGridGlob->clusterCount; i++) + { + if (CompareLightGridColors(&lightGridGlob->colors[i], colors, &unk)) + { + if (!unk) + { + colorIndex = i; + break; + } + } + } + + return colorIndex; +} + +void __cdecl GuessLightGridColors(int index, int unk) +{ + GfxLightGridEntry *entry = &lightGridGlob->points[index].entry; + entry->colorsIndex = AssignLightGridColors(entry->colorsIndex, &lightGridGlob->colors[index]); +} + +union GfxLightGridColorSums +{ + int rgb[56][3]; + int all[56 * 3]; +}; + +void __cdecl ImproveLightGridValues(int threadCount) +{ + GfxLightGridColorSums* sums = new GfxLightGridColorSums[lightGridGlob->clusterCount]; + if (!sums) + Com_FatalError("Couldn't allocate %i bytes for light grid color sums", sizeof(GfxLightGridColorSums) * lightGridGlob->clusterCount); + memset(sums, 0, sizeof(GfxLightGridColorSums) * lightGridGlob->clusterCount); + + int* counts = new int[LIGHTGRID_MAX_COLORCOUNT]; + if (!counts) + Com_FatalError("Couldn't allocate %i bytes for light grid color counts", sizeof(int) * LIGHTGRID_MAX_COLORCOUNT); + memset(counts, 0, sizeof(int) * lightGridGlob->clusterCount); + + BeginProgress("Improving quantization..."); + ForEachQuantum(lightGridGlob->pointCount, GuessLightGridColors, threadCount); + EndProgress(); + + for (unsigned int pointIndex = 0; pointIndex < lightGridGlob->pointCount; pointIndex++) + { + counts[lightGridGlob->points[pointIndex].entry.colorsIndex]++; + GfxLightGridColors* colors = (GfxLightGridColors*)lightGridGlob->colors; + for (int i = 0; i < 56; i++) + { + sums[lightGridGlob->points[pointIndex].entry.colorsIndex].rgb[i][0] += colors[pointIndex].rgb[i][0]; + sums[lightGridGlob->points[pointIndex].entry.colorsIndex].rgb[i][1] += colors[pointIndex].rgb[i][1]; + sums[lightGridGlob->points[pointIndex].entry.colorsIndex].rgb[i][2] += colors[pointIndex].rgb[i][2]; + } + } + + for (unsigned int colorIndex = 0; colorIndex < lightGridColorCount; ) + { + if (counts[colorIndex]) + { + for (int i = 0; i < 56; i++) + { + // Add HDR Later + disk_lightGridColors[colorIndex].rgb[i][0] = (sums[colorIndex].rgb[i][0] + (counts[colorIndex] >> 1)) / counts[colorIndex]; + disk_lightGridColors[colorIndex].rgb[i][1] = (sums[colorIndex].rgb[i][1] + (counts[colorIndex] >> 1)) / counts[colorIndex]; + disk_lightGridColors[colorIndex].rgb[i][2] = (sums[colorIndex].rgb[i][2] + (counts[colorIndex] >> 1)) / counts[colorIndex]; + } + + colorIndex++; + } + else + { + counts[colorIndex] = counts[--lightGridColorCount]; + memcpy(&sums[colorIndex], &sums[lightGridColorCount], sizeof(GfxLightGridColorSums)); + + for (unsigned int i = 0; i < lightGridGlob->pointCount; i++) + { + if (lightGridGlob->points[i].entry.colorsIndex == lightGridColorCount) + lightGridGlob->points[i].entry.colorsIndex = colorIndex; + } + } + } + + delete[] counts; + delete[] sums; +} diff --git a/components/cod2rad/r_lightgrid.h b/components/cod2rad/r_lightgrid.h index 99256935..f8bbd70c 100644 --- a/components/cod2rad/r_lightgrid.h +++ b/components/cod2rad/r_lightgrid.h @@ -1,8 +1,86 @@ #pragma once +#define LIGHTGRID_MAX_COLORCOUNT 0xFFFF + const static DWORD* g_lightgridSampleCount = (DWORD*)0x153C91D0; const static DWORD* g_diskLightgridSampleCount = (DWORD*)0x153C91E0; +union GfxLightGridColors +{ + BYTE rgb[56][3]; + BYTE all[56 * 3]; +}; +STATIC_ASSERT_SIZE(GfxLightGridColors, 56 * 3); + +union GfxLightGridColorsHDR +{ + short rgb[56][3]; + short all[56 * 3]; +}; + +struct GridColorsCluster +{ + unsigned int first; // 0x00 + unsigned int count; // 0x04 + unsigned int unknown1; // 0x08 + float unknown2; // 0x0C + float unknown3; // 0x10 +}; +STATIC_ASSERT_SIZE(GridColorsCluster, 0x14); + +struct GfxLightGridEntry +{ + unsigned __int16 colorsIndex; + char primaryLightIndex; + char needsTrace; +}; +STATIC_ASSERT_SIZE(GfxLightGridEntry, 0x04); + +struct GridSamplePoint +{ + WORD pos[3]; // 0x00 + WORD unknown4; // 0x06 ??? + //WORD unknown5; // 0x08 ColorsIndex? + //BYTE unknown6; // 0x0A PrimaryLightIndex? + //BYTE unknown7; // 0x0C NeedsTrace? + GfxLightGridEntry entry; +}; +STATIC_ASSERT_SIZE(GridSamplePoint, 0xC); + +struct LightGridGlob +{ + unsigned int pointCount; // 0x00 0x153C91D0 + unsigned int maxPoints; // 0x04 0x153C91D4 + GridSamplePoint* points; // 0x08 0x153C91D8 + GfxLightGridColorsHDR* colors; // 0x0C 0x153C91DC (In vanilla this is a GfxLightGridColors* which is allocated and freed in CalculateLightGrid) + unsigned int clusterCount; // 0x10 0x153C91E0 + GridColorsCluster *clusters; // 0x14 0x153C91E4 + unsigned int *mapping; // 0x18 0x153C91E8 + unsigned int dword_153C91EC; // 0x1C 0x153C91EC + unsigned int dword_153C91F0; // 0x20 0x153C91F0 + float flt_153C91F4; // 0x24 0x153C91F4 + float flt_153C91F8; // 0x28 0x153C91F8 + float flt_153C91FC; // 0x2C 0x153C91FC + float flt_153C9200; // 0x30 0x153C9200 + unsigned int dword_153C9204; // 0x34 0x153C9204 + // ... +}; + +static LightGridGlob *lightGridGlob = (LightGridGlob *)0x153C91D0; + +static unsigned int& lightGridColorCount = *(unsigned int *)0x112BAAB4; +static GfxLightGridColors *disk_lightGridColors = (GfxLightGridColors *)0x96CAE08; +extern GfxLightGridColorsHDR disk_lightGridColorsHDR[0xFFFF]; + +static bool& options_ImproveLights = *(bool *)0x153C9005; +static float& options_clusterThreshold = *(float*)0x153C902C; + +typedef WORD DiskSampleColorHDR[56][3]; +typedef BYTE DiskSampleColor[56][3]; + +//static QuantumFunc_t GuessLightGridColors = (QuantumFunc_t)0x004342F0; + +#if USE_LEGACY_HDR typedef float SampleColorHDR[56][3]; typedef WORD DiskSampleColorHDR[56][3]; typedef BYTE DiskSampleColor[56][3]; @@ -16,4 +94,13 @@ extern R_Store_QuantizedLightGridSample_t o_R_Store_QuantizedLightGridSample; void mfh_R_Init_Lightgrid(); void mfh_R_Store_LightgridSample(); void mfh_R_Alloc_DiskLightGridColors(); -void hk_R_Store_QuantizedLightGridSample(); \ No newline at end of file +void hk_R_Store_QuantizedLightGridSample(); +#else + +typedef void(__cdecl* SwapClusters_t)(int fromIndex, int toIndex); +static SwapClusters_t SwapClusters_o = (SwapClusters_t)0x00432EF0; + +void __cdecl StoreLightingForDir(vec3* lighting, GfxLightGridColorsHDR* dst); +void __cdecl ClusterLightGridValues(int ThreadCount); +void __cdecl ImproveLightGridValues(int threadCount); +#endif diff --git a/components/cod2rad/r_lightmaps.cpp b/components/cod2rad/r_lightmaps.cpp index 32120149..21b53b4b 100644 --- a/components/cod2rad/r_lightmaps.cpp +++ b/components/cod2rad/r_lightmaps.cpp @@ -1,4 +1,40 @@ -#include "stdafx.h" +#include "stdafx.h" + +#define LOG_VEC_EQ(A,B) if (A != B) { printf("(%f) %f %f %f == %f %f %f\n", Vec3Variance(&A, &B), A.x, A.y, A.z, B.x, B.y, B.z); } + +#if VARIANCE_TRACKER +VarianceTracker vt_GetInitialLightingHighlightDir; +VarianceTracker vt_GetColorsForHighlightDir_1; +VarianceTracker vt_GetColorsForHighlightDir_2; +VarianceTracker vt_GetLightingApproximationError; +VarianceTracker vt_GetGradientOfLightingErrorFunctionWithRespectToDir; +VarianceTracker vt_ImproveLightingApproximation_1; +VarianceTracker vt_ImproveLightingApproximation_2; + +VarianceTracker::VarianceTracker() : _min(FLT_MAX), _max(DBL_MIN), _total(0.0), _count(0) +{ +} + +void VarianceTracker::Track(double v) +{ + mtx.lock(); + if (v < _min) + _min = v; + + if (v > _max) + _max = v; + + _total += v; + _count++; + mtx.unlock(); +} + +double VarianceTracker::Min(void) const { return _min; } +double VarianceTracker::Max(void) const { return _max; } +double VarianceTracker::Total(void) const { return _total; } +double VarianceTracker::Average(void) const { return _total / _count; } + +#endif R_BuildFinalLightmaps_t o_R_BuildFinalLightmaps = (R_BuildFinalLightmaps_t)0x00432D70; @@ -14,6 +50,7 @@ void __declspec(naked) hk_R_BuildFinalLightmaps() } } +#if USE_LEGACY_HDR // // Store pixel information for later use by mfh_R_StoreLightmapPixel // @@ -34,13 +71,16 @@ void __declspec(naked) hk_R_StoreLightmapPixel() //00432830 } } +// +// Currently only pel1 appears to have correct alpha data +// void __cdecl R_StoreLightmapPixelHDR(vec4* pel1, vec4* pel2, int lightmap, int row, int pel) { LightmapBytes_HDR[0x40000 * lightmap + 512 * row + pel] = *pel1; Lightmap2Bytes_HDR[0x40000 * lightmap + 512 * row + pel] = *pel2; } -void* sub_4323E0 = (void*)0x4323E0; +void* ImproveLightingApproximation = (void*)0x4323E0; void* rtn_R_StoreLightmapPixel2 = (void*)0x0043288D; void __declspec(naked) mfh_R_StoreLightmapPixel() { @@ -60,8 +100,490 @@ void __declspec(naked) mfh_R_StoreLightmapPixel() add esp, 20 popad - call sub_4323E0 + call ImproveLightingApproximation xorps xmm1, xmm1 jmp rtn_R_StoreLightmapPixel2 } -} \ No newline at end of file +} +#else + +void __cdecl GetInitialLightingHighlightDir_o(vec3* lighting, vec3* highlightDir) +{ + _asm + { + pushad + push lighting + mov eax, highlightDir + mov ebx, 0x00431980 + call ebx + add esp, 4 + popad + } +} + +// +// Verified +// Variance: 0.000000 0.000014 +// +void GetInitialLightingHighlightDir(vec3 *lighting, vec3 *out) +{ + float totals[256]; + + // + // Get the total R+G+B values for each sample and store it in 'totals' + // + for (int i = 0; i < g_basisDirectionsCount; i++) + { + totals[i] = lighting[i].r + lighting[i].g + lighting[i].b; + } + + vec3 v25; + v25.x = 0; + v25.y = 0; + v25.z = 0; + + for (int i = 0; i < g_basisDirectionsCount; i++) + { + v25.z = g_basisDirections[i].z * 0.5f + 0.5f; + v25.y = totals[i] * v25.z + v25.y; + v25.x = v25.z + v25.x; + } + + v25.y = v25.y / v25.x; + + out->x = 0.0f; + out->y = 0.0f; + out->z = 0.0f; + + for (int i = 0; i < g_basisDirectionsCount; i++) + { + vec3* dir = &g_basisDirections[i]; + v25.z = dir->z * 0.5f + 0.5f; + v25.z = totals[i] - v25.z * v25.y; + totals[i] = v25.z; + + out->x = dir->x * v25.z + out->x; + out->y = dir->y * v25.z + out->y; + out->z = dir->z * v25.z + out->z; + } + + Vec3Normalize(out); +} + +void __cdecl GetColorsForHighlightDir_o(vec3* lighting, vec3* highlightDir, vec3* pel1, vec3* pel2) +{ + _asm + { + pushad + push pel2 + push pel1 + mov ecx, lighting + mov eax, highlightDir + mov ebx, 0x00430EF0 + call ebx + add esp, 8 + popad + } +} + +// +// Verified +// Variance: 0.000000 0.000393 (pel1) +// Variance: 0.000000 0.000372 (pel2) +// +void GetColorsForHighlightDir(vec3 *lighting, vec3 *highlightDir, vec3 *dstA, vec3 *dstB) +{ + vec3 total(0.0f, 0.0f, 0.0f); + for (int i = 0; i < g_basisDirectionsCount; i++) + { + total += lighting[i]; + } + + vec3 baz(0.0f, 0.0f, 0.0f); + vec3 total_gamma__lighting(0.0f, 0.0f, 0.0f); + vec3 total_dotpr__lighting(0.0f, 0.0f, 0.0f); + + for (int i = 0; i < g_basisDirectionsCount; i++) + { + vec3* dir = &g_basisDirections[i]; + float gamma_ = dir->z * 0.5f + 0.5f; + + float dotpr_ = Vec3Dot(dir, highlightDir); + if (dotpr_ < 0.0f) + dotpr_ = 0.0f; + + baz.x = dotpr_ * dotpr_ + baz.x; + baz.y = gamma_ * gamma_ + baz.y; + baz.z = dotpr_ * gamma_ + baz.z; + + total_gamma__lighting += lighting[i] * gamma_; + total_dotpr__lighting += lighting[i] * dotpr_; + } + + float unk2 = baz.x * baz.y - baz.z * baz.z; + if (unk2 == 0.0f) + { + dstA->r = 0.0; + dstA->g = 0.0; + dstA->b = 0.0; + dstB->r = 0.0; + dstB->g = 0.0; + dstB->b = 0.0; + } + else + { + vec3 srcA; + vec3 srcB; + + unk2 = 1.0f / unk2; + srcA.r = (total_gamma__lighting.x * baz.x - total_dotpr__lighting.x * baz.z) * unk2; + srcB.r = (total_dotpr__lighting.x * baz.y - total_gamma__lighting.x * baz.z) * unk2; + srcA.g = (total_gamma__lighting.y * baz.x - total_dotpr__lighting.y * baz.z) * unk2; + srcB.g = (total_dotpr__lighting.y * baz.y - total_gamma__lighting.y * baz.z) * unk2; + srcA.b = (total_gamma__lighting.z * baz.x - total_dotpr__lighting.z * baz.z) * unk2; + srcB.b = (total_dotpr__lighting.z * baz.y - total_gamma__lighting.z * baz.z) * unk2; + + if (ClampColor(dstB, &srcB)) + { + srcA = *dstB * -baz.z + total_gamma__lighting; + srcA /= baz.y; + } + + if (ClampColor(dstA, &srcA)) + { + srcB = *dstA * -baz.z + total_dotpr__lighting; + srcB /= baz.x; + + ClampColor(dstB, &srcB); + } + } +} + +// +// Regenerate the lighting sample from dir, highlightDir, and the two pels +// +void GetLightingSampleForDirFromLightmap(vec3* dir, vec3* highlightDir, vec3* pel_amb, vec3* pel_dir, vec3* out) +{ + // It would appear that half lambert is used for the ambient lighting + float weight_amb = dir->z * 0.5f + 0.5f; // weight_amb = Dot(dir, vec3(0, 0, 1)) * 0.5 + 0.5; + float weight_dir = Vec3Dot(dir, highlightDir); + + if (weight_dir < 0.0) + weight_dir = 0.0f; + + *out = (*pel_amb) * weight_amb + (*pel_dir) * weight_dir; +} + +float GetLightingApproximationError_o(vec3 *lighting, vec3 *highlightDir, vec3 *pel1, vec3 *pel2) +{ + static DWORD dwCall = 0x004313B0; + + DWORD outFloat; + + _asm + { + push pel1 + push lighting + mov edx, pel2 + mov eax, highlightDir + call [dwCall] + add esp, 8 + movd [outFloat], xmm0 + xorps xmm0, xmm0 + xorps xmm1, xmm1 + } + + return *(float *)&outFloat; +} + +/* + Get the total value of error^2 for each lighting sample and the current lightmap pixels + Variance: 0.000000 0.000001 +*/ +double GetLightingApproximationError(vec3 *lighting, vec3 *highlightDir, vec3 *pel_amb, vec3 *pel_dir) +{ + double error = 0.0; + + for (int i = 0; i < g_basisDirectionsCount; i++) + { + vec3 sample; + GetLightingSampleForDirFromLightmap(&g_basisDirections[i], highlightDir, pel_amb, pel_dir, &sample); + + vec3 dif = lighting[i] - sample; + error += Vec3Dot(&dif, &dif); + } + + return error; +} + +void __cdecl GetGradientOfLightingErrorFunctionWithRespectToDir_o(vec3 *lighting, vec3 *highlightDir, vec3 *pel1, vec3 *pel2, vec2 *gradient) +{ + void* fn = (void*)0x00431D50; + _asm + { + push gradient + push highlightDir + push pel1 + mov ecx, lighting + mov eax, pel2 + call fn + add esp, 12 + } +} + +// +// Verified +// Variance: 0.000000 0.000002 +// +void GetGradientOfLightingErrorFunctionWithRespectToDir(vec3 *lighting, vec3 *highlightDir, vec3 *pel1, vec3 *pel2, vec2 *gradient) +{ + vec2 total(0.0f, 0.0f); + + for (int i = 0; i < g_basisDirectionsCount; i++) + { + vec3* basis = &g_basisDirections[i]; + + vec3 sample; + GetLightingSampleForDirFromLightmap(basis, highlightDir, pel1, pel2, &sample); + + float yaa = Vec3Dot(basis, highlightDir); + + vec2 tmp; + tmp.x = highlightDir->x * -yaa + basis->x; + tmp.y = highlightDir->y * -yaa + basis->y; + + vec3 dif = lighting[i] - sample; + + float dotp = Vec3Dot(pel2, &dif); + total += tmp * dotp; + } + + *gradient = total * (highlightDir->z + highlightDir->z); +} + +void __cdecl ImproveLightingApproximation_o(vec3* lighting, vec3* highlightDir, vec3* pel1, vec3* pel2) +{ + _asm + { + pushad + push pel2 + push pel1 + push lighting + mov edi, highlightDir + mov ebx, 0x004323E0 + call ebx + add esp, 12 + popad + } +} + +// +// Verified: Bad +// Variance: 0.000000 0.813436 (pel1) +// Variance: 0.000000 0.590278 (pel2) +// +void ImproveLightingApproximation(vec3* lighting, vec3 *highlightDir, vec3* pel_amb, vec3* pel_dir) +{ + double curError = 0.0; + double error = GetLightingApproximationError(lighting, highlightDir, pel_amb, pel_dir); + +#if VARIANCE_TRACKER + double err2 = GetLightingApproximationError_o(lighting, highlightDir, pel_amb, pel_dir); + vt_GetLightingApproximationError.Track(abs(error - err2)); +#endif + + // Don't fix what isn't broken + if (error == 0.0) + return; + + BYTE initialByteDir[2]; + BYTE updatedByteDir[2]; + + EncodeNormalToBytes(highlightDir, initialByteDir); + + // the formula for weight is 2^(2-x) where x is the iteration number + float weight = 4.0; + for(int iterations = 16;;) // the original number of iterations was 4 + { + vec2 gradient; + GetGradientOfLightingErrorFunctionWithRespectToDir(lighting, highlightDir, pel_amb, pel_dir, &gradient); + +#if VARIANCE_TRACKER + vec2 gradient2; + GetGradientOfLightingErrorFunctionWithRespectToDir_o(lighting, highlightDir, pel_amb, pel_dir, &gradient2); + vt_GetGradientOfLightingErrorFunctionWithRespectToDir.Track(Vec2Variance(&gradient, &gradient2)); +#endif + + vec2 absoluteGradient; + absoluteGradient.x = fabs(gradient.x); + absoluteGradient.y = fabs(gradient.y); + + float tmp = absoluteGradient.x; + if (absoluteGradient.x - absoluteGradient.y < 0.0f) + tmp = absoluteGradient.y; + + vec3 dir; + vec3 new_pel_amb; + vec3 new_pel_dir; + + // + // Adjust using the gradient, keep adjustment if the new error is lower than the old one, otherwise try again + // + while (true) + { + float scalar = weight / tmp; + + updatedByteDir[0] = ClampByte(initialByteDir[0] + (int)(gradient.x * scalar)); + updatedByteDir[1] = ClampByte(initialByteDir[1] + (int)(gradient.y * scalar)); + + DecodeNormalFromBytes(updatedByteDir[0], updatedByteDir[1], &dir); + + GetColorsForHighlightDir(lighting, &dir, &new_pel_amb, &new_pel_dir); +#if VARIANCE_TRACKER + vec3 new_pel3; + vec3 new_pel4; + GetColorsForHighlightDir_o(lighting, &dir, &new_pel3, &new_pel4); + vt_GetColorsForHighlightDir_1.Track(Vec3Variance(&new_pel_amb, &new_pel3)); + vt_GetColorsForHighlightDir_2.Track(Vec3Variance(&new_pel_dir, &new_pel4)); +#endif + curError = GetLightingApproximationError(lighting, &dir, &new_pel_amb, &new_pel_dir); +#if VARIANCE_TRACKER + err2 = GetLightingApproximationError_o(lighting, &dir, &new_pel_amb, &new_pel_dir); + vt_GetLightingApproximationError.Track(abs(curError - err2)); +#endif + + // + // If the adjusted approximation has a smaller error value - use the approximation + // + if (curError < error) + break; + + weight /= 2.0f; + + // Abort if we were unable to find a better approximation within a reasonable amount of iterations + if (!--iterations) + { + return; + } + } + + *pel_amb = new_pel_amb; + *pel_dir = new_pel_dir; + + initialByteDir[0] = updatedByteDir[0]; + initialByteDir[1] = updatedByteDir[1]; + + *highlightDir = dir; + + error = curError; + } +} + +void __declspec(naked) hk_StoreLightBytes() +{ + _asm + { + push[esp+12] //pFloats esp + 12 + 0 + push[esp+12] //highlightDir esp + 8 + 4 + push[esp+12] //pixelIndex esp + 4 + 8 + push ecx // lmapRow + push eax // lmapSet + call StoreLightBytes + add esp, 20 + retn + } +} + +/* + lighting - a pointer to an array of [g_basisDirectionsCount] RGB vec3 values for the given pixel - these correspond to each basis direction in g_basisDirections for that point + pFloats - a pointer to the primary light values (1 float per pixel) that are used for baked shadowmaps + + pel1 - amb + pel2 - dir + pFloats - sm +*/ +void __cdecl StoreLightBytes(int lmapSet, int lmapRow, int pixelIndex, vec3* lighting, float* pFloats) +{ + int subOffset = 0x600 * lmapSet + lmapRow; + + vec3 highlightDir; + GetInitialLightingHighlightDir(lighting, &highlightDir); + +#if VARIANCE_TRACKER + vec3 highlightDir2; + GetInitialLightingHighlightDir_o(lighting, &highlightDir2); + + vt_GetInitialLightingHighlightDir.Track(Vec3Variance(&highlightDir, &highlightDir2)); +#endif + + vec3 pel1; + vec3 pel2; + GetColorsForHighlightDir(lighting, &highlightDir, &pel1, &pel2); + +#if VARIANCE_TRACKER + vec3 pel3; + vec3 pel4; + GetColorsForHighlightDir_o(lighting, &highlightDir, &pel3, &pel4); + + vt_GetColorsForHighlightDir_1.Track(Vec3Variance(&pel1, &pel3)); + vt_GetColorsForHighlightDir_2.Track(Vec3Variance(&pel2, &pel4)); +#endif + + ImproveLightingApproximation(lighting, &highlightDir, &pel1, &pel2); +#if VARIANCE_TRACKER + // Ensure that the starting values are the same + pel3 = pel1; + pel4 = pel2; + + ImproveLightingApproximation_o(lighting, &highlightDir, &pel3, &pel4); + vt_ImproveLightingApproximation_1.Track(Vec3Variance(&pel1, &pel3)); + vt_ImproveLightingApproximation_2.Track(Vec3Variance(&pel2, &pel4)); +#endif + + BYTE packed_normal[2]; + EncodeNormalToBytes(&highlightDir, packed_normal); + + BYTE* lightBytes = (BYTE*)0x00471590; + WAW_LMAP_PEL* pel = (WAW_LMAP_PEL*)&lightBytes[4 * (pixelIndex + (subOffset << 9))]; + + pel->B = EncodeFloatInByte(pel1.b); + pel->G = EncodeFloatInByte(pel1.g); + pel->R = EncodeFloatInByte(pel1.r); + pel->A = packed_normal[0]; + + pel += 0x40000; + + pel->B = EncodeFloatInByte(pel2.b); + pel->G = EncodeFloatInByte(pel2.g); + pel->R = EncodeFloatInByte(pel2.r); + pel->A = packed_normal[0]; + + if (g_HDR) + { + vec2 n; + EncodeNormalToFloats(&highlightDir, &n); + + vec4* out1 = &LightmapBytes_HDR[0x40000 * lmapSet + 512 * lmapRow + pixelIndex]; + *out1 = vec4(pel1, n.x); + + vec4* out2 = &Lightmap2Bytes_HDR[0x40000 * lmapSet + 512 * lmapRow + pixelIndex]; + *out2 = vec4(pel2, n.y); + } + + // + // For each lightmap pixel copy a block of 4 pixels for the shadowmap + // since the colored images are 512x512 and the shadowmap is 1024x1024 this is necessary + // + BYTE* lightBytes_PrimaryImage = (BYTE*)0x00671590; + BYTE* sm_pel = &lightBytes_PrimaryImage[2 * (pixelIndex + (subOffset << 10))]; + + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 2; j++) + { + sm_pel[1024*i + j] = EncodeFloatInByte(pFloats[2*i + j]); + } + } +} + +#endif diff --git a/components/cod2rad/r_lightmaps.h b/components/cod2rad/r_lightmaps.h index 99b9917a..cf31f66f 100644 --- a/components/cod2rad/r_lightmaps.h +++ b/components/cod2rad/r_lightmaps.h @@ -1,4 +1,7 @@ #pragma once +#include + +#define VARIANCE_TRACKER _DEBUG const static DWORD* g_LightmapCount = (DWORD*)0x16E99F58; @@ -7,5 +10,53 @@ extern R_BuildFinalLightmaps_t o_R_BuildFinalLightmaps; void hk_R_BuildFinalLightmaps(); +BYTE __cdecl EncodeFloatInByte(float flt); + +#if USE_LEGACY_HDR void hk_R_StoreLightmapPixel(); -void mfh_R_StoreLightmapPixel(); \ No newline at end of file +void mfh_R_StoreLightmapPixel(); +#else + +#if VARIANCE_TRACKER +struct VarianceTracker +{ +private: + std::mutex mtx; + + double _min; + double _max; + double _total; + + unsigned int _count; + +public: + VarianceTracker(); + + void Track(double v); + + double Min(void) const; + double Max(void) const; + double Total(void) const; + double Average(void) const; +}; + +// +// Global variance trackers for the rewritten functions +// +extern VarianceTracker vt_GetInitialLightingHighlightDir; +extern VarianceTracker vt_GetColorsForHighlightDir_1; +extern VarianceTracker vt_GetColorsForHighlightDir_2; +extern VarianceTracker vt_GetLightingApproximationError; +extern VarianceTracker vt_GetGradientOfLightingErrorFunctionWithRespectToDir; +extern VarianceTracker vt_ImproveLightingApproximation_1; +extern VarianceTracker vt_ImproveLightingApproximation_2; + +#ifndef VARIANCE_LOG +#define VARIANCE_LOG(TRACKER) Con_Printf("%s:\n\tMin: %f\n\tMax: %f\n\tAverage: %f\n", #TRACKER, TRACKER.Min(), TRACKER.Max(), TRACKER.Average()); +#endif + +#endif + +void hk_StoreLightBytes(); +void __cdecl StoreLightBytes(int lmapSet, int lmapRow, int pixelIndex, vec3* lighting, float* pFloats); +#endif \ No newline at end of file diff --git a/components/cod2rad/stdafx.h b/components/cod2rad/stdafx.h index bc051dc3..1dde0a0e 100644 --- a/components/cod2rad/stdafx.h +++ b/components/cod2rad/stdafx.h @@ -1,22 +1,30 @@ #pragma once +#pragma comment(lib, "detours.lib") +#include "../shared/detours/Detours.h" + #define WIN32_LEAN_AND_MEAN #include #include +#define USE_LEGACY_HDR 0 + // // Shared files // -#include "../shared/utility.h" +#include "../shared/shared_utility.h" +#include "../shared/shared_version.h" #include "PageGuard.h" -#include "console.h" +#include "print.h" #include "threading.h" #include "arguments.h" #include "vector.h" #include "hdr.h" +#include "lighting.h" + #include "r_lightmaps.h" #include "r_lightgrid.h" #include "r_xmodel_load_obj.h" @@ -25,8 +33,10 @@ #include "common.h" #include "com_memory.h" #include "com_files.h" +#include "com_math.h" #include "com_bsp_load_obj.h" +#include "r_light_load_obj.h" #include "r_xmodel_load_obj.h" #include "r_xsurface_load_obj.h" @@ -38,3 +48,7 @@ #else #pragma comment(lib, "../../build/Release/D3DBSP_Lib.lib") #endif + +#define VANILLA_VALUE(NAME, TYPE, ADDRESS) static TYPE& NAME = *(TYPE*)ADDRESS; + +using namespace D3DBSP_Lib; diff --git a/components/cod2rad/threading.cpp b/components/cod2rad/threading.cpp index 4206820d..1180b260 100644 --- a/components/cod2rad/threading.cpp +++ b/components/cod2rad/threading.cpp @@ -21,6 +21,20 @@ void(__cdecl *g_QuantumWorkerCallback)(int, int); volatile DWORD *g_ThreadLocks = (DWORD *)0x17302434; volatile DWORD g_ThreadCounter; +void __cdecl ForEachQuantum(int stepCount, QuantumFunc_t func, int threadCount) +{ + *(LONG *)0x17303430 = stepCount; + SetProgress(0, stepCount); + if (threadCount == 1) + for (int i = 0; i < *(LONG *)0x17303430; i++) + { + func(i, 0); + UpdateProgress(1); + } + else + ForEachQuantumMultiThreaded(threadCount, func); +} + DWORD WINAPI ForEachQuantumWorkerThread(LPVOID ThreadParameter) { for (LONG i = InterlockedExchangeAdd(&g_ThreadCounter, 1); i < *(LONG *)0x17303430; i = InterlockedExchangeAdd(&g_ThreadCounter, 1)) diff --git a/components/cod2rad/threading.h b/components/cod2rad/threading.h index bd9d37e6..8cd9e757 100644 --- a/components/cod2rad/threading.h +++ b/components/cod2rad/threading.h @@ -1,5 +1,8 @@ #pragma once +typedef void(__cdecl* QuantumFunc_t)(int stepIndex, int a2); + +void __cdecl ForEachQuantum(int stepCount, void(__cdecl *func)(int, int), int threadCount); DWORD WINAPI ForEachQuantumWorkerThread(LPVOID ThreadParameter); void ForEachQuantumMultiThreaded(ULONG ThreadCount, void(__cdecl *Callback)(int, int)); diff --git a/components/cod2rad/vector.cpp b/components/cod2rad/vector.cpp new file mode 100644 index 00000000..44edd7eb --- /dev/null +++ b/components/cod2rad/vector.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" + +void Vec3Normalize(vec3* v) +{ + float m2 = v->x * v->x + v->y * v->y + v->z * v->z; + + // Avoid sqrt of values <= 0 + float m = m2 > 0.0f ? (float)sqrt(m2) : 1.0f; + + // Avoid division by 0 + if (-m >= 0.0f) + m = 1.0f; + + *v /= m; +} + +void Vec3Normalize(float* v) +{ + Vec3Normalize((vec3*)v); +} + +float Vec3Dot(vec3* a, vec3* b) +{ + return a->x * b->x + a->y * b->y + a->z * b->z; +} + +float Vec3Dot(float* a, float* b) +{ + return Vec3Dot((vec3*)a, (vec3*)b); +} + +double Vec2Variance(vec2* a, vec2* b) +{ + return abs(a->x - b->x) + abs(a->y - b->y); +} + +double Vec3Variance(vec3* a, vec3* b) +{ + return abs(a->x - b->x) + abs(a->y - b->y) + abs(a->z - b->z); +} \ No newline at end of file diff --git a/components/cod2rad/vector.h b/components/cod2rad/vector.h index 35e4db44..fe854c3d 100644 --- a/components/cod2rad/vector.h +++ b/components/cod2rad/vector.h @@ -1,23 +1,916 @@ #pragma once -#include +#ifndef __VECTOR_H_ +#define __VECTOR_H_ template -struct tvec3 +class tvec2; +typedef tvec2 vec2; +typedef tvec2 dvec2; +typedef tvec2 ivec2; +typedef tvec2 uvec2; +typedef tvec2 bvec2; + +template +class tvec3; +typedef tvec3 vec3; +typedef tvec3 dvec3; +typedef tvec3 ivec3; +typedef tvec3 uvec3; +typedef tvec3 bvec3; + +template +class tvec4; +typedef tvec4 vec4; +typedef tvec4 dvec4; +typedef tvec4 ivec4; +typedef tvec4 uvec4; +typedef tvec4 bvec4; + +template +class tvec2 { - T r; - T g; - T b; +public: + union{ T x, r, s; }; + union{ T y, g, t; }; + + template + T dot(tvec2 vec) + { + return(this->x * vec.x + this->y * vec.y); + } + + T operator[](BYTE elem) + { + return ((T*)&x)[elem]; + } + + template + operator tvec2() + { + return tvec2((T2)this->x, (T2)this->y); + } + + template + operator tvec3() + { + return tvec3((T3)this->x, (T3)this->y, (T3)0); + } + + template + operator tvec4() + { + return tvec4((T4)this->x, (T4)this->y, (T4)0, (T4)0); + } + + template + bool operator==(tvec2& arg) + { + return (this->x == arg.x || this->y == arg.y); + } + + template + bool operator!=(tvec2& arg) + { + return !(*this == arg); + } + + //Addition + tvec2 operator+(T arg) + { + return tvec2(this->x + arg, this->y + arg); + } + + template + tvec2 operator+(tvec2& arg) + { + return tvec2(this->x + arg.x, this->y + arg.y); + } + + template + tvec2 operator+(tvec3& arg) + { + return tvec2(this->x + arg.x, this->y + arg.y); + } + + template + tvec2 operator+(tvec4& arg) + { + return tvec2(this->x + arg.x, this->y + arg.y); + } + + tvec2& operator+=(T arg) + { + this->x += arg; + this->y += arg; + return *this; + } + + template + tvec2& operator+=(tvec2& arg) + { + this->x += arg.x; + this->y += arg.y; + return *this; + } + + template + tvec2& operator+=(tvec3& arg) + { + this->x += arg.x; + this->y += arg.y; + return *this; + } + + template + tvec2& operator+=(tvec4& arg) + { + this->x += arg.x; + this->y += arg.y; + return *this; + } + + //Subtraction + tvec2 operator-(T arg) + { + return tvec2(this->x - arg, this->y - arg); + } + + template + tvec2 operator-(tvec2& arg) + { + return tvec2(this->x - arg.x, this->y - arg.y); + } + + template + tvec2 operator-(tvec3& arg) + { + return tvec2(this->x - arg.x, this->y - arg.y); + } + + template + tvec2 operator-(tvec4& arg) + { + return tvec2(this->x - arg.x, this->y - arg.y); + } + + tvec2& operator-=(T arg) + { + this->x -= arg; + this->y -= arg; + return *this; + } + + template + tvec2& operator-=(tvec2& arg) + { + this->x -= arg.x; + this->y -= arg.y; + return *this; + } + + template + tvec2& operator-=(tvec3& arg) + { + this->x -= arg.x; + this->y -= arg.y; + return *this; + } + + template + tvec2& operator-=(tvec4& arg) + { + this->x -= arg.x; + this->y -= arg.y; + return *this; + } + + //Multiplication + tvec2 operator*(T arg) + { + return tvec2(this->x * arg, this->y * arg); + } + + template + tvec2 operator*(tvec2& arg) + { + return tvec2(this->x * arg.x, this->y * arg.y); + } + + template + tvec2 operator*(tvec3& arg) + { + return tvec2(this->x * arg.x, this->y * arg.y); + } + + template + tvec2 operator*(tvec4& arg) + { + return tvec2(this->x * arg.x, this->y * arg.y); + } + + tvec2& operator*=(T arg) + { + this->x *= arg; + this->y *= arg; + return *this; + } + + template + tvec2& operator*=(tvec2& arg) + { + this->x *= arg.x; + this->y *= arg.y; + return *this; + } + + template + tvec2& operator*=(tvec3& arg) + { + this->x *= arg.x; + this->y *= arg.y; + return *this; + } + + template + tvec2& operator*=(tvec4& arg) + { + this->x *= arg.x; + this->y *= arg.y; + return *this; + } + + //Division + tvec2 operator/(T arg) + { + return tvec2(this->x / arg, this->y / arg); + } + + template + tvec2 operator/(tvec2& arg) + { + return tvec2(this->x / arg.x, this->y / arg.y); + } + + template + tvec2 operator/(tvec3& arg) + { + return tvec2(this->x / arg.x, this->y / arg.y); + } + + template + tvec2 operator/(tvec4& arg) + { + return tvec2(this->x / arg.x, this->y / arg.y); + } + + tvec2& operator/=(T arg) + { + this->x /= arg; + this->y /= arg; + return *this; + } + + template + tvec2& operator/=(tvec2& arg) + { + this->x /= arg.x; + this->y /= arg.y; + return *this; + } + + template + tvec2& operator/=(tvec3& arg) + { + this->x /= arg.x; + this->y /= arg.y; + return *this; + } + + template + tvec2& operator/=(tvec4& arg) + { + this->x /= arg.x; + this->y /= arg.y; + return *this; + } + + tvec2(){}; + tvec2(T initVal) : x(initVal), y(initVal){}; + tvec2(T X, T Y) : x(X), y(Y){}; + ~tvec2(){}; +}; + + +template +class tvec3 +{ +public: + union{ T x, r, s; }; + union{ T y, g, t; }; + union{ T z, b, p; }; + + template + T dot(tvec3 vec) + { + return(this->x * (T)vec.x + this->y * (T)vec.y + this->z * (T)vec.z); + } + + T operator[](BYTE elem) + { + return ((T*)&x)[elem]; + } + + template + tvec3 cross(tvec3 vec) + { + return tvec3(this->y * vec.z - this->z * vec.y, this->z*vec.x - this->x*vec.z, this->x * vec.y - this->y * vec.x); + } + + template + operator tvec2() + { + return tvec2((T2)this->x, (T2)this->y); + } + + template + operator tvec3() + { + return tvec3((T3)this->x, (T3)this->y, (T3)this->z); + } + + template + operator tvec4() + { + return tvec4((T4)this->x, (T4)this->y, (T4)this->z, (T4)0); + } + + template + bool operator==(tvec3& arg) + { + return (this->x == arg.x || this->y == arg.y || this->z == arg.z); + } + + template + bool operator!=(tvec3& arg) + { + return !(*this == arg); + } + + //Addition + tvec3 operator+(T arg) + { + return tvec3(this->x + arg, this->y + arg, this->z + arg); + } + + template + tvec3 operator+(tvec2& arg) + { + return tvec3(this->x + arg.x, this->y + arg.y, this->z); + } + + template + tvec3 operator+(tvec3& arg) + { + return tvec3(this->x + arg.x, this->y + arg.y, this->z + arg.z); + } + + template + tvec3 operator+(tvec4& arg) + { + return tvec3(this->x + arg.x, this->y + arg.y, this->z + arg.z); + } + + tvec3& operator+=(T arg) + { + this->x += arg; + this->y += arg; + this->z += arg; + return *this; + } + + template + tvec3& operator+=(tvec2& arg) + { + this->x += arg.x; + this->y += arg.y; + return *this; + } + + template + tvec3& operator+=(tvec3& arg) + { + this->x += arg.x; + this->y += arg.y; + this->z += arg.z; + return *this; + } + + template + tvec3& operator+=(tvec4& arg) + { + this->x += arg.x; + this->y += arg.y; + this->z += arg.z; + return *this; + } + + //Subtraction + tvec3 operator-(T arg) + { + return tvec3(this->x - arg, this->y - arg, this->z - arg); + } + + template + tvec3 operator-(tvec2& arg) + { + return tvec3(this->x - arg.x, this->y - arg.y, this->z); + } + + template + tvec3 operator-(tvec3& arg) + { + return tvec3(this->x - arg.x, this->y - arg.y, this->z - arg.z); + } + + template + tvec3 operator-(tvec4& arg) + { + return tvec3(this->x - arg.x, this->y - arg.y, this->z - arg.z); + } + + tvec3& operator-=(T arg) + { + this->x -= arg; + this->y -= arg; + this->z -= arg; + return *this; + } + + template + tvec3& operator-=(tvec2& arg) + { + this->x -= arg.x; + this->y -= arg.y; + return *this; + } + + template + tvec3& operator-=(tvec3& arg) + { + this->x -= arg.x; + this->y -= arg.y; + this->z -= arg.z; + return *this; + } + + template + tvec3& operator-=(tvec4& arg) + { + this->x -= arg.x; + this->y -= arg.y; + this->z -= arg.z; + return *this; + } + + //Multiplication + tvec3 operator*(T arg) + { + return tvec3(this->x * arg, this->y * arg, this->z * arg); + } + + template + tvec3 operator*(tvec2& arg) + { + return tvec3(this->x * arg.x, this->y * arg.y, this->z); + } + + template + tvec3 operator*(tvec3& arg) + { + return tvec3(this->x * arg.x, this->y * arg.y, this->z * arg.z); + } + + template + tvec3 operator*(tvec4& arg) + { + return tvec3(this->x * arg.x, this->y * arg.y, this->z * arg.z); + } + + tvec3& operator*=(T arg) + { + this->x *= arg; + this->y *= arg; + this->z *= arg; + return *this; + } + + template + tvec3& operator*=(tvec2& arg) + { + this->x *= arg.x; + this->y *= arg.y; + return *this; + } + + template + tvec3& operator*=(tvec3& arg) + { + this->x *= arg.x; + this->y *= arg.y; + this->z *= arg.z; + return *this; + } + + template + tvec3& operator*=(tvec4& arg) + { + this->x *= arg.x; + this->y *= arg.y; + this->z *= arg.z; + return *this; + } + + //Division + tvec3 operator/(T arg) + { + return tvec3(this->x / arg, this->y / arg, this->z / arg); + } + + template + tvec3 operator/(tvec2& arg) + { + return tvec3(this->x / arg.x, this->y / arg.y, this->z); + } + + template + tvec3 operator/(tvec3& arg) + { + return tvec3(this->x / arg.x, this->y / arg.y, this->z / arg.z); + } + + template + tvec3 operator/(tvec4& arg) + { + return tvec3(this->x / arg.x, this->y / arg.y, this->z / arg.z); + } + + tvec3& operator/=(T arg) + { + this->x /= arg; + this->y /= arg; + this->z /= arg; + return *this; + } + + template + tvec3& operator/=(tvec2& arg) + { + this->x /= arg.x; + this->y /= arg.y; + return *this; + } + + template + tvec3& operator/=(tvec3& arg) + { + this->x /= arg.x; + this->y /= arg.y; + this->z /= arg.z; + return *this; + } + + template + tvec3& operator/=(tvec4& arg) + { + this->x /= arg.x; + this->y /= arg.y; + this->z /= arg.z; + return *this; + } + + tvec3(){}; + tvec3(T initVal) : x(initVal), y(initVal), z(initVal){}; + tvec3(T X, tvec2 Vec2) : x(X), y(Vec2.x), z(Vec2.y){}; + tvec3(tvec2 Vec2, T Z) : x(Vec2.x), y(Vec2.y), z(Z){}; + tvec3(T X, T Y, T Z) : x(X), y(Y), z(Z){}; + ~tvec3(){}; }; + template -struct tvec4 +class tvec4 { - T r; - T g; - T b; - T a; +public: + union{ T x, r, s; }; + union{ T y, g, t; }; + union{ T z, b, p; }; + union{ T w, a, q; }; + + template + T dot(tvec4 vec) + { + return(this->x * vec.x + this->y * vec.y + this->z * vec.z + this->w * vec.w); + } + + T operator[](BYTE elem) + { + return ((T*)&x)[elem]; + } + + template + operator tvec2() + { + return tvec2((T2)this->x, (T2)this->y); + } + + template + operator tvec3() + { + return tvec3((T3)this->x, (T3)this->y, (T3)this->z); + } + + template + operator tvec4() + { + return tvec4((T4)this->x, (T4)this->y, (T4)this->z, (T4)this->w); + } + + template + bool operator==(tvec4& arg) + { + return (this->x == arg.x || this->y == arg.y || this->z == arg.z || this->w == arg.w); + } + + template + bool operator!=(tvec4& arg) + { + return !(*this == arg); + } + + //Addition + tvec4 operator+(T arg) + { + return tvec4(this->x + arg, this->y + arg, this->z + arg, this->w + arg); + } + + template + tvec4 operator+(tvec2& arg) + { + return tvec4(this->x + arg.x, this->y + arg.y, this->z, this->w); + } + + template + tvec4 operator+(tvec3& arg) + { + return tvec4(this->x + arg.x, this->y + arg.y, this->z + arg.z, this->w); + } + + template + tvec4 operator+(tvec4& arg) + { + return tvec4(this->x + arg.x, this->y + arg.y, this->z + arg.z, this->w + arg.w); + } + + tvec4& operator+=(T arg) + { + this->x += arg; + this->y += arg; + this->z += arg; + this->w += arg; + return *this; + } + + template + tvec4& operator+=(tvec2& arg) + { + this->x += arg.x; + this->y += arg.y; + return *this; + } + + template + tvec4& operator+=(tvec3& arg) + { + this->x += arg.x; + this->y += arg.y; + this->z += arg.z; + return *this; + } + + template + tvec4& operator+=(tvec4& arg) + { + this->x += arg.x; + this->y += arg.y; + this->z += arg.z; + this->w += arg.w; + return *this; + } + + //Subtraction + tvec4 operator-(T arg) + { + return tvec4(this->x - arg, this->y - arg, this->z - arg, this->w - arg); + } + + template + tvec4 operator-(tvec2& arg) + { + return tvec4(this->x - arg.x, this->y - arg.y, this->z, this->w); + } + + template + tvec4 operator-(tvec3& arg) + { + return tvec4(this->x - arg.x, this->y - arg.y, this->z - arg.z, this->w); + } + + template + tvec4 operator-(tvec4& arg) + { + return tvec4(this->x - arg.x, this->y - arg.y, this->z - arg.z, this->w - arg.w); + } + + tvec4& operator-=(T arg) + { + this->x -= arg; + this->y -= arg; + this->z -= arg; + this->w -= arg; + return *this; + } + + template + tvec4& operator-=(tvec2& arg) + { + this->x -= arg.x; + this->y -= arg.y; + return *this; + } + + template + tvec4& operator-=(tvec3& arg) + { + this->x -= arg.x; + this->y -= arg.y; + this->z -= arg.z; + return *this; + } + + template + tvec4& operator-=(tvec4& arg) + { + this->x -= arg.x; + this->y -= arg.y; + this->z -= arg.z; + this->w -= arg.w; + return *this; + } + + //Multiplication + tvec4 operator*(T arg) + { + return tvec4(this->x * arg, this->y * arg, this->z * arg, this->w * arg); + } + + template + tvec4 operator*(tvec2& arg) + { + return tvec4(this->x * arg.x, this->y * arg.y, this->z, this->w); + } + + template + tvec4 operator*(tvec3& arg) + { + return tvec4(this->x * arg.x, this->y * arg.y, this->z * arg.z, this->w); + } + + template + tvec4 operator*(tvec4& arg) + { + return tvec4(this->x * arg.x, this->y * arg.y, this->z * arg.z, this->w * arg.w); + } + + tvec4& operator*=(T arg) + { + this->x *= arg; + this->y *= arg; + this->z *= arg; + this->w *= arg; + return *this; + } + + template + tvec4& operator*=(tvec2& arg) + { + this->x *= arg.x; + this->y *= arg.y; + return *this; + } + + template + tvec4& operator*=(tvec3& arg) + { + this->x *= arg.x; + this->y *= arg.y; + this->z *= arg.z; + return *this; + } + + template + tvec4& operator*=(tvec4& arg) + { + this->x *= arg.x; + this->y *= arg.y; + this->z *= arg.z; + this->w *= arg.w; + return *this; + } + + //Division + tvec4 operator/(T arg) + { + return tvec4(this->x / arg, this->y / arg, this->z / arg, this->w / arg); + } + + template + tvec4 operator/(tvec2& arg) + { + return tvec4(this->x / arg.x, this->y / arg.y, this->z, this->w); + } + + template + tvec4 operator/(tvec3& arg) + { + return tvec4(this->x / arg.x, this->y / arg.y, this->z / arg.z, this->w); + } + + template + tvec4 operator/(tvec4& arg) + { + return tvec4(this->x / arg.x, this->y / arg.y, this->z / arg.z, this->w / arg.w); + } + + tvec4& operator/=(T arg) + { + this->x /= arg; + this->y /= arg; + this->z /= arg; + this->w /= arg; + return *this; + } + + template + tvec4& operator/=(tvec2& arg) + { + this->x /= arg.x; + this->y /= arg.y; + return *this; + } + + template + tvec4& operator/=(tvec3& arg) + { + this->x /= arg.x; + this->y /= arg.y; + this->z /= arg.z; + return *this; + } + + template + tvec4& operator/=(tvec4& arg) + { + this->x /= arg.x; + this->y /= arg.y; + this->z /= arg.z; + this->w /= arg.w; + return *this; + } + + tvec4(){}; + tvec4(T initVal) : x(initVal), y(initVal), z(initVal), w(initVal){}; + tvec4(T X, T Y, tvec2 Vec2) : x(X), y(Y), z(Vec2.x), w(Vec2.y){}; + tvec4(T X, tvec2 Vec2, T W) : x(X), y(Vec2.x), z(Vec2.y), w(W){}; + tvec4(tvec2 Vec2, T Z, T W) : x(Vec2.x), y(Vec2.y), z(Z), w(W){}; + tvec4(T X, tvec3 Vec3) : x(X), y(Vec3.x), z(Vec3.y), w(Vec3.z){}; + tvec4(tvec3 Vec3, T W) : x(Vec3.x), y(Vec3.y), z(Vec3.z), w(W){}; + tvec4(T X, T Y, T Z, T W) : x(X), y(Y), z(Z), w(W){}; + ~tvec4(){}; }; -typedef tvec3 vec3; -typedef tvec4 vec4; +void Vec3Normalize(vec3* v); +void Vec3Normalize(float* v); + +float Vec3Dot(vec3* a, vec3* b); +float Vec3Dot(float* a, float* b); + +// +// Get the total of the absolute values of the diferences for each axis in two vectors +// +double Vec2Variance(vec2* a, vec2* b); +double Vec3Variance(vec3* a, vec3* b); + +#endif + + diff --git a/components/common.props b/components/common.props index 2b6f0d06..c6c3c6d6 100644 --- a/components/common.props +++ b/components/common.props @@ -12,6 +12,10 @@ $(SolutionDir)\components\shared + __CONFIGURATION__="$(ConfigurationName)";%(PreprocessorDefinitions) + true + false + StreamingSIMDExtensions2 diff --git a/components/game_mod/CEG.cpp b/components/game_mod/CEG.cpp index 7578b8fd..e33786b5 100644 --- a/components/game_mod/CEG.cpp +++ b/components/game_mod/CEG.cpp @@ -35,8 +35,8 @@ void Patch_CEG() g_ImageCodeSize = 0x5A1C00; g_ImageEnd = g_ImageBase + g_ImageCodeSize; - g_MemoryBuffer = VirtualAlloc(nullptr, g_ImageCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); - memcpy(g_MemoryBuffer, (LPVOID)g_ImageBase, g_ImageCodeSize); + //g_MemoryBuffer = VirtualAlloc(nullptr, g_ImageCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + //memcpy(g_MemoryBuffer, (LPVOID)g_ImageBase, g_ImageCodeSize); sub_5CBF00_o = (sub_5CBF00_t)Detours::X86::DetourFunction((PBYTE)0x5CBF00, (PBYTE)&sub_5CBF00); sub_661450_o = (sub_661450_t)Detours::X86::DetourFunction((PBYTE)0x661450, (PBYTE)&sub_661450); @@ -49,16 +49,102 @@ void Patch_CEG() Detours::X86::DetourFunction((PBYTE)0x8EF04F, (PBYTE)&hk_inline_memcpy); Detours::X86::DetourFunction((PBYTE)0x8EF168, (PBYTE)&hk_inline_memcpy2); - Detours::X86::DetourFunction((PBYTE)0x8EE640, (PBYTE)&sub_8EE640); - - FixupFunction(0x0060CC10, 0x004F20F0);// CEGObfuscate => LiveStats_Init - FixupFunction(0x00580460, 0x0079E6D0);// CEGObfuscate => Con_Restricted_SetLists + PatchMemory(0x00662F20, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); // Kill Steam DRM IPC creation + PatchMemory(0x0046C9A0, (PBYTE)"\xCC", 1); + PatchMemory(0x005F3290, (PBYTE)"\xCC", 1); + + PatchMemory(0x0064F6A0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); // Single function, 32bit hash check + PatchMemory(0x005614A0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00417360, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x0056AB40, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x0059BEB0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + + PatchMemory(0x00676740, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); // Direct ExitProcess() check + PatchMemory(0x005DB020, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x004F02C0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00572DF0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00679B40, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x004BFB50, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x004D4B80, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00501080, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x005CAB50, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x004C0DE0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + + PatchMemory(0x0041CEB0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); // Wrapper ExitProcess() check, executes + PatchMemory(0x0041CF50, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); // on certain days of the week + PatchMemory(0x00427E00, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00437350, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00444E80, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00449C30, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x004545A0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x0045C550, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00462B60, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x004682B0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00487D80, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x0048C7B0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x004A9970, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x004C3360, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x004D9650, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x004E3860, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00515B20, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x005268E0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00527200, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x005474A0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x0054F280, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00561410, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x0059D820, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x005A8800, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x005B4330, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x005D3F20, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x005EF2A0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x006099E0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00610A60, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00625500, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00625980, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x0064FFB0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00662EC0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00662F80, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x00671860, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x0067B3B0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x0067D830, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x006857B0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x0068D300, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x0068E8F0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + + PatchMemory(0x009A23B0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); // Wrapper ExitProcess() check with HWBP detection, + PatchMemory(0x009A23F0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); // executes on certain days of the week + PatchMemory(0x009A2430, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x009A2470, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x009A24B0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x009A24F0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x009A2530, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x009A2570, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x009A25B0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x009A25F0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x009A2630, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x009A2670, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x009A26B0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x009A26F0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x009A2730, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x009A2770, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + + PatchMemory(0x009A2980, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); // Direct HWBP check + PatchMemory(0x009A29B0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + PatchMemory(0x009A29E0, (PBYTE)"\xB8\x01\x00\x00\x00\xC3", 6); + + Detours::X86::DetourFunction((PBYTE)0x8EE640, (PBYTE)&sub_8EE640); // Patch intentional nullptr + + FixupFunction(0x0060CC10, 0x004F20F0); // CEGObfuscate => LiveStats_Init + FixupFunction(0x00580460, 0x0079E6D0); // CEGObfuscate => Con_Restricted_SetLists } DWORD __declspec(noinline) GetNewAddress(DWORD dwOld) { if (dwOld > g_ImageBase && dwOld < g_ImageEnd) + { + __debugbreak(); return ((DWORD)g_MemoryBuffer + (dwOld - g_ImageBase)); + } return dwOld; } @@ -123,6 +209,8 @@ void __declspec(naked) hk_inline_memcpy2() void *sub_8EE640(void *Nullptr1, void *Nullptr2) { + __debugbreak(); + if (Nullptr1 != nullptr || Nullptr2 != nullptr) __debugbreak(); diff --git a/components/game_mod/bg_perks.cpp b/components/game_mod/bg_perks.cpp new file mode 100644 index 00000000..598205d4 --- /dev/null +++ b/components/game_mod/bg_perks.cpp @@ -0,0 +1,47 @@ +#include "stdafx.h" + +std::unordered_map gPerkList( +{ + { "specialty_longersprint", 0 }, + { "specialty_unlimitedsprint", 1 }, + { "specialty_scavanger", 2 }, + { "specialty_fastreload", PERK_FASTRELOAD }, + { "specialty_bulletdamage", 4 }, + { "specialty_bulletaccuracy", 5 }, + { "specialty_flakjacket", 6 }, + { "specialty_armorvest", 7 }, + { "specialty_quickrevive", 8 }, + { "specialty_altmelee", 9 }, + { "specialty_rof", PERK_RATEOFFIRE }, + { "specialty_extraammo", 11 }, + { "specialty_endurance", 12 }, + { "specialty_deadshot", 13 }, + { "specialty_additionalprimaryweapon", 14 }, + { "specialty_fastswitch", PERK_FASTSWITCH }, + { "specialty_stockpile", PERK_STOCKPILE }, +}); + +// /game/bg_perks.cpp:129 +unsigned int BG_GetPerkIndexForName(const char *perkName) +{ + auto found = gPerkList.find(perkName); + + if (found != gPerkList.end()) + return found->second; + + return PERK_UNKNOWN; +} + +// /game/bg_perks.cpp:143 +const char *BG_GetPerkNameForIndex(unsigned int perkIndex) +{ + ASSERT(perkIndex < PERK_COUNT); + + for(const auto& entry : gPerkList) + { + if (entry.second == perkIndex) + return entry.first.c_str(); + } + + return ""; +} \ No newline at end of file diff --git a/components/game_mod/bg_perks.h b/components/game_mod/bg_perks.h new file mode 100644 index 00000000..aee3684d --- /dev/null +++ b/components/game_mod/bg_perks.h @@ -0,0 +1,64 @@ +#pragma once + +// Vanilla calculation: (3 << 2 * perkIndex) -- 2 bits per perk +// Newer perk calculation: (1 << (15 + perkIndex)) -- 1 bit per perk +inline unsigned int BG_PERK_BITS(int x) +{ + return ((x >= 15) ? (1UL << (15 + x)) : (3UL << 2 * x)); +} + +enum +{ + PERK_FASTRELOAD = 3, // (3 << (2 * 3)) == 0xC0 + PERK_RATEOFFIRE = 10, // (3 << (2 * 10)) == 0x300000 + PERK_FASTSWITCH = 15, // (1 << (15 + 15)) == 0x40000000 + PERK_STOCKPILE = 16, // (1 << (15 + 16)) == 0x80000000 + + PERK_COUNT, // Game original: 15 + PERK_UNKNOWN = PERK_COUNT, // +}; + +// /game/bg_perks.h:135 +static bool BG_HasPerk(const unsigned int *perks, unsigned int perkIndex) +{ + ASSERT(perks); + ASSERT(perkIndex >= 0 && perkIndex < PERK_COUNT); + + return (perks[0] & BG_PERK_BITS(perkIndex)) != 0; +} + +// /game/bg_perks.h:144 +static void BG_SetPerk(unsigned int *perks, unsigned int perkIndex) +{ + ASSERT(perks); + ASSERT(perkIndex >= 0 && perkIndex < PERK_COUNT); + + perks[0] |= BG_PERK_BITS(perkIndex); +} + +// /game/bg_perks.h:159 +static void BG_UnsetPerk(unsigned int *perks, unsigned int perkIndex) +{ + ASSERT(perks); + ASSERT(perkIndex >= 0 && perkIndex < PERK_COUNT); + + perks[0] &= ~BG_PERK_BITS(perkIndex); +} + +// /game/bg_perks.h:174 +static void BG_ClearPerks(unsigned int *perks) +{ + ASSERT(perks); + + perks[0] = 0; +} + +// /game/bg_perks.h:??? +static bool BG_WeaponHasPerk(WeaponDef *weapDef, unsigned int perkIndex) +{ + // This is only implemented in BO2 + return false; +} + +unsigned int BG_GetPerkIndexForName(const char *perkName); +const char *BG_GetPerkNameForIndex(unsigned int perkIndex); \ No newline at end of file diff --git a/components/game_mod/bg_weapons.cpp b/components/game_mod/bg_weapons.cpp new file mode 100644 index 00000000..013bd01e --- /dev/null +++ b/components/game_mod/bg_weapons.cpp @@ -0,0 +1,305 @@ +#include "stdafx.h" + +// /bgame/bg_weapons.cpp:278 +void PM_StartWeaponAnim(playerState_s *ps, int anim, int leftAnim) +{ + if (ps->pm_type < 9) + { + if (leftAnim) + ps->weapAnimLeft = anim | (~ps->weapAnimLeft & 0x400); + else + ps->weapAnim = anim | (~ps->weapAnim & 0x400); + } +} + +// /bgame/bg_weapons.cpp:290 +void PM_ContinueWeaponAnim(playerState_s *ps, int anim, int leftAnim) +{ + if (((ps->weapAnim & ~0x400) != anim || ps->bRunLeftGun) && + ((ps->weapAnimLeft & ~0x400) != anim || !ps->bRunLeftGun)) + { + PM_StartWeaponAnim(ps, anim, leftAnim); + } +} + +// /bgame/bg_weapons.cpp:432 +int *BG_GetWeaponState(playerState_s *ps, bool leftGun) +{ + if (leftGun) + return &ps->weaponstateLeft; + + return &ps->weaponstate; +} + +// /bgame/bg_weapons.cpp:440 +int *BG_GetWeaponTime(playerState_s *ps, bool leftGun) +{ + if (leftGun) + return &ps->weaponTimeLeft; + + return &ps->weaponTime; +} + +// /bgame/bg_weapons.cpp:448 +int *BG_GetWeaponDelay(playerState_s *ps, bool leftGun) +{ + if (leftGun) + return &ps->weaponDelayLeft; + + return &ps->weaponDelay; +} + +// /bgame/bg_weapons.cpp:456 +unsigned int *BG_GetWeaponShotCount(playerState_s *ps, bool leftGun) +{ + if (leftGun) + return &ps->weaponShotCountLeft; + + return &ps->weaponShotCount; +} + +// /bgame/bg_weapons.cpp:1204 +void PM_Weapon_FinishRechamber(playerState_s *ps) +{ + PM_ContinueWeaponAnim(ps, 0, 0); + + PlayerHeldWeapon *weap = BG_GetHeldWeapon(ps, ps->weapon); + + if (weap) + weap->needsRechamber = false; + + ps->weaponstate = WEAPON_READY; +} + +// /bgame/bg_weapons.cpp:1266 +unsigned int PM_GetWeaponIndexForHand(playerState_s *ps) +{ + if (ps->bRunLeftGun) + return BG_GetWeaponDef(ps->weapon)->dualWieldWeaponIndex; + + return ps->weapon; +} + +// /bgame/bg_weapons.cpp:1518 +bool BG_CanFastSwitch(WeaponDef *weapDef, int weaponState) +{ + switch (weaponState) + { + case WEAPON_RAISING: + case WEAPON_RAISING_ALTSWITCH: + case WEAPON_DROPPING: + case WEAPON_DROPPING_QUICK: + case WEAPON_DROPPING_ALTSWITCH: + return weapDef->offhandSlot != 4; + } + + return false; +} + +// /bgame/bg_weapons.cpp:2541 +bool ShotLimitReached(playerState_s *ps, WeaponDef *weapDef) +{ + ASSERT(ps); + ASSERT(weapDef); + + unsigned int *weaponShotCount = BG_GetWeaponShotCount(ps, ps->bRunLeftGun); + + switch (weapDef->fireType) + { + case WEAPON_FIRETYPE_SINGLESHOT:return *weaponShotCount >= 1; + case WEAPON_FIRETYPE_BURSTFIRE2:return *weaponShotCount >= 2; + case WEAPON_FIRETYPE_BURSTFIRE3:return *weaponShotCount >= 3; + case WEAPON_FIRETYPE_BURSTFIRE4:return *weaponShotCount >= 4; + case WEAPON_FIRETYPE_STACKED: return *weaponShotCount >= ps->stackFireCount; + } + + return false; +} + +// /bgame/bg_weapons.cpp:2580 +bool BurstFirePending(playerState_s *ps) +{ + static DWORD dwCall = 0x00766800; + + __asm + { + mov esi, ps + call dwCall + } +} + +// /bgame/bg_weapons.cpp:2614 +bool WeaponUsesBurstCooldown(unsigned int weaponIdx) +{ + if (weaponIdx) + { + switch (BG_GetWeaponDef(weaponIdx)->fireType) + { + case WEAPON_FIRETYPE_BURSTFIRE2: + case WEAPON_FIRETYPE_BURSTFIRE3: + case WEAPON_FIRETYPE_BURSTFIRE4: + return true; + } + } + + return false; +} + +// /bgame/bg_weapons.cpp:2699 +int PM_Weapon_WeaponTimeAdjust(pmove_t *pm, pml_t *pml) +{ + playerState_s *ps = pm->ps; + ASSERT(ps); + + int msec = 0; + int *weaponState = BG_GetWeaponState(ps, ps->bRunLeftGun); + int *weaponTime = BG_GetWeaponTime(ps, ps->bRunLeftGun); + int *weaponDelay = BG_GetWeaponDelay(ps, ps->bRunLeftGun); + unsigned int *weaponShotCount = BG_GetWeaponShotCount(ps, ps->bRunLeftGun); + unsigned int weaponIndex = PM_GetWeaponIndexForHand(ps); + + WeaponVariantDef *weapVariantDef = BG_GetWeaponVariantDef(weaponIndex); + WeaponDef *weapDef = BG_GetWeaponDef(weaponIndex); + + if (ps->weaponRestrictKickTime > 0) + ps->weaponRestrictKickTime = max(ps->weaponRestrictKickTime - pml->msec, 0); + + // Apply perk modifications to the animation time + if (IS_WEAPONSTATE_RELOAD(*weaponState) && BG_HasPerk(ps->perks, PERK_FASTRELOAD)) + { + // Fast weapon reload perk + if (perk_weapReloadMultiplier->current.value != 0.0f) + msec = (int)((float)pml->msec / perk_weapReloadMultiplier->current.value); + else + msec = max(*weaponDelay, *weaponTime); + } + else if (IS_WEAPONSTATE_FIRE(*weaponState) && !IS_WEAPONSTATE_MELEE(*weaponState) && BG_HasPerk(ps->perks, PERK_RATEOFFIRE)) + { + // Doubletap (rate of fire) perk + if (perk_weapRateMultiplier->current.value != 0.0f) + msec = (int)((float)pml->msec / perk_weapRateMultiplier->current.value); + else + msec = max(*weaponDelay, *weaponTime); + } + else if (BG_CanFastSwitch(weapDef, *weaponState) && BG_HasPerk(ps->perks, PERK_FASTSWITCH)) + { + // Fast weapon switch perk + ASSERT(perk_weapSwitchMultiplier->current.value > 0.0f); + + msec = (int)((float)pml->msec / perk_weapSwitchMultiplier->current.value); + } + else + { + // Default modifier time + msec = pml->msec; + } + + if (*weaponTime) + { + *weaponTime -= msec; + + if (weapDef->fireType == WEAPON_FIRETYPE_STACKED && BurstFirePending(ps)) + *weaponTime = 0; + + if (*weaponTime <= 0) + { + if ((*weaponState == WEAPON_FIRING || *weaponState == WEAPON_CONT_FIRE_LOOP) && + WeaponUsesBurstCooldown(weaponIndex) && !BurstFirePending(ps)) + { + float scalar = player_burstFireCooldown->current.value; + + if (scalar != 0.0f) + *weaponTime = (int)(scalar * 1000.0f); + else + *weaponTime = 1; + + // Bugfix? BO2 uses ps->bRunLeftGun + PM_ContinueWeaponAnim(ps, 0, ps->bRunLeftGun); + *weaponState = WEAPON_READY; + return 0; + } + + bool pausedAfterFiring = !(ps->weapFlags & 0x400) && ShotLimitReached(ps, weapDef); + bool holdingGrenadeBtn = (weapDef->weapType == WEAPTYPE_GRENADE || weapDef->weapType == WEAPTYPE_MINE) && weapDef->holdButtonToThrow; + bool holdingFireBtn = false; + + if (ps->bRunLeftGun) + holdingFireBtn = pm->cmd.buttons.testBit(0x18) != 0; + else + holdingFireBtn = pm->cmd.buttons.testBit(0) != 0; + + if (*weaponState >= WEAPON_OFFHAND_INIT && *weaponState <= WEAPON_OFFHAND_END || !pausedAfterFiring && !holdingGrenadeBtn) + goto LABEL_105; + if (!holdingFireBtn) + goto LABEL_97; + if (ps->weapon == pm->cmd.weapon || !PM_WeaponAmmoAvailable(ps) && !weapDef->unlimitedAmmo) + { + LABEL_105: + if (holdingFireBtn && !(ps->weapFlags & 0x400)) + { + *weaponTime = 0; + goto LABEL_100; + } + LABEL_97: + if (!BurstFirePending(ps)) + *weaponShotCount = 0; + + *weaponTime = 0; + goto LABEL_100; + } + + *weaponTime = 1; + + if (IS_WEAPONSTATE_RELOAD(*weaponState)) + { + *weaponTime = 0; + *weaponShotCount = 0; + } + else if (*weaponState == WEAPON_RECHAMBERING) + { + PM_Weapon_FinishRechamber(ps); + } + else if (IS_WEAPONSTATE_FIRE(*weaponState)) + { + // Bugfix? BO2 uses ps->bRunLeftGun + PM_ContinueWeaponAnim(ps, 0, ps->bRunLeftGun); + *weaponState = WEAPON_READY; + } + } + } + +LABEL_100: + if (!*weaponDelay) + return 0; + + *weaponDelay -= msec; + + if (*weaponDelay > 0) + return 0; + + *weaponDelay = 0; + return 1; +} + +// /bgame/bg_weapons.cpp:3276 +void PM_Weapon_Jam(/*playerState_s *ps*/) +{ + // Do nothing. ESI is argument. +} + +// /bgame/bg_weapons.cpp:5958 +WeaponVariantDef *BG_LoadWeaponVariantDef(const char *name) +{ + // Load from rawfiles: BG_LoadWeaponVariantDef_LoadObj + if (!useFastFile->current.enabled) + return ((WeaponVariantDef *(__cdecl *)(const char *))0x00768110)(name); + + // Load from fastfile: BG_LoadWeaponVariantDef_FastFile + return ((WeaponVariantDef *(__cdecl *)(const char *))0x00768170)(name); +} + +// /bgame/bg_weapons.cpp:6072 +const char *BG_WeaponName(int weapon) +{ + return BG_GetWeaponVariantDef(weapon)->szInternalName; +} \ No newline at end of file diff --git a/components/game_mod/bg_weapons.h b/components/game_mod/bg_weapons.h new file mode 100644 index 00000000..5eb1dc24 --- /dev/null +++ b/components/game_mod/bg_weapons.h @@ -0,0 +1,161 @@ +#pragma once + +enum weaponstate_t +{ + WEAPON_READY = 0x0, + WEAPON_RAISING = 0x1, + WEAPON_RAISING_ALTSWITCH = 0x2, + WEAPON_DROPPING = 0x3, + WEAPON_DROPPING_QUICK = 0x4, + WEAPON_DROPPING_ALTSWITCH = 0x5, + WEAPON_FIRING = 0x6, + WEAPON_RECHAMBER_START = 0x7, + WEAPON_RECHAMBERING = 0x8, + WEAPON_RELOADING_RIGHT = 0x9, + WEAPON_RELOADING_LEFT = 0xA, + WEAPON_RELOADING = 0xB, + WEAPON_RELOADING_INTERUPT = 0xC, + WEAPON_RELOAD_START = 0xD, + WEAPON_RELOAD_START_INTERUPT = 0xE, + WEAPON_RELOAD_END = 0xF, + WEAPON_RELOAD_QUICK = 0x10, + WEAPON_RELOAD_QUICK_EMPTY = 0x11, + WEAPON_MELEE_INIT = 0x12, + WEAPON_MELEE_FIRE = 0x13, + WEAPON_MELEE_END = 0x14, + WEAPON_OFFHAND_INIT = 0x15, + WEAPON_OFFHAND_PREPARE = 0x16, + WEAPON_OFFHAND_HOLD = 0x17, + WEAPON_OFFHAND_START = 0x18, + WEAPON_OFFHAND = 0x19, + WEAPON_OFFHAND_END = 0x1A, + WEAPON_DETONATING = 0x1B, + WEAPON_SPRINT_RAISE = 0x1C, + WEAPON_SPRINT_LOOP = 0x1D, + WEAPON_SPRINT_DROP = 0x1E, + WEAPON_CONT_FIRE_IN = 0x1F, + WEAPON_CONT_FIRE_LOOP = 0x20, + WEAPON_CONT_FIRE_OUT = 0x21, + WEAPON_NIGHTVISION_WEAR = 0x22, + WEAPON_NIGHTVISION_REMOVE = 0x23, + WEAPON_DEPLOYING = 0x24, + WEAPON_DEPLOYED = 0x25, + WEAPON_BREAKING_DOWN = 0x26, + WEAPON_SWIM_IN = 0x27, + WEAPON_SWIM_OUT = 0x28, + WEAPON_DTP_IN = 0x29, + WEAPON_DTP_LOOP = 0x2A, + WEAPON_DTP_OUT = 0x2B, + WEAPON_SLIDE_IN = 0x2C, + WEAPON_FIRING_LEFT = 0x2D, + WEAPON_FIRING_BOTH = 0x2E, + WEAPON_JAMMED = 0x2F, + WEAPON_LOWREADY_RAISE = 0x30, + WEAPON_LOWREADY_LOOP = 0x31, + WEAPON_LOWREADY_DROP = 0x32, + WEAPONSTATES_NUM = 0x33, +}; + +#define IS_WEAPONSTATE_RELOAD(x) (\ + (x) == WEAPON_RELOADING || \ + (x) == WEAPON_RELOADING_INTERUPT || \ + (x) == WEAPON_RELOAD_START || \ + (x) == WEAPON_RELOAD_START_INTERUPT || \ + (x) == WEAPON_RELOAD_END || \ + (x) == WEAPON_RELOAD_QUICK || \ + (x) == WEAPON_RELOAD_QUICK_EMPTY) + +#define IS_WEAPONSTATE_MELEE(x) (\ + (x) == WEAPON_MELEE_INIT || \ + (x) == WEAPON_MELEE_FIRE || \ + (x) == WEAPON_MELEE_END) + +#define IS_WEAPONSTATE_FIRE(x) (\ + (x) == WEAPON_FIRING || \ + (x) == WEAPON_RECHAMBER_START || \ + (x) == WEAPON_RECHAMBERING || \ + (x) == WEAPON_CONT_FIRE_LOOP || \ + IS_WEAPONSTATE_MELEE(x)) + +struct usercmd_s +{ + int serverTime; + bitarray<51> buttons; + int angles[3]; + unsigned __int16 weapon; + + // The rest omitted +}; +STATIC_ASSERT_OFFSET(usercmd_s, serverTime, 0x0); +STATIC_ASSERT_OFFSET(usercmd_s, buttons, 0x4); +STATIC_ASSERT_OFFSET(usercmd_s, angles, 0xC); +STATIC_ASSERT_OFFSET(usercmd_s, weapon, 0x18); + +struct pmove_t +{ + playerState_s *ps; + usercmd_s cmd; + + // The rest omitted +}; +STATIC_ASSERT_OFFSET(pmove_t, ps, 0x0); +STATIC_ASSERT_OFFSET(pmove_t, cmd, 0x4); + +struct pml_t +{ + float forward[3]; + float right[3]; + float up[3]; + float frametime; + int msec; + + // The rest omitted +}; +STATIC_ASSERT_OFFSET(pml_t, msec, 0x28); + +void PM_StartWeaponAnim(playerState_s *ps, int anim, int leftAnim); +void PM_ContinueWeaponAnim(playerState_s *ps, int anim, int leftAnim); +int *BG_GetWeaponState(playerState_s *ps, bool leftGun); +int *BG_GetWeaponTime(playerState_s *ps, bool leftGun); +int *BG_GetWeaponDelay(playerState_s *ps, bool leftGun); +unsigned int *BG_GetWeaponShotCount(playerState_s *ps, bool leftGun); +void PM_Weapon_FinishRechamber(playerState_s *ps); +unsigned int PM_GetWeaponIndexForHand(playerState_s *ps); +bool BG_CanFastSwitch(WeaponDef *weapDef, int weaponState); +bool ShotLimitReached(playerState_s *ps, WeaponDef *weapDef); +bool BurstFirePending(playerState_s *ps); +bool WeaponUsesBurstCooldown(unsigned int weaponIdx); +int PM_Weapon_WeaponTimeAdjust(pmove_t *pm, pml_t *pml); +void PM_Weapon_Jam(/*playerState_s *ps*/); +WeaponVariantDef *BG_LoadWeaponVariantDef(const char *name); +const char *BG_WeaponName(int weapon); + +// /bgame/bg_weapons.h:300 +static int BG_GetHeldWeaponSlot(playerState_s *ps, int weapon) +{ + ASSERT(ps); + + for (int i = 0; i < ARRAYSIZE(ps->heldWeapons); i++) + { + if (ps->heldWeapons[i].weapon == weapon) + return i; + } + + return -1; +} + +// /bgame/bg_weapons.h:??? +static PlayerHeldWeapon *BG_GetHeldWeapon(playerState_s *ps, int weapon) +{ + ASSERT(ps); + + if (weapon == 0) + return nullptr; + + int index = BG_GetHeldWeaponSlot(ps, weapon); + + if (index == -1) + return nullptr; + + return &ps->heldWeapons[index]; +} \ No newline at end of file diff --git a/components/game_mod/bg_weapons_ammo.cpp b/components/game_mod/bg_weapons_ammo.cpp new file mode 100644 index 00000000..dd811528 --- /dev/null +++ b/components/game_mod/bg_weapons_ammo.cpp @@ -0,0 +1,429 @@ +#include "stdafx.h" + +WeaponVariantDef *bg_weapAmmoTypes[2048]; +WeaponDef *bg_sharedAmmoCaps[2048]; +WeaponVariantDef *bg_weapClips[2048]; + +// /bgame/bg_weapons_ammo.cpp:39 +void BG_SetupWeaponDefAmmoIndexes(unsigned int weapIndex, WeaponDef *weapDef, WeaponVariantDef *weapVarDef) +{ + ASSERT(weapDef); + + if (weapDef->sharedAmmo) + { + for (unsigned int index = 0; index < bg_numAmmoTypes; ++index) + { + if (!strcmp(bg_weapAmmoTypes[index]->szAmmoName, weapVarDef->szAmmoName)) + { + weapVarDef->iAmmoIndex = index; + return; + } + } + } + + // Check if this weapon was dual-wield + if (weapVarDef->iAmmoIndex < 0 + || weapVarDef->iAmmoIndex >= (int)bg_numAmmoTypes + || bg_weapAmmoTypes[weapVarDef->iAmmoIndex] != weapVarDef) + { + WeaponVariantDef *dwWeaponVarDef; + unsigned int dwIndex; + + if (weapDef->bDualWield + && (dwIndex = BG_GetWeaponIndexForName(weapDef->szDualWieldWeaponName)) != 0 + && (dwWeaponVarDef = BG_GetWeaponVariantDef(dwIndex), dwWeaponVarDef->iAmmoIndex >= 0) + && dwWeaponVarDef->iAmmoIndex < (int)bg_numAmmoTypes + && bg_weapAmmoTypes[dwWeaponVarDef->iAmmoIndex] == dwWeaponVarDef) + { + weapVarDef->iAmmoIndex = dwWeaponVarDef->iAmmoIndex; + } + else + { + ASSERT_MSG(bg_numWeapClips < ARRAYSIZE(bg_weapClips), "bg_numWeapClips doesn't index ARRAY_COUNT(bg_weapClips)"); + + bg_weapAmmoTypes[bg_numAmmoTypes] = weapVarDef; + weapVarDef->iAmmoIndex = bg_numAmmoTypes++; + } + } +} + +// /bgame/bg_weapons_ammo.cpp:86 +void BG_SetupWeaponDefSharedAmmoIndexes(unsigned int weapIndex, WeaponDef *weapDef) +{ + ASSERT(weapDef); + ASSERT(weapDef->szSharedAmmoCapName); + + weapDef->iSharedAmmoCapIndex = -1; + + if (!*weapDef->szSharedAmmoCapName) + return; + + Com_DPrintf(17, "%s: %s\n", BG_WeaponName(weapIndex), weapDef->szSharedAmmoCapName); + + unsigned int index = 0; + for (;; index++) + { + // Check if this was a new entry + if (index >= bg_numSharedAmmoCaps) + { + bg_sharedAmmoCaps[index] = weapDef; + weapDef->iSharedAmmoCapIndex = index; + bg_numSharedAmmoCaps++; + return; + } + + // Duplicate entry, now check for conflicting ammo counts + if (!_stricmp(bg_sharedAmmoCaps[index]->szSharedAmmoCapName, weapDef->szSharedAmmoCapName)) + break; + } + + weapDef->iSharedAmmoCapIndex = index; + + if (bg_sharedAmmoCaps[index]->iSharedAmmoCap != weapDef->iSharedAmmoCap && index) + { + for (unsigned int otherWeapIndex = 1; otherWeapIndex < weapIndex; otherWeapIndex++) + { + WeaponDef *otherWeapDef = BG_GetWeaponDef(otherWeapIndex); + + if (!_stricmp(bg_sharedAmmoCaps[index]->szSharedAmmoCapName, otherWeapDef->szSharedAmmoCapName) + && otherWeapDef->iSharedAmmoCap == bg_sharedAmmoCaps[index]->iSharedAmmoCap) + { + Com_Error(ERR_DROP, "Shared ammo cap mismatch for \"%s\" shared ammo cap: '%s\" set it to %i, but \"%s\" already set it to %i.", + weapDef->szSharedAmmoCapName, + BG_WeaponName(weapIndex), + weapDef->iSharedAmmoCap, + BG_WeaponName(otherWeapIndex), + otherWeapDef->iSharedAmmoCap); + } + } + + ASSERT_MSG(false, "unreachable"); + } +} + +// /bgame/bg_weapons_ammo.cpp:134 +void BG_SetupWeaponDefClipIndexes(WeaponDef *weapDef, WeaponVariantDef *weapVarDef) +{ + ASSERT(weapDef); + + if (weapDef->unlimitedAmmo) + { + // Nothing to use/draw with unlimited ammo weapons + weapVarDef->iClipIndex = -1; + return; + } + + // Determine shared ammo clip index + if (weapDef->sharedAmmo) + { + for (unsigned int index = 0; index < bg_numWeapClips; index++) + { + if (!strcmp(bg_weapClips[index]->szClipName, weapVarDef->szClipName)) + { + weapVarDef->iClipIndex = index; + return; + } + } + } + + // Add a new entry if this was a new clip type + if (weapVarDef->iClipIndex < 0 + || weapVarDef->iClipIndex >= (int)bg_numWeapClips + || bg_weapClips[weapVarDef->iClipIndex] != weapVarDef) + { + ASSERT_MSG(bg_numWeapClips < ARRAYSIZE(bg_weapClips), "bg_numWeapClips doesn't index ARRAY_COUNT(bg_weapClips)"); + + bg_weapClips[bg_numWeapClips] = weapVarDef; + weapVarDef->iClipIndex = bg_numWeapClips++; + } +} + +// /bgame/bg_weapons_ammo.cpp:170 +void BG_ClearWeaponDefAmmo() +{ + WeaponVariantDef *defaultWeap = BG_LoadDefaultWeaponVariantDef(); + + bg_weapAmmoTypes[0] = defaultWeap; + bg_numAmmoTypes = 1; + + bg_sharedAmmoCaps[0] = defaultWeap->weapDef; + bg_numSharedAmmoCaps = 1; + + bg_weapClips[0] = defaultWeap; + bg_numWeapClips = 1; +} + +// /bgame/bg_weapons_ammo.cpp:194 +int BG_GetSharedAmmoCapSize(unsigned int capIndex) +{ + if (capIndex < bg_numSharedAmmoCaps) + return bg_sharedAmmoCaps[capIndex]->iSharedAmmoCap; + + ASSERT_MSG(capIndex < bg_numSharedAmmoCaps, "capIndex doesn't index bg_numSharedAmmoCaps"); + return 0; +} + +// /bgame/bg_weapons_ammo.cpp:257 +AmmoClip *BG_GetAmmoClip(playerState_s *ps, int clipIndex) +{ + ASSERT(ps); + + for (int slot = 0; slot < ARRAYSIZE(ps->ammoInClip); slot++) + { + if (ps->ammoInClip[slot].clipIndex == clipIndex) + return &ps->ammoInClip[slot]; + } + + return nullptr; +} + +// /bgame/bg_weapons_ammo.cpp:272 +AmmoClip *BG_GetFreeAmmoClip(playerState_s *ps, int clipIndex) +{ + ASSERT(ps); + + for (int slot = 0; slot < ARRAYSIZE(ps->ammoInClip); slot++) + { + // If we find a free/empty slot, zero it and return + if (!ps->ammoInClip[slot].clipIndex) + { + memset(&ps->ammoInClip[slot], 0, sizeof(AmmoClip)); + return &ps->ammoInClip[slot]; + } + } + + for (int slot = 0; slot < ARRAYSIZE(ps->ammoInClip); slot++) + { + AmmoClip *clip = &ps->ammoInClip[slot]; + + ASSERT(clip->clipIndex != 0); + + bool matched = false; + for (int weaponSlot = 0; weaponSlot < ARRAYSIZE(ps->heldWeapons); weaponSlot++) + { + if (BG_GetWeaponVariantDef(ps->heldWeapons[weaponSlot].weapon)->iClipIndex == clip->clipIndex) + { + Com_PrintError(17, "%i ammo for %s\n", clip->count, BG_WeaponName(ps->heldWeapons[weaponSlot].weapon)); + matched = true; + break; + } + } + + if (!matched) + Com_PrintError(17, "Found ammo clip %i that we do not have a weapon for\n", slot); + } + + return nullptr; +} + +// /bgame/bg_weapons_ammo.cpp:318 +AmmoClip *BG_AddAmmoClip(playerState_s *ps, int clipIndex) +{ + ASSERT(ps); + + AmmoClip *clip = BG_GetAmmoClip(ps, clipIndex); + + // Wasn't found, find the next free ammo clip index + if (!clip) + { + clip = BG_GetFreeAmmoClip(ps, clipIndex); + clip->clipIndex = clipIndex; + } + + return clip; +} + +// /bgame/bg_weapons_ammo.cpp:334 +void BG_AddAmmoToClip(playerState_s *ps, int clipIndex, int amount) +{ + ASSERT(ps); + + AmmoClip *clip = BG_AddAmmoClip(ps, clipIndex); + ASSERT_MSG_VA(clip, "Tried to add ammo for clipIndex %i but there are no free available clips", clipIndex); + + if ((amount + clip->count) <= 0) + clip->count = 0; + else + clip->count += amount; + + // Stockpile perk: the player can have one extra regular or special grenade + for (int slot = 0; slot < ARRAYSIZE(ps->heldWeapons); slot++) + { + WeaponVariantDef *weapVariantDef = BG_GetWeaponVariantDef(ps->heldWeapons[slot].weapon); + + if (weapVariantDef->iClipIndex != clipIndex) + continue; + + // Was this one of the variants we patched (BG_SetupWeaponIndex)? If not, skip it + if (std::find(bg_PatchedVariantDefs.begin(), bg_PatchedVariantDefs.end(), weapVariantDef) == bg_PatchedVariantDefs.end()) + continue; + + // Perk enabled: clipMax + // Perk disabled: clipMax - 1 + int maximum = weapVariantDef->iClipSize; + + if (!BG_HasPerk(ps->perks, PERK_STOCKPILE)) + maximum -= 1; + + clip->count = min(clip->count, maximum); + break; + } +} + +// /bgame/bg_weapons_ammo.cpp:470 +int BG_GetAmmoPlayerMax(playerState_s *ps, unsigned int weaponIndex, unsigned int weaponIndexToSkip) +{ + WeaponDef *weapDef = BG_GetWeaponDef(weaponIndex); + WeaponVariantDef *weapVarDef = BG_GetWeaponVariantDef(weaponIndex); + + if (weapDef->iSharedAmmoCapIndex >= 0) + return BG_GetSharedAmmoCapSize(weapDef->iSharedAmmoCapIndex); + + if (BG_WeaponIsClipOnly(weaponIndex)) + return BG_GetClipSize(weaponIndex); + + int total = 0; + for (int slot = 0; slot < ARRAYSIZE(ps->heldWeapons); slot++) + { + int thisWeapIdx = ps->heldWeapons[slot].weapon; + + if (thisWeapIdx == weaponIndexToSkip) + continue; + + WeaponDef *thisWeapDef = BG_GetWeaponDef(thisWeapIdx); + + if (BG_GetWeaponVariantDef(thisWeapIdx)->iAmmoIndex == weapVarDef->iAmmoIndex && + (thisWeapDef->inventoryType != WEAPINVENTORY_ALTMODE || BG_GetWeaponVariantDef(thisWeapIdx)->altWeaponIndex != thisWeapIdx)) + { + if (thisWeapDef->iSharedAmmoCapIndex >= 0) + return BG_GetSharedAmmoCapSize(thisWeapDef->iSharedAmmoCapIndex); + + total += BG_GetMaxAmmo(thisWeapIdx); + + // Stockpile perk: all of the player's weapons can store two extra magazines + if (BG_HasPerk(ps->perks, PERK_STOCKPILE)) + total += (BG_GetClipSize(thisWeapIdx) * 2); + } + } + + return total; +} + +// /bgame/bg_weapons_ammo.cpp:513 +bool ValueInArray(const int *weaponArray, int value) +{ + for (int slot = 0; slot < 15; slot++) + { + if (weaponArray[slot] == value) + return true; + } + + return false; +} + +// /bgame/bg_weapons_ammo.cpp:525 +void AddValueToArray(int *weaponArray, int value) +{ + for (int slot = 0; slot < 15; slot++) + { + if (weaponArray[slot]) + continue; + + weaponArray[slot] = value; + return; + } + + ASSERT(false); +} + +// /bgame/bg_weapons_ammo.cpp:542 +int BG_GetMaxPickupableAmmo(playerState_s *ps, unsigned int weaponIndex) +{ + int clipCounted[15]; + int ammoCounted[15]; + + memset(ammoCounted, 0, sizeof(clipCounted)); + memset(clipCounted, 0, sizeof(ammoCounted)); + + WeaponDef *weapDef = BG_GetWeaponDef(weaponIndex); + + // If this weapon uses part of the shared ammo pool + if (weapDef->iSharedAmmoCapIndex >= 0) + { + int ammo = BG_GetSharedAmmoCapSize(weapDef->iSharedAmmoCapIndex); + + for (int slot = 0; slot < ARRAYSIZE(ps->heldWeapons); slot++) + { + unsigned int currWeap = ps->heldWeapons[slot].weapon; + + if (!currWeap) + continue; + + WeaponDef *curWeapDef = BG_GetWeaponDef(currWeap); + + if (curWeapDef->iSharedAmmoCapIndex != weapDef->iSharedAmmoCapIndex) + continue; + + if (BG_WeaponIsClipOnly(currWeap)) + { + if (!ValueInArray(clipCounted, BG_ClipForWeapon(currWeap))) + { + AddValueToArray(clipCounted, BG_ClipForWeapon(currWeap)); + ammo -= BG_GetAmmoInClip(ps, currWeap); + } + } + else + { + if (!ValueInArray(ammoCounted, BG_AmmoForWeapon(currWeap))) + { + AddValueToArray(ammoCounted, BG_AmmoForWeapon(currWeap)); + ammo -= BG_GetAmmoNotInClip(ps, currWeap); + } + } + } + + return ammo; + } + + // Simple check if this weapon has no ammo stock (clip only) + if (BG_WeaponIsClipOnly(weaponIndex)) + return BG_GetClipSize(weaponIndex) - BG_GetAmmoInClip(ps, weaponIndex); + + // Otherwise do the normal calculation with clip/stock + int maxAmmo = BG_GetAmmoPlayerMax(ps, weaponIndex, 0); + return maxAmmo - BG_GetAmmoNotInClip(ps, weaponIndex); +} + +// /bgame/bg_weapons_ammo.cpp:660 +int BG_ClipForWeapon(int weapon) +{ + return BG_GetWeaponVariantDef(weapon)->iClipIndex; +} + +// /bgame/bg_weapons_ammo.cpp:665 +int BG_AmmoForWeapon(int weapon) +{ + return BG_GetWeaponVariantDef(weapon)->iAmmoIndex; +} + +// /bgame/bg_weapons_ammo.cpp:670 +int BG_WeaponIsClipOnly(int weapon) +{ + return BG_GetWeaponDef(weapon)->bClipOnly; +} + +// /bgame/bg_weapons_ammo.cpp:796 +void PM_WeaponUseAmmo(playerState_s *ps, int wp, int amount) +{ + if (player_sustainAmmo->current.enabled) + return; + + int usedAmmo = min(BG_GetAmmoInClip(ps, wp), amount); + BG_AddAmmoToClip(ps, BG_GetWeaponVariantDef(wp)->iClipIndex, -usedAmmo); +} + +// /bgame/bg_weapons_ammo.cpp:805 +int PM_WeaponAmmoAvailable(playerState_s *ps) +{ + return BG_GetAmmoInClip(ps, PM_GetWeaponIndexForHand(ps)); +} \ No newline at end of file diff --git a/components/game_mod/bg_weapons_ammo.h b/components/game_mod/bg_weapons_ammo.h new file mode 100644 index 00000000..75b8b7b0 --- /dev/null +++ b/components/game_mod/bg_weapons_ammo.h @@ -0,0 +1,96 @@ +#pragma once + +extern WeaponVariantDef *bg_weapAmmoTypes[2048]; +extern WeaponDef *bg_sharedAmmoCaps[2048]; +extern WeaponVariantDef *bg_weapClips[2048]; + +static unsigned int& bg_numAmmoTypes = *(unsigned int *)0x00BDF794; +static unsigned int& bg_numSharedAmmoCaps = *(unsigned int *)0x00BDFD98; +static unsigned int& bg_numWeapClips = *(unsigned int *)0x00BDF790; + +void BG_SetupWeaponDefAmmoIndexes(unsigned int weapIndex, WeaponDef *weapDef, WeaponVariantDef *weapVarDef); +void BG_SetupWeaponDefSharedAmmoIndexes(unsigned int weapIndex, WeaponDef *weapDef); +void BG_SetupWeaponDefClipIndexes(WeaponDef *weapDef, WeaponVariantDef *weapVarDef); +void BG_ClearWeaponDefAmmo(); +int BG_GetSharedAmmoCapSize(unsigned int capIndex); +AmmoClip *BG_GetAmmoClip(playerState_s *ps, int clipIndex); +AmmoClip *BG_GetFreeAmmoClip(playerState_s *ps, int clipIndex); +AmmoClip *BG_AddAmmoClip(playerState_s *ps, int clipIndex); +void BG_AddAmmoToClip(playerState_s *ps, int clipIndex, int amount); +int BG_GetAmmoPlayerMax(playerState_s *ps, unsigned int weaponIndex, unsigned int weaponIndexToSkip); +int BG_GetMaxPickupableAmmo(playerState_s *ps, unsigned int weaponIndex); +int BG_ClipForWeapon(int weapon); +int BG_AmmoForWeapon(int weapon); +int BG_WeaponIsClipOnly(int weapon); +void PM_WeaponUseAmmo(playerState_s *ps, int wp, int amount); +int PM_WeaponAmmoAvailable(playerState_s *ps); + +// /bgame/bg_weapons_ammo.h:42 +static int BG_GetClipSize(int weaponIndex) +{ + WeaponVariantDef *weapVariantDef = BG_GetWeaponVariantDef(weaponIndex); + + if (weapVariantDef->iClipSize <= 1) + return weapVariantDef->iClipSize; + + int clipSize = (int)((float)weapVariantDef->iClipSize * player_clipSizeMultiplier->current.value); + + // Minimum size of one bullet after modifiers + return max(clipSize, 1); +} + +// /bgame/bg_weapons_ammo.h:69 +static int BG_GetMaxAmmo(int weapon) +{ + WeaponDef *weap = BG_GetWeaponDef(weapon); + + if (weap->ammoCountClipRelative) + return weap->iMaxAmmo * BG_GetClipSize(weapon); + + return weap->iMaxAmmo; +} + +// /bgame/bg_weapons_ammo.h:118 +static int BG_GetAmmoInClipForWeaponDef(playerState_s *ps, WeaponVariantDef *weapVarDef) +{ + ASSERT(ps); + ASSERT(weapVarDef); + + for (int i = 0; i < ARRAYSIZE(ps->ammoInClip); i++) + { + if (ps->ammoInClip[i].clipIndex == weapVarDef->iClipIndex) + return ps->ammoInClip[i].count; + } + + return 0; +} + +// /bgame/bg_weapons_ammo.h:129 +static int BG_GetAmmoInClip(playerState_s *ps, unsigned int weaponIndex) +{ + ASSERT(ps); + + return BG_GetAmmoInClipForWeaponDef(ps, BG_GetWeaponVariantDef(weaponIndex)); +} + +// /bgame/bg_weapons_ammo.h:137 +static int BG_GetAmmoNotInClipForAmmoIndex(playerState_s *ps, int ammoIndex) +{ + ASSERT(ps); + + for (int slot = 0; slot < ARRAYSIZE(ps->ammoNotInClip); slot++) + { + if (ps->ammoNotInClip[slot].ammoIndex == ammoIndex) + return ps->ammoNotInClip[slot].count; + } + + return 0; +} + +// /bgame/bg_weapons_ammo.h:152 +static int BG_GetAmmoNotInClip(playerState_s *ps, unsigned int weaponIndex) +{ + ASSERT(ps); + + return BG_GetAmmoNotInClipForAmmoIndex(ps, BG_AmmoForWeapon(weaponIndex)); +} \ No newline at end of file diff --git a/components/game_mod/bg_weapons_def.cpp b/components/game_mod/bg_weapons_def.cpp new file mode 100644 index 00000000..2d8c98ea --- /dev/null +++ b/components/game_mod/bg_weapons_def.cpp @@ -0,0 +1,417 @@ +#include "stdafx.h" + +WeaponVariantDef *bg_weaponVariantDefs[2048]; +WeaponVariantDefHash bg_weaponVariantNameHashTable[2048]; + +std::vector bg_CheckedVariantDefs; +std::vector bg_PatchedVariantDefs; +std::vector bg_PatchedWeapDefs; + +// /bgame/bg_weapons_def.cpp:57 +unsigned int BG_GetNumWeapons() +{ + return bg_lastParsedWeaponIndex + 1; +} + +// /bgame/bg_weapons_def.cpp:62 +WeaponVariantDef *BG_GetWeaponVariantDef(unsigned int weaponIndex) +{ + ASSERT_MSG(weaponIndex >= 0 && weaponIndex <= bg_lastParsedWeaponIndex, "weaponIndex not in [0, bg_lastParsedWeaponIndex]"); + + return bg_weaponVariantDefs[weaponIndex]; +} + +// /bgame/bg_weapons_def.cpp:70 +WeaponDef *BG_GetWeaponDef(unsigned int weaponIndex) +{ + return BG_GetWeaponVariantDef(weaponIndex)->weapDef; +} + +// /bgame/bg_weapons_def.cpp:83 +unsigned int BG_GetWeaponIndex(WeaponVariantDef *weapVariantDef) +{ + ASSERT(weapVariantDef); + + for (unsigned int weapIndex = 0; weapIndex <= bg_lastParsedWeaponIndex; weapIndex++) + { + if (weapVariantDef == bg_weaponVariantDefs[weapIndex]) + return weapIndex; + } + + ASSERT_MSG(false, "Weapon variant not in table, unknown weapon index?!"); + return 0; +} + +// /bgame/bg_weapons_def.cpp:100 +void BG_FreeWeaponDefStrings() +{ + for (unsigned int i = 1; i <= bg_lastParsedWeaponIndex; i++) + { + WeaponVariantDef *weapVariantDef = bg_weaponVariantDefs[i]; + WeaponDef *weapDef = weapVariantDef->weapDef; + + ASSERT(weapVariantDef && weapDef); + + // Weapon hide tag names + for (int j = 0; j < 32; j++) + { + if (weapVariantDef->hideTags[j]) + SL_RemoveRefToString(SCRIPTINSTANCE_SERVER, weapVariantDef->hideTags[j]); + } + + // Weapon notetrack names + for (int j = 0; j < 20; j++) + { + if (weapDef->notetrackSoundMapKeys[j]) + SL_RemoveRefToString(SCRIPTINSTANCE_SERVER, weapDef->notetrackSoundMapKeys[j]); + + if (weapDef->notetrackSoundMapValues[j]) + SL_RemoveRefToString(SCRIPTINSTANCE_SERVER, weapDef->notetrackSoundMapValues[j]); + } + } +} + +// /bgame/bg_weapons_def.cpp:159 +void BG_FillInWeaponItems(unsigned int weapIndex) +{ + int result = 4 * weapIndex + 0xBDD2B8; + int v2 = 16; + do + { + *(DWORD *)result = 1; + result += 512; + --v2; + } while (v2); +} + +// /bgame/bg_weapons_def.cpp:179 +void BG_SetupWeaponAlts(unsigned int weapIndex, void(__cdecl *regWeap)(unsigned int)) +{ + ASSERT_MSG(weapIndex >= 0 && weapIndex <= bg_lastParsedWeaponIndex, "weapIndex not in [0, bg_lastParsedWeaponIndex]"); + + WeaponVariantDef *weapVariantDef = bg_weaponVariantDefs[weapIndex]; + weapVariantDef->altWeaponIndex = 0; + + if (*weapVariantDef->szAltWeaponName) + { + unsigned int altWeaponIndex = BG_GetWeaponIndexForName(weapVariantDef->szAltWeaponName, regWeap); + + if (!altWeaponIndex) + Com_Error(ERR_DROP, "Could not find altWeapon '%s' for weapon '%s'", weapVariantDef->szAltWeaponName, BG_WeaponName(weapIndex)); + + if (Com_SessionMode_IsZombiesGame() || weapVariantDef->weapDef->inventoryType != WEAPINVENTORY_ALTMODE) + weapVariantDef->altWeaponIndex = altWeaponIndex; + else + weapVariantDef->altWeaponIndex = 0; + } +} + +// /bgame/bg_weapons_def.cpp:208 +void BG_SetupDualWieldAlts(unsigned int weapIndex, void(__cdecl *regWeap)(unsigned int)) +{ + WeaponDef *weapDef = bg_weaponVariantDefs[weapIndex]->weapDef; + + // Don't care if not dual wield + if (!weapDef->bDualWield) + return; + + weapDef->dualWieldWeaponIndex = 0; + + if (*weapDef->szDualWieldWeaponName) + { + unsigned int altWeaponIndex = BG_GetWeaponIndexForName(weapDef->szDualWieldWeaponName, regWeap); + + if (!altWeaponIndex) + Com_Error(ERR_DROP, "Could not find alt Dual Wield Weapon '%s' for weapon '%s'",weapDef->szDualWieldWeaponName, BG_WeaponName(weapIndex)); + + weapDef->dualWieldWeaponIndex = altWeaponIndex; + } +} + +// /bgame/bg_weapons_def.cpp:231 +void BG_SetupWeaponMountedVersions(unsigned int weaponIndex, void(__cdecl *regWeap)(unsigned int)) +{ + WeaponDef *weapDef = bg_weaponVariantDefs[weaponIndex]->weapDef; + + weapDef->standMountedIndex = 0; + weapDef->crouchMountedIndex = 0; + weapDef->proneMountedIndex = 0; + + if (*weapDef->standMountedWeapdef) + { + unsigned int mountedWeaponIndex = BG_GetWeaponIndexForName(weapDef->standMountedWeapdef, regWeap); + + if (!mountedWeaponIndex) + Com_Error(ERR_DROP, "could not find standingWeapdef '%s' for weapon '%s' Please add the line 'weapon,sp/%s' to your level csv", + weapDef->standMountedWeapdef, + BG_WeaponName(weaponIndex), + weapDef->standMountedWeapdef); + + weapDef->standMountedIndex = mountedWeaponIndex; + } + + if (*weapDef->crouchMountedWeapdef) + { + unsigned int mountedWeaponIndex = BG_GetWeaponIndexForName(weapDef->crouchMountedWeapdef, regWeap); + + if (!mountedWeaponIndex) + Com_Error(ERR_DROP, "could not find crouchingWeapdef '%s' for weapon '%s' Please add the line 'weapon,sp/%s' to your level csv", + weapDef->crouchMountedWeapdef, + BG_WeaponName(weaponIndex), + weapDef->crouchMountedWeapdef); + + weapDef->crouchMountedIndex = mountedWeaponIndex; + } + + if (*weapDef->proneMountedWeapdef) + { + unsigned int mountedWeaponIndex = BG_GetWeaponIndexForName(weapDef->proneMountedWeapdef, regWeap); + + if (!mountedWeaponIndex) + Com_Error(ERR_DROP, "could not find proneWeapdef '%s' for weapon '%s' Please add the line 'weapon,sp/%s' to your level csv", + weapDef->proneMountedWeapdef, + BG_WeaponName(weaponIndex), + weapDef->proneMountedWeapdef); + + weapDef->proneMountedIndex = mountedWeaponIndex; + } +} + +// /bgame/bg_weapons_def.cpp:265 +void BG_InitDefaultWeaponDef() +{ + ASSERT(bg_lastParsedWeaponIndex == 0); + + WeaponVariantDef *defaultWeap = BG_LoadDefaultWeaponVariantDef(); + + bg_weaponVariantDefs[0] = defaultWeap; + bg_weaponVariantNameHashTable[0].weaponIndex = 0; + bg_weaponVariantNameHashTable[0].hash = StringTable_HashString(defaultWeap->szInternalName); + bg_weaponVariantNameHashTableSorted = false; +} + +// /bgame/bg_weapons_def.cpp:274 +void BG_ClearWeaponDef() +{ + bg_CheckedVariantDefs.clear(); + bg_PatchedVariantDefs.clear(); + bg_PatchedWeapDefs.clear(); + + BG_InitDefaultWeaponDef(); + + //for (int itemIdx = 1; itemIdx < 2048; itemIdx++) + // bg_itemlist[itemIdx].giType = 0; + + memset((void *)0xBDD2BC, 0, 0x1FFC); + + BG_ClearWeaponDefAmmo(); + BG_LoadPlayerAnimTypes(); + BG_LoadWeaponMergeSupport(); + BG_ClearWeaponDefInternal(); +} + +// /bgame/bg_weapons_def.cpp:298 +void BG_SetupWeaponIndex(unsigned int weapIndex) +{ + Com_DPrintf(0, "[Idx %04d] Setting up weapon %s...\n", weapIndex, bg_weaponVariantDefs[weapIndex]->szInternalName); + + // Override the default grenade limit (See PERK_STOCKPILE) + WeaponVariantDef *variant = BG_GetWeaponVariantDef(weapIndex); + + if (variant->weapDef->weapType == WEAPTYPE_GRENADE) + { + auto& usedDefs = bg_CheckedVariantDefs; + + // Make sure we've never used this variant before + if (std::find(usedDefs.begin(), usedDefs.end(), variant) == usedDefs.end()) + { + usedDefs.push_back(variant); + + // Some weapons (perk bottle/xbow/syrette) are classified as grenades, but they + // dont't have a valid clip size + if (variant->iClipSize == 3 || + variant->iClipSize == 4) + { + variant->weapDef->iStartAmmo += 1; + variant->weapDef->iMaxAmmo += 1; + variant->weapDef->iSharedAmmoCap += 1; + variant->iClipSize += 1; + + bg_PatchedVariantDefs.push_back(variant); + bg_PatchedWeapDefs.push_back(variant->weapDef); + } + } + } + + // Continue with normal setup + BG_SetUpAmmoForWeapon(weapIndex); + BG_FillInWeaponItems(weapIndex); +} + +// /bgame/bg_weapons_def.cpp:305 +int BG_SetupWeaponVariantDef(WeaponVariantDef *weapVariantDef, void(__cdecl *regWeap)(unsigned int)) +{ + ASSERT_MSG(bg_lastParsedWeaponIndex < ARRAYSIZE(bg_weaponVariantDefs), "bg_lastParsedWeaponIndex doesn't index ARRAY_COUNT(bg_weaponVariantDefs)"); + + unsigned int weapIndex = bg_lastParsedWeaponIndex + 1; + bg_lastParsedWeaponIndex = weapIndex; + + bg_weaponVariantDefs[weapIndex] = weapVariantDef; + bg_weaponVariantNameHashTable[weapIndex].weaponIndex = weapIndex; + bg_weaponVariantNameHashTable[weapIndex].hash = StringTable_HashString(bg_weaponVariantDefs[weapIndex]->szInternalName); + bg_weaponVariantNameHashTableSorted = false; + + BG_SetupWeaponIndex(weapIndex); + BG_SetupWeaponAlts(weapIndex, regWeap); + BG_SetupDualWieldAlts(weapIndex, regWeap); + BG_SetupWeaponMountedVersions(weapIndex, regWeap); + + if (regWeap) + regWeap(weapIndex); + + return weapIndex; +} + +// /bgame/bg_weapons_def.cpp:335 +void BG_FillInAllWeaponItems() +{ + BG_ResetWeaponUsedMasks(); + + for (unsigned int weaponIndex = 1; weaponIndex < BG_GetNumWeapons(); weaponIndex++) + BG_SetupWeaponIndex(weaponIndex); +} + +// /bgame/bg_weapons_ammo.cpp:348 +void BG_SetupAmmoIndexes(unsigned int weapIndex) +{ + BG_SetupWeaponDefAmmoIndexes(weapIndex, bg_weaponVariantDefs[weapIndex]->weapDef, bg_weaponVariantDefs[weapIndex]); +} + +// /bgame/bg_weapons_ammo.cpp:353 +void BG_SetupSharedAmmoIndexes(unsigned int weapIndex) +{ + BG_SetupWeaponDefSharedAmmoIndexes(weapIndex, bg_weaponVariantDefs[weapIndex]->weapDef); +} + +// /bgame/bg_weapons_ammo.cpp:358 +void BG_SetupClipIndexes(unsigned int weapIndex) +{ + BG_SetupWeaponDefClipIndexes(bg_weaponVariantDefs[weapIndex]->weapDef, bg_weaponVariantDefs[weapIndex]); +} + +// /bgame/bg_weapons_def.cpp:363 +void BG_SetUpAmmoForWeapon(unsigned int weapIndex) +{ + BG_SetupAmmoIndexes(weapIndex); + BG_SetupSharedAmmoIndexes(weapIndex); + BG_SetupClipIndexes(weapIndex); +} + +// /bgame/bg_weapons_def.cpp:371 +int BG_WeaponVariantNameHashCompare(const void *a, const void *b) +{ + auto weapA = (WeaponVariantDefHash *)a; + auto weapB = (WeaponVariantDefHash *)b; + + if (weapA->hash <= weapB->hash) + { + if (weapA->hash >= weapB->hash) + return 0; + + return -1; + } + + return 1; +} + +// /bgame/bg_weapons_def.cpp:385 +int BG_FindWeaponIndexForName(const char *name) +{ + if (!name) + return 0; + + // Hash the weapon name before comparing anything + int nameHash = StringTable_HashString(name); + + if (!bg_weaponVariantNameHashTableSorted) + { + std::qsort(bg_weaponVariantNameHashTable, bg_lastParsedWeaponIndex + 1, sizeof(WeaponVariantDefHash), BG_WeaponVariantNameHashCompare); + bg_weaponVariantNameHashTableSorted = true; + } + + // Binary search + WeaponVariantDefHash key; + key.hash = nameHash; + key.weaponIndex = 0; + + auto ret = (WeaponVariantDefHash *)std::bsearch( + &key, + bg_weaponVariantNameHashTable, + bg_lastParsedWeaponIndex + 1, + sizeof(WeaponVariantDefHash), + BG_WeaponVariantNameHashCompare); + + if (ret) + return ret->weaponIndex; + + // Weapon name wasn't found + return 0; +} + +// /bgame/bg_weapons_def.cpp:420 +bool BG_IsDefaultWeapon(const char *name) +{ + // If a mod is loaded or fastfiles are disabled + if ((fs_gameDirVar && *fs_gameDirVar->current.string) || !useFastFile->current.enabled) + return false; + + return DB_IsXAssetDefault(ASSET_TYPE_WEAPON, name); +} + +// /bgame/bg_weapons_def.cpp:429 +unsigned int BG_GetWeaponIndexForName(const char *name, void(__cdecl *regWeap)(unsigned int)) +{ + // The weapon "none" is hardcoded as 0 + if (!*name || !_stricmp(name, "none")) + return 0; + + // Check if the weapon was loaded already + unsigned int weapIndex = BG_FindWeaponIndexForName(name); + + if (weapIndex) + return weapIndex; + + // Try to load it + WeaponVariantDef *weapVariantDef = BG_LoadWeaponVariantDef(name); + + if (!weapVariantDef) + { + Com_DPrintf(17, "Couldn't find weapon \"%s\"\n", name); + return 0; + } + + // The default weapon also maps to index 0 + if (BG_IsDefaultWeapon(name)) + return 0; + + return BG_SetupWeaponVariantDef(weapVariantDef, regWeap); +} + +// /bgame/bg_weapons_def.cpp:456 +unsigned int BG_GetWeaponIndexForName(const char *name) +{ + return BG_GetWeaponIndexForName(name, nullptr); +} + +// /bgame/bg_weapons_def.cpp:??? +void BG_ResetWeaponUsedMasks() +{ + memset((void *)0x00BDF508, 0, 0x40); +} + +// /bgame/bg_weapons_def.cpp:??? +void BG_SetWeaponUsed(int clientIndex, int weapIndex) +{ + // Sets a bit flag if a player used the weapon at least once + ((DWORD *)0xBDF508)[weapIndex / 32 + 4 * clientIndex] |= 1 << weapIndex % 32; +} \ No newline at end of file diff --git a/components/game_mod/bg_weapons_def.h b/components/game_mod/bg_weapons_def.h new file mode 100644 index 00000000..9ed6b5dd --- /dev/null +++ b/components/game_mod/bg_weapons_def.h @@ -0,0 +1,249 @@ +#pragma once + +enum weapType_t +{ + WEAPTYPE_BULLET = 0x0, + WEAPTYPE_GRENADE = 0x1, + WEAPTYPE_PROJECTILE = 0x2, + WEAPTYPE_BINOCULARS = 0x3, + WEAPTYPE_GAS = 0x4, + WEAPTYPE_BOMB = 0x5, + WEAPTYPE_MINE = 0x6, + WEAPTYPE_MELEE = 0x7, + WEAPTYPE_NUM = 0x8, +}; + +enum weapClass_t +{ + WEAPCLASS_RIFLE = 0x0, + WEAPCLASS_MG = 0x1, + WEAPCLASS_SMG = 0x2, + WEAPCLASS_SPREAD = 0x3, + WEAPCLASS_PISTOL = 0x4, + WEAPCLASS_GRENADE = 0x5, + WEAPCLASS_ROCKETLAUNCHER = 0x6, + WEAPCLASS_TURRET = 0x7, + WEAPCLASS_NON_PLAYER = 0x8, + WEAPCLASS_GAS = 0x9, + WEAPCLASS_ITEM = 0xA, + WEAPCLASS_MELEE = 0xB, + WEAPCLASS_KILLSTREAK_ALT_STORED_WEAPON = 0xC, + WEAPCLASS_NUM = 0xD, +}; + +enum PenetrateType +{ + PENETRATE_TYPE_NONE = 0x0, + PENETRATE_TYPE_SMALL = 0x1, + PENETRATE_TYPE_MEDIUM = 0x2, + PENETRATE_TYPE_LARGE = 0x3, + PENETRATE_TYPE_COUNT = 0x4, +}; + +enum weapFireType_t +{ + WEAPON_FIRETYPE_FULLAUTO = 0x0, + WEAPON_FIRETYPE_SINGLESHOT = 0x1, + WEAPON_FIRETYPE_BURSTFIRE2 = 0x2, + WEAPON_FIRETYPE_BURSTFIRE3 = 0x3, + WEAPON_FIRETYPE_BURSTFIRE4 = 0x4, + WEAPON_FIRETYPE_STACKED = 0x5, + WEAPON_FIRETYPE_MINIGUN = 0x6, + WEAPON_FIRETYPECOUNT = 0x7, +}; + +enum weapInventoryType_t +{ + WEAPINVENTORY_PRIMARY = 0x0, + WEAPINVENTORY_OFFHAND = 0x1, + WEAPINVENTORY_ITEM = 0x2, + WEAPINVENTORY_ALTMODE = 0x3, + WEAPINVENTORY_MELEE = 0x4, + WEAPINVENTORYCOUNT = 0x5, +}; + +struct WeaponDef +{ + char _pad0[0x10]; + unsigned __int16 *notetrackSoundMapKeys; + unsigned __int16 *notetrackSoundMapValues; + char _pad1[0x4]; + weapType_t weapType; + weapClass_t weapClass; + PenetrateType penetrateType; + char _pad2[0x4]; + weapInventoryType_t inventoryType; + weapFireType_t fireType; + char _pad3[0x38]; + int offhandSlot; + char _pad4[0x108]; + const char *standMountedWeapdef; + const char *crouchMountedWeapdef; + const char *proneMountedWeapdef; + int standMountedIndex; + int crouchMountedIndex; + int proneMountedIndex; + char _pad5[0x17C]; + struct XModel **worldModel; + char _pad6[0xC]; + struct XModel *additionalMeleeModel; + char _pad7[0x1C]; + int iStartAmmo; + char _pad8[0x4]; + int iMaxAmmo; + int shotCount; + const char *szSharedAmmoCapName; + int iSharedAmmoCapIndex; + int iSharedAmmoCap; + bool unlimitedAmmo; + bool ammoCountClipRelative; + char _pad9[0x1F2]; + bool sharedAmmo; + bool bRifleBullet; + char _pad10[0x13]; + bool bClipOnly; + char _pad11[0x8]; + bool bDualWield; + char _pad12[0x25]; + const char *szDualWieldWeaponName; + unsigned int dualWieldWeaponIndex; + char _pad13[0x50]; + struct XModel *projectileModel; + char _pad14[0x41]; + bool bBulletImpactExplode; + char _pad15[0x10]; + bool holdButtonToThrow; + char _pad16[0x145]; + const char *szUseHintString; + const char *dropHintString; + int iUseHintStringIndex; + int dropHintStringIndex; + char _pad17[0x18]; + float fMinDamageRange; +}; +STATIC_ASSERT_OFFSET(WeaponDef, notetrackSoundMapKeys, 0x10); +STATIC_ASSERT_OFFSET(WeaponDef, notetrackSoundMapValues, 0x14); +STATIC_ASSERT_OFFSET(WeaponDef, weapType, 0x1C); +STATIC_ASSERT_OFFSET(WeaponDef, weapClass, 0x20); +STATIC_ASSERT_OFFSET(WeaponDef, penetrateType, 0x24); +STATIC_ASSERT_OFFSET(WeaponDef, inventoryType, 0x2C); +STATIC_ASSERT_OFFSET(WeaponDef, fireType, 0x30); +STATIC_ASSERT_OFFSET(WeaponDef, offhandSlot, 0x6C); +STATIC_ASSERT_OFFSET(WeaponDef, standMountedWeapdef, 0x178); +STATIC_ASSERT_OFFSET(WeaponDef, crouchMountedWeapdef, 0x17C); +STATIC_ASSERT_OFFSET(WeaponDef, proneMountedWeapdef, 0x180); +STATIC_ASSERT_OFFSET(WeaponDef, standMountedIndex, 0x184); +STATIC_ASSERT_OFFSET(WeaponDef, crouchMountedIndex, 0x188); +STATIC_ASSERT_OFFSET(WeaponDef, proneMountedIndex, 0x18C); +STATIC_ASSERT_OFFSET(WeaponDef, worldModel, 0x30C); +STATIC_ASSERT_OFFSET(WeaponDef, additionalMeleeModel, 0x31C); +STATIC_ASSERT_OFFSET(WeaponDef, iStartAmmo, 0x33C); +STATIC_ASSERT_OFFSET(WeaponDef, iMaxAmmo, 0x344); +STATIC_ASSERT_OFFSET(WeaponDef, shotCount, 0x348); +STATIC_ASSERT_OFFSET(WeaponDef, szSharedAmmoCapName, 0x34C); +STATIC_ASSERT_OFFSET(WeaponDef, iSharedAmmoCapIndex, 0x350); +STATIC_ASSERT_OFFSET(WeaponDef, iSharedAmmoCap, 0x354); +STATIC_ASSERT_OFFSET(WeaponDef, unlimitedAmmo, 0x358); +STATIC_ASSERT_OFFSET(WeaponDef, ammoCountClipRelative, 0x359); +STATIC_ASSERT_OFFSET(WeaponDef, sharedAmmo, 0x54C); +STATIC_ASSERT_OFFSET(WeaponDef, bRifleBullet, 0x54D); +STATIC_ASSERT_OFFSET(WeaponDef, bClipOnly, 0x561); +STATIC_ASSERT_OFFSET(WeaponDef, bDualWield, 0x56A); +STATIC_ASSERT_OFFSET(WeaponDef, szDualWieldWeaponName, 0x590); +STATIC_ASSERT_OFFSET(WeaponDef, dualWieldWeaponIndex, 0x594); +STATIC_ASSERT_OFFSET(WeaponDef, projectileModel, 0x5E8); +STATIC_ASSERT_OFFSET(WeaponDef, bBulletImpactExplode, 0x62D); +STATIC_ASSERT_OFFSET(WeaponDef, holdButtonToThrow, 0x63E); +STATIC_ASSERT_OFFSET(WeaponDef, szUseHintString, 0x784); +STATIC_ASSERT_OFFSET(WeaponDef, dropHintString, 0x788); +STATIC_ASSERT_OFFSET(WeaponDef, iUseHintStringIndex, 0x78C); +STATIC_ASSERT_OFFSET(WeaponDef, dropHintStringIndex, 0x790); +STATIC_ASSERT_OFFSET(WeaponDef, fMinDamageRange, 0x7AC); + +struct WeaponVariantDef +{ + const char *szInternalName; + char _pad1[0x4]; + WeaponDef *weapDef; + char _pad2[0x8]; + const char *szAltWeaponName; + unsigned __int16 *hideTags; + unsigned int altWeaponIndex; + int iClipSize; + char _pad4[0x1C]; + const char *szAmmoName; + int iAmmoIndex; + const char *szClipName; + int iClipIndex; +}; +STATIC_ASSERT_OFFSET(WeaponVariantDef, szInternalName, 0x0); +STATIC_ASSERT_OFFSET(WeaponVariantDef, weapDef, 0x8); +STATIC_ASSERT_OFFSET(WeaponVariantDef, szAltWeaponName, 0x14); +STATIC_ASSERT_OFFSET(WeaponVariantDef, hideTags, 0x18); +STATIC_ASSERT_OFFSET(WeaponVariantDef, altWeaponIndex, 0x1C); +STATIC_ASSERT_OFFSET(WeaponVariantDef, iClipSize, 0x20); +STATIC_ASSERT_OFFSET(WeaponVariantDef, szAmmoName, 0x40); +STATIC_ASSERT_OFFSET(WeaponVariantDef, iAmmoIndex, 0x44); +STATIC_ASSERT_OFFSET(WeaponVariantDef, szClipName, 0x48); +STATIC_ASSERT_OFFSET(WeaponVariantDef, iClipIndex, 0x4C); + +struct weaponParms +{ + float forward[3]; + float right[3]; + float up[3]; + float muzzleTrace[3]; + float gunForward[3]; + WeaponVariantDef *weapVariantDef; + WeaponDef *weapDef; +}; +STATIC_ASSERT_OFFSET(weaponParms, forward, 0x0); +STATIC_ASSERT_OFFSET(weaponParms, muzzleTrace, 0x24); +STATIC_ASSERT_OFFSET(weaponParms, weapVariantDef, 0x3C); +STATIC_ASSERT_OFFSET(weaponParms, weapDef, 0x40); + +struct WeaponVariantDefHash +{ + int hash; + int weaponIndex; +}; +STATIC_ASSERT_OFFSET(WeaponVariantDefHash, hash, 0x0); +STATIC_ASSERT_OFFSET(WeaponVariantDefHash, weaponIndex, 0x4); + +extern WeaponVariantDef *bg_weaponVariantDefs[2048]; +extern WeaponVariantDefHash bg_weaponVariantNameHashTable[2048]; + +static unsigned int& bg_firstWeaponTableIndex = *(unsigned int *)0x00BE1FA8; +static unsigned int& bg_lastWeaponTableIndex = *(unsigned int *)0x00BE1FAC; +static unsigned int& bg_lastParsedWeaponIndex = *(unsigned int *)0x00BE1FB0; +static bool& bg_weaponVariantNameHashTableSorted = *(bool *)0x00BE1FB4; + +extern std::vector bg_CheckedVariantDefs; +extern std::vector bg_PatchedVariantDefs; +extern std::vector bg_PatchedWeapDefs; + +unsigned int BG_GetNumWeapons(); +WeaponVariantDef *BG_GetWeaponVariantDef(unsigned int weaponIndex); +WeaponDef *BG_GetWeaponDef(unsigned int weaponIndex); +unsigned int BG_GetWeaponIndex(WeaponVariantDef *weapVariantDef); +void BG_FreeWeaponDefStrings(); +void BG_FillInWeaponItems(unsigned int weapIndex); +void BG_SetupWeaponAlts(unsigned int weapIndex, void(__cdecl *regWeap)(unsigned int)); +void BG_SetupDualWieldAlts(unsigned int weapIndex, void(__cdecl *regWeap)(unsigned int)); +void BG_SetupWeaponMountedVersions(unsigned int weaponIndex, void(__cdecl *regWeap)(unsigned int)); +void BG_InitDefaultWeaponDef(); +void BG_ClearWeaponDef(); +void BG_SetupWeaponIndex(unsigned int weapIndex); +int BG_SetupWeaponVariantDef(WeaponVariantDef *weapVariantDef, void(__cdecl *regWeap)(unsigned int)); +void BG_FillInAllWeaponItems(); +void BG_SetupAmmoIndexes(unsigned int weapIndex); +void BG_SetupSharedAmmoIndexes(unsigned int weapIndex); +void BG_SetupClipIndexes(unsigned int weapIndex); +void BG_SetUpAmmoForWeapon(unsigned int weapIndex); +int BG_WeaponVariantNameHashCompare(const void *a, const void *b); +int BG_FindWeaponIndexForName(const char *name); +bool BG_IsDefaultWeapon(const char *name); +unsigned int BG_GetWeaponIndexForName(const char *name, void(__cdecl *regWeap)(unsigned int)); +unsigned int BG_GetWeaponIndexForName(const char *name); +void BG_ResetWeaponUsedMasks(); +void BG_SetWeaponUsed(int clientIndex, int weapIndex); \ No newline at end of file diff --git a/components/game_mod/bg_weapons_load_obj.cpp b/components/game_mod/bg_weapons_load_obj.cpp new file mode 100644 index 00000000..2d6e08f2 --- /dev/null +++ b/components/game_mod/bg_weapons_load_obj.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" + +// /bgame/bg_weapons_load_obj.cpp:1504 +void BG_LoadPlayerAnimTypes() +{ + ((void(__cdecl *)())0x00591DB0)(); +} + +// /bgame/bg_weapons_load_obj.cpp:1574 +void BG_ClearWeaponDefInternal() +{ + // surfaceTypeSoundListCount + *(int *)0x00BE2D3C = 0; +} + +// /bgame/bg_weapons_load_obj.cpp:2316 +void BG_LoadWeaponMergeSupport() +{ + ((void(__cdecl *)())0x00513A70)(); +} + +// /bgame/bg_weapons_load_obj.cpp:2888 +WeaponVariantDef *BG_LoadDefaultWeaponVariantDef() +{ + // Load from rawfiles: BG_LoadDefaultWeaponVariantDef_LoadObj + if (!useFastFile->current.enabled) + return ((WeaponVariantDef *(__cdecl *)())0x0076C740)(); + + // Load from fastfile: BG_LoadDefaultWeaponVariantDef_FastFile + return ((WeaponVariantDef *(__cdecl *)())0x0076C7E0)(); +} \ No newline at end of file diff --git a/components/game_mod/bg_weapons_load_obj.h b/components/game_mod/bg_weapons_load_obj.h new file mode 100644 index 00000000..1fa66320 --- /dev/null +++ b/components/game_mod/bg_weapons_load_obj.h @@ -0,0 +1,6 @@ +#pragma once + +void BG_LoadPlayerAnimTypes(); +void BG_ClearWeaponDefInternal(); +void BG_LoadWeaponMergeSupport(); +WeaponVariantDef *BG_LoadDefaultWeaponVariantDef(); \ No newline at end of file diff --git a/components/game_mod/bitarray.h b/components/game_mod/bitarray.h new file mode 100644 index 00000000..cdc21a09 --- /dev/null +++ b/components/game_mod/bitarray.h @@ -0,0 +1,23 @@ +#pragma once + +template +class bitarray +{ +public: + enum + { + BITS_PER_WORD = sizeof(unsigned int) * 8, + WORD_COUNT = (BIT_COUNT + BITS_PER_WORD - 1) / BITS_PER_WORD, + }; + + // N bits -> array of size int(N/32) rounded up + unsigned int array[WORD_COUNT]; + + // /qcommon/bitarray.h:108 + bool testBit(unsigned int pos) + { + ASSERT(pos < BIT_COUNT); + + return (array[pos / BITS_PER_WORD] & (0x80000000 >> (pos & 0x1F))) != 0; + } +}; \ No newline at end of file diff --git a/components/game_mod/bullet.cpp b/components/game_mod/bullet.cpp new file mode 100644 index 00000000..9f48b43f --- /dev/null +++ b/components/game_mod/bullet.cpp @@ -0,0 +1,290 @@ +#include "stdafx.h" + +// /game/bullet.cpp:54 +float G_GoodRandomFloat(int *idum) +{ + // http://www.biostat.umn.edu/~cavanr/random.c ran1() + #define IA 16807 + #define IM 2147483647 + #define AM (1.0/IM) + #define IQ 127773 + #define IR 2836 + #define NTAB 32 + #define NDIV (1+(IM-1)/NTAB) + #define EPS 1.2e-7 + #define RNMX (1.0-EPS) + + long k; + int iv[NTAB]; + + *idum = -*idum; + + for (int j = NTAB + 7; j >= 0; j--) + { + k = (*idum) / IQ; + *idum = IA * (*idum - k * IQ) - IR * k; + + if (*idum < 0) + *idum += IM; + + if (j < NTAB) + iv[j] = *idum; + } + + k = (*idum) / IQ; + *idum = IA * (*idum - k * IQ) - IR * k; + + if (*idum < 0) + *idum += IM; + + if (RNMX - iv[iv[0] / NDIV] * 4.656612875245797e-10 < 0.0) + return (float)(RNMX); + + return (float)(iv[iv[0] / NDIV] * 4.656612875245797e-10); +} + +// /game/bullet.cpp:85 +void Bullet_RandomDir(unsigned int *randSeed, float *x, float *y) +{ + ASSERT(x); + ASSERT(y); + + // + // Original code before swapping to BO2: + // float theta = G_GoodRandomFloat(&time) * 360.0; + // float r = G_GoodRandomFloat(&time); + // + double theta = BG_random(randSeed) * 360.0; + BG_srand(randSeed); + double r = BG_random(randSeed); + double radians = (theta * 0.017453292); // PI / 180.0 + + *x = (float)(r * cos(radians)); + *y = (float)(r * sin(radians)); +} + +// /game/bullet.cpp:106 +void Bullet_Endpos(unsigned int *randSeed, float spread, float *end, float *dir, weaponParms *wp, float maxRange, int shotIndex, int maxShotIndex) +{ + ASSERT(!IS_NAN(spread)); + ASSERT(end); + ASSERT(wp); + + float aimOffset = tan(spread * 0.017453292f) * maxRange; + + ASSERT(!IS_NAN(aimOffset)); + + float right = 0.0f; + float up = 0.0f; + Bullet_RandomDir(randSeed, &right, &up); + + // BO2 weapon perk: bullet spread is horizontal only + if (Com_SessionMode_IsZombiesGame() && BG_WeaponHasPerk(wp->weapDef, 3)) + { + right = 0.0f; + up = 0.0f; + + if (shotIndex && maxShotIndex > 1) + { + float modifier = (shotIndex % 2) ? -1.0f : 1.0f; + right = (((spread * 0.5f) / (maxShotIndex >> 1)) * (((shotIndex - 1) >> 1) + 1)) * modifier; + } + } + + right *= aimOffset; + up *= aimOffset; + + ASSERT(!IS_NAN(right)); + ASSERT(!IS_NAN(up)); + + ASSERT(!IS_NAN(wp->muzzleTrace[0]) && !IS_NAN(wp->muzzleTrace[1]) && !IS_NAN(wp->muzzleTrace[2])); + ASSERT(!IS_NAN(wp->forward[0]) && !IS_NAN(wp->forward[1]) && !IS_NAN(wp->forward[2])); + ASSERT(!IS_NAN(wp->right[0]) && !IS_NAN(wp->right[1]) && !IS_NAN(wp->right[2])); + ASSERT(!IS_NAN(wp->up[0]) && !IS_NAN(wp->up[1]) && !IS_NAN(wp->up[2])); + + end[0] = (maxRange * wp->forward[0]) + wp->muzzleTrace[0]; + end[1] = (maxRange * wp->forward[1]) + wp->muzzleTrace[1]; + end[2] = (maxRange * wp->forward[2]) + wp->muzzleTrace[2]; + + ASSERT(!IS_NAN(end[0]) && !IS_NAN(end[1]) && !IS_NAN(end[2])); + + end[0] = (right * wp->right[0]) + end[0]; + end[1] = (right * wp->right[1]) + end[1]; + end[2] = (right * wp->right[2]) + end[2]; + + end[0] = (up * wp->up[0]) + end[0]; + end[1] = (up * wp->up[1]) + end[1]; + end[2] = (up * wp->up[2]) + end[2]; + + ASSERT(!IS_NAN(end[0]) && !IS_NAN(end[1]) && !IS_NAN(end[2])); + + if (dir) + { + dir[0] = end[0] - wp->muzzleTrace[0]; + dir[1] = end[1] - wp->muzzleTrace[1]; + dir[2] = end[2] - wp->muzzleTrace[2]; + Vec3Normalize(dir); + + ASSERT(!IS_NAN(dir[0]) && !IS_NAN(dir[1]) && !IS_NAN(dir[2])); + } +} + +// /game/bullet.cpp:655 +void Bullet_FireExtended(BulletFireParams *params, WeaponVariantDef *weapVariantDef, gentity_s *attacker, int gameTime) +{ + static DWORD dwCall = 0x007D42F0; + + __asm + { + push gameTime + push attacker + mov esi, weapVariantDef + mov edi, params + call [dwCall] + add esp, 0x8 + } +} + +// /game/bullet.cpp:721 +void Bullet_FirePenetrate(BulletFireParams *params, WeaponVariantDef *weapVariantDef, gentity_s *attacker, int gameTime) +{ + static DWORD dwCall = 0x007D3CD0; + + __asm + { + push gameTime + push attacker + push weapVariantDef + mov esi, params + call [dwCall] + add esp, 0xC + } +} + +// /game/bullet.cpp:869 +bool BG_WeaponBulletFire_ExplodeOnImpact(WeaponDef *weapDef) +{ + ASSERT(weapDef); + ASSERT(weapDef->weapType == WEAPTYPE_BULLET); + + return weapDef->bBulletImpactExplode || bg_forceExplosiveBullets->current.enabled; +} + +// /game/bullet.cpp:880 +bool BG_WeaponBulletFire_ShouldPenetrate(WeaponDef *weapDef) +{ + ASSERT(weapDef); + ASSERT(weapDef->weapType == WEAPTYPE_BULLET); + + if (weapDef->penetrateType != PENETRATE_TYPE_NONE) + return BG_WeaponBulletFire_ExplodeOnImpact(weapDef) == false; + + return false; +} + +// /game/bullet.cpp:897 +void Bullet_Fire(gentity_s *attacker, float spread, weaponParms *wp, gentity_s *weaponEnt, int gameTime) +{ + ASSERT(attacker); + ASSERT(wp); + ASSERT(wp->weapDef); + ASSERT(wp->weapDef->weapType == WEAPTYPE_BULLET); + + DWORD v22 = 0; + int shotCount = 1; + float range = sv_bullet_range->current.value; + + if (wp->weapDef->weapClass == WEAPCLASS_SPREAD) + { + shotCount = wp->weapDef->shotCount; + range = wp->weapDef->fMinDamageRange; + } + + if (attacker->client) + { + DWORD v11 = *(DWORD *)((DWORD)attacker->client + 0x1C84); + + if (v11) + { + v22 = *(DWORD *)(v11 + 0x100); + *(DWORD *)(v11 + 0x100) = 0; + } + + // Used for various stat tracking (GSC/mystery box) + BG_SetWeaponUsed(attacker->client->ps.clientNum, BG_GetWeaponIndex(wp->weapVariantDef)); + } + + // BO2 double-tap modification + if (Com_SessionMode_IsZombiesGame() && perk_weapRateEnhanced->current.enabled && attacker->s.eType == 1) + { + if (BG_HasPerk(attacker->client->ps.perks, PERK_RATEOFFIRE)) + shotCount *= 2; + } + + unsigned int randSeed = gameTime; + BG_seedRandWithGameTime(&randSeed); + + for (int shotIndex = 0; shotIndex < shotCount; shotIndex++) + { + BulletFireParams fireParams; + memset(&fireParams, 0, sizeof(fireParams)); + + if (weaponEnt) + { + fireParams.weaponEntIndex = weaponEnt->s.number; + fireParams.ignoreEntIndex = weaponEnt->s.number; + } + else + { + fireParams.weaponEntIndex = 1022; + fireParams.ignoreEntIndex = 1022; + } + + if (vehicle_selfCollision->current.enabled && weaponEnt && weaponEnt->vehicle) + fireParams.ignoreEntIndex = 1022; + + fireParams.damageMultiplier = 1.0f; + fireParams.methodOfDeath = wp->weapDef->bRifleBullet ? 2 : 1; + fireParams.origStart[0] = wp->muzzleTrace[0]; + fireParams.origStart[1] = wp->muzzleTrace[1]; + fireParams.origStart[2] = wp->muzzleTrace[2]; + fireParams.start[0] = wp->muzzleTrace[0]; + fireParams.start[1] = wp->muzzleTrace[1]; + fireParams.start[2] = wp->muzzleTrace[2]; + Bullet_Endpos(&randSeed, spread, fireParams.end, fireParams.dir, wp, range, shotIndex, shotCount); + + if (g_debugBullets->current.integer == 1) + CG_DebugLine(fireParams.start, fireParams.end, (float *)0x00A5E834, 1, 1000); + + if (BG_WeaponBulletFire_ShouldPenetrate(wp->weapDef)) + Bullet_FirePenetrate(&fireParams, wp->weapVariantDef, attacker, gameTime); + else + Bullet_FireExtended(&fireParams, wp->weapVariantDef, attacker, gameTime); + } + + if (attacker->client) + { + DWORD v20 = *(DWORD *)((DWORD)attacker->client + 0x1C84); + + if (v20) + *(DWORD *)(v20 + 0x100) = v22; + } +} + +void BG_seedRandWithGameTime(unsigned int *pHoldrand) +{ + unsigned int v1 = *pHoldrand << 8; + *pHoldrand ^= v1; + *pHoldrand ^= (v1 << 8) ^ (v1 << 16); +} + +void BG_srand(unsigned int *pHoldrand) +{ + *pHoldrand = 214013 * (214013 * (214013 * (214013 * *pHoldrand + 2531011) + 2531011) + 2531011) + 2531011; +} + +double BG_random(unsigned int *pHoldrand) +{ + *pHoldrand = 214013 * *pHoldrand + 2531011; + return (*pHoldrand >> 17) * (1.0 / 32768.0); +} \ No newline at end of file diff --git a/components/game_mod/bullet.h b/components/game_mod/bullet.h new file mode 100644 index 00000000..ec182e1d --- /dev/null +++ b/components/game_mod/bullet.h @@ -0,0 +1,34 @@ +#pragma once + +struct BulletFireParams +{ + int weaponEntIndex; + int ignoreEntIndex; + float damageMultiplier; + int methodOfDeath; + float origStart[3]; + float start[3]; + float end[3]; + float dir[3]; +}; +STATIC_ASSERT_OFFSET(BulletFireParams, weaponEntIndex, 0x0); +STATIC_ASSERT_OFFSET(BulletFireParams, ignoreEntIndex, 0x4); +STATIC_ASSERT_OFFSET(BulletFireParams, damageMultiplier, 0x8); +STATIC_ASSERT_OFFSET(BulletFireParams, methodOfDeath, 0xC); +STATIC_ASSERT_OFFSET(BulletFireParams, origStart, 0x10); +STATIC_ASSERT_OFFSET(BulletFireParams, start, 0x1C); +STATIC_ASSERT_OFFSET(BulletFireParams, end, 0x28); +STATIC_ASSERT_OFFSET(BulletFireParams, dir, 0x34); + +float G_GoodRandomFloat(int *idum); +void Bullet_RandomDir(unsigned int *randSeed, float *x, float *y); +void Bullet_Endpos(unsigned int *randSeed, float spread, float *end, float *dir, weaponParms *wp, float maxRange, int shotIndex, int maxShotIndex); +void Bullet_FireExtended(BulletFireParams *params, WeaponVariantDef *weapVariantDef, gentity_s *attacker, int gameTime); +void Bullet_FirePenetrate(BulletFireParams *params, WeaponVariantDef *weapVariantDef, gentity_s *attacker, int gameTime); +bool BG_WeaponBulletFire_ExplodeOnImpact(WeaponDef *weapDef); +bool BG_WeaponBulletFire_ShouldPenetrate(WeaponDef *weapDef); +void Bullet_Fire(gentity_s *attacker, float spread, weaponParms *wp, gentity_s *weaponEnt, int gameTime); + +void BG_seedRandWithGameTime(unsigned int *pHoldrand); +void BG_srand(unsigned int *pHoldrand); +double BG_random(unsigned int *pHoldrand); \ No newline at end of file diff --git a/components/game_mod/cg_draw_debug.cpp b/components/game_mod/cg_draw_debug.cpp new file mode 100644 index 00000000..5bd2cb85 --- /dev/null +++ b/components/game_mod/cg_draw_debug.cpp @@ -0,0 +1,255 @@ +#include "stdafx.h" + +VANILLA_VALUE(font, struct Font_s*, 0x02FF55F8); + +col_context_t::col_context_t(void) +{ + this->mask = 0; + this->prims = 0; + this->nprims = 0; + this->ignoreEntParams = 0; + this->passEntityNum0 = 1023; + this->passEntityNum1 = 1023; + this->locational = 0; + this->staticmodels = 0; + this->priorityMap = 0; + this->collide_entity_func = 0; +} + +col_context_t::col_context_t(int _mask) +{ + this->mask = _mask; + this->prims = 0; + this->nprims = 0; + this->ignoreEntParams = 0; + this->passEntityNum0 = 1023; + this->passEntityNum1 = 1023; + this->locational = 0; + this->staticmodels = 0; + this->priorityMap = 0; + this->collide_entity_func = 0; +} + +inline int __cdecl CG_DrawSmallDevStringColor(struct ScreenPlacement *scrPlace, float x, float y, const char *s, const float *color, int align) +{ + return CG_DrawDevString(scrPlace, x, y, 1.0, 1.0, s, color, align, font); +} +int __cdecl R_PickMaterial(int traceMask, const float *org, const float *dir, char *name, char *surfaceFlags, char *contents, int charLimit) +{ + float end[3]; + end[0] = dir[0] * 2621440.0f + org[0]; + end[1] = dir[1] * 2621440.0f + org[1]; + end[2] = dir[2] * 2621440.0f + org[2]; + + float* vec3_origin = (float*)0x00A5E370; + + col_context_t context; + trace_t trace; + trace.normal.vec.v[0] = 0.0f; + trace.normal.vec.v[1] = 0.0f; + trace.normal.vec.v[2] = 0.0f; + + CG_TraceCapsule(&trace, org, vec3_origin, vec3_origin, end, 0, 0x680CC31, &context); + + if (trace.startsolid || trace.allsolid) + return 0; + if (isnan(trace.fraction)) + return 0; + + sprintf_s(name, charLimit, ""); + + *surfaceFlags = 0; + surfaceFlags[charLimit - 1] = 0; + + int flags = (trace.sflags >> 20) & 0x1F; + if (flags - 1 > 0x1D) + strncpy_s(surfaceFlags, charLimit, "^1default^7", _TRUNCATE); + else + strncpy_s(surfaceFlags, charLimit, _infoParms[flags].name, _TRUNCATE); + + if (surfaceFlags[charLimit - 1]) + return 0; + + int surfaceFlagsLen = strlen(surfaceFlags); + if (trace.cflags & 1) + strncpy_s(contents, charLimit, "solid", _TRUNCATE); + else + strncpy_s(contents, charLimit, "^3nonsolid^7", _TRUNCATE); + + if (contents[charLimit - 1]) + return 0; + + int contentsLen = strlen(contents); + for (int i = 30; _infoParms[i].name; i++) + { + if (trace.sflags & _infoParms[i].surfaceFlags) + { + surfaceFlags[surfaceFlagsLen++] = ' '; + char* str = &surfaceFlags[surfaceFlagsLen]; + strncpy_s(str, charLimit - surfaceFlagsLen, _infoParms[i].name, _TRUNCATE); + + if (surfaceFlags[charLimit - 1]) + return 0; + + surfaceFlagsLen += strlen(str); + } + + if (trace.cflags * _infoParms[i].contents) + { + contents[contentsLen++] = ' '; + char* str = &contents[contentsLen]; + strncpy_s(str, charLimit - contentsLen, _infoParms[i].name, _TRUNCATE); + + if (contents[charLimit - 1]) + return 0; + + contentsLen += strlen(str); + } + } + + return 1; +} + +void CG_DrawMaterial(int localClientNum, int drawMaterialType) +{ + const int charLimit = 4096; + + char name[charLimit] = "\0"; + char surfaceFlags[charLimit] = "\0"; + char contents[charLimit] = "\0"; + + float* cgArray = *(float**)0x02FF5354; + float* viewOrg = &cgArray[143433]; + float* viewAxis = &cgArray[143437]; + + struct ScreenPlacement* scrPlaceView = (struct ScreenPlacement*)0x00C78DA0; + + if (R_PickMaterial(NULL, viewOrg, viewAxis, name, surfaceFlags, contents, charLimit)) + { + struct ScreenPlacement* scrPlace = scrPlaceView; + + float y = CG_DrawSmallDevStringColor(scrPlace, 8.0f, 240.0f, name, colorWhite, 5) + 240.0f; + y += CG_DrawSmallDevStringColor(scrPlace, 8.0, y, surfaceFlags, colorWhite, 5); + CG_DrawSmallDevStringColor(scrPlace, 8.0, y, contents, colorWhite, 5); + } +} + + + +void __declspec(naked) mfh_CG_DrawFullScreenDebugOverlays(void) +{ + static DWORD dwRetn = 0x004075F9; + _asm + { + push [esp] //localClientNum + call CG_DrawFullScreenDebugOverlays + add esp, 4 + + jmp dwRetn + } +} + +void __cdecl CG_DrawFullScreenDebugOverlays(int localClientNum) +{ + if (cg_drawVersion->current.enabled) + CG_DrawVersion(); + if (cg_drawMaterial->current.integer > 0) + CG_DrawMaterial(localClientNum, cg_drawMaterial->current.integer); +} + +void __cdecl DrawVisionSetDebug(int localClientNum, visionSetMode_t curChannel) +{ + const float textX = 75.0f; + const float textScaleX = 1.25f; + const float textScaleY = 1.5f; + + const float textStartY = 200.0f; + + const float textVertSpacing = 20.0f; + + const float curChannelScaleMult = 1.2f; + + if (showVisionSetDebugInfo && showVisionSetDebugInfo->current.integer == localClientNum) + { + // Not accurate for SP + const char* visionsetModeNames[6] = { + "NAKED", + "NIGHT", + "FLARE", + "INFRARED", + "TVGUIDED", + "EXTRACAM" + }; + + struct Font_s *font = R_RegisterFont("fonts/consoleFont", 1); + + char* cgameGlob = *(char**)0x02FF5354; + + //cgameGlob = CG_GetLocalClientGlobals(localClientNum); + R_AddCmdProjectionSet2D(); + + char* visionSetNaked = cgameGlob + 837024; + char* visionSetNight = cgameGlob + 837088; + char* visionSetDamage = cgameGlob + 837408; + char* visionSetUnderwater = cgameGlob + 837600; + + char text[256]; + sprintf_s( + text, + "Naked: %s\nNight: %s\nDamage: %s\nUnderwater: %s", + visionSetNaked, + visionSetNight, + visionSetDamage, + visionSetUnderwater); + + float scale = 1.0f; + float* color = colorWhite; + R_AddCmdDrawText(text, 256, font, textX, textStartY, textScaleX * scale, textScaleY * scale, 0.0, color, 0); + + //float textY = textStartY; + //for (int modeIndex = 0; modeIndex < 6; ++modeIndex) + //{ + // char text[256]; + // sprintf_s( + // text, + // "%s c:%s f:%s t:%s", + // visionsetModeNames[modeIndex], + // "current", + // "from", + // "to"); + // //"cgameGlob->visionSetCurrent[modeIndex].name", + // //"cgameGlob->visionSetFrom[modeIndex].name", + // //"cgameGlob->visionSetTo[modeIndex].name"); + // + // float* color = (modeIndex == curChannel) ? colorRed : colorWhite; + // float scale = (modeIndex == curChannel) ? curChannelScaleMult : 1.0f; + // R_AddCmdDrawText(text, 256, font, textX, textY, textScaleX * scale, textScaleY * scale, 0.0, color, 0); + // textY = textY + textVertSpacing; + //} + } +} + + +void __declspec(naked) mfh_CG_VisionSetApplyToRefdef(void) +{ + static DWORD dwRetnLoc = 0x0045EB25; + + _asm + { + push eax + push ecx + push esi + + push eax // visionChannel + push [esp + 12] // localClientNum + call DrawVisionSetDebug + add esp, 8 + + pop esi + pop ecx + pop eax + + cmp dword ptr[esi + ecx * 4 + 0CC530h], 0 + jmp dwRetnLoc + } +} diff --git a/components/game_mod/cg_draw_debug.h b/components/game_mod/cg_draw_debug.h new file mode 100644 index 00000000..270a3d56 --- /dev/null +++ b/components/game_mod/cg_draw_debug.h @@ -0,0 +1,174 @@ +#pragma once + +union float4 +{ + float v[4]; + unsigned int u[4]; +}; + +struct hybrid_vector +{ + float4 vec; +}; +STATIC_ASSERT_SIZE(hybrid_vector, 16); + +enum TraceHitType : int +{ + TRACE_HITTYPE_NONE = 0x0, + TRACE_HITTYPE_ENTITY = 0x1, + TRACE_HITTYPE_DYNENT_MODEL = 0x2, + TRACE_HITTYPE_DYNENT_BRUSH = 0x3, +}; +STATIC_ASSERT_SIZE(TraceHitType, 4); + +struct trace_t +{ + hybrid_vector normal; + float fraction; + int sflags; // surfaceFlags + int cflags; // contents + TraceHitType hitType; + unsigned __int16 hitId; + unsigned __int16 modelIndex; + unsigned __int16 partName; + unsigned __int16 boneIndex; + unsigned __int16 partGroup; + bool allsolid; + bool startsolid; + bool walkable; + struct cStaticModel_s *staticModel; + int hitPartition; +}; +STATIC_ASSERT_SIZE(trace_t, 0x38); + +struct infoParm_t +{ + const char *name; + int clearSolid; + int surfaceFlags; + int contents; + int toolFlags; +}; + +class col_context_t +{ +private: + int mask; + struct col_prim_t *prims; + int nprims; + struct IgnoreEntParams *ignoreEntParams; + int passEntityNum0; + int passEntityNum1; + int staticmodels; + int locational; + char *priorityMap; + int(__cdecl *collide_entity_func)(int, col_context_t *); + +public: + col_context_t(void); + col_context_t(int _mask); +}; + +enum visionSetMode_t : int +{ + VISIONSETMODE_NAKED = 0x0, + VISIONSETMODE_NIGHT = 0x1, + VISIONSETMODE_FLARE = 0x2, + VISIONSETMODE_INFRARED = 0x3, + VISIONSETMODE_TVGUIDED = 0x4, + VISIONSETMODE_EXTRACAM = 0x5, + VISIONSETMODECOUNT = 0x6, +}; + + +static infoParm_t* _infoParms = (infoParm_t*)0x00B7E938; + +typedef int (__cdecl* CG_DrawDevString_t)(struct ScreenPlacement *scrPlace, float x, float y, float xScale, float yScale, const char *s, const float *color, int align, struct Font_s *font); +static CG_DrawDevString_t CG_DrawDevString = (CG_DrawDevString_t)0x00589F90; + +typedef void (__cdecl* CG_DrawVersion_t)(void); +static CG_DrawVersion_t CG_DrawVersion = (CG_DrawVersion_t)0x0067DC00; + +typedef void (__cdecl* CG_TraceCapsule_t)(trace_t *results, const float *start, const float *mins, const float *maxs, const float *end, int passEntityNum, int contentMask, col_context_t *context); +static CG_TraceCapsule_t CG_TraceCapsule = (CG_TraceCapsule_t)0x0068FD90; + +void CG_DrawMaterial(int localClientNum, int drawMaterialType); + +void mfh_CG_DrawFullScreenDebugOverlays(void); +void __cdecl CG_DrawFullScreenDebugOverlays(int localClientNum); + +struct visionSetVars_t +{ + bool filmEnable; + float filmMidStart; + float filmMidEnd; + float filmDarkFeather; + float filmLightFeather; + float filmHue[3]; + float filmSaturation[3]; + float filmColorTemp[3]; + float filmLightTint[3]; + float filmMidTint[3]; + float filmDarkTint[3]; + float filmContrast[3]; + float filmBleach[3]; + float filmLut; + bool reviveEnable; + float reviveEdgeColorTemp; + float reviveEdgeSaturation; + float reviveEdgeScale[3]; + float reviveEdgeContrast[3]; + float reviveEdgeOffset[3]; + float reviveEdgeMaskAdjust; + float reviveEdgeAmount; + bool charPrimaryUseTweaks; + float charPrimaryDiffuseScale; + float charPrimarySpecularScale; + float masterRingmod; + float reverbRingmod; + float hiFilter; + float lowFilter; + int sCurveEnable; + float sCurveShoulderStrength; + float sCurveLinearStrength; + float sCurveLinearAngle; + float sCurveToeStrength; + float sCurveToeNumerator; + float sCurveToeDenominator; + float postEmissiveBrightening; + float bloomBlurRadius; + float bloomTintWeights[4]; + float bloomColorScale[4]; + float bloomTintScale[4]; + float bloomCurveBreakpoint[4]; + float bloomCurveLoBlack[4]; + float bloomCurveLoGamma[4]; + float bloomCurveLoWhite[4]; + float bloomCurveLoRemapBlack[4]; + float bloomCurveLoRemapWhite[4]; + float bloomCurveHiBlack[4]; + float bloomCurveHiGamma[4]; + float bloomCurveHiWhite[4]; + float bloomCurveHiRemapBlack[4]; + float bloomCurveHiRemapWhite[4]; + float bloomExpansionControl[4]; + float bloomExpansionWeights[4]; + int bloomExpansionSource; + float bloomPersistence; + float bloomStreakXLevels0[4]; + float bloomStreakXLevels1[4]; + float bloomStreakXInnerTint[3]; + float bloomStreakXOuterTint[3]; + float bloomStreakXTintControl[4]; + float bloomStreakXTint[3]; + float bloomStreakYLevels0[4]; + float bloomStreakYLevels1[4]; + float bloomStreakYInnerTint[3]; + float bloomStreakYOuterTint[3]; + float bloomStreakYTintControl[4]; + float bloomStreakYTint[3]; + //char name[64]; +}; +STATIC_ASSERT_SIZE(visionSetVars_t, 672); + +void mfh_CG_VisionSetApplyToRefdef(void); diff --git a/components/game_mod/cg_scoreboard.cpp b/components/game_mod/cg_scoreboard.cpp index 24fb5fa0..2bc7f53c 100644 --- a/components/game_mod/cg_scoreboard.cpp +++ b/components/game_mod/cg_scoreboard.cpp @@ -1,4 +1,4 @@ -#include "cg_scoreboard.h" +#include "stdafx.h" CG_RegisterScoreboardDvars_t CG_RegisterScoreboardDvars_o = 0x0; dvar_s* cg_showServerInfo = 0x0; diff --git a/components/game_mod/cg_weapons.cpp b/components/game_mod/cg_weapons.cpp new file mode 100644 index 00000000..807b56cb --- /dev/null +++ b/components/game_mod/cg_weapons.cpp @@ -0,0 +1,286 @@ +#include "stdafx.h" + +void XAnimSetTime(XAnimTree_s *tree, unsigned int animIndex, float time, int cmdIndex) +{ + ((void(__cdecl *)(XAnimTree_s *, unsigned int, float, int))0x005E79C0)(tree, animIndex, time, cmdIndex); +} + +void XAnimSetGoalWeight(DObj *obj, unsigned int animIndex, float goalWeight, float goalTime, float rate, unsigned int notifyName, unsigned int notifyType, int bRestart, int cmdIndex) +{ + ((void(__cdecl *)(DObj *, unsigned int, float, float, float, unsigned int, unsigned int, int, int))0x004076C0) + (obj, animIndex, goalWeight, goalTime, rate, notifyName, notifyType, bRestart, cmdIndex); +} + +// /cgame/cg_weapons.cpp:264 +float GetWeaponAnimRate(int localClientNum, WeaponVariantDef *weapVariantDef, XAnim_s *anims, int animIndex) +{ + static DWORD dwCall = 0x00795040; + + ASSERT(weapVariantDef); + ASSERT(anims); + + __asm + { + push anims + push weapVariantDef + mov esi, animIndex + mov eax, localClientNum + call[dwCall] + add esp, 0x8 + } +} + +// /cgame/cg_weapons.cpp:298 +float GetWeaponAnimTimeFrac(int localClientNum, WeaponVariantDef *weapVariantDef, XAnim_s *anims, int animIndex) +{ + static DWORD dwCall = 0x007950F0; + + ASSERT(weapVariantDef); + ASSERT(anims); + + __asm + { + mov edx, weapVariantDef + mov eax, animIndex + call[dwCall] + } +} + +// /cgame/cg_weapons.cpp:373 +void StartWeaponAnim(int localClientNum, int weaponNum, DObj *obj, int animIndex, float transitionTime, int newPlayerstate) +{ + ASSERT((animIndex > WEAP_ANIM_VIEWMODEL_START) && (animIndex < WEAP_ANIM_VIEWMODEL_END)); + + ViewModelInfo *viewModelInfo = CG_GetLocalClientViewModelInfo(localClientNum); + XAnim_s *anims = viewModelInfo->anims; + + ASSERT(anims); + + WeaponDef *weapDef = BG_GetWeaponDef(weaponNum); + WeaponVariantDef *weapVariantDef = BG_GetWeaponVariantDef(weaponNum); + + float rate = GetWeaponAnimRate(localClientNum, weapVariantDef, anims, animIndex); + float timeFrac = 0.0f; + + if (newPlayerstate) + timeFrac = GetWeaponAnimTimeFrac(localClientNum, weapVariantDef, anims, animIndex); + + char *cgameGlob = CG_GetLocalClientGlobals(localClientNum); + + unsigned int *ps_perks = (unsigned int *)(cgameGlob + 0x8A860);// perks + int weaponstate = *(int *)(cgameGlob + 0x8A4BC); // cg->predictedPlayerState.weaponstate + int weaponstateLeft = *(int *)(cgameGlob + 0x8A4C0); // cg->predictedPlayerState.weaponstateLeft + + if ((IS_WEAPONSTATE_RELOAD(weaponstate) || IS_WEAPONSTATE_RELOAD(weaponstateLeft)) && BG_HasPerk(ps_perks, PERK_FASTRELOAD)) + { + // Fast weapon reload perk + if (perk_weapReloadMultiplier->current.value != 0.0f) + rate /= perk_weapReloadMultiplier->current.value; + else + rate = 1000.0f; + } + else if ((weaponstate == WEAPON_RECHAMBER_START || weaponstate == WEAPON_RECHAMBERING) && BG_HasPerk(ps_perks, PERK_RATEOFFIRE)) + { + // Doubletap (rate of fire) perk + if (perk_weapRateMultiplier->current.value != 0.0f) + rate /= perk_weapRateMultiplier->current.value; + else + rate = 1000.0f; + } + else if (BG_CanFastSwitch(weapDef, weaponstate) && BG_HasPerk(ps_perks, PERK_FASTSWITCH)) + { + // Fast weapon switch perk + ASSERT(perk_weapSwitchMultiplier->current.value > 0.0f); + rate /= perk_weapSwitchMultiplier->current.value; + } + + for (int i = 1; i < WEAP_ANIM_VIEWMODEL_END; ++i) + { + if (animIndex == i) + { + XAnimSetGoalWeight(obj, i, 1.0f, transitionTime, rate, 0, 1, 1, -1); + + if (newPlayerstate) + XAnimSetTime(viewModelInfo->tree, i, timeFrac, -1); + } + else if (weapDef->bDualWield + && viewModelInfo->hand[0].iHandAnimIndex != i + && viewModelInfo->hand[1].iHandAnimIndex != i) + { + XAnimSetGoalWeight(obj, i, 0.0f, transitionTime, 1.0f, 0, 0, 0, -1); + } + else if (!weapDef->bDualWield) + { + XAnimSetGoalWeight(obj, i, 0.0f, transitionTime, 1.0f, 0, 0, 0, -1); + } + } +} + +void hk_StartWeaponAnim(int localClientNum, DObj *obj, int animIndex, float transitionTime, int newPlayerstate) +{ + __asm + { + push newPlayerstate + push transitionTime + push animIndex + push obj + push eax + push localClientNum + call StartWeaponAnim + add esp, 0x18 + } +} + +// /cgame/cg_weapons.cpp:3329 +void CG_BulletEndpos(unsigned int *randSeed, const float spread, const float *start, float *end, float *dir, const float *forwardDir, const float *rightDir, const float *upDir, const float maxRange, int shotIndex, int maxShotIndex) +{ + ASSERT(!IS_NAN(spread)); + ASSERT(end); + + float aimOffset = tan(spread * 0.017453292f) * maxRange; + + ASSERT(!IS_NAN(aimOffset)); + + float right = 0.0f; + float up = 0.0f; + Bullet_RandomDir(randSeed, &right, &up); + + // BO2 weapon perk: bullet spread is horizontal only + if (Com_SessionMode_IsZombiesGame() && BG_WeaponHasPerk(nullptr, 3)) + { + right = 0.0f; + up = 0.0f; + + if (shotIndex && maxShotIndex > 1) + { + float modifier = (shotIndex % 2) ? -1.0f : 1.0f; + right = (((spread * 0.5f) / (maxShotIndex >> 1)) * (((shotIndex - 1) >> 1) + 1)) * modifier; + } + } + + right *= aimOffset; + up *= aimOffset; + + ASSERT(!IS_NAN(right)); + ASSERT(!IS_NAN(up)); + + end[0] = (maxRange * forwardDir[0]) + start[0]; + end[1] = (maxRange * forwardDir[1]) + start[1]; + end[2] = (maxRange * forwardDir[2]) + start[2]; + + ASSERT(!IS_NAN(end[0]) && !IS_NAN(end[1]) && !IS_NAN(end[2])); + + end[0] = (right * rightDir[0]) + end[0]; + end[1] = (right * rightDir[1]) + end[1]; + end[2] = (right * rightDir[2]) + end[2]; + + end[0] = (up * upDir[0]) + end[0]; + end[1] = (up * upDir[1]) + end[1]; + end[2] = (up * upDir[2]) + end[2]; + + ASSERT(!IS_NAN(end[0]) && !IS_NAN(end[1]) && !IS_NAN(end[2])); + + if (dir) + { + dir[0] = end[0] - start[0]; + dir[1] = end[1] - start[1]; + dir[2] = end[2] - start[2]; + Vec3Normalize(dir); + + ASSERT(!IS_NAN(dir[0]) && !IS_NAN(dir[1]) && !IS_NAN(dir[2])); + } +} + +// /cgame/cg_weapons.cpp:3486 +void __declspec(naked) mfh_CG_DrawBulletImpacts1() +{ + static DWORD dwJmp = 0x0079A4F7; + + playerState_s *ps; + unsigned int *randSeed; + unsigned int *shotCount; + + __asm + { + // [shotCount, SP - 0x114] + // [secondBarrel, SP - 0xE0] + // [ps, SP + 0x10] + pushad + lea edi, [esp + 0x150 + 0x20] + + mov ebp, esp + sub esp, __LOCAL_SIZE + + mov eax, dword ptr [edi + 0x10] // playerState *ps + mov ps, eax // + lea eax, [edi - 0xE0] // secondBarrel [See mfh_CG_DrawBulletImpacts2 comment] + mov randSeed, eax // + lea eax, [edi - 0x114] // shotCount + mov shotCount, eax // + } + + *randSeed = ps->commandTime; + BG_seedRandWithGameTime(randSeed); + + if (Com_SessionMode_IsZombiesGame() && perk_weapRateEnhanced->current.enabled) + { + if (BG_HasPerk(ps->perks, PERK_RATEOFFIRE)) + *shotCount *= 2; + } + + __asm + { + mov esp, ebp // + popad // Restore stack + xor ebx, ebx // Original instructions + cmp dword ptr [esp + 0x3C], ebx // + jmp [dwJmp] // + } +} + +void __declspec(naked) mfh_CG_DrawBulletImpacts2() +{ + static DWORD dwJmp = 0x0079A5BA; + + __asm + { + // [shotIndex, ebx] + // [spread, SP - 0x130] + // [maxRange, SP - 0x118] + // [shotCount, SP - 0x114] + // [forwardDir, SP - 0x104] + // [rightDir, SP - 0xF8] + // [upDir, SP - 0xEC] + // [secondBarrel, SP - 0xE0] + // [start, SP - 0xA8] + // [end, SP - 0x9C] + // [dir, SP - 0x90] + // [ps, SP + 0x10] + lea edi, [esp + 0x168] + + // Call CG_BulletEndpos with completely rewritten arguments + push dword ptr [edi - 0x114] // shotCount + push ebx // shotIndex + push dword ptr [edi - 0x118] // maxRange + lea eax, [edi - 0xEC] // &upDir + push eax // + lea eax, [edi - 0xF8] // &rightDir + push eax // + lea eax, [edi - 0x104] // &forwardDir + push eax // + lea eax, [edi - 0x90] // &dir + push eax // + lea eax, [edi - 0x9C] // &end + push eax // + lea eax, [edi - 0xA8] // &start + push eax // + push dword ptr [edi - 0x130] // spread + lea eax, [edi - 0xE0] // secondBarrel [NOTE: Hijacked to instead be a pointer as 'randSeed'] + push eax // + + call CG_BulletEndpos + add esp, 0x2C + + jmp [dwJmp] + } +} \ No newline at end of file diff --git a/components/game_mod/cg_weapons.h b/components/game_mod/cg_weapons.h new file mode 100644 index 00000000..3637d8d5 --- /dev/null +++ b/components/game_mod/cg_weapons.h @@ -0,0 +1,12 @@ +#pragma once + +#define WEAP_ANIM_VIEWMODEL_START 0 +#define WEAP_ANIM_VIEWMODEL_END 64 + +float GetWeaponAnimRate(int localClientNum, WeaponVariantDef *weapVariantDef, XAnim_s *anims, int animIndex); +float GetWeaponAnimTimeFrac(int localClientNum, WeaponVariantDef *weapVariantDef, XAnim_s *anims, int animIndex); +void StartWeaponAnim(int localClientNum, int weaponNum, DObj *obj, int animIndex, float transitionTime, int newPlayerstate); +void hk_StartWeaponAnim(int localClientNum, DObj *obj, int animIndex, float transitionTime, int newPlayerstate); +void CG_BulletEndpos(unsigned int *randSeed, const float spread, const float *start, float *end, float *dir, const float *forwardDir, const float *rightDir, const float *upDir, const float maxRange, int shotIndex, int maxShotIndex); +void mfh_CG_DrawBulletImpacts1(); +void mfh_CG_DrawBulletImpacts2(); \ No newline at end of file diff --git a/components/game_mod/cl_console.cpp b/components/game_mod/cl_console.cpp index 10fdc2d6..93f31013 100644 --- a/components/game_mod/cl_console.cpp +++ b/components/game_mod/cl_console.cpp @@ -1,11 +1,27 @@ #include "stdafx.h" +// /client/cl_console.cpp:289 +void Con_ToggleConsole() +{ + Field_Clear(g_consoleField); + Con_CancelAutoComplete(); + + g_consoleField->widthInPixels = 620; + g_consoleField->charHeight = 16.0f; + g_consoleField->fixedSize = true; + + *(bool *)0xC6924C = false; + *(DWORD *)0x2910160 ^= 1; +} + +// /client/cl_console.cpp:2568 bool Con_HasActiveAutoComplete() { - //return conDrawInputGlob.matchIndex >= 0 && conDrawInputGlob.autoCompleteChoice[0]; + // return conDrawInputGlob.matchIndex >= 0 && conDrawInputGlob.autoCompleteChoice[0]; return (*(int *)0xC48A48 >= 0 && ((BYTE *)0xC48A08)[0]); } +// /client/cl_console.cpp:2599 bool Con_CancelAutoComplete() { if (Con_HasActiveAutoComplete()) @@ -17,17 +33,4 @@ bool Con_CancelAutoComplete() } return false; -} - -void Con_ToggleConsole() -{ - Field_Clear(g_consoleField); - Con_CancelAutoComplete(); - - g_consoleField->widthInPixels = 620; - g_consoleField->charHeight = 16.0f; - g_consoleField->fixedSize = true; - - *(bool *)0xC6924C = false; - *(DWORD *)0x2910160 ^= 1; } \ No newline at end of file diff --git a/components/game_mod/cl_console.h b/components/game_mod/cl_console.h index 685855cd..8b188a0e 100644 --- a/components/game_mod/cl_console.h +++ b/components/game_mod/cl_console.h @@ -2,6 +2,6 @@ static field_t *g_consoleField = (field_t *)0x00C72280; +void Con_ToggleConsole(); bool Con_HasActiveAutoComplete(); -bool Con_CancelAutoComplete(); -void Con_ToggleConsole(); \ No newline at end of file +bool Con_CancelAutoComplete(); \ No newline at end of file diff --git a/components/game_mod/cl_debugdata.cpp b/components/game_mod/cl_debugdata.cpp new file mode 100644 index 00000000..19d924a1 --- /dev/null +++ b/components/game_mod/cl_debugdata.cpp @@ -0,0 +1,13 @@ +#include "stdafx.h" + +// /client/cl_debugdata.cpp:223 +void CL_AddDebugLine(const float *start, const float *end, const float *color, int depthTest, int duration) +{ + ((void(__cdecl *)(const float *, const float *, const float *, int, int))0x004C9AF0)(start, end, color, depthTest, duration); +} + +// This is the wrong file +void CG_DebugLine(const float *start, const float *end, const float *color, int depthTest, int duration) +{ + CL_AddDebugLine(start, end, color, depthTest, duration); +} \ No newline at end of file diff --git a/components/game_mod/cl_debugdata.h b/components/game_mod/cl_debugdata.h new file mode 100644 index 00000000..44c13c88 --- /dev/null +++ b/components/game_mod/cl_debugdata.h @@ -0,0 +1,4 @@ +#pragma once + +void CL_AddDebugLine(const float *start, const float *end, const float *color, int depthTest, int duration); +void CG_DebugLine(const float *start, const float *end, const float *color, int depthTest, int duration); \ No newline at end of file diff --git a/components/game_mod/cl_keys.cpp b/components/game_mod/cl_keys.cpp new file mode 100644 index 00000000..3918f7d6 --- /dev/null +++ b/components/game_mod/cl_keys.cpp @@ -0,0 +1,13 @@ +#include "stdafx.h" + +// /client/cl_keys.cpp:2219 +void CL_KeyEvent(int localClientNum, int key, const int down, const unsigned int time) +{ + ((void(__cdecl *)(int, int, const int, const unsigned int))0x00587500)(localClientNum, key, down, time); +} + +// /client/cl_keys.cpp:2759 +void CL_CharEvent(int localClientNum, int key) +{ + ((void(__cdecl *)(int, int))0x004CEC00)(localClientNum, key); +} \ No newline at end of file diff --git a/components/game_mod/cl_keys.h b/components/game_mod/cl_keys.h new file mode 100644 index 00000000..b3ee309e --- /dev/null +++ b/components/game_mod/cl_keys.h @@ -0,0 +1,4 @@ +#pragma once + +void CL_KeyEvent(int localClientNum, int key, const int down, const unsigned int time); +void CL_CharEvent(int localClientNum, int key); \ No newline at end of file diff --git a/components/game_mod/cl_main_mp.cpp b/components/game_mod/cl_main_mp.cpp index 32698e25..e36e92e8 100644 --- a/components/game_mod/cl_main_mp.cpp +++ b/components/game_mod/cl_main_mp.cpp @@ -6,10 +6,8 @@ void (__cdecl * CL_Vid_Restart_Complete_f)(); void hk_CL_Vid_Restart_Complete_f() { - // // If the server is currently running, kill it first - // - if ((*com_sv_running)->current.enabled) + if (com_sv_running->current.enabled) SV_KillServer_f(); CL_Vid_Restart_Complete_f(); @@ -25,9 +23,23 @@ CL_GetServerIPAddress_t CL_GetServerIPAddress_o = 0x0; const char *__cdecl CL_GetServerIPAddress() { if (cg_showServerInfo->current.enabled) - { return CL_GetServerIPAddress_o(); - } return ""; } + +char *CG_GetLocalClientGlobals(int localClientNum) +{ + ASSERT(localClientNum == 0); + + // cgArray + return *(char **)0x02FF5354; +} + +ViewModelInfo *CG_GetLocalClientViewModelInfo(int localClientNum) +{ + ASSERT(localClientNum == 0); + + // cg_viewModelArray + return *(ViewModelInfo **)0x00C1C6D8; +} \ No newline at end of file diff --git a/components/game_mod/cl_main_mp.h b/components/game_mod/cl_main_mp.h index 16318067..00a0c61e 100644 --- a/components/game_mod/cl_main_mp.h +++ b/components/game_mod/cl_main_mp.h @@ -1,5 +1,29 @@ #pragma once +typedef int DObj; +typedef int XAnimTree_s; +typedef int XAnim_s; + +struct ViewModelHand +{ + int iPrevAnim; + int iHandAnimIndex; +}; +STATIC_ASSERT_OFFSET(ViewModelHand, iPrevAnim, 0x0); +STATIC_ASSERT_OFFSET(ViewModelHand, iHandAnimIndex, 0x4); + +struct ViewModelInfo +{ + DObj *viewModelDObj; + int hasAnimTree; + XAnimTree_s *tree; + XAnim_s *anims; + unsigned int partBits[5]; + ViewModelHand hand[2]; +}; +STATIC_ASSERT_OFFSET(ViewModelInfo, anims, 0xC); +STATIC_ASSERT_OFFSET(ViewModelInfo, hand, 0x24); + extern void (__cdecl * CL_Vid_Restart_Complete_f)(); void hk_CL_Vid_Restart_Complete_f(); @@ -8,4 +32,7 @@ void CL_CM_LoadMap(const char *mapname); typedef const char *(__cdecl* CL_GetServerIPAddress_t)(); extern CL_GetServerIPAddress_t CL_GetServerIPAddress_o; -const char *__cdecl CL_GetServerIPAddress(); \ No newline at end of file +const char *__cdecl CL_GetServerIPAddress(); + +char *CG_GetLocalClientGlobals(int localClientNum); +ViewModelInfo *CG_GetLocalClientViewModelInfo(int localClientNum); \ No newline at end of file diff --git a/components/game_mod/cmd.cpp b/components/game_mod/cmd.cpp index 7ac8c8a2..cd040d69 100644 --- a/components/game_mod/cmd.cpp +++ b/components/game_mod/cmd.cpp @@ -3,6 +3,9 @@ char cfg_default_path[] = "/config.cfg"; void* Cmd_ExecFromDisk = (void*)0x008295D0; +typedef void *(__cdecl* Sys_GetValue_t)(int valueIndex); +static Sys_GetValue_t Sys_GetValue = (Sys_GetValue_t)0x0067D4F0; + void* rtn_Cmd_Exec_f = (void*)0x0082A26E; void __declspec(naked) mfh_Cmd_Exec_f() { @@ -31,9 +34,7 @@ void __declspec(naked) mfh_Cmd_Exec_f() void(__cdecl * Cmd_ExecuteSingleCommandInternal)(int localClientNum, int controllerIndex, void *item, const char *text, bool restrict) = NULL; void hk_Cmd_ExecuteSingleCommandInternal(int localClientNum, int controllerIndex, void *item, const char *text, bool restrict) { - // // Bypass restricted "#dcr#" marker - // if (_strnicmp(text, "#dcr#", 5) == 0) text += 5; @@ -41,4 +42,31 @@ void hk_Cmd_ExecuteSingleCommandInternal(int localClientNum, int controllerIndex text = "echo \"Command not executed. VAC is disabled.\""; Cmd_ExecuteSingleCommandInternal(localClientNum, controllerIndex, item, text, false); -} \ No newline at end of file +} + +void Cbuf_AddText(int localClientNum, const char *text) +{ + ((void(__cdecl *)(int, const char *))0x0049B930)(localClientNum, text); +} + +CmdArgs *__cdecl Cmd_Args() +{ + CmdArgs* cmd_args = (CmdArgs*)Sys_GetValue(4); + return cmd_args; +} + +int __cdecl Cmd_Argc() +{ + CmdArgs* cmd_args = Cmd_Args(); + return cmd_args->argc[cmd_args->nesting]; +} + +const char *__cdecl Cmd_Argv(int argIndex) +{ + CmdArgs* cmd_args = Cmd_Args(); + + if (argIndex >= cmd_args->argc[cmd_args->nesting]) + return ""; + + return cmd_args->argv[cmd_args->nesting][argIndex]; +} diff --git a/components/game_mod/cmd.h b/components/game_mod/cmd.h index 2c6ce779..888f90d7 100644 --- a/components/game_mod/cmd.h +++ b/components/game_mod/cmd.h @@ -1,6 +1,59 @@ #pragma once +#define CMD_MAX_NESTING 8 + +struct __declspec(align(4)) cmd_function_s +{ + cmd_function_s *next; + const char *name; + const char *autoCompleteDir; + const char *autoCompleteExt; + void(__cdecl *function)(); + bool consoleAccess; +}; + +struct CmdArgs +{ + int nesting; + int localClientNum[8]; + int controllerIndex[8]; + struct itemDef_s *itemDef[8]; + int argshift[8]; + int argc[8]; + const char **argv[8]; + char textPool[8192]; + const char *argvPool[512]; + int usedTextPool[8]; + int totalUsedArgvPool; + int totalUsedTextPool; +}; + +static CmdArgs& sv_cmd_args = *(CmdArgs *)0x0243D208; + +typedef void (__cdecl* Cmd_AddCommandInternal_t)(const char *cmdName, void(__cdecl *function)(), cmd_function_s *allocedCmd); +static Cmd_AddCommandInternal_t Cmd_AddCommandInternal = (Cmd_AddCommandInternal_t)0x00661400; + +typedef void (__cdecl* Cmd_RemoveCommand_t)(const char *cmdName); +static Cmd_RemoveCommand_t Cmd_RemoveCommand = (Cmd_RemoveCommand_t)0x005F1A90; + void mfh_Cmd_Exec_f(); extern void(__cdecl * Cmd_ExecuteSingleCommandInternal)(int localClientNum, int controllerIndex, void *item, const char *text, bool restrict); void hk_Cmd_ExecuteSingleCommandInternal(int localClientNum, int controllerIndex, void *item, const char *text, bool restrict); +void Cbuf_AddText(int localClientNum, const char *text); + +CmdArgs *__cdecl Cmd_Args(); + +int __cdecl Cmd_Argc(); +const char *__cdecl Cmd_Argv(int argIndex); + +static const char *SV_Cmd_Argv(int argIndex) +{ + ASSERT(sv_cmd_args.nesting < CMD_MAX_NESTING); + ASSERT(argIndex >= 0); + + if (argIndex >= sv_cmd_args.argc[sv_cmd_args.nesting]) + return ""; + + return sv_cmd_args.argv[sv_cmd_args.nesting][argIndex]; +} \ No newline at end of file diff --git a/components/game_mod/com_bsp_load_obj.cpp b/components/game_mod/com_bsp_load_obj.cpp new file mode 100644 index 00000000..005ff14e --- /dev/null +++ b/components/game_mod/com_bsp_load_obj.cpp @@ -0,0 +1,34 @@ +#include "stdafx.h" + +// /qcommon/com_bsp_load_obj.cpp:94 +const void *Com_GetBspLump(LumpType type, unsigned int elemSize, unsigned int *count) +{ + return ((void *(__cdecl *)(LumpType, unsigned int, unsigned int *))0x00560F80)(type, elemSize, count); +} + +// /qcommon/com_bsp_load_obj.cpp:260 +unsigned int Com_GetBspVersion() +{ + return ((unsigned int(__cdecl *)())0x004D07E0)(); +} + +// /qcommon/com_bsp_load_obj.cpp:307 +void Com_LoadBsp(const char *filename) +{ + ((void(__cdecl *)(const char *filename))0x00474BB0)(filename); +} + +// /qcommon/com_bsp_load_obj.cpp:395 +void Com_SaveLump(LumpType type, const void *newLump, unsigned int size, ComSaveLumpBehavior behavior) +{ + ((void(__cdecl *)(LumpType, const void *, unsigned int, ComSaveLumpBehavior))0x004E1250)(type, newLump, size, behavior); +} + +// /qcommon/com_bsp_load_obj.cpp:483 +const char *Com_GetHunkStringCopy(const char *string) +{ + size_t len = strlen(string); + void *hunkCopy = Hunk_AllocAlign(len + 1, 1, "Com_GetHunkStringCopy", 14); + + return (const char *)memcpy(hunkCopy, string, len + 1); +} \ No newline at end of file diff --git a/components/game_mod/com_bsp_load_obj.h b/components/game_mod/com_bsp_load_obj.h new file mode 100644 index 00000000..bdcbb354 --- /dev/null +++ b/components/game_mod/com_bsp_load_obj.h @@ -0,0 +1,95 @@ +#pragma once + +enum LumpType +{ + LUMP_MATERIALS = 0x0, + LUMP_LIGHTBYTES = 0x1, + LUMP_LIGHTGRIDENTRIES = 0x2, + LUMP_LIGHTGRIDCOLORS = 0x3, + LUMP_PLANES = 0x4, + LUMP_BRUSHSIDES = 0x5, + LUMP_BRUSHSIDEEDGECOUNTS = 0x6, + LUMP_BRUSHEDGES = 0x7, + LUMP_BRUSHES = 0x8, + LUMP_TRIANGLES = 0x9, + LUMP_DRAWVERTS = 0xA, + LUMP_DRAWINDICES = 0xB, + LUMP_CULLGROUPS = 0xC, + LUMP_CULLGROUPINDICES = 0xD, + LUMP_OBSOLETE_1 = 0xE, + LUMP_OBSOLETE_2 = 0xF, + LUMP_OBSOLETE_3 = 0x10, + LUMP_OBSOLETE_4 = 0x11, + LUMP_OBSOLETE_5 = 0x12, + LUMP_PORTALVERTS = 0x13, + LUMP_OBSOLETE_6 = 0x14, + LUMP_UINDS = 0x15, + LUMP_BRUSHVERTSCOUNTS = 0x16, + LUMP_BRUSHVERTS = 0x17, + LUMP_AABBTREES = 0x18, + LUMP_CELLS = 0x19, + LUMP_PORTALS = 0x1A, + LUMP_NODES = 0x1B, + LUMP_LEAFS = 0x1C, + LUMP_LEAFBRUSHES = 0x1D, + LUMP_LEAFSURFACES = 0x1E, + LUMP_COLLISIONVERTS = 0x1F, + LUMP_COLLISIONTRIS = 0x20, + LUMP_COLLISIONEDGEWALKABLE = 0x21, + LUMP_COLLISIONBORDERS = 0x22, + LUMP_COLLISIONPARTITIONS = 0x23, + LUMP_COLLISIONAABBS = 0x24, + LUMP_MODELS = 0x25, + LUMP_VISIBILITY = 0x26, + LUMP_ENTITIES = 0x27, + LUMP_PATHCONNECTIONS = 0x28, + LUMP_REFLECTION_PROBES = 0x29, + LUMP_VERTEX_LAYER_DATA = 0x2A, + LUMP_PRIMARY_LIGHTS = 0x2B, + LUMP_LIGHTGRIDHEADER = 0x2C, + LUMP_LIGHTGRIDROWS = 0x2D, + LUMP_OBSOLETE_10 = 0x2E, + LUMP_OBSOLETE_11 = 0x2F, + LUMP_OBSOLETE_12 = 0x30, + LUMP_OBSOLETE_13 = 0x31, + LUMP_OBSOLETE_14 = 0x32, + LUMP_OBSOLETE_15 = 0x33, + LUMP_WATERHEADER = 0x34, + LUMP_WATERCELLS = 0x35, + LUMP_WATERCELLDATA = 0x36, + LUMP_BURNABLEHEADER = 0x37, + LUMP_BURNABLECELLS = 0x38, + LUMP_BURNABLECELLDATA = 0x39, + LUMP_SIMPLELIGHTMAPBYTES = 0x3A, + LUMP_LODCHAINS = 0x3B, + LUMP_LODINFOS = 0x3C, + LUMP_LODSURFACES = 0x3D, + LUMP_LIGHTREGIONS = 0x3E, + LUMP_LIGHTREGION_HULLS = 0x3F, + LUMP_LIGHTREGION_AXES = 0x40, + LUMP_WIILIGHTGRID = 0x41, + LUMP_LIGHTGRID2D_LIGHTS = 0x42, + LUMP_LIGHTGRID2D_INDICES = 0x43, + LUMP_LIGHTGRID2D_POINTS = 0x44, + LUMP_LIGHTGRID2D_CELLS = 0x45, + LUMP_LIGHT_CORONAS = 0x46, + LUMP_SHADOWMAP_VOLUMES = 0x47, + LUMP_SHADOWMAP_VOLUME_PLANES = 0x48, + LUMP_EXPOSURE_VOLUMES = 0x49, + LUMP_EXPOSURE_VOLUME_PLANES = 0x4A, + LUMP_OCCLUDERS = 0x4B, + LUMP_OUTDOORBOUNDS = 0x4C, + LUMP_HERO_ONLY_LIGHTS = 0x4D, +}; + +enum ComSaveLumpBehavior +{ + COM_SAVE_LUMP_AND_CLOSE = 0x0, + COM_SAVE_LUMP_AND_REOPEN = 0x1, +}; + +const void *Com_GetBspLump(LumpType type, unsigned int elemSize, unsigned int *count); +unsigned int Com_GetBspVersion(); +void Com_LoadBsp(const char *filename); +void Com_SaveLump(LumpType type, const void *newLump, unsigned int size, ComSaveLumpBehavior behavior); +const char *Com_GetHunkStringCopy(const char *string); \ No newline at end of file diff --git a/components/game_mod/com_files.cpp b/components/game_mod/com_files.cpp new file mode 100644 index 00000000..6326d2e5 --- /dev/null +++ b/components/game_mod/com_files.cpp @@ -0,0 +1,153 @@ +#include "stdafx.h" +#include + +char dirList[DIRLIST_LEN]; + +FS_ModDesc::FS_ModDesc(void) +{ + this->Clear(); +} + +void FS_ModDesc::Clear(void) +{ + this->type = DESC_ERROR; + ZeroMemory(this->data, bufLen); +} + +size_t FS_ModDesc::Length(void) const +{ + return strlen((const char*)this) + 1; +} + +unsigned int __cdecl FS_ReadModDescription(void *ptr, unsigned int len, _iobuf *stream) +{ + return FS_FileRead(ptr, MODDESC_LEN, stream); +} + +void __declspec(naked) mfh_FS_GetModList(void) +{ + static void* dwRetn = (void*)0x00455A34; + + _asm + { + push esi + lea edx, [esp + 0x44] + push MODDESC_LEN + push edx + jmp dwRetn; + } +} + +unsigned int __cdecl Sys_CountFileList(char **list) +{ + int i = 0; + if (list) + { + while (*list) + { + ++list; + ++i; + } + } + return i; +} + +int __cdecl FS_GetModList(char *listbuf, int bufsize) +{ + *listbuf = 0; + + int nTotal = 0; + int nPotential = 0; + int nMods = 0; + + const char* basepath = fs_homepath->current.string; + + char descPath[MAX_PATH]; + sprintf_s(descPath, "%s/%s", basepath, "mods"); + + int dummy = 0; + char** pFiles = Sys_ListFiles(descPath, 0, 0, &dummy, 1); + nPotential = Sys_CountFileList(pFiles); + for (int i = 0; i < nPotential; ++i) + { + char* name = pFiles[i]; + int nLen = strlen(name) + 1; + + // + // Try to load mod.json first + // + sprintf_s(descPath, "%s%s", name, "/mod.json"); + + FS_ModDesc desc; + + int descHandle = 0; + if (FS_SV_FOpenFileRead(descPath, "mods", &descHandle, NULL) > 0 && descHandle) + { + _iobuf* file = FS_FileForHandle(descHandle); + desc.Clear(); + int nDescLen = FS_ReadModDescription(desc.data, desc.bufLen, file); + + // Ensure that the string is null terminated + if (nDescLen >= 0) + desc.data[nDescLen] = '\0'; + + desc.type = DESC_JSON; + FS_FCloseFile(descHandle); + } + else + { + // + // Otherwise fallback to legacy description.txt + // + sprintf_s(descPath, "%s%s", name, "/description.txt"); + + int descHandle = 0; + if (FS_SV_FOpenFileRead(descPath, "mods", &descHandle, NULL) > 0 && descHandle) + { + _iobuf* file = FS_FileForHandle(descHandle); + desc.Clear(); + int nDescLen = FS_ReadModDescription(desc.data, desc.bufLen, file); + + // Ensure that the string is null terminated + if (nDescLen >= 0) + desc.data[nDescLen] = '\0'; + + desc.type = DESC_DESC; + FS_FCloseFile(descHandle); + } + else + { + Com_Printf(10, "FS_GetModList: failed to open '%s' or '%s' for mod '%s'\n", "mod.json", "description.txt", name); + desc.Clear(); + } + } + + int nDescLen = desc.Length(); + if (nLen + nTotal + nDescLen + 2 >= bufsize) + break; + +#pragma warning( push ) +#pragma warning( disable : 4996 ) + strcpy(listbuf, name); + strcpy(&listbuf[nLen], (char*)&desc); +#pragma warning( pop ) + + listbuf = &listbuf[nLen + nDescLen]; + nTotal += nDescLen + nLen; + ++nMods; + } + + FS_FreeFileList((const char**)pFiles, 0); + return nMods; +} + +typedef int(__cdecl* FS_FOpenFileWriteToDir_t)(const char *filename, const char *dir, const char *osbasepath); +FS_FOpenFileWriteToDir_t FS_FOpenFileWriteToDir_o = (FS_FOpenFileWriteToDir_t)0x00625D90; + +int __cdecl FS_FOpenFileWriteToDir(const char *filename, const char *dir, const char *osbasepath) +{ + if (com_cfg_readOnly && com_cfg_readOnly->current.enabled) + return 0; + + return FS_FOpenFileWriteToDir_o(filename, dir, osbasepath); +} diff --git a/components/game_mod/com_files.h b/components/game_mod/com_files.h index e06a54cc..b120cdaa 100644 --- a/components/game_mod/com_files.h +++ b/components/game_mod/com_files.h @@ -7,11 +7,82 @@ struct XFile unsigned int blockSize[7]; }; +enum fsMode_t +{ + FS_READ, + FS_WRITE, + FS_APPEND, + FS_APPEND_SYNC, +}; + +enum DESC_TYPE +{ + DESC_ERROR = 0, + DESC_DESC = 1, + DESC_JSON = 2, +}; + +#define MODDESC_LEN 254 +#define DIRLIST_LEN 8192 * (MODDESC_LEN / 48) + +extern char dirList[DIRLIST_LEN]; + +struct FS_ModDesc +{ + // The size of the description data buffer + // Must be MODDESC_LEN minus the size of 'type' + static const unsigned int bufLen = MODDESC_LEN - 1; + + BYTE type; + char data[bufLen]; + + FS_ModDesc(void); + void Clear(void); + + // Logical length + size_t Length(void) const; +}; +STATIC_ASSERT_SIZE(FS_ModDesc, MODDESC_LEN); + typedef int (__cdecl* FS_FOpenFileRead_t)(const char *filename, int *file); static FS_FOpenFileRead_t FS_FOpenFileRead = (FS_FOpenFileRead_t)0x004C6E20; +typedef int (__cdecl* FS_FOpenFileByMode_t)(const char *qpath, int *f, fsMode_t mode); +static FS_FOpenFileByMode_t FS_FOpenFileByMode = (FS_FOpenFileByMode_t)0x004DD530; + +typedef int (__cdecl* FS_FOpenTextFileWrite_t)(const char *filename); +static FS_FOpenTextFileWrite_t FS_FOpenTextFileWrite = (FS_FOpenTextFileWrite_t)0x004483C0; + typedef void (__cdecl* FS_FCloseFile_t)(int h); static FS_FCloseFile_t FS_FCloseFile = (FS_FCloseFile_t)0x0046CAA0; +typedef int (__cdecl* FS_Write_t)(const void *buffer, int len, int h); +static FS_Write_t FS_Write = (FS_Write_t)0x00401090; + typedef int (__cdecl* FS_Read_t)(void *buffer, int len, int h); static FS_Read_t FS_Read = (FS_Read_t)0x004CFB60; + +typedef unsigned int (__cdecl* FS_FileRead_t)(void *ptr, unsigned int len, struct _iobuf *stream); +static FS_FileRead_t FS_FileRead = (FS_FileRead_t)0x0047F210; + +// If pResult is NULL, no result value is written +typedef int(__cdecl* FS_SV_FOpenFileRead_t)(const char *filename, const char *dir, int *fp, int* pResult); +static FS_SV_FOpenFileRead_t FS_SV_FOpenFileRead = (FS_SV_FOpenFileRead_t)0x00464020; + +typedef _iobuf *(__cdecl* FS_FileForHandle_t)(int f); +static FS_FileForHandle_t FS_FileForHandle = (FS_FileForHandle_t)0x00516DA0; + +typedef char **(__cdecl* Sys_ListFiles_t)(const char *directory, const char *extension, const char *filter, int *numfiles, int wantsubs); +static Sys_ListFiles_t Sys_ListFiles = (Sys_ListFiles_t)0x00690E40; + +typedef void (__cdecl* FS_FreeFileList_t)(const char **list, int allocTrackType); +static FS_FreeFileList_t FS_FreeFileList = (FS_FreeFileList_t)0x0067DFE0; + +unsigned int __cdecl FS_ReadModDescription(void *ptr, unsigned int len, struct _iobuf *stream); + +int __cdecl FS_GetModList(char *listbuf, int bufsize); + +// +// Used only by Com_WriteConfigToFile and Com_WriteKeyConfigToFile +// +int __cdecl FS_FOpenFileWriteToDir(const char *filename, const char *dir, const char *osbasepath); diff --git a/components/game_mod/com_math.cpp b/components/game_mod/com_math.cpp new file mode 100644 index 00000000..a9a8ff78 --- /dev/null +++ b/components/game_mod/com_math.cpp @@ -0,0 +1,49 @@ +#include "stdafx.h" + +float Vec3DistanceSq(const float *p1, const float *p2) +{ + float v[3]; + v[0] = p2[0] - p1[0]; + v[1] = p2[1] - p1[1]; + v[2] = p2[2] - p1[2]; + + return (v[0] * v[0]) + (v[1] * v[1]) + (v[2] * v[2]); +} + +void Vec3Normalize(float *v) +{ + float m; + float length = sqrt((v[0] * v[0]) + (v[1] * v[1]) + (v[2] * v[2])); + + if (length <= 0.0f) + m = 1.0f; + else + m = 1.0f / length; + + v[0] *= m; + v[1] *= m; + v[2] *= m; +} + +float flrand(float min, float max) +{ + static unsigned int holdrand = 0x89ABCDEF; + + holdrand = 214013 * holdrand + 2531011; + float result = (float)(double)(holdrand >> 17); + + return (((max - min) * result) / 32768.0f) + min; +} + +void __cdecl AxisCopy(const float(*in)[3], float(*out)[3]) +{ + (*out)[0] = (*in)[0]; + (*out)[1] = (*in)[1]; + (*out)[2] = (*in)[2]; + (*out)[3] = (*in)[3]; + (*out)[4] = (*in)[4]; + (*out)[5] = (*in)[5]; + (*out)[6] = (*in)[6]; + (*out)[7] = (*in)[7]; + (*out)[8] = (*in)[8]; +} diff --git a/components/game_mod/com_math.h b/components/game_mod/com_math.h new file mode 100644 index 00000000..3749c515 --- /dev/null +++ b/components/game_mod/com_math.h @@ -0,0 +1,9 @@ +#pragma once + +#define IS_NAN(x) ((*(unsigned int *)(&x) & 0x7F800000) == 0x7F800000) + +float Vec3DistanceSq(const float *p1, const float *p2); +void Vec3Normalize(float *v); +float flrand(float min, float max); + +void __cdecl AxisCopy(const float(*in)[3], float(*out)[3]); diff --git a/components/game_mod/com_memory.cpp b/components/game_mod/com_memory.cpp new file mode 100644 index 00000000..435b1abf --- /dev/null +++ b/components/game_mod/com_memory.cpp @@ -0,0 +1,32 @@ +#include "stdafx.h" + +// /universal/com_memory.cpp:1220 +void *Hunk_Alloc(int size, const char *name, int type) +{ + ASSERT(Sys_IsMainThread() || Sys_IsRenderThread()); + + return Hunk_AllocAlign(size, 32, name, type); +} + +// /universal/com_memory.cpp:1242 +void *Hunk_AllocAlign(int size, int alignment, const char *name, int type) +{ + ASSERT(Sys_IsMainThread() || Sys_IsRenderThread()); + // ASSERT(s_hunkData); + ASSERT(!(alignment & (alignment - 1))); + ASSERT(alignment <= HUNK_MAX_ALIGNMENT); + + return ((void *(__cdecl *)(int, int))0x0043EEB0)(size, alignment); +} + +// /universal/com_memory.cpp:1894 +void Z_Free(void *ptr, int type) +{ + free(ptr); +} + +// /universal/com_memory.cpp:1913 +void *Z_Malloc(int size, const char *name, int type) +{ + return malloc(size); +} \ No newline at end of file diff --git a/components/game_mod/com_memory.h b/components/game_mod/com_memory.h new file mode 100644 index 00000000..6222bbf3 --- /dev/null +++ b/components/game_mod/com_memory.h @@ -0,0 +1,8 @@ +#pragma once + +#define HUNK_MAX_ALIGNMENT 4096 + +void *Hunk_Alloc(int size, const char *name, int type); +void *Hunk_AllocAlign(int size, int alignment, const char *name, int type); +void Z_Free(void *ptr, int type); +void *Z_Malloc(int size, const char *name, int type); \ No newline at end of file diff --git a/components/game_mod/common.cpp b/components/game_mod/common.cpp index 2c61103c..f485f4d3 100644 --- a/components/game_mod/common.cpp +++ b/components/game_mod/common.cpp @@ -1,5 +1,7 @@ #include "stdafx.h" +std::vector g_LevelDependencies; + void (__cdecl * Com_Init)(char *commandLine); void hk_Com_Init(char *commandLine) @@ -50,10 +52,146 @@ void Com_ToolError(int channel, const char* fmt, ...) Com_Error(channel, "%s", msg); } -void __cdecl Com_LoadCommonFastFile() +void Com_DPrintf(int channel, const char *fmt, ...) +{ + if (com_developer_print && com_developer_print->current.integer == 2) + return; + + if (com_developer_print == NULL + || (com_developer_print && com_developer_print->current.integer == 0)) + { + if (!com_developer || !com_developer->current.integer) + return; + } + + if (channel > 31) + return; + + va_list va; + char msg[4096]; + + va_start(va, fmt); + _vsnprintf_s(msg, _TRUNCATE, fmt, va); + va_end(va); + + msg[4095] = '\0'; + Com_Printf(channel, "%s", msg); +} + +int StringTable_HashString(const char *string) +{ + return ((int (__cdecl *)(const char *))0x005F4650)(string); +} + +bool Com_IsMenuLevel(const char *name) +{ + if (!name) + name = sv_mapname->current.string; + + return !I_strnicmp(name, "menu_", 5) || !I_strcmp(name, "frontend"); +} + +bool Com_IsSpecopLevel(const char *name) +{ + if (!name) + name = sv_mapname->current.string; + + return !I_strnicmp(name, "so_", 3); +} + +void Com_GetLevelSharedFastFiles(const char *mapName) +{ + static bool allDependenciesInit = false; + static std::vector allDependencies; + + if (!allDependenciesInit) + { + allDependenciesInit = true; + + char loadBuffer[16384]; + const char* buffer = Com_LoadInfoString("level_dependencies.csv", "level_dependency_info", "", loadBuffer); + + Com_BeginParseSession("level_dependencies.csv"); + Com_SetCSV(true); + + while (true) + { + const char* token = Com_Parse(&buffer); + + if (!*token) + break; + + if (*token != '/') + { + LevelDependency m; + memset(&m, 0, sizeof(m)); + + strcpy_s(m.base, token); + token = Com_Parse(&buffer); + strcpy_s(m.required, token); + + Com_Printf(1, "zone: %s dep: %s\n", m.base, m.required); + + allDependencies.push_back(m); + } + else + { + // Skip the rest of the line if the token was a comment + Com_ParseRestOfLine(&buffer); + } + } + + Com_EndParseSession(); + } + + // Now return each entry for this map name + std::vector returnedMaps; + + for (auto& dependency : allDependencies) + { + if (!_stricmp(dependency.base, mapName)) + returnedMaps.push_back(dependency); + } + + // Will be empty if there's nothing to load + g_LevelDependencies = std::move(returnedMaps); +} + +void DB_RemoveLevelDependency(const char *dependency) +{ + for (auto itr = g_LevelDependencies.begin(); itr != g_LevelDependencies.end();) + { + if (!_stricmp(itr->required, dependency)) + itr = g_LevelDependencies.erase(itr); + else + itr++; + } +} + +bool DB_IsLevelDependency(const char *name) { - dvar_s** fs_gameDirVar = (dvar_s**)0x025FADE8; + for (const LevelDependency& dep : g_LevelDependencies) + { + if (!_stricmp(dep.required, name)) + return true; + } + + return false; +} +bool DB_IsZoneLoaded(const char *name) +{ + for (int i = 1; i < 33; ++i) + { + if (g_zoneNames[i].name[0] && !_stricmp(g_zoneNames[i].name, name)) + return g_zoneNames[i].loaded; + } + + return false; +} + +void Com_LoadCommonFastFile() +{ XZoneInfo zoneInfo[4]; int zoneCount = 0; @@ -66,7 +204,7 @@ void __cdecl Com_LoadCommonFastFile() { zoneInfo[zoneCount].name = nullptr; zoneInfo[zoneCount].allocFlags = 0; - zoneInfo[zoneCount++].freeFlags = 0x100; + zoneInfo[zoneCount++].freeFlags = DB_ZONE_COMMON; DB_LoadXAssets(zoneInfo, zoneCount, 0); return; @@ -77,165 +215,123 @@ void __cdecl Com_LoadCommonFastFile() // if (zombiemode->current.enabled || blackopsmode->current.enabled) { - if ( DB_IsZoneLoaded("common_zombie") ) + if (DB_IsZoneLoaded("common_zombie")) return; + // common_zombie must be unloaded after common/en_common + int zoneAllocFlags = (blackopsmode->current.enabled) ? 0x400 : DB_ZONE_COMMON; + zoneInfo[zoneCount].name = "common_zombie"; - zoneInfo[zoneCount].allocFlags = 0x100; + zoneInfo[zoneCount].allocFlags = zoneAllocFlags; zoneInfo[zoneCount++].freeFlags = 0; - if ((*fs_gameDirVar) != NULL && (*fs_gameDirVar)->current.string[0] != NULL) + if (strlen(Dvar_GetString("fs_game")) > 0) { if (DB_IsZoneLoaded("common_zombie_patch_override")) return; zoneInfo[zoneCount].name = "common_zombie_patch_override"; - zoneInfo[zoneCount].allocFlags = 0x100; + zoneInfo[zoneCount].allocFlags = zoneAllocFlags; zoneInfo[zoneCount++].freeFlags = 0; } } - + if (!zombiemode->current.enabled) { - if ( DB_IsZoneLoaded("common") ) + if (DB_IsZoneLoaded("common")) return; - + zoneInfo[zoneCount].name = "common"; - zoneInfo[zoneCount].allocFlags = 0x100; + zoneInfo[zoneCount].allocFlags = DB_ZONE_COMMON; zoneInfo[zoneCount++].freeFlags = 0; // // No override is needed for common_patch because common_patch doesn't exist in the vanilla game // - /*if ((*fs_gameDirVar) != NULL && (*fs_gameDirVar)->current.string[0] != NULL) + /*if (strlen(Dvar_GetString("fs_game")) > 0) { - if (DB_IsZoneLoaded("common_patch_override")) - return; + if (DB_IsZoneLoaded("common_patch_override")) + return; - zoneInfo[zoneCount].name = "common_patch_override"; - zoneInfo[zoneCount].allocFlags = 0x100; - zoneInfo[zoneCount++].freeFlags = 0; + zoneInfo[zoneCount].name = "common_patch_override"; + zoneInfo[zoneCount].allocFlags = DB_ZONE_COMMON; + zoneInfo[zoneCount++].freeFlags = 0; }*/ } - DB_LoadXAssets(zoneInfo, zoneCount, 0); -} - -char *__cdecl Com_GetLevelSharedFastFile(const char *mapName) -{ - char loadBuffer[16384]; - - int& gLevelDependenciesInited = *(int*)0x02487BE8; - int& gLevelDependenciesCount = *(int*)0x02487BE4; - auto gLevelDependencies = *(char(*)[16][2][64])0x02480F08; - - if (!gLevelDependenciesInited) + if (zoneCount) { - gLevelDependenciesInited = 1; - const char* buffer = Com_LoadInfoString("level_dependencies.csv", "level_dependency_info", "", loadBuffer); - Com_BeginParseSession("level_dependencies.csv"); - Com_SetCSV(1); - while (1) - { - const char* token = Com_Parse(&buffer); - - if (!*token) - break; + // Don't load map required zones if we already did here + for (int i = 0; i < zoneCount; i++) + DB_RemoveLevelDependency(zoneInfo[i].name); - if (*token != '/') - { - if (gLevelDependenciesCount >= 16) - { - Com_PrintWarning(10, "Failed to load level dependencies: Max is %d\n", 16); - } - else - { - I_strncpyz(gLevelDependencies[gLevelDependenciesCount][0], token, 64); - token = Com_Parse(&buffer); - I_strncpyz(gLevelDependencies[gLevelDependenciesCount][1], token, 64); - gLevelDependenciesCount++; - } - } - else - { - // - // Skip the rest of the line if the token was a comment - // - Com_ParseRestOfLine(&buffer); - } - } - Com_EndParseSession(); - } - - for (int i = 0; i < gLevelDependenciesCount; ++i) - { - if (!I_strcmp(gLevelDependencies[i][0], mapName)) - return gLevelDependencies[i][1]; + DB_LoadXAssets(zoneInfo, zoneCount, 0); } - - return NULL; } -void __cdecl Com_LoadLevelFastFiles(const char *mapName) +void Com_LoadLevelFastFiles(const char *mapName) { int zoneCount = 0; - XZoneInfo zoneInfo[6]; + XZoneInfo zoneInfo[10]; DB_ResetZoneSize(0); UI_SetLoadingScreenMaterial(mapName); + Com_GetLevelSharedFastFiles(mapName); if (I_stristr(mapName, "zombietron")) { - dvar_s* zombiemode = *(dvar_s**)(0x0243FDD4); - dvar_s* zombietron = *(dvar_s**)(0x0247FDE8); - - Dvar_SetBool(zombiemode, 1); - Dvar_SetBool(zombietron, 1); + Dvar_SetBool(zombiemode, true); + Dvar_SetBool(zombietron, true); } - if (!I_strnicmp(mapName, "menu_", 5) || !I_strcmp(mapName, "frontend")) + if (Com_IsMenuLevel(mapName)) { zoneInfo[zoneCount].name = "patch_ui"; - zoneInfo[zoneCount].allocFlags = 0x4000000; + zoneInfo[zoneCount].allocFlags = DB_ZONE_PATCH_UI; zoneInfo[zoneCount++].freeFlags = 0; } - - if (I_strnicmp(mapName, "menu_", 5) && I_strcmp(mapName, "frontend")) + else { + // If we aren't on a menu level, load common assets Com_LoadCommonFastFile(); } // // Enable the use of level_dependencies.csv // -#if _UNSTABLE && _USE_LEVEL_DEPENDENCIES - char* levelSharedFastFile = Com_GetLevelSharedFastFile(mapName); - if (levelSharedFastFile) +#if _USE_LEVEL_DEPENDENCIES + for (auto& dependency : g_LevelDependencies) { - zoneInfo[zoneCount].name = levelSharedFastFile; - zoneInfo[zoneCount].allocFlags = 0x800; + if (DB_IsZoneLoaded(dependency.required)) + continue; + + // The allocFlags were originally 0x800 - but would cause a free error (these new flags appear to load / unload correctly) + zoneInfo[zoneCount].name = dependency.required; + zoneInfo[zoneCount].allocFlags = 0x4000000; zoneInfo[zoneCount++].freeFlags = 0; } #endif char specOpsZoneName[64]; - if (!I_strncmp("so_", mapName, strlen("so_"))) + if (Com_IsSpecopLevel(mapName)) { - const char* basename = NULL; - for (basename = &mapName[strlen("so_")]; *basename && *basename != '_'; ++basename) {}; + const char* basename = nullptr; + for (basename = &mapName[strlen("so_")]; *basename && *basename != '_'; ++basename) + /* Do nothing */; if (!*basename) Com_PrintError(1, "Bad specop level name\n"); - sprintf_s(specOpsZoneName, 64, "%s", basename + 1); + sprintf_s(specOpsZoneName, "%s", basename + 1); zoneInfo[zoneCount].name = specOpsZoneName; zoneInfo[zoneCount].allocFlags = 0x800; zoneInfo[zoneCount++].freeFlags = 0; } - int allocFlags = 0x0; - if (I_strnicmp(mapName, "menu_", 5) && I_strcmp(mapName, "frontend")) - allocFlags = I_strncmp("so_", mapName, 3) != 0 ? 0x800 : 0x4000; + int allocFlags = 0; + + if (!Com_IsMenuLevel(mapName)) + allocFlags = Com_IsSpecopLevel(mapName) ? 0x4000 : 0x800; else allocFlags = 0x2000000; @@ -246,7 +342,7 @@ void __cdecl Com_LoadLevelFastFiles(const char *mapName) // // Enable _patch_override.ff for legacy mod support // - char patchOverrideFastFile[256]; + char patchOverrideFastFile[64]; sprintf_s(patchOverrideFastFile, "%s_patch_override", mapName); zoneInfo[zoneCount].name = patchOverrideFastFile; zoneInfo[zoneCount].allocFlags = 0x4000000; @@ -254,5 +350,23 @@ void __cdecl Com_LoadLevelFastFiles(const char *mapName) R_BeginRemoteScreenUpdate(); DB_LoadXAssets(zoneInfo, zoneCount, 0); - R_EndRemoteScreenUpdate(NULL); + R_EndRemoteScreenUpdate(nullptr); } + +void Com_FreeWeaponInfoMemory(int source) +{ + if (weaponInfoSource == source) + { + weaponInfoSource = 0; + + if (!useFastFile->current.enabled) + { + BG_ClearWeaponDefInternal(); + BG_FreeWeaponDefStrings(); + } + + bg_lastParsedWeaponIndex = 0; + bg_firstWeaponTableIndex = 0; + bg_lastWeaponTableIndex = 0; + } +} \ No newline at end of file diff --git a/components/game_mod/common.h b/components/game_mod/common.h index c37a8230..5d868abf 100644 --- a/components/game_mod/common.h +++ b/components/game_mod/common.h @@ -13,6 +13,19 @@ struct field_t char buffer[256]; }; +enum errorParm_t +{ + ERR_FATAL = 0x0, + ERR_DROP = 0x1, + ERR_SERVERDISCONNECT = 0x2, + ERR_DISCONNECT = 0x3, + ERR_SCRIPT = 0x4, + ERR_SCRIPT_DROP = 0x5, + ERR_LOCALIZATION = 0x6, +}; + +static int& weaponInfoSource = *(int *)0x024817D8; + extern void (__cdecl * Com_Init)(char *commandLine); void hk_Com_Init(char *commandLine); @@ -29,6 +42,7 @@ static Com_Printf_t Com_Error = (Com_Printf_t)0x00651D90; // void Com_ToolPrintf(int channel, const char* fmt, ...); void Com_ToolError(int channel, const char* fmt, ...); +void Com_DPrintf(int channel, const char *fmt, ...); typedef char *va_t(const char *format, ...); static va_t* va = (va_t*)0x0057CDD0; @@ -56,8 +70,6 @@ typedef ParseThreadInfo *(__cdecl* Com_EndParseSession_t)(); static Com_EndParseSession_t Com_EndParseSession = (Com_EndParseSession_t)0x005C11C0; -void __cdecl Com_LoadCommonFastFile(); - typedef int (__cdecl* I_strcmp_t)(const char *s0, const char *s1); static I_strcmp_t I_strcmp = (I_strcmp_t)0x0063E630; @@ -73,5 +85,19 @@ static I_stristr_t I_stristr = (I_stristr_t)0x0062F110; typedef void (__cdecl* I_strncpyz_t)(char *dest, const char *src, int destsize); static I_strncpyz_t I_strncpyz = (I_strncpyz_t)0x005D4D60; -char *__cdecl Com_GetLevelSharedFastFile(const char *mapName); -void __cdecl Com_LoadLevelFastFiles(const char *mapName); +struct LevelDependency +{ + char base[64]; // Base map + char required[64]; // FF required for loading the map +}; + +int StringTable_HashString(const char *string); +bool Com_IsMenuLevel(const char *name); +bool Com_IsSpecopLevel(const char *name); +void Com_GetLevelSharedFastFiles(const char *mapName); +void DB_RemoveLevelDependency(const char *dependency); +bool DB_IsLevelDependency(const char *name); +bool DB_IsZoneLoaded(const char *name); +void Com_LoadCommonFastFile(); +void Com_LoadLevelFastFiles(const char *mapName); +void Com_FreeWeaponInfoMemory(int source); \ No newline at end of file diff --git a/components/game_mod/cscr_parser.cpp b/components/game_mod/cscr_parser.cpp new file mode 100644 index 00000000..36417c37 --- /dev/null +++ b/components/game_mod/cscr_parser.cpp @@ -0,0 +1,67 @@ +#include "stdafx.h" + +void __declspec(naked) hk_Scr_ReadFile() +{ + _asm + { + push[esp+16] // archive + push[esp+16] // codePos + push esi // extFilename + push[esp+20] // filename + push[esp+20] // inst + + call Scr_ReadFile + add esp, 20 + + retn + } +} + +char *__cdecl Scr_ReadFile(scriptInstance_t inst, const char *filename, const char *extFilename, const char *codePos, bool archive) +{ + char* result = nullptr; + + if (!scr_useFastFileOnly->current.integer && (fs_gameDirVar && *fs_gameDirVar->current.string || developer->current.enabled)) + { + int file = 0; + if (FS_FOpenFileRead(extFilename, &file) < 0) + { + result = Scr_ReadFile_FastFile(inst, filename, extFilename, codePos, archive); + } + else + { + FS_FCloseFile(file); + result = Scr_ReadFile_LoadObj(inst, filename, extFilename, codePos, archive); + } + } + else + { + Scr_ReadFile_t func = Scr_ReadFile_FastFile; + if (!useFastFile->current.enabled) + func = Scr_ReadFile_LoadObj; + result = func(inst, filename, extFilename, codePos, archive); + } + + return result; +} + +void __declspec(naked) mfh_RuntimeError(void) +{ + static DWORD dwRetn_SuppressError = 0x005A17E9; + static DWORD dwRetn_HandleError = 0x005A1738; + + __asm + { + jz SUPPRESS_ERROR + + push ecx + mov ecx, scr_suppressErrors + cmp byte ptr[ecx + 0x18], 0 + pop ecx + jnz SUPPRESS_ERROR + jmp dwRetn_HandleError + + SUPPRESS_ERROR: + jmp dwRetn_SuppressError + } +} diff --git a/components/game_mod/cscr_parser.h b/components/game_mod/cscr_parser.h new file mode 100644 index 00000000..e63cd63f --- /dev/null +++ b/components/game_mod/cscr_parser.h @@ -0,0 +1,10 @@ +#pragma once + +typedef char *(__cdecl* Scr_ReadFile_t)(scriptInstance_t inst, const char *filename, const char *extFilename, const char *codePos, bool archive); +static Scr_ReadFile_t Scr_ReadFile_FastFile = (Scr_ReadFile_t)0x008A57B0; +static Scr_ReadFile_t Scr_ReadFile_LoadObj = (Scr_ReadFile_t)0x008A58C0; + +void hk_Scr_ReadFile();//scriptInstance_t inst, const char *filename, const char *codePos, bool archive); +char *__cdecl Scr_ReadFile(scriptInstance_t inst, const char *filename, const char *extFilename, const char *codePos, bool archive); + +void mfh_RuntimeError(void); diff --git a/components/game_mod/cscr_vm.cpp b/components/game_mod/cscr_vm.cpp new file mode 100644 index 00000000..bca3c33d --- /dev/null +++ b/components/game_mod/cscr_vm.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" + +// /clientscript/cscr_vm.cpp:5230 +const char *Scr_GetString(unsigned int index, scriptInstance_t inst) +{ + return ((const char *(__cdecl *)(unsigned int, scriptInstance_t))0x00567CB0)(index, inst); +} + +// /clientscript/cscr_vm.cpp:5416 +void Scr_AddBool(int value, scriptInstance_t inst) +{ + ((void(__cdecl *)(int, scriptInstance_t))0x0045DBB0)(value, inst); +} + +// /clientscript/cscr_vm.cpp:5603 +void Scr_Error(const char *error, bool force_terminal) +{ + ((void(__cdecl *)(const char *, bool))0x00644900)(error, force_terminal); +} + +// /clientscript/cscr_vm.cpp:5656 +void Scr_ObjectError(const char *error, scriptInstance_t inst) +{ + ((void(__cdecl *)(const char *, scriptInstance_t))0x0064A840)(error, inst); +} + +void SL_RemoveRefToString(scriptInstance_t inst, unsigned int stringValue) +{ + ((void(__cdecl *)(scriptInstance_t, unsigned int))0x00545370)(inst, stringValue); +} + +void __cdecl GScr_NewDebugHudElem() +{ + if (developer->current.enabled) + GScr_NewHudElem(); +} diff --git a/components/game_mod/cscr_vm.h b/components/game_mod/cscr_vm.h new file mode 100644 index 00000000..9f683ecd --- /dev/null +++ b/components/game_mod/cscr_vm.h @@ -0,0 +1,26 @@ +#pragma once + +enum scriptInstance_t +{ + SCRIPTINSTANCE_SERVER = 0x0, + SCRIPTINSTANCE_CLIENT = 0x1, + SCRIPT_INSTANCE_MAX = 0x2, +}; + +struct scr_entref_t +{ + unsigned __int16 entnum; + unsigned __int16 classnum; + unsigned __int16 client; +}; + +const char *Scr_GetString(unsigned int index, scriptInstance_t inst); +void Scr_AddBool(int value, scriptInstance_t inst); +void Scr_Error(const char *error, bool force_terminal); +void Scr_ObjectError(const char *error, scriptInstance_t inst); +void SL_RemoveRefToString(scriptInstance_t inst, unsigned int stringValue); + +typedef void (__cdecl* GScr_NewHudElem_t)(); +static GScr_NewHudElem_t GScr_NewHudElem = (GScr_NewHudElem_t)0x006707C0; + +void __cdecl GScr_NewDebugHudElem(); diff --git a/components/game_mod/d3d9ex.cpp b/components/game_mod/d3d9ex.cpp new file mode 100644 index 00000000..9f5c47a6 --- /dev/null +++ b/components/game_mod/d3d9ex.cpp @@ -0,0 +1,103 @@ +#include "stdafx.h" + +bool IsD3D9ExAvailable() +{ + return r_d3d9ex->current.enabled; +} + +IDirect3D9 *D3DAPI hk_Direct3DCreate9(UINT SDKVersion) +{ + HMODULE d3d9 = GetModuleHandleA("d3d9.dll"); + + if (!d3d9) + d3d9 = LoadLibraryA("d3d9.dll"); + + if (!d3d9) + return nullptr; + + auto pDirect3DCreate9 = (decltype(&Direct3DCreate9))GetProcAddress(d3d9, "Direct3DCreate9"); + auto pDirect3DCreate9Ex = (decltype(&Direct3DCreate9Ex))GetProcAddress(d3d9, "Direct3DCreate9Ex"); + + IDirect3D9 *d3d = nullptr; + IDirect3D9Ex *d3dex = nullptr; + + // Check for Direct3DCreate9Ex first (user must manually enable dvar, autodetection disabled) + if (r_d3d9ex->current.enabled && pDirect3DCreate9Ex) + { + if (SUCCEEDED(pDirect3DCreate9Ex(SDKVersion, &d3dex))) + { + Com_Printf(1, "Using Direct3D9Ex interface\n"); + return d3dex; + } + } + + // Otherwise default to the normal Direct3DCreate9 + if (pDirect3DCreate9) + { + if (d3d = pDirect3DCreate9(SDKVersion)) + { + r_d3d9ex->current.enabled = false; + return d3d; + } + } + + r_d3d9ex->current.enabled = false; + return nullptr; +} + +HRESULT D3DAPI hk_CreateDevice(IDirect3D9 *This, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DDevice9 **ppReturnedDeviceInterface) +{ + // Short-circuit to the original call + if (!IsD3D9ExAvailable()) + return This->CreateDevice(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface); + + // D3D requires a specific structure when in windowed mode + D3DDISPLAYMODEEX displayMode; + displayMode.Size = sizeof(D3DDISPLAYMODEEX); + displayMode.Width = pPresentationParameters->BackBufferWidth; + displayMode.Height = pPresentationParameters->BackBufferHeight; + displayMode.RefreshRate = pPresentationParameters->FullScreen_RefreshRateInHz; + displayMode.Format = pPresentationParameters->BackBufferFormat; + displayMode.ScanLineOrdering = D3DSCANLINEORDERING_UNKNOWN; + + // This must be null when in windowed mode + D3DDISPLAYMODEEX *realMode = (pPresentationParameters->Windowed) ? nullptr : &displayMode; + + HRESULT hr = ((IDirect3D9Ex *)This)->CreateDeviceEx(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, realMode, (IDirect3DDevice9Ex **)ppReturnedDeviceInterface); + + if (FAILED(hr)) + return hr; + + printf("Using CreateDeviceEx for device creation\n"); + return D3D_OK; +} + +HRESULT D3DAPI hk_GetSwapChain(IDirect3DDevice9 *This, UINT iSwapChain, IDirect3DSwapChain9 **ppSwapChain) +{ + // Get the standard DirectX 9 swapchain + HRESULT hr = This->GetSwapChain(iSwapChain, ppSwapChain); + + if (FAILED(hr)) + return hr; + + if (!IsD3D9ExAvailable()) + return D3D_OK; + + // Get a handle to the IDirect3DSwapChain9Ex interface via COM GUID + return (*ppSwapChain)->QueryInterface(__uuidof(IDirect3DSwapChain9Ex), (void **)ppSwapChain); +} + +HRESULT D3DAPI hk_CreateAdditionalSwapChain(IDirect3DDevice9 *This, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DSwapChain9 **ppSwapChain) +{ + // Short-circuit to the original call + HRESULT hr = This->CreateAdditionalSwapChain(pPresentationParameters, ppSwapChain); + + if (FAILED(hr)) + return hr; + + if (!IsD3D9ExAvailable()) + return D3D_OK; + + // Get a handle to the IDirect3DSwapChain9Ex interface via COM GUID + return (*ppSwapChain)->QueryInterface(__uuidof(IDirect3DSwapChain9Ex), (void **)ppSwapChain); +} \ No newline at end of file diff --git a/components/game_mod/d3d9ex.h b/components/game_mod/d3d9ex.h new file mode 100644 index 00000000..e9f82a5c --- /dev/null +++ b/components/game_mod/d3d9ex.h @@ -0,0 +1,8 @@ +#pragma once + +bool IsD3D9ExAvailable(); + +IDirect3D9 *D3DAPI hk_Direct3DCreate9(UINT SDKVersion); +HRESULT D3DAPI hk_CreateDevice(IDirect3D9 *This, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DDevice9 **ppReturnedDeviceInterface); +HRESULT D3DAPI hk_GetSwapChain(IDirect3DDevice9 *This, UINT iSwapChain, IDirect3DSwapChain9 **ppSwapChain); +HRESULT D3DAPI hk_CreateAdditionalSwapChain(IDirect3DDevice9 *This, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DSwapChain9 **ppSwapChain); \ No newline at end of file diff --git a/components/game_mod/db_registry.cpp b/components/game_mod/db_registry.cpp index 215b2670..5e188c39 100644 --- a/components/game_mod/db_registry.cpp +++ b/components/game_mod/db_registry.cpp @@ -1,13 +1,84 @@ #include "stdafx.h" -void** DB_XAssetPool = (void**)0x00B741B8; -DWORD* g_poolSize = (DWORD*)0x00B73EF8; +static_assert(GENERIC_READ == 0x80000000, "GENERIC_READ must have a value of 0x80000000"); +static_assert((FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING) == 0x60000000, "(FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING) must have a value of 0x60000000"); +static_assert(FILE_SHARE_READ == 0x1, "FILE_SHARE_READ must have a value of 0x1"); +static_assert(OPEN_EXISTING == 0x3, "OPEN_EXISTING must have a value of 0x3"); + +XAssetEntry *DB_LinkXAssetEntry(XAssetEntry *newEntry, int allowOverride) +{ + // If yes, skip loading certain assets in this FF + if (DB_IsLevelDependency(g_load_filename)) + { + const char *assetName = DB_GetXAssetName(&newEntry->asset); + XAssetType assetType = newEntry->asset.type; + + // Com_Printf(1, "Asset: [%01d] %s\n", assetType, assetName); + + switch (assetType) + { + case ASSET_TYPE_CLIPMAP: + case ASSET_TYPE_CLIPMAP_PVS: + case ASSET_TYPE_COMWORLD: + case ASSET_TYPE_GAMEWORLD_SP: + case ASSET_TYPE_GAMEWORLD_MP: + case ASSET_TYPE_GFXWORLD: + case ASSET_TYPE_GLASSES: + case ASSET_TYPE_MATERIAL: + case ASSET_TYPE_DESTRUCTIBLEDEF: + case ASSET_TYPE_XMODEL: + case ASSET_TYPE_WEAPON: + case ASSET_TYPE_WEAPONDEF: + case ASSET_TYPE_WEAPON_VARIANT: + // Never load certain types (maps), regardless of what the user says + return newEntry; + + // case ASSET_TYPE_SOUND: + // case ASSET_TYPE_SOUND_PATCH: + // // NOTE: If excluded, SND_AddPatch/SND_AddBank must be fixed + // break; + + default: + // TODO: Allow certain asset types from the CSV. For now allow asset overrides. + allowOverride = 1; + break; + } + } + + return ((XAssetEntry *(__cdecl *)(XAssetEntry *, int))0x007A2F10)(newEntry, allowOverride); +} + +const char *DB_GetXAssetName(XAsset *asset) +{ + ASSERT(asset); + + return DB_GetXAssetHeaderName(asset->type, &asset->header); +} + +const char *DB_GetXAssetHeaderName(XAssetType type, XAssetHeader *header) +{ + ASSERT(header); + ASSERT(DB_XAssetGetNameHandler[type]); + ASSERT(header->data); + + const char *name = DB_XAssetGetNameHandler[type](header); + ASSERT_MSG_VA(name, "Name not found for asset type %s\n", DB_GetXAssetTypeName(type)); + + return name; +} + +const char *DB_GetXAssetTypeName(int type) +{ + ASSERT(type >= 0 && type < ASSET_TYPE_COUNT); + + return g_assetNames[type]; +} void DB_SyncXAssets() { R_BeginRemoteScreenUpdate(); Sys_SyncDatabase(); - R_EndRemoteScreenUpdate(0); + R_EndRemoteScreenUpdate(nullptr); SocketRouter_EmergencyFrame("DB_SyncXAssets"); DB_PostLoadXZone(); } @@ -18,27 +89,27 @@ void DB_LoadGraphicsAssetsForPC() XZoneInfo zoneInfo[6]; zoneInfo[0].name = "code_post_gfx"; - zoneInfo[0].allocFlags = 1; - zoneInfo[0].freeFlags = 0x80000000; + zoneInfo[0].allocFlags = DB_ZONE_CODE; + zoneInfo[0].freeFlags = DB_FLAG_STRICTFREE; DB_LoadXAssets(zoneInfo, zoneCount, 0); DB_SyncXAssets(); zoneInfo[0].name = "patch"; - zoneInfo[0].allocFlags = 8; - zoneInfo[0].freeFlags = 0; + zoneInfo[0].allocFlags = DB_ZONE_PATCH; + zoneInfo[0].freeFlags = DB_FLAG_NULL; //Add frontend_patch to the zone list (enable mods button) zoneInfo[zoneCount].name = "frontend_patch"; - zoneInfo[zoneCount].allocFlags = 32; - zoneInfo[zoneCount].freeFlags = 0; + zoneInfo[zoneCount].allocFlags = DB_ZONE_MOD; + zoneInfo[zoneCount].freeFlags = DB_FLAG_NULL; zoneCount++; if(DB_ModFileExists()) { zoneInfo[zoneCount].name = "mod"; - zoneInfo[zoneCount].allocFlags = 32; - zoneInfo[zoneCount].freeFlags = 0; + zoneInfo[zoneCount].allocFlags = DB_ZONE_MOD; + zoneInfo[zoneCount].freeFlags = DB_FLAG_NULL; zoneCount++; } @@ -60,43 +131,46 @@ void __declspec(naked) DB_ModXFileHandle_hk() void DB_ModXFileHandle(HANDLE *zoneFile, char* zoneName, FF_DIR *zoneDir) { - dvar_s** fs_gameDirVar = (dvar_s**)0x025FADE8; - dvar_s** fs_usermapDir = (dvar_s**)0x025FADE4; - dvar_s** fs_homepath = (dvar_s**)0x025FBF0C; - char* str_mod = (char*)0x00A18DE8; - - HANDLE h = 0; char filename[MAX_PATH]; - - if( (*fs_gameDirVar) && (*fs_gameDirVar)->current.string && !strcmp(zoneName,str_mod)) //Loads mods/fs_game/mod.ff + const char *gameModDir = (fs_gameDirVar) ? fs_gameDirVar->current.string : ""; + + // Try loading /mods/fs_game/mod.ff + if(strlen(gameModDir) > 0 && !strcmp(zoneName, "mod")) { - sprintf_s(filename, "%s\\%s\\%s.ff", (*fs_homepath)->current.string, (*fs_gameDirVar)->current.string, zoneName); - HANDLE h = CreateFileA(filename, 0x80000000, 1u, 0, 3u, 0x60000000u, 0); - *zoneFile = h; - if ( h != (HANDLE)-1 ) + sprintf_s(filename, "%s\\%s\\%s.ff", fs_homepath->current.string, gameModDir, zoneName); + *zoneFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, nullptr); + + if (*zoneFile != INVALID_HANDLE_VALUE) + { *zoneDir = FFD_MOD_DIR; + return; + } } - else if(strcmp((*fs_gameDirVar)->current.string, "raw") != 0 && strlen((*fs_gameDirVar)->current.string) > 0) //Load custom maps associated with mods + + // Load custom maps associated with mods + if(strlen(gameModDir) > 0 && strcmp(gameModDir, "raw") != 0) { - sprintf_s(filename,"%s\\%s\\%s.ff", (*fs_homepath)->current.string, (*fs_gameDirVar)->current.string, zoneName); - - h = CreateFileA(filename, 0x80000000, 1u, 0, 3u, 0x60000000u, 0); - *zoneFile = h; - if(h != (HANDLE)-1) + sprintf_s(filename,"%s\\%s\\%s.ff", fs_homepath->current.string, gameModDir, zoneName); + *zoneFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, nullptr); + + if (*zoneFile != INVALID_HANDLE_VALUE) + { *zoneDir = FFD_MOD_DIR; + return; + } } - else //Load usermaps/map + + // Try /usermaps/map + if (fs_usermapDir && fs_usermapDir->current.string) { - //h = (HANDLE)(*fs_usermapDir); - if( (*fs_usermapDir) && (*fs_usermapDir)->current.string) - { - const char* usermapDir = (*fs_usermapDir)->current.string; - sprintf_s(filename, "%s\\%s\\%s\\%s.ff", (*fs_homepath)->current.string, "usermaps", usermapDir, zoneName); + const char* usermapDir = fs_usermapDir->current.string; + sprintf_s(filename, "%s\\usermaps\\%s\\%s.ff", fs_homepath->current.string, usermapDir, zoneName); + *zoneFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, nullptr); - h = CreateFileA(filename, 0x80000000, 1u, 0, 3u, 0x60000000u, 0); - *zoneFile = h; - if(h != (HANDLE)-1) - *zoneDir = FFD_USER_MAP; + if (*zoneFile != INVALID_HANDLE_VALUE) + { + *zoneDir = FFD_USER_MAP; + return; } } } @@ -110,3 +184,65 @@ void* DB_ReallocXAssetPool(XAssetType type, unsigned int size) return assetPool; } + +void __cdecl DB_ListAssetPool_f(void) +{ + if (Cmd_Argc() >= 2) + { + const char* val = Cmd_Argv(1); + + // Assume the user typed a number by default + // Check if any non-numeric chars appear + bool isNumeric = true; + for (const char* c = val; *c; c++) + { + if (!isdigit(*c)) + { + isNumeric = false; + break; + } + } + + XAssetType type = ASSET_TYPE_COUNT; + if (isNumeric) + { + type = (XAssetType)atoi(val); + if (type < 0 || type >= ASSET_TYPE_COUNT) + type = ASSET_TYPE_COUNT; + } + else + { + for (int i = 0; i < ASSET_TYPE_COUNT; i++) + { + if (strcmp(val, DB_GetXAssetTypeName(i)) == 0) + { + type = (XAssetType)i; + break; + } + } + } + + if (type != ASSET_TYPE_COUNT) + { + DB_ListAssetPool(type); + return; + } + } + + Com_Printf(0, "listassetpool : lists all the assets in the specified pool\n"); + for (int i = 0; i < ASSET_TYPE_COUNT; ++i) + { + const char* name = DB_GetXAssetTypeName(i); + Com_Printf(0, "%d %s\n", i, name); + } +} + +bool DB_IsXAssetDefault(XAssetType type, const char *name) +{ + return ((bool (__cdecl *)(XAssetType, const char *))0x0062DDD0)(type, name); +} + +void DB_ExternalInitAssets() +{ + BG_FillInAllWeaponItems(); +} \ No newline at end of file diff --git a/components/game_mod/db_registry.h b/components/game_mod/db_registry.h index f8e0594f..10d5f101 100644 --- a/components/game_mod/db_registry.h +++ b/components/game_mod/db_registry.h @@ -1,5 +1,5 @@ #pragma once -#include +#include "bg_weapons_def.h" enum FF_DIR : DWORD { @@ -8,6 +8,22 @@ enum FF_DIR : DWORD FFD_USER_MAP }; +struct XZoneName +{ + char name[64]; + int flags; + int fileSize; + FF_DIR dir; + bool loaded; +}; +STATIC_ASSERT_SIZE(XZoneName, 0x50); + +struct XZoneInfoInternal +{ + char name[64]; + int flags; +}; + struct XZoneInfo { const char *name; @@ -63,9 +79,106 @@ enum XAssetType ASSET_TYPE_COUNT }; +union XAssetHeader +{ + struct Material *material; + struct MaterialPixelShader *pixelShader; + struct MaterialVertexShader *vertexShader; + struct MaterialTechniqueSet *techniqueSet; + struct GfxImage *image; + + struct GfxLightDef *lightDef; + + WeaponVariantDef *weapon; + + void *data; +}; -extern void** DB_XAssetPool; -extern DWORD* g_poolSize; +struct XAsset +{ + XAssetType type; + XAssetHeader header; +}; +STATIC_ASSERT_SIZE(XAsset, 0x8); + +struct XAssetEntry +{ + XAsset asset; + char zoneIndex; + bool inuse; + unsigned __int16 nextHash; + unsigned __int16 nextOverride; + unsigned __int16 usageFrame; +}; + +enum XZoneFlags +{ + DB_ZONE_NULL = 0, + DB_ZONE_CODE = 1, // 0x1, + DB_ZONE_CODE_LOC = 1 << 1, // 0x2, + + DB_ZONE_DEV = 1 << 2, // 0x4, (IN SP: DB_ZONE_DEV == DB_ZONE_DEV_LOC ) + // DB_ZONE_DEV_LOC = 1 << 2, // 0x4, + + DB_ZONE_PATCH = 1 << 3, // 0x8, + DB_ZONE_PATCH_LOC = 1 << 4, // 0x10, + DB_ZONE_MOD = 1 << 5, // 0x20, + DB_ZONE_MOD_LOC = 1 << 6, // 0x40, + DB_ZONE_COMMON = 1 << 8, // 0x100, + DB_ZONE_COMMON_LOC = 1 << 8, // 0x100, + +#if 0 + // These values need to be validated + DB_ZONE_FFOTD = 1 << 9, // 0x200, + DB_ZONE_FFOTD_LOC = 1 << 10, // 0x400, + DB_ZONE_LEVELCOMMON = 1 << 11, // 0x800, + DB_ZONE_LEVELCOMMON_LOC = 1 << 12, // 0x1000, + DB_ZONE_LEVEL = 1 << 13, // 0x2000, + DB_ZONE_LEVEL_LOC = 1 << 14, // 0x4000, + DB_ZONE_LEVELOVERLAY = 1 << 15, // 0x8000, + DB_ZONE_LEVELOVERLAY_LOC = 1 << 16, // 0x10000, +#endif + + DB_ZONE_GUMP = 1 << 17, // 0x20000, + DB_ZONE_GUMP_LOC = 1 << 18, // 0x40000, + +#if 0 + // These values need to be validated + DB_ZONE_LOW_MARKER = 1 << 19, // 0x80000, + DB_ZONE_MYCHANGES_LOC = 1 << 20, // 0x100000, + DB_ZONE_MYCHANGES = 1 << 21, // 0x200000, + + // Probably not used in SP + DB_ZONE_UI_VIEWER_LOC = 1 << 22, // 0x400000, + DB_ZONE_UI_VIEWER = 1 << 23, // 0x800000, + + DB_ZONE_FRONTEND_LOC = 1 << 24, // 0x1000000, + DB_ZONE_FRONTEND = 1 << 25, // 0x2000000, +#endif + + DB_ZONE_PATCH_UI = 1 << 26, // 0x4000000, + DB_ZONE_PATCH_UI_LOC = 1 << 27, // 0x8000000, + +#if 0 + // These values need to be validated + DB_ZONE_HIGH_MARKER = 1 << 26, // 0x4000000, + DB_ZONE_LOAD = 1 << 27, // 0x8000000, +#endif + + DB_FLAG_NULL = 0, + DB_FLAG_RSTREAM = 1 << 30, // 0x40000000, + DB_FLAG_STRICTFREE = 1 << 31, // 0x80000000, +}; + +VANILLA_VALUE(g_zoneInfoCount, volatile unsigned int, 0x00E72984); +static XZoneInfoInternal *g_zoneInfo = (XZoneInfoInternal *)0x00C84308; +static XZoneName *g_zoneNames = (XZoneName *)0x010C6608; +static void **DB_XAssetPool = (void **)0x00B741B8; +static DWORD *g_poolSize = (DWORD *)0x00B73EF8; +static char **g_assetNames = (char **)0x00B73BB0; +VANILLA_VALUE(g_load_filename, char*, 0x00C7934C); + +static auto DB_XAssetGetNameHandler = (const char *(__cdecl **)(XAssetHeader *))0x00B73C60; typedef void (__cdecl* DB_LoadXAssets_t)(XZoneInfo *zoneInfo, unsigned int zoneCount, int sync); static DB_LoadXAssets_t DB_LoadXAssets = (DB_LoadXAssets_t)0x00631B10; @@ -91,18 +204,30 @@ static R_EndRemoteScreenUpdate_t R_EndRemoteScreenUpdate = (R_EndRemoteScreenUpd typedef void (__cdecl* SocketRouter_EmergencyFrame_t)(const char *from); static SocketRouter_EmergencyFrame_t SocketRouter_EmergencyFrame = (SocketRouter_EmergencyFrame_t)0x004F11D0; -typedef bool (__cdecl* DB_IsZoneLoaded_t)(const char *name); -static DB_IsZoneLoaded_t DB_IsZoneLoaded = (DB_IsZoneLoaded_t)0x00528A20; - typedef void (__cdecl* DB_PostLoadXZone_t)(); static DB_PostLoadXZone_t DB_PostLoadXZone = (DB_PostLoadXZone_t)0x007A48D0; typedef int (__cdecl* DB_GetXAssetTypeSize_t)(int type); static DB_GetXAssetTypeSize_t DB_GetXAssetTypeSize = (DB_GetXAssetTypeSize_t)0x00514840; +typedef void(__cdecl* DB_LogMissingAsset_t)(XAssetType type, const char *name); +static DB_LogMissingAsset_t DB_LogMissingAsset = (DB_LogMissingAsset_t)0x004AEC20; + +typedef void(__cdecl* DB_EnumXAssets_t)(XAssetType type, void(__cdecl *func)(XAssetHeader, void *), void *inData, bool includeOverride); +static DB_EnumXAssets_t DB_EnumXAssets = (DB_EnumXAssets_t)0x0054C1C0; + +typedef void (__cdecl* DB_ListAssetPool_t)(XAssetType type); +static DB_ListAssetPool_t DB_ListAssetPool = (DB_ListAssetPool_t)0x007A2C00; + +XAssetEntry *DB_LinkXAssetEntry(XAssetEntry *newEntry, int allowOverride); +const char *DB_GetXAssetName(XAsset *asset); +const char *DB_GetXAssetHeaderName(XAssetType type, XAssetHeader *header); +const char *DB_GetXAssetTypeName(int type); void DB_SyncXAssets(); void DB_LoadGraphicsAssetsForPC(); void DB_ModXFileHandle_hk(); void DB_ModXFileHandle(HANDLE *zoneFile, char* zoneName, FF_DIR *zoneDir); - void* DB_ReallocXAssetPool(XAssetType type, unsigned int size); +void __cdecl DB_ListAssetPool_f(void); +bool DB_IsXAssetDefault(XAssetType type, const char *name); +void DB_ExternalInitAssets(); \ No newline at end of file diff --git a/components/game_mod/dllmain.cpp b/components/game_mod/dllmain.cpp index f5590f1b..69dc91a5 100644 --- a/components/game_mod/dllmain.cpp +++ b/components/game_mod/dllmain.cpp @@ -1,5 +1,11 @@ +#define G_VERSION 1, 1, 0 #include "stdafx.h" +// Defined in patch_misc.cpp +#if USE_MISC_PATCHES +void PatchMisc(); +#endif + BOOL GameMod_Init() { // @@ -51,13 +57,65 @@ BOOL GameMod_Init() // // "com_introPlayed" // "com_startupIntroPlayed" - // "cg_fov" (disable DVAR_CHEAT flag) // "cg_fov_default" (max 165.0) + // "r_enablePlayerShadow" remove cheat // PatchMemory(0x0082C0F9, (PBYTE)"\x01", 1); PatchMemory(0x0082C111, (PBYTE)"\x01", 1); - PatchMemory(0x004A3920, (PBYTE)"\x68\x01\x00\x00\x00", 5); PatchMemory(0x004A394A, (PBYTE)"\xD9\x05\xE4\x9F\xA1\x00", 6); + PatchMemory(0x006CBE24, (PBYTE)"\x00", 1); + + DWORD flags = 0x1; // DVAR_ARCHIVED only + PatchMemory(0x004A3921, (PBYTE)&flags, 4); // cg_fov + PatchMemory(0x006CA4D6, (PBYTE)&flags, 4); // r_lodScaleRigid + PatchMemory(0x006CA504, (PBYTE)&flags, 4); // r_lodBiasRigid + PatchMemory(0x006CA53A, (PBYTE)&flags, 4); // r_lodScaleSkinned + PatchMemory(0x006CA568, (PBYTE)&flags, 4); // r_lodBiasSkinned + PatchMemory(0x006CBE29, (PBYTE)&flags, 4); // r_enablePlayerShadow + PatchMemory(0x004A6CF1, (PBYTE)&flags, 4); // ai_water_trails + + // + // Stop vanilla scripts from resetting the user's fov + // + Detours::X86::DetourFunction((PBYTE)0x0062A0B0, (PBYTE)&Dvar_SetFromStringByName); + + // + // R_RegisterCmds / R_UnregisterCmds replacement + // + PatchCall(0x006B8300, (PBYTE)&R_RegisterCmds); + PatchJump(0x006B8549, (PBYTE)&R_UnregisterCmds); + + // + // Add scr_useFastFileOnly dvar to enable/disable reading of raw script files + // + Detours::X86::DetourFunction((PBYTE)0x008A5980, (PBYTE)&hk_Scr_ReadFile); + + // + // Detour GScr_NewDebugHudElem to GScr_NewHudElem + // + Detours::X86::DetourFunction((PBYTE)0x00600BF0, (PBYTE)&GScr_NewDebugHudElem); + // + // Patch openfile, closefile + // + Scr_PatchFunctions(); + + // + // Add r_showTess + // + R_DrawXModelSkinnedCached_o = Detours::X86::DetourFunction((PBYTE)0x0073BF30, (PBYTE)hk_R_DrawXModelSkinnedCached); + R_DrawXModelSkinnedUncached_o = Detours::X86::DetourFunction((PBYTE)0x0073BFC0, (PBYTE)hk_R_DrawXModelSkinnedUncached); + R_DrawXModelRigidModelSurf_o = (R_DrawXModelRigidModelSurf_t)Detours::X86::DetourFunction((PBYTE)0x0074A9C0, (PBYTE)hk_R_DrawXModelRigidModelSurf); + Detours::X86::DetourFunction((PBYTE)0x0073D8B2, (PBYTE)mfh_R_TessXModelWaterList); // Don't enable this until we know what it does + + // + // Add CG_DrawFullScreenDebugOverlays + // + Detours::X86::DetourFunction((PBYTE)0x004075E8, (PBYTE)&mfh_CG_DrawFullScreenDebugOverlays); + + // + // Add Support for DrawVisionSetDebug() + // + //Detours::X86::DetourFunction((PBYTE)0x0045EB1A, (PBYTE)&mfh_CG_VisionSetApplyToRefdef); // // Always force the cursor to be shown @@ -96,32 +154,48 @@ BOOL GameMod_Init() // // Enable the in-game console // - Detours::X86::DetourFunction((PBYTE)0x00587DC8, (PBYTE)&Con_ToggleConsole); - Detours::X86::DetourFunction((PBYTE)0x00587633, (PBYTE)&Con_ToggleConsole); - PatchMemory(0x00587DC8, (PBYTE)"\xE8", 1); - PatchMemory(0x00587633, (PBYTE)"\xE8", 1); + Detours::X86::DetourFunction((PBYTE)0x00587DC8, (PBYTE)&Con_ToggleConsole, Detours::X86Option::USE_CALL); + Detours::X86::DetourFunction((PBYTE)0x00587633, (PBYTE)&Con_ToggleConsole, Detours::X86Option::USE_CALL); PatchMemory(0x0058761C, (PBYTE)"\xEB", 1); -#if _DEBUG // - // Set the max number of suggestions to show in the console autocomplete preview + // Run console/packet events even during map load (Com_EventLoop() during DB wait) // - unsigned int con_inputMaxMatchesShown = 32; - PatchMemory(0x00B72F7C, (PBYTE)&con_inputMaxMatchesShown, 4); -#endif + Detours::X86::DetourFunction((PBYTE)0x00699565, (PBYTE)0x00589430, Detours::X86Option::USE_CALL); + PatchMemory_WithNOP(0x007A283C, 5); + + // + // Allow the console to be opened during loadscreens + // + //PatchMemory_WithNOP(0x0057011A, 5); // CL_MapLoading + PatchMemory_WithNOP(0x00570130, 10); // CL_MapLoading + PatchMemory_WithNOP(0x0051B0BD, 5); // CL_InitCGame + PatchMemory_WithNOP(0x00445AA1, 5); // CL_ParseGamestate // - // Disable error message boxes with developer_script + // Add scr_supressErrors to disable error message boxes with developer_script // - PatchMemory(0x005A16F7, (PBYTE)"\x90\x90", 2); - PatchMemory(0x005A1700, (PBYTE)"\x90\x90", 2); + Detours::X86::DetourFunction((PBYTE)0x005A1732, (PBYTE)&mfh_RuntimeError); + + // + // Add com_cfg_readOnly dvar - to allow prevention of writing to the config + // + Patch_WriteToConfig(); + + // + // Don't allow `openmenu main` unless the one following conditions are met: + // 1: The player is not in a server + // 2: The player is on a server with no map loaded + // 3: The player is on the frontend + // This is designed to prevent scripts or menus (ex: error_popmenu) from opening the main menu + // and locking the game controls in the process + // + Detours::X86::DetourFunction((PBYTE)0x00847210, (PBYTE)&UI_OpenMenu_f); -#if _DEBUG || _USE_COM_DPRINTF // // Enable Com_DPrintf // - PatchMemory_WithNOP(0x0060F7E7, 2); -#endif + Detours::X86::DetourFunction((PBYTE)0x0060F7D0, (PBYTE)&Com_DPrintf); // // Allow joining in-game (set party_joinInProgressAllowed) @@ -144,6 +218,12 @@ BOOL GameMod_Init() // *(uint8_t **)&Com_Init = Detours::X86::DetourFunction((PBYTE)0x004069C0, (PBYTE)&hk_Com_Init); + // + // Add support for listassetpool + // + const void* pfn = &DB_ListAssetPool_f; + PatchMemory(0x00631B59, (PBYTE)&pfn, 4); + // // CL_Vid_Restart_Complete_f hook to prevent crashes // @@ -160,28 +240,46 @@ BOOL GameMod_Init() // Detours::X86::DetourFunction((PBYTE)0x008352F6, (PBYTE)&mfh_UI_FeederSelection_AllMaps); + // + // UI_GetModInfo enhancements + // + Detours::X86::DetourFunction((PBYTE)0x00858150, (PBYTE)&UI_GetModInfo); + // // DB_ModXFileHandle hook to enable loading maps from mods // Detours::X86::DetourFunction((PBYTE)0x007A3610, (PBYTE)&DB_ModXFileHandle_hk); // - // Enable the use of level_dependencies.csv + // Increase mod description length limit // -#if _UNSTABLE && _USE_LEVEL_DEPENDENCIES - PatchMemory_WithNOP(0x0082CA3B, 6); -#endif + const int dirListLength = DIRLIST_LEN; + ptr = dirList; + PatchMemory(0x00623A0B, (PBYTE)&ptr, 4); // dirList + PatchMemory(0x00623A65, (PBYTE)&ptr, 4); // dirList + PatchMemory(0x00623A06, (PBYTE)&dirListLength, 4); + + // This is superfluous since we hook FS_GetModList anyway + PatchCall(0x00455A34, (PBYTE)&FS_ReadModDescription); + + Detours::X86::DetourFunction((PBYTE)0x00455920, (PBYTE)&FS_GetModList); // - // Enable legacy mod (patch override) support - // (This detour is used for both level_dependencies AND patch overrides) + // Allow the OS to cache fastfiles when loading // - Detours::X86::DetourFunction((PBYTE)0x004C8890, (PBYTE)&Com_LoadLevelFastFiles); + ptr = (void *)(FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED); + PatchMemory(0x007A37BB, (PBYTE)&ptr, 4); // - // Enable loading of common fastfiles in blackopsmode + // Enable legacy mod (patch override) support + // (This detour is used for both level_dependencies AND patch overrides) // - Detours::X86::DetourFunction((PBYTE)0x0082CB50, (PBYTE)&Com_LoadCommonFastFile); + Detours::X86::DetourFunction((PBYTE)0x004C8890, (PBYTE)&Com_LoadLevelFastFiles); +#if _UNSTABLE + PatchCall(0x007A32E0, (PBYTE)&DB_LinkXAssetEntry); + PatchCall(0x007A334E, (PBYTE)&DB_LinkXAssetEntry); + PatchCall(0x007A49AA, (PBYTE)&DB_LinkXAssetEntry); +#endif // // DB_LoadGraphicsAssetsForPC hook to automatically attempt to load frontend_patch.ff @@ -260,30 +358,65 @@ BOOL GameMod_Init() Detours::X86::DetourFunction((PBYTE)0x006CA27B, (PBYTE)&mfh_R_RegisterDvars); CG_RegisterDvars_o = (CG_RegisterDvars_t)Detours::X86::DetourFunction((PBYTE)0x004A3860, (PBYTE)&CG_RegisterDvars); + // + // Add cg_showServerInfo dvar to show/hide server ip and name in coop scoreboards + // + CG_RegisterScoreboardDvars_o = (CG_RegisterScoreboardDvars_t)Detours::X86::DetourFunction((PBYTE)0x005C74D0, (PBYTE)&CG_RegisterScoreboardDvars); + CL_GetServerIPAddress_o = (CL_GetServerIPAddress_t)Detours::X86::DetourFunction((PBYTE)0x0053BE60, (PBYTE)&CL_GetServerIPAddress); + Detours::X86::DetourFunction((PBYTE)0x00890E23, (PBYTE)&mfh_CG_DrawBackdropServerInfo); + + // + // Draw the Lagometer even if the player is the host + // + PatchMemory_WithNOP(0x0068ABFA, 6); + + // + // Add Game_Mod Version info to the 'version' dvar + // can be viewed via 'cg_drawVersion 1' + // + static const char* version = "%s %s build %s %s\nGame_Mod - " __CONFIGURATION__ " - " __TIMESTAMP__; + PatchMemory(0x0082D0D5, (PBYTE)&version, 4); + // // Misc Bug Fixes // Detours::X86::DetourFunction((PBYTE)0x007D9590, (PBYTE)&nullsub); + Detours::X86::DetourFunction((PBYTE)0x00674C20, (PBYTE)&nullsub); // - // Fix for misleading (incorrect) assertion message + // Fix for misleading (incorrect) assertion / error message // - const char* msg_assertion = "expected 'constant' or 'material', found '%s' instead\n"; - PatchMemory(0x00700492, (PBYTE)&msg_assertion, 4); + const char* msg = "expected 'constant' or 'material', found '%s' instead\n"; + PatchMemory(0x00700492, (PBYTE)&msg, 4); + + msg = "You can only call SetBlur on players."; + PatchMemory(0x00896DBC, (PBYTE)&msg, 4); // - // Add cg_showServerInfo dvar to show/hide server ip and name in coop scoreboards + // Add error handler to prevent missing physpreset segfault // - CG_RegisterScoreboardDvars_o = (CG_RegisterScoreboardDvars_t)Detours::X86::DetourFunction((PBYTE)0x005C74D0, (PBYTE)&CG_RegisterScoreboardDvars); - CL_GetServerIPAddress_o = (CL_GetServerIPAddress_t)Detours::X86::DetourFunction((PBYTE)0x0053BE60, (PBYTE)&CL_GetServerIPAddress); - Detours::X86::DetourFunction((PBYTE)0x00890E23, (PBYTE)&mfh_CG_DrawBackdropServerInfo); + Detours::X86::DetourFunction((PBYTE)0x0081E030, (PBYTE)hk_Phys_ObjCreateAxis); // - // Live radiant initialization hook + // Live radiant initialization hooks // -#if _UNSTABLE Detours::X86::DetourFunction((PBYTE)0x004B7870, (PBYTE)&RadiantRemoteInit); -#endif + Detours::X86::DetourFunction((PBYTE)0x0087E4E5, (PBYTE)&SV_PostFrame, Detours::X86Option::USE_CALL); + PatchMemory_WithNOP(0x0087E4D7, 10);// Functions moved into SV_PostFrame + PatchMemory_WithNOP(0x0087E4ED, 5); // Functions moved into SV_PostFrame + ptr = &G_ProcessPathnodeCommand; + PatchMemory(0x00A56758, (PBYTE)&ptr, 4); + ptr = &G_ClearSelectedPathNode; + PatchMemory(0x00A5675C, (PBYTE)&ptr, 4); + + // + // Additional perk logic + // + Detours::X86::DetourFunction((PBYTE)0x00584480, (PBYTE)&BG_GetPerkIndexForName); + Detours::X86::DetourFunction((PBYTE)0x0087CF80, (PBYTE)&SV_SetPerk_f); + Detours::X86::DetourFunction((PBYTE)0x007D8FF0, (PBYTE)&PlayerCmd_SetPerk); + Detours::X86::DetourFunction((PBYTE)0x007D90B0, (PBYTE)&PlayerCmd_HasPerk); + Detours::X86::DetourFunction((PBYTE)0x007D9170, (PBYTE)&PlayerCmd_UnsetPerk); // // Increase PMem size @@ -318,17 +451,119 @@ BOOL GameMod_Init() PatchMemory(0x008171A6, (PBYTE)&msg_nodeVisUpdate, 4); PatchMemory(0x008172D3, (PBYTE)&msg_nodeVisUpdate, 4); + // + // Map load-time path linking + // +#if _UNSTABLE + Detours::X86::DetourFunction((PBYTE)0x0060C4E0, (PBYTE)&Path_Init); + Detours::X86::DetourFunction((PBYTE)0x005DEA60, (PBYTE)&Path_InitPaths); + PatchMemory_WithNOP(0x00517600, 2); +#endif + + // + // Replace "Missing Image" assertion with silent warning (and add image to missingasset.csv) + // + PatchCall(0x0070A4CD, (PBYTE)&Image_HandleMissingImage); + + // + // Enable borderless windowed mode + // + ptr = &hk_CreateWindowExA; + PatchMemory(0x009A338C, (PBYTE)&ptr, 4); + + ptr = &hk_AdjustWindowRectEx; + PatchMemory(0x009A33D4, (PBYTE)&ptr, 4); + + ptr = &hk_SetWindowLongA; + PatchMemory(0x009A33C8, (PBYTE)&ptr, 4); + + // + // Utilize DirectX 9Ex when possible + // + Detours::X86::DetourFunction((PBYTE)0x006B7780, (PBYTE)&hk_Direct3DCreate9, Detours::X86Option::USE_CALL); + Detours::X86::DetourFunction((PBYTE)0x006B7597, (PBYTE)&hk_CreateDevice, Detours::X86Option::USE_CALL); + Detours::X86::DetourFunction((PBYTE)0x0071F387, (PBYTE)&hk_GetSwapChain, Detours::X86Option::USE_CALL); + PatchMemory(0x006EB4F1, (PBYTE)"\xEB", 1); + + Detours::X86::DetourFunction((PBYTE)0x0070A050, (PBYTE)&hk_Image_Create2DTexture_PC); + Detours::X86::DetourFunction((PBYTE)0x0070A0F0, (PBYTE)&hk_Image_Create3DTexture_PC); + Detours::X86::DetourFunction((PBYTE)0x0070A140, (PBYTE)&hk_Image_CreateCubeTexture_PC); + + // + // Disable system config has changed message box when building paths or reflections + // + Detours::X86::DetourFunction((PBYTE)0x00417750, (PBYTE)&Sys_HasInfoChanged); + + // + // Increase the maximum number of ragdolls + // + PatchMemory(0x00830076, (PBYTE)"\x40", 1); // Ragdoll_GetUnusedBody + PatchMemory(0x00621694, (PBYTE)"\x40", 1); // ragdoll_max_simulating + Detours::X86::DetourFunction((PBYTE)0x004672D0, (PBYTE)&Ragdoll_ExplosionEvent); + + // + // Increase maximum number of reflection probes from 8 + // + Detours::X86::DetourFunction((PBYTE)0x006CF200, (PBYTE)&hk_R_GenerateReflections); + + // + // Improve bullets with various perks + // + Detours::X86::DetourFunction((PBYTE)0x004625C0, (PBYTE)&Bullet_Fire); + Detours::X86::DetourFunction((PBYTE)0x0079A4F1, (PBYTE)&mfh_CG_DrawBulletImpacts1); + Detours::X86::DetourFunction((PBYTE)0x0079A5B5, (PBYTE)&mfh_CG_DrawBulletImpacts2); + Detours::X86::DetourFunction((PBYTE)0x00654BE0, (PBYTE)&BG_GetAmmoPlayerMax); + Detours::X86::DetourFunction((PBYTE)0x0041E480, (PBYTE)&BG_AddAmmoToClip); + Detours::X86::DetourFunction((PBYTE)0x006339E0, (PBYTE)&G_RegisterWeapon); + Detours::X86::DetourFunction((PBYTE)0x004B2F90, (PBYTE)&IsItemRegistered); + Detours::X86::DetourFunction((PBYTE)0x004B5030, (PBYTE)&ClearRegisteredItems); + Detours::X86::DetourFunction((PBYTE)0x00517950, (PBYTE)(unsigned int(*)(const char*, void(*)(unsigned int)))BG_GetWeaponIndexForName); + Detours::X86::DetourFunction((PBYTE)0x0063E060, (PBYTE)(unsigned int(*)(const char*))BG_GetWeaponIndexForName); + Detours::X86::DetourFunction((PBYTE)0x004A0570, (PBYTE)&BG_FindWeaponIndexForName); + Detours::X86::DetourFunction((PBYTE)0x004F2900, (PBYTE)&BG_ClearWeaponDef); + Detours::X86::DetourFunction((PBYTE)0x00425770, (PBYTE)&BG_GetWeaponDef); + Detours::X86::DetourFunction((PBYTE)0x00553DF0, (PBYTE)&BG_GetWeaponIndex); + Detours::X86::DetourFunction((PBYTE)0x00444740, (PBYTE)&BG_GetWeaponVariantDef); + Detours::X86::DetourFunction((PBYTE)0x00595AC0, (PBYTE)&BG_GetMaxPickupableAmmo); + Detours::X86::DetourFunction((PBYTE)0x004B7D10, (PBYTE)&Com_FreeWeaponInfoMemory); + Detours::X86::DetourFunction((PBYTE)0x00506E80, (PBYTE)&DB_ExternalInitAssets); + + // + // PM (Player Movement) hooks for infinite ammo and weapon jamming + // + Detours::X86::DetourFunction((PBYTE)0x006979B0, (PBYTE)&PM_WeaponUseAmmo); + Detours::X86::DetourFunction((PBYTE)0x00766CF0, (PBYTE)&PM_Weapon_Jam); + Detours::X86::DetourFunction((PBYTE)0x007951E0, (PBYTE)&hk_StartWeaponAnim); + Detours::X86::DetourFunction((PBYTE)0x007694A0, (PBYTE)&PM_Weapon_WeaponTimeAdjust); + + // + // Hook reliable command handling + // + Detours::X86::DetourFunction((PBYTE)0x0087D940, (PBYTE)&hk_SV_ClientCommand); + + // + // Increase default sv_network_fps to 200 + // + //PatchMemory(0x00698BFA, (PBYTE)"\xC8", 1); + +#if USE_MISC_PATCHES + PatchMisc(); +#endif + // // Increase Asset Limits // - DB_ReallocXAssetPool(ASSET_TYPE_WEAPON, 256); + DB_ReallocXAssetPool(ASSET_TYPE_WEAPON, 254); // Must not exceed BYTE_MAX (255) + + void* g_XModelPool_entries = (PBYTE)(DB_ReallocXAssetPool(ASSET_TYPE_XMODEL, 2048)) + 4; + PatchMemory(0x0047298B, (PBYTE)&g_XModelPool_entries, 4); // DB_GetXModelAtIndex + PatchMemory(0x004F3E36, (PBYTE)&g_XModelPool_entries, 4); // DB_GetXModelIndex void* g_MaterialPool_entries = (PBYTE)(DB_ReallocXAssetPool(ASSET_TYPE_MATERIAL, 4096)) + 8; PatchMemory(0x0052C28B, (PBYTE)&g_MaterialPool_entries, 4); // DB_GetMaterialAtIndex PatchMemory(0x005FB286, (PBYTE)&g_MaterialPool_entries, 4); // DB_GetMaterialIndex - int g_GfxImagePoolSize = 6000; - void* g_GfxImagePool_entries = (PBYTE)(DB_ReallocXAssetPool(ASSET_TYPE_IMAGE, g_GfxImagePoolSize)) + 4; + void* g_GfxImagePool_entries = (PBYTE)(DB_ReallocXAssetPool(ASSET_TYPE_IMAGE, 6000)) + 4; PatchMemory(0x004561D8, (PBYTE)&g_GfxImagePool_entries, 4); // DB_GetImageAtIndex PatchMemory(0x005A1DC6, (PBYTE)&g_GfxImagePool_entries, 4); // DB_GetImageIndex @@ -358,4 +593,4 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv } return TRUE; -} +} \ No newline at end of file diff --git a/components/game_mod/dvar.cpp b/components/game_mod/dvar.cpp index e4adee38..f901e2e1 100644 --- a/components/game_mod/dvar.cpp +++ b/components/game_mod/dvar.cpp @@ -1,16 +1,60 @@ #include "stdafx.h" -dvar_s* sm_quality; -dvar_s* r_noborder; -dvar_s* con_extcon; +dvar_s* gm_build_date = NULL; +dvar_s* gm_version = NULL; + +dvar_s* r_d3d9ex = NULL; +dvar_s* r_noborder = NULL; +dvar_s* r_showTess = NULL; +dvar_s* sm_quality = NULL; +dvar_s* con_extcon = NULL; +dvar_s* con_inputMaxMatchesShown = NULL; +dvar_s* cg_drawViewpos = NULL; +dvar_s* scr_useFastFileOnly = NULL; +dvar_s* scr_suppressErrors = NULL; +dvar_s* perk_weapRateEnhanced = NULL; + +dvar_s* com_developer_print = NULL; + +dvar_s* radiant_live = NULL; +dvar_s* radiant_livePort = NULL; +dvar_s* radiant_liveDebug = NULL; + +dvar_s* r_renderTweaks = NULL; +dvar_s* r_renderLit = NULL; +dvar_s* r_renderStandardPostFx = NULL; +dvar_s* r_renderDistortion = NULL; +dvar_s* r_renderEmissive = NULL; +dvar_s* r_renderCorona = NULL; +dvar_s* r_renderSuperflare = NULL; +dvar_s* r_renderSun = NULL; +dvar_s* r_renderReflected = NULL; +dvar_s* r_renderCloakHDR = NULL; + +bool com_cfg_readOnly_default = false; +dvar_s* com_cfg_readOnly = NULL; + +const char* r_showTessNames[] = +{ + "off", + "tech", + "techset", + "material", + "vertexshader", + "pixelshader", + NULL +}; void R_RegisterCustomDvars() { + // DirectX9 extensions + r_d3d9ex = Dvar_RegisterInt("r_d3d9ex", 0, 0, 1, 1, "Enable DirectX 9Ex improvements"); + // Borderless window toggle r_noborder = Dvar_RegisterInt("r_noborder", 0, 0, 1, 1, "Enable borderless windowed mode"); - // This can go up to 13 - but anything > 11 has artifacts - sm_quality = Dvar_RegisterInt("sm_quality", 10, 5, 13, 0x2001, "Exponent for shadow map resolution (2^n) - requires restart - anything over 11 has potential for artifacts"); + //this can go up to 13 - but anything > 11 has artifacts + sm_quality = Dvar_RegisterInt("sm_quality", 10, 5, 13, 0x2000 | DVAR_ARCHIVED, "Exponent for shadow map resolution (2^n) - requires restart - anything over 11 has potential for artifacts"); // // Patch ShadowMap SampleSize @@ -29,16 +73,18 @@ void R_RegisterCustomDvars() PatchMemory(0x0073534F, (PBYTE)&sampleSize, 4); PatchMemory(0x00735361, (PBYTE)&smResPower, 1); - // - // Patch CreateWindowExA style - // - DWORD style = WS_VISIBLE | WS_SYSMENU | WS_CAPTION; - - if (r_noborder->current.enabled) - style = WS_VISIBLE | WS_POPUP; + r_showTess = Dvar_RegisterEnum("r_showTess", r_showTessNames, 0, 0x80u, "Show details for each surface"); + com_cfg_readOnly = Dvar_RegisterInt("com_cfg_readOnly", com_cfg_readOnly_default ? 1 : 0, 0, 1, DVAR_AUTOEXEC, "Prevent writing to the config"); // Old flags were 0x2001 + + static const char* com_dprintf_options[] = + { + "default", + "always", + "never", + NULL + }; - PatchMemory(0x006B7A74, (PBYTE)&style, sizeof(DWORD));// R_CreateWindow - PatchMemory(0x006B7EC1, (PBYTE)&style, sizeof(DWORD));// R_ResizeWindow + com_developer_print = Dvar_RegisterEnum("developer_print", com_dprintf_options, 0, 0x80, "Modify Com_DPrintf() behavior"); } void* rtn_R_RegisterDvars = (void*)0x006CA283; @@ -56,9 +102,68 @@ void __declspec(naked) mfh_R_RegisterDvars() } CG_RegisterDvars_t CG_RegisterDvars_o = NULL; -void __cdecl CG_RegisterDvars(void) +void __cdecl CG_RegisterDvars() { CG_RegisterDvars_o(); + gm_build_date = Dvar_RegisterString("gm_build_date", __TIMESTAMP__, 0x10 | 0x8, "Compile time for game_mod"); + gm_version = Dvar_RegisterString("gm_version", DLL_VersionString(), 0x10 | 0x8, "Game_Mod version"); + con_extcon = Dvar_RegisterInt("con_extcon", 0, 0, 1, 1, "Enable external console window"); + con_inputMaxMatchesShown = Dvar_RegisterInt("con_inputMaxMatchesShown", 24, 1, 64, 1, "Maximum number of suggestions in the console autocomplete preview"); + + cg_drawViewpos = Dvar_RegisterInt("cg_drawViewpos", 0, 0, 1, 1, "Draw the current player origin and view angles"); + scr_useFastFileOnly = Dvar_RegisterInt("scr_useFastFileOnly", 0, 0, 1, 0x2, "Disable the loading of raw script files from the current mod's directory"); + + scr_suppressErrors = Dvar_RegisterBool("scr_suppressErrors", false, 0x2, "Suppress fatal script errors"); + + perk_weapRateEnhanced = Dvar_RegisterInt("perk_weapRateEnhanced", 0, 0, 1, 0x80, "Double tap will shoot 2x the bullets for every shot"); + + radiant_live = Dvar_RegisterInt("radiant_live", 0, 0, 1, 0x0, "Enable live Radiant updates in the game"); + radiant_livePort = Dvar_RegisterInt("radiant_livePort", 3700, 0, 65535, 0x0, "Network port for Radiant"); + radiant_liveDebug = Dvar_RegisterInt("radiant_liveDebug", 0, 0, 1, 0x0, "Debugging prints for Radiant commands"); + + // Must be set at launch time to function correctly + r_renderTweaks = Dvar_RegisterBool("r_renderTweaks", false, 0x2, "Allow renderer tweaks"); + + r_renderLit = Dvar_RegisterBool("r_renderLit", true, 0x2, ""); + r_renderStandardPostFx = Dvar_RegisterBool("r_renderStandardPostFx", true, 0x2, ""); + r_renderDistortion = Dvar_RegisterBool("r_renderDistortion", true, 0x2, ""); + r_renderEmissive = Dvar_RegisterBool("r_renderEmissive", true, 0x2, ""); + r_renderCorona = Dvar_RegisterBool("r_renderCorona", true, 0x2, ""); + r_renderSuperflare = Dvar_RegisterBool("r_renderSuperflare", true, 0x2, ""); + r_renderSun = Dvar_RegisterBool("r_renderSun", true, 0x2, ""); + r_renderReflected = Dvar_RegisterBool("r_renderReflected", true, 0x2, ""); + r_renderCloakHDR = Dvar_RegisterBool("r_renderCloakHDR", true, 0x2, ""); + + // Set the max number of suggestions to show in the console autocomplete preview + PatchMemory(0x00B72F7C, (PBYTE)&con_inputMaxMatchesShown->current.value, 4); + + // Remap "debug_show_viewpos" to "cg_drawViewpos" + PatchMemory(0x005BF103, (PBYTE)&cg_drawViewpos->name, 4); +} + +dvar_s *Dvar_SetFromStringByNameFromSource(const char *dvarName, const char *string, DvarSetSource source, unsigned int flags) +{ + return ((dvar_s *(__cdecl *)(const char *, const char *, DvarSetSource, unsigned int))0x00426820)(dvarName, string, source, flags); +} + +void Dvar_SetFromStringByName(const char *dvarName, const char *string) +{ + // Do not allow the default FOV to be set. Generally sent with CG_DeployServerCommand. + if (dvarName && string) + { + if (!_stricmp(dvarName, "cg_fov") && !_stricmp(string, "65")) + return; + + if (!_stricmp(dvarName, "cg_default_fov") && !_stricmp(string, "65")) + return; + } + + Dvar_SetFromStringByNameFromSource(dvarName, string, DVAR_SOURCE_INTERNAL, 0); +} + +void Dvar_ClearModified(dvar_s *dvar) +{ + dvar->modified = 0; } diff --git a/components/game_mod/dvar.h b/components/game_mod/dvar.h index 9182b9e7..24c8a134 100644 --- a/components/game_mod/dvar.h +++ b/components/game_mod/dvar.h @@ -1,5 +1,8 @@ #pragma once +#define VANILLA_DVAR(NAME, ADDRESS) static dvar_s*& NAME = *(dvar_s **)ADDRESS; +#define VANILLA_VALUE(NAME, TYPE, ADDRESS) static TYPE& NAME = *(TYPE*)ADDRESS; + enum dvarType_t { DVAR_TYPE_BOOL = 0x0, @@ -17,6 +20,14 @@ enum dvarType_t DVAR_TYPE_COUNT = 0xC, }; +enum DvarSetSource +{ + DVAR_SOURCE_INTERNAL = 0x0, + DVAR_SOURCE_EXTERNAL = 0x1, + DVAR_SOURCE_SCRIPT = 0x2, + DVAR_SOURCE_DEVGUI = 0x3, +}; + union DvarValue { bool enabled; @@ -63,6 +74,20 @@ union DvarLimits } vector; }; +enum dvarFlags_t +{ + DVAR_ARCHIVED = 0x1, //seta + DVAR_UPDATE = 0x2, //setu + DVAR_S = 0x4, //sets + DVAR_CHEAT = 0x80, + DVAR_CHANGEABLE_RESET = 0x200, + DVAR_SERVER_INFO = 0x500, + DVAR_SAVED = 0x1000, + DVAR_EXTERNAL = 0x4000, + DVAR_AUTOEXEC = 0x8000, + DVAR_ADMIN = 0x10000, +}; + struct dvar_s { const char *name; @@ -79,24 +104,104 @@ struct dvar_s DvarLimits domain; dvar_s *hashNext; }; - -static dvar_s **com_sv_running = (dvar_s **)0x0243FD3C; - -static dvar_s **developer = (dvar_s **)0x0243FCA0; -static dvar_s **developer_script = (dvar_s **)0x02481714; - -static dvar_s*& zombietron = *(dvar_s**)0x0247FDE8; -static dvar_s*& zombiemode = *(dvar_s**)0x0243FDD4; -static dvar_s*& blackopsmode = *(dvar_s**)0x0243FD24; -static dvar_s*& useFastFile = *(dvar_s**)0x0247FEC8; +STATIC_ASSERT_OFFSET(dvar_s, current, 0x18); + +VANILLA_DVAR(fs_gameDirVar, 0x025FADE8); +VANILLA_DVAR(fs_usermapDir, 0x025FADE4); +VANILLA_DVAR(fs_homepath, 0x025FBF0C); +VANILLA_DVAR(fs_basepath, 0x025FBF04); + +VANILLA_DVAR(cg_fov, 0x02FF6888); +VANILLA_DVAR(cg_fov_default, 0x02FF669C); +VANILLA_DVAR(cg_fov_default_thirdperson, 0x02F67B9C); +VANILLA_DVAR(cg_fovScale, 0x02FF66A8); + +VANILLA_DVAR(uiscript_debug, 0x02562A20); + +VANILLA_DVAR(cl_ingame, 0x02910158); +VANILLA_DVAR(com_sv_running, 0x0243FD3C); +VANILLA_DVAR(com_maxclients, 0x02481720); +VANILLA_DVAR(com_developer, 0x0243FCA0); +VANILLA_DVAR(sv_mapname, 0x02899CEC); +VANILLA_DVAR(sv_floodProtect, 0x02866FE4); + +VANILLA_DVAR(developer, 0x0243FCA0); +VANILLA_DVAR(developer_script, 0x02481714); +VANILLA_DVAR(zombietron, 0x0247FDE8); +VANILLA_DVAR(zombiemode, 0x0243FDD4); +VANILLA_DVAR(blackopsmode, 0x0243FD24); +VANILLA_DVAR(useFastFile, 0x0247FEC8); +VANILLA_DVAR(g_connectpaths, 0x01C01850); + +VANILLA_DVAR(r_lockPvs, 0x03B1FD24); + +VANILLA_DVAR(r_reflectionProbeGenerate, 0x03B35038); +VANILLA_DVAR(r_reflectionProbeGenerateExit, 0x03B3503C); +VANILLA_DVAR(r_reflectionProbeRegenerateAll, 0x03B20030); + +VANILLA_DVAR(cg_drawVersion, 0x02F67B14); +VANILLA_DVAR(cg_drawMaterial, 0x02FF676C); + +VANILLA_DVAR(ragdoll_reactivation_cutoff, 0xBCAEB0); +VANILLA_DVAR(ragdoll_explode_upbias, 0x251CBCC); +VANILLA_DVAR(ragdoll_explode_force, 0x251CBC8); + +VANILLA_DVAR(g_debugBullets, 0x01BD9B48); +VANILLA_DVAR(sv_bullet_range, 0x02899D64); +VANILLA_DVAR(bg_forceExplosiveBullets, 0x00BCD1E8); +VANILLA_DVAR(vehicle_selfCollision, 0x00BCAFCC); +VANILLA_DVAR(player_sustainAmmo, 0x00BCD250); +VANILLA_DVAR(player_burstFireCooldown, 0x00BCD110); +VANILLA_DVAR(player_clipSizeMultiplier, 0x00BCAF84); + +VANILLA_DVAR(perk_weapRateMultiplier, 0x00BDF35C); +VANILLA_DVAR(perk_weapReloadMultiplier, 0x00BDF358); +VANILLA_DVAR(perk_weapSwitchMultiplier, 0x00BDF2C0); +extern dvar_s* perk_weapRateEnhanced; + +VANILLA_DVAR(phys_gravity, 0x023D2FA8); + +extern dvar_s* gm_build_date; +extern dvar_s* gm_version; + +extern dvar_s* r_d3d9ex; +extern dvar_s* r_noborder; +extern dvar_s* r_showTess; +extern dvar_s* sm_quality; +extern dvar_s* con_extcon; +extern dvar_s* con_inputMaxMatchesShown; +extern dvar_s* cg_drawViewpos; +extern dvar_s* scr_useFastFileOnly; +extern dvar_s* scr_suppressErrors; +extern dvar_s* cg_showServerInfo; + +VANILLA_DVAR(sys_configureGHz, 0x0276D9E0) +VANILLA_DVAR(sys_sysMB, 0x0276DAEC); +VANILLA_DVAR(sys_gpu, 0x0276D9E4); +VANILLA_DVAR(sys_configSum, 0x0276F558); + +VANILLA_DVAR(showVisionSetDebugInfo, 0x00C23D40); + +extern dvar_s* com_developer_print; + +extern dvar_s* radiant_live; +extern dvar_s* radiant_livePort; +extern dvar_s* radiant_liveDebug; + +extern dvar_s* r_renderTweaks; +extern dvar_s* r_renderLit; +extern dvar_s* r_renderStandardPostFx; +extern dvar_s* r_renderDistortion; +extern dvar_s* r_renderEmissive; +extern dvar_s* r_renderCorona; +extern dvar_s* r_renderSuperflare; +extern dvar_s* r_renderSun; +extern dvar_s* r_renderReflected; +extern dvar_s* r_renderCloakHDR; extern bool com_cfg_readOnly_default; extern dvar_s* com_cfg_readOnly; -extern dvar_s* sm_quality; -extern dvar_s* r_noborder; -extern dvar_s* con_extcon; - typedef const char* Dvar_GetString_t(const char* dvarName); static Dvar_GetString_t* Dvar_GetString = (Dvar_GetString_t*)0x0057FF80; @@ -109,12 +214,32 @@ static Dvar_SetStringByName_t* Dvar_SetStringByName = (Dvar_SetStringByName_t*)0 typedef void __cdecl Dvar_SetBool_t(dvar_s *dvar, bool value); static Dvar_SetBool_t* Dvar_SetBool = (Dvar_SetBool_t*)0x004B0C10; +typedef void __cdecl Dvar_SetFloat_t(dvar_s *dvar, float value); +static Dvar_SetFloat_t* Dvar_SetFloat = (Dvar_SetFloat_t*)0x0061F7A9; + +typedef dvar_s *__cdecl Dvar_RegisterString_t(const char *dvarName, const char *value, unsigned __int16 flags, const char *description); +static Dvar_RegisterString_t* Dvar_RegisterString = (Dvar_RegisterString_t*)0x0059B3B0; + typedef dvar_s *__cdecl Dvar_RegisterInt_t(const char *dvarName, int value, int min, int max, unsigned __int16 flags, const char *description); static Dvar_RegisterInt_t* Dvar_RegisterInt = (Dvar_RegisterInt_t*)0x00651910; +typedef dvar_s *(__cdecl* Dvar_RegisterFloat_t)(const char *dvarName, float value, float min, float max, unsigned __int16 flags, const char *description); +static Dvar_RegisterFloat_t Dvar_RegisterFloat = (Dvar_RegisterFloat_t)0x00679020; + +typedef dvar_s *(__cdecl* Dvar_RegisterBool_t)(const char *dvarName, bool value, unsigned __int16 flags, const char *description); +static Dvar_RegisterBool_t Dvar_RegisterBool = (Dvar_RegisterBool_t)0x0045BB20; + +typedef dvar_s *__cdecl Dvar_RegisterEnum_t(const char *dvarName, const char **valueList, int defaultIndex, unsigned int flags, const char *description); +static Dvar_RegisterEnum_t* Dvar_RegisterEnum = (Dvar_RegisterEnum_t*)0x0051BD00; + void mfh_R_RegisterDvars(); -typedef void (__cdecl* CG_RegisterDvars_t)(void); +typedef void(__cdecl* CG_RegisterDvars_t)(); extern CG_RegisterDvars_t CG_RegisterDvars_o; -void __cdecl CG_RegisterDvars(void); +void __cdecl CG_RegisterDvars(); + +dvar_s *Dvar_SetFromStringByNameFromSource(const char *dvarName, const char *string, DvarSetSource source, unsigned int flags); +void Dvar_SetFromStringByName(const char *dvarName, const char *string); + +void Dvar_ClearModified(dvar_s *dvar); \ No newline at end of file diff --git a/components/game_mod/g_client_script_cmd_mp.cpp b/components/game_mod/g_client_script_cmd_mp.cpp new file mode 100644 index 00000000..ca347ab5 --- /dev/null +++ b/components/game_mod/g_client_script_cmd_mp.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" + +// /game_mp/g_client_script_cmd_mp.cpp:4125 +void PlayerCmd_SetPerk(scr_entref_t entref) +{ + gentity_s *pSelf = nullptr; + + if (entref.classnum) + Scr_ObjectError("not an entity", SCRIPTINSTANCE_SERVER); + + ASSERT(entref.entnum < MAX_GENTITIES); + + pSelf = &g_entities[entref.entnum]; + + if (!pSelf->client) + Scr_ObjectError(va("entity %i is not a player", entref.entnum), SCRIPTINSTANCE_SERVER); + + const char *perkName = Scr_GetString(0, SCRIPTINSTANCE_SERVER); + unsigned int perkIndex = BG_GetPerkIndexForName(perkName); + + if (perkIndex == PERK_UNKNOWN) + Scr_Error(va("Unknown perk: %s\n", perkName), false); + + BG_SetPerk(pSelf->client->ps.perks, perkIndex); + BG_SetPerk((unsigned int *)((DWORD)pSelf->client + 0x1B64), perkIndex); +} + +// /game_mp/g_client_script_cmd_mp.cpp:4155 +void PlayerCmd_HasPerk(scr_entref_t entref) +{ + gentity_s *pSelf = nullptr; + + if (entref.classnum) + Scr_ObjectError("not an entity", SCRIPTINSTANCE_SERVER); + + ASSERT(entref.entnum < MAX_GENTITIES); + + pSelf = &g_entities[entref.entnum]; + + if (!pSelf->client) + Scr_ObjectError(va("entity %i is not a player", entref.entnum), SCRIPTINSTANCE_SERVER); + + const char *perkName = Scr_GetString(0, SCRIPTINSTANCE_SERVER); + unsigned int perkIndex = BG_GetPerkIndexForName(perkName); + + if (perkIndex == PERK_UNKNOWN) + Scr_Error(va("Unknown perk: %s\n", perkName), false); + + Scr_AddBool(BG_HasPerk(pSelf->client->ps.perks, perkIndex), SCRIPTINSTANCE_SERVER); +} + +// /game_mp/g_client_script_cmd_mp.cpp:4210 +void PlayerCmd_UnsetPerk(scr_entref_t entref) +{ + gentity_s *pSelf = nullptr; + + if (entref.classnum) + Scr_ObjectError("not an entity", SCRIPTINSTANCE_SERVER); + + ASSERT(entref.entnum < MAX_GENTITIES); + + pSelf = &g_entities[entref.entnum]; + + if (!pSelf->client) + Scr_ObjectError(va("entity %i is not a player", entref.entnum), SCRIPTINSTANCE_SERVER); + + const char *perkName = Scr_GetString(0, SCRIPTINSTANCE_SERVER); + unsigned int perkIndex = BG_GetPerkIndexForName(perkName); + + if (perkIndex == PERK_UNKNOWN) + Scr_Error(va("Unknown perk: %s\n", perkName), false); + + BG_UnsetPerk(pSelf->client->ps.perks, perkIndex); + BG_UnsetPerk((unsigned int *)((DWORD)pSelf->client + 0x1B64), perkIndex); +} \ No newline at end of file diff --git a/components/game_mod/g_client_script_cmd_mp.h b/components/game_mod/g_client_script_cmd_mp.h new file mode 100644 index 00000000..9bba4ba1 --- /dev/null +++ b/components/game_mod/g_client_script_cmd_mp.h @@ -0,0 +1,5 @@ +#pragma once + +void PlayerCmd_SetPerk(scr_entref_t entref); +void PlayerCmd_HasPerk(scr_entref_t entref); +void PlayerCmd_UnsetPerk(scr_entref_t entref); \ No newline at end of file diff --git a/components/game_mod/g_items.cpp b/components/game_mod/g_items.cpp new file mode 100644 index 00000000..1bc840ba --- /dev/null +++ b/components/game_mod/g_items.cpp @@ -0,0 +1,97 @@ +#include "stdafx.h" + +struct XModel +{ + const char *name; + char _pad1[0xE4]; + bool bad; +}; +STATIC_ASSERT_OFFSET(XModel, name, 0x0); +STATIC_ASSERT_OFFSET(XModel, bad, 0xE8); + +const char *XModelGetName(XModel *model) +{ + ASSERT(model); + + return model->name; +} + +int G_ModelIndex(const char *name) +{ + return ((int(__cdecl *)(const char *))0x004294B0)(name); +} + +bool XModelBad(XModel *model) +{ + ASSERT(model); + + if (useFastFile->current.enabled) + return DB_IsXAssetDefault(ASSET_TYPE_XMODEL, model->name); + + return model->bad; +} + +void G_OverrideModel(int modelIndex, const char *defaultModelName) +{ + ((void(__cdecl *)(int, const char *))0x0068C110)(modelIndex, defaultModelName); +} + +int G_GetHintStringIndex(int *piIndex, const char *pszString) +{ + return ((int(__cdecl *)(int *, const char *))0x0065AB40)(piIndex, pszString); +} + +// /game/g_items.cpp:1374 +void ClearRegisteredItems() +{ + memset(itemRegistered, 0, MAX_WEAPONS * sizeof(int)); + + // defaultweapon is always registered + itemRegistered[0] = 1; +} + +// /game/g_items.cpp:1525 +void G_RegisterWeapon(unsigned int weapIndex) +{ + ASSERT(!IsItemRegistered(weapIndex)); + + itemRegistered[weapIndex] = 1; + *(DWORD *)0x1C05348 = 1; + *(DWORD *)0x1C05344 = 1; + + WeaponDef *weapDef = bg_weaponVariantDefs[weapIndex]->weapDef; + + if (*weapDef->szUseHintString && !G_GetHintStringIndex(&weapDef->iUseHintStringIndex, weapDef->szUseHintString)) + Com_Error(ERR_DROP, "Too many different hintstring values on weapons. Max allowed is %i different strings", 96); + + if (*weapDef->dropHintString && !G_GetHintStringIndex(&weapDef->dropHintStringIndex, weapDef->dropHintString)) + Com_Error(ERR_DROP, "Too many different hintstring values on weapons. Max allowed is %i different strings", 96); + + for (int i = 0; i < 16; i++) + { + if (!weapDef->worldModel[i]) + continue; + + int modelIndex = G_ModelIndex(XModelGetName(weapDef->worldModel[i])); + + if (XModelBad(weapDef->worldModel[i])) + G_OverrideModel(modelIndex, "defaultweapon"); + } + + if (weapDef->projectileModel) + G_ModelIndex(XModelGetName(weapDef->projectileModel)); + + if (weapDef->worldModel[1]) + G_ModelIndex(XModelGetName(weapDef->worldModel[1])); + + if (weapDef->additionalMeleeModel) + G_ModelIndex(XModelGetName(weapDef->additionalMeleeModel)); +} + +// /game/g_items.cpp:1584 +int IsItemRegistered(int iItemIndex) +{ + ASSERT((iItemIndex >= 0) && (iItemIndex < MAX_WEAPONS)); + + return itemRegistered[iItemIndex]; +} \ No newline at end of file diff --git a/components/game_mod/g_items.h b/components/game_mod/g_items.h new file mode 100644 index 00000000..6fbf565e --- /dev/null +++ b/components/game_mod/g_items.h @@ -0,0 +1,9 @@ +#pragma once + +#define MAX_WEAPONS 2048 + +static int *itemRegistered = (int *)0x01A775B8; + +void ClearRegisteredItems(); +void G_RegisterWeapon(unsigned int weapIndex); +int IsItemRegistered(int iItemIndex); \ No newline at end of file diff --git a/components/game_mod/g_main_mp.h b/components/game_mod/g_main_mp.h new file mode 100644 index 00000000..c4ab8cd8 --- /dev/null +++ b/components/game_mod/g_main_mp.h @@ -0,0 +1,211 @@ +#pragma once + +#define MAX_GENTITIES 1024 + +struct gentity_s; +struct gclient_s; +struct scr_vehicle_s; + +struct entityState_s +{ + int number; + char _pad1[0xBA]; + __int16 eType; + char _pad2[0x18]; +}; +STATIC_ASSERT_OFFSET(entityState_s, number, 0x0); +STATIC_ASSERT_OFFSET(entityState_s, eType, 0xBE); +STATIC_ASSERT_SIZE(entityState_s, 0xD8); + +struct entityShared_t +{ + char _pad1[0x64]; +}; +STATIC_ASSERT_SIZE(entityShared_t, 0x64); + +struct gentity_s +{ + entityState_s s; + entityShared_t r; + gclient_s *client; + char _pad1[0x8]; + scr_vehicle_s *vehicle; + char _pad2[0x200]; +}; +STATIC_ASSERT_OFFSET(gentity_s, client, 0x13C); +STATIC_ASSERT_OFFSET(gentity_s, vehicle, 0x148); +STATIC_ASSERT_SIZE(gentity_s, 0x34C); + +struct renderOptions_s +{ + union + { + unsigned int i; + + struct + { + int _bf0; + }; + }; +}; + +struct PlayerHeldWeapon +{ + unsigned int weapon; + unsigned char model; + renderOptions_s options; + + union + { + float heatPercent; + int fuelTankTime; + }; + + bool overHeating; + bool needsRechamber; + bool heldBefore; + bool quickReload; + bool blockWeaponPickup; +}; +STATIC_ASSERT_OFFSET(PlayerHeldWeapon, weapon, 0x0); +STATIC_ASSERT_OFFSET(PlayerHeldWeapon, needsRechamber, 0x11); +STATIC_ASSERT_SIZE(PlayerHeldWeapon, 0x18); + +struct AmmoPool +{ + int ammoIndex; + int count; +}; +STATIC_ASSERT_SIZE(AmmoPool, 0x8); + +struct AmmoClip +{ + int clipIndex; + int count; +}; +STATIC_ASSERT_SIZE(AmmoClip, 0x8); + +struct playerState_s +{ + int commandTime; + int pm_type; + char _pad1[0x8]; + int weapFlags; + char _pad2[0x28]; + int weaponTime; + int weaponDelay; + int weaponTimeLeft; + int weaponDelayLeft; + char _pad3[0x10]; + int weaponRestrictKickTime; + char _pad4[0x1]; + bool bRunLeftGun; + char _pad5[0xCE]; + char clientNum; + char _pad6[0x13]; + unsigned char weapon; + char _pad7[0x12]; + int weaponstate; + int weaponstateLeft; + unsigned int weaponShotCount; + unsigned int weaponShotCountLeft; + char _pad8[0x48]; + unsigned int stackFireCount; + char _pad9[0x28]; + PlayerHeldWeapon heldWeapons[15]; + AmmoPool ammoNotInClip[15]; + AmmoClip ammoInClip[15]; + char _pad10[0xC8]; + unsigned int perks[1]; + char _pad11[0x24]; + int weapAnim; + int weapAnimLeft; +}; +STATIC_ASSERT_OFFSET(playerState_s, commandTime, 0x0); +STATIC_ASSERT_OFFSET(playerState_s, pm_type, 0x4); +STATIC_ASSERT_OFFSET(playerState_s, weapFlags, 0x10); +STATIC_ASSERT_OFFSET(playerState_s, weaponTime, 0x3C); +STATIC_ASSERT_OFFSET(playerState_s, weaponDelay, 0x40); +STATIC_ASSERT_OFFSET(playerState_s, weaponTimeLeft, 0x44); +STATIC_ASSERT_OFFSET(playerState_s, weaponDelayLeft, 0x48); +STATIC_ASSERT_OFFSET(playerState_s, weaponRestrictKickTime, 0x5C); +STATIC_ASSERT_OFFSET(playerState_s, bRunLeftGun, 0x61); +STATIC_ASSERT_OFFSET(playerState_s, clientNum, 0x130); +STATIC_ASSERT_OFFSET(playerState_s, weapon, 0x144); +STATIC_ASSERT_OFFSET(playerState_s, weaponstate, 0x158); +STATIC_ASSERT_OFFSET(playerState_s, weaponstateLeft, 0x15C); +STATIC_ASSERT_OFFSET(playerState_s, weaponShotCount, 0x160); +STATIC_ASSERT_OFFSET(playerState_s, weaponShotCountLeft, 0x164); +STATIC_ASSERT_OFFSET(playerState_s, stackFireCount, 0x1B0); +STATIC_ASSERT_OFFSET(playerState_s, heldWeapons, 0x1DC); +STATIC_ASSERT_OFFSET(playerState_s, ammoNotInClip, 0x344); +STATIC_ASSERT_OFFSET(playerState_s, ammoInClip, 0x3BC); +STATIC_ASSERT_OFFSET(playerState_s, perks, 0x4FC); +STATIC_ASSERT_OFFSET(playerState_s, weapAnim, 0x524); +STATIC_ASSERT_OFFSET(playerState_s, weapAnimLeft, 0x528); +//STATIC_ASSERT_SIZE(playerState_s, 0x0); + +struct clientState_s +{ + char _pad1[0x7C]; + unsigned int perks[1]; + char _pad2[0x1CA8]; +}; +STATIC_ASSERT_OFFSET(clientState_s, perks, 0x7C); +STATIC_ASSERT_SIZE(clientState_s, 0x1D28); + +struct clientSession_t +{ +}; +//STATIC_ASSERT_SIZE(clientSession_t, 0x0); + +struct gclient_s +{ + playerState_s ps; + //clientSession_t sess; +}; +STATIC_ASSERT_OFFSET(gclient_s, ps, 0x0); +//STATIC_ASSERT_SIZE(gclient_s, 0x0); + +struct clientHeader_t +{ + int state; + char _pad1[0x20]; + struct + { + struct + { + int type; + } remoteAddress; + } netchan; + char _pad2[0x6F0]; +}; +STATIC_ASSERT_OFFSET(clientHeader_t, state, 0x0); +//STATIC_ASSERT_OFFSET(clientHeader_t, netchan, 0x24); +STATIC_ASSERT_SIZE(clientHeader_t, 0x718); + +struct client_t +{ + clientHeader_t header; + const char *dropReason; + BYTE gap28[0x10850]; + int lastClientCommand; + char lastClientCommandString[1024]; + gentity_s *gentity; + char name[32]; + BYTE gap11394[184]; + int nextReliableTime; + int nextReliableCount; + char _pad1[0x2F26C]; +}; +STATIC_ASSERT_OFFSET(client_t, header, 0x0); +STATIC_ASSERT_OFFSET(client_t, dropReason, 0x718); +STATIC_ASSERT_OFFSET(client_t, lastClientCommand, 0x10F6C); +STATIC_ASSERT_OFFSET(client_t, lastClientCommandString, 0x10F70); +STATIC_ASSERT_OFFSET(client_t, gentity, 0x11370); +STATIC_ASSERT_OFFSET(client_t, name, 0x11374); +STATIC_ASSERT_OFFSET(client_t, nextReliableTime, 0x1144C); +STATIC_ASSERT_OFFSET(client_t, nextReliableCount, 0x11450); +STATIC_ASSERT_SIZE(client_t, 0x406C0); + +static gentity_s *g_entities = (gentity_s *)0x01A796F8; \ No newline at end of file diff --git a/components/game_mod/game_mod.vcxproj b/components/game_mod/game_mod.vcxproj index 6462c44d..cf936b2a 100644 --- a/components/game_mod/game_mod.vcxproj +++ b/components/game_mod/game_mod.vcxproj @@ -22,15 +22,15 @@ DynamicLibrary true - v120_xp Unicode + v120_xp DynamicLibrary false - v120_xp true Unicode + v120_xp @@ -64,6 +64,7 @@ true UseLinkTimeCodeGeneration /ignore:4099 /opt:nolbr %(AdditionalOptions) /opt:nolbr + %(AdditionalDependencies);imgui.lib @@ -84,63 +85,145 @@ true /ignore:4099 %(AdditionalOptions) + %(AdditionalDependencies);imgui.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {1e6c2c1c-6453-4129-ae3f-0ee8e6599c89} + + + {5fb372dd-a7e1-4a4b-9a28-3ac02543e9e8} + diff --git a/components/game_mod/live_win.cpp b/components/game_mod/live_win.cpp index 8e98ce64..97a485a2 100644 --- a/components/game_mod/live_win.cpp +++ b/components/game_mod/live_win.cpp @@ -11,6 +11,11 @@ void* Live_AcceptInviteAsyncComplete_Continue = (void*)0x0046B31C; Session_Modify_t* Session_Modify = (Session_Modify_t*)0x00611930; +bool Com_SessionMode_IsZombiesGame() +{ + return zombiemode->current.enabled; +} + void __cdecl Session_Modify_Fix(const int localControllerIndex, void *session, const int flags, const int publicSlots, const int privateSlots) { MatchMakingInfo* g_matchmakingInfo = *(MatchMakingInfo**)0x03879A24; diff --git a/components/game_mod/live_win.h b/components/game_mod/live_win.h index ab1e8691..73391572 100644 --- a/components/game_mod/live_win.h +++ b/components/game_mod/live_win.h @@ -63,6 +63,7 @@ extern Session_Modify_t* Session_Modify; int SV_UpdateCategory(); +bool Com_SessionMode_IsZombiesGame(); void __cdecl Session_Modify_Fix(const int localControllerIndex, void *session, const int flags, const int publicSlots, const int privateSlots); void Live_JoinSessionInProgressComplete_CheckMod(); void Live_AcceptInviteAsyncComplete_CheckMod(); \ No newline at end of file diff --git a/components/game_mod/patch_common.cpp b/components/game_mod/patch_common.cpp index 3d939e6d..ced449b7 100644 --- a/components/game_mod/patch_common.cpp +++ b/components/game_mod/patch_common.cpp @@ -1,11 +1,13 @@ #include "stdafx.h" bool g_isPatched_WriteToConfig = false; -void Patch_Disable_WriteToConfig() +void Patch_WriteToConfig() { if (g_isPatched_WriteToConfig) return; - PatchMemory(0x0082C510, (PBYTE)"\xC3\x90\x90\x90\x90", 5); + PatchCall(0x0082C525, (PBYTE)FS_FOpenFileWriteToDir); // Com_WriteConfigToFile + PatchCall(0x0082C5A6, (PBYTE)FS_FOpenFileWriteToDir); // Com_WriteKeyConfigToFile + g_isPatched_WriteToConfig = true; } diff --git a/components/game_mod/patch_common.h b/components/game_mod/patch_common.h index dec5a62d..e574a69c 100644 --- a/components/game_mod/patch_common.h +++ b/components/game_mod/patch_common.h @@ -1,6 +1,6 @@ #pragma once // -// Prevent overwriting the config file with launch args +// Allows use of "com_configReadOnly" to prevent overwriting the config file with launch args // -void Patch_Disable_WriteToConfig(); +void Patch_WriteToConfig(); diff --git a/components/game_mod/patch_misc.cpp b/components/game_mod/patch_misc.cpp new file mode 100644 index 00000000..21ba9bca --- /dev/null +++ b/components/game_mod/patch_misc.cpp @@ -0,0 +1,16 @@ +#include "stdafx.h" +#if USE_MISC_PATCHES + +// +// Useful for various debug tweaks +// +void PatchMisc() +{ + // + // Disable "cannot spawn turret '%s' which has a script, unless one instance placed in map" + // Allows devmapping into creek_1 with g_loadScripts 0 + // + PatchMemory_WithNOP(0x004D5061, 5); +} + +#endif diff --git a/components/game_mod/patch_reflections.cpp b/components/game_mod/patch_reflections.cpp index ab1972e4..5526fb55 100644 --- a/components/game_mod/patch_reflections.cpp +++ b/components/game_mod/patch_reflections.cpp @@ -84,7 +84,7 @@ BOOL ReflectionMod_Init() // // Prevent overwriting the config file // - Patch_Disable_WriteToConfig(); + com_cfg_readOnly_default = true; // // Always force the cursor to be shown @@ -101,16 +101,24 @@ BOOL ReflectionMod_Init() PatchMemory(0x006CF382, (PBYTE)"\x90\x90", 2); PatchMemory(0x006CF388, (PBYTE)"\x90\x90", 2); - Detours::X86::DetourFunction((PBYTE)0x006CF150, (PBYTE)&hk_R_GenerateReflectionRawDataAll); - Detours::X86::DetourFunction((PBYTE)0x006CEE30, (PBYTE)R_GenerateReflectionRawData); + // + // Hide console spam from script errors + // + PatchMemory(0x008A5C10, (PBYTE)"\xC3", 1); // RuntimeErrorInternal + + // + // Fake the return value of G_ExitAfterToolComplete (return false) when injecting reflections or + // using fastfiles to prevent a crash + // + if (IsInjectionMode() || !LaunchArg_NoFF()) + PatchMemory(0x0049EF60, (PBYTE)"\xB8\x00\x00\x00\x00\xC3", 6); // - // Check g_reflectionsUpdated for ReflectionExit (mov [g_reflectionsUpdate], 1) + // Change each cubemap shot delay to 16ms (Disabled - Causes some cubemap sides to fail to be saved) // - BYTE buf[7] = {0xC6,0x05,0xFF,0xFF,0xFF,0xFF,0x01}; - bool* pTmp = &g_reflectionsUpdated; - memcpy(buf+2,&pTmp,4); - PatchMemory(0x006CF383, buf, 7); + //PatchMemory(0x006E4F52, (PBYTE)"\x6A\x16", 2); // R_Resolve + //PatchMemory(0x00708AD8, (PBYTE)"\x68\x16\x00\x00\x00", 5); // R_CreateReflectionRawDataFromCubemapShot + //PatchMemory(0x00707146, (PBYTE)"\x68\x40\x00\x00\x00", 5); // R_GetBackBufferDataHDR return TRUE; } @@ -143,118 +151,6 @@ char* formatTime(int seconds) return str[index]; } -void __cdecl R_GenerateReflectionRawDataAll(DiskGfxReflectionProbe *probeRawData, int probeCount, bool *generateProbe) -{ - Com_ToolPrintf(0, "----------------------------------------\n"); - Com_ToolPrintf(0, "Compiling reflections...\n"); - - g_probeCount = probeCount; - - time_t initTime; - time_t cTime; - time(&initTime); - - for (int probeIndex = 0; probeIndex < probeCount; probeIndex++) - { - if (generateProbe[probeIndex]) - R_GenerateReflectionRawData(&probeRawData[probeIndex]); - - time(&cTime); - float percentComplete = (float)(probeIndex + 1) / (float)probeCount; - float elapsedTime = (float)difftime(cTime, initTime); - float remainingTime = elapsedTime / percentComplete - elapsedTime; - - Com_ToolPrintf(0, "%.1f%% complete, %s done, %s remaining\r", percentComplete * 100.0f, formatTime((int)elapsedTime), formatTime((int)remainingTime)); - } - - Com_ToolPrintf(0, "Finished in %s.\n", formatTime((int)difftime(cTime, initTime))); - printf("----------------------------------------\n"); - - g_reflectionsUpdated = true; -} - -void __declspec(naked) hk_R_GenerateReflectionRawDataAll() -{ - __asm - { - push[esp + 8] //generateProbe - push[esp + 8] //probeCount - push eax //probeRawData - call R_GenerateReflectionRawDataAll - add esp, 12 - retn - } -} - -void __cdecl R_GenerateReflectionRawData(DiskGfxReflectionProbe* probeRawData) -{ - if (!*g_ffDir) - { - sprintf_s(g_ffDir, "%s/%s/", Dvar_GetString("fs_basepath"), Dvar_GetString("fs_game")); - } - - refdef_s refdef; - char* ptr = *((char**)0x02FF5354); - refdef_s* refsrc = (refdef_s*)(ptr + 0x8C100); - memcpy(&refdef, refsrc, sizeof(refdef_s)); - - refdef.vieworg[1] = probeRawData->origin[0]; - refdef.vieworg[2] = probeRawData->origin[1]; - refdef.yaw = probeRawData->origin[2]; - - R_InitPrimaryLights(refdef.primaryLights[0].dir); - - for (int cubemapShot = 1; cubemapShot < 7; cubemapShot++) - { - R_BeginCubemapShot(256, 0); - R_BeginFrame(); - - GfxCmdArray* s_cmdList = *(GfxCmdArray**)0x03B370C0; - GfxBackEndData* frontEndDataOut = *(GfxBackEndData**)0x03B3708C; - frontEndDataOut->cmds = &s_cmdList->cmds[s_cmdList->usedTotal]; - frontEndDataOut->viewInfo[frontEndDataOut->viewInfoCount].cmds = NULL; - - R_ClearScene(0); - FX_BeginUpdate(0); - R_CalcCubeMapViewValues(&refdef, cubemapShot, 256); - - float* vec = (float*)0x3AC3060; - vec[0] = refdef.vieworg[0]; - vec[1] = refdef.vieworg[1]; - vec[2] = refdef.vieworg[2]; - - int* unk1 = (int*)0x3AC3058; *unk1 = refdef.time; - float* unk2 = (float*)0x3AC305C; *unk2 = refdef.time * 0.001f; - - R_UpdateFrameFog(refdef.localClientNum); - R_UpdateFrameSun(); - - float zFar = R_GetFarPlaneDist(); - FxCameraUpdate fxUpdateCmd; - memset(&fxUpdateCmd, 0, sizeof(FxCameraUpdate)); - FxCmd cmd; - memset(&cmd, 0, sizeof(FxCmd)); - - FX_GetCameraUpdateFromRefdefAndZFar(&fxUpdateCmd, &refdef, zFar); - FX_SetNextUpdateCamera(0, &fxUpdateCmd); - FX_FillUpdateCmd(0, &cmd); - Sys_ResetUpdateSpotLightEffectEvent(); - Sys_AddWorkerCmdInternal((void*)0x00BA5420, &cmd, 0); - Sys_ResetUpdateNonDependentEffectsEvent(); - Sys_AddWorkerCmdInternal((void*)0x00BA52E0, &cmd, 0); - Sys_AddWorkerCmdInternal((void*)0x00BA5300, &cmd, 0); - Sys_AddWorkerCmdInternal((void*)0x00BA5330, &cmd, 0); - - R_RenderScene(&refdef, 0); - R_EndFrame(); - - R_IssueRenderCommands(3); - R_EndCubemapShot(cubemapShot); - } - - R_CreateReflectionRawDataFromCubemapShot(probeRawData); -} - unsigned int padded(unsigned int i) { return (i + 3) & 0xFFFFFFFC; diff --git a/components/game_mod/patch_reflections.h b/components/game_mod/patch_reflections.h index c6f72111..19c3d10b 100644 --- a/components/game_mod/patch_reflections.h +++ b/components/game_mod/patch_reflections.h @@ -1,6 +1,4 @@ #pragma once -#include -#include "r_reflection_probe.h" struct DiskGfxCubemap256 { @@ -16,7 +14,7 @@ struct DiskGfxCubemap256 char mip4[8]; char mip2[8]; char mip1[8]; - }mips[6]; + } mips[6]; }; struct ZoneCubemapFace256 @@ -36,9 +34,5 @@ BOOL IsReflectionMode(); BOOL IsInjectionMode(); BOOL ReflectionMod_Init(); -void hk_R_GenerateReflectionRawDataAll(); -void __cdecl R_GenerateReflectionRawDataAll(DiskGfxReflectionProbe *probeRawData, int probeCount, bool *generateProbe); -void __cdecl R_GenerateReflectionRawData(DiskGfxReflectionProbe* probeRawData); - BOOL ReflectionsWereUpdated(); BOOL InjectReflections(); \ No newline at end of file diff --git a/components/game_mod/patch_usefastfile.cpp b/components/game_mod/patch_usefastfile.cpp index 6a1deedb..132b32b0 100644 --- a/components/game_mod/patch_usefastfile.cpp +++ b/components/game_mod/patch_usefastfile.cpp @@ -11,14 +11,12 @@ void PatchUseFF() // Only apply patch if 'useFastFile 0' was present at launch // if (!LaunchArg_NoFF()) - { return; - } // - // Prevent overwriting the config file with launch args + // Prevent overwriting the config file // - Patch_Disable_WriteToConfig(); + com_cfg_readOnly_default = true; // // Live_Init @@ -36,9 +34,9 @@ void PatchUseFF() PatchMemory_WithNOP(0x005FEC99, 5); // LiveSteam_Client_SteamDisconnect Detours::X86::DetourFunction((PBYTE)0x00501320, (PBYTE)LiveSteam_GetUid); // LiveSteam_GetUid, nullptr crash fix - BYTE data[] = "\xB8\x00\x00\x00\x00"; - PatchMemory(0x0057362A, data, 5); // nullptr fix - PatchMemory(0x00866C92, data, 5); // ^ + BYTE data[] = "\xB8\x00\x00\x00\x00"; // SteamApps() nullptr crash fix + PatchMemory(0x0057362A, data, 5); + PatchMemory(0x00866C92, data, 5); // // Shaders @@ -72,9 +70,7 @@ bool LaunchArg_NoFF(void) for (int i = 0; i < argc - 1; i++) { if (_wcsicmp(argv[i], L"useFastFile") == 0 && wcscmp(argv[i + 1], L"0") == 0) - { return true; - } } return false; diff --git a/components/game_mod/pathnode.cpp b/components/game_mod/pathnode.cpp new file mode 100644 index 00000000..8817d361 --- /dev/null +++ b/components/game_mod/pathnode.cpp @@ -0,0 +1,165 @@ +#include "stdafx.h" + +// /game/pathnode.cpp:1332 +void node_droptofloor(pathnode_t *node) +{ + static DWORD dwCall = 0x00815190; + + __asm + { + mov eax, node + call [dwCall] + } +} + +// /game/pathnode.cpp:1447 +void G_InitPathBaseNode(pathbasenode_t *pbnode, pathnode_t *pnode) +{ + pbnode->vOrigin[0] = pnode->constant.vOrigin[0]; + pbnode->vOrigin[1] = pnode->constant.vOrigin[1]; + pbnode->vOrigin[2] = pnode->constant.vOrigin[2]; + pbnode->type = 1 << pnode->constant.type; + + if (pnode->constant.spawnflags & 1) + pbnode->type |= 0x200000; +} + +// /game/pathnode.cpp:1461 +void G_DropPathNodeToFloor(unsigned int nodeIndex) +{ + node_droptofloor(&gameWorldCurrent->path.nodes[nodeIndex]); + G_InitPathBaseNode(&gameWorldCurrent->path.basenodes[nodeIndex], &gameWorldCurrent->path.nodes[nodeIndex]); +} + +// /game/pathnode.cpp:1505 +void G_SpawnPathnodeDynamic(SpawnVar *spawnVar) +{ + ((void(__cdecl *)(SpawnVar *))0x004B08E0)(spawnVar); +} + +// /game/pathnode.cpp:1948 +unsigned int Path_ConvertNodeToIndex(pathnode_t *node) +{ + unsigned int nodeIndex = node - gameWorldCurrent->path.nodes; + + ASSERT(node); + // ASSERT(nodeIndex < g_path_actualNodeCount); + + return nodeIndex; +} + +// /game/pathnode.cpp:1973 +void Path_Init(int restart) +{ + if (true || !useFastFile->current.enabled) + { + memset(gameWorldCurrent, 0, sizeof(GameWorldMp)); + + gameWorldCurrent->name = Com_GetHunkStringCopy(Dvar_GetString("mapname")); + + Path_InitStatic(restart); + + if (!restart) + Path_CreateNodes(); + } + + memset(g_path, 0, sizeof(pathlocal_t)); + ASSERT(gameWorldCurrent->path.nodes); + + debugPath = nullptr; + Path_InitBadPlaces(); +} + +void Path_InitBadPlaces() +{ + memset(g_badplaces, 0, MAX_BADPLACES * sizeof(badplace_t)); +} + +// /game/pathnode.cpp:2289 +void Path_InitLinkCounts() +{ + ((void(__cdecl *)())0x00814190)(); +} + +// /game/pathnode.cpp:2327 +void Path_InitLinkInfoArray() +{ + ((void(__cdecl *)())0x00814260)(); +} + +// /game/pathnode.cpp:2387 +void Path_InitPaths() +{ + if (true || !useFastFile->current.enabled) + { + Path_BuildChains(); + + if (true || g_connectpaths->current.integer) + { + if (*(DWORD *)0x1C0706C == 2) + Com_Error(ERR_DROP, "Attempting to load save game when g_connectpaths is set"); + + Path_ConnectPaths(); + } + else + Path_LoadPaths(); + + Path_InitLinkCounts(); + } + + Path_InitLinkInfoArray(); + + if (!useFastFile->current.enabled && Path_FindOverlappingNodes() && g_connectpaths->current.integer >= 2) + Com_Error(ERR_FATAL, "Overlapping path nodes. Check console log"); + + Path_ValidateAllNodes(); + + if (g_connectpaths->current.integer) + { + Path_CheckSpawnExitNodesConnectivity(); + Path_SavePaths(); + + if (g_connectpaths->current.integer >= 2) + { + Sys_NormalExit(); + fflush(stdout); + ExitProcess(0); + } + + g_connectpaths->current.integer = 0; + //Dvar_SetInt(g_connectpaths, 0); + } +} + +// /game/pathnode.cpp:2471 +void Path_CheckSpawnExitNodesConnectivity() +{ + // This function only applies to multiplayer (dog actors must have map exit nodes) +} + +// /game/pathnode.cpp:5034 +void Path_ValidateNode(pathnode_t *node) +{ + int j = j = node->constant.totalLinkCount - 1; + + for (; j >= node->dynamic.wLinkCount; --j) + { + pathlink_s *link = &node->constant.Links[j]; + ASSERT(link->disconnectCount > 0); + } + + ASSERT(j == node->dynamic.wLinkCount - 1); + + for (; j >= 0; --j) + { + pathlink_s *link = &node->constant.Links[j]; + ASSERT_MSG_VA(!link->disconnectCount, "%d, %d, %d", Path_ConvertNodeToIndex(node), j, link->nodeNum); + } +} + +// /game/pathnode.cpp:5054 +void Path_ValidateAllNodes() +{ + // for (int i = 0; i < g_path_actualNodeCount; ++i) + // Path_ValidateNode(&gameWorldCurrent->path.nodes[i]); +} \ No newline at end of file diff --git a/components/game_mod/pathnode.h b/components/game_mod/pathnode.h new file mode 100644 index 00000000..08fd9551 --- /dev/null +++ b/components/game_mod/pathnode.h @@ -0,0 +1,205 @@ +#pragma once + +struct SpawnVar +{ + bool spawnVarsValid; + int numSpawnVars; + char *spawnVars[64][2]; + int numSpawnVarChars; + char spawnVarChars[2048]; +}; +STATIC_ASSERT_OFFSET(SpawnVar, numSpawnVars, 0x4); +STATIC_ASSERT_OFFSET(SpawnVar, spawnVars, 0x8); + +struct SentientHandle +{ + unsigned __int16 number; + unsigned __int16 infoIndex; +}; + +#define MAX_PATH_DYNAMIC_NODES 128 +#define MAX_SPAWN_EXIT_NODES 100 +#define MAX_NODES_IN_BRUSH 512 + +#define PATH_MAX_NODES 8192 +#define PATH_MAX_TOTAL_LINKS 524288 +#define MAX_BADPLACES 256 + +enum nodeType +{ + NODE_BADNODE = 0x0, + NODE_PATHNODE = 0x1, + NODE_COVER_STAND = 0x2, + NODE_COVER_CROUCH = 0x3, + NODE_COVER_CROUCH_WINDOW = 0x4, + NODE_COVER_PRONE = 0x5, + NODE_COVER_RIGHT = 0x6, + NODE_COVER_LEFT = 0x7, + NODE_COVER_WIDE_RIGHT = 0x8, + NODE_COVER_WIDE_LEFT = 0x9, + NODE_COVER_PILLAR = 0xA, + NODE_CONCEALMENT_STAND = 0xB, + NODE_CONCEALMENT_CROUCH = 0xC, + NODE_CONCEALMENT_PRONE = 0xD, + NODE_REACQUIRE = 0xE, + NODE_BALCONY = 0xF, + NODE_SCRIPTED = 0x10, + NODE_NEGOTIATION_BEGIN = 0x11, + NODE_NEGOTIATION_END = 0x12, + NODE_TURRET = 0x13, + NODE_GUARD = 0x14, + NODE_NUMTYPES = 0x15, + NODE_DONTLINK = 0x15, +}; + +struct pathlink_s; +struct pathnode_t; + +struct pathlink_s +{ + float fDist; // this+0x0 + unsigned short nodeNum; // this+0x4 + unsigned char disconnectCount; // this+0x6 + unsigned char negotiationLink; // this+0x7 + unsigned char ubBadPlaceCount[4]; // this+0x8 +}; +STATIC_ASSERT_SIZE(pathlink_s, 0xC); + +struct pathnode_constant_t +{ + nodeType type; + unsigned __int16 spawnflags; + unsigned __int16 targetname; + unsigned __int16 script_linkName; + unsigned __int16 script_noteworthy; + unsigned __int16 target; + unsigned __int16 animscript; + int animscriptfunc; + float vOrigin[3]; + float fAngle; + float forward[2]; + float fRadius; + float minUseDistSq; + __int16 wOverlapNode[2]; + __int16 wChainId; + __int16 wChainDepth; + __int16 wChainParent; + unsigned __int16 totalLinkCount; + pathlink_s *Links; +}; + +struct pathnode_dynamic_t +{ + SentientHandle pOwner; + int iFreeTime; + int iValidTime[3]; + int inPlayerLOSTime; + __int16 wLinkCount; + __int16 wOverlapCount; + __int16 turretEntNumber; + __int16 userCount; +}; + +struct pathnode_transient_t +{ + int iSearchFrame; + pathnode_t *pNextOpen; + pathnode_t *pPrevOpen; + pathnode_t *pParent; + float fCost; + float fHeuristic; + float costFactor; +}; + +struct pathnode_t +{ + pathnode_constant_t constant; + pathnode_dynamic_t dynamic; + pathnode_transient_t transient; +}; +STATIC_ASSERT_SIZE(pathnode_t, 0x80); + +struct pathbasenode_t +{ + float vOrigin[3]; + unsigned int type; +}; +STATIC_ASSERT_OFFSET(pathbasenode_t, vOrigin, 0x0); +STATIC_ASSERT_OFFSET(pathbasenode_t, type, 0xC); + +struct pathnode_tree_t +{ + char _pad[0x1]; +}; + +struct badplace_t +{ + char _pad[0x2C]; +}; +STATIC_ASSERT_SIZE(badplace_t, 0x2C); + +struct path_t +{ + char _pad[0x1]; +}; + +struct pathstatic_t +{ + pathlink_s *pathLinks; // this+0x0 + unsigned short *indirectNodes; // this+0x4 + unsigned char *pathbuf; // this+0x8 +}; +STATIC_ASSERT_SIZE(pathstatic_t, 0xC); + +struct pathlocal_t +{ + char _pad[0x4080]; +}; +STATIC_ASSERT_SIZE(pathlocal_t, 0x4080); + +struct PathData +{ + unsigned int nodeCount; // this+0x0 + pathnode_t *nodes; // this+0x4 + pathbasenode_t *basenodes; // this+0x8 + unsigned int chainNodeCount; // this+0xC + unsigned short *chainNodeForNode; // this+0x10 + unsigned short *nodeForChainNode; // this+0x14 + int visBytes; // this+0x18 + unsigned char *pathVis; // this+0x1C + int nodeTreeCount; // this+0x20 + pathnode_tree_t *nodeTree; // this+0x24 +}; +STATIC_ASSERT_SIZE(PathData, 0x28); + +static pathlocal_t *g_path = (pathlocal_t *)0x01D00400; +static const char*& g_pathsError = *(const char **)0x01D0496C; +static badplace_t *g_badplaces = (badplace_t *)0x01A4C088; +static path_t*& debugPath = *(path_t **)0x01D04484; +static pathstatic_t *pathstatic = (pathstatic_t *)0x01D04970; + +typedef bool (*G_OnlyConnectingPaths_t)(); +static G_OnlyConnectingPaths_t G_OnlyConnectingPaths = (G_OnlyConnectingPaths_t)0x0049D640; + +void node_droptofloor(pathnode_t *node); +void G_InitPathBaseNode(pathbasenode_t *pbnode, pathnode_t *pnode); +void G_DropPathNodeToFloor(unsigned int nodeIndex); +void G_SpawnPathnodeDynamic(SpawnVar *spawnVar); +unsigned int Path_ConvertNodeToIndex(pathnode_t *node); +void Path_Init(int restart); +void Path_InitBadPlaces(); +void Path_InitLinkCounts(); +void Path_InitLinkInfoArray(); +void Path_InitPaths(); +void Path_CheckSpawnExitNodesConnectivity(); +void Path_ValidateNode(pathnode_t *node); +void Path_ValidateAllNodes(); + +typedef struct +{ + const char *name; + PathData path; +} GameWorldMp, GameWorldSp; +STATIC_ASSERT_SIZE(GameWorldMp, 0x2C); + +static GameWorldMp*& gameWorldCurrent = *(GameWorldMp **)0x00B77734; \ No newline at end of file diff --git a/components/game_mod/pathnode_load_obj.cpp b/components/game_mod/pathnode_load_obj.cpp new file mode 100644 index 00000000..1182fc8d --- /dev/null +++ b/components/game_mod/pathnode_load_obj.cpp @@ -0,0 +1,202 @@ +#include "stdafx.h" + +int g_tempPathNodeLinksCount; +char g_tempPathNodeLinks[MAX_PATH_DYNAMIC_NODES * 0xC0]; + +// /game/pathnode_load_obj.cpp:207 +void Path_CreateNodes() +{ + pathstatic->pathLinks = nullptr; + pathstatic->indirectNodes = nullptr; + pathstatic->pathbuf = nullptr; + + gameWorldCurrent->path.nodes = (pathnode_t *)Hunk_Alloc(PATH_MAX_NODES * sizeof(pathnode_t), "Path_CreateNodes", 6); + gameWorldCurrent->path.basenodes = (pathbasenode_t *)Hunk_Alloc(PATH_MAX_NODES * sizeof(pathbasenode_t), "Path_CreateNodes", 6); +} + +// /game/pathnode_load_obj.cpp:221 +void Path_InitStatic(int restart) +{ + if (restart) + { + // The game keeps existing node data when restarting a map + gameWorldCurrent->path.nodeCount = 0; + } + else + { + // New map load. Initialize everything to zero. + memset(&gameWorldCurrent->path, 0, sizeof(PathData)); + } + + g_pathsError = nullptr; +} + +// /game/pathnode_load_obj.cpp:324 +void G_ParsePathnodeFields(SpawnVar *spawnVar, pathnode_t *node, nodeType type) +{ + ((void(__cdecl *)(SpawnVar *, pathnode_t *, nodeType))0x00626C00)(spawnVar, node, type); +} + +// /game/pathnode_load_obj.cpp:381 +void SP_spawn_node(SpawnVar *spawnVar, nodeType type) +{ + static unsigned int overCount; + + ASSERT(type < NODE_NUMTYPES); + ASSERT(gameWorldCurrent->path.nodes); + + // Check if we exceeded the hardcoded limit + if (gameWorldCurrent->path.nodeCount >= (PATH_MAX_NODES - MAX_PATH_DYNAMIC_NODES)) + { + Com_Printf(18, "PATH_MAX_NODES (%i) exceeded. Nodecount: %d\n", PATH_MAX_NODES, ++overCount + gameWorldCurrent->path.nodeCount); + return; + } + + // Otherwise parse the new index + pathnode_t *loadNode = &gameWorldCurrent->path.nodes[gameWorldCurrent->path.nodeCount]; + memset(loadNode, 0, sizeof(pathnode_t)); + + ASSERT(gameWorldCurrent->path.nodeCount == (unsigned short)gameWorldCurrent->path.nodeCount); + + G_ParsePathnodeFields(spawnVar, loadNode, type); + gameWorldCurrent->path.nodeCount++; +} + +// /game/pathnode_load_obj.cpp:407 +nodeType G_GetNodeTypeFromClassname(const char *classname) +{ + for (nodespawn_t *ns = nodespawns; ns->name; ns++) + { + if (!strcmp(ns->name, classname)) + return ns->type; + } + + Com_Error(ERR_DROP, "%s doesn't have a spawn function", classname); + return NODE_PATHNODE; +} + +// /game/pathnode_load_obj.cpp:430 +void G_SpawnPathnodeStatic(SpawnVar *spawnVar, const char *classname) +{ + SP_spawn_node(spawnVar, G_GetNodeTypeFromClassname(classname)); +} + +// /game/pathnode_load_obj.cpp:557 +void Path_BuildChains() +{ + ((void(__cdecl *)())0x0047CA10)(); +} + +// /game/pathnode_load_obj.cpp:695 +bool Path_FindOverlappingNodes() +{ + return ((bool(__cdecl *)())0x0048E0E0)(); +} + +// /game/pathnode_load_obj.cpp:1120 +void Path_LoadPaths() +{ + ((void(__cdecl *)())0x00653D40)(); +} + +// /game/pathnode_load_obj.cpp:1346 +bool Path_AttemptLink(pathnode_t *pNodeFrom, pathnode_t *pNodeTo, pathlink_s *pLinks, int iMaxLinks) +{ + static DWORD dwCall = 0x00817AC0; + + __asm + { + push iMaxLinks + push pLinks + push pNodeTo + mov eax, pNodeFrom + call [dwCall] + add esp, 0xC + } +} + +// /game/pathnode_load_obj.cpp:1603 +pathlink_s *G_GetNextAvailableTempLinks() +{ + ASSERT(g_tempPathNodeLinksCount < MAX_PATH_DYNAMIC_NODES); + + pathlink_s *links = (pathlink_s *)&g_tempPathNodeLinks[0xC0 * g_tempPathNodeLinksCount]; + g_tempPathNodeLinksCount++; + + memset(links, 0, 0xC0); + return links; +} + +// /game/pathnode_load_obj.cpp:1618 +void Path_ConnectPathsForSingleNode(pathnode_t *node) +{ + if (g_tempPathNodeLinksCount >= MAX_PATH_DYNAMIC_NODES) + return; + + if (node->constant.type == NODE_BADNODE) + return; + + if (node->constant.spawnflags & 1) + return; + + if (!node->constant.Links || !(node->constant.spawnflags & 0x4000)) + { + node->constant.spawnflags |= 0x4000; + node->constant.Links = G_GetNextAvailableTempLinks(); + } + + node->constant.totalLinkCount = 0; + unsigned int nodeindex = Path_ConvertNodeToIndex(node); + + for (unsigned int i = 0; i < gameWorldCurrent->path.nodeCount; ++i) + { + pathnode_t *othernode = &gameWorldCurrent->path.nodes[i]; + + if (!(othernode->constant.spawnflags & 1) && othernode->constant.type != NODE_BADNODE && othernode != node) + { + for (unsigned short j = 0; j < othernode->constant.totalLinkCount; ++j) + { + if (othernode->constant.Links[j].nodeNum == nodeindex) + { + othernode->constant.Links[j] = othernode->constant.Links[--othernode->constant.totalLinkCount]; + othernode->dynamic.wLinkCount = othernode->constant.totalLinkCount; + } + } + + if (Path_AttemptLink(node, othernode, node->constant.Links, 16)) + { + if (!othernode->constant.Links || !(othernode->constant.spawnflags & 0x4000)) + { + othernode->constant.spawnflags |= 0x4000; + + pathlink_s *linksBuffer = G_GetNextAvailableTempLinks(); + + if (othernode->constant.Links) + memcpy(linksBuffer, othernode->constant.Links, 12 * othernode->constant.totalLinkCount); + + othernode->constant.Links = linksBuffer; + } + + if (othernode->constant.totalLinkCount < 16 && Path_AttemptLink(othernode, node, othernode->constant.Links, 16)) + othernode->dynamic.wLinkCount = othernode->constant.totalLinkCount; + } + + if (node->constant.totalLinkCount >= 16) + break; + } + } + + node->dynamic.wLinkCount = node->constant.totalLinkCount; +} + +// /game/pathnode_load_obj.cpp:1704 +void Path_ConnectPaths() +{ + ((void(__cdecl *)())0x004C4780)(); +} + +// /game/pathnode_load_obj.cpp:1856 +void Path_SavePaths() +{ + ((void(__cdecl *)())0x00462950)(); +} \ No newline at end of file diff --git a/components/game_mod/pathnode_load_obj.h b/components/game_mod/pathnode_load_obj.h new file mode 100644 index 00000000..564cb87a --- /dev/null +++ b/components/game_mod/pathnode_load_obj.h @@ -0,0 +1,26 @@ +#pragma once + +struct nodespawn_t +{ + char *name; + nodeType type; +}; + +static nodespawn_t *nodespawns = (nodespawn_t *)0x00A55B78; +extern int g_tempPathNodeLinksCount; +extern char g_tempPathNodeLinks[]; + +void Path_CreateNodes(); +void Path_InitStatic(int restart); +void G_ParsePathnodeFields(SpawnVar *spawnVar, pathnode_t *node, nodeType type); +void SP_spawn_node(SpawnVar *spawnVar, nodeType type); +nodeType G_GetNodeTypeFromClassname(const char *classname); +void G_SpawnPathnodeStatic(SpawnVar *spawnVar, const char *classname); +void Path_BuildChains(); +bool Path_FindOverlappingNodes(); +void Path_LoadPaths(); +bool Path_AttemptLink(pathnode_t *pNodeFrom, pathnode_t *pNodeTo, pathlink_s *pLinks, int iMaxLinks); +pathlink_s *G_GetNextAvailableTempLinks(); +void Path_ConnectPathsForSingleNode(pathnode_t *node); +void Path_ConnectPaths(); +void Path_SavePaths(); \ No newline at end of file diff --git a/components/game_mod/phys_main.cpp b/components/game_mod/phys_main.cpp new file mode 100644 index 00000000..2aed3308 --- /dev/null +++ b/components/game_mod/phys_main.cpp @@ -0,0 +1,49 @@ +#include "stdafx.h" +#include + +void Phys_ObjAddForce(int id, const float *worldPos, const float *impulse, const bool relative) +{ + ((void(__cdecl *)(int, const float *, const float *, const bool))0x00448900)(id, worldPos, impulse, relative); +} + +void Phys_ObjGetCenterOfMass(int id, float *outPosition) +{ + ((void(__cdecl *)(int, float *))0x0054E920)(id, outPosition); +} + +int Phys_AssertValidPreset(PhysPreset* physPreset) +{ + if (!physPreset || physPreset == (PhysPreset*)0xFFFFFFFF) + { + Com_Error(ERR_DROP, "Attempted to spawn physics object without a valid physPreset\n", physPreset); + return 1; + } + + return 0; +} + +void __declspec(naked) hk_Phys_ObjCreateAxis() +{ + static DWORD dwRetn = 0x0081E037; + + _asm + { + pushad + push esi // physPreset + call Phys_AssertValidPreset + add esp, 4 + + cmp eax, 0 + jnz SKIP + + + popad + sub esp, 88h + push ebx + jmp dwRetn + + SKIP: + popad + retn + } +} diff --git a/components/game_mod/phys_main.h b/components/game_mod/phys_main.h new file mode 100644 index 00000000..8ba3075a --- /dev/null +++ b/components/game_mod/phys_main.h @@ -0,0 +1,47 @@ +#pragma once + +struct PhysPreset +{ + const char *name; + int flags; + float mass; + float bounce; + float friction; + float bulletForceScale; + float explosiveForceScale; + const char *sndAliasPrefix; + float piecesSpreadFraction; + float piecesUpwardVelocity; + int canFloat; + float gravityScale; + float centerOfMassOffset[3]; + float buoyancyBoxMin[3]; + float buoyancyBoxMax[3]; +}; + +struct BodyState +{ + float position[3]; + float rotation[3][3]; + float velocity[3]; + float angVelocity[3]; + float centerOfMassOffset[3]; + float buoyancyBoxMin[3]; + float buoyancyBoxMax[3]; + float mass; + float friction; + float bounce; + int timeLastAsleep; + int id; + int buoyancy; + int underwater; +}; + +void Phys_ObjAddForce(int id, const float *worldPos, const float *impulse, const bool relative); +void Phys_ObjGetCenterOfMass(int id, float *outPosition); + +void hk_Phys_ObjCreateAxis(); //int worldIndex, const float *position, struct gjk_geom_list_t* gjk_geom_list, int id, int do_collision_test); +int __cdecl Phys_ObjCreateAxis(int worldIndex, const float *position, const float(*axis)[3], const float *velocity, PhysPreset *physPreset, struct gjk_geom_list_t *gjk_geom_list, int id, const bool do_collision_test); + +typedef int (__cdecl* Phys_CreateBodyFromState_t)(int worldIndex, BodyState *state, struct gjk_geom_list_t *gjk_geom_list, const bool do_collision_test); +static Phys_CreateBodyFromState_t Phys_CreateBodyFromState = (Phys_CreateBodyFromState_t)0x005785C0; diff --git a/components/game_mod/r_cinematic.cpp b/components/game_mod/r_cinematic.cpp index bbf11046..bcf5673d 100644 --- a/components/game_mod/r_cinematic.cpp +++ b/components/game_mod/r_cinematic.cpp @@ -2,7 +2,7 @@ void __declspec(naked) hk_R_Cinematic_BinkOpen(void) { - _asm + __asm { push[esp + 8] push[esp + 8] @@ -15,7 +15,8 @@ void __declspec(naked) hk_R_Cinematic_BinkOpen(void) bool __cdecl R_Cinematic_BinkOpen(const char *filename, unsigned int playbackFlags, char *errText) { - char filepath[256] = "\0"; + char filepath[256]; + memset(filepath, 0, sizeof(filepath)); const char* cwd = Sys_Cwd(); @@ -23,37 +24,28 @@ bool __cdecl R_Cinematic_BinkOpen(const char *filename, unsigned int playbackFla // Attempt to load any custom loadscreens before the stock ones (Allows for mod-specific loadscreens for stock maps) // const char* fs_game = Dvar_GetString("fs_game"); - if (fs_game || fs_game[0] != '\0') + if (fs_game && fs_game[0] != '\0') { - _snprintf_s(filepath, 256, "%s\\%s\\video\\%s.%s", cwd, fs_game, filename, "bik"); - if (filepath[0] && R_Cinematic_BinkOpenPath(filepath, playbackFlags, errText)) - return 1; + _snprintf_s(filepath, _TRUNCATE, "%s\\%s\\video\\%s.bik", cwd, fs_game, filename); + if (R_Cinematic_BinkOpenPath(filepath, playbackFlags, errText)) + return true; } if (playbackFlags & 8) - { - _snprintf_s(filepath, 256, "bik/%s.%s", filename, "bik"); - } + _snprintf_s(filepath, _TRUNCATE, "bik/%s.bik", filename); else - { - _snprintf_s(filepath, 256, "%s\\main\\video\\%s.%s", cwd, filename, "bik"); - } + _snprintf_s(filepath, _TRUNCATE, "%s\\main\\video\\%s.bik", cwd, filename); if (R_Cinematic_BinkOpenPath(filepath, playbackFlags, errText)) - return 1; + return true; - _snprintf_s(filepath, 256, "%s\\raw\\video\\%s.%s", cwd, filename, "bik"); - if (filepath[0] && R_Cinematic_BinkOpenPath(filepath, playbackFlags, errText)) - return 1; - - return 0; + _snprintf_s(filepath, _TRUNCATE, "%s\\raw\\video\\%s.bik", cwd, filename); + return R_Cinematic_BinkOpenPath(filepath, playbackFlags, errText); } -bool __cdecl R_Cinematic_BinkOpenPath(const char *filepath, unsigned int playbackFlags, char *errText) +bool R_Cinematic_BinkOpenPath(const char *filepath, unsigned int playbackFlags, char *errText) { - bool result = 0; - - _asm + __asm { push errText push playbackFlags @@ -62,9 +54,6 @@ bool __cdecl R_Cinematic_BinkOpenPath(const char *filepath, unsigned int playbac mov ebx, 0x006D96E0 call ebx add esp, 8 - mov result, al } - - return result; } diff --git a/components/game_mod/r_cinematic.h b/components/game_mod/r_cinematic.h index 286d5aa1..ee2617cc 100644 --- a/components/game_mod/r_cinematic.h +++ b/components/game_mod/r_cinematic.h @@ -3,4 +3,4 @@ void hk_R_Cinematic_BinkOpen(void); bool __cdecl R_Cinematic_BinkOpen(const char *filename, unsigned int playbackFlags, char *errText); -bool __cdecl R_Cinematic_BinkOpenPath(const char *filepath, unsigned int playbackFlags, char *errText); +bool R_Cinematic_BinkOpenPath(const char *filepath, unsigned int playbackFlags, char *errText); diff --git a/components/game_mod/r_debug.cpp b/components/game_mod/r_debug.cpp new file mode 100644 index 00000000..b3eac68d --- /dev/null +++ b/components/game_mod/r_debug.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" + +void __cdecl R_AddDebugString(DebugGlobals *debugGlobalsEntry, const float *origin, const float *color, float scale, const char *string) +{ + _asm + { + pushad + push string + push scale + push color + push origin + mov edi, debugGlobalsEntry + mov ebx, 0x006FCC50 + call ebx + add esp, 0x10 + popad + } +} diff --git a/components/game_mod/r_debug.h b/components/game_mod/r_debug.h new file mode 100644 index 00000000..738b1d20 --- /dev/null +++ b/components/game_mod/r_debug.h @@ -0,0 +1,11 @@ +#pragma once + +struct DebugGlobals +{ + char data[0x6C]; +}; + +static float* const colorWhite = (float* const)0x00A5E8C4; +static float* const colorRed = (float* const)0x00A5E794; + +void __cdecl R_AddDebugString(DebugGlobals *debugGlobalsEntry, const float *origin, const float *color, float scale, const char *string); diff --git a/components/game_mod/r_draw_xmodel.cpp b/components/game_mod/r_draw_xmodel.cpp new file mode 100644 index 00000000..7fff4e8d --- /dev/null +++ b/components/game_mod/r_draw_xmodel.cpp @@ -0,0 +1,141 @@ +#pragma once +#include "stdafx.h" + +void* R_DrawXModelSkinnedCached_o = nullptr; +void* R_DrawXModelSkinnedUncached_o = nullptr; + +R_DrawXModelRigidModelSurf_t R_DrawXModelRigidModelSurf_o = (R_DrawXModelRigidModelSurf_t)0x0; + +void __cdecl R_WorldMatrixChanged(GfxCmdBufSourceState *source) +{ + ++source->matrixVersions[0]; + ++source->matrixVersions[3]; + ++source->matrixVersions[5]; + ++source->matrixVersions[7]; + + source->constVersions[194] = source->matrixVersions[0]; +} + +GfxMatrix *__cdecl R_GetActiveWorldMatrix(GfxCmdBufSourceState *source) +{ + R_WorldMatrixChanged(source); + return source->matrices.matrix; +} + +void __declspec(naked) hk_R_DrawXModelSkinnedCached(GfxCmdBufContext context) // 0x0073BF30 +{ + _asm + { + push eax // modelSurf + push[esp + 12] // context.state + push[esp + 12] // context.source + call R_DrawXModelSkinnedCached + add esp, 12 + retn + } +} + +void __cdecl R_DrawXModelSkinnedCached(GfxCmdBufContext context, struct GfxModelSkinnedSurface *modelSurf) +{ + GfxCmdBufSourceState* source = context.source; + GfxCmdBufState* state = context.state; + + DBG_ASSERT(R_DrawXModelSkinnedCached_o); + + _asm + { + push state // [&context+4] + push source // [&context] + mov eax, modelSurf + call R_DrawXModelSkinnedCached_o + add esp, 8 + } + + if (r_showTess->current.enabled) + { + GfxMatrix* mtx = R_GetActiveWorldMatrix(context.source); + RB_ShowTess(context, (float*)&mtx->m[3], "XMSkin$", colorWhite); + } +} + +void __declspec(naked) hk_R_DrawXModelSkinnedUncached() // 0x0073BFC0 +{ + _asm + { + push[esp + 12] // skinnedVert + push eax // xsurf + push[esp + 16] // context.state + push[esp + 16] // context.source + call R_DrawXModelSkinnedUncached + add esp, 16 + retn + } +} + +void __cdecl R_DrawXModelSkinnedUncached(GfxCmdBufContext context, XSurface *xsurf, GfxPackedVertex *skinnedVert) +{ + GfxCmdBufSourceState* source = context.source; + GfxCmdBufState* state = context.state; + + DBG_ASSERT(R_DrawXModelSkinnedUncached_o); + + _asm + { + push skinnedVert + push state // [&context+4] + push source // [&context] + mov eax, xsurf + call R_DrawXModelSkinnedUncached_o + add esp, 12 + } + + if (r_showTess->current.enabled) + { + GfxMatrix* mtx = R_GetActiveWorldMatrix(context.source); + RB_ShowTess(context, (float*)&mtx->m[3], "XMSkinUn$", colorWhite); + } +} + +void __cdecl hk_R_DrawXModelRigidModelSurf(XSurface *xsurf, GfxCmdBufContext context) +{ + R_DrawXModelRigidModelSurf(context, xsurf); +} + +void __cdecl mfh_R_TessXModelWaterList_ShowTess(GfxCmdBufContext context) +{ + MessageBoxA(0, "R_TessXModelWaterList", 0, 0); // Todo: Confirm that this func even runs + + if (r_showTess->current.enabled) + { + GfxMatrix* mtx = R_GetActiveWorldMatrix(context.source); + RB_ShowTess(context, (float*)&mtx->m[3], "XMRigid", colorWhite); + } +} + +void* rtn_R_TessXModelWaterList = (void*)0x0073D8B8; +void __declspec(naked) mfh_R_TessXModelWaterList(void) // 0x0073D8B2 +{ + _asm + { + call eax + mov eax, [esp + 0x14] + push esi // context.state + push ebx // context.source + call mfh_R_TessXModelWaterList_ShowTess + add esp, 8 + + jmp rtn_R_TessXModelWaterList + } +} + +void __cdecl R_DrawXModelRigidModelSurf(GfxCmdBufContext context, XSurface *xsurf) +{ + DBG_ASSERT(R_DrawXModelRigidModelSurf_o); + R_DrawXModelRigidModelSurf_o(xsurf, context); + + if (r_showTess->current.enabled) + { + GfxMatrix* mtx = R_GetActiveWorldMatrix(context.source); + RB_ShowTess(context, (float*)&mtx->m[3], "XMRigid", colorWhite); + } +} diff --git a/components/game_mod/r_draw_xmodel.h b/components/game_mod/r_draw_xmodel.h new file mode 100644 index 00000000..68227666 --- /dev/null +++ b/components/game_mod/r_draw_xmodel.h @@ -0,0 +1,18 @@ +#pragma once +#include "r_scene.h" + +extern void* R_DrawXModelSkinnedCached_o; +extern void* R_DrawXModelSkinnedUncached_o; + +typedef void(__cdecl* R_DrawXModelRigidModelSurf_t)(struct XSurface *xsurf, GfxCmdBufContext context); +extern R_DrawXModelRigidModelSurf_t R_DrawXModelRigidModelSurf_o; + +void hk_R_DrawXModelSkinnedCached(GfxCmdBufContext context); +void hk_R_DrawXModelSkinnedUncached(void); +void hk_R_DrawXModelRigidModelSurf(XSurface *xsurf, GfxCmdBufContext context); + +void mfh_R_TessXModelWaterList(void); + +void __cdecl R_DrawXModelSkinnedCached(GfxCmdBufContext context, struct GfxModelSkinnedSurface *modelSurf); +void __cdecl R_DrawXModelSkinnedUncached(GfxCmdBufContext context, XSurface *xsurf, struct GfxPackedVertex *skinnedVert); +void __cdecl R_DrawXModelRigidModelSurf(GfxCmdBufContext context, XSurface *xsurf); diff --git a/components/game_mod/r_image.cpp b/components/game_mod/r_image.cpp new file mode 100644 index 00000000..084a55fa --- /dev/null +++ b/components/game_mod/r_image.cpp @@ -0,0 +1,377 @@ +#include "stdafx.h" +#include + +const char* g_platform_name[] = { "current", "min_pc", "bad allocation", "bad allocation", "bad allocation", "bad allocation", "bad allocation", "bad allocation" }; +const char* imageTypeName[] = { "misc", "debug", "$tex+?", "ui", "lmap", "light", "f/x", "hud", "model", "world" }; + +void Image_HandleMissingImage(int code, const char *fmt, const char* image) +{ + DB_LogMissingAsset(ASSET_TYPE_IMAGE, image); + Com_PrintWarning(8, fmt, image); +} + +unsigned int Image_GetUsage(int imageFlags, D3DFORMAT imageFormat) +{ + if (imageFlags & 0x20000) + { + if (imageFormat != D3DFMT_D24S8 + && imageFormat != D3DFMT_D24X8 + && imageFormat != D3DFMT_D16) + return D3DUSAGE_RENDERTARGET; + + return D3DUSAGE_DEPTHSTENCIL; + } + + if (imageFlags & 0x10000) + return D3DUSAGE_DYNAMIC; + + return 0; +} + +void Image_Create2DTexture_PC(GfxImage *image, unsigned __int16 width, unsigned __int16 height, int mipmapCount, int imageFlags, D3DFORMAT imageFormat) +{ + ASSERT(image != nullptr); + ASSERT(!image->texture.basemap); + ASSERT(Sys_IsRenderThread()); + + image->width = width; + image->height = height; + image->depth = 1; + image->mapType = 3; + + D3DPOOL memPool = D3DPOOL_DEFAULT; + DWORD usage = Image_GetUsage(imageFlags, imageFormat); + + if (imageFlags & 0x40000 || imageFlags & 0x100) + memPool = D3DPOOL_SYSTEMMEM; + else + memPool = (usage == 0) ? D3DPOOL_MANAGED : D3DPOOL_DEFAULT; + + // D3D9Ex does not allow D3DPOOL_MANAGED + if (IsD3D9ExAvailable()) + { + usage = (usage == 0) ? D3DUSAGE_DYNAMIC : usage; + memPool = (memPool == D3DPOOL_MANAGED) ? D3DPOOL_DEFAULT : memPool; + } + + HRESULT hr = dx_device->CreateTexture(width, height, mipmapCount, usage, imageFormat, memPool, &image->texture.map, nullptr); + + if (FAILED(hr)) + { + g_disableRendering++; + Com_Error(ERR_FATAL, "dx.device->CreateTexture(width, height, mipmapCount, usage, imageFormat, memPool, &image->texture.map, nullptr) failed: %s\n", R_ErrorDescription(hr)); + } +} + +void Image_Create3DTexture_PC(GfxImage *image, unsigned __int16 width, unsigned __int16 height, unsigned __int16 depth, int mipmapCount, int imageFlags, D3DFORMAT imageFormat) +{ + ASSERT(image != nullptr); + ASSERT(!image->texture.basemap); + ASSERT(Sys_IsRenderThread()); + + image->width = width; + image->height = height; + image->depth = depth; + image->mapType = 4; + + // D3D9Ex does not allow D3DPOOL_MANAGED + DWORD usage = (IsD3D9ExAvailable()) ? D3DUSAGE_DYNAMIC : 0; + D3DPOOL memPool = (IsD3D9ExAvailable()) ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; + + HRESULT hr = dx_device->CreateVolumeTexture(width, height, depth, mipmapCount, usage, imageFormat, memPool, &image->texture.volmap, nullptr); + + if (FAILED(hr)) + { + g_disableRendering++; + Com_Error(ERR_FATAL, "dx.device->CreateVolumeTexture(width, height, depth, mipmapCount, usage, imageFormat, memPool, &image->texture.volmap, nullptr) failed: %s\n", R_ErrorDescription(hr)); + } +} + +void Image_CreateCubeTexture_PC(GfxImage *image, unsigned __int16 edgeLen, int mipmapCount, D3DFORMAT imageFormat) +{ + ASSERT(image != nullptr); + ASSERT(!image->texture.basemap); + ASSERT(Sys_IsRenderThread()); + + image->width = edgeLen; + image->height = edgeLen; + image->depth = 1; + image->mapType = 5; + + // D3DDeviceCaps support for mipping + if (!r_supportCubedMipMaps) + mipmapCount = 1; + + // D3D9Ex does not allow D3DPOOL_MANAGED + DWORD usage = (IsD3D9ExAvailable()) ? D3DUSAGE_DYNAMIC : 0; + D3DPOOL memPool = (IsD3D9ExAvailable()) ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; + + HRESULT hr = dx_device->CreateCubeTexture(edgeLen, mipmapCount, usage, imageFormat, memPool, &image->texture.cubemap, nullptr); + + if (FAILED(hr)) + { + g_disableRendering++; + Com_Error(ERR_FATAL, "dx.device->CreateCubeTexture(edgeLen, mipmapCount, usage, imageFormat, memPool, &image->texture.cubemap, nullptr) failed: %s\n", R_ErrorDescription(hr)); + } +} + +void __declspec(naked) hk_Image_Create2DTexture_PC(unsigned __int16 width, int mipmapCount, int imageFormat) +{ + __asm + { + push ebp + mov ebp, esp + + push imageFormat // a6: imageFormat + push edx // a5: imageFlags + push mipmapCount // a4: mipmapCount + movzx ecx, di // + push ecx // a3: height + movzx ecx, width // + push ecx // a2: width + push eax // a1: image + call Image_Create2DTexture_PC + add esp, 0x18 + + mov esp, ebp + pop ebp + ret + } +} + +void __declspec(naked) hk_Image_Create3DTexture_PC(int image, int mipmapCount, int imageFormat) +{ + __asm + { + push ebp + mov ebp, esp + + push imageFormat // a7: imageFormat + push 0 // a6: imageFlags + push mipmapCount // a5: mipmapCount + movzx eax, cx // + push eax // a4: depth + movzx eax, dx // + push eax // a3: height + movzx eax, si // + push eax // a2: width + push image // a1: image + call Image_Create3DTexture_PC + add esp, 0x1C + + mov esp, ebp + pop ebp + ret + } +} + +void __declspec(naked) hk_Image_CreateCubeTexture_PC(signed int mipmapCount, int imageFormat) +{ + __asm + { + push ebp + mov ebp, esp + + push imageFormat // a4: imageFormat + push mipmapCount // a3: mipmapCount + movzx edx, cx // + push edx // a2: edgeLen + push eax // a1: image + call Image_CreateCubeTexture_PC + add esp, 0x10 + + mov esp, ebp + pop ebp + ret + } +} + +bool __cdecl Image_IsCodeImage(int track) +{ + return track >= 0 && (track <= 1 || track == 4); +} + +struct ImageList +{ + unsigned int count; + GfxImage *image[4096*2]; // make sure there are enough pointers for the modified image limit +}; + +_D3DFORMAT __cdecl R_ImagePixelFormat(GfxImage *image) +{ + _D3DSURFACE_DESC surfaceDesc; + _D3DVOLUME_DESC volumeDesc; + + switch (image->mapType) + { + case MAPTYPE_2D: + ASSERT(image->texture.map); + image->texture.map->GetLevelDesc(0, &surfaceDesc); + return surfaceDesc.Format; + case MAPTYPE_3D: + ASSERT(image->texture.volmap); + image->texture.volmap->GetLevelDesc(0, &volumeDesc); + return volumeDesc.Format; + case MAPTYPE_CUBE: + ASSERT(image->texture.cubemap); + image->texture.cubemap->GetLevelDesc(0, &surfaceDesc); + return surfaceDesc.Format; + default: + ASSERT_MSG_VA(false, "unhandled case %i for %s", image->mapType, image->name); + return D3DFMT_UNKNOWN; + } + + return D3DFMT_UNKNOWN; +} + +#include + +void __cdecl R_AddImageToList(GfxImage* image, ImageList *list) +{ + ASSERT(list->count < _countof(list->image)); + list->image[list->count++] = image; +} + +void __cdecl R_GetImageList(ImageList *imageList) +{ + ASSERT(imageList); + imageList->count = 0; + DB_EnumXAssets(ASSET_TYPE_IMAGE, (void(__cdecl *)(XAssetHeader, void *))R_AddImageToList, imageList, 1); +} + +void __cdecl R_ImageList_f() +{ + CardMemory total; + total.platform[0] = 0; + total.platform[1] = 0; + + CardMemory imageTrack[10]; + memset(imageTrack, 0, sizeof(CardMemory) * 10); + + ImageList imageList; + R_GetImageList(&imageList); + + std::sort(imageList.image, &imageList.image[imageList.count], + [](GfxImage* image1, GfxImage* image2) -> int + { + if (image1->track <= image2->track) + { + if (image1->track >= image2->track) + return image1->cardMemory.platform[0] < image2->cardMemory.platform[0]; + else + return 1; + } + + return 0; + }); + + Com_Printf(8, "\n-reqrd w*h-"); + Com_Printf(8, "-fmt- "); + + for (int platform = 0; platform < 2; ++platform) + { + Com_Printf(8, "%s", g_platform_name[platform]); + } + + Com_Printf(8, " --name-------\n"); + + for (unsigned int i = 0; i < imageList.count; ++i) + { + GfxImage* image = imageList.image[i]; + Com_Printf(8, "%4i x %-4i ", image->width, image->height); + _D3DFORMAT format = R_ImagePixelFormat(image); + + switch (format) + { + case D3DFMT_X8R8G8B8: + Com_Printf(8, "RGB32 "); + break; + case D3DFMT_A8R8G8B8: + Com_Printf(8, "RGBA32"); + break; + case D3DFMT_R5G6B5: + Com_Printf(8, "RGB565"); + break; + case D3DFMT_A8: + Com_Printf(8, "A8 "); + break; + case D3DFMT_G16R16: + Com_Printf(8, "G16R16"); + break; + case D3DFMT_L8: + Com_Printf(8, "L8 "); + break; + case D3DFMT_A8L8: + Com_Printf(8, "AL16 "); + break; + case D3DFMT_R32F: + Com_Printf(8, "R32F "); + break; + case D3DFMT_DXT1: + Com_Printf(8, "DXT1 "); + break; + case D3DFMT_DXT3: + Com_Printf(8, "DXT3 "); + break; + case D3DFMT_DXT5: + Com_Printf(8, "DXT5 "); + break; + + default: + ASSERT_MSG_VA(0, "unhandled case: %d", format); + } + + Com_Printf(8, " %s", imageTypeName[image->track]); + + for (int platform = 0; platform < 2; ++platform) + { + int cardMemory = image->cardMemory.platform[platform]; + float value = (float)cardMemory / 1024.0f; + + const char* fmt = (value >= 10.0) ? "%7.0fk" : "%7.1fk"; + Com_Printf(8, fmt, value); + + if (!useFastFile->current.enabled) + { + imageTrack[image->track].platform[platform] += cardMemory; + if (Image_IsCodeImage(image->track)) + continue; + } + total.platform[platform] += cardMemory; + } + Com_Printf(8, " %-48s 0x%x\n", image->name, image); + } + + Com_Printf(8, " ---------\n"); + Com_Printf(8, " %i total images\n", imageList.count); + + for (int platform = 0; platform < 2; ++platform) + { + Com_Printf(8, " %5.1f MB %s total non-streamed image size\n", (float)((float)total.platform[platform] / 1048576.0), g_platform_name[platform]); + } + + if (!useFastFile->current.enabled) + { + Com_Printf(8, "\n"); + Com_Printf(8, " "); + for (int platform = 0; platform < 2; ++platform) + { + Com_Printf(8, "%s", g_platform_name[platform]); + } + + Com_Printf(8, "\n"); + + for (int i = 0; i < 10; ++i) + { + Com_Printf(8, "%s:", imageTypeName[i]); + for (int platform = 0; platform < 2; ++platform) + { + Com_Printf(8, " %5.1f", (float)((float)imageTrack[i].platform[platform] / 1048576.0)); + } + + Com_Printf(8, " MB\n"); + } + } + + Com_Printf(8, "Related commands: imagelist, gfx_world, cg_drawfps\n"); +} diff --git a/components/game_mod/r_image.h b/components/game_mod/r_image.h new file mode 100644 index 00000000..18a6098f --- /dev/null +++ b/components/game_mod/r_image.h @@ -0,0 +1,70 @@ +#pragma once + +union GfxTexture +{ + IDirect3DBaseTexture9 *basemap; + IDirect3DTexture9 *map; + IDirect3DVolumeTexture9 *volmap; + IDirect3DCubeTexture9 *cubemap; + void *loadDef;// GfxImageLoadDef +}; + +struct Picmip +{ + char platform[2]; +}; + +struct CardMemory +{ + int platform[2]; +}; + +struct GfxImage +{ + GfxTexture texture; + char mapType; + char semantic; + char category; + bool delayLoadPixels; + Picmip picmip; + bool noPicmip; + char track; + CardMemory cardMemory; + unsigned __int16 width; + unsigned __int16 height; + unsigned __int16 depth; + char levelCount; + char streaming; + unsigned int baseSize; + char *pixels; + unsigned int loadedSize; + char skippedMipLevels; + const char *name; + unsigned int hash; +}; + +enum +{ + MAPTYPE_NONE = 0x0, + MAPTYPE_INVALID1 = 0x1, + MAPTYPE_INVALID2 = 0x2, + MAPTYPE_2D = 0x3, + MAPTYPE_3D = 0x4, + MAPTYPE_CUBE = 0x5, + MAPTYPE_COUNT = 0x6, +}; + +void Image_HandleMissingImage(int code, const char *fmt, const char* image); +unsigned int Image_GetUsage(int imageFlags, D3DFORMAT imageFormat); +void Image_Create2DTexture_PC(GfxImage *image, unsigned __int16 width, unsigned __int16 height, int mipmapCount, int imageFlags, D3DFORMAT imageFormat); +void Image_Create3DTexture_PC(GfxImage *image, unsigned __int16 width, unsigned __int16 height, unsigned __int16 depth, int mipmapCount, int imageFlags, D3DFORMAT imageFormat); +void Image_CreateCubeTexture_PC(GfxImage *image, unsigned __int16 edgeLen, int mipmapCount, D3DFORMAT imageFormat); + +void hk_Image_Create2DTexture_PC(unsigned __int16 width, int mipmapCount, int imageFormat); +void hk_Image_Create3DTexture_PC(int image, int mipmapCount, int imageFormat); +void hk_Image_CreateCubeTexture_PC(signed int mipmapCount, int imageFormat); + +bool __cdecl Image_IsCodeImage(int track); +_D3DFORMAT __cdecl R_ImagePixelFormat(GfxImage *image); + +void __cdecl R_ImageList_f(); diff --git a/components/game_mod/r_init.cpp b/components/game_mod/r_init.cpp new file mode 100644 index 00000000..32536570 --- /dev/null +++ b/components/game_mod/r_init.cpp @@ -0,0 +1,161 @@ +#include "stdafx.h" + +cmd_function_s& R_Cmd_Screenshot_VAR = *(cmd_function_s*)0x046461F0; +cmd_function_s& R_Cmd_ScreenshotJpeg_VAR = *(cmd_function_s*)0x046461D8; +cmd_function_s& R_ImageList_f_VAR = *(cmd_function_s*)0x046461C0; +cmd_function_s& R_Cmd_ApplyPicmip_VAR = *(cmd_function_s*)0x046461A8; +cmd_function_s& R_Cmd_ReloadMaterialTextures_VAR = *(cmd_function_s*)0x04646190; +cmd_function_s& R_Cmd_LoadSun_VAR = *(cmd_function_s*)0x04646178; +cmd_function_s& R_Cmd_SaveSun_VAR = *(cmd_function_s*)0x04646160; +cmd_function_s& R_StaticModelCacheStats_f_VAR = *(cmd_function_s*)0x04646148; +cmd_function_s& R_StaticModelCacheFlush_f_VAR = *(cmd_function_s*)0x04646130; + +cmd_function_s R_FullscreenToggle_f_VAR; +cmd_function_s R_MaterialList_f_VAR; + +const char *R_ErrorDescription(HRESULT hr) +{ + return ((const char *(__cdecl *)(HRESULT))0x0098F062)(hr); +} + +HWND WINAPI hk_CreateWindowExA(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam) +{ + // If this is the main window, set the customized style + if (lpClassName && strstr(lpClassName, "CoDBlackOps")) + { + if (r_noborder->current.enabled) + dwStyle = WS_VISIBLE | WS_POPUP; + } + + return CreateWindowExA(dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam); +} + +BOOL WINAPI hk_AdjustWindowRectEx(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle) +{ + // If windowed mode + if (dwStyle & (WS_CAPTION | WS_SYSMENU)) + { + if (r_noborder->current.enabled) + dwStyle = WS_VISIBLE | WS_POPUP; + } + + return AdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle); +} + +LONG WINAPI hk_SetWindowLongA(HWND hWnd, int nIndex, LONG dwNewLong) +{ + // If windowed mode + if (nIndex == GWL_STYLE && (dwNewLong & (WS_CAPTION | WS_SYSMENU))) + { + if (r_noborder->current.enabled) + dwNewLong = WS_VISIBLE | WS_POPUP; + } + + return SetWindowLongA(hWnd, nIndex, dwNewLong); +} + +void R_FullscreenToggle_f() +{ + Cbuf_AddText(0, "toggle r_fullscreen; vid_restart;"); +} + +// +// Function template for jmping to a specific address if a dvar is enabled +// or doing retn otherwise +// +template +void __declspec(naked) DvarConditionalCallTemplate(void) +{ + static dvar_s* const & dvar = _dvar; + static const DWORD dwJmpTrue = _dwJmpTrue; + + _asm + { + pushad + mov ebx, dvar + mov eax, dword ptr [ebx] + cmp byte ptr[eax + 0x18], 0 + jz CASE_FALSE + + //CASE_TRUE: + popad + jmp dwJmpTrue + + CASE_FALSE: + popad + retn + } +} + +#define ADD_DVARCONDITIONALCALL(_ADDRESS, _JMP_TGT_TRUE, _DVAR) PatchCall(_ADDRESS, ForceCastPointer(DvarConditionalCallTemplate<_JMP_TGT_TRUE, _DVAR >)) + +void R_InitRenderTweaks(void) +{ + if (r_renderTweaks == NULL || !r_renderTweaks->current.enabled) + return; + /* + Add Toggles for various render functions + A (semi) fix for reflection HDR issues is to disable: + r_renderLit // Disable (water, etc.) materials + r_renderStandardPostFx // Disable the screenspace reflection buffer + r_renderDistortion // Disable the screenspace reflection buffer + */ + ADD_DVARCONDITIONALCALL(0x006D27E2, 0x00737ED0, r_renderLit); // This tends to hide water materials + ADD_DVARCONDITIONALCALL(0x006D2C94, 0x006D1D60, r_renderStandardPostFx); + ADD_DVARCONDITIONALCALL(0x006D27BA, 0x006D1CF0, r_renderDistortion); // R_ResolveDistortion + ADD_DVARCONDITIONALCALL(0x006D29FC, 0x006D1CF0, r_renderDistortion); // R_ResolveDistortion + ADD_DVARCONDITIONALCALL(0x006D2A0D, 0x006D1A80, r_renderEmissive); + ADD_DVARCONDITIONALCALL(0x006D28FC, 0x006E3350, r_renderCorona); + ADD_DVARCONDITIONALCALL(0x006D290C, 0x00722EF0, r_renderSuperflare); + ADD_DVARCONDITIONALCALL(0x006D28D6, 0x00723E90, r_renderSun); + ADD_DVARCONDITIONALCALL(0x006D27D1, 0x006D1C60, r_renderReflected); + ADD_DVARCONDITIONALCALL(0x006D2C5E, 0x00737720, r_renderCloakHDR); +} + +void __cdecl R_RegisterCmds() +{ + Cmd_AddCommandInternal("screenshot", R_Cmd_Screenshot, &R_Cmd_Screenshot_VAR); + Cmd_AddCommandInternal("screenshotJpeg", R_Cmd_ScreenshotJpeg, &R_Cmd_ScreenshotJpeg_VAR); + Cmd_AddCommandInternal("imagelist", R_ImageList_f, &R_ImageList_f_VAR); // custom + Cmd_AddCommandInternal("r_applyPicmip", R_Cmd_ApplyPicmip, &R_Cmd_ApplyPicmip_VAR); + Cmd_AddCommandInternal("reloadmaterialtextures", R_Cmd_ReloadMaterialTextures, &R_Cmd_ReloadMaterialTextures_VAR); + Cmd_AddCommandInternal("r_loadsun", R_Cmd_LoadSun, &R_Cmd_LoadSun_VAR); + Cmd_AddCommandInternal("r_savesun", R_Cmd_SaveSun, &R_Cmd_SaveSun_VAR); + Cmd_AddCommandInternal("r_smc_stats", R_StaticModelCacheStats_f, &R_StaticModelCacheStats_f_VAR); + Cmd_AddCommandInternal("r_smc_flush", R_StaticModelCacheFlush_f, &R_StaticModelCacheFlush_f_VAR); + + Cmd_AddCommandInternal("r_fullscreen_toggle", R_FullscreenToggle_f, &R_FullscreenToggle_f_VAR); + + Cmd_AddCommandInternal("gfx_world", R_MaterialList_f, &R_MaterialList_f_VAR); // custom + + R_InitRenderTweaks(); +} + +void __cdecl R_UnregisterCmds() +{ + Cmd_RemoveCommand("screenshot"); + Cmd_RemoveCommand("screenshotJpeg"); + Cmd_RemoveCommand("imagelist"); + Cmd_RemoveCommand("r_applyPicmip"); + Cmd_RemoveCommand("reloadmaterialtextures"); + Cmd_RemoveCommand("r_loadsun"); + Cmd_RemoveCommand("r_savesun"); + Cmd_RemoveCommand("r_smc_stats"); + Cmd_RemoveCommand("r_smc_flush"); + + Cmd_RemoveCommand("r_fullscreen_toggle"); + + Cmd_RemoveCommand("gfx_world"); +} + +struct Font_s *__cdecl R_RegisterFont(const char *name, int imageTrack) +{ + typedef Font_s *(__cdecl *R_RegisterFont_t)(const char *, int); + R_RegisterFont_t func; + + if (useFastFile->current.enabled) + func = (R_RegisterFont_t)0x006D30B0; // R_RegisterFont_FastFile; + else + func = (R_RegisterFont_t)0x006D3010; // R_RegisterFont_LoadObj; + return func(name, imageTrack); +} diff --git a/components/game_mod/r_init.h b/components/game_mod/r_init.h new file mode 100644 index 00000000..1057158e --- /dev/null +++ b/components/game_mod/r_init.h @@ -0,0 +1,26 @@ +#pragma once + +static IDirect3DDevice9*& dx_device = *(IDirect3DDevice9 **)0x03963448; +static int g_disableRendering; +static bool& r_supportCubedMipMaps = *(bool *)0x0396A4F7; + +typedef void(__cdecl *cmd_function)(); +static cmd_function R_Cmd_Screenshot = (cmd_function)0x007244D0; +static cmd_function R_Cmd_ScreenshotJpeg = (cmd_function)0x007244E0; +static cmd_function R_Cmd_ApplyPicmip = (cmd_function)0x007244C0; +static cmd_function R_Cmd_ReloadMaterialTextures = (cmd_function)0x006D4420; +static cmd_function R_Cmd_LoadSun = (cmd_function)0x00735DA0; +static cmd_function R_Cmd_SaveSun = (cmd_function)0x00735E90; +static cmd_function R_StaticModelCacheStats_f = (cmd_function)0x0070C240; +static cmd_function R_StaticModelCacheFlush_f = (cmd_function)0x0070C410; + +const char *R_ErrorDescription(HRESULT hr); + +HWND WINAPI hk_CreateWindowExA(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam); +BOOL WINAPI hk_AdjustWindowRectEx(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle); +LONG WINAPI hk_SetWindowLongA(HWND hWnd, int nIndex, LONG dwNewLong); + +void __cdecl R_RegisterCmds(); +void __cdecl R_UnregisterCmds(); + +struct Font_s *__cdecl R_RegisterFont(const char *name, int imageTrack); diff --git a/components/game_mod/r_material.cpp b/components/game_mod/r_material.cpp new file mode 100644 index 00000000..751463c9 --- /dev/null +++ b/components/game_mod/r_material.cpp @@ -0,0 +1,99 @@ +#include "stdafx.h" +#include + +struct MaterialMemory +{ + Material *material; + int memory; +}; + +struct MaterialList +{ + unsigned int count; + MaterialMemory sorted[4096*2]; // Ensure that there are enough entries for the increased limit +}; + +int R_GetMaterialMemory(Material* material) +{ + BYTE* world = *(BYTE**)0x0396A24C; // rgp.world + + int materialMemoryCount = *(int*)(world + 0x274); + MaterialMemory* materialMemory = *(MaterialMemory**)(world + 0x278); + + if (world) + { + for (int i = 0; i < *(int*)(world + 0x274); i++) + { + if (material == materialMemory[i].material) + return materialMemory[i].memory; + } + } + + return 0; +} + +void __cdecl R_GetMaterialList(XAssetHeader header, MaterialList *materialList) +{ + int memory = R_GetMaterialMemory(header.material); + if (memory) + { + ASSERT(materialList->count < _countof(materialList->sorted)); + MaterialMemory *materialMemory = &materialList->sorted[materialList->count]; + materialMemory->material = header.material; + materialMemory->memory = memory; + ++materialList->count; + } +} + +void __cdecl R_MaterialList_f() +{ + unsigned int totalMemUsage = 0; + + Com_Printf(8, "-----------------------\n"); + + MaterialList materialList; + materialList.count = 0; + DB_EnumXAssets(ASSET_TYPE_MATERIAL, (void(__cdecl *)(XAssetHeader, void *))R_GetMaterialList, &materialList, false); + + std::sort(materialList.sorted, &materialList.sorted[materialList.count], + [](MaterialMemory& material0, MaterialMemory& material1) -> bool + { + return material0.memory < material1.memory; + }); + + Com_Printf(8, "geo KB name\n"); + for (unsigned int mtlIndex = 0; mtlIndex < materialList.count; mtlIndex++) + { + MaterialMemory* materialMemory = &materialList.sorted[mtlIndex]; + Material* material = materialList.sorted[mtlIndex].material; + ASSERT(material); + + totalMemUsage += materialMemory->memory; + + float value = (float)materialMemory->memory / 1024.0f; + const char* fmt = (value >= 10.0f) ? "%6.0f" : "%6.1f"; + Com_Printf(8, fmt, value); + Com_Printf(8, " %s\n", material->info.name); + } + + Com_Printf(8, "-----------------------\n"); + Com_Printf(8, "current total %5.1f MB\n", (float)totalMemUsage / (1024.f * 1024.f)); + Com_Printf(8, "%i total geometry materials\n", materialList.count); + Com_Printf(8, "Related commands: imagelist, gfx_world, cg_drawfps\n"); +} + +MaterialTechniqueSet *__cdecl Material_GetTechniqueSet(Material *material) +{ + ASSERT(material); + ASSERT_MSG_VA(material->localTechniqueSet, "material '%s' missing techset. If you are building fastfile, check Launcher for error messages.", material->info.name); + + return material->localTechniqueSet; +} + +MaterialTechnique *__cdecl Material_GetTechnique(Material *material, char techType) +{ + MaterialTechniqueSet* techSet = techSet = Material_GetTechniqueSet(material); + ASSERT_MSG_VA(techSet, "material '%s' missing techset. %d tech %d", material->info.name, material->localTechniqueSet != 0, techType); + + return techSet->techniques[techType]; +} diff --git a/components/game_mod/r_material.h b/components/game_mod/r_material.h new file mode 100644 index 00000000..01b67cfd --- /dev/null +++ b/components/game_mod/r_material.h @@ -0,0 +1,231 @@ +#pragma once +#include "r_image.h" + +struct GfxDrawSurfFields +{ + __int64 _bf0; +}; + +union GfxDrawSurf +{ + GfxDrawSurfFields fields; + unsigned __int64 packed; +}; + +struct GfxStateBits +{ + unsigned int loadBits[2]; +}; + +struct MaterialInfo +{ + const char *name; + unsigned int gameFlags; + char pad; + char sortKey; + char textureAtlasRowCount; + char textureAtlasColumnCount; + GfxDrawSurf drawSurf; + unsigned int surfaceTypeBits; + unsigned int layeredSurfaceTypes; + unsigned __int16 hashIndex; +}; + +struct MaterialStreamRouting +{ + char source; + char dest; +}; + +struct MaterialVertexStreamRouting +{ + MaterialStreamRouting data[16]; + IDirect3DVertexDeclaration9 *decl[18]; +}; + +struct MaterialVertexDeclaration +{ + char streamCount; + bool hasOptionalSource; + bool isLoaded; + MaterialVertexStreamRouting routing; +}; + +enum MaterialVertexDeclType +{ + VERTDECL_GENERIC = 0x0, + VERTDECL_PACKED = 0x1, + VERTDECL_WORLD = 0x2, + VERTDECL_WORLD_T1N0 = 0x3, + VERTDECL_WORLD_T1N1 = 0x4, + VERTDECL_WORLD_T2N0 = 0x5, + VERTDECL_WORLD_T2N1 = 0x6, + VERTDECL_WORLD_T2N2 = 0x7, + VERTDECL_WORLD_T3N0 = 0x8, + VERTDECL_WORLD_T3N1 = 0x9, + VERTDECL_WORLD_T3N2 = 0xA, + VERTDECL_WORLD_T4N0 = 0xB, + VERTDECL_WORLD_T4N1 = 0xC, + VERTDECL_WORLD_T4N2 = 0xD, + VERTDECL_POS_TEX = 0xE, + VERTDECL_STATICMODELCACHE = 0xF, + VERTDECL_WATER = 0x10, + VERTDECL_PARTICLECLOUD = 0x11, + VERTDECL_COUNT = 0x12, +}; + +struct GfxVertexShaderLoadDef +{ + unsigned int *program; + unsigned __int16 programSize; +}; + +struct MaterialVertexShaderProgram +{ + IDirect3DVertexShader9 *vs; + GfxVertexShaderLoadDef loadDef; +}; + +struct MaterialVertexShader +{ + const char *name; + MaterialVertexShaderProgram prog; +}; + +struct GfxPixelShaderLoadDef +{ + unsigned int *program; + unsigned __int16 programSize; +}; + +struct MaterialPixelShaderProgram +{ + IDirect3DPixelShader9 *ps; + GfxPixelShaderLoadDef loadDef; +}; + +struct MaterialPixelShader +{ + const char *name; + MaterialPixelShaderProgram prog; +}; + +struct MaterialArgumentCodeConst +{ + unsigned __int16 index; + char firstRow; + char rowCount; +}; + +union MaterialArgumentDef +{ + const float *literalConst; + MaterialArgumentCodeConst codeConst; + unsigned int codeSampler; + unsigned int nameHash; +}; + +struct MaterialShaderArgument +{ + unsigned __int16 type; + unsigned __int16 dest; + MaterialArgumentDef u; +}; + +struct MaterialPass +{ + MaterialVertexDeclaration *vertexDecl; + MaterialVertexShader *vertexShader; + + union + { + MaterialPixelShader *pixelShader; + MaterialPixelShader *localPixelShader; + }; + + char perPrimArgCount; + char perObjArgCount; + char stableArgCount; + char customSamplerFlags; + + union + { + MaterialShaderArgument *localArgs; + MaterialShaderArgument *args; + }; +}; + +struct MaterialTechnique +{ + const char *name; + unsigned __int16 flags; + unsigned __int16 passCount; + MaterialPass passArray[1]; +}; + +struct MaterialTechniqueSet +{ + const char *name; + char worldVertFormat; + char unused[1]; + unsigned __int16 techsetFlags; + MaterialTechnique *techniques[130]; +}; + +struct MaterialConstantDef +{ + unsigned int nameHash; + char name[12]; + float _literal[4]; +}; + +union MaterialTextureDefInfo +{ + GfxImage *image; + //water_t *water; +}; + +struct MaterialTextureDef +{ + unsigned int nameHash; + char nameStart; + char nameEnd; + char samplerState; + char semantic; + char isMatureContent; + char pad[3]; + MaterialTextureDefInfo u; +}; + +struct Material +{ + MaterialInfo info; + char stateBitsEntry[130]; + char textureCount; + char constantCount; + char stateBitsCount; + char stateFlags; + char cameraRegion; + char maxStreamedMips; + + union + { + MaterialTechniqueSet *localTechniqueSet; + MaterialTechniqueSet *techniqueSet; + }; + + MaterialTextureDef *textureTable; + + union + { + MaterialConstantDef *localConstantTable; + MaterialConstantDef *constantTable; + }; + + GfxStateBits *stateBitsTable; +}; + +void __cdecl R_MaterialList_f(); + +MaterialTechniqueSet *__cdecl Material_GetTechniqueSet(Material *material); +MaterialTechnique *__cdecl Material_GetTechnique(Material *material, char techType); diff --git a/components/game_mod/r_material_load_obj.cpp b/components/game_mod/r_material_load_obj.cpp index 90e7592a..f0fd4b6b 100644 --- a/components/game_mod/r_material_load_obj.cpp +++ b/components/game_mod/r_material_load_obj.cpp @@ -6,7 +6,7 @@ bool Material_CopyTextToDXBuffer(void *cachedShader, unsigned int shaderLen, LPD if (!SUCCEEDED(hr)) { - Com_PrintError(8, "ERROR: Material_CopyTextToDXBuffer: D3DXCreateBuffer(%d) failed: %s (0x%08x)\n", shaderLen, /*R_ErrorDescription(hr)*/"HRESULT", hr); + Com_PrintError(8, "ERROR: Material_CopyTextToDXBuffer: D3DXCreateBuffer(%d) failed: %s (0x%08x)\n", shaderLen, R_ErrorDescription(hr), hr); free(cachedShader); return false; @@ -18,9 +18,7 @@ bool Material_CopyTextToDXBuffer(void *cachedShader, unsigned int shaderLen, LPD FILE *Material_OpenShader_BlackOps(const char *shaderTarget, const char *shaderName) { - // // Determine if this was a vertex shader or pixel shader - // const char *shaderMain; if (shaderTarget[0] == 'v' && shaderTarget[1] == 's') @@ -28,23 +26,19 @@ FILE *Material_OpenShader_BlackOps(const char *shaderTarget, const char *shaderN else shaderMain = "ps"; - // // Load the shader directly from the name - // char shaderPath[MAX_PATH]; sprintf_s(shaderPath, "%s\\raw\\shadercache_mods\\%s_%s_3_0_%s", - *(char **)(*(DWORD *)0x25FBF04 + 0x18), + fs_basepath->current.string, shaderTarget, shaderMain, shaderName); - FILE* h = NULL; - if (fopen_s(&h, shaderPath, "rb") != 0) - { - return NULL; - } + FILE *handle; + if (fopen_s(&handle, shaderPath, "rb") != 0) + return nullptr; - return h; + return handle; } ID3DXBuffer *Material_CompileShader(const char *shaderName, int shaderType, const char *entryPoint, const char *target) @@ -71,7 +65,10 @@ ID3DXBuffer *Material_CompileShader(const char *shaderName, int shaderType, cons ID3DXBuffer *shader = nullptr; if (!Material_CopyTextToDXBuffer(shaderMemory, shaderDataSize, &shader)) - ASSERT_MSG(false, "SHADER UPLOAD FAILED\n"); + { + // Error message was already printed in above function + ASSERT_MSG(false, "Shader upload failed"); + } fclose(shaderFile); free(shaderMemory); @@ -85,10 +82,10 @@ void __declspec(naked) hk_Material_CompileShader() push ebp mov ebp, esp - push[ebp + 0xC] // target - push[ebp + 0x8] // entryPoint - push[ebp + 0x4] // shaderType - push ecx // shaderName + push[ebp + 0xC] // a4: target + push[ebp + 0x8] // a3: entryPoint + push[ebp + 0x4] // a2: shaderType + push ecx // a1: shaderName call Material_CompileShader add esp, 0x10 diff --git a/components/game_mod/r_reflection_probe.cpp b/components/game_mod/r_reflection_probe.cpp new file mode 100644 index 00000000..1979e8bd --- /dev/null +++ b/components/game_mod/r_reflection_probe.cpp @@ -0,0 +1,298 @@ +#include "stdafx.h" + +// Declarations in patch_reflections.cpp +char* formatTime(int seconds); +extern bool g_reflectionsUpdated; +extern char g_ffDir[MAX_PATH]; +extern int g_probeCount; + +VANILLA_VALUE(lockPvsViewParms, GfxViewParms, 0x0396F730); + +VANILLA_VALUE(s_cmdList, GfxCmdArray*, 0x03B370C0); +VANILLA_VALUE(frontEndDataOut, GfxBackEndData*, 0x03B3708C); + +void __declspec(naked) R_SetViewParmsForScene(refdef_s *refdef, GfxViewParms *viewParms) +{ + _asm + { + pushad + mov eax, 0x006C7F80 + mov edi, viewParms + mov esi, refdef + call eax + popad + retn + } +} + +void R_CalcCubeMapViewValues(refdef_s *refdef, CubemapShot cubemapShot, int cubemapSize) +{ + ((void(__cdecl *)(refdef_s *, CubemapShot, int))0x006CEC80)(refdef, cubemapShot, cubemapSize); +} + +inline void R_BeginSharedCmdList(void) +{ + frontEndDataOut->cmds = &s_cmdList->cmds[s_cmdList->usedTotal]; +} + +inline void R_ClearClientCmdList2D(void) +{ + frontEndDataOut->viewInfo[frontEndDataOut->viewInfoCount].cmds = NULL; +} + +inline void R_SetLodOrigin(refdef_s *refdef) +{ + if (r_lockPvs->modified) + { + Dvar_ClearModified(r_lockPvs); + R_SetViewParmsForScene(refdef, &lockPvsViewParms); + } + + float* vec = (float*)0x3AC3060; + vec[0] = refdef->vieworg[0]; + vec[1] = refdef->vieworg[1]; + vec[2] = refdef->vieworg[2]; + + int* unk1 = (int*)0x3AC3058; *unk1 = refdef->time; + float* unk2 = (float*)0x3AC305C; *unk2 = refdef->time * 0.001f; + + R_UpdateFrameFog(refdef->localClientNum); + R_UpdateFrameSun(); +} + +inline void R_UpdateSpotLightEffect(FxCmd *cmd) +{ + Sys_ResetUpdateSpotLightEffectEvent(); + Sys_AddWorkerCmdInternal((void*)0x00BA5420, cmd, 0); +} + +inline void R_UpdateNonDependentEffects(FxCmd *cmd) +{ + Sys_ResetUpdateNonDependentEffectsEvent(); + Sys_AddWorkerCmdInternal((void*)0x00BA52E0, cmd, 0); +} + +inline void R_UpdateRemainingEffects(FxCmd *cmd) +{ + Sys_AddWorkerCmdInternal((void*)0x00BA5300, cmd, 0); + Sys_AddWorkerCmdInternal((void*)0x00BA5330, cmd, 0); +} + +void R_GenerateReflectionRawData(DiskGfxReflectionProbe* probeRawData) +{ + if (!*g_ffDir) + sprintf_s(g_ffDir, "%s/%s/", Dvar_GetString("fs_basepath"), Dvar_GetString("fs_game")); + + refdef_s refdef; + char* ptr = *((char**)0x02FF5354); + refdef_s* refsrc = (refdef_s*)(ptr + 0x8C100); + memcpy(&refdef, refsrc, sizeof(refdef_s)); + + refdef.vieworg[1] = probeRawData->origin[0]; + refdef.vieworg[2] = probeRawData->origin[1]; + refdef.yaw = probeRawData->origin[2]; + + R_InitPrimaryLights(refdef.primaryLights[0].dir); + + FxCmd cmd; + FxCameraUpdate fxCam; + + for (int cubemapShot = 1; cubemapShot < 7; cubemapShot++) + { + R_BeginCubemapShot(256, 0); + R_BeginFrame(); + R_BeginSharedCmdList(); + R_ClearClientCmdList2D(); + + R_ClearScene(0); + + FX_BeginUpdate(0); + R_CalcCubeMapViewValues(&refdef, cubemapShot, 256); + + R_SetLodOrigin(&refdef); + + R_UpdateFrameFog(refdef.localClientNum); + R_UpdateFrameSun(); + + float zFar = R_GetFarPlaneDist(); + + // Not entirely sure if this is needed or not... + //memset(&fxCam, 0, sizeof(FxCameraUpdate)); + //memset(&cmd, 0, sizeof(FxCmd)); + + FX_GetCameraUpdateFromRefdefAndZFar(&fxCam, &refdef, zFar); + FX_SetNextUpdateCamera(0, &fxCam); + FX_FillUpdateCmd(0, &cmd); + + R_UpdateSpotLightEffect(&cmd); + R_UpdateNonDependentEffects(&cmd); + R_UpdateRemainingEffects(&cmd); + + R_RenderScene(&refdef, 0); + R_EndFrame(); + + R_IssueRenderCommands(3); + R_EndCubemapShot(cubemapShot); + } + + R_CreateReflectionRawDataFromCubemapShot(probeRawData); +} + +void R_GenerateReflectionRawDataAll(DiskGfxReflectionProbe *probeRawData, int probeCount, bool *generateProbe) +{ + Com_ToolPrintf(0, "----------------------------------------\n"); + Com_ToolPrintf(0, "Generating reflections...\n"); + fflush(stdout); + + g_probeCount = probeCount; + + time_t initTime; + time_t currTime; + time(&initTime); + + for (int probeIndex = 0; probeIndex < probeCount; probeIndex++) + { + if (generateProbe[probeIndex]) + R_GenerateReflectionRawData(&probeRawData[probeIndex]); + + time(&currTime); + float percentComplete = (float)(probeIndex + 1) / (float)probeCount; + float elapsedTime = (float)difftime(currTime, initTime); + float remainingTime = elapsedTime / percentComplete - elapsedTime; + + Com_ToolPrintf(0, "%.1f%% (%d of %d) complete, %s done, %s remaining\n", + percentComplete * 100.0f, + probeIndex + 1, + probeCount, + formatTime((int)elapsedTime), + formatTime((int)remainingTime)); + fflush(stdout); + } + + Com_ToolPrintf(0, "Finished in %s.\n", formatTime((int)difftime(currTime, initTime))); + printf("----------------------------------------\n"); +} + +bool R_CopyReflectionsFromLumpData(DiskGfxReflectionProbe *probeRawData, DiskGfxReflectionProbe *probeRawLumpData, const int lumpProbeCount) +{ + if (lumpProbeCount <= 0) + return false; + + // TODO: Figure out what the LAHF instructions are + return false; +} + +void R_GenerateReflections(const char *mapname, GfxReflectionProbe *probes, const unsigned int probeCount) +{ + //return; + + ASSERT(r_reflectionProbeGenerate); + ASSERT(probeCount < MAX_MAP_REFLECTION_PROBES); + + // Skip this function if the user didn't explicitly set dvar + if (!r_reflectionProbeGenerate->current.enabled) + return; + + // Version checking + Com_LoadBsp(mapname); + unsigned int version = Com_GetBspVersion(); + + if (version != 45) + { + Com_Error( + ERR_DROP, + "You can only generate reflections for BSP version %i, but the BSP is version %i. You need to recompile the map.", + 45, + version); + return; + } + + unsigned int lumpSize = 0; + unsigned int lumpProbeCount = 0; + DiskGfxReflectionProbe *probeRawLumpData = nullptr; + DiskGfxReflectionProbe *probeRawGeneratedData = nullptr; + + probeRawLumpData = (DiskGfxReflectionProbe *)Com_GetBspLump(LUMP_REFLECTION_PROBES, sizeof(DiskGfxReflectionProbe), &lumpProbeCount); + + ASSERT(probeCount == lumpProbeCount); + + // Generate images for each probe in the BSP + if (probeCount) + { + bool generateProbe[MAX_MAP_REFLECTION_PROBES]; + memset(generateProbe, 0, sizeof(generateProbe)); + + lumpSize = sizeof(DiskGfxReflectionProbe) * probeCount; + probeRawGeneratedData = (DiskGfxReflectionProbe *)Z_Malloc(lumpSize, "R_GenerateReflections", 0); + + for (unsigned int probeIndex = 0; probeIndex < lumpProbeCount; ++probeIndex) + { + strcpy_s(probeRawGeneratedData[probeIndex].colorCorrectionFilename, probeRawLumpData[probeIndex].colorCorrectionFilename); + + probeRawGeneratedData[probeIndex].origin[0] = probes[probeIndex].origin[0]; + probeRawGeneratedData[probeIndex].origin[1] = probes[probeIndex].origin[1]; + probeRawGeneratedData[probeIndex].origin[2] = probes[probeIndex].origin[2]; + + if (r_reflectionProbeRegenerateAll->current.enabled) + { + // Generate every single probe, ignoring anything in the BSP + generateProbe[probeIndex] = true; + } + else + { + // See if we can copy old reflection data from the map + generateProbe[probeIndex] = !R_CopyReflectionsFromLumpData(&probeRawGeneratedData[probeIndex], probeRawLumpData, probeCount); + } + } + + R_GenerateReflectionRawDataAll(probeRawGeneratedData, lumpProbeCount, generateProbe); + + Com_SaveLump(LUMP_REFLECTION_PROBES, probeRawGeneratedData, lumpSize, COM_SAVE_LUMP_AND_CLOSE); + Com_ToolPrintf(0, "Reflections saved with lump size %u\n", lumpSize); + + R_GenerateReflectionImages(probes, probeRawGeneratedData, probeCount, 0); + Z_Free(probeRawGeneratedData, 0); + } + else + { + Com_SaveLump(LUMP_REFLECTION_PROBES, nullptr, 0, COM_SAVE_LUMP_AND_CLOSE); + Com_ToolPrintf(0, "Reflections saved with empty lump data\n"); + } + + // Custom tracker for injected reflections + g_reflectionsUpdated = true; + + // Terminate the game if there's nothing left to do + if (R_ReflectionProbeGenerateExitWhenDone()) + { + Sys_NormalExit(); + exit(0); + } + + // Allow viewing the map after images are generated + Dvar_SetBool(r_reflectionProbeGenerate, false); +} + +bool R_ReflectionProbeGenerateExitWhenDone() +{ + return r_reflectionProbeGenerate && r_reflectionProbeGenerate->current.enabled && + r_reflectionProbeGenerateExit && r_reflectionProbeGenerateExit->current.enabled; +} + +void R_GenerateReflectionImages(GfxReflectionProbe *probes, DiskGfxReflectionProbe *probeRawData, const int probeCount, const int probeBaseIndex) +{ + ((void(__cdecl *)(GfxReflectionProbe *, DiskGfxReflectionProbe *, const int, const int))0x006CF810)(probes, probeRawData, probeCount, probeBaseIndex); +} + +void __declspec(naked) hk_R_GenerateReflections() +{ + __asm + { + push[esp + 8] // probeCount + push[esp + 8] // probes + push ecx // mapname + call R_GenerateReflections + add esp, 0xC + retn + } +} \ No newline at end of file diff --git a/components/game_mod/r_reflection_probe.h b/components/game_mod/r_reflection_probe.h index 6a301605..b0f75bea 100644 --- a/components/game_mod/r_reflection_probe.h +++ b/components/game_mod/r_reflection_probe.h @@ -1,327 +1,7 @@ #pragma once +#include "r_scene.h" -struct GfxFilm -{ - float filmMidStart; - float filmMidEnd; - float filmDarkFeather; - float filmLightFeather; - float filmBleach[3]; - float filmColorTemp[3]; - float filmHue[3]; - float filmSaturation[3]; - float filmDarkTint[3]; - float filmMidTint[3]; - float filmLightTint[3]; - float filmContrast[3]; - bool enabled; - float filmLut; - float sCurveEnable; - float sCurveShoulderStrength; - float sCurveLinearStrength; - float sCurveLinearAngle; - float sCurveToeStrength; - float sCurveToeNumerator; - float sCurveToeDenominator; -}; - -struct GfxBloom -{ - float bloomTintWeights[4]; - float bloomColorScale[4]; - float bloomTintScale[4]; - float bloomCurveBreakpoint[4]; - float bloomCurveLoBlack[4]; - float bloomCurveLoGamma[4]; - float bloomCurveLoWhite[4]; - float bloomCurveLoRemapBlack[4]; - float bloomCurveLoRemapWhite[4]; - float bloomCurveHiBlack[4]; - float bloomCurveHiGamma[4]; - float bloomCurveHiWhite[4]; - float bloomCurveHiRemapBlack[4]; - float bloomCurveHiRemapWhite[4]; - float bloomExpansionControl[4]; - float bloomExpansionWeights[4]; - int bloomExpansionSource; - float bloomBlurRadius; - float bloomPersistence; - float bloomStreakXLevels0[4]; - float bloomStreakXLevels1[4]; - float bloomStreakXInnerTint[3]; - float bloomStreakXOuterTint[3]; - float bloomStreakXTintControl[4]; - float bloomStreakXTint[3]; - float bloomStreakYLevels0[4]; - float bloomStreakYLevels1[4]; - float bloomStreakYInnerTint[3]; - float bloomStreakYOuterTint[3]; - float bloomStreakYTintControl[4]; - float bloomStreakYTint[3]; -}; - -struct GfxReviveFx -{ - bool enabled; - float reviveEdgeColorTemp; - float reviveEdgeSaturation; - float reviveEdgeScale[3]; - float reviveEdgeContrast[3]; - float reviveEdgeOffset[3]; - float reviveEdgeMaskAdjust; - float reviveEdgeAmount; -}; - -struct GfxLightScale -{ - float diffuseScale; - float specularScale; -}; - -struct GfxVisionSet -{ - GfxFilm film; - GfxBloom bloom; - GfxReviveFx reviveFx; - GfxLightScale charPrimaryLightScale; -}; - -struct GfxDepthOfField -{ - float viewModelStart; - float viewModelEnd; - float nearStart; - float nearEnd; - float farStart; - float farEnd; - float nearBlur; - float farBlur; -}; - -struct GfxDoubleVision -{ - float direction[3]; - float motionBlurMagnitude; - float deltaPerMS; - float cur; - float targ; -}; - -struct GfxGenericFilter -{ - bool passEnabled[3][16]; - void *passMaterial[3][16]; //Material* - int passTarget[3][16]; - int passSampler0[3][16]; - int passSampler1[3][16]; - float passParam[3][16][16]; - int passQuads[3][16]; - float passFlareOcclusion[3][16]; - float sunPosition[3]; -}; - -struct GfxPoison -{ - float curAmountTarget; - float curAmount; -}; - -struct GfxCompositeFx -{ - float distortionScale[2]; - float blurRadius; - float distortionMagnitude; - float frameRate; - int lastUpdate; - int frame; - int startMSec; - int currentTime; - int duration; - bool enabled; - bool scriptEnabled; -}; - -struct GfxSaveScreenParam -{ - float s0; - float t0; - float ds; - float dt; - int screenTimerId; - char mode; -}; - -struct GfxBlendSaveScreenBlurredParam -{ - int fadeMsec; - float s0; - float t0; - float ds; - float dt; - int screenTimerId; - char enabled; -}; - -struct GfxBlendSaveScreenFlashedParam -{ - float intensityWhiteout; - float intensityScreengrab; - float s0; - float t0; - float ds; - float dt; - int screenTimerId; - char enabled; -}; - -struct GfxSaveScreenFx -{ - GfxSaveScreenParam saveScreenParam; - GfxBlendSaveScreenBlurredParam blendBlurredParam; - GfxBlendSaveScreenFlashedParam blendFlashedParam; -}; - -struct float44 -{ - union - { - float m[4][4]; - float member[16]; - }; -}; - -struct GfxLightImage -{ - void *image; - char samplerState; -}; - -struct GfxLightDef -{ - const char *name; - GfxLightImage attenuation; - int lmapLookupStart; -}; - -struct __declspec(align(16)) GfxLight -{ - char type; - char canUseShadowMap; - __int16 cullDist; - float color[3]; - float dir[3]; - float origin[3]; - float radius; - float cosHalfFovOuter; - float cosHalfFovInner; - int exponent; - unsigned int spotShadowIndex; - float angles[3]; - float spotShadowHiDistance; - float diffuseColor[4]; - float specularColor[4]; - float shadowColor[4]; - float falloff[4]; - float attenuation[4]; - float aAbB[4]; - float cookieControl0[4]; - float cookieControl1[4]; - float cookieControl2[4]; - __declspec(align(8)) float44 viewMatrix; - float44 projMatrix; - GfxLightDef *def; -}; - -struct GfxViewport -{ - int x; - int y; - int width; - int height; -}; - -struct GfxExposureValue -{ - float blackPoint[4]; - float whitePoint[4]; - float linearStart[4]; - float linearEnd[4]; - float remapStart[4]; - float remapEnd[4]; - float scurveStart[4]; - float scurveEnd[4]; - float bloomCurveLDR[4]; - float bloomCurveHDR[4]; - float bloomScale[4]; -}; - -struct WaterFogDef -{ - int startTime; - int finishTime; - float color[4]; - float fogStart; - float density; - float heightDensity; - float baseHeight; - float sunFogColor[4]; - float sunFogDir[3]; - float sunFogStartAng; - float sunFogEndAng; -}; - -struct __declspec(align(16)) refdef_s -{ - unsigned int x; - unsigned int y; - unsigned int width; - unsigned int height; - float tanHalfFovX; - float tanHalfFovY; - float fov_x; - float vieworg[3]; - float yaw; - float viewaxis[3][3]; - int time; - float zNear; - float zFar; - GfxVisionSet visionset; - float blurRadius; - GfxDepthOfField dof; - GfxDoubleVision doubleVision; - GfxCompositeFx flameFx; - GfxCompositeFx waterSheetingFx; - GfxGenericFilter genericFilter; - GfxPoison poisonFx; - GfxCompositeFx electrifiedFx; - GfxCompositeFx transportedFx; - GfxSaveScreenFx saveScreenFx; - float sunVisibility; - GfxLight primaryLights[255]; - GfxViewport scissorViewport; - bool useScissorViewport; - int localClientNum; - int hideMatureContent; - int splitscreen; - int playerTeleported; - int oldExposureId; - int newExposureId; - GfxExposureValue exposureValue; - float lerpcount; - int lastTime; - unsigned int exposureMode; - float exposure; - float postEmissiveBrightening; - bool noLodCullOut; - WaterFogDef waterFog; - float extraCamPos[3]; - bool extraCamPosValid; - bool extraCamLargeFrame; - float preExtraCamVieworg[3]; - float preExtraCamViewaxis[3][3]; - float preExtraCamTanHalfFovX; - float preExtraCamTanHalfFovY; -}; +#define MAX_MAP_REFLECTION_PROBES 64 typedef int CubemapShot; @@ -358,9 +38,33 @@ struct DiskGfxReflectionProbe char colorCorrectionFilename[64]; char name[64]; }; +static_assert(sizeof(DiskGfxReflectionProbe) == 0x406E0, "Size mismatch"); + +struct GfxReflectionProbeVolumeData +{ + float volumePlanes[6][4]; +}; + +struct GfxReflectionProbe +{ + float origin[3]; + GfxImage *reflectionImage; + GfxReflectionProbeVolumeData *probeVolumes; + unsigned int probeVolumeCount; +}; +static_assert(sizeof(GfxReflectionProbe) == 0x18, "Size mismatch"); + +typedef void (__cdecl* R_CubemapShotRestoreState_t)(void); +static R_CubemapShotRestoreState_t R_CubemapShotRestoreState = (R_CubemapShotRestoreState_t)0x007087D0; + +void R_SetViewParmsForScene(refdef_s *refdef, GfxViewParms *viewParms); -typedef void __cdecl R_CalcCubeMapViewValues_t(refdef_s *refdef, CubemapShot cubemapShot, int cubemapSize); -static R_CalcCubeMapViewValues_t* R_CalcCubeMapViewValues = (R_CalcCubeMapViewValues_t*)0x006CEC80; +void R_CalcCubeMapViewValues(refdef_s *refdef, CubemapShot cubemapShot, int cubemapSize); +void R_GenerateReflectionRawData(DiskGfxReflectionProbe* probeRawData); +void R_GenerateReflectionRawDataAll(DiskGfxReflectionProbe *probeRawData, int probeCount, bool *generateProbe); +bool R_CopyReflectionsFromLumpData(DiskGfxReflectionProbe *probeRawData, DiskGfxReflectionProbe *probeRawLumpData, const int lumpProbeCount); +void R_GenerateReflections(const char *mapname, GfxReflectionProbe *probes, const unsigned int probeCount); +bool R_ReflectionProbeGenerateExitWhenDone(); +void R_GenerateReflectionImages(GfxReflectionProbe *probes, DiskGfxReflectionProbe *probeRawData, const int probeCount, const int probeBaseIndex); -typedef int __cdecl R_CreateReflectionRawDataFromCubemapShot_t(DiskGfxReflectionProbe *probeRawData); -static R_CreateReflectionRawDataFromCubemapShot_t* R_CreateReflectionRawDataFromCubemapShot = (R_CreateReflectionRawDataFromCubemapShot_t*)0x007088E0; \ No newline at end of file +void hk_R_GenerateReflections(); \ No newline at end of file diff --git a/components/game_mod/r_rendercmds.h b/components/game_mod/r_rendercmds.h index 1acc06a6..a6089022 100644 --- a/components/game_mod/r_rendercmds.h +++ b/components/game_mod/r_rendercmds.h @@ -52,4 +52,10 @@ typedef void __cdecl FX_SetNextUpdateCamera_t(int localClientNum, FxCameraUpdate static FX_SetNextUpdateCamera_t* FX_SetNextUpdateCamera = (FX_SetNextUpdateCamera_t*)0x00653FA0; typedef void __cdecl FX_FillUpdateCmd_t(int localClientNum, FxCmd *cmd); -static FX_FillUpdateCmd_t* FX_FillUpdateCmd = (FX_FillUpdateCmd_t*)0x005C8FB0; \ No newline at end of file +static FX_FillUpdateCmd_t* FX_FillUpdateCmd = (FX_FillUpdateCmd_t*)0x005C8FB0; + +typedef void (__cdecl* R_AddCmdDrawText_t)(const char *text, int maxChars, Font_s *font, float x, float y, float xScale, float yScale, float rotation, const float *color, int style); +static R_AddCmdDrawText_t R_AddCmdDrawText = (R_AddCmdDrawText_t)0x006D6460; + +typedef void(__cdecl* R_AddCmdProjectionSet2D_t)(); +static R_AddCmdProjectionSet2D_t R_AddCmdProjectionSet2D = (R_AddCmdProjectionSet2D_t)0x006D7D30; diff --git a/components/game_mod/r_scene.h b/components/game_mod/r_scene.h index 75e9aba6..e0a163e0 100644 --- a/components/game_mod/r_scene.h +++ b/components/game_mod/r_scene.h @@ -1,28 +1,20 @@ #pragma once +#include "r_debug.h" +#include "r_material.h" -#include "r_reflection_probe.h" - -struct FxCodeMeshData +struct GfxMatrix { - char data[0x10]; + float m[4][4]; }; -struct GfxMatrix +struct GfxCodeMatrices { - float m[4][4]; + GfxMatrix matrix[32]; }; -struct GfxViewParms +struct FxCodeMeshData { - GfxMatrix viewMatrix; - GfxMatrix projectionMatrix; - GfxMatrix viewProjectionMatrix; - GfxMatrix inverseViewProjectionMatrix; - float origin[4]; - float axis[3][3]; - float depthHackNearClip; - float zNear; - float zFar; + char data[0x10]; }; struct GfxParticleCloud @@ -96,11 +88,6 @@ struct GfxCmdArray int warnSize; }; -struct GfxSceneDef -{ - char data[0x14]; -}; - typedef int ShadowType; struct PointLightPartition @@ -118,20 +105,6 @@ struct GfxDrawSurfListInfo char data[0x50]; }; -struct GfxCodeImageRenderTarget -{ - char data[0x8]; -}; - -struct __declspec(align(16)) GfxCmdBufInput -{ - float consts[197][4]; - void *codeImages[43]; - char codeImageSamplerStates[43]; - GfxCodeImageRenderTarget codeImageRenderTargetControl[43]; - void *data; -}; - struct GfxExposureShaderRemap { float remapMul[3]; @@ -172,6 +145,361 @@ struct DynSModelGfxState char data[0x180C]; }; + +struct GfxFilm +{ + float filmMidStart; + float filmMidEnd; + float filmDarkFeather; + float filmLightFeather; + float filmBleach[3]; + float filmColorTemp[3]; + float filmHue[3]; + float filmSaturation[3]; + float filmDarkTint[3]; + float filmMidTint[3]; + float filmLightTint[3]; + float filmContrast[3]; + bool enabled; + float filmLut; + float sCurveEnable; + float sCurveShoulderStrength; + float sCurveLinearStrength; + float sCurveLinearAngle; + float sCurveToeStrength; + float sCurveToeNumerator; + float sCurveToeDenominator; +}; + +struct GfxBloom +{ + float bloomTintWeights[4]; + float bloomColorScale[4]; + float bloomTintScale[4]; + float bloomCurveBreakpoint[4]; + float bloomCurveLoBlack[4]; + float bloomCurveLoGamma[4]; + float bloomCurveLoWhite[4]; + float bloomCurveLoRemapBlack[4]; + float bloomCurveLoRemapWhite[4]; + float bloomCurveHiBlack[4]; + float bloomCurveHiGamma[4]; + float bloomCurveHiWhite[4]; + float bloomCurveHiRemapBlack[4]; + float bloomCurveHiRemapWhite[4]; + float bloomExpansionControl[4]; + float bloomExpansionWeights[4]; + int bloomExpansionSource; + float bloomBlurRadius; + float bloomPersistence; + float bloomStreakXLevels0[4]; + float bloomStreakXLevels1[4]; + float bloomStreakXInnerTint[3]; + float bloomStreakXOuterTint[3]; + float bloomStreakXTintControl[4]; + float bloomStreakXTint[3]; + float bloomStreakYLevels0[4]; + float bloomStreakYLevels1[4]; + float bloomStreakYInnerTint[3]; + float bloomStreakYOuterTint[3]; + float bloomStreakYTintControl[4]; + float bloomStreakYTint[3]; +}; + +struct GfxReviveFx +{ + bool enabled; + float reviveEdgeColorTemp; + float reviveEdgeSaturation; + float reviveEdgeScale[3]; + float reviveEdgeContrast[3]; + float reviveEdgeOffset[3]; + float reviveEdgeMaskAdjust; + float reviveEdgeAmount; +}; + +struct GfxLightScale +{ + float diffuseScale; + float specularScale; +}; + +struct GfxVisionSet +{ + GfxFilm film; + GfxBloom bloom; + GfxReviveFx reviveFx; + GfxLightScale charPrimaryLightScale; +}; + +struct GfxDepthOfField +{ + float viewModelStart; + float viewModelEnd; + float nearStart; + float nearEnd; + float farStart; + float farEnd; + float nearBlur; + float farBlur; +}; + +struct GfxDoubleVision +{ + float direction[3]; + float motionBlurMagnitude; + float deltaPerMS; + float cur; + float targ; +}; + +struct GfxGenericFilter +{ + bool passEnabled[3][16]; + void *passMaterial[3][16]; //Material* + int passTarget[3][16]; + int passSampler0[3][16]; + int passSampler1[3][16]; + float passParam[3][16][16]; + int passQuads[3][16]; + float passFlareOcclusion[3][16]; + float sunPosition[3]; +}; + +struct GfxPoison +{ + float curAmountTarget; + float curAmount; +}; + +struct GfxCompositeFx +{ + float distortionScale[2]; + float blurRadius; + float distortionMagnitude; + float frameRate; + int lastUpdate; + int frame; + int startMSec; + int currentTime; + int duration; + bool enabled; + bool scriptEnabled; +}; + +struct GfxSaveScreenParam +{ + float s0; + float t0; + float ds; + float dt; + int screenTimerId; + char mode; +}; + +struct GfxBlendSaveScreenBlurredParam +{ + int fadeMsec; + float s0; + float t0; + float ds; + float dt; + int screenTimerId; + char enabled; +}; + +struct GfxBlendSaveScreenFlashedParam +{ + float intensityWhiteout; + float intensityScreengrab; + float s0; + float t0; + float ds; + float dt; + int screenTimerId; + char enabled; +}; + +struct GfxSaveScreenFx +{ + GfxSaveScreenParam saveScreenParam; + GfxBlendSaveScreenBlurredParam blendBlurredParam; + GfxBlendSaveScreenFlashedParam blendFlashedParam; +}; + +struct float44 +{ + union + { + float m[4][4]; + float member[16]; + }; +}; + +struct GfxLightImage +{ + void *image; + char samplerState; +}; + +struct GfxLightDef +{ + const char *name; + GfxLightImage attenuation; + int lmapLookupStart; +}; + +struct __declspec(align(16)) GfxLight +{ + char type; + char canUseShadowMap; + __int16 cullDist; + float color[3]; + float dir[3]; + float origin[3]; + float radius; + float cosHalfFovOuter; + float cosHalfFovInner; + int exponent; + unsigned int spotShadowIndex; + float angles[3]; + float spotShadowHiDistance; + float diffuseColor[4]; + float specularColor[4]; + float shadowColor[4]; + float falloff[4]; + float attenuation[4]; + float aAbB[4]; + float cookieControl0[4]; + float cookieControl1[4]; + float cookieControl2[4]; + __declspec(align(8)) float44 viewMatrix; + float44 projMatrix; + GfxLightDef *def; +}; + +struct GfxExposureValue +{ + float blackPoint[4]; + float whitePoint[4]; + float linearStart[4]; + float linearEnd[4]; + float remapStart[4]; + float remapEnd[4]; + float scurveStart[4]; + float scurveEnd[4]; + float bloomCurveLDR[4]; + float bloomCurveHDR[4]; + float bloomScale[4]; +}; + +struct WaterFogDef +{ + int startTime; + int finishTime; + float color[4]; + float fogStart; + float density; + float heightDensity; + float baseHeight; + float sunFogColor[4]; + float sunFogDir[3]; + float sunFogStartAng; + float sunFogEndAng; +}; + +struct GfxSceneDef +{ + int time; + float floatTime; + float viewOffset[3]; +}; + +struct GfxCodeImageRenderTargetFields +{ + __int64 _bf0; +}; + +union GfxCodeImageRenderTarget +{ + GfxCodeImageRenderTargetFields fields; + unsigned int packed; +}; + +struct __declspec(align(16)) GfxCmdBufInput +{ + float consts[197][4]; + GfxImage *codeImages[43]; + char codeImageSamplerStates[43]; + GfxCodeImageRenderTarget codeImageRenderTargetControl[43]; + struct GfxBackEndData *data; +}; + +struct GfxSunShadow +{ + char data[0x450]; +}; + +struct GfxSpotShadow +{ + char data[0x1D0]; +}; + + +struct GfxPlacement +{ + float quat[4]; + float origin[3]; +}; + +struct GfxScaledPlacement +{ + GfxPlacement base; + float scale; +}; + +enum GfxDepthRangeType +{ + GFX_DEPTH_RANGE_SCENE = 0x0, + GFX_DEPTH_RANGE_VIEWMODEL = 0x2, + GFX_DEPTH_RANGE_FULL = 0xFFFFFFFF, +}; + +enum GfxViewMode +{ + VIEW_MODE_NONE = 0x0, + VIEW_MODE_3D = 0x1, + VIEW_MODE_2D = 0x2, + VIEW_MODE_IDENTITY = 0x3, +}; + +struct GfxViewport +{ + int x; + int y; + int width; + int height; +}; + +enum GfxViewportBehavior +{ + GFX_USE_VIEWPORT_FOR_VIEW = 0x0, + GFX_USE_VIEWPORT_FULL = 0x1, +}; + +struct GfxViewParms +{ + GfxMatrix viewMatrix; + GfxMatrix projectionMatrix; + GfxMatrix viewProjectionMatrix; + GfxMatrix inverseViewProjectionMatrix; + float origin[4]; + float axis[3][3]; + float depthHackNearClip; + float zNear; + float zFar; +}; + struct __declspec(align(16)) GfxViewInfo { char unk1[0x170]; @@ -223,26 +551,6 @@ struct __declspec(align(16)) GfxViewInfo DynSModelGfxState *dynSModelState; }; -struct DebugGlobals -{ - char data[0x6C]; -}; - -struct GfxDrawSurf -{ - char data[0x8]; -}; - -struct GfxSunShadow -{ - char data[0x450]; -}; - -struct GfxSpotShadow -{ - char data[0x1D0]; -}; - const struct __declspec(align(32)) GfxBackEndData { char surfsBuffer[262144]; @@ -315,6 +623,187 @@ const struct __declspec(align(32)) GfxBackEndData void *dynSModelClientViewArray; }; +struct GfxPrimStats +{ + int primCount; + int triCount; + int staticIndexCount; + int staticVertexCount; + int dynamicIndexCount; + int dynamicVertexCount; +}; + +struct GfxViewStats +{ + GfxPrimStats primStats[10]; + int drawSurfCount; + int drawMatCount; + int drawPrimHistogram[16]; +}; + +struct GfxFrameStats +{ + GfxViewStats viewStats[2]; + int gfxEntCount; + int geoIndexCount; + int fxIndexCount; +}; + +struct GfxCmdBufPrimState +{ + union + { + IDirect3DDevice9 *device; + IDirect3DDevice9 *localDevice; + }; + + IDirect3DIndexBuffer9 *indexBuffer; + MaterialVertexDeclType vertDeclType; + + struct + { + unsigned int stride; + IDirect3DVertexBuffer9 *vb; + unsigned int offset; + } streams[3]; + + IDirect3DVertexDeclaration9 *vertexDecl; + + /* + GfxFrameStats frameStats; + struct GfxPrimStats *primStats; + struct GfxPrimStats *backupPrimStats; + struct GfxViewStats *viewStats; + */ +}; + +struct GfxCmdBufSourceState +{ + GfxCodeMatrices matrices; + GfxCmdBufInput input; + GfxViewParms viewParms; + GfxMatrix shadowLookupMatrix; + unsigned __int16 constVersions[229]; + unsigned __int16 matrixVersions[8]; + float eyeOffset[4]; + unsigned int shadowableLightForShadowLookupMatrix; + const GfxScaledPlacement *objectPlacement; + const GfxViewParms *viewParms3D; + unsigned int depthHackFlags; + GfxScaledPlacement skinnedPlacement; + int cameraView; + GfxViewMode viewMode; + GfxSceneDef sceneDef; + GfxViewport sceneViewport; + GfxViewport scissorViewport; + float materialTime; + float deibleBurnAmount; + float deibleFadeAmount; + float wetness; + GfxViewportBehavior viewportBehavior; + int renderTargetWidth; + int renderTargetHeight; + bool viewportIsDirty; + bool scissorEnabled; + unsigned int shadowableLightIndex; +}; + +STATIC_ASSERT_SIZE(GfxCmdBufSourceState, 0x1A90); + +struct GfxCmdBufState +{ + char refSamplerState[16]; + unsigned int samplerState[16]; + GfxTexture *samplerTexture[16]; + GfxCmdBufPrimState prim; + Material *material; // 0xC4 + char techType; + MaterialTechnique *technique; // 0xCC + MaterialPass *pass; + unsigned int passIndex; + GfxDepthRangeType depthRangeType; + float depthRangeNear; + float depthRangeFar; + unsigned __int64 vertexShaderConstState[256]; + unsigned __int64 pixelShaderConstState[256]; + char alphaRef; + unsigned int refStateBits[2]; + unsigned int activeStateBits[2]; + MaterialPixelShader *pixelShader; + MaterialVertexShader *vertexShader; + unsigned int pixPrimarySortKey; + Material *pixMaterial; + MaterialTechnique *pixTechnique; + int pixCombine; + GfxViewport viewport; + GfxViewport scissor; + int scissorEnabled; + char renderTargetId; + Material *origMaterial; + char origTechType; + int stateOverride; +}; +STATIC_ASSERT_SIZE(GfxCmdBufState, 0x1148); + +struct GfxCmdBufContext +{ + GfxCmdBufSourceState *source; + GfxCmdBufState *state; +}; + +struct __declspec(align(16)) refdef_s +{ + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int height; + float tanHalfFovX; + float tanHalfFovY; + float fov_x; + float vieworg[3]; + float yaw; + float viewaxis[3][3]; + int time; + float zNear; + float zFar; + GfxVisionSet visionset; + float blurRadius; + GfxDepthOfField dof; + GfxDoubleVision doubleVision; + GfxCompositeFx flameFx; + GfxCompositeFx waterSheetingFx; + GfxGenericFilter genericFilter; + GfxPoison poisonFx; + GfxCompositeFx electrifiedFx; + GfxCompositeFx transportedFx; + GfxSaveScreenFx saveScreenFx; + float sunVisibility; + GfxLight primaryLights[255]; + GfxViewport scissorViewport; + bool useScissorViewport; + int localClientNum; + int hideMatureContent; + int splitscreen; + int playerTeleported; + int oldExposureId; + int newExposureId; + GfxExposureValue exposureValue; + float lerpcount; + int lastTime; + unsigned int exposureMode; + float exposure; + float postEmissiveBrightening; + bool noLodCullOut; + WaterFogDef waterFog; + float extraCamPos[3]; + bool extraCamPosValid; + bool extraCamLargeFrame; + float preExtraCamVieworg[3]; + float preExtraCamViewaxis[3][3]; + float preExtraCamTanHalfFovX; + float preExtraCamTanHalfFovY; +}; + typedef void __cdecl R_ClearScene_t(int localClientNum); static R_ClearScene_t* R_ClearScene = (R_ClearScene_t*)0x006C7DC0; @@ -329,4 +818,4 @@ typedef int __cdecl R_InitPrimaryLights_t(void*); static R_InitPrimaryLights_t* R_InitPrimaryLights = (R_InitPrimaryLights_t*)0x006F6800; typedef float __cdecl R_GetFarPlaneDist_t(void); -static R_GetFarPlaneDist_t* R_GetFarPlaneDist = (R_GetFarPlaneDist_t*)0x006B6350; \ No newline at end of file +static R_GetFarPlaneDist_t* R_GetFarPlaneDist = (R_GetFarPlaneDist_t*)0x006B6350; diff --git a/components/game_mod/r_screenshot.cpp b/components/game_mod/r_screenshot.cpp index e926a01c..9774a118 100644 --- a/components/game_mod/r_screenshot.cpp +++ b/components/game_mod/r_screenshot.cpp @@ -7,4 +7,9 @@ void R_BeginCubemapShot(const int pixelWidthHeight, const int pixelBorder) gfxMetrics->cubemapShotRes = pixelWidthHeight; gfxMetrics->cubemapShotPixelBorder = pixelBorder; R_CubemapShotSetInitialState(); +} + +void R_CreateReflectionRawDataFromCubemapShot(DiskGfxReflectionProbe *probeRawData) +{ + ((void(__cdecl *)(DiskGfxReflectionProbe *))0x007088E0)(probeRawData); } \ No newline at end of file diff --git a/components/game_mod/r_screenshot.h b/components/game_mod/r_screenshot.h index bdc7eb72..839a0c15 100644 --- a/components/game_mod/r_screenshot.h +++ b/components/game_mod/r_screenshot.h @@ -1,4 +1,5 @@ #pragma once +#include "r_reflection_probe.h" static_assert(sizeof(D3DFORMAT) == sizeof(DWORD), "D3DFORMAT must be 4 bytes!"); @@ -23,6 +24,7 @@ struct GfxMetrics }; void R_BeginCubemapShot(const int pixelWidthHeight, const int pixelBorder); +void R_CreateReflectionRawDataFromCubemapShot(DiskGfxReflectionProbe *probeRawData); typedef int __cdecl R_CubemapShotSetInitialState_t(void); static R_CubemapShotSetInitialState_t* R_CubemapShotSetInitialState = (R_CubemapShotSetInitialState_t*)0x00708710; diff --git a/components/game_mod/radiant_remote.cpp b/components/game_mod/radiant_remote.cpp index bc37cc19..e5afbd77 100644 --- a/components/game_mod/radiant_remote.cpp +++ b/components/game_mod/radiant_remote.cpp @@ -1,13 +1,18 @@ #include "stdafx.h" -HANDLE liveUpdateThread; +SOCKET g_ServerSocket = INVALID_SOCKET; +SOCKET g_ClientSocket = INVALID_SOCKET; -int& savedCommandCount = *(int *)0x0251AE58; -auto savedCommands = (RadiantCommand *)0x02507990; -int& gCommandCount = *(int *)0x251AE50; -auto gCommands = (RadiantCommand *)0x02517D90; -int& gObjectMappingCount = *(int *)0x0251AE60; -auto gObjectMapping = (RadaintToGameMapping *)0x2507180; +const char *GetPairValue(SpawnVar *spawnVar, const char *key) +{ + for (int i = 0; i < spawnVar->numSpawnVars; ++i) + { + if (!_stricmp(key, spawnVar->spawnVars[i][0])) + return spawnVar->spawnVars[i][1]; + } + + return nullptr; +} void RadiantRemoteInit() { @@ -20,75 +25,317 @@ void RadiantRemoteInit() gObjectMappingCount = 0; memset(gObjectMapping, 0, sizeof(RadaintToGameMapping) * 128); - if (!liveUpdateThread) - liveUpdateThread = CreateThread(nullptr, 0, RadiantRemoteThread, nullptr, 0, nullptr); -} + // Initialize network and server socket (only if it hasn't been set up) + if (!radiant_live->current.enabled || g_ServerSocket != INVALID_SOCKET) + return; -DWORD WINAPI RadiantRemoteThread(LPVOID Arg) -{ - // Initialize winsock if the game hasn't yet WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) + Com_Error(ERR_FATAL, "LiveRadiant: Socket startup failed\n"); + + // Create a TCP server socket + g_ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (g_ServerSocket == INVALID_SOCKET) + Com_Error(ERR_FATAL, "LiveRadiant: Socket creation failed\n"); + + // Bind socket to any local address on port X + sockaddr_in addrIn; + addrIn.sin_family = AF_INET; + addrIn.sin_port = htons(radiant_livePort->current.integer); + addrIn.sin_addr.s_addr = inet_addr("127.0.0.1"); + + Com_Printf(1, "LiveRadiant: Attempting to bind on port %d... ", (int)ntohs(addrIn.sin_port)); + + if (bind(g_ServerSocket, (SOCKADDR *)&addrIn, sizeof(addrIn)) == SOCKET_ERROR) { - Com_PrintError(1, "LiveRadiant: WSAStartup failed\n"); - return 0; + Com_PrintError(1, "Failed to bind socket. Port in use?\n"); + + RadiantRemoteShutdown(); + return; + } + + // Listen for any number of incoming connections + if (listen(g_ServerSocket, SOMAXCONN) == SOCKET_ERROR) + { + Com_PrintError(1, "Failed to listen for incoming connections\n"); + + RadiantRemoteShutdown(); + return; } - // Create a UDP socket - SOCKET udpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + Com_Printf(1, "Succeeded\n"); +} + +void RadiantRemoteShutdown() +{ + shutdown(g_ServerSocket, 2 /*SD_BOTH*/); + closesocket(g_ServerSocket); + + shutdown(g_ClientSocket, 2 /*SD_BOTH*/); + closesocket(g_ClientSocket); - if (udpSocket == INVALID_SOCKET) + g_ServerSocket = INVALID_SOCKET; + g_ClientSocket = INVALID_SOCKET; +} + +void RadiantRemoteUpdate() +{ + if (!radiant_live->current.enabled || !RadiantRemoteUpdateSocket()) + return; + + // Non-blocking read + RadiantCommand recvCommands[16]; + memset(recvCommands, 0, sizeof(recvCommands)); + + int recvSize = recv(g_ClientSocket, (char *)&recvCommands, sizeof(recvCommands), 0); + + // Skip everything if there's no data + if (recvSize == SOCKET_ERROR) { - Com_PrintError(1, "LiveRadiant: Socket creation failed\n"); - return 0; + if (WSAGetLastError() == WSAEWOULDBLOCK) + return; + + // Some other problem occurred and now the socket is bad + shutdown(g_ClientSocket, 2 /*SD_BOTH*/); + closesocket(g_ClientSocket); + + g_ClientSocket = INVALID_SOCKET; + return; } - // Bind socket to any incoming address on port 3700 - sockaddr_in addrSender; - sockaddr_in addrIn; - addrIn.sin_family = AF_INET; - addrIn.sin_port = htons(3700); - addrIn.sin_addr.s_addr = htonl(INADDR_ANY); + // Determine the number of commands sent, then tell the game + size_t commandCount = recvSize / sizeof(RadiantCommand); - if (bind(udpSocket, (SOCKADDR *)&addrIn, sizeof(addrIn)) == SOCKET_ERROR) + Sys_EnterCriticalSection((CriticalSection)61); { - Com_PrintError(1, "LiveRadiant: Failed to bind socket\n"); - closesocket(udpSocket); - return 0; + for (size_t i = 0; i < commandCount; i++) + { + // + // This must be enforced on the server side: + // Classname must be supplied or the game will crash + // + if (recvCommands[i].type == RADIANT_COMMAND_SELECT) + { + if (!strstr(recvCommands[i].strCommand, "\"classname\"")) + continue; + } + + // Print each network message to the console if enabled + if (radiant_liveDebug->current.enabled) + { + if (recvCommands[i].type != RADIANT_COMMAND_CAMERA) + Com_Printf(1, "Command %d [ID %d]:\n%s\n", recvCommands[i].type, recvCommands[i].liveUpdateId, recvCommands[i].strCommand); + } + + memcpy(&gCommands[gCommandCount++], &recvCommands[i], sizeof(RadiantCommand)); + } } + Sys_LeaveCriticalSection((CriticalSection)61); +} + +bool RadiantRemoteUpdateSocket() +{ + if (g_ClientSocket != INVALID_SOCKET) + return true; + + if (g_ServerSocket == INVALID_SOCKET) + return false; + + // Check if there's a pending client connection request + fd_set readSet; + FD_ZERO(&readSet); + FD_SET(g_ServerSocket, &readSet); + + // Zero timeout (poll) + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + int status = select(g_ServerSocket, &readSet, nullptr, nullptr, &timeout); + + if (status == SOCKET_ERROR) + Com_Error(ERR_FATAL, "LiveRadiant: Failed to query socket status\n"); - Com_Printf(1, "LiveRadiant: Socket bound on port 3700\n"); + // Must be 1 (handle) if there is a pending connection + if (status != 1) + return false; - while (true) + g_ClientSocket = accept(g_ServerSocket, nullptr, nullptr); + + if (g_ClientSocket == INVALID_SOCKET) + Com_Error(ERR_FATAL, "LiveRadiant: Failed to accept a connection?\n"); + + // Set non-blocking flag + u_long socketMode = 1; + ioctlsocket(g_ClientSocket, FIONBIO, &socketMode); + + Com_Printf(1, "LiveRadiant: Client connected\n"); + return true; +} + +void G_AssignGameIdMapping(int liveUpdateId, int gameId) +{ + if (liveUpdateId > 0) + gObjectMapping[liveUpdateId - 1].gameId = gameId; +} + +int G_GetGameIdMapping(int liveUpdateId) +{ + if (liveUpdateId <= 0) + return -1; + + return gObjectMapping[liveUpdateId - 1].gameId; +} + +pathnode_t *G_FindPathNode(SpawnVar *spawnVar, nodeType type, const int gameId) +{ + pathnode_t compareNode; + memset(&compareNode, 0, sizeof(pathnode_t)); + + G_ParsePathnodeFields(spawnVar, &compareNode, type); + + pathnode_t *bestNode = nullptr; + int bestScore = 1; + + for (unsigned int i = 0; i < gameWorldCurrent->path.nodeCount; ++i) + { + int score = 0; + pathnode_t *node = &gameWorldCurrent->path.nodes[i]; + + // 2D distance, ignores height + float dist = + ((compareNode.constant.vOrigin[0] - node->constant.vOrigin[0]) * + (compareNode.constant.vOrigin[0] - node->constant.vOrigin[0])) + + ((compareNode.constant.vOrigin[1] - node->constant.vOrigin[1]) * + (compareNode.constant.vOrigin[1] - node->constant.vOrigin[1])); + + // The closer the distance, the higher the base match score + if (dist < 100.0f) + score = (dist < 0.5f) ? 2 : 1; + + if (node->constant.target && node->constant.target == compareNode.constant.target) + ++score; + + if (node->constant.targetname && node->constant.targetname == compareNode.constant.targetname) + ++score; + + if (node->constant.script_noteworthy && node->constant.script_noteworthy == compareNode.constant.script_noteworthy) + ++score; + + if (score > bestScore) + { + bestNode = &gameWorldCurrent->path.nodes[i]; + bestScore = score; + } + } + + // If the search failed, check if there was a previous mapping + if (gameId > 0) + { + if ((unsigned int)gameId < gameWorldCurrent->path.nodeCount && !bestNode) + bestNode = &gameWorldCurrent->path.nodes[gameId]; + } + + return bestNode; +} + +void G_ProcessPathnodeCommand(RadiantCommand *command, SpawnVar *spawnVar) +{ + const char *classname = GetPairValue(spawnVar, "classname"); + nodeType nodetype = G_GetNodeTypeFromClassname(classname); + + RadiantCommandType commandType = command->type; + int gameId = G_GetGameIdMapping(command->liveUpdateId); + + if (commandType == RADIANT_COMMAND_CREATE) + { + G_SpawnPathnodeStatic(spawnVar, classname); + G_SpawnPathnodeDynamic(spawnVar); + + g_radiant_selected_pathnode = &gameWorldCurrent->path.nodes[gameWorldCurrent->path.nodeCount - 1]; + + unsigned int nodeIndex = Path_ConvertNodeToIndex(g_radiant_selected_pathnode); + G_DropPathNodeToFloor(nodeIndex); + + Path_ConnectPathsForSingleNode(g_radiant_selected_pathnode); + + Com_Printf(5, "Radiant Live Update: Created new path node\n"); + G_AssignGameIdMapping(command->liveUpdateId, nodeIndex); + } + else if (commandType == RADIANT_COMMAND_DELETE) { - char recvBuf[2048]; - memset(recvBuf, 0, sizeof(recvBuf)); + // Remove all node links and then set it to invalid status + g_radiant_selected_pathnode = G_FindPathNode(spawnVar, nodetype, gameId); + + if (g_radiant_selected_pathnode) + { + int nodeindex = Path_ConvertNodeToIndex(g_radiant_selected_pathnode); + + for (unsigned int i = 0; i < gameWorldCurrent->path.nodeCount; ++i) + { + pathnode_t *othernode = &gameWorldCurrent->path.nodes[i]; + + for (unsigned short j = 0; j < othernode->constant.totalLinkCount; ++j) + { + if (othernode->constant.Links[j].nodeNum == nodeindex) + { + othernode->constant.Links[j] = othernode->constant.Links[--othernode->constant.totalLinkCount]; + othernode->dynamic.wLinkCount = othernode->constant.totalLinkCount; + } + } + } + g_radiant_selected_pathnode->constant.totalLinkCount = 0; + g_radiant_selected_pathnode->dynamic.wLinkCount = 0; + g_radiant_selected_pathnode->constant.type = NODE_BADNODE; + } - int fromSize = sizeof(sockaddr_in); - int recvSize = recvfrom(udpSocket, recvBuf, sizeof(recvBuf), 0, (SOCKADDR *)&addrSender, &fromSize); + g_radiant_selected_pathnode = nullptr; + } + else + { + pathnode_t *node = 0; - if (recvSize == SOCKET_ERROR) + if (commandType) { - Com_PrintError(1, "LiveRadiant: Socket receive from failed\n"); - break; + if (commandType == RADIANT_COMMAND_UPDATE_SELECTED) + { + node = g_radiant_selected_pathnode; + } + else if (commandType == RADIANT_COMMAND_UPDATE) + { + node = G_FindPathNode(spawnVar, nodetype, gameId); + } } + else + { + node = g_radiant_selected_pathnode = G_FindPathNode(spawnVar, nodetype, gameId); - // Data received from network, now tell the game - Sys_EnterCriticalSection((CriticalSection)61); + if (!node) + Com_Printf(5, "Radiant Live Update: Can't find pathnode. Maps out of sync (Radiant/Game), re-bsp!\n"); + } + if (node) { - RadiantCommand *c = (RadiantCommand *)&recvBuf; + // Prevent the temporary link flag from getting lost when parsing SpawnVars + unsigned short tempLinks = node->constant.spawnflags & 0x4000; + + G_ParsePathnodeFields(spawnVar, node, nodetype); - if (c->type != RADIANT_COMMAND_CAMERA) - Com_Printf(1, "Command %d %d:\n%s\n", c->type, c->liveUpdateId, c->strCommand); + if (tempLinks) + node->constant.spawnflags |= 0x4000u; - // Insert in global array - memcpy(&gCommands[gCommandCount++], c, sizeof(RadiantCommand)); + // Node attributes have been updated. Force it back to ground level. + unsigned int nodeIndex = Path_ConvertNodeToIndex(node); + G_DropPathNodeToFloor(nodeIndex); + + Path_ConnectPathsForSingleNode(node); + G_AssignGameIdMapping(command->liveUpdateId, nodeIndex); } - Sys_LeaveCriticalSection((CriticalSection)61); } +} - shutdown(udpSocket, 2 /*SD_BOTH*/); - closesocket(udpSocket); - return 0; +void G_ClearSelectedPathNode() +{ + g_radiant_selected_pathnode = nullptr; } \ No newline at end of file diff --git a/components/game_mod/radiant_remote.h b/components/game_mod/radiant_remote.h index aa8bb74b..f1d52735 100644 --- a/components/game_mod/radiant_remote.h +++ b/components/game_mod/radiant_remote.h @@ -17,6 +17,7 @@ struct RadiantCommand int liveUpdateId; char strCommand[512]; }; +STATIC_ASSERT_SIZE(RadiantCommand, 0x208); struct RadaintToGameMapping { @@ -25,9 +26,24 @@ struct RadaintToGameMapping int gameId; int cg_gameId; }; +STATIC_ASSERT_SIZE(RadaintToGameMapping, 0x10); -static_assert(sizeof(RadiantCommand) == 520, "Size check"); -static_assert(sizeof(RadaintToGameMapping) == 16, "Size check"); +static int& savedCommandCount = *(int *)0x0251AE58; +static auto savedCommands = (RadiantCommand *)0x02507990; +static int& gCommandCount = *(int *)0x251AE50; +static auto gCommands = (RadiantCommand *)0x02517D90; +static int& gObjectMappingCount = *(int *)0x0251AE60; +static auto gObjectMapping = (RadaintToGameMapping *)0x2507180; + +static pathnode_t*& g_radiant_selected_pathnode = *(pathnode_t **)0x01D04870; void RadiantRemoteInit(); -DWORD WINAPI RadiantRemoteThread(LPVOID Arg); \ No newline at end of file +void RadiantRemoteShutdown(); +void RadiantRemoteUpdate(); +bool RadiantRemoteUpdateSocket(); + +void G_AssignGameIdMapping(int liveUpdateId, int gameId); +int G_GetGameIdMapping(int liveUpdateId); +pathnode_t *G_FindPathNode(SpawnVar *spawnVar, nodeType type, const int gameId); +void G_ProcessPathnodeCommand(RadiantCommand *command, SpawnVar *spawnVar); +void G_ClearSelectedPathNode(); \ No newline at end of file diff --git a/components/game_mod/ragdoll.cpp b/components/game_mod/ragdoll.cpp new file mode 100644 index 00000000..9cfc0e98 --- /dev/null +++ b/components/game_mod/ragdoll.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" + +phys_free_list *g_ragdoll_body_pool = (phys_free_list *)0x00B79D38; + +void Ragdoll_BodyRootOrigin(RagdollBody *body, float *origin) +{ + ((void(__cdecl *)(RagdollBody *, float *))0x00554310)(body, origin); +} + +bool Ragdoll_BodyInUse(RagdollBody *body) +{ + ASSERT(body); + return body->references > 0; +} + +bool Ragdoll_BodyIdle(RagdollBody *body) +{ + ASSERT(body); + return body->state == BS_IDLE; +} + +BoneOrientation *Ragdoll_BodyBoneOrientations(RagdollBody *body) +{ + return ((BoneOrientation *(__cdecl *)(RagdollBody *))0x0054E920)(body); +} \ No newline at end of file diff --git a/components/game_mod/ragdoll.h b/components/game_mod/ragdoll.h new file mode 100644 index 00000000..83638134 --- /dev/null +++ b/components/game_mod/ragdoll.h @@ -0,0 +1,82 @@ +#pragma once + +enum RagdollBodyState +{ + BS_DEAD = 0x0, + BS_DOBJ_WAIT = 0x1, + BS_VELOCITY_CAPTURE = 0x2, + BS_TUNNEL_TEST = 0x3, + BS_RUNNING = 0x4, + BS_IDLE = 0x5, + RAGDOLL_NUM_STATES = 0x6, +}; + +struct Bone +{ + int parentBone; + char animBones[2]; + int rigidBody; + float length; + float center[3]; +}; +STATIC_ASSERT_SIZE(Bone, 0x1C); +STATIC_ASSERT_OFFSET(Bone, rigidBody, 0x8); + +struct RagdollBody +{ + int references; + int ragdollDef; + int dobj; + /*DObj*/int *obj; + /*cpose_t*/int *pose; + float poseOffset[3]; + int localClientNum; + RagdollBodyState state; + int stateMsec; + int stateFrames; + int velCaptureMsec; + int stableTime; + int numBones; + Bone bones[14]; + /* omitted */ +}; +STATIC_ASSERT_OFFSET(RagdollBody, state, 0x24); +STATIC_ASSERT_OFFSET(RagdollBody, stateMsec, 0x28); +STATIC_ASSERT_OFFSET(RagdollBody, numBones, 0x38); +STATIC_ASSERT_OFFSET(RagdollBody, bones, 0x3C); + +struct BoneOrientation +{ + float origin[3]; + int boneFlags; + float orientation[4]; +}; + +template +struct phys_free_list +{ + struct T_internal_base + { + T_internal_base *m_prev_T_internal; + T_internal_base *m_next_T_internal; + }; + + struct T_internal : T_internal_base + { + T m_data; + int m_ptr_list_index; + }; + + T_internal_base m_dummy_head; + int m_list_count; + int m_list_count_high_water; + T *m_ptr_list[256]; + int m_ptr_list_count; +}; + +extern phys_free_list *g_ragdoll_body_pool; + +void Ragdoll_BodyRootOrigin(RagdollBody *body, float *origin); +bool Ragdoll_BodyInUse(RagdollBody *body); +bool Ragdoll_BodyIdle(RagdollBody *body); +BoneOrientation *Ragdoll_BodyBoneOrientations(RagdollBody *body); \ No newline at end of file diff --git a/components/game_mod/ragdoll_update.cpp b/components/game_mod/ragdoll_update.cpp new file mode 100644 index 00000000..af9834d3 --- /dev/null +++ b/components/game_mod/ragdoll_update.cpp @@ -0,0 +1,174 @@ +#include "stdafx.h" + +bool R_CullPoint(int localClient, const float *p0, float cutoff) +{ + return ((bool(__cdecl *)(int, const float *, float))0x006B63D0)(localClient, p0, cutoff); +} + +bool Ragdoll_BodyNewState(RagdollBody *body, RagdollBodyState state) +{ + return ((bool (__cdecl *)(RagdollBody *, RagdollBodyState))0x0064A2D0)(body, state); +} + +void Ragdoll_GetTorsoPosition(RagdollBody *body, float *center) +{ + ASSERT(body && Ragdoll_BodyInUse(body)); + + BoneOrientation *orientation = Ragdoll_BodyBoneOrientations(body); + center[0] = orientation->origin[0]; + center[1] = orientation->origin[1]; + center[2] = orientation->origin[2]; +} + +void Ragdoll_ExplosionEvent(int localClientNum, bool isCylinder, const float *origin, float innerRadius, float outerRadius, const float *impulse, float inScale) +{ + ASSERT(origin); + ASSERT(innerRadius >= 0.0f); + ASSERT(outerRadius >= innerRadius); + + if (localClientNum != 0) + return; + + if (outerRadius == 0.0f) + return; + + float outerRadiusSqr = outerRadius * outerRadius; + float innerRadiusSqr = innerRadius * innerRadius; + float invRange = 0.0f; + + if (outerRadiusSqr > innerRadiusSqr) + invRange = 1.0f / (outerRadiusSqr - innerRadiusSqr); + + RagdollSortStruct hitEntsSorter[64]; + int hitCount = 0; + + for (auto i = g_ragdoll_body_pool->m_dummy_head.m_next_T_internal; + i != &g_ragdoll_body_pool->m_dummy_head; i = i->m_next_T_internal) + { + RagdollBody * body = &((phys_free_list::T_internal *)i)->m_data; + + ASSERT(Ragdoll_BodyInUse(body)); + + float ragdollOrigin[3]; + Ragdoll_BodyRootOrigin(body, ragdollOrigin); + + hitEntsSorter[hitCount].distSq = Vec3DistanceSq(origin, ragdollOrigin); + hitEntsSorter[hitCount++].body = body; + } + + // std::sort function + ((void (__cdecl *)(RagdollSortStruct *, RagdollSortStruct *, int, LPVOID))0x004A2870)( + hitEntsSorter, + &hitEntsSorter[hitCount], + 8 * hitCount >> 3, + (LPVOID)0x00831C70); + + for (int i = 0; i < 4 && i < hitCount; ++i) + { + RagdollBody *body = hitEntsSorter[i].body; + + ASSERT(Ragdoll_BodyInUse(body)); + + if (body->state < BS_RUNNING) + continue; + + float torsoPos[3]; + Ragdoll_GetTorsoPosition(body, torsoPos); + + auto cgameGlob = CG_GetLocalClientGlobals(localClientNum); + float dist = Vec3DistanceSq((const float *)(cgameGlob + 0x8A388), torsoPos); + + if (dist <= (ragdoll_reactivation_cutoff->current.value * ragdoll_reactivation_cutoff->current.value) + && !R_CullPoint(localClientNum, torsoPos, -100.0f)) + { + float delta[3]; + if (isCylinder) + { + delta[0] = origin[0] - torsoPos[0]; + delta[1] = origin[1] - torsoPos[1]; + delta[2] = 0.0f; + } + else + { + delta[0] = torsoPos[0] - origin[0]; + delta[1] = torsoPos[1] - origin[1]; + delta[2] = torsoPos[2] - origin[2]; + } + + if (((delta[0] * delta[0]) + (delta[1] * delta[1]) + (delta[2] * delta[2])) < outerRadiusSqr + && (!Ragdoll_BodyIdle(body) || Ragdoll_BodyNewState(body, BS_RUNNING))) + { + body->stateMsec = 0; + float boneScale = 1.0f / body->numBones; + + for (int boneIdx = 0; boneIdx < body->numBones; ++boneIdx) + { + float scale = inScale; + Bone *bone = &body->bones[boneIdx]; + + if (!bone->rigidBody) + continue; + + float centerOfMass[3]; + Phys_ObjGetCenterOfMass(bone->rigidBody, centerOfMass); + + centerOfMass[0] += flrand(-1.0f, 1.0f); + centerOfMass[1] += flrand(-1.0f, 1.0f); + centerOfMass[2] += flrand(-1.0f, 1.0f); + + delta[0] = centerOfMass[0] - origin[0]; + delta[1] = centerOfMass[1] - origin[1]; + delta[2] = centerOfMass[2] - origin[2]; + + float distSqr = (delta[0] * delta[0]) + (delta[1] * delta[1]) + (delta[2] * delta[2]); + + if (distSqr < outerRadiusSqr) + { + if (distSqr > innerRadiusSqr) + { + ASSERT(outerRadiusSqr > innerRadiusSqr); + scale *= ((outerRadiusSqr - distSqr) * invRange); + } + + // + // __asm movss xmm, vec3_origin + // __asm lahf + // if (__SETP__(_AH & 0x44, 0)) + // + float hitForce[3]; + + if (impulse[0] != 0.0f || + impulse[1] != 0.0f || + impulse[2] != 0.0f) + { + hitForce[0] = impulse[0]; + hitForce[1] = impulse[1]; + hitForce[2] = impulse[2]; + } + else + { + hitForce[0] = delta[0]; + hitForce[1] = delta[1]; + hitForce[2] = delta[2]; + + if (isCylinder) + hitForce[2] = 0.0f; + + Vec3Normalize(hitForce); + } + + hitForce[2] += ragdoll_explode_upbias->current.value; + Vec3Normalize(hitForce); + + float explodeForce = (scale * ragdoll_explode_force->current.value) * boneScale; + hitForce[0] *= explodeForce; + hitForce[1] *= explodeForce; + hitForce[2] *= explodeForce; + + Phys_ObjAddForce(bone->rigidBody, centerOfMass, hitForce, false); + } + } + } + } + } +} \ No newline at end of file diff --git a/components/game_mod/ragdoll_update.h b/components/game_mod/ragdoll_update.h new file mode 100644 index 00000000..96b2625a --- /dev/null +++ b/components/game_mod/ragdoll_update.h @@ -0,0 +1,9 @@ +#pragma once + +struct RagdollSortStruct +{ + float distSq; + RagdollBody *body; +}; + +void Ragdoll_ExplosionEvent(int localClientNum, bool isCylinder, const float *origin, float innerRadius, float outerRadius, const float *impulse, float inScale); \ No newline at end of file diff --git a/components/game_mod/rb_tess.cpp b/components/game_mod/rb_tess.cpp new file mode 100644 index 00000000..f89304d2 --- /dev/null +++ b/components/game_mod/rb_tess.cpp @@ -0,0 +1,68 @@ +#include "stdafx.h" + +void __cdecl RB_ShowTess(GfxCmdBufContext context, const float *center, const char *tessName, const float *color) +{ + const float TEXT_SIZE = 0.6f; + + ASSERT(center); + ASSERT(tessName); + + float offsetCenter[3]; + offsetCenter[0] = center[0]; + offsetCenter[1] = center[1]; + offsetCenter[2] = center[2]; + + ASSERT(context.state->material); // This appears to always be null + + MaterialTechniqueSet* techSet; + MaterialTechnique* tech = Material_GetTechnique(context.state->material, context.state->techType); + ASSERT(tech); + + const char* infoString; + const char* infoIdString; + + switch (r_showTess->current.integer) + { + case 1: + infoString = tech->name; + offsetCenter[2] = ((context.state->techType - 16.0f) * 0.3f) + offsetCenter[2]; + infoIdString = "T"; + break; + case 4: + ASSERT(tech->passCount); + if (tech->passArray[0].vertexShader) + infoString = tech->passArray[0].vertexShader->name; + else + infoString = ""; + offsetCenter[2] = ((context.state->techType - 16.0f) * 0.3f) + offsetCenter[2]; + infoIdString = "VS"; + break; + case 5: + ASSERT(tech->passCount); + if (tech->passArray[0].pixelShader) + infoString = tech->passArray[0].pixelShader->name; + else + infoString = ""; + offsetCenter[2] = ((context.state->techType - 16.0f) * 0.3f) + offsetCenter[2]; + infoIdString = "PS"; + break; + case 2: + techSet = Material_GetTechniqueSet(context.state->material); + ASSERT(techSet); + infoString = techSet->name; + infoIdString = "TS"; + break; + case 3: + infoString = context.state->material->info.name; + infoIdString = "M"; + break; + default: + ASSERT_MSG_VA(0, "Unknown value for r_showTess: %i", r_showTess); + infoString = "?"; + infoIdString = "?"; + break; + } + GfxBackEndData* data = context.source->input.data; + const char* str = va("%s:%s=%s", tessName, infoIdString, infoString); + R_AddDebugString(&data->debugGlobals, offsetCenter, color, TEXT_SIZE, str); +} diff --git a/components/game_mod/rb_tess.h b/components/game_mod/rb_tess.h new file mode 100644 index 00000000..2d44fd3d --- /dev/null +++ b/components/game_mod/rb_tess.h @@ -0,0 +1,3 @@ +#pragma once + +void __cdecl RB_ShowTess(GfxCmdBufContext context, const float *center, const char *tessName, const float *color); diff --git a/components/game_mod/reshade.cpp b/components/game_mod/reshade.cpp index b728c08b..e3fed54e 100644 --- a/components/game_mod/reshade.cpp +++ b/components/game_mod/reshade.cpp @@ -5,25 +5,19 @@ HMODULE hRS = NULL; BOOL ReShade_Init(void) { if (IsReflectionMode()) - { return FALSE; - } hRS = LoadLibraryA(RESHADE_DLL); if (hRS == NULL) - { return FALSE; - } void* pfn_Direct3DCreate9 = GetProcAddress(hRS, "Direct3DCreate9"); void* pfn_D3DPERF_BeginEvent = GetProcAddress(hRS, "D3DPERF_BeginEvent"); void* pfn_D3DPERF_EndEvent = GetProcAddress(hRS, "D3DPERF_EndEvent"); if (pfn_Direct3DCreate9 == NULL || pfn_D3DPERF_BeginEvent == NULL || pfn_D3DPERF_EndEvent == NULL) - { return FALSE; - } void** pfnIA_Direct3DCreate9 = (void**)0x009A34DC; void** pfnIA_D3DPERF_BeginEvent = (void**)0x009A34E0; diff --git a/components/game_mod/scr_cmds.cpp b/components/game_mod/scr_cmds.cpp new file mode 100644 index 00000000..092e863a --- /dev/null +++ b/components/game_mod/scr_cmds.cpp @@ -0,0 +1,428 @@ +#include "stdafx.h" + +int level_openScriptIOFileHandles[1]; +BYTE* level_openScriptIOFileBuffers[1]; + +int client_openScriptIOFileHandles[1]; +BYTE* client_openScriptIOFileBuffers[1]; + +BuiltinFunctionDef* client_functions = (BuiltinFunctionDef*)0x00B71DE0; +BuiltinFunctionDef* functions = (BuiltinFunctionDef*)0x00B75EE0; + +void Scr_PatchFunctions() +{ + // + // GSC Patches + // + void* ptr = GScr_OpenFile; + PatchMemory((ULONG_PTR)&functions[369].actionFunc, (PBYTE)&ptr, 4); + + ptr = GScr_CloseFile; + PatchMemory((ULONG_PTR)&functions[370].actionFunc, (PBYTE)&ptr, 4); + + ptr = GScr_FPrintln; + PatchMemory((ULONG_PTR)&functions[371].actionFunc, (PBYTE)&ptr, 4); + + ptr = GScr_FPrintFields; + PatchMemory((ULONG_PTR)&functions[372].actionFunc, (PBYTE)&ptr, 4); + + // + // CSC Patches + // + ptr = CScr_OpenFile; + PatchMemory((ULONG_PTR)&client_functions[139].actionFunc, (PBYTE)&ptr, 4); + + ptr = CScr_CloseFile; + PatchMemory((ULONG_PTR)&client_functions[140].actionFunc, (PBYTE)&ptr, 4); + + ptr = CScr_FPrintln; + PatchMemory((ULONG_PTR)&client_functions[141].actionFunc, (PBYTE)&ptr, 4); + + ptr = CScr_FPrintFields; + PatchMemory((ULONG_PTR)&client_functions[142].actionFunc, (PBYTE)&ptr, 4); +} + +void CScr_OpenFile() +{ + if (!developer->current.enabled || !developer_script->current.enabled) + { + Scr_AddInt(-1, SCRIPTINSTANCE_CLIENT); + } + + int* f = 0; + if (Scr_GetNumParam(SCRIPTINSTANCE_CLIENT) > 1) + { + const char* filename = Scr_GetString(0, SCRIPTINSTANCE_CLIENT); + const char* mode = Scr_GetString(1, SCRIPTINSTANCE_CLIENT); + + int filenum = 0; + for (filenum = 0; filenum < 1; ++filenum) + { + if (!client_openScriptIOFileHandles[filenum]) + { + f = &client_openScriptIOFileHandles[filenum]; + break; + } + } + + if (f) + { + char *fullpathname = va("%s/%s", "scriptdata", filename); + + // + // Prevent Scripts from Opening Files Outside of the ScriptData Directory + // + char* possibleRelativePath = strstr((char*)filename, ".."); + if (possibleRelativePath == filename && strlen(possibleRelativePath) >= 3) + { + if (possibleRelativePath[2] == '/' || possibleRelativePath[2] == '\\') + { + Com_Printf(24, "OpenFile failed. Relative paths are not allowed!\n"); + Scr_AddInt(-1, SCRIPTINSTANCE_CLIENT); + } + } + + if (!strcmp(mode, "read")) + { + int tempFile = NULL; + int filesize = FS_FOpenFileByMode(fullpathname, &tempFile, FS_READ); + + if (filesize >= 0) + { + client_openScriptIOFileBuffers[filenum] = (BYTE*)malloc(filesize + 1); + FS_Read(client_openScriptIOFileBuffers[filenum], filesize, tempFile); + FS_FCloseFile(tempFile); + client_openScriptIOFileBuffers[filenum][filesize] = 0; + + Com_BeginParseSession(filename); + //Com_SetCSV(1); + + //dword_3E53D10[5 * filenum] = 0; + + Scr_AddInt(filenum, SCRIPTINSTANCE_CLIENT); + } + else + { + Scr_AddInt(-1, SCRIPTINSTANCE_CLIENT); + } + } + else if (!strcmp(mode, "write")) + { + *f = FS_FOpenTextFileWrite(fullpathname); + if (*f) + Scr_AddInt(filenum, SCRIPTINSTANCE_CLIENT); + else + Scr_AddInt(-1, SCRIPTINSTANCE_CLIENT); + } + else if (!strcmp(mode, "append")) + { + if (FS_FOpenFileByMode(fullpathname, f, FS_APPEND) >= 0) + Scr_AddInt(filenum, SCRIPTINSTANCE_CLIENT); + else + Scr_AddInt(-1, SCRIPTINSTANCE_CLIENT); + } + else + { + Com_Printf(24, "Valid openfile modes are 'write', 'read', and 'append'\n"); + Scr_AddInt(-1, SCRIPTINSTANCE_CLIENT); + } + } + else + { + Com_Printf(24, "OpenFile failed. %i files already open\n", 1); + Scr_AddInt(-1, SCRIPTINSTANCE_CLIENT); + } + } +} + +void GScr_OpenFile() +{ + if (!developer->current.enabled || !developer_script->current.enabled) + { + Scr_AddInt(-1, SCRIPTINSTANCE_SERVER); + } + + int* f = 0; + if (Scr_GetNumParam(SCRIPTINSTANCE_SERVER) > 1) + { + const char* filename = Scr_GetString(0, SCRIPTINSTANCE_SERVER); + const char* mode = Scr_GetString(1, SCRIPTINSTANCE_SERVER); + + int filenum = 0; + for (filenum = 0; filenum < 1; ++filenum) + { + if (!level_openScriptIOFileHandles[filenum]) + { + f = &level_openScriptIOFileHandles[filenum]; + break; + } + } + + if (f) + { + char *fullpathname = va("%s/%s", "scriptdata", filename); + + // + // Prevent Scripts from Opening Files Outside of the ScriptData Directory + // + char* possibleRelativePath = strstr((char*)filename, ".."); + if (possibleRelativePath == filename && strlen(possibleRelativePath) >= 3) + { + if (possibleRelativePath[2] == '/' || possibleRelativePath[2] == '\\') + { + Com_Printf(24, "OpenFile failed. Relative paths are not allowed!\n"); + Scr_AddInt(-1, SCRIPTINSTANCE_SERVER); + } + } + + if (!strcmp(mode, "read")) + { + int tempFile = NULL; + int filesize = FS_FOpenFileByMode(fullpathname, &tempFile, FS_READ); + + if (filesize >= 0) + { + level_openScriptIOFileBuffers[filenum] = (BYTE*)malloc(filesize + 1); + FS_Read(level_openScriptIOFileBuffers[filenum], filesize, tempFile); + FS_FCloseFile(tempFile); + level_openScriptIOFileBuffers[filenum][filesize] = 0; + + Com_BeginParseSession(filename); + //Com_SetCSV(1); + + //dword_3E53D10[5 * filenum] = 0; + + Scr_AddInt(filenum, SCRIPTINSTANCE_SERVER); + } + else + { + Scr_AddInt(-1, SCRIPTINSTANCE_SERVER); + } + } + else if (!strcmp(mode, "write")) + { + *f = FS_FOpenTextFileWrite(fullpathname); + if (*f) + Scr_AddInt(filenum, SCRIPTINSTANCE_SERVER); + else + Scr_AddInt(-1, SCRIPTINSTANCE_SERVER); + } + else if (!strcmp(mode, "append")) + { + if (FS_FOpenFileByMode(fullpathname, f, FS_APPEND) >= 0) + Scr_AddInt(filenum, SCRIPTINSTANCE_SERVER); + else + Scr_AddInt(-1, SCRIPTINSTANCE_SERVER); + } + else + { + Com_Printf(24, "Valid openfile modes are 'write', 'read', and 'append'\n"); + Scr_AddInt(-1, SCRIPTINSTANCE_SERVER); + } + } + else + { + Com_Printf(24, "OpenFile failed. %i files already open\n", 1); + Scr_AddInt(-1, SCRIPTINSTANCE_SERVER); + } + } +} + +void CScr_CloseFile() +{ + if (!developer->current.enabled || !developer_script->current.enabled) + { + Scr_AddInt(-1, SCRIPTINSTANCE_CLIENT); + } + + if (Scr_GetNumParam(SCRIPTINSTANCE_CLIENT)) + { + int filenum = Scr_GetInt(0, SCRIPTINSTANCE_CLIENT); + if (filenum < 0 || filenum > 1) + { + Com_Printf(24, "CloseFile failed, invalid file number %i\n", filenum); + Scr_AddInt(-1, SCRIPTINSTANCE_CLIENT); + return; + } + + //ASSERT(!client_openScriptIOFileHandles[filenum] && !client_openScriptIOFileBuffers[filenum]); + + if (client_openScriptIOFileHandles[filenum]) + { + FS_FCloseFile((int)client_openScriptIOFileHandles[filenum]); + client_openScriptIOFileHandles[filenum] = 0; + } + else + { + if (!client_openScriptIOFileBuffers[filenum]) + { + Com_Printf(24, "CloseFile failed, file number %i was not open\n", filenum); + Scr_AddInt(-1, SCRIPTINSTANCE_CLIENT); + return; + } + + Com_EndParseSession(); + + free(client_openScriptIOFileBuffers[filenum]); + client_openScriptIOFileBuffers[filenum] = 0; + } + Scr_AddInt(1, SCRIPTINSTANCE_CLIENT); + } +} + +void GScr_CloseFile() +{ + if (!developer->current.enabled || !developer_script->current.enabled) + { + Scr_AddInt(-1, SCRIPTINSTANCE_SERVER); + } + + if (Scr_GetNumParam(SCRIPTINSTANCE_SERVER)) + { + int filenum = Scr_GetInt(0, SCRIPTINSTANCE_SERVER); + if (filenum < 0 || filenum > 1) + { + Com_Printf(24, "CloseFile failed, invalid file number %i\n", filenum); + Scr_AddInt(-1, SCRIPTINSTANCE_SERVER); + return; + } + + //ASSERT(!level_openScriptIOFileHandles[filenum] && !level_openScriptIOFileBuffers[filenum]); + + if (level_openScriptIOFileHandles[filenum]) + { + FS_FCloseFile((int)level_openScriptIOFileHandles[filenum]); + level_openScriptIOFileHandles[filenum] = 0; + } + else + { + if (!level_openScriptIOFileBuffers[filenum]) + { + Com_Printf(24, "CloseFile failed, file number %i was not open\n", filenum); + Scr_AddInt(-1, SCRIPTINSTANCE_SERVER); + return; + } + + Com_EndParseSession(); + + free(level_openScriptIOFileBuffers[filenum]); + level_openScriptIOFileBuffers[filenum] = 0; + } + Scr_AddInt(1, SCRIPTINSTANCE_SERVER); + } +} + +void __cdecl CScr_FPrint_internal(bool commaBetweenFields) +{ + if (!developer->current.enabled || !developer_script->current.enabled) + { + Scr_AddInt(-1, SCRIPTINSTANCE_CLIENT); + } + + if (Scr_GetNumParam(SCRIPTINSTANCE_CLIENT) > 1) + { + int filenum = Scr_GetInt(0, SCRIPTINSTANCE_CLIENT); + if (filenum >= 0 && filenum <= 1) + { + if (client_openScriptIOFileHandles[filenum]) + { + for (unsigned int arg = 1; arg < Scr_GetNumParam(SCRIPTINSTANCE_CLIENT); ++arg) + { + const char* s = Scr_GetString(arg, SCRIPTINSTANCE_CLIENT); + FS_Write(s, strlen(s), client_openScriptIOFileHandles[filenum]); + + if (commaBetweenFields) + FS_Write(",", 1, client_openScriptIOFileHandles[filenum]); + } + + FS_Write("\n", 1, client_openScriptIOFileHandles[filenum]); + + int val = Scr_GetNumParam(SCRIPTINSTANCE_CLIENT); + Scr_AddInt(val - 1, SCRIPTINSTANCE_CLIENT); + } + else + { + Com_Printf(24, "FPrintln failed, file number %i was not open for writing\n", filenum); + Scr_AddInt(-1, SCRIPTINSTANCE_CLIENT); + } + } + else + { + Com_Printf(24, "FPrintln failed, invalid file number %i\n", filenum); + Scr_AddInt(-1, SCRIPTINSTANCE_CLIENT); + } + } + else + { + Com_Printf(24, "fprintln requires at least 2 parameters (file, output)\n"); + Scr_AddInt(-1, SCRIPTINSTANCE_CLIENT); + } +} + +void __cdecl Scr_FPrint_internal(bool commaBetweenFields) +{ + if (!developer->current.enabled || !developer_script->current.enabled) + { + Scr_AddInt(-1, SCRIPTINSTANCE_SERVER); + } + + if (Scr_GetNumParam(SCRIPTINSTANCE_SERVER) > 1) + { + int filenum = Scr_GetInt(0, SCRIPTINSTANCE_SERVER); + if (filenum >= 0 && filenum <= 1) + { + if (level_openScriptIOFileHandles[filenum]) + { + for (unsigned int arg = 1; arg < Scr_GetNumParam(SCRIPTINSTANCE_SERVER); ++arg) + { + const char* s = Scr_GetString(arg, SCRIPTINSTANCE_SERVER); + FS_Write(s, strlen(s), level_openScriptIOFileHandles[filenum]); + + if (commaBetweenFields) + FS_Write(",", 1, level_openScriptIOFileHandles[filenum]); + } + + FS_Write("\n", 1, level_openScriptIOFileHandles[filenum]); + + int val = Scr_GetNumParam(SCRIPTINSTANCE_SERVER); + Scr_AddInt(val - 1, SCRIPTINSTANCE_SERVER); + } + else + { + Com_Printf(24, "FPrintln failed, file number %i was not open for writing\n", filenum); + Scr_AddInt(-1, SCRIPTINSTANCE_SERVER); + } + } + else + { + Com_Printf(24, "FPrintln failed, invalid file number %i\n", filenum); + Scr_AddInt(-1, SCRIPTINSTANCE_SERVER); + } + } + else + { + Com_Printf(24, "fprintln requires at least 2 parameters (file, output)\n"); + Scr_AddInt(-1, SCRIPTINSTANCE_SERVER); + } +} + +void CScr_FPrintln() +{ + CScr_FPrint_internal(0); +} + +void GScr_FPrintln() +{ + Scr_FPrint_internal(0); +} + +void CScr_FPrintFields() +{ + CScr_FPrint_internal(1); +} + +void GScr_FPrintFields() +{ + Scr_FPrint_internal(1); +} + diff --git a/components/game_mod/scr_cmds.h b/components/game_mod/scr_cmds.h new file mode 100644 index 00000000..4239d864 --- /dev/null +++ b/components/game_mod/scr_cmds.h @@ -0,0 +1,32 @@ +#pragma once +#include "cscr_parser.h" + +struct BuiltinFunctionDef +{ + const char *actionString; + void(__cdecl *actionFunc)(); + int type; +}; + +typedef unsigned int(__cdecl* Scr_GetNumParam_t)(scriptInstance_t inst); +static Scr_GetNumParam_t Scr_GetNumParam = (Scr_GetNumParam_t)0x005C6DA0; + +typedef int (__cdecl* Scr_GetInt_t)(unsigned int index, scriptInstance_t inst); +static Scr_GetInt_t Scr_GetInt = (Scr_GetInt_t)0x004C1BB0; + +typedef void (__cdecl* Scr_AddInt_t)(int value, scriptInstance_t inst); +static Scr_AddInt_t Scr_AddInt = (Scr_AddInt_t)0x0045DBB0; + +void CScr_OpenFile(); +void GScr_OpenFile(); + +void CScr_CloseFile(); +void GScr_CloseFile(); + +void CScr_FPrintln(); +void GScr_FPrintln(); + +void CScr_FPrintFields(); +void GScr_FPrintFields(); + +void Scr_PatchFunctions(); diff --git a/components/game_mod/stdafx.h b/components/game_mod/stdafx.h index 9f67c112..380c9d10 100644 --- a/components/game_mod/stdafx.h +++ b/components/game_mod/stdafx.h @@ -1,5 +1,8 @@ #pragma once +#pragma comment(lib, "detours.lib") +#include "../shared/detours/Detours.h" + #define WIN32_LEAN_AND_MEAN #include #include @@ -7,42 +10,82 @@ #include #include #include +#include +#include +#include #pragma comment(lib, "ws2_32.lib") // // Shared files // -#include "../shared/utility.h" +#include "../shared/shared_utility.h" +#include "../shared/shared_version.h" + #include "../shared/minidx9/Include/d3dx9.h" #pragma comment(lib, "../shared/minidx9/Lib/x86/d3dx9.lib") #include "CEG.h" +#include "bitarray.h" +#include "d3d9ex.h" #include "common.h" #include "com_files.h" +#include "com_math.h" +#include "com_memory.h" #include "cmd.h" #include "dvar.h" +#include "g_main_mp.h" +#include "bg_weapons_def.h" +#include "bg_weapons_ammo.h" +#include "bg_weapons_load_obj.h" +#include "g_items.h" #include "sv_ccmds_mp.h" +#include "sv_client_mp.h" #include "cl_main_mp.h" #include "cl_console.h" #include "db_registry.h" #include "ui_main_pc.h" #include "live_win.h" #include "cg_scoreboard.h" +#include "cg_draw_debug.h" #include "sys_cmds.h" +#include "r_init.h" +#include "r_image.h" +#include "r_debug.h" #include "r_rendercmds.h" #include "r_cinematic.h" #include "r_screenshot.h" #include "r_scene.h" #include "r_reflection_probe.h" +#include "r_material.h" #include "r_material_load_obj.h" +#include "r_draw_xmodel.h" +#include "rb_tess.h" +#include "cscr_vm.h" +#include "scr_cmds.h" +#include "cscr_parser.h" #include "threads.h" +#include "ragdoll.h" +#include "ragdoll_update.h" +#include "phys_main.h" +#include "com_bsp_load_obj.h" +#include "bullet.h" +#include "cg_weapons.h" +#include "bg_perks.h" +#include "cl_debugdata.h" +#include "bg_weapons.h" +#include "cl_keys.h" +#include "sv_main_mp.h" +#include "pathnode.h" +#include "pathnode_load_obj.h" #include "radiant_remote.h" +#include "g_client_script_cmd_mp.h" #include "win_localize.h" #include "win_exception.h" #include "win_common.h" +#include "win_main.h" #include "patch_common.h" #include "patch_reflections.h" @@ -51,4 +94,5 @@ #include "reshade.h" #define GM_NET_VERSION 0x01 -#define _USE_COM_DPRINTF 0 + +#define _USE_LEVEL_DEPENDENCIES 1 diff --git a/components/game_mod/sv_ccmds_mp.cpp b/components/game_mod/sv_ccmds_mp.cpp index 925739ad..af48bcf9 100644 --- a/components/game_mod/sv_ccmds_mp.cpp +++ b/components/game_mod/sv_ccmds_mp.cpp @@ -33,21 +33,67 @@ void __declspec(naked) SV_ValidateMap() PartyClient_CheckMapExists_t PartyClient_CheckMapExists_o = (PartyClient_CheckMapExists_t)0x0; bool __cdecl PartyClient_CheckMapExists(const char *map) { - if (!I_strncmp("so_", map, strlen("so_"))) + if (Com_IsSpecopLevel(map)) { const char* basename = nullptr; + + // Loop through each char until a '_' char is found. for (basename = &map[strlen("so_")]; *basename && *basename != '_'; ++basename) - { - // Loop through each char until a '_' char is found. - } + /* Do nothing */; - if (*basename == NULL) - { + if (!*basename) Com_PrintError(1, "Bad specop level name\n"); - } map = basename + 1; } return PartyClient_CheckMapExists_o(map); +} + +playerState_s *SV_GameClientNum(int num) +{ + return ((playerState_s *(__cdecl *)(int))0x0068FAB0)(num); +} + +clientState_s *G_GetClientState(int clientNum) +{ + return ((clientState_s *(__cdecl *)(int))0x005DA470)(clientNum); +} + +client_t *SV_GetPlayerByName() +{ + return ((client_t *(__cdecl *)())0x0087C350)(); +} + +void SV_SetPerk_f() +{ + client_t *cl = SV_GetPlayerByName(); + + if (!cl) + { + Com_DPrintf(0, "Unknown player\n"); + return; + } + + // Determine if the perk was valid + const char *perkName = SV_Cmd_Argv(2); + unsigned int perkIndex = BG_GetPerkIndexForName(perkName); + + if (perkIndex == PERK_UNKNOWN) + { + Com_DPrintf(0, "Unknown perk: %s\n", perkName); + return; + } + + // Determine the server index for this player + int i = 0; + + for (client_t *clIdx = *(client_t **)0x286D01C; i < com_maxclients->current.integer && clIdx != cl; clIdx++) + ++i; + + ASSERT_MSG(i < com_maxclients->current.integer, "i doesn't index com_maxclients->current.integer"); + + // Assign perk bit flag to player + BG_SetPerk(SV_GameClientNum(i)->perks, perkIndex); + BG_SetPerk(G_GetClientState(i)->perks, perkIndex); } \ No newline at end of file diff --git a/components/game_mod/sv_ccmds_mp.h b/components/game_mod/sv_ccmds_mp.h index f688629c..63a6dd46 100644 --- a/components/game_mod/sv_ccmds_mp.h +++ b/components/game_mod/sv_ccmds_mp.h @@ -10,3 +10,5 @@ typedef bool(__cdecl* PartyClient_CheckMapExists_t)(const char *map); extern PartyClient_CheckMapExists_t PartyClient_CheckMapExists_o; bool __cdecl PartyClient_CheckMapExists(const char *map); + +void SV_SetPerk_f(); \ No newline at end of file diff --git a/components/game_mod/sv_client_mp.cpp b/components/game_mod/sv_client_mp.cpp new file mode 100644 index 00000000..a8ece6fc --- /dev/null +++ b/components/game_mod/sv_client_mp.cpp @@ -0,0 +1,110 @@ +#include "stdafx.h" + +int& gametime = *(int *)0x0286D014; + +int MSG_ReadLong(msg_t *msg) +{ + return ((int (__cdecl *)(msg_t *))0x0061C490)(msg); +} + +char *MSG_ReadString(msg_t *msg, char *string, unsigned int maxChars) +{ + ASSERT(string); + + return ((char *(__cdecl *)(msg_t *, char *, unsigned int))0x005B0A80)(msg, string, maxChars); +} + +// /server_mp/sv_client_mp.cpp:2303 +void SV_DropClient(client_t *drop, const char *reason, bool tellThem, bool writeStats) +{ + ((void(__cdecl *)(client_t *, const char *, bool))0x0051F980)(drop, reason, tellThem); +} + +// /server_mp/sv_client_mp.cpp:4358 +void SV_ExecuteClientCommand(client_t *cl, const char *s, int clientOK, int fromOldServer) +{ + ((void (__cdecl *)(client_t *, const char *, int))0x004FB320)(cl, s, clientOK); +} + +// /server_mp/sv_client_mp.cpp:4394 +int SV_ClientCommand(client_t *cl, msg_t *msg, int fromOldServer) +{ + // ASSERT_MSG(cl - svs_clients) / 544000) >= com_maxclients->current.integer, "cl - svs.clients doesn't index com_maxclients->current.integer"); + + char strBuf[1024]; + int seq = MSG_ReadLong(msg); + const char *s = MSG_ReadString(msg, strBuf, 1024); + + bool clientOk = true; + bool floodprotect = sv_floodProtect->current.integer && cl->header.state >= 5 && cl->header.netchan.remoteAddress.type != 2; + + // If current command is older than previous, skip + if (cl->lastClientCommand >= seq) + return 1; + + // if (sv_showCommands->current.enabled) + Com_Printf(15, "clientCommand: %i : %s\n", seq, s); + + // Check if the client skipped/dropped commands on their end (behind server) + if (seq > cl->lastClientCommand + 1) + { + Com_Printf(15, "Client %s lost %i clientCommands\n", cl->name, seq - cl->lastClientCommand + 1); + SV_DropClient(cl, "EXE_LOSTRELIABLECOMMANDS", true, true); + return 0; + } + + // Specific commands are whitelisted from floodprotect + if (!_strnicmp("team ", s, 5) || + !_strnicmp("score", s, 5) || + !_strnicmp("mr ", s, 3) || + !_strnicmp("act", s, 3)) + floodprotect = false; + + ASSERT(cl->nextReliableCount >= 0); + + // Ignore if too many were sent in under 800ms + if (fromOldServer || floodprotect && !cl->nextReliableCount && gametime < cl->nextReliableTime) + { + clientOk = false; + Com_DPrintf(15, "Client text ignored for %s: %s\n", cl->name, s); + } + + if (floodprotect) + { + ASSERT(sv_floodProtect->current.integer != 0); + + if (gametime >= cl->nextReliableTime) + { + cl->nextReliableCount = sv_floodProtect->current.integer - 1; + } + else + { + if (cl->nextReliableCount - 1 > 0) + cl->nextReliableCount -= 1; + else + cl->nextReliableCount = 0; + } + + cl->nextReliableTime = gametime + 800; + } + + // Parse & execute buffer + SV_ExecuteClientCommand(cl, s, clientOk, fromOldServer); + + cl->lastClientCommand = seq; + sprintf_s(cl->lastClientCommandString, "%s", s); + return 1; +} + +void __declspec(naked) hk_SV_ClientCommand() +{ + __asm + { + push 0 + push[esp + 8] + push eax + call SV_ClientCommand + add esp, 0xC + retn + } +} \ No newline at end of file diff --git a/components/game_mod/sv_client_mp.h b/components/game_mod/sv_client_mp.h new file mode 100644 index 00000000..c2676577 --- /dev/null +++ b/components/game_mod/sv_client_mp.h @@ -0,0 +1,12 @@ +#pragma once + +struct msg_t +{ + char wtf[1]; +}; + +void SV_DropClient(client_t *drop, const char *reason, bool tellThem, bool writeStats); +void SV_ExecuteClientCommand(client_t *cl, const char *s, int clientOK, int fromOldServer); +int SV_ClientCommand(client_t *cl, msg_t *msg, int fromOldServer); + +void hk_SV_ClientCommand(); \ No newline at end of file diff --git a/components/game_mod/sv_main_mp.cpp b/components/game_mod/sv_main_mp.cpp new file mode 100644 index 00000000..4cb1dd96 --- /dev/null +++ b/components/game_mod/sv_main_mp.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" + +// /server_mp/sv_main_mp.cpp:2086 +void SV_CheckTimeouts() +{ + ((void(__cdecl *)())0x004F5810)(); +} + +// /server_mp/sv_main_mp.cpp:2264 +void SV_PostFrame(bool FrameTimeout) +{ + RadiantRemoteUpdate(); // + // ((void(__cdecl *)(int))0x00000000)(0); // Scr_UpdateDebugger + ((void(__cdecl *)())0x004BC500)(); // SV_ProcessPendingSaves + SV_CheckTimeouts(); // + ((void(__cdecl *)(bool))0x00465890)(FrameTimeout); // SV_SendClientMessages + ((void(__cdecl *)())0x00601CC0)(); // ???? +} \ No newline at end of file diff --git a/components/game_mod/sv_main_mp.h b/components/game_mod/sv_main_mp.h new file mode 100644 index 00000000..41fdcf0c --- /dev/null +++ b/components/game_mod/sv_main_mp.h @@ -0,0 +1,4 @@ +#pragma once + +void SV_CheckTimeouts(); +void SV_PostFrame(bool Unknown); \ No newline at end of file diff --git a/components/game_mod/sys_cmds.cpp b/components/game_mod/sys_cmds.cpp new file mode 100644 index 00000000..c4376767 --- /dev/null +++ b/components/game_mod/sys_cmds.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" + +SysInfo& sys_info = *(SysInfo*)0x0276F2F0; + +bool __cdecl Sys_ShouldUpdateForInfoChange() +{ + Sys_ArchiveInfo(0); + + // Don't ask when building reflections or paths + if (G_OnlyConnectingPaths() || r_reflectionProbeGenerate->current.integer != 0) + { + return false; + } + + const char* title = Win_LocalizeRef("WIN_COMPUTER_CHANGE_TITLE"); + const char* body = Win_LocalizeRef("WIN_COMPUTER_CHANGE_BODY"); + const HWND hWnd = GetActiveWindow(); + + return MessageBoxA(hWnd, body, title, MB_ICONINFORMATION | MB_YESNO) == IDYES; +} + +bool __cdecl Sys_HasInfoChanged() +{ + Sys_RegisterInfoDvars(); + if (sys_configureGHz->current.value <= sys_info.configureGHz * 1.100000023841858 + && sys_info.configureGHz * 0.8999999761581421 <= sys_configureGHz->current.value) + { + if (sys_sysMB->current.integer <= sys_info.sysMB + 32 && sys_sysMB->current.integer >= sys_info.sysMB - 32) + { + if (!strcmp(sys_gpu->current.string, sys_info.gpuDescription)) + return false; + } + } + + return Sys_ShouldUpdateForInfoChange(); +} diff --git a/components/game_mod/sys_cmds.h b/components/game_mod/sys_cmds.h index 2c675a8f..cc5dffb1 100644 --- a/components/game_mod/sys_cmds.h +++ b/components/game_mod/sys_cmds.h @@ -1,5 +1,20 @@ #pragma once +struct __declspec(align(8)) SysInfo +{ + long double cpuGHz; + long double configureGHz; + int logicalCpuCount; + int physicalCpuCount; + int sysMB; + char gpuDescription[512]; + bool SSE; + char cpuVendor[13]; + char cpuName[49]; +}; + +extern SysInfo& sys_info; + typedef int Sys_ResetUpdateSpotLightEffectEvent_t(void); static Sys_ResetUpdateSpotLightEffectEvent_t* Sys_ResetUpdateSpotLightEffectEvent = (Sys_ResetUpdateSpotLightEffectEvent_t*)0x0048D2D0; @@ -11,3 +26,11 @@ static Sys_AddWorkerCmdInternal_t* Sys_AddWorkerCmdInternal = (Sys_AddWorkerCmdI typedef const char* __cdecl Sys_Cwd_t(void); static Sys_Cwd_t* Sys_Cwd = (Sys_Cwd_t*)0x0068B1A0; + +typedef struct dvar_s* __cdecl Sys_RegisterInfoDvars_t(); +static Sys_RegisterInfoDvars_t* Sys_RegisterInfoDvars = (Sys_RegisterInfoDvars_t*)0x00867980; + +typedef void __cdecl Sys_ArchiveInfo_t(int checksum); +static Sys_ArchiveInfo_t* Sys_ArchiveInfo = (Sys_ArchiveInfo_t*)0x00610A00; + +bool __cdecl Sys_HasInfoChanged(void); diff --git a/components/game_mod/threads.cpp b/components/game_mod/threads.cpp index dd13814c..ae1c662e 100644 --- a/components/game_mod/threads.cpp +++ b/components/game_mod/threads.cpp @@ -1,7 +1,5 @@ #include "stdafx.h" -#define THREAD_CONTEXT_COUNT 15 - auto threadFunc = (void(__cdecl **)(unsigned int))0x0251CB44; HANDLE *threadHandle = (HANDLE *)0x0251CADC; DWORD *threadId = (DWORD *)0x0251CA98; @@ -11,7 +9,7 @@ void Sys_ConsoleThread(unsigned int index) // Loop forever until someone enables this while (true) { - if (con_extcon && con_extcon->current.value) + if (con_extcon && con_extcon->current.enabled) break; Sleep(50); @@ -48,5 +46,15 @@ void Sys_CreateThread(void(__cdecl * function)(unsigned int index), unsigned int DWORD WINAPI Sys_ThreadMain(LPVOID Arg) { - return ((DWORD(WINAPI *)(LPVOID))0x0082FDE0)(Arg); + return ((LPTHREAD_START_ROUTINE)0x0082FDE0)(Arg); +} + +bool Sys_IsMainThread() +{ + return true; +} + +bool Sys_IsRenderThread() +{ + return true; } \ No newline at end of file diff --git a/components/game_mod/threads.h b/components/game_mod/threads.h index f39cb4f2..2dec95a3 100644 --- a/components/game_mod/threads.h +++ b/components/game_mod/threads.h @@ -1,5 +1,9 @@ #pragma once +#define THREAD_CONTEXT_COUNT 15 + void Sys_ConsoleThread(unsigned int index); void Sys_CreateThread(void(__cdecl * function)(unsigned int index), unsigned int threadContext); -DWORD WINAPI Sys_ThreadMain(LPVOID Arg); \ No newline at end of file +DWORD WINAPI Sys_ThreadMain(LPVOID Arg); +bool Sys_IsMainThread(); +bool Sys_IsRenderThread(); \ No newline at end of file diff --git a/components/game_mod/ui_main_pc.cpp b/components/game_mod/ui_main_pc.cpp index 9bfee2e0..175fc6fe 100644 --- a/components/game_mod/ui_main_pc.cpp +++ b/components/game_mod/ui_main_pc.cpp @@ -1,4 +1,5 @@ #include "stdafx.h" +#include "jsoncpp\include\json\json.h" #define ARENA_FILE_MAX_SIZE 8192 @@ -7,6 +8,33 @@ sharedUiInfo_t * const sharedUiInfo = (sharedUiInfo_t * const)0x025760F0; int& ui_numArenas = *(int*)0x025F6940; char** ui_arenaInfos = *(char*(*)[128])0x025F6740; +// +// Needed because the Detours lib can't handle relocating relative calls +// +void __declspec(naked) UI_OpenMenu_f_o(void) +{ + static DWORD UI_GetInfo = 0x0046BE80; + static DWORD dwRetn = 0x00847219; + + _asm + { + push esi + push edi + push 0 + call UI_GetInfo + jmp dwRetn + } +} + +void __cdecl UI_OpenMenu_f(void) +{ + if (sv_mapname && sv_mapname->current.string && cl_ingame && cl_ingame->current.enabled) + { + if (strcmp(sv_mapname->current.string, "frontend") == 0) + UI_OpenMenu_f_o(); + } +} + bool __cdecl UI_LoadModArenas() { int fileHandle = 0; @@ -79,4 +107,138 @@ void __declspec(naked) mfh_UI_FeederSelection_AllMaps() movss xmm0, [esp + 0x10] jmp rtn_UI_FeederSelection_AllMaps } +} + +bool __cdecl GetOperand(OperandStack *dataStack, Operand *data) +{ + bool result = false; + + _asm + { + mov edi, data + mov esi, dataStack + mov eax, 0x00851930 + call eax + mov result, al + } + + return result; +} + +const char* UI_GetModInfo_JSon_GetString(const char* str, const char* key, char* buf, unsigned int buf_len) +{ + memset(buf, 0, buf_len); + + Json::Reader reader; + Json::Value root; + + const char* nullstr = ""; +#if _DEBUG + const bool isDebug = true; + #else + const bool isDebug = false; +#endif + + if (!reader.parse(str, root)) + return isDebug ? "Couldn't parse JSon" : nullstr; + + if (!root.isObject()) + return isDebug ? "Root isn't an object!" : nullstr; + + if (!root.isMember(key)) + return isDebug ? "Can't find key" : nullstr; + + if (!root[key].isString()) + return isDebug ? "Key does not refer to a string type" : nullstr;; + + std::string tmp = root[key].asString(); // Ensures the string data isn't destroyed before strcpy runs + strcpy_s(buf, buf_len, tmp.c_str()); + return buf; +} + +const char* UI_GetModInfo_GetDescription(const char* str) +{ + static char desc[MODDESC_LEN]; + return UI_GetModInfo_JSon_GetString(str , "desc", desc, ARRAYSIZE(desc)); +} + +const char* UI_GetModInfo_GetCompatibilityInfo(const char* str) +{ + const char* nullstr = ""; + + char versionString[32]; + const char* data = UI_GetModInfo_JSon_GetString(str, "comp", versionString, ARRAYSIZE(versionString)); + if (!*data) + return nullstr; + + static char compInfo[MAX_PATH]; + compInfo[0] = '\0'; + + Version_t dep_ver(data); + if (!semver::v2::IsCompatibleVersion(dep_ver, (Version_t&)DLL_Version())) + { + sprintf_s(compInfo, "^1Requires Game_Mod %d.%d.%d+\n", + dep_ver.GetMajorVersion(), + dep_ver.GetMinorVersion(), + dep_ver.GetPatchVersion()); + return compInfo; + } + + return nullstr; +} + +void __cdecl UI_GetModInfo(const int localClientNum, struct itemDef_s *item, OperandStack *dataStack) +{ + const char *str = "GetModInfo unhandled"; + + Operand operand; + GetOperand(dataStack, &operand); + const char* src = GetSourceString(operand); + if (_stricmp(src, "modName") == 0) + str = sharedUiInfo_modList[sharedUiInfo_modIndex].modName; + else if (_stricmp(src, "modDescr") == 0) + { + FS_ModDesc* desc = (FS_ModDesc*)sharedUiInfo_modList[sharedUiInfo_modIndex].modDescr; + if (desc != NULL) + { + switch (desc->type) + { + case DESC_DESC: + str = desc->data; + break; + case DESC_JSON: + str = UI_GetModInfo_GetDescription(desc->data); + break; + default: + str = ""; + break; + } + } + } + else if (_stricmp(src, "modCompat") == 0) + { + FS_ModDesc* desc = (FS_ModDesc*)sharedUiInfo_modList[sharedUiInfo_modIndex].modDescr; + if (desc != NULL) + { + switch (desc->type) + { + case DESC_JSON: + str = UI_GetModInfo_GetCompatibilityInfo(desc->data); + break; + case DESC_DESC: + // We just assume that legacy mods are compatible for now + default: + str = ""; + break; + } + } + } + + if (uiscript_debug && uiscript_debug->current.integer) + Expression_TraceInternal("GetModInfo() = %s\n", str); + + int index = dataStack->numOperandLists; + dataStack->stack[index].operands[0].dataType = VAL_STRING; + dataStack->stack[index].operands[0].internals.string = str; + dataStack->stack[dataStack->numOperandLists++].operandCount = 1; } \ No newline at end of file diff --git a/components/game_mod/ui_main_pc.h b/components/game_mod/ui_main_pc.h index 4283fda6..788aa24c 100644 --- a/components/game_mod/ui_main_pc.h +++ b/components/game_mod/ui_main_pc.h @@ -25,6 +25,7 @@ struct CachedAssets_t void *itemFocusSound; // snd_alias_list_t* }; +#pragma pack(push, 2) struct __declspec(align(2)) mapInfo { char mapName[32]; @@ -45,6 +46,51 @@ struct __declspec(align(2)) sharedUiInfo_t mapInfo mapList[128]; char gap_tmp[1]; }; +#pragma pack(pop) + + +enum expDataType +{ + VAL_INT = 0x0, + VAL_FLOAT = 0x1, + VAL_STRING = 0x2, +}; + +union operandInternalDataUnion +{ + int intVal; + float floatVal; + const char *string; +}; + +struct Operand +{ + expDataType dataType; + operandInternalDataUnion internals; +}; + +struct OperandList +{ + Operand operands[10]; + int operandCount; +}; + +struct OperandStack +{ + OperandList stack[60]; + int numOperandLists; +}; + +struct modInfo_t +{ + const char *modName; + const char *modDescr; +}; + +VANILLA_VALUE(sharedUiInfo_modCount, int, 0x02580BA4); +VANILLA_VALUE(sharedUiInfo_modIndex, int, 0x02580BA8); + +static modInfo_t* sharedUiInfo_modList = (modInfo_t*)0x025805A4; extern sharedUiInfo_t * const sharedUiInfo; @@ -62,9 +108,19 @@ static Live_UpdateUiPopup_t* Live_UpdateUiPopup = (Live_UpdateUiPopup_t*)0x005C8 typedef void (__cdecl* UI_SetLoadingScreenMaterial_t)(const char *name); static UI_SetLoadingScreenMaterial_t UI_SetLoadingScreenMaterial = (UI_SetLoadingScreenMaterial_t)0x005C6F70; +typedef const char *(__cdecl* GetSourceString_t)(Operand operand); +static GetSourceString_t GetSourceString = (GetSourceString_t)0x00579BC0; + +typedef void (*Expression_TraceInternal_t)(const char *str, ...); +static Expression_TraceInternal_t Expression_TraceInternal = (Expression_TraceInternal_t)0x0049E210; + +void __cdecl UI_OpenMenu_f(void); + // // Functionally the same as UI_SelectedMap except it returns mapLoadName // const char* __cdecl UI_SelectedMap_LoadName(int index, int* actual); -void mfh_UI_FeederSelection_AllMaps(); \ No newline at end of file +void mfh_UI_FeederSelection_AllMaps(); + +void __cdecl UI_GetModInfo(const int localClientNum, struct itemDef_s *item, OperandStack *dataStack); diff --git a/components/game_mod/version.h b/components/game_mod/version.h new file mode 100644 index 00000000..5523270e --- /dev/null +++ b/components/game_mod/version.h @@ -0,0 +1,28 @@ +#pragma once +#include "semver\semantic_version.h" + +// +// The library's comparison operators are screwed up - so we need a custom comparison func +// +namespace semver +{ + namespace v2 + { + static bool IsCompatibleVersion(Version& constraint, Version& version) + { + if (constraint.GetMajorVersion() != version.GetMajorVersion()) + return false; + + if (constraint.GetMajorVersion() > version.GetMajorVersion()) + return false; + + if (constraint.GetMajorVersion() < version.GetMajorVersion()) + return true; + + if (constraint.GetPatchVersion() > version.GetPatchVersion()) + return false; + + return true; + } + } +} diff --git a/components/game_mod/win_common.cpp b/components/game_mod/win_common.cpp index b912d79b..1577c4e3 100644 --- a/components/game_mod/win_common.cpp +++ b/components/game_mod/win_common.cpp @@ -1,30 +1 @@ -#include "stdafx.h" - -void __cdecl Sys_OutOfMemErrorInternal(const char *filename, int line) -{ - Sys_EnterCriticalSection(CRITSECT_FATAL_ERROR); - Com_Printf(16, "Out of memory: filename '%s', line %d\n", filename, line); - - const char* title = Win_LocalizeRef("WIN_OUT_OF_MEM_TITLE"); - const char* body = Win_LocalizeRef("WIN_OUT_OF_MEM_BODY"); - HWND hWnd = GetActiveWindow(); - - char msg[EXCEPTION_STR_MAXLEN]; - sprintf_s(msg, "%s\n\n", body); - - void* frame[EXCEPTION_STACKTRACE_MAXFRAMECOUNT]; - int frameCount = CaptureStackBackTrace(0, ARRAYSIZE(frame), frame, nullptr); - - if (frameCount != 0) - strcat_s(msg, "Stack Trace:\n"); - - for (int i = 0; i < frameCount; i++) - { - char buf[256]; - sprintf_s(buf, "frame[%s%d]: %08x%s", i < 10 ? " " : "", i, frame[i], i % 2 ? "\n" : "\t"); - strcat_s(msg, buf); - } - - MessageBoxA(hWnd, msg, title, MB_ICONERROR); - exit(-1); -} +#include "stdafx.h" \ No newline at end of file diff --git a/components/game_mod/win_common.h b/components/game_mod/win_common.h index 35c95dd2..1430c2e9 100644 --- a/components/game_mod/win_common.h +++ b/components/game_mod/win_common.h @@ -9,6 +9,4 @@ typedef void(__cdecl* Sys_EnterCriticalSection_t)(CriticalSection critSect); static Sys_EnterCriticalSection_t Sys_EnterCriticalSection = (Sys_EnterCriticalSection_t)0x005FEA60; typedef void(__cdecl* Sys_LeaveCriticalSection_t)(CriticalSection critSect); -static Sys_LeaveCriticalSection_t Sys_LeaveCriticalSection = (Sys_LeaveCriticalSection_t)0x0056D400; - -void __cdecl Sys_OutOfMemErrorInternal(const char *filename, int line); +static Sys_LeaveCriticalSection_t Sys_LeaveCriticalSection = (Sys_LeaveCriticalSection_t)0x0056D400; \ No newline at end of file diff --git a/components/game_mod/win_exception.cpp b/components/game_mod/win_exception.cpp index 26db40f2..3285a4c8 100644 --- a/components/game_mod/win_exception.cpp +++ b/components/game_mod/win_exception.cpp @@ -1,4 +1,5 @@ #include "stdafx.h" +#include static_assert(EXCEPTION_STACKTRACE_MAXFRAMECOUNT < 63, "EXCEPTION_STACKTRACE_MAXFRAMECOUNT must be less than 63 to support Windows XP"); @@ -17,7 +18,7 @@ LONG WINAPI PrivateUnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) sprintf_s(buf, "Exception Address: %p\n\n", ExceptionInfo->ExceptionRecord->ExceptionAddress); strcat_s(g_ExceptionStr, buf); - if (ExceptionInfo->ContextRecord->ContextFlags & 0x10001) + if (ExceptionInfo->ContextRecord->ContextFlags & CONTEXT_CONTROL) { sprintf_s(buf, "EBP: %08x\tEIP: %08x\nESP: %08x\tEFLAGS: %08x\n\nSEGCS: %08x\tSEGSS: %08x\t\n", ExceptionInfo->ContextRecord->Ebp, @@ -29,7 +30,7 @@ LONG WINAPI PrivateUnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) strcat_s(g_ExceptionStr, buf); } - if (ExceptionInfo->ContextRecord->ContextFlags & 0x10004) + if (ExceptionInfo->ContextRecord->ContextFlags & CONTEXT_SEGMENTS) { sprintf_s(buf, "SEGGS: %08x\tSEGFS: %08x\nSEGES: %08x\tSEGDS: %08x\n", ExceptionInfo->ContextRecord->SegGs, @@ -39,10 +40,10 @@ LONG WINAPI PrivateUnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) strcat_s(g_ExceptionStr, buf); } - if (ExceptionInfo->ContextRecord->ContextFlags & (0x10001 | 0x10004)) + if (ExceptionInfo->ContextRecord->ContextFlags & (CONTEXT_CONTROL | CONTEXT_SEGMENTS)) strcat_s(g_ExceptionStr, "\n"); - if (ExceptionInfo->ContextRecord->ContextFlags & 0x10002) + if (ExceptionInfo->ContextRecord->ContextFlags & CONTEXT_INTEGER) { sprintf_s(buf, "EDI: %08x\tESI: %08x\nEAX: %08x\tEBX: %08x\nECX: %08x\tEDX: %08x\n\n", ExceptionInfo->ContextRecord->Edi, @@ -54,60 +55,71 @@ LONG WINAPI PrivateUnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) strcat_s(g_ExceptionStr, buf); } - // - // Print the stack context bytes (disabled due to overflow issues) - // - /*if (ExceptionInfo->ContextRecord->ContextFlags & 0x10001) + HMODULE dbgHelp = LoadLibraryA("dbghelp.dll"); + + if (dbgHelp) { - strcat_s( g_ExceptionStr, EXCEPTION_STR_MAXLEN, "Stack Bytes: \n"); + auto pStackWalk64 = (decltype(&StackWalk64))GetProcAddress(dbgHelp, "StackWalk64"); + auto pFunctionTableAccess = (decltype(&SymFunctionTableAccess64))GetProcAddress(dbgHelp, "SymFunctionTableAccess64"); + auto pGetModuleBase = (decltype(&SymGetModuleBase64))GetProcAddress(dbgHelp, "SymGetModuleBase64"); - for (int i = 0; i < 128; ++i) + if (pStackWalk64 && pFunctionTableAccess && pGetModuleBase) { - for (int j = 0; j < 8; ++j) - { - sprintf_s(buf, "%02x ", *(BYTE*)(ExceptionInfo->ContextRecord->Esp + j + 8 * i)); - strcat_s(g_ExceptionStr, EXCEPTION_STR_MAXLEN, buf); - } + strcat_s(g_ExceptionStr, "Stack Trace:\n"); + + STACKFRAME64 frame; + memset(&frame, 0, sizeof(STACKFRAME64)); - strcat_s(g_ExceptionStr, EXCEPTION_STR_MAXLEN, "\t["); + frame.AddrPC.Offset = ExceptionInfo->ContextRecord->Eip; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrFrame.Offset = ExceptionInfo->ContextRecord->Ebp; + frame.AddrFrame.Mode = AddrModeFlat; + frame.AddrStack.Offset = ExceptionInfo->ContextRecord->Esp; + frame.AddrStack.Mode = AddrModeFlat; - for (int j = 0; j < 8; ++j) + for (int i = 0; i < EXCEPTION_STACKTRACE_MAXFRAMECOUNT; i++) { - if ((signed int)*(BYTE*)(ExceptionInfo->ContextRecord->Esp + j + 8 * i) >= 33) + if (!pStackWalk64( + IMAGE_FILE_MACHINE_I386, + GetCurrentProcess(), + GetCurrentThread(), + &frame, + ExceptionInfo->ContextRecord, + nullptr, + pFunctionTableAccess, + pGetModuleBase, + nullptr)) { - sprintf_s(buf, "%c", *(BYTE*)(ExceptionInfo->ContextRecord->Esp + j + 8 * i)); - strcat_s(g_ExceptionStr, EXCEPTION_STR_MAXLEN, buf); + // Failed or no frames left + break; + } + + if (frame.AddrPC.Offset != 0) + { + // Valid frame + sprintf_s(buf, "frame[%02d]: %08llX %08llX %08llX %08llX\n", + i, + frame.AddrPC.Offset - sizeof(PVOID), + frame.AddrReturn.Offset, + frame.AddrFrame.Offset, + frame.AddrStack.Offset); + + strcat_s(g_ExceptionStr, buf); } else { - sprintf_s(buf, "."); - strcat_s(g_ExceptionStr, EXCEPTION_STR_MAXLEN, buf); + // Base reached + break; } } - - strcat_s(g_ExceptionStr, EXCEPTION_STR_MAXLEN, "]\n"); } - - strcat_s(g_ExceptionStr, EXCEPTION_STR_MAXLEN, "\n"); - }*/ - - void* frame[EXCEPTION_STACKTRACE_MAXFRAMECOUNT]; - int frameCount = CaptureStackBackTrace(0, ARRAYSIZE(frame), frame, nullptr); - - if (frameCount != 0) - strcat_s(g_ExceptionStr, "Stack Trace:\n"); - - for (int i = 0; i < frameCount; i++) - { - sprintf_s(buf, "frame[%s%d]: %08x%s", i < 10 ? " " : "", i, frame[i], i % 2 ? "\n" : "\t"); - strcat_s(g_ExceptionStr, buf); } - + #else sprintf_s(g_ExceptionStr, "%s", localizedErr); #endif - Com_Error(0, g_ExceptionStr); + Com_Error(ERR_FATAL, g_ExceptionStr); return EXCEPTION_EXECUTE_HANDLER; } diff --git a/components/game_mod/win_main.cpp b/components/game_mod/win_main.cpp new file mode 100644 index 00000000..9cf30abb --- /dev/null +++ b/components/game_mod/win_main.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" + +void Sys_GetEvent(sysEvent_t *event) +{ + ((void(__cdecl *)(sysEvent_t *))0x0068BB40)(event); +} + +void Sys_NormalExit() +{ + ((void(__cdecl *)())0x00553790)(); +} + +void Sys_OutOfMemErrorInternal(const char *filename, int line) +{ + Sys_EnterCriticalSection(CRITSECT_FATAL_ERROR); + Com_Printf(16, "Out of memory: filename '%s', line %d\n", filename, line); + + const char* title = Win_LocalizeRef("WIN_OUT_OF_MEM_TITLE"); + const char* body = Win_LocalizeRef("WIN_OUT_OF_MEM_BODY"); + HWND hWnd = GetActiveWindow(); + + char msg[EXCEPTION_STR_MAXLEN]; + sprintf_s(msg, "%s\n\n", body); + + void* frame[EXCEPTION_STACKTRACE_MAXFRAMECOUNT]; + int frameCount = CaptureStackBackTrace(0, ARRAYSIZE(frame), frame, nullptr); + + if (frameCount != 0) + strcat_s(msg, "Stack Trace:\n"); + + for (int i = 0; i < frameCount; i++) + { + char buf[256]; + sprintf_s(buf, "frame[%s%d]: %0*p%s", i < 10 ? " " : "", i, 10, frame[i], i % 2 ? "\n" : "\t"); + strcat_s(msg, buf); + } + + MessageBoxA(hWnd, msg, title, MB_ICONERROR); + exit(-1); +} \ No newline at end of file diff --git a/components/game_mod/win_main.h b/components/game_mod/win_main.h new file mode 100644 index 00000000..035bb3f0 --- /dev/null +++ b/components/game_mod/win_main.h @@ -0,0 +1,23 @@ +#pragma once + +enum sysEventType_t +{ + SE_NONE = 0x0, + SE_KEY = 0x1, + SE_CHAR = 0x2, + SE_CONSOLE = 0x3, +}; + +struct sysEvent_t +{ + int evTime; + sysEventType_t evType; + int evValue; + int evValue2; + int evPtrLength; + void *evPtr; +}; + +void Sys_GetEvent(sysEvent_t *event); +void Sys_NormalExit(); +void Sys_OutOfMemErrorInternal(const char *filename, int line); \ No newline at end of file diff --git a/components/gsc_parser/custom_build_rules/win_flex_bison_custom_build.props b/components/gsc_parser/custom_build_rules/win_flex_bison_custom_build.props new file mode 100644 index 00000000..2f915d93 --- /dev/null +++ b/components/gsc_parser/custom_build_rules/win_flex_bison_custom_build.props @@ -0,0 +1,47 @@ + + + + Midl + CustomBuild + + + _SelectedFiles;$(BisonDependsOn) + + + + %(Filename).tab.cpp + %(Filename).tab.h + +WHERE win_bison.exe >nul 2>nul +IF %ERRORLEVEL% NEQ 0 EXIT /b 0 +start /B /WAIT /D "%(RootDir)%(Directory)" win_bison.exe [AllOptions] [AdditionalOptions] "%(Filename)%(Extension)" +exit /b %errorlevel% + %(RootDir)%(Directory)%(OutputFile); + Process "%(Filename)%(Extension)" bison file + + + + Midl + CustomBuild + + + _SelectedFiles;$(FlexDependsOn) + + + + %(Filename).flex.cpp + true + +WHERE win_flex.exe >nul 2>nul +IF %ERRORLEVEL% NEQ 0 EXIT /b 0 +start /B /WAIT /D "%(RootDir)%(Directory)" win_flex.exe [AllOptions] [AdditionalOptions] "%(Filename)%(Extension)" +exit /b %errorlevel% + %(RootDir)%(Directory)%(OutputFile); + Process "%(Filename)%(Extension)" flex file + + + \ No newline at end of file diff --git a/components/gsc_parser/custom_build_rules/win_flex_bison_custom_build.targets b/components/gsc_parser/custom_build_rules/win_flex_bison_custom_build.targets new file mode 100644 index 00000000..2fabf745 --- /dev/null +++ b/components/gsc_parser/custom_build_rules/win_flex_bison_custom_build.targets @@ -0,0 +1,178 @@ + + + + + + BisonTarget + + + FlexTarget + + + + $(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml + + + $(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml + + + + + + + + @(Bison, '|') + + + + + + + + + $(ComputeLinkInputsTargets); + ComputeBisonOutput; + + + $(ComputeLibInputsTargets); + ComputeBisonOutput; + + + + + + + + + + + + + + + + + + @(Flex, '|') + + + + + + + + + $(ComputeLinkInputsTargets); + ComputeFlexOutput; + + + $(ComputeLibInputsTargets); + ComputeFlexOutput; + + + + + + + + + + + + \ No newline at end of file diff --git a/components/gsc_parser/custom_build_rules/win_flex_bison_custom_build.xml b/components/gsc_parser/custom_build_rules/win_flex_bison_custom_build.xml new file mode 100644 index 00000000..d42d5143 --- /dev/null +++ b/components/gsc_parser/custom_build_rules/win_flex_bison_custom_build.xml @@ -0,0 +1,521 @@ + + + + + + + + + + General + + + + + Bison Options + + + + + Command Line + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Execute Before + + + Specifies the targets for the build customization to run before. + + + + + + + + + + + Execute After + + + Specifies the targets for the build customization to run after. + + + + + + + + + + + + + + Additional Options + + + Additional Options + + + + + + + + + + + + + + General + + + + + Flex Options + + + + + Command Line + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Execute Before + + + Specifies the targets for the build customization to run before. + + + + + + + + + + + Execute After + + + Specifies the targets for the build customization to run after. + + + + + + + + + + + + + + Additional Options + + + Additional Options + + + + + + + \ No newline at end of file diff --git a/components/gsc_parser/gsc_parser.h b/components/gsc_parser/gsc_parser.h new file mode 100644 index 00000000..3469e1e0 --- /dev/null +++ b/components/gsc_parser/gsc_parser.h @@ -0,0 +1,12 @@ +#pragma once + +// +// Library Header - This is the only header that should be included when using the library +// + +#include "src/cpp/symbols/symbol.h" +#include "src/cpp/sys/sys_platform.h" +#include "src/cpp/cache/cache.h" +#include "src/cpp/fs/fs.h" + +#include "src/cpp/parser/gsc.yy.h" diff --git a/components/gsc_parser/gsc_parser.vcxproj b/components/gsc_parser/gsc_parser.vcxproj new file mode 100644 index 00000000..4525d1be --- /dev/null +++ b/components/gsc_parser/gsc_parser.vcxproj @@ -0,0 +1,178 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + + + Document + + + + {C61A8481-11F8-4A98-9776-C5A4E1F13D98} + Win32Proj + gsc_parser + + + + StaticLibrary + true + v120 + Unicode + + + StaticLibrary + false + v120 + true + Unicode + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + + + cpp/parser/%(Filename).tab.cpp + + + cpp/parser/%(Filename).tab.hpp + true + + + cpp/parser/%(Filename).yy.cpp + + + cpp/parser/%(Filename).yy.h + true + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + cpp/parser/%(Filename).tab.cpp + + + cpp/parser/%(Filename).tab.hpp + + + cpp/parser/%(Filename).yy.cpp + + + cpp/parser/%(Filename).yy.h + true + + + + + + + \ No newline at end of file diff --git a/components/gsc_parser/parser.vcxproj b/components/gsc_parser/parser.vcxproj new file mode 100644 index 00000000..26d98d6c --- /dev/null +++ b/components/gsc_parser/parser.vcxproj @@ -0,0 +1,209 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {BC639CC4-91E1-4F6F-8953-FEC7200DB40C} + Win32Proj + parser + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + + true + .\bin\ + obj\$(Configuration)\ + + + false + .\bin\ + obj\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + + + $(ProjectDir)/src/cpp/parser/%(Filename).tab.cpp + $(ProjectDir)/src/cpp/parser/%(Filename).tab.hpp + + + + + true + $(ProjectDir)/output.txt;%(ReportFile) + + + $(ProjectDir)/src/cpp/parser/%(Filename).yy.cpp + $(ProjectDir)/src/cpp/parser/%(Filename).yy.h + true + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + $(ProjectDir)/src/cpp/parser/%(Filename).tab.cpp + $(ProjectDir)/src/cpp/parser/%(Filename).tab.hpp + all + + + + + $(ProjectDir)/src/cpp/parser/%(Filename).yy.cpp + $(ProjectDir)/src/cpp/parser/%(Filename).yy.h + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + + + + + + + + + + + \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/cache/cache.cpp b/components/gsc_parser/src/cpp/cache/cache.cpp new file mode 100644 index 00000000..0565169f --- /dev/null +++ b/components/gsc_parser/src/cpp/cache/cache.cpp @@ -0,0 +1,204 @@ +#include "cache.h" +#include + +#include "../cl/cl_watch_mode.h" + +#include "../util/hash_table.h" +#include "../parser/gsc.yy.h" +#include "../sys/sys_worker.h" + +HashTable script_cache; + +ScriptCacheEntry* Cache_Update(const char* key) +{ + return script_cache.Add(key); +} + +void Cache_Remove(const char* key) +{ + script_cache.RemoveNode(key); +} + +void Cache_Clear() +{ + script_cache.Clear(); +} + +void Cache_List_Callback_f(int index, const char* key, void* value) +{ + ScriptCacheEntry* entry = (ScriptCacheEntry*)value; + printf("[%d] %s %s\n", index, key, entry ? "VALID" : "NULL"); +} + +void Cache_List() +{ + int elemCount = script_cache.Traverse(Cache_List_Callback_f); + printf("%d elements in cache\n", elemCount); +} + + +ScriptCacheEntry::ScriptCacheEntry(void) : file_data(NULL), file_size(0), ast(NULL) +{ + sem_init(&sem_file, 0, 1); + sem_init(&sem_ast, 0, 1); +} + +ScriptCacheEntry::~ScriptCacheEntry(void) +{ + FlushStreamBuffer(); + FlushAST(); + + sem_destroy(&sem_ast); + sem_destroy(&sem_file); +} + +void ScriptCacheEntry::LockAST(void) +{ + sem_wait(&sem_ast); +} + +void ScriptCacheEntry::LockStreamBuffer(void) +{ + sem_wait(&sem_file); +} + +void ScriptCacheEntry::UnlockAST(void) +{ + sem_post(&sem_ast); +} + +void ScriptCacheEntry::UnlockStreamBuffer(void) +{ + sem_post(&sem_file); +} + +Symbol* ScriptCacheEntry::AST(void) const +{ + return this->ast; +} + +size_t ScriptCacheEntry::UpdateStreamBuffer(size_t len, FILE* h) +{ + if(len > file_size) + { + delete[] file_data; + file_data = new char[len]; + } + + file_size = len; + return fread(file_data, 1, file_size, h); +} + +// +// The file data is NOT flushed after parsing to allow for other threads +// to refresh it while the AST is being used +// +// arg must be deleted from within this function +// +int ScriptCacheEntry::ParseStreamBuffer(analysis_arg_s* arg) +{ + ScriptCacheEntry* entry = arg->entry; + + entry->LockStreamBuffer(); + if(entry->file_data == NULL) + { + entry->UnlockStreamBuffer(); + delete arg; + return 1; + } + + yyscan_t scanner = NULL; + yylex_init(&scanner); + + yy_scan_bytes(entry->file_data, entry->file_size, scanner); + entry->UnlockStreamBuffer(); + + Symbol* AST = NULL; + int err = yyparse(&AST, scanner); + yylex_destroy(scanner); + + // + // If the new file cannot be parsed + // the old AST is reused + // + if(err) + { + delete arg; + return err; + } + + // + // Otherwise use the new one + // + entry->LockAST(); + Symbol* old = entry->ast; + entry->ast = AST; + + if(arg->callback_func) + { + arg->callback_func(AST, arg->callback_argv); + + // + // Prevents node.js from waiting until the parser exits to handle stdout data + // + fflush(stdout); + } + + entry->UnlockAST(); + delete old; + + delete arg; + return 0; +} + +void ScriptCacheEntry::FlushStreamBuffer() +{ + LockStreamBuffer(); + file_size = 0; + delete[] file_data; + file_data = NULL; + UnlockStreamBuffer(); +} + +void ScriptCacheEntry::FlushAST() +{ + LockAST(); + delete ast; + ast = NULL; + UnlockAST(); +} + +int ScriptCacheEntry::PostAnalysisJob(analysis_callback_t callback, void* argv_s) +{ + // + // Cleaned up by the ParseStreamBuffer function + // + analysis_arg_s* arg = new analysis_arg_s; + arg->entry = this; + arg->callback_func = callback; + arg->callback_argv = argv_s; + + if(!CL_WatchMode_IsEnabled()) + { + ScriptCacheEntry::ParseStreamBuffer(arg); + return 0; + } + + Job* job = new Job((job_func_t)ScriptCacheEntry::ParseStreamBuffer, arg); + job->Register(); + return 0; +} + +int ScriptCacheEntry::PostAnalysisJob_Sync(analysis_callback_t callback, void* argv_s) +{ + // + // Cleaned up by the ParseStreamBuffer function + // + analysis_arg_s* arg = new analysis_arg_s; + arg->entry = this; + arg->callback_func = callback; + arg->callback_argv = argv_s; //TODO: prevent this from leaking + + ScriptCacheEntry::ParseStreamBuffer(arg); + return 0; +} diff --git a/components/gsc_parser/src/cpp/cache/cache.h b/components/gsc_parser/src/cpp/cache/cache.h new file mode 100644 index 00000000..233f5a60 --- /dev/null +++ b/components/gsc_parser/src/cpp/cache/cache.h @@ -0,0 +1,59 @@ +#pragma once + +#include "../symbols/symbol.h" +#include "../sys/sys_worker.h" + +class ScriptCacheEntry; + +typedef int (*analysis_callback_t)(Symbol* AST, void* argv); + +struct analysis_arg_s +{ + ScriptCacheEntry* entry; + analysis_callback_t callback_func; + void* callback_argv; +}; + +class ScriptCacheEntry +{ +private: + sem_t sem_file; + sem_t sem_ast; + + char* file_data; + size_t file_size; + + Symbol* ast; + + void FlushAST(void); // AST is only flushed when the entry is destroyed + + // + // Parse the contents of the stream buffer and store the result in ast + // returns non-zero if there was an error (in which case the old AST is used) + // + static int ParseStreamBuffer(analysis_arg_s* arg); + +public: + ScriptCacheEntry(void); + ~ScriptCacheEntry(void); + + void LockAST(void); + void LockStreamBuffer(void); + void UnlockAST(void); + void UnlockStreamBuffer(void); + + Symbol* AST(void) const; + + size_t UpdateStreamBuffer(size_t len, FILE* h); + void FlushStreamBuffer(void); + + //int PostAnalysisJob(analysis_callback_t callback = NULL); + int PostAnalysisJob(analysis_callback_t callback, void* arg); + //int PostAnalysisJob_Sync(analysis_callback_t callback = NULL); + int PostAnalysisJob_Sync(analysis_callback_t callback, void* argv_s); +}; + +void Cache_List(); +ScriptCacheEntry* Cache_Update(const char* key); +void Cache_Remove(const char* key); +void Cache_Clear(); diff --git a/components/gsc_parser/src/cpp/cl/cl_arg.cpp b/components/gsc_parser/src/cpp/cl/cl_arg.cpp new file mode 100644 index 00000000..e5dbc07c --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/cl_arg.cpp @@ -0,0 +1,286 @@ +#include +#include + +#include "cl_arg.h" +#include +#include "cl_cvar.h" +#include "cl_cmd.h" +#include "../util/llist.h" +#include "../sys/sys_platform.h" + +Argument* g_shortcut[255] = {NULL}; + +int Argument::RegisterShortcut(char shortcut) +{ + if(this->shortcut) + { + fprintf(stderr, "Error: Could not register shortcut '-%c' for argument '%s' - arg already has shortcut '-%c'\n", shortcut, this->name, this->shortcut); + return 3; + } + + if(shortcut) + { + if(!g_shortcut[(int)shortcut]) + { + this->shortcut = shortcut; + g_shortcut[(int)shortcut] = this; + return 0; + } + else + { + fprintf(stderr, "Error: Could not initialize argument '%s' - shortcut '-%c' already exists for another argument\n", name, shortcut); + return 1; + } + } + + fprintf(stderr, "Error: Could not register shortcut'-%c' for argument '%s'\n", shortcut, this->name); + return 2; +} + +const char* Argument::Name(void) const +{ + return this->name; +} + +const char* Argument::Description(void) const +{ + return this->desc; +} + +int Argument::Flags(void) const +{ + return this->flags; +} + +ArgParsedInfo::ArgParsedInfo(void) : cmd(NULL), argc(0), argv(NULL) +{ +} + +ArgParsedInfo::~ArgParsedInfo(void) +{ + for(; argc; ) + { + free((void*)this->argv[--argc]); + } + + delete[] this->argv; + this->argv = NULL; +} + +Command* ArgParsedInfo::Cmd(void) const +{ + return this->cmd; +} + +int ArgParsedInfo::Argc(void) const +{ + return this->argc; +} + +char** ArgParsedInfo::Argv(void) const +{ + return this->argv; +} + +char* ArgParsedInfo::Argv(int index) const +{ + if(index >= argc || index < 0) + { + return (char*)""; + } + + return this->argv[index]; +} + +void Arg_PrintUsage(void) +{ + printf( "%-9s%s\n%-9s%s\n\n", + "Usage:", APPLICATION_NAME" [command] [options]", + "Example:", APPLICATION_NAME" --tree -f 'maps/utility.gsc'"); + + printf("Options:\n"); + for(int i = 0; i < 255; i++) + { + if(g_shortcut[i] && g_shortcut[i]->Flags() & (ARG_GLOBAL | ARG_CVAR)) + { + printf(" -%c, --%-22s%s\n", i, g_shortcut[i]->Name(), g_shortcut[i]->Description()); + } + } + printf("\n"); + + printf("Launch Commands:\n"); + for(Command* cmd = Command::GlobalCommands(); cmd; cmd = cmd->NextElem()) + { + if(cmd->CmdFlags() & COMMAND_LAUNCH) + { + printf(" %-22s%s\n", cmd->Name(), cmd->Description()); + } + } + printf("\n"); + + printf("Watch Commands:\n"); + for(Command* cmd = Command::GlobalCommands(); cmd; cmd = cmd->NextElem()) + { + if(cmd->CmdFlags() & COMMAND_WATCH) + { + printf(" %-22s%s\n", cmd->Name(), cmd->Description()); + } + } + printf("\n"); +} + +int Arg_ParseArgument(char*** consumable_argv, int* consumable_argc) +{ + char**& argv = *consumable_argv; + int& argc = *consumable_argc; + + const char* argStr = argv[0]; + + int len = strlen(argStr); + if(!len) + { + fprintf(stderr, "Error: Zero length argument\n"); + return 1; + } + + if(len >= 2 && (argStr[0] == '-' && argStr[1] == '-')) + { + printf("Full name found\n"); + return 0; + } + + if(len == 2 && argStr[0] == '-') + { + CVar* cvar = (CVar*)g_shortcut[(int)argStr[1]]; + if(!cvar) + { + fprintf(stderr, "Error: Unrecognized argument '%s'\n", argStr); + return 1; + } + + if(cvar->Flags() & (ARG_CVAR | ARG_GLOBAL)) + { + if(argc < 2) + { + fprintf(stderr, "Error: No value provided for argument '%s'\n", argStr); + return 3; + } + + cvar->AssignRawString(argv[1]); + argc-=2; + argv+=2; + return 0; + } + } + + // + // No arguments were consumed - print error & abort oncoming infinite loop + // + fprintf(stderr, "Error: Unrecognized argument '%s'\n", argStr); + return 1; +} + +int Arg_ParseArguments(int argc, char** argv, ArgParsedInfo* out_info) +{ + if(argc < 1) + { + return -1; + } + + out_info->cmd = Command::ResolveCommand(*argv); + if(!out_info->cmd) + { + fprintf(stderr, "Error: Command '%s' not recognized\n", *argv); + return 2; + } + + out_info->argc = argc; + out_info->argv = new char*[argc]; + for(int i = 0; i < argc; i++) + { + out_info->argv[i] = strdup(argv[i]); + } + + /*char** consumable_argv = &argv[1]; + for(int consumable_argc = argc - 1; consumable_argc; ) + { + if(int err = Arg_ParseArgument(&consumable_argv, &consumable_argc)) + { + return err; + } + }*/ + + return 0; +} + +int Arg_ParseCmdLine(char* cmdLine, ArgParsedInfo* out_info) +{ + char* cl = strdup(cmdLine); + int cl_len = strlen(cl); + + int argc = 0; + + char* arg_list[32]; + memset(arg_list, 0, sizeof(char*) * 32); + + bool last_was_space = true; //skips leading spaces + for(int i = 0; i < cl_len; i++) + { + switch(cl[i]) + { + case '\\': + i++; + last_was_space = false; + continue; + + case '"': + case '\'': + for(int j = i+1; j < cl_len; j++) + { + if(cl[j] == cl[i] && cl[j-1] != '\\') + { + i = j; + } + } + + last_was_space = false; + continue; + + default: + int is_space = isspace(cl[i]); + if(last_was_space && !is_space) + { + last_was_space = false; + arg_list[argc++] = &cl[i]; + if(argc >= 32) + { + i = cl_len; + continue; + } + } + + if((last_was_space = !!is_space)) + { + cl[i] = '\0'; + } + + continue; + } + } + + /*printf("Argc: %d\n", argc); + for(int i = 0; i < argc; i++) + { + printf("[%d]: %s\n", i, arg_list[i]); + }*/ + + int result = -1; + + if(argc) + { + result = Arg_ParseArguments(argc, arg_list, out_info); + } + + free(cl); + return result; +} diff --git a/components/gsc_parser/src/cpp/cl/cl_arg.h b/components/gsc_parser/src/cpp/cl/cl_arg.h new file mode 100644 index 00000000..0d038cee --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/cl_arg.h @@ -0,0 +1,64 @@ +#pragma once +#include + +#define APPLICATION_NAME "parser" + +enum ARG_FLAGS +{ + ARG_NULL = 0 << 0, + + ARG_GLOBAL = 1 << 0, + + ARG_CVAR = 1 << 2, + ARG_COMMAND = 1 << 3, + + // + // Only used for boolean CVars + // Enables grouping usage such as "-dv" as well as unary usage "-d" + // + ARG_IMPLICIT_VALUE = 1 << 4 +}; + +class Argument +{ +protected: + int flags; + + const char* name; + const char* desc; + char shortcut; + + int RegisterShortcut(char shortcut); + +public: + const char* Name(void) const; + const char* Description(void) const; + int Flags(void) const; +}; + +class Command; + +class ArgParsedInfo +{ +private: + Command* cmd; + + int argc; + char** argv; + +public: + ArgParsedInfo(void); + ~ArgParsedInfo(void); + + Command* Cmd(void) const; + + int Argc(void) const; + char** Argv(void) const; + char* Argv(int index) const; + + friend int Arg_ParseArguments(int argc, char** argv, ArgParsedInfo* out_info); +}; + +void Arg_PrintUsage(void); +int Arg_ParseArguments(int argc, char** argv, ArgParsedInfo* out_info); +int Arg_ParseCmdLine(char* cmdLine, ArgParsedInfo* out_info); diff --git a/components/gsc_parser/src/cpp/cl/cl_cmd.cpp b/components/gsc_parser/src/cpp/cl/cl_cmd.cpp new file mode 100644 index 00000000..e2a3101d --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/cl_cmd.cpp @@ -0,0 +1,82 @@ +#include "cl_cmd.h" +#include +#include + +#include "commands/cmd_common.h" + +#define REGISTER_COMMAND(IDENTIFIER, NAME, DESCRIPTION, FUNC, FLAGS) Command IDENTIFIER(NAME, DESCRIPTION, FUNC, FLAGS); + +Command* Command::g_cmds = NULL; + +REGISTER_COMMAND(g_cmd_help, "help", "Print usage information", Cmd_Help_f, COMMAND_LAUNCH | COMMAND_WATCH); +REGISTER_COMMAND(g_cmd_tree, "tree", "Print the AST for a given script file", Cmd_Tree_f, COMMAND_LAUNCH | COMMAND_WATCH); +REGISTER_COMMAND(g_cmd_symbols, "symbols", "Print the top level symbols for a given script file", Cmd_Symbols_f, COMMAND_LAUNCH | COMMAND_WATCH); + +REGISTER_COMMAND(g_cmd_watch, "watch", "Start watch/reentrant mode", Cmd_Watch_f, COMMAND_LAUNCH); + +REGISTER_COMMAND(g_cmd_exit, "exit", "Close the parser", Cmd_Exit_f, COMMAND_WATCH); +REGISTER_COMMAND(g_cmd_cache_list, "cache-list", "Start watch/reentrant mode", Cmd_Cache_List_f, COMMAND_WATCH); +REGISTER_COMMAND(g_cmd_cache_update, "cache-update", "Add or Update a cache entry", Cmd_Cache_Update_f, COMMAND_WATCH); +REGISTER_COMMAND(g_cmd_cache_remove, "cache-remove", "Remove a cache entry", Cmd_Cache_Remove_f, COMMAND_WATCH); +REGISTER_COMMAND(g_cmd_cache_clear, "cache-clear", "Remove all cache entries", Cmd_Cache_Clear_f, COMMAND_WATCH); + +#undef REGISTER_COMMAND + +Command::Command(const char* name, const char* description, cmd_func_t func, int cmd_flags) : func(func), cmd_flags(cmd_flags) +{ + this->SetOwner(this); + + if(!Command::g_cmds) + { + g_cmds = this; + } + else + { + g_cmds->AddToEnd(this); + } + + this->name = name; + this->desc = description; + this->flags = ARG_COMMAND; +} + +int Command::CmdFlags(void) const +{ + return cmd_flags; +} + +int Command::Exec(int argc, char** argv) const +{ +#if _DEBUG + printf("Executing command: '%s' with the following arguments:\n", this->name); + for(int i = 0; i < argc; i++) + { + printf(" [%d] %s\n", i, argv[i]); + } + printf("\n"); +#endif + + return this->func(argc, argv); +} + +Command* Command::GlobalCommands(void) +{ + return Command::g_cmds; +} + +// +// Attempts to resolve a command from a given argument string +// Returns NULL if there is no match +// +Command* Command::ResolveCommand(const char* str) +{ + for(Command* cmd = Command::g_cmds; cmd; cmd = cmd->NextElem()) + { + if(strcmp(cmd->Name(), str) == 0) + { + return cmd; + } + } + + return NULL; +} diff --git a/components/gsc_parser/src/cpp/cl/cl_cmd.h b/components/gsc_parser/src/cpp/cl/cl_cmd.h new file mode 100644 index 00000000..0785ae8e --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/cl_cmd.h @@ -0,0 +1,46 @@ +#pragma once +#include "cl_arg.h" +#include "../util/llist.h" + +typedef int (*cmd_func_t)(int argc, char** argv); + +enum COMMAND_FLAGS +{ + COMMAND_LAUNCH = 1 << 0, // Launch command + COMMAND_WATCH = 1 << 1, // Watch mode command +}; + +class Command : public Argument, public LList +{ +private: + static Command* g_cmds; + cmd_func_t func; + int cmd_flags; + +public: + + Command(const char* name, const char* description, cmd_func_t func, int cmd_flags); + + int Exec(int argc, char** argv) const; + int CmdFlags(void) const; + + static Command* GlobalCommands(void); + static Command* ResolveCommand(const char* str); +}; + +#define REGISTER_COMMAND(IDENTIFIER) extern Command IDENTIFIER; + + +REGISTER_COMMAND(g_cmd_help); +REGISTER_COMMAND(g_cmd_tree); +REGISTER_COMMAND(g_cmd_symbols); + +REGISTER_COMMAND(g_cmd_watch); + +REGISTER_COMMAND(g_cmd_exit); +REGISTER_COMMAND(g_cmd_cache_list); +REGISTER_COMMAND(g_cmd_cache_update); +REGISTER_COMMAND(g_cmd_cache_remove); +REGISTER_COMMAND(g_cmd_cache_clear); + +#undef REGISTER_COMMAND diff --git a/components/gsc_parser/src/cpp/cl/cl_cvar.cpp b/components/gsc_parser/src/cpp/cl/cl_cvar.cpp new file mode 100644 index 00000000..63c14752 --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/cl_cvar.cpp @@ -0,0 +1,251 @@ +#include "cl_cvar.h" +#include "../sys/sys_platform.h" +#include +#include +#include + +CVar* g_cvar[GLOBAL_CVAR_MAX]; + +int g_cvar_count = 0; + +// +// Used by the registration macro to register global cvars with g_cvar +// +class GCVar +{ +public: + GCVar(CVar* cvar) + { + g_cvar[g_cvar_count++] = cvar; + cvar->flags |= ARG_GLOBAL; + } + + ~GCVar(void) { } +}; + +#define REGISTER_GLOBAL_CVAR(IDENTIFIER, NAME, SHORTCUT, DESCRIPTION, VALUE) CVar IDENTIFIER (NAME, SHORTCUT, DESCRIPTION, VALUE); GCVar gcv_##IDENTIFIER ( &IDENTIFIER ); + +REGISTER_GLOBAL_CVAR(g_verbose, "verbose", 'v', "Enable verbose logging", false); +REGISTER_GLOBAL_CVAR(g_logfile, "logfile", 'l', "Enable logging to file", false); +REGISTER_GLOBAL_CVAR(g_dumpCVars, "dump_cvars", 'd', "Print the cvar values to the console", false); + +#undef REGISTER_GLOBAL_CVAR + +CVar::CVar(void) +{ + memset(this, 0, sizeof(CVar)); + this->type = CVAR_NULL; + this->flags = ARG_CVAR; +} + +CVar::CVar(const char* name, char shortcut, const char* description, int defaultValue) : int_val(defaultValue) +{ + this->type = CVAR_INT; + this->flags = ARG_CVAR; + + this->name = name; + this->desc = description; + + if(shortcut) + { + this->shortcut = 0; + this->RegisterShortcut(shortcut); + } + + this->bool_val = (int_val != 0); + snprintf(str_val, 32, "%d", int_val); + this->float_val = (float)int_val; +} + +CVar::CVar(const char* name, char shortcut, const char* description, bool defaultValue) : bool_val(defaultValue) +{ + this->type = CVAR_BOOL; + this->flags = ARG_CVAR; + + this->name = name; + this->desc = description; + + // + // Allow all booleans to implicitly toggle when used with no value argument (or in a group) + // + this->flags |= ARG_IMPLICIT_VALUE; + + if(shortcut) + { + this->shortcut = 0; + this->RegisterShortcut(shortcut); + } + + this->int_val = bool_val ? 1 : 0; + snprintf(str_val, 32, "%s", bool_val ? "true" : "false"); + this->float_val = (float)int_val; +} + +CVar::CVar(const char* name, char shortcut, const char* description, float defaultValue) : float_val(defaultValue) +{ + this->type = CVAR_FLOAT; + this->flags = ARG_CVAR; + + this->name = name; + this->desc = description; + + if(shortcut) + { + this->shortcut = 0; + this->RegisterShortcut(shortcut); + } + + this->int_val = (int)float_val; + this->bool_val = (int_val != 0); + snprintf(str_val, 32, "%f", float_val); +} + +CVar::CVar(const char* name, char shortcut, const char* description, const char* defaultValue) +{ + this->type = CVAR_STRING; + this->flags = ARG_CVAR; + + this->name = name; + this->desc = description; + + if(shortcut) + { + this->shortcut = 0; + this->RegisterShortcut(shortcut); + } + + strncpy(this->str_val, defaultValue, 31); + this->str_val[31] = '\0'; + + this->bool_val = (str_val[0] != 0); + this->int_val = bool_val ? 1 : 0; + this->float_val = (float)this->int_val; +} + + +CVar::~CVar(void) +{ +} + +/*bool CVar::Enable(void) +{ + switch(this->type) + { + case CVAR_BOOL: + if(stricmp(this->str_val, "false") == 0) + this->bool_val = false; + else if (stricmp(this->str_val, "true") == 0) + this->bool_val = true; + else + this->bool_val = (bool)atoi(str_val); + this->int_val = bool_val ? 1 : 0; + this->float_val = (float)int_val; + break; + case CVAR_INT: + this->int_val = atoi(str_val); + this->bool_val = (int_val != 0); + this->float_val = (float)int_val; + break; + case CVAR_FLOAT: + this->float_val = (float)atof(str_val); + this->int_val = (int)float_val; + this->bool_val = (int_val != 0); + break; + case CVAR_STRING: + this->bool_val = (str_val[0] != 0); + this->int_val = bool_val ? 1 : 0; + this->float_val = (float)this->int_val; + break; + default: + return 1; + } +} + +bool CVar::Disable(void) +{ + this->bool_val = false; + this->int_val = 0; + this->float_val = 0.0f; + this->str_val = "NULL"; +}*/ + +// +// Assign raw command line string data +// automatically convert to the proper data type +// +int CVar::AssignRawString(const char* val) +{ + strncpy(this->str_val, val, 31); + this->str_val[31] = '\0'; + + switch(this->type) + { + case CVAR_BOOL: + if(stricmp(this->str_val, "false") == 0) + this->bool_val = false; + else if (stricmp(this->str_val, "true") == 0) + this->bool_val = true; + else + this->bool_val = (atoi(str_val) != 0); + this->int_val = bool_val ? 1 : 0; + this->float_val = (float)int_val; + break; + case CVAR_INT: + this->int_val = atoi(str_val); + this->bool_val = (int_val != 0); + this->float_val = (float)int_val; + break; + case CVAR_FLOAT: + this->float_val = (float)atof(str_val); + this->int_val = (int)float_val; + this->bool_val = (int_val != 0); + break; + case CVAR_STRING: + this->bool_val = (str_val[0] != 0); + this->int_val = bool_val ? 1 : 0; + this->float_val = (float)this->int_val; + break; + default: + return 1; + } + + return 0; +} + +int CVar::ValueInt(void) const +{ + return this->int_val; +} + +bool CVar::ValueBool(void) const +{ + return this->bool_val; +} + +float CVar::ValueFloat(void) const +{ + return this->float_val; +} + +const char* CVar::ValueString(void) const +{ + return this->str_val; +} + +void CVar_DumpCVars(void) +{ + for(int i = 0; i < GLOBAL_CVAR_MAX; i++) + { + if(CVar* cvar = g_cvar[i]) + { + if(strlen(cvar->Name()) <= 16) + { + printf(" %-16s : %-22s\n", cvar->Name(), cvar->ValueString()); + } + else + { + printf(" %-32s : %-22s\n", cvar->Name(), cvar->ValueString()); + } + } + } +} diff --git a/components/gsc_parser/src/cpp/cl/cl_cvar.h b/components/gsc_parser/src/cpp/cl/cl_cvar.h new file mode 100644 index 00000000..61e73b14 --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/cl_cvar.h @@ -0,0 +1,72 @@ +#pragma once +#include "cl_arg.h" + +// +// The maximum number of global cvars +// +#define GLOBAL_CVAR_MAX 32 + +enum CVAR_TYPE +{ + CVAR_NULL, + + CVAR_BOOL, + CVAR_INT, + CVAR_FLOAT, + CVAR_STRING, +}; + +class CVar : public Argument +{ +private: + CVAR_TYPE type; + + int int_val; + bool bool_val; + char str_val[32]; + float float_val; + + friend class GCVar; + +public: + CVar(void); + + CVar(const char* name, char arg_shortcut, const char* description, int defaultValue); + CVar(const char* name, char arg_shortcut, const char* description, bool defaultValue); + CVar(const char* name, char arg_shortcut, const char* description, float defaultValue); + CVar(const char* name, char arg_shortcut, const char* description, const char* defaultValue); + + ~CVar(void); + + // + // Currently only supports bool cvars + // + //bool Enable(void); + //bool Disable(void); + + //int AssignValue(bool value); + //int AssignValue(int value); + //int AssignValue(float value); + //int AssignValue(const char* value); + + // + // Assign raw command line string data + // automatically convert to the proper data type + // + int AssignRawString(const char* val); + + int ValueInt(void) const; + bool ValueBool(void) const; + float ValueFloat(void) const; + const char* ValueString(void) const; +}; + +#define REGISTER_GLOBAL_CVAR(IDENTIFIER) extern CVar IDENTIFIER; + +REGISTER_GLOBAL_CVAR(g_verbose); +REGISTER_GLOBAL_CVAR(g_logfile); +REGISTER_GLOBAL_CVAR(g_dumpCVars); + +#undef REGISTER_GLOBAL_CVAR + +void CVar_DumpCVars(void); diff --git a/components/gsc_parser/src/cpp/cl/cl_watch_mode.cpp b/components/gsc_parser/src/cpp/cl/cl_watch_mode.cpp new file mode 100644 index 00000000..cd0b6db6 --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/cl_watch_mode.cpp @@ -0,0 +1,18 @@ +#include "cl_watch_mode.h" + +bool g_watch_mode = false; + +void CL_WatchMode_Enable(void) +{ + g_watch_mode = true; +} + +void CL_WatchMode_Disable(void) +{ + g_watch_mode = false; +} + +bool CL_WatchMode_IsEnabled(void) +{ + return g_watch_mode; +} diff --git a/components/gsc_parser/src/cpp/cl/cl_watch_mode.h b/components/gsc_parser/src/cpp/cl/cl_watch_mode.h new file mode 100644 index 00000000..1aa9f3da --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/cl_watch_mode.h @@ -0,0 +1,6 @@ +#pragma once + +void CL_WatchMode_Enable(void); +void CL_WatchMode_Disable(void); + +bool CL_WatchMode_IsEnabled(void); diff --git a/components/gsc_parser/src/cpp/cl/commands/cmd_cache_clear.cpp b/components/gsc_parser/src/cpp/cl/commands/cmd_cache_clear.cpp new file mode 100644 index 00000000..0fad9100 --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/commands/cmd_cache_clear.cpp @@ -0,0 +1,8 @@ +#include "cmd_common.h" +#include "../../cache/cache.h" + +int Cmd_Cache_Clear_f(int argc, char** argv) +{ + Cache_Clear(); + return 0; +} diff --git a/components/gsc_parser/src/cpp/cl/commands/cmd_cache_list.cpp b/components/gsc_parser/src/cpp/cl/commands/cmd_cache_list.cpp new file mode 100644 index 00000000..b86c07f9 --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/commands/cmd_cache_list.cpp @@ -0,0 +1,8 @@ +#include "cmd_common.h" +#include "../../cache/cache.h" + +int Cmd_Cache_List_f(int argc, char** argv) +{ + Cache_List(); + return 0; +} diff --git a/components/gsc_parser/src/cpp/cl/commands/cmd_cache_remove.cpp b/components/gsc_parser/src/cpp/cl/commands/cmd_cache_remove.cpp new file mode 100644 index 00000000..28b783b1 --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/commands/cmd_cache_remove.cpp @@ -0,0 +1,18 @@ +#include "cmd_common.h" +#include "../../cache/cache.h" + +int Cmd_Cache_Remove_f(int argc, char** argv) +{ + if(argc <= 1) + { + fprintf(stderr, "Invalid number of arguments\n"); + return 1; + } + + for(int i = 1; i < argc; i++) + { + Cache_Remove(argv[i]); + } + + return 0; +} diff --git a/components/gsc_parser/src/cpp/cl/commands/cmd_cache_update.cpp b/components/gsc_parser/src/cpp/cl/commands/cmd_cache_update.cpp new file mode 100644 index 00000000..bf1ecd4d --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/commands/cmd_cache_update.cpp @@ -0,0 +1,19 @@ +#include "cmd_common.h" +#include "../../cache/cache.h" +#include + +int Cmd_Cache_Update_f(int argc, char** argv) +{ + if(argc <= 1) + { + fprintf(stderr, "Invalid number of arguments\n"); + return 1; + } + + for(int i = 1; i < argc; i++) + { + Cache_Update(argv[i]); + } + + return 0; +} diff --git a/components/gsc_parser/src/cpp/cl/commands/cmd_common.h b/components/gsc_parser/src/cpp/cl/commands/cmd_common.h new file mode 100644 index 00000000..abc70902 --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/commands/cmd_common.h @@ -0,0 +1,13 @@ +#pragma once + +int Cmd_Help_f(int argc, char** argv); +int Cmd_Tree_f(int argc, char** argv); +int Cmd_Symbols_f(int argc, char** argv); + +int Cmd_Watch_f(int argc, char** argv); + +int Cmd_Exit_f(int argc, char** argv); +int Cmd_Cache_List_f(int argc, char** argv); +int Cmd_Cache_Update_f(int argc, char** argv); +int Cmd_Cache_Remove_f(int argc, char** argv); +int Cmd_Cache_Clear_f(int argc, char** argv); diff --git a/components/gsc_parser/src/cpp/cl/commands/cmd_exit.cpp b/components/gsc_parser/src/cpp/cl/commands/cmd_exit.cpp new file mode 100644 index 00000000..7985306c --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/commands/cmd_exit.cpp @@ -0,0 +1,10 @@ +#include "cmd_common.h" +#include "../cl_watch_mode.h" +#include "../../cache/cache.h" + +int Cmd_Exit_f(int argc, char** argv) +{ + Cache_Clear(); + CL_WatchMode_Disable(); + return 0; +} diff --git a/components/gsc_parser/src/cpp/cl/commands/cmd_help.cpp b/components/gsc_parser/src/cpp/cl/commands/cmd_help.cpp new file mode 100644 index 00000000..67af57fa --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/commands/cmd_help.cpp @@ -0,0 +1,8 @@ +#include "cmd_common.h" +#include "../cl_arg.h" + +int Cmd_Help_f(int argc, char** argv) +{ + Arg_PrintUsage(); + return 0; +} \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/cl/commands/cmd_symbols.cpp b/components/gsc_parser/src/cpp/cl/commands/cmd_symbols.cpp new file mode 100644 index 00000000..833c22e1 --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/commands/cmd_symbols.cpp @@ -0,0 +1,184 @@ +#include +#include +#include + +#ifdef _WIN32 + #include +#endif + +#include "cmd_common.h" +#include "../cl_arg.h" +#include "../cl_cvar.h" +#include "../cl_cmd.h" + +#include "../cl_watch_mode.h" + +#include "../../symbols/symbol.h" +#include "../../sys/sys_platform.h" +#include "../../cache/cache.h" +#include "../../fs/fs.h" + +#include "../../util/hash_table.h" + +#include + +// +// Provide all variables defined by assignment expressions +// +void Cmd_Symbols_PrintVars(Symbol* symbol, Position* pos, HashTable* hash_table) +{ + for(Symbol* node = symbol->Children(); node; node = node->NextElem()) + { + if(node->Type() == S_TYPE_EXPRESSION && ((Expression*)node)->Operator() == OP_TYPE_ASSIGN) + { + Symbol* left_group = node->Children(); + Symbol* left = left_group->Children(); + if(left->Type() == S_TYPE_IDENTIFIER) + { + Identifier* id = (Identifier*)left; + Symbol** s = hash_table->Add(id->value); + if(*s == NULL) + { + *s = id; + left->PrintSymbol(); + } + } + } + + Cmd_Symbols_PrintVars(node, pos, hash_table); + } +} + +int Cmd_Symbols_ASTCallback_f(Symbol* AST, void* _arg) +{ + Position* pos = (Position*)_arg; + + // + // Upon deletion this hash table deletes dynamically allocated pointers to symbols + // but not the symbols themselves + // + HashTable* hash_table = new HashTable(); + + for(Symbol* node = AST->Children(); node; node = node->NextElem()) + { + node->PrintSymbol(); + + if(pos && node->Type() == S_TYPE_FUNCTION_DECL) + { + if(node->Location().start <= *pos && node->Location().end >= *pos) + { + // Provide the function arguments + ((Function*)node)->PrintArgs(); + + // Provide all variables defined within that function + Cmd_Symbols_PrintVars(node, pos, hash_table); + } + } + } + + delete hash_table; + + printf("JOB_COMPLETE\n"); + return 0; +} + +/* + USAGE: symbols [filepath] + symbols [filepath line:char fileSize fileData] //loc -1:-1 displays all functions in the file +*/ +int Cmd_Symbols_f(int argc, char** argv) +{ + if(argc < 2 || argc == 3) + { + fprintf(stderr, "Error: Incorrect number of arguments\n"); + return 1; + } + + FILE* f = NULL; + + Position* pos = NULL; + ScriptCacheEntry* entry = Cache_Update(argv[1]); + + if(argc == 2) + { + long int file_size = FS_FileSize(argv[1]); + if(file_size == -1) + { + fprintf(stderr, "Error: File '%s' could not be opened\n", argv[1]); + return 1; + } + + f = fopen(argv[1], "r"); + if(!f) + { + fprintf(stderr, "Error: File '%s' could not be opened\n", argv[1]); + return 1; + } + + entry->UpdateStreamBuffer(file_size, f); + fclose(f); + } + else if( argc == 4 && CL_WatchMode_IsEnabled() ) + { + char* end = NULL; + + long int line = strtol(argv[2], &end, 10); + long int character = strtol(end + 1, &end, 10); + + pos = new Position(line, character); + + long int file_size = strtol(argv[3], &end, 10); + + printf("Waiting for %ld bytes on stdin\n", file_size); + printf("Using position %ld:%ld\n", line, character); + + entry->UpdateStreamBuffer(file_size, stdin); + } + + entry->PostAnalysisJob_Sync(Cmd_Symbols_ASTCallback_f, pos); + return 0; +} + +// +// PRE EXECUTION TIMER CODE (DEPRECATED) +// +/*#ifdef _WIN32 + LARGE_INTEGER freq, start; + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&start); +#else //LINUX + timespec start; + clock_gettime(CLOCK_REALTIME, &start); +#endif*/ + +// +// POST EXECUTION TIMER CODE (DEPRECATED) +// +/* + double elapsed_time_ms = 0.0; +#ifdef _WIN32 + LARGE_INTEGER end; + QueryPerformanceCounter(&end); + + elapsed_time_ms = (double)end.QuadPart - (double)start.QuadPart; + elapsed_time_ms /= (double)(freq.QuadPart / 1000); +#else //LINUX + timespec end; + clock_gettime(CLOCK_REALTIME, &end); + + timespec delta; + if(end.tv_nsec - start.tv_nsec < 0) + { + delta.tv_sec = end.tv_sec - start.tv_sec - 1; + delta.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } + else + { + delta.tv_sec = end.tv_sec - start.tv_sec; + delta.tv_nsec = end.tv_nsec - start.tv_nsec; + } + + elapsed_time_ms = 1000.0 * (double)delta.tv_sec; + elapsed_time_ms += (double)delta.tv_nsec / 1000000.0; + +#endif*/ diff --git a/components/gsc_parser/src/cpp/cl/commands/cmd_tree.cpp b/components/gsc_parser/src/cpp/cl/commands/cmd_tree.cpp new file mode 100644 index 00000000..80997d13 --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/commands/cmd_tree.cpp @@ -0,0 +1,75 @@ +#include +#include +#include + +#ifdef _WIN32 + #include +#endif + +#include "cmd_common.h" +#include "../cl_arg.h" +#include "../cl_cvar.h" +#include "../cl_cmd.h" + +#include "../cl_watch_mode.h" + +#include "../../symbols/symbol.h" +#include "../../sys/sys_platform.h" +#include "../../cache/cache.h" +#include "../../fs/fs.h" + +#include + +int Cmd_Tree_ASTCallback_f(Symbol* AST, void* _arg) +{ + AST->PrintInfoRecursive(); + return 0; +} + +/* + USAGE: tree [filepath] + tree [filepath fileSize -d fileData] +*/ +int Cmd_Tree_f(int argc, char** argv) +{ + if(argc < 2) + { + fprintf(stderr, "Error: Incorrect number of arguments\n"); + return 1; + } + + FILE* f = NULL; + + ScriptCacheEntry* entry = Cache_Update(argv[1]); + + if(argc == 2) + { + long int file_size = FS_FileSize(argv[1]); + if(file_size == -1) + { + fprintf(stderr, "Error: File '%s' could not be opened\n", argv[1]); + return 1; + } + + f = fopen(argv[1], "r"); + if(!f) + { + fprintf(stderr, "Error: File '%s' could not be opened\n", argv[1]); + return 1; + } + + entry->UpdateStreamBuffer(file_size, f); + fclose(f); + } + else if( argc == 3 && CL_WatchMode_IsEnabled() ) + { + char* end = NULL; + long int file_size = strtol(argv[2], &end, 10); + printf("Waiting for %ld bytes on stdin\n", file_size); + + entry->UpdateStreamBuffer(file_size, stdin); + } + + entry->PostAnalysisJob(Cmd_Tree_ASTCallback_f, NULL); + return 0; +} diff --git a/components/gsc_parser/src/cpp/cl/commands/cmd_watch.cpp b/components/gsc_parser/src/cpp/cl/commands/cmd_watch.cpp new file mode 100644 index 00000000..451a0348 --- /dev/null +++ b/components/gsc_parser/src/cpp/cl/commands/cmd_watch.cpp @@ -0,0 +1,104 @@ +#include +#include +#include + +#ifdef _WIN32 + #include +#endif + +#include "../cl_watch_mode.h" + +#include "../../sys/sys_platform.h" +#include "../../sys/sys_worker.h" +#include "../../sys/sys_cpu.h" + +#include "../../cache/cache.h" + +#include "../cl_arg.h" +#include "../cl_cmd.h" + +int Cmd_Watch_f(int argc, char** argv) +{ + CL_WatchMode_Enable(); + +#ifdef _WIN32 + LARGE_INTEGER freq, start; + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&start); +#else //LINUX + timespec start; + clock_gettime(CLOCK_REALTIME, &start); +#endif + + int workerCount = Sys_CPUCount() - 1; + printf("Using %d threads\n", workerCount + 1); + Worker* workers = new Worker[workerCount]; + + for(ssize_t readLen = 0; readLen != -1 && CL_WatchMode_IsEnabled(); ) + { + size_t bufLen = 64; + char* buf = (char*)malloc(bufLen); + + readLen = getline(&buf, &bufLen, stdin); + if(readLen > 0) + { + //printf("Read %d bytes from stdin '%s'\n", (int)readLen, buf); + + ArgParsedInfo cmd_info; + if(Arg_ParseCmdLine(buf, &cmd_info) != 0) + { + free(buf); + continue; + } + + if(cmd_info.Cmd() && cmd_info.Cmd()->CmdFlags() & COMMAND_WATCH) + { + cmd_info.Cmd()->Exec(cmd_info.Argc(), cmd_info.Argv()); + } + else + { + fprintf(stderr, "Error: '%s' is not a valid command in watch mode\n", cmd_info.Cmd()->Name()); + } + } + + free(buf); + } + + double elapsed_time_ms = 0.0; +#ifdef _WIN32 + LARGE_INTEGER end; + QueryPerformanceCounter(&end); + + elapsed_time_ms = (double)end.QuadPart - (double)start.QuadPart; + elapsed_time_ms /= (double)(freq.QuadPart / 1000); +#else //LINUX + timespec end; + clock_gettime(CLOCK_REALTIME, &end); + + timespec delta; + if(end.tv_nsec - start.tv_nsec < 0) + { + delta.tv_sec = end.tv_sec - start.tv_sec - 1; + delta.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } + else + { + delta.tv_sec = end.tv_sec - start.tv_sec; + delta.tv_nsec = end.tv_nsec - start.tv_nsec; + } + + elapsed_time_ms = 1000.0 * (double)delta.tv_sec; + elapsed_time_ms += (double)delta.tv_nsec / 1000000.0; + +#endif + + Job::PostQuitJob(); + delete[] workers; + + Cache_Clear(); + CL_WatchMode_Disable(); + + printf("Watch mode ended after %f ms\n", elapsed_time_ms); + + return 0; +} diff --git a/components/gsc_parser/src/cpp/fs/fs.cpp b/components/gsc_parser/src/cpp/fs/fs.cpp new file mode 100644 index 00000000..5e4894fc --- /dev/null +++ b/components/gsc_parser/src/cpp/fs/fs.cpp @@ -0,0 +1,144 @@ +#include "fs.h" +#include +#include + +#ifdef WIN32 +#include +const char fs_dir_separator = '\\'; +#else +#include +const char fs_dir_separator = '/'; +#endif + +long int FS_FileSize(const char* path) +{ + if(!path) + { + return -1; + } + + struct stat _stat; + if(stat(path, &_stat) == -1) + { + return -1; + } + + return _stat.st_size; +} + +#ifdef WIN32 + +long int _FS_Iterate(const char* path, fs_iterator_callback_t file_f, fs_iterator_callback_t dir_f, bool recursive) +{ + WIN32_FIND_DATAA file_data = { NULL }; + HANDLE h_dir; + if( (h_dir = FindFirstFileA(path, &file_data)) == INVALID_HANDLE_VALUE ) + { + return 0; + } + + // Saves room on the stack + char* entry_path = new char[1024]; + + WIN32_FIND_DATAA entry; + do + { + if( strcmp(entry.cFileName, ".") == 0 || strcmp(entry.cFileName, "..") == 0) + { + continue; + } + + sprintf_s(entry_path, 1024, "%s%c%s", path, fs_dir_separator, entry.cFileName); + + if(entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if(dir_f) + { + dir_f(entry_path); + } + + if(recursive) + { + _FS_Iterate(entry_path, file_f, dir_f, recursive); + } + } + else + { + file_f(entry_path); + } + } while(FindNextFileA(h_dir, &file_data)); + + delete[] entry_path; + + FindClose(h_dir); + return 0; +} + +#else + +long int _FS_Iterate(const char* path, fs_iterator_callback_t file_f, fs_iterator_callback_t dir_f, bool recursive) +{ + DIR* p_dir; + if( !(p_dir = opendir(path)) ) + { + return 0; + } + + // Saves room on the stack + char* entry_path = new char[1024]; + + struct dirent* entry; + while((entry = readdir(p_dir)) != NULL) + { + if( strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + { + continue; + } + + sprintf(entry_path, "%s%c%s", path, fs_dir_separator, entry->d_name); + + if(entry->d_type == DT_DIR) + { + if(dir_f) + { + dir_f(entry_path); + } + + if(recursive) + { + _FS_Iterate(entry_path, file_f, dir_f, recursive); + } + } + else + { + file_f(entry_path); + } + } + + delete[] entry_path; + + closedir(p_dir); + return 0; +} + +#endif + +long int FS_IterateDirectory(const char* path, fs_iterator_callback_t file_callback) +{ + return _FS_Iterate(path, file_callback, NULL, false); +} + +long int FS_IterateDirectory(const char* path, fs_iterator_callback_t file_callback, fs_iterator_callback_t dir_callback) +{ + return _FS_Iterate(path, file_callback, dir_callback, false); +} + +long int FS_IterateDirectoryTree(const char* path, fs_iterator_callback_t file_callback) +{ + return _FS_Iterate(path, file_callback, NULL, true); +} + +long int FS_IterateDirectoryTree(const char* path, fs_iterator_callback_t file_callback, fs_iterator_callback_t dir_callback) +{ + return _FS_Iterate(path, file_callback, dir_callback, true); +} diff --git a/components/gsc_parser/src/cpp/fs/fs.h b/components/gsc_parser/src/cpp/fs/fs.h new file mode 100644 index 00000000..b60e8e0a --- /dev/null +++ b/components/gsc_parser/src/cpp/fs/fs.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +// +// Returns the size of a given file, or -1 if the file does not exist +// +long int FS_FileSize(const char* path); + +typedef long int (*fs_iterator_callback_t)(const char* path); + +// +// Iterate through a directory or directory tree +// file_callback is called for each file +// dir_callback is called for each directory +// +long int FS_IterateDirectory(const char* path, fs_iterator_callback_t file_callback); +long int FS_IterateDirectory(const char* path, fs_iterator_callback_t file_callback, fs_iterator_callback_t dir_callback); +long int FS_IterateDirectoryTree(const char* path, fs_iterator_callback_t file_callback); +long int FS_IterateDirectoryTree(const char* path, fs_iterator_callback_t file_callback, fs_iterator_callback_t dir_callback); diff --git a/components/gsc_parser/src/cpp/main.cpp b/components/gsc_parser/src/cpp/main.cpp new file mode 100644 index 00000000..feca4d3a --- /dev/null +++ b/components/gsc_parser/src/cpp/main.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +#ifdef _WIN32 + #include +#endif + +#include "sys/sys_platform.h" +#include "sys/sys_worker.h" +#include "sys/sys_cpu.h" +#include "cl/cl_arg.h" +#include "cl/cl_cvar.h" +#include "cl/cl_cmd.h" + +int main(int argc, char** argv) +{ + if(argc <= 1) + { + Arg_PrintUsage(); + return 1; + } + + ArgParsedInfo cmd_info; + if(int err = Arg_ParseArguments(argc - 1, argv + 1, &cmd_info)) + { + fprintf(stderr, "Fatal Error: %d\n", err); + return err; + } + + return cmd_info.Cmd()->Exec(cmd_info.Argc(), cmd_info.Argv()); +} \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/parser/gsc.tab.cpp b/components/gsc_parser/src/cpp/parser/gsc.tab.cpp new file mode 100644 index 00000000..44f6b3aa --- /dev/null +++ b/components/gsc_parser/src/cpp/parser/gsc.tab.cpp @@ -0,0 +1,2818 @@ +/* A Bison parser, made by GNU Bison 3.0.4. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + + This program 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.0.4" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 2 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* Copy the first part of user declarations. */ +#line 1 "gsc.ypp" /* yacc.c:339 */ + + #include + #include + #include + + #include "../symbols/symbol.h" + #include "gsc.tab.hpp" + #include "gsc.yy.h" + + int yylex(YYSTYPE *yylval_param, YYLTYPE *yylloc_param, void* yyscanner); + extern void yyerror(YYLTYPE* loc, Symbol** yyAST_out, void* scanner, const char* err); + +#line 79 "cpp/parser/gsc.tab.cpp" /* yacc.c:339 */ + +# ifndef YY_NULL +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULL nullptr +# else +# define YY_NULL 0 +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* In a future release of Bison, this section will be replaced + by #include "gsc.tab.hpp". */ +#ifndef YY_YY_CPP_PARSER_GSC_TAB_HPP_INCLUDED +# define YY_YY_CPP_PARSER_GSC_TAB_HPP_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif +/* "%code requires" blocks. */ +#line 30 "gsc.ypp" /* yacc.c:355 */ + + #include "../sys/sys_platform.h" + #include "../symbols/operator_enum.h" + + // + // Forward declarations for any custom classes used in YYSTYPE + // + class Symbol; + class Include; + + class Literal; + class Identifier; + + class Conditional; + + class Animtree; + class Expression; + class Member; + class Reference; + class Pointer; + + class Function; + class Call; + + class Return; + class Wait; + +#line 137 "cpp/parser/gsc.tab.cpp" /* yacc.c:355 */ + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + T_EOF = 0, + T_STRING = 258, + T_FLOAT = 259, + T_INT = 260, + INCLUDE = 261, + USING_ANIMTREE = 262, + ANIMTREE = 263, + OP_LPAREN = 264, + OP_RPAREN = 265, + FUNC_POINTER_BEGIN = 266, + OP_LBRACE = 267, + OP_RBRACE = 268, + OP_COMMA = 269, + OP_INC = 270, + OP_DEC = 271, + OP_COLON_DOUBLE = 272, + OP_COLON = 273, + OP_COLON_SEMI = 274, + OP_QMARK = 275, + OP_HASH = 276, + T_IF = 277, + T_ELSE = 278, + T_SWITCH = 279, + T_CASE = 280, + T_DEFAULT = 281, + T_BREAK = 282, + T_FOR = 283, + T_WHILE = 284, + T_CONTINUE = 285, + T_RETURN = 286, + T_THREAD = 287, + T_WAIT = 288, + T_FILEPATH = 289, + T_IDENTIFIER = 290, + OP_LBRACKET = 291, + OP_RBRACKET = 292, + OP_DOT = 293, + OP_NOT = 294, + OP_BW_NOT = 295, + OP_MULT = 296, + OP_DIV = 297, + OP_MOD = 298, + OP_ADD = 299, + OP_SUB = 300, + OP_LSHIFT = 301, + OP_RSHIFT = 302, + OP_CMP_LT = 303, + OP_CMP_LE = 304, + OP_CMP_GT = 305, + OP_CMP_GE = 306, + OP_CMP_EQ = 307, + OP_CMP_NEQ = 308, + OP_BW_AND = 309, + OP_BW_XOR = 310, + OP_BW_OR = 311, + OP_CMP_AND = 312, + OP_CMP_OR = 313, + OP_ASSIGN = 314, + OP_ASSIGN_ADD = 315, + OP_ASSIGN_SUB = 316, + OP_ASSIGN_MULT = 317, + OP_ASSIGN_DIV = 318, + OP_ASSIGN_BW_AND = 319, + OP_ASSIGN_BW_XOR = 320, + OP_ASSIGN_BW_OR = 321, + T_INVALID = 322, + OP_INC_PRE = 323, + OP_DEC_PRE = 324, + OP_POSITIVE = 325, + OP_NEGATIVE = 326, + OP_INC_POST = 327, + OP_DEC_POST = 328 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE YYSTYPE; +union YYSTYPE +{ +#line 58 "gsc.ypp" /* yacc.c:355 */ + + OPERATOR_TYPE op; //operator + + int literal_int; + float literal_float; + char* literal_string; + + char* t_identifier; + + Symbol* symbol; + Include* include; + Animtree* animtree; + + Literal* literal; + + Member* member; + + Expression* expression; + Reference* reference; + Pointer* pointer; + + Function* function; + Call* call; + + Return* retn; + +#line 251 "cpp/parser/gsc.tab.cpp" /* yacc.c:355 */ +}; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int yyparse (Symbol** yyAST_out, void* scanner); + +#endif /* !YY_YY_CPP_PARSER_GSC_TAB_HPP_INCLUDED */ + +/* Copy the second part of user declarations. */ + +#line 279 "cpp/parser/gsc.tab.cpp" /* yacc.c:358 */ + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if (! defined __GNUC__ || __GNUC__ < 2 \ + || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)) +# define __attribute__(Spec) /* empty */ +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 15 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 493 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 74 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 37 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 117 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 234 + +/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned + by yylex, with out-of-bounds checking. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 328 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, without out-of-bounds checking. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 211, 211, 217, 223, 231, 236, 239, 242, 243, + 244, 247, 252, 255, 261, 263, 265, 270, 272, 277, + 282, 287, 290, 294, 300, 306, 312, 318, 324, 333, + 338, 343, 345, 350, 356, 364, 369, 371, 373, 378, + 383, 391, 396, 397, 403, 404, 408, 410, 414, 416, + 418, 420, 422, 427, 429, 434, 438, 440, 442, 444, + 446, 448, 452, 454, 458, 460, 462, 464, 466, 468, + 470, 472, 474, 476, 478, 480, 482, 484, 486, 488, + 490, 492, 494, 496, 498, 500, 502, 504, 506, 508, + 510, 515, 519, 524, 526, 531, 538, 543, 545, 550, + 555, 558, 564, 566, 573, 574, 575, 576, 577, 578, + 579, 580, 581, 583, 585, 589, 601, 604 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "T_EOF", "error", "$undefined", "T_STRING", "T_FLOAT", "T_INT", + "INCLUDE", "USING_ANIMTREE", "ANIMTREE", "OP_LPAREN", "OP_RPAREN", + "FUNC_POINTER_BEGIN", "OP_LBRACE", "OP_RBRACE", "OP_COMMA", "OP_INC", + "OP_DEC", "OP_COLON_DOUBLE", "OP_COLON", "OP_COLON_SEMI", "OP_QMARK", + "OP_HASH", "T_IF", "T_ELSE", "T_SWITCH", "T_CASE", "T_DEFAULT", + "T_BREAK", "T_FOR", "T_WHILE", "T_CONTINUE", "T_RETURN", "T_THREAD", + "T_WAIT", "T_FILEPATH", "T_IDENTIFIER", "OP_LBRACKET", "OP_RBRACKET", + "OP_DOT", "OP_NOT", "OP_BW_NOT", "OP_MULT", "OP_DIV", "OP_MOD", "OP_ADD", + "OP_SUB", "OP_LSHIFT", "OP_RSHIFT", "OP_CMP_LT", "OP_CMP_LE", + "OP_CMP_GT", "OP_CMP_GE", "OP_CMP_EQ", "OP_CMP_NEQ", "OP_BW_AND", + "OP_BW_XOR", "OP_BW_OR", "OP_CMP_AND", "OP_CMP_OR", "OP_ASSIGN", + "OP_ASSIGN_ADD", "OP_ASSIGN_SUB", "OP_ASSIGN_MULT", "OP_ASSIGN_DIV", + "OP_ASSIGN_BW_AND", "OP_ASSIGN_BW_XOR", "OP_ASSIGN_BW_OR", "T_INVALID", + "OP_INC_PRE", "OP_DEC_PRE", "OP_POSITIVE", "OP_NEGATIVE", "OP_INC_POST", + "OP_DEC_POST", "$accept", "IncludeDirective", "AnimtreeDirective", + "FunctionDeclaration", "FormalParameterList", "SourceElement", + "SourceElements", "Program", "StringLiteral", "NumericLiteral", "Block", + "FunctionParameterList", "FunctionCall", "FunctionExpression", + "PointerExpression", "ReferenceExpression", "AnimReferenceExpression", + "MemberExpression", "ElementList", "ListExpression", "ObjectExpression", + "LiteralExpression", "OptionalExpression", "BasicExpression", + "ModifiableExpression", "e", "Expression", "ExpressionStatement", + "ReturnStatement", "WaitStatement", "EmptyStatement", "IfStatement", + "SwitchStatement", "CaseStatement", "LoopStatement", "Statement", + "StatementList", YY_NULL +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328 +}; +# endif + +#define YYPACT_NINF -198 + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-198))) + +#define YYTABLE_NINF -43 + +#define yytable_value_is_error(Yytable_value) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 20, -16, 14, 33, -198, -198, -198, -198, 22, 45, + 28, 46, 24, -198, -198, -198, -198, 54, -198, -6, + 57, 74, 42, -198, -198, -198, -198, 254, -198, -198, + -198, -198, -198, 342, 124, -198, 124, 124, 52, -198, + 94, 83, 298, 114, 27, 106, 115, 117, 135, 123, + 35, 129, 342, 116, 136, 111, 342, 342, 126, 342, + 342, 147, -198, -198, -198, -198, -198, 143, 153, -198, + -198, -198, 84, -198, -198, 19, 356, 146, -198, -198, + -198, -198, -198, -198, -198, -198, -198, 2, 188, 152, + 16, 77, -198, 153, 84, 119, 119, -198, -198, 342, + -198, 342, 149, -198, -198, 342, 342, -198, -198, 159, + 162, 171, 172, 163, 148, 342, -198, -198, -198, -198, + -198, -198, -198, 342, 342, 136, 342, 124, -198, -198, + -198, 342, 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, -198, -198, 342, -198, + 342, 131, 150, 174, 176, -198, 169, -198, 179, -198, + 342, 342, 342, -198, -198, 48, -198, 51, 88, 155, + -198, -198, -198, -198, 65, 65, 132, 132, 323, 323, + 323, 323, 442, 442, 429, 415, 399, 382, 356, 356, + 356, 356, 356, 356, 356, 356, -198, -198, -198, -198, + 298, 298, 342, 298, 89, 107, 133, -198, 342, -198, + -198, -198, -198, -198, 175, -198, -198, -198, -198, -198, + 342, 180, 298, -198 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 0, 0, 0, 8, 9, 10, 12, 0, 0, + 0, 0, 7, 13, 11, 1, 2, 0, 6, 0, + 0, 0, 0, 3, 117, 4, 5, 0, 116, 14, + 18, 17, 52, 0, 0, 19, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 53, 0, 0, 0, 0, 0, + 0, 0, 45, 44, 104, 30, 43, 0, 51, 89, + 54, 50, 48, 49, 55, 42, 91, 0, 106, 111, + 105, 114, 107, 108, 109, 110, 115, 0, 91, 0, + 0, 0, 42, 0, 0, 56, 57, 34, 16, 0, + 98, 0, 0, 101, 112, 47, 0, 113, 93, 0, + 0, 0, 0, 0, 0, 22, 38, 61, 60, 35, + 58, 59, 15, 22, 22, 0, 0, 0, 29, 62, + 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 92, 41, 0, 90, + 0, 0, 0, 0, 0, 100, 0, 46, 0, 94, + 22, 22, 22, 95, 33, 0, 21, 0, 0, 0, + 37, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 40, 39, 32, 31, + 0, 0, 47, 0, 0, 0, 0, 23, 0, 25, + 27, 36, 97, 99, 0, 102, 24, 26, 28, 20, + 47, 0, 0, 103 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -198, -198, -198, -198, -198, 185, -198, -198, -198, -198, + 178, -87, -70, -198, 144, -31, -198, -198, -198, -198, + -27, 156, -197, -198, -23, 72, -33, -198, -198, -198, + -198, -198, -198, -198, -198, -41, -198 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 4, 5, 6, 19, 7, 8, 9, 62, 63, + 64, 175, 65, 66, 67, 68, 69, 70, 87, 71, + 72, 73, 166, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 27 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 89, 100, 128, 90, 21, 93, 93, 91, 22, 94, + 94, 92, 157, 95, 96, 224, 158, 109, 10, 113, + 112, 128, 13, 11, 128, 124, 1, 2, 1, 2, + 29, 30, 31, 231, 129, 130, 177, 178, 29, 30, + 31, 93, 12, 32, 33, 15, 34, 16, 40, 17, + 36, 37, 38, 161, 108, 3, 40, 3, 217, 18, + 93, 219, 218, 93, 20, 218, 163, 51, 164, 53, + 54, 55, 167, 168, 56, 57, 23, 26, 58, 59, + 60, 61, 176, 214, 215, 216, 24, 97, 34, 61, + 176, 176, 99, 179, 38, 34, 93, 98, 220, 226, + 180, 38, 218, 218, 92, 88, 131, 132, 133, 51, + 128, 53, 125, 126, 162, 127, 51, 227, 53, 125, + 126, 218, 127, 101, 103, 206, 105, 207, 117, 118, + -42, 120, 121, 114, 104, 34, -42, 176, 176, 176, + 34, 38, 107, 228, 106, 115, 38, 218, 116, 93, + 122, -42, 123, -42, -42, -42, 51, -42, 53, 54, + 55, 119, 124, 53, 110, 156, 160, 165, 208, 222, + 223, 170, 225, 131, 132, 133, 134, 135, 169, 167, + 171, 172, 173, 174, 210, 229, 211, 209, 212, 213, + 232, 233, 221, 14, 230, 111, 0, 167, 159, 25, + 102, 0, 0, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 0, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 0, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 28, 0, 29, 30, 31, + 0, 0, 32, 33, 0, 34, 24, 35, 0, 36, + 37, 38, 0, 39, 0, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 0, 0, 56, 57, 0, 0, 58, 59, 60, + 0, 29, 30, 31, 0, 0, 32, 33, 61, 34, + 24, 0, 0, 36, 37, 38, 0, 39, 0, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 0, 0, 56, 57, 0, + 0, 58, 59, 60, 0, 29, 30, 31, 0, 0, + 32, 33, 61, 34, 0, 0, 0, 36, 37, 38, + 0, 0, 0, 40, 131, 132, 133, 134, 135, 136, + 137, 0, 0, 0, 51, 0, 53, 54, 55, 0, + 0, 56, 57, 0, 0, 58, 59, 60, 0, 0, + 0, 0, 0, 0, 0, 0, 61, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 0, 145, 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 0, 145, 146, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 0, 145, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141 +}; + +static const yytype_int16 yycheck[] = +{ + 33, 42, 72, 34, 10, 36, 37, 34, 14, 36, + 37, 34, 10, 36, 37, 212, 14, 50, 34, 52, + 51, 91, 0, 9, 94, 9, 6, 7, 6, 7, + 3, 4, 5, 230, 15, 16, 123, 124, 3, 4, + 5, 72, 9, 8, 9, 0, 11, 19, 21, 3, + 15, 16, 17, 37, 19, 35, 21, 35, 10, 35, + 91, 10, 14, 94, 10, 14, 99, 32, 101, 34, + 35, 36, 105, 106, 39, 40, 19, 35, 43, 44, + 45, 54, 115, 170, 171, 172, 12, 35, 11, 54, + 123, 124, 9, 126, 17, 11, 127, 3, 10, 10, + 127, 17, 14, 14, 127, 33, 41, 42, 43, 32, + 180, 34, 35, 36, 37, 38, 32, 10, 34, 35, + 36, 14, 38, 9, 18, 158, 9, 160, 56, 57, + 11, 59, 60, 17, 19, 11, 17, 170, 171, 172, + 11, 17, 19, 10, 9, 9, 17, 14, 37, 180, + 3, 32, 9, 34, 35, 36, 32, 38, 34, 35, + 36, 35, 9, 34, 35, 19, 14, 18, 37, 210, + 211, 9, 213, 41, 42, 43, 44, 45, 19, 212, + 9, 9, 19, 35, 10, 218, 10, 37, 19, 10, + 10, 232, 37, 8, 19, 51, -1, 230, 10, 21, + 44, -1, -1, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, -1, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, -1, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 1, -1, 3, 4, 5, + -1, -1, 8, 9, -1, 11, 12, 13, -1, 15, + 16, 17, -1, 19, -1, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, -1, -1, 39, 40, -1, -1, 43, 44, 45, + -1, 3, 4, 5, -1, -1, 8, 9, 54, 11, + 12, -1, -1, 15, 16, 17, -1, 19, -1, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, -1, -1, 39, 40, -1, + -1, 43, 44, 45, -1, 3, 4, 5, -1, -1, + 8, 9, 54, 11, -1, -1, -1, 15, 16, 17, + -1, -1, -1, 21, 41, 42, 43, 44, 45, 46, + 47, -1, -1, -1, 32, -1, 34, 35, 36, -1, + -1, 39, 40, -1, -1, 43, 44, 45, -1, -1, + -1, -1, -1, -1, -1, -1, 54, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, -1, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, -1, 56, 57, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, -1, 56, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 6, 7, 35, 75, 76, 77, 79, 80, 81, + 34, 9, 9, 0, 79, 0, 19, 3, 35, 78, + 10, 10, 14, 19, 12, 84, 35, 110, 1, 3, + 4, 5, 8, 9, 11, 13, 15, 16, 17, 19, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 39, 40, 43, 44, + 45, 54, 82, 83, 84, 86, 87, 88, 89, 90, + 91, 93, 94, 95, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 92, 99, 100, + 89, 94, 98, 89, 94, 98, 98, 35, 3, 9, + 109, 9, 95, 18, 19, 9, 9, 19, 19, 100, + 35, 88, 89, 100, 17, 9, 37, 99, 99, 35, + 99, 99, 3, 9, 9, 35, 36, 38, 86, 15, + 16, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 19, 10, 14, 10, + 14, 37, 37, 100, 100, 18, 96, 100, 100, 19, + 9, 9, 9, 19, 35, 85, 100, 85, 85, 100, + 94, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 100, 100, 37, 37, + 10, 10, 19, 10, 85, 85, 85, 10, 14, 10, + 10, 37, 109, 109, 96, 109, 10, 10, 10, 100, + 19, 96, 10, 109 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 74, 75, 76, 77, 78, 78, 78, 79, 79, + 79, 80, 80, 81, 82, 82, 82, 83, 83, 84, + 85, 85, 85, 86, 86, 86, 86, 86, 86, 87, + 87, 88, 88, 89, 89, 90, 91, 91, 91, 92, + 92, 93, 94, 94, 95, 95, 96, 96, 97, 97, + 97, 97, 97, 98, 98, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 100, 101, 102, 102, 103, 104, 105, 105, 106, + 107, 107, 108, 108, 109, 109, 109, 109, 109, 109, + 109, 109, 109, 109, 109, 110, 110, 110 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 3, 5, 5, 3, 1, 0, 1, 1, + 1, 2, 1, 2, 1, 2, 2, 1, 1, 3, + 3, 1, 0, 4, 5, 4, 5, 4, 5, 2, + 1, 4, 4, 3, 2, 2, 4, 3, 2, 3, + 3, 3, 1, 1, 1, 1, 1, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, + 3, 1, 2, 2, 3, 3, 1, 5, 2, 5, + 3, 2, 5, 9, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 1, 2, 2, 0 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, yyAST_out, scanner, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +__attribute__((__unused__)) +static unsigned +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + unsigned res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, yyAST_out, scanner); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*----------------------------------------. +| Print this symbol's value on YYOUTPUT. | +`----------------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, Symbol** yyAST_out, void* scanner) +{ + FILE *yyo = yyoutput; + YYUSE (yyo); + YYUSE (yylocationp); + YYUSE (yyAST_out); + YYUSE (scanner); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + YYUSE (yytype); +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, Symbol** yyAST_out, void* scanner) +{ + YYFPRINTF (yyoutput, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + YY_LOCATION_PRINT (yyoutput, *yylocationp); + YYFPRINTF (yyoutput, ": "); + yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, yyAST_out, scanner); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, Symbol** yyAST_out, void* scanner) +{ + unsigned long int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[yyssp[yyi + 1 - yynrhs]], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , &(yylsp[(yyi + 1) - (yynrhs)]) , yyAST_out, scanner); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, yyAST_out, scanner); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +yystrlen (const char *yystr) +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULL; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, Symbol** yyAST_out, void* scanner) +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (yyAST_out); + YYUSE (scanner); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + switch (yytype) + { + case 3: /* T_STRING */ +#line 204 "gsc.ypp" /* yacc.c:1257 */ + { free(((*yyvaluep).literal_string)); } +#line 1390 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 4: /* T_FLOAT */ +#line 204 "gsc.ypp" /* yacc.c:1257 */ + { free(((*yyvaluep).literal_string)); } +#line 1396 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 5: /* T_INT */ +#line 204 "gsc.ypp" /* yacc.c:1257 */ + { free(((*yyvaluep).literal_string)); } +#line 1402 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 34: /* T_FILEPATH */ +#line 204 "gsc.ypp" /* yacc.c:1257 */ + { free(((*yyvaluep).literal_string)); } +#line 1408 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 35: /* T_IDENTIFIER */ +#line 204 "gsc.ypp" /* yacc.c:1257 */ + { free(((*yyvaluep).t_identifier)); } +#line 1414 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 75: /* IncludeDirective */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1420 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 76: /* AnimtreeDirective */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1426 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 77: /* FunctionDeclaration */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1432 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 78: /* FormalParameterList */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1438 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 79: /* SourceElement */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1444 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 80: /* SourceElements */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1450 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 82: /* StringLiteral */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).literal); } +#line 1456 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 83: /* NumericLiteral */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).literal); } +#line 1462 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 84: /* Block */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1468 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 85: /* FunctionParameterList */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1474 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 86: /* FunctionCall */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).call); } +#line 1480 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 87: /* FunctionExpression */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1486 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 88: /* PointerExpression */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).pointer); } +#line 1492 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 89: /* ReferenceExpression */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).reference); } +#line 1498 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 90: /* AnimReferenceExpression */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1504 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 91: /* MemberExpression */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1510 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 92: /* ElementList */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1516 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 93: /* ListExpression */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1522 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 94: /* ObjectExpression */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1528 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 95: /* LiteralExpression */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).literal); } +#line 1534 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 96: /* OptionalExpression */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).expression); } +#line 1540 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 97: /* BasicExpression */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).expression); } +#line 1546 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 98: /* ModifiableExpression */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1552 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 99: /* e */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).expression); } +#line 1558 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 100: /* Expression */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).expression); } +#line 1564 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 101: /* ExpressionStatement */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1570 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 102: /* ReturnStatement */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1576 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 103: /* WaitStatement */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1582 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 104: /* EmptyStatement */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1588 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 105: /* IfStatement */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1594 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 106: /* SwitchStatement */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1600 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 107: /* CaseStatement */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1606 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 108: /* LoopStatement */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1612 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 109: /* Statement */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1618 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + case 110: /* StatementList */ +#line 203 "gsc.ypp" /* yacc.c:1257 */ + { delete ((*yyvaluep).symbol); } +#line 1624 "cpp/parser/gsc.tab.cpp" /* yacc.c:1257 */ + break; + + + default: + break; + } + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (Symbol** yyAST_out, void* scanner) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs; + + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + 'yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + +/* User initialization code. */ +#line 22 "gsc.ypp" /* yacc.c:1429 */ +{ + #if VSCODE_COMPATIBLE_LOCATION + yylloc.first_line = yylloc.last_line = 0; + yylloc.first_column = yylloc.last_column = 0; + #endif +} + +#line 1740 "cpp/parser/gsc.tab.cpp" /* yacc.c:1429 */ + yylsp[0] = yylloc; + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yyls1, yysize * sizeof (*yylsp), + &yystacksize); + + yyls = yyls1; + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, &yylloc, scanner); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 212 "gsc.ypp" /* yacc.c:1646 */ + { + (yyval.symbol) = new Include(new Literal((yyvsp[-1].literal_string), (yylsp[-1]), S_TYPE_LITERAL_FILEPATH), (yyloc)); + } +#line 1931 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 3: +#line 218 "gsc.ypp" /* yacc.c:1646 */ + { + (yyval.symbol) = new Animtree(new Literal((yyvsp[-2].literal_string), (yylsp[-2]), S_TYPE_LITERAL_STRING), (yyloc)); + } +#line 1939 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 4: +#line 224 "gsc.ypp" /* yacc.c:1646 */ + { + (yyval.symbol) = new Function(new Identifier((yyvsp[-4].t_identifier), (yylsp[-4])), (yyloc)); + (yyval.symbol)->AddChild(new Group((yyvsp[-2].symbol), (yylsp[-2]))); + (yyval.symbol)->AddChild((yyvsp[0].symbol)); + } +#line 1949 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 5: +#line 232 "gsc.ypp" /* yacc.c:1646 */ + { + (yyvsp[-2].symbol)->AddToEnd( new Identifier((yyvsp[0].t_identifier), (yylsp[0])) ); + (yyval.symbol) = (yyvsp[-2].symbol); + } +#line 1958 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 6: +#line 237 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Identifier((yyvsp[0].t_identifier), (yylsp[0])); } +#line 1964 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 7: +#line 239 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = NULL; } +#line 1970 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 11: +#line 248 "gsc.ypp" /* yacc.c:1646 */ + { + (yyvsp[-1].symbol)->AddToEnd((yyvsp[0].symbol)); + (yyval.symbol) = (yyvsp[-1].symbol); + } +#line 1979 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 13: +#line 256 "gsc.ypp" /* yacc.c:1646 */ + { + *yyAST_out = new Group((yyvsp[-1].symbol), (yylsp[-1])); + } +#line 1987 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 14: +#line 262 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.literal) = new Literal((yyvsp[0].literal_string), (yylsp[0]), S_TYPE_LITERAL_STRING); } +#line 1993 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 15: +#line 264 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.literal) = new Literal((yyvsp[0].literal_string), (yyloc), S_TYPE_LITERAL_STRING); } +#line 1999 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 16: +#line 266 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.literal) = new Literal((yyvsp[0].literal_string), (yyloc), S_TYPE_LITERAL_STRING); } +#line 2005 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 17: +#line 271 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.literal) = new Literal((yyvsp[0].literal_string), (yylsp[0]), S_TYPE_LITERAL_INT); } +#line 2011 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 18: +#line 273 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.literal) = new Literal((yyvsp[0].literal_string), (yylsp[0]), S_TYPE_LITERAL_FLOAT); } +#line 2017 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 19: +#line 278 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Group((yyvsp[-1].symbol), (yylsp[-1])); } +#line 2023 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 20: +#line 283 "gsc.ypp" /* yacc.c:1646 */ + { + (yyvsp[-2].symbol)->AddToEnd((yyvsp[0].expression)); + (yyval.symbol) = (yyvsp[-2].symbol); + } +#line 2032 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 21: +#line 288 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = (yyvsp[0].expression); } +#line 2038 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 22: +#line 290 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = NULL; } +#line 2044 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 23: +#line 295 "gsc.ypp" /* yacc.c:1646 */ + { + (yyval.call) = new Call((yyloc), CALL_FLAG_IDENTIFIER); + (yyval.call)->AddChild(new Identifier((yyvsp[-3].t_identifier),(yylsp[-3]))); + (yyval.call)->AddChild(new Group((yyvsp[-1].symbol), (yylsp[-1]))); + } +#line 2054 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 24: +#line 301 "gsc.ypp" /* yacc.c:1646 */ + { + (yyval.call) = new Call((yyloc), CALL_FLAG_THREAD | CALL_FLAG_IDENTIFIER); + (yyval.call)->AddChild(new Identifier((yyvsp[-3].t_identifier),(yylsp[-3]))); + (yyval.call)->AddChild(new Group((yyvsp[-1].symbol), (yylsp[-1]))); + } +#line 2064 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 25: +#line 307 "gsc.ypp" /* yacc.c:1646 */ + { + (yyval.call) = new Call((yyloc), CALL_FLAG_POINTER); + (yyval.call)->AddChild((yyvsp[-3].pointer)); + (yyval.call)->AddChild(new Group((yyvsp[-1].symbol), (yylsp[-1]))); + } +#line 2074 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 26: +#line 313 "gsc.ypp" /* yacc.c:1646 */ + { + (yyval.call) = new Call((yyloc), CALL_FLAG_THREAD | CALL_FLAG_POINTER); + (yyval.call)->AddChild((yyvsp[-3].pointer)); + (yyval.call)->AddChild(new Group((yyvsp[-1].symbol), (yylsp[-1]))); + } +#line 2084 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 27: +#line 319 "gsc.ypp" /* yacc.c:1646 */ + { + (yyval.call) = new Call((yyloc), CALL_FLAG_REFERENCE); + (yyval.call)->AddChild((yyvsp[-3].reference)); + (yyval.call)->AddChild(new Group((yyvsp[-1].symbol), (yylsp[-1]))); + } +#line 2094 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 28: +#line 325 "gsc.ypp" /* yacc.c:1646 */ + { + (yyval.call) = new Call((yyloc), CALL_FLAG_THREAD | CALL_FLAG_REFERENCE); + (yyval.call)->AddChild((yyvsp[-3].reference)); + (yyval.call)->AddChild(new Group((yyvsp[-1].symbol), (yylsp[-1]))); + } +#line 2104 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 29: +#line 334 "gsc.ypp" /* yacc.c:1646 */ + { + (yyvsp[0].call)->SetCaller((yyvsp[-1].expression)); + (yyval.symbol) = (yyvsp[0].call); + } +#line 2113 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 30: +#line 339 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = (yyvsp[0].call); } +#line 2119 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 31: +#line 344 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.pointer) = new Pointer((yyvsp[-2].expression), (yyloc)); } +#line 2125 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 32: +#line 346 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.pointer) = new Pointer((yyvsp[-2].expression), (yyloc)); } +#line 2131 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 33: +#line 351 "gsc.ypp" /* yacc.c:1646 */ + { + Literal* file = new Literal((yyvsp[-2].literal_string), (yylsp[-2]), S_TYPE_LITERAL_FILEPATH); + Identifier* identifier = new Identifier((yyvsp[0].t_identifier), (yylsp[0])); + (yyval.reference) = new Reference(file, identifier, (yyloc)); + } +#line 2141 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 34: +#line 357 "gsc.ypp" /* yacc.c:1646 */ + { + Identifier* identifier = new Identifier((yyvsp[0].t_identifier), (yylsp[0])); + (yyval.reference) = new Reference(NULL, identifier, (yyloc)); + } +#line 2150 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 35: +#line 365 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Identifier((yyvsp[0].t_identifier), (yylsp[0])); } +#line 2156 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 36: +#line 370 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Member((yyvsp[-3].expression), (yyvsp[-1].expression), (yyloc), S_TYPE_MEMBER_ARRAY_ELEMENT); } +#line 2162 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 37: +#line 372 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Member((yyvsp[-2].expression), (yyvsp[0].expression), (yyloc), S_TYPE_MEMBER_OBJECT_PROPERTY); } +#line 2168 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 38: +#line 374 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Member(NULL, NULL, (yyloc), S_TYPE_MEMBER_ARRAY_EMPTY); } +#line 2174 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 39: +#line 379 "gsc.ypp" /* yacc.c:1646 */ + { + (yyvsp[-2].expression)->AddToEnd((yyvsp[0].expression)); + (yyval.symbol) = (yyvsp[-2].expression); + } +#line 2183 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 40: +#line 384 "gsc.ypp" /* yacc.c:1646 */ + { + (yyvsp[-2].symbol)->AddToEnd((yyvsp[0].expression)); + (yyval.symbol) = (yyvsp[-2].symbol); + } +#line 2192 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 41: +#line 392 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = (yyvsp[-1].symbol); } +#line 2198 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 47: +#line 410 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = NULL; } +#line 2204 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 48: +#line 415 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = (yyvsp[0].expression); } +#line 2210 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 49: +#line 417 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = (yyvsp[0].expression); } +#line 2216 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 50: +#line 419 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = (yyvsp[0].expression); } +#line 2222 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 51: +#line 421 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = (yyvsp[0].expression); } +#line 2228 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 52: +#line 423 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyloc)); } +#line 2234 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 53: +#line 428 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Identifier((yyvsp[0].t_identifier), (yylsp[0])); } +#line 2240 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 55: +#line 435 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = (Expression*)new Group((yyvsp[0].expression), (yylsp[0])); } +#line 2246 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 56: +#line 439 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression(OP_TYPE_INC, (yyvsp[0].expression), (yyloc)); } +#line 2252 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 57: +#line 441 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression(OP_TYPE_DEC, (yyvsp[0].expression), (yyloc)); } +#line 2258 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 58: +#line 443 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression(OP_TYPE_ADD, (yyvsp[0].expression), (yyloc)); } +#line 2264 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 59: +#line 445 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression(OP_TYPE_SUB, (yyvsp[0].expression), (yyloc)); } +#line 2270 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 60: +#line 447 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression(OP_TYPE_BW_NOT, (yyvsp[0].expression), (yyloc)); } +#line 2276 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 61: +#line 449 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression(OP_TYPE_NOT, (yyvsp[0].expression), (yyloc)); } +#line 2282 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 62: +#line 453 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-1].expression), OP_TYPE_INC, (yyloc)); } +#line 2288 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 63: +#line 455 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-1].expression), OP_TYPE_INC, (yyloc)); } +#line 2294 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 64: +#line 459 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_MULT, (yyvsp[0].expression), (yyloc)); } +#line 2300 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 65: +#line 461 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_DIV, (yyvsp[0].expression), (yyloc)); } +#line 2306 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 66: +#line 463 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_MOD, (yyvsp[0].expression), (yyloc)); } +#line 2312 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 67: +#line 465 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_ADD, (yyvsp[0].expression), (yyloc)); } +#line 2318 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 68: +#line 467 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_SUB, (yyvsp[0].expression), (yyloc)); } +#line 2324 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 69: +#line 469 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_LSHIFT, (yyvsp[0].expression), (yyloc)); } +#line 2330 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 70: +#line 471 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_RSHIFT, (yyvsp[0].expression), (yyloc)); } +#line 2336 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 71: +#line 473 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_CMP_LT, (yyvsp[0].expression), (yyloc)); } +#line 2342 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 72: +#line 475 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_CMP_LE, (yyvsp[0].expression), (yyloc)); } +#line 2348 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 73: +#line 477 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_CMP_GT, (yyvsp[0].expression), (yyloc)); } +#line 2354 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 74: +#line 479 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_CMP_GE, (yyvsp[0].expression), (yyloc)); } +#line 2360 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 75: +#line 481 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_CMP_EQ, (yyvsp[0].expression), (yyloc)); } +#line 2366 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 76: +#line 483 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_CMP_NEQ, (yyvsp[0].expression), (yyloc)); } +#line 2372 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 77: +#line 485 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_BW_AND, (yyvsp[0].expression), (yyloc)); } +#line 2378 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 78: +#line 487 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_BW_OR, (yyvsp[0].expression), (yyloc)); } +#line 2384 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 79: +#line 489 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_CMP_AND, (yyvsp[0].expression), (yyloc)); } +#line 2390 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 80: +#line 491 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_CMP_OR, (yyvsp[0].expression), (yyloc)); } +#line 2396 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 81: +#line 493 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_ASSIGN, (yyvsp[0].expression), (yyloc)); } +#line 2402 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 82: +#line 495 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_ASSIGN_ADD, (yyvsp[0].expression), (yyloc)); } +#line 2408 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 83: +#line 497 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_ASSIGN_SUB, (yyvsp[0].expression), (yyloc)); } +#line 2414 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 84: +#line 499 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_ASSIGN_MULT, (yyvsp[0].expression), (yyloc)); } +#line 2420 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 85: +#line 501 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_ASSIGN_DIV, (yyvsp[0].expression), (yyloc)); } +#line 2426 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 86: +#line 503 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_ASSIGN_BW_AND, (yyvsp[0].expression), (yyloc)); } +#line 2432 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 87: +#line 505 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_ASSIGN_BW_XOR, (yyvsp[0].expression), (yyloc)); } +#line 2438 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 88: +#line 507 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = new Expression((yyvsp[-2].expression), OP_TYPE_ASSIGN_BW_OR, (yyvsp[0].expression), (yyloc)); } +#line 2444 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 89: +#line 509 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = (yyvsp[0].expression); } +#line 2450 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 90: +#line 511 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.expression) = (yyvsp[-1].expression); } +#line 2456 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 92: +#line 520 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = (yyvsp[-1].expression); /*new Symbol(@$);*/ } +#line 2462 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 93: +#line 525 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Return(NULL, (yyloc)); } +#line 2468 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 94: +#line 527 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Return((yyvsp[-1].expression), (yyloc)); } +#line 2474 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 95: +#line 532 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Wait((yyvsp[-1].expression), (yyloc)); } +#line 2480 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 96: +#line 539 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Symbol((yyloc)); } +#line 2486 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 97: +#line 544 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Conditional((yyvsp[-2].expression), (yyvsp[0].symbol), (yyloc), S_TYPE_CONDITIONAL_IF); } +#line 2492 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 98: +#line 546 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Conditional(NULL, (yyvsp[0].symbol), (yyloc), S_TYPE_CONDITIONAL_ELSE); } +#line 2498 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 99: +#line 551 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Conditional((yyvsp[-2].expression), (yyvsp[0].symbol), (yyloc), S_TYPE_CONDITIONAL_SWITCH); } +#line 2504 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 100: +#line 556 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Conditional((yyvsp[-1].expression), NULL, (yyloc), S_TYPE_CONDITIONAL_CASE); + } +#line 2511 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 101: +#line 559 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Conditional(NULL, NULL, (yyloc), S_TYPE_CONDITIONAL_CASE); + } +#line 2518 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 102: +#line 565 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Conditional((yyvsp[-2].expression), (yyvsp[0].symbol), (yyloc), S_TYPE_CONDITIONAL_WHILE); } +#line 2524 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 103: +#line 567 "gsc.ypp" /* yacc.c:1646 */ + { + (yyval.symbol) = new Conditional((yyvsp[-6].expression), (yyvsp[-4].expression), (yyvsp[-2].expression), (yyvsp[0].symbol), (yyloc), S_TYPE_CONDITIONAL_FOR); + } +#line 2532 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 112: +#line 582 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Symbol((yyloc)); } +#line 2538 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 113: +#line 584 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = new Symbol((yyloc)); } +#line 2544 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 115: +#line 590 "gsc.ypp" /* yacc.c:1646 */ + { + if((yyvsp[-1].symbol) != NULL) + { + (yyvsp[-1].symbol)->AddToEnd((yyvsp[0].symbol)); + (yyval.symbol) = (yyvsp[-1].symbol); + } + else + { + (yyval.symbol) = (yyvsp[0].symbol); + } + } +#line 2560 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + case 117: +#line 604 "gsc.ypp" /* yacc.c:1646 */ + { (yyval.symbol) = NULL; } +#line 2566 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + break; + + +#line 2570 "cpp/parser/gsc.tab.cpp" /* yacc.c:1646 */ + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, yyAST_out, scanner, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (&yylloc, yyAST_out, scanner, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, yyAST_out, scanner); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + yyerror_range[1] = yylsp[1-yylen]; + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, yyAST_out, scanner); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, yyAST_out, scanner, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, yyAST_out, scanner); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, yylsp, yyAST_out, scanner); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} +#line 607 "gsc.ypp" /* yacc.c:1906 */ + + +// +// Notify the user that there was an error while parsing +// if error recovery is impossible, YYABORT is automatically called +// and the symbol tree is cleaned up +// +void yyerror(YYLTYPE* loc, Symbol** AST, yyscan_t scanner, const char* err) +{ + fprintf(stderr, "PARSE ERROR AT LINE %d(%d): %s\n", loc->first_line, loc->first_column, err); +/*#if !(_DEBUG) + exit(1); +#endif*/ +} diff --git a/components/gsc_parser/src/cpp/parser/gsc.tab.hpp b/components/gsc_parser/src/cpp/parser/gsc.tab.hpp new file mode 100644 index 00000000..7ac42da6 --- /dev/null +++ b/components/gsc_parser/src/cpp/parser/gsc.tab.hpp @@ -0,0 +1,209 @@ +/* A Bison parser, made by GNU Bison 3.0.4. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + + This program 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +#ifndef YY_YY_CPP_PARSER_GSC_TAB_HPP_INCLUDED +# define YY_YY_CPP_PARSER_GSC_TAB_HPP_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif +/* "%code requires" blocks. */ +#line 30 "gsc.ypp" /* yacc.c:1909 */ + + #include "../sys/sys_platform.h" + #include "../symbols/operator_enum.h" + + // + // Forward declarations for any custom classes used in YYSTYPE + // + class Symbol; + class Include; + + class Literal; + class Identifier; + + class Conditional; + + class Animtree; + class Expression; + class Member; + class Reference; + class Pointer; + + class Function; + class Call; + + class Return; + class Wait; + +#line 72 "cpp/parser/gsc.tab.hpp" /* yacc.c:1909 */ + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + T_EOF = 0, + T_STRING = 258, + T_FLOAT = 259, + T_INT = 260, + INCLUDE = 261, + USING_ANIMTREE = 262, + ANIMTREE = 263, + OP_LPAREN = 264, + OP_RPAREN = 265, + FUNC_POINTER_BEGIN = 266, + OP_LBRACE = 267, + OP_RBRACE = 268, + OP_COMMA = 269, + OP_INC = 270, + OP_DEC = 271, + OP_COLON_DOUBLE = 272, + OP_COLON = 273, + OP_COLON_SEMI = 274, + OP_QMARK = 275, + OP_HASH = 276, + T_IF = 277, + T_ELSE = 278, + T_SWITCH = 279, + T_CASE = 280, + T_DEFAULT = 281, + T_BREAK = 282, + T_FOR = 283, + T_WHILE = 284, + T_CONTINUE = 285, + T_RETURN = 286, + T_THREAD = 287, + T_WAIT = 288, + T_FILEPATH = 289, + T_IDENTIFIER = 290, + OP_LBRACKET = 291, + OP_RBRACKET = 292, + OP_DOT = 293, + OP_NOT = 294, + OP_BW_NOT = 295, + OP_MULT = 296, + OP_DIV = 297, + OP_MOD = 298, + OP_ADD = 299, + OP_SUB = 300, + OP_LSHIFT = 301, + OP_RSHIFT = 302, + OP_CMP_LT = 303, + OP_CMP_LE = 304, + OP_CMP_GT = 305, + OP_CMP_GE = 306, + OP_CMP_EQ = 307, + OP_CMP_NEQ = 308, + OP_BW_AND = 309, + OP_BW_XOR = 310, + OP_BW_OR = 311, + OP_CMP_AND = 312, + OP_CMP_OR = 313, + OP_ASSIGN = 314, + OP_ASSIGN_ADD = 315, + OP_ASSIGN_SUB = 316, + OP_ASSIGN_MULT = 317, + OP_ASSIGN_DIV = 318, + OP_ASSIGN_BW_AND = 319, + OP_ASSIGN_BW_XOR = 320, + OP_ASSIGN_BW_OR = 321, + T_INVALID = 322, + OP_INC_PRE = 323, + OP_DEC_PRE = 324, + OP_POSITIVE = 325, + OP_NEGATIVE = 326, + OP_INC_POST = 327, + OP_DEC_POST = 328 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE YYSTYPE; +union YYSTYPE +{ +#line 58 "gsc.ypp" /* yacc.c:1909 */ + + OPERATOR_TYPE op; //operator + + int literal_int; + float literal_float; + char* literal_string; + + char* t_identifier; + + Symbol* symbol; + Include* include; + Animtree* animtree; + + Literal* literal; + + Member* member; + + Expression* expression; + Reference* reference; + Pointer* pointer; + + Function* function; + Call* call; + + Return* retn; + +#line 186 "cpp/parser/gsc.tab.hpp" /* yacc.c:1909 */ +}; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int yyparse (Symbol** yyAST_out, void* scanner); + +#endif /* !YY_YY_CPP_PARSER_GSC_TAB_HPP_INCLUDED */ diff --git a/components/gsc_parser/src/cpp/parser/gsc.yy.cpp b/components/gsc_parser/src/cpp/parser/gsc.yy.cpp new file mode 100644 index 00000000..0f76f7b2 --- /dev/null +++ b/components/gsc_parser/src/cpp/parser/gsc.yy.cpp @@ -0,0 +1,2473 @@ +#line 2 "cpp/parser/gsc.yy.cpp" + +#line 4 "cpp/parser/gsc.yy.cpp" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 37 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart(yyin ,yyscanner ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart (FILE *input_file ,yyscan_t yyscanner ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); +void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +void yypop_buffer_state (yyscan_t yyscanner ); + +static void yyensure_buffer_stack (yyscan_t yyscanner ); +static void yy_load_buffer_state (yyscan_t yyscanner ); +static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); + +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); + +void *yyalloc (yy_size_t ,yyscan_t yyscanner ); +void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); +void yyfree (void * ,yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define yywrap(yyscanner) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); +static int yy_get_next_buffer (yyscan_t yyscanner ); +static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (size_t) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 73 +#define YY_END_OF_BUFFER 74 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[178] = + { 0, + 0, 0, 0, 0, 0, 0, 74, 72, 1, 24, + 72, 57, 25, 49, 72, 14, 15, 41, 36, 21, + 39, 22, 43, 10, 54, 55, 30, 27, 33, 56, + 71, 17, 18, 51, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 19, 46, 20, 52, 4, 4, + 72, 23, 0, 8, 0, 0, 0, 0, 47, 48, + 0, 0, 40, 34, 35, 37, 38, 9, 6, 3, + 2, 42, 9, 10, 0, 0, 53, 28, 29, 26, + 32, 31, 71, 71, 16, 0, 50, 71, 71, 71, + 71, 71, 71, 58, 71, 71, 71, 71, 71, 45, + + 44, 5, 7, 0, 8, 0, 0, 0, 0, 0, + 8, 0, 9, 2, 9, 9, 70, 71, 71, 71, + 71, 71, 64, 71, 71, 71, 71, 71, 0, 0, + 0, 71, 61, 71, 71, 59, 71, 71, 71, 69, + 71, 0, 0, 0, 63, 71, 71, 71, 71, 71, + 65, 0, 0, 0, 71, 71, 67, 60, 68, 0, + 0, 0, 71, 62, 0, 11, 0, 66, 13, 0, + 0, 0, 0, 0, 0, 12, 0 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 6, 1, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 19, 20, 21, + 22, 23, 24, 1, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 27, 28, 29, 30, 1, 31, 32, 33, 34, + + 35, 36, 37, 38, 39, 25, 40, 41, 42, 43, + 44, 25, 25, 45, 46, 47, 48, 25, 49, 25, + 25, 25, 50, 51, 52, 53, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[54] = + { 0, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 4, 1, 5, 1, 1, + 1, 1, 1, 1, 5, 1, 6, 1, 1, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, + 1, 1, 1 + } ; + +static yyconst flex_int16_t yy_base[189] = + { 0, + 0, 0, 364, 363, 368, 367, 372, 377, 377, 349, + 49, 24, 377, 48, 48, 377, 377, 348, 45, 377, + 44, 351, 56, 53, 349, 377, 39, 345, 42, 377, + 59, 51, 377, 344, 64, 67, 66, 69, 68, 72, + 87, 77, 90, 92, 377, 67, 377, 377, 377, 347, + 341, 377, 98, 377, 110, 299, 269, 265, 377, 377, + 79, 118, 377, 377, 377, 377, 377, 98, 377, 377, + 0, 377, 102, 113, 108, 0, 377, 377, 377, 377, + 377, 377, 0, 117, 377, 107, 377, 121, 124, 128, + 127, 131, 134, 132, 135, 137, 142, 145, 151, 377, + + 377, 377, 377, 162, 168, 178, 271, 271, 264, 159, + 165, 182, 377, 0, 157, 377, 275, 173, 179, 181, + 184, 183, 186, 192, 197, 202, 205, 206, 258, 258, + 255, 208, 207, 211, 212, 215, 216, 226, 231, 230, + 236, 248, 246, 243, 239, 240, 241, 249, 250, 257, + 254, 234, 236, 219, 258, 260, 259, 263, 274, 201, + 190, 185, 278, 281, 168, 377, 159, 282, 377, 158, + 138, 118, 96, 65, 55, 377, 377, 313, 319, 325, + 331, 334, 338, 344, 350, 356, 80, 360 + } ; + +static yyconst flex_int16_t yy_def[189] = + { 0, + 177, 1, 178, 178, 179, 179, 177, 177, 177, 177, + 180, 177, 177, 177, 181, 177, 177, 177, 177, 177, + 177, 177, 177, 182, 177, 177, 177, 177, 177, 177, + 183, 177, 177, 177, 183, 183, 183, 183, 183, 183, + 183, 183, 183, 183, 177, 177, 177, 177, 177, 177, + 177, 177, 180, 177, 184, 177, 177, 177, 177, 177, + 181, 185, 177, 177, 177, 177, 177, 177, 177, 177, + 186, 177, 177, 182, 182, 187, 177, 177, 177, 177, + 177, 177, 188, 183, 177, 177, 177, 183, 183, 183, + 183, 183, 183, 183, 183, 183, 183, 183, 183, 177, + + 177, 177, 177, 180, 180, 184, 177, 177, 177, 181, + 181, 185, 177, 186, 177, 177, 187, 183, 183, 183, + 183, 183, 183, 183, 183, 183, 183, 183, 177, 177, + 177, 183, 183, 183, 183, 183, 183, 183, 183, 183, + 183, 177, 177, 177, 183, 183, 183, 183, 183, 183, + 183, 177, 177, 177, 183, 183, 183, 183, 183, 177, + 177, 177, 183, 183, 177, 177, 177, 183, 177, 177, + 177, 177, 177, 177, 177, 177, 0, 177, 177, 177, + 177, 177, 177, 177, 177, 177, 177, 177 + } ; + +static yyconst flex_int16_t yy_nxt[431] = + { 0, + 8, 9, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 8, 33, 34, 31, + 31, 35, 36, 37, 38, 39, 31, 31, 40, 31, + 31, 31, 31, 31, 41, 42, 43, 31, 44, 45, + 46, 47, 48, 54, 56, 59, 54, 64, 66, 78, + 79, 69, 57, 81, 82, 67, 65, 70, 73, 60, + 74, 58, 71, 83, 62, 55, 85, 72, 83, 76, + 83, 83, 83, 83, 117, 76, 83, 54, 100, 176, + 76, 83, 76, 76, 76, 76, 86, 89, 76, 175, + + 91, 83, 54, 76, 83, 62, 83, 94, 88, 92, + 90, 93, 53, 76, 105, 68, 76, 101, 76, 115, + 61, 95, 98, 177, 55, 96, 111, 97, 73, 99, + 74, 83, 85, 113, 76, 83, 106, 116, 83, 76, + 174, 83, 83, 76, 112, 83, 83, 76, 83, 83, + 76, 83, 86, 76, 76, 118, 83, 76, 76, 83, + 76, 76, 121, 76, 173, 83, 54, 54, 76, 119, + 120, 76, 54, 54, 115, 125, 122, 76, 123, 172, + 53, 124, 105, 127, 61, 62, 126, 83, 55, 128, + 111, 62, 116, 83, 55, 83, 171, 83, 83, 76, + + 83, 170, 169, 132, 106, 76, 83, 76, 112, 76, + 76, 83, 76, 133, 135, 167, 83, 136, 76, 83, + 83, 83, 83, 76, 166, 83, 83, 134, 76, 83, + 83, 76, 76, 76, 76, 165, 139, 76, 76, 137, + 83, 76, 76, 138, 83, 83, 141, 145, 162, 146, + 83, 140, 76, 83, 83, 83, 76, 76, 149, 147, + 148, 150, 76, 83, 83, 76, 76, 76, 83, 161, + 151, 83, 83, 83, 83, 76, 76, 83, 160, 154, + 76, 156, 155, 76, 76, 76, 76, 158, 83, 76, + 159, 157, 83, 153, 152, 83, 83, 144, 143, 142, + + 76, 76, 131, 130, 76, 163, 164, 76, 76, 129, + 109, 108, 168, 49, 49, 49, 49, 49, 49, 8, + 8, 8, 8, 8, 8, 53, 53, 53, 53, 53, + 53, 61, 61, 61, 61, 61, 61, 75, 75, 75, + 84, 107, 84, 84, 104, 104, 104, 104, 104, 104, + 110, 110, 110, 110, 110, 110, 114, 103, 114, 114, + 114, 114, 83, 102, 83, 87, 80, 77, 68, 63, + 52, 177, 51, 51, 50, 50, 7, 177, 177, 177, + 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, + 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, + + 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, + 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, + 177, 177, 177, 177, 177, 177, 177, 177, 177, 177 + } ; + +static yyconst flex_int16_t yy_chk[431] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 11, 12, 14, 15, 19, 21, 27, + 27, 23, 12, 29, 29, 21, 19, 23, 24, 14, + 24, 12, 23, 31, 15, 11, 32, 23, 35, 24, + 37, 36, 39, 38, 187, 31, 40, 61, 46, 175, + 35, 42, 37, 36, 39, 38, 32, 36, 40, 174, + + 37, 41, 53, 42, 43, 61, 44, 40, 35, 38, + 36, 39, 55, 41, 55, 68, 43, 46, 44, 73, + 62, 41, 44, 75, 53, 42, 62, 43, 74, 44, + 74, 84, 86, 68, 75, 88, 55, 73, 89, 74, + 173, 91, 90, 84, 62, 92, 94, 88, 93, 95, + 89, 96, 86, 91, 90, 88, 97, 92, 94, 98, + 93, 95, 91, 96, 172, 99, 104, 110, 97, 89, + 90, 98, 105, 111, 115, 96, 92, 99, 93, 171, + 106, 95, 106, 98, 112, 110, 97, 118, 104, 99, + 112, 111, 115, 119, 105, 120, 170, 122, 121, 118, + + 123, 167, 165, 118, 106, 119, 124, 120, 112, 122, + 121, 125, 123, 119, 121, 162, 126, 122, 124, 127, + 128, 133, 132, 125, 161, 134, 135, 120, 126, 136, + 137, 127, 128, 133, 132, 160, 126, 134, 135, 124, + 138, 136, 137, 125, 140, 139, 128, 132, 154, 134, + 141, 127, 138, 145, 146, 147, 140, 139, 138, 135, + 137, 139, 141, 148, 149, 145, 146, 147, 151, 153, + 141, 150, 155, 157, 156, 148, 149, 158, 152, 144, + 151, 147, 146, 150, 155, 157, 156, 149, 159, 158, + 150, 148, 163, 143, 142, 164, 168, 131, 130, 129, + + 159, 117, 109, 108, 163, 155, 156, 164, 168, 107, + 58, 57, 163, 178, 178, 178, 178, 178, 178, 179, + 179, 179, 179, 179, 179, 180, 180, 180, 180, 180, + 180, 181, 181, 181, 181, 181, 181, 182, 182, 182, + 183, 56, 183, 183, 184, 184, 184, 184, 184, 184, + 185, 185, 185, 185, 185, 185, 186, 51, 186, 186, + 186, 186, 188, 50, 188, 34, 28, 25, 22, 18, + 10, 7, 6, 5, 4, 3, 177, 177, 177, 177, + 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, + 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, + + 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, + 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, + 177, 177, 177, 177, 177, 177, 177, 177, 177, 177 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "gsc.l" +#line 2 "gsc.l" + #include + #include "../sys/sys_platform.h" + + #include "gsc.tab.hpp" + #include "../symbols/symbol.h" + + #define YY_DECL int yylex (YYSTYPE *yylval_param, YYLTYPE *yylloc_param, void* yyscanner) + #define YY_USER_ACTION yy_update_loc(yyscanner); + + YYLTYPE *yyget_lloc (yyscan_t yyscanner); + void yyset_lloc (YYLTYPE * yylloc_param , void* yyscanner); + char *yyget_text (void* yyscanner ); + + void yy_update_loc(void* yyscanner) + { + YYLTYPE& yylloc = *yyget_lloc(yyscanner); + char* text = yyget_text(yyscanner); + + yylloc.first_line = yylloc.last_line; + yylloc.first_column = yylloc.last_column; + + for(int i = 0; text[i] != '\0'; i++) + { + if(text[i] == '\n') + { + yylloc.last_line++; + yylloc.last_column = 0; + } + else + { + yylloc.last_column++; + } + } + } + + #define PRINT_TOKEN(S) + //#define PRINT_TOKEN(S) printf(S) +#define YY_NO_UNISTD_H 1 + + +#line 627 "cpp/parser/gsc.yy.cpp" + +#define INITIAL 0 +#define S_BLOCK_COMMENT 1 +#define S_BLOCK_DEVSCRIPT 2 + +/*windows compatibility case*/ +#include +#define isatty _isatty +#define fileno _fileno + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + yy_size_t yy_n_chars; + yy_size_t yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals (yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy (yyscan_t yyscanner ); + +int yyget_debug (yyscan_t yyscanner ); + +void yyset_debug (int debug_flag ,yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner ); + +void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); + +FILE *yyget_in (yyscan_t yyscanner ); + +void yyset_in (FILE * in_str ,yyscan_t yyscanner ); + +FILE *yyget_out (yyscan_t yyscanner ); + +void yyset_out (FILE * out_str ,yyscan_t yyscanner ); + +yy_size_t yyget_leng (yyscan_t yyscanner ); + +char *yyget_text (yyscan_t yyscanner ); + +int yyget_lineno (yyscan_t yyscanner ); + +void yyset_lineno (int line_number ,yyscan_t yyscanner ); + +int yyget_column (yyscan_t yyscanner ); + +void yyset_column (int column_no ,yyscan_t yyscanner ); + +YYSTYPE * yyget_lval (yyscan_t yyscanner ); + +void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc (yyscan_t yyscanner ); + + void yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (yyscan_t yyscanner ); +#else +extern int yywrap (yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (yyscan_t yyscanner ); +#else +static int input (yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + +#line 62 "gsc.l" + + +#line 872 "cpp/parser/gsc.yy.cpp" + + yylval = yylval_param; + + yylloc = yylloc_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } + + yy_load_buffer_state(yyscanner ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 178 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 377 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +#line 64 "gsc.l" +/* skip whitespace & newlines */ + YY_BREAK +case 2: +YY_RULE_SETUP +#line 65 "gsc.l" +/* skip single line comments */ + YY_BREAK +case 3: +YY_RULE_SETUP +#line 66 "gsc.l" +{ BEGIN(S_BLOCK_COMMENT); } + YY_BREAK +case 4: +/* rule 4 can match eol */ +YY_RULE_SETUP +#line 68 "gsc.l" +/* ignore unicode shenanigans in comment blocks */ + YY_BREAK +case 5: +YY_RULE_SETUP +#line 69 "gsc.l" +{ BEGIN(INITIAL); } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 71 "gsc.l" +{ BEGIN(S_BLOCK_DEVSCRIPT); } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 72 "gsc.l" +{ BEGIN(INITIAL); } + YY_BREAK +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +#line 74 "gsc.l" +{ PRINT_TOKEN("T_STRING, "); yylval->literal_string = strdup(yytext); return T_STRING; } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 75 "gsc.l" +{ PRINT_TOKEN("T_FLOAT, "); yylval->literal_string = strdup(yytext); return T_FLOAT; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 76 "gsc.l" +{ PRINT_TOKEN("T_INT, "); yylval->literal_string = strdup(yytext); return T_INT; } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 78 "gsc.l" +{ PRINT_TOKEN("INCLUDE, "); return INCLUDE; } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 79 "gsc.l" +{ PRINT_TOKEN("USING_ANIMTREE, "); return USING_ANIMTREE; } + YY_BREAK +case 13: +YY_RULE_SETUP +#line 80 "gsc.l" +{ PRINT_TOKEN("ANIMTREE, "); return ANIMTREE; } + YY_BREAK +case 14: +YY_RULE_SETUP +#line 82 "gsc.l" +{ PRINT_TOKEN("OP_LPAREN, "); return OP_LPAREN; } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 83 "gsc.l" +{ PRINT_TOKEN("OP_RPAREN, "); return OP_RPAREN; } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 84 "gsc.l" +{ PRINT_TOKEN("FUNC_POINTER_BEGIN, "); return FUNC_POINTER_BEGIN; } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 85 "gsc.l" +{ PRINT_TOKEN("OP_LBRACKET, "); return OP_LBRACKET; } /* +\]\s*\] { PRINT_TOKEN("FUNC_POINTER_END, "); return FUNC_POINTER_END; } //Using this would override ']' in nested array expressions */ + YY_BREAK +case 18: +YY_RULE_SETUP +#line 87 "gsc.l" +{ PRINT_TOKEN("OP_RBRACKET, "); return OP_RBRACKET; } + YY_BREAK +case 19: +YY_RULE_SETUP +#line 88 "gsc.l" +{ PRINT_TOKEN("OP_LBRACE, "); return OP_LBRACE; } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 89 "gsc.l" +{ PRINT_TOKEN("OP_RBRACE, "); return OP_RBRACE; } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 90 "gsc.l" +{ PRINT_TOKEN("OP_COMMA, "); return OP_COMMA; } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 91 "gsc.l" +{ PRINT_TOKEN("OP_DOT, "); return OP_DOT; } + YY_BREAK +case 23: +YY_RULE_SETUP +#line 92 "gsc.l" +{ PRINT_TOKEN("OP_CMP_NEQ, "); return OP_CMP_NEQ; } + YY_BREAK +case 24: +YY_RULE_SETUP +#line 93 "gsc.l" +{ PRINT_TOKEN("OP_NOT, "); return OP_NOT; } + YY_BREAK +case 25: +YY_RULE_SETUP +#line 94 "gsc.l" +{ PRINT_TOKEN("OP_MOD, "); return OP_MOD; } + YY_BREAK +case 26: +YY_RULE_SETUP +#line 95 "gsc.l" +{ PRINT_TOKEN("OP_CMP_EQ, "); return OP_CMP_EQ; } + YY_BREAK +case 27: +YY_RULE_SETUP +#line 96 "gsc.l" +{ PRINT_TOKEN("OP_ASSIGN, "); return OP_ASSIGN; } + YY_BREAK +case 28: +YY_RULE_SETUP +#line 97 "gsc.l" +{ PRINT_TOKEN("OP_LSHIFT, "); return OP_LSHIFT; } + YY_BREAK +case 29: +YY_RULE_SETUP +#line 98 "gsc.l" +{ PRINT_TOKEN("OP_CMP_LE, "); return OP_CMP_LE; } + YY_BREAK +case 30: +YY_RULE_SETUP +#line 99 "gsc.l" +{ PRINT_TOKEN("OP_CMP_LT, "); return OP_CMP_LT; } + YY_BREAK +case 31: +YY_RULE_SETUP +#line 100 "gsc.l" +{ PRINT_TOKEN("OP_RSHIFT, "); return OP_RSHIFT; } + YY_BREAK +case 32: +YY_RULE_SETUP +#line 101 "gsc.l" +{ PRINT_TOKEN("OP_CMP_GE, "); return OP_CMP_GE; } + YY_BREAK +case 33: +YY_RULE_SETUP +#line 102 "gsc.l" +{ PRINT_TOKEN("OP_CMP_GT, "); return OP_CMP_GT; } + YY_BREAK +case 34: +YY_RULE_SETUP +#line 103 "gsc.l" +{ PRINT_TOKEN("OP_INC, "); return OP_INC; } //neither post nor pre + YY_BREAK +case 35: +YY_RULE_SETUP +#line 104 "gsc.l" +{ PRINT_TOKEN("OP_ASSIGN_ADD, "); return OP_ASSIGN_ADD; } + YY_BREAK +case 36: +YY_RULE_SETUP +#line 105 "gsc.l" +{ PRINT_TOKEN("OP_ADD, "); return OP_ADD; } + YY_BREAK +case 37: +YY_RULE_SETUP +#line 106 "gsc.l" +{ PRINT_TOKEN("OP_DEC, "); return OP_DEC; } //neither post nor pre + YY_BREAK +case 38: +YY_RULE_SETUP +#line 107 "gsc.l" +{ PRINT_TOKEN("OP_ASSIGN_SUB, "); return OP_ASSIGN_SUB; } + YY_BREAK +case 39: +YY_RULE_SETUP +#line 108 "gsc.l" +{ PRINT_TOKEN("OP_SUB, "); return OP_SUB; } + YY_BREAK +case 40: +YY_RULE_SETUP +#line 109 "gsc.l" +{ PRINT_TOKEN("OP_ASSIGN_MULT, "); return OP_ASSIGN_MULT; } + YY_BREAK +case 41: +YY_RULE_SETUP +#line 110 "gsc.l" +{ PRINT_TOKEN("OP_MULT, "); return OP_MULT; } + YY_BREAK +case 42: +YY_RULE_SETUP +#line 111 "gsc.l" +{ PRINT_TOKEN("OP_ASSIGN_DIV, "); return OP_ASSIGN_DIV; } + YY_BREAK +case 43: +YY_RULE_SETUP +#line 112 "gsc.l" +{ PRINT_TOKEN("OP_DIV, "); return OP_DIV; } + YY_BREAK +case 44: +YY_RULE_SETUP +#line 113 "gsc.l" +{ PRINT_TOKEN("OP_CMP_OR, "); return OP_CMP_OR; } + YY_BREAK +case 45: +YY_RULE_SETUP +#line 114 "gsc.l" +{ PRINT_TOKEN("OP_ASSIGN_BW_OR, "); return OP_ASSIGN_BW_OR; } + YY_BREAK +case 46: +YY_RULE_SETUP +#line 115 "gsc.l" +{ PRINT_TOKEN("OP_BW_OR, "); return OP_BW_OR; } + YY_BREAK +case 47: +YY_RULE_SETUP +#line 116 "gsc.l" +{ PRINT_TOKEN("OP_CMP_AND, "); return OP_CMP_AND; } + YY_BREAK +case 48: +YY_RULE_SETUP +#line 117 "gsc.l" +{ PRINT_TOKEN("OP_ASSIGN_BW_AND, "); return OP_ASSIGN_BW_AND; } + YY_BREAK +case 49: +YY_RULE_SETUP +#line 118 "gsc.l" +{ PRINT_TOKEN("OP_BW_AND, "); return OP_BW_AND; } + YY_BREAK +case 50: +YY_RULE_SETUP +#line 119 "gsc.l" +{ PRINT_TOKEN("OP_ASSIGN_BW_XOR, "); return OP_ASSIGN_BW_XOR; } + YY_BREAK +case 51: +YY_RULE_SETUP +#line 120 "gsc.l" +{ PRINT_TOKEN("OP_BW_XOR, "); return OP_BW_XOR; } + YY_BREAK +case 52: +YY_RULE_SETUP +#line 121 "gsc.l" +{ PRINT_TOKEN("OP_BW_NOT, "); return OP_BW_NOT; } + YY_BREAK +case 53: +YY_RULE_SETUP +#line 122 "gsc.l" +{ PRINT_TOKEN("OP_COLON_DOUBLE, "); return OP_COLON_DOUBLE; } + YY_BREAK +case 54: +YY_RULE_SETUP +#line 123 "gsc.l" +{ PRINT_TOKEN("OP_COLON, "); return OP_COLON; } + YY_BREAK +case 55: +YY_RULE_SETUP +#line 124 "gsc.l" +{ PRINT_TOKEN("OP_COLON_SEMI, "); return OP_COLON_SEMI; } + YY_BREAK +case 56: +YY_RULE_SETUP +#line 125 "gsc.l" +{ PRINT_TOKEN("OP_QMARK, "); return OP_QMARK; } /* +// \@ { PRINT_TOKEN("OP_AT, "); return OP_AT; } //disabled*/ + YY_BREAK +case 57: +YY_RULE_SETUP +#line 127 "gsc.l" +{ PRINT_TOKEN("OP_HASH, "); return OP_HASH; } + YY_BREAK +case 58: +YY_RULE_SETUP +#line 129 "gsc.l" +{ PRINT_TOKEN("T_IF, "); return T_IF; } + YY_BREAK +case 59: +YY_RULE_SETUP +#line 130 "gsc.l" +{ PRINT_TOKEN("T_ELSE, "); return T_ELSE; } + YY_BREAK +case 60: +YY_RULE_SETUP +#line 131 "gsc.l" +{ PRINT_TOKEN("T_SWITCH, "); return T_SWITCH; } + YY_BREAK +case 61: +YY_RULE_SETUP +#line 132 "gsc.l" +{ PRINT_TOKEN("T_CASE, "); return T_CASE; } + YY_BREAK +case 62: +YY_RULE_SETUP +#line 133 "gsc.l" +{ PRINT_TOKEN("T_DEFAULT, "); return T_DEFAULT; } + YY_BREAK +case 63: +YY_RULE_SETUP +#line 134 "gsc.l" +{ PRINT_TOKEN("T_BREAK, "); return T_BREAK; } + YY_BREAK +case 64: +YY_RULE_SETUP +#line 135 "gsc.l" +{ PRINT_TOKEN("T_FOR, "); return T_FOR; } + YY_BREAK +case 65: +YY_RULE_SETUP +#line 136 "gsc.l" +{ PRINT_TOKEN("T_WHILE, "); return T_WHILE; } + YY_BREAK +case 66: +YY_RULE_SETUP +#line 137 "gsc.l" +{ PRINT_TOKEN("T_CONTINUE, "); return T_CONTINUE; } + YY_BREAK +case 67: +YY_RULE_SETUP +#line 138 "gsc.l" +{ PRINT_TOKEN("T_RETURN, "); return T_RETURN; } + YY_BREAK +case 68: +YY_RULE_SETUP +#line 140 "gsc.l" +{ PRINT_TOKEN("T_THREAD, "); return T_THREAD; } + YY_BREAK +case 69: +YY_RULE_SETUP +#line 141 "gsc.l" +{ PRINT_TOKEN("T_WAIT, "); return T_WAIT; } + YY_BREAK +case 70: +YY_RULE_SETUP +#line 143 "gsc.l" +{ PRINT_TOKEN("T_FILEPATH, "); yylval->literal_string = strdup(yytext); return T_FILEPATH; } + YY_BREAK +case 71: +YY_RULE_SETUP +#line 144 "gsc.l" +{ PRINT_TOKEN("T_IDENTIFIER, "); yylval->t_identifier = strdup(yytext); return T_IDENTIFIER; } + YY_BREAK +case 72: +/* rule 72 can match eol */ +YY_RULE_SETUP +#line 146 "gsc.l" +/* Ignore unexpected characters (this should never happen) + { fprintf(stderr, "RX_DEFAULT ACTION AT %d(%d) FOR '%s'\n", yyget_lloc(yyscanner)->first_line, yyget_lloc(yyscanner)->first_column, yytext);*/ + YY_BREAK +case 73: +YY_RULE_SETUP +#line 149 "gsc.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1331 "cpp/parser/gsc.yy.cpp" +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(S_BLOCK_COMMENT): +case YY_STATE_EOF(S_BLOCK_DEVSCRIPT): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap(yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = yyg->yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + yy_size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + yy_size_t new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart(yyin ,yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 178 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + register int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + register char *yy_cp = yyg->yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 178 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 177); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart(yyin ,yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap(yyscanner ) ) + return EOF; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } + + yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); + yy_load_buffer_state(yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state(yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ,yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer(b,file ,yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree((void *) b->yy_ch_buf ,yyscanner ); + + yyfree((void *) b ,yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer(b ,yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state(yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer(b ,yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (yyconst char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes(yystr,strlen(yystr) ,yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + yy_size_t i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) yyalloc(n ,yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer(buf,n ,yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +yy_size_t yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param line_number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = line_number; +} + +/** Set the current column. + * @param line_number + * @param yyscanner The scanner object. + */ +void yyset_column (int column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = in_str ; +} + +void yyset_out (FILE * out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ + +int yylex_init(yyscan_t* ptr_yy_globals) + +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ + +int yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) + +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = 0; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = (char *) 0; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = (FILE *) 0; + yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack ,yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree(yyg->yy_start_stack ,yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + return (void *) malloc( size ); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 148 "gsc.l" + + + diff --git a/components/gsc_parser/src/cpp/parser/gsc.yy.h b/components/gsc_parser/src/cpp/parser/gsc.yy.h new file mode 100644 index 00000000..b0c0a83c --- /dev/null +++ b/components/gsc_parser/src/cpp/parser/gsc.yy.h @@ -0,0 +1,349 @@ +#ifndef yyHEADER_H +#define yyHEADER_H 1 +#define yyIN_HEADER 1 + +#line 6 "cpp/parser/gsc.yy.h" + +#line 8 "cpp/parser/gsc.yy.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 37 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart (FILE *input_file ,yyscan_t yyscanner ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); +void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +void yypop_buffer_state (yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); + +void *yyalloc (yy_size_t ,yyscan_t yyscanner ); +void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); +void yyfree (void * ,yyscan_t yyscanner ); + +/* Begin user sect3 */ + +#define yywrap(yyscanner) 1 +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 +#define S_BLOCK_COMMENT 1 +#define S_BLOCK_DEVSCRIPT 2 + +#endif + +/*windows compatibility case*/ +#include +#define isatty _isatty +#define fileno _fileno + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy (yyscan_t yyscanner ); + +int yyget_debug (yyscan_t yyscanner ); + +void yyset_debug (int debug_flag ,yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner ); + +void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); + +FILE *yyget_in (yyscan_t yyscanner ); + +void yyset_in (FILE * in_str ,yyscan_t yyscanner ); + +FILE *yyget_out (yyscan_t yyscanner ); + +void yyset_out (FILE * out_str ,yyscan_t yyscanner ); + +yy_size_t yyget_leng (yyscan_t yyscanner ); + +char *yyget_text (yyscan_t yyscanner ); + +int yyget_lineno (yyscan_t yyscanner ); + +void yyset_lineno (int line_number ,yyscan_t yyscanner ); + +int yyget_column (yyscan_t yyscanner ); + +void yyset_column (int column_no ,yyscan_t yyscanner ); + +YYSTYPE * yyget_lval (yyscan_t yyscanner ); + +void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc (yyscan_t yyscanner ); + + void yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (yyscan_t yyscanner ); +#else +extern int yywrap (yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#line 148 "gsc.l" + + +#line 348 "cpp/parser/gsc.yy.h" +#undef yyIN_HEADER +#endif /* yyHEADER_H */ diff --git a/components/gsc_parser/src/cpp/symbols/animtree.cpp b/components/gsc_parser/src/cpp/symbols/animtree.cpp new file mode 100644 index 00000000..c573868c --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/animtree.cpp @@ -0,0 +1,33 @@ +#include "animtree.h" + +Animtree::Animtree(void) : string(NULL) +{ + this->type = S_TYPE_ANIMTREE; + //printf("%s\n", SYMBOL_TYPE_STRING(type)); +} + +Animtree::Animtree(Literal* animtree, YYLTYPE loc): string(animtree) +{ + this->type = S_TYPE_ANIMTREE; + this->location = loc; + this->AddChild(animtree); + //printf("%s animtree: '%s'\n", SYMBOL_TYPE_STRING(type), this->string->value); +} + +Animtree::~Animtree() +{ + //delete[] string; + //printf("~Animtree()\n"); +} + +void Animtree::PrintInfo() const +{ + printf("%s with %d children at %d(%d) - %d(%d), str '%s'\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character, + this->string->value); +} diff --git a/components/gsc_parser/src/cpp/symbols/animtree.h b/components/gsc_parser/src/cpp/symbols/animtree.h new file mode 100644 index 00000000..a5e367f1 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/animtree.h @@ -0,0 +1,15 @@ +#pragma once +#include "symbol.h" + +class Animtree : public Symbol +{ + public: + Literal* string; + + Animtree(void); + Animtree(Literal* animtree, YYLTYPE loc); + + ~Animtree(void); + + void PrintInfo() const; +}; diff --git a/components/gsc_parser/src/cpp/symbols/conditional.cpp b/components/gsc_parser/src/cpp/symbols/conditional.cpp new file mode 100644 index 00000000..593894c0 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/conditional.cpp @@ -0,0 +1,42 @@ +#include "expression.h" +#include "symbol.h" + +Conditional::Conditional(void) : statement(NULL) +{ + this->type = S_TYPE_NONE; +} + +Conditional::Conditional(Expression* expr, Symbol* stmt, YYLTYPE loc, SYMBOL_TYPE type) : expression(expr), statement(stmt) +{ + this->type = type; + this->location = loc; + + if(expr) { this->AddChild(expr); } + if(stmt) { this->AddChild(stmt); } +} + +Conditional::Conditional(Expression* expr0, Expression* expr1, Expression* expr2, Symbol* stmt, YYLTYPE loc, SYMBOL_TYPE type) : expression(expr0), statement(stmt) +{ + this->type = type; + this->location = loc; + + if(expr0) { this->AddChild(expr0); } + if(expr1) { this->AddChild(expr1); } + if(expr2) { this->AddChild(expr2); } + if(stmt) { this->AddChild(stmt); } +} + +Conditional::~Conditional() +{ +} + +void Conditional::PrintInfo() const +{ + printf("%s with %d children at %d(%d) - %d(%d)\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character); +} \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/conditional.h b/components/gsc_parser/src/cpp/symbols/conditional.h new file mode 100644 index 00000000..ba2fa8bc --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/conditional.h @@ -0,0 +1,19 @@ +#pragma once + +#include "symbol.h" +//#include "operator_enum.cpp" + +class Conditional : public Symbol +{ +public: + Expression* expression; + Symbol* statement; + + Conditional(void); + Conditional(Expression* expr, Symbol* stmt, YYLTYPE loc, SYMBOL_TYPE type); + Conditional(Expression* expr0, Expression* expr1, Expression* expr2, Symbol* stmt, YYLTYPE loc, SYMBOL_TYPE type); + + ~Conditional(void); + + void PrintInfo(void) const; +}; diff --git a/components/gsc_parser/src/cpp/symbols/expression.cpp b/components/gsc_parser/src/cpp/symbols/expression.cpp new file mode 100644 index 00000000..1a538d47 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/expression.cpp @@ -0,0 +1,65 @@ +#include "expression.h" +#include "symbol.h" + +Expression::Expression(void) : op_type(OP_TYPE_NONE) +{ + this->type = S_TYPE_EXPRESSION; +} + +Expression::Expression(YYLTYPE loc) : op_type(OP_TYPE_NONE) +{ + this->type = S_TYPE_EXPRESSION; + this->location = loc; +} + +Expression::Expression(OPERATOR_TYPE prefix, Expression* expr, YYLTYPE range) +{ + this->type = S_TYPE_EXPRESSION; + + this->op_type = prefix; + + this->location = range; + this->AddChild(expr); +} + +Expression::Expression(Expression* left, OPERATOR_TYPE mid_op, Expression* right, YYLTYPE range) +{ + this->type = S_TYPE_EXPRESSION; + + this->op_type = mid_op; + + this->location = range; + this->AddChild(left); + this->AddChild(right); +} + +Expression::Expression(Expression* expr, OPERATOR_TYPE postfix, YYLTYPE range) +{ + this->type = S_TYPE_EXPRESSION; + + this->op_type = postfix; + + this->location = range; + this->AddChild(expr); +} + +Expression::~Expression() +{ +} + +OPERATOR_TYPE Expression::Operator(void) const +{ + return this->op_type; +} + +void Expression::PrintInfo() const +{ + printf("%s with %d children at %d(%d) - %d(%d), op '%s'\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character, + OPERATOR_TYPE_STRING(this->op_type)); +} \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/expression.h b/components/gsc_parser/src/cpp/symbols/expression.h new file mode 100644 index 00000000..b205d872 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/expression.h @@ -0,0 +1,23 @@ +#pragma once + +#include "symbol.h" +//#include "operator_enum.cpp" + +class Expression : public Symbol +{ +private: + OPERATOR_TYPE op_type; + +public: + Expression(void); + Expression(YYLTYPE loc); + Expression(OPERATOR_TYPE prefix, Expression* expr, YYLTYPE range); + Expression(Expression* left, OPERATOR_TYPE mid_op, Expression* right, YYLTYPE range); + Expression(Expression* expr, OPERATOR_TYPE postfix, YYLTYPE range); + + ~Expression(void); + + OPERATOR_TYPE Operator(void) const; + + void PrintInfo(void) const; +}; diff --git a/components/gsc_parser/src/cpp/symbols/function.cpp b/components/gsc_parser/src/cpp/symbols/function.cpp new file mode 100644 index 00000000..8fe108a4 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/function.cpp @@ -0,0 +1,112 @@ +#include "function.h" + +Function::Function(void) : identifier(NULL) +{ + this->type = S_TYPE_FUNCTION_DECL; +} + +Function::Function(Identifier* identifier, YYLTYPE loc) +{ + this->type = S_TYPE_FUNCTION_DECL; + + this->identifier = identifier; + this->location = loc; + + this->AddChild(identifier); +} + +Function::~Function() +{ + //delete this->identifier; +} + +void Function::PrintArgs() const +{ + Symbol* args = this->children->NextElem(); + + for(Symbol* arg = args->Children(); arg; arg = arg->NextElem()) + { + arg->PrintSymbol(); + } +} + +void Function::PrintInfo() const +{ + printf("%s with %d children at %d(%d) - %d(%d), name '%s'\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character, + this->identifier->value); +} + +// +// Used to provide symbol data to CoD-Sense +// +void Function::PrintSymbol() const +{ + // + // type|name|location[|details] + // By default do not provide type specific info + // + printf("%s|%s|%d %d %d %d|%s\n", + SYMBOL_TYPE_STRING(type), + this->identifier->value, + location.start.line, + location.start.character, + location.end.line, + location.end.character, + "..."); +} + + +Call::Call(void) : flags(CALL_FLAG_NULL), identifier(NULL), caller(NULL) +{ + this->type = S_TYPE_FUNCTION_CALL; +} + +Call::Call(YYLTYPE loc, int flags) : flags(flags), identifier(NULL), caller(NULL) +{ + this->type = S_TYPE_FUNCTION_CALL; + this->location = loc; +} + +Call::~Call() +{ + //delete this->identifier; +} + +void Call::SetCaller(Expression* caller) +{ + if(this->flags & CALL_FLAGS_EXPLICIT_CALLER) + return; + + this->caller = caller; + if(caller) + { + // Swap the head of the children list with the caller, manually append the old list after that + for(Symbol* child = this->children; child; ) + { + Symbol* next = child->NextElem(); + caller->AddToEnd(child); + child = next; + } + + this->children = caller; + } + this->flags |= CALL_FLAGS_EXPLICIT_CALLER; +} + +void Call::PrintInfo() const +{ + printf("%s with %d children at %d(%d) - %d(%d), flags 0x%X\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character, + this->flags); +} \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/function.h b/components/gsc_parser/src/cpp/symbols/function.h new file mode 100644 index 00000000..fc9750f0 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/function.h @@ -0,0 +1,46 @@ +#pragma once +#include "symbol.h" + +class Function : public Symbol +{ + public: + Identifier* identifier; + + Function(void); + Function(Identifier* identifier, YYLTYPE loc); + + ~Function(void); + + void PrintArgs() const; + void PrintInfo() const; + void PrintSymbol() const; +}; + +enum ENUM_CALL_FLAGS +{ + CALL_FLAG_NULL = 0, + CALL_FLAG_THREAD = (1 << 0), + CALL_FLAG_IDENTIFIER = (1 << 1), + CALL_FLAG_POINTER = (1 << 2), + CALL_FLAG_REFERENCE = (1 << 3), + CALL_FLAGS_EXPLICIT_CALLER = (1 << 4) +}; + +class Call : public Symbol +{ + private: + int flags; + + public: + Identifier* identifier; + Expression* caller; + + Call(void); + Call(YYLTYPE loc, int flags); + + ~Call(void); + + void SetCaller(Expression* caller); + + void PrintInfo() const; +}; \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/group.cpp b/components/gsc_parser/src/cpp/symbols/group.cpp new file mode 100644 index 00000000..f9212176 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/group.cpp @@ -0,0 +1,12 @@ +#include "group.h" + +Group::Group(Symbol* childList, YYLTYPE range) +{ + this->type = S_TYPE_GROUP; + this->children = childList; + this->location = range; +} + +Group::~Group(void) +{ +} diff --git a/components/gsc_parser/src/cpp/symbols/group.h b/components/gsc_parser/src/cpp/symbols/group.h new file mode 100644 index 00000000..b91cd0de --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/group.h @@ -0,0 +1,13 @@ +#pragma once +#include "symbol.h" + +// +// A Group consists of multiple child symbols +// it is generally used to define scopes, etc. +// +class Group : public Symbol +{ +public: + Group(Symbol* childList, YYLTYPE range); + ~Group(void); +}; diff --git a/components/gsc_parser/src/cpp/symbols/identifier.cpp b/components/gsc_parser/src/cpp/symbols/identifier.cpp new file mode 100644 index 00000000..7910712c --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/identifier.cpp @@ -0,0 +1,52 @@ +#include "identifier.h" + +Identifier::Identifier(void) : value(NULL) +{ + this->type = S_TYPE_IDENTIFIER; +} + +Identifier::Identifier(char* str) : value(str) +{ + this->type = S_TYPE_IDENTIFIER; +} + +Identifier::Identifier(char* str, YYLTYPE loc) : value(str) +{ + this->type = S_TYPE_IDENTIFIER; + this->location = loc; +} + +Identifier::~Identifier() +{ + free((void*)this->value); +} + +void Identifier::PrintInfo() const +{ + printf("%s with %d children at %d(%d) - %d(%d), name '%s'\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character, + this->value); +} + +void Identifier::PrintSymbol() const +{ + // + // type|name|location[|details] + // By default do not provide type specific info + // + printf("%s|%s|%d %d %d %d|%s at line %d, char %d\n", + SYMBOL_TYPE_STRING(type), + this->value, + location.start.line, + location.start.character, + location.end.line, + location.end.character, + "var", + location.start.line, + location.start.character); +} \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/identifier.h b/components/gsc_parser/src/cpp/symbols/identifier.h new file mode 100644 index 00000000..89327ad8 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/identifier.h @@ -0,0 +1,17 @@ +#pragma once +#include "symbol.h" + +class Identifier : public Symbol +{ + public: + char* value; + + Identifier(void); + Identifier(char* str); + Identifier(char* str, YYLTYPE loc); + + virtual ~Identifier(void); + + void PrintInfo() const; + void PrintSymbol() const; +}; \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/include.cpp b/components/gsc_parser/src/cpp/symbols/include.cpp new file mode 100644 index 00000000..c98981b9 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/include.cpp @@ -0,0 +1,32 @@ +#include "include.h" + + +Include::Include(void) : file(NULL) +{ + this->type = S_TYPE_INCLUDE; + //printf("%s\n", SYMBOL_TYPE_STRING(type)); +} + +Include::Include(Literal* filepath, YYLTYPE loc): file(filepath) +{ + this->type = S_TYPE_INCLUDE; + this->location = loc; + this->children = filepath; + //printf("%s file: '%s'\n", SYMBOL_TYPE_STRING(type), this->file->value); +} + +Include::~Include() +{ +} + +void Include::PrintInfo() const +{ + printf("%s with %d children at %d(%d) - %d(%d), file '%s'\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character, + this->file->value); +} diff --git a/components/gsc_parser/src/cpp/symbols/include.h b/components/gsc_parser/src/cpp/symbols/include.h new file mode 100644 index 00000000..a920ff11 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/include.h @@ -0,0 +1,15 @@ +#pragma once +#include "symbol.h" + +class Include : public Symbol +{ + public: + Literal* file; + + Include(void); + Include(Literal* filepath, YYLTYPE loc); + + ~Include(void); + + void PrintInfo() const; +}; diff --git a/components/gsc_parser/src/cpp/symbols/literal.cpp b/components/gsc_parser/src/cpp/symbols/literal.cpp new file mode 100644 index 00000000..8bb0c6bd --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/literal.cpp @@ -0,0 +1,31 @@ +#include "literal.h" + +Literal::Literal(void) : value(NULL) +{ + this->type = S_TYPE_LITERAL_UNDEFINED; +} + +Literal::Literal(char* val, YYLTYPE loc, SYMBOL_TYPE type_override) +{ + this->type = type_override; + this->value = val; + this->location = loc; +} + +Literal::~Literal() +{ + free((void*)value); + value = NULL; +} + +void Literal::PrintInfo() const +{ + printf("%s with %d children at %d(%d) - %d(%d), name '%s'\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character, + this->value); +} \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/literal.h b/components/gsc_parser/src/cpp/symbols/literal.h new file mode 100644 index 00000000..b376e35d --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/literal.h @@ -0,0 +1,15 @@ +#pragma once +#include "symbol.h" + +class Literal : public Symbol +{ + public: + const char* value; + + Literal(void); + Literal(char* val, YYLTYPE loc, SYMBOL_TYPE type_override); + + virtual ~Literal(void); + + void PrintInfo() const; +}; \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/member.cpp b/components/gsc_parser/src/cpp/symbols/member.cpp new file mode 100644 index 00000000..408f7a70 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/member.cpp @@ -0,0 +1,37 @@ +#include "member.h" + +Member::Member(void) : object(NULL), expression(NULL) +{ + this->type = S_TYPE_MEMBER_NULL; +} + +Member::Member(Expression* obj, Expression* expr, YYLTYPE loc, SYMBOL_TYPE type_override) : object(obj), expression(expr) +{ + this->type = type_override; + this->location = loc; + + if(!obj || !expr) + { + delete obj; + delete expr; + return; + } + + this->AddChild(obj); + this->AddChild(expr); +} + +Member::~Member() +{ +} + +void Member::PrintInfo() const +{ + printf("%s with %d children at %d(%d) - %d(%d)\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character); +} \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/member.h b/components/gsc_parser/src/cpp/symbols/member.h new file mode 100644 index 00000000..944e980d --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/member.h @@ -0,0 +1,16 @@ +#pragma once +#include "symbol.h" + +class Member : public Symbol +{ + public: + Expression* object; + Expression* expression; + + Member(void); + Member(Expression* obj, Expression* expr, YYLTYPE loc, SYMBOL_TYPE type_override); + + virtual ~Member(void); + + void PrintInfo() const; +}; \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/operator_enum.cpp b/components/gsc_parser/src/cpp/symbols/operator_enum.cpp new file mode 100644 index 00000000..82c8191d --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/operator_enum.cpp @@ -0,0 +1,7 @@ +#define OPERATOR_TYPE_ENUM_STRING 1 +#include "operator_enum.h" + +const char* OPERATOR_TYPE_STRING(OPERATOR_TYPE type) +{ + return OPERATOR_TYPE_STRINGS[type]; +} diff --git a/components/gsc_parser/src/cpp/symbols/operator_enum.h b/components/gsc_parser/src/cpp/symbols/operator_enum.h new file mode 100644 index 00000000..c677c248 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/operator_enum.h @@ -0,0 +1,81 @@ +#ifndef __OPERATOR_ENUM_H__ + +#if OPERATOR_TYPE_ENUM_STRING + #undef OPERATOR_TYPE_ENUM_STRING + #include "operator_enum.h" + #define OPERATOR_TYPE_ENUM_STRING 1 + + #define REGISTER_OPERATOR_TYPE(X) #X +#else + #define REGISTER_OPERATOR_TYPE(X) X +#endif + +#if OPERATOR_TYPE_ENUM_STRING + static const char* OPERATOR_TYPE_STRINGS[] = { +#else + enum OPERATOR_TYPE { +#endif + + REGISTER_OPERATOR_TYPE( OP_TYPE_NONE ), + + REGISTER_OPERATOR_TYPE( OP_TYPE_INC ), + REGISTER_OPERATOR_TYPE( OP_TYPE_DEC ), + + REGISTER_OPERATOR_TYPE( OP_TYPE_INC_POST ), + REGISTER_OPERATOR_TYPE( OP_TYPE_DEC_POST ), + REGISTER_OPERATOR_TYPE( OP_TYPE_LB ), + REGISTER_OPERATOR_TYPE( OP_TYPE_RB ), + REGISTER_OPERATOR_TYPE( OP_TYPE_DOT ), + + REGISTER_OPERATOR_TYPE( OP_TYPE_INC_PRE ), + REGISTER_OPERATOR_TYPE( OP_TYPE_DEC_PRE ), + REGISTER_OPERATOR_TYPE( OP_TYPE_UPLUS ), + REGISTER_OPERATOR_TYPE( OP_TYPE_UMINUS ), + REGISTER_OPERATOR_TYPE( OP_TYPE_NOT ), + REGISTER_OPERATOR_TYPE( OP_TYPE_BW_NOT ), + + REGISTER_OPERATOR_TYPE( OP_TYPE_MULT ), + REGISTER_OPERATOR_TYPE( OP_TYPE_DIV ), + REGISTER_OPERATOR_TYPE( OP_TYPE_MOD ), + + REGISTER_OPERATOR_TYPE( OP_TYPE_ADD ), + REGISTER_OPERATOR_TYPE( OP_TYPE_SUB ), + + REGISTER_OPERATOR_TYPE( OP_TYPE_LSHIFT ), + REGISTER_OPERATOR_TYPE( OP_TYPE_RSHIFT ), + + REGISTER_OPERATOR_TYPE( OP_TYPE_CMP_LT ), + REGISTER_OPERATOR_TYPE( OP_TYPE_CMP_LE ), + REGISTER_OPERATOR_TYPE( OP_TYPE_CMP_GT ), + REGISTER_OPERATOR_TYPE( OP_TYPE_CMP_GE ), + + REGISTER_OPERATOR_TYPE( OP_TYPE_CMP_EQ ), + REGISTER_OPERATOR_TYPE( OP_TYPE_CMP_NEQ ), + + REGISTER_OPERATOR_TYPE( OP_TYPE_BW_AND ), + + REGISTER_OPERATOR_TYPE( OP_TYPE_BW_XOR ), + + REGISTER_OPERATOR_TYPE( OP_TYPE_BW_OR ), + + REGISTER_OPERATOR_TYPE( OP_TYPE_CMP_AND ), + + REGISTER_OPERATOR_TYPE( OP_TYPE_CMP_OR ), + //ternary + + REGISTER_OPERATOR_TYPE( OP_TYPE_ASSIGN ), + REGISTER_OPERATOR_TYPE( OP_TYPE_ASSIGN_ADD ), + REGISTER_OPERATOR_TYPE( OP_TYPE_ASSIGN_SUB ), + REGISTER_OPERATOR_TYPE( OP_TYPE_ASSIGN_MULT ), + REGISTER_OPERATOR_TYPE( OP_TYPE_ASSIGN_DIV ), + REGISTER_OPERATOR_TYPE( OP_TYPE_ASSIGN_BW_AND ), + REGISTER_OPERATOR_TYPE( OP_TYPE_ASSIGN_BW_XOR ), + REGISTER_OPERATOR_TYPE( OP_TYPE_ASSIGN_BW_OR ) + }; + + #undef REGISTER_OPERATOR_TYPE + +const char* OPERATOR_TYPE_STRING(OPERATOR_TYPE type); + +#define __OPERATOR_ENUM_H__ +#endif \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/pointer.cpp b/components/gsc_parser/src/cpp/symbols/pointer.cpp new file mode 100644 index 00000000..4652287c --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/pointer.cpp @@ -0,0 +1,29 @@ +#include "pointer.h" + +Pointer::Pointer(void) : expression(NULL) +{ + this->type = S_TYPE_POINTER; +} + +Pointer::Pointer(Expression* expr, YYLTYPE loc) : expression(expr) +{ + this->type = S_TYPE_POINTER; + + this->location = loc; + this->AddChild(expr); +} + +Pointer::~Pointer() +{ +} + +void Pointer::PrintInfo() const +{ + printf("%s with %d children at %d(%d) - %d(%d)\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character); +} \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/pointer.h b/components/gsc_parser/src/cpp/symbols/pointer.h new file mode 100644 index 00000000..f44a6ab4 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/pointer.h @@ -0,0 +1,15 @@ +#pragma once +#include "symbol.h" + +class Pointer : public Symbol +{ + public: + Expression* expression; + + Pointer(void); + Pointer(Expression* expression, YYLTYPE loc); + + ~Pointer(void); + + void PrintInfo() const; +}; \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/reference.cpp b/components/gsc_parser/src/cpp/symbols/reference.cpp new file mode 100644 index 00000000..ddaab3c8 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/reference.cpp @@ -0,0 +1,35 @@ +#include "reference.h" + +Reference::Reference(void) : file(NULL), identifier(NULL) +{ + this->type = S_TYPE_REFERENCE; +} + +Reference::Reference(Literal* filepath, Identifier* identifier, YYLTYPE loc) +{ + this->type = S_TYPE_REFERENCE; + + this->file = filepath; + this->identifier = identifier; + this->location = loc; + + if(filepath) { this->AddChild(filepath); } + this->AddChild(identifier); +} + +Reference::~Reference() +{ +} + +void Reference::PrintInfo() const +{ + printf("%s with %d children at %d(%d) - %d(%d), file '%s', func '%s'\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character, + (this->file) ? this->file->value : "$this", + this->identifier->value); +} \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/reference.h b/components/gsc_parser/src/cpp/symbols/reference.h new file mode 100644 index 00000000..de4a2629 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/reference.h @@ -0,0 +1,16 @@ +#pragma once +#include "symbol.h" + +class Reference : public Symbol +{ + public: + Literal* file; + Identifier* identifier; + + Reference(void); + Reference(Literal* filepath, Identifier* identifier, YYLTYPE loc); + + ~Reference(void); + + void PrintInfo() const; +}; \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/return.cpp b/components/gsc_parser/src/cpp/symbols/return.cpp new file mode 100644 index 00000000..28222e9c --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/return.cpp @@ -0,0 +1,28 @@ +#include "return.h" + +Return::Return(void) : expr(NULL) +{ + this->type = S_TYPE_STATEMENT_RETURN; +} + +Return::Return(Expression* expr, YYLTYPE loc) : expr(expr) +{ + this->type = S_TYPE_STATEMENT_RETURN; + this->location = loc; + this->children = (expr) ? expr : NULL; +} + +Return::~Return() +{ +} + +void Return::PrintInfo() const +{ + printf("%s with %d children at %d(%d) - %d(%d)\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character); +} \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/return.h b/components/gsc_parser/src/cpp/symbols/return.h new file mode 100644 index 00000000..59b6b228 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/return.h @@ -0,0 +1,15 @@ +#pragma once +#include "symbol.h" + +class Return : public Symbol +{ + public: + Expression* expr; + + Return(void); + Return(Expression* expr, YYLTYPE loc); + + virtual ~Return(void); + + void PrintInfo() const; +}; \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/symbol.cpp b/components/gsc_parser/src/cpp/symbols/symbol.cpp new file mode 100644 index 00000000..4e8b3bc3 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/symbol.cpp @@ -0,0 +1,125 @@ +#include "symbol.h" + +Symbol::Symbol(void) : type(S_TYPE_NONE), prev(NULL), next(NULL), children(NULL) +{ + this->SetOwner(this); + //printf("SYMBOL CTOR %s\n", SYMBOL_TYPE_STRING(type)); +} + +Symbol::Symbol(YYLTYPE loc) : type(S_TYPE_NONE), prev(NULL), next(NULL), children(NULL), location(loc) +{ + this->SetOwner(this); + //printf("CTOR %s\n", SYMBOL_TYPE_STRING(type)); +} + +Symbol::~Symbol() +{ + /*printf("~%s with %d children at %d(%d) - %d(%d)\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character);*/ + + delete this->children; + this->children = NULL; + delete this->NextElem(); + this->next = NULL; +} + +SYMBOL_TYPE Symbol::Type(void) const +{ + return this->type; +} + +Range Symbol::Location(void) const +{ + return this->location; +} + +void Symbol::AddChild(Symbol* child) +{ + if(!this->children) + { + children = child; + child->parent = this; + } + else + { + children->AddToEnd(child); + child->parent = this; + } +} + +void Symbol::FreeChildren(void) +{ + for(Symbol* s = this->children; s; s = s->NextElem()) + { + s->FreeChildren(); + } + + delete this->children; + this->children = NULL; +} + +Symbol* Symbol::Children(void) const +{ + return this->children; +} + +void Symbol::PrintInfo() const +{ + printf("%s with %d children at %d(%d) - %d(%d)\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character); +} + +// +// Used to provide symbol data to CoD-Sense +// +void Symbol::PrintSymbol() const +{ + // + // type|location[|name|details] + // By default do not provide type specific info + // + //printf("%s|%d %d %d %d\n", + // SYMBOL_TYPE_STRING(type), + // location.start.line, + // location.start.character, + // location.end.line, + // location.end.character); +} + +void Symbol::PrintInfoRecursive(int indentLevel) const +{ + this->PrintInfo(); + + for(Symbol* c = this->children; c; c = c->NextElem()) + { +#ifndef WIN32 + for(int i = 0; i < indentLevel; i++) + { + printf("│   "); + } + printf("%s", c->NextElem() ? "├── " : "└── "); +#else + for (int i = 0; i < indentLevel; i++) + { + printf("| "); + } + printf("%s", c->NextElem() ? "|-- " : "|__ "); +#endif + c->PrintInfoRecursive(indentLevel + 1); + } +} + +void Symbol::_debug_override_type(SYMBOL_TYPE type) +{ + this->type = type; +} diff --git a/components/gsc_parser/src/cpp/symbols/symbol.h b/components/gsc_parser/src/cpp/symbols/symbol.h new file mode 100644 index 00000000..34973add --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/symbol.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include + +#include + +#include "../parser/gsc.tab.hpp" + +#include "operator_enum.h" +#include "symbol_enum.h" +#include "../util/location.h" +#include "../util/llist.h" + +// +// A symbol consists of symbol meta-data +// and also represent's a linked-list of symbols +// for each child, a new list is defined +// +class Symbol : public LList +{ + protected: + SYMBOL_TYPE type; + + Symbol* parent; + + Symbol* prev; + Symbol* next; + + // + // A list of children for this symbol + // + Symbol* children; + + Range location; + + public: + Symbol(void); + Symbol(YYLTYPE loc); + + virtual ~Symbol(); + + SYMBOL_TYPE Type(void) const; + Range Location(void) const; + + void AddChild(Symbol* child); + void FreeChildren(void); + + Symbol* Children(void) const; + + virtual void PrintInfo() const; + void PrintInfoRecursive(int indentLevel = 0) const; + virtual void PrintSymbol() const; + + void _debug_override_type(SYMBOL_TYPE type); +}; + +// +// Include any child class types AFTER Symbol is defined +// +#include "literal.h" + +#include "include.h" +#include "animtree.h" +#include "function.h" + +#include "conditional.h" + +#include "expression.h" +#include "member.h" +#include "identifier.h" +#include "reference.h" +#include "pointer.h" +#include "group.h" + +#include "return.h" +#include "wait.h" diff --git a/components/gsc_parser/src/cpp/symbols/symbol_enum.cpp b/components/gsc_parser/src/cpp/symbols/symbol_enum.cpp new file mode 100644 index 00000000..07e5aa6a --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/symbol_enum.cpp @@ -0,0 +1,7 @@ +#define SYMBOL_TYPE_ENUM_STRING 1 +#include "symbol_enum.h" + +const char* SYMBOL_TYPE_STRING(SYMBOL_TYPE type) +{ + return SYMBOL_TYPE_STRINGS[type]; +} diff --git a/components/gsc_parser/src/cpp/symbols/symbol_enum.h b/components/gsc_parser/src/cpp/symbols/symbol_enum.h new file mode 100644 index 00000000..418c02c6 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/symbol_enum.h @@ -0,0 +1,71 @@ +#ifndef __SYMBOL_ENUM_H__ + +#if SYMBOL_TYPE_ENUM_STRING + #undef SYMBOL_TYPE_ENUM_STRING + #include "symbol_enum.h" + #define SYMBOL_TYPE_ENUM_STRING 1 + + #define REGISTER_SYMBOL_TYPE(X) #X +#else + #define REGISTER_SYMBOL_TYPE(X) X +#endif + +#if SYMBOL_TYPE_ENUM_STRING + static const char* SYMBOL_TYPE_STRINGS[] = { +#else + enum SYMBOL_TYPE { +#endif + REGISTER_SYMBOL_TYPE( S_TYPE_NONE ), + + REGISTER_SYMBOL_TYPE( S_TYPE_GROUP ), + + REGISTER_SYMBOL_TYPE( S_TYPE_LITERAL_UNDEFINED ), + REGISTER_SYMBOL_TYPE( S_TYPE_LITERAL_INT ), + REGISTER_SYMBOL_TYPE( S_TYPE_LITERAL_FLOAT ), + REGISTER_SYMBOL_TYPE( S_TYPE_LITERAL_STRING ), + REGISTER_SYMBOL_TYPE( S_TYPE_LITERAL_FILEPATH ), + + REGISTER_SYMBOL_TYPE( S_TYPE_IDENTIFIER ), + + REGISTER_SYMBOL_TYPE( S_TYPE_INCLUDE ), + REGISTER_SYMBOL_TYPE( S_TYPE_ANIMTREE ), + + REGISTER_SYMBOL_TYPE( S_TYPE_FUNCTION_DECL ), + REGISTER_SYMBOL_TYPE( S_TYPE_FUNCTION_CALL ), + + REGISTER_SYMBOL_TYPE( S_TYPE_STATEMENT ), + REGISTER_SYMBOL_TYPE( S_TYPE_STATEMENT_BLOCK ), + REGISTER_SYMBOL_TYPE( S_TYPE_STATEMENT_WAIT ), + REGISTER_SYMBOL_TYPE( S_TYPE_STATEMENT_EXPRESSION ), + REGISTER_SYMBOL_TYPE( S_TYPE_STATEMENT_IF ), + REGISTER_SYMBOL_TYPE( S_TYPE_STATEMENT_SWITCH ), + REGISTER_SYMBOL_TYPE( S_TYPE_STATEMENT_CASE ), + REGISTER_SYMBOL_TYPE( S_TYPE_STATEMENT_LOOP ), + REGISTER_SYMBOL_TYPE( S_TYPE_STATEMENT_RETURN ), + REGISTER_SYMBOL_TYPE( S_TYPE_STATEMENT_BREAK ), + REGISTER_SYMBOL_TYPE( S_TYPE_STATEMENT_CONTINUE ), + REGISTER_SYMBOL_TYPE( S_TYPE_STATEMENT_EMPTY ), + + REGISTER_SYMBOL_TYPE( S_TYPE_CONDITIONAL_IF ), + REGISTER_SYMBOL_TYPE( S_TYPE_CONDITIONAL_ELSE ), + REGISTER_SYMBOL_TYPE( S_TYPE_CONDITIONAL_WHILE ), + REGISTER_SYMBOL_TYPE( S_TYPE_CONDITIONAL_FOR ), + REGISTER_SYMBOL_TYPE( S_TYPE_CONDITIONAL_SWITCH ), + REGISTER_SYMBOL_TYPE( S_TYPE_CONDITIONAL_CASE ), + + REGISTER_SYMBOL_TYPE( S_TYPE_MEMBER_NULL ), + REGISTER_SYMBOL_TYPE( S_TYPE_MEMBER_OBJECT_PROPERTY ), + REGISTER_SYMBOL_TYPE( S_TYPE_MEMBER_ARRAY_ELEMENT ), + REGISTER_SYMBOL_TYPE( S_TYPE_MEMBER_ARRAY_EMPTY ), + + REGISTER_SYMBOL_TYPE( S_TYPE_EXPRESSION ), + REGISTER_SYMBOL_TYPE( S_TYPE_REFERENCE ), + REGISTER_SYMBOL_TYPE( S_TYPE_POINTER ) + }; + + #undef REGISTER_SYMBOL_TYPE + +const char* SYMBOL_TYPE_STRING(SYMBOL_TYPE type); + +#define __SYMBOL_ENUM_H__ +#endif \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/wait.cpp b/components/gsc_parser/src/cpp/symbols/wait.cpp new file mode 100644 index 00000000..93959888 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/wait.cpp @@ -0,0 +1,28 @@ +#include "wait.h" + +Wait::Wait(void) : expr(NULL) +{ + this->type = S_TYPE_STATEMENT_WAIT; +} + +Wait::Wait(Expression* expr, YYLTYPE loc) : expr(expr) +{ + this->type = S_TYPE_STATEMENT_WAIT; + this->location = loc; + this->children = (expr) ? expr : NULL; +} + +Wait::~Wait() +{ +} + +void Wait::PrintInfo() const +{ + printf("%s with %d children at %d(%d) - %d(%d)\n", + SYMBOL_TYPE_STRING(type), + this->children ? this->children->Size() + 1 : 0, + location.start.line, + location.start.character, + location.end.line, + location.end.character); +} \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/symbols/wait.h b/components/gsc_parser/src/cpp/symbols/wait.h new file mode 100644 index 00000000..ab7bcab4 --- /dev/null +++ b/components/gsc_parser/src/cpp/symbols/wait.h @@ -0,0 +1,15 @@ +#pragma once +#include "symbol.h" + +class Wait : public Symbol +{ + public: + Expression* expr; + + Wait(void); + Wait(Expression* expr, YYLTYPE loc); + + virtual ~Wait(void); + + void PrintInfo() const; +}; \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/sys/sys_cpu.cpp b/components/gsc_parser/src/cpp/sys/sys_cpu.cpp new file mode 100644 index 00000000..2eb8d4fd --- /dev/null +++ b/components/gsc_parser/src/cpp/sys/sys_cpu.cpp @@ -0,0 +1,13 @@ +#include "sys_cpu.h" + +int Sys_CPUCount(void) +{ +#if WIN32 + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + + return sysInfo.dwNumberOfProcessors; +#else + return sysconf(_SC_NPROCESSORS_ONLN); +#endif +} diff --git a/components/gsc_parser/src/cpp/sys/sys_cpu.h b/components/gsc_parser/src/cpp/sys/sys_cpu.h new file mode 100644 index 00000000..4adeae65 --- /dev/null +++ b/components/gsc_parser/src/cpp/sys/sys_cpu.h @@ -0,0 +1,9 @@ +#pragma once + +#if WIN32 + #include +#else + #include +#endif + +int Sys_CPUCount(void); diff --git a/components/gsc_parser/src/cpp/sys/sys_platform.cpp b/components/gsc_parser/src/cpp/sys/sys_platform.cpp new file mode 100644 index 00000000..de954c77 --- /dev/null +++ b/components/gsc_parser/src/cpp/sys/sys_platform.cpp @@ -0,0 +1,61 @@ +#include "sys_platform.h" + +#ifdef _WIN32 + +#include + +ssize_t getline(char **lineptr, size_t *n, FILE *stream) +{ + if (lineptr == NULL || stream == NULL || n == NULL) + return -1; + + // + // ssize_t is SIGNED + // + if (*n > INT_MAX) + return -1; + + char* bufptr = *lineptr; + char *p = bufptr; + ssize_t size = *n; + + int c = fgetc(stream); + + if (c == EOF) + return -1; + + if (bufptr == NULL) + { + size = 128; + bufptr = (char*)malloc(size); + if (bufptr == NULL) + return -1; + } + + p = bufptr; + while (c != EOF) + { + if ((p - bufptr) > (size - 1)) + { + size = size + 128; + bufptr = (char*)realloc(bufptr, size); + + if (bufptr == NULL) + return -1; + } + *p++ = c; + + if (c == '\n') + break; + else + c = fgetc(stream); + } + + *p++ = '\0'; + *lineptr = bufptr; + *n = size; + + return p - bufptr - 1; +} + +#endif \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/sys/sys_platform.h b/components/gsc_parser/src/cpp/sys/sys_platform.h new file mode 100644 index 00000000..e7573b66 --- /dev/null +++ b/components/gsc_parser/src/cpp/sys/sys_platform.h @@ -0,0 +1,41 @@ +#pragma once + +// +// Include these headers regardless of platform to preserve +// consistent header functionality +// +#include // required for _fileno & fopen_s (WIN32) +#include // require for _strdup (WIN32) + +#if _WIN32 + +// WIN32 Specific ( required for _isatty ) +#include + +#define isatty _isatty +#define fileno _fileno +#define strdup _strdup +#define snprintf _snprintf + +#define W32_WARNING_DISABLE(NUM) __pragma(warning(disable:NUM)) +#define W32_WARNING_ENABLE(NUM) __pragma(warning(default:NUM)) + +W32_WARNING_DISABLE(4996) // Disable POSIX Name Deprecation + +#ifndef _SSIZE_T_DEFINED +#ifdef _WIN64 +typedef signed __int64 ssize_t; +#else /* _WIN64 */ +typedef _W64 signed int ssize_t; +#endif /* _WIN64 */ +#define _SSIZE_T_DEFINED +#endif + +ssize_t getline(char **lineptr, size_t *n, FILE *stream); + +#else //LINUX + +#include // required for isatty (LINUX) +#define stricmp strcasecmp + +#endif diff --git a/components/gsc_parser/src/cpp/sys/sys_semaphore.cpp b/components/gsc_parser/src/cpp/sys/sys_semaphore.cpp new file mode 100644 index 00000000..64018c5b --- /dev/null +++ b/components/gsc_parser/src/cpp/sys/sys_semaphore.cpp @@ -0,0 +1,46 @@ +#include "sys_semaphore.h" + +#if WIN32 + +int sem_init(sem_t *sem, int pshared, unsigned int value) +{ + if(*sem = CreateSemaphore(NULL, value, 999, NULL)) + { + return 0; + } + + return -1; +} + +int sem_destroy(sem_t *sem) +{ + if(CloseHandle(*sem)) + { + return 0; + } + + return GetLastError(); +} + +int sem_wait(sem_t *sem) +{ + if(WaitForSingleObject(*sem, INFINITE) == WAIT_FAILED) + { + return GetLastError(); + } + + return 0; +} + +int sem_post(sem_t *sem) +{ + long val = 0; + if(!ReleaseSemaphore(*sem, 1, &val)) + { + return GetLastError(); + } + + return 0; +} + +#endif diff --git a/components/gsc_parser/src/cpp/sys/sys_semaphore.h b/components/gsc_parser/src/cpp/sys/sys_semaphore.h new file mode 100644 index 00000000..6a860613 --- /dev/null +++ b/components/gsc_parser/src/cpp/sys/sys_semaphore.h @@ -0,0 +1,15 @@ +#pragma once + +#if WIN32 + #include + + typedef HANDLE sem_t; + + int sem_init(sem_t *sem, int pshared, unsigned int value); + int sem_destroy(sem_t *sem); + int sem_wait(sem_t *sem); + int sem_post(sem_t *sem); +#else + #include +#endif + diff --git a/components/gsc_parser/src/cpp/sys/sys_thread.cpp b/components/gsc_parser/src/cpp/sys/sys_thread.cpp new file mode 100644 index 00000000..c2998458 --- /dev/null +++ b/components/gsc_parser/src/cpp/sys/sys_thread.cpp @@ -0,0 +1,122 @@ +#include "sys_thread.h" +#include "sys_worker.h" + +#if WIN32 + +int pthread_create(pthread_t* handle, int flags, thread_func_t entryPoint, void* args) +{ + if(*handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)entryPoint, args, 0, NULL)) + { + return 0; + } + + return GetLastError(); +} + +int pthread_detach(pthread_t handle) +{ + if(CloseHandle(handle) == FALSE) + { + return GetLastError(); + } + return 0; +} + +int pthread_join(pthread_t handle, void** val) +{ + if(WaitForSingleObject(handle, INFINITE) == WAIT_FAILED) + { + return GetLastError(); + } + return CloseHandle(handle); +} + +#endif + +Thread::Thread(thread_func_t entryPoint, Worker* owner) : owner(owner), argv(NULL) +{ + this->state = THREAD_ALIVE; + this->state = (pthread_create(&this->handle, 0, entryPoint, this) == 0) * THREAD_ALIVE; +} + +Thread::Thread(thread_func_t entryPoint, Worker* owner, void* arg) : owner(owner), argv(arg) +{ + this->state = THREAD_ALIVE; + this->state = (pthread_create(&this->handle, 0, entryPoint, this) == 0) * THREAD_ALIVE; +} + +Thread::~Thread(void) +{ + this->Join(); +} + +Worker* Thread::Owner(void) const +{ + return this->owner; +} + +bool Thread::IsAlive(void) const +{ + return (this->state & THREAD_ALIVE) != 0; +} + +bool Thread::IsExecuting(void) const +{ + return (this->state & THREAD_COMPLETED) != 0; +} + +bool Thread::IsDetached(void) const +{ + return (this->owner == NULL) || this->state & THREAD_DETACHED; +} + +int Thread::Detach(void) +{ + if(state & (THREAD_DETACHED | THREAD_JOINED)) + { + return -1; + } + + if(this->owner) + { + owner->thread = NULL; + this->owner = NULL; + } + + int err = pthread_detach(handle); + if(!err) + { + state |= THREAD_DETACHED; + } + + return err; +} + +int Thread::Join(void) +{ + void* tmp = NULL; + return this->Join(&tmp); +} + +int Thread::Join(void** value_ptr) +{ + if(this->state & THREAD_JOINED || this->IsDetached()) + { + return -1; + } + + this->state |= THREAD_JOINING; + int err = pthread_join(handle, value_ptr); + + if(!err) + { + state |= THREAD_JOINED; + } + + return err; +} + +void Thread::Kill(void) +{ + this->state &= ~THREAD_ALIVE; +} \ No newline at end of file diff --git a/components/gsc_parser/src/cpp/sys/sys_thread.h b/components/gsc_parser/src/cpp/sys/sys_thread.h new file mode 100644 index 00000000..8aea7cfe --- /dev/null +++ b/components/gsc_parser/src/cpp/sys/sys_thread.h @@ -0,0 +1,60 @@ +#pragma once + +#if WIN32 + #include + typedef HANDLE pthread_t; + + #define THREAD_CALL WINAPI + typedef void* (THREAD_CALL *thread_func_t)(void* args); + + int pthread_create(pthread_t* handle, int flags, thread_func_t entryPoint, void* args); + int pthread_detach(pthread_t handle); + int pthread_join(pthread_t handle, void** val); +#else + #include + + #define THREAD_CALL + typedef void* (*thread_func_t)(void* args); +#endif + +class Worker; + +enum THREAD_STATE_FLAGS +{ + THREAD_ALIVE = 1 << 0, + THREAD_JOINING = 1 << 1, + THREAD_JOINED = 1 << 2, + THREAD_DETACHED = 1 << 3, + THREAD_COMPLETED = 1 << 4, +}; + +class Thread +{ +friend class Worker; +private: + pthread_t handle; + Worker* owner; + + void* argv; + +protected: + volatile int state; + bool completed; + +public: + Thread(thread_func_t entryPoint, Worker* owner); + Thread(thread_func_t entryPoint, Worker* owner, void* argv); + virtual ~Thread(void); + + Worker* Owner(void) const; + + bool IsAlive(void) const; + bool IsExecuting(void) const; + bool IsDetached(void) const; + + int Detach(void); + int Join(void); + int Join(void** value_ptr); + + void Kill(void); +}; diff --git a/components/gsc_parser/src/cpp/sys/sys_worker.cpp b/components/gsc_parser/src/cpp/sys/sys_worker.cpp new file mode 100644 index 00000000..c564a68e --- /dev/null +++ b/components/gsc_parser/src/cpp/sys/sys_worker.cpp @@ -0,0 +1,186 @@ +#include "sys_worker.h" +#include + +int Worker::count = 0; + +sem_t Job::sem_access; +sem_t Job::sem_jobs; + +Job Job::queue(NULL, NULL); + +Job::Job() : func(NULL) , argv(NULL) +{ + this->SetOwner(this); +} + +Job::Job(job_func_t job_func, void* job_args) : func(job_func), argv(job_args) +{ + this->SetOwner(this); +} + +Job::~Job() +{ +} + +void Job::Register() +{ + LockAccess(); + + queue.AddToEnd(this); + sem_post(&sem_jobs); + + UnlockAccess(); +} + + +void Job::PostQuitJob() +{ + Job::LockAccess(); + + queue.flags |= (JOB_QUIT | JOB_STATIC); + for(Job* j = queue.NextElem(); j; j = queue.NextElem()) + { + delete j; + } + + // Tell the workers that there is a job waiting + // and then kill them when they find it + sem_post(&sem_jobs); + + Job::UnlockAccess(); +} + +void Job::LockAccess(void) +{ + sem_wait(&sem_access); +} + +void Job::UnlockAccess(void) +{ + sem_post(&sem_access); +} + +Worker::Worker() +{ + if(!(this->id = Worker::count++)) + { + sem_init(&Job::sem_jobs, 0, 0); + sem_init(&Job::sem_access, 0, 1); + } + + //printf("Worker[%d]: Init\n", this->id); + this->thread = new Thread(Worker::JobHandler, this); +} + +Worker::~Worker() +{ + //printf("Worker[%d]: Free\n", id); + + if(this->thread && thread->state == THREAD_ALIVE) + { + //thread->Kill(); + thread->Join(); + //printf("Worker[%d]: Free %d\n", id, this->thread->state); + + } + delete this->thread; + + if(!--Worker::count) + { + sem_destroy(&Job::sem_access); + sem_destroy(&Job::sem_jobs); + } +} + +void* THREAD_CALL Worker::JobHandler(void* args) +{ + Thread* thread = (Thread*)args; + + //printf("Worker[%d]: Alive\n", id); + while(thread->IsAlive()) + { + sem_wait(&Job::sem_jobs); + //printf("Worker[%d]: Job available\n", thread->owner->id); + + Job::LockAccess(); + //printf("Worker[%d]: Aquired access %d\n", thread->owner->id, thread->IsAlive()); + + //Get the job stuff - pop it from the list + + // Check for quit message + if(Job::queue.flags & JOB_QUIT) + { + thread->Kill(); + sem_post(&Job::sem_jobs); + Job::UnlockAccess(); + continue; + } + + Job* job = Job::queue.NextElem(); + if(job) + { + job->Remove(); + } + else + { + //printf("Worker[%d]: Job error - invalid job\n", thread->owner->id); + } + + Job::UnlockAccess(); + + job->func(job->argv); + delete job; + + if(!thread->IsAlive()) + { + continue; + } + } + + if(~thread->state & THREAD_JOINING) + { + thread->Detach(); + delete thread; + //printf("Worker[%d]: Exit via Detach\n", thread->owner->id); + } + + //printf("Worker[%d]: Exit via JOIN\n", thread->owner->id); + + return 0; +} + +void Worker::Detach() +{ + if(!this->thread) + { + return; + } + + thread->Detach(); + this->thread = NULL; +} + +void Worker::Kill() +{ + if(this->thread) + { + this->thread->Kill(); + } +} + +void* Worker::Join() +{ + if(this->thread) + { + void* out = NULL; + thread->Join(&out); + return out; + } + + return NULL; +} + +int Worker::Count(void) +{ + return Worker::count; +} diff --git a/components/gsc_parser/src/cpp/sys/sys_worker.h b/components/gsc_parser/src/cpp/sys/sys_worker.h new file mode 100644 index 00000000..9e190579 --- /dev/null +++ b/components/gsc_parser/src/cpp/sys/sys_worker.h @@ -0,0 +1,87 @@ +#pragma once + +#include +#include "sys_thread.h" +#include "sys_semaphore.h" + +#include "../util/llist.h" + +typedef int (*job_func_t)(void* argv); + +enum JOB_FLAGS +{ + JOB_QUIT = 1 << 0, + JOB_STATIC = 1 << 1, // Do not remove the job from the queue + //JOB_SYNCRONOUS = 1 << 2, // Execute the job on the main thread +}; + +class Job : public LList +{ +friend class Worker; + +private: + static sem_t sem_access; + static sem_t sem_jobs; + + static Job queue; + + job_func_t func; + void* argv; + + int flags; + +public: + Job(); + Job(job_func_t job_func, void* job_args); + ~Job(); + + void Register(void); + void Unregister(void); + + static void PostQuitJob(); + + static void AddJob(); + + // + // Blocks the calling thread until it aquires access to the job list + // + static void LockAccess(void); + static void UnlockAccess(void); +}; + +class Worker +{ +friend class Job; +friend class Thread; +private: + static int count; + static void* THREAD_CALL JobHandler(void* args); + + int id; + + Thread* thread; + +public: + Worker(); + ~Worker(); + + // + // Close the worker thread after current job + // + void Detach(); + + // + // Block the calling function until the worker thread completes and is closed + // + void* Join(); + + // + // Kill the worker thread + // + void Kill(); + + // + // Returns the current number of workers + // + static int Count(void); +}; diff --git a/components/gsc_parser/src/cpp/util/hash_table.cpp b/components/gsc_parser/src/cpp/util/hash_table.cpp new file mode 100644 index 00000000..cba87372 --- /dev/null +++ b/components/gsc_parser/src/cpp/util/hash_table.cpp @@ -0,0 +1,16 @@ +#include "hash_table.h" +#include "../sys/sys_platform.h" +#include +#include + +int Str_CalcHash(const char* str) +{ + return 2; + const char* s = str; + int h = 0; + for (int i = 0; *s != '\0'; i++) + { + h += (*s++) ^ (i + 55); + } + return h; +} diff --git a/components/gsc_parser/src/cpp/util/hash_table.h b/components/gsc_parser/src/cpp/util/hash_table.h new file mode 100644 index 00000000..c562bbd1 --- /dev/null +++ b/components/gsc_parser/src/cpp/util/hash_table.h @@ -0,0 +1,276 @@ +#pragma once +#include "../sys/sys_platform.h" +#include +#include +#include "llist.h" + +typedef void (*hash_table_traverse_f)(int index, const char* key, void* value); + +int Str_CalcHash(const char* str); + +template +class HashTable; + +template +class HashNode +{ +friend class HashTable; +private: + const char* key; + int hash; + + T value; + + HashNode* prev; + HashNode* next; + +protected: + // + // Adds this node before the argument node + // + void AddBefore(HashNode* node); + +public: + HashNode(void); + HashNode(const HashNode& node); + HashNode(const char* key); + + ~HashNode(void); + + HashNode& operator=(const HashNode& node); + + int Hash(void) const; + const char* Key(void) const; + T& Value(void); +}; + +template +HashNode::HashNode(void) : value(), prev(NULL), next(NULL) +{ +} + +template +HashNode::HashNode(const HashNode& node) : value(), prev(NULL), next(NULL) +{ + this->hash = node->hash; + this->key = strdup(node.key); +} + +template +HashNode::HashNode(const char* key) : value(), prev(NULL), next(NULL) +{ + this->key = strdup(key); + this->hash = Str_CalcHash(key); +} + +template +HashNode::~HashNode() +{ + free((void*)key); + + if(this->prev) + { + if(next) + { + next->prev = prev; + prev->next = next; + } + else + { + prev->next = NULL; + } + } + else if(this->next) + { + next->prev = NULL; + } + + ///delete value; + //value = NULL; +} + +template +HashNode& HashNode::operator=(const HashNode& node) +{ + free(key); + + this->hash = node->hash; + this->key = strdup(node.key); +} + +// +// Add this before node +// +template +void HashNode::AddBefore(HashNode* node) +{ + this->next = node; + this->prev = node->prev; + if(prev) + { + prev->next = this; + } + node->prev = this; +} + +template +int HashNode::Hash(void) const +{ + return this->hash; +} + +template +const char* HashNode::Key(void) const +{ + return this->key; +} + +template +T& HashNode::Value(void) +{ + return this->value; +} + +template +class HashTable +{ +private: + static const int bucketCount = 256; //bucketCount must be a power of 2 + static const int hashMask = bucketCount - 1; + HashNode** buckets; + +public: + HashTable(void); + ~HashTable(void); + + // + // Returns a pointer to the contents of a node with a matching key + // or NULL if no matching node is found + // + T* Get(const char* key); + + // + // Adds a new node with the given key, and return a pointer to its value + // or returns a ptr to the contents of an existing node if a match already exists + // + T* Add(const char* key); + + // + // Remove a node with the given key (if it exists) from the table + // + void RemoveNode(const char* key); + + // + // Remove all nodes + // + void Clear(void); + + // + // Traverse the hash table calling callback on each entry + // return the number of elements + // + int Traverse(hash_table_traverse_f callback); +}; + +template +HashTable::HashTable() +{ + this->buckets = new HashNode*[bucketCount]; + memset(buckets, 0, sizeof(HashNode*) * bucketCount); +} + +template +HashTable::~HashTable() +{ + this->Clear(); + delete[] buckets; +} + +template +T* HashTable::Get(const char* key) +{ + int hash = Str_CalcHash(key); + + for(HashNode* node = buckets[hash & hashMask]; node; node = node->next) + { + if(strcmp(node->Key(), key) == 0) + { + return &node->value; + } + } + + return NULL; +} + +template +T* HashTable::Add(const char* key) +{ + int hash = Str_CalcHash(key); + + if(buckets[hash & hashMask] == NULL) + { + HashNode* node = new HashNode(key); + buckets[hash & hashMask] = node; + return &node->value; + } + + for(HashNode* node = buckets[hash & hashMask]; node; node = node->next) + { + if(strcmp(node->Key(), key) == 0) + { + return &node->value; + } + } + + HashNode* head = buckets[hash & hashMask]; + HashNode* node = new HashNode(key); + node->AddBefore(head); + buckets[hash & hashMask] = node; + return &node->value; +} + +template +void HashTable::RemoveNode(const char* key) +{ + int hash = Str_CalcHash(key); + + for(HashNode* node = buckets[hash & hashMask]; node; node = node->next) + { + if(strcmp(node->Key(), key) == 0) + { + delete node; + return; + } + } +} + +template +void HashTable::Clear(void) +{ + for(int i = 0; i < bucketCount; i++) + { + for(HashNode* node = buckets[i]; node;) + { + HashNode* next = node->next; + delete node; + node = next; + } + + buckets[i] = NULL; + } +} + +template +int HashTable::Traverse(hash_table_traverse_f callback) +{ + int count = 0; + for(int i = 0; i < bucketCount; i++) + { + for(HashNode* node = buckets[i]; node; node = node->next) + { + callback(count++, node->Key(), &node->value); + } + } + + return count; +} diff --git a/components/gsc_parser/src/cpp/util/llist.h b/components/gsc_parser/src/cpp/util/llist.h new file mode 100644 index 00000000..0e422e37 --- /dev/null +++ b/components/gsc_parser/src/cpp/util/llist.h @@ -0,0 +1,196 @@ +#pragma once + +#include + +template +class LList +{ +private: + LList* head; + + LList* prev; + LList* next; + + T* owner; + +public: + + LList(void); + virtual ~LList(void); + + bool IsEmpty(void) const; + bool InList(void) const; + + int Size(void) const; + void Clear(void); + + void InsertBefore(LList* node); + void InsertAfter(LList* node); + + void AddToFront(LList* node); + void AddToEnd(LList* node); + + void Remove(void); + + T* PrevElem(void) const; + T* NextElem(void) const; + + T* Owner(void) const; + void SetOwner(T* elem); + + LList* HeadNode(void) const; + LList* PrevNode(void) const; + LList* NextNode(void) const; +}; + +template +LList::LList(void) : head(this), prev(this), next(this), owner(NULL) +{ +} + +template +LList::~LList(void) +{ + this->Clear(); +} + +template +bool LList::IsEmpty(void) const +{ + return head == head->next; +} + +template +bool LList::InList(void) const +{ + return this != head; +} + +// +// Returns the size of the current list (without the head) +// +template +int LList::Size(void) const +{ + int result = 0; + for(LList* node = head->next; node != head; node = node->next) + { + result++; + } + return result; +} + +template +void LList::Clear(void) +{ + if(this == head) { + while(next != this) { + next->Remove(); + } + } else { + this->Remove(); + } +} + +template +void LList::Remove(void) +{ + this->prev->next = this->next; + this->next->prev = this->prev; + + this->next = this; + this->prev = this; + this->head = this; +} + +template +T* LList::PrevElem(void) const +{ + return (!this->prev || this->prev == head) ? NULL : this->prev->owner; +} + +template +T* LList::NextElem(void) const +{ + return (!this->next || this->next == head) ? NULL : this->next->owner; +} + +template +T* LList::Owner(void) const +{ + return this->owner; +} + +template +void LList::SetOwner(T* elem) +{ + this->owner = elem; +} + +// +// Insert THIS before NODE +// +template +void LList::InsertBefore(LList* node) +{ + this->Remove(); + + this->prev = node->prev; + this->next = node; + this->head = node->head; + + node->prev = this; + prev->next = this; +} + +// +// Insert THIS after NODE +// +template +void LList::InsertAfter(LList* node) +{ + this->Remove(); + + this->prev = node; + this->next = node->next; + this->head = node->head; + + node->next = this; + next->prev = this; +} + +// +// Add the node to the beginning of THIS list +// +template +void LList::AddToFront(LList* node) +{ + node->InsertAfter(this->head); +} + +// +// Add the node to the end of THIS list +// +template +void LList::AddToEnd(LList* node) +{ + node->InsertBefore(this->head); +} + +template +LList* LList::HeadNode(void) const +{ + return this->head; +} + +template +LList* LList::PrevNode(void) const +{ + return (this->prev == head) ? NULL : this->prev; +} + +template +LList* LList::NextNode(void) const +{ + return (this->next == head) ? NULL : this->next; +} diff --git a/components/gsc_parser/src/cpp/util/location.cpp b/components/gsc_parser/src/cpp/util/location.cpp new file mode 100644 index 00000000..1e6bdbfa --- /dev/null +++ b/components/gsc_parser/src/cpp/util/location.cpp @@ -0,0 +1,102 @@ +#include "location.h" + +// +// Position +// + +Position::Position(void) : line(0), character(0) { }; +Position::Position(int line, int character) : line(line), character(character) { }; + +Position::~Position(void) { }; + +void Position::Print(void) +{ + printf("%d(%d)", this->line, this->character); +} + +const Position& Position::operator=(const Position& arg) +{ + this->line = arg.line; + this->character = arg.character; + + return *this; +} + +bool Position::operator==(const Position& arg) +{ + return (this->line == arg.line && this->character == arg.character); +} + +bool Position::operator<(const Position& arg) +{ + if(this->line < arg.line) + return true; + if(this->line == arg.line) + return this->character < arg.character; + return false; +} + +bool Position::operator>(const Position& arg) +{ + if(this->line > arg.line) + return true; + if(this->line == arg.line) + return this->character > arg.character; + return false; +} + +bool Position::operator<=(const Position& arg) +{ + if(this->line < arg.line) + return true; + if(this->line == arg.line) + return this->character <= arg.character; + return false; +} + +bool Position::operator>=(const Position& arg) +{ + if(this->line > arg.line) + return true; + if(this->line == arg.line) + return this->character >= arg.character; + return false; +} + +// +// Range +// + +Range::Range(void) { }; + +Range::Range(const YYLTYPE& loc) : + start(loc.first_line, loc.first_column), + end(loc.last_line, loc.last_column) + { }; + +Range::Range(Position start, Position end) : + start(start), + end(end) + { }; + +Range::Range(Position start, int endLine, int endChar) : + start(start), + end(endLine, endChar) + { }; + +Range::Range(int startLine, int startChar, Position end) : + start(startLine, startChar), + end(end) + { }; + +Range::Range(int startLine, int startChar, int endLine, int endChar) : + start(startLine, startChar), + end(endLine, endChar) + { }; + +Range::~Range(void) { }; + +void Range::Print(void) +{ + printf("%d(%d) - %d(%d)\n", this->start.line, this->start.character, this->end.line, this->end.character); +} diff --git a/components/gsc_parser/src/cpp/util/location.h b/components/gsc_parser/src/cpp/util/location.h new file mode 100644 index 00000000..7655abe7 --- /dev/null +++ b/components/gsc_parser/src/cpp/util/location.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include + +#include +#include "../parser/gsc.tab.hpp" + +class Position +{ +public: + int line; + int character; + + Position(void); + Position(int line, int character); + + ~Position(void); + + void Print(void); + + const Position& operator=(const Position&); + bool operator==(const Position&); + bool operator<(const Position&); + bool operator>(const Position&); + bool operator<=(const Position&); + bool operator>=(const Position&); +}; + +class Range +{ +public: + Position start; + Position end; + + Range(void); + Range(const YYLTYPE& loc); + + Range(Position start, Position end); + Range(Position start, int endLine, int endChar); + Range(int startLine, int startChar, Position end); + Range(int startLine, int startChar, int endLine, int endChar); + + ~Range(void); + + void Print(void); + + //const Range& operator=(const YYLTYPE& loc); + /*operator==(const Position&); + operator<(const Position&); + operator>(const Position&); + operator<=(const Position&); + operator>=(const Position&);*/ + + /* + Contains(point/ range) + Intersects? + */ +}; diff --git a/components/gsc_parser/src/gsc.l b/components/gsc_parser/src/gsc.l new file mode 100644 index 00000000..f6131919 --- /dev/null +++ b/components/gsc_parser/src/gsc.l @@ -0,0 +1,149 @@ +%{ + #include + #include "../sys/sys_platform.h" + + #include "gsc.tab.hpp" + #include "../symbols/symbol.h" + + #define YY_DECL int yylex (YYSTYPE *yylval_param, YYLTYPE *yylloc_param, void* yyscanner) + #define YY_USER_ACTION yy_update_loc(yyscanner); + + YYLTYPE *yyget_lloc (yyscan_t yyscanner); + void yyset_lloc (YYLTYPE * yylloc_param , void* yyscanner); + char *yyget_text (void* yyscanner ); + + void yy_update_loc(void* yyscanner) + { + YYLTYPE& yylloc = *yyget_lloc(yyscanner); + char* text = yyget_text(yyscanner); + + yylloc.first_line = yylloc.last_line; + yylloc.first_column = yylloc.last_column; + + for(int i = 0; text[i] != '\0'; i++) + { + if(text[i] == '\n') + { + yylloc.last_line++; + yylloc.last_column = 0; + } + else + { + yylloc.last_column++; + } + } + } + + #define PRINT_TOKEN(S) + //#define PRINT_TOKEN(S) printf(S) +%} + +%option nodefault + +%option reentrant +%option bison-bridge +%option bison-locations + +%option nounput +%option nounistd +%option noyywrap + +RX_T_STRING \"(?:\\.|[^\"])*?\"|\'(?:\\.|[^\'])*?\' +RX_T_FLOAT [0-9]+\.(?:[0-9]*)?f?|\.[0-9]+f? +RX_T_INT [0-9]+ +RX_T_FILEPATH ([_a-zA-Z0-9]+\\)+[_a-zA-Z0-9]+ +RX_T_IDENTIFIER [a-zA-Z_][a-zA-Z\-_0-9]* + +RX_DEFAULT (.|\n) + +%x S_BLOCK_COMMENT +%x S_BLOCK_DEVSCRIPT + +%% + +[ \t\n\r] /* skip whitespace & newlines */ +"//".* /* skip single line comments */ +"/*" { BEGIN(S_BLOCK_COMMENT); } + +(.|\n) /* ignore unicode shenanigans in comment blocks */ +"*/" { BEGIN(INITIAL); } + +"/#" { BEGIN(S_BLOCK_DEVSCRIPT); } +"#/" { BEGIN(INITIAL); } + +{RX_T_STRING} { PRINT_TOKEN("T_STRING, "); yylval->literal_string = strdup(yytext); return T_STRING; } +{RX_T_FLOAT} { PRINT_TOKEN("T_FLOAT, "); yylval->literal_string = strdup(yytext); return T_FLOAT; } +{RX_T_INT} { PRINT_TOKEN("T_INT, "); yylval->literal_string = strdup(yytext); return T_INT; } + +"#include" { PRINT_TOKEN("INCLUDE, "); return INCLUDE; } +"#using_animtree" { PRINT_TOKEN("USING_ANIMTREE, "); return USING_ANIMTREE; } +"#animtree" { PRINT_TOKEN("ANIMTREE, "); return ANIMTREE; } + +\( { PRINT_TOKEN("OP_LPAREN, "); return OP_LPAREN; } +\) { PRINT_TOKEN("OP_RPAREN, "); return OP_RPAREN; } +\[\s*\[ { PRINT_TOKEN("FUNC_POINTER_BEGIN, "); return FUNC_POINTER_BEGIN; } +\[ { PRINT_TOKEN("OP_LBRACKET, "); return OP_LBRACKET; } /* +\]\s*\] { PRINT_TOKEN("FUNC_POINTER_END, "); return FUNC_POINTER_END; } //Using this would override ']' in nested array expressions */ +\] { PRINT_TOKEN("OP_RBRACKET, "); return OP_RBRACKET; } +\{ { PRINT_TOKEN("OP_LBRACE, "); return OP_LBRACE; } +\} { PRINT_TOKEN("OP_RBRACE, "); return OP_RBRACE; } +\, { PRINT_TOKEN("OP_COMMA, "); return OP_COMMA; } +\. { PRINT_TOKEN("OP_DOT, "); return OP_DOT; } +\!\= { PRINT_TOKEN("OP_CMP_NEQ, "); return OP_CMP_NEQ; } +\! { PRINT_TOKEN("OP_NOT, "); return OP_NOT; } +% { PRINT_TOKEN("OP_MOD, "); return OP_MOD; } +\=\= { PRINT_TOKEN("OP_CMP_EQ, "); return OP_CMP_EQ; } +\= { PRINT_TOKEN("OP_ASSIGN, "); return OP_ASSIGN; } +\<\< { PRINT_TOKEN("OP_LSHIFT, "); return OP_LSHIFT; } +\<\= { PRINT_TOKEN("OP_CMP_LE, "); return OP_CMP_LE; } +\< { PRINT_TOKEN("OP_CMP_LT, "); return OP_CMP_LT; } +\>\> { PRINT_TOKEN("OP_RSHIFT, "); return OP_RSHIFT; } +\>\= { PRINT_TOKEN("OP_CMP_GE, "); return OP_CMP_GE; } +\> { PRINT_TOKEN("OP_CMP_GT, "); return OP_CMP_GT; } +\+\+ { PRINT_TOKEN("OP_INC, "); return OP_INC; } //neither post nor pre +\+\= { PRINT_TOKEN("OP_ASSIGN_ADD, "); return OP_ASSIGN_ADD; } +\+ { PRINT_TOKEN("OP_ADD, "); return OP_ADD; } +\-\- { PRINT_TOKEN("OP_DEC, "); return OP_DEC; } //neither post nor pre +\-\= { PRINT_TOKEN("OP_ASSIGN_SUB, "); return OP_ASSIGN_SUB; } +\- { PRINT_TOKEN("OP_SUB, "); return OP_SUB; } +\*\= { PRINT_TOKEN("OP_ASSIGN_MULT, "); return OP_ASSIGN_MULT; } +\* { PRINT_TOKEN("OP_MULT, "); return OP_MULT; } +\/= { PRINT_TOKEN("OP_ASSIGN_DIV, "); return OP_ASSIGN_DIV; } +\/ { PRINT_TOKEN("OP_DIV, "); return OP_DIV; } +\|\| { PRINT_TOKEN("OP_CMP_OR, "); return OP_CMP_OR; } +\|\= { PRINT_TOKEN("OP_ASSIGN_BW_OR, "); return OP_ASSIGN_BW_OR; } +\| { PRINT_TOKEN("OP_BW_OR, "); return OP_BW_OR; } +\&\& { PRINT_TOKEN("OP_CMP_AND, "); return OP_CMP_AND; } +\&\= { PRINT_TOKEN("OP_ASSIGN_BW_AND, "); return OP_ASSIGN_BW_AND; } +\& { PRINT_TOKEN("OP_BW_AND, "); return OP_BW_AND; } +\^\= { PRINT_TOKEN("OP_ASSIGN_BW_XOR, "); return OP_ASSIGN_BW_XOR; } +\^ { PRINT_TOKEN("OP_BW_XOR, "); return OP_BW_XOR; } +\~ { PRINT_TOKEN("OP_BW_NOT, "); return OP_BW_NOT; } +\:\: { PRINT_TOKEN("OP_COLON_DOUBLE, "); return OP_COLON_DOUBLE; } +\: { PRINT_TOKEN("OP_COLON, "); return OP_COLON; } +\; { PRINT_TOKEN("OP_COLON_SEMI, "); return OP_COLON_SEMI; } +\? { PRINT_TOKEN("OP_QMARK, "); return OP_QMARK; } /* +// \@ { PRINT_TOKEN("OP_AT, "); return OP_AT; } //disabled*/ +\# { PRINT_TOKEN("OP_HASH, "); return OP_HASH; } + +"if" { PRINT_TOKEN("T_IF, "); return T_IF; } +"else" { PRINT_TOKEN("T_ELSE, "); return T_ELSE; } +"switch" { PRINT_TOKEN("T_SWITCH, "); return T_SWITCH; } +"case" { PRINT_TOKEN("T_CASE, "); return T_CASE; } +"default" { PRINT_TOKEN("T_DEFAULT, "); return T_DEFAULT; } +"break" { PRINT_TOKEN("T_BREAK, "); return T_BREAK; } +"for" { PRINT_TOKEN("T_FOR, "); return T_FOR; } +"while" { PRINT_TOKEN("T_WHILE, "); return T_WHILE; } +"continue" { PRINT_TOKEN("T_CONTINUE, "); return T_CONTINUE; } +"return" { PRINT_TOKEN("T_RETURN, "); return T_RETURN; } + +"thread" { PRINT_TOKEN("T_THREAD, "); return T_THREAD; } +"wait" { PRINT_TOKEN("T_WAIT, "); return T_WAIT; } + +{RX_T_FILEPATH} { PRINT_TOKEN("T_FILEPATH, "); yylval->literal_string = strdup(yytext); return T_FILEPATH; } +{RX_T_IDENTIFIER} { PRINT_TOKEN("T_IDENTIFIER, "); yylval->t_identifier = strdup(yytext); return T_IDENTIFIER; } + +<*>{RX_DEFAULT} /* Ignore unexpected characters (this should never happen) + { fprintf(stderr, "RX_DEFAULT ACTION AT %d(%d) FOR '%s'\n", yyget_lloc(yyscanner)->first_line, yyget_lloc(yyscanner)->first_column, yytext);*/ + +%% diff --git a/components/gsc_parser/src/gsc.ypp b/components/gsc_parser/src/gsc.ypp new file mode 100644 index 00000000..f195132c --- /dev/null +++ b/components/gsc_parser/src/gsc.ypp @@ -0,0 +1,620 @@ +%{ + #include + #include + #include + + #include "../symbols/symbol.h" + #include "gsc.tab.hpp" + #include "gsc.yy.h" + + int yylex(YYSTYPE *yylval_param, YYLTYPE *yylloc_param, void* yyscanner); + extern void yyerror(YYLTYPE* loc, Symbol** yyAST_out, void* scanner, const char* err); +%} + +%define api.pure full +%lex-param { void* scanner } +%parse-param { Symbol** yyAST_out } { void* scanner } + +%define parse.error verbose + +%locations +%initial-action +{ + #if VSCODE_COMPATIBLE_LOCATION + @$.first_line = @$.last_line = 0; + @$.first_column = @$.last_column = 0; + #endif +} + +%code requires +{ + #include "../sys/sys_platform.h" + #include "../symbols/operator_enum.h" + + // + // Forward declarations for any custom classes used in YYSTYPE + // + class Symbol; + class Include; + + class Literal; + class Identifier; + + class Conditional; + + class Animtree; + class Expression; + class Member; + class Reference; + class Pointer; + + class Function; + class Call; + + class Return; + class Wait; +} + +%union { + OPERATOR_TYPE op; //operator + + int literal_int; + float literal_float; + char* literal_string; + + char* t_identifier; + + Symbol* symbol; + Include* include; + Animtree* animtree; + + Literal* literal; + + Member* member; + + Expression* expression; + Reference* reference; + Pointer* pointer; + + Function* function; + Call* call; + + Return* retn; +} + +%token T_STRING +%token T_FLOAT +%token T_INT + +%token INCLUDE +%token USING_ANIMTREE +%token ANIMTREE + +%token OP_LPAREN +%token OP_RPAREN +%token FUNC_POINTER_BEGIN + +%token OP_LBRACE +%token OP_RBRACE +%token OP_COMMA +%token OP_INC +%token OP_DEC + +%token OP_COLON_DOUBLE +%token OP_COLON +%token OP_COLON_SEMI +%token OP_QMARK +%token OP_HASH + +%token T_IF +%token T_ELSE +%token T_SWITCH +%token T_CASE +%token T_DEFAULT +%token T_BREAK +%token T_FOR +%token T_WHILE +%token T_CONTINUE +%token T_RETURN +%token T_THREAD +%token T_WAIT +%token T_FILEPATH +%token T_IDENTIFIER + +// Define Token +%token OP_LBRACKET OP_RBRACKET OP_DOT +%token OP_NOT OP_BW_NOT +%token OP_MULT OP_DIV OP_MOD +%token OP_ADD OP_SUB +%token OP_LSHIFT OP_RSHIFT +%token OP_CMP_LT OP_CMP_LE OP_CMP_GT OP_CMP_GE +%token OP_CMP_EQ OP_CMP_NEQ +%token OP_BW_AND +%token OP_BW_XOR +%token OP_BW_OR +%token OP_CMP_AND +%token OP_CMP_OR +//ternary +%token OP_ASSIGN +%token OP_ASSIGN_ADD OP_ASSIGN_SUB OP_ASSIGN_MULT OP_ASSIGN_DIV OP_ASSIGN_BW_AND OP_ASSIGN_BW_XOR OP_ASSIGN_BW_OR + +%token T_INVALID +%token T_EOF 0 + +// Special Operators & Operator-Like Tokens +%nonassoc T_THREAD T_FILEPATH T_IDENTIFIER +%left OP_COLON_DOUBLE FUNC_POINTER_BEGIN + +// Operator Precedence ( highest line number = highest precedence ) +%right OP_ASSIGN OP_ASSIGN_ADD OP_ASSIGN_SUB OP_ASSIGN_MULT OP_ASSIGN_DIV OP_ASSIGN_BW_AND OP_ASSIGN_BW_XOR OP_ASSIGN_BW_OR +//ternary +%left OP_CMP_OR +%left OP_CMP_AND +%left OP_BW_OR +%left OP_BW_XOR +%left OP_BW_AND +%left OP_CMP_EQ OP_CMP_NEQ +%left OP_CMP_LT OP_CMP_LE OP_CMP_GT OP_CMP_GE +%left OP_LSHIFT OP_RSHIFT +%left OP_ADD OP_SUB +%left OP_MULT OP_DIV OP_MOD +%right OP_INC_PRE OP_DEC_PRE OP_POSITIVE OP_NEGATIVE OP_NOT OP_BW_NOT +%left OP_INC_POST OP_DEC_POST OP_LBRACKET OP_RBRACKET OP_DOT + +%type IncludeDirective; +%type AnimtreeDirective; +%type FunctionDeclaration; + +%type FormalParameterList; + +%type SourceElements; +%type SourceElement; + +%type StringLiteral; +%type NumericLiteral; +%type Block; +%type FunctionParameterList; +%type FunctionCall; +%type FunctionExpression; +%type PointerExpression; +%type ReferenceExpression; +%type AnimReferenceExpression; +%type MemberExpression; +%type ElementList; +%type ListExpression; +%type ObjectExpression; +%type ModifiableExpression; +%type LiteralExpression; +%type OptionalExpression; +%type BasicExpression; +%type e; +%type Expression; +%type ExpressionStatement; +%type ReturnStatement; +%type WaitStatement; +%type EmptyStatement; +%type IfStatement; +%type SwitchStatement; +%type CaseStatement; +%type LoopStatement; +%type Statement; +%type StatementList; + +%destructor { delete $$; } <*> +%destructor { free($$); } T_FILEPATH T_IDENTIFIER T_STRING T_FLOAT T_INT + +%start Program + +%% + +IncludeDirective: + INCLUDE T_FILEPATH OP_COLON_SEMI + { + $$ = new Include(new Literal($2, @2, S_TYPE_LITERAL_FILEPATH), @$); + }; + +AnimtreeDirective: + USING_ANIMTREE OP_LPAREN T_STRING OP_RPAREN OP_COLON_SEMI + { + $$ = new Animtree(new Literal($3, @3, S_TYPE_LITERAL_STRING), @$); + }; + +FunctionDeclaration: + T_IDENTIFIER OP_LPAREN FormalParameterList OP_RPAREN Block + { + $$ = new Function(new Identifier($1, @1), @$); + $$->AddChild(new Group($3, @3)); + $$->AddChild($5); + }; + +FormalParameterList + : FormalParameterList OP_COMMA T_IDENTIFIER + { + $1->AddToEnd( new Identifier($3, @3) ); + $$ = $1; + } + | T_IDENTIFIER + { $$ = new Identifier($1, @1); } + | + { $$ = NULL; }; + +SourceElement: + IncludeDirective + | AnimtreeDirective + | FunctionDeclaration + +SourceElements: + SourceElements SourceElement + { + $1->AddToEnd($2); + $$ = $1; + } + | SourceElement + ; + +Program: SourceElements T_EOF + { + *yyAST_out = new Group($1, @1); + } + +StringLiteral + : T_STRING + { $$ = new Literal($1, @1, S_TYPE_LITERAL_STRING); } + | OP_BW_AND T_STRING + { $$ = new Literal($2, @$, S_TYPE_LITERAL_STRING); } + | OP_HASH T_STRING + { $$ = new Literal($2, @$, S_TYPE_LITERAL_STRING); } + ; + +NumericLiteral + : T_INT + { $$ = new Literal($1, @1, S_TYPE_LITERAL_INT); } + | T_FLOAT + { $$ = new Literal($1, @1, S_TYPE_LITERAL_FLOAT); } + ; + +Block + : OP_LBRACE StatementList OP_RBRACE + { $$ = new Group($2, @2); } + ; + +FunctionParameterList + : FunctionParameterList OP_COMMA Expression + { + $1->AddToEnd($3); + $$ = $1; + } + | Expression + { $$ = $1; } + | + { $$ = NULL; } + ; + +FunctionCall + : T_IDENTIFIER OP_LPAREN FunctionParameterList OP_RPAREN + { + $$ = new Call(@$, CALL_FLAG_IDENTIFIER); + $$->AddChild(new Identifier($1,@1)); + $$->AddChild(new Group($3, @3)); + } + | T_THREAD T_IDENTIFIER OP_LPAREN FunctionParameterList OP_RPAREN + { + $$ = new Call(@$, CALL_FLAG_THREAD | CALL_FLAG_IDENTIFIER); + $$->AddChild(new Identifier($2,@2)); + $$->AddChild(new Group($4, @4)); + } + | PointerExpression OP_LPAREN FunctionParameterList OP_RPAREN + { + $$ = new Call(@$, CALL_FLAG_POINTER); + $$->AddChild($1); + $$->AddChild(new Group($3, @3)); + } + | T_THREAD PointerExpression OP_LPAREN FunctionParameterList OP_RPAREN + { + $$ = new Call(@$, CALL_FLAG_THREAD | CALL_FLAG_POINTER); + $$->AddChild($2); + $$->AddChild(new Group($4, @4)); + } + | ReferenceExpression OP_LPAREN FunctionParameterList OP_RPAREN + { + $$ = new Call(@$, CALL_FLAG_REFERENCE); + $$->AddChild($1); + $$->AddChild(new Group($3, @3)); + } + | T_THREAD ReferenceExpression OP_LPAREN FunctionParameterList OP_RPAREN + { + $$ = new Call(@$, CALL_FLAG_THREAD | CALL_FLAG_REFERENCE); + $$->AddChild($2); + $$->AddChild(new Group($4, @4)); + } + ; + +FunctionExpression + : ObjectExpression FunctionCall + { + $2->SetCaller($1); + $$ = $2; + } + | FunctionCall + { $$ = $1; } + ; + +PointerExpression + : FUNC_POINTER_BEGIN ObjectExpression OP_RBRACKET OP_RBRACKET + { $$ = new Pointer($2, @$); } + | FUNC_POINTER_BEGIN ReferenceExpression OP_RBRACKET OP_RBRACKET + { $$ = new Pointer($2, @$); } + ; + +ReferenceExpression + : T_FILEPATH OP_COLON_DOUBLE T_IDENTIFIER + { + Literal* file = new Literal($1, @1, S_TYPE_LITERAL_FILEPATH); + Identifier* identifier = new Identifier($3, @3); + $$ = new Reference(file, identifier, @$); + } + | OP_COLON_DOUBLE T_IDENTIFIER + { + Identifier* identifier = new Identifier($2, @2); + $$ = new Reference(NULL, identifier, @$); + } + ; + +AnimReferenceExpression + : OP_MOD T_IDENTIFIER + { $$ = new Identifier($2, @2); } + ; + +MemberExpression + : ObjectExpression OP_LBRACKET Expression OP_RBRACKET + { $$ = new Member($1, $3, @$, S_TYPE_MEMBER_ARRAY_ELEMENT); } + | ObjectExpression OP_DOT ObjectExpression + { $$ = new Member($1, $3, @$, S_TYPE_MEMBER_OBJECT_PROPERTY); } + | OP_LBRACKET OP_RBRACKET + { $$ = new Member(NULL, NULL, @$, S_TYPE_MEMBER_ARRAY_EMPTY); } + ; + +ElementList + : Expression OP_COMMA Expression //Lists must have at least two elements + { + $1->AddToEnd($3); + $$ = $1; + } + | ElementList OP_COMMA Expression + { + $1->AddToEnd($3); + $$ = $1; + } + ; + +ListExpression + : OP_LPAREN ElementList OP_RPAREN + { $$ = $2; } + ; + +ObjectExpression + : ModifiableExpression + | FunctionExpression + /*| OP_LPAREN ObjectExpression OP_RPAREN // DEPRECATED + { $$ = $2; }*/ + ; + +LiteralExpression + : NumericLiteral + | StringLiteral + ; + +OptionalExpression + : Expression + | + { $$ = NULL; } + ; + +BasicExpression + : ObjectExpression + { $$ = $1; } + | LiteralExpression + { $$ = $1; } + | ListExpression //used for things like vectors + { $$ = $1; } + | ReferenceExpression + { $$ = $1; } + | ANIMTREE + { $$ = new Expression(@$); } + ; + +ModifiableExpression + : T_IDENTIFIER + { $$ = new Identifier($1, @1); } + | MemberExpression + /*| OP_LPAREN ModifiableExpression OP_RPAREN // DEPRECATED + { $$ = $2; }*/ + +e + : BasicExpression + { $$ = (Expression*)new Group($1, @1); } + + /* PREFIX OPERATORS */ + | OP_INC ModifiableExpression %prec OP_INC_PRE + { $$ = new Expression(OP_TYPE_INC, $2, @$); }; + | OP_DEC ModifiableExpression %prec OP_DEC_PRE + { $$ = new Expression(OP_TYPE_DEC, $2, @$); }; + | OP_ADD e %prec OP_POSITIVE + { $$ = new Expression(OP_TYPE_ADD, $2, @$); }; + | OP_SUB e %prec OP_NEGATIVE + { $$ = new Expression(OP_TYPE_SUB, $2, @$); }; + | OP_BW_NOT e + { $$ = new Expression(OP_TYPE_BW_NOT, $2, @$); }; + | OP_NOT e + { $$ = new Expression(OP_TYPE_NOT, $2, @$); }; + + /* POSTFIX OPERATORS */ + | ModifiableExpression OP_INC %prec OP_INC_POST + { $$ = new Expression($1, OP_TYPE_INC, @$); }; + | ModifiableExpression OP_DEC %prec OP_DEC_POST + { $$ = new Expression($1, OP_TYPE_INC, @$); }; + + /* MID OPERATORS */ + | e OP_MULT e + { $$ = new Expression($1, OP_TYPE_MULT, $3, @$); }; + | e OP_DIV e + { $$ = new Expression($1, OP_TYPE_DIV, $3, @$); }; + | e OP_MOD e + { $$ = new Expression($1, OP_TYPE_MOD, $3, @$); }; + | e OP_ADD e + { $$ = new Expression($1, OP_TYPE_ADD, $3, @$); }; + | e OP_SUB e + { $$ = new Expression($1, OP_TYPE_SUB, $3, @$); }; + | e OP_LSHIFT e + { $$ = new Expression($1, OP_TYPE_LSHIFT, $3, @$); }; + | e OP_RSHIFT e + { $$ = new Expression($1, OP_TYPE_RSHIFT, $3, @$); }; + | e OP_CMP_LT e + { $$ = new Expression($1, OP_TYPE_CMP_LT, $3, @$); }; + | e OP_CMP_LE e + { $$ = new Expression($1, OP_TYPE_CMP_LE, $3, @$); }; + | e OP_CMP_GT e + { $$ = new Expression($1, OP_TYPE_CMP_GT, $3, @$); }; + | e OP_CMP_GE e + { $$ = new Expression($1, OP_TYPE_CMP_GE, $3, @$); }; + | e OP_CMP_EQ e + { $$ = new Expression($1, OP_TYPE_CMP_EQ, $3, @$); }; + | e OP_CMP_NEQ e + { $$ = new Expression($1, OP_TYPE_CMP_NEQ, $3, @$); }; + | e OP_BW_AND e + { $$ = new Expression($1, OP_TYPE_BW_AND, $3, @$); }; + | e OP_BW_OR e + { $$ = new Expression($1, OP_TYPE_BW_OR, $3, @$); }; + | e OP_CMP_AND e + { $$ = new Expression($1, OP_TYPE_CMP_AND, $3, @$); }; + | e OP_CMP_OR e + { $$ = new Expression($1, OP_TYPE_CMP_OR, $3, @$); }; + | e OP_ASSIGN e + { $$ = new Expression($1, OP_TYPE_ASSIGN, $3, @$); }; + | e OP_ASSIGN_ADD e + { $$ = new Expression($1, OP_TYPE_ASSIGN_ADD, $3, @$); }; + | e OP_ASSIGN_SUB e + { $$ = new Expression($1, OP_TYPE_ASSIGN_SUB, $3, @$); }; + | e OP_ASSIGN_MULT e + { $$ = new Expression($1, OP_TYPE_ASSIGN_MULT, $3, @$); }; + | e OP_ASSIGN_DIV e + { $$ = new Expression($1, OP_TYPE_ASSIGN_DIV, $3, @$); }; + | e OP_ASSIGN_BW_AND e + { $$ = new Expression($1, OP_TYPE_ASSIGN_BW_AND, $3, @$); }; + | e OP_ASSIGN_BW_XOR e + { $$ = new Expression($1, OP_TYPE_ASSIGN_BW_XOR, $3, @$); }; + | e OP_ASSIGN_BW_OR e + { $$ = new Expression($1, OP_TYPE_ASSIGN_BW_OR, $3, @$); }; + | AnimReferenceExpression + { $$ = $1; } + | OP_LPAREN e OP_RPAREN + { $$ = $2; } + ; + +Expression + : e + ; + +ExpressionStatement + : Expression OP_COLON_SEMI + { $$ = $1; /*new Symbol(@$);*/ } + ; + +ReturnStatement + : T_RETURN OP_COLON_SEMI + { $$ = new Return(NULL, @$); } + | T_RETURN Expression OP_COLON_SEMI + { $$ = new Return($2, @$); } + ; + +WaitStatement + : T_WAIT Expression OP_COLON_SEMI + { $$ = new Wait($2, @$); } + //| T_WAIT OP_LPAREN Expression OP_RPAREN OP_COLON_SEMI + // { $$ = new Symbol(@$); } + ; + +EmptyStatement + : OP_COLON_SEMI + { $$ = new Symbol(@$); } + ; + +IfStatement + : T_IF OP_LPAREN Expression OP_RPAREN Statement + { $$ = new Conditional($3, $5, @$, S_TYPE_CONDITIONAL_IF); } + | T_ELSE Statement + { $$ = new Conditional(NULL, $2, @$, S_TYPE_CONDITIONAL_ELSE); } + ; + +SwitchStatement + : T_SWITCH OP_LPAREN Expression OP_RPAREN Statement + { $$ = new Conditional($3, $5, @$, S_TYPE_CONDITIONAL_SWITCH); } + ; + +CaseStatement + : T_CASE LiteralExpression OP_COLON + { $$ = new Conditional($2, NULL, @$, S_TYPE_CONDITIONAL_CASE); + } + | T_DEFAULT OP_COLON + { $$ = new Conditional(NULL, NULL, @$, S_TYPE_CONDITIONAL_CASE); + } + ; + +LoopStatement + : T_WHILE OP_LPAREN Expression OP_RPAREN Statement + { $$ = new Conditional($3, $5, @$, S_TYPE_CONDITIONAL_WHILE); } + | T_FOR OP_LPAREN OptionalExpression OP_COLON_SEMI OptionalExpression OP_COLON_SEMI OptionalExpression OP_RPAREN Statement + { + $$ = new Conditional($3, $5, $7, $9, @$, S_TYPE_CONDITIONAL_FOR); + } + ; + +Statement + : Block + | WaitStatement + | ExpressionStatement + | IfStatement + | SwitchStatement + | CaseStatement + | LoopStatement + | ReturnStatement + | T_BREAK OP_COLON_SEMI + { $$ = new Symbol(@$); } + | T_CONTINUE OP_COLON_SEMI + { $$ = new Symbol(@$); } + | EmptyStatement + ; + +StatementList + : StatementList Statement + { + if($1 != NULL) + { + $1->AddToEnd($2); + $$ = $1; + } + else + { + $$ = $2; + } + } + | StatementList error + /* Catches most errors, but also causes the next function / statement / etc. to be skipped in certain cases */ + | + { $$ = NULL; } + ; + +%% + +// +// Notify the user that there was an error while parsing +// if error recovery is impossible, YYABORT is automatically called +// and the symbol tree is cleaned up +// +void yyerror(YYLTYPE* loc, Symbol** AST, yyscan_t scanner, const char* err) +{ + fprintf(stderr, "PARSE ERROR AT LINE %d(%d): %s\n", loc->first_line, loc->first_column, err); +/*#if !(_DEBUG) + exit(1); +#endif*/ +} diff --git a/components/launcher/launcher.vcxproj b/components/launcher/launcher.vcxproj new file mode 100644 index 00000000..c2df1436 --- /dev/null +++ b/components/launcher/launcher.vcxproj @@ -0,0 +1,94 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {99C1298E-362E-455B-B938-04CB5B5510BA} + Win32Proj + launcher + + + + Application + true + Unicode + v120_xp + + + Application + false + true + Unicode + v120_xp + + + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + $(SolutionDir)\components\shared;$(SolutionDir)\components\shared\imgui + false + + + Windows + true + %(AdditionalDependencies);imgui.lib + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + $(SolutionDir)\components\shared;$(SolutionDir)\components\shared\imgui + true + + + Windows + true + true + true + %(AdditionalDependencies);imgui.lib + + + + + + + + + \ No newline at end of file diff --git a/components/launcher/main.cpp b/components/launcher/main.cpp new file mode 100644 index 00000000..c9fbc10d --- /dev/null +++ b/components/launcher/main.cpp @@ -0,0 +1,367 @@ +#include +#include +#include +#define DIRECTINPUT_VERSION 0x0800 +#include +#include + +#pragma comment(lib, "d3d9.lib") + +// Data +static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; +static D3DPRESENT_PARAMETERS g_d3dpp; + +extern LRESULT ImGui_ImplDX9_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (ImGui_ImplDX9_WndProcHandler(hWnd, msg, wParam, lParam)) + return true; + + switch (msg) + { + case WM_SIZE: + if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED) + { + ImGui_ImplDX9_InvalidateDeviceObjects(); + g_d3dpp.BackBufferWidth = LOWORD(lParam); + g_d3dpp.BackBufferHeight = HIWORD(lParam); + HRESULT hr = g_pd3dDevice->Reset(&g_d3dpp); + if (hr == D3DERR_INVALIDCALL) + IM_ASSERT(0); + ImGui_ImplDX9_CreateDeviceObjects(); + } + return 0; + case WM_SYSCOMMAND: + if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu + return 0; + break; + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + return DefWindowProc(hWnd, msg, wParam, lParam); +} + +int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + // Create application window + WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, LoadCursor(NULL, IDC_ARROW), NULL, NULL, _T("ImGui Example"), NULL }; + RegisterClassEx(&wc); + HWND hwnd = CreateWindow(_T("ImGui Example"), _T("ImGui DirectX9 Example"), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL); + + // Initialize Direct3D + LPDIRECT3D9 pD3D; + if ((pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL) + { + UnregisterClass(_T("ImGui Example"), wc.hInstance); + return 0; + } + ZeroMemory(&g_d3dpp, sizeof(g_d3dpp)); + g_d3dpp.Windowed = TRUE; + g_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + g_d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; + g_d3dpp.EnableAutoDepthStencil = TRUE; + g_d3dpp.AutoDepthStencilFormat = D3DFMT_D16; + //g_d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + + // Enable VSYNC + g_d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; + g_d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; + + // Create the D3DDevice + if (pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_d3dpp, &g_pd3dDevice) < 0) + { + pD3D->Release(); + UnregisterClass(_T("ImGui Example"), wc.hInstance); + return 0; + } + + // Show the window + ShowWindow(hwnd, SW_SHOWDEFAULT); + UpdateWindow(hwnd); + + // Setup ImGui binding + ImGui_ImplDX9_Init(hwnd, g_pd3dDevice); + + bool show_another_window = true; + ImVec4 clear_col = ImColor(114, 144, 154); + + int moveOffsetX = 0; + int moveOffsetY = 0; + + ImGui::GetStyle().WindowRounding = 0.0f; + + // Main loop + MSG msg; + ZeroMemory(&msg, sizeof(msg)); + while (msg.message != WM_QUIT) + { + if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + continue; + } + + ImGui_ImplDX9_NewFrame(); + + if (ImGui::IsMouseClicked(0)) + { + POINT point; + RECT rect; + + GetCursorPos(&point); + GetWindowRect(hwnd, &rect); + + // Calculate the difference between the cursor pos and window pos + moveOffsetX = point.x - rect.left; + moveOffsetY = point.y - rect.top; + } + + float windowWidth = 1920.0f; + float windowHeight = 1080.0f; + + float titlebarHeight = 30.0f; + + ImGui::GetStyle().Colors[ImGuiCol_Button] = ImVec4(.92f, .92f, .92f, 1.0f); + ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered] = ImVec4(.90f, .945f, .988f, 1.0f); + + + ImGui::GetStyle().WindowMinSize = ImVec2(10, 10); + ImGui::GetStyle().Colors[ImGuiCol_MenuBarBg] = ImVec4(.96f, .96f, .96f, 1.0f); + ImGui::GetStyle().Colors[ImGuiCol_Text] = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + ImGui::GetStyle().Colors[ImGuiCol_WindowBg] = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + + // 2. Show another simple window, this time using an explicit Begin/End pair + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(.16f, .55f, .84f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); + ImGui::Begin("Another Window", &show_another_window, + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoInputs | + ImGuiWindowFlags_ShowBorders); + { + ImGui::SetWindowPos(ImVec2(0, 0), ImGuiSetCond_Once); + ImGui::SetWindowSize(ImVec2(windowWidth, windowHeight), ImGuiSetCond_Once); + + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); // White + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); // Black + ImGui::Button("Test Program", ImVec2(windowWidth, titlebarHeight)); + ImGui::PopStyleColor(2); + + if ((moveOffsetY >= 0 && moveOffsetY <= (int)titlebarHeight) && // Cursor must be *on* the titlebar + ImGui::IsMouseDragging()) // User must drag mouse (hold LMB) + { + POINT point; + GetCursorPos(&point); + + SetWindowPos(hwnd, nullptr, point.x - moveOffsetX, point.y - moveOffsetY, 0, 0, SWP_NOSIZE | SWP_NOZORDER); + } + } + ImGui::End(); + ImGui::PopStyleColor(2); + ImGui::PopStyleVar(); + + ImGui::Begin("keking", &show_another_window, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_ShowBorders); + { + ImGui::SetWindowPos(ImVec2(1, titlebarHeight - 1), ImGuiSetCond_Once); + ImGui::SetWindowSize(ImVec2(windowWidth - 2, windowHeight - titlebarHeight), ImGuiSetCond_Once); + + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + ImGui::MenuItem("View Game Directory", "F5"); + ImGui::MenuItem("Exit", "Alt+F4"); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Docs")) + { + ImGui::MenuItem("Maya CoDTools"); + ImGui::MenuItem("Exporter Tutorial"); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Tools")) + { + ImGui::MenuItem("New Mod", "F8"); + ImGui::MenuItem("Maya Plugin Setup"); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Help")) + { + ImGui::MenuItem("Wiki"); + ImGui::EndMenu(); + } + + ImGui::EndMenuBar(); + } + + ImGui::PushClipRect(ImVec2(1, titlebarHeight - 1), ImVec2(windowWidth - 1, windowHeight - titlebarHeight), false); + ImGui::SetCursorPos(ImVec2(0, 20)); + ImGui::Separator(); + ImGui::PopClipRect(); + + ImGui::BeginGroup(); + { + ImGui::Text(""); + ImGui::NewLine(); + + ImGui::Text("Game"); + ImGui::BeginChild("launch pane", ImVec2(150, 104), true, ImGuiWindowFlags_AlwaysAutoResize); + { + bool dothing; + ImGui::Checkbox("Developer", &dothing); + bool dothing2; + ImGui::Checkbox("Logfile", &dothing2); + + ImGui::Button("Singleplayer", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)); + ImGui::Button("Multiplayer", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)); + } + ImGui::EndChild(); + + ImGui::NewLine(); + + ImGui::Text("Tools"); + ImGui::BeginChild("tools pane", ImVec2(150, 127), true); + { + ImGui::Button("Effects Editor", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)); + ImGui::Button("Asset Manager", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)); + ImGui::Button("Asset Viewer", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)); + ImGui::Button("Converter", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)); + ImGui::Button("Radiant", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)); + } + ImGui::EndChild(); + + ImGui::NewLine(); + + ImGui::Text("Processes"); + ImGui::BeginChild("processes pane", ImVec2(150, 500), true); + { + } + ImGui::EndChild(); + } + ImGui::EndGroup(); ImGui::SameLine(); + + ImGui::BeginGroup(); + { + static const char* tabNames[] = { "Mod Builder", "Level", "Explore" }; + static int tabOrder[] = { 0, 1, 2 }; + static int tabSelected = 0; + const bool tabChanged = false; // ImGui::TabLabels(tabNames, ARRAYSIZE(tabNames), tabSelected, tabOrder); + + if (tabSelected == 0) + { + ImGui::BeginGroup(); + ImGui::BeginChild("##unnamed", ImVec2(1000, 500), true); + { + ImGui::BeginGroup(); + ImGui::Text("Mod"); + ImGui::BeginChild("##unnamed3", ImVec2(500, 0), true); + { + ImGui::Text("Fastfile Zone Source"); + ImGui::BeginChild("##unnamed2", ImVec2(0, 58), true); + { + ImGui::Button("Edit Zone Source", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)); + ImGui::Button("Missing Assets", ImVec2(ImGui::GetContentRegionAvailWidth() / 2, 0)); ImGui::SameLine(); + ImGui::Button("Zone Source", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)); + } + ImGui::EndChild(); + + ImGui::Text("Build Mod"); + ImGui::BeginChild("##unnamed4", ImVec2(0, 138), true); + { + bool linkFF; + bool buildIWD; + bool verbose; + + ImGui::Checkbox("Link Fastfile", &linkFF); ImGui::SameLine(); + ImGui::Checkbox("Build IWD", &buildIWD); + ImGui::Checkbox("Verbose (More detailed information)", &verbose); + + ImGui::NewLine(); + + char buf[256]; + ImGui::Text("Custom Linker Options"); + ImGui::InputText("##option_input", buf, 256); + + ImGui::Button("Build Mod", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)); + } + ImGui::EndChild(); + + ImGui::Text("Mod Folder"); + ImGui::BeginChild("##unnamed5", ImVec2(0, 35), true); + { + ImGui::Button("View Mod", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)); + } + ImGui::EndChild(); + } + ImGui::EndChild(); + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + ImGui::Text("IWD File List"); + ImGui::BeginChild("##unnamed6", ImVec2(0, 0), true); + { + bool v = false; + ImGui::Checkbox("##test1", &v); ImGui::SameLine(); + if (ImGui::TreeNodeEx("images", ImGuiTreeNodeFlags_DefaultOpen)) + { + ImGui::Checkbox("##test2", &v); ImGui::SameLine(); + ImGui::TreeNodeEx("$identitynormalmap.iwi", ImGuiTreeNodeFlags_Leaf); ImGui::TreePop(); + ImGui::Checkbox("##test2", &v); ImGui::SameLine(); + ImGui::TreeNodeEx("$identitynormalmap.iwi", ImGuiTreeNodeFlags_Leaf); ImGui::TreePop(); + ImGui::Checkbox("##test2", &v); ImGui::SameLine(); + ImGui::TreeNodeEx("$identitynormalmap.iwi", ImGuiTreeNodeFlags_Leaf); ImGui::TreePop(); + ImGui::Checkbox("##test2", &v); ImGui::SameLine(); + ImGui::TreeNodeEx("$identitynormalmap.iwi", ImGuiTreeNodeFlags_Leaf); ImGui::TreePop(); + ImGui::Checkbox("##test2", &v); ImGui::SameLine(); + ImGui::TreeNodeEx("$identitynormalmap.iwi", ImGuiTreeNodeFlags_Leaf); ImGui::TreePop(); + ImGui::Checkbox("##test2", &v); ImGui::SameLine(); + ImGui::TreeNodeEx("$identitynormalmap.iwi", ImGuiTreeNodeFlags_Leaf); ImGui::TreePop(); + ImGui::Checkbox("##test2", &v); ImGui::SameLine(); + ImGui::TreeNodeEx("$identitynormalmap.iwi", ImGuiTreeNodeFlags_Leaf); ImGui::TreePop(); + ImGui::Checkbox("##test2", &v); ImGui::SameLine(); + ImGui::TreeNodeEx("$identitynormalmap.iwi", ImGuiTreeNodeFlags_Leaf); ImGui::TreePop(); + ImGui::TreePop(); + } + } + ImGui::EndChild(); + ImGui::EndGroup(); + } + ImGui::EndChild(); + ImGui::EndGroup(); + } + } + ImGui::EndGroup(); + } + ImGui::End(); + + // Rendering + g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, false); + g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false); + g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, false); + D3DCOLOR clear_col_dx = D3DCOLOR_RGBA((int)(clear_col.x*255.0f), (int)(clear_col.y*255.0f), (int)(clear_col.z*255.0f), (int)(clear_col.w*255.0f)); + g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, clear_col_dx, 1.0f, 0); + if (g_pd3dDevice->BeginScene() >= 0) + { + ImGui::Render(); + g_pd3dDevice->EndScene(); + } + g_pd3dDevice->Present(NULL, NULL, NULL, NULL); + } + + ImGui_ImplDX9_Shutdown(); + if (g_pd3dDevice) g_pd3dDevice->Release(); + if (pD3D) pD3D->Release(); + UnregisterClass(_T("ImGui Example"), wc.hInstance); + + return 0; +} \ No newline at end of file diff --git a/components/launcher_ldr/launcher_ldr.cpp b/components/launcher_ldr/launcher_ldr.cpp index 97e01dd6..854b8925 100644 --- a/components/launcher_ldr/launcher_ldr.cpp +++ b/components/launcher_ldr/launcher_ldr.cpp @@ -54,7 +54,7 @@ void FixCommandLine(int argc, char *argv[]) { strcat_s(g_CommandLine, " "); - //Fix for arguments in quotes being split into multiple args + // Fix for arguments in quotes being split into multiple args char buf[8192]; if(StrContainsChar(argv[i], ' ') != -1) { @@ -83,27 +83,21 @@ void FixCommandLine(int argc, char *argv[]) void FixDirectory(int argc, char *argv[]) { - // // Get the current directory to resolve relative paths - // char temp[MAX_PATH]; sprintf_s(temp, "%s\\%s", g_Directory, argv[2]); char *filePart = nullptr; GetFullPathNameA(temp, ARRAYSIZE(temp), g_ExeDirectory, &filePart); - // - // Trim off the file name - // + // Trim off the file name part if (filePart) *filePart = '\0'; } int main(int argc, char *argv[]) { - // // Disable STDOUT buffering - // setvbuf(stdout, nullptr, _IONBF, 0); if (argc < 3) @@ -115,23 +109,13 @@ int main(int argc, char *argv[]) // // Redirect output to this console // - STARTUPINFOA startupInfo; - memset(&startupInfo, 0, sizeof(STARTUPINFOA)); - - startupInfo.cb = sizeof(STARTUPINFOA); - startupInfo.hStdError = stderr; - startupInfo.hStdInput = stdin; - startupInfo.hStdOutput = stdout; - - // - // Process/thread handles - // PROCESS_INFORMATION processInfo; memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); - // + STARTUPINFOA startupInfo; + memset(&startupInfo, 0, sizeof(STARTUPINFOA)); + // Create a process job object to kill children on exit - // HANDLE ghJob = CreateJobObject(nullptr, nullptr); if (ghJob == nullptr) @@ -144,9 +128,7 @@ int main(int argc, char *argv[]) JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; memset(&info, 0, sizeof(info)); - // - // Query the information first - // + // Query original information first if (!QueryInformationJobObject(ghJob, JobObjectExtendedLimitInformation, &info, sizeof(info), nullptr)) { printf("Could not QueryInformationJobObject\n"); @@ -166,9 +148,7 @@ int main(int argc, char *argv[]) } } - // // Call the real process - // FixCommandLine(argc, argv); FixDirectory(argc, argv); @@ -178,18 +158,14 @@ int main(int argc, char *argv[]) return 1; } - // // Assign the job object - // if (!AssignProcessToJobObject(ghJob, processInfo.hProcess)) { printf("Unable to assign child process job object (0x%X)\n", GetLastError()); return 1; } - // // Inject the DLL - // HANDLE injectThread = InjectDll(processInfo.hProcess, g_Directory, argv[1]); if (!injectThread) @@ -198,39 +174,26 @@ int main(int argc, char *argv[]) return 1; } - // - // Resume the process - // + // Resume injection thread, then the process ResumeThread(injectThread); WaitForSingleObject(injectThread, INFINITE); ResumeThread(processInfo.hThread); - // - // Close thread handles - // CloseHandle(processInfo.hThread); CloseHandle(injectThread); - // - // Wait until the process exits - // + // Wait until it exits WaitForSingleObject(processInfo.hProcess, INFINITE); - // // Determine the real process exit code - // DWORD exitCode = 0; GetExitCodeProcess(processInfo.hProcess, &exitCode); - //printf("Exited with code: 0x%X\n",exitCode); - CloseHandle(processInfo.hProcess); CloseHandle(ghJob); - // // Send the exit code to the caller - // ExitProcess(exitCode); return exitCode; } \ No newline at end of file diff --git a/components/launcher_ldr/launcher_ldr.vcxproj b/components/launcher_ldr/launcher_ldr.vcxproj index 46d6da01..9e155529 100644 --- a/components/launcher_ldr/launcher_ldr.vcxproj +++ b/components/launcher_ldr/launcher_ldr.vcxproj @@ -22,15 +22,15 @@ Application true - v120_xp Unicode + v120_xp Application false - v120_xp true Unicode + v120_xp diff --git a/components/launcher_ldr/loader.cpp b/components/launcher_ldr/loader.cpp index 4d4642d8..c76380b9 100644 --- a/components/launcher_ldr/loader.cpp +++ b/components/launcher_ldr/loader.cpp @@ -12,9 +12,7 @@ LPVOID GetLoadLibraryAddr() HANDLE InjectDll(HANDLE ProcessHandle, const char *Path, const char *Module) { - // // Allocate memory for the string buffer - // LPVOID memory = VirtualAllocEx(ProcessHandle, nullptr, 0x1000, MEM_COMMIT, PAGE_READWRITE); if (!memory) @@ -23,18 +21,14 @@ HANDLE InjectDll(HANDLE ProcessHandle, const char *Path, const char *Module) return nullptr; } - // // Write to the remote buffer - // char dllBuffer[MAX_PATH]; sprintf_s(dllBuffer, "%s\\%s", Path, Module); DWORD bytesWritten = 0; WriteProcessMemory(ProcessHandle, memory, (LPVOID)&dllBuffer, strlen(dllBuffer) + 1, &bytesWritten); - // - // Execute LoadLibrary - // + // Execute LoadLibrary (thread is suspended) HANDLE remoteThread = CreateRemoteThread(ProcessHandle, nullptr, 0, (LPTHREAD_START_ROUTINE)GetLoadLibraryAddr(), memory, CREATE_SUSPENDED, nullptr); if (!remoteThread) diff --git a/components/linker_pc/T5.h b/components/linker_pc/T5.h index 9d3ab321..c055c8b7 100644 --- a/components/linker_pc/T5.h +++ b/components/linker_pc/T5.h @@ -1,10 +1,19 @@ #pragma once +enum ConChannel +{ + DEFAULT = 0, + VERBOSE = 2, +}; + typedef void (__cdecl * Com_Printf_t)(int level, const char *fmt, ...); -static Com_Printf_t Com_Printf = (Com_Printf_t)0x416B30; + +// level must be 2 for it to only print when -verbose is used +static Com_Printf_t Com_Printf = (Com_Printf_t)0x00407300; typedef void (__cdecl * Com_Error_t)(int code, const char *fmt, ...); static Com_Error_t Com_Error = (Com_Error_t)0x416810; +static Com_Printf_t Com_PrintError = (Com_Printf_t)0x416B30; typedef int (__cdecl * FS_FOpenFile_t)(const char *qpath, int *fileHandle); static FS_FOpenFile_t FS_FOpenFile = (FS_FOpenFile_t)0x4C9280; diff --git a/components/linker_pc/assertive.cpp b/components/linker_pc/assertive.cpp new file mode 100644 index 00000000..5b868435 --- /dev/null +++ b/components/linker_pc/assertive.cpp @@ -0,0 +1,13 @@ +#include "stdafx.h" + +bool Assert_MyHandler(const char *filename, int line, int type, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + char buffer[4096]; + _vsnprintf_s(buffer, _TRUNCATE, fmt, ap); + + shared_assert(filename, line, buffer); + return true; +} \ No newline at end of file diff --git a/components/linker_pc/assertive.h b/components/linker_pc/assertive.h new file mode 100644 index 00000000..3babc794 --- /dev/null +++ b/components/linker_pc/assertive.h @@ -0,0 +1,3 @@ +#pragma once + +bool Assert_MyHandler(const char *filename, int line, int type, const char *fmt, ...); \ No newline at end of file diff --git a/components/linker_pc/com_files.cpp b/components/linker_pc/com_files.cpp index 716e3e5b..bb108e1a 100644 --- a/components/linker_pc/com_files.cpp +++ b/components/linker_pc/com_files.cpp @@ -1,5 +1,12 @@ #include "stdafx.h" +struct searchpath_s; + +VANILLA_VALUE(fs_searchpaths, searchpath_s*, 0x011CB4C4); + +typedef const char **(__cdecl* FS_ListFilteredFiles_t)(searchpath_s *searchPath, const char *path, const char *extension, const char *filter, FsListBehavior_e behavior, int *numfiles, int allocTrackType); +static FS_ListFilteredFiles_t FS_ListFilteredFiles = (FS_ListFilteredFiles_t)0x004C7D50; + FILE** fDeps = (FILE**)0x010133C8; void* sub_41DDD0 = (void*)0x0041DDD0; @@ -18,4 +25,15 @@ void __declspec(naked) mfh_fcloseDeps() push ebx jmp rtn_fcloseDeps } +} + +const char ** FS_ListFiles(const char *path, const char *extension, FsListBehavior_e behavior, int *numfiles, int allocTrackType) +{ + return FS_ListFilteredFiles(fs_searchpaths, path, extension, 0, behavior, numfiles, allocTrackType); +} + +void FS_FreeFileList(const char **list, int allocTrackType) +{ + if (list) + Hunk_UserDestroy((HunkUser *)*(list - 1)); } \ No newline at end of file diff --git a/components/linker_pc/com_files.h b/components/linker_pc/com_files.h index 2e914564..b39fc556 100644 --- a/components/linker_pc/com_files.h +++ b/components/linker_pc/com_files.h @@ -2,7 +2,16 @@ #include +enum FsListBehavior_e +{ + FS_LIST_PURE_ONLY = 0x0, + FS_LIST_ALL = 0x1, +}; + typedef int __cdecl fclose_t(FILE *File); static fclose_t* o_fclose = (fclose_t*)0x004F3C80; -void mfh_fcloseDeps(); \ No newline at end of file +void mfh_fcloseDeps(); + +const char ** FS_ListFiles(const char *path, const char *extension, FsListBehavior_e behavior, int *numfiles, int allocTrackType); +void FS_FreeFileList(const char **list, int allocTrackType=0); diff --git a/components/linker_pc/com_memory.h b/components/linker_pc/com_memory.h index 91d0cf29..3397b9c0 100644 --- a/components/linker_pc/com_memory.h +++ b/components/linker_pc/com_memory.h @@ -1,5 +1,7 @@ #pragma once +struct HunkUser; + typedef void *(__cdecl * Z_Malloc_t)(int size); static Z_Malloc_t Z_Malloc = (Z_Malloc_t)0x4C5B20; @@ -19,4 +21,7 @@ typedef bool (__cdecl * Hunk_CheckTempMemoryHighClear_t)(); static Hunk_CheckTempMemoryHighClear_t Hunk_CheckTempMemoryHighClear = (Hunk_CheckTempMemoryHighClear_t)0x4C5330; typedef void (__cdecl * Hunk_FreeTempMemory_t)(void *buf); -static Hunk_FreeTempMemory_t Hunk_FreeTempMemory = (Hunk_FreeTempMemory_t)0x4C5F90; \ No newline at end of file +static Hunk_FreeTempMemory_t Hunk_FreeTempMemory = (Hunk_FreeTempMemory_t)0x4C5F90; + +typedef void (__cdecl* Hunk_UserDestroy_t)(HunkUser *user); +static Hunk_UserDestroy_t Hunk_UserDestroy = (Hunk_UserDestroy_t)0x004D37A0; diff --git a/components/linker_pc/dllmain.cpp b/components/linker_pc/dllmain.cpp index a5a9a8ef..0c0045f3 100644 --- a/components/linker_pc/dllmain.cpp +++ b/components/linker_pc/dllmain.cpp @@ -1,4 +1,8 @@ +#define G_VERSION 1, 0, 0 #include "stdafx.h" +#include "r_material_load_obj.h" + +#define FIND_STATEMAPS 1 LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) { @@ -29,7 +33,7 @@ BOOL LinkerMod_Init() SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); - Detours::X86::DetourFunction((PBYTE)0x004C3D30, (PBYTE)assert); + Detours::X86::DetourFunction((PBYTE)0x004C3D30, (PBYTE)Assert_MyHandler); Detours::X86::DetourFunction((PBYTE)0x00401940, (PBYTE)Com_LoadBsp); Detours::X86::DetourFunction((PBYTE)0x004A6D20, (PBYTE)Path_LoadPaths); @@ -52,6 +56,25 @@ BOOL LinkerMod_Init() const char* msg_assertion = "expected 'constant' or 'material', found '%s' instead\n"; PatchMemory(0x00480D10, (PBYTE)&msg_assertion, 4); + // + // Prevent "_acess" spam + // + PatchMemory_WithNOP(0x004C6F9B, 5); + + // + // Prevent dropping pathnodes if a custom ents file is loaded + // + Detours::X86::DetourFunction((PBYTE)0x00420493, (PBYTE)&mfh_MapEnts_ParseEntities); + +#if FIND_STATEMAPS + // + // Custom material loading code for bruteforcing statemaps for missing techniques + // + Detours::X86::DetourFunction((PBYTE)0x004853C0, (PBYTE)&Material_LoadRaw); + + PatchCall(0x0047FA0E, (PBYTE)&hk_Material_RegisterStateMap); +#endif + g_initted = TRUE; return TRUE; } diff --git a/components/linker_pc/linker.cpp b/components/linker_pc/linker.cpp new file mode 100644 index 00000000..09cabe00 --- /dev/null +++ b/components/linker_pc/linker.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" + +// 0x00420493 +void __declspec(naked) mfh_MapEnts_ParseEntities() +{ + // Drop the node if no custom ents file is present + _asm + { + call MapEnts_CustomEntsFile_IsPresent + test eax, eax + jz DROP_NODE + +// LOAD_NODE: + mov eax, 0x00420499 + jmp eax + +DROP_NODE: + mov eax, 0x0042083C + jmp eax + } +} \ No newline at end of file diff --git a/components/linker_pc/linker.h b/components/linker_pc/linker.h new file mode 100644 index 00000000..ffbb3999 --- /dev/null +++ b/components/linker_pc/linker.h @@ -0,0 +1,6 @@ +#pragma once + +typedef bool(__cdecl *MapEnts_CustomEntsFile_IsPresent_t)(); +static MapEnts_CustomEntsFile_IsPresent_t MapEnts_CustomEntsFile_IsPresent = (MapEnts_CustomEntsFile_IsPresent_t)0x0041F6E0; + +void mfh_MapEnts_ParseEntities(); diff --git a/components/linker_pc/linker_pc.vcxproj b/components/linker_pc/linker_pc.vcxproj index 6527d7f5..2b25af03 100644 --- a/components/linker_pc/linker_pc.vcxproj +++ b/components/linker_pc/linker_pc.vcxproj @@ -21,15 +21,15 @@ DynamicLibrary true - v120_xp Unicode + v120_xp DynamicLibrary false - v120_xp true Unicode + v120_xp @@ -89,17 +89,21 @@ + + + + @@ -112,12 +116,19 @@ + + Create Create + + + {5fb372dd-a7e1-4a4b-9a28-3ac02543e9e8} + + diff --git a/components/linker_pc/r_material_load_obj.cpp b/components/linker_pc/r_material_load_obj.cpp new file mode 100644 index 00000000..4833aed4 --- /dev/null +++ b/components/linker_pc/r_material_load_obj.cpp @@ -0,0 +1,533 @@ +#include "stdafx.h" +#include "r_material_load_obj.h" + +#include + +std::vector g_stateMapCache; + +enum +{ + SAMPLER_FILTER_SHIFT = 0x0, + SAMPLER_FILTER_NEAREST = 0x1, + SAMPLER_FILTER_LINEAR = 0x2, + SAMPLER_FILTER_ANISO2X = 0x3, + SAMPLER_FILTER_ANISO4X = 0x4, + SAMPLER_FILTER_MASK = 0x7, + SAMPLER_MIPMAP_SHIFT = 0x3, + SAMPLER_MIPMAP_DISABLED = 0x0, + SAMPLER_MIPMAP_NEAREST = 0x8, + SAMPLER_MIPMAP_LINEAR = 0x10, + SAMPLER_MIPMAP_COUNT = 0x3, + SAMPLER_MIPMAP_MASK = 0x18, + SAMPLER_CLAMP_U_SHIFT = 0x5, + SAMPLER_CLAMP_V_SHIFT = 0x6, + SAMPLER_CLAMP_W_SHIFT = 0x7, + SAMPLER_CLAMP_U = 0x20, + SAMPLER_CLAMP_V = 0x40, + SAMPLER_CLAMP_W = 0x80, + SAMPLER_CLAMP_MASK = 0xE0, + SAMPLER_ANISO_SHIFT = 0x8, + SAMPLER_ANISO_1X = 0x0, + SAMPLER_ANISO_2X = 0x100, + SAMPLER_ANISO_4X = 0x200, + SAMPLER_ANISO_6X = 0x300, + SAMPLER_ANISO_8X = 0x400, + SAMPLER_ANISO_10X = 0x500, + SAMPLER_ANISO_12X = 0x600, + SAMPLER_ANISO_16X = 0x700, + SAMPLER_ANISO_MASK = 0x700, + SAMPLER_CONVOLUTION = 0x20000, + SAMPLER_GAMMA = 0x40000, + SAMPLER_UNNORMALIZED_UV = 0x80000, + SAMPLER_DIRECT_FILTER_UNNORMALIZED = 0x80000, +}; + +struct MaterialTypeInfo +{ + const char *prefix; + const char *techniqueSetPrefix; + unsigned int prefixLen; +}; + +struct MaterialMetaStateMap : public MaterialStateMap +{ + MaterialMetaStateMap(const char* _name) + { + name = _strdup(_name); + for (int i = 0; i < 10; i++) + { + ruleSet[i] = new MaterialStateMapRuleSet; + memset(ruleSet[i], 0, sizeof(MaterialStateMapRuleSet)); + ruleSet[i]->ruleCount = 1; + } + } + + ~MaterialMetaStateMap(void) + { + free((void*)name); + for (int i = 0; i < 10; i++) + { + delete ruleSet[i]; + } + } +}; + +static MaterialTypeInfo* g_materialTypeInfo = (MaterialTypeInfo*)0x005717DC; +static MaterialStateMap** smHashTable = (MaterialStateMap**)0x011685F0; + + +water_t * Material_RegisterWaterImage(struct MaterialWaterDef *water) +{ + static DWORD dwCall = 0x00484B40; + + water_t* result = NULL; + + _asm + { + pushad + mov esi, water + call dwCall + mov result, eax + popad + } + + return result; +} + +bool Material_FinishLoadingInstance(MaterialRaw *mtlRaw, const char *techniqueSetVertDeclPrefix, MaterialTechniqueSet **techniqueSet, unsigned int materialType) +{ + static DWORD dwCall = 0x00484C70; + + bool result=false; + + _asm + { + pushad + push techniqueSetVertDeclPrefix + mov edx, materialType + mov esi, mtlRaw + mov ecx, techniqueSet + call dwCall + mov result, al + add esp, 4 + popad + } + + return result; +} + +MaterialStateMap *Material_FindStateMap(const char *name) +{ + unsigned int hashIndex; + + if (Material_HashStateMap(name, &hashIndex)) + return smHashTable[hashIndex]; + + return NULL; +} + +void Material_SetStateMap(const char *name, MaterialStateMap *stateMap) +{ + unsigned int hashIndex; + Material_HashStateMap(name, &hashIndex); + smHashTable[hashIndex] = stateMap; +} + +MaterialStateMap *__cdecl Material_RegisterStateMap(const char *name) +{ +#if STATEMAP_PRECACHE_ALL + static bool needToCache = true; + if (needToCache) + { + needToCache = false; + + int fileCount = 0; + const char** files = FS_ListFiles("statemaps", "sm", FS_LIST_ALL, &fileCount, 11); + + if (fileCount == 0) + Com_Error(DEFAULT, "ERROR: Unable to cache statemaps - no statemaps found!"); + + Com_Printf(VERBOSE, "Attempting to cache %d statemaps...\n", fileCount); + + int successCount = 0; + for (int i = 0; i < fileCount; i++) + { + int len = strlen(files[i]); + char* str = (char*)files[i]; + str[len - 3] = '\0'; + MaterialStateMap* sm = Material_RegisterStateMap(str); + if (sm) + { + g_stateMapCache.push_back(sm); + successCount++; + } + else + Com_PrintError(DEFAULT, "^3WARNING: Unable to cache statemap '%s'\n", str); + } + + FS_FreeFileList(files, 0); + + Com_Printf(VERBOSE, "Cached %d of %d statemaps\n", successCount, fileCount); + } +#endif + +#if TECHNIQUE_FIND_STATEMAPS + // Pass a dummy meta-statemap to be handled by Material_LoadRaw + if (*name == '<') + { + return (MaterialStateMap*)new MaterialMetaStateMap(name); + } +#endif + + MaterialStateMap *stateMap = Material_FindStateMap(name); + if (stateMap) + { + return stateMap; + } + + stateMap = Material_LoadStateMap(name); + if (stateMap) + { + Material_SetStateMap(name, stateMap); + return stateMap; + } + + return NULL; +} + +void __declspec(naked) hk_Material_RegisterStateMap(void) +{ + _asm + { + push edi // name + call Material_RegisterStateMap + add esp, 4 + retn + } +} + +unsigned int __cdecl Material_GetTechniqueSetDrawRegion(MaterialTechniqueSet *techniqueSet) +{ + int cameraRegion; + + ASSERT(techniqueSet); + + if (techniqueSet->techniques[10]) + cameraRegion = 0; + else if (techniqueSet->techniques[5]) + cameraRegion = 2; + else + cameraRegion = 3; + return cameraRegion; +} + +void Material_SetMaterialDrawRegion(Material *material) +{ + unsigned int cameraRegion = Material_GetTechniqueSetDrawRegion(material->localTechniqueSet); + if (!cameraRegion && material->info.sortKey >= 24) + cameraRegion = 1; + material->cameraRegion = (char)cameraRegion; +} + +//bool Material_BuildStateBitsForTarget(Material* material) +//{ +// +//} +class GfxStateBitsTargetData +{ +private: + int techType; + Material* material; + + GfxStateBits targetBits; + + // The index of the current statemap we're using in g_stateMapCache + int stateMapIndex; + +public: + GfxStateBitsTargetData(Material* mtl, int techniqueType, GfxStateBits& targetStateBits) + { + this->material = mtl; + this->techType = techniqueType; + this->targetBits = targetStateBits; + + stateMapIndex = -1; + } + + GfxStateBits StateBits(void) const + { + int entry = material->stateBitsEntry[techType]; + return material->stateBitsTable[entry]; + } + + GfxStateBits TargetBits(void) const + { + return targetBits; + } + + MaterialTechnique* Technique(void) const + { + if (!material->techniqueSet) + return NULL; + + return material->techniqueSet->techniques[techType]; + } + + MaterialStateMap** StateMap(void) const + { + if (!material->techniqueSet) + return NULL; + + MaterialTechnique* tech = this->Technique(); + return (MaterialStateMap**)&tech->passArray[tech->passCount]; + } + + bool IsValid(void) + { + int entry = material->stateBitsEntry[techType]; + GfxStateBits& stateBits = material->stateBitsTable[entry]; + + if (stateBits.loadBits[0] == targetBits.loadBits[0] && + stateBits.loadBits[1] == targetBits.loadBits[1]) + return true; + + return false; + } + + bool TryNextStateMap(void) + { + // We should really do some sort of cleanup - but we need to be careful + // because other techniques might? use the same statemap pointer... + if (stateMapIndex == -1) + { + delete (MaterialMetaStateMap*)*this->StateMap(); + } + + stateMapIndex++; + + if ((unsigned int)stateMapIndex >= g_stateMapCache.size()) + return false; + + *this->StateMap() = g_stateMapCache[stateMapIndex]; + return true; + } +}; + +void Material_BuildStateBits_Regen(Material* material, MaterialRaw* mtlRaw) +{ + Material_BuildStateBitsTable(material, mtlRaw->info.toolFlags, mtlRaw->refStateBits); + + std::vector targets; + for (int techType = 0; techType < 130; techType++) + { + if (material->techniqueSet->techniques[techType] == NULL) + continue; + + MaterialTechnique* tech = material->techniqueSet->techniques[techType]; + MaterialStateMap ** stateMapTable = (MaterialStateMap **)&tech->passArray[tech->passCount]; + + if (*stateMapTable[0]->name != '<') + continue; + + // Extract the target statebit values from the string + GfxStateBits targetBits; +#pragma warning( push ) +#pragma warning( disable : 4996 ) + sscanf(stateMapTable[0]->name, "%*c%i%*c%i%*c", &targetBits.loadBits[0], &targetBits.loadBits[1]); +#pragma warning( pop ) + + unsigned int entry = material->stateBitsEntry[techType]; + GfxStateBits* stateBits = material->stateBitsTable; + + GfxStateBitsTargetData targetData(material, techType, targetBits); + + if (targetData.IsValid()) + continue; + + // Statbits mismatch - add to the vector + targets.push_back(targetData); + } + + // Try a different statemap for each technique with mismatched bits + // Once we find one with a valid match we print it and remove it from list + while (targets.size() != 0) + { + for (unsigned int i = 0; i < targets.size(); i++) + { + if (!targets[i].TryNextStateMap()) + { + Com_PrintError(0, "Unable to resolve statemap for technique '%s'", targets[i].Technique()->name); + targets.erase(targets.begin() + i--); + } + } + + Material_BuildStateBitsTable(material, mtlRaw->info.toolFlags, mtlRaw->refStateBits); + + for (unsigned int i = 0; i < targets.size(); i++) + { + if (targets[i].IsValid()) + { + Com_Printf(0, "^5Tech: '%s': StateMap: '%s'!\n", targets[i].Technique()->name, targets[i].StateMap()[0]->name); + + //printf("TARGET: 0x%X, 0x%X\n", targets[i].TargetBits().loadBits[0], targets[i].TargetBits().loadBits[1]); + //printf("CURRENT: 0x%X, 0x%X\n", targets[i].StateBits().loadBits[0], targets[i].StateBits().loadBits[1]); + + targets.erase(targets.begin() + i--); + } + } + } +} + +Material *__cdecl Material_LoadRaw(MaterialRaw *mtlRaw, unsigned int materialType) +{ + MaterialTechniqueSet *techniqueSet; + if (Material_FinishLoadingInstance( + mtlRaw, + g_materialTypeInfo[materialType].techniqueSetPrefix, + &techniqueSet, + materialType)) + { + char* name = (char *)mtlRaw + mtlRaw->info.nameOffset; + unsigned int prefixLen = g_materialTypeInfo[materialType].prefixLen; + unsigned int nameLen = strlen(name); + Material* materialMem = (Material *)Material_Alloc(prefixLen + nameLen + 1 + 192); + Material* material = materialMem; + char* strDest = (char *)&materialMem[1]; + memcpy((char *)&materialMem[1], (char *)g_materialTypeInfo[materialType].prefix, prefixLen); + memcpy(&strDest[prefixLen], name, nameLen + 1); + material->info.name = strDest; + + //ASSERT(!material->info.drawSurf.fields.materialSortedIndex); + //ASSERT(!((unsigned __int64)material->info.drawSurf.fields._bf0 >> 31) & 0xFFF); + + material->info.gameFlags = mtlRaw->info.gameFlags; + unsigned int surfIndex = (unsigned __int8)((mtlRaw->info.surfaceFlags & 0x3F00000) >> 20); + + if ((unsigned __int8)((mtlRaw->info.surfaceFlags & 0x3F00000) >> 20)) + { + material->info.surfaceTypeBits = 1 << (surfIndex - 1); + material->info.layeredSurfaceTypes = surfIndex; + } + else + { + material->info.surfaceTypeBits = 0; + material->info.layeredSurfaceTypes = 0; + } + + material->info.layeredSurfaceTypes |= 0x20000000u; + + if (materialType != 3 && materialType != 4) + material->info.gameFlags &= 0xFFFFFFFD; // disable bit 2 + + material->info.sortKey = mtlRaw->info.sortKey; + material->info.textureAtlasRowCount = mtlRaw->info.textureAtlasRowCount; + material->info.textureAtlasColumnCount = mtlRaw->info.textureAtlasColumnCount; + material->textureCount = (BYTE)mtlRaw->textureCount; + material->constantCount = (BYTE)mtlRaw->constantCount; + material->localTechniqueSet = techniqueSet; + + if (mtlRaw->textureCount) + { + material->textureTable = (MaterialTextureDef *)Material_Alloc(16 * mtlRaw->textureCount); + MaterialTextureDefRaw* textureTableRaw = (MaterialTextureDefRaw *)((char *)mtlRaw + mtlRaw->textureTableOffset); + int texIndex = 0; + for (unsigned int texIndexIn = 0; texIndexIn < mtlRaw->textureCount; ++texIndexIn) + { + char* tableEntryName = (char *)mtlRaw + textureTableRaw[texIndexIn].nameOffset; + + material->textureTable[texIndex].nameHash = R_HashString(tableEntryName); + material->textureTable[texIndex].nameStart = *tableEntryName; + material->textureTable[texIndex].nameEnd = tableEntryName[strlen(tableEntryName) - 1]; + material->textureTable[texIndex].samplerState = textureTableRaw[texIndexIn].samplerState; + + ASSERT(material->textureTable[texIndex].samplerState & SAMPLER_FILTER_MASK); + + material->textureTable[texIndex].semantic = textureTableRaw[texIndexIn].semantic; + material->textureTable[texIndex].isMatureContent = 0; + + if (material->textureTable[texIndex].semantic == 11) + { + material->textureTable[texIndex++].u.image = (GfxImage *)Material_RegisterWaterImage((MaterialWaterDef *)((char *)mtlRaw + textureTableRaw[texIndexIn].u.imageNameOffset)); + } + else if (material->textureTable[texIndex].semantic != 28) + { + char* imageName = (char *)mtlRaw + textureTableRaw[texIndexIn].u.imageNameOffset; + material->textureTable[texIndex].u.image = Image_Register(imageName, textureTableRaw[texIndexIn].semantic); + material->textureTable[texIndex++].isMatureContent = MaterialTexture_IsMatureContent( material->textureTable[texIndexIn].semantic, imageName); + } + } + material->textureCount = texIndex; + } + + if (mtlRaw->constantCount) + { + material->localConstantTable = (MaterialConstantDef *)Material_Alloc(32 * mtlRaw->constantCount); + MaterialConstantDefRaw* constantTableRaw = (MaterialConstantDefRaw *)((char *)mtlRaw + mtlRaw->constantTableOffset); + + for (unsigned int constIndex = 0; constIndex < mtlRaw->constantCount; ++constIndex) + { + char* constName = (char *)mtlRaw + constantTableRaw[constIndex].nameOffset; + material->localConstantTable[constIndex].nameHash = R_HashString(constName); + +#pragma warning( push ) +#pragma warning( disable : 4996 ) + strncpy(material->localConstantTable[constIndex].name, constName, 12); +#pragma warning( pop ) + + float* _literal = material->localConstantTable[constIndex]._literal; + float* literal_raw = constantTableRaw[constIndex].literal; + _literal[0] = literal_raw[0]; + _literal[1] = literal_raw[1]; + _literal[2] = literal_raw[2]; + _literal[3] = literal_raw[3]; + } + } + + Material_BuildStateBitsTable(material, mtlRaw->info.toolFlags, mtlRaw->refStateBits); + +#if TECHNIQUE_FIND_STATEMAPS + Material_BuildStateBits_Regen(material, mtlRaw); +#else + Material_BuildStateBitsTable(material, mtlRaw->info.toolFlags, mtlRaw->refStateBits); +#endif + + Material_SetMaterialDrawRegion(material); + + if (Material_Validate(material)) + { + ASSERT(material->techniqueSet); + ASSERT(!(material->info.gameFlags & MTL_GAMEFLAG_CASTS_SHADOW)); + + if (!(mtlRaw->info.surfaceFlags & 0x40000) + && Material_GetTechnique(material, 2) != 0 + && !(material->stateFlags & 4)) + { + material->info.gameFlags |= 0x40u; + } + if (mtlRaw->info.surfaceFlags & 0x80000 && Material_GetTechnique(material, 2) != 0 && !(material->stateFlags & 4)) + material->info.gameFlags |= 1u; + if (mtlRaw->info.surfaceFlags & 0x100) + material->info.gameFlags |= 0x80u; + if (mtlRaw->info.surfaceFlags & 0x200) + material->info.gameFlags |= 0x100u; + if (mtlRaw->info.surfaceFlags & 0x800) + material->info.gameFlags |= 0x200u; + +#if MATERIAL_LOG_STATEBITS + printf("Material: %s\n\tStateBits (%d):\n", material->info.name, material->stateBitsCount); + for (unsigned int i = 0; i < (BYTE)material->stateBitsCount; i++) + { + unsigned int entry = material->stateBitsEntry[i]; + GfxStateBits* stateBits = material->stateBitsTable; + printf("\t[%3d]: 0x%08X 0x%08X\n", stateBits[entry].loadBits[0], stateBits[entry].loadBits[1]); + } +#endif + + return material; + } + + return NULL; + } + + return NULL; +} \ No newline at end of file diff --git a/components/linker_pc/r_material_load_obj.h b/components/linker_pc/r_material_load_obj.h new file mode 100644 index 00000000..fa444740 --- /dev/null +++ b/components/linker_pc/r_material_load_obj.h @@ -0,0 +1,291 @@ +#pragma once + +#define MTL_GAMEFLAG_CASTS_SHADOW 0x40 + +struct GfxImage; +struct water_t; + +struct MaterialStateMapRule +{ + unsigned int stateBitsMask[2]; + unsigned int stateBitsValue[2]; + unsigned int stateBitsSet[2]; + unsigned int stateBitsClear[2]; +}; + +struct MaterialStateMapRuleSet +{ + int ruleCount; + MaterialStateMapRule rules[1]; +}; + +struct MaterialStateMap +{ + const char *name; + MaterialStateMapRuleSet *ruleSet[10]; +}; + +struct MaterialTechniqueSet +{ + const char *name; + char worldVertFormat; + char unused[1]; + unsigned __int16 techsetFlags; + struct MaterialTechnique *techniques[130]; +}; + +union MaterialTextureDefInfo +{ + GfxImage *image; + water_t *water; +}; + +struct MaterialTextureDef +{ + unsigned int nameHash; + char nameStart; + char nameEnd; + char samplerState; + char semantic; + char isMatureContent; + char pad[3]; + MaterialTextureDefInfo u; +}; + +struct MaterialConstantDef +{ + unsigned int nameHash; + char name[12]; + float _literal[4]; +}; + +struct GfxStateBits +{ + unsigned int loadBits[2]; +}; + +struct GfxDrawSurfFields +{ + __int64 _bf0; +}; + +union GfxDrawSurf +{ + GfxDrawSurfFields fields; + unsigned __int64 packed; +}; + +struct MaterialInfo +{ + const char *name; + unsigned int gameFlags; + char pad; + char sortKey; + char textureAtlasRowCount; + char textureAtlasColumnCount; + GfxDrawSurf drawSurf; + unsigned int surfaceTypeBits; + unsigned int layeredSurfaceTypes; + unsigned __int16 hashIndex; +}; + +#pragma pack(push, 1) +struct Material +{ + MaterialInfo info; + char stateBitsEntry[130]; + char textureCount; + char constantCount; + char stateBitsCount; + char stateFlags; + char cameraRegion; + char maxStreamedMips; + + union + { + MaterialTechniqueSet *localTechniqueSet; + MaterialTechniqueSet *techniqueSet; + }; + + MaterialTextureDef *textureTable; + + union + { + MaterialConstantDef *localConstantTable; + MaterialConstantDef *constantTable; + }; + + GfxStateBits *stateBitsTable; +}; +#pragma pack(pop) + +struct MaterialTextureDefRaw +{ + unsigned int nameOffset; + char samplerState; + char semantic; + + union + { + unsigned int imageNameOffset; + unsigned int waterDefOffset; + } u; +}; + + +struct MaterialConstantDefRaw +{ + unsigned int nameOffset; + float literal[4]; +}; + +struct MaterialInfoRaw +{ + unsigned int nameOffset; + unsigned int refImageNameOffset; + char gameFlags; + char sortKey; + char textureAtlasRowCount; + char textureAtlasColumnCount; + float maxDeformMove; + char deformFlags; + char usage; + unsigned __int16 toolFlags; + unsigned int locale; + unsigned __int16 autoTexScaleWidth; + unsigned __int16 autoTexScaleHeight; + float tessSize; + int surfaceFlags; + int contents; +}; + +struct MaterialRaw +{ + MaterialInfoRaw info; + unsigned int refStateBits[2]; + unsigned __int16 textureCount; + unsigned __int16 constantCount; + unsigned int techSetNameOffset; + unsigned int textureTableOffset; + unsigned int constantTableOffset; +}; + +struct GfxVertexShaderLoadDef +{ + unsigned int *program; + unsigned __int16 programSize; +}; + +struct MaterialVertexShaderProgram +{ + void *vs; + GfxVertexShaderLoadDef loadDef; +}; + +struct MaterialVertexShader +{ + const char *name; + MaterialVertexShaderProgram prog; +}; + +struct GfxPixelShaderLoadDef +{ + unsigned int *program; + unsigned __int16 programSize; +}; + +struct MaterialPixelShaderProgram +{ + void *ps; + GfxPixelShaderLoadDef loadDef; +}; + +struct MaterialPixelShader +{ + const char *name; + MaterialPixelShaderProgram prog; +}; + +struct MaterialArgumentCodeConst +{ + unsigned __int16 index; + char firstRow; + char rowCount; +}; + +union MaterialArgumentDef +{ + const float *literalConst; + MaterialArgumentCodeConst codeConst; + unsigned int codeSampler; + unsigned int nameHash; +}; + +struct MaterialShaderArgument +{ + unsigned __int16 type; + unsigned __int16 dest; + MaterialArgumentDef u; +}; + +struct MaterialPass +{ + struct MaterialVertexDeclaration *vertexDecl; + MaterialVertexShader *vertexShader; + + union + { + MaterialPixelShader *pixelShader; + MaterialPixelShader *localPixelShader; + }; + + char perPrimArgCount; + char perObjArgCount; + char stableArgCount; + char customSamplerFlags; + + union + { + MaterialShaderArgument *localArgs; + MaterialShaderArgument *args; + }; +}; + +struct MaterialTechnique +{ + const char *name; + unsigned __int16 flags; + unsigned __int16 passCount; + MaterialPass passArray[1]; +}; + +typedef bool(__cdecl* Material_HashStateMap_t)(const char *name, unsigned int *foundHashIndex); +static Material_HashStateMap_t Material_HashStateMap = (Material_HashStateMap_t)0x0047ECB0; + +typedef MaterialStateMap *(__cdecl* Material_LoadStateMap_t)(const char *name); +static Material_LoadStateMap_t Material_LoadStateMap = (Material_LoadStateMap_t)0x0047F870; + +typedef unsigned int (__cdecl* R_HashString_t)(const char *string); +static R_HashString_t R_HashString = (R_HashString_t)0x0048D200; + +typedef GfxImage *(__cdecl* Image_Register_t)(const char *imageName, char semantic); +static Image_Register_t Image_Register = (Image_Register_t)0x0041B4E0; + +typedef void* (__cdecl* Material_Alloc_t)(unsigned int size); +static Material_Alloc_t Material_Alloc = (Material_Alloc_t)0x004072F0; + +typedef bool (__cdecl* Material_Validate_t)(Material *material); +static Material_Validate_t Material_Validate = (Material_Validate_t)0x00483B20; + +typedef struct MaterialTechnique *(__cdecl* Material_GetTechnique_t)(Material *material, char techType); +static Material_GetTechnique_t Material_GetTechnique = (Material_GetTechnique_t)0x00470F20; + +typedef void (__cdecl* Material_BuildStateBitsTable_t)(Material *material, unsigned int toolFlags, const unsigned int *refStateBits); +static Material_BuildStateBitsTable_t Material_BuildStateBitsTable = (Material_BuildStateBitsTable_t)0x004837D0; + +typedef char (__cdecl* MaterialTexture_IsMatureContent_t)(const char texSemantic, const char *texImageName); +static MaterialTexture_IsMatureContent_t MaterialTexture_IsMatureContent = (MaterialTexture_IsMatureContent_t)0x00483DF0; + +void hk_Material_RegisterStateMap(void); + +Material *__cdecl Material_LoadRaw(MaterialRaw *mtlRaw, unsigned int materialType); diff --git a/components/linker_pc/stdafx.h b/components/linker_pc/stdafx.h index 69010e4e..2b06a904 100644 --- a/components/linker_pc/stdafx.h +++ b/components/linker_pc/stdafx.h @@ -1,5 +1,8 @@ #pragma once +#pragma comment(lib, "detours.lib") +#include "../shared/detours/Detours.h" + #define WIN32_LEAN_AND_MEAN #include #include @@ -8,7 +11,13 @@ // // Shared files // -#include "../shared/utility.h" +#include "../shared/shared_utility.h" +#include "../shared/shared_version.h" + +#include "linker.h" #include "T5.h" -#include "crc32.h" \ No newline at end of file +#include "crc32.h" +#include "assertive.h" + +#define VANILLA_VALUE(NAME, TYPE, ADDRESS) static TYPE& NAME = *(TYPE*)ADDRESS; diff --git a/components/path_mod/stdafx.h b/components/path_mod/stdafx.h index 268b707e..57f27e22 100644 --- a/components/path_mod/stdafx.h +++ b/components/path_mod/stdafx.h @@ -1,5 +1,8 @@ #pragma once +#pragma comment(lib, "detours.lib") +#include "../shared/detours/Detours.h" + #define WIN32_LEAN_AND_MEAN #include #include diff --git a/components/radiant_mod/CCamWnd.cpp b/components/radiant_mod/CCamWnd.cpp new file mode 100644 index 00000000..ebc84421 --- /dev/null +++ b/components/radiant_mod/CCamWnd.cpp @@ -0,0 +1,10 @@ +#include "stdafx.h" + +CCamWnd *(__stdcall *CCamWnd::ctor_o)(CCamWnd *This); +CCamWnd *CCamWnd::ActiveWindow; + +CCamWnd *CCamWnd::ctor(CCamWnd *This) +{ + CCamWnd::ActiveWindow = CCamWnd::ctor_o(This); + return CCamWnd::ActiveWindow; +} \ No newline at end of file diff --git a/components/radiant_mod/CCamWnd.h b/components/radiant_mod/CCamWnd.h new file mode 100644 index 00000000..fe9ff2a0 --- /dev/null +++ b/components/radiant_mod/CCamWnd.h @@ -0,0 +1,20 @@ +#pragma once + +class CCamWnd +{ +private: + // Do not allow this class to be instanced + CCamWnd() = delete; + ~CCamWnd() = delete; + +public: + char _pad[0x70]; + float cameraOrigin[3]; + float cameraAngles[3]; + + static CCamWnd * __stdcall ctor(CCamWnd *This); + static CCamWnd *(__stdcall *ctor_o)(CCamWnd *This); + static CCamWnd *ActiveWindow; +}; +STATIC_ASSERT_OFFSET(CCamWnd, cameraOrigin, 0x70); +STATIC_ASSERT_OFFSET(CCamWnd, cameraAngles, 0x7C); \ No newline at end of file diff --git a/components/radiant_mod/afx_resource.cpp b/components/radiant_mod/afx_resource.cpp new file mode 100644 index 00000000..10149e01 --- /dev/null +++ b/components/radiant_mod/afx_resource.cpp @@ -0,0 +1,91 @@ +#include "stdafx.h" + +HMODULE GetLocalDllHandle() +{ + static HMODULE localDll = nullptr; + + if (!localDll) + localDll = GetModuleHandleA("radiant_mod.dll"); + + return localDll; +} + +HGLOBAL WINAPI hk_LoadResource(HMODULE hModule, HRSRC hResInfo) +{ + HGLOBAL handle = LoadResource(GetLocalDllHandle(), hResInfo); + + if (!handle) + handle = LoadResource(hModule, hResInfo); + + return handle; +} + +DWORD WINAPI hk_SizeofResource(HMODULE hModule, HRSRC hResInfo) +{ + DWORD resourceSize = SizeofResource(GetLocalDllHandle(), hResInfo); + + if (!resourceSize) + resourceSize = SizeofResource(hModule, hResInfo); + + return resourceSize; +} + +HRSRC WINAPI hk_FindResourceA(HMODULE hModule, LPCSTR lpName, LPCSTR lpType) +{ + HRSRC resource = FindResourceA(GetLocalDllHandle(), lpName, lpType); + + if (!resource) + resource = FindResourceA(hModule, lpName, lpType); + + return resource; +} + +HMENU WINAPI hk_LoadMenuA(HINSTANCE hInstance, LPCSTR lpMenuName) +{ + HMENU menu = LoadMenuA((HINSTANCE)GetLocalDllHandle(), lpMenuName); + + if (!menu) + menu = LoadMenuA(hInstance, lpMenuName); + + return menu; +} + +HCURSOR WINAPI hk_LoadCursorA(HINSTANCE hInstance, LPCSTR lpCursorName) +{ + HCURSOR cursor = LoadCursorA((HINSTANCE)GetLocalDllHandle(), lpCursorName); + + if (!cursor) + cursor = LoadCursorA(hInstance, lpCursorName); + + return cursor; +} + +HACCEL WINAPI hk_LoadAcceleratorsA(HINSTANCE hInstance, LPCSTR lpTableName) +{ + HACCEL accelerators = LoadAcceleratorsA((HINSTANCE)GetLocalDllHandle(), lpTableName); + + if (!accelerators) + accelerators = LoadAcceleratorsA(hInstance, lpTableName); + + return accelerators; +} + +HICON WINAPI hk_LoadIconA(HINSTANCE hInstance, LPCSTR lpIconName) +{ + HICON icon = LoadIconA((HINSTANCE)GetLocalDllHandle(), lpIconName); + + if (!icon) + icon = LoadIconA(hInstance, lpIconName); + + return icon; +} + +HBITMAP WINAPI hk_LoadBitmapA(HINSTANCE hInstance, LPCSTR lpBitmapName) +{ + HBITMAP bitmap = LoadBitmapA((HINSTANCE)GetLocalDllHandle(), lpBitmapName); + + if (!bitmap) + bitmap = LoadBitmapA(hInstance, lpBitmapName); + + return bitmap; +} \ No newline at end of file diff --git a/components/radiant_mod/afx_resource.h b/components/radiant_mod/afx_resource.h new file mode 100644 index 00000000..35ee948f --- /dev/null +++ b/components/radiant_mod/afx_resource.h @@ -0,0 +1,11 @@ +#pragma once + +HMODULE GetLocalDllHandle(); +HGLOBAL WINAPI hk_LoadResource(HMODULE hModule, HRSRC hResInfo); +DWORD WINAPI hk_SizeofResource(HMODULE hModule, HRSRC hResInfo); +HRSRC WINAPI hk_FindResourceA(HMODULE hModule, LPCSTR lpName, LPCSTR lpType); +HMENU WINAPI hk_LoadMenuA(HINSTANCE hInstance, LPCSTR lpMenuName); +HCURSOR WINAPI hk_LoadCursorA(HINSTANCE hInstance, LPCSTR lpCursorName); +HACCEL WINAPI hk_LoadAcceleratorsA(HINSTANCE hInstance, LPCSTR lpTableName); +HICON WINAPI hk_LoadIconA(HINSTANCE hInstance, LPCSTR lpIconName); +HBITMAP WINAPI hk_LoadBitmapA(HINSTANCE hInstance, LPCSTR lpBitmapName); \ No newline at end of file diff --git a/components/radiant_mod/brush.h b/components/radiant_mod/brush.h new file mode 100644 index 00000000..1c681c47 --- /dev/null +++ b/components/radiant_mod/brush.h @@ -0,0 +1,15 @@ +#pragma once + +struct entity_s; +struct brush_s +{ + char pad[4]; + brush_s *next; + entity_s *owner; + brush_s *ownerNext; +}; +STATIC_ASSERT_OFFSET(brush_s, next, 0x4); +STATIC_ASSERT_OFFSET(brush_s, owner, 0x8); +STATIC_ASSERT_OFFSET(brush_s, ownerNext, 0xC); + +static brush_s *selected_brushes = (brush_s *)0x0256459C; \ No newline at end of file diff --git a/components/radiant_mod/com_files.cpp b/components/radiant_mod/com_files.cpp index e83ecf23..1d67c45e 100644 --- a/components/radiant_mod/com_files.cpp +++ b/components/radiant_mod/com_files.cpp @@ -57,4 +57,42 @@ void FS_Init_TechsetOverride(void) fclose(h); } -} \ No newline at end of file +} + +int __cdecl FS_HashFileName(const char *fname, int hashSize) +{ + int hash = 0; + for (int i = 0; fname[i]; ++i) + { + int letter = tolower(fname[i]); + if (letter == '.') + break; + if (letter == '\\') + letter = '/'; + hash += letter * (i + 119); + } + + return ((hash >> 20) ^ hash ^ (hash >> 10)) & (hashSize - 1); +} + +const char* FS_GetExtensionSubString(const char* filename) +{ + if (filename == '\0') + return NULL; + + const char* substr = 0; + while (*filename) + { + if (*filename == '.') + substr = filename; + else if (*filename == '/' || *filename == '\\') + substr = 0; + + ++filename; + } + + if (!substr) + substr = filename; + + return substr; +} diff --git a/components/radiant_mod/com_files.h b/components/radiant_mod/com_files.h index 8ec702bb..454d3e28 100644 --- a/components/radiant_mod/com_files.h +++ b/components/radiant_mod/com_files.h @@ -17,4 +17,8 @@ static FS_FreeFile_t FS_FreeFile = (FS_FreeFile_t)0x004BC910; int __cdecl FS_ReadFile(const char *qpath, void **buffer); -void FS_Init_TechsetOverride(void); \ No newline at end of file +void FS_Init_TechsetOverride(void); + +int __cdecl FS_HashFileName(const char *fname, int hashSize); + +const char* FS_GetExtensionSubString(const char* filename); diff --git a/components/radiant_mod/com_memory.cpp b/components/radiant_mod/com_memory.cpp index bc1ce3de..1559288e 100644 --- a/components/radiant_mod/com_memory.cpp +++ b/components/radiant_mod/com_memory.cpp @@ -16,4 +16,46 @@ SRCLINE(1913) void *Z_Malloc(int size) { return ((void *(__cdecl *)(int))0x004C7820)(size); -} \ No newline at end of file +} + +void *__cdecl Hunk_FindDataForFile(int type, const char *name) +{ + int hash = FS_HashFileName(name, 1024); + return Hunk_FindDataForFileInternal(type, name, hash); +} + +void *__cdecl Hunk_FindDataForFileInternal(int _type, const char *name, int hash) +{ + void* result = NULL; + + _asm + { + push _type + mov edi, name + mov eax, hash + mov ebx, 0x004C7A90 + call ebx + add esp, 4 + mov result, eax + } + + return result; +} + +const char *__cdecl Hunk_SetDataForFile(int _type, const char *name, void *data, void *(__cdecl *alloc)(int)) +{ + const char* result = NULL; + + _asm + { + push alloc + push data + push _type + mov esi, name + mov ebx, 0x004C7B10 + add esp, 12 + mov result, eax + } + + return result; +} diff --git a/components/radiant_mod/com_memory.h b/components/radiant_mod/com_memory.h index 27adeed0..6b2b1209 100644 --- a/components/radiant_mod/com_memory.h +++ b/components/radiant_mod/com_memory.h @@ -8,3 +8,8 @@ static Hunk_FreeTempMemory_t* Hunk_FreeTempMemory = (Hunk_FreeTempMemory_t*)0x00 void *Hunk_Alloc(int size, const char *name, int type); void Z_Free(void *ptr); void *Z_Malloc(int size); + +void *__cdecl Hunk_FindDataForFile(int type, const char *name); +void *__cdecl Hunk_FindDataForFileInternal(int type, const char *name, int hash); + +const char *__cdecl Hunk_SetDataForFile(int type, const char *name, void *data, void *(__cdecl *alloc)(int)); diff --git a/components/radiant_mod/d3d9ex.cpp b/components/radiant_mod/d3d9ex.cpp new file mode 100644 index 00000000..e2d6380d --- /dev/null +++ b/components/radiant_mod/d3d9ex.cpp @@ -0,0 +1,107 @@ +#include "stdafx.h" + +bool r_d3d9ex = false; + +bool IsD3D9ExAvailable() +{ + return r_d3d9ex; +} + +IDirect3D9 *D3DAPI hk_Direct3DCreate9(UINT SDKVersion) +{ + HMODULE d3d9 = GetModuleHandleA("d3d9.dll"); + + if (!d3d9) + d3d9 = LoadLibraryA("d3d9.dll"); + + if (!d3d9) + return nullptr; + + auto pDirect3DCreate9 = (decltype(&Direct3DCreate9))GetProcAddress(d3d9, "Direct3DCreate9"); + auto pDirect3DCreate9Ex = (decltype(&Direct3DCreate9Ex))GetProcAddress(d3d9, "Direct3DCreate9Ex"); + + IDirect3D9 *d3d = nullptr; + IDirect3D9Ex *d3dex = nullptr; + + // Check for Direct3DCreate9Ex first (user must manually enable dvar, autodetection disabled) + if (pDirect3DCreate9Ex) + { + if (SUCCEEDED(pDirect3DCreate9Ex(SDKVersion, &d3dex))) + { + printf("Using Direct3D9Ex interface\n"); + + r_d3d9ex = true; + return d3dex; + } + } + + // Otherwise default to the normal Direct3DCreate9 + if (pDirect3DCreate9) + { + if (d3d = pDirect3DCreate9(SDKVersion)) + { + r_d3d9ex = false; + return d3d; + } + } + + r_d3d9ex = false; + return nullptr; +} + +HRESULT D3DAPI hk_CreateDevice(IDirect3D9 *This, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DDevice9 **ppReturnedDeviceInterface) +{ + // Short-circuit to the original call + if (!IsD3D9ExAvailable()) + return This->CreateDevice(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface); + + // D3D requires a specific structure when in windowed mode + D3DDISPLAYMODEEX displayMode; + displayMode.Size = sizeof(D3DDISPLAYMODEEX); + displayMode.Width = pPresentationParameters->BackBufferWidth; + displayMode.Height = pPresentationParameters->BackBufferHeight; + displayMode.RefreshRate = pPresentationParameters->FullScreen_RefreshRateInHz; + displayMode.Format = pPresentationParameters->BackBufferFormat; + displayMode.ScanLineOrdering = D3DSCANLINEORDERING_UNKNOWN; + + // This must be null when in windowed mode + D3DDISPLAYMODEEX *realMode = (pPresentationParameters->Windowed) ? nullptr : &displayMode; + + HRESULT hr = ((IDirect3D9Ex *)This)->CreateDeviceEx(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, realMode, (IDirect3DDevice9Ex **)ppReturnedDeviceInterface); + + if (FAILED(hr)) + return hr; + + printf("Using CreateDeviceEx for device creation\n"); + return D3D_OK; +} + +HRESULT D3DAPI hk_GetSwapChain(IDirect3DDevice9 *This, UINT iSwapChain, IDirect3DSwapChain9 **ppSwapChain) +{ + // Get the standard DirectX 9 swapchain + HRESULT hr = This->GetSwapChain(iSwapChain, ppSwapChain); + + if (FAILED(hr)) + return hr; + + if (!IsD3D9ExAvailable()) + return D3D_OK; + + // Get a handle to the IDirect3DSwapChain9Ex interface via COM GUID + return (*ppSwapChain)->QueryInterface(__uuidof(IDirect3DSwapChain9Ex), (void **)ppSwapChain); +} + +HRESULT D3DAPI hk_CreateAdditionalSwapChain(IDirect3DDevice9 *This, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DSwapChain9 **ppSwapChain) +{ + // Short-circuit to the original call + HRESULT hr = This->CreateAdditionalSwapChain(pPresentationParameters, ppSwapChain); + + if (FAILED(hr)) + return hr; + + if (!IsD3D9ExAvailable()) + return D3D_OK; + + // Get a handle to the IDirect3DSwapChain9Ex interface via COM GUID + return (*ppSwapChain)->QueryInterface(__uuidof(IDirect3DSwapChain9Ex), (void **)ppSwapChain); +} \ No newline at end of file diff --git a/components/radiant_mod/d3d9ex.h b/components/radiant_mod/d3d9ex.h new file mode 100644 index 00000000..e9f82a5c --- /dev/null +++ b/components/radiant_mod/d3d9ex.h @@ -0,0 +1,8 @@ +#pragma once + +bool IsD3D9ExAvailable(); + +IDirect3D9 *D3DAPI hk_Direct3DCreate9(UINT SDKVersion); +HRESULT D3DAPI hk_CreateDevice(IDirect3D9 *This, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DDevice9 **ppReturnedDeviceInterface); +HRESULT D3DAPI hk_GetSwapChain(IDirect3DDevice9 *This, UINT iSwapChain, IDirect3DSwapChain9 **ppSwapChain); +HRESULT D3DAPI hk_CreateAdditionalSwapChain(IDirect3DDevice9 *This, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DSwapChain9 **ppSwapChain); \ No newline at end of file diff --git a/components/radiant_mod/dllmain.cpp b/components/radiant_mod/dllmain.cpp index d845d2e4..0df1c1fb 100644 --- a/components/radiant_mod/dllmain.cpp +++ b/components/radiant_mod/dllmain.cpp @@ -1,3 +1,4 @@ +#define G_VERSION 1, 2, 0 #include "stdafx.h" void strcpy_safe(char *Dest, const char *Src) @@ -35,13 +36,8 @@ void __declspec(naked) hk_Com_Printf() } } -bool g_Initted = false; - BOOL RadiantMod_Init() { - if (g_Initted) - return FALSE; - // // Disable STDOUT buffering // @@ -67,14 +63,52 @@ BOOL RadiantMod_Init() #endif // - // Hook any needed functions + // Use D3D9Ex when available to prevent lost devices + // + Detours::X86::DetourFunction((PBYTE)0x0051B75A, (PBYTE)&hk_Direct3DCreate9, Detours::X86Option::USE_CALL); + Detours::X86::DetourFunction((PBYTE)0x0051B5CC, (PBYTE)&hk_CreateDevice, Detours::X86Option::USE_CALL); + Detours::X86::DetourFunction((PBYTE)0x0054F34F, (PBYTE)&hk_GetSwapChain, Detours::X86Option::USE_CALL); + Detours::X86::DetourFunction((PBYTE)0x0051B891, (PBYTE)&hk_CreateAdditionalSwapChain, Detours::X86Option::USE_CALL); + Detours::X86::DetourFunction((PBYTE)0x0051C25E, (PBYTE)&hk_CreateAdditionalSwapChain, Detours::X86Option::USE_CALL); + Detours::X86::DetourFunction((PBYTE)0x0056AC60, (PBYTE)&Image_Setup); + PatchMemory(0x0053E492, (PBYTE)"\xEB", 1); // (Present() patch when focus is lost) + + // + // Redirect all resource (resx) loading to this dll first + // + void *ptr = hk_LoadResource; + PatchMemory(0x006414F0, (PBYTE)&ptr, 4); + + ptr = hk_SizeofResource; + PatchMemory(0x006414F8, (PBYTE)&ptr, 4); + + ptr = hk_FindResourceA; + PatchMemory(0x006414EC, (PBYTE)&ptr, 4); + + ptr = hk_LoadMenuA; + PatchMemory(0x0064183C, (PBYTE)&ptr, 4); + + ptr = hk_LoadCursorA; + PatchMemory(0x006418B4, (PBYTE)&ptr, 4); + + ptr = hk_LoadAcceleratorsA; + PatchMemory(0x00641678, (PBYTE)&ptr, 4); + + ptr = hk_LoadIconA; + PatchMemory(0x0064180C, (PBYTE)&ptr, 4); + + ptr = hk_LoadBitmapA; + PatchMemory(0x00641828, (PBYTE)&ptr, 4); + + // + // Enable com_printf again // FixupFunction(0x004683F0, (ULONG_PTR)&hk_Com_Printf); // // Hook CWinApp::Run to allow for automatic map loading via command line arguments // - //Detours::X86::DetourClassFunction((PBYTE)0x005BF26E, &CWinApp::Run); + //Detours::X86::DetourFunctionClass((PBYTE)0x005BF26E, &CWinApp::Run); // // FS_ReadFile Hook - Set Up Fallback Location for Techsets and Techniques @@ -119,16 +153,12 @@ BOOL RadiantMod_Init() Detours::X86::DetourFunction((PBYTE)0x0053519E, (PBYTE)&mfh_XModelReadSurface); // 4 byte xmodelsurfs file adjustment (MagicNumber) // - // FixRegistryEntries to prevent collision with CoDWAWRadiant + // Re-brand CoDWAWRadiant to CoDBORadiant // strcpy_safe((char *)0x006F8688, "Software\\iw\\CoDBORadiantModTool\\CoDBORadiantModTool"); strcpy_safe((char *)0x006F0CD0, "Software\\iw\\CoDBORadiantModTool\\IniPrefs"); strcpy_safe((char *)0x006EC300, "Software\\iw\\CoDBORadiantModTool\\MRU"); strcpy_safe((char *)0x006F0D08, "iw\\CoDBORadiantModTool"); - - // - // More BO Radiant re-branding of names - // strcpy_safe((char *)0x006F7984, "CoDBORadiantModTool"); strcpy_safe((char *)0x006ECA30, "You will need to restart CoDBORadiantModTool for the view changes to take place."); strcpy_safe((char *)0x006EC5CC, "CoDBORadiantModTool Project files( *.prj )|*.prj||"); @@ -158,6 +188,9 @@ BOOL RadiantMod_Init() DO_NOT_USE(0x0052F6B0);// Material_CopyTextToDXBuffer DO_NOT_USE(0x0052FE70);// Material_SetPassShaderArguments_DX DO_NOT_USE(0x00567450);// Image_LoadFromData + DO_NOT_USE(0x0052B160);// Image_CreateCubeTexture_PC + DO_NOT_USE(0x0052B040);// Image_Create3DTexture_PC + DO_NOT_USE(0x0052AF20);// Image_Create2DTexture_PC #undef DO_NOT_USE #endif @@ -174,9 +207,9 @@ BOOL RadiantMod_Init() Detours::X86::DetourFunction((PBYTE)0x004D70DB, (PBYTE)&mfh3_Sys_ListFiles); // - // Leak pointfile compatibility fix + // (Deprecated): Leak pointfile compatibility fix // - PatchMemory(0x006F7378, (PBYTE)".pts", 4); + //PatchMemory(0x006F7378, (PBYTE)".pts", 4); // // Fix for misleading (incorrect) assertion message @@ -230,7 +263,7 @@ BOOL RadiantMod_Init() Detours::X86::DetourFunction((PBYTE)0x004A814E, (PBYTE)&mfh_Ent_Connect); // Generate the new default spotLight KVs when creating a spotLight #if RADIANT_USE_AFX_OVERRIDES - CWnd::OnCtlColor_o = (OnCtlColor_t)Detours::X86::DetourClassFunction((PBYTE)0x0059B96E, &CWnd::OnCtlColor); + CWnd::OnCtlColor_o = (OnCtlColor_t)Detours::X86::DetourFunctionClass((PBYTE)0x0059B96E, &CWnd::OnCtlColor); #endif #if RADIANT_USE_SPLASH @@ -260,10 +293,46 @@ BOOL RadiantMod_Init() PatchMemory(0x0042F882, (PBYTE)&ppfn, 4); // Main Window //PatchMemory(0x004B36DF, (PBYTE)&ppfn, 4); // Entity Window (Doesn't work) //PatchMemory(0x004018B0, (PBYTE)&ppfn, 4); // Advanced Curve Dialog (Doesn''t work) - #endif - g_Initted = true; + // + // Fix the grid block coordinates + // + PatchMemory(0x004827E9, (PBYTE)"\x84", 1); + PatchMemory(0x004827D3, (PBYTE)"\x84", 1); + + // + // Fix Crash when opening maps that don't have a valid skybox + // (Prevent attempting to use the default model as a skybox) + // + PatchCall(0x0040309F, (PBYTE)&R_RegisterSkyboxModel); + + // + // Add Maps Loaded via Launcher (Args) to the MRU + // + PatchCall(0x0042DD2E, (PBYTE)&hk_HandleLaunchArgs); + + // + // Live game update hooks + // + *(PBYTE *)&CCamWnd::ctor_o = Detours::X86::DetourFunction((PBYTE)0x00402B90, (PBYTE)&CCamWnd::ctor); + + *(PBYTE *)&Entity_Clone_o = Detours::X86::DetourFunction((PBYTE)0x0049E7C0, (PBYTE)&hk_Entity_Clone); + *(PBYTE *)&Entity_Free_o = Detours::X86::DetourFunction((PBYTE)0x0049D3D0, (PBYTE)&hk_Entity_Free); + + *(PBYTE *)&Undo_Start_o = Detours::X86::DetourFunction((PBYTE)0x004769D0, (PBYTE)&hk_Undo_Start); + *(PBYTE *)&Undo_End_o = Detours::X86::DetourFunction((PBYTE)0x00476E30, (PBYTE)&hk_Undo_End); + + *(PBYTE *)&SetEntityKeyValue_o = Detours::X86::DetourFunction((PBYTE)0x0049CE00, (PBYTE)&hk_SetEntityKeyValue); + *(PBYTE *)&SetKeyValue_o = Detours::X86::DetourFunction((PBYTE)0x0049CD20, (PBYTE)&hk_SetKeyValue); + + *(PBYTE *)&Map_LoadFile_o = Detours::X86::DetourFunction((PBYTE)0x0049FF90, (PBYTE)&hk_Map_LoadFile); + *(PBYTE *)&Map_SaveFile_o = Detours::X86::DetourFunction((PBYTE)0x004A07E0, (PBYTE)&hk_Map_SaveFile); + + *(PBYTE *)&MoveSelection_o = Detours::X86::DetourFunction((PBYTE)0x00498AE0, (PBYTE)&hk_MoveSelection); + + CreateThread(nullptr, 0, RemoteNet_Thread, nullptr, 0, nullptr); + return TRUE; } diff --git a/components/radiant_mod/drag.cpp b/components/radiant_mod/drag.cpp new file mode 100644 index 00000000..9485f3fd --- /dev/null +++ b/components/radiant_mod/drag.cpp @@ -0,0 +1,20 @@ +#include "stdafx.h" + +void(*MoveSelection_o)(int a1, int a2, int a3, int a4); + +void hk_MoveSelection(int a1, int a2, int a3, int a4) +{ + // Set the list of the originally selected ents + for (brush_s *b = selected_brushes->next; b != selected_brushes; b = b->next) + { + //entity_s *ent = (entity_s *)*(DWORD *)((DWORD)selected_brushes->next->owner + 8); + entity_s *ent = (entity_s *)*(DWORD *)((DWORD)b->owner + 8); + + if (ent) + Remote_AddEntityUpdate(ent); + } + + MoveSelection_o(a1, a2, a3, a4); + + Remote_CommitChanges(); +} \ No newline at end of file diff --git a/components/radiant_mod/drag.h b/components/radiant_mod/drag.h new file mode 100644 index 00000000..72988063 --- /dev/null +++ b/components/radiant_mod/drag.h @@ -0,0 +1,5 @@ +#pragma once + +extern void(*MoveSelection_o)(int a1, int a2, int a3, int a4); + +void hk_MoveSelection(int a1, int a2, int a3, int a4); \ No newline at end of file diff --git a/components/radiant_mod/entity.cpp b/components/radiant_mod/entity.cpp new file mode 100644 index 00000000..c56ec756 --- /dev/null +++ b/components/radiant_mod/entity.cpp @@ -0,0 +1,63 @@ +#include "stdafx.h" + +void(*Entity_Clone_o)(); +void(*Entity_Free_o)(entity_s *ent); + +void(*SetEntityKeyValue_o)(entity_s *ent, const char *key, const char *value); +void(*SetKeyValue_o)(epair_s *pair, const char *key, const char *value); + +void __declspec(naked) hk_Entity_Clone() +{ + // Input entity is in eax, clone output is in eax + __asm + { + mov eax, eax + + // ... + + mov eax, eax + jmp [Entity_Clone_o] + } +} + +void hk_Entity_Free(entity_s *ent) +{ + // First tell the game, then free it + // -> sendpacket(); + + Entity_Free_o(ent); +} + +void hk_SetEntityKeyValue(entity_s *ent, const char *key, const char *value) +{ + Remote_AddEntityUpdate(ent); + SetEntityKeyValue_o(ent, key, value); +} + +void __declspec(naked) hk_SetKeyValue(const char *key, const char *value) +{ + epair_s *pairs; + + __asm + { + push ebp + mov ebp, esp + sub esp, __LOCAL_SIZE + mov pairs, eax + + // Commit changes later + push pairs + call Remote_AddEntityEpairUpdate + add esp, 0x4 + + push value + push key + mov eax, pairs + call [SetKeyValue_o] + add esp, 0x8 + + mov esp, ebp + pop ebp + retn + } +} \ No newline at end of file diff --git a/components/radiant_mod/entity.h b/components/radiant_mod/entity.h new file mode 100644 index 00000000..d91e314c --- /dev/null +++ b/components/radiant_mod/entity.h @@ -0,0 +1,56 @@ +#pragma once + +struct eclass_s +{ + char _pa1[0x4]; + const char *name; +}; +STATIC_ASSERT_OFFSET(eclass_s, name, 0x4); + +struct epair_s +{ + epair_s *next; + char *key; + char *value; +}; +STATIC_ASSERT_OFFSET(epair_s, next, 0x0); +STATIC_ASSERT_OFFSET(epair_s, key, 0x4); +STATIC_ASSERT_OFFSET(epair_s, value, 0x8); + +struct entity_s +{ + entity_s *prev; + entity_s *next; + char _pad1[0x58]; // brushes @ 0xC + eclass_s *eclass; + char _pad2[0x4]; + float origin[3]; + epair_s *epairs; + char _pad3[0x10]; + int refCount; +}; +STATIC_ASSERT_OFFSET(entity_s, prev, 0x0); +STATIC_ASSERT_OFFSET(entity_s, next, 0x4); +//STATIC_ASSERT_OFFSET(entity_s, brushes, 0xC); +STATIC_ASSERT_OFFSET(entity_s, eclass, 0x60); +STATIC_ASSERT_OFFSET(entity_s, origin, 0x68); +STATIC_ASSERT_OFFSET(entity_s, epairs, 0x74); +STATIC_ASSERT_OFFSET(entity_s, refCount, 0x88); + +static int& d_select_count = *(int *)0x02857C5C; // Total number of selected objects +static int& multiple_entities = *(int *)0x0257CEDC; // True if more than one entity is selected + +static entity_s*& g_qeglobals_d_project_entity = *(entity_s **)0x027E1824; +static entity_s* world_entity = (entity_s *)0x027C3400; +static entity_s* edit_entity = (entity_s *)0x0257CED8; + +extern void(*Entity_Clone_o)(); +extern void(*Entity_Free_o)(entity_s *ent); + +extern void(*SetEntityKeyValue_o)(entity_s *ent, const char *key, const char *value); +extern void(*SetKeyValue_o)(epair_s *pair, const char *key, const char *value); + +void hk_Entity_Free(entity_s *ent); +void hk_Entity_Clone(); +void hk_SetEntityKeyValue(entity_s *ent, const char *key, const char *value); +void hk_SetKeyValue(const char *key, const char *value); \ No newline at end of file diff --git a/components/radiant_mod/map.cpp b/components/radiant_mod/map.cpp new file mode 100644 index 00000000..bdd81b11 --- /dev/null +++ b/components/radiant_mod/map.cpp @@ -0,0 +1,19 @@ +#include "stdafx.h" + +void(__thiscall * Map_LoadFile_o)(const char *filename); +void(*Map_SaveFile_o)(const char *filename, bool use_region, bool autosave); + +void __fastcall hk_Map_LoadFile(const char *filename) +{ + // We're loading a new map - all old data is invalid + Remote_EnableUpdates(false, true); + Map_LoadFile_o(filename); + Remote_EnableUpdates(true, true); +} + +void hk_Map_SaveFile(const char *filename, bool useRegion, bool autosave) +{ + Remote_EnableUpdates(false); + Map_SaveFile_o(filename, useRegion, autosave); + Remote_EnableUpdates(true); +} diff --git a/components/radiant_mod/map.h b/components/radiant_mod/map.h new file mode 100644 index 00000000..fe896f68 --- /dev/null +++ b/components/radiant_mod/map.h @@ -0,0 +1,7 @@ +#pragma once + +extern void(__thiscall * Map_LoadFile_o)(const char *filename); +extern void(*Map_SaveFile_o)(const char *filename, bool use_region, bool autosave); + +void __fastcall hk_Map_LoadFile(const char *filename); +void hk_Map_SaveFile(const char *filename, bool useRegion, bool autosave); diff --git a/components/radiant_mod/r_image.cpp b/components/radiant_mod/r_image.cpp index 21b7246e..4467a4ba 100644 --- a/components/radiant_mod/r_image.cpp +++ b/components/radiant_mod/r_image.cpp @@ -14,15 +14,14 @@ unsigned int Image_GetUsage(int imageFlags, D3DFORMAT imageFormat) if (imageFormat != D3DFMT_D24S8 && imageFormat != D3DFMT_D24X8 && imageFormat != D3DFMT_D16) - return 1; + return D3DUSAGE_RENDERTARGET; - return 2; - } - else if (imageFlags & 0x10000) - { - return 512; + return D3DUSAGE_DEPTHSTENCIL; } + if (imageFlags & 0x10000) + return D3DUSAGE_DYNAMIC; + return 0; } @@ -31,33 +30,33 @@ void Image_Create2DTexture_PC(GfxImage *image, unsigned __int16 width, unsigned { ASSERT(image != nullptr); ASSERT(!image->texture.basemap); + // ASSERT(Sys_IsRenderThread()); image->width = width; image->height = height; image->depth = 1; image->mapType = 3; - D3DPOOL memPool = D3DPOOL_DEFAULT; - unsigned int usage = Image_GetUsage(imageFlags, imageFormat); + D3DPOOL memPool = D3DPOOL_DEFAULT; + DWORD usage = Image_GetUsage(imageFlags, imageFormat); - if (imageFlags & 0x40000) + if (imageFlags & 0x40000 || imageFlags & 0x100) memPool = D3DPOOL_SYSTEMMEM; else memPool = (usage == 0) ? D3DPOOL_MANAGED : D3DPOOL_DEFAULT; - if (imageFlags & 0x100) - memPool = D3DPOOL_SYSTEMMEM; + // D3D9Ex does not allow D3DPOOL_MANAGED + if (IsD3D9ExAvailable()) + { + usage = (usage == 0) ? D3DUSAGE_DYNAMIC : usage; + memPool = (memPool == D3DPOOL_MANAGED) ? D3DPOOL_DEFAULT : memPool; + } - // ASSERT(Sys_IsRenderThread()); - - // if (r_logFile && r_logFile->current.integer) - // RB_LogPrint("dx.device->CreateTexture(width, height, mipmapCount, usage, imageFormat, memPool, &image->texture.map, nullptr)\n"); - - HRESULT hr = (*dx_device)->CreateTexture(width, height, mipmapCount, usage, imageFormat, memPool, &image->texture.map, nullptr); + HRESULT hr = dx_device->CreateTexture(width, height, mipmapCount, usage, imageFormat, memPool, &image->texture.map, nullptr); if (FAILED(hr)) { - (*g_disableRendering)++; + g_disableRendering++; Com_Error(ERR_FATAL, "dx.device->CreateTexture(width, height, mipmapCount, usage, imageFormat, memPool, &image->texture.map, nullptr) failed: %s\n", R_ErrorDescription(hr)); } } @@ -67,25 +66,23 @@ void Image_Create3DTexture_PC(GfxImage *image, unsigned __int16 width, unsigned { ASSERT(image != nullptr); ASSERT(!image->texture.basemap); + // ASSERT(Sys_IsRenderThread()); image->width = width; image->height = height; image->depth = depth; image->mapType = 4; - Image_GetUsage(imageFlags, imageFormat); - - // ASSERT(Sys_IsRenderThread()); + // D3D9Ex does not allow D3DPOOL_MANAGED + DWORD usage = (IsD3D9ExAvailable()) ? D3DUSAGE_DYNAMIC : 0; + D3DPOOL memPool = (IsD3D9ExAvailable()) ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; - // if (r_logFile && r_logFile->current.integer) - // RB_LogPrint("dx.device->CreateVolumeTexture(width, height, depth, mipmapCount, 0, imageFormat, D3DPOOL_MANAGED, &image->texture.volmap, nullptr)\n"); - - HRESULT hr = (*dx_device)->CreateVolumeTexture(width, height, depth, mipmapCount, 0, imageFormat, D3DPOOL_MANAGED, &image->texture.volmap, nullptr); + HRESULT hr = dx_device->CreateVolumeTexture(width, height, depth, mipmapCount, usage, imageFormat, memPool, &image->texture.volmap, nullptr); if (FAILED(hr)) { - (*g_disableRendering)++; - Com_Error(ERR_FATAL, "dx.device->CreateVolumeTexture(width, height, depth, mipmapCount, 0, imageFormat, D3DPOOL_MANAGED, &image->texture.volmap, nullptr) failed: %s\n", R_ErrorDescription(hr)); + g_disableRendering++; + Com_Error(ERR_FATAL, "dx.device->CreateVolumeTexture(width, height, depth, mipmapCount, usage, imageFormat, memPool, &image->texture.volmap, nullptr) failed: %s\n", R_ErrorDescription(hr)); } } @@ -94,6 +91,7 @@ void Image_CreateCubeTexture_PC(GfxImage *image, unsigned __int16 edgeLen, int m { ASSERT(image != nullptr); ASSERT(!image->texture.basemap); + // ASSERT(Sys_IsRenderThread()); image->width = edgeLen; image->height = edgeLen; @@ -101,20 +99,19 @@ void Image_CreateCubeTexture_PC(GfxImage *image, unsigned __int16 edgeLen, int m image->mapType = 5; // D3DDeviceCaps support for mipping - if (!*(bool *)0x13ACAD6) + if (!r_supportCubedMipMaps) mipmapCount = 1; - // ASSERT(Sys_IsRenderThread()); - - //if (r_logFile && r_logFile->current.integer) - // RB_LogPrint("dx.device->CreateCubeTexture(edgeLen, mipmapCount, 0, imageFormat, D3DPOOL_MANAGED, &image->texture.cubemap, nullptr)\n"); + // D3D9Ex does not allow D3DPOOL_MANAGED + DWORD usage = (IsD3D9ExAvailable()) ? D3DUSAGE_DYNAMIC : 0; + D3DPOOL memPool = (IsD3D9ExAvailable()) ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; - HRESULT hr = (*dx_device)->CreateCubeTexture(edgeLen, mipmapCount, 0, imageFormat, D3DPOOL_MANAGED, &image->texture.cubemap, nullptr); + HRESULT hr = dx_device->CreateCubeTexture(edgeLen, mipmapCount, usage, imageFormat, memPool, &image->texture.cubemap, nullptr); if (FAILED(hr)) { - (*g_disableRendering)++; - Com_Error(ERR_FATAL, "dx.device->CreateCubeTexture(edgeLen, mipmapCount, 0, imageFormat, D3DPOOL_MANAGED, &image->texture.cubemap, nullptr) failed: %s\n", R_ErrorDescription(hr)); + g_disableRendering++; + Com_Error(ERR_FATAL, "dx.device->CreateCubeTexture(edgeLen, mipmapCount, usage, imageFormat, memPool, &image->texture.cubemap, nullptr) failed: %s\n", R_ErrorDescription(hr)); } } diff --git a/components/radiant_mod/r_image_load_obj.cpp b/components/radiant_mod/r_image_load_obj.cpp index 14970d3f..1fcbe561 100644 --- a/components/radiant_mod/r_image_load_obj.cpp +++ b/components/radiant_mod/r_image_load_obj.cpp @@ -1,4 +1,5 @@ #include "stdafx.h" +#include "r_image_wavelet.h" SRCLINE(103) char Image_GetPcStreamedMips(GfxImageFileHeader *fileHeader) @@ -219,29 +220,21 @@ void Image_LoadFromData(GfxImage *image, GfxImageFileHeader *fileHeader, char *s case 5: Image_LoadBitmap(image, fileHeader, srcData, D3DFMT_A8, 1, allocFlags); break; - // case 6: - // Image_LoadWavelet(image, fileHeader, srcData, D3DFMT_A8R8G8B8, 4, allocFlags); - // break; - // case 7: - // Image_LoadWavelet(image, fileHeader, srcData, D3DFMT_X8R8G8B8, 3, allocFlags); - // break; - // case 8: - // Image_LoadWavelet(image, fileHeader, srcData, D3DFMT_A8L8, 2, allocFlags); - // break; - // case 9: - // Image_LoadWavelet(image, fileHeader, srcData, D3DFMT_L8, 1, allocFlags); - // break; - // case 10: - // Image_LoadWavelet(image, fileHeader, srcData, D3DFMT_A8, 1, allocFlags); - // break; case 6: + Image_LoadWavelet(image, fileHeader, srcData, D3DFMT_A8R8G8B8, 4, allocFlags); + break; case 7: + Image_LoadWavelet(image, fileHeader, srcData, D3DFMT_X8R8G8B8, 3, allocFlags); + break; case 8: + Image_LoadWavelet(image, fileHeader, srcData, D3DFMT_A8L8, 2, allocFlags); + break; case 9: + Image_LoadWavelet(image, fileHeader, srcData, D3DFMT_L8, 1, allocFlags); + break; case 10: - Image_LoadDxtc(image, fileHeader, srcData, D3DFMT_DXT1, 8, allocFlags); + Image_LoadWavelet(image, fileHeader, srcData, D3DFMT_A8, 1, allocFlags); break; - case 11: Image_LoadDxtc(image, fileHeader, srcData, D3DFMT_DXT1, 8, allocFlags); break; @@ -269,7 +262,7 @@ void Image_PrintTruncatedFileError(const char *filepath) SRCLINE(1039) void Image_UploadData(GfxImage *image, D3DFORMAT format, D3DCUBEMAP_FACES face, unsigned int mipLevel, const char *src) { - if (image->mapType != MAPTYPE_CUBE || !mipLevel || *r_supportCubedMipMaps) + if (image->mapType != MAPTYPE_CUBE || !mipLevel || r_supportCubedMipMaps) { if (image->mapType == MAPTYPE_3D) Image_Upload3D_CopyData_PC(image, format, mipLevel, src); diff --git a/components/radiant_mod/r_image_wavelet.cpp b/components/radiant_mod/r_image_wavelet.cpp new file mode 100644 index 00000000..3faa4943 --- /dev/null +++ b/components/radiant_mod/r_image_wavelet.cpp @@ -0,0 +1,311 @@ +#include "stdafx.h" +#include "r_image_wavelet.h" + +struct WaveletDecode +{ + unsigned __int16 value; + unsigned __int16 bit; + const char *data; + int width; + int height; + int channels; + int bpp; + int mipLevel; + bool dataInitialized; +}; + +struct WaveletHuffmanDecode +{ + __int16 value; + __int16 bits; +}; + +static WaveletHuffmanDecode* waveletDecodeBlue = (WaveletHuffmanDecode*)0x0065BE60; +static WaveletHuffmanDecode* waveletDecodeRedGreen = (WaveletHuffmanDecode*)0x0065FE60; +static WaveletHuffmanDecode* waveletDecodeAlpha = (WaveletHuffmanDecode*)0x00663E60; + +static void Wavelet_ConsumeBits(unsigned __int16 bitCount, WaveletDecode *decode) +{ + ASSERT(bitCount > 0 && bitCount <= 16); + ASSERT(decode->bit < 8); + + decode->value >>= bitCount; + + decode->value |= (((BYTE)decode->data[0] << 0) | + ((BYTE)decode->data[1] << 8) | + ((BYTE)decode->data[2] << 16) | + ((BYTE)decode->data[3] << 24)) >> decode->bit << (16 - bitCount); + + decode->bit += bitCount; + decode->data += decode->bit >> 3; + decode->bit &= 7u; +} + +static int Wavelet_DecodeValue(WaveletHuffmanDecode *decodeTable, unsigned __int16 bitCount, int bias, WaveletDecode *decode) +{ + ASSERT((1 << bitCount) - 1 >= bias * 2 - 1); + ASSERT((1 << (bitCount - 1)) - 1 < bias * 2 - 1); + + int index = decode->value & 0xFFF; + Wavelet_ConsumeBits(decodeTable[index].bits, decode); + int value = decodeTable[index].value; + + if (value == 0xFFFF8000) + { + value = (((1 << bitCount) - 1) & decode->value) - bias; + Wavelet_ConsumeBits(bitCount, decode); + } + + return value; +} + +static void Wavelet_AddDeltaToMipmap(char *inout, int size, WaveletDecode *decode, const int *dstChanOffset) +{ + for (int i = 0; i < size; i++) + { + for (int chanIndex = 0; chanIndex < decode->channels; chanIndex++) + { + char* value = &inout[dstChanOffset[chanIndex]]; + char old = *value; + *value = Wavelet_DecodeValue(waveletDecodeAlpha, 9u, 255, decode) + old; + } + + inout += decode->bpp; + } +} + +static void Wavelet_DecompressLevel(char *src, char *dst, WaveletDecode *decode) +{ + ASSERT_MSG_VA(decode->bpp >= 1 && decode->bpp <= 4, "decode->bpp not in [1, 4]\n\t%i not in [%i, %i]", decode->bpp, 1, 4); + + ASSERT(decode->bpp == decode->channels || (decode->bpp == 4 && decode->channels == 3)); + ASSERT(decode->mipLevel >= 0); + + int dstChanOffset[4]; + int dstBpp = decode->bpp; + + switch (dstBpp) + { + case 1: + dstChanOffset[0] = 0; + break; + case 2: + dstChanOffset[0] = 0; + dstChanOffset[1] = 1; + break; + case 3: + case 4: + dstChanOffset[0] = 0; + dstChanOffset[1] = 1; + dstChanOffset[2] = 2; + dstChanOffset[3] = 3; + break; + default: + break; + } + + int w = decode->width >> decode->mipLevel; + int h = decode->height >> decode->mipLevel; + + if (w > 1 && h > 1) + { + if (!decode->dataInitialized) + { + decode->value = (((BYTE)decode->data[1]) << 8) | (BYTE)decode->data[0]; + decode->bit = 0; + decode->data += 2; + decode->dataInitialized = 1; + } + + bool needsMipDelta = (decode->value & 1) != 0; + Wavelet_ConsumeBits(1, decode); + + if (needsMipDelta) + Wavelet_AddDeltaToMipmap(src, h * w / 4, decode, dstChanOffset); + + int stride = dstBpp * w; + + int coeff[4][3]; + for (int y = 0; y < h; y += 2) + { + for (int x = 0; x < w; x += 2) + { + ASSERT(dst + stride + dstBpp <= src || dst > src); + + if (decode->channels != 1) + { + int evenOddParity = decode->value & 1; + Wavelet_ConsumeBits(1, decode); + + coeff[0][0] = Wavelet_DecodeValue(waveletDecodeBlue, 9, 255, decode); + coeff[0][1] = Wavelet_DecodeValue(waveletDecodeBlue, 9, 255, decode); + coeff[0][2] = Wavelet_DecodeValue(waveletDecodeBlue, 9, 255, decode); + + int base = 2 * (BYTE)src[dstChanOffset[0]]; + char* dstChan = &dst[dstChanOffset[0]]; + + dst[dstChanOffset[0]] = evenOddParity + ((coeff[0][2] + coeff[0][1] + coeff[0][0] + base) >> 1); + + dstChan[dstBpp] = (coeff[0][0] + base - (coeff[0][2] + coeff[0][1])) >> 1; + dstChan[stride] = (coeff[0][1] - coeff[0][2] + base - coeff[0][0]) >> 1; + dstChan[dstBpp + stride] = (base - coeff[0][0] - (coeff[0][1] - coeff[0][2])) >> 1; + + if (decode->channels >= 3) + { + evenOddParity = decode->value & 1; + Wavelet_ConsumeBits(1, decode); + + coeff[1][0] = coeff[0][0] + Wavelet_DecodeValue(waveletDecodeRedGreen, 10, 510, decode); + coeff[1][1] = coeff[0][1] + Wavelet_DecodeValue(waveletDecodeRedGreen, 10, 510, decode); + coeff[1][2] = coeff[0][2] + Wavelet_DecodeValue(waveletDecodeRedGreen, 10, 510, decode); + + base = 2 * (BYTE)src[dstChanOffset[1]]; + dstChan = &dst[dstChanOffset[1]]; + + dst[dstChanOffset[1]] = evenOddParity + ((coeff[1][2] + coeff[1][1] + coeff[1][0] + base) >> 1); + + dstChan[dstBpp] = (coeff[1][0] + base - (coeff[1][2] + coeff[1][1])) >> 1; + dstChan[stride] = (coeff[1][1] - coeff[1][2] + base - coeff[1][0]) >> 1; + dstChan[dstBpp + stride] = (base - coeff[1][0] - (coeff[1][1] - coeff[1][2])) >> 1; + + evenOddParity = decode->value & 1; + Wavelet_ConsumeBits(1, decode); + + coeff[2][0] = coeff[0][0] + Wavelet_DecodeValue(waveletDecodeRedGreen, 10, 510, decode); + coeff[2][1] = coeff[0][1] + Wavelet_DecodeValue(waveletDecodeRedGreen, 10, 510, decode); + coeff[2][2] = coeff[0][2] + Wavelet_DecodeValue(waveletDecodeRedGreen, 10, 510, decode); + + base = 2 * (BYTE)src[dstChanOffset[2]]; + dstChan = &dst[dstChanOffset[2]]; + + dst[dstChanOffset[2]] = evenOddParity + ((coeff[2][2] + coeff[2][1] + coeff[2][0] + base) >> 1); + + dstChan[dstBpp] = (coeff[2][0] + base - (coeff[2][2] + coeff[2][1])) >> 1; + dstChan[stride] = (coeff[2][1] - coeff[2][2] + base - coeff[2][0]) >> 1; + dstChan[dstBpp + stride] = (base - coeff[2][0] - (coeff[2][1] - coeff[2][2])) >> 1; + } + } + + if (decode->channels == 3) + { + if (decode->channels != decode->bpp) + { + char* dstChan = &dst[dstChanOffset[3]]; + + dst[dstChanOffset[3]] = -1; + + dstChan[dstBpp] = -1; + dstChan[stride] = -1; + dstChan[dstBpp + stride] = -1; + } + } + else + { + int evenOddParity = decode->value & 1; + Wavelet_ConsumeBits(1, decode); + + coeff[3][0] = Wavelet_DecodeValue(waveletDecodeAlpha, 9, 255, decode); + coeff[3][1] = Wavelet_DecodeValue(waveletDecodeAlpha, 9, 255, decode); + coeff[3][2] = Wavelet_DecodeValue(waveletDecodeAlpha, 9, 255, decode); + + int base = 2 * src[dstChanOffset[decode->channels - 1]]; + char* dstChan = &dst[dstChanOffset[decode->channels - 1]]; + + dstChan[0] = evenOddParity + ((coeff[3][2] + coeff[3][1] + coeff[3][0] + base) >> 1); + + dstChan[dstBpp] = (coeff[3][0] + base - (coeff[3][2] + coeff[3][1])) >> 1; + dstChan[stride] = (coeff[3][1] - coeff[3][2] + base - coeff[3][0]) >> 1; + dstChan[dstBpp + stride] = (base - coeff[3][0] - (coeff[3][1] - coeff[3][2])) >> 1; + } + + src += dstBpp; + dst += 2 * dstBpp; + } + + dst += stride; + } + } + else + { + w = max(w, 1); + h = max(h, 1); + + int size = h * w; + ASSERT(size >= 1); + + for (; size > 0; size--) + { + for (int chanIndex = 0; chanIndex < decode->channels; ++chanIndex) + dst[dstChanOffset[chanIndex]] = *decode->data++; + + if (decode->bpp != decode->channels) + dst[dstChanOffset[3]] = -1; + + dst += dstBpp; + } + } +} + +void __cdecl Image_LoadWavelet(GfxImage *image, GfxImageFileHeader *fileHeader, const char *data, _D3DFORMAT format, int bytesPerPixel, unsigned int allocFlags) +{ + PBYTE to[6]; + PBYTE from[6]; + + PBYTE pixels[6]; + + ASSERT(image != nullptr); + ASSERT(fileHeader != nullptr); + ASSERT(data != nullptr); + + Image_SetupFromFile(image, fileHeader, format, allocFlags); + + WaveletDecode decode; + decode.width = fileHeader->dimensions[0]; + decode.height = fileHeader->dimensions[1]; + decode.value = 0; + decode.bit = 0; + + int mipCount = Image_CountMipmapsForFile(fileHeader); + int height = fileHeader->dimensions[1]; + int width = fileHeader->dimensions[0]; + + decode.mipLevel = mipCount - 1; + decode.channels = bytesPerPixel; + decode.bpp = (bytesPerPixel == 3) ? 4 : bytesPerPixel; + decode.dataInitialized = 0; + + unsigned int faceCount = (image->mapType == MAPTYPE_CUBE) ? 6 : 1; + + int totalSize = decode.bpp * fileHeader->dimensions[1] * fileHeader->dimensions[0]; + for (unsigned int faceIndex = 0; faceIndex < faceCount; faceIndex++) + { + pixels[faceIndex++] = (PBYTE)Z_Malloc(totalSize); //_Hunk_AllocTempMemoryInternal(totalSize); + to[faceIndex] = NULL; + } + + int picmip = image->picmip.platform[/*useFastFile->current.enabled == 0*/0]; + decode.data = data; + + for (int mipLevel = decode.mipLevel; mipLevel >= picmip; mipLevel = --decode.mipLevel) + { + unsigned int mipWidth = max(decode.width >> mipLevel, 1); + unsigned int mipHeight = max(decode.height >> mipLevel, 1); + + int mipSize = decode.bpp * mipHeight * mipWidth; + for (unsigned int faceIndex = 0; faceIndex < faceCount; ++faceIndex) + { + from[faceIndex] = to[faceIndex]; + to[faceIndex] = &pixels[faceIndex][totalSize] - mipSize; + + Wavelet_DecompressLevel((char*)from[faceIndex], (char*)to[faceIndex], &decode); + + D3DCUBEMAP_FACES face = Image_CubemapFace(faceIndex); + Image_UploadData(image, format, face, mipLevel - picmip, (const char*)to[faceIndex]); + } + } + + for (unsigned int i = 0; i < faceCount; i++) + { + Z_Free(pixels[i]); + } +} diff --git a/components/radiant_mod/r_image_wavelet.h b/components/radiant_mod/r_image_wavelet.h new file mode 100644 index 00000000..7b1109df --- /dev/null +++ b/components/radiant_mod/r_image_wavelet.h @@ -0,0 +1,4 @@ +#pragma once +#include "r_image_load_common.h" + +void __cdecl Image_LoadWavelet(GfxImage *image, GfxImageFileHeader *fileHeader, const char *data, _D3DFORMAT format, int bytesPerPixel, unsigned int allocFlags); diff --git a/components/radiant_mod/r_init.h b/components/radiant_mod/r_init.h index df88de0f..f44531f5 100644 --- a/components/radiant_mod/r_init.h +++ b/components/radiant_mod/r_init.h @@ -1,7 +1,7 @@ #pragma once -static IDirect3DDevice9 **dx_device = (IDirect3DDevice9 **)0x13A15A0; -static int *g_disableRendering = (int *)0x00EE4F80; -static bool *r_supportCubedMipMaps = (bool *)0x013ACAD6; +static IDirect3DDevice9*& dx_device = *(IDirect3DDevice9 **)0x13A15A0; +static int& g_disableRendering = *(int *)0x00EE4F80; +static bool& r_supportCubedMipMaps = *(bool *)0x013ACAD6; const char *R_ErrorDescription(HRESULT hr); \ No newline at end of file diff --git a/components/radiant_mod/r_material_load_obj.cpp b/components/radiant_mod/r_material_load_obj.cpp index 9f29b04f..b5b18e4f 100644 --- a/components/radiant_mod/r_material_load_obj.cpp +++ b/components/radiant_mod/r_material_load_obj.cpp @@ -54,14 +54,14 @@ LPDIRECT3DVERTEXDECLARATION9 Material_BuildVertexDecl(MaterialStreamRouting *rou // memcpy(&elemTable[elemIndex], &endDecl, sizeof(D3DVERTEXELEMENT9)); - if (*dx_device) + if (dx_device) { LPDIRECT3DVERTEXDECLARATION9 decl = nullptr; - HRESULT hr = (*dx_device)->CreateVertexDeclaration(elemTable, &decl); + HRESULT hr = dx_device->CreateVertexDeclaration(elemTable, &decl); if (FAILED(hr)) { - (*g_disableRendering)++; + g_disableRendering++; Com_Error(ERR_FATAL, "dx.device->CreateVertexDeclaration(elemTable, &decl) failed: %s\n", R_ErrorDescription(hr)); } @@ -718,7 +718,7 @@ bool Material_DefaultSamplerSourceFromTable(const char *constantName, ShaderInde && Material_DefaultIndexRange(indexRange, sourceTable[sourceIndex].arrayCount, &argSource->indexRange)) { argSource->type = MTL_ARG_CODE_PIXEL_SAMPLER; - argSource->u.codeIndex = LOWORD(sourceTable[sourceIndex].source); + argSource->u.codeIndex = (unsigned short)sourceTable[sourceIndex].source; return true; } } @@ -902,9 +902,6 @@ bool Material_ParseConstantSource(MaterialShaderType shaderType, const char **te SRCLINE(3758) bool Material_DefaultConstantSourceFromTable(MaterialShaderType shaderType, const char *constantName, ShaderIndexRange *indexRange, CodeConstantSource *sourceTable, ShaderArgumentSource *argSource) { - _VERBOSE( printf("CONST: %s\n", constantName) ); - - int sourceIndex = 0; for (;; sourceIndex++) { @@ -915,8 +912,6 @@ bool Material_DefaultConstantSourceFromTable(MaterialShaderType shaderType, cons { unsigned int arrayCount; - _VERBOSE( printf("MATCH: %s\n", constantName) ); - if (sourceTable[sourceIndex].source < R_MAX_CODE_INDEX) { int count = (sourceTable[sourceIndex].arrayCount > 1) ? sourceTable[sourceIndex].arrayCount : 1; @@ -925,7 +920,6 @@ bool Material_DefaultConstantSourceFromTable(MaterialShaderType shaderType, cons else { ASSERT(sourceTable[sourceIndex].arrayCount == 0); - arrayCount = 4; } @@ -1595,8 +1589,6 @@ bool Material_ParseShaderArguments(const char **text, const char *shaderName, Ma &argDest.indexRange, &argSource)) { - _VERBOSE(printf("success\n")); - if (argSource.type == MTL_ARG_CODE_PIXEL_CONST) { if (argSource.u.codeIndex == 4) diff --git a/components/radiant_mod/radiant_mod.vcxproj b/components/radiant_mod/radiant_mod.vcxproj index fa8b2c68..ce91ae5c 100644 --- a/components/radiant_mod/radiant_mod.vcxproj +++ b/components/radiant_mod/radiant_mod.vcxproj @@ -21,15 +21,15 @@ DynamicLibrary true - v120_xp Unicode + v120_xp DynamicLibrary false - v120_xp true Unicode + v120_xp @@ -95,20 +95,29 @@ + + + + + + + + + @@ -116,6 +125,7 @@ + @@ -123,19 +133,29 @@ + + + + + + + + + + @@ -145,18 +165,15 @@ + - - - - true - Text - true - + + {5fb372dd-a7e1-4a4b-9a28-3ac02543e9e8} + diff --git a/components/radiant_mod/remote.cpp b/components/radiant_mod/remote.cpp new file mode 100644 index 00000000..04b56fea --- /dev/null +++ b/components/radiant_mod/remote.cpp @@ -0,0 +1,138 @@ +#include "stdafx.h" + +std::unordered_map g_PendingEntityUpdates; +std::unordered_map g_UpdateStrings; + +bool g_EnableUpdates = true; +bool g_CreatingEntity = false; + +void CreateEntityData2(entity_s *ent, char *buf, int len) +{ + char temp[512]; + memset(temp, 0, sizeof(temp)); + memset(buf, 0, len * sizeof(char)); + + strcat_s(buf, len, "{\n"); + + // Append each key and value + for (epair_s *pair = ent->epairs; pair; pair = pair->next) + { + // Skip origin since it is not synced all the time + if (!_stricmp(pair->key, "origin")) + continue; + + sprintf_s(temp, "\"%s\" \"%s\"\n", pair->key, pair->value); + strcat_s(buf, len, temp); + } + + // Append origin manually + if (!strstr(buf, "\"origin\"")) + { + sprintf_s(temp, "\"origin\" \"%.4f %.4f %.4f\"\n", ent->origin[0], ent->origin[1], ent->origin[2]); + strcat_s(buf, len, temp); + } + + strcat_s(buf, len, "}\n"); +} + +void Remote_EnableUpdates(bool Enable, bool ResetChanges) +{ + g_EnableUpdates = Enable; + + if (ResetChanges) + { + g_UpdateStrings.clear(); + g_PendingEntityUpdates.clear(); + } +} + +void Remote_CommitChanges() +{ + if (!g_EnableUpdates) + return; + + for (auto& itr : g_PendingEntityUpdates) + { + char buf[1024]; + CreateEntityData2(itr.first, buf, 1024); + + RemoteNet_SendSelect(g_UpdateStrings[itr.first].c_str()); + RemoteNet_SendUpdate(buf); + } + + // Restore the original selection + // TODO RemoteNet_SendSelect + + Remote_EnableUpdates(true, true); + g_CreatingEntity = false; +} + +void Remote_AddEntityCreate(entity_s *Entity) +{ + if (!g_EnableUpdates) + return; +} + +void Remote_AddEntityUpdate(entity_s *Entity) +{ + if (!g_EnableUpdates) + return; + + // We don't want the internal project entity (not actually a world entity) + if (Entity == g_qeglobals_d_project_entity) + return; + + char buf[512]; + CreateEntityData2(Entity, buf, ARRAYSIZE(buf)); + + g_UpdateStrings[Entity] = buf; + g_PendingEntityUpdates[Entity] = true; +} + +void Remote_AddEntityEpairUpdate(epair_s *Pairs) +{ + if (!g_EnableUpdates) + return; + + // + // We need to scan the entire entity list to find out + // who this epair pointer belongs to. + // + // Use a cached search to (hopefully) skip iterating the + // entire world. + // + bool found = false; + + for (auto& itr : g_PendingEntityUpdates) + { + if (itr.first->epairs == Pairs) + { + found = true; + break; + } + } + + if (found) + return; + + // NOTE: This search does include worldspawn + entity_s *itr = world_entity; + + if (itr) + { + do + { + // Continue scanning even if we found a match + if (itr->epairs == Pairs) + Remote_AddEntityUpdate(itr); + + itr = itr->next; + } while ((itr != nullptr) && (itr != world_entity)); + } +} + +void Remote_AddEntityDelete(entity_s *Entity) +{ + if (!g_EnableUpdates) + return; +} \ No newline at end of file diff --git a/components/radiant_mod/remote.h b/components/radiant_mod/remote.h new file mode 100644 index 00000000..3839fbcd --- /dev/null +++ b/components/radiant_mod/remote.h @@ -0,0 +1,8 @@ +#pragma once + +void Remote_EnableUpdates(bool Enable, bool ResetChanges = false); +void Remote_CommitChanges(); +void Remote_AddEntityCreate(entity_s *Entity); +void Remote_AddEntityUpdate(entity_s *Entity); +void Remote_AddEntityEpairUpdate(epair_s *Pairs); +void Remote_AddEntityDelete(entity_s *Entity); \ No newline at end of file diff --git a/components/radiant_mod/remote_net.cpp b/components/radiant_mod/remote_net.cpp new file mode 100644 index 00000000..3070a172 --- /dev/null +++ b/components/radiant_mod/remote_net.cpp @@ -0,0 +1,116 @@ +#include "stdafx.h" + +SOCKET g_RemoteSocket; + +void RemoteNet_SendSelect(const char *KVCommand) +{ + RadiantCommand cmd; + memset(&cmd, 0, sizeof(RadiantCommand)); + + cmd.type = RADIANT_COMMAND_SELECT; + strcpy_s(cmd.strCommand, KVCommand); + + RemoteNet_SendPacket(&cmd); +} + +void RemoteNet_SendUpdate(const char *KVCommand) +{ + RadiantCommand cmd; + memset(&cmd, 0, sizeof(RadiantCommand)); + + cmd.type = RADIANT_COMMAND_UPDATE_SELECTED; + strcpy_s(cmd.strCommand, KVCommand); + + RemoteNet_SendPacket(&cmd); +} + +void RemoteNet_UpdateCamera(float *Origin, float *Angles) +{ + RadiantCommand cmd; + memset(&cmd, 0, sizeof(RadiantCommand)); + + cmd.type = RADIANT_COMMAND_CAMERA; + + sprintf_s(cmd.strCommand, "{\n\"origin\" \"%.4f %.4f %.4f\"\n\"angles\" \"%.4f %.4f %.4f\"\n}", + Origin[0], Origin[1], Origin[2], + Angles[0], Angles[1], Angles[2]); + + RemoteNet_SendPacket(&cmd); +} + +void RemoteNet_SendPacket(RadiantCommand *Command) +{ + if (g_RemoteSocket == INVALID_SOCKET) + return; + + int ret = send(g_RemoteSocket, (const char *)Command, sizeof(RadiantCommand), 0); + + ASSERT(ret != SOCKET_ERROR); + + if (ret == SOCKET_ERROR) + { + closesocket(g_RemoteSocket); + g_RemoteSocket = INVALID_SOCKET; + } +} + +DWORD WINAPI RemoteNet_Thread(LPVOID) +{ + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) + ASSERT_MSG(false, "WSAStartup ERROR!"); + + while (true) + { + // Connect to the remote game TCP server + g_RemoteSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (g_RemoteSocket == INVALID_SOCKET) + { + printf("Failed to initialize client TCP socket\n"); + return 0; + } + + // Loop indefinitely until we successfully connect + while (true) + { + sockaddr_in remoteAddr; + remoteAddr.sin_family = AF_INET; + remoteAddr.sin_port = htons(3700); + remoteAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + if (connect(g_RemoteSocket, (sockaddr *)&remoteAddr, sizeof(sockaddr)) != SOCKET_ERROR) + break; + + Sleep(200); + } + + // Send updates until the game exits/connection is terminated + printf("NOTICE: Game connected\n"); + + while (true) + { + // Check for a socket error + if (g_RemoteSocket == INVALID_SOCKET) + break; + + int error = 0; + int len = sizeof(int); + int retval = getsockopt(g_RemoteSocket, SOL_SOCKET, SO_ERROR, (char *)&error, &len); + + if (retval == SOCKET_ERROR || error != 0) + break; + + // Send the camera packet every few milliseconds + if (CCamWnd::ActiveWindow) + RemoteNet_UpdateCamera(CCamWnd::ActiveWindow->cameraOrigin, CCamWnd::ActiveWindow->cameraAngles); + + Sleep(50); + } + + printf("NOTICE: Game disconnected\n"); + closesocket(g_RemoteSocket); + } + + return 0; +} \ No newline at end of file diff --git a/components/radiant_mod/remote_net.h b/components/radiant_mod/remote_net.h new file mode 100644 index 00000000..8d4a9036 --- /dev/null +++ b/components/radiant_mod/remote_net.h @@ -0,0 +1,25 @@ +#pragma once + +enum RadiantCommandType +{ + RADIANT_COMMAND_SELECT = 0x0, + RADIANT_COMMAND_DESELECT = 0x1, + RADIANT_COMMAND_UPDATE_SELECTED = 0x2, + RADIANT_COMMAND_UPDATE = 0x3, + RADIANT_COMMAND_CREATE = 0x4, + RADIANT_COMMAND_DELETE = 0x5, + RADIANT_COMMAND_CAMERA = 0x6, +}; + +struct RadiantCommand +{ + RadiantCommandType type; + int liveUpdateId; + char strCommand[512]; +}; + +void RemoteNet_SendSelect(const char *KVCommand); +void RemoteNet_SendUpdate(const char *KVCommand); +void RemoteNet_UpdateCamera(float *Origin, float *Angles); +void RemoteNet_SendPacket(RadiantCommand *Command); +DWORD WINAPI RemoteNet_Thread(LPVOID); \ No newline at end of file diff --git a/components/radiant_mod/stdafx.h b/components/radiant_mod/stdafx.h index 98e09cb0..deb461ca 100644 --- a/components/radiant_mod/stdafx.h +++ b/components/radiant_mod/stdafx.h @@ -6,6 +6,10 @@ #include #include #include +#include +#include +#pragma comment(lib, "ws2_32.lib") + #define RADIANT_MOD 1 #define BO1_SEMI_NATIVE_BUILD 1 @@ -30,10 +34,14 @@ // // Shared files // -#include "../shared/utility.h" +#include "../shared/shared_utility.h" +#include "../shared/shared_version.h" #include "../shared/minidx9/Include/d3dx9.h" #pragma comment(lib, "../shared/minidx9/Lib/x86/d3dx9.lib") +#include "../shared/detours/Detours.h" +#pragma comment(lib, "detours.lib") + // // Nvidia Nsight patches // @@ -50,6 +58,7 @@ #define SRCLINE(x) #define CHECK_SIZE(Type, Size) static_assert(sizeof(Type) == Size, "Invalid type size!"); +#include "d3d9ex.h" #include "CWinApp.h" #include "win_main.h" #include "win_splash.h" @@ -78,6 +87,16 @@ #include "kvs.h" #include "ent_light.h" +#include "brush.h" +#include "entity.h" +#include "undo.h" +#include "map.h" +#include "drag.h" +#include "remote.h" +#include "remote_net.h" #include "afx.h" #include "afx_colors.h" +#include "afx_resource.h" + +#include "CCamWnd.h" \ No newline at end of file diff --git a/components/radiant_mod/undo.cpp b/components/radiant_mod/undo.cpp new file mode 100644 index 00000000..57a26af7 --- /dev/null +++ b/components/radiant_mod/undo.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" + +void(*Undo_Start_o)(const char *undoName); +void(*Undo_End_o)(); + +void hk_Undo_Start(const char *undoName) +{ + //if (!_stricmp(undoName, "create entity")) + // Remote_AddEntityCreate(nullptr); + + Undo_Start_o(undoName); +} + +void hk_Undo_End() +{ + Undo_End_o(); + Remote_CommitChanges(); +} \ No newline at end of file diff --git a/components/radiant_mod/undo.h b/components/radiant_mod/undo.h new file mode 100644 index 00000000..1be987d8 --- /dev/null +++ b/components/radiant_mod/undo.h @@ -0,0 +1,7 @@ +#pragma once + +extern void(*Undo_Start_o)(const char *undoName); +extern void(*Undo_End_o)(); + +void hk_Undo_Start(const char *undoName); +void hk_Undo_End(); \ No newline at end of file diff --git a/components/radiant_mod/win_main.cpp b/components/radiant_mod/win_main.cpp index 4a78d5ad..bc3422ea 100644 --- a/components/radiant_mod/win_main.cpp +++ b/components/radiant_mod/win_main.cpp @@ -66,3 +66,115 @@ BOOL __stdcall SetWindowPlacement_Hidden(HWND hWnd, WINDOWPLACEMENT *lpwndpl) lpwndpl->showCmd = SW_HIDE; return SetWindowPlacement(hWnd, lpwndpl); } + +void _cdecl MRU_AddFile(void* pmem, LPCSTR file) +{ + static DWORD dwCall = 0x004A5C70; + + _asm + { + push file + mov esi, pmem + call dwCall + add esp, 4 + } +} + +void __cdecl MRU_UpdateListMenu(void* pmem, HMENU hMenu) +{ + static DWORD dwCall = 0x004A5DB0; + + _asm + { + push hMenu + mov edi, pmem + call dwCall + add esp, 4 + } +} + +int SplitArgs(char* cmd_line, int bufferSize, char** buffer) +{ + static DWORD dwCall = 0x004A85A0; + int argc = 0; + + _asm + { + mov esi, buffer + mov edi, bufferSize + mov ecx, cmd_line + call dwCall + mov argc, eax + } + + return argc; +} + +int ExecuteCommand(char** argv) +{ + static DWORD dwCall = 0x0042D280; + int result = 0; + + _asm + { + mov eax, argv + call dwCall + mov result, eax + } + + return result; +} + +void HandleLaunchArgs(const char* cmdline, void* window) +{ + char cmd_line[0x10000]; + strncpy(cmd_line, cmdline, 0x10000); + cmd_line[0xFFFF] = '\0'; + + char* argv[128]; + int argc = SplitArgs(cmd_line, 128, argv); + + _ASSERT(argc >= 1); + + for (int i = 1; i < argc; i++) + { + if (*argv[i] == '+') + { + int r = ExecuteCommand(&argv[i]); + i -= (!r) ? 1 : 0; + continue; + } + + const char* ext = FS_GetExtensionSubString(argv[1]); + if (ext == NULL) + continue; + + if (strcmp(ext, ".map") == 0) + { + // Add the map to the MRU registry + MRU_AddFile(*(void**)0x02857980, argv[1]); + + // Actually load the map + Map_LoadFile_o(argv[1]); + + // Update the MRU menu list + HMENU menu = GetMenu(*((HWND*)window + 8)); + HMENU subMenu = GetSubMenu(menu, 0); + MRU_UpdateListMenu(*(void**)0x02857980, subMenu); + } + } +} + +void __declspec(naked) hk_HandleLaunchArgs(void) +{ + _asm + { + pushad + push edi // window + push eax // cmd_line + call HandleLaunchArgs + add esp, 4 + popad + } +} + diff --git a/components/radiant_mod/win_main.h b/components/radiant_mod/win_main.h index a7b0c2ab..ebd0590b 100644 --- a/components/radiant_mod/win_main.h +++ b/components/radiant_mod/win_main.h @@ -14,3 +14,5 @@ void mfh_WinMain(); // BOOL __stdcall SetWindowPlacement_Hidden(HWND hWnd, WINDOWPLACEMENT *lpwndpl); static const void* pfn_SetWindowPlacement_Hidden = &SetWindowPlacement_Hidden; + +void hk_HandleLaunchArgs(void); diff --git a/components/radiant_mod/xmodel_load_obj.cpp b/components/radiant_mod/xmodel_load_obj.cpp index c16287a9..59e19f9d 100644 --- a/components/radiant_mod/xmodel_load_obj.cpp +++ b/components/radiant_mod/xmodel_load_obj.cpp @@ -114,4 +114,50 @@ bool __declspec(naked) hk_XModelLoadConfigFile() pop ebp retn } -} \ No newline at end of file +} + +XModel *__cdecl XModelLoad(const char *name, void *(__cdecl *Alloc)(int), void *(__cdecl *AllocColl)(int)) +{ + XModel* result = NULL; + + _asm + { + push Alloc + mov edx, AllocColl + mov ecx, name + mov ebx, 0x004E0210 + call ebx + add esp, 4 + mov result, eax + } + + return result; +} + +XModel *__cdecl R_RegisterSkyboxModel(const char *name) +{ + return XModelPrecache_Skybox(name, Hunk_AllocXModelPrecache, Hunk_AllocXModelPrecacheColl); +} + +XModel *__cdecl XModelPrecache_Skybox(const char *name, void *(__cdecl *Alloc)(int), void *(__cdecl *AllocColl)(int)) +{ + return XModelPrecache_Skybox_LoadObj(name, Alloc, AllocColl); +} + +XModel *__cdecl XModelPrecache_Skybox_LoadObj(const char *name, void *(__cdecl *Alloc)(int), void *(__cdecl *AllocColl)(int)) +{ + XModel* model = (XModel*)Hunk_FindDataForFile(5, name); + + if (model) + return model; + + model = XModelLoad(name, Alloc, AllocColl); + if (model) + { + model->name = Hunk_SetDataForFile(5, name, model, Alloc); + return model; + } + + Com_PrintError(19, "ERROR: Cannot find xmodel '%s'.\n", name); + return NULL; +} diff --git a/components/radiant_mod/xmodel_load_obj.h b/components/radiant_mod/xmodel_load_obj.h index 7b12b76e..2aa64892 100644 --- a/components/radiant_mod/xmodel_load_obj.h +++ b/components/radiant_mod/xmodel_load_obj.h @@ -1,5 +1,21 @@ #pragma once +struct XModel +{ + const char* name; + // ... +}; + +typedef void *(__cdecl* Hunk_AllocXModelPrecache_t)(int size); +static Hunk_AllocXModelPrecache_t Hunk_AllocXModelPrecache = (Hunk_AllocXModelPrecache_t)0x00519FD0; +static Hunk_AllocXModelPrecache_t Hunk_AllocXModelPrecacheColl = (Hunk_AllocXModelPrecache_t)0x00519FE0; + bool XModelLoadConfigFile(const char **pos, int name, int config); -bool hk_XModelLoadConfigFile(); \ No newline at end of file +bool hk_XModelLoadConfigFile(); + +XModel *__cdecl XModelLoad(const char *name, void *(__cdecl *Alloc)(int), void *(__cdecl *AllocColl)(int)); + +XModel *__cdecl R_RegisterSkyboxModel(const char *name); +XModel *__cdecl XModelPrecache_Skybox(const char *name, void *(__cdecl *Alloc)(int), void *(__cdecl *AllocColl)(int)); +XModel *__cdecl XModelPrecache_Skybox_LoadObj(const char *name, void *(__cdecl *Alloc)(int), void *(__cdecl *AllocColl)(int)); diff --git a/components/resource/raw/createfx.cfg b/components/resource/raw/createfx.cfg new file mode 100644 index 00000000..7e655ac9 --- /dev/null +++ b/components/resource/raw/createfx.cfg @@ -0,0 +1,34 @@ +set com_cfg_readOnly 1 +unbindall +exec default +set loc_warningsAsErrors 0 +set loc_warnings 0 +set createfx on +set developer 1 +set developer_script 1 +take all +map_restart + +unbind c +unbind ctrl +unbind space +unbind f + +//keybindings +bind u "ufo" +bind n "noclip" +bind t "toggle timescale 1 100" +bind y "toggle timescale 1 .25" +bind p "toggle fx_profile 0 1; toggle cg_drawHUD 0 1; toggle cg_draw2D 0 1" +bind h "toggle fx_enable 0 1" +bind m "toggle r_showtris" +bind \ "toggle cl_paused 0 2" +bind home toggle r_lockpvs 1 0 +bind pgdn toggle snd_drawinfo 0 1 2 3 +bind pgup toggle snd_draw3d 2 0 + + +set con_minicon 0 +set con_showchannel script +set bg_fallDamageMaxHeight 12000 +set bg_fallDamageMinHeight 10000 \ No newline at end of file diff --git a/components/scripts/README.md b/components/scripts/README.md new file mode 100644 index 00000000..d7e1ddbe --- /dev/null +++ b/components/scripts/README.md @@ -0,0 +1,4 @@ +# /components/scripts +This directory contains various setup scripts that automatically generate / regenerate / repair / etc. various files for the mod tools package + +For installation, these files should be installed to /bin/scripts diff --git a/components/scripts/csv.bat b/components/scripts/csv.bat new file mode 100644 index 00000000..9f2a1266 --- /dev/null +++ b/components/scripts/csv.bat @@ -0,0 +1,17 @@ +::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:: This script should automatically call any child scripts to setup all assets needed for the tools :: +::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +@echo off + +set GAME_DIR=..\.. +set ASSET_UTIL=%GAME_DIR%\bin\asset_util.exe + +set ASSET_UTIL_FLAGS= + +set OVERWRITE=1 +if [%OVERWRITE%] == [1] (set ASSET_UTIL_FLAGS=-o) + +"%ASSET_UTIL%" csvgen %ASSET_UTIL_FLAGS% --aitype --character --xmodelalias * + +set OVERWRITE= diff --git a/components/scripts/ents.bat b/components/scripts/ents.bat new file mode 100644 index 00000000..0c3dd608 --- /dev/null +++ b/components/scripts/ents.bat @@ -0,0 +1,118 @@ +::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:: Automatically generate Radiant compatible map files from the ent strings of all game maps :: +::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +@echo off + +set GAME_DIR=..\.. +set ASSET_UTIL=%GAME_DIR%\bin\asset_util.exe + +set FF_DIR=%GAME_DIR%\zone\Common\ + +set SP_MAPS=cuba vorkuta pentagon flashpoint khe_sanh hue_city kowloon fullahead creek_1 river wmd_sr71 wmd pow rebirth int_escape underwaterbase +set ZM_MAPS=zombie_theater zombie_pentagon zombietron zombie_cosmodrome zombie_coast zombie_temple zombie_moon zombie_cod5_asylum zombie_cod5_factory zombie_cod5_prototype zombie_cod5_sumpf +set MP_MAPS=mp_area51 mp_array mp_berlinwall2 mp_cairo mp_cosmodrome mp_cracked mp_crisis mp_discovery mp_drivein mp_duga mp_firingrange mp_golfcourse mp_gridlock mp_hanoi mp_havoc mp_hotel mp_kowloon mp_mountain mp_nuked mp_outskirts mp_radiation mp_russianbase mp_silo mp_stadium mp_villa mp_zoo + +set MAP= + +set OUT_DIR=%GAME_DIR%\map_source\_prefabs\maps\ +if not exist %OUT_DIR% mkdir %OUT_DIR% + +(for %%m in (%SP_MAPS%) do ( + set MAP=%%m + if [%OVERWRITE%] == [] ( + if not exist "%OUT_DIR%%%m.map" (call :export) else (echo Skipping %%m ...) + ) else ( + call :export + ) +)) + +(for %%m in (%ZM_MAPS%) do ( + set MAP=%%m + if [%OVERWRITE%] == [] ( + if not exist "%OUT_DIR%%%m.map" (call :export) else (echo Skipping %%m ...) + ) else ( + call :export + ) +)) + +set OUT_DIR=%GAME_DIR%\map_source\_prefabs\maps\mp\ +if not exist %OUT_DIR% mkdir %OUT_DIR% +(for %%m in (%MP_MAPS%) do ( + set MAP=%%m + if [%OVERWRITE%] == [] ( + if not exist "%OUT_DIR%%%m.map" (call :export) else (echo Skipping %%m ...) + ) else ( + call :export + ) +)) + +goto:eof + +:export +echo Exporting %MAP% ... +"%ASSET_UTIL%" ents "%FF_DIR%%MAP%.ff" >> "%OUT_DIR%%MAP%.map" + +:: If Asset Util did not exit correctly +:: we need to delete the map file +IF %ERRORLEVEL% NEQ 0 ( + echo ERROR^(%ERRORLEVEL%^): Deleting file... + del "%OUT_DIR%%MAP%.map" +) + +:: +::cuba +::vorkuta +::pentagon +::flashpoint +::khe_sanh +::hue_city +::kowloon +::fullahead +::creek_1 +::river +::wmd_sr71 +::wmd +::pow +::rebirth +::int_escape +::underwaterbase +:: +::zombie_theater +::zombie_pentagon +::zombietron +::zombie_cosmodrome +::zombie_coast +::zombie_temple +::zombie_moon +::zombie_cod5_asylum +::zombie_cod5_factory +::zombie_cod5_prototype +::zombie_cod5_sumpf +:: +::mp_area51 +::mp_array +::mp_berlinwall2 +::mp_cairo +::mp_cosmodrome +::mp_cracked +::mp_crisis +::mp_discovery +::mp_drivein +::mp_duga +::mp_firingrange +::mp_golfcourse +::mp_gridlock +::mp_hanoi +::mp_havoc +::mp_hotel +::mp_kowloon +::mp_mountain +::mp_nuked +::mp_outskirts +::mp_radiation +::mp_russianbase +::mp_silo +::mp_stadium +::mp_villa +::mp_zoo diff --git a/components/scripts/setup.bat b/components/scripts/setup.bat new file mode 100644 index 00000000..d27e39d0 --- /dev/null +++ b/components/scripts/setup.bat @@ -0,0 +1,22 @@ +::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:: This script should automatically call any child scripts to setup all assets needed for the tools :: +::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +@echo off + +set GAME_DIR=..\.. +set ASSET_UTIL=%GAME_DIR%\bin\asset_util.exe + +set ASSET_UTIL_FLAGS= + +set OVERWRITE=1 +if [%OVERWRITE%] == [1] (set ASSET_UTIL_FLAGS=-o) + +"%ASSET_UTIL%" extract-iwd %ASSET_UTIL_FLAGS% --all +"%ASSET_UTIL%" extract-ff %ASSET_UTIL_FLAGS% --all + +csvgen.bat +soundalias.bat +ents.bat + +set OVERWRITE= diff --git a/components/scripts/soundalias.bat b/components/scripts/soundalias.bat new file mode 100644 index 00000000..1d421697 --- /dev/null +++ b/components/scripts/soundalias.bat @@ -0,0 +1,56 @@ +::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:: Automatically generate Radiant compatible map files from the ent strings of all game maps :: +::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +@echo off + +set GAME_DIR=..\.. +set ASSET_UTIL=%GAME_DIR%\bin\asset_util.exe +set LAUNCHER_LDR=%GAME_DIR%\bin\launcher_ldr.exe +set GAME_MOD=%GAME_DIR%\bin\game_mod.dll +set BLACKOPS=%GAME_DIR%\BlackOps.exe + +set LAUNCH_ARGS= + +set FF_DIR=%GAME_DIR%\zone\Common\ + +set SP_MAPS=cuba vorkuta pentagon flashpoint khe_sanh hue_city kowloon fullahead creek_1 river wmd_sr71 wmd pow rebirth int_escape underwaterbase +set ZM_MAPS=zombie_theater zombie_pentagon zombietron zombie_cosmodrome zombie_coast zombie_temple zombie_moon zombie_cod5_asylum zombie_cod5_factory zombie_cod5_prototype zombie_cod5_sumpf +set MP_MAPS=mp_area51 mp_array mp_berlinwall2 mp_cairo mp_cosmodrome mp_cracked mp_crisis mp_discovery mp_drivein mp_duga mp_firingrange mp_golfcourse mp_gridlock mp_hanoi mp_havoc mp_hotel mp_kowloon mp_mountain mp_nuked mp_outskirts mp_radiation mp_russianbase mp_silo mp_stadium mp_villa mp_zoo + +set MAP= + +::set OUT_DIR=%GAME_DIR%\map_source\_prefabs\maps\ +::if not exist %OUT_DIR% mkdir %OUT_DIR% + +(for %%m in (%SP_MAPS%) do ( + set MAP=%%m + call :export +)) + +(for %%m in (%ZM_MAPS%) do ( + set MAP=%%m + call :export +)) + +(for %%m in (%SP_MAPS%) do ( + set MAP=%%m + call :export +)) + +goto:eof + +:export_mp +set MAP=so_dummy_%MAP% +set LAUNCH_ARGS=+set g_loadScripts 0 + +:export +echo Ripping Soundaliases for %MAP% ... +:: Start the game process - automatically terminated by Asset Util +start %LAUNCHER_LDR% %GAME_MOD% %BLACKOPS% +devmap %MAP% %LAUNCH_ARGS% + +:: Launch Asset Util and make sure the game process was started +:: The loop automatically continues when asset util exits +:: Must be run as blocking to ensure that it prints to the same console as the batch script +%ASSET_UTIL% rip --waitForProcess --waitForMap --killProcess +taskkill /F /IM launcher_ldr.exe diff --git a/components/shared/detours/AsmGen.cpp b/components/shared/detours/AsmGen.cpp new file mode 100644 index 00000000..ce729a94 --- /dev/null +++ b/components/shared/detours/AsmGen.cpp @@ -0,0 +1,101 @@ +#include +#include +#include "AsmGen.h" + +AsmGen::AsmGen(ULONGLONG Ip, AsmGenType Type) +{ + m_Ip = Ip; + m_X64Code = (Type == ASMGEN_64); + m_StreamData = nullptr; + + m_Stream.reserve(64); +} + +AsmGen::~AsmGen() +{ + if (m_StreamData) + free(m_StreamData); + + // m_Stream is cleaned up by its own destructor +} + +bool AsmGen::AddCode(char *Instruction) +{ + asmjit::ArchInfo::Type type = (m_X64Code) ? + asmjit::ArchInfo::kTypeX64 : asmjit::ArchInfo::kTypeX32; + + // Initialize CodeHolder with architecture + asmjit::CodeInfo ci; + ci.init(type, 0, m_Ip); + + // Initialize CodeHolder + asmjit::CodeHolder code; + asmjit::Error err = code.init(ci); + + if (err) + { + __debugbreak(); + return false; + } + + // Parse instructions + asmjit::X86Assembler a(&code); + err = asmtk::AsmParser(&a).parse(Instruction, strlen(Instruction)); + + if (err) + { + __debugbreak(); + return false; + } + + // Send each assembled byte to the stream + code.sync(); + asmjit::CodeBuffer& buffer = code.getSectionEntry(0)->getBuffer(); + + for (unsigned int i = 0; i < buffer.getLength(); i++) + m_Stream.push_back(buffer.getData()[i]); + + // Shift the internal instruction pointer + m_Ip += buffer.getLength(); + return true; +} + +bool AsmGen::AddCode(const char *Format, ...) +{ + char buffer[2048]; + va_list va; + + va_start(va, Format); + _vsnprintf_s(buffer, _TRUNCATE, Format, va); + va_end(va); + + return AddCode(buffer); +} + +bool AsmGen::AddCodeArray(const char **Instructions, size_t Count) +{ + for (size_t i = 0; i < Count; i++) + { + if (!AddCode(Instructions[i])) + return false; + } + + return true; +} + +BYTE *AsmGen::GetStream(bool Free) +{ + BYTE *data = (BYTE *)malloc(m_Stream.size() * sizeof(BYTE)); + + for (size_t i = 0; i < m_Stream.size(); i++) + data[i] = m_Stream.at(i); + + m_StreamData = (Free) ? data : nullptr; + + return data; +} + +size_t AsmGen::GetStreamLength() +{ + return m_Stream.size(); +} \ No newline at end of file diff --git a/components/shared/detours/AsmGen.h b/components/shared/detours/AsmGen.h new file mode 100644 index 00000000..e2015dfe --- /dev/null +++ b/components/shared/detours/AsmGen.h @@ -0,0 +1,32 @@ +#pragma once + +#include "stdafx.h" + +enum AsmGenType +{ + ASMGEN_32, + ASMGEN_64, +}; + +class AsmGen +{ +public: + +private: + ULONGLONG m_Ip; + bool m_X64Code; + std::vector m_Stream; + BYTE *m_StreamData; + +public: + AsmGen(BYTE *Ip, AsmGenType Type) : AsmGen((ULONGLONG)Ip, Type) {} + AsmGen(ULONGLONG Ip, AsmGenType Type); + ~AsmGen(); + + bool AddCode(char *Instruction); + bool AddCode(const char *Format, ...); + bool AddCodeArray(const char **Instructions, size_t Count); + + BYTE *GetStream(bool Free = true); + size_t GetStreamLength(); +}; \ No newline at end of file diff --git a/components/shared/detours/Detours.cpp b/components/shared/detours/Detours.cpp new file mode 100644 index 00000000..ca9b75e6 --- /dev/null +++ b/components/shared/detours/Detours.cpp @@ -0,0 +1,85 @@ +#include "stdafx.h" + +namespace Detours +{ + uint32_t GlobalOptions; + + void SetGlobalOptions(uint32_t Options) + { + InterlockedExchange((volatile LONG *)&GlobalOptions, Options & OPT_MASK); + } + + uint32_t GetGlobalOptions() + { + return GlobalOptions; + } + + uint8_t *DetourAlignAddress(uint64_t Address, uint8_t Align) + { + if (Address % Align != 0) + Address += Align - Address % 8; + + return (uint8_t *)Address; + } + + bool DetourAtomicCopy4X8(uint8_t *Target, uint8_t *Memory, sizeptr_t Length) + { + // Buffer to hold temporary opcodes + char buffer[8]; + + // Only 4/8byte sizes supported + if(Length > sizeof(buffer)) + return false; + + DWORD dwOld = 0; + if (!VirtualProtect(Target, Length, PAGE_EXECUTE_READWRITE, &dwOld)) + return false; + + if(Length <= 4) + { + // Copy the old data + memcpy(&buffer, Target, 4); + + // Rewrite it with the new data + memcpy(&buffer, Memory, Length); + + // Write all 4 bytes at once + InterlockedExchange((volatile LONG *)Target, *(LONG *)&buffer); + } + else if(Length <= 8) + { + // Copy the old data + memcpy(&buffer, Target, 8); + + // Rewrite it with the new data + memcpy(&buffer, Memory, Length); + + // Write all 8 bytes at once + _intrinInterlockedExchange64((volatile LONGLONG *)Target, *(LONGLONG *)&buffer); + } + + // Ignore if this fails, the memory was copied either way + VirtualProtect(Target, Length, dwOld, &dwOld); + + return true; + } + + bool DetourCopyMemory(uint8_t *Target, uint8_t *Memory, sizeptr_t Length) + { + DWORD dwOld = 0; + if (!VirtualProtect(Target, Length, PAGE_EXECUTE_READWRITE, &dwOld)) + return false; + + memcpy(Target, Memory, Length); + + // Ignore if this fails, the memory was copied either way + VirtualProtect(Target, Length, dwOld, &dwOld); + + return true; + } + + bool DetourFlushCache(uint8_t *Target, sizeptr_t Length) + { + return FlushInstructionCache(GetCurrentProcess(), Target, Length) != FALSE; + } +} \ No newline at end of file diff --git a/components/shared/detours/Detours.h b/components/shared/detours/Detours.h index 3ebb1113..134c4ccd 100644 --- a/components/shared/detours/Detours.h +++ b/components/shared/detours/Detours.h @@ -2,47 +2,43 @@ namespace Detours { - const static uint32_t DISASM_MAX_INSTRUCTIONS = 20; // Maximum number of instructions to decode at once + // --------- Typedef --------- + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; - const static uint32_t OPT_MASK = 0xFFF; // Mask for all options - const static uint32_t OPT_NONE = 0x000; // No options - const static uint32_t OPT_INSTRUCTION_PADDING = 0x100; // Add useless instructions to help hide the detour +#ifdef _M_IX86 + typedef __w64 unsigned long sizeptr_t; +#else + typedef unsigned long long sizeptr_t; +#endif // _M_IX86 + // --------------------------- - enum class X86Option - { - USE_JUMP, // jmp
; - USE_CALL, // call
; - USE_JUMP_PTR, // jmp dword ptr
; - USE_PUSH_RET, // push
; retn; - }; + const static uint32_t DISASM_MAX_INSTRUCTIONS = 50; // Maximum number of instructions to decode at once -#ifdef _WIN64 - enum class X64Option - { - USE_PUSH_RET, // push ; push ; ret; - }; -#endif + const static uint32_t OPT_MASK = 0xFFF; // Mask for all options + const static uint32_t OPT_NONE = 0x000; // No options struct JumpTrampolineHeader { uint32_t Magic; // Used to verify the header uint32_t Random; // Variable to change the code/data hash - uint8_t *CodeOffset; // Code offset that was hooked - uint8_t *DetourOffset; // User function offset + uint8_t *CodeOffset; // Offset, in code, that was hooked: "target" + uint8_t *DetourOffset; // User function that is called: "destination" - uint32_t InstructionLength; // Length of the instructions that were replaced - uint8_t *InstructionOffset; // Where the backed-up instructions are placed + size_t InstructionLength; // Length of the instructions that were replaced + uint8_t *InstructionOffset; // Where the backed-up instructions are - uint32_t TrampolineLength; // Length of the trampoline + size_t TrampolineLength; // Length of the trampoline uint8_t *TrampolineOffset; // Code offset where 'jmp (q/d)word ptr ' occurs // Anything after this struct is null data or pure code (instructions/trampoline) }; - extern uint32_t GlobalOptions; - void SetGlobalOptions(uint32_t Options); + uint32_t GetGlobalOptions(); uint8_t *DetourAlignAddress(uint64_t Address, uint8_t Align); @@ -50,7 +46,16 @@ namespace Detours bool DetourCopyMemory(uint8_t *Target, uint8_t *Memory, sizeptr_t Length); bool DetourFlushCache(uint8_t *Target, sizeptr_t Length); -#if defined(_WIN32) || defined(_WIN64) +#ifdef _M_IX86 + enum class X86Option + { + USE_JUMP, // jmp
; + USE_CALL, // call
; + USE_EAX_JUMP, // mov eax,
; jmp eax; + USE_JUMP_PTR, // jmp dword ptr [
]; + USE_PUSH_RET, // push
; retn; + }; + namespace X86 { // Redirects a single static function to another @@ -58,7 +63,7 @@ namespace Detours // Redirects a class member function (__thiscall) to another template - uint8_t *DetourClassFunction(uint8_t *Target, T Detour, X86Option Options = X86Option::USE_JUMP) + uint8_t *DetourFunctionClass(uint8_t *Target, T Detour, X86Option Options = X86Option::USE_JUMP) { return DetourFunction(Target, *(uint8_t **)&Detour, Options); } @@ -69,20 +74,34 @@ namespace Detours // Redirects an index in a virtual table uint8_t *DetourVTable(uint8_t *Target, uint8_t *Detour, uint32_t TableIndex); + // Redirects a class member virtual function (__thiscall) to another + template + uint8_t *DetourClassVTable(uint8_t *Target, T Detour, uint32_t TableIndex) + { + return DetourVTable(Target, *(uint8_t **)&Detour, TableIndex); + } + // Removes a detoured virtual table index bool VTableRemove(uint8_t *Target, uint8_t *Function, uint32_t TableIndex); void DetourWriteStub(JumpTrampolineHeader *Header); bool DetourWriteJump(JumpTrampolineHeader *Header); bool DetourWriteCall(JumpTrampolineHeader *Header); + bool DetourWriteEaxJump(JumpTrampolineHeader *Header); bool DetourWriteJumpPtr(JumpTrampolineHeader *Header); bool DetourWritePushRet(JumpTrampolineHeader *Header); uint32_t DetourGetHookLength(X86Option Options); } -#endif // defined(_WIN32) || defined(_WIN64) +#endif // _M_IX86 #ifdef _WIN64 + enum class X64Option + { + USE_PUSH_RET, // push ; [rsp+4h] = ; retn; + USE_RAX_JUMP, // mov rax,
; jmp rax; + }; + namespace X64 { // Redirects a single static function to another @@ -90,7 +109,7 @@ namespace Detours // Redirects a class member function (__thiscall) to another template - uint8_t *DetourClassFunction(uint8_t *Target, T Detour, X64Option Options = X64Option::USE_PUSH_RET) + uint8_t *DetourFunctionClass(uint8_t *Target, T Detour, X64Option Options = X64Option::USE_PUSH_RET) { return DetourFunction(Target, *(uint8_t **)&Detour, Options); } @@ -101,9 +120,20 @@ namespace Detours // Redirects an index in a virtual table uint8_t *DetourVTable(uint8_t *Target, uint8_t *Detour, uint32_t TableIndex); + // Redirects a class member virtual function (__thiscall) to another + template + uint8_t *DetourClassVTable(uint8_t *Target, T Detour, uint32_t TableIndex) + { + return DetourVTable(Target, *(uint8_t **)&Detour, TableIndex); + } + // Removes a detoured virtual table index bool VTableRemove(uint8_t *Target, uint8_t *Function, uint32_t TableIndex); + void DetourWriteStub(JumpTrampolineHeader *Header); + bool DetourWriteRaxJump(JumpTrampolineHeader *Header); + bool DetourWritePushRet(JumpTrampolineHeader *Header); + uint32_t DetourGetHookLength(X64Option Options); } #endif // _WIN64 diff --git a/components/shared/detours/Detours32.cpp b/components/shared/detours/Detours32.cpp new file mode 100644 index 00000000..7b785e22 --- /dev/null +++ b/components/shared/detours/Detours32.cpp @@ -0,0 +1,253 @@ +#include "stdafx.h" + +#ifdef _M_IX86 +namespace Detours +{ + namespace X86 + { + #define HEADER_MAGIC '@D32' + + #define MAX_INSTRUCT_SIZE 0x08 + + #define JUMP_LENGTH_32 0x05 // jmp + #define CALL_LENGTH_32 0x05 // call + #define JUMP_EAX_LENGTH_32 0x07 // mov eax, ; jmp eax + #define JUMP_PTR_LENGTH_32 0x06 // jmp dword ptr + #define PUSH_RET_LENGTH_32 0x06 // push ; retn + + #define ALIGN_32(x) DetourAlignAddress((uint64_t)(x), 0x4); + + uint8_t *DetourFunction(uint8_t *Target, uint8_t *Detour, X86Option Options) + { + // Decode the data + _DecodedInst decodedInstructions[DISASM_MAX_INSTRUCTIONS]; + uint32_t decodedCount = 0; + + _DecodeResult res = distorm_decode((_OffsetType)Target, (const unsigned char *)Target, 32, Decode32Bits, decodedInstructions, DISASM_MAX_INSTRUCTIONS, &decodedCount); + + // Check if decoding failed + if (res != DECRES_SUCCESS) + return nullptr; + + // Calculate the hook length from options + uint32_t totalInstrSize = 0; + uint32_t neededSize = DetourGetHookLength(Options); + + // Calculate how many instructions are needed to place the jump + for (uint32_t i = 0; i < decodedCount; i++) + { + totalInstrSize += decodedInstructions[i].size; + + if (totalInstrSize >= neededSize) + break; + } + + // Unable to find a needed length + if (totalInstrSize < neededSize) + return nullptr; + + // Allocate the trampoline data + uint32_t allocSize = 0; + allocSize += sizeof(JumpTrampolineHeader); // Base structure + allocSize += totalInstrSize; // Size of the copied instructions + allocSize += MAX_INSTRUCT_SIZE; // Maximum instruction size + allocSize += MAX_INSTRUCT_SIZE; // Maximum instruction size + allocSize += 0x64; // Padding for any memory alignment + + uint8_t *jumpTrampolinePtr = (uint8_t *)VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + if (!jumpTrampolinePtr) + return nullptr; + + // Fill out the header + JumpTrampolineHeader *header = (JumpTrampolineHeader *)jumpTrampolinePtr; + + header->Magic = HEADER_MAGIC; + header->Random = GetCurrentProcessId() + GetCurrentThreadId() + totalInstrSize; + + header->CodeOffset = Target; + header->DetourOffset = Detour; + + header->InstructionLength = totalInstrSize; + header->InstructionOffset = ALIGN_32(jumpTrampolinePtr + sizeof(JumpTrampolineHeader)); + + // Apply any changes because of the different IP + { + //BYTE *data = Reassembler::Reassemble32((uint32_t)header->InstructionOffset, decodedInstructions, decodedInstructionsCount, &header->InstructionLength); + + // Copy the fixed instructions over + //memcpy(header->InstructionOffset, data, header->InstructionLength); + memcpy(header->InstructionOffset, Target, header->InstructionLength); + + // Free unneeded data + //Reassembler::Free(data); + } + + header->TrampolineLength = JUMP_LENGTH_32; + header->TrampolineOffset = ALIGN_32(header->InstructionOffset + header->InstructionLength + JUMP_LENGTH_32 + header->TrampolineLength); + + // Write the assembly in the allocation block + DetourWriteStub(header); + + bool result = true; + + switch (Options) + { + case X86Option::USE_JUMP: result = DetourWriteJump(header); break; + case X86Option::USE_CALL: result = DetourWriteCall(header); break; + case X86Option::USE_EAX_JUMP: result = DetourWriteEaxJump(header); break; + case X86Option::USE_JUMP_PTR: result = DetourWriteJumpPtr(header); break; + case X86Option::USE_PUSH_RET: result = DetourWritePushRet(header); break; + default: result = false; break; + } + + // If an operation failed free the memory and exit + if (!result) + { + VirtualFree(jumpTrampolinePtr, 0, MEM_RELEASE); + return nullptr; + } + + // Force flush any possible CPU cache + DetourFlushCache(Target, totalInstrSize); + DetourFlushCache(jumpTrampolinePtr, allocSize); + + // Set read/execution on the page + DWORD dwOld = 0; + VirtualProtect(jumpTrampolinePtr, allocSize, PAGE_EXECUTE_READ, &dwOld); + + return header->InstructionOffset; + } + + bool DetourRemove(uint8_t *Trampoline) + { + JumpTrampolineHeader *header = (JumpTrampolineHeader *)(Trampoline - sizeof(JumpTrampolineHeader)); + + if (header->Magic != HEADER_MAGIC) + return false; + + // Rewrite the backed-up code + if (!DetourCopyMemory(header->CodeOffset, header->InstructionOffset, header->InstructionLength)) + return false; + + DetourFlushCache(header->CodeOffset, header->InstructionLength); + VirtualFree(header, 0, MEM_RELEASE); + + return true; + } + + uint8_t *DetourVTable(uint8_t *Target, uint8_t *Detour, uint32_t TableIndex) + { + // Each function is stored in an array + uint8_t *virtualPointer = (Target + (TableIndex * sizeof(ULONG))); + + DWORD dwOld = 0; + if (!VirtualProtect(virtualPointer, sizeof(ULONG), PAGE_EXECUTE_READWRITE, &dwOld)) + return nullptr; + + uint8_t *original = (uint8_t *)InterlockedExchange((volatile ULONG *)virtualPointer, (ULONG)Detour); + + VirtualProtect(virtualPointer, sizeof(ULONG), dwOld, &dwOld); + + return original; + } + + bool VTableRemove(uint8_t *Target, uint8_t *Function, uint32_t TableIndex) + { + // Reverse VTable detour + return DetourVTable(Target, Function, TableIndex) != nullptr; + } + + void DetourWriteStub(JumpTrampolineHeader *Header) + { + /********** Allocated code block modifications **********/ + + // Determine where the 'unhooked' part of the function starts + uint8_t *unhookStart = (Header->CodeOffset + Header->InstructionLength); + + // Jump to hooked function (Backed up instructions) + uint8_t *binstr_ptr = (Header->InstructionOffset + Header->InstructionLength); + + AsmGen hookGen(binstr_ptr, ASMGEN_32); + hookGen.AddCode("jmp 0x%X", unhookStart); + + memcpy(binstr_ptr, hookGen.GetStream(), hookGen.GetStreamLength()); + + // Jump to user function (Write the trampoline) + AsmGen userGen(Header->TrampolineOffset, ASMGEN_32); + userGen.AddCode("jmp 0x%X", Header->DetourOffset); + + memcpy(Header->TrampolineOffset, userGen.GetStream(), userGen.GetStreamLength()); + } + + bool DetourWriteJump(JumpTrampolineHeader *Header) + { + AsmGen gen(Header->CodeOffset, ASMGEN_32); + + // Jump to trampoline (from hooked function) + gen.AddCode("jmp 0x%X", Header->TrampolineOffset); + + return DetourAtomicCopy4X8(Header->CodeOffset, gen.GetStream(), gen.GetStreamLength()); + } + + bool DetourWriteCall(JumpTrampolineHeader *Header) + { + AsmGen gen(Header->CodeOffset, ASMGEN_32); + + // Call to trampoline (from hooked function) + gen.AddCode("call 0x%X", Header->TrampolineOffset); + + return DetourAtomicCopy4X8(Header->CodeOffset, gen.GetStream(), gen.GetStreamLength()); + } + + bool DetourWriteEaxJump(JumpTrampolineHeader *Header) + { + AsmGen gen(Header->CodeOffset, ASMGEN_32); + + // Jump to trampoline with eax + gen.AddCode("mov eax, 0x%X", Header->TrampolineOffset); + gen.AddCode("jmp eax"); + + return DetourAtomicCopy4X8(Header->CodeOffset, gen.GetStream(), gen.GetStreamLength()); + } + + bool DetourWriteJumpPtr(JumpTrampolineHeader *Header) + { + AsmGen gen(Header->CodeOffset, ASMGEN_32); + + // Pointer jump to trampoline + gen.AddCode("jmp dword ptr ds:[0x%X]", &Header->TrampolineOffset); + + return DetourAtomicCopy4X8(Header->CodeOffset, gen.GetStream(), gen.GetStreamLength()); + } + + bool DetourWritePushRet(JumpTrampolineHeader *Header) + { + AsmGen gen(Header->CodeOffset, ASMGEN_32); + + // RET-Jump to trampoline + gen.AddCode("push 0x%X", Header->TrampolineOffset); + gen.AddCode("retn"); + + return DetourAtomicCopy4X8(Header->CodeOffset, gen.GetStream(), gen.GetStreamLength()); + } + + uint32_t DetourGetHookLength(X86Option Options) + { + uint32_t size = 0; + + switch (Options) + { + case X86Option::USE_JUMP: size += JUMP_LENGTH_32; break; + case X86Option::USE_CALL: size += CALL_LENGTH_32; break; + case X86Option::USE_EAX_JUMP: size += JUMP_EAX_LENGTH_32; break; + case X86Option::USE_JUMP_PTR: size += JUMP_PTR_LENGTH_32; break; + case X86Option::USE_PUSH_RET: size += PUSH_RET_LENGTH_32; break; + default: size = 0; break; + } + + return size; + } + } +} +#endif // _M_IX86 \ No newline at end of file diff --git a/components/shared/detours/Detours64.cpp b/components/shared/detours/Detours64.cpp new file mode 100644 index 00000000..a1ac4ccc --- /dev/null +++ b/components/shared/detours/Detours64.cpp @@ -0,0 +1,198 @@ +#include "stdafx.h" + +#ifdef _WIN64 +namespace Detours +{ + namespace X64 + { + #define HEADER_MAGIC '@D64' + + #define MAX_INSTRUCT_SIZE 0x10 + + #define RAX_JUMP_64 0x0C // mov rax, ; jmp rax; + #define PUSH_RET_LENGTH_64 0x0E // push ; mov [rsp+4h], ; ret; + + #define ALIGN_64(x) DetourAlignAddress((uint64_t)(x), 0x10); + + uint8_t *DetourFunction(uint8_t *Target, uint8_t *Detour, X64Option Options) + { + // Decode the actual assembly + _DecodedInst decodedInstructions[DISASM_MAX_INSTRUCTIONS]; + uint32_t decodedInstructionsCount = 0; + + _DecodeResult res = distorm_decode64((_OffsetType)Target, (const unsigned char *)Target, 32, Decode64Bits, decodedInstructions, DISASM_MAX_INSTRUCTIONS, &decodedInstructionsCount); + + // Check if decoding failed + if (res != DECRES_SUCCESS) + return nullptr; + + // Calculate the hook length from options + uint32_t totalInstrSize = 0; + uint32_t neededSize = DetourGetHookLength(Options); + + // Calculate how many instructions are needed to place the jump + for (uint32_t i = 0; i < decodedInstructionsCount; i++) + { + totalInstrSize += decodedInstructions[i].size; + + if (totalInstrSize >= neededSize) + break; + } + + // Unable to find a needed length + if (totalInstrSize < neededSize) + return nullptr; + + // Allocate the trampoline data + uint32_t allocSize = 0; + allocSize += sizeof(JumpTrampolineHeader); // Base structure + allocSize += totalInstrSize; // Size of the copied instructions + allocSize += MAX_INSTRUCT_SIZE; // See PUSH_RET_LENGTH_64 + allocSize += MAX_INSTRUCT_SIZE; // See PUSH_RET_LENGTH_64 + allocSize += 0x64; // Padding for any memory alignment + + uint8_t *jumpTrampolinePtr = (uint8_t *)VirtualAlloc(nullptr, allocSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + if (!jumpTrampolinePtr) + return nullptr; + + // Fill out the header + JumpTrampolineHeader *header = (JumpTrampolineHeader *)jumpTrampolinePtr; + + header->Magic = HEADER_MAGIC; + header->Random = GetCurrentProcessId() + GetCurrentThreadId() + totalInstrSize; + + header->CodeOffset = Target; + header->DetourOffset = Detour; + + header->InstructionLength = totalInstrSize; + header->InstructionOffset = ALIGN_64(jumpTrampolinePtr + sizeof(JumpTrampolineHeader)); + + header->TrampolineLength = MAX_INSTRUCT_SIZE; + header->TrampolineOffset = ALIGN_64(header->InstructionOffset + header->InstructionLength + MAX_INSTRUCT_SIZE + header->TrampolineLength); + + // Copy the fixed instructions over + memcpy(header->InstructionOffset, Target, header->InstructionLength); + + // Write the assembly in the allocation block + DetourWriteStub(header); + + bool result = true; + + switch (Options) + { + case X64Option::USE_RAX_JUMP: result = DetourWriteRaxJump(header);break; + case X64Option::USE_PUSH_RET: result = DetourWritePushRet(header);break; + default: result = false; break; + } + + // If an operation failed free the memory and exit + if (!result) + { + VirtualFree(jumpTrampolinePtr, 0, MEM_RELEASE); + return nullptr; + } + + // Force flush any possible CPU cache + DetourFlushCache(Target, totalInstrSize); + DetourFlushCache(jumpTrampolinePtr, allocSize); + + // Set read/execution on the page + DWORD dwOld = 0; + VirtualProtect(jumpTrampolinePtr, allocSize, PAGE_EXECUTE_READ, &dwOld); + + return header->InstructionOffset; + } + + bool DetourRemove(uint8_t *Trampoline) + { + return false; + } + + uint8_t *DetourVTable(uint8_t *Target, uint8_t *Detour, uint32_t TableIndex) + { + // Each function is stored in an array + uint8_t *virtualPointer = (Target + (TableIndex * sizeof(sizeptr_t))); + + DWORD dwOld = 0; + if (!VirtualProtect(virtualPointer, sizeof(sizeptr_t), PAGE_EXECUTE_READWRITE, &dwOld)) + return nullptr; + + uint8_t *original = (uint8_t *)InterlockedExchange64((volatile LONG64 *)virtualPointer, (LONG64)Detour); + + VirtualProtect(virtualPointer, sizeof(sizeptr_t), dwOld, &dwOld); + + return original; + } + + bool VTableRemove(uint8_t *Target, uint8_t *Function, uint32_t TableIndex) + { + // Reverse VTable detour + return DetourVTable(Target, Function, TableIndex) != nullptr; + } + + void DetourWriteStub(JumpTrampolineHeader *Header) + { + /********** Allocated code block modifications **********/ + + // Determine where the 'unhooked' part of the function starts + uint8_t *unhookStart = (Header->CodeOffset + Header->InstructionLength); + + // Jump to hooked function (Backed up instructions) + uint8_t *binstr_ptr = (Header->InstructionOffset + Header->InstructionLength); + + AsmGen hookGen(binstr_ptr, ASMGEN_64); + hookGen.AddCode("push 0x%X", ((uint64_t)unhookStart) & 0xFFFFFFFF); + hookGen.AddCode("mov dword ptr ss:[rsp+0x4], 0x%X", ((uint64_t)unhookStart) >> 32); + hookGen.AddCode("retn"); + + memcpy(binstr_ptr, hookGen.GetStream(), hookGen.GetStreamLength()); + + // Jump to user function (Write the trampoline) + AsmGen userGen(Header->TrampolineOffset, ASMGEN_64); + userGen.AddCode("push 0x%X", ((uint64_t)Header->DetourOffset) & 0xFFFFFFFF); + userGen.AddCode("mov dword ptr ss:[rsp+0x4], 0x%X", ((uint64_t)Header->DetourOffset) >> 32); + userGen.AddCode("retn"); + + memcpy(Header->TrampolineOffset, userGen.GetStream(), userGen.GetStreamLength()); + } + + bool DetourWriteRaxJump(JumpTrampolineHeader *Header) + { + AsmGen gen(Header->CodeOffset, ASMGEN_64); + + // Jump to trampoline (from hooked function) + gen.AddCode("mov rax, 0x%llx", Header->TrampolineOffset); + gen.AddCode("jmp rax"); + + return DetourCopyMemory(Header->CodeOffset, gen.GetStream(), gen.GetStreamLength()); + } + + bool DetourWritePushRet(JumpTrampolineHeader *Header) + { + AsmGen gen(Header->CodeOffset, ASMGEN_64); + + // Jump with PUSH/RET (push low32, [rsp+4h] = hi32) + gen.AddCode("push 0x%X", ((uint64_t)Header->TrampolineOffset) & 0xFFFFFFFF); + gen.AddCode("mov dword ptr ss:[rsp+0x4], 0x%X", ((uint64_t)Header->TrampolineOffset) >> 32); + gen.AddCode("retn"); + + return DetourCopyMemory(Header->CodeOffset, gen.GetStream(), gen.GetStreamLength()); + } + + uint32_t DetourGetHookLength(X64Option Options) + { + uint32_t size = 0; + + switch(Options) + { + case X64Option::USE_RAX_JUMP: size += RAX_JUMP_64; break; + case X64Option::USE_PUSH_RET: size += PUSH_RET_LENGTH_64; break; + default: size = 0; break; + } + + return size; + } + } +} +#endif // _WIN64 \ No newline at end of file diff --git a/components/shared/detours/Intrinsic.h b/components/shared/detours/Intrinsic.h new file mode 100644 index 00000000..0fb04435 --- /dev/null +++ b/components/shared/detours/Intrinsic.h @@ -0,0 +1,42 @@ +#pragma once + +#ifdef _M_IX86 +static +LONGLONG +_intrinInterlockedCompareExchange64( + __inout LONGLONG volatile *Destination, + __in LONGLONG ExChange, + __in LONGLONG Comperand +) +{ + __asm + { + mov esi, [Destination] + mov ebx, dword ptr [ExChange] + mov ecx, dword ptr [ExChange + 4] + mov eax, dword ptr [Comperand] + mov edx, dword ptr [Comperand + 4] + lock cmpxchg8b qword ptr [esi] + } +} + +FORCEINLINE +LONGLONG +_intrinInterlockedExchange64( + __inout LONGLONG volatile *Target, + __in LONGLONG Value +) +{ + LONGLONG Old; + + do + { + Old = *Target; + } while (_intrinInterlockedCompareExchange64(Target, Value, Old) != Old); + + return Old; +} +#else +#define _intrinInterlockedCompareExchange64 InterlockedCompareExchange64 +#define _intrinInterlockedExchange64 InterlockedExchange64 +#endif // _M_IX86 \ No newline at end of file diff --git a/components/shared/detours/LICENSE b/components/shared/detours/LICENSE new file mode 100644 index 00000000..9cecc1d4 --- /dev/null +++ b/components/shared/detours/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/components/shared/detours/README.md b/components/shared/detours/README.md new file mode 100644 index 00000000..521200e6 --- /dev/null +++ b/components/shared/detours/README.md @@ -0,0 +1,7 @@ +# Detours + + +This project uses: +- Asmjit: https://github.com/asmjit/asmjit +- Asmtk: https://github.com/asmjit/asmtk +- diStorm 3: https://github.com/gdabah/distorm diff --git a/components/shared/detours/Reassembler.cpp b/components/shared/detours/Reassembler.cpp new file mode 100644 index 00000000..73bdd3fd --- /dev/null +++ b/components/shared/detours/Reassembler.cpp @@ -0,0 +1,37 @@ +#include "stdafx.h" + +namespace Reassembler +{ + // This converts a distorm decoded instruction into text format + // XEDParse then re-assembles it in order to account for the IP changing + // Not all instructions will work (2 or 4GB boundary) + BYTE *Reassemble(uint64_t Ip, _DecodedInst *Instructions, int Count, size_t *LengthOut, AsmGen *Gen) + { + for (int i = 0; i < Count; i++) + { + _DecodedInst *instr = &Instructions[i]; + + Gen->AddCode("%s %s", instr->mnemonic.p, instr->operands.p); + } + + *LengthOut = Gen->GetStreamLength(); + return Gen->GetStream(false); + } + + BYTE *Reassemble32(uint32_t Ip, _DecodedInst *Instructions, int Count, size_t *LengthOut) + { + AsmGen gen(Ip, ASMGEN_32); + return Reassemble(Ip, Instructions, Count, LengthOut, &gen); + } + + BYTE *Reassemble64(uint64_t Ip, _DecodedInst *Instructions, int Count, size_t *LengthOut) + { + AsmGen gen(Ip, ASMGEN_64); + return Reassemble(Ip, Instructions, Count, LengthOut, &gen); + } + + void Free(BYTE *Data) + { + free(Data); + } +} \ No newline at end of file diff --git a/components/shared/detours/Reassembler.h b/components/shared/detours/Reassembler.h new file mode 100644 index 00000000..a2b184a5 --- /dev/null +++ b/components/shared/detours/Reassembler.h @@ -0,0 +1,8 @@ +#pragma once + +namespace Reassembler +{ + BYTE *Reassemble32(uint32_t Ip, _DecodedInst *Instructions, int Count, size_t *LengthOut); + BYTE *Reassemble64(uint64_t Ip, _DecodedInst *Instructions, int Count, size_t *LengthOut); + void Free(BYTE *Data); +} \ No newline at end of file diff --git a/components/shared/detours/Typedef.h b/components/shared/detours/Typedef.h deleted file mode 100644 index 647c1e0a..00000000 --- a/components/shared/detours/Typedef.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -typedef __int8 int8_t; -typedef __int16 int16_t; -typedef __int32 int32_t; -typedef __int64 int64_t; - -typedef unsigned __int8 uint8_t; -typedef unsigned __int16 uint16_t; -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; - -typedef void *voidptr_t; - -#ifdef _WIN64 -typedef unsigned long long sizeptr_t; -#else -typedef __w64 unsigned long sizeptr_t; -#endif - -typedef sizeptr_t addrptr_t; \ No newline at end of file diff --git a/components/shared/detours/asmjit/asmjit/.appveyor.yml b/components/shared/detours/asmjit/asmjit/.appveyor.yml new file mode 100644 index 00000000..5a122811 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/.appveyor.yml @@ -0,0 +1,92 @@ +version: "{build}" + +image: Visual Studio 2015 +clone_folder: c:\dev\asmjit + +environment: + matrix: + - BUILD_TYPE: Debug + MINGW_PATH: C:\MinGW + TOOLCHAIN: "MinGW Makefiles" + + - BUILD_TYPE: Release + MINGW_PATH: C:\MinGW + TOOLCHAIN: "MinGW Makefiles" + + - BUILD_TYPE: Debug + MINGW_PATH: C:\msys64\mingw64 + TOOLCHAIN: "MinGW Makefiles" + + - BUILD_TYPE: Release + MINGW_PATH: C:\msys64\mingw64 + TOOLCHAIN: "MinGW Makefiles" + + - BUILD_TYPE: Debug + TOOLCHAIN: "Visual Studio 9 2008" + + - BUILD_TYPE: Release + TOOLCHAIN: "Visual Studio 9 2008" + + - BUILD_TYPE: Debug + TOOLCHAIN: "Visual Studio 10 2010" + + - BUILD_TYPE: Release + TOOLCHAIN: "Visual Studio 10 2010" + + - BUILD_TYPE: Debug + TOOLCHAIN: "Visual Studio 10 2010 Win64" + + - BUILD_TYPE: Release + TOOLCHAIN: "Visual Studio 10 2010 Win64" + + - BUILD_TYPE: Debug + TOOLCHAIN: "Visual Studio 12 2013" + + - BUILD_TYPE: Release + TOOLCHAIN: "Visual Studio 12 2013" + + - BUILD_TYPE: Debug + TOOLCHAIN: "Visual Studio 12 2013 Win64" + + - BUILD_TYPE: Release + TOOLCHAIN: "Visual Studio 12 2013 Win64" + + - BUILD_TYPE: Debug + TOOLCHAIN: "Visual Studio 14 2015" + + - BUILD_TYPE: Release + TOOLCHAIN: "Visual Studio 14 2015" + + - BUILD_TYPE: Debug + TOOLCHAIN: "Visual Studio 14 2015 Win64" + + - BUILD_TYPE: Release + TOOLCHAIN: "Visual Studio 14 2015 Win64" + +install: + - if "%TOOLCHAIN%"=="MinGW Makefiles" set PATH=%PATH:C:\Program Files\Git\usr\bin;=% + - if "%TOOLCHAIN%"=="MinGW Makefiles" set PATH=%MINGW_PATH%\bin;%PATH% + +build_script: + - cd c:\dev\asmjit + - md build + - cd build + - if "%TOOLCHAIN%"=="MinGW Makefiles" ( + cmake .. -G"%TOOLCHAIN%" -DCMAKE_PREFIX_PATH="%MINGW_PATH%" -DCMAKE_BUILD_TYPE="%BUILD_TYPE%" -DASMJIT_BUILD_TEST=1 && + mingw32-make + ) + else ( + cmake .. -G"%TOOLCHAIN%" -DASMJIT_BUILD_TEST=1 && + msbuild /m /nologo /v:quiet /p:Configuration=%BUILD_TYPE% asmjit.sln + ) + +test_script: + - if "%TOOLCHAIN%"=="MinGW Makefiles" ( + cd c:\dev\asmjit\build + ) + else ( + cd c:\dev\asmjit\build\%BUILD_TYPE% + ) + - asmjit_test_unit.exe + - asmjit_test_x86_asm.exe + - asmjit_test_x86_cc.exe diff --git a/components/shared/detours/asmjit/asmjit/.gitignore b/components/shared/detours/asmjit/asmjit/.gitignore new file mode 100644 index 00000000..8689ffe3 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/.gitignore @@ -0,0 +1,5 @@ +.kdev4 +*.kdev4 +build_* +tools/x86data.js +tools/x86util.js diff --git a/components/shared/detours/asmjit/asmjit/.travis.yml b/components/shared/detours/asmjit/asmjit/.travis.yml new file mode 100644 index 00000000..fbd80b2b --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/.travis.yml @@ -0,0 +1,55 @@ +language: cpp + +os: [linux, osx] +compiler: [gcc, clang] + +addons: + apt: + packages: [cmake, gcc-multilib, g++-multilib, valgrind] + sources: [ubuntu-toolchain-r-test] + +env: + matrix: + - BUILD_TYPE=Debug CFLAGS=-m32 CXXFLAGS=-m32 + - BUILD_TYPE=Debug CFLAGS=-m64 CXXFLAGS=-m64 + - BUILD_TYPE=Release CFLAGS=-m32 CXXFLAGS=-m32 + - BUILD_TYPE=Release CFLAGS=-m64 CXXFLAGS=-m64 + +matrix: + exclude: + - os: osx + compiler: gcc + +install: + - | + if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then + CMAKE_PACKAGE="https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz" + mkdir -p deps/cmake + wget --no-check-certificate --quiet -O - ${CMAKE_PACKAGE} | tar --strip-components=1 -xz -C deps/cmake + export PATH=${TRAVIS_BUILD_DIR}/deps/cmake/bin:${PATH} + else + brew update + brew outdated cmake || brew upgrade cmake + fi + +before_script: + - mkdir build + - cd build + - cmake --version + - cmake .. -G"Unix Makefiles" -DCMAKE_BUILD_TYPE="$BUILD_TYPE" -DASMJIT_BUILD_TEST=1 + - cd .. + +script: + - cd build + - make + - cd .. + + - ./build/asmjit_test_unit + - ./build/asmjit_test_opcode > /dev/null + - ./build/asmjit_test_x86_asm + - ./build/asmjit_test_x86_cc + +after_success: + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then valgrind --leak-check=full --show-reachable=yes ./build/asmjit_test_unit; fi; + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then valgrind --leak-check=full --show-reachable=yes ./build/asmjit_test_x86_asm; fi; + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then valgrind --leak-check=full --show-reachable=yes ./build/asmjit_test_x86_cc; fi; diff --git a/components/shared/detours/asmjit/asmjit/BREAKING.md b/components/shared/detours/asmjit/asmjit/BREAKING.md new file mode 100644 index 00000000..2ff57107 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/BREAKING.md @@ -0,0 +1,75 @@ +2016-07-20 +---------- + + * Global `asmjit_cast<>` removed and introduced a more type-safe `asmjit::ptr_cast<>`, which can cast a function to `void*` (and vice-versa), but will refuse to cast a function to `void**`, for example. Just change `asmjit_cast` to `asmjit::ptr_cast` and everything should work as usual. As a consequence, the Runtime now contains a typesafe (templated) `add()` and `remove()` methods that accept a function type directly, no need to cast manually to `void*` and `void**`. If you use your own runtime rename your virtual methods from `add` to `_add` and from `release` to `_release` and enjoy the type-safe wrappers. + * Removed `Logger::Style` and `uint32_t style` parameter in Logging API. It was never used for anything so it was removed. + * There is a new `CodeEmitter` base class that defines assembler building blocks that are implemented by `Assembler` and `CodeBuilder`. `CodeCompiler` is now based on `CodeBuilder` and shares its instruction storage functionality. Most API haven't changed, just base classes and new functionality has been added. It's now possible to serialize code for further processing by using `CodeBuilder`. + * Renamed compile-time macro `ASMJIT_DISABLE_LOGGER` to `ASMJIT_DISABLE_LOGGING`. There is a new `Formatter` class which is also disabled with this option. + + * Operand API is mostly intact, omitting Var/Reg should fix most compile-time errors. There is now no difference between a register index and register id internally. If you ever used `reg.getRegIndex()` then use `reg.getId()` instead. Also renamed `isInitialized()` to `isValid()`. + * There are much more changes, but they are mostly internal and keeping most operand methods compatible. + * Added new functionality into `asmjit::x86` namespace related to operands. + * X86Xmm/X86Ymm/X86Zmm register operands now inherit from X86Vec. + * Register kind (was register class) is now part of `Reg` operand, you can get it by using `reg.getRegKind()`. + * Register class enum moved to `X86Reg`, `kX86RegClassGp` is now `X86Reg::kKindGp`. + * Register type enum moved to `X86Reg`, `kX86RegTypeXmm` is now `X86Reg::kRegXmm`. + * Register index enum moved to `X86Gp`, `kX86RegIndexAx` is now `X86Gp::kIdAx`. + * Segment index enum moved to `X86Seg`, `kX86SegFs` is now `X86Seg::kIdFs`. + * If you used `asmjit::noOperand` for any reason, change it to `Operand()`. + + * CodeBuilder and CodeCompiler now contain different prefix of their nodes to distinguish between them: + + * Rename `HLNode` to `CBNode` (CodeBuilder node). + * Rename all other `HL` to `CB`. + * Rename `X86FuncNode` to `CCFunc` (CodeCompiler function), no more arch specific prefixes here. + * Rename `X86CallNode` to `CCFuncCall` (CodeCompiler function-call), also, no more X86 prefix. + + * AsmJit now uses CodeHolder to hold code. You don't need `Runtime` anymore if you don't plan to execute the code or if you plan to relocate it yourself: + +```c++ +CodeHolder code; // Create CodeHolder (holds the code). +code.init(CodeInfo(ArchInfo::kIdX64)); // Initialize CodeHolder to hold X64 code. + +// Everything else as usual: +X86Assembler a(&code); // Create the emitter (Assembler, CodeBuilder, CodeCompiler). +``` + + * Initializing with JitRuntime involves using CodeHolder: + +```c++ +JitRuntime rt; // Create JitRuntime. + +CodeHolder code; // Create CodeHolder. +code.init(rt.getCodeInfo()); // Initialize CodeHolder to match the JitRuntime. + +X86Assembler a(&code); // Create the emitter (Assembler, CodeBuilder, CodeCompiler). +... // Generate some code. + +typedef void (*SomeFunc)(void); // Prototype of the function you generated. + +SomeFunc func; // Function pointer. +Error err = rt.add(&func, &code); // Add the generated function to the runtime. + +rt.remove(func); // Remove the generated function from the runtime. +``` + + * Merged virtual registers (known as variables or Vars) into registers themselves, making the interface simpler: + +```c++ +X86GpReg/X86GpVar merged to X86Gp +X86MmReg/X86MmVar merged to X86Mm +X86XmmReg/X86XmmVar merged to X86Xmm +X86YmmReg/X86YmmVar merged to X86Ymm +``` + + * Refactored instruction database, moved many enums related to instructions into `X86Inst`. Also some instructions were wrong (having wrong signature in Assembler and Compiler) and were fixed. + +```c++ +X86InstInfo renamed to X86Inst +kX86InstIdSomething renamed to X86Inst::kIdSomething +kX86InstOptionSomething renamed to X86Inst::kOptionSomething +kX86CondSomething renamed to X86Inst::kCondSomething +kX86CmpSomething renamed to X86Inst::kCmpSomething +kX86VCmpSomething renamed to X86Inst::kVCmpSomething +kX86PrefetchSomething renamed to X86Inst::kPrefetchSomething +``` diff --git a/components/shared/detours/asmjit/asmjit/CMakeLists.txt b/components/shared/detours/asmjit/asmjit/CMakeLists.txt new file mode 100644 index 00000000..e2152285 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/CMakeLists.txt @@ -0,0 +1,226 @@ +cmake_minimum_required(VERSION 3.1) + +# ============================================================================= +# [AsmJit - Configuration] +# ============================================================================= + +# set(ASMJIT_EMBED FALSE) # Embed (no targets, just project). +# set(ASMJIT_STATIC FALSE) # Whether to build a static library. +# set(ASMJIT_TRACE FALSE) # Used for debugging asmjit itself. + +# set(ASMJIT_BUILD_ARM FALSE) # Whether to build ARM backend. +# set(ASMJIT_BUILD_X86 FALSE) # Whether to build X86 backend. +# set(ASMJIT_BUILD_TEST FALSE) # Whether to build tests and samples. + +# ============================================================================= +# [AsmJit - Project] +# ============================================================================= + +# Don't create a project if it was already created by another CMakeLists.txt. +# This allows one library to embed another library without a project collision. +if(NOT CMAKE_PROJECT_NAME OR "${CMAKE_PROJECT_NAME}" STREQUAL "asmjit") + project(asmjit C CXX) +endif() + +if(NOT ASMJIT_DIR) + set(ASMJIT_DIR ${CMAKE_CURRENT_LIST_DIR}) +endif() + +include("${ASMJIT_DIR}/CxxProject.cmake") +cxx_project(asmjit) + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set(ASMJIT_PRIVATE_LFLAGS "/OPT:REF /OPT:ICF") + + list(APPEND ASMJIT_PRIVATE_CFLAGS /GF) + list(APPEND ASMJIT_PRIVATE_CFLAGS_DBG /GS /GR-) + list(APPEND ASMJIT_PRIVATE_CFLAGS_REL /Oi /Oy /GS- /GR-) + if(NOT MSVC60 AND NOT MSVC70 AND NOT MSVC71) + list(APPEND ASMJIT_PRIVATE_CFLAGS /MP) # Enable multi-process compilation. + endif() +endif() + +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(GNU|Clang)$") + cxx_detect_standard(ASMJIT_PRIVATE_CFLAGS) + cxx_detect_cflags(ASMJIT_PRIVATE_CFLAGS + "-fno-tree-vectorize" + "-fvisibility=hidden" + "-Winconsistent-missing-override") + cxx_detect_cflags(ASMJIT_PRIVATE_CFLAGS_REL + "-O2" # CMake by default uses -O3, which does nothing useful. + "-fno-keep-static-consts" + "-fmerge-all-constants") +endif() + +if(ASMJIT_TRACE) + list(APPEND ASMJIT_PRIVATE_CFLAGS "${CXX_DEFINE}ASMJIT_TRACE") +endif() + +if(WIN32) + list(APPEND ASMJIT_PRIVATE_CFLAGS "${CXX_DEFINE}_UNICODE") +else() + list(APPEND ASMJIT_DEPS pthread) +endif() + +if("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") + list(APPEND ASMJIT_DEPS rt) +endif() + +set(ASMJIT_LIBS ${ASMJIT_DEPS}) +if(NOT ASMJIT_EMBED) + list(INSERT ASMJIT_LIBS 0 asmjit) +endif() + +foreach(BUILD_OPTION + ASMJIT_BUILD_ARM + ASMJIT_BUILD_X86 + ASMJIT_DISABLE_BUILDER + ASMJIT_DISABLE_COMPILER + ASMJIT_DISABLE_TEXT + ASMJIT_DISABLE_LOGGING + ASMJIT_DISABLE_VALIDATION) + if(${BUILD_OPTION}) + List(APPEND ASMJIT_CFLAGS "${CXX_DEFINE}${BUILD_OPTION}") + List(APPEND ASMJIT_PRIVATE_CFLAGS "${CXX_DEFINE}${BUILD_OPTION}") + endif() +endforeach() + +cxx_project_info(asmjit) + +# ============================================================================= +# [AsmJit - Source] +# ============================================================================= + +set(ASMJIT_SRC "") + +cxx_add_source(asmjit ASMJIT_SRC asmjit + asmjit.h + asmjit_apibegin.h + asmjit_apiend.h + asmjit_build.h + base.h + arm.h + x86.h +) + +cxx_add_source(asmjit ASMJIT_SRC asmjit/base + arch.cpp + arch.h + assembler.cpp + assembler.h + codebuilder.cpp + codebuilder.h + codecompiler.cpp + codecompiler.h + codeemitter.cpp + codeemitter.h + codeholder.cpp + codeholder.h + constpool.cpp + constpool.h + cpuinfo.cpp + cpuinfo.h + func.cpp + func.h + globals.cpp + globals.h + logging.cpp + logging.h + operand.cpp + operand.h + osutils.cpp + osutils.h + regalloc.cpp + regalloc_p.h + runtime.cpp + runtime.h + simdtypes.h + string.cpp + string.h + utils.cpp + utils.h + vmem.cpp + vmem.h + zone.cpp + zone.h + zonecontainers.cpp + zonecontainers.h + zoneheap.cpp + zoneheap.h +) + +if(0) +cxx_add_source(asmjit ASMJIT_SRC asmjit/arm + armassembler.cpp + armassembler.h + arminst.cpp + arminst.h + armoperand.cpp + armoperand_regs.cpp + armoperand.h +) +endif() + +cxx_add_source(asmjit ASMJIT_SRC asmjit/x86 + x86assembler.cpp + x86assembler.h + x86builder.cpp + x86builder.h + x86compiler.cpp + x86compiler.h + x86emitter.h + x86internal.cpp + x86internal_p.h + x86inst.cpp + x86inst.h + x86logging.cpp + x86logging.h + x86misc.h + x86operand.cpp + x86operand_regs.cpp + x86operand.h + x86regalloc.cpp + x86regalloc_p.h + x86ssetoavxpass.cpp + x86ssetoavxpass_p.h +) + +# ============================================================================= +# [AsmJit - Targets] +# ============================================================================= + +if(NOT ASMJIT_EMBED) + # Add `asmjit` library. + cxx_add_library(asmjit asmjit + "${ASMJIT_SRC}" + "${ASMJIT_DEPS}" + "${ASMJIT_PRIVATE_CFLAGS}" + "${ASMJIT_PRIVATE_CFLAGS_DBG}" + "${ASMJIT_PRIVATE_CFLAGS_REL}") + + foreach(_src_file ${ASMJIT_SRC}) + get_filename_component(_src_dir ${_src_file} PATH) + get_filename_component(_src_name ${_src_file} NAME) + string(REGEX REPLACE "^${ASMJIT_SOURCE_DIR}/" "" targetpath "${_src_dir}") + if("${_src_name}" MATCHES ".h$") + if(NOT "${_src_name}" MATCHES "_p.h$") + install(FILES ${_src_file} DESTINATION "include/${targetpath}") + endif() + endif() + endforeach() + + # Add `asmjit` tests and samples. + if(ASMJIT_BUILD_TEST) + cxx_add_source(asmjit ASMJIT_TEST_SRC ../test asmjit_test_unit.cpp broken.cpp broken.h) + cxx_add_executable(asmjit asmjit_test_unit + "${ASMJIT_SRC};${ASMJIT_TEST_SRC}" + "${ASMJIT_DEPS}" + "${ASMJIT_PRIVATE_CFLAGS};${CXX_DEFINE}ASMJIT_TEST;${CXX_DEFINE}ASMJIT_EMBED" + "${ASMJIT_PRIVATE_CFLAGS_DBG}" + "${ASMJIT_PRIVATE_CFLAGS_REL}") + + foreach(_target asmjit_bench_x86 asmjit_test_opcode asmjit_test_x86_asm asmjit_test_x86_cc) + cxx_add_executable(asmjit ${_target} "test/${_target}.cpp" "${ASMJIT_LIBS}" "${ASMJIT_CFLAGS}" "" "") + endforeach() + endif() +endif() diff --git a/components/shared/detours/asmjit/asmjit/CxxProject.cmake b/components/shared/detours/asmjit/asmjit/CxxProject.cmake new file mode 100644 index 00000000..3edc17b8 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/CxxProject.cmake @@ -0,0 +1,342 @@ +# CxxProject 1.0.0 +# ---------------- + +# Don't include CxxProject more than once. +if (NOT __CXX_INCLUDED) + +set(__CXX_INCLUDED TRUE) +include(CheckCXXCompilerFlag) + +# ----------------------------------------------------------------------------- +# C++ COMPILER SUPPORT: +# +# * cxx_detect_cflags(out, ...) +# * cxx_detect_standard(out) +# ----------------------------------------------------------------------------- +function(cxx_detect_cflags out) + set(out_array ${${out}}) + + foreach(flag ${ARGN}) + string(REGEX REPLACE "[-=:;/.]" "_" flag_signature "${flag}") + check_cxx_compiler_flag(${flag} "__CxxFlag_${flag_signature}") + if(${__CxxFlag_${flag_signature}}) + list(APPEND out_array "${flag}") + endif() + endforeach() + + set(${out} "${out_array}" PARENT_SCOPE) +endfunction() + +function(cxx_detect_standard out) + set(out_array) + cxx_detect_cflags(out_array "-std=c++14" "-std=c++11" "-std=c++0x") + + # Keep only the first flag detected, which keeps the highest version supported. + if(out_array) + list(GET out_array 0 out_array) + endif() + + set(out_array ${${out}} ${out_array}) + set(${out} "${out_array}" PARENT_SCOPE) +endfunction() + +function(cxx_print_cflags cflags_any cflags_dbg cflags_rel) + foreach(flag ${cflags_any}) + message(" ${flag}") + endforeach() + foreach(flag ${cflags_dbg}) + message(" ${flag} [DEBUG]") + endforeach() + foreach(flag ${cflags_rel}) + message(" ${flag} [RELEASE]") + endforeach() +endfunction() + +# ----------------------------------------------------------------------------- +# This part detects the c++ compiler and fills basic CXX_... variables to make +# integration with that compiler easier. It provides the most common flags in +# a cross-platform way. +# ----------------------------------------------------------------------------- +set(CXX_DEFINE "-D") # Define preprocessor macro: "${CXX_DEFINE}VAR=1" +set(CXX_INCLUDE "-I") # Define include directory : "${CXX_INCLUDE}PATH" + +set(CXX_CFLAGS_SSE "") # Compiler flags to build a file that uses SSE intrinsics. +set(CXX_CFLAGS_SSE2 "") # Compiler flags to build a file that uses SSE2 intrinsics. +set(CXX_CFLAGS_SSE3 "") # Compiler flags to build a file that uses SSE3 intrinsics. +set(CXX_CFLAGS_SSSE3 "") # Compiler flags to build a file that uses SSSE3 intrinsics. +set(CXX_CFLAGS_SSE4_1 "") # Compiler flags to build a file that uses SSE4.1 intrinsics. +set(CXX_CFLAGS_SSE4_2 "") # Compiler flags to build a file that uses SSE4.2 intrinsics. +set(CXX_CFLAGS_AVX "") # Compiler flags to build a file that uses AVX intrinsics. +set(CXX_CFLAGS_AVX2 "") # Compiler flags to build a file that uses AVX2 intrinsics. + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set(CXX_DEFINE "/D") + set(CXX_INCLUDE "/I") + + if(CMAKE_CL_64) + # 64-bit MSVC compiler doesn't like /arch:SSE[2] as it's implicit. + list(APPEND CXX_CFLAGS_SSE "${CXX_DEFINE}__SSE__=1") + list(APPEND CXX_CFLAGS_SSE2 "${CXX_DEFINE}__SSE__=1;${CXX_DEFINE}__SSE2__=1") + else() + cxx_detect_cflags(CXX_CFLAGS_SSE "/arch:SSE") + if(CXX_CFLAGS_SSE) + list(APPEND CXX_CFLAGS_SSE "${CXX_DEFINE}__SSE__=1") + endif() + + cxx_detect_cflags(CXX_CFLAGS_SSE2 "/arch:SSE2") + if(CXX_CFLAGS_SSE2) + list(APPEND CXX_CFLAGS_SSE2 "${CXX_DEFINE}__SSE__=1;${CXX_DEFINE}__SSE2__=1") + endif() + endif() + + # MSVC doesn't provide any preprocessor definitions to detect SSE3+, + # these unify MSVC with definitions defined by Clang|GCC|Intel. + if(CXX_CFLAGS_SSE2) + list(APPEND CXX_CFLAGS_SSE3 "${CXX_CFLAGS_SSE2};${CXX_DEFINE}__SSE3__=1") + list(APPEND CXX_CFLAGS_SSSE3 "${CXX_CFLAGS_SSE3};${CXX_DEFINE}__SSSE3__=1") + list(APPEND CXX_CFLAGS_SSE4_1 "${CXX_CFLAGS_SSSE3};${CXX_DEFINE}__SSE4_1__=1") + list(APPEND CXX_CFLAGS_SSE4_2 "${CXX_CFLAGS_SSE4_1};${CXX_DEFINE}__SSE4_2__=1") + endif() + + # When using AVX and AVX2 MSVC does define '__AVX__' and '__AVX2__', respectively. + cxx_detect_cflags(CXX_CFLAGS_AVX "/arch:AVX") + cxx_detect_cflags(CXX_CFLAGS_AVX2 "/arch:AVX2") + + if(CXX_CFLAGS_AVX) + list(APPEND CXX_CFLAGS_AVX "${CXX_DEFINE}__SSE__=1;${CXX_DEFINE}__SSE2__=1;${CXX_DEFINE}__SSE3__=1;${CXX_DEFINE}__SSSE3__=1;${CXX_DEFINE}__SSE4_1__=1;${CXX_DEFINE}__SSE4_2__=1") + endif() + if(CXX_CFLAGS_AVX2) + list(APPEND CXX_CFLAGS_AVX2 "${CXX_DEFINE}__SSE__=1;${CXX_DEFINE}__SSE2__=1;${CXX_DEFINE}__SSE3__=1;${CXX_DEFINE}__SSSE3__=1;${CXX_DEFINE}__SSE4_1__=1;${CXX_DEFINE}__SSE4_2__=1") + endif() +elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel" AND WIN32) + # Intel on Windows uses CL syntax. + set(CXX_DEFINE "/D") + set(CXX_INCLUDE "/I") + + # Intel deprecated /arch:SSE, so it's implicit. In contrast to MSVC, Intel + # also provides /arch:SSE3+ options and uses the same definitions as GCC + # and Clang, so no magic needed here. + cxx_detect_cflags(CXX_CFLAGS_SSE2 "/arch:SSE2") + cxx_detect_cflags(CXX_CFLAGS_SSE3 "/arch:SSE3") + cxx_detect_cflags(CXX_CFLAGS_SSSE3 "/arch:SSSE3") + cxx_detect_cflags(CXX_CFLAGS_SSE4_1 "/arch:SSE4.1") + cxx_detect_cflags(CXX_CFLAGS_SSE4_2 "/arch:SSE4.2") + cxx_detect_cflags(CXX_CFLAGS_AVX "/arch:AVX") + cxx_detect_cflags(CXX_CFLAGS_AVX2 "/arch:AVX2") +else() + cxx_detect_cflags(CXX_CFLAGS_SSE "-msse") + cxx_detect_cflags(CXX_CFLAGS_SSE2 "-msse2") + cxx_detect_cflags(CXX_CFLAGS_SSE3 "-msse3") + cxx_detect_cflags(CXX_CFLAGS_SSSE3 "-mssse3") + cxx_detect_cflags(CXX_CFLAGS_SSE4_1 "-msse4.1") + cxx_detect_cflags(CXX_CFLAGS_SSE4_2 "-msse4.2") + cxx_detect_cflags(CXX_CFLAGS_AVX "-mavx") + cxx_detect_cflags(CXX_CFLAGS_AVX2 "-mavx2") +endif() + +# ----------------------------------------------------------------------------- +# Function +# cxx_project(product) +# +# Create a master project or embed other project in a master project. +# ----------------------------------------------------------------------------- +function(cxx_project product) + string(TOUPPER "${product}" PRODUCT) + + set(MODE_EMBED ${${PRODUCT}_EMBED}) + set(MODE_STATIC ${${PRODUCT}_STATIC}) + + # EMBED implies STATIC. + if(MODE_EMBED) + set(MODE_STATIC TRUE) + set(${PRODUCT}_STATIC TRUE PARENT_SCOPE) + endif() + + # Deduce source and include directories. By default CxxProject assumes that + # both source and include files are located at './src'. + set(SOURCE_DIR "${${PRODUCT}_SOURCE_DIR}") + set(INCLUDE_DIR "${${PRODUCT}_INCLUDE_DIR}") + + if(NOT SOURCE_DIR) + set(SOURCE_DIR "${${PRODUCT}_DIR}/src") + set(${PRODUCT}_SOURCE_DIR "${SOURCE_DIR}" PARENT_SCOPE) + endif() + + if(NOT INCLUDE_DIR) + set(INCLUDE_DIR "${SOURCE_DIR}") + set(${PRODUCT}_INCLUDE_DIR "${INCLUDE_DIR}" PARENT_SCOPE) + endif() + + set(DEPS "") # Dependencies (list of libraries) for the linker. + set(LIBS "") # Dependencies with project included, for consumers. + set(CFLAGS "") # Public compiler flags. + set(PRIVATE_CFLAGS "") # Private compiler flags independent of build type. + set(PRIVATE_CFLAGS_DBG "") # Private compiler flags used by debug builds. + set(PRIVATE_CFLAGS_REL "") # Private compiler flags used by release builds. + set(PRIVATE_LFLAGS "") # Private linker flags. + + if(MODE_EMBED) + list(APPEND CFLAGS "${CXX_DEFINE}${PRODUCT}_EMBED") + list(APPEND PRIVATE_CFLAGS "${CXX_DEFINE}${PRODUCT}_EMBED") + endif() + + if(MODE_STATIC) + list(APPEND CFLAGS "${CXX_DEFINE}${PRODUCT}_STATIC") + list(APPEND PRIVATE_CFLAGS "${CXX_DEFINE}${PRODUCT}_STATIC") + endif() + + # PUBLIC properties - usable by third parties. + set(${PRODUCT}_DEPS "${DEPS}" PARENT_SCOPE) + set(${PRODUCT}_LIBS "${LIBS}" PARENT_SCOPE) + set(${PRODUCT}_CFLAGS "${CFLAGS}" PARENT_SCOPE) + + # PRIVATE properties - only used during build. + set(${PRODUCT}_PRIVATE_CFLAGS "${PRIVATE_CFLAGS}" PARENT_SCOPE) + set(${PRODUCT}_PRIVATE_CFLAGS_DBG "${PRIVATE_CFLAGS_DBG}" PARENT_SCOPE) + set(${PRODUCT}_PRIVATE_CFLAGS_REL "${PRIVATE_CFLAGS_REL}" PARENT_SCOPE) + set(${PRODUCT}_PRIVATE_LFLAGS "${PRIVATE_LFLAGS}" PARENT_SCOPE) +endfunction() + +function(cxx_project_info product) + string(TOUPPER "${product}" PRODUCT) + + set(BUILD_MODE "") + set(BUILD_TEST "") + + if(${PRODUCT}_EMBED) + set(BUILD_MODE "Embed") + elseif(${PRODUCT}_STATIC) + set(BUILD_MODE "Static") + else() + set(BUILD_MODE "Shared") + endif() + + if(${PRODUCT}_BUILD_TEST) + set(BUILD_TEST "On") + else() + set(BUILD_TEST "Off") + endif() + + message("-- [${product}]") + message(" BuildMode=${BUILD_MODE}") + message(" BuildTest=${BUILD_TEST}") + message(" ${PRODUCT}_DIR=${${PRODUCT}_DIR}") + message(" ${PRODUCT}_DEPS=${${PRODUCT}_DEPS}") + message(" ${PRODUCT}_LIBS=${${PRODUCT}_LIBS}") + message(" ${PRODUCT}_CFLAGS=${${PRODUCT}_CFLAGS}") + message(" ${PRODUCT}_SOURCE_DIR=${${PRODUCT}_SOURCE_DIR}") + message(" ${PRODUCT}_INCLUDE_DIR=${${PRODUCT}_INCLUDE_DIR}") + message(" ${PRODUCT}_PRIVATE_CFLAGS=") + cxx_print_cflags( + "${${PRODUCT}_PRIVATE_CFLAGS}" + "${${PRODUCT}_PRIVATE_CFLAGS_DBG}" + "${${PRODUCT}_PRIVATE_CFLAGS_REL}") +endfunction() + +function(cxx_add_source product out src_dir) + string(TOUPPER "${product}" PRODUCT) + + set(src_path "${${PRODUCT}_SOURCE_DIR}/${src_dir}") + set(src_array) + + foreach(file ${ARGN}) + set(src_file "${src_path}/${file}") + set(src_used TRUE) + set(src_cflags "") + + if(file MATCHES "\\.c|\\.cc|\\.cxx|\\.cpp|\\.m|\\.mm") + if(file MATCHES "_sse\\." AND NOT "${CXX_CFLAGS_SSE}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_SSE}) + endif() + if(file MATCHES "_sse2\\." AND NOT "${CXX_CFLAGS_SSE2}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_SSE2}) + endif() + if(file MATCHES "_sse3\\." AND NOT "${CXX_CFLAGS_SSE3}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_SSE3}) + endif() + if(file MATCHES "_ssse3\\." AND NOT "${CXX_CFLAGS_SSSE3}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_SSSE3}) + endif() + if(file MATCHES "_sse4_1\\." AND NOT "${CXX_CFLAGS_SSE4_1}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_SSE4_1}) + endif() + if(file MATCHES "_sse4_2\\." AND NOT "${CXX_CFLAGS_SSE4_2}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_SSE4_2}) + endif() + if(file MATCHES "_avx\\." AND NOT "${CXX_CFLAGS_AVX}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_AVX}) + endif() + if(file MATCHES "_avx2\\." AND NOT "${CXX_CFLAGS_AVX2}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_AVX2}) + endif() + + if(${src_used}) + if(NOT "${src_cflags}" STREQUAL "") + set_source_files_properties(${src_file} PROPERTIES COMPILE_FLAGS ${src_cflags}) + endif() + list(APPEND src_array ${src_file}) + endif() + endif() + endforeach() + source_group(${src_dir} FILES ${src_array}) + + set(out_tmp ${${out}}) + list(APPEND out_tmp ${src_array}) + set("${out}" "${out_tmp}" PARENT_SCOPE) +endfunction() + +function(cxx_add_library product target src deps cflags cflags_dbg cflags_rel) + string(TOUPPER "${product}" PRODUCT) + + if(NOT ${PRODUCT}_STATIC) + add_library(${target} SHARED ${src}) + else() + add_library(${target} STATIC ${src}) + endif() + + target_link_libraries(${target} ${deps}) + if (NOT "${${PRODUCT}_PRIVATE_LFLAGS}" STREQUAL "") + set_target_properties(${target} PROPERTIES LINK_FLAGS "${${PRODUCT}_PRIVATE_LFLAGS}") + endif() + + if(CMAKE_BUILD_TYPE) + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + target_compile_options(${target} PRIVATE ${cflags} ${cflags_dbg}) + else() + target_compile_options(${target} PRIVATE ${cflags} ${cflags_rel}) + endif() + else() + target_compile_options(${target} PRIVATE ${cflags} $<$:${cflags_dbg}> $<$>:${cflags_rel}>) + endif() + + if(NOT ${PRODUCT}_STATIC) + install(TARGETS ${target} DESTINATION "lib${LIB_SUFFIX}") + endif() +endfunction() + +function(cxx_add_executable product target src deps cflags cflags_dbg cflags_rel) + string(TOUPPER "${product}" PRODUCT) + add_executable(${target} ${src}) + + target_link_libraries(${target} ${deps}) + if (NOT "${${PRODUCT}_PRIVATE_LFLAGS}" STREQUAL "") + set_target_properties(${target} PROPERTIES LINK_FLAGS "${${PRODUCT}_PRIVATE_LFLAGS}") + endif() + + if(CMAKE_BUILD_TYPE) + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + target_compile_options(${target} PRIVATE ${cflags} ${cflags_dbg}) + else() + target_compile_options(${target} PRIVATE ${cflags} ${cflags_rel}) + endif() + else() + target_compile_options(${target} PRIVATE ${cflags} $<$:${cflags_dbg}> $<$>:${cflags_rel}>) + endif() + + if(NOT ${PRODUCT}_STATIC) + install(TARGETS ${target} DESTINATION "lib${LIB_SUFFIX}") + endif() +endfunction() + +# Guard. +endif() diff --git a/components/shared/detours/asmjit/asmjit/LICENSE.md b/components/shared/detours/asmjit/asmjit/LICENSE.md new file mode 100644 index 00000000..9f8aad26 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/LICENSE.md @@ -0,0 +1,18 @@ +AsmJit - Complete x86/x64 JIT and Remote Assembler for C++ +Copyright (c) 2008-2016, Petr Kobalicek + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/components/shared/detours/asmjit/asmjit/README.md b/components/shared/detours/asmjit/asmjit/README.md new file mode 100644 index 00000000..fc3cbdb5 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/README.md @@ -0,0 +1,1455 @@ +AsmJit +------ + +Complete x86/x64 JIT and Remote Assembler for C++. + + * [Official Repository (asmjit/asmjit)](https://github.com/asmjit/asmjit) + * [Official Blog (asmbits)](https://asmbits.blogspot.com/ncr) + * [Official Chat (gitter)](https://gitter.im/asmjit/asmjit) + * [Permissive ZLIB license](./LICENSE.md) + +'NEXT' TODO: + * This README contains outdated code and is not complete. + * Only the first section works atm. + * AVX512 {sae} and {er} not supported yet. + +Introduction +------------ + +AsmJit is a complete JIT and remote assembler for C++ language. It can generate native code for x86 and x64 architectures and supports the whole x86/x64 instruction set - from legacy MMX to the newest AVX512. It has a type-safe API that allows C++ compiler to do semantic checks at compile-time even before the assembled code is generated and/or executed. + +AsmJit, as the name implies, started as a project that provided JIT code-generation and execution. However, AsmJit evolved and it now contains features that are far beyond the scope of a simple JIT compilation. To keep the library small and lightweight the functionality not strictly related to JIT is provided by a sister project called [asmtk](https://github.com/asmjit/asmtk). + +Minimal Example +--------------- + +```c++ +#include +#include + +using namespace asmjit; + +// Signature of the generated function. +typedef int (*Func)(void); + +int main(int argc, char* argv[]) { + JitRuntime rt; // Runtime specialized for JIT code execution. + + CodeHolder code; // Holds code and relocation information. + code.init(rt.getCodeInfo()); // Initialize to the same arch as JIT runtime. + + X86Assembler a(&code); // Create and attach X86Assembler to `code`. + a.mov(x86::eax, 1); // Move one to 'eax' register. + a.ret(); // Return from function. + // ----> X86Assembler is no longer needed from here and can be destroyed <---- + + Func fn; + Error err = rt.add(&fn, &code); // Add the code generated to the runtime. + if (err) return 1; // Handle a possible error returned by AsmJit. + // ----> CodeHolder is no longer needed from here and can be destroyed <---- + + int result = fn(); // Execute the generated code. + printf("%d\n", result); // Print the resulting "1". + + // All classes use RAII, all resources will be released before `main()` returns, + // the generated function can be, however, released explicitly if you intend to + // reuse or keep the runtime alive, which you should in a production-ready code. + rt.release(fn); + + return 0; +} +``` + +AsmJit Summary +-------------- + + * Complete x86/x64 instruction set - MMX, SSEx, BMIx, ADX, TBM, XOP, AVXx, FMAx, and AVX512. + * Assembler, CodeBuilder, and CodeCompiler emitters - each suitable for different tasks. + * Built-in CPU vendor and features detection. + * Advanced logging/formatting and robust error handling. + * Virtual memory management similar to malloc/free for JIT code-generation and execution. + * Lightweight and embeddable - 200-250kB compiled with all built-in features. + * Modularity - unneeded features can be disabled at compile-time to make the library smaller. + * Zero dependencies - no external libraries, no STL/RTTI - easy to embed and/or link statically. + * Doesn't use exceptions internally, but allows to attach a "throwable" error handler (your choice). + +Advanced Features +----------------- + + * AsmJit contains a highly compressed instruction database: + * Instruction names - allows to convert instruction id to its name and vice versa. + * Instruction metadata - access (read|write|rw) of all operand combinations of all instructions. + * Instruction signatures - allows to strictly validate if an instruction (with all its operands) is valid. + * AsmJit allows to precisely control how instructions are encoded if there are multiple variations. + * AsmJit is highly dynamic, constructing operands at runtime is a common practice. + * Multiple emitters with the same interface - emit machine code directly or to a representation that can be processed afterwards. + +Important +--------- + + * Breaking changes are described in [BREAKING.md](./BREAKING.md) + +Supported Environments +---------------------- + +### C++ Compilers: + + * Tested + * **Clang** - tested by Travis-CI. + * **GCC** - tested by Travis-CI. + * **MinGW** - tested by AppVeyor. + * **MSVC** - tested by AppVeyor. + * Maybe + * **CodeGear** - no maintainers. + * **Intel** - no maintainers. + * Other c++ compilers would require some testing and support in [asmjit_build.h](./src/asmjit/asmjit_build.h). + +### Operating Systems: + + * Tested + * **Linux** - tested by Travis-CI. + * **Mac** - tested by Travis-CI. + * **Windows** - tested by AppVeyor. + * Maybe + * **BSDs** - no maintainers. + * Other operating systems would require some testing and support in [asmjit_build.h](./src/asmjit/asmjit_build.h) and [osutils.cpp](./src/asmjit/base/osutils.cpp). + +### Backends: + + * **X86** - tested by both Travis-CI and AppVeyor. + * **X64** - tested by both Travis-CI and AppVeyor. + * **ARM** - work-in-progress (not public at the moment). + +Project Organization +-------------------- + + * **`/`** - Project root + * **src** - Source code + * **asmjit** - Source code and headers (always point include path in here) + * **base** - Backend independent API + * **arm** - ARM specific API, used only by ARM32 and ARM64 backends + * **x86** - X86 specific API, used only by X86 and X64 backends + * **test** - Unit and integration tests (don't embed in your project) + * **tools** - Tools used for configuring, documenting and generating files + +Configuring & Building +---------------------- + +AsmJit is designed to be easy embeddable in any project. However, it depends on some compile-time macros that can be used to build a specific version of AsmJit that includes or excludes certain features. A typical way of building AsmJit is to use [cmake](https://www.cmake.org), but it's also possible to just include AsmJit source code in your project and just build it. The easiest way to include AsmJit in your project is to just include **src** directory in your project and to define **ASMJIT_STATIC** or **ASMJIT_EMBED**. AsmJit can be just updated from time to time without any changes to this integration process. Do not embed AsmJit's [/test](./test) files in such case as these are used for testing. + +### Build Type: + + * **ASMJIT_DEBUG** - Define to always turn debugging on (regardless of compile-time options detected). + * **ASMJIT_RELEASE** - Define to always turn debugging off (regardless of compiler-time options detected). + * **ASMJIT_TRACE** - Define to enable AsmJit tracing. Tracing is used to catch bugs in AsmJit and it has to be enabled explicitly. When AsmJit is compiled with `ASMJIT_TRACE` it uses `stdout` to log information related to AsmJit execution. This log can be helpful AsmJit developers and users in case something goes wrong.. + +By default none of these is defined, AsmJit detects build-type based on compile-time macros and supports most IDE and compiler settings out of box. + +### Build Mode: + + * **ASMJIT_STATIC** - Define to build AsmJit as a static library. No symbols are exported in such case. + * **ASMJIT_EMBED** - Define to embed AsmJit in another project. Embedding means that neither shared nor static library is created and AsmJit's source files and source files of the product that embeds AsmJit are part of the same target. This way of building AsmJit has certain advantages that are beyond this manual. **ASMJIT_EMBED** behaves identically to **ASMJIT_STATIC** (no API exports). + +By default AsmJit build is configured to be built as a shared library, thus note of **ASMJIT_EMBED** and **ASMJIT_STATIC** is defined. + +### Build Backends: + + * **ASMJIT_BUILD_ARM** - Build ARM32 and ARM64 backends (work-in-progress). + * **ASMJIT_BUILD_X86** - Build X86 and X64 backends. + * **ASMJIT_BUILD_HOST** - Build only the host backend (default). + +If none of **ASMJIT_BUILD_...** is defined AsmJit bails to **ASMJIT_BUILD_HOST**, which will detect the target architecture at compile-time. Each backend automatically supports 32-bit and 64-bit targets, so for example AsmJit with X86 support can generate both 32-bit and 64-bit code. + +### Disabling Features: + + * **ASMJIT_DISABLE_BUILDER** - Disables both **CodeBuilder** and **CodeCompiler** emitters (only **Assembler** will be available). Ideal for users that don't use **CodeBuilder** concept and want to create smaller AsmJit. + * **ASMJIT_DISABLE_COMPILER** - Disables **CodeCompiler** emitter. For users that use **CodeBuilder**, but not **CodeCompiler** + * **ASMJIT_DISABLE_LOGGING** - Disables logging (**Logger** and all classes that inherit it) and formatting features. + * **ASMJIT_DISABLE_TEXT** - Disables everything that uses text-representation and that causes certain strings to be stored in the resulting binary. For example when this flag is enabled all instruction and error names (and related APIs) will not be available. This flag has to be disabled together with **ASMJIT_DISABLE_LOGGING**. This option is suitable for deployment builds or builds that don't want to reveal the use of AsmJit. + * **ASMJIT_DISABLE_VALIDATION** - Disables instruction validation. Saves around 5kB of space when used. + +NOTE: Please don't disable any features if you plan to build AsmJit as a shared library that will be used by multiple projects that you don't control (for example asmjit in a Linux distribution). The possibility to disable certain features exists mainly for static builds of AsmJit. + +Using AsmJit +------------ + +AsmJit library uses one global namespace called `asmjit` that provides the whole functionality. Architecture specific code is prefixed by the architecture name and architecture specific registers and operand builders have their own namespace. For example API targeting both X86 and X64 architectures is prefixed with `X86` and registers & operand builders are accessible through `x86` namespace. This design is very different from the initial version of AsmJit and it seems now as the most convenient one. + +### CodeHolder & CodeEmitter + +AsmJit provides two classes that are used together for code generation: + + * **CodeHolder** - Provides functionality to hold generated code and stores all necessary information about code sections, labels, symbols, and possible relocations. + * **CodeEmitter** - Provides functionality to emit code into `CodeHolder`. `CodeEmitter` is abstract and provides just basic building blocks that are then implemented by `Assembler`, `CodeBuilder`, and `CodeCompiler`. + +Code emitters: + + * **Assembler** - Emitter designed to emit machine code directly. + * **CodeBuilder** - Emitter designed to emit code into a representation that can be processed. It stores the whole code in a double linked list consisting of nodes (`CBNode` aka code-builder node). There are nodes that represent instructions (`CBInst`), labels (`CBLabel`), and other building blocks (`CBAlign`, `CBData`, ...). Some nodes are used as markers (`CBSentinel`) and comments (`CBComment`). + * **CodeCompiler** - High-level code emitter that uses virtual registers and contains high-level function building features. `CodeCompiler` is based on `CodeBuilder`, but extends its functionality and introduces new node types starting with CC (`CCFunc`, `CCFuncRet`, `CCFuncCall`). `CodeCompiler` is the simplest way to start with AsmJit as it abstracts many details required to generate a function in asm language. + +### Runtime + +AsmJit's `Runtime` is designed for execution and/or linking. The `Runtime` itself is abstract and defines only how to `add` and `release` code held by `CodeHolder`. `CodeHolder` holds machine code and relocation entries, but should be seen as a temporary object only - after the code in `CodeHolder` is ready, it should be passed to `Runtime` or relocated manually. Users interested in inspecting the generated machine-code (instead of executing or linking) can keep it in `CodeHodler` and process it manually of course. + +The only `Runtime` implementation provided directly by AsmJit is called `JitRuntime`, which is suitable for storing and executing dynamically generated code. JitRuntime is used in most AsmJit examples as it makes the code management easy. It allows to add and release dynamically generated functions, so it's suitable for JIT code generators that want to keep many functions alive, and release functions which are no longer needed. + +### Instructions & Operands + +Instructions specify operations performed by the CPU, and operands specify the operation's input(s) and output(s). Each AsmJit's instruction has it's own unique id (`X86Inst::Id` for example) and platform specific code emitters always provide a type safe intrinsic (or multiple overloads) to emit such instruction. There are two ways of emitting an instruction: + + * Using emitter.**instName**(operands...) - A type-safe way provided by platform specific emitters - for example `X86Assembler` provides `mov(X86Gp, X86Gp)`. + * Using emitter.emit(**instId**, operands...) - Allows to emit an instruction in a dynamic way - you just need to know instruction's id and provide its operands. + +AsmJit's operands all inherit from a base class called `Operand` and then specialize its type to: + + * **None** (not used or uninitialized operand). + * **Register** (**Reg**) - Describes either physical or virtual register. Physical registers have id that matches the target's machine id directly, whereas virtual registers must be allocated into physical registers by a register allocator pass. Each `Reg` provides: + * *Register Type* - Unique id that describes each possible register provided by the target architecture - for example X86 backend provides `X86Reg::RegType`, which defines all variations of general purpose registers (GPB-LO, GPB-HI, GPW, GPD, and GPQ) and all types of other registers like K, MM, BND, XMM, YMM, and ZMM. + * *Register Kind* - Groups multiple register types under a single kind - for example all general-purpose registers (of all sizes) on X86 are `X86Reg::kKindGp`, all SIMD registers (XMM, YMM, ZMM) are `X86Reg::kKindVec`, etc. + * *Register Size* - Contains the size of the register in bytes. If the size depends on the mode (32-bit vs 64-bit) then generally the higher size is used (for example RIP register has size 8 by default). + * *Register ID* - Contains physical or virtual id of the register. + * **Memory Address** (**Mem**) - Used to reference a memory location. Each `Mem` provides: + * *Base Register* - A base register id (physical or virtual). + * *Index Register* - An index register id (physical or virtual). + * *Offset* - Displacement or absolute address to be referenced (32-bit if base register is used and 64-bit if base register is not used). + * *Flags* that can describe various architecture dependent information (like scale and segment-override on X86). + * **Immediate Value** (**Imm**) - Immediate values are usually part of instructions (encoded within the instruction itself) or data. + * **Label** - used to reference a location in code or data. Labels must be created by the `CodeEmitter` or by `CodeHolder`. Each label has its unique id per `CodeHolder` instance. + +AsmJit allows to construct operands dynamically, to store them, and to query a complete information about them at run-time. Operands are small (always 16 bytes per **Operand**) and should be always copied if you intend to store them (don't create operands by using `new` keyword, it's not recommended). Operands are safe to be `memcpy()`ed and `memset()`ed if you work with arrays of operands. + +Small example of manipulating and using operands: + +```c++ +using namespace asmjit; + +X86Gp getDstRegByValue() { return x86::ecx; } + +void usingOperandsExample(X86Assembler& a) { + // Create some operands. + X86Gp dst = getDstRegByValue(); // Get `ecx` register returned by a function. + X86Gp src = x86::rax; // Get `rax` register directly from the provided `x86` namespace. + X86Gp idx = x86::gpq(10); // Construct `r10` dynamically. + X86Mem m = x86::ptr(src, idx); // Construct [src + idx] memory address - referencing [rax + r10]. + + // Examine `m`: + m.getIndexType(); // Returns `X86Reg::kRegGpq`. + m.getIndexId(); // Returns 10 (`r10`). + + // Reconstruct `idx` stored in mem: + X86Gp idx_2 = X86Gp::fromTypeAndId(m.getIndexType(), m.getIndexId()); + idx == idx_2; // True, `idx` and idx_2` are identical. + + Operand op = m; // Possible. + op.isMem(); // True (can be casted to Mem and X86Mem). + + m == op; // True, `op` is just a copy of `m`. + static_cast(op).addOffset(1); // Static cast is fine and valid here. + m == op; // False, `op` now points to [rax + r10 + 1], which is not [rax + r10]. + + // Emitting 'mov' + a.mov(dst, m); // Type-safe way. + a.mov(dst, op); // Not possible, `mov` doesn't provide `X86Reg, Operand` overload. + + a.emit(X86Inst::kIdMov, dst, m); // Type-unsafe, but possible. + a.emit(X86Inst::kIdMov, dst, op); // Also possible, `emit()` is typeless and can be used dynamically. +} +``` + +Some operands have to be created explicitly by `CodeEmitter`. For example labels must be created by `newLabel()` before they are used. + +### Assembler Example + +X86Assembler is a code emitter that emits machine code into a CodeBuffer directly. It's capable of targeting both 32-bit and 64-bit instruction sets and it's possible to target both instruction sets within the same code-base. The following example shows how to generate a function that works in both 32-bit and 64-bit modes, and how to use JitRuntime, CodeHolder, and X86Assembler together. + +The example handles 3 calling conventions manually just to show how it could be done, however, AsmJit contains utilities that can be used to create function prologs and epilogs automatically, but these concepts will be explained later. + +```c++ +using namespace asmjit; + +// Signature of the generated function. +typedef int (*SumFunc)(const int* arr, size_t count); + +int main(int argc, char* argv[]) { + assert(sizeof(void*) == 8 && + "This example requires 64-bit environment."); + + JitRuntime rt; // Create a runtime specialized for JIT. + + CodeHolder code; // Create a CodeHolder. + code.init(rt.getCodeInfo()); // Initialize it to be compatible with `rt`. + + X86Assembler a(&code); // Create and attach X86Assembler to `code`. + + // Decide between 32-bit CDECL, WIN64, and SysV64 calling conventions: + // 32-BIT - passed all arguments by stack. + // WIN64 - passes first 4 arguments by RCX, RDX, R8, and R9. + // UNIX64 - passes first 6 arguments by RDI, RSI, RCX, RDX, R8, and R9. + X86Gp arr, cnt; + X86Gp sum = x86::eax; // Use EAX as 'sum' as it's a return register. + + if (ASMJIT_ARCH_64BIT) { + bool isWinOS = static_cast(ASMJIT_OS_WINDOWS); + arr = isWinOS ? x86::rcx : x86::rdi; // First argument (array ptr). + cnt = isWinOS ? x86::rdx : x86::rsi; // Second argument (number of elements) + } + else { + arr = x86::edx; // Use EDX to hold the array pointer. + cnt = x86::ecx; // Use ECX to hold the counter. + a.mov(arr, ptr(x86::esp, 4)); // Fetch first argument from [ESP + 4]. + a.mov(cnt, ptr(x86::esp, 8)); // Fetch second argument from [ESP + 8]. + } + + Label Loop = a.newLabel(); // To construct the loop, we need some labels. + Label Exit = a.newLabel(); + + a.xor_(sum, sum); // Clear 'sum' register (shorter than 'mov'). + a.test(cnt, cnt); // Border case: + a.jz(Exit); // If 'cnt' is zero jump to 'Exit' now. + + a.bind(Loop); // Start of a loop iteration. + a.add(sum, dword_ptr(arr)); // Add int at [arr] to 'sum'. + a.add(arr, 4); // Increment 'arr' pointer. + a.dec(cnt); // Decrease 'cnt'. + a.jnz(Loop); // If not zero jump to 'Loop'. + + a.bind(Exit); // Exit to handle the border case. + a.ret(); // Return from function ('sum' == 'eax'). + // ----> X86Assembler is no longer needed from here and can be destroyed <---- + + SumFunc fn; + Error err = rt.add(&fn, &code); // Add the code generated to the runtime. + + if (err) return 1; // Handle a possible error returned by AsmJit. + // ----> CodeHolder is no longer needed from here and can be destroyed <---- + + static const int array[6] = { 4, 8, 15, 16, 23, 42 }; + + int result = fn(array, 6); // Execute the generated code. + printf("%d\n", result); // Print sum of array (108). + + rt.release(fn); // Remove the function from the runtime. + return 0; +} +``` + +The example should be self-explanatory. It shows how to work with labels, how to use operands, and how to emit instructions that can use different registers based on runtime selection. It implements 32-bit CDECL, WIN64, and SysV64 caling conventions and will work on most X86 environments. + +### More About Memory Addresses + +X86 provides a complex memory addressing model that allows to encode addresses having a BASE register, INDEX register with a possible scale (left shift), and displacement (called offset in AsmJit). Memory address can also specify memory segment (segment-override in X86 terminology) and some instructions (gather / scatter) require INDEX to be a VECTOR register instead of a general-purpose register. AsmJit allows to encode and work with all forms of addresses mentioned and implemented by X86. It also allows to construct a 64-bit memory address, which is only allowed in one form of 'mov' instruction. + +```c++ +// Memory operand construction is provided by x86 namespace. +using namespace asmjit; +using namespace asmjit::x86; // Easier to access x86 regs. + +// BASE + OFFSET. +X86Mem a = ptr(rax); // a = [rax] +X86Mem b = ptr(rax, 15) // b = [rax + 15] + +// BASE + INDEX + SCALE - Scale is in BITS as used by X86! +X86Mem c = ptr(rax, rbx) // c = [rax + rbx] +X86Mem d = ptr(rax, rbx, 2) // d = [rax + rbx << 2] +X86Mem e = ptr(rax, rbx, 2, 15) // e = [rax + rbx << 2 + 15] + +// BASE + VM (Vector Index) (encoded as MOD+VSIB). +X86Mem f = ptr(rax, xmm1) // f = [rax + xmm1] +X86Mem g = ptr(rax, xmm1, 2) // g = [rax + xmm1 << 2] +X86Mem h = ptr(rax, xmm1, 2, 15) // h = [rax + xmm1 << 2 + 15] + +// WITHOUT BASE: +uint64_t ADDR = (uint64_t)0x1234; +X86Mem i = ptr(ADDR); // i = [0x1234] +X86Mem j = ptr(ADDR, rbx); // j = [0x1234 + rbx] +X86Mem k = ptr(ADDR, rbx, 2); // k = [0x1234 + rbx << 2] + +// LABEL - Will be encoded as RIP (64-bit) or absolute address (32-bit). +Label L = ...; +X86Mem m = ptr(L); // m = [L] +X86Mem n = ptr(L, rbx); // n = [L + rbx] +X86Mem o = ptr(L, rbx, 2); // o = [L + rbx << 2] +X86Mem p = ptr(L, rbx, 2, 15); // p = [L + rbx << 2 + 15] + +// RIP - 64-bit only (RIP can't use INDEX). +X86Mem q = ptr(rip, 24); // q = [rip + 24] +``` + +Memory operands can optionally contain memory size. This is required by instructions where the memory size cannot be deduced from other operands, like `inc` and `dec`: + +```c++ +X86Mem a = dword_ptr(rax, rbx); // dword ptr [rax + rbx]. +X86Mem b = qword_ptr(rdx, rsi, 0, 1); // qword ptr [rdx + rsi << 0 + 1]. +``` + +Memory operands provide API that can be used to work with them: + +```c++ +X86Mem mem = dword_ptr(rax, 12); // dword ptr [rax + 12]. + +mem.hasBase(); // true. +mem.hasIndex(); // false. +mem.getSize(); // 4. +mem.getOffset(); // 12. + +mem.setSize(0); // Sets the size to 0 (makes it sizeless). +mem.addOffset(-1); // Adds -1 to the offset and makes it 11. +mem.setOffset(0); // Sets the offset to 0. +mem.setBase(rcx); // Changes BASE to RCX. +mem.setIndex(rax); // Changes INDEX to RAX. +mem.hasIndex(); // true. + +// ... +``` + +Making changes to memory operand is very comfortable when emitting loads and stores: + +```c++ +using namespace asmjit; +using namespace asmjit::x86; + +X86Assembler a(...); // Your initialized X86Assembler. +X86Mem m = ptr(eax); // Construct [eax] memory operand. + +// One way of emitting bunch of loads is to use `mem.adjusted()`. It returns +// a new memory operand and keeps the source operand unchanged. +a.movaps(xmm0, m); // No adjustment needed to load [eax]. +a.movaps(xmm1, m.adjusted(16)); // Loads [eax + 16]. +a.movaps(xmm2, m.adjusted(32)); // Loads [eax + 32]. +a.movaps(xmm3, m.adjusted(48)); // Loads [eax + 48]. + +// ... do something with xmm0-3 ... + +// Another way of adjusting memory is to change the operand in-place. If you +// want to keep the original operand you can simply clone it. +X86Mem mx = m.clone(); +a.movaps(mx, xmm0); mx.addOffset(16); // Stores [eax] (and adds 16 to mx). +a.movaps(mx, xmm1); mx.addOffset(16); // Stores [eax + 16] (and adds 16 to mx). +a.movaps(mx, xmm2); mx.addOffset(16); // Stores [eax + 32] (and adds 16 to mx). +a.movaps(mx, xmm3); // Stores [eax + 48]. +``` + +You can explore the possibilities by taking a look at [base/operand.h](./src/asmjit/base/operand.h) and [x86/x86operand.h](./src/asmjit/x86/x86operand.h). Always use `X86Mem` when targeting X86 as it extends the base `Mem` operand with features provided only by X86. + +### More About CodeInfo + +In the first complete example the `CodeInfo` is retrieved from `JitRuntime`. It's logical as `JitRuntime` will always return a `CodeInfo` that is compatible with the runtime environment. For example if your application runs in 64-bit mode the `CodeInfo` will use `ArchInfo::kTypeX64` architecture in contrast to `ArchInfo::kTypeX86`, which will be used in 32-bit mode. AsmJit also allows to setup `CodeInfo` manually, and to select a different architecture when needed. So let's do something else this time, let's always generate a 32-bit code and print it's binary representation. To do that, we create our own `CodeInfo` and initialize it to `ArchInfo::kTypeX86` architecture. CodeInfo will populate all basic fields just based on the architecture we provide, so it's super-easy: + +```c++ +using namespace asmjit; + +int main(int argc, char* argv[]) { + using namespace asmjit::x86; // Easier access to x86/x64 registers. + + CodeHolder code; // Create a CodeHolder. + code.init(CodeInfo(ArchInfo::kTypeX86));// Initialize it for a 32-bit X86 target. + + // Generate a 32-bit function that sums 4 floats and looks like: + // void func(float* dst, const float* a, const float* b) + X86Assembler a(&code); // Create and attach X86Assembler to `code`. + + a.mov(eax, dword_ptr(esp, 4)); // Load the destination pointer. + a.mov(ecx, dword_ptr(esp, 8)); // Load the first source pointer. + a.mov(edx, dword_ptr(esp, 12)); // Load the second source pointer. + + a.movups(xmm0, ptr(ecx)); // Load 4 floats from [ecx] to XMM0. + a.movups(xmm1, ptr(edx)); // Load 4 floats from [edx] to XMM1. + a.addps(xmm0, xmm1); // Add 4 floats in XMM1 to XMM0. + a.movups(ptr(eax), xmm0); // Store the result to [eax]. + a.ret(); // Return from function. + + // Now we have two options if we want to do something with the code hold + // by CodeHolder. In order to use it we must first sync X86Assembler with + // the CodeHolder as it doesn't do it for every instruction it generates for + // performance reasons. The options are: + // + // 1. Detach X86Assembler from CodeHolder (will automatically sync). + // 2. Sync explicitly, allows to use X86Assembler again if needed. + // + // NOTE: AsmJit always syncs internally when CodeHolder needs to access these + // buffers and knows that there is an Assembler attached, so you have to sync + // explicitly only if you bypass CodeHolder and intend to do something on your + // own. + code.sync(); // So let's sync, it's easy. + + // We have no Runtime this time, it's on us what we do with the code. + // CodeHolder stores code in SectionEntry, which embeds CodeSection + // and CodeBuffer structures. We are interested in section's CodeBuffer only. + // + // NOTE: The first section is always '.text', so it's safe to just use 0 index. + CodeBuffer& buf = code.getSectionEntry(0)->buffer; + + // Print the machine-code generated or do something more interesting with it? + // 8B4424048B4C24048B5424040F28010F58010F2900C3 + for (size_t i = 0; i < buf.length; i++) + printf("%02X", buf.data[i]); + + return 0; +} +``` + +### Explicit Code Relocation + +CodeInfo contains much more information than just the target architecture. It can be configured to specify a base-address (or a virtual base-address in a linker terminology), which could be static (useful when you know the location of the target's machine code) or dynamic. AsmJit assumes dynamic base-address by default and relocates the code held by `CodeHolder` to a user-provided address on-demand. To be able to relocate to a user-provided address it needs to store some information about relocations, which is represented by `CodeHolder::RelocEntry`. Relocation entries are only required if you call external functions from the generated code that cannot be encoded by using a 32-bit displacement (X64 architecture doesn't provide 64-bit encodable displacement) and when a label referenced in one section is bound in another, but this is not really a JIT case and it's more related to AOT (ahead-of-time) compilation. + +Next example shows how to use a built-in virtual memory manager `VMemMgr` instead of using `JitRuntime` (just in case you want to use your own memory management) and how to relocate the generated code into your own memory block - you can use your own virtual memory allocator if you need that, but that's OS specific and it's already provided by AsmJit, so we will use what AsmJit offers instead of rolling our own here. + +The following code is similar to the previous one, but implements a function working in both 32-bit and 64-bit environments: + +```c++ +using namespace asmjit; + +typedef void (*SumIntsFunc)(int* dst, const int* a, const int* b); + +int main(int argc, char* argv[]) { + using namespace asmjit::x86; // Easier access to x86/x64 registers. + + CodeHolder code; // Create a CodeHolder. + code.init(CodeInfo(ArchInfo::kTypeHost));// Initialize it for the host architecture. + + X86Assembler a(&code); // Create and attach X86Assembler to `code`. + + // Generate a function runnable in both 32-bit and 64-bit architectures: + bool isX86 = static_cast(ASMJIT_ARCH_X86); + bool isWin = static_cast(ASMJIT_OS_WINDOWS); + + // Signature: 'void func(int* dst, const int* a, const int* b)'. + X86Gp dst; + X86Gp src_a; + X86Gp src_b; + + // Handle the difference between 32-bit and 64-bit calling convention. + // (arguments passed through stack vs. arguments passed by registers). + if (isX86) { + dst = eax; + src_a = ecx; + src_b = edx; + a.mov(dst , dword_ptr(esp, 4)); // Load the destination pointer. + a.mov(src_a, dword_ptr(esp, 8)); // Load the first source pointer. + a.mov(src_b, dword_ptr(esp, 12)); // Load the second source pointer. + } + else { + dst = isWin ? rcx : rdi; // First argument (destination pointer). + src_a = isWin ? rdx : rsi; // Second argument (source 'a' pointer). + src_b = isWin ? r8 : rdx; // Third argument (source 'b' pointer). + } + + a.movdqu(xmm0, ptr(src_a)); // Load 4 ints from [src_a] to XMM0. + a.movdqu(xmm1, ptr(src_b)); // Load 4 ints from [src_b] to XMM1. + a.paddd(xmm0, xmm1); // Add 4 ints in XMM1 to XMM0. + a.movdqu(ptr(dst), xmm0); // Store the result to [dst]. + a.ret(); // Return from function. + + // After the code was generated it can be relocated manually to any memory + // location, however, we need to know it's size before we perform memory + // allocation. CodeHolder's `getCodeSize()` returns the worst estimated + // code-size (the biggest possible) in case that relocations are not + // possible without trampolines (in that case some extra code at the end + // of the current code buffer is generated during relocation). + size_t size = code.getCodeSize(); + + // Instead of rolling our own virtual memory allocator we can use the one + // AsmJit uses. It's decoupled so you don't need to use Runtime for that. + VMemMgr vm; + + void* p = vm.alloc(size); // Allocate a virtual memory (executable). + if (!p) return 0; // Handle a possible out-of-memory case. + + size_t realSize = code.relocate(p); // Relocate & store the output in 'p'. + + // Execute the generated function. + int inA[4] = { 4, 3, 2, 1 }; + int inB[4] = { 1, 5, 2, 8 }; + int out[4]; + + // This code uses AsmJit's ptr_cast<> to cast between void* and SumIntsFunc. + ptr_cast(p)(result, arr_a, arr_b); + + // Prints {5 8 4 9} + printf("{%d %d %d %d}\n", out[0], out[1], out[2], out[3]); + + // Release 'p' is it's no longer needed. It will be destroyed with 'vm' + // instance anyway, but it's a good practice to release it explicitly + // when you know that the function will not be needed anymore. + vm.release(p); + + return 0; +} +``` + +Configure the CodeInfo by calling `CodeInfo::setBaseAddress()` to initialize it to a user-provided base-address before passing it to `CodeHolder`: + +```c++ +// Configure CodeInfo. +CodeInfo ci(...); +ci.setBaseAddress(uint64_t(0x1234)); + +// Then initialize CodeHolder with it. +CodeHolder code; +code.init(ci); + +// ... after you emit the machine code it will be relocated to the base address +// provided and stored in the pointer passed to `CodeHolder::relocate()`. +``` + +TODO: Maybe `CodeHolder::relocate()` is not the best name? + +### Using Native Registers - zax, zbx, zcx, ... + +AsmJit's X86 code emitters always provide functions to construct machine-size registers depending on the target. This feature is for people that want to write code targeting both 32-bit and 64-bit at the same time. In AsmJit terminology these registers are named **zax**, **zcx**, **zdx**, **zbx**, **zsp**, **zbp**, **zsi**, and **zdi** (they are defined in this exact order by X86). They are accessible through `X86Assembler`, `X86Builder`, and `X86Compiler`. The following example illustrates how to use this feature: + +```c++ +using namespace asmjit; + +typedef int (*Func)(void); + +int main(int argc, char* argv[]) { + JitRuntime rt; // Create a runtime specialized for JIT. + + CodeHolder code; // Create a CodeHolder. + code.init(rt.getCodeInfo()); // Initialize it to be compatible with `rt`. + + X86Assembler a(&code); // Create and attach X86Assembler to `code`. + + // Let's get these registers from X86Assembler. + X86Gp zbp = a.zbp(); + X86Gp zsp = a.zsp(); + + int stackSize = 32; + + // Function prolog. + a.push(zbp); + a.mov(zbp, zsp); + a.sub(zsp, stackSize); + + // ... emit some code (this just sets return value to zero) ... + a.xor_(x86::eax, x86::eax); + + // Function epilog and return. + a.mov(zsp, zbp); + a.pop(zbp); + a.ret(); + + // To make the example complete let's call it. + Func fn; + Error err = rt.add(&fn, &code); // Add the code generated to the runtime. + if (err) return 1; // Handle a possible error returned by AsmJit. + + int result = fn(); // Execute the generated code. + printf("%d\n", result); // Print the resulting "0". + + rt.release(fn); // Remove the function from the runtime. + return 0; +} +``` + +The example just returns `0`, but the function generated contains a standard prolog and epilog sequence and the function itself reserves 32 bytes of local stack. The advantage is clear - a single code-base can handle multiple targets easily. If you want to create a register of native size dynamically by specifying its id it's also possible: + +```c++ +void example(X86Assembler& a) { + X86Gp zax = a.gpz(X86Gp::kIdAx); + X86Gp zbx = a.gpz(X86Gp::kIdBx); + X86Gp zcx = a.gpz(X86Gp::kIdCx); + X86Gp zdx = a.gpz(X86Gp::kIdDx); + + // You can also change register's id easily. + X86Gp zsp = zax; + zsp.setId(4); // or X86Gp::kIdSp. +} +``` + +Cloning existing registers and chaning their IDs is fine in AsmJit; and this technique is used internally in many places. + +### Using Assembler as Code-Patcher + +This is an advanced topic that is sometimes unavoidable. AsmJit by default appends machine-code it generates into a `CodeBuffer`, however, it also allows to set the offset in `CodeBuffer` explicitly and to overwrite its content. This technique is extremely dangerous for asm beginners as X86 instructions have variable length (see below), so you should in general only patch code to change instruction's offset or some basic other details you didn't know about the first time you emitted it. A typical scenario that requires code-patching is when you start emitting function and you don't know how much stack you want to reserve for it. + +Before we go further it's important to introduce instruction options, because they can help with code-patching (and not only patching, but that will be explained in AVX-512 section): + + * Many general-purpose instructions (especially arithmetic ones) on X86 have multiple encodings - in AsmJit this is usually called 'short form' and 'long form'. + * AsmJit always tries to use 'short form' as it makes the resulting machine-code smaller, which is always good - this decision is used by majority of assemblers out there. + * AsmJit allows to override the default decision by using `short_()` and `long()_` instruction options to force short or long form, respectively. The most useful is `long_()` as it basically forces AsmJit to always emit the long form. The `short_()` is not that useful as it's automatic (except jumps to non-bound labels). Note the underscore after each function name as it avoids collision with built-in C++ types. + +To illustrate what short form and long form means in binary let's assume we want to emit `add esp, 16` instruction, which has two possible binary encodings: + + * `83C410` - This is a short form aka `short add esp, 16` - You can see opcode byte (0x8C), MOD/RM byte (0xC4) and an 8-bit immediate value representing `16`. + * `81C410000000` - This is a long form aka `long add esp, 16` - You can see a different opcode byte (0x81), the same Mod/RM byte (0xC4) and a 32-bit immediate in little-endian representing `16`. + +If you generate an instruction in a short form and then patch it in a long form or vice-versa then something really bad will happen when you try to execute such code. The following example illustrates how to patch the code properly (it just extends the previous example): + +```c++ +using namespace asmjit; + +typedef int (*Func)(void); + +int main(int argc, char* argv[]) { + JitRuntime rt; // Create a runtime specialized for JIT. + + CodeHolder code; // Create a CodeHolder. + code.init(rt.getCodeInfo()); // Initialize it to be compatible with `rt`. + + X86Assembler a(&code); // Create and attach X86Assembler to `code`. + + // Let's get these registers from X86Assembler. + X86Gp zbp = a.zbp(); + X86Gp zsp = a.zsp(); + + // Function prolog. + a.push(zbp); + a.mov(zbp, zsp); + + // This is where we are gonna patch the code later, so let's get the offset + // (the current location) from the beginning of the code-buffer. + size_t patchOffset = a.getOffset(); + // Let's just emit 'sub zsp, 0' for now, but don't forget to use LONG form. + a.long_().sub(zsp, 0); + + // ... emit some code (this just sets return value to zero) ... + a.xor_(x86::eax, x86::eax); + + // Function epilog and return. + a.mov(zsp, zbp); + a.pop(zbp); + a.ret(); + + // Now we know how much stack size we want to reserve. I have chosen 128 + // bytes on purpose as it's encodable only in long form that we have used. + + int stackSize = 128; // Number of bytes to reserve on the stack. + a.setOffset(patchOffset); // Move the current cursor to `patchOffset`. + a.long_().sub(zsp, stackSize); // Patch the code; don't forget to use LONG form. + + // Now the code is ready to be called + Func fn; + Error err = rt.add(&fn, &code); // Add the code generated to the runtime. + if (err) return 1; // Handle a possible error returned by AsmJit. + + int result = fn(); // Execute the generated code. + printf("%d\n", result); // Print the resulting "0". + + rt.release(fn); // Remove the function from the runtime. + return 0; +} +``` + +If you run the example it would just work. As an experiment you can try removing `long_()` form to see what happens when wrong code is generated. + +### Code Patching and REX Prefix + +In 64-bit mode there is one more thing to worry about when patching code - REX prefix. It's a single byte prefix designed to address registers with ids from 9 to 15 and to override the default width of operation from 32 to 64 bits. AsmJit, like other assemblers, only emits REX prefix when it's necessary. If the patched code only changes the immediate value as shown in the previous example then there is nothing to worry about as it doesn't change the logic behind emitting REX prefix, however, if the patched code changes register id or overrides the operation width then it's important to take care of REX prefix as well. + +AsmJit contains another instruction option that controls (forces) REX prefix - `rex()`. If you use it the instruction emitted will always use REX prefix even when it's encodable without it. The following list contains some instructions and their binary representations to illustrate when it's emitted: + + * `__83C410` - `add esp, 16` - 32-bit operation in 64-bit mode doesn't require REX prefix. + * `4083C410` - `rex add esp, 16` - 32-bit operation in 64-bit mode with forced REX prefix (0x40). + * `4883C410` - `add rsp, 16` - 64-bit operation in 64-bit mode requires REX prefix (0x48). + * `4183C410` - `add r12d, 16` - 32-bit operation in 64-bit mode using R12D requires REX prefix (0x41). + * `4983C410` - `add r12, 16` - 64-bit operation in 64-bit mode using R12 requires REX prefix (0x49). + +### Using Func-API + +So far all examples shown above handled creating function prologs and epilogs manually. While it's possible to do it that way it's much better to automate such process as function calling conventions vary across architectures and also across operating systems. + +AsmJit contains a functionality that can be used to define function signatures and to calculate automatically optimal frame layout that can be used directly by a prolog and epilog inserter. This feature was exclusive to AsmJit's CodeCompiler for a very long time, but was abstracted out and is now available for all users regardless of CodeEmitter they use. The design of handling functions prologs and epilogs allows generally two use cases: + + * Calculate function layout before the function is generated - this is the only way if you use pure `Assembler` emitter and shown in the next example. + * Calculate function layout after the function is generated - this way is generally used by `CodeBuilder` and `CodeCompiler` (will be described together with `X86Compiler`). + +The following concepts are used to describe and create functions in AsmJit: + + * **CallConv** - Describes a calling convention - this class contains instructions to assign registers and stack addresses to function arguments and return value(s), but doesn't specify any function signature. Calling conventions are architecture and OS dependent. + + * **TypeId** - TypeId is an 8-bit value that describes a platform independent type. It provides abstractions for most common types like `int8_t`, `uint32_t`, `uintptr_t`, `float`, `double`, and all possible vector types to match ISAs up to AVX512. TypeId was introduced originally for CodeCompiler, but is also used by FuncSignature. + + * **FuncSignature** - Describes a function signature, for example `int func(int, int)`. FuncSignature contains a function calling convention id, return value type, and function arguments. The signature itself is platform independent and uses TypeId to describe types of function arguments and its return value(s). + + * **FuncDetail** - Architecture and ABI dependent information that describes CallConv and expanded FuncSignature. Each function argument and return value is represented as **FuncDetail::Value** that contains the original TypeId enriched by additional information that specifies if the value is passed/returned by register (and which register) or by stack. Each value also contains some other metadata that provide additional information required to handle it properly (for example if a vector value is passed indirectly by a pointer as required by WIN64 calling convention, etc...). + + * **FuncArgsMapper** - A helper that can be used to define where each function argument is expected to be. It's architecture and ABI dependent mapping from function arguments described by CallConv and FuncDetail into registers specified by the user. + + * **FuncFrameInfo** - Contains information about a function-frame. Holds callout-stack size and alignment (i.e. stack used to call functions), stack-frame size and alignment (the stack required by the function itself), and various attributes that describe how prolog and epilog should be constructed. FuncFrameInfo doesn't know anything about function arguments or returns, it should be seen as a class that describes minimum requirements of the function frame and its attributes before the final `FuncFrameLayout` is calculated. + + * **FuncFrameLayout** - Contains the final function layout that can be passed to `FuncUtils::emitProlog()` and `FuncUtils::emitEpilog()`. The content of this class should always be calculated by AsmJit by calling `FuncFrameLayout::init(const FuncDetail& detail, const FuncFrameInfo& ffi)`. + +It's a lot of concepts where each represents one step in the function layout calculation. In addition, the whole machinery can also be used to create function calls, instead of function prologs and epilogs. The next example shows how AsmJit can be used to create functions for both 32-bit and 64-bit targets and various calling conventions: + +```c++ +using namespace asmjit; + +typedef void (*SumIntsFunc)(int* dst, const int* a, const int* b); + +int main(int argc, char* argv[]) { + JitRuntime rt; // Create JIT Runtime + + CodeHolder code; // Create a CodeHolder. + code.init(rt.getCodeInfo()); // Initialize it to match `rt`. + X86Assembler a(&code); // Create and attach X86Assembler to `code`. + + // Decide which registers will be mapped to function arguments. Try changing + // registers of `dst`, `src_a`, and `src_b` and see what happens in function's + // prolog and epilog. + X86Gp dst = a.zax(); + X86Gp src_a = a.zcx(); + X86Gp src_b = a.zdx(); + + X86Xmm vec0 = x86::xmm0; + X86Xmm vec1 = x86::xmm1; + + // Create and initialize `FuncDetail` and `FuncFrameInfo`. Both are + // needed to create a function and they hold different kind of data. + FuncDetail func; + func.init(FuncSignature3(CallConv::kIdHost)); + + FuncFrameInfo ffi; + ffi.setDirtyRegs(X86Reg::kKindVec, // Make XMM0 and XMM1 dirty. VEC kind + Utils::mask(0, 1)); // describes XMM|YMM|ZMM registers. + + FuncArgsMapper args(&func); // Create function arguments mapper. + args.assignAll(dst, src_a, src_b); // Assign our registers to arguments. + args.updateFrameInfo(ffi); // Reflect our args in FuncFrameInfo. + + FuncFrameLayout layout; // Create the FuncFrameLayout, which + layout.init(func, ffi); // contains metadata of prolog/epilog. + + FuncUtils::emitProlog(&a, layout); // Emit function prolog. + FuncUtils::allocArgs(&a, layout, args); // Allocate arguments to registers. + a.movdqu(vec0, x86::ptr(src_a)); // Load 4 ints from [src_a] to XMM0. + a.movdqu(vec1, x86::ptr(src_b)); // Load 4 ints from [src_b] to XMM1. + a.paddd(vec0, vec1); // Add 4 ints in XMM1 to XMM0. + a.movdqu(x86::ptr(dst), vec0); // Store the result to [dst]. + FuncUtils::emitEpilog(&a, layout); // Emit function epilog and return. + + SumIntsFunc fn; + Error err = rt.add(&fn, &code); // Add the code generated to the runtime. + if (err) return 1; // Handle a possible error case. + + // Execute the generated function. + int inA[4] = { 4, 3, 2, 1 }; + int inB[4] = { 1, 5, 2, 8 }; + int out[4]; + fn(out, inA, inB); + + // Prints {5 8 4 9} + printf("{%d %d %d %d}\n", out[0], out[1], out[2], out[3]); + + rt.release(fn); // Remove the function from the runtime. + return 0; +} +``` + +CodeBuilder & CodeCompiler +-------------------------- + +Both **CodeBuilder** and **CodeCompiler** are emitters that emit everything to a representation that allows further processing. The code stored in such representation is completely safe to be patched, simplified, reordered, obfuscated, removed, injected, analyzed, and 'think-of-something-else'. Each instruction (or label, directive, ...) is stored as **CBNode** (Code-Builder Node) and contains all the necessary information to emit machine code out of it later. + +There is a difference between **CodeBuilder** and **CodeCompiler**: + + * **CodeBuilder** (low-level): + * Maximum compatibility with *Assembler**, easy to switch from **Assembler** to **CodeBuilder** and vice versa. + * Doesn't generate machine code directly, allows to serialize to **Assembler** when the whole code is ready to be encoded. + + * **CodeCompiler** (high-level): + * Virtual registers - allows to use unlimited number of virtual registers which are allocated into physical registers by a built-in register allocator. + * Function nodes - allows to create functions by specifying their signatures and assigning virtual registers to function arguments and return value(s). + * Function calls - allows to call other functions withing the generated code by using the same interface for defining function signatures. + +There are multiple node types used by both: + + * Basic Nodes: + * **CBNode** - Base class for all nodes. + * **CBInst** - Instruction node. + * **CBAlign** - Alignment directive (.align). + * **CBLabel** - Bound label. + + * Data Nodes: + * **CBData** - Data embedded into the code. + * **CBConstPool** - Constant pool data. + * **CBLabelData** - Label address embedded as data. + + * Informative Nodes: + * **CBComment** - Contains a comment string, doesn't affect code generation. + * **CBSentinel** - A marker that can be used to remember certain position, doesn't affect code generation. + + * **CodeCompiler** Nodes: + * **CCFunc** - Start of a function. + * **CCFuncRet** - Return from a function. + * **CCFuncCall* - Function call. + +### Using CodeBuilder + + + + + + + + +### TODO + +Documentation not updated from here... + + + + + + + + + + + + + +### Function Signature + +AsmJit needs to know the prototype of the function it will generate or call. AsmJit contains a mapping between a type and the register that will be used to represent it. To make life easier there is a function builder that does the mapping on the fly. Function builder is a template class that helps with creating a function prototype by using native C/C++ types that describe function arguments and return value. It translates C/C++ native types into AsmJit specific IDs and makes these IDs accessible to Compiler. + +### Putting It All Together + +Let's put all together and generate a first function that sums its two arguments and returns the result. At the end the generated function is called from a C++ code. + +```c++ +#include + +using namespace asmjit; + +int main(int argc, char* argv[]) { + // Create JitRuntime. + JitRuntime rt; + + // Create X86Assembler and X86Compiler. + X86Assembler a(&rt); + X86Compiler c(&a); + + // Build function having two arguments and a return value of type 'int'. + // First type in function builder describes the return value. kCallConvHost + // tells the compiler to use the host calling convention. + c.addFunc(FuncBuilder2(kCallConvHost)); + + // Create 32-bit virtual registers and assign some names to them. Using names + // is not necessary, however, it can make debugging easier as these show in + // annotations, if turned-on. + X86Gp x = c.newI32("x"); + X86Gp y = c.newI32("y"); + + // Tell asmjit to use these variables as function arguments. + c.setArg(0, x); + c.setArg(1, y); + + // x = x + y; + c.add(x, y); + + // Tell asmjit to return `x`. + c.ret(x); + + // Finalize the current function. + c.endFunc(); + + // Now the Compiler contains the whole function, but the code is not yet + // generated. To tell the compiler to serialize the code to `Assembler` + // `c.finalize()` has to be called. After finalization the `Compiler` + // won't contain the code anymore and will be detached from the `Assembler`. + c.finalize(); + + // After finalization the code has been send to `Assembler`. It contains + // a handy method `make()`, which returns a pointer that points to the + // first byte of the generated code, which is the function entry in our + // case. + void* funcPtr = a.make(); + + // In order to run 'funcPtr' it has to be casted to the desired type. + // Typedef is a recommended and safe way to create a function-type. + typedef int (*FuncType)(int, int); + + // Using asmjit_cast is purely optional, it's basically a C-style cast + // that tries to make it visible that a function-type is returned. + FuncType func = asmjit_cast(funcPtr); + + // Finally, run it and do something with the result... + int z = func(1, 2); + printf("z=%d\n", z); // Outputs "z=3". + + // The function will remain in memory after Compiler and Assembler are + // destroyed. This is why the `JitRuntime` is used - it keeps track of + // the code generated. When `Runtime` is destroyed it also invalidates + // all code relocated by it (which is in our case also our `func`). So + // it's safe to just do nothing in our case, because destroying `Runtime` + // will free `func` as well, however, it's always better to release the + // generated code that is not needed anymore manually. + rt.release((void*)func); + + return 0; +} +``` + +The code should be self explanatory, however there are some details to be clarified. + +The code above generates and calls a function of `kCallConvHost` calling convention. 32-bit architecture contains a wide range of function calling conventions that can be all used by a single program, so it's important to know which calling convention is used by your C/C++ compiler so you can call the function. However, most compilers should generate CDecl by default. In 64-bit mode there are only two calling conventions, one is specific for Windows (Win64 calling convention) and the other for Unix (AMD64 calling convention). The `kCallConvHost` is defined to be one of CDecl, Win64 or AMD64 depending on your architecture and operating system. + +Default integer size is platform specific, virtual types `kVarTypeIntPtr` and `kVarTypeUIntPtr` can be used to make the code more portable and they should be always used when a pointer type is needed. When no type is specified AsmJit always defaults to `kVarTypeIntPtr`. The code above works with integers where the default behavior has been overidden to 32-bits. Note it's always a good practice to specify the type of the variable used. Alternative form of creating a variable is `c.newGpVar(...)`, `c.newMmVar(...)`, `c.newXmmVar` and so on... + +The function starts with `c.addFunc()` and ends with `c.endFunc()`. It's not allowed to put code outside of the function; however, embedding data outside of the function body is allowed. + +### Using Labels + +Labels are essential for making jumps, function calls or to refer to a data that is embedded in the code section. Label has to be explicitly created by using `newLabel()` method of your code generator in order to be used. The following example executes a code that depends on the condition by using a `Label` and conditional jump instruction. If the first parameter is zero it returns `a + b`, otherwise `a - b`. + +```c++ +#include + +using namespace asmjit; + +int main(int argc, char* argv[]) { + JitRuntime rt; + + X86Assembler a(&rt); + X86Compiler c(&a); + + // This function uses 3 arguments - `int func(int, int, int)`. + c.addFunc(FuncBuilder3(kCallConvHost)); + + X86Gp op = c.newI32("op"); + X86Gp x = c.newI32("x"); + X86Gp y = c.newI32("y"); + + c.setArg(0, op); + c.setArg(1, x); + c.setArg(2, y); + + // Create labels. + Label L_Sub = c.newLabel(); + Label L_Skip = c.newLabel(); + + // If (op != 0) + // goto L_Sub; + c.test(op, op); + c.jne(L_Sub); + + // x = x + y; + // goto L_Skip; + c.add(x, y); + c.jmp(L_Skip); + + // L_Sub: + // x = x - y; + c.bind(L_Sub); + c.sub(x, y); + + // L_Skip: + c.bind(L_Skip); + + c.ret(x); + c.endFunc(); + c.finalize(); + + // The prototype of the generated function. + typedef int (*FuncType)(int, int, int); + FuncType func = asmjit_cast(a.make()); + + int res0 = func(0, 1, 2); + int res1 = func(1, 1, 2); + + printf("res0=%d\n", res0); // Outputs "res0=3". + printf("res1=%d\n", res1); // Outputs "res1=-1". + + rt.release((void*)func); + return 0; +} +``` + +In this example conditional and unconditional jumps were used with labels together. Labels have to be created explicitely by `Compiler` by using a `Label L = c.newLabel()` form. Each label as an unique ID that identifies it, however it's not a string and there is no way to query for a `Label` instance that already exists at the moment. Label is like any other operand moved by value, so the copy of the label will still reference the same label and changing a copied label will not change the original label. + +Each label has to be bound to the location in the code by using `bind()`; however, it can be bound only once! Trying to bind the same label multiple times has undefined behavior - assertion failure is the best case. + +### Memory Addressing + +X86/X64 architectures have several memory addressing modes which can be used to combine base register, index register and a displacement. In addition, index register can be shifted by a constant from 1 to 3 that can help with addressing elements up to 8-byte long in an array. AsmJit supports all forms of memory addressing. Memory operand can be created by using `asmjit::X86Mem` or by using related non-member functions like `asmjit::x86::ptr`. Use `ptr` to create a memory operand having a base register with optional index register and a displacement; use and `ptr_abs` to create a memory operand referring to an absolute address in memory (32-bit) and optionally having an index register. + +In the following example various memory addressing modes are used to demonstrate how to construct and use them. It creates a function that accepts an array and two indexes which specify which elements to sum and return. + +```c++ +#include + +using namespace asmjit; + +int main(int argc, char* argv[]) { + JitRuntime rt; + + X86Assembler a(&rt); + X86Compiler c(&a); + + // Function returning 'int' accepting pointer and two indexes. + c.addFunc(FuncBuilder3(kCallConvHost)); + + X86Gp p = c.newIntPtr("p"); + X86Gp xIndex = c.newIntPtr("xIndex"); + X86Gp yIndex = c.newIntPtr("yIndex"); + + c.setArg(0, p); + c.setArg(1, xIndex); + c.setArg(2, yIndex); + + X86Gp x = c.newI32("x"); + X86Gp y = c.newI32("y"); + + // Read `x` by using a memory operand having base register, index register + // and scale. Translates to `mov x, dword ptr [p + xIndex << 2]`. + c.mov(x, x86::ptr(p, xIndex, 2)); + + // Read `y` by using a memory operand having base register only. Registers + // `p` and `yIndex` are both modified. + + // Shift bIndex by 2 (exactly the same as multiplying by 4). + // And add scaled 'bIndex' to 'p' resulting in 'p = p + bIndex * 4'. + c.shl(yIndex, 2); + c.add(p, yIndex); + + // Read `y`. + c.mov(y, x86::ptr(p)); + + // x = x + y; + c.add(x, y); + + c.ret(x); + c.endFunc(); + c.finalize(); + + // The prototype of the generated function. + typedef int (*FuncType)(const int*, intptr_t, intptr_t); + FuncType func = asmjit_cast(a.make()); + + // Array passed to `func`. + static const int array[] = { 1, 2, 3, 5, 8, 13 }; + + int xVal = func(array, 1, 2); + int yVal = func(array, 3, 5); + + printf("xVal=%d\n", xVal); // Outputs "xVal=5". + printf("yVal=%d\n", yVal); // Outputs "yVal=18". + + rt.release((void*)func); + return 0; +} +``` + +### Using Stack + +AsmJit uses stack automatically to spill virtual registers if there is not enough physical registers to keep them all allocated. The stack frame is managed by `Compiler` that provides also an interface to allocate chunks of memory of user specified size and alignment. + +In the following example a stack of 256 bytes size is allocated, filled by bytes starting from 0 to 255 and then iterated again to sum all the values. + +```c++ +#include + +using namespace asmjit; + +int main(int argc, char* argv[]) { + JitRuntime rt; + + X86Assembler a(&rt); + X86Compiler c(&a); + + // Function returning 'int' without any arguments. + c.addFunc(FuncBuilder0(kCallConvHost)); + + // Allocate 256 bytes on the stack aligned to 4 bytes. + X86Mem stack = c.newStack(256, 4); + + X86Gp p = c.newIntPtr("p"); + X86Gp i = c.newIntPtr("i"); + + // Load a stack address to `p`. This step is purely optional and shows + // that `lea` is useful to load a memory operands address (even absolute) + // to a general purpose register. + c.lea(p, stack); + + // Clear `i`. Notice that `xor_()` is used instead of `xor()` as it's keyword. + c.xor_(i, i); + + Label L1 = c.newLabel(); + Label L2 = c.newLabel(); + + // First loop, fill the stack allocated by a sequence of bytes from 0 to 255. + c.bind(L1); + + // Mov byte ptr[p + i], i. + // + // Any operand can be cloned and modified. By cloning `stack` and calling + // `setIndex()` we created a new memory operand based on stack having an + // index register assigned to it. + c.mov(stack.clone().setIndex(i), i.r8()); + + // if (++i < 256) + // goto L1; + c.inc(i); + c.cmp(i, 256); + c.jb(L1); + + // Second loop, sum all bytes stored in `stack`. + X86Gp sum = c.newI32("sum"); + X86Gp val = c.newI32("val"); + + c.xor_(i, i); + c.xor_(sum, sum); + + c.bind(L2); + + // Movzx val, byte ptr [stack + i] + c.movzx(val, stack.clone().setIndex(i).setSize(1)); + // sum += val; + c.add(sum, val); + + // if (++i < 256) + // goto L2; + c.inc(i); + c.cmp(i, 256); + c.jb(L2); + + c.ret(sum); + c.endFunc(); + c.finalize(); + + typedef int (*FuncType)(void); + FuncType func = asmjit_cast(a.make()); + + printf("sum=%d\n", func()); // Outputs "sum=32640". + + rt.release((void*)func); + return 0; +} +``` + +### Built-In Logging + +Failures are common when working at machine-code level. AsmJit does already a good job with function overloading to prevent from emitting semantically incorrect instructions; however, AsmJit can't prevent from emitting code that is semantically correct, but contains bug(s). Logging has always been an important part of AsmJit's infrastructure and the output can be very valuable after something went wrong. + +AsmJit contains extensible logging interface defined by `Logger` and specialized by `FileLogger` and `StringLogger` classes (you can create your own as well). `FileLogger` can log into a standard C-based `FILE*` stream while `StringLogger` logs into an internal buffer that can be used after the code generation is done. + +After a logger is attached to `Assembler` its methods will be called every time `Assembler` emits something. A single `Logger` instance can be used multiple times, however, loggers that contain state(s) like `StringLogger` must not be used by two or more threads at the same time. + +The following snippet describes how to log into `FILE*`: + +```c++ +// Create logger logging to `stdout`. Logger life-time should always be +// greater than a life-time of the code generator. Alternatively the +// logger can be reset before it's destroyed. +FileLogger logger(stdout); + +// Create runtime and assembler and attach the logger to the assembler. +JitRuntime rt; +X86Assembler a(&rt); +a.setLogger(&logger); + +// ... Generate the code ... +``` + +The following snippet describes how to log into a string: + +```c++ +StringLogger logger; + +JitRuntime rt; +X86Assembler a(&rt); +a.setLogger(&logger); + +// ... Generate the code ... + +printf("Logger Content:\n%s", logger.getString()); + +// You can use `logger.clearString()` if the intend is to reuse the logger. +``` + +Logger can be configured to show more information by using `logger.addOptions()` method. The following options are available: + + * `Logger::kOptionBinaryForm` - Output instructions also in binary form. + * `Logger::kOptionHexImmediate` - Output constants in hexadecimal form. + * `Logger::kOptionHexDisplacement` - Output displacements in hexadecimal form. + +### Error Handling + +AsmJit uses error codes to represent and return errors; and every function where error can occur will also return it. It's recommended to only check errors of the most important functions and to write a custom `ErrorHandler` to handle the rest. An error can happen in many places, but error handler is mostly used by `Assembler` to report a fatal problem. There are multiple ways of using `ErrorHandler`: + + * 1. Returning `true` or `false` from `handleError()`. If `true` is returned it means that error was handled and AsmJit can continue execution. The error code still be propagated to the caller, but won't put the origin into an error state (it won't set last-error). However, `false` reports to AsmJit that the error cannot be handled - in such case it stores the error, which can retrieved later by `getLastError()`. Returning `false` is the default behavior when no error handler is provided. To put the assembler into a non-error state again the `resetLastError()` must be called. + * 2. Throwing an exception. AsmJit doesn't use exceptions and is completely exception-safe, but you can throw exception from the error handler if this way is easier / preferred by you. Throwing an exception acts virtually as returning `true` - AsmJit won't store the error. + * 3. Using plain old C's `setjmp()` and `longjmp()`. Asmjit always puts `Assembler` and `Compiler` to a consistent state before calling the `handleError()` so `longjmp()` can be used without issues to cancel the code-generation if an error occurred. + +Here is an example of using `ErrorHandler` to just print the error but do nothing else: + +```c++ +// Error handling #1: +#include + +using namespace asmjit; + +class MyErrorHandler : public ErrorHandler { +public: + // Return `true` to set last error to `err`, return `false` to do nothing. + // The `origin` points to the `X86Assembler` instance (&a in our case). + virtual bool handleError(Error err, const char* message, void* origin) { + printf("ASMJIT ERROR: 0x%08X [%s]\n", err, message); + return false; + } +}; + +int main(int argc, char* argv[]) { + JitRuntime rt; + MyErrorHandler eh; + + X86Assembler a(&rt); + a.setErrorHandler(&eh); + + // Use RAW emit to emit an illegal instruction. + Error err = a.emit(X86Inst::kIdMov, x86::eax, x86::xmm4, x86::xmm1); + + // Message printed, the error contains the same error as passed to the + // error handler. Since we returned `false` the assembler is in an error + // state. Use `resetLastError` to reset it back to normal. + assert(a.getLastError() == err); + a.resetLastError(); + + // After the error is reset it should return `kErrorOk` like nothing happened. + assert(a.getLastError() == kErrorOk); + + return 0; +} +``` + +NOTE: If error happens during instruction emitting / encoding the assembler behaves transactionally - the output buffer won't advance if failed, thus a fully encoded instruction is either emitted or not. AsmJit is very safe and strict in this regard. The error handling shown above is useful, but it's still not the best way of dealing with errors in AsmJit. The following example shows how to use exception handling to handle errors in a safe way: + +```c++ +// Error handling #2: +#include +#include + +using namespace asmjit; + +class AsmJitException : public std::exception { +public: + AsmJitException(Error err, const char* message) noexcept + : error(err), + : message(message) {} + + Error error; + std::string message; +} + +class MyErrorHandler : public ErrorHandler { +public: + // `origin` points to the `X86Assembler` instance (&a) in our case. + virtual bool handleError(Error err, const char* message, void* origin) { + throw AsmJitException(err, message); + } +}; + +int main(int argc, char* argv[]) { + JitRuntime rt; + MyErrorHandler eh; + + X86Assembler a(&rt); + a.setErrorHandler(&eh); + + try { + // This will call `eh.handleError()`, which will throw. + a.emit(X86Inst::kIdMov, x86::eax, x86::xmm4, x86::xmm1); + } + catch (const AsmJitException& ex) { + printf("ASMJIT ERROR: 0x%08X [%s]\n", ex.error, ex.message.c_str()); + } +} +``` + +If C++ exceptions are not what you like or your project turns off them completely there is still a way of reducing the error handling to a minimum by using a standard `setjmp/longjmp` pair. AsmJit is exception-safe and doesn't use RAII for resource management internally, so you can just jump from the error handler without causing any side-effects or memory leaks. The following example demonstrates how to do that: + +```c++ +// Error handling #2: +#include +#include + +using namespace asmjit; + +class MyErrorHandler : public ErrorHandler { +public: + inline bool init() noexcept { + return setjmp(_state) == 0; + } + + virtual bool handleError(Error err, const char* message, void* origin) { + longjmp(_state, 1); + } + + jmp_buf _state; +}; + +int main(int argc, char* argv[]) { + JitRuntime rt; + MyErrorHandler eh; + + X86Assembler a(&rt); + a.setErrorHandler(&eh); + + if (eh.init()) { + // This will call `eh.handleError()`, which will call `longjmp()`. + a.emit(X86Inst::kIdMov, x86::eax, x86::xmm4, x86::xmm1); + } + else { + Error err = a.getLastError(); + printf("ASMJIT ERROR: 0x%08X [%s]\n", err, DebugUtils::errorAsString(err)); + } +} +``` + +### Using Constant Pool + +To be documented. + +### Code Injection + +Code injection was one of key concepts of Compiler from the beginning. Compiler records all emitted instructions in a double-linked list which can be manipulated before `make()` is called. Any call to Compiler that adds instruction, function or anything else in fact manipulates this list by inserting nodes into it. + +To manipulate the current cursor use Compiler's `getCursor()` and `setCursor()` methods. The following snippet demonstrates the proper way of code injection. + +```c++ +X86Compiler c(...); + +X86Gp x = c.newI32("x"); +X86Gp y = c.newI32("y"); + +AsmNode* here = c.getCursor(); +c.mov(y, 2); + +// Now, `here` can be used to inject something before `mov y, 2`. To inject +// something it's always good to remember the current cursor so it can be set +// back after the injecting is done. When `setCursor()` is called it returns +// the old cursor to be remembered. +AsmNode* prev = c.setCursor(here); +c.mov(x, 1); +c.setCursor(prev); +``` + +The resulting code would look like: + +``` +c.mov(x, 1); +c.mov(y, 2); +``` + +### TODO + +...More documentation... + +Support +------- + +Please consider a donation if you use the project and would like to keep it active in the future. + + * [Donate by PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=QDRM6SRNG7378&lc=EN;&item_name=asmjit¤cy_code=EUR) + +Received From: + + * [PELock - Software copy protection and license key system](https://www.pelock.com) + +Authors & Maintainers +--------------------- + + * Petr Kobalicek diff --git a/components/shared/detours/asmjit/asmjit/cxxconfig.js b/components/shared/detours/asmjit/asmjit/cxxconfig.js new file mode 100644 index 00000000..c82eb744 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/cxxconfig.js @@ -0,0 +1,16 @@ +module.exports = { + product: "asmjit", + version: "1.0.0", + + prefix: "ASMJIT", + source: "src/asmjit", + + tools: { + NoTabs : true, + NoTrailingLines : true, + NoTrailingSpaces: true, + UnixEOL : true, + SortIncludes : true, + ExpandTemplates : true + } +}; diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/arm.h b/components/shared/detours/asmjit/asmjit/src/asmjit/arm.h new file mode 100644 index 00000000..0a916d9c --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/arm.h @@ -0,0 +1,21 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_ARM_H +#define _ASMJIT_ARM_H + +// [Dependencies] +#include "./base.h" + +#include "./arm/armassembler.h" +#include "./arm/armbuilder.h" +#include "./arm/armcompiler.h" +#include "./arm/arminst.h" +#include "./arm/armoperand.h" + +// [Guard] +#endif // _ASMJIT_ARM_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/asmjit.h b/components/shared/detours/asmjit/asmjit/src/asmjit/asmjit.h new file mode 100644 index 00000000..0fb75663 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/asmjit.h @@ -0,0 +1,43 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_ASMJIT_H +#define _ASMJIT_ASMJIT_H + +// ============================================================================ +// [asmjit_mainpage] +// ============================================================================ + +//! \mainpage +//! +//! AsmJit - Complete x86/x64 JIT and Remote Assembler for C++. +//! +//! Introduction provided by the project page at https://github.com/asmjit/asmjit. + +//! \defgroup asmjit_base AsmJit Base API (architecture independent) +//! +//! \brief Backend Neutral API. + +//! \defgroup asmjit_x86 AsmJit X86/X64 API +//! +//! \brief X86/X64 Backend API. + +// [Dependencies] +#include "./base.h" + +// [X86/X64] +#if defined(ASMJIT_BUILD_X86) +#include "./x86.h" +#endif // ASMJIT_BUILD_X86 + +// [ARM32/ARM64] +#if defined(ASMJIT_BUILD_ARM) +#include "./arm.h" +#endif // ASMJIT_BUILD_ARM + +// [Guard] +#endif // _ASMJIT_ASMJIT_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/asmjit_apibegin.h b/components/shared/detours/asmjit/asmjit/src/asmjit/asmjit_apibegin.h new file mode 100644 index 00000000..3e5b8d99 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/asmjit_apibegin.h @@ -0,0 +1,77 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Dependencies] +#if !defined(_ASMJIT_BUILD_H) +#include "./build.h" +#endif // !_ASMJIT_BUILD_H + +// [Guard] +#if !defined(ASMJIT_API_SCOPE) +# define ASMJIT_API_SCOPE +#else +# error "[asmjit] api-scope is already active, previous scope not closed by asmjit_apiend.h?" +#endif // ASMJIT_API_SCOPE + +// [NoExcept] +#if !ASMJIT_CC_HAS_NOEXCEPT && !defined(noexcept) +# define noexcept ASMJIT_NOEXCEPT +# define ASMJIT_UNDEF_NOEXCEPT +#endif // !ASMJIT_CC_HAS_NOEXCEPT && !noexcept + +// [NullPtr] +#if !ASMJIT_CC_HAS_NULLPTR && !defined(nullptr) +# define nullptr NULL +# define ASMJIT_UNDEF_NULLPTR +#endif // !ASMJIT_CC_HAS_NULLPTR && !nullptr + +// [Override] +#if !ASMJIT_CC_HAS_OVERRIDE && !defined(override) +# define override +# define ASMJIT_UNDEF_OVERRIDE +#endif // !ASMJIT_CC_HAS_OVERRIDE && !override + +// [Clang] +#if ASMJIT_CC_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wc++11-extensions" +# pragma clang diagnostic ignored "-Wconstant-logical-operand" +# pragma clang diagnostic ignored "-Wunnamed-type-template-args" +#endif // ASMJIT_CC_CLANG + +// [GCC] +#if ASMJIT_CC_GCC +# pragma GCC diagnostic push +#endif // ASMJIT_CC_GCC + +// [MSC] +#if ASMJIT_CC_MSC + +# pragma warning(push) +# pragma warning(disable: 4127) // conditional expression is constant +# pragma warning(disable: 4201) // nameless struct/union +# pragma warning(disable: 4244) // '+=' : conversion from 'int' to 'x', possible + // loss of data +# pragma warning(disable: 4251) // struct needs to have dll-interface to be used + // by clients of struct ... +# pragma warning(disable: 4275) // non dll-interface struct ... used as base for + // dll-interface struct +# pragma warning(disable: 4355) // this used in base member initializer list +# pragma warning(disable: 4480) // specifying underlying type for enum +# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' + +# if _MSC_VER < 1900 +# if !defined(vsnprintf) +# define ASMJIT_UNDEF_VSNPRINTF +# define vsnprintf _vsnprintf +# endif // !vsnprintf +# if !defined(snprintf) +# define ASMJIT_UNDEF_SNPRINTF +# define snprintf _snprintf +# endif // !snprintf +# endif + +#endif // ASMJIT_CC_MSC diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/asmjit_apiend.h b/components/shared/detours/asmjit/asmjit/src/asmjit/asmjit_apiend.h new file mode 100644 index 00000000..5c8a30ce --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/asmjit_apiend.h @@ -0,0 +1,55 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#if defined(ASMJIT_API_SCOPE) +# undef ASMJIT_API_SCOPE +#else +# error "[asmjit] api-scope not active, forgot to include asmjit_apibegin.h?" +#endif // ASMJIT_API_SCOPE + +// [NoExcept] +#if defined(ASMJIT_UNDEF_NOEXCEPT) +# undef noexcept +# undef ASMJIT_UNDEF_NOEXCEPT +#endif // ASMJIT_UNDEF_NOEXCEPT + +// [NullPtr] +#if defined(ASMJIT_UNDEF_NULLPTR) +# undef nullptr +# undef ASMJIT_UNDEF_NULLPTR +#endif // ASMJIT_UNDEF_NULLPTR + +// [Override] +#if defined(ASMJIT_UNDEF_OVERRIDE) +# undef override +# undef ASMJIT_UNDEF_OVERRIDE +#endif // ASMJIT_UNDEF_OVERRIDE + +// [Clang] +#if ASMJIT_CC_CLANG +# pragma clang diagnostic pop +#endif // ASMJIT_CC_CLANG + +// [GCC] +#if ASMJIT_CC_GCC +# pragma GCC diagnostic pop +#endif // ASMJIT_CC_GCC + +// [MSC] +#if ASMJIT_CC_MSC +# pragma warning(pop) +# if _MSC_VER < 1900 +# if defined(ASMJIT_UNDEF_VSNPRINTF) +# undef vsnprintf +# undef ASMJIT_UNDEF_VSNPRINTF +# endif // ASMJIT_UNDEF_VSNPRINTF +# if defined(ASMJIT_UNDEF_SNPRINTF) +# undef snprintf +# undef ASMJIT_UNDEF_SNPRINTF +# endif // ASMJIT_UNDEF_SNPRINTF +# endif +#endif // ASMJIT_CC_MSC diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/asmjit_build.h b/components/shared/detours/asmjit/asmjit/src/asmjit/asmjit_build.h new file mode 100644 index 00000000..af1a94c1 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/asmjit_build.h @@ -0,0 +1,1011 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BUILD_H +#define _ASMJIT_BUILD_H + +// ============================================================================ +// [asmjit::Build - Configuration] +// ============================================================================ + +// AsmJit is by default compiled only for a host processor for the purpose of +// JIT code generation. Both Assembler and CodeCompiler emitters are compiled +// by default. Preprocessor macros can be used to change the default behavior. + +// External Config File +// -------------------- +// +// Define in case your configuration is generated in an external file to be +// included. + +#if defined(ASMJIT_CONFIG_FILE) +# include ASMJIT_CONFIG_FILE +#endif // ASMJIT_CONFIG_FILE + +// AsmJit Static Builds and Embedding +// ---------------------------------- +// +// These definitions can be used to enable static library build. Embed is used +// when AsmJit's source code is embedded directly in another project, implies +// static build as well. +// +// #define ASMJIT_EMBED // Asmjit is embedded (implies ASMJIT_STATIC). +// #define ASMJIT_STATIC // Define to enable static-library build. + +// AsmJit Build Modes +// ------------------ +// +// These definitions control the build mode and tracing support. The build mode +// should be auto-detected at compile time, but it's possible to override it in +// case that the auto-detection fails. +// +// Tracing is a feature that is never compiled by default and it's only used to +// debug AsmJit itself. +// +// #define ASMJIT_DEBUG // Define to enable debug-mode. +// #define ASMJIT_RELEASE // Define to enable release-mode. +// #define ASMJIT_TRACE // Define to enable tracing. + +// AsmJit Build Backends +// --------------------- +// +// These definitions control which backends to compile. If none of these is +// defined AsmJit will use host architecture by default (for JIT code generation). +// +// #define ASMJIT_BUILD_X86 // Define to enable X86 and X64 code-generation. +// #define ASMJIT_BUILD_ARM // Define to enable ARM32 and ARM64 code-generation. +// #define ASMJIT_BUILD_HOST // Define to enable host instruction set. + +// AsmJit Build Features +// --------------------- +// +// Flags can be defined to disable standard features. These are handy especially +// when building AsmJit statically and some features are not needed or unwanted +// (like CodeCompiler). +// +// AsmJit features are enabled by default. +// #define ASMJIT_DISABLE_COMPILER // Disable CodeCompiler (completely). +// #define ASMJIT_DISABLE_LOGGING // Disable Logger and Formatter (completely). +// #define ASMJIT_DISABLE_TEXT // Disable everything that contains text +// // representation (instructions, errors, ...). +// #define ASMJIT_DISABLE_VALIDATION // Disable Validation (completely). + +// Prevent compile-time errors caused by misconfiguration. +#if defined(ASMJIT_DISABLE_TEXT) && !defined(ASMJIT_DISABLE_LOGGING) +# error "[asmjit] ASMJIT_DISABLE_TEXT requires ASMJIT_DISABLE_LOGGING to be defined." +#endif // ASMJIT_DISABLE_TEXT && !ASMJIT_DISABLE_LOGGING + +// Detect ASMJIT_DEBUG and ASMJIT_RELEASE if not forced from outside. +#if !defined(ASMJIT_DEBUG) && !defined(ASMJIT_RELEASE) && !defined(NDEBUG) +# define ASMJIT_DEBUG +#else +#ifndef ASMJIT_RELEASE +# define ASMJIT_RELEASE +#endif +#endif + +// ASMJIT_EMBED implies ASMJIT_STATIC. +#if defined(ASMJIT_EMBED) && !defined(ASMJIT_STATIC) +# define ASMJIT_STATIC +#endif + +// ============================================================================ +// [asmjit::Build - VERSION] +// ============================================================================ + +// [@VERSION{@] +#define ASMJIT_VERSION_MAJOR 1 +#define ASMJIT_VERSION_MINOR 0 +#define ASMJIT_VERSION_PATCH 0 +#define ASMJIT_VERSION_STRING "1.0.0" +// [@VERSION}@] + +// ============================================================================ +// [asmjit::Build - WIN32] +// ============================================================================ + +// [@WIN32_CRT_NO_DEPRECATE{@] +#if defined(_MSC_VER) && defined(ASMJIT_EXPORTS) +# if !defined(_CRT_SECURE_NO_DEPRECATE) +# define _CRT_SECURE_NO_DEPRECATE +# endif +# if !defined(_CRT_SECURE_NO_WARNINGS) +# define _CRT_SECURE_NO_WARNINGS +# endif +#endif +// [@WIN32_CRT_NO_DEPRECATE}@] + +// [@WIN32_LEAN_AND_MEAN{@] +#if (defined(_WIN32) || defined(_WINDOWS)) && !defined(_WINDOWS_) +# if !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +# define ASMJIT_UNDEF_WIN32_LEAN_AND_MEAN +# endif +# if !defined(NOMINMAX) +# define NOMINMAX +# define ASMJIT_UNDEF_NOMINMAX +# endif +# include +# if defined(ASMJIT_UNDEF_NOMINMAX) +# undef NOMINMAX +# undef ASMJIT_UNDEF_NOMINMAX +# endif +# if defined(ASMJIT_UNDEF_WIN32_LEAN_AND_MEAN) +# undef WIN32_LEAN_AND_MEAN +# undef ASMJIT_UNDEF_WIN32_LEAN_AND_MEAN +# endif +#endif +// [@WIN32_LEAN_AND_MEAN}@] + +// ============================================================================ +// [asmjit::Build - OS] +// ============================================================================ + +// [@OS{@] +#if defined(_WIN32) || defined(_WINDOWS) +#define ASMJIT_OS_WINDOWS (1) +#else +#define ASMJIT_OS_WINDOWS (0) +#endif + +#if defined(__APPLE__) +# include +# define ASMJIT_OS_MAC (TARGET_OS_MAC) +# define ASMJIT_OS_IOS (TARGET_OS_IPHONE) +#else +# define ASMJIT_OS_MAC (0) +# define ASMJIT_OS_IOS (0) +#endif + +#if defined(__ANDROID__) +# define ASMJIT_OS_ANDROID (1) +#else +# define ASMJIT_OS_ANDROID (0) +#endif + +#if defined(__linux__) || defined(__ANDROID__) +# define ASMJIT_OS_LINUX (1) +#else +# define ASMJIT_OS_LINUX (0) +#endif + +#if defined(__DragonFly__) +# define ASMJIT_OS_DRAGONFLYBSD (1) +#else +# define ASMJIT_OS_DRAGONFLYBSD (0) +#endif + +#if defined(__FreeBSD__) +# define ASMJIT_OS_FREEBSD (1) +#else +# define ASMJIT_OS_FREEBSD (0) +#endif + +#if defined(__NetBSD__) +# define ASMJIT_OS_NETBSD (1) +#else +# define ASMJIT_OS_NETBSD (0) +#endif + +#if defined(__OpenBSD__) +# define ASMJIT_OS_OPENBSD (1) +#else +# define ASMJIT_OS_OPENBSD (0) +#endif + +#if defined(__QNXNTO__) +# define ASMJIT_OS_QNX (1) +#else +# define ASMJIT_OS_QNX (0) +#endif + +#if defined(__sun) +# define ASMJIT_OS_SOLARIS (1) +#else +# define ASMJIT_OS_SOLARIS (0) +#endif + +#if defined(__CYGWIN__) +# define ASMJIT_OS_CYGWIN (1) +#else +# define ASMJIT_OS_CYGWIN (0) +#endif + +#define ASMJIT_OS_BSD ( \ + ASMJIT_OS_FREEBSD || \ + ASMJIT_OS_DRAGONFLYBSD || \ + ASMJIT_OS_NETBSD || \ + ASMJIT_OS_OPENBSD || \ + ASMJIT_OS_MAC) +#define ASMJIT_OS_POSIX (!ASMJIT_OS_WINDOWS) +// [@OS}@] + +// ============================================================================ +// [asmjit::Build - ARCH] +// ============================================================================ + +// [@ARCH{@] +// \def ASMJIT_ARCH_ARM32 +// True if the target architecture is a 32-bit ARM. +// +// \def ASMJIT_ARCH_ARM64 +// True if the target architecture is a 64-bit ARM. +// +// \def ASMJIT_ARCH_X86 +// True if the target architecture is a 32-bit X86/IA32 +// +// \def ASMJIT_ARCH_X64 +// True if the target architecture is a 64-bit X64/AMD64 +// +// \def ASMJIT_ARCH_LE +// True if the target architecture is little endian. +// +// \def ASMJIT_ARCH_BE +// True if the target architecture is big endian. +// +// \def ASMJIT_ARCH_64BIT +// True if the target architecture is 64-bit. + +#if (defined(_M_X64 ) || defined(__x86_64) || defined(__x86_64__) || \ + defined(_M_AMD64) || defined(__amd64 ) || defined(__amd64__ )) +# define ASMJIT_ARCH_X64 1 +#else +# define ASMJIT_ARCH_X64 0 +#endif + +#if (defined(_M_IX86 ) || defined(__X86__ ) || defined(__i386 ) || \ + defined(__IA32__) || defined(__I86__ ) || defined(__i386__) || \ + defined(__i486__) || defined(__i586__) || defined(__i686__)) +# define ASMJIT_ARCH_X86 (!ASMJIT_ARCH_X64) +#else +# define ASMJIT_ARCH_X86 0 +#endif + +#if defined(__aarch64__) +# define ASMJIT_ARCH_ARM64 1 +#else +# define ASMJIT_ARCH_ARM64 0 +#endif + +#if (defined(_M_ARM ) || defined(__arm ) || defined(__thumb__ ) || \ + defined(_M_ARMT ) || defined(__arm__ ) || defined(__thumb2__)) +# define ASMJIT_ARCH_ARM32 (!ASMJIT_ARCH_ARM64) +#else +# define ASMJIT_ARCH_ARM32 0 +#endif + +#define ASMJIT_ARCH_LE ( \ + ASMJIT_ARCH_X86 || \ + ASMJIT_ARCH_X64 || \ + ASMJIT_ARCH_ARM32 || \ + ASMJIT_ARCH_ARM64 ) +#define ASMJIT_ARCH_BE (!(ASMJIT_ARCH_LE)) +#define ASMJIT_ARCH_64BIT (ASMJIT_ARCH_X64 || ASMJIT_ARCH_ARM64) +// [@ARCH}@] + +// [@ARCH_UNALIGNED_RW{@] +// \def ASMJIT_ARCH_UNALIGNED_16 +// True if the target architecture allows unaligned 16-bit reads and writes. +// +// \def ASMJIT_ARCH_UNALIGNED_32 +// True if the target architecture allows unaligned 32-bit reads and writes. +// +// \def ASMJIT_ARCH_UNALIGNED_64 +// True if the target architecture allows unaligned 64-bit reads and writes. + +#define ASMJIT_ARCH_UNALIGNED_16 (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) +#define ASMJIT_ARCH_UNALIGNED_32 (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) +#define ASMJIT_ARCH_UNALIGNED_64 (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) +// [@ARCH_UNALIGNED_RW}@] + +// ============================================================================ +// [asmjit::Build - CC] +// ============================================================================ + +// [@CC{@] +// \def ASMJIT_CC_CLANG +// Non-zero if the detected C++ compiler is CLANG (contains normalized CLANG version). +// +// \def ASMJIT_CC_CODEGEAR +// Non-zero if the detected C++ compiler is CODEGEAR or BORLAND (version not normalized). +// +// \def ASMJIT_CC_INTEL +// Non-zero if the detected C++ compiler is INTEL (version not normalized). +// +// \def ASMJIT_CC_GCC +// Non-zero if the detected C++ compiler is GCC (contains normalized GCC version). +// +// \def ASMJIT_CC_MSC +// Non-zero if the detected C++ compiler is MSC (contains normalized MSC version). +// +// \def ASMJIT_CC_MINGW +// Non-zero if the detected C++ compiler is MINGW32 (set to 32) or MINGW64 (set to 64). + +#define ASMJIT_CC_CLANG 0 +#define ASMJIT_CC_CODEGEAR 0 +#define ASMJIT_CC_GCC 0 +#define ASMJIT_CC_INTEL 0 +#define ASMJIT_CC_MSC 0 + +// Intel masquerades as GCC, so check for it first. +#if defined(__INTEL_COMPILER) +# undef ASMJIT_CC_INTEL +# define ASMJIT_CC_INTEL __INTEL_COMPILER +#elif defined(__CODEGEARC__) +# undef ASMJIT_CC_CODEGEAR +# define ASMJIT_CC_CODEGEAR (__CODEGEARC__) +#elif defined(__BORLANDC__) +# undef ASMJIT_CC_CODEGEAR +# define ASMJIT_CC_CODEGEAR (__BORLANDC__) +#elif defined(__clang__) && defined(__clang_minor__) +# undef ASMJIT_CC_CLANG +# define ASMJIT_CC_CLANG (__clang_major__ * 10000000 + __clang_minor__ * 100000 + __clang_patchlevel__) +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) +# undef ASMJIT_CC_GCC +# define ASMJIT_CC_GCC (__GNUC__ * 10000000 + __GNUC_MINOR__ * 100000 + __GNUC_PATCHLEVEL__) +#elif defined(_MSC_VER) && defined(_MSC_FULL_VER) +# undef ASMJIT_CC_MSC +# if _MSC_VER == _MSC_FULL_VER / 10000 +# define ASMJIT_CC_MSC (_MSC_VER * 100000 + (_MSC_FULL_VER % 10000)) +# else +# define ASMJIT_CC_MSC (_MSC_VER * 100000 + (_MSC_FULL_VER % 100000)) +# endif +#else +# error "[asmjit] Unable to detect the C/C++ compiler." +#endif + +#if ASMJIT_CC_INTEL && (defined(__GNUC__) || defined(__clang__)) +# define ASMJIT_CC_INTEL_COMPAT_MODE 1 +# else +# define ASMJIT_CC_INTEL_COMPAT_MODE 0 +#endif + +#define ASMJIT_CC_CODEGEAR_EQ(x, y) (ASMJIT_CC_CODEGEAR == (((x) << 8) + (y))) +#define ASMJIT_CC_CODEGEAR_GE(x, y) (ASMJIT_CC_CODEGEAR >= (((x) << 8) + (y))) + +#define ASMJIT_CC_CLANG_EQ(x, y, z) (ASMJIT_CC_CLANG == ((x) * 10000000 + (y) * 100000 + (z))) +#define ASMJIT_CC_CLANG_GE(x, y, z) (ASMJIT_CC_CLANG >= ((x) * 10000000 + (y) * 100000 + (z))) + +#define ASMJIT_CC_GCC_EQ(x, y, z) (ASMJIT_CC_GCC == ((x) * 10000000 + (y) * 100000 + (z))) +#define ASMJIT_CC_GCC_GE(x, y, z) (ASMJIT_CC_GCC >= ((x) * 10000000 + (y) * 100000 + (z))) + +#define ASMJIT_CC_INTEL_EQ(x, y) (ASMJIT_CC_INTEL == (((x) * 100) + (y))) +#define ASMJIT_CC_INTEL_GE(x, y) (ASMJIT_CC_INTEL >= (((x) * 100) + (y))) + +#define ASMJIT_CC_MSC_EQ(x, y, z) (ASMJIT_CC_MSC == ((x) * 10000000 + (y) * 100000 + (z))) +#define ASMJIT_CC_MSC_GE(x, y, z) (ASMJIT_CC_MSC >= ((x) * 10000000 + (y) * 100000 + (z))) + +#if defined(__MINGW64__) +# define ASMJIT_CC_MINGW 64 +#elif defined(__MINGW32__) +# define ASMJIT_CC_MINGW 32 +#else +# define ASMJIT_CC_MINGW 0 +#endif + +#if defined(__cplusplus) +# if __cplusplus >= 201103L +# define ASMJIT_CC_CXX_VERSION __cplusplus +# elif defined(__GXX_EXPERIMENTAL_CXX0X__) || ASMJIT_CC_MSC_GE(18, 0, 0) || ASMJIT_CC_INTEL_GE(14, 0) +# define ASMJIT_CC_CXX_VERSION 201103L +# else +# define ASMJIT_CC_CXX_VERSION 199711L +# endif +#endif + +#if !defined(ASMJIT_CC_CXX_VERSION) +# define ASMJIT_CC_CXX_VERSION 0 +#endif +// [@CC}@] + +// [@CC_FEATURES{@] +#if ASMJIT_CC_CLANG +# define ASMJIT_CC_HAS_ATTRIBUTE (1) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED (__has_attribute(__aligned__)) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE (__has_attribute(__always_inline__)) +# define ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE (__has_attribute(__noinline__)) +# define ASMJIT_CC_HAS_ATTRIBUTE_NORETURN (__has_attribute(__noreturn__)) +# define ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE (__has_attribute(__optimize__)) +# define ASMJIT_CC_HAS_BUILTIN_ASSUME (__has_builtin(__builtin_assume)) +# define ASMJIT_CC_HAS_BUILTIN_ASSUME_ALIGNED (__has_builtin(__builtin_assume_aligned)) +# define ASMJIT_CC_HAS_BUILTIN_EXPECT (__has_builtin(__builtin_expect)) +# define ASMJIT_CC_HAS_BUILTIN_UNREACHABLE (__has_builtin(__builtin_unreachable)) +# define ASMJIT_CC_HAS_ALIGNAS (__has_extension(__cxx_alignas__)) +# define ASMJIT_CC_HAS_ALIGNOF (__has_extension(__cxx_alignof__)) +# define ASMJIT_CC_HAS_CONSTEXPR (__has_extension(__cxx_constexpr__)) +# define ASMJIT_CC_HAS_DECLTYPE (__has_extension(__cxx_decltype__)) +# define ASMJIT_CC_HAS_DEFAULT_FUNCTION (__has_extension(__cxx_defaulted_functions__)) +# define ASMJIT_CC_HAS_DELETE_FUNCTION (__has_extension(__cxx_deleted_functions__)) +# define ASMJIT_CC_HAS_FINAL (__has_extension(__cxx_override_control__)) +# define ASMJIT_CC_HAS_INITIALIZER_LIST (__has_extension(__cxx_generalized_initializers__)) +# define ASMJIT_CC_HAS_LAMBDA (__has_extension(__cxx_lambdas__)) +# define ASMJIT_CC_HAS_NATIVE_CHAR (1) +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) +# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (__has_extension(__cxx_unicode_literals__)) +# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (__has_extension(__cxx_unicode_literals__)) +# define ASMJIT_CC_HAS_NOEXCEPT (__has_extension(__cxx_noexcept__)) +# define ASMJIT_CC_HAS_NULLPTR (__has_extension(__cxx_nullptr__)) +# define ASMJIT_CC_HAS_OVERRIDE (__has_extension(__cxx_override_control__)) +# define ASMJIT_CC_HAS_RVALUE (__has_extension(__cxx_rvalue_references__)) +# define ASMJIT_CC_HAS_STATIC_ASSERT (__has_extension(__cxx_static_assert__)) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (__has_extension(__cxx_variadic_templates__)) +#endif + +#if ASMJIT_CC_CODEGEAR +# define ASMJIT_CC_HAS_DECLSPEC_ALIGN (ASMJIT_CC_CODEGEAR >= 0x0610) +# define ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE (0) +# define ASMJIT_CC_HAS_DECLSPEC_NOINLINE (0) +# define ASMJIT_CC_HAS_DECLSPEC_NORETURN (ASMJIT_CC_CODEGEAR >= 0x0610) +# define ASMJIT_CC_HAS_ALIGNAS (0) +# define ASMJIT_CC_HAS_ALIGNOF (0) +# define ASMJIT_CC_HAS_CONSTEXPR (0) +# define ASMJIT_CC_HAS_DECLTYPE (ASMJIT_CC_CODEGEAR >= 0x0610) +# define ASMJIT_CC_HAS_DEFAULT_FUNCTION (0) +# define ASMJIT_CC_HAS_DELETE_FUNCTION (0) +# define ASMJIT_CC_HAS_FINAL (0) +# define ASMJIT_CC_HAS_INITIALIZER_LIST (0) +# define ASMJIT_CC_HAS_LAMBDA (0) +# define ASMJIT_CC_HAS_NATIVE_CHAR (1) +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) +# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (0) +# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (0) +# define ASMJIT_CC_HAS_NOEXCEPT (0) +# define ASMJIT_CC_HAS_NULLPTR (0) +# define ASMJIT_CC_HAS_OVERRIDE (0) +# define ASMJIT_CC_HAS_RVALUE (ASMJIT_CC_CODEGEAR >= 0x0610) +# define ASMJIT_CC_HAS_STATIC_ASSERT (ASMJIT_CC_CODEGEAR >= 0x0610) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (0) +#endif + +#if ASMJIT_CC_GCC +# define ASMJIT_CC_HAS_ATTRIBUTE (1) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED (ASMJIT_CC_GCC_GE(2, 7, 0)) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE (ASMJIT_CC_GCC_GE(4, 4, 0) && !ASMJIT_CC_MINGW) +# define ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE (ASMJIT_CC_GCC_GE(3, 4, 0) && !ASMJIT_CC_MINGW) +# define ASMJIT_CC_HAS_ATTRIBUTE_NORETURN (ASMJIT_CC_GCC_GE(2, 5, 0)) +# define ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE (ASMJIT_CC_GCC_GE(4, 4, 0)) +# define ASMJIT_CC_HAS_BUILTIN_ASSUME (0) +# define ASMJIT_CC_HAS_BUILTIN_ASSUME_ALIGNED (ASMJIT_CC_GCC_GE(4, 7, 0)) +# define ASMJIT_CC_HAS_BUILTIN_EXPECT (1) +# define ASMJIT_CC_HAS_BUILTIN_UNREACHABLE (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_ALIGNAS (ASMJIT_CC_GCC_GE(4, 8, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_ALIGNOF (ASMJIT_CC_GCC_GE(4, 8, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_CONSTEXPR (ASMJIT_CC_GCC_GE(4, 6, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_DECLTYPE (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_DEFAULT_FUNCTION (ASMJIT_CC_GCC_GE(4, 4, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_DELETE_FUNCTION (ASMJIT_CC_GCC_GE(4, 4, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_FINAL (ASMJIT_CC_GCC_GE(4, 7, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_INITIALIZER_LIST (ASMJIT_CC_GCC_GE(4, 4, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_LAMBDA (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_NATIVE_CHAR (1) +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) +# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_NOEXCEPT (ASMJIT_CC_GCC_GE(4, 6, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_NULLPTR (ASMJIT_CC_GCC_GE(4, 6, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_OVERRIDE (ASMJIT_CC_GCC_GE(4, 7, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_RVALUE (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_STATIC_ASSERT (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +#endif + +#if ASMJIT_CC_INTEL +# define ASMJIT_CC_HAS_ATTRIBUTE (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_NORETURN (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_BUILTIN_EXPECT (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_DECLSPEC_ALIGN (ASMJIT_CC_INTEL_COMPAT_MODE == 0) +# define ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE (ASMJIT_CC_INTEL_COMPAT_MODE == 0) +# define ASMJIT_CC_HAS_DECLSPEC_NOINLINE (ASMJIT_CC_INTEL_COMPAT_MODE == 0) +# define ASMJIT_CC_HAS_DECLSPEC_NORETURN (ASMJIT_CC_INTEL_COMPAT_MODE == 0) +# define ASMJIT_CC_HAS_ASSUME (1) +# define ASMJIT_CC_HAS_ASSUME_ALIGNED (1) +# define ASMJIT_CC_HAS_ALIGNAS (ASMJIT_CC_INTEL >= 1500) +# define ASMJIT_CC_HAS_ALIGNOF (ASMJIT_CC_INTEL >= 1500) +# define ASMJIT_CC_HAS_CONSTEXPR (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_DECLTYPE (ASMJIT_CC_INTEL >= 1200) +# define ASMJIT_CC_HAS_DEFAULT_FUNCTION (ASMJIT_CC_INTEL >= 1200) +# define ASMJIT_CC_HAS_DELETE_FUNCTION (ASMJIT_CC_INTEL >= 1200) +# define ASMJIT_CC_HAS_FINAL (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_INITIALIZER_LIST (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_LAMBDA (ASMJIT_CC_INTEL >= 1200) +# define ASMJIT_CC_HAS_NATIVE_CHAR (1) +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) +# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (ASMJIT_CC_INTEL >= 1400 || (ASMJIT_CC_INTEL_COMPAT_MODE > 0 && ASMJIT_CC_INTEL >= 1206)) +# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (ASMJIT_CC_INTEL >= 1400 || (ASMJIT_CC_INTEL_COMPAT_MODE > 0 && ASMJIT_CC_INTEL >= 1206)) +# define ASMJIT_CC_HAS_NOEXCEPT (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_NULLPTR (ASMJIT_CC_INTEL >= 1206) +# define ASMJIT_CC_HAS_OVERRIDE (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_RVALUE (ASMJIT_CC_INTEL >= 1110) +# define ASMJIT_CC_HAS_STATIC_ASSERT (ASMJIT_CC_INTEL >= 1110) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (ASMJIT_CC_INTEL >= 1206) +#endif + +#if ASMJIT_CC_MSC +# define ASMJIT_CC_HAS_DECLSPEC_ALIGN (1) +# define ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE (1) +# define ASMJIT_CC_HAS_DECLSPEC_NOINLINE (1) +# define ASMJIT_CC_HAS_DECLSPEC_NORETURN (1) +# define ASMJIT_CC_HAS_ASSUME (1) +# define ASMJIT_CC_HAS_ASSUME_ALIGNED (0) +# define ASMJIT_CC_HAS_ALIGNAS (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_ALIGNOF (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_CONSTEXPR (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_DECLTYPE (ASMJIT_CC_MSC_GE(16, 0, 0)) +# define ASMJIT_CC_HAS_DEFAULT_FUNCTION (ASMJIT_CC_MSC_GE(18, 0, 0)) +# define ASMJIT_CC_HAS_DELETE_FUNCTION (ASMJIT_CC_MSC_GE(18, 0, 0)) +# define ASMJIT_CC_HAS_FINAL (ASMJIT_CC_MSC_GE(14, 0, 0)) +# define ASMJIT_CC_HAS_INITIALIZER_LIST (ASMJIT_CC_MSC_GE(18, 0, 0)) +# define ASMJIT_CC_HAS_LAMBDA (ASMJIT_CC_MSC_GE(16, 0, 0)) +# define ASMJIT_CC_HAS_NATIVE_CHAR (1) +# if defined(_NATIVE_WCHAR_T_DEFINED) +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) +# else +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (0) +# endif +# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_NOEXCEPT (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_NULLPTR (ASMJIT_CC_MSC_GE(16, 0, 0)) +# define ASMJIT_CC_HAS_OVERRIDE (ASMJIT_CC_MSC_GE(14, 0, 0)) +# define ASMJIT_CC_HAS_RVALUE (ASMJIT_CC_MSC_GE(16, 0, 0)) +# define ASMJIT_CC_HAS_STATIC_ASSERT (ASMJIT_CC_MSC_GE(16, 0, 0)) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (ASMJIT_CC_MSC_GE(18, 0, 0)) +#endif + +// Fixup some vendor specific keywords. +#if !defined(ASMJIT_CC_HAS_ASSUME) +# define ASMJIT_CC_HAS_ASSUME (0) +#endif +#if !defined(ASMJIT_CC_HAS_ASSUME_ALIGNED) +# define ASMJIT_CC_HAS_ASSUME_ALIGNED (0) +#endif + +// Fixup compilers that don't support '__attribute__'. +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE) +# define ASMJIT_CC_HAS_ATTRIBUTE (0) +#endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED (0) +#endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE (0) +#endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE) +# define ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE (0) +#endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_NORETURN) +# define ASMJIT_CC_HAS_ATTRIBUTE_NORETURN (0) +#endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE) +# define ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE (0) +#endif + +// Fixup compilers that don't support '__builtin?'. +#if !defined(ASMJIT_CC_HAS_BUILTIN_ASSUME) +# define ASMJIT_CC_HAS_BUILTIN_ASSUME (0) +#endif +#if !defined(ASMJIT_CC_HAS_BUILTIN_ASSUME_ALIGNED) +# define ASMJIT_CC_HAS_BUILTIN_ASSUME_ALIGNED (0) +#endif +#if !defined(ASMJIT_CC_HAS_BUILTIN_EXPECT) +# define ASMJIT_CC_HAS_BUILTIN_EXPECT (0) +#endif +#if !defined(ASMJIT_CC_HAS_BUILTIN_UNREACHABLE) +# define ASMJIT_CC_HAS_BUILTIN_UNREACHABLE (0) +#endif + +// Fixup compilers that don't support 'declspec'. +#if !defined(ASMJIT_CC_HAS_DECLSPEC_ALIGN) +# define ASMJIT_CC_HAS_DECLSPEC_ALIGN (0) +#endif +#if !defined(ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE) +# define ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE (0) +#endif +#if !defined(ASMJIT_CC_HAS_DECLSPEC_NOINLINE) +# define ASMJIT_CC_HAS_DECLSPEC_NOINLINE (0) +#endif +#if !defined(ASMJIT_CC_HAS_DECLSPEC_NORETURN) +# define ASMJIT_CC_HAS_DECLSPEC_NORETURN (0) +#endif +// [@CC_FEATURES}@] + +// [@CC_API{@] +// \def ASMJIT_API +// The decorated function is asmjit API and should be exported. +#if !defined(ASMJIT_API) +# if defined(ASMJIT_STATIC) +# define ASMJIT_API +# elif ASMJIT_OS_WINDOWS +# if (ASMJIT_CC_GCC || ASMJIT_CC_CLANG) && !ASMJIT_CC_MINGW +# if defined(ASMJIT_EXPORTS) +# define ASMJIT_API __attribute__((__dllexport__)) +# else +# define ASMJIT_API __attribute__((__dllimport__)) +# endif +# else +# if defined(ASMJIT_EXPORTS) +# define ASMJIT_API __declspec(dllexport) +# else +# define ASMJIT_API __declspec(dllimport) +# endif +# endif +# else +# if ASMJIT_CC_CLANG || ASMJIT_CC_GCC_GE(4, 0, 0) +# define ASMJIT_API __attribute__((__visibility__("default"))) +# endif +# endif +#endif +// [@CC_API}@] + +// [@CC_VARAPI{@] +// \def ASMJIT_VARAPI +// The decorated variable is part of asmjit API and is exported. +#if !defined(ASMJIT_VARAPI) +# define ASMJIT_VARAPI extern ASMJIT_API +#endif +// [@CC_VARAPI}@] + +// [@CC_VIRTAPI{@] +// \def ASMJIT_VIRTAPI +// The decorated class has a virtual table and is part of asmjit API. +// +// This is basically a workaround. When using MSVC and marking class as DLL +// export everything gets exported, which is unwanted in most projects. MSVC +// automatically exports typeinfo and vtable if at least one symbol of the +// class is exported. However, GCC has some strange behavior that even if +// one or more symbol is exported it doesn't export typeinfo unless the +// class itself is decorated with "visibility(default)" (i.e. asmjit_API). +#if (ASMJIT_CC_GCC || ASMJIT_CC_CLANG) && !ASMJIT_OS_WINDOWS +# define ASMJIT_VIRTAPI ASMJIT_API +#else +# define ASMJIT_VIRTAPI +#endif +// [@CC_VIRTAPI}@] + +// [@CC_INLINE{@] +// \def ASMJIT_INLINE +// Always inline the decorated function. +#if ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE +# define ASMJIT_INLINE inline __attribute__((__always_inline__)) +#elif ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE +# define ASMJIT_INLINE __forceinline +#else +# define ASMJIT_INLINE inline +#endif +// [@CC_INLINE}@] + +// [@CC_NOINLINE{@] +// \def ASMJIT_NOINLINE +// Never inline the decorated function. +#if ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE +# define ASMJIT_NOINLINE __attribute__((__noinline__)) +#elif ASMJIT_CC_HAS_DECLSPEC_NOINLINE +# define ASMJIT_NOINLINE __declspec(noinline) +#else +# define ASMJIT_NOINLINE +#endif +// [@CC_NOINLINE}@] + +// [@CC_NORETURN{@] +// \def ASMJIT_NORETURN +// The decorated function never returns (exit, assertion failure, etc...). +#if ASMJIT_CC_HAS_ATTRIBUTE_NORETURN +# define ASMJIT_NORETURN __attribute__((__noreturn__)) +#elif ASMJIT_CC_HAS_DECLSPEC_NORETURN +# define ASMJIT_NORETURN __declspec(noreturn) +#else +# define ASMJIT_NORETURN +#endif +// [@CC_NORETURN}@] + +// [@CC_CDECL{@] +// \def ASMJIT_CDECL +// Standard C function calling convention decorator (__cdecl). +#if ASMJIT_ARCH_X86 +# if ASMJIT_CC_HAS_ATTRIBUTE +# define ASMJIT_CDECL __attribute__((__cdecl__)) +# else +# define ASMJIT_CDECL __cdecl +# endif +#else +# define ASMJIT_CDECL +#endif +// [@CC_CDECL}@] + +// [@CC_STDCALL{@] +// \def ASMJIT_STDCALL +// StdCall function calling convention decorator (__stdcall). +#if ASMJIT_ARCH_X86 +# if ASMJIT_CC_HAS_ATTRIBUTE +# define ASMJIT_STDCALL __attribute__((__stdcall__)) +# else +# define ASMJIT_STDCALL __stdcall +# endif +#else +# define ASMJIT_STDCALL +#endif +// [@CC_STDCALL}@] + +// [@CC_FASTCALL{@] +// \def ASMJIT_FASTCALL +// FastCall function calling convention decorator (__fastcall). +#if ASMJIT_ARCH_X86 +# if ASMJIT_CC_HAS_ATTRIBUTE +# define ASMJIT_FASTCALL __attribute__((__fastcall__)) +# else +# define ASMJIT_FASTCALL __fastcall +# endif +#else +# define ASMJIT_FASTCALL +#endif +// [@CC_FASTCALL}@] + +// [@CC_REGPARM{@] +// \def ASMJIT_REGPARM(n) +// A custom calling convention which passes n arguments in registers. +#if ASMJIT_ARCH_X86 && ASMJIT_CC_HAS_ATTRIBUTE +# define ASMJIT_REGPARM(n) __attribute__((__regparm__(n))) +#else +# define ASMJIT_REGPARM(n) +#endif +// [@CC_REGPARM}@] + +// [@CC_NOEXCEPT{@] +// \def ASMJIT_NOEXCEPT +// The decorated function never throws an exception (noexcept). +#if ASMJIT_CC_HAS_NOEXCEPT +# define ASMJIT_NOEXCEPT noexcept +#else +# define ASMJIT_NOEXCEPT +#endif +// [@CC_NOEXCEPT}@] + +// [@CC_NOP{@] +// \def ASMJIT_NOP +// No operation. +#if !defined(ASMJIT_NOP) +# define ASMJIT_NOP ((void)0) +#endif +// [@CC_NOP}@] + +// [@CC_ASSUME{@] +// \def ASMJIT_ASSUME(exp) +// Assume that the expression exp is always true. +#if ASMJIT_CC_HAS_ASSUME +# define ASMJIT_ASSUME(exp) __assume(exp) +#elif ASMJIT_CC_HAS_BUILTIN_ASSUME +# define ASMJIT_ASSUME(exp) __builtin_assume(exp) +#elif ASMJIT_CC_HAS_BUILTIN_UNREACHABLE +# define ASMJIT_ASSUME(exp) do { if (!(exp)) __builtin_unreachable(); } while (0) +#else +# define ASMJIT_ASSUME(exp) ((void)0) +#endif +// [@CC_ASSUME}@] + +// [@CC_ASSUME_ALIGNED{@] +// \def ASMJIT_ASSUME_ALIGNED(p, alignment) +// Assume that the pointer 'p' is aligned to at least 'alignment' bytes. +#if ASMJIT_CC_HAS_ASSUME_ALIGNED +# define ASMJIT_ASSUME_ALIGNED(p, alignment) __assume_aligned(p, alignment) +#elif ASMJIT_CC_HAS_BUILTIN_ASSUME_ALIGNED +# define ASMJIT_ASSUME_ALIGNED(p, alignment) p = __builtin_assume_aligned(p, alignment) +#else +# define ASMJIT_ASSUME_ALIGNED(p, alignment) ((void)0) +#endif +// [@CC_ASSUME_ALIGNED}@] + +// [@CC_EXPECT{@] +// \def ASMJIT_LIKELY(exp) +// Expression exp is likely to be true. +// +// \def ASMJIT_UNLIKELY(exp) +// Expression exp is likely to be false. +#if ASMJIT_CC_HAS_BUILTIN_EXPECT +# define ASMJIT_LIKELY(exp) __builtin_expect(!!(exp), 1) +# define ASMJIT_UNLIKELY(exp) __builtin_expect(!!(exp), 0) +#else +# define ASMJIT_LIKELY(exp) exp +# define ASMJIT_UNLIKELY(exp) exp +#endif +// [@CC_EXPECT}@] + +// [@CC_FALLTHROUGH{@] +// \def ASMJIT_FALLTHROUGH +// The code falls through annotation (switch / case). +#if ASMJIT_CC_CLANG && __cplusplus >= 201103L +# define ASMJIT_FALLTHROUGH [[clang::fallthrough]] +#else +# define ASMJIT_FALLTHROUGH (void)0 +#endif +// [@CC_FALLTHROUGH}@] + +// [@CC_UNUSED{@] +// \def ASMJIT_UNUSED(x) +// Mark a variable x as unused. +#define ASMJIT_UNUSED(x) (void)(x) +// [@CC_UNUSED}@] + +// [@CC_OFFSET_OF{@] +// \def ASMJIT_OFFSET_OF(x, y). +// Get the offset of a member y of a struct x at compile-time. +#define ASMJIT_OFFSET_OF(x, y) ((int)(intptr_t)((const char*)&((const x*)0x1)->y) - 1) +// [@CC_OFFSET_OF}@] + +// [@CC_ARRAY_SIZE{@] +// \def ASMJIT_ARRAY_SIZE(x) +// Get the array size of x at compile-time. +#define ASMJIT_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +// [@CC_ARRAY_SIZE}@] + +// ============================================================================ +// [asmjit::Build - STDTYPES] +// ============================================================================ + +// [@STDTYPES{@] +#if defined(__MINGW32__) || defined(__MINGW64__) +# include +#endif +#if defined(_MSC_VER) && (_MSC_VER < 1600) +# include +# if !defined(ASMJIT_SUPPRESS_STD_TYPES) +# if (_MSC_VER < 1300) +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed __int64 int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned __int64 uint64_t; +# else +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +# endif +# endif +# define ASMJIT_INT64_C(x) (x##i64) +# define ASMJIT_UINT64_C(x) (x##ui64) +#else +# include +# include +# define ASMJIT_INT64_C(x) (x##ll) +# define ASMJIT_UINT64_C(x) (x##ull) +#endif +// [@STDTYPES}@] + +// ============================================================================ +// [asmjit::Build - Dependencies] +// ============================================================================ + +#include +#include +#include +#include +#include + +#if ASMJIT_OS_POSIX +# include +#endif // ASMJIT_OS_POSIX + +// ============================================================================ +// [asmjit::Build - Additional] +// ============================================================================ + +// Build host architecture if no architecture is selected. +#if !defined(ASMJIT_BUILD_HOST) && \ + !defined(ASMJIT_BUILD_X86) && \ + !defined(ASMJIT_BUILD_ARM) +# define ASMJIT_BUILD_HOST +#endif + +// Detect host architecture if building only for host. +#if defined(ASMJIT_BUILD_HOST) +# if (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) && !defined(ASMJIT_BUILD_X86) +# define ASMJIT_BUILD_X86 +# endif // ASMJIT_ARCH_X86 +#endif // ASMJIT_BUILD_HOST + +#if defined(_MSC_VER) && _MSC_VER >= 1400 +# define ASMJIT_ENUM(NAME) enum NAME : uint32_t +#else +# define ASMJIT_ENUM(NAME) enum NAME +#endif + +#if ASMJIT_ARCH_LE +# define ASMJIT_PACK32_4x8(A, B, C, D) ((A) + ((B) << 8) + ((C) << 16) + ((D) << 24)) +#else +# define ASMJIT_PACK32_4x8(A, B, C, D) ((D) + ((C) << 8) + ((B) << 16) + ((A) << 24)) +#endif + +#if !defined(ASMJIT_ALLOC) && !defined(ASMJIT_REALLOC) && !defined(ASMJIT_FREE) +# define ASMJIT_ALLOC(SIZE) ::malloc(SIZE) +# define ASMJIT_REALLOC(PTR, SIZE) ::realloc(PTR, SIZE) +# define ASMJIT_FREE(PTR) ::free(PTR) +#else +# if !defined(ASMJIT_ALLOC) || !defined(ASMJIT_REALLOC) || !defined(ASMJIT_FREE) +# error "[asmjit] You must provide ASMJIT_ALLOC, ASMJIT_REALLOC and ASMJIT_FREE." +# endif +#endif // !ASMJIT_ALLOC && !ASMJIT_REALLOC && !ASMJIT_FREE + +#if ASMJIT_CC_HAS_DELETE_FUNCTION +#define ASMJIT_NONCONSTRUCTIBLE(...) \ +private: \ + __VA_ARGS__() = delete; \ + __VA_ARGS__(const __VA_ARGS__& other) = delete; \ + __VA_ARGS__& operator=(const __VA_ARGS__& other) = delete; \ +public: +#define ASMJIT_NONCOPYABLE(...) \ +private: \ + __VA_ARGS__(const __VA_ARGS__& other) = delete; \ + __VA_ARGS__& operator=(const __VA_ARGS__& other) = delete; \ +public: +#else +#define ASMJIT_NONCONSTRUCTIBLE(...) \ +private: \ + inline __VA_ARGS__(); \ + inline __VA_ARGS__(const __VA_ARGS__& other); \ + inline __VA_ARGS__& operator=(const __VA_ARGS__& other); \ +public: +#define ASMJIT_NONCOPYABLE(...) \ +private: \ + inline __VA_ARGS__(const __VA_ARGS__& other); \ + inline __VA_ARGS__& operator=(const __VA_ARGS__& other); \ +public: +#endif // ASMJIT_CC_HAS_DELETE_FUNCTION + +// Internal macros that are only used when building AsmJit itself. +#if defined(ASMJIT_EXPORTS) +# if !defined(ASMJIT_DEBUG) && ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE +# define ASMJIT_FAVOR_SIZE __attribute__((__optimize__("Os"))) +# else +# define ASMJIT_FAVOR_SIZE +# endif +#endif // ASMJIT_EXPORTS + +// ============================================================================ +// [asmjit::Build - Relative Path] +// ============================================================================ + +namespace asmjit { +namespace DebugUtils { + +// ASMJIT_TRACE is only used by sources and private headers. It's safe to make +// it unavailable outside of AsmJit. +#if defined(ASMJIT_EXPORTS) +static inline int disabledTrace(...) { return 0; } +# if defined(ASMJIT_TRACE) +# define ASMJIT_TSEC(section) section +# define ASMJIT_TLOG ::printf +# else +# define ASMJIT_TSEC(section) ASMJIT_NOP +# define ASMJIT_TLOG 0 && ::asmjit::DebugUtils::disabledTrace +# endif // ASMJIT_TRACE +#endif // ASMJIT_EXPORTS + +} // DebugUtils namespace +} // asmjit namespace + +// ============================================================================ +// [asmjit::Build - Test] +// ============================================================================ + +// Include a unit testing package if this is a `asmjit_test` build. +#if defined(ASMJIT_TEST) +# include "../../test/broken.h" +#endif // ASMJIT_TEST + +// [Guard] +#endif // _ASMJIT_BUILD_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base.h new file mode 100644 index 00000000..efc43c63 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base.h @@ -0,0 +1,35 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_H +#define _ASMJIT_BASE_H + +// [Dependencies] +#include "./base/arch.h" +#include "./base/assembler.h" +#include "./base/codebuilder.h" +#include "./base/codecompiler.h" +#include "./base/codeemitter.h" +#include "./base/codeholder.h" +#include "./base/constpool.h" +#include "./base/cpuinfo.h" +#include "./base/func.h" +#include "./base/globals.h" +#include "./base/logging.h" +#include "./base/operand.h" +#include "./base/osutils.h" +#include "./base/runtime.h" +#include "./base/simdtypes.h" +#include "./base/string.h" +#include "./base/utils.h" +#include "./base/vmem.h" +#include "./base/zone.h" +#include "./base/zonecontainers.h" +#include "./base/zoneheap.h" + +// [Guard] +#endif // _ASMJIT_BASE_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/arch.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/arch.cpp new file mode 100644 index 00000000..53189e37 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/arch.cpp @@ -0,0 +1,161 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/arch.h" + +#if defined(ASMJIT_BUILD_X86) +#include "../x86/x86operand.h" +#endif // ASMJIT_BUILD_X86 + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::ArchInfo] +// ============================================================================ + +static const uint32_t archInfoTable[] = { + // <-------------+---------------------+-----------------------+-------+ + // | Type | SubType | GPInfo| + // <-------------+---------------------+-----------------------+-------+ + ASMJIT_PACK32_4x8(ArchInfo::kTypeNone , ArchInfo::kSubTypeNone, 0, 0), + ASMJIT_PACK32_4x8(ArchInfo::kTypeX86 , ArchInfo::kSubTypeNone, 4, 8), + ASMJIT_PACK32_4x8(ArchInfo::kTypeX64 , ArchInfo::kSubTypeNone, 8, 16), + ASMJIT_PACK32_4x8(ArchInfo::kTypeX32 , ArchInfo::kSubTypeNone, 8, 16), + ASMJIT_PACK32_4x8(ArchInfo::kTypeArm32 , ArchInfo::kSubTypeNone, 4, 16), + ASMJIT_PACK32_4x8(ArchInfo::kTypeArm64 , ArchInfo::kSubTypeNone, 8, 32) +}; + +ASMJIT_FAVOR_SIZE void ArchInfo::init(uint32_t type, uint32_t subType) noexcept { + uint32_t index = type < ASMJIT_ARRAY_SIZE(archInfoTable) ? type : uint32_t(0); + + // Make sure the `archInfoTable` array is correctly indexed. + _signature = archInfoTable[index]; + ASMJIT_ASSERT(_type == index); + + // Even if the architecture is not known we setup its type and sub-type, + // however, such architecture is not really useful. + _type = type; + _subType = subType; +} + +// ============================================================================ +// [asmjit::ArchUtils] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error ArchUtils::typeIdToRegInfo(uint32_t archType, uint32_t& typeIdInOut, RegInfo& regInfo) noexcept { + uint32_t typeId = typeIdInOut; + + // Zero the signature so it's clear in case that typeId is not invalid. + regInfo.signature = 0; + +#if defined(ASMJIT_BUILD_X86) + if (ArchInfo::isX86Family(archType)) { + // Passed RegType instead of TypeId? + if (typeId <= Reg::kRegMax) + typeId = x86OpData.archRegs.regTypeToTypeId[typeId]; + + if (ASMJIT_UNLIKELY(!TypeId::isValid(typeId))) + return DebugUtils::errored(kErrorInvalidTypeId); + + // First normalize architecture dependent types. + if (TypeId::isAbstract(typeId)) { + if (typeId == TypeId::kIntPtr) + typeId = (archType == ArchInfo::kTypeX86) ? TypeId::kI32 : TypeId::kI64; + else + typeId = (archType == ArchInfo::kTypeX86) ? TypeId::kU32 : TypeId::kU64; + } + + // Type size helps to construct all kinds of registers. If the size is zero + // then the TypeId is invalid. + uint32_t size = TypeId::sizeOf(typeId); + if (ASMJIT_UNLIKELY(!size)) + return DebugUtils::errored(kErrorInvalidTypeId); + + if (ASMJIT_UNLIKELY(typeId == TypeId::kF80)) + return DebugUtils::errored(kErrorInvalidUseOfF80); + + uint32_t regType = 0; + + switch (typeId) { + case TypeId::kI8: + case TypeId::kU8: + regType = X86Reg::kRegGpbLo; + break; + + case TypeId::kI16: + case TypeId::kU16: + regType = X86Reg::kRegGpw; + break; + + case TypeId::kI32: + case TypeId::kU32: + regType = X86Reg::kRegGpd; + break; + + case TypeId::kI64: + case TypeId::kU64: + if (archType == ArchInfo::kTypeX86) + return DebugUtils::errored(kErrorInvalidUseOfGpq); + + regType = X86Reg::kRegGpq; + break; + + // F32 and F64 are always promoted to use vector registers. + case TypeId::kF32: + typeId = TypeId::kF32x1; + regType = X86Reg::kRegXmm; + break; + + case TypeId::kF64: + typeId = TypeId::kF64x1; + regType = X86Reg::kRegXmm; + break; + + // Mask registers {k}. + case TypeId::kMask8: + case TypeId::kMask16: + case TypeId::kMask32: + case TypeId::kMask64: + regType = X86Reg::kRegK; + break; + + // MMX registers. + case TypeId::kMmx32: + case TypeId::kMmx64: + regType = X86Reg::kRegMm; + break; + + // XMM|YMM|ZMM registers. + default: + if (size <= 16) + regType = X86Reg::kRegXmm; + else if (size == 32) + regType = X86Reg::kRegYmm; + else + regType = X86Reg::kRegZmm; + break; + } + + typeIdInOut = typeId; + regInfo.signature = x86OpData.archRegs.regInfo[regType].signature; + return kErrorOk; + } +#endif // ASMJIT_BUILD_X86 + + return DebugUtils::errored(kErrorInvalidArch); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/arch.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/arch.h new file mode 100644 index 00000000..9a431a80 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/arch.h @@ -0,0 +1,175 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_ARCH_H +#define _ASMJIT_BASE_ARCH_H + +// [Dependencies] +#include "../base/globals.h" +#include "../base/operand.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::ArchInfo] +// ============================================================================ + +class ArchInfo { +public: + //! Architecture type. + ASMJIT_ENUM(Type) { + kTypeNone = 0, //!< No/Unknown architecture. + kTypeX86 = 1, //!< X86 architecture (32-bit). + kTypeX64 = 2, //!< X64 architecture (64-bit) (AMD64). + kTypeX32 = 3, //!< X32 architecture (DEAD-END). + kTypeArm32 = 4, //!< ARM32 architecture (32-bit). + kTypeArm64 = 5, //!< ARM64 architecture (64-bit). + + //! Architecture detected at compile-time (architecture of the host). + kTypeHost = ASMJIT_ARCH_X86 ? kTypeX86 : + ASMJIT_ARCH_X64 ? kTypeX64 : + ASMJIT_ARCH_ARM32 ? kTypeArm32 : + ASMJIT_ARCH_ARM64 ? kTypeArm64 : kTypeNone + }; + + //! Architecture sub-type. + ASMJIT_ENUM(SubType) { + kSubTypeNone = 0, //!< Default mode (or no specific mode). + + kX86SubTypeLegacy = 0, //!< Legacy (the most compatible) mode. + kX86SubTypeAVX = 1, //!< AVX mode. + kX86SubTypeAVX512F = 2 //!< AVX512F mode. + }; + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE bool isX86Family(uint32_t archType) noexcept { return archType >= kTypeX86 && archType <= kTypeX32; } + static ASMJIT_INLINE bool isArmFamily(uint32_t archType) noexcept { return archType >= kTypeArm32 && archType <= kTypeArm64; } + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ArchInfo() noexcept : _signature(0) {} + ASMJIT_INLINE ArchInfo(const ArchInfo& other) noexcept : _signature(other._signature) {} + + explicit ASMJIT_INLINE ArchInfo(uint32_t type, uint32_t subType = kSubTypeNone) noexcept { + init(type, subType); + } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isInitialized() const noexcept { return _type != kTypeNone; } + + ASMJIT_API void init(uint32_t type, uint32_t subType = kSubTypeNone) noexcept; + ASMJIT_INLINE void reset() noexcept { _signature = 0; } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get if the architecture is 32-bit. + ASMJIT_INLINE bool is32Bit() const noexcept { return _gpSize == 4; } + //! Get if the architecture is 64-bit. + ASMJIT_INLINE bool is64Bit() const noexcept { return _gpSize == 8; } + + //! Get architecture type, see \ref Type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + + //! Get architecture sub-type, see \ref SubType. + //! + //! X86 & X64 + //! --------- + //! + //! Architecture subtype describe the highest instruction-set level that can + //! be used. + //! + //! ARM32 + //! ----- + //! + //! Architecture mode means the instruction encoding to be used when generating + //! machine code, thus mode can be used to force generation of THUMB and THUMB2 + //! encoding of regular ARM encoding. + //! + //! ARM64 + //! ----- + //! + //! No meaning yet. + ASMJIT_INLINE uint32_t getSubType() const noexcept { return _subType; } + + //! Get if the architecture is X86, X64, or X32. + ASMJIT_INLINE bool isX86Family() const noexcept { return isX86Family(_type); } + //! Get if the architecture is ARM32 or ARM64. + ASMJIT_INLINE bool isArmFamily() const noexcept { return isArmFamily(_type); } + + //! Get a size of a general-purpose register. + ASMJIT_INLINE uint32_t getGpSize() const noexcept { return _gpSize; } + //! Get number of general-purpose registers. + ASMJIT_INLINE uint32_t getGpCount() const noexcept { return _gpCount; } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE const ArchInfo& operator=(const ArchInfo& other) noexcept { _signature = other._signature; return *this; } + ASMJIT_INLINE bool operator==(const ArchInfo& other) const noexcept { return _signature == other._signature; } + ASMJIT_INLINE bool operator!=(const ArchInfo& other) const noexcept { return _signature != other._signature; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + union { + struct { + uint8_t _type; //!< Architecture type. + uint8_t _subType; //!< Architecture sub-type. + uint8_t _gpSize; //!< Default size of a general purpose register. + uint8_t _gpCount; //!< Count of all general purpose registers. + }; + uint32_t _signature; //!< Architecture signature (32-bit int). + }; +}; + +// ============================================================================ +// [asmjit::ArchRegs] +// ============================================================================ + +//! Information about all architecture registers. +struct ArchRegs { + //! Register information and signatures indexed by \ref Reg::Type. + RegInfo regInfo[Reg::kRegMax + 1]; + //! Converts RegType to TypeId, see \ref TypeId::Id. + uint8_t regTypeToTypeId[Reg::kRegMax + 1]; +}; + +// ============================================================================ +// [asmjit::ArchUtils] +// ============================================================================ + +struct ArchUtils { + static ASMJIT_API Error typeIdToRegInfo(uint32_t archType, uint32_t& typeIdInOut, RegInfo& regInfo) noexcept; +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_ARCH_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/assembler.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/assembler.cpp new file mode 100644 index 00000000..ffdd64e2 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/assembler.cpp @@ -0,0 +1,314 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/constpool.h" +#include "../base/utils.h" +#include "../base/vmem.h" +#include + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::Assembler - Construction / Destruction] +// ============================================================================ + +Assembler::Assembler() noexcept + : CodeEmitter(kTypeAssembler), + _section(nullptr), + _bufferData(nullptr), + _bufferEnd(nullptr), + _bufferPtr(nullptr) {} + +Assembler::~Assembler() noexcept { + if (_code) sync(); +} + +// ============================================================================ +// [asmjit::Assembler - Events] +// ============================================================================ + +Error Assembler::onAttach(CodeHolder* code) noexcept { + // Attach to the end of the .text section. + _section = code->_sections[0]; + uint8_t* p = _section->_buffer._data; + + _bufferData = p; + _bufferEnd = p + _section->_buffer._capacity; + _bufferPtr = p + _section->_buffer._length; + return Base::onAttach(code); +} + +Error Assembler::onDetach(CodeHolder* code) noexcept { + _section = nullptr; + _bufferData = nullptr; + _bufferEnd = nullptr; + _bufferPtr = nullptr; + return Base::onDetach(code); +} + +// ============================================================================ +// [asmjit::Assembler - Sync] +// ============================================================================ + +void Assembler::sync() noexcept { + ASMJIT_ASSERT(_code != nullptr); // Only called by CodeHolder, so we must be attached. + ASMJIT_ASSERT(_section != nullptr); // One section must always be active, no matter what. + ASMJIT_ASSERT(_bufferData == _section->_buffer._data); // `_bufferStart` is a shortcut to `_section->buffer.data`. + + // Update only if the current offset is greater than the section length. + size_t offset = (size_t)(_bufferPtr - _bufferData); + if (_section->getBuffer().getLength() < offset) + _section->_buffer._length = offset; +} + +// ============================================================================ +// [asmjit::Assembler - Code-Buffer] +// ============================================================================ + +Error Assembler::setOffset(size_t offset) { + if (_lastError) return _lastError; + + size_t length = Utils::iMax(_section->getBuffer().getLength(), getOffset()); + if (offset > length) + return setLastError(DebugUtils::errored(kErrorInvalidArgument)); + + // If the `Assembler` generated any code the `_bufferPtr` may be higher than + // the section length stored in `CodeHolder` as it doesn't update it each + // time it generates machine code. This is the same as calling `sync()`. + if (_section->_buffer._length < length) + _section->_buffer._length = length; + + _bufferPtr = _bufferData + offset; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::Assembler - Comment] +// ============================================================================ + +Error Assembler::comment(const char* s, size_t len) { + if (_lastError) return _lastError; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) { + Logger* logger = _code->getLogger(); + logger->log(s, len); + logger->log("\n", 1); + return kErrorOk; + } +#else + ASMJIT_UNUSED(s); + ASMJIT_UNUSED(len); +#endif + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::Assembler - Building Blocks] +// ============================================================================ + +Label Assembler::newLabel() { + uint32_t id = kInvalidValue; + if (!_lastError) { + ASMJIT_ASSERT(_code != nullptr); + Error err = _code->newLabelId(id); + if (ASMJIT_UNLIKELY(err)) setLastError(err); + } + return Label(id); +} + +Label Assembler::newNamedLabel(const char* name, size_t nameLength, uint32_t type, uint32_t parentId) { + uint32_t id = kInvalidValue; + if (!_lastError) { + ASMJIT_ASSERT(_code != nullptr); + Error err = _code->newNamedLabelId(id, name, nameLength, type, parentId); + if (ASMJIT_UNLIKELY(err)) setLastError(err); + } + return Label(id); +} + +Error Assembler::bind(const Label& label) { + if (_lastError) return _lastError; + ASMJIT_ASSERT(_code != nullptr); + + LabelEntry* le = _code->getLabelEntry(label); + if (ASMJIT_UNLIKELY(!le)) + return setLastError(DebugUtils::errored(kErrorInvalidLabel)); + + // Label can be bound only once. + if (ASMJIT_UNLIKELY(le->isBound())) + return setLastError(DebugUtils::errored(kErrorLabelAlreadyBound)); + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) { + StringBuilderTmp<256> sb; + sb.setFormat("L%u:", Operand::unpackId(label.getId())); + + size_t binSize = 0; + if (!_code->_logger->hasOption(Logger::kOptionBinaryForm)) + binSize = kInvalidIndex; + + LogUtil::formatLine(sb, nullptr, binSize, 0, 0, getInlineComment()); + _code->_logger->log(sb.getData(), sb.getLength()); + } +#endif // !ASMJIT_DISABLE_LOGGING + + Error err = kErrorOk; + size_t pos = getOffset(); + + LabelLink* link = le->_links; + LabelLink* prev = nullptr; + + while (link) { + intptr_t offset = link->offset; + uint32_t relocId = link->relocId; + + if (relocId != RelocEntry::kInvalidId) { + // Adjust relocation data. + RelocEntry* re = _code->_relocations[relocId]; + re->_data += static_cast(pos); + } + else { + // Not using relocId, this means that we are overwriting a real + // displacement in the CodeBuffer. + int32_t patchedValue = static_cast( + static_cast(pos) - offset + link->rel); + + // Size of the value we are going to patch. Only BYTE/DWORD is allowed. + uint32_t size = _bufferData[offset]; + if (size == 4) + Utils::writeI32u(_bufferData + offset, static_cast(patchedValue)); + else if (size == 1 && Utils::isInt8(patchedValue)) + _bufferData[offset] = static_cast(patchedValue & 0xFF); + else + err = DebugUtils::errored(kErrorInvalidDisplacement); + } + + prev = link->prev; + _code->_unresolvedLabelsCount--; + _code->_baseHeap.release(link, sizeof(LabelLink)); + + link = prev; + } + + // Set as bound. + le->_sectionId = _section->getId(); + le->_offset = pos; + le->_links = nullptr; + resetInlineComment(); + + if (err != kErrorOk) + return setLastError(err); + + return kErrorOk; +} + +Error Assembler::embed(const void* data, uint32_t size) { + if (_lastError) return _lastError; + + if (getRemainingSpace() < size) { + Error err = _code->growBuffer(&_section->_buffer, size); + if (ASMJIT_UNLIKELY(err != kErrorOk)) return setLastError(err); + } + + ::memcpy(_bufferPtr, data, size); + _bufferPtr += size; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) + _code->_logger->logBinary(data, size); +#endif // !ASMJIT_DISABLE_LOGGING + + return kErrorOk; +} + +Error Assembler::embedLabel(const Label& label) { + if (_lastError) return _lastError; + ASMJIT_ASSERT(_code != nullptr); + + RelocEntry* re; + LabelEntry* le = _code->getLabelEntry(label); + + if (ASMJIT_UNLIKELY(!le)) + return setLastError(DebugUtils::errored(kErrorInvalidLabel)); + + Error err; + uint32_t gpSize = getGpSize(); + + if (getRemainingSpace() < gpSize) { + err = _code->growBuffer(&_section->_buffer, gpSize); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + } + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) + _code->_logger->logf(gpSize == 4 ? ".dd L%u\n" : ".dq L%u\n", Operand::unpackId(label.getId())); +#endif // !ASMJIT_DISABLE_LOGGING + + err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, gpSize); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + + re->_sourceSectionId = _section->getId(); + re->_sourceOffset = static_cast(getOffset()); + + if (le->isBound()) { + re->_targetSectionId = le->getSectionId(); + re->_data = static_cast(static_cast(le->getOffset())); + } + else { + LabelLink* link = _code->newLabelLink(le, _section->getId(), getOffset(), 0); + if (ASMJIT_UNLIKELY(!link)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + link->relocId = re->getId(); + } + + // Emit dummy DWORD/QWORD depending on the address size. + ::memset(_bufferPtr, 0, gpSize); + _bufferPtr += gpSize; + + return kErrorOk; +} + +Error Assembler::embedConstPool(const Label& label, const ConstPool& pool) { + if (_lastError) return _lastError; + + if (!isLabelValid(label)) + return DebugUtils::errored(kErrorInvalidLabel); + + ASMJIT_PROPAGATE(align(kAlignData, static_cast(pool.getAlignment()))); + ASMJIT_PROPAGATE(bind(label)); + + size_t size = pool.getSize(); + if (getRemainingSpace() < size) { + Error err = _code->growBuffer(&_section->_buffer, size); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + } + + uint8_t* p = _bufferPtr; + pool.fill(p); + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) + _code->_logger->logBinary(p, size); +#endif // !ASMJIT_DISABLE_LOGGING + + _bufferPtr += size; + return kErrorOk; +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/assembler.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/assembler.h new file mode 100644 index 00000000..2b4aeb2e --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/assembler.h @@ -0,0 +1,113 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_ASSEMBLER_H +#define _ASMJIT_BASE_ASSEMBLER_H + +// [Dependencies] +#include "../base/codeemitter.h" +#include "../base/codeholder.h" +#include "../base/operand.h" +#include "../base/simdtypes.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::Assembler] +// ============================================================================ + +//! Base assembler. +//! +//! This class implements a base interface that is used by architecture +//! specific assemblers. +//! +//! \sa CodeCompiler. +class ASMJIT_VIRTAPI Assembler : public CodeEmitter { +public: + ASMJIT_NONCOPYABLE(Assembler) + typedef CodeEmitter Base; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `Assembler` instance. + ASMJIT_API Assembler() noexcept; + //! Destroy the `Assembler` instance. + ASMJIT_API virtual ~Assembler() noexcept; + + // -------------------------------------------------------------------------- + // [Events] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Error onAttach(CodeHolder* code) noexcept override; + ASMJIT_API virtual Error onDetach(CodeHolder* code) noexcept override; + + // -------------------------------------------------------------------------- + // [Code-Buffer] + // -------------------------------------------------------------------------- + + //! Called by \ref CodeHolder::sync(). + ASMJIT_API virtual void sync() noexcept; + + //! Get the capacity of the current CodeBuffer. + ASMJIT_INLINE size_t getBufferCapacity() const noexcept { return (size_t)(_bufferEnd - _bufferData); } + //! Get the number of remaining bytes in the current CodeBuffer. + ASMJIT_INLINE size_t getRemainingSpace() const noexcept { return (size_t)(_bufferEnd - _bufferPtr); } + + //! Get the current position in the CodeBuffer. + ASMJIT_INLINE size_t getOffset() const noexcept { return (size_t)(_bufferPtr - _bufferData); } + //! Set the current position in the CodeBuffer to `offset`. + //! + //! NOTE: The `offset` cannot be outside of the buffer length (even if it's + //! within buffer's capacity). + ASMJIT_API Error setOffset(size_t offset); + + //! Get start of the CodeBuffer of the current section. + ASMJIT_INLINE uint8_t* getBufferData() const noexcept { return _bufferData; } + //! Get end (first invalid byte) of the current section. + ASMJIT_INLINE uint8_t* getBufferEnd() const noexcept { return _bufferEnd; } + //! Get pointer in the CodeBuffer of the current section. + ASMJIT_INLINE uint8_t* getBufferPtr() const noexcept { return _bufferPtr; } + + // -------------------------------------------------------------------------- + // [Code-Generation] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Label newLabel() override; + ASMJIT_API virtual Label newNamedLabel(const char* name, size_t nameLength = kInvalidIndex, uint32_t type = Label::kTypeGlobal, uint32_t parentId = kInvalidValue) override; + ASMJIT_API virtual Error bind(const Label& label) override; + ASMJIT_API virtual Error embed(const void* data, uint32_t size) override; + ASMJIT_API virtual Error embedLabel(const Label& label) override; + ASMJIT_API virtual Error embedConstPool(const Label& label, const ConstPool& pool) override; + ASMJIT_API virtual Error comment(const char* s, size_t len = kInvalidIndex) override; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + SectionEntry* _section; //!< Current section where the assembling happens. + uint8_t* _bufferData; //!< Start of the CodeBuffer of the current section. + uint8_t* _bufferEnd; //!< End (first invalid byte) of the current section. + uint8_t* _bufferPtr; //!< Pointer in the CodeBuffer of the current section. +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_ASSEMBLER_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/codebuilder.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codebuilder.cpp new file mode 100644 index 00000000..ba79d64b --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codebuilder.cpp @@ -0,0 +1,605 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Guard] +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_BUILDER) + +// [Dependencies] +#include "../base/codebuilder.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::CodeBuilder - Construction / Destruction] +// ============================================================================ + +CodeBuilder::CodeBuilder() noexcept + : CodeEmitter(kTypeBuilder), + _cbBaseZone(32768 - Zone::kZoneOverhead), + _cbDataZone(16384 - Zone::kZoneOverhead), + _cbPassZone(32768 - Zone::kZoneOverhead), + _cbHeap(&_cbBaseZone), + _cbPasses(&_cbHeap), + _cbLabels(&_cbHeap), + _nodeFlowId(0), + _nodeFlags(0), + _firstNode(nullptr), + _lastNode(nullptr), + _cursor(nullptr) {} +CodeBuilder::~CodeBuilder() noexcept {} + +// ============================================================================ +// [asmjit::CodeBuilder - Events] +// ============================================================================ + +Error CodeBuilder::onAttach(CodeHolder* code) noexcept { + return Base::onAttach(code); +} + +Error CodeBuilder::onDetach(CodeHolder* code) noexcept { + _cbPasses.reset(&_cbHeap); + _cbLabels.reset(&_cbHeap); + _cbHeap.reset(&_cbBaseZone); + + _cbBaseZone.reset(false); + _cbDataZone.reset(false); + _cbPassZone.reset(false); + + _nodeFlowId = 0; + _nodeFlags = 0; + + _firstNode = nullptr; + _lastNode = nullptr; + _cursor = nullptr; + + return Base::onDetach(code); +} + +// ============================================================================ +// [asmjit::CodeBuilder - Node-Factory] +// ============================================================================ + +Error CodeBuilder::getCBLabel(CBLabel** pOut, uint32_t id) noexcept { + if (_lastError) return _lastError; + ASMJIT_ASSERT(_code != nullptr); + + size_t index = Operand::unpackId(id); + if (ASMJIT_UNLIKELY(index >= _code->getLabelsCount())) + return DebugUtils::errored(kErrorInvalidLabel); + + if (index >= _cbLabels.getLength()) + ASMJIT_PROPAGATE(_cbLabels.resize(index + 1)); + + CBLabel* node = _cbLabels[index]; + if (!node) { + node = newNodeT(id); + if (ASMJIT_UNLIKELY(!node)) + return DebugUtils::errored(kErrorNoHeapMemory); + _cbLabels[index] = node; + } + + *pOut = node; + return kErrorOk; +} + +Error CodeBuilder::registerLabelNode(CBLabel* node) noexcept { + if (_lastError) return _lastError; + ASMJIT_ASSERT(_code != nullptr); + + // Don't call setLastError() from here, we are noexcept and we are called + // by `newLabelNode()` and `newFuncNode()`, which are noexcept as well. + uint32_t id; + ASMJIT_PROPAGATE(_code->newLabelId(id)); + size_t index = Operand::unpackId(id); + + // We just added one label so it must be true. + ASMJIT_ASSERT(_cbLabels.getLength() < index + 1); + ASMJIT_PROPAGATE(_cbLabels.resize(index + 1)); + + _cbLabels[index] = node; + node->_id = id; + return kErrorOk; +} + +CBLabel* CodeBuilder::newLabelNode() noexcept { + CBLabel* node = newNodeT(); + if (!node || registerLabelNode(node) != kErrorOk) + return nullptr; + return node; +} + +CBAlign* CodeBuilder::newAlignNode(uint32_t mode, uint32_t alignment) noexcept { + return newNodeT(mode, alignment); +} + +CBData* CodeBuilder::newDataNode(const void* data, uint32_t size) noexcept { + if (size > CBData::kInlineBufferSize) { + void* cloned = _cbDataZone.alloc(size); + if (!cloned) return nullptr; + + if (data) ::memcpy(cloned, data, size); + data = cloned; + } + + return newNodeT(const_cast(data), size); +} + +CBConstPool* CodeBuilder::newConstPool() noexcept { + CBConstPool* node = newNodeT(); + if (!node || registerLabelNode(node) != kErrorOk) + return nullptr; + return node; +} + +CBComment* CodeBuilder::newCommentNode(const char* s, size_t len) noexcept { + if (s) { + if (len == kInvalidIndex) len = ::strlen(s); + if (len > 0) { + s = static_cast(_cbDataZone.dup(s, len, true)); + if (!s) return nullptr; + } + } + + return newNodeT(s); +} + +// ============================================================================ +// [asmjit::CodeBuilder - Code-Emitter] +// ============================================================================ + +Label CodeBuilder::newLabel() { + uint32_t id = kInvalidValue; + + if (!_lastError) { + CBLabel* node = newNodeT(id); + if (ASMJIT_UNLIKELY(!node)) { + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + else { + Error err = registerLabelNode(node); + if (ASMJIT_UNLIKELY(err)) + setLastError(err); + else + id = node->getId(); + } + } + + return Label(id); +} + +Label CodeBuilder::newNamedLabel(const char* name, size_t nameLength, uint32_t type, uint32_t parentId) { + uint32_t id = kInvalidValue; + + if (!_lastError) { + CBLabel* node = newNodeT(id); + if (ASMJIT_UNLIKELY(!node)) { + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + else { + Error err = _code->newNamedLabelId(id, name, nameLength, type, parentId); + if (ASMJIT_UNLIKELY(err)) + setLastError(err); + else + id = node->getId(); + } + } + + return Label(id); +} + +Error CodeBuilder::bind(const Label& label) { + if (_lastError) return _lastError; + + CBLabel* node; + Error err = getCBLabel(&node, label); + if (ASMJIT_UNLIKELY(err)) + return setLastError(err); + + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::align(uint32_t mode, uint32_t alignment) { + if (_lastError) return _lastError; + + CBAlign* node = newAlignNode(mode, alignment); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::embed(const void* data, uint32_t size) { + if (_lastError) return _lastError; + + CBData* node = newDataNode(data, size); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::embedLabel(const Label& label) { + if (_lastError) return _lastError; + + CBLabelData* node = newNodeT(label.getId()); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::embedConstPool(const Label& label, const ConstPool& pool) { + if (_lastError) return _lastError; + + if (!isLabelValid(label)) + return setLastError(DebugUtils::errored(kErrorInvalidLabel)); + + ASMJIT_PROPAGATE(align(kAlignData, static_cast(pool.getAlignment()))); + ASMJIT_PROPAGATE(bind(label)); + + CBData* node = newDataNode(nullptr, static_cast(pool.getSize())); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + pool.fill(node->getData()); + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::comment(const char* s, size_t len) { + if (_lastError) return _lastError; + + CBComment* node = newCommentNode(s, len); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeBuilder - Node-Management] +// ============================================================================ + +CBNode* CodeBuilder::addNode(CBNode* node) noexcept { + ASMJIT_ASSERT(node); + ASMJIT_ASSERT(node->_prev == nullptr); + ASMJIT_ASSERT(node->_next == nullptr); + + if (!_cursor) { + if (!_firstNode) { + _firstNode = node; + _lastNode = node; + } + else { + node->_next = _firstNode; + _firstNode->_prev = node; + _firstNode = node; + } + } + else { + CBNode* prev = _cursor; + CBNode* next = _cursor->_next; + + node->_prev = prev; + node->_next = next; + + prev->_next = node; + if (next) + next->_prev = node; + else + _lastNode = node; + } + + _cursor = node; + return node; +} + +CBNode* CodeBuilder::addAfter(CBNode* node, CBNode* ref) noexcept { + ASMJIT_ASSERT(node); + ASMJIT_ASSERT(ref); + + ASMJIT_ASSERT(node->_prev == nullptr); + ASMJIT_ASSERT(node->_next == nullptr); + + CBNode* prev = ref; + CBNode* next = ref->_next; + + node->_prev = prev; + node->_next = next; + + prev->_next = node; + if (next) + next->_prev = node; + else + _lastNode = node; + + return node; +} + +CBNode* CodeBuilder::addBefore(CBNode* node, CBNode* ref) noexcept { + ASMJIT_ASSERT(node != nullptr); + ASMJIT_ASSERT(node->_prev == nullptr); + ASMJIT_ASSERT(node->_next == nullptr); + ASMJIT_ASSERT(ref != nullptr); + + CBNode* prev = ref->_prev; + CBNode* next = ref; + + node->_prev = prev; + node->_next = next; + + next->_prev = node; + if (prev) + prev->_next = node; + else + _firstNode = node; + + return node; +} + +static ASMJIT_INLINE void CodeBuilder_nodeRemoved(CodeBuilder* self, CBNode* node_) noexcept { + if (node_->isJmpOrJcc()) { + CBJump* node = static_cast(node_); + CBLabel* label = node->getTarget(); + + if (label) { + // Disconnect. + CBJump** pPrev = &label->_from; + for (;;) { + ASMJIT_ASSERT(*pPrev != nullptr); + + CBJump* current = *pPrev; + if (!current) break; + + if (current == node) { + *pPrev = node->_jumpNext; + break; + } + + pPrev = ¤t->_jumpNext; + } + + label->subNumRefs(); + } + } +} + +CBNode* CodeBuilder::removeNode(CBNode* node) noexcept { + CBNode* prev = node->_prev; + CBNode* next = node->_next; + + if (_firstNode == node) + _firstNode = next; + else + prev->_next = next; + + if (_lastNode == node) + _lastNode = prev; + else + next->_prev = prev; + + node->_prev = nullptr; + node->_next = nullptr; + + if (_cursor == node) + _cursor = prev; + CodeBuilder_nodeRemoved(this, node); + + return node; +} + +void CodeBuilder::removeNodes(CBNode* first, CBNode* last) noexcept { + if (first == last) { + removeNode(first); + return; + } + + CBNode* prev = first->_prev; + CBNode* next = last->_next; + + if (_firstNode == first) + _firstNode = next; + else + prev->_next = next; + + if (_lastNode == last) + _lastNode = prev; + else + next->_prev = prev; + + CBNode* node = first; + for (;;) { + CBNode* next = node->getNext(); + ASMJIT_ASSERT(next != nullptr); + + node->_prev = nullptr; + node->_next = nullptr; + + if (_cursor == node) + _cursor = prev; + CodeBuilder_nodeRemoved(this, node); + + if (node == last) + break; + node = next; + } +} + +CBNode* CodeBuilder::setCursor(CBNode* node) noexcept { + CBNode* old = _cursor; + _cursor = node; + return old; +} + +// ============================================================================ +// [asmjit::CodeBuilder - Passes] +// ============================================================================ + +ASMJIT_FAVOR_SIZE CBPass* CodeBuilder::getPassByName(const char* name) const noexcept { + for (size_t i = 0, len = _cbPasses.getLength(); i < len; i++) { + CBPass* pass = _cbPasses[i]; + if (::strcmp(pass->getName(), name) == 0) + return pass; + } + + return nullptr; +} + +ASMJIT_FAVOR_SIZE Error CodeBuilder::addPass(CBPass* pass) noexcept { + if (ASMJIT_UNLIKELY(pass == nullptr)) { + // Since this is directly called by `addPassT()` we treat `null` argument + // as out-of-memory condition. Otherwise it would be API misuse. + return DebugUtils::errored(kErrorNoHeapMemory); + } + else if (ASMJIT_UNLIKELY(pass->_cb)) { + // Kind of weird, but okay... + if (pass->_cb == this) + return kErrorOk; + return DebugUtils::errored(kErrorInvalidState); + } + + ASMJIT_PROPAGATE(_cbPasses.append(pass)); + pass->_cb = this; + return kErrorOk; +} + +ASMJIT_FAVOR_SIZE Error CodeBuilder::deletePass(CBPass* pass) noexcept { + if (ASMJIT_UNLIKELY(pass == nullptr)) + return DebugUtils::errored(kErrorInvalidArgument); + + if (pass->_cb != nullptr) { + if (pass->_cb != this) + return DebugUtils::errored(kErrorInvalidState); + + size_t index = _cbPasses.indexOf(pass); + ASMJIT_ASSERT(index != kInvalidIndex); + + pass->_cb = nullptr; + _cbPasses.removeAt(index); + } + + pass->~CBPass(); + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeBuilder - Serialization] +// ============================================================================ + +Error CodeBuilder::serialize(CodeEmitter* dst) { + Error err = kErrorOk; + CBNode* node_ = getFirstNode(); + + do { + dst->setInlineComment(node_->getInlineComment()); + + switch (node_->getType()) { + case CBNode::kNodeAlign: { + CBAlign* node = static_cast(node_); + err = dst->align(node->getMode(), node->getAlignment()); + break; + } + + case CBNode::kNodeData: { + CBData* node = static_cast(node_); + err = dst->embed(node->getData(), node->getSize()); + break; + } + + case CBNode::kNodeFunc: + case CBNode::kNodeLabel: { + CBLabel* node = static_cast(node_); + err = dst->bind(node->getLabel()); + break; + } + + case CBNode::kNodeLabelData: { + CBLabelData* node = static_cast(node_); + err = dst->embedLabel(node->getLabel()); + break; + } + + case CBNode::kNodeConstPool: { + CBConstPool* node = static_cast(node_); + err = dst->embedConstPool(node->getLabel(), node->getConstPool()); + break; + } + + case CBNode::kNodeInst: + case CBNode::kNodeFuncCall: { + CBInst* node = static_cast(node_); + + uint32_t instId = node->getInstId(); + uint32_t options = node->getOptions(); + + const Operand* opArray = node->getOpArray(); + uint32_t opCount = node->getOpCount(); + + const Operand_* o0 = &dst->_none; + const Operand_* o1 = &dst->_none; + const Operand_* o2 = &dst->_none; + const Operand_* o3 = &dst->_none; + + switch (opCount) { + case 6: dst->_op5 = opArray[5]; options |= CodeEmitter::kOptionOp5; ASMJIT_FALLTHROUGH; + case 5: dst->_op4 = opArray[4]; options |= CodeEmitter::kOptionOp4; ASMJIT_FALLTHROUGH; + case 4: o3 = &opArray[3]; ASMJIT_FALLTHROUGH; + case 3: o2 = &opArray[2]; ASMJIT_FALLTHROUGH; + case 2: o1 = &opArray[1]; ASMJIT_FALLTHROUGH; + case 1: o0 = &opArray[0]; ASMJIT_FALLTHROUGH; + case 0: break; + } + + dst->setOptions(options); + err = dst->_emit(instId, *o0, *o1, *o2, *o3); + break; + } + + case CBNode::kNodeComment: { + CBComment* node = static_cast(node_); + err = dst->comment(node->getInlineComment()); + break; + } + + default: + break; + } + + if (err) break; + node_ = node_->getNext(); + } while (node_); + + return err; +} + +// ============================================================================ +// [asmjit::CBPass] +// ============================================================================ + +CBPass::CBPass(const char* name) noexcept + : _cb(nullptr), + _name(name) {} +CBPass::~CBPass() noexcept {} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_BUILDER diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/codebuilder.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codebuilder.h new file mode 100644 index 00000000..c9929a99 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codebuilder.h @@ -0,0 +1,909 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CODEBUILDER_H +#define _ASMJIT_BASE_CODEBUILDER_H + +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_BUILDER) + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/codeholder.h" +#include "../base/constpool.h" +#include "../base/operand.h" +#include "../base/utils.h" +#include "../base/zone.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class CBNode; +class CBPass; + +class CBAlign; +class CBComment; +class CBConstPool; +class CBData; +class CBInst; +class CBJump; +class CBLabel; +class CBLabelData; +class CBSentinel; + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::CodeBuilder] +// ============================================================================ + +class ASMJIT_VIRTAPI CodeBuilder : public CodeEmitter { +public: + ASMJIT_NONCOPYABLE(CodeBuilder) + typedef CodeEmitter Base; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CodeBuilder` instance. + ASMJIT_API CodeBuilder() noexcept; + //! Destroy the `CodeBuilder` instance. + ASMJIT_API virtual ~CodeBuilder() noexcept; + + // -------------------------------------------------------------------------- + // [Events] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Error onAttach(CodeHolder* code) noexcept override; + ASMJIT_API virtual Error onDetach(CodeHolder* code) noexcept override; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get a vector of CBPass objects that will be executed by `process()`. + ASMJIT_INLINE const ZoneVector& getPasses() const noexcept { return _cbPasses; } + + //! Get a vector of CBLabel nodes. + //! + //! NOTE: If a label of some index is not associated with `CodeBuilder` it + //! would be null, so always check for nulls if you iterate over the vector. + ASMJIT_INLINE const ZoneVector& getLabels() const noexcept { return _cbLabels; } + + //! Get the first node. + ASMJIT_INLINE CBNode* getFirstNode() const noexcept { return _firstNode; } + //! Get the last node. + ASMJIT_INLINE CBNode* getLastNode() const noexcept { return _lastNode; } + + // -------------------------------------------------------------------------- + // [Node-Management] + // -------------------------------------------------------------------------- + + //! \internal + template + ASMJIT_INLINE T* newNodeT() noexcept { return new(_cbHeap.alloc(sizeof(T))) T(this); } + + //! \internal + template + ASMJIT_INLINE T* newNodeT(P0 p0) noexcept { return new(_cbHeap.alloc(sizeof(T))) T(this, p0); } + + //! \internal + template + ASMJIT_INLINE T* newNodeT(P0 p0, P1 p1) noexcept { return new(_cbHeap.alloc(sizeof(T))) T(this, p0, p1); } + + //! \internal + template + ASMJIT_INLINE T* newNodeT(P0 p0, P1 p1, P2 p2) noexcept { return new(_cbHeap.alloc(sizeof(T))) T(this, p0, p1, p2); } + + ASMJIT_API Error registerLabelNode(CBLabel* node) noexcept; + //! Get `CBLabel` by `id`. + ASMJIT_API Error getCBLabel(CBLabel** pOut, uint32_t id) noexcept; + //! Get `CBLabel` by `label`. + ASMJIT_INLINE Error getCBLabel(CBLabel** pOut, const Label& label) noexcept { return getCBLabel(pOut, label.getId()); } + + //! Create a new \ref CBLabel node. + ASMJIT_API CBLabel* newLabelNode() noexcept; + //! Create a new \ref CBAlign node. + ASMJIT_API CBAlign* newAlignNode(uint32_t mode, uint32_t alignment) noexcept; + //! Create a new \ref CBData node. + ASMJIT_API CBData* newDataNode(const void* data, uint32_t size) noexcept; + //! Create a new \ref CBConstPool node. + ASMJIT_API CBConstPool* newConstPool() noexcept; + //! Create a new \ref CBComment node. + ASMJIT_API CBComment* newCommentNode(const char* s, size_t len) noexcept; + + // -------------------------------------------------------------------------- + // [Code-Emitter] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Label newLabel() override; + ASMJIT_API virtual Label newNamedLabel(const char* name, size_t nameLength = kInvalidIndex, uint32_t type = Label::kTypeGlobal, uint32_t parentId = kInvalidValue) override; + ASMJIT_API virtual Error bind(const Label& label) override; + ASMJIT_API virtual Error align(uint32_t mode, uint32_t alignment) override; + ASMJIT_API virtual Error embed(const void* data, uint32_t size) override; + ASMJIT_API virtual Error embedLabel(const Label& label) override; + ASMJIT_API virtual Error embedConstPool(const Label& label, const ConstPool& pool) override; + ASMJIT_API virtual Error comment(const char* s, size_t len = kInvalidIndex) override; + + // -------------------------------------------------------------------------- + // [Node-Management] + // -------------------------------------------------------------------------- + + //! Add `node` after the current and set current to `node`. + ASMJIT_API CBNode* addNode(CBNode* node) noexcept; + //! Insert `node` after `ref`. + ASMJIT_API CBNode* addAfter(CBNode* node, CBNode* ref) noexcept; + //! Insert `node` before `ref`. + ASMJIT_API CBNode* addBefore(CBNode* node, CBNode* ref) noexcept; + //! Remove `node`. + ASMJIT_API CBNode* removeNode(CBNode* node) noexcept; + //! Remove multiple nodes. + ASMJIT_API void removeNodes(CBNode* first, CBNode* last) noexcept; + + //! Get current node. + //! + //! \note If this method returns null it means that nothing has been + //! emitted yet. + ASMJIT_INLINE CBNode* getCursor() const noexcept { return _cursor; } + //! Set the current node without returning the previous node. + ASMJIT_INLINE void _setCursor(CBNode* node) noexcept { _cursor = node; } + //! Set the current node to `node` and return the previous one. + ASMJIT_API CBNode* setCursor(CBNode* node) noexcept; + + // -------------------------------------------------------------------------- + // [Passes] + // -------------------------------------------------------------------------- + + template + ASMJIT_INLINE T* newPassT() noexcept { return new(_cbBaseZone.alloc(sizeof(T))) T(); } + template + ASMJIT_INLINE T* newPassT(P0 p0) noexcept { return new(_cbBaseZone.alloc(sizeof(T))) T(p0); } + template + ASMJIT_INLINE T* newPassT(P0 p0, P1 p1) noexcept { return new(_cbBaseZone.alloc(sizeof(T))) T(p0, p1); } + + template + ASMJIT_INLINE Error addPassT() noexcept { return addPass(newPassT()); } + template + ASMJIT_INLINE Error addPassT(P0 p0) noexcept { return addPass(newPassT(p0)); } + template + ASMJIT_INLINE Error addPassT(P0 p0, P1 p1) noexcept { return addPass(newPassT(p0, p1)); } + + //! Get a `CBPass` by name. + ASMJIT_API CBPass* getPassByName(const char* name) const noexcept; + //! Add `pass` to the list of passes. + ASMJIT_API Error addPass(CBPass* pass) noexcept; + //! Remove `pass` from the list of passes and delete it. + ASMJIT_API Error deletePass(CBPass* pass) noexcept; + + // -------------------------------------------------------------------------- + // [Serialization] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Error serialize(CodeEmitter* dst); + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Zone _cbBaseZone; //!< Base zone used to allocate nodes and `CBPass`. + Zone _cbDataZone; //!< Data zone used to allocate data and names. + Zone _cbPassZone; //!< Zone passed to `CBPass::process()`. + ZoneHeap _cbHeap; //!< ZoneHeap that uses `_cbBaseZone`. + + ZoneVector _cbPasses; //!< Array of `CBPass` objects. + ZoneVector _cbLabels; //!< Maps label indexes to `CBLabel` nodes. + + CBNode* _firstNode; //!< First node of the current section. + CBNode* _lastNode; //!< Last node of the current section. + CBNode* _cursor; //!< Current node (cursor). + + uint32_t _nodeFlowId; //!< Flow-id assigned to each new node. + uint32_t _nodeFlags; //!< Flags assigned to each new node. +}; + +// ============================================================================ +// [asmjit::CBPass] +// ============================================================================ + +//! `CodeBuilder` pass used to code transformations, analysis, and lowering. +class ASMJIT_VIRTAPI CBPass { +public: + ASMJIT_NONCOPYABLE(CBPass); + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_API CBPass(const char* name) noexcept; + ASMJIT_API virtual ~CBPass() noexcept; + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + //! Process the code stored in CodeBuffer `cb`. + //! + //! This is the only function that is called by the `CodeBuilder` to process + //! the code. It passes the CodeBuilder itself (`cb`) and also a zone memory + //! allocator `zone`, which will be reset after the `process()` returns. The + //! allocator should be used for all allocations as it's fast and everything + //! it allocates will be released at once when `process()` returns. + virtual Error process(Zone* zone) noexcept = 0; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE const CodeBuilder* cb() const noexcept { return _cb; } + ASMJIT_INLINE const char* getName() const noexcept { return _name; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CodeBuilder* _cb; //!< CodeBuilder this pass is assigned to. + const char* _name; //!< Name of the pass. +}; + +// ============================================================================ +// [asmjit::CBNode] +// ============================================================================ + +//! Node (CodeBuilder). +//! +//! Every node represents a building-block used by \ref CodeBuilder. It can be +//! instruction, data, label, comment, directive, or any other high-level +//! representation that can be transformed to the building blocks mentioned. +//! Every class that inherits \ref CodeBuilder can define its own nodes that it +//! can lower to basic nodes. +class CBNode { +public: + ASMJIT_NONCOPYABLE(CBNode) + + // -------------------------------------------------------------------------- + // [Type] + // -------------------------------------------------------------------------- + + //! Type of \ref CBNode. + ASMJIT_ENUM(NodeType) { + kNodeNone = 0, //!< Invalid node (internal, don't use). + + // [CodeBuilder] + kNodeInst = 1, //!< Node is \ref CBInst or \ref CBJump. + kNodeData = 2, //!< Node is \ref CBData. + kNodeAlign = 3, //!< Node is \ref CBAlign. + kNodeLabel = 4, //!< Node is \ref CBLabel. + kNodeLabelData = 5, //!< Node is \ref CBLabelData. + kNodeConstPool = 6, //!< Node is \ref CBConstPool. + kNodeComment = 7, //!< Node is \ref CBComment. + kNodeSentinel = 8, //!< Node is \ref CBSentinel. + + // [CodeCompiler] + kNodeFunc = 16, //!< Node is \ref CCFunc (considered as \ref CBLabel by \ref CodeBuilder). + kNodeFuncExit = 17, //!< Node is \ref CCFuncRet. + kNodeFuncCall = 18, //!< Node is \ref CCFuncCall. + kNodePushArg = 19, //!< Node is \ref CCPushArg. + kNodeHint = 20, //!< Node is \ref CCHint. + + // [UserDefined] + kNodeUser = 32 //!< First id of a user-defined node. + }; + + // -------------------------------------------------------------------------- + // [Flags] + // -------------------------------------------------------------------------- + + ASMJIT_ENUM(Flags) { + //! The node has been translated by the CodeCompiler. + kFlagIsTranslated = 0x0001, + //! If the node can be safely removed (has no effect). + kFlagIsRemovable = 0x0004, + //! If the node is informative only and can be safely removed. + kFlagIsInformative = 0x0008, + + //! If the `CBInst` is a jump. + kFlagIsJmp = 0x0010, + //! If the `CBInst` is a conditional jump. + kFlagIsJcc = 0x0020, + + //! If the `CBInst` is an unconditional jump or conditional jump that is + //! likely to be taken. + kFlagIsTaken = 0x0040, + + //! If the `CBNode` will return from a function. + //! + //! This flag is used by both `CBSentinel` and `CCFuncRet`. + kFlagIsRet = 0x0080, + + //! Whether the instruction is special. + kFlagIsSpecial = 0x0100, + + //! Whether the instruction is an FPU instruction. + kFlagIsFp = 0x0200 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new \ref CBNode - always use \ref CodeBuilder to allocate nodes. + ASMJIT_INLINE CBNode(CodeBuilder* cb, uint32_t type) noexcept { + _prev = nullptr; + _next = nullptr; + _type = static_cast(type); + _opCount = 0; + _flags = static_cast(cb->_nodeFlags); + _flowId = cb->_nodeFlowId; + _inlineComment = nullptr; + _passData = nullptr; + } + //! Destroy the `CBNode` instance (NEVER CALLED). + ASMJIT_INLINE ~CBNode() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get previous node in the compiler stream. + ASMJIT_INLINE CBNode* getPrev() const noexcept { return _prev; } + //! Get next node in the compiler stream. + ASMJIT_INLINE CBNode* getNext() const noexcept { return _next; } + + //! Get the node type, see \ref Type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + //! Get the node flags. + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; } + + //! Get whether the instruction has flag `flag`. + ASMJIT_INLINE bool hasFlag(uint32_t flag) const noexcept { return (static_cast(_flags) & flag) != 0; } + //! Set node flags to `flags`. + ASMJIT_INLINE void setFlags(uint32_t flags) noexcept { _flags = static_cast(flags); } + //! Add instruction `flags`. + ASMJIT_INLINE void orFlags(uint32_t flags) noexcept { _flags |= static_cast(flags); } + //! And instruction `flags`. + ASMJIT_INLINE void andFlags(uint32_t flags) noexcept { _flags &= static_cast(flags); } + //! Clear instruction `flags`. + ASMJIT_INLINE void andNotFlags(uint32_t flags) noexcept { _flags &= ~static_cast(flags); } + + //! Get whether the node has been translated. + ASMJIT_INLINE bool isTranslated() const noexcept { return hasFlag(kFlagIsTranslated); } + + //! Get whether the node is removable if it's in unreachable code block. + ASMJIT_INLINE bool isRemovable() const noexcept { return hasFlag(kFlagIsRemovable); } + //! Get whether the node is informative only (comment, hint). + ASMJIT_INLINE bool isInformative() const noexcept { return hasFlag(kFlagIsInformative); } + + //! Whether the node is `CBLabel`. + ASMJIT_INLINE bool isLabel() const noexcept { return _type == kNodeLabel; } + //! Whether the `CBInst` node is an unconditional jump. + ASMJIT_INLINE bool isJmp() const noexcept { return hasFlag(kFlagIsJmp); } + //! Whether the `CBInst` node is a conditional jump. + ASMJIT_INLINE bool isJcc() const noexcept { return hasFlag(kFlagIsJcc); } + //! Whether the `CBInst` node is a conditional/unconditional jump. + ASMJIT_INLINE bool isJmpOrJcc() const noexcept { return hasFlag(kFlagIsJmp | kFlagIsJcc); } + //! Whether the `CBInst` node is a return. + ASMJIT_INLINE bool isRet() const noexcept { return hasFlag(kFlagIsRet); } + + //! Get whether the node is `CBInst` and the instruction is special. + ASMJIT_INLINE bool isSpecial() const noexcept { return hasFlag(kFlagIsSpecial); } + //! Get whether the node is `CBInst` and the instruction uses x87-FPU. + ASMJIT_INLINE bool isFp() const noexcept { return hasFlag(kFlagIsFp); } + + //! Get flow index. + ASMJIT_INLINE uint32_t getFlowId() const noexcept { return _flowId; } + //! Set flow index. + ASMJIT_INLINE void setFlowId(uint32_t flowId) noexcept { _flowId = flowId; } + + //! Get if the node has an inline comment. + ASMJIT_INLINE bool hasInlineComment() const noexcept { return _inlineComment != nullptr; } + //! Get an inline comment string. + ASMJIT_INLINE const char* getInlineComment() const noexcept { return _inlineComment; } + //! Set an inline comment string to `s`. + ASMJIT_INLINE void setInlineComment(const char* s) noexcept { _inlineComment = s; } + //! Set an inline comment string to null. + ASMJIT_INLINE void resetInlineComment() noexcept { _inlineComment = nullptr; } + + //! Get if the node has associated work-data. + ASMJIT_INLINE bool hasPassData() const noexcept { return _passData != nullptr; } + //! Get work-data - data used during processing & transformations. + template + ASMJIT_INLINE T* getPassData() const noexcept { return (T*)_passData; } + //! Set work-data to `data`. + template + ASMJIT_INLINE void setPassData(T* data) noexcept { _passData = (void*)data; } + //! Reset work-data to null. + ASMJIT_INLINE void resetPassData() noexcept { _passData = nullptr; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CBNode* _prev; //!< Previous node. + CBNode* _next; //!< Next node. + + uint8_t _type; //!< Node type, see \ref NodeType. + uint8_t _opCount; //!< Count of operands or zero. + uint16_t _flags; //!< Flags, different meaning for every type of the node. + uint32_t _flowId; //!< Flow index. + + const char* _inlineComment; //!< Inline comment or null if not used. + void* _passData; //!< Data used exclusively by the current `CBPass`. +}; + +// ============================================================================ +// [asmjit::CBInst] +// ============================================================================ + +//! Instruction (CodeBuilder). +//! +//! Wraps an instruction with its options and operands. +class CBInst : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBInst) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBInst` instance. + ASMJIT_INLINE CBInst(CodeBuilder* cb, uint32_t instId, uint32_t options, Operand* opArray, uint32_t opCount) noexcept + : CBNode(cb, kNodeInst) { + + orFlags(kFlagIsRemovable); + _instId = static_cast(instId); + _reserved = 0; + _options = options; + + _opCount = static_cast(opCount); + _opArray = opArray; + + _updateMemOp(); + } + + //! Destroy the `CBInst` instance (NEVER CALLED). + ASMJIT_INLINE ~CBInst() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the instruction id, see \ref X86Inst::Id. + ASMJIT_INLINE uint32_t getInstId() const noexcept { return _instId; } + //! Set the instruction id to `instId`. + //! + //! NOTE: Please do not modify instruction code if you don't know what you + //! are doing. Incorrect instruction code and/or operands can cause random + //! errors in production builds and will most probably trigger assertion + //! failures in debug builds. + ASMJIT_INLINE void setInstId(uint32_t instId) noexcept { _instId = static_cast(instId); } + + //! Whether the instruction is either a jump or a conditional jump likely to + //! be taken. + ASMJIT_INLINE bool isTaken() const noexcept { return hasFlag(kFlagIsTaken); } + + //! Get emit options. + ASMJIT_INLINE uint32_t getOptions() const noexcept { return _options; } + //! Set emit options. + ASMJIT_INLINE void setOptions(uint32_t options) noexcept { _options = options; } + //! Add emit options. + ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _options |= options; } + //! Mask emit options. + ASMJIT_INLINE void andOptions(uint32_t options) noexcept { _options &= options; } + //! Clear emit options. + ASMJIT_INLINE void delOptions(uint32_t options) noexcept { _options &= ~options; } + + //! Get op-mask operand (used to represent AVX-512 op-mask selector). + ASMJIT_INLINE Operand& getOpExtra() noexcept { return _opExtra; } + //! \overload + ASMJIT_INLINE const Operand& getOpExtra() const noexcept { return _opExtra; } + //1 Set op-mask operand. + ASMJIT_INLINE void setOpExtra(const Operand& opExtra) noexcept { _opExtra = opExtra; } + + //! Get operands count. + ASMJIT_INLINE uint32_t getOpCount() const noexcept { return _opCount; } + //! Get operands list. + ASMJIT_INLINE Operand* getOpArray() noexcept { return _opArray; } + //! \overload + ASMJIT_INLINE const Operand* getOpArray() const noexcept { return _opArray; } + + //! Get whether the instruction contains a memory operand. + ASMJIT_INLINE bool hasMemOp() const noexcept { return _memOpIndex != 0xFF; } + //! Get memory operand. + //! + //! NOTE: Can only be called if the instruction has such operand, + //! see `hasMemOp()`. + ASMJIT_INLINE Mem* getMemOp() const noexcept { + ASMJIT_ASSERT(hasMemOp()); + return static_cast(&_opArray[_memOpIndex]); + } + //! \overload + template + ASMJIT_INLINE T* getMemOp() const noexcept { + ASMJIT_ASSERT(hasMemOp()); + return static_cast(&_opArray[_memOpIndex]); + } + + //! Set memory operand index, `0xFF` means no memory operand. + ASMJIT_INLINE void setMemOpIndex(uint32_t index) noexcept { _memOpIndex = static_cast(index); } + //! Reset memory operand index to `0xFF` (no operand). + ASMJIT_INLINE void resetMemOpIndex() noexcept { _memOpIndex = 0xFF; } + + // -------------------------------------------------------------------------- + // [Utils] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void _updateMemOp() noexcept { + Operand* opArray = getOpArray(); + uint32_t opCount = getOpCount(); + + uint32_t i; + for (i = 0; i < opCount; i++) + if (opArray[i].isMem()) + goto Update; + i = 0xFF; + +Update: + setMemOpIndex(i); + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint16_t _instId; //!< Instruction id (architecture dependent). + uint8_t _memOpIndex; //!< \internal + uint8_t _reserved; //!< \internal + uint32_t _options; //!< Instruction options. + Operand _opExtra; //!< Extra operand (op-mask {k} on AVX-512). + Operand* _opArray; //!< Instruction operands. +}; + +// ============================================================================ +// [asmjit::CBInstEx] +// ============================================================================ + +struct CBInstEx : public CBInst { + Operand _op4; + Operand _op5; + Operand _opExtra; +}; + +// ============================================================================ +// [asmjit::CBJump] +// ============================================================================ + +//! Asm jump (conditional or direct). +//! +//! Extension of `CBInst` node, which stores more information about the jump. +class CBJump : public CBInst { +public: + ASMJIT_NONCOPYABLE(CBJump) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CBJump(CodeBuilder* cb, uint32_t instId, uint32_t options, Operand* opArray, uint32_t opCount) noexcept + : CBInst(cb, instId, options, opArray, opCount), + _target(nullptr), + _jumpNext(nullptr) {} + ASMJIT_INLINE ~CBJump() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CBLabel* getTarget() const noexcept { return _target; } + ASMJIT_INLINE CBJump* getJumpNext() const noexcept { return _jumpNext; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CBLabel* _target; //!< Target node. + CBJump* _jumpNext; //!< Next jump to the same target in a single linked-list. +}; + +// ============================================================================ +// [asmjit::CBData] +// ============================================================================ + +//! Asm data (CodeBuilder). +//! +//! Wraps `.data` directive. The node contains data that will be placed at the +//! node's position in the assembler stream. The data is considered to be RAW; +//! no analysis nor byte-order conversion is performed on RAW data. +class CBData : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBData) + enum { kInlineBufferSize = static_cast(64 - sizeof(CBNode) - 4) }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBData` instance. + ASMJIT_INLINE CBData(CodeBuilder* cb, void* data, uint32_t size) noexcept : CBNode(cb, kNodeData) { + if (size <= kInlineBufferSize) { + if (data) ::memcpy(_buf, data, size); + } + else { + _externalPtr = static_cast(data); + } + _size = size; + } + + //! Destroy the `CBData` instance (NEVER CALLED). + ASMJIT_INLINE ~CBData() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get size of the data. + uint32_t getSize() const noexcept { return _size; } + //! Get pointer to the data. + uint8_t* getData() const noexcept { return _size <= kInlineBufferSize ? const_cast(_buf) : _externalPtr; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + union { + struct { + uint8_t _buf[kInlineBufferSize]; //!< Embedded data buffer. + uint32_t _size; //!< Size of the data. + }; + struct { + uint8_t* _externalPtr; //!< Pointer to external data. + }; + }; +}; + +// ============================================================================ +// [asmjit::CBAlign] +// ============================================================================ + +//! Align directive (CodeBuilder). +//! +//! Wraps `.align` directive. +class CBAlign : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBAlign) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBAlign` instance. + ASMJIT_INLINE CBAlign(CodeBuilder* cb, uint32_t mode, uint32_t alignment) noexcept + : CBNode(cb, kNodeAlign), + _mode(mode), + _alignment(alignment) {} + //! Destroy the `CBAlign` instance (NEVER CALLED). + ASMJIT_INLINE ~CBAlign() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get align mode. + ASMJIT_INLINE uint32_t getMode() const noexcept { return _mode; } + //! Set align mode. + ASMJIT_INLINE void setMode(uint32_t mode) noexcept { _mode = mode; } + + //! Get align offset in bytes. + ASMJIT_INLINE uint32_t getAlignment() const noexcept { return _alignment; } + //! Set align offset in bytes to `offset`. + ASMJIT_INLINE void setAlignment(uint32_t alignment) noexcept { _alignment = alignment; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _mode; //!< Align mode, see \ref AlignMode. + uint32_t _alignment; //!< Alignment (in bytes). +}; + +// ============================================================================ +// [asmjit::CBLabel] +// ============================================================================ + +//! Label (CodeBuilder). +class CBLabel : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBLabel) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBLabel` instance. + ASMJIT_INLINE CBLabel(CodeBuilder* cb, uint32_t id = kInvalidValue) noexcept + : CBNode(cb, kNodeLabel), + _id(id), + _numRefs(0), + _from(nullptr) {} + //! Destroy the `CBLabel` instance (NEVER CALLED). + ASMJIT_INLINE ~CBLabel() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the label id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + //! Get the label as `Label` operand. + ASMJIT_INLINE Label getLabel() const noexcept { return Label(_id); } + + //! Get first jmp instruction. + ASMJIT_INLINE CBJump* getFrom() const noexcept { return _from; } + + //! Get number of jumps to this target. + ASMJIT_INLINE uint32_t getNumRefs() const noexcept { return _numRefs; } + //! Set number of jumps to this target. + ASMJIT_INLINE void setNumRefs(uint32_t i) noexcept { _numRefs = i; } + + //! Add number of jumps to this target. + ASMJIT_INLINE void addNumRefs(uint32_t i = 1) noexcept { _numRefs += i; } + //! Subtract number of jumps to this target. + ASMJIT_INLINE void subNumRefs(uint32_t i = 1) noexcept { _numRefs -= i; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _id; //!< Label id. + uint32_t _numRefs; //!< Count of jumps here. + CBJump* _from; //!< Linked-list of nodes that can jump here. +}; + +// ============================================================================ +// [asmjit::CBLabelData] +// ============================================================================ + +class CBLabelData : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBLabelData) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBLabelData` instance. + ASMJIT_INLINE CBLabelData(CodeBuilder* cb, uint32_t id = kInvalidValue) noexcept + : CBNode(cb, kNodeLabelData), + _id(id) {} + + //! Destroy the `CBLabelData` instance (NEVER CALLED). + ASMJIT_INLINE ~CBLabelData() noexcept {} + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + //! Get the label id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + //! Get the label as `Label` operand. + ASMJIT_INLINE Label getLabel() const noexcept { return Label(_id); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _id; +}; + +// ============================================================================ +// [asmjit::CBConstPool] +// ============================================================================ + +class CBConstPool : public CBLabel { +public: + ASMJIT_NONCOPYABLE(CBConstPool) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBConstPool` instance. + ASMJIT_INLINE CBConstPool(CodeBuilder* cb, uint32_t id = kInvalidValue) noexcept + : CBLabel(cb, id), + _constPool(&cb->_cbBaseZone) { _type = kNodeConstPool; } + + //! Destroy the `CBConstPool` instance (NEVER CALLED). + ASMJIT_INLINE ~CBConstPool() noexcept {} + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ConstPool& getConstPool() noexcept { return _constPool; } + ASMJIT_INLINE const ConstPool& getConstPool() const noexcept { return _constPool; } + + //! Get whether the constant-pool is empty. + ASMJIT_INLINE bool isEmpty() const noexcept { return _constPool.isEmpty(); } + //! Get the size of the constant-pool in bytes. + ASMJIT_INLINE size_t getSize() const noexcept { return _constPool.getSize(); } + //! Get minimum alignment. + ASMJIT_INLINE size_t getAlignment() const noexcept { return _constPool.getAlignment(); } + + //! See \ref ConstPool::add(). + ASMJIT_INLINE Error add(const void* data, size_t size, size_t& dstOffset) noexcept { + return _constPool.add(data, size, dstOffset); + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + ConstPool _constPool; +}; + +// ============================================================================ +// [asmjit::CBComment] +// ============================================================================ + +//! Comment (CodeBuilder). +class CBComment : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBComment) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBComment` instance. + ASMJIT_INLINE CBComment(CodeBuilder* cb, const char* comment) noexcept : CBNode(cb, kNodeComment) { + orFlags(kFlagIsRemovable | kFlagIsInformative); + _inlineComment = comment; + } + + //! Destroy the `CBComment` instance (NEVER CALLED). + ASMJIT_INLINE ~CBComment() noexcept {} +}; + +// ============================================================================ +// [asmjit::CBSentinel] +// ============================================================================ + +//! Sentinel (CodeBuilder). +//! +//! Sentinel is a marker that is completely ignored by the code builder. It's +//! used to remember a position in a code as it never gets removed by any pass. +class CBSentinel : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBSentinel) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBSentinel` instance. + ASMJIT_INLINE CBSentinel(CodeBuilder* cb) noexcept : CBNode(cb, kNodeSentinel) {} + //! Destroy the `CBSentinel` instance (NEVER CALLED). + ASMJIT_INLINE ~CBSentinel() noexcept {} +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_BUILDER +#endif // _ASMJIT_BASE_CODEBUILDER_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/codecompiler.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codecompiler.cpp new file mode 100644 index 00000000..fb5de60e --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codecompiler.cpp @@ -0,0 +1,571 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Guard] +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_COMPILER) + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/codecompiler.h" +#include "../base/cpuinfo.h" +#include "../base/logging.h" +#include "../base/regalloc_p.h" +#include "../base/utils.h" +#include + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [Constants] +// ============================================================================ + +static const char noName[1] = { '\0' }; + +// ============================================================================ +// [asmjit::CCFuncCall - Arg / Ret] +// ============================================================================ + +bool CCFuncCall::_setArg(uint32_t i, const Operand_& op) noexcept { + if ((i & ~kFuncArgHi) >= _funcDetail.getArgCount()) + return false; + + _args[i] = op; + return true; +} + +bool CCFuncCall::_setRet(uint32_t i, const Operand_& op) noexcept { + if (i >= 2) + return false; + + _ret[i] = op; + return true; +} + +// ============================================================================ +// [asmjit::CodeCompiler - Construction / Destruction] +// ============================================================================ + +CodeCompiler::CodeCompiler() noexcept + : CodeBuilder(), + _func(nullptr), + _vRegZone(4096 - Zone::kZoneOverhead), + _vRegArray(&_cbHeap), + _localConstPool(nullptr), + _globalConstPool(nullptr) { + + _type = kTypeCompiler; +} +CodeCompiler::~CodeCompiler() noexcept {} + +// ============================================================================ +// [asmjit::CodeCompiler - Events] +// ============================================================================ + +Error CodeCompiler::onAttach(CodeHolder* code) noexcept { + return Base::onAttach(code); +} + +Error CodeCompiler::onDetach(CodeHolder* code) noexcept { + _func = nullptr; + + _localConstPool = nullptr; + _globalConstPool = nullptr; + + _vRegArray.reset(&_cbHeap); + _vRegZone.reset(false); + + return Base::onDetach(code); +} + +// ============================================================================ +// [asmjit::CodeCompiler - Node-Factory] +// ============================================================================ + +CCHint* CodeCompiler::newHintNode(Reg& r, uint32_t hint, uint32_t value) noexcept { + if (!r.isVirtReg()) return nullptr; + + VirtReg* vr = getVirtReg(r); + return newNodeT(vr, hint, value); +} + +// ============================================================================ +// [asmjit::CodeCompiler - Func] +// ============================================================================ + +CCFunc* CodeCompiler::newFunc(const FuncSignature& sign) noexcept { + Error err; + + CCFunc* func = newNodeT(); + if (!func) goto _NoMemory; + + err = registerLabelNode(func); + if (ASMJIT_UNLIKELY(err)) { + // TODO: Calls setLastError, maybe rethink noexcept? + setLastError(err); + return nullptr; + } + + // Create helper nodes. + func->_end = newNodeT(); + func->_exitNode = newLabelNode(); + if (!func->_exitNode || !func->_end) goto _NoMemory; + + // Function prototype. + err = func->getDetail().init(sign); + if (err != kErrorOk) { + setLastError(err); + return nullptr; + } + + // Override the natural stack alignment of the calling convention to what's + // specified by CodeInfo. + func->_funcDetail._callConv.setNaturalStackAlignment(_codeInfo.getStackAlignment()); + + // Allocate space for function arguments. + func->_args = nullptr; + if (func->getArgCount() != 0) { + func->_args = _cbHeap.allocT(func->getArgCount() * sizeof(VirtReg*)); + if (!func->_args) goto _NoMemory; + + ::memset(func->_args, 0, func->getArgCount() * sizeof(VirtReg*)); + } + + return func; + +_NoMemory: + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; +} + +CCFunc* CodeCompiler::addFunc(CCFunc* func) { + ASMJIT_ASSERT(_func == nullptr); + _func = func; + + addNode(func); // Function node. + CBNode* cursor = getCursor(); // {CURSOR}. + addNode(func->getExitNode()); // Function exit label. + addNode(func->getEnd()); // Function end marker. + + _setCursor(cursor); + return func; +} + +CCFunc* CodeCompiler::addFunc(const FuncSignature& sign) { + CCFunc* func = newFunc(sign); + + if (!func) { + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; + } + + return addFunc(func); +} + +CBSentinel* CodeCompiler::endFunc() { + CCFunc* func = getFunc(); + if (!func) { + // TODO: + return nullptr; + } + + // Add the local constant pool at the end of the function (if exist). + setCursor(func->getExitNode()); + + if (_localConstPool) { + addNode(_localConstPool); + _localConstPool = nullptr; + } + + // Mark as finished. + func->_isFinished = true; + _func = nullptr; + + setCursor(func->getEnd()); + return func->getEnd(); +} + +// ============================================================================ +// [asmjit::CodeCompiler - Ret] +// ============================================================================ + +CCFuncRet* CodeCompiler::newRet(const Operand_& o0, const Operand_& o1) noexcept { + CCFuncRet* node = newNodeT(o0, o1); + if (!node) { + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; + } + return node; +} + +CCFuncRet* CodeCompiler::addRet(const Operand_& o0, const Operand_& o1) noexcept { + CCFuncRet* node = newRet(o0, o1); + if (!node) return nullptr; + return static_cast(addNode(node)); +} + +// ============================================================================ +// [asmjit::CodeCompiler - Call] +// ============================================================================ + +CCFuncCall* CodeCompiler::newCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept { + Error err; + uint32_t nArgs; + + CCFuncCall* node = _cbHeap.allocT(sizeof(CCFuncCall) + sizeof(Operand)); + Operand* opArray = reinterpret_cast(reinterpret_cast(node) + sizeof(CCFuncCall)); + + if (ASMJIT_UNLIKELY(!node)) + goto _NoMemory; + + opArray[0].copyFrom(o0); + new (node) CCFuncCall(this, instId, 0, opArray, 1); + + if ((err = node->getDetail().init(sign)) != kErrorOk) { + setLastError(err); + return nullptr; + } + + // If there are no arguments skip the allocation. + if ((nArgs = sign.getArgCount()) == 0) + return node; + + node->_args = static_cast(_cbHeap.alloc(nArgs * sizeof(Operand))); + if (!node->_args) goto _NoMemory; + + ::memset(node->_args, 0, nArgs * sizeof(Operand)); + return node; + +_NoMemory: + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; +} + +CCFuncCall* CodeCompiler::addCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept { + CCFuncCall* node = newCall(instId, o0, sign); + if (!node) return nullptr; + return static_cast(addNode(node)); +} + +// ============================================================================ +// [asmjit::CodeCompiler - Vars] +// ============================================================================ + +Error CodeCompiler::setArg(uint32_t argIndex, const Reg& r) { + CCFunc* func = getFunc(); + + if (!func) + return setLastError(DebugUtils::errored(kErrorInvalidState)); + + if (!isVirtRegValid(r)) + return setLastError(DebugUtils::errored(kErrorInvalidVirtId)); + + VirtReg* vr = getVirtReg(r); + func->setArg(argIndex, vr); + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeCompiler - Hint] +// ============================================================================ + +Error CodeCompiler::_hint(Reg& r, uint32_t hint, uint32_t value) { + if (!r.isVirtReg()) return kErrorOk; + + CCHint* node = newHintNode(r, hint, value); + if (!node) return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeCompiler - Vars] +// ============================================================================ + +VirtReg* CodeCompiler::newVirtReg(uint32_t typeId, uint32_t signature, const char* name) noexcept { + size_t index = _vRegArray.getLength(); + if (ASMJIT_UNLIKELY(index > Operand::kPackedIdCount)) + return nullptr; + + VirtReg* vreg; + if (_vRegArray.willGrow(1) != kErrorOk || !(vreg = _vRegZone.allocZeroedT())) + return nullptr; + + vreg->_id = Operand::packId(static_cast(index)); + vreg->_regInfo.signature = signature; + vreg->_name = noName; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (name && name[0] != '\0') + vreg->_name = static_cast(_cbDataZone.dup(name, ::strlen(name), true)); +#endif // !ASMJIT_DISABLE_LOGGING + + vreg->_size = TypeId::sizeOf(typeId); + vreg->_typeId = typeId; + vreg->_alignment = static_cast(Utils::iMin(vreg->_size, 64)); + vreg->_priority = 10; + + // The following are only used by `RAPass`. + vreg->_raId = kInvalidValue; + vreg->_state = VirtReg::kStateNone; + vreg->_physId = kInvalidReg; + + _vRegArray.appendUnsafe(vreg); + return vreg; +} + +Error CodeCompiler::_newReg(Reg& out, uint32_t typeId, const char* name) { + RegInfo regInfo; + + Error err = ArchUtils::typeIdToRegInfo(getArchType(), typeId, regInfo); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + + VirtReg* vReg = newVirtReg(typeId, regInfo.signature, name); + if (ASMJIT_UNLIKELY(!vReg)) { + out.reset(); + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + + out._initReg(regInfo.signature, vReg->getId()); + return kErrorOk; +} + +Error CodeCompiler::_newReg(Reg& out, uint32_t typeId, const char* nameFmt, va_list ap) { + StringBuilderTmp<256> sb; + sb.appendFormatVA(nameFmt, ap); + return _newReg(out, typeId, sb.getData()); +} + +Error CodeCompiler::_newReg(Reg& out, const Reg& ref, const char* name) { + RegInfo regInfo; + uint32_t typeId; + + if (isVirtRegValid(ref)) { + VirtReg* vRef = getVirtReg(ref); + typeId = vRef->getTypeId(); + + // NOTE: It's possible to cast one register type to another if it's the + // same register kind. However, VirtReg always contains the TypeId that + // was used to create the register. This means that in some cases we may + // end up having different size of `ref` and `vRef`. In such case we + // adjust the TypeId to match the `ref` register type instead of the + // original register type, which should be the expected behavior. + uint32_t typeSize = TypeId::sizeOf(typeId); + uint32_t refSize = ref.getSize(); + + if (typeSize != refSize) { + if (TypeId::isInt(typeId)) { + // GP register - change TypeId to match `ref`, but keep sign of `vRef`. + switch (refSize) { + case 1: typeId = TypeId::kI8 | (typeId & 1); break; + case 2: typeId = TypeId::kI16 | (typeId & 1); break; + case 4: typeId = TypeId::kI32 | (typeId & 1); break; + case 8: typeId = TypeId::kI64 | (typeId & 1); break; + default: typeId = TypeId::kVoid; break; + } + } + else if (TypeId::isMmx(typeId)) { + // MMX register - always use 64-bit. + typeId = TypeId::kMmx64; + } + else if (TypeId::isMask(typeId)) { + // Mask register - change TypeId to match `ref` size. + switch (refSize) { + case 1: typeId = TypeId::kMask8; break; + case 2: typeId = TypeId::kMask16; break; + case 4: typeId = TypeId::kMask32; break; + case 8: typeId = TypeId::kMask64; break; + default: typeId = TypeId::kVoid; break; + } + } + else { + // VEC register - change TypeId to match `ref` size, keep vector metadata. + uint32_t elementTypeId = TypeId::elementOf(typeId); + + switch (refSize) { + case 16: typeId = TypeId::_kVec128Start + (elementTypeId - TypeId::kI8); break; + case 32: typeId = TypeId::_kVec256Start + (elementTypeId - TypeId::kI8); break; + case 64: typeId = TypeId::_kVec512Start + (elementTypeId - TypeId::kI8); break; + default: typeId = TypeId::kVoid; break; + } + } + + if (typeId == TypeId::kVoid) + return setLastError(DebugUtils::errored(kErrorInvalidState)); + } + } + else { + typeId = ref.getRegType(); + } + + Error err = ArchUtils::typeIdToRegInfo(getArchType(), typeId, regInfo); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + + VirtReg* vReg = newVirtReg(typeId, regInfo.signature, name); + if (ASMJIT_UNLIKELY(!vReg)) { + out.reset(); + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + + out._initReg(regInfo.signature, vReg->getId()); + return kErrorOk; +} + +Error CodeCompiler::_newReg(Reg& out, const Reg& ref, const char* nameFmt, va_list ap) { + StringBuilderTmp<256> sb; + sb.appendFormatVA(nameFmt, ap); + return _newReg(out, ref, sb.getData()); +} + +Error CodeCompiler::_newStack(Mem& out, uint32_t size, uint32_t alignment, const char* name) { + if (size == 0) + return setLastError(DebugUtils::errored(kErrorInvalidArgument)); + + if (alignment == 0) alignment = 1; + if (!Utils::isPowerOf2(alignment)) + return setLastError(DebugUtils::errored(kErrorInvalidArgument)); + + if (alignment > 64) alignment = 64; + + VirtReg* vReg = newVirtReg(0, 0, name); + if (ASMJIT_UNLIKELY(!vReg)) { + out.reset(); + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + + vReg->_size = size; + vReg->_isStack = true; + vReg->_alignment = static_cast(alignment); + + // Set the memory operand to GPD/GPQ and its id to VirtReg. + out = Mem(Init, _nativeGpReg.getRegType(), vReg->getId(), Reg::kRegNone, kInvalidValue, 0, 0, Mem::kFlagRegHome); + return kErrorOk; +} + +Error CodeCompiler::_newConst(Mem& out, uint32_t scope, const void* data, size_t size) { + CBConstPool** pPool; + if (scope == kConstScopeLocal) + pPool = &_localConstPool; + else if (scope == kConstScopeGlobal) + pPool = &_globalConstPool; + else + return setLastError(DebugUtils::errored(kErrorInvalidArgument)); + + if (!*pPool && !(*pPool = newConstPool())) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + CBConstPool* pool = *pPool; + size_t off; + + Error err = pool->add(data, size, off); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + + out = Mem(Init, + Label::kLabelTag, // Base type. + pool->getId(), // Base id. + 0, // Index type. + kInvalidValue, // Index id. + static_cast(off), // Offset. + static_cast(size), // Size. + 0); // Flags. + return kErrorOk; +} + +Error CodeCompiler::alloc(Reg& reg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintAlloc, kInvalidValue); +} + +Error CodeCompiler::alloc(Reg& reg, uint32_t physId) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintAlloc, physId); +} + +Error CodeCompiler::alloc(Reg& reg, const Reg& physReg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintAlloc, physReg.getId()); +} + +Error CodeCompiler::save(Reg& reg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintSave, kInvalidValue); +} + +Error CodeCompiler::spill(Reg& reg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintSpill, kInvalidValue); +} + +Error CodeCompiler::unuse(Reg& reg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintUnuse, kInvalidValue); +} + +uint32_t CodeCompiler::getPriority(Reg& reg) const { + if (!reg.isVirtReg()) return 0; + return getVirtRegById(reg.getId())->getPriority(); +} + +void CodeCompiler::setPriority(Reg& reg, uint32_t priority) { + if (!reg.isVirtReg()) return; + if (priority > 255) priority = 255; + + VirtReg* vreg = getVirtRegById(reg.getId()); + if (vreg) vreg->_priority = static_cast(priority); +} + +bool CodeCompiler::getSaveOnUnuse(Reg& reg) const { + if (!reg.isVirtReg()) return false; + + VirtReg* vreg = getVirtRegById(reg.getId()); + return static_cast(vreg->_saveOnUnuse); +} + +void CodeCompiler::setSaveOnUnuse(Reg& reg, bool value) { + if (!reg.isVirtReg()) return; + + VirtReg* vreg = getVirtRegById(reg.getId()); + if (!vreg) return; + + vreg->_saveOnUnuse = value; +} + +void CodeCompiler::rename(Reg& reg, const char* fmt, ...) { + if (!reg.isVirtReg()) return; + + VirtReg* vreg = getVirtRegById(reg.getId()); + if (!vreg) return; + + vreg->_name = noName; + if (fmt && fmt[0] != '\0') { + char buf[64]; + + va_list ap; + va_start(ap, fmt); + + vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap); + buf[ASMJIT_ARRAY_SIZE(buf) - 1] = '\0'; + + vreg->_name = static_cast(_cbDataZone.dup(buf, ::strlen(buf), true)); + va_end(ap); + } +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_COMPILER diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/codecompiler.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codecompiler.h new file mode 100644 index 00000000..865d90c8 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codecompiler.h @@ -0,0 +1,740 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CODECOMPILER_H +#define _ASMJIT_BASE_CODECOMPILER_H + +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_COMPILER) + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/codebuilder.h" +#include "../base/constpool.h" +#include "../base/func.h" +#include "../base/operand.h" +#include "../base/utils.h" +#include "../base/zone.h" +#include "../base/zonecontainers.h" +#include "../base/zoneheap.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +struct VirtReg; +struct TiedReg; +struct RAState; +struct RACell; + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::ConstScope] +// ============================================================================ + +//! Scope of the constant. +ASMJIT_ENUM(ConstScope) { + //! Local constant, always embedded right after the current function. + kConstScopeLocal = 0, + //! Global constant, embedded at the end of the currently compiled code. + kConstScopeGlobal = 1 +}; + +// ============================================================================ +// [asmjit::VirtReg] +// ============================================================================ + +//! Virtual register data (CodeCompiler). +struct VirtReg { + //! A state of a virtual register (used during register allocation). + ASMJIT_ENUM(State) { + kStateNone = 0, //!< Not allocated, not used. + kStateReg = 1, //!< Allocated in register. + kStateMem = 2 //!< Allocated in memory or spilled. + }; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the virtual-register id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + //! Get virtual-register's name. + ASMJIT_INLINE const char* getName() const noexcept { return _name; } + + //! Get a physical register type. + ASMJIT_INLINE uint32_t getRegType() const noexcept { return _regInfo.regType; } + //! Get a physical register kind. + ASMJIT_INLINE uint32_t getRegKind() const noexcept { return _regInfo.regKind; } + //! Get a physical register size. + ASMJIT_INLINE uint32_t getRegSize() const noexcept { return _regInfo.size; } + //! Get a register signature of this virtual register. + ASMJIT_INLINE uint32_t getSignature() const noexcept { return _regInfo.signature; } + + //! Get a register's type-id, see \ref TypeId. + ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _typeId; } + + //! Get virtual-register's size. + ASMJIT_INLINE uint32_t getSize() const noexcept { return _size; } + //! Get virtual-register's alignment. + ASMJIT_INLINE uint32_t getAlignment() const noexcept { return _alignment; } + + //! Get the virtual-register priority, used by compiler to decide which variable to spill. + ASMJIT_INLINE uint32_t getPriority() const noexcept { return _priority; } + //! Set the virtual-register priority. + ASMJIT_INLINE void setPriority(uint32_t priority) noexcept { + ASMJIT_ASSERT(priority <= 0xFF); + _priority = static_cast(priority); + } + + //! Get variable state, only used by `RAPass`. + ASMJIT_INLINE uint32_t getState() const noexcept { return _state; } + //! Set variable state, only used by `RAPass`. + ASMJIT_INLINE void setState(uint32_t state) { + ASMJIT_ASSERT(state <= 0xFF); + _state = static_cast(state); + } + + //! Get register index. + ASMJIT_INLINE uint32_t getPhysId() const noexcept { return _physId; } + //! Set register index. + ASMJIT_INLINE void setPhysId(uint32_t physId) { + ASMJIT_ASSERT(physId <= kInvalidReg); + _physId = static_cast(physId); + } + //! Reset register index. + ASMJIT_INLINE void resetPhysId() { + _physId = static_cast(kInvalidReg); + } + + //! Get home registers mask. + ASMJIT_INLINE uint32_t getHomeMask() const { return _homeMask; } + //! Add a home register index to the home registers mask. + ASMJIT_INLINE void addHomeId(uint32_t physId) { _homeMask |= Utils::mask(physId); } + + ASMJIT_INLINE bool isFixed() const noexcept { return static_cast(_isFixed); } + + //! Get whether the VirtReg is only memory allocated on the stack. + ASMJIT_INLINE bool isStack() const noexcept { return static_cast(_isStack); } + + //! Get whether to save variable when it's unused (spill). + ASMJIT_INLINE bool saveOnUnuse() const noexcept { return static_cast(_saveOnUnuse); } + + //! Get whether the variable was changed. + ASMJIT_INLINE bool isModified() const noexcept { return static_cast(_modified); } + //! Set whether the variable was changed. + ASMJIT_INLINE void setModified(bool modified) noexcept { _modified = modified; } + + //! Get home memory offset. + ASMJIT_INLINE int32_t getMemOffset() const noexcept { return _memOffset; } + //! Set home memory offset. + ASMJIT_INLINE void setMemOffset(int32_t offset) noexcept { _memOffset = offset; } + + //! Get home memory cell. + ASMJIT_INLINE RACell* getMemCell() const noexcept { return _memCell; } + //! Set home memory cell. + ASMJIT_INLINE void setMemCell(RACell* cell) noexcept { _memCell = cell; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _id; //!< Virtual register id. + RegInfo _regInfo; //!< Physical register info & signature. + const char* _name; //!< Virtual name (user provided). + uint32_t _size; //!< Virtual size (can be smaller than `regInfo._size`). + uint8_t _typeId; //!< Type-id. + uint8_t _alignment; //!< Register's natural alignment (for spilling). + uint8_t _priority; //!< Allocation priority (hint for RAPass that can be ignored). + uint8_t _isFixed : 1; //!< True if this is a fixed register, never reallocated. + uint8_t _isStack : 1; //!< True if the virtual register is only used as a stack. + uint8_t _isMaterialized : 1; //!< Register is constant that is easily created by a single instruction. + uint8_t _saveOnUnuse : 1; //!< Save on unuse (at end of the variable scope). + + // ------------------------------------------------------------------------- + // The following members are used exclusively by RAPass. They are initialized + // when the VirtReg is created and then changed during RAPass. + // ------------------------------------------------------------------------- + + uint32_t _raId; //!< Register allocator work-id (used by RAPass). + int32_t _memOffset; //!< Home memory offset. + uint32_t _homeMask; //!< Mask of all registers variable has been allocated to. + + uint8_t _state; //!< Variable state (connected with actual `RAState)`. + uint8_t _physId; //!< Actual register index (only used by `RAPass)`, during translate. + uint8_t _modified; //!< Whether variable was changed (connected with actual `RAState)`. + + RACell* _memCell; //!< Home memory cell, used by `RAPass` (initially nullptr). + + //! Temporary link to TiedReg* used by the `RAPass` used in + //! various phases, but always set back to nullptr when finished. + //! + //! This temporary data is designed to be used by algorithms that need to + //! store some data into variables themselves during compilation. But it's + //! expected that after variable is compiled & translated the data is set + //! back to zero/null. Initial value is nullptr. + TiedReg* _tied; +}; + +// ============================================================================ +// [asmjit::CCHint] +// ============================================================================ + +//! Hint for register allocator (CodeCompiler). +class CCHint : public CBNode { +public: + ASMJIT_NONCOPYABLE(CCHint) + + //! Hint type. + ASMJIT_ENUM(Hint) { + //! Alloc to physical reg. + kHintAlloc = 0, + //! Spill to memory. + kHintSpill = 1, + //! Save if modified. + kHintSave = 2, + //! Save if modified and mark it as unused. + kHintSaveAndUnuse = 3, + //! Mark as unused. + kHintUnuse = 4 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCHint` instance. + ASMJIT_INLINE CCHint(CodeBuilder* cb, VirtReg* vreg, uint32_t hint, uint32_t value) noexcept : CBNode(cb, kNodeHint) { + orFlags(kFlagIsRemovable | kFlagIsInformative); + _vreg = vreg; + _hint = hint; + _value = value; + } + + //! Destroy the `CCHint` instance (NEVER CALLED). + ASMJIT_INLINE ~CCHint() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get variable. + ASMJIT_INLINE VirtReg* getVReg() const noexcept { return _vreg; } + + //! Get hint it, see \ref Hint. + ASMJIT_INLINE uint32_t getHint() const noexcept { return _hint; } + //! Set hint it, see \ref Hint. + ASMJIT_INLINE void setHint(uint32_t hint) noexcept { _hint = hint; } + + //! Get hint value. + ASMJIT_INLINE uint32_t getValue() const noexcept { return _value; } + //! Set hint value. + ASMJIT_INLINE void setValue(uint32_t value) noexcept { _value = value; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Variable. + VirtReg* _vreg; + //! Hint id. + uint32_t _hint; + //! Value. + uint32_t _value; +}; + +// ============================================================================ +// [asmjit::CCFunc] +// ============================================================================ + +//! Function entry (CodeCompiler). +class CCFunc : public CBLabel { +public: + ASMJIT_NONCOPYABLE(CCFunc) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCFunc` instance. + //! + //! Always use `CodeCompiler::addFunc()` to create \ref CCFunc. + ASMJIT_INLINE CCFunc(CodeBuilder* cb) noexcept + : CBLabel(cb), + _exitNode(nullptr), + _funcDetail(), + _frameInfo(), + _end(nullptr), + _args(nullptr), + _isFinished(false) { + + _type = kNodeFunc; + } + + //! Destroy the `CCFunc` instance (NEVER CALLED). + ASMJIT_INLINE ~CCFunc() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get function exit `CBLabel`. + ASMJIT_INLINE CBLabel* getExitNode() const noexcept { return _exitNode; } + //! Get function exit label. + ASMJIT_INLINE Label getExitLabel() const noexcept { return _exitNode->getLabel(); } + + //! Get the function end sentinel. + ASMJIT_INLINE CBSentinel* getEnd() const noexcept { return _end; } + + //! Get function declaration. + ASMJIT_INLINE FuncDetail& getDetail() noexcept { return _funcDetail; } + //! Get function declaration. + ASMJIT_INLINE const FuncDetail& getDetail() const noexcept { return _funcDetail; } + + //! Get function declaration. + ASMJIT_INLINE FuncFrameInfo& getFrameInfo() noexcept { return _frameInfo; } + //! Get function declaration. + ASMJIT_INLINE const FuncFrameInfo& getFrameInfo() const noexcept { return _frameInfo; } + + //! Get arguments count. + ASMJIT_INLINE uint32_t getArgCount() const noexcept { return _funcDetail.getArgCount(); } + //! Get returns count. + ASMJIT_INLINE uint32_t getRetCount() const noexcept { return _funcDetail.getRetCount(); } + + //! Get arguments list. + ASMJIT_INLINE VirtReg** getArgs() const noexcept { return _args; } + + //! Get argument at `i`. + ASMJIT_INLINE VirtReg* getArg(uint32_t i) const noexcept { + ASMJIT_ASSERT(i < getArgCount()); + return _args[i]; + } + + //! Set argument at `i`. + ASMJIT_INLINE void setArg(uint32_t i, VirtReg* vreg) noexcept { + ASMJIT_ASSERT(i < getArgCount()); + _args[i] = vreg; + } + + //! Reset argument at `i`. + ASMJIT_INLINE void resetArg(uint32_t i) noexcept { + ASMJIT_ASSERT(i < getArgCount()); + _args[i] = nullptr; + } + + ASMJIT_INLINE uint32_t getAttributes() const noexcept { return _frameInfo.getAttributes(); } + ASMJIT_INLINE void addAttributes(uint32_t attrs) noexcept { _frameInfo.addAttributes(attrs); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + FuncDetail _funcDetail; //!< Function detail. + FuncFrameInfo _frameInfo; //!< Function frame information. + + CBLabel* _exitNode; //!< Function exit. + CBSentinel* _end; //!< Function end. + + VirtReg** _args; //!< Arguments array as `VirtReg`. + + //! Function was finished by `Compiler::endFunc()`. + uint8_t _isFinished; +}; + +// ============================================================================ +// [asmjit::CCFuncRet] +// ============================================================================ + +//! Function return (CodeCompiler). +class CCFuncRet : public CBNode { +public: + ASMJIT_NONCOPYABLE(CCFuncRet) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCFuncRet` instance. + ASMJIT_INLINE CCFuncRet(CodeBuilder* cb, const Operand_& o0, const Operand_& o1) noexcept : CBNode(cb, kNodeFuncExit) { + orFlags(kFlagIsRet); + _ret[0].copyFrom(o0); + _ret[1].copyFrom(o1); + } + + //! Destroy the `CCFuncRet` instance (NEVER CALLED). + ASMJIT_INLINE ~CCFuncRet() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the first return operand. + ASMJIT_INLINE Operand& getFirst() noexcept { return static_cast(_ret[0]); } + //! \overload + ASMJIT_INLINE const Operand& getFirst() const noexcept { return static_cast(_ret[0]); } + + //! Get the second return operand. + ASMJIT_INLINE Operand& getSecond() noexcept { return static_cast(_ret[1]); } + //! \overload + ASMJIT_INLINE const Operand& getSecond() const noexcept { return static_cast(_ret[1]); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Return operands. + Operand_ _ret[2]; +}; + +// ============================================================================ +// [asmjit::CCFuncCall] +// ============================================================================ + +//! Function call (CodeCompiler). +class CCFuncCall : public CBInst { +public: + ASMJIT_NONCOPYABLE(CCFuncCall) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCFuncCall` instance. + ASMJIT_INLINE CCFuncCall(CodeBuilder* cb, uint32_t instId, uint32_t options, Operand* opArray, uint32_t opCount) noexcept + : CBInst(cb, instId, options, opArray, opCount), + _funcDetail(), + _args(nullptr) { + + _type = kNodeFuncCall; + _ret[0].reset(); + _ret[1].reset(); + orFlags(kFlagIsRemovable); + } + + //! Destroy the `CCFuncCall` instance (NEVER CALLED). + ASMJIT_INLINE ~CCFuncCall() noexcept {} + + // -------------------------------------------------------------------------- + // [Signature] + // -------------------------------------------------------------------------- + + //! Set function signature. + ASMJIT_INLINE Error setSignature(const FuncSignature& sign) noexcept { + return _funcDetail.init(sign); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get function declaration. + ASMJIT_INLINE FuncDetail& getDetail() noexcept { return _funcDetail; } + //! Get function declaration. + ASMJIT_INLINE const FuncDetail& getDetail() const noexcept { return _funcDetail; } + + //! Get target operand. + ASMJIT_INLINE Operand& getTarget() noexcept { return static_cast(_opArray[0]); } + //! \overload + ASMJIT_INLINE const Operand& getTarget() const noexcept { return static_cast(_opArray[0]); } + + //! Get return at `i`. + ASMJIT_INLINE Operand& getRet(uint32_t i = 0) noexcept { + ASMJIT_ASSERT(i < 2); + return static_cast(_ret[i]); + } + //! \overload + ASMJIT_INLINE const Operand& getRet(uint32_t i = 0) const noexcept { + ASMJIT_ASSERT(i < 2); + return static_cast(_ret[i]); + } + + //! Get argument at `i`. + ASMJIT_INLINE Operand& getArg(uint32_t i) noexcept { + ASMJIT_ASSERT(i < kFuncArgCountLoHi); + return static_cast(_args[i]); + } + //! \overload + ASMJIT_INLINE const Operand& getArg(uint32_t i) const noexcept { + ASMJIT_ASSERT(i < kFuncArgCountLoHi); + return static_cast(_args[i]); + } + + //! Set argument at `i` to `op`. + ASMJIT_API bool _setArg(uint32_t i, const Operand_& op) noexcept; + //! Set return at `i` to `op`. + ASMJIT_API bool _setRet(uint32_t i, const Operand_& op) noexcept; + + //! Set argument at `i` to `reg`. + ASMJIT_INLINE bool setArg(uint32_t i, const Reg& reg) noexcept { return _setArg(i, reg); } + //! Set argument at `i` to `imm`. + ASMJIT_INLINE bool setArg(uint32_t i, const Imm& imm) noexcept { return _setArg(i, imm); } + + //! Set return at `i` to `var`. + ASMJIT_INLINE bool setRet(uint32_t i, const Reg& reg) noexcept { return _setRet(i, reg); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + FuncDetail _funcDetail; //!< Function detail. + Operand_ _ret[2]; //!< Return. + Operand_* _args; //!< Arguments. +}; + +// ============================================================================ +// [asmjit::CCPushArg] +// ============================================================================ + +//! Push argument before a function call (CodeCompiler). +class CCPushArg : public CBNode { +public: + ASMJIT_NONCOPYABLE(CCPushArg) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCPushArg` instance. + ASMJIT_INLINE CCPushArg(CodeBuilder* cb, CCFuncCall* call, VirtReg* src, VirtReg* cvt) noexcept + : CBNode(cb, kNodePushArg), + _call(call), + _src(src), + _cvt(cvt), + _args(0) { + orFlags(kFlagIsRemovable); + } + + //! Destroy the `CCPushArg` instance. + ASMJIT_INLINE ~CCPushArg() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the associated function-call. + ASMJIT_INLINE CCFuncCall* getCall() const noexcept { return _call; } + //! Get source variable. + ASMJIT_INLINE VirtReg* getSrcReg() const noexcept { return _src; } + //! Get conversion variable. + ASMJIT_INLINE VirtReg* getCvtReg() const noexcept { return _cvt; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CCFuncCall* _call; //!< Associated `CCFuncCall`. + VirtReg* _src; //!< Source variable. + VirtReg* _cvt; //!< Temporary variable used for conversion (or null). + uint32_t _args; //!< Affected arguments bit-array. +}; + +// ============================================================================ +// [asmjit::CodeCompiler] +// ============================================================================ + +//! Code emitter that uses virtual registers and performs register allocation. +//! +//! Compiler is a high-level code-generation tool that provides register +//! allocation and automatic handling of function calling conventions. It was +//! primarily designed for merging multiple parts of code into a function +//! without worrying about registers and function calling conventions. +//! +//! CodeCompiler can be used, with a minimum effort, to handle 32-bit and 64-bit +//! code at the same time. +//! +//! CodeCompiler is based on CodeBuilder and contains all the features it +//! provides. It means that the code it stores can be modified (removed, added, +//! injected) and analyzed. When the code is finalized the compiler can emit +//! the code into an Assembler to translate the abstract representation into a +//! machine code. +class ASMJIT_VIRTAPI CodeCompiler : public CodeBuilder { +public: + ASMJIT_NONCOPYABLE(CodeCompiler) + typedef CodeBuilder Base; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CodeCompiler` instance. + ASMJIT_API CodeCompiler() noexcept; + //! Destroy the `CodeCompiler` instance. + ASMJIT_API virtual ~CodeCompiler() noexcept; + + // -------------------------------------------------------------------------- + // [Events] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Error onAttach(CodeHolder* code) noexcept override; + ASMJIT_API virtual Error onDetach(CodeHolder* code) noexcept override; + + // -------------------------------------------------------------------------- + // [Node-Factory] + // -------------------------------------------------------------------------- + + //! \internal + //! + //! Create a new `CCHint`. + ASMJIT_API CCHint* newHintNode(Reg& reg, uint32_t hint, uint32_t value) noexcept; + + // -------------------------------------------------------------------------- + // [Func] + // -------------------------------------------------------------------------- + + //! Get the current function. + ASMJIT_INLINE CCFunc* getFunc() const noexcept { return _func; } + + //! Create a new `CCFunc`. + ASMJIT_API CCFunc* newFunc(const FuncSignature& sign) noexcept; + //! Add a function `node` to the stream. + ASMJIT_API CCFunc* addFunc(CCFunc* func); + //! Add a new function. + ASMJIT_API CCFunc* addFunc(const FuncSignature& sign); + //! Emit a sentinel that marks the end of the current function. + ASMJIT_API CBSentinel* endFunc(); + + // -------------------------------------------------------------------------- + // [Ret] + // -------------------------------------------------------------------------- + + //! Create a new `CCFuncRet`. + ASMJIT_API CCFuncRet* newRet(const Operand_& o0, const Operand_& o1) noexcept; + //! Add a new `CCFuncRet`. + ASMJIT_API CCFuncRet* addRet(const Operand_& o0, const Operand_& o1) noexcept; + + // -------------------------------------------------------------------------- + // [Call] + // -------------------------------------------------------------------------- + + //! Create a new `CCFuncCall`. + ASMJIT_API CCFuncCall* newCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept; + //! Add a new `CCFuncCall`. + ASMJIT_API CCFuncCall* addCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept; + + // -------------------------------------------------------------------------- + // [Args] + // -------------------------------------------------------------------------- + + //! Set a function argument at `argIndex` to `reg`. + ASMJIT_API Error setArg(uint32_t argIndex, const Reg& reg); + + // -------------------------------------------------------------------------- + // [Hint] + // -------------------------------------------------------------------------- + + //! Emit a new hint (purely informational node). + ASMJIT_API Error _hint(Reg& reg, uint32_t hint, uint32_t value); + + // -------------------------------------------------------------------------- + // [VirtReg / Stack] + // -------------------------------------------------------------------------- + + //! Create a new virtual register representing the given `vti` and `signature`. + //! + //! This function accepts either register type representing a machine-specific + //! register, like `X86Reg`, or RegTag representation, which represents + //! machine independent register, and from the machine-specific register + //! is deduced. + ASMJIT_API VirtReg* newVirtReg(uint32_t typeId, uint32_t signature, const char* name) noexcept; + + ASMJIT_API Error _newReg(Reg& out, uint32_t typeId, const char* name); + ASMJIT_API Error _newReg(Reg& out, uint32_t typeId, const char* nameFmt, va_list ap); + + ASMJIT_API Error _newReg(Reg& out, const Reg& ref, const char* name); + ASMJIT_API Error _newReg(Reg& out, const Reg& ref, const char* nameFmt, va_list ap); + + ASMJIT_API Error _newStack(Mem& out, uint32_t size, uint32_t alignment, const char* name); + ASMJIT_API Error _newConst(Mem& out, uint32_t scope, const void* data, size_t size); + + // -------------------------------------------------------------------------- + // [VirtReg] + // -------------------------------------------------------------------------- + + //! Get whether the virtual register `r` is valid. + ASMJIT_INLINE bool isVirtRegValid(const Reg& reg) const noexcept { + return isVirtRegValid(reg.getId()); + } + //! \overload + ASMJIT_INLINE bool isVirtRegValid(uint32_t id) const noexcept { + size_t index = Operand::unpackId(id); + return index < _vRegArray.getLength(); + } + + //! Get \ref VirtReg associated with the given `r`. + ASMJIT_INLINE VirtReg* getVirtReg(const Reg& reg) const noexcept { + return getVirtRegById(reg.getId()); + } + //! Get \ref VirtReg associated with the given `id`. + ASMJIT_INLINE VirtReg* getVirtRegById(uint32_t id) const noexcept { + ASMJIT_ASSERT(id != kInvalidValue); + size_t index = Operand::unpackId(id); + + ASMJIT_ASSERT(index < _vRegArray.getLength()); + return _vRegArray[index]; + } + + //! Get an array of all virtual registers managed by CodeCompiler. + ASMJIT_INLINE const ZoneVector& getVirtRegArray() const noexcept { return _vRegArray; } + + //! Alloc a virtual register `reg`. + ASMJIT_API Error alloc(Reg& reg); + //! Alloc a virtual register `reg` using `physId` as a register id. + ASMJIT_API Error alloc(Reg& reg, uint32_t physId); + //! Alloc a virtual register `reg` using `ref` as a register operand. + ASMJIT_API Error alloc(Reg& reg, const Reg& ref); + //! Spill a virtual register `reg`. + ASMJIT_API Error spill(Reg& reg); + //! Save a virtual register `reg` if the status is `modified` at this point. + ASMJIT_API Error save(Reg& reg); + //! Unuse a virtual register `reg`. + ASMJIT_API Error unuse(Reg& reg); + + //! Get priority of a virtual register `reg`. + ASMJIT_API uint32_t getPriority(Reg& reg) const; + //! Set priority of variable `reg` to `priority`. + ASMJIT_API void setPriority(Reg& reg, uint32_t priority); + + //! Get save-on-unuse `reg` property. + ASMJIT_API bool getSaveOnUnuse(Reg& reg) const; + //! Set save-on-unuse `reg` property to `value`. + ASMJIT_API void setSaveOnUnuse(Reg& reg, bool value); + + //! Rename variable `reg` to `name`. + //! + //! NOTE: Only new name will appear in the logger. + ASMJIT_API void rename(Reg& reg, const char* fmt, ...); + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CCFunc* _func; //!< Current function. + + Zone _vRegZone; //!< Allocates \ref VirtReg objects. + ZoneVector _vRegArray; //!< Stores array of \ref VirtReg pointers. + + CBConstPool* _localConstPool; //!< Local constant pool, flushed at the end of each function. + CBConstPool* _globalConstPool; //!< Global constant pool, flushed at the end of the compilation. +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_COMPILER +#endif // _ASMJIT_BASE_CODECOMPILER_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/codeemitter.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codeemitter.cpp new file mode 100644 index 00000000..8969b8b7 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codeemitter.cpp @@ -0,0 +1,254 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/utils.h" +#include "../base/vmem.h" +#include + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::CodeEmitter - Construction / Destruction] +// ============================================================================ + +CodeEmitter::CodeEmitter(uint32_t type) noexcept + : _codeInfo(), + _code(nullptr), + _nextEmitter(nullptr), + _type(static_cast(type)), + _destroyed(false), + _finalized(false), + _reserved(false), + _lastError(kErrorNotInitialized), + _privateData(0), + _globalHints(0), + _globalOptions(kOptionMaybeFailureCase), + _options(0), + _inlineComment(nullptr), + _op4(), + _op5(), + _opExtra(), + _none(), + _nativeGpReg(), + _nativeGpArray(nullptr) {} + +CodeEmitter::~CodeEmitter() noexcept { + if (_code) { + _destroyed = true; + _code->detach(this); + } +} + +// ============================================================================ +// [asmjit::CodeEmitter - Events] +// ============================================================================ + +Error CodeEmitter::onAttach(CodeHolder* code) noexcept { + _codeInfo = code->getCodeInfo(); + _lastError = kErrorOk; + + _globalHints = code->getGlobalHints(); + _globalOptions = code->getGlobalOptions(); + + return kErrorOk; +} + +Error CodeEmitter::onDetach(CodeHolder* code) noexcept { + _codeInfo.reset(); + _finalized = false; + _lastError = kErrorNotInitialized; + + _privateData = 0; + _globalHints = 0; + _globalOptions = kOptionMaybeFailureCase; + + _options = 0; + _inlineComment = nullptr; + _op4.reset(); + _op5.reset(); + _opExtra.reset(); + _nativeGpReg.reset(); + _nativeGpArray = nullptr; + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeEmitter - Finalize] +// ============================================================================ + +Label CodeEmitter::getLabelByName(const char* name, size_t nameLength, uint32_t parentId) noexcept { + return Label(_code ? _code->getLabelIdByName(name, nameLength, parentId) + : static_cast(kInvalidValue)); +} + +// ============================================================================ +// [asmjit::CodeEmitter - Finalize] +// ============================================================================ + +Error CodeEmitter::finalize() { + // Finalization does nothing by default, overridden by `CodeBuilder`. + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeEmitter - Error Handling] +// ============================================================================ + +Error CodeEmitter::setLastError(Error error, const char* message) { + // This is fatal, CodeEmitter can't set error without being attached to `CodeHolder`. + ASMJIT_ASSERT(_code != nullptr); + + // Special case used to reset the last error. + if (error == kErrorOk) { + _lastError = kErrorOk; + _globalOptions &= ~kOptionMaybeFailureCase; + return kErrorOk; + } + + if (!message) + message = DebugUtils::errorAsString(error); + + // Logging is skipped if the error is handled by `ErrorHandler`. + ErrorHandler* handler = _code->_errorHandler; + ASMJIT_TLOG("[ERROR] 0x%08u: %s%s\n", + static_cast(error), message, handler ? "" : " (ErrorHandler not attached)"); + + if (handler && handler->handleError(error, message, this)) + return error; + + // The handler->handleError() function may throw an exception or longjmp() + // to terminate the execution of `setLastError()`. This is the reason why + // we have delayed changing the `_error` member until now. + _lastError = error; + + return error; +} + +// ============================================================================ +// [asmjit::CodeEmitter - Helpers] +// ============================================================================ + +bool CodeEmitter::isLabelValid(uint32_t id) const noexcept { + size_t index = Operand::unpackId(id); + return _code && index < _code->_labels.getLength(); +} + +Error CodeEmitter::commentf(const char* fmt, ...) { + Error err = _lastError; + if (err) return err; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) { + va_list ap; + va_start(ap, fmt); + Error err = _code->_logger->logv(fmt, ap); + va_end(ap); + } +#else + ASMJIT_UNUSED(fmt); +#endif + + return err; +} + +Error CodeEmitter::commentv(const char* fmt, va_list ap) { + Error err = _lastError; + if (err) return err; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) + err = _code->_logger->logv(fmt, ap); +#else + ASMJIT_UNUSED(fmt); + ASMJIT_UNUSED(ap); +#endif + + return err; +} + +// ============================================================================ +// [asmjit::CodeEmitter - Emit] +// ============================================================================ + +#define OP const Operand_& +#define NO _none + +Error CodeEmitter::emit(uint32_t instId) { return _emit(instId, NO, NO, NO, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0) { return _emit(instId, o0, NO, NO, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1) { return _emit(instId, o0, o1, NO, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2) { return _emit(instId, o0, o1, o2, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3) { return _emit(instId, o0, o1, o2, o3); } + +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4) { + _op4 = o4; + + if (!o4.isNone()) _options |= kOptionOp4; + return _emit(instId, o0, o1, o2, o3); +} + +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, OP o5) { + _op4 = o4; + _op5 = o5; + + if (!o4.isNone()) _options |= kOptionOp4; + if (!o5.isNone()) _options |= kOptionOp5; + return _emit(instId, o0, o1, o2, o3); +} + +Error CodeEmitter::emit(uint32_t instId, int o0) { return _emit(instId, Imm(o0), NO, NO, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, int o1) { return _emit(instId, o0, Imm(o1), NO, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, int o2) { return _emit(instId, o0, o1, Imm(o2), NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, int o3) { return _emit(instId, o0, o1, o2, Imm(o3)); } + +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, int o4) { + _options |= kOptionOp4; + _op4 = Imm(o4); + return _emit(instId, o0, o1, o2, o3); +} + +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, int o5) { + _op4 = o4; + _op5 = Imm(o5); + + _options |= kOptionOp4 | kOptionOp5; + return _emit(instId, o0, o1, o2, o3); +} + +Error CodeEmitter::emit(uint32_t instId, int64_t o0) { return _emit(instId, Imm(o0), NO, NO, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, int64_t o1) { return _emit(instId, o0, Imm(o1), NO, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, int64_t o2) { return _emit(instId, o0, o1, Imm(o2), NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, int64_t o3) { return _emit(instId, o0, o1, o2, Imm(o3)); } + +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, int64_t o4) { + _options |= kOptionOp4; + _op4 = Imm(o4); + return _emit(instId, o0, o1, o2, o3); +} + +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, int64_t o5) { + _op4 = o4; + _op5 = Imm(o5); + + _options |= kOptionOp4 | kOptionOp5; + return _emit(instId, o0, o1, o2, o3); +} + +#undef NO +#undef OP + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/codeemitter.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codeemitter.h new file mode 100644 index 00000000..a57d7a10 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codeemitter.h @@ -0,0 +1,494 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CODEEMITTER_H +#define _ASMJIT_BASE_CODEEMITTER_H + +// [Dependencies] +#include "../base/arch.h" +#include "../base/codeholder.h" +#include "../base/operand.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class ConstPool; + +// ============================================================================ +// [asmjit::CodeEmitter] +// ============================================================================ + +//! Provides a base foundation to emit code - specialized by \ref Assembler and +//! \ref CodeBuilder. +class ASMJIT_VIRTAPI CodeEmitter { +public: + //! CodeEmitter type. + ASMJIT_ENUM(Type) { + kTypeNone = 0, + kTypeAssembler = 1, + kTypeBuilder = 2, + kTypeCompiler = 3, + kTypeCount = 4 + }; + + //! CodeEmitter hints - global settings that affect machine-code generation. + ASMJIT_ENUM(Hints) { + //! Emit optimized code-alignment sequences. + //! + //! Default `true`. + //! + //! X86/X64 Specific + //! ---------------- + //! + //! Default align sequence used by X86/X64 architecture is one-byte (0x90) + //! opcode that is often shown by disassemblers as nop. However there are + //! more optimized align sequences for 2-11 bytes that may execute faster. + //! If this feature is enabled AsmJit will generate specialized sequences + //! for alignment between 2 to 11 bytes. + kHintOptimizedAlign = 0x00000001U, + + //! Emit jump-prediction hints. + //! + //! Default `false`. + //! + //! X86/X64 Specific + //! ---------------- + //! + //! Jump prediction is usually based on the direction of the jump. If the + //! jump is backward it is usually predicted as taken; and if the jump is + //! forward it is usually predicted as not-taken. The reason is that loops + //! generally use backward jumps and conditions usually use forward jumps. + //! However this behavior can be overridden by using instruction prefixes. + //! If this option is enabled these hints will be emitted. + //! + //! This feature is disabled by default, because the only processor that + //! used to take into consideration prediction hints was P4. Newer processors + //! implement heuristics for branch prediction that ignores any static hints. + kHintPredictedJumps = 0x00000002U + }; + + //! CodeEmitter options that are merged with instruction options. + ASMJIT_ENUM(Options) { + //! Reserved, used to check for errors in `Assembler::_emit()`. In addition, + //! if an emitter is in error state it will have `kOptionMaybeFailureCase` + //! set + kOptionMaybeFailureCase = 0x00000001U, + + //! Perform a strict validation before the instruction is emitted. + kOptionStrictValidation = 0x00000002U, + + //! Logging is enabled and `CodeHolder::getLogger()` should return a valid + //! \ref Logger pointer. + kOptionLoggingEnabled = 0x00000004U, + + //! Mask of all internal options that are not used to represent instruction + //! options, but are used to instrument Assembler and CodeBuilder. These + //! options are internal and should not be used outside of AsmJit itself. + //! + //! NOTE: Reserved options should never appear in `CBInst` options. + kOptionReservedMask = 0x00000007U, + + //! Instruction has `_op4` (5th operand, indexed from zero). + kOptionOp4 = 0x0000008U, + //! Instruction has `_op5` (6th operand, indexed from zero). + kOptionOp5 = 0x0000010U, + //! Instruction has `_opExtra` operand (mask-op {k} operand when using AVX-512). + kOptionOpExtra = 0x00000020U, + + //! Prevents following a jump during compilation (CodeCompiler). + kOptionUnfollow = 0x00000040U, + + //! Overwrite the destination operand (CodeCompiler). + //! + //! Hint that is important for register liveness analysis. It tells the + //! compiler that the destination operand will be overwritten now or by + //! adjacent instructions. CodeCompiler knows when a register is completely + //! overwritten by a single instruction, for example you don't have to + //! mark "movaps" or "pxor x, x", however, if a pair of instructions is + //! used and the first of them doesn't completely overwrite the content + //! of the destination, CodeCompiler fails to mark that register as dead. + //! + //! X86/X64 Specific + //! ---------------- + //! + //! - All instructions that always overwrite at least the size of the + //! register the virtual-register uses , for example "mov", "movq", + //! "movaps" don't need the overwrite option to be used - conversion, + //! shuffle, and other miscellaneous instructions included. + //! + //! - All instructions that clear the destination register if all operands + //! are the same, for example "xor x, x", "pcmpeqb x x", etc... + //! + //! - Consecutive instructions that partially overwrite the variable until + //! there is no old content require the `overwrite()` to be used. Some + //! examples (not always the best use cases thought): + //! + //! - `movlps xmm0, ?` followed by `movhps xmm0, ?` and vice versa + //! - `movlpd xmm0, ?` followed by `movhpd xmm0, ?` and vice versa + //! - `mov al, ?` followed by `and ax, 0xFF` + //! - `mov al, ?` followed by `mov ah, al` + //! - `pinsrq xmm0, ?, 0` followed by `pinsrq xmm0, ?, 1` + //! + //! - If allocated variable is used temporarily for scalar operations. For + //! example if you allocate a full vector like `X86Compiler::newXmm()` + //! and then use that vector for scalar operations you should use + //! `overwrite()` directive: + //! + //! - `sqrtss x, y` - only LO element of `x` is changed, if you don't use + //! HI elements, use `X86Compiler.overwrite().sqrtss(x, y)`. + kOptionOverwrite = 0x00000080U + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_API CodeEmitter(uint32_t type) noexcept; + ASMJIT_API virtual ~CodeEmitter() noexcept; + + // -------------------------------------------------------------------------- + // [Events] + // -------------------------------------------------------------------------- + + //! Called after the \ref CodeEmitter was attached to the \ref CodeHolder. + virtual Error onAttach(CodeHolder* code) noexcept = 0; + //! Called after the \ref CodeEmitter was detached from the \ref CodeHolder. + virtual Error onDetach(CodeHolder* code) noexcept = 0; + + // -------------------------------------------------------------------------- + // [Code-Generation] + // -------------------------------------------------------------------------- + + //! Emit instruction. + virtual Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) = 0; + + //! Create a new label. + virtual Label newLabel() = 0; + //! Create a new named label. + virtual Label newNamedLabel(const char* name, size_t nameLength = kInvalidIndex, uint32_t type = Label::kTypeGlobal, uint32_t parentId = kInvalidValue) = 0; + + //! Get a label by name. + //! + //! Returns invalid Label in case that the name is invalid or label was not found. + //! + //! NOTE: This function doesn't trigger ErrorHandler in case the name is + //! invalid or no such label exist. You must always check the validity of the + //! \ref Label returned. + ASMJIT_API Label getLabelByName(const char* name, size_t nameLength = kInvalidIndex, uint32_t parentId = kInvalidValue) noexcept; + + //! Bind the `label` to the current position of the current section. + //! + //! NOTE: Attempt to bind the same label multiple times will return an error. + virtual Error bind(const Label& label) = 0; + + //! Align to the `alignment` specified. + //! + //! The sequence that is used to fill the gap between the aligned location + //! and the current location depends on the align `mode`, see \ref AlignMode. + virtual Error align(uint32_t mode, uint32_t alignment) = 0; + + //! Embed raw data into the code-buffer. + virtual Error embed(const void* data, uint32_t size) = 0; + + //! Embed absolute label address as data (4 or 8 bytes). + virtual Error embedLabel(const Label& label) = 0; + + //! Embed a constant pool into the code-buffer in the following steps: + //! 1. Align by using kAlignData to the minimum `pool` alignment. + //! 2. Bind `label` so it's bound to an aligned location. + //! 3. Emit constant pool data. + virtual Error embedConstPool(const Label& label, const ConstPool& pool) = 0; + + //! Emit a comment string `s` with an optional `len` parameter. + virtual Error comment(const char* s, size_t len = kInvalidIndex) = 0; + + // -------------------------------------------------------------------------- + // [Code-Generation Status] + // -------------------------------------------------------------------------- + + //! Get if the CodeEmitter is initialized (i.e. attached to a \ref CodeHolder). + ASMJIT_INLINE bool isInitialized() const noexcept { return _code != nullptr; } + + ASMJIT_API virtual Error finalize(); + + // -------------------------------------------------------------------------- + // [Code Information] + // -------------------------------------------------------------------------- + + //! Get information about the code, see \ref CodeInfo. + ASMJIT_INLINE const CodeInfo& getCodeInfo() const noexcept { return _codeInfo; } + //! Get \ref CodeHolder this CodeEmitter is attached to. + ASMJIT_INLINE CodeHolder* getCode() const noexcept { return _code; } + + //! Get information about the architecture, see \ref ArchInfo. + ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _codeInfo.getArchInfo(); } + + //! Get if the target architecture is 32-bit. + ASMJIT_INLINE bool is32Bit() const noexcept { return getArchInfo().is32Bit(); } + //! Get if the target architecture is 64-bit. + ASMJIT_INLINE bool is64Bit() const noexcept { return getArchInfo().is64Bit(); } + + //! Get the target architecture type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return getArchInfo().getType(); } + //! Get the target architecture sub-type. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return getArchInfo().getSubType(); } + //! Get the target architecture's GP register size (4 or 8 bytes). + ASMJIT_INLINE uint32_t getGpSize() const noexcept { return getArchInfo().getGpSize(); } + //! Get the number of target GP registers. + ASMJIT_INLINE uint32_t getGpCount() const noexcept { return getArchInfo().getGpCount(); } + + // -------------------------------------------------------------------------- + // [Code-Emitter Type] + // -------------------------------------------------------------------------- + + //! Get the type of this CodeEmitter, see \ref Type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + + ASMJIT_INLINE bool isAssembler() const noexcept { return _type == kTypeAssembler; } + ASMJIT_INLINE bool isCodeBuilder() const noexcept { return _type == kTypeBuilder; } + ASMJIT_INLINE bool isCodeCompiler() const noexcept { return _type == kTypeCompiler; } + + // -------------------------------------------------------------------------- + // [Global Information] + // -------------------------------------------------------------------------- + + //! Get global hints. + ASMJIT_INLINE uint32_t getGlobalHints() const noexcept { return _globalHints; } + + //! Get global options. + //! + //! Global options are merged with instruction options before the instruction + //! is encoded. These options have some bits reserved that are used for error + //! checking, logging, and strict validation. Other options are globals that + //! affect each instruction, for example if VEX3 is set globally, it will all + //! instructions, even those that don't have such option set. + ASMJIT_INLINE uint32_t getGlobalOptions() const noexcept { return _globalOptions; } + + // -------------------------------------------------------------------------- + // [Error Handling] + // -------------------------------------------------------------------------- + + //! Get if the object is in error state. + //! + //! Error state means that it does not consume anything unless the error + //! state is reset by calling `resetLastError()`. Use `getLastError()` to + //! get the last error that put the object into the error state. + ASMJIT_INLINE bool isInErrorState() const noexcept { return _lastError != kErrorOk; } + + //! Get the last error code. + ASMJIT_INLINE Error getLastError() const noexcept { return _lastError; } + //! Set the last error code and propagate it through the error handler. + ASMJIT_API Error setLastError(Error error, const char* message = nullptr); + //! Clear the last error code and return `kErrorOk`. + ASMJIT_INLINE Error resetLastError() noexcept { return setLastError(kErrorOk); } + + // -------------------------------------------------------------------------- + // [Accessors That Affect the Next Instruction] + // -------------------------------------------------------------------------- + + //! Get options of the next instruction. + ASMJIT_INLINE uint32_t getOptions() const noexcept { return _options; } + //! Set options of the next instruction. + ASMJIT_INLINE void setOptions(uint32_t options) noexcept { _options = options; } + //! Add options of the next instruction. + ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _options |= options; } + //! Reset options of the next instruction. + ASMJIT_INLINE void resetOptions() noexcept { _options = 0; } + + //! Get if the 5th operand (indexed from zero) of the next instruction is used. + ASMJIT_INLINE bool hasOp4() const noexcept { return (_options & kOptionOp4) != 0; } + //! Get if the 6th operand (indexed from zero) of the next instruction is used. + ASMJIT_INLINE bool hasOp5() const noexcept { return (_options & kOptionOp5) != 0; } + //! Get if the op-mask operand of the next instruction is used. + ASMJIT_INLINE bool hasOpExtra() const noexcept { return (_options & kOptionOpExtra) != 0; } + + ASMJIT_INLINE const Operand& getOp4() const noexcept { return static_cast(_op4); } + ASMJIT_INLINE const Operand& getOp5() const noexcept { return static_cast(_op5); } + ASMJIT_INLINE const Operand& getOpExtra() const noexcept { return static_cast(_opExtra); } + + ASMJIT_INLINE void setOp4(const Operand_& op4) noexcept { _options |= kOptionOp4; _op4 = op4; } + ASMJIT_INLINE void setOp5(const Operand_& op5) noexcept { _options |= kOptionOp5; _op5 = op5; } + ASMJIT_INLINE void setOpExtra(const Operand_& opExtra) noexcept { _options |= kOptionOpExtra; _opExtra = opExtra; } + + //! Get annotation of the next instruction. + ASMJIT_INLINE const char* getInlineComment() const noexcept { return _inlineComment; } + //! Set annotation of the next instruction. + //! + //! NOTE: This string is set back to null by `_emit()`, but until that it has + //! to remain valid as `CodeEmitter` is not required to make a copy of it (and + //! it would be slow to do that for each instruction). + ASMJIT_INLINE void setInlineComment(const char* s) noexcept { _inlineComment = s; } + //! Reset annotation of the next instruction to null. + ASMJIT_INLINE void resetInlineComment() noexcept { _inlineComment = nullptr; } + + // -------------------------------------------------------------------------- + // [Helpers] + // -------------------------------------------------------------------------- + + //! Get if the `label` is valid (i.e. registered). + ASMJIT_INLINE bool isLabelValid(const Label& label) const noexcept { + return isLabelValid(label.getId()); + } + + //! Get if the label `id` is valid (i.e. registered). + ASMJIT_API bool isLabelValid(uint32_t id) const noexcept; + + //! Emit a formatted string `fmt`. + ASMJIT_API Error commentf(const char* fmt, ...); + //! Emit a formatted string `fmt` (va_list version). + ASMJIT_API Error commentv(const char* fmt, va_list ap); + + // -------------------------------------------------------------------------- + // [Emit] + // -------------------------------------------------------------------------- + + // NOTE: These `emit()` helpers are designed to address a code-bloat generated + // by C++ compilers to call a function having many arguments. Each parameter to + // `_emit()` requires code to pass it, which means that if we default to 4 + // operand parameters in `_emit()` and instId the C++ compiler would have to + // generate a virtual function call having 5 parameters, which is quite a lot. + // Since by default asm instructions have 2 to 3 operands it's better to + // introduce helpers that pass those and fill all the remaining with `_none`. + + //! Emit an instruction. + ASMJIT_API Error emit(uint32_t instId); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5); + + //! Emit an instruction that has a 32-bit signed immediate operand. + ASMJIT_API Error emit(uint32_t instId, int o0); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, int o1); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, int o2); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, int o3); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, int o4); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, int o5); + + //! Emit an instruction that has a 64-bit signed immediate operand. + ASMJIT_API Error emit(uint32_t instId, int64_t o0); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, int64_t o1); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, int64_t o2); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, int64_t o3); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, int64_t o4); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, int64_t o5); + + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, unsigned int o0) { + return emit(instId, static_cast(o0)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, unsigned int o1) { + return emit(instId, o0, static_cast(o1)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, unsigned int o2) { + return emit(instId, o0, o1, static_cast(o2)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, unsigned int o3) { + return emit(instId, o0, o1, o2, static_cast(o3)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, unsigned int o4) { + return emit(instId, o0, o1, o2, o3, static_cast(o4)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, unsigned int o5) { + return emit(instId, o0, o1, o2, o3, o4, static_cast(o5)); + } + + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, uint64_t o0) { + return emit(instId, static_cast(o0)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, uint64_t o1) { + return emit(instId, o0, static_cast(o1)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, uint64_t o2) { + return emit(instId, o0, o1, static_cast(o2)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, uint64_t o3) { + return emit(instId, o0, o1, o2, static_cast(o3)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, uint64_t o4) { + return emit(instId, o0, o1, o2, o3, static_cast(o4)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, uint64_t o5) { + return emit(instId, o0, o1, o2, o3, o4, static_cast(o5)); + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CodeInfo _codeInfo; //!< Basic information about the code (matches CodeHolder::_codeInfo). + CodeHolder* _code; //!< CodeHolder the CodeEmitter is attached to. + CodeEmitter* _nextEmitter; //!< Linked list of `CodeEmitter`s attached to the same \ref CodeHolder. + + uint8_t _type; //!< See CodeEmitter::Type. + uint8_t _destroyed; //!< Set by ~CodeEmitter() before calling `_code->detach()`. + uint8_t _finalized; //!< True if the CodeEmitter is finalized (CodeBuilder & CodeCompiler). + uint8_t _reserved; //!< \internal + Error _lastError; //!< Last error code. + + uint32_t _privateData; //!< Internal private data used freely by any CodeEmitter. + uint32_t _globalHints; //!< Global hints, always in sync with CodeHolder. + uint32_t _globalOptions; //!< Global options, combined with `_options` before used by each instruction. + + uint32_t _options; //!< Used to pass instruction options (affects the next instruction). + const char* _inlineComment; //!< Inline comment of the next instruction (affects the next instruction). + Operand_ _op4; //!< 5th operand data (indexed from zero) (affects the next instruction). + Operand_ _op5; //!< 6th operand data (indexed from zero) (affects the next instruction). + Operand_ _opExtra; //!< Extra operand (op-mask {k} on AVX-512) (affects the next instruction). + + Operand_ _none; //!< Used to pass unused operands to `_emit()` instead of passing null. + Reg _nativeGpReg; //!< Native GP register with zero id. + const Reg* _nativeGpArray; //!< Array of native registers indexed from zero. +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_CODEEMITTER_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/codeholder.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codeholder.cpp new file mode 100644 index 00000000..ee49519e --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codeholder.cpp @@ -0,0 +1,696 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/utils.h" +#include "../base/vmem.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::ErrorHandler] +// ============================================================================ + +ErrorHandler::ErrorHandler() noexcept {} +ErrorHandler::~ErrorHandler() noexcept {} + +// ============================================================================ +// [asmjit::CodeHolder - Utilities] +// ============================================================================ + +static void CodeHolder_setGlobalOption(CodeHolder* self, uint32_t clear, uint32_t add) noexcept { + // Modify global options of `CodeHolder` itself. + self->_globalOptions = (self->_globalOptions & ~clear) | add; + + // Modify all global options of all `CodeEmitter`s attached. + CodeEmitter* emitter = self->_emitters; + while (emitter) { + emitter->_globalOptions = (emitter->_globalOptions & ~clear) | add; + emitter = emitter->_nextEmitter; + } +} + +static void CodeHolder_resetInternal(CodeHolder* self, bool releaseMemory) noexcept { + // Detach all `CodeEmitter`s. + while (self->_emitters) + self->detach(self->_emitters); + + // Reset everything into its construction state. + self->_codeInfo.reset(); + self->_globalHints = 0; + self->_globalOptions = 0; + self->_logger = nullptr; + self->_errorHandler = nullptr; + + self->_unresolvedLabelsCount = 0; + self->_trampolinesSize = 0; + + // Reset all sections. + size_t numSections = self->_sections.getLength(); + for (size_t i = 0; i < numSections; i++) { + SectionEntry* section = self->_sections[i]; + if (section->_buffer.hasData() && !section->_buffer.isExternal()) + ASMJIT_FREE(section->_buffer._data); + section->_buffer._data = nullptr; + section->_buffer._capacity = 0; + } + + // Reset zone allocator and all containers using it. + ZoneHeap* heap = &self->_baseHeap; + + self->_namedLabels.reset(heap); + self->_relocations.reset(heap); + self->_labels.reset(heap); + self->_sections.reset(heap); + + heap->reset(&self->_baseZone); + self->_baseZone.reset(releaseMemory); +} + +// ============================================================================ +// [asmjit::CodeHolder - Construction / Destruction] +// ============================================================================ + +CodeHolder::CodeHolder() noexcept + : _codeInfo(), + _globalHints(0), + _globalOptions(0), + _emitters(nullptr), + _cgAsm(nullptr), + _logger(nullptr), + _errorHandler(nullptr), + _trampolinesSize(0), + _baseZone(16384 - Zone::kZoneOverhead), + _dataZone(16384 - Zone::kZoneOverhead), + _baseHeap(&_baseZone), + _labels(&_baseHeap), + _sections(&_baseHeap), + _relocations(&_baseHeap) { +} + +CodeHolder::~CodeHolder() noexcept { + CodeHolder_resetInternal(this, true); +} + +// ============================================================================ +// [asmjit::CodeHolder - Init / Reset] +// ============================================================================ + +Error CodeHolder::init(const CodeInfo& info) noexcept { + // Cannot reinitialize if it's locked or there is one or more CodeEmitter + // attached. + if (isInitialized()) + return DebugUtils::errored(kErrorAlreadyInitialized); + + // If we are just initializing there should be no emitters attached). + ASMJIT_ASSERT(_emitters == nullptr); + + // Create the default section and insert it to the `_sections` array. + Error err = _sections.willGrow(1); + if (err == kErrorOk) { + SectionEntry* se = _baseZone.allocZeroedT(); + if (ASMJIT_LIKELY(se)) { + se->_flags = SectionEntry::kFlagExec | SectionEntry::kFlagConst; + se->_setDefaultName('.', 't', 'e', 'x', 't'); + _sections.appendUnsafe(se); + } + else { + err = DebugUtils::errored(kErrorNoHeapMemory); + } + } + + if (ASMJIT_UNLIKELY(err)) { + _baseZone.reset(false); + return err; + } + else { + _codeInfo = info; + return kErrorOk; + } +} + +void CodeHolder::reset(bool releaseMemory) noexcept { + CodeHolder_resetInternal(this, releaseMemory); +} + +// ============================================================================ +// [asmjit::CodeHolder - Attach / Detach] +// ============================================================================ + +Error CodeHolder::attach(CodeEmitter* emitter) noexcept { + // Catch a possible misuse of the API. + if (!emitter) + return DebugUtils::errored(kErrorInvalidArgument); + + uint32_t type = emitter->getType(); + if (type == CodeEmitter::kTypeNone || type >= CodeEmitter::kTypeCount) + return DebugUtils::errored(kErrorInvalidState); + + // This is suspicious, but don't fail if `emitter` matches. + if (emitter->_code != nullptr) { + if (emitter->_code == this) return kErrorOk; + return DebugUtils::errored(kErrorInvalidState); + } + + // Special case - attach `Assembler`. + CodeEmitter** pSlot = nullptr; + if (type == CodeEmitter::kTypeAssembler) { + if (_cgAsm) + return DebugUtils::errored(kErrorSlotOccupied); + pSlot = reinterpret_cast(&_cgAsm); + } + + Error err = emitter->onAttach(this); + if (err != kErrorOk) return err; + + // Add to a single-linked list of `CodeEmitter`s. + emitter->_nextEmitter = _emitters; + _emitters = emitter; + if (pSlot) *pSlot = emitter; + + // Establish the connection. + emitter->_code = this; + return kErrorOk; +} + +Error CodeHolder::detach(CodeEmitter* emitter) noexcept { + if (!emitter) + return DebugUtils::errored(kErrorInvalidArgument); + + if (emitter->_code != this) + return DebugUtils::errored(kErrorInvalidState); + + uint32_t type = emitter->getType(); + Error err = kErrorOk; + + // NOTE: We always detach if we were asked to, if error happens during + // `emitter->onDetach()` we just propagate it, but the CodeEmitter will + // be detached. + if (!emitter->_destroyed) + err = emitter->onDetach(this); + + // Special case - detach `Assembler`. + if (type == CodeEmitter::kTypeAssembler) _cgAsm = nullptr; + + // Remove from a single-linked list of `CodeEmitter`s. + CodeEmitter** pPrev = &_emitters; + for (;;) { + ASMJIT_ASSERT(*pPrev != nullptr); + CodeEmitter* cur = *pPrev; + + if (cur == emitter) { + *pPrev = emitter->_nextEmitter; + break; + } + + pPrev = &cur->_nextEmitter; + } + + emitter->_code = nullptr; + emitter->_nextEmitter = nullptr; + + return err; +} + +// ============================================================================ +// [asmjit::CodeHolder - Sync] +// ============================================================================ + +void CodeHolder::sync() noexcept { + if (_cgAsm) _cgAsm->sync(); +} + +// ============================================================================ +// [asmjit::CodeHolder - Result Information] +// ============================================================================ + +size_t CodeHolder::getCodeSize() const noexcept { + // Reflect all changes first. + const_cast(this)->sync(); + + // TODO: Support sections. + return _sections[0]->_buffer._length + getTrampolinesSize(); +} + +// ============================================================================ +// [asmjit::CodeHolder - Logging & Error Handling] +// ============================================================================ + +#if !defined(ASMJIT_DISABLE_LOGGING) +void CodeHolder::setLogger(Logger* logger) noexcept { + uint32_t opt = 0; + if (logger) opt = CodeEmitter::kOptionLoggingEnabled; + + _logger = logger; + CodeHolder_setGlobalOption(this, CodeEmitter::kOptionLoggingEnabled, opt); +} +#endif // !ASMJIT_DISABLE_LOGGING + +Error CodeHolder::setErrorHandler(ErrorHandler* handler) noexcept { + _errorHandler = handler; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeHolder - Sections] +// ============================================================================ + +static Error CodeHolder_reserveInternal(CodeHolder* self, CodeBuffer* cb, size_t n) noexcept { + uint8_t* oldData = cb->_data; + uint8_t* newData; + + if (oldData && !cb->isExternal()) + newData = static_cast(ASMJIT_REALLOC(oldData, n)); + else + newData = static_cast(ASMJIT_ALLOC(n)); + + if (ASMJIT_UNLIKELY(!newData)) + return DebugUtils::errored(kErrorNoHeapMemory); + + cb->_data = newData; + cb->_capacity = n; + + // Update the `Assembler` pointers if attached. Maybe we should introduce an + // event for this, but since only one Assembler can be attached at a time it + // should not matter how these pointers are updated. + Assembler* a = self->_cgAsm; + if (a && &a->_section->_buffer == cb) { + size_t offset = a->getOffset(); + + a->_bufferData = newData; + a->_bufferEnd = newData + n; + a->_bufferPtr = newData + offset; + } + + return kErrorOk; +} + +Error CodeHolder::growBuffer(CodeBuffer* cb, size_t n) noexcept { + // This is most likely called by `Assembler` so `sync()` shouldn't be needed, + // however, if this is called by the user and the currently attached Assembler + // did generate some code we could lose that, so sync now and make sure the + // section length is updated. + if (_cgAsm) _cgAsm->sync(); + + // Now the length of the section must be valid. + size_t length = cb->getLength(); + if (ASMJIT_UNLIKELY(n > IntTraits::maxValue() - length)) + return DebugUtils::errored(kErrorNoHeapMemory); + + // We can now check if growing the buffer is really necessary. It's unlikely + // that this function is called while there is still room for `n` bytes. + size_t capacity = cb->getCapacity(); + size_t required = cb->getLength() + n; + if (ASMJIT_UNLIKELY(required <= capacity)) return kErrorOk; + + if (cb->isFixedSize()) + return DebugUtils::errored(kErrorCodeTooLarge); + + if (capacity < 8096) + capacity = 8096; + else + capacity += kMemAllocOverhead; + + do { + size_t old = capacity; + if (capacity < kMemAllocGrowMax) + capacity *= 2; + else + capacity += kMemAllocGrowMax; + + if (capacity < kMemAllocGrowMax) + capacity *= 2; + else + capacity += kMemAllocGrowMax; + + // Overflow. + if (ASMJIT_UNLIKELY(old > capacity)) + return DebugUtils::errored(kErrorNoHeapMemory); + } while (capacity - kMemAllocOverhead < required); + + return CodeHolder_reserveInternal(this, cb, capacity - kMemAllocOverhead); +} + +Error CodeHolder::reserveBuffer(CodeBuffer* cb, size_t n) noexcept { + size_t capacity = cb->getCapacity(); + if (n <= capacity) return kErrorOk; + + if (cb->isFixedSize()) + return DebugUtils::errored(kErrorCodeTooLarge); + + // We must sync, as mentioned in `growBuffer()` as well. + if (_cgAsm) _cgAsm->sync(); + + return CodeHolder_reserveInternal(this, cb, n); +} + +// ============================================================================ +// [asmjit::CodeHolder - Labels & Symbols] +// ============================================================================ + +namespace { + +//! \internal +//! +//! Only used to lookup a label from `_namedLabels`. +class LabelByName { +public: + ASMJIT_INLINE LabelByName(const char* name, size_t nameLength, uint32_t hVal) noexcept + : name(name), + nameLength(static_cast(nameLength)) {} + + ASMJIT_INLINE bool matches(const LabelEntry* entry) const noexcept { + return static_cast(entry->getNameLength()) == nameLength && + ::memcmp(entry->getName(), name, nameLength) == 0; + } + + const char* name; + uint32_t nameLength; + uint32_t hVal; +}; + +// Returns a hash of `name` and fixes `nameLength` if it's `kInvalidIndex`. +static uint32_t CodeHolder_hashNameAndFixLen(const char* name, size_t& nameLength) noexcept { + uint32_t hVal = 0; + if (nameLength == kInvalidIndex) { + size_t i = 0; + for (;;) { + uint8_t c = static_cast(name[i]); + if (!c) break; + hVal = Utils::hashRound(hVal, c); + i++; + } + nameLength = i; + } + else { + for (size_t i = 0; i < nameLength; i++) { + uint8_t c = static_cast(name[i]); + if (ASMJIT_UNLIKELY(!c)) return DebugUtils::errored(kErrorInvalidLabelName); + hVal = Utils::hashRound(hVal, c); + } + } + return hVal; +} + +} // anonymous namespace + +LabelLink* CodeHolder::newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel) noexcept { + LabelLink* link = _baseHeap.allocT(); + if (ASMJIT_UNLIKELY(!link)) return nullptr; + + link->prev = le->_links; + le->_links = link; + + link->sectionId = sectionId; + link->relocId = RelocEntry::kInvalidId; + link->offset = offset; + link->rel = rel; + + _unresolvedLabelsCount++; + return link; +} + +Error CodeHolder::newLabelId(uint32_t& idOut) noexcept { + idOut = kInvalidValue; + + size_t index = _labels.getLength(); + if (ASMJIT_LIKELY(index > Label::kPackedIdCount)) + return DebugUtils::errored(kErrorLabelIndexOverflow); + + ASMJIT_PROPAGATE(_labels.willGrow(1)); + LabelEntry* le = _baseHeap.allocZeroedT(); + + if (ASMJIT_UNLIKELY(!le)) + return DebugUtils::errored(kErrorNoHeapMemory);; + + uint32_t id = Operand::packId(static_cast(index)); + le->_setId(id); + le->_parentId = kInvalidValue; + le->_sectionId = SectionEntry::kInvalidId; + le->_offset = 0; + + _labels.appendUnsafe(le); + idOut = id; + return kErrorOk; +} + +Error CodeHolder::newNamedLabelId(uint32_t& idOut, const char* name, size_t nameLength, uint32_t type, uint32_t parentId) noexcept { + idOut = kInvalidValue; + uint32_t hVal = CodeHolder_hashNameAndFixLen(name, nameLength); + + if (ASMJIT_UNLIKELY(nameLength == 0)) + return DebugUtils::errored(kErrorInvalidLabelName); + + if (ASMJIT_UNLIKELY(nameLength > Label::kMaxNameLength)) + return DebugUtils::errored(kErrorLabelNameTooLong); + + switch (type) { + case Label::kTypeLocal: + if (ASMJIT_UNLIKELY(Operand::unpackId(parentId) >= _labels.getLength())) + return DebugUtils::errored(kErrorInvalidParentLabel); + + hVal ^= parentId; + break; + + case Label::kTypeGlobal: + if (ASMJIT_UNLIKELY(parentId != kInvalidValue)) + return DebugUtils::errored(kErrorNonLocalLabelCantHaveParent); + + break; + + default: + return DebugUtils::errored(kErrorInvalidArgument); + } + + // Don't allow to insert duplicates. Local labels allow duplicates that have + // different id, this is already accomplished by having a different hashes + // between the same label names having different parent labels. + LabelEntry* le = _namedLabels.get(LabelByName(name, nameLength, hVal)); + if (ASMJIT_UNLIKELY(le)) + return DebugUtils::errored(kErrorLabelAlreadyDefined); + + Error err = kErrorOk; + size_t index = _labels.getLength(); + + if (ASMJIT_UNLIKELY(index > Label::kPackedIdCount)) + return DebugUtils::errored(kErrorLabelIndexOverflow); + + ASMJIT_PROPAGATE(_labels.willGrow(1)); + le = _baseHeap.allocZeroedT(); + + if (ASMJIT_UNLIKELY(!le)) + return DebugUtils::errored(kErrorNoHeapMemory); + + uint32_t id = Operand::packId(static_cast(index)); + le->_hVal = hVal; + le->_setId(id); + le->_type = static_cast(type); + le->_parentId = kInvalidValue; + le->_sectionId = SectionEntry::kInvalidId; + le->_offset = 0; + + if (le->_name.mustEmbed(nameLength)) { + le->_name.setEmbedded(name, nameLength); + } + else { + char* nameExternal = static_cast(_dataZone.dup(name, nameLength, true)); + if (ASMJIT_UNLIKELY(!nameExternal)) + return DebugUtils::errored(kErrorNoHeapMemory); + le->_name.setExternal(nameExternal, nameLength); + } + + _labels.appendUnsafe(le); + _namedLabels.put(le); + + idOut = id; + return err; +} + +uint32_t CodeHolder::getLabelIdByName(const char* name, size_t nameLength, uint32_t parentId) noexcept { + uint32_t hVal = CodeHolder_hashNameAndFixLen(name, nameLength); + if (ASMJIT_UNLIKELY(!nameLength)) return kInvalidValue; + + LabelEntry* le = _namedLabels.get(LabelByName(name, nameLength, hVal)); + return le ? le->getId() : static_cast(kInvalidValue); +} + +// ============================================================================ +// [asmjit::CodeEmitter - Relocations] +// ============================================================================ + +//! Encode MOD byte. +static ASMJIT_INLINE uint32_t x86EncodeMod(uint32_t m, uint32_t o, uint32_t rm) noexcept { + return (m << 6) | (o << 3) | rm; +} + +Error CodeHolder::newRelocEntry(RelocEntry** dst, uint32_t type, uint32_t size) noexcept { + ASMJIT_PROPAGATE(_relocations.willGrow(1)); + + size_t index = _relocations.getLength(); + if (ASMJIT_UNLIKELY(index > size_t(0xFFFFFFFFU))) + return DebugUtils::errored(kErrorRelocIndexOverflow); + + RelocEntry* re = _baseHeap.allocZeroedT(); + if (ASMJIT_UNLIKELY(!re)) + return DebugUtils::errored(kErrorNoHeapMemory); + + re->_id = static_cast(index); + re->_type = static_cast(type); + re->_size = static_cast(size); + re->_sourceSectionId = SectionEntry::kInvalidId; + re->_targetSectionId = SectionEntry::kInvalidId; + _relocations.appendUnsafe(re); + + *dst = re; + return kErrorOk; +} + +// TODO: Support multiple sections, this only relocates the first. +// TODO: This should go to Runtime as it's responsible for relocating the +// code, CodeHolder should just hold it. +size_t CodeHolder::relocate(void* _dst, uint64_t baseAddress) const noexcept { + SectionEntry* section = _sections[0]; + ASMJIT_ASSERT(section != nullptr); + + uint32_t archType = getArchType(); + uint8_t* dst = static_cast(_dst); + + if (baseAddress == kNoBaseAddress) + baseAddress = static_cast((uintptr_t)dst); + +#if !defined(ASMJIT_DISABLE_LOGGING) + Logger* logger = getLogger(); +#endif // ASMJIT_DISABLE_LOGGING + + size_t minCodeSize = section->getBuffer().getLength(); // Minimum code size. + size_t maxCodeSize = getCodeSize(); // Includes all possible trampolines. + + // We will copy the exact size of the generated code. Extra code for trampolines + // is generated on-the-fly by the relocator (this code doesn't exist at the moment). + ::memcpy(dst, section->_buffer._data, minCodeSize); + + // Trampoline offset from the beginning of dst/baseAddress. + size_t trampOffset = minCodeSize; + + // Relocate all recorded locations. + size_t numRelocs = _relocations.getLength(); + const RelocEntry* const* reArray = _relocations.getData(); + + for (size_t i = 0; i < numRelocs; i++) { + const RelocEntry* re = reArray[i]; + + // Possibly deleted or optimized out relocation entry. + if (re->getType() == RelocEntry::kTypeNone) + continue; + + uint64_t ptr = re->getData(); + size_t codeOffset = static_cast(re->getSourceOffset()); + + // Make sure that the `RelocEntry` is correct, we don't want to write + // out of bounds in `dst`. + if (ASMJIT_UNLIKELY(codeOffset + re->getSize() > maxCodeSize)) + return DebugUtils::errored(kErrorInvalidRelocEntry); + + // Whether to use trampoline, can be only used if relocation type is `kRelocTrampoline`. + bool useTrampoline = false; + + switch (re->getType()) { + case RelocEntry::kTypeAbsToAbs: { + break; + } + + case RelocEntry::kTypeRelToAbs: { + ptr += baseAddress; + break; + } + + case RelocEntry::kTypeAbsToRel: { + ptr -= baseAddress + re->getSourceOffset() + re->getSize(); + break; + } + + case RelocEntry::kTypeTrampoline: { + if (re->getSize() != 4) + return DebugUtils::errored(kErrorInvalidRelocEntry); + + ptr -= baseAddress + re->getSourceOffset() + re->getSize(); + if (!Utils::isInt32(static_cast(ptr))) { + ptr = (uint64_t)trampOffset - re->getSourceOffset() - re->getSize(); + useTrampoline = true; + } + break; + } + + default: + return DebugUtils::errored(kErrorInvalidRelocEntry); + } + + switch (re->getSize()) { + case 1: + Utils::writeU8(dst + codeOffset, static_cast(ptr & 0xFFU)); + break; + + case 4: + Utils::writeU32u(dst + codeOffset, static_cast(ptr & 0xFFFFFFFFU)); + break; + + case 8: + Utils::writeU64u(dst + codeOffset, ptr); + break; + + default: + return DebugUtils::errored(kErrorInvalidRelocEntry); + } + + // Handle the trampoline case. + if (useTrampoline) { + // Bytes that replace [REX, OPCODE] bytes. + uint32_t byte0 = 0xFF; + uint32_t byte1 = dst[codeOffset - 1]; + + if (byte1 == 0xE8) { + // Patch CALL/MOD byte to FF/2 (-> 0x15). + byte1 = x86EncodeMod(0, 2, 5); + } + else if (byte1 == 0xE9) { + // Patch JMP/MOD byte to FF/4 (-> 0x25). + byte1 = x86EncodeMod(0, 4, 5); + } + else { + return DebugUtils::errored(kErrorInvalidRelocEntry); + } + + // Patch `jmp/call` instruction. + ASMJIT_ASSERT(codeOffset >= 2); + dst[codeOffset - 2] = static_cast(byte0); + dst[codeOffset - 1] = static_cast(byte1); + + // Store absolute address and advance the trampoline pointer. + Utils::writeU64u(dst + trampOffset, re->getData()); + trampOffset += 8; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (logger) + logger->logf("[reloc] dq 0x%016llX ; Trampoline\n", re->getData()); +#endif // !ASMJIT_DISABLE_LOGGING + } + } + + // If there are no trampolines this is the same as `minCodeSize`. + return trampOffset; +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/codeholder.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codeholder.h new file mode 100644 index 00000000..b096660e --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/codeholder.h @@ -0,0 +1,745 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CODEHOLDER_H +#define _ASMJIT_BASE_CODEHOLDER_H + +// [Dependencies] +#include "../base/arch.h" +#include "../base/func.h" +#include "../base/logging.h" +#include "../base/operand.h" +#include "../base/simdtypes.h" +#include "../base/utils.h" +#include "../base/zone.h" +#include "../base/zonecontainers.h" +#include "../base/zoneheap.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class Assembler; +class CodeEmitter; +class CodeHolder; + +// ============================================================================ +// [asmjit::AlignMode] +// ============================================================================ + +//! Code/Data align-mode. +ASMJIT_ENUM(AlignMode) { + kAlignCode = 0, //!< Align executable code. + kAlignData = 1, //!< Align non-executable code. + kAlignZero = 2 //!< Align by a sequence of zeros. +}; + +// ============================================================================ +// [asmjit::ErrorHandler] +// ============================================================================ + +//! Error handler can be used to override the default behavior of error handling +//! available to all classes that inherit \ref CodeEmitter. See \ref handleError(). +class ASMJIT_VIRTAPI ErrorHandler { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `ErrorHandler` instance. + ASMJIT_API ErrorHandler() noexcept; + //! Destroy the `ErrorHandler` instance. + ASMJIT_API virtual ~ErrorHandler() noexcept; + + // -------------------------------------------------------------------------- + // [Handle Error] + // -------------------------------------------------------------------------- + + //! Error handler (abstract). + //! + //! Error handler is called after an error happened and before it's propagated + //! to the caller. There are multiple ways how the error handler can be used: + //! + //! 1. Returning `true` or `false` from `handleError()`. If `true` is returned + //! it means that the error was reported and AsmJit can continue execution. + //! The reported error still be propagated to the caller, but won't put the + //! CodeEmitter into an error state (it won't set last-error). However, + //! returning `false` means that the error cannot be handled - in such case + //! it stores the error, which can be then retrieved by using `getLastError()`. + //! Returning `false` is the default behavior when no error handler is present. + //! To put the assembler into a non-error state again a `resetLastError()` must + //! be called. + //! + //! 2. Throwing an exception. AsmJit doesn't use exceptions and is completely + //! exception-safe, but you can throw exception from your error handler if + //! this way is the preferred way of handling errors in your project. Throwing + //! an exception acts virtually as returning `true` as AsmJit won't be able + //! to store the error because the exception changes execution path. + //! + //! 3. Using plain old C's `setjmp()` and `longjmp()`. Asmjit always puts + //! `CodeEmitter` to a consistent state before calling the `handleError()` + //! so `longjmp()` can be used without any issues to cancel the code + //! generation if an error occurred. There is no difference between + //! exceptions and longjmp() from AsmJit's perspective. + virtual bool handleError(Error err, const char* message, CodeEmitter* origin) = 0; +}; + +// ============================================================================ +// [asmjit::CodeInfo] +// ============================================================================ + +//! Basic information about a code (or target). It describes its architecture, +//! code generation mode (or optimization level), and base address. +class CodeInfo { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CodeInfo() noexcept + : _archInfo(), + _stackAlignment(0), + _cdeclCallConv(CallConv::kIdNone), + _stdCallConv(CallConv::kIdNone), + _fastCallConv(CallConv::kIdNone), + _baseAddress(kNoBaseAddress) {} + ASMJIT_INLINE CodeInfo(const CodeInfo& other) noexcept { init(other); } + + explicit ASMJIT_INLINE CodeInfo(uint32_t archType, uint32_t archMode = 0, uint64_t baseAddress = kNoBaseAddress) noexcept + : _archInfo(archType, archMode), + _packedMiscInfo(0), + _baseAddress(baseAddress) {} + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isInitialized() const noexcept { + return _archInfo._type != ArchInfo::kTypeNone; + } + + ASMJIT_INLINE void init(const CodeInfo& other) noexcept { + _archInfo = other._archInfo; + _packedMiscInfo = other._packedMiscInfo; + _baseAddress = other._baseAddress; + } + + ASMJIT_INLINE void init(uint32_t archType, uint32_t archMode = 0, uint64_t baseAddress = kNoBaseAddress) noexcept { + _archInfo.init(archType, archMode); + _packedMiscInfo = 0; + _baseAddress = baseAddress; + } + + ASMJIT_INLINE void reset() noexcept { + _archInfo.reset(); + _stackAlignment = 0; + _cdeclCallConv = CallConv::kIdNone; + _stdCallConv = CallConv::kIdNone; + _fastCallConv = CallConv::kIdNone; + _baseAddress = kNoBaseAddress; + } + + // -------------------------------------------------------------------------- + // [Architecture Information] + // -------------------------------------------------------------------------- + + //! Get architecture information, see \ref ArchInfo. + ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _archInfo; } + + //! Get architecture type, see \ref ArchInfo::Type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return _archInfo.getType(); } + //! Get architecture sub-type, see \ref ArchInfo::SubType. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return _archInfo.getSubType(); } + //! Get a size of a GP register of the architecture the code is using. + ASMJIT_INLINE uint32_t getGpSize() const noexcept { return _archInfo.getGpSize(); } + //! Get number of GP registers available of the architecture the code is using. + ASMJIT_INLINE uint32_t getGpCount() const noexcept { return _archInfo.getGpCount(); } + + // -------------------------------------------------------------------------- + // [High-Level Information] + // -------------------------------------------------------------------------- + + //! Get a natural stack alignment that must be honored (or 0 if not known). + ASMJIT_INLINE uint32_t getStackAlignment() const noexcept { return _stackAlignment; } + //! Set a natural stack alignment that must be honored. + ASMJIT_INLINE void setStackAlignment(uint8_t sa) noexcept { _stackAlignment = static_cast(sa); } + + ASMJIT_INLINE uint32_t getCdeclCallConv() const noexcept { return _cdeclCallConv; } + ASMJIT_INLINE void setCdeclCallConv(uint32_t cc) noexcept { _cdeclCallConv = static_cast(cc); } + + ASMJIT_INLINE uint32_t getStdCallConv() const noexcept { return _stdCallConv; } + ASMJIT_INLINE void setStdCallConv(uint32_t cc) noexcept { _stdCallConv = static_cast(cc); } + + ASMJIT_INLINE uint32_t getFastCallConv() const noexcept { return _fastCallConv; } + ASMJIT_INLINE void setFastCallConv(uint32_t cc) noexcept { _fastCallConv = static_cast(cc); } + + // -------------------------------------------------------------------------- + // [Addressing Information] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool hasBaseAddress() const noexcept { return _baseAddress != kNoBaseAddress; } + ASMJIT_INLINE uint64_t getBaseAddress() const noexcept { return _baseAddress; } + ASMJIT_INLINE void setBaseAddress(uint64_t p) noexcept { _baseAddress = p; } + ASMJIT_INLINE void resetBaseAddress() noexcept { _baseAddress = kNoBaseAddress; } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CodeInfo& operator=(const CodeInfo& other) noexcept { init(other); return *this; } + ASMJIT_INLINE bool operator==(const CodeInfo& other) const noexcept { return ::memcmp(this, &other, sizeof(*this)) == 0; } + ASMJIT_INLINE bool operator!=(const CodeInfo& other) const noexcept { return ::memcmp(this, &other, sizeof(*this)) != 0; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + ArchInfo _archInfo; //!< Architecture information. + + union { + struct { + uint8_t _stackAlignment; //!< Natural stack alignment (ARCH+OS). + uint8_t _cdeclCallConv; //!< Default CDECL calling convention. + uint8_t _stdCallConv; //!< Default STDCALL calling convention. + uint8_t _fastCallConv; //!< Default FASTCALL calling convention. + }; + uint32_t _packedMiscInfo; //!< \internal + }; + + uint64_t _baseAddress; //!< Base address. +}; + +// ============================================================================ +// [asmjit::CodeBuffer] +// ============================================================================ + +//! Code or data buffer. +struct CodeBuffer { + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool hasData() const noexcept { return _data != nullptr; } + ASMJIT_INLINE uint8_t* getData() noexcept { return _data; } + ASMJIT_INLINE const uint8_t* getData() const noexcept { return _data; } + + ASMJIT_INLINE size_t getLength() const noexcept { return _length; } + ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; } + + ASMJIT_INLINE bool isExternal() const noexcept { return _isExternal; } + ASMJIT_INLINE bool isFixedSize() const noexcept { return _isFixedSize; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t* _data; //!< The content of the buffer (data). + size_t _length; //!< Number of bytes of `data` used. + size_t _capacity; //!< Buffer capacity (in bytes). + bool _isExternal; //!< True if this is external buffer. + bool _isFixedSize; //!< True if this buffer cannot grow. +}; + +// ============================================================================ +// [asmjit::SectionEntry] +// ============================================================================ + +//! Section entry. +class SectionEntry { +public: + ASMJIT_ENUM(Id) { + kInvalidId = 0xFFFFFFFFU //!< Invalid section id. + }; + + //! Section flags. + ASMJIT_ENUM(Flags) { + kFlagExec = 0x00000001U, //!< Executable (.text sections). + kFlagConst = 0x00000002U, //!< Read-only (.text and .data sections). + kFlagZero = 0x00000004U, //!< Zero initialized by the loader (BSS). + kFlagInfo = 0x00000008U, //!< Info / comment flag. + kFlagImplicit = 0x80000000U //!< Section created implicitly (can be deleted by the Runtime). + }; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + ASMJIT_INLINE const char* getName() const noexcept { return _name; } + + ASMJIT_INLINE void _setDefaultName( + char c0 = 0, char c1 = 0, char c2 = 0, char c3 = 0, + char c4 = 0, char c5 = 0, char c6 = 0, char c7 = 0) noexcept { + reinterpret_cast(_name)[0] = Utils::pack32_4x8(c0, c1, c2, c3); + reinterpret_cast(_name)[1] = Utils::pack32_4x8(c4, c5, c6, c7); + } + + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; } + ASMJIT_INLINE bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; } + ASMJIT_INLINE void addFlags(uint32_t flags) noexcept { _flags |= flags; } + ASMJIT_INLINE void clearFlags(uint32_t flags) noexcept { _flags &= ~flags; } + + ASMJIT_INLINE uint32_t getAlignment() const noexcept { return _alignment; } + ASMJIT_INLINE void setAlignment(uint32_t alignment) noexcept { _alignment = alignment; } + + ASMJIT_INLINE size_t getPhysicalSize() const noexcept { return _buffer.getLength(); } + + ASMJIT_INLINE size_t getVirtualSize() const noexcept { return _virtualSize; } + ASMJIT_INLINE void setVirtualSize(uint32_t size) noexcept { _virtualSize = size; } + + ASMJIT_INLINE CodeBuffer& getBuffer() noexcept { return _buffer; } + ASMJIT_INLINE const CodeBuffer& getBuffer() const noexcept { return _buffer; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _id; //!< Section id. + uint32_t _flags; //!< Section flags. + uint32_t _alignment; //!< Section alignment requirements (0 if no requirements). + uint32_t _virtualSize; //!< Virtual size of the section (zero initialized mostly). + char _name[36]; //!< Section name (max 35 characters, PE allows max 8). + CodeBuffer _buffer; //!< Code or data buffer. +}; + +// ============================================================================ +// [asmjit::LabelLink] +// ============================================================================ + +//! Data structure used to link labels. +struct LabelLink { + LabelLink* prev; //!< Previous link (single-linked list). + uint32_t sectionId; //!< Section id. + uint32_t relocId; //!< Relocation id or RelocEntry::kInvalidId. + size_t offset; //!< Label offset relative to the start of the section. + intptr_t rel; //!< Inlined rel8/rel32. +}; + +// ============================================================================ +// [asmjit::LabelEntry] +// ============================================================================ + +//! Label entry. +//! +//! Contains the following properties: +//! * Label id - This is the only thing that is set to the `Label` operand. +//! * Label name - Optional, used mostly to create executables and libraries. +//! * Label type - Type of the label, default `Label::kTypeAnonymous`. +//! * Label parent id - Derived from many assemblers that allow to define a +//! local label that falls under a global label. This allows to define +//! many labels of the same name that have different parent (global) label. +//! * Offset - offset of the label bound by `Assembler`. +//! * Links - single-linked list that contains locations of code that has +//! to be patched when the label gets bound. Every use of unbound label +//! adds one link to `_links` list. +//! * HVal - Hash value of label's name and optionally parentId. +//! * HashNext - Hash-table implementation detail. +class LabelEntry : public ZoneHashNode { +public: + // NOTE: Label id is stored in `_customData`, which is provided by ZoneHashNode + // to fill a padding that a C++ compiler targeting 64-bit CPU will add to align + // the structure to 64-bits. + + //! Get label id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _customData; } + //! Set label id (internal, used only by \ref CodeHolder). + ASMJIT_INLINE void _setId(uint32_t id) noexcept { _customData = id; } + + //! Get label type, see \ref Label::Type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + //! Get label flags, returns 0 at the moment. + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; } + + //! Get label's parent id. + ASMJIT_INLINE uint32_t getParentId() const noexcept { return _parentId; } + + //! Get label's section id where it's bound to (or `SectionEntry::kInvalidId` if it's not bound yet). + ASMJIT_INLINE uint32_t getSectionId() const noexcept { return _sectionId; } + + //! Get if the label has name. + ASMJIT_INLINE bool hasName() const noexcept { return !_name.isEmpty(); } + + //! Get the label's name. + //! + //! NOTE: Local labels will return their local name without their parent + //! part, for example ".L1". + ASMJIT_INLINE const char* getName() const noexcept { return _name.getData(); } + + //! Get length of label's name. + //! + //! NOTE: Label name is always null terminated, so you can use `strlen()` to + //! get it, however, it's also cached in `LabelEntry`, so if you want to know + //! the length the easiest way is to use `LabelEntry::getNameLength()`. + ASMJIT_INLINE size_t getNameLength() const noexcept { return _name.getLength(); } + + //! Get if the label is bound. + ASMJIT_INLINE bool isBound() const noexcept { return _sectionId != SectionEntry::kInvalidId; } + //! Get the label offset (only useful if the label is bound). + ASMJIT_INLINE intptr_t getOffset() const noexcept { return _offset; } + + //! Get the hash-value of label's name and its parent label (if any). + //! + //! Label hash is calculated as `HASH(Name) ^ ParentId`. The hash function + //! is implemented in `Utils::hashString()` and `Utils::hashRound()`. + ASMJIT_INLINE uint32_t getHVal() const noexcept { return _hVal; } + + // ------------------------------------------------------------------------ + // [Members] + // ------------------------------------------------------------------------ + + // Let's round the size of `LabelEntry` to 64 bytes (as ZoneHeap has 32 + // bytes granularity anyway). This gives `_name` the remaining space, which + // is roughly 16 bytes on 64-bit and 28 bytes on 32-bit architectures. + enum { kNameBytes = 64 - (sizeof(ZoneHashNode) + 16 + sizeof(intptr_t) + sizeof(LabelLink*)) }; + + uint8_t _type; //!< Label type, see Label::Type. + uint8_t _flags; //!< Must be zero. + uint16_t _reserved16; //!< Reserved. + uint32_t _parentId; //!< Label parent id or `kInvalidValue`. + uint32_t _sectionId; //!< Section id or `SectionEntry::kInvalidId`. + uint32_t _reserved32; //!< Reserved. + intptr_t _offset; //!< Label offset. + LabelLink* _links; //!< Label links. + SmallString _name; //!< Label name. +}; + +// ============================================================================ +// [asmjit::RelocEntry] +// ============================================================================ + +//! Relocation entry. +struct RelocEntry { + ASMJIT_ENUM(Id) { + kInvalidId = 0xFFFFFFFFU //!< Invalid relocation id. + }; + + //! Relocation type. + ASMJIT_ENUM(Type) { + kTypeNone = 0, //!< Deleted entry (no relocation). + kTypeAbsToAbs = 1, //!< Relocate absolute to absolute. + kTypeRelToAbs = 2, //!< Relocate relative to absolute. + kTypeAbsToRel = 3, //!< Relocate absolute to relative. + kTypeTrampoline = 4 //!< Relocate absolute to relative or use trampoline. + }; + + // ------------------------------------------------------------------------ + // [Accessors] + // ------------------------------------------------------------------------ + + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + ASMJIT_INLINE uint32_t getSize() const noexcept { return _size; } + + ASMJIT_INLINE uint32_t getSourceSectionId() const noexcept { return _sourceSectionId; } + ASMJIT_INLINE uint32_t getTargetSectionId() const noexcept { return _targetSectionId; } + + ASMJIT_INLINE uint64_t getSourceOffset() const noexcept { return _sourceOffset; } + ASMJIT_INLINE uint64_t getData() const noexcept { return _data; } + + // ------------------------------------------------------------------------ + // [Members] + // ------------------------------------------------------------------------ + + uint32_t _id; //!< Relocation id. + uint8_t _type; //!< Type of the relocation. + uint8_t _size; //!< Size of the relocation (1, 2, 4 or 8 bytes). + uint8_t _reserved[2]; //!< Reserved. + uint32_t _sourceSectionId; //!< Source section id. + uint32_t _targetSectionId; //!< Destination section id. + uint64_t _sourceOffset; //!< Source offset (relative to start of the section). + uint64_t _data; //!< Relocation data (target offset, target address, etc). +}; + +// ============================================================================ +// [asmjit::CodeHolder] +// ============================================================================ + +//! Contains basic information about the target architecture plus its settings, +//! and holds code & data (including sections, labels, and relocation information). +//! CodeHolder can store both binary and intermediate representation of assembly, +//! which can be generated by \ref Assembler and/or \ref CodeBuilder. +//! +//! NOTE: CodeHolder has ability to attach an \ref ErrorHandler, however, this +//! error handler is not triggered by CodeHolder itself, it's only used by the +//! attached code generators. +class ASMJIT_VIRTAPI CodeHolder { +public: + ASMJIT_NONCOPYABLE(CodeHolder) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create an uninitialized CodeHolder (you must init() it before it can be used). + ASMJIT_API CodeHolder() noexcept; + //! Destroy the CodeHolder. + ASMJIT_API ~CodeHolder() noexcept; + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isInitialized() const noexcept { return _codeInfo.isInitialized(); } + + //! Initialize to CodeHolder to hold code described by `codeInfo`. + ASMJIT_API Error init(const CodeInfo& info) noexcept; + //! Detach all code-generators attached and reset the \ref CodeHolder. + ASMJIT_API void reset(bool releaseMemory = false) noexcept; + + // -------------------------------------------------------------------------- + // [Attach / Detach] + // -------------------------------------------------------------------------- + + //! Attach a \ref CodeEmitter to this \ref CodeHolder. + ASMJIT_API Error attach(CodeEmitter* emitter) noexcept; + //! Detach a \ref CodeEmitter from this \ref CodeHolder. + ASMJIT_API Error detach(CodeEmitter* emitter) noexcept; + + // -------------------------------------------------------------------------- + // [Sync] + // -------------------------------------------------------------------------- + + //! Synchronize all states of all `CodeEmitter`s associated with the CodeHolder. + //! This is required as some code generators don't sync every time they do + //! something - for example \ref Assembler generally syncs when it needs to + //! reallocate the \ref CodeBuffer, but not each time it encodes instruction + //! or directive. + ASMJIT_API void sync() noexcept; + + // -------------------------------------------------------------------------- + // [Code-Information] + // -------------------------------------------------------------------------- + + //! Get code/target information, see \ref CodeInfo. + ASMJIT_INLINE const CodeInfo& getCodeInfo() const noexcept { return _codeInfo; } + //! Get architecture information, see \ref ArchInfo. + ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _codeInfo.getArchInfo(); } + + //! Get the target's architecture type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return getArchInfo().getType(); } + //! Get the target's architecture sub-type. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return getArchInfo().getSubType(); } + + //! Get if a static base-address is set. + ASMJIT_INLINE bool hasBaseAddress() const noexcept { return _codeInfo.hasBaseAddress(); } + //! Get a static base-address (uint64_t). + ASMJIT_INLINE uint64_t getBaseAddress() const noexcept { return _codeInfo.getBaseAddress(); } + + // -------------------------------------------------------------------------- + // [Global Information] + // -------------------------------------------------------------------------- + + //! Get global hints, internally propagated to all `CodeEmitter`s attached. + ASMJIT_INLINE uint32_t getGlobalHints() const noexcept { return _globalHints; } + //! Get global options, internally propagated to all `CodeEmitter`s attached. + ASMJIT_INLINE uint32_t getGlobalOptions() const noexcept { return _globalOptions; } + + // -------------------------------------------------------------------------- + // [Result Information] + // -------------------------------------------------------------------------- + + //! Get the size code & data of all sections. + ASMJIT_API size_t getCodeSize() const noexcept; + + //! Get size of all possible trampolines. + //! + //! Trampolines are needed to successfully generate relative jumps to absolute + //! addresses. This value is only non-zero if jmp of call instructions were + //! used with immediate operand (this means jumping or calling an absolute + //! address directly). + ASMJIT_INLINE size_t getTrampolinesSize() const noexcept { return _trampolinesSize; } + + // -------------------------------------------------------------------------- + // [Logging & Error Handling] + // -------------------------------------------------------------------------- + +#if !defined(ASMJIT_DISABLE_LOGGING) + //! Get if a logger attached. + ASMJIT_INLINE bool hasLogger() const noexcept { return _logger != nullptr; } + //! Get the attached logger. + ASMJIT_INLINE Logger* getLogger() const noexcept { return _logger; } + //! Attach a `logger` to CodeHolder and propagate it to all attached `CodeEmitter`s. + ASMJIT_API void setLogger(Logger* logger) noexcept; + //! Reset the logger (does nothing if not attached). + ASMJIT_INLINE void resetLogger() noexcept { setLogger(nullptr); } +#endif // !ASMJIT_DISABLE_LOGGING + + //! Get if error-handler is attached. + ASMJIT_INLINE bool hasErrorHandler() const noexcept { return _errorHandler != nullptr; } + //! Get the error-handler. + ASMJIT_INLINE ErrorHandler* getErrorHandler() const noexcept { return _errorHandler; } + //! Set the error handler, will affect all attached `CodeEmitter`s. + ASMJIT_API Error setErrorHandler(ErrorHandler* handler) noexcept; + //! Reset the error handler (does nothing if not attached). + ASMJIT_INLINE void resetErrorHandler() noexcept { setErrorHandler(nullptr); } + + // -------------------------------------------------------------------------- + // [Sections] + // -------------------------------------------------------------------------- + + //! Get array of `SectionEntry*` records. + ASMJIT_INLINE const ZoneVector& getSections() const noexcept { return _sections; } + + //! Get a section entry of the given index. + ASMJIT_INLINE SectionEntry* getSectionEntry(size_t index) const noexcept { return _sections[index]; } + + ASMJIT_API Error growBuffer(CodeBuffer* cb, size_t n) noexcept; + ASMJIT_API Error reserveBuffer(CodeBuffer* cb, size_t n) noexcept; + + // -------------------------------------------------------------------------- + // [Labels & Symbols] + // -------------------------------------------------------------------------- + + //! Create a new anonymous label and return its id in `idOut`. + //! + //! Returns `Error`, does not report error to \ref ErrorHandler. + ASMJIT_API Error newLabelId(uint32_t& idOut) noexcept; + + //! Create a new named label label-type `type`. + //! + //! Returns `Error`, does not report error to \ref ErrorHandler. + ASMJIT_API Error newNamedLabelId(uint32_t& idOut, const char* name, size_t nameLength, uint32_t type, uint32_t parentId) noexcept; + + //! Get a label id by name. + ASMJIT_API uint32_t getLabelIdByName(const char* name, size_t nameLength = kInvalidIndex, uint32_t parentId = kInvalidValue) noexcept; + + //! Create a new label-link used to store information about yet unbound labels. + //! + //! Returns `null` if the allocation failed. + ASMJIT_API LabelLink* newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel) noexcept; + + //! Get array of `LabelEntry*` records. + ASMJIT_INLINE const ZoneVector& getLabelEntries() const noexcept { return _labels; } + + //! Get number of labels created. + ASMJIT_INLINE size_t getLabelsCount() const noexcept { return _labels.getLength(); } + + //! Get number of label references, which are unresolved at the moment. + ASMJIT_INLINE size_t getUnresolvedLabelsCount() const noexcept { return _unresolvedLabelsCount; } + + //! Get if the `label` is valid (i.e. created by `newLabelId()`). + ASMJIT_INLINE bool isLabelValid(const Label& label) const noexcept { + return isLabelValid(label.getId()); + } + //! Get if the label having `id` is valid (i.e. created by `newLabelId()`). + ASMJIT_INLINE bool isLabelValid(uint32_t labelId) const noexcept { + size_t index = Operand::unpackId(labelId); + return index < _labels.getLength(); + } + + //! Get if the `label` is already bound. + //! + //! Returns `false` if the `label` is not valid. + ASMJIT_INLINE bool isLabelBound(const Label& label) const noexcept { + return isLabelBound(label.getId()); + } + //! \overload + ASMJIT_INLINE bool isLabelBound(uint32_t id) const noexcept { + size_t index = Operand::unpackId(id); + return index < _labels.getLength() && _labels[index]->isBound(); + } + + //! Get a `label` offset or -1 if the label is not yet bound. + ASMJIT_INLINE intptr_t getLabelOffset(const Label& label) const noexcept { + return getLabelOffset(label.getId()); + } + //! \overload + ASMJIT_INLINE intptr_t getLabelOffset(uint32_t id) const noexcept { + ASMJIT_ASSERT(isLabelValid(id)); + return _labels[Operand::unpackId(id)]->getOffset(); + } + + //! Get information about the given `label`. + ASMJIT_INLINE LabelEntry* getLabelEntry(const Label& label) const noexcept { + return getLabelEntry(label.getId()); + } + //! Get information about a label having the given `id`. + ASMJIT_INLINE LabelEntry* getLabelEntry(uint32_t id) const noexcept { + size_t index = static_cast(Operand::unpackId(id)); + return index < _labels.getLength() ? _labels[index] : static_cast(nullptr); + } + + // -------------------------------------------------------------------------- + // [Relocations] + // -------------------------------------------------------------------------- + + //! Create a new relocation entry of type `type` and size `size`. + //! + //! Additional fields can be set after the relocation entry was created. + ASMJIT_API Error newRelocEntry(RelocEntry** dst, uint32_t type, uint32_t size) noexcept; + + //! Get if the code contains relocations. + ASMJIT_INLINE bool hasRelocations() const noexcept { return !_relocations.isEmpty(); } + //! Get array of `RelocEntry*` records. + ASMJIT_INLINE const ZoneVector& getRelocEntries() const noexcept { return _relocations; } + + ASMJIT_INLINE RelocEntry* getRelocEntry(uint32_t id) const noexcept { return _relocations[id]; } + + //! Relocate the code to `baseAddress` and copy it to `dst`. + //! + //! \param dst Contains the location where the relocated code should be + //! copied. The pointer can be address returned by virtual memory allocator + //! or any other address that has sufficient space. + //! + //! \param baseAddress Base address used for relocation. `JitRuntime` always + //! sets the `baseAddress` to be the same as `dst`. + //! + //! \return The number bytes actually used. If the code emitter reserved + //! space for possible trampolines, but didn't use it, the number of bytes + //! used can actually be less than the expected worst case. Virtual memory + //! allocator can shrink the memory it allocated initially. + //! + //! A given buffer will be overwritten, to get the number of bytes required, + //! use `getCodeSize()`. + ASMJIT_API size_t relocate(void* dst, uint64_t baseAddress = kNoBaseAddress) const noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CodeInfo _codeInfo; //!< Basic information about the code (architecture and other info). + + uint32_t _globalHints; //!< Global hints, propagated to all `CodeEmitter`s. + uint32_t _globalOptions; //!< Global options, propagated to all `CodeEmitter`s. + + CodeEmitter* _emitters; //!< Linked-list of all attached `CodeEmitter`s. + Assembler* _cgAsm; //!< Attached \ref Assembler (only one at a time). + + Logger* _logger; //!< Attached \ref Logger, used by all consumers. + ErrorHandler* _errorHandler; //!< Attached \ref ErrorHandler. + + uint32_t _unresolvedLabelsCount; //!< Count of label references which were not resolved. + uint32_t _trampolinesSize; //!< Size of all possible trampolines. + + Zone _baseZone; //!< Base zone (used to allocate core structures). + Zone _dataZone; //!< Data zone (used to allocate extra data like label names). + ZoneHeap _baseHeap; //!< Zone allocator, used to manage internal containers. + + ZoneVector _sections; //!< Section entries. + ZoneVector _labels; //!< Label entries (each label is stored here). + ZoneVector _relocations; //!< Relocation entries. + ZoneHash _namedLabels; //!< Label name -> LabelEntry (only named labels). +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_CODEHOLDER_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/constpool.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/constpool.cpp new file mode 100644 index 00000000..b215ae00 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/constpool.cpp @@ -0,0 +1,509 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/constpool.h" +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// Binary tree code is based on Julienne Walker's "Andersson Binary Trees" +// article and implementation. However, only three operations are implemented - +// get, insert and traverse. + +// ============================================================================ +// [asmjit::ConstPool::Tree - Ops] +// ============================================================================ + +//! \internal +//! +//! Remove left horizontal links. +static ASMJIT_INLINE ConstPool::Node* ConstPoolTree_skewNode(ConstPool::Node* node) noexcept { + ConstPool::Node* link = node->_link[0]; + uint32_t level = node->_level; + + if (level != 0 && link && link->_level == level) { + node->_link[0] = link->_link[1]; + link->_link[1] = node; + + node = link; + } + + return node; +} + +//! \internal +//! +//! Remove consecutive horizontal links. +static ASMJIT_INLINE ConstPool::Node* ConstPoolTree_splitNode(ConstPool::Node* node) noexcept { + ConstPool::Node* link = node->_link[1]; + uint32_t level = node->_level; + + if (level != 0 && link && link->_link[1] && link->_link[1]->_level == level) { + node->_link[1] = link->_link[0]; + link->_link[0] = node; + + node = link; + node->_level++; + } + + return node; +} + +ConstPool::Node* ConstPool::Tree::get(const void* data) noexcept { + ConstPool::Node* node = _root; + size_t dataSize = _dataSize; + + while (node) { + int c = ::memcmp(node->getData(), data, dataSize); + if (c == 0) + return node; + node = node->_link[c < 0]; + } + + return nullptr; +} + +void ConstPool::Tree::put(ConstPool::Node* newNode) noexcept { + size_t dataSize = _dataSize; + _length++; + + if (!_root) { + _root = newNode; + return; + } + + ConstPool::Node* node = _root; + ConstPool::Node* stack[kHeightLimit]; + + unsigned int top = 0; + unsigned int dir; + + // Find a spot and save the stack. + for (;;) { + stack[top++] = node; + dir = ::memcmp(node->getData(), newNode->getData(), dataSize) < 0; + + ConstPool::Node* link = node->_link[dir]; + if (!link) break; + + node = link; + } + + // Link and rebalance. + node->_link[dir] = newNode; + + while (top > 0) { + // Which child? + node = stack[--top]; + + if (top != 0) { + dir = stack[top - 1]->_link[1] == node; + } + + node = ConstPoolTree_skewNode(node); + node = ConstPoolTree_splitNode(node); + + // Fix the parent. + if (top != 0) + stack[top - 1]->_link[dir] = node; + else + _root = node; + } +} + +// ============================================================================ +// [asmjit::ConstPool - Construction / Destruction] +// ============================================================================ + +ConstPool::ConstPool(Zone* zone) noexcept { reset(zone); } +ConstPool::~ConstPool() noexcept {} + +// ============================================================================ +// [asmjit::ConstPool - Reset] +// ============================================================================ + +void ConstPool::reset(Zone* zone) noexcept { + _zone = zone; + + size_t dataSize = 1; + for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) { + _tree[i].reset(); + _tree[i].setDataSize(dataSize); + _gaps[i] = nullptr; + dataSize <<= 1; + } + + _gapPool = nullptr; + _size = 0; + _alignment = 0; +} + +// ============================================================================ +// [asmjit::ConstPool - Ops] +// ============================================================================ + +static ASMJIT_INLINE ConstPool::Gap* ConstPool_allocGap(ConstPool* self) noexcept { + ConstPool::Gap* gap = self->_gapPool; + if (!gap) return self->_zone->allocT(); + + self->_gapPool = gap->_next; + return gap; +} + +static ASMJIT_INLINE void ConstPool_freeGap(ConstPool* self, ConstPool::Gap* gap) noexcept { + gap->_next = self->_gapPool; + self->_gapPool = gap; +} + +static void ConstPool_addGap(ConstPool* self, size_t offset, size_t length) noexcept { + ASMJIT_ASSERT(length > 0); + + while (length > 0) { + size_t gapIndex; + size_t gapLength; + + gapIndex = ConstPool::kIndex16; + if (length >= 16 && Utils::isAligned(offset, 16)) { + gapLength = 16; + } + else if (length >= 8 && Utils::isAligned(offset, 8)) { + gapIndex = ConstPool::kIndex8; + gapLength = 8; + } + else if (length >= 4 && Utils::isAligned(offset, 4)) { + gapIndex = ConstPool::kIndex4; + gapLength = 4; + } + else if (length >= 2 && Utils::isAligned(offset, 2)) { + gapIndex = ConstPool::kIndex2; + gapLength = 2; + } + else { + gapIndex = ConstPool::kIndex1; + gapLength = 1; + } + + // We don't have to check for errors here, if this failed nothing really + // happened (just the gap won't be visible) and it will fail again at + // place where checking will cause kErrorNoHeapMemory. + ConstPool::Gap* gap = ConstPool_allocGap(self); + if (!gap) return; + + gap->_next = self->_gaps[gapIndex]; + self->_gaps[gapIndex] = gap; + + gap->_offset = offset; + gap->_length = gapLength; + + offset += gapLength; + length -= gapLength; + } +} + +Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) noexcept { + size_t treeIndex; + + if (size == 32) + treeIndex = kIndex32; + else if (size == 16) + treeIndex = kIndex16; + else if (size == 8) + treeIndex = kIndex8; + else if (size == 4) + treeIndex = kIndex4; + else if (size == 2) + treeIndex = kIndex2; + else if (size == 1) + treeIndex = kIndex1; + else + return DebugUtils::errored(kErrorInvalidArgument); + + ConstPool::Node* node = _tree[treeIndex].get(data); + if (node) { + dstOffset = node->_offset; + return kErrorOk; + } + + // Before incrementing the current offset try if there is a gap that can + // be used for the requested data. + size_t offset = ~static_cast(0); + size_t gapIndex = treeIndex; + + while (gapIndex != kIndexCount - 1) { + ConstPool::Gap* gap = _gaps[treeIndex]; + + // Check if there is a gap. + if (gap) { + size_t gapOffset = gap->_offset; + size_t gapLength = gap->_length; + + // Destroy the gap for now. + _gaps[treeIndex] = gap->_next; + ConstPool_freeGap(this, gap); + + offset = gapOffset; + ASMJIT_ASSERT(Utils::isAligned(offset, size)); + + gapLength -= size; + if (gapLength > 0) + ConstPool_addGap(this, gapOffset, gapLength); + } + + gapIndex++; + } + + if (offset == ~static_cast(0)) { + // Get how many bytes have to be skipped so the address is aligned accordingly + // to the 'size'. + size_t diff = Utils::alignDiff(_size, size); + + if (diff != 0) { + ConstPool_addGap(this, _size, diff); + _size += diff; + } + + offset = _size; + _size += size; + } + + // Add the initial node to the right index. + node = ConstPool::Tree::_newNode(_zone, data, size, offset, false); + if (!node) return DebugUtils::errored(kErrorNoHeapMemory); + + _tree[treeIndex].put(node); + _alignment = Utils::iMax(_alignment, size); + + dstOffset = offset; + + // Now create a bunch of shared constants that are based on the data pattern. + // We stop at size 4, it probably doesn't make sense to split constants down + // to 1 byte. + size_t pCount = 1; + while (size > 4) { + size >>= 1; + pCount <<= 1; + + ASMJIT_ASSERT(treeIndex != 0); + treeIndex--; + + const uint8_t* pData = static_cast(data); + for (size_t i = 0; i < pCount; i++, pData += size) { + node = _tree[treeIndex].get(pData); + if (node) continue; + + node = ConstPool::Tree::_newNode(_zone, pData, size, offset + (i * size), true); + _tree[treeIndex].put(node); + } + } + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::ConstPool - Reset] +// ============================================================================ + +struct ConstPoolFill { + ASMJIT_INLINE ConstPoolFill(uint8_t* dst, size_t dataSize) noexcept : + _dst(dst), + _dataSize(dataSize) {} + + ASMJIT_INLINE void visit(const ConstPool::Node* node) noexcept { + if (!node->_shared) + ::memcpy(_dst + node->_offset, node->getData(), _dataSize); + } + + uint8_t* _dst; + size_t _dataSize; +}; + +void ConstPool::fill(void* dst) const noexcept { + // Clears possible gaps, asmjit should never emit garbage to the output. + ::memset(dst, 0, _size); + + ConstPoolFill filler(static_cast(dst), 1); + for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) { + _tree[i].iterate(filler); + filler._dataSize <<= 1; + } +} + +// ============================================================================ +// [asmjit::ConstPool - Test] +// ============================================================================ + +#if defined(ASMJIT_TEST) +UNIT(base_constpool) { + Zone zone(32384 - Zone::kZoneOverhead); + ConstPool pool(&zone); + + uint32_t i; + uint32_t kCount = 1000000; + + INFO("Adding %u constants to the pool.", kCount); + { + size_t prevOffset; + size_t curOffset; + uint64_t c = ASMJIT_UINT64_C(0x0101010101010101); + + EXPECT(pool.add(&c, 8, prevOffset) == kErrorOk, + "pool.add() - Returned error."); + EXPECT(prevOffset == 0, + "pool.add() - First constant should have zero offset."); + + for (i = 1; i < kCount; i++) { + c++; + EXPECT(pool.add(&c, 8, curOffset) == kErrorOk, + "pool.add() - Returned error."); + EXPECT(prevOffset + 8 == curOffset, + "pool.add() - Returned incorrect curOffset."); + EXPECT(pool.getSize() == (i + 1) * 8, + "pool.getSize() - Reported incorrect size."); + prevOffset = curOffset; + } + + EXPECT(pool.getAlignment() == 8, + "pool.getAlignment() - Expected 8-byte alignment."); + } + + INFO("Retrieving %u constants from the pool.", kCount); + { + uint64_t c = ASMJIT_UINT64_C(0x0101010101010101); + + for (i = 0; i < kCount; i++) { + size_t offset; + EXPECT(pool.add(&c, 8, offset) == kErrorOk, + "pool.add() - Returned error."); + EXPECT(offset == i * 8, + "pool.add() - Should have reused constant."); + c++; + } + } + + INFO("Checking if the constants were split into 4-byte patterns."); + { + uint32_t c = 0x01010101; + for (i = 0; i < kCount; i++) { + size_t offset; + EXPECT(pool.add(&c, 4, offset) == kErrorOk, + "pool.add() - Returned error."); + EXPECT(offset == i * 8, + "pool.add() - Should reuse existing constant."); + c++; + } + } + + INFO("Adding 2 byte constant to misalign the current offset."); + { + uint16_t c = 0xFFFF; + size_t offset; + + EXPECT(pool.add(&c, 2, offset) == kErrorOk, + "pool.add() - Returned error."); + EXPECT(offset == kCount * 8, + "pool.add() - Didn't return expected position."); + EXPECT(pool.getAlignment() == 8, + "pool.getAlignment() - Expected 8-byte alignment."); + } + + INFO("Adding 8 byte constant to check if pool gets aligned again."); + { + uint64_t c = ASMJIT_UINT64_C(0xFFFFFFFFFFFFFFFF); + size_t offset; + + EXPECT(pool.add(&c, 8, offset) == kErrorOk, + "pool.add() - Returned error."); + EXPECT(offset == kCount * 8 + 8, + "pool.add() - Didn't return aligned offset."); + } + + INFO("Adding 2 byte constant to verify the gap is filled."); + { + uint16_t c = 0xFFFE; + size_t offset; + + EXPECT(pool.add(&c, 2, offset) == kErrorOk, + "pool.add() - Returned error."); + EXPECT(offset == kCount * 8 + 2, + "pool.add() - Didn't fill the gap."); + EXPECT(pool.getAlignment() == 8, + "pool.getAlignment() - Expected 8-byte alignment."); + } + + INFO("Checking reset functionality."); + { + pool.reset(&zone); + zone.reset(); + + EXPECT(pool.getSize() == 0, + "pool.getSize() - Expected pool size to be zero."); + EXPECT(pool.getAlignment() == 0, + "pool.getSize() - Expected pool alignment to be zero."); + } + + INFO("Checking pool alignment when combined constants are added."); + { + uint8_t bytes[32] = { 0 }; + size_t offset; + + pool.add(bytes, 1, offset); + + EXPECT(pool.getSize() == 1, + "pool.getSize() - Expected pool size to be 1 byte."); + EXPECT(pool.getAlignment() == 1, + "pool.getSize() - Expected pool alignment to be 1 byte."); + EXPECT(offset == 0, + "pool.getSize() - Expected offset returned to be zero."); + + pool.add(bytes, 2, offset); + + EXPECT(pool.getSize() == 4, + "pool.getSize() - Expected pool size to be 4 bytes."); + EXPECT(pool.getAlignment() == 2, + "pool.getSize() - Expected pool alignment to be 2 bytes."); + EXPECT(offset == 2, + "pool.getSize() - Expected offset returned to be 2."); + + pool.add(bytes, 4, offset); + + EXPECT(pool.getSize() == 8, + "pool.getSize() - Expected pool size to be 8 bytes."); + EXPECT(pool.getAlignment() == 4, + "pool.getSize() - Expected pool alignment to be 4 bytes."); + EXPECT(offset == 4, + "pool.getSize() - Expected offset returned to be 4."); + + pool.add(bytes, 4, offset); + + EXPECT(pool.getSize() == 8, + "pool.getSize() - Expected pool size to be 8 bytes."); + EXPECT(pool.getAlignment() == 4, + "pool.getSize() - Expected pool alignment to be 4 bytes."); + EXPECT(offset == 4, + "pool.getSize() - Expected offset returned to be 8."); + + pool.add(bytes, 32, offset); + EXPECT(pool.getSize() == 64, + "pool.getSize() - Expected pool size to be 64 bytes."); + EXPECT(pool.getAlignment() == 32, + "pool.getSize() - Expected pool alignment to be 32 bytes."); + EXPECT(offset == 32, + "pool.getSize() - Expected offset returned to be 32."); + } +} +#endif // ASMJIT_TEST + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/constpool.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/constpool.h new file mode 100644 index 00000000..945ea647 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/constpool.h @@ -0,0 +1,257 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CONSTPOOL_H +#define _ASMJIT_BASE_CONSTPOOL_H + +// [Dependencies] +#include "../base/zone.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::ConstPool] +// ============================================================================ + +//! Constant pool. +class ConstPool { +public: + ASMJIT_NONCOPYABLE(ConstPool) + + enum { + kIndex1 = 0, + kIndex2 = 1, + kIndex4 = 2, + kIndex8 = 3, + kIndex16 = 4, + kIndex32 = 5, + kIndexCount = 6 + }; + + // -------------------------------------------------------------------------- + // [Gap] + // -------------------------------------------------------------------------- + + //! \internal + //! + //! Zone-allocated const-pool gap. + struct Gap { + Gap* _next; //!< Pointer to the next gap + size_t _offset; //!< Offset of the gap. + size_t _length; //!< Remaining bytes of the gap (basically a gap size). + }; + + // -------------------------------------------------------------------------- + // [Node] + // -------------------------------------------------------------------------- + + //! \internal + //! + //! Zone-allocated const-pool node. + struct Node { + ASMJIT_INLINE void* getData() const noexcept { + return static_cast(const_cast(this) + 1); + } + + Node* _link[2]; //!< Left/Right nodes. + uint32_t _level : 31; //!< Horizontal level for balance. + uint32_t _shared : 1; //!< If this constant is shared with another. + uint32_t _offset; //!< Data offset from the beginning of the pool. + }; + + // -------------------------------------------------------------------------- + // [Tree] + // -------------------------------------------------------------------------- + + //! \internal + //! + //! Zone-allocated const-pool tree. + struct Tree { + enum { + //! Maximum tree height == log2(1 << 64). + kHeightLimit = 64 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Tree(size_t dataSize = 0) noexcept + : _root(nullptr), + _length(0), + _dataSize(dataSize) {} + ASMJIT_INLINE ~Tree() {} + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void reset() noexcept { + _root = nullptr; + _length = 0; + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isEmpty() const noexcept { return _length == 0; } + ASMJIT_INLINE size_t getLength() const noexcept { return _length; } + + ASMJIT_INLINE void setDataSize(size_t dataSize) noexcept { + ASMJIT_ASSERT(isEmpty()); + _dataSize = dataSize; + } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + ASMJIT_API Node* get(const void* data) noexcept; + ASMJIT_API void put(Node* node) noexcept; + + // -------------------------------------------------------------------------- + // [Iterate] + // -------------------------------------------------------------------------- + + template + ASMJIT_INLINE void iterate(Visitor& visitor) const noexcept { + Node* node = const_cast(_root); + if (!node) return; + + Node* stack[kHeightLimit]; + size_t top = 0; + + for (;;) { + Node* left = node->_link[0]; + if (left != nullptr) { + ASMJIT_ASSERT(top != kHeightLimit); + stack[top++] = node; + + node = left; + continue; + } + +Visit: + visitor.visit(node); + node = node->_link[1]; + if (node != nullptr) + continue; + + if (top == 0) + return; + + node = stack[--top]; + goto Visit; + } + } + + // -------------------------------------------------------------------------- + // [Helpers] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE Node* _newNode(Zone* zone, const void* data, size_t size, size_t offset, bool shared) noexcept { + Node* node = zone->allocT(sizeof(Node) + size); + if (ASMJIT_UNLIKELY(!node)) return nullptr; + + node->_link[0] = nullptr; + node->_link[1] = nullptr; + node->_level = 1; + node->_shared = shared; + node->_offset = static_cast(offset); + + ::memcpy(node->getData(), data, size); + return node; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Node* _root; //!< Root of the tree + size_t _length; //!< Length of the tree (count of nodes). + size_t _dataSize; //!< Size of the data. + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_API ConstPool(Zone* zone) noexcept; + ASMJIT_API ~ConstPool() noexcept; + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + ASMJIT_API void reset(Zone* zone) noexcept; + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + //! Get whether the constant-pool is empty. + ASMJIT_INLINE bool isEmpty() const noexcept { return _size == 0; } + //! Get the size of the constant-pool in bytes. + ASMJIT_INLINE size_t getSize() const noexcept { return _size; } + //! Get minimum alignment. + ASMJIT_INLINE size_t getAlignment() const noexcept { return _alignment; } + + //! Add a constant to the constant pool. + //! + //! The constant must have known size, which is 1, 2, 4, 8, 16 or 32 bytes. + //! The constant is added to the pool only if it doesn't not exist, otherwise + //! cached value is returned. + //! + //! AsmJit is able to subdivide added constants, so for example if you add + //! 8-byte constant 0x1122334455667788 it will create the following slots: + //! + //! 8-byte: 0x1122334455667788 + //! 4-byte: 0x11223344, 0x55667788 + //! + //! The reason is that when combining MMX/SSE/AVX code some patterns are used + //! frequently. However, AsmJit is not able to reallocate a constant that has + //! been already added. For example if you try to add 4-byte constant and then + //! 8-byte constant having the same 4-byte pattern as the previous one, two + //! independent slots will be generated by the pool. + ASMJIT_API Error add(const void* data, size_t size, size_t& dstOffset) noexcept; + + // -------------------------------------------------------------------------- + // [Fill] + // -------------------------------------------------------------------------- + + //! Fill the destination with the constants from the pool. + ASMJIT_API void fill(void* dst) const noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Zone* _zone; //!< Zone allocator. + Tree _tree[kIndexCount]; //!< Tree per size. + Gap* _gaps[kIndexCount]; //!< Gaps per size. + Gap* _gapPool; //!< Gaps pool + + size_t _size; //!< Size of the pool (in bytes). + size_t _alignment; //!< Required pool alignment. +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_CONSTPOOL_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/cpuinfo.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/cpuinfo.cpp new file mode 100644 index 00000000..79518aaa --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/cpuinfo.cpp @@ -0,0 +1,648 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/cpuinfo.h" +#include "../base/utils.h" + +#if ASMJIT_OS_POSIX +# include +# include +# include +# include +#endif // ASMJIT_OS_POSIX + +#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 +# if ASMJIT_CC_MSC_GE(14, 0, 0) + # include // Required by `__cpuid()` and `_xgetbv()`. +# endif // _MSC_VER >= 1400 +#endif + +#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 +# if ASMJIT_OS_LINUX +# include // Required by `getauxval()`. +# endif +#endif + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::CpuInfo - Detect ARM] +// ============================================================================ + +// ARM information has to be retrieved by the OS (this is how ARM was designed). +#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 + +#if ASMJIT_ARCH_ARM32 +static ASMJIT_INLINE void armPopulateBaselineArm32Features(CpuInfo* cpuInfo) noexcept { + cpuInfo->_archInfo.init(ArchInfo::kTypeArm32); +} +#endif // ASMJIT_ARCH_ARM32 + +#if ASMJIT_ARCH_ARM64 +static ASMJIT_INLINE void armPopulateBaselineArm64Features(CpuInfo* cpuInfo) noexcept { + cpuInfo->_archInfo.init(ArchInfo::kTypeArm64); + + // Thumb (including all variations) is only supported on ARM32. + + // ARM64 is based on ARMv8 and newer. + cpuInfo->addFeature(CpuInfo::kArmFeatureV6); + cpuInfo->addFeature(CpuInfo::kArmFeatureV7); + cpuInfo->addFeature(CpuInfo::kArmFeatureV8); + + // ARM64 comes with these features by default. + cpuInfo->addFeature(CpuInfo::kArmFeatureDSP); + cpuInfo->addFeature(CpuInfo::kArmFeatureIDIV); + cpuInfo->addFeature(CpuInfo::kArmFeatureVFP2); + cpuInfo->addFeature(CpuInfo::kArmFeatureVFP3); + cpuInfo->addFeature(CpuInfo::kArmFeatureVFP4); +} +#endif // ASMJIT_ARCH_ARM64 + +#if ASMJIT_OS_WINDOWS +//! \internal +//! +//! Detect ARM CPU features on Windows. +//! +//! The detection is based on `IsProcessorFeaturePresent()` API call. +static ASMJIT_INLINE void armDetectCpuInfoOnWindows(CpuInfo* cpuInfo) noexcept { +#if ASMJIT_ARCH_ARM32 + armPopulateBaselineArm32Features(cpuInfo); + + // Windows for ARM requires at least ARMv7 with DSP extensions. + cpuInfo->addFeature(CpuInfo::kArmFeatureV6); + cpuInfo->addFeature(CpuInfo::kArmFeatureV7); + cpuInfo->addFeature(CpuInfo::kArmFeatureDSP); + + // Windows for ARM requires VFP3. + cpuInfo->addFeature(CpuInfo::kArmFeatureVFP2); + cpuInfo->addFeature(CpuInfo::kArmFeatureVFP3); + + // Windows for ARM requires and uses THUMB2. + cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB); + cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB2); +#else + armPopulateBaselineArm64Features(cpuInfo); +#endif + + // Windows for ARM requires NEON. + cpuInfo->addFeature(CpuInfo::kArmFeatureNEON); + + // Detect additional CPU features by calling `IsProcessorFeaturePresent()`. + struct WinPFPMapping { + uint32_t pfpId, featureId; + }; + + static const WinPFPMapping mapping[] = { + { PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE , CpuInfo::kArmFeatureVFP4 }, + { PF_ARM_VFP_32_REGISTERS_AVAILABLE , CpuInfo::kArmFeatureVFP_D32 }, + { PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE, CpuInfo::kArmFeatureIDIV }, + { PF_ARM_64BIT_LOADSTORE_ATOMIC , CpuInfo::kArmFeatureAtomics64 } + }; + + for (uint32_t i = 0; i < ASMJIT_ARRAY_SIZE(mapping); i++) + if (::IsProcessorFeaturePresent(mapping[i].pfpId)) + cpuInfo->addFeature(mapping[i].featureId); +} +#endif // ASMJIT_OS_WINDOWS + +#if ASMJIT_OS_LINUX +struct LinuxHWCapMapping { + uint32_t hwcapMask, featureId; +}; + +static void armDetectHWCaps(CpuInfo* cpuInfo, + unsigned long type, const LinuxHWCapMapping* mapping, size_t length) noexcept { + unsigned long mask = getauxval(type); + + for (size_t i = 0; i < length; i++) + if ((mask & mapping[i].hwcapMask) == mapping[i].hwcapMask) + cpuInfo->addFeature(mapping[i].featureId); +} + +//! \internal +//! +//! Detect ARM CPU features on Linux. +//! +//! The detection is based on `getauxval()`. +ASMJIT_FAVOR_SIZE static void armDetectCpuInfoOnLinux(CpuInfo* cpuInfo) noexcept { +#if ASMJIT_ARCH_ARM32 + armPopulateBaselineArm32Features(cpuInfo); + + // `AT_HWCAP` provides ARMv7 (and less) related flags. + static const LinuxHWCapMapping hwCapMapping[] = { + { /* HWCAP_VFPv3 */ (1 << 13), CpuInfo::kArmFeatureVFP3 }, + { /* HWCAP_VFPv4 */ (1 << 16), CpuInfo::kArmFeatureVFP4 }, + { /* HWCAP_IDIVA */ (3 << 17), CpuInfo::kArmFeatureIDIV }, + { /* HWCAP_VFPD32 */ (1 << 19), CpuInfo::kArmFeatureVFP_D32 }, + { /* HWCAP_NEON */ (1 << 12), CpuInfo::kArmFeatureNEON }, + { /* HWCAP_EDSP */ (1 << 7), CpuInfo::kArmFeatureDSP } + }; + armDetectHWCaps(cpuInfo, AT_HWCAP, hwCapMapping, ASMJIT_ARRAY_SIZE(hwCapMapping)); + + // VFP3 implies VFP2. + if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFP3)) + cpuInfo->addFeature(CpuInfo::kArmFeatureVFP2); + + // VFP2 implies ARMv6. + if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFP2)) + cpuInfo->addFeature(CpuInfo::kArmFeatureV6); + + // VFP3 or NEON implies ARMv7. + if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFP3) || + cpuInfo->hasFeature(CpuInfo::kArmFeatureNEON)) + cpuInfo->addFeature(CpuInfo::kArmFeatureV7); + + // `AT_HWCAP2` provides ARMv8 related flags. + static const LinuxHWCapMapping hwCap2Mapping[] = { + { /* HWCAP2_AES */ (1 << 0), CpuInfo::kArmFeatureAES }, + { /* HWCAP2_CRC32 */ (1 << 4), CpuInfo::kArmFeatureCRC32 }, + { /* HWCAP2_PMULL */ (1 << 1), CpuInfo::kArmFeaturePMULL }, + { /* HWCAP2_SHA1 */ (1 << 2), CpuInfo::kArmFeatureSHA1 }, + { /* HWCAP2_SHA2 */ (1 << 3), CpuInfo::kArmFeatureSHA256 } + }; + armDetectHWCaps(cpuInfo, AT_HWCAP2, hwCap2Mapping, ASMJIT_ARRAY_SIZE(hwCapMapping2)); + + if (cpuInfo->hasFeature(CpuInfo::kArmFeatureAES ) || + cpuInfo->hasFeature(CpuInfo::kArmFeatureCRC32 ) || + cpuInfo->hasFeature(CpuInfo::kArmFeaturePMULL ) || + cpuInfo->hasFeature(CpuInfo::kArmFeatureSHA1 ) || + cpuInfo->hasFeature(CpuInfo::kArmFeatureSHA256)) { + cpuInfo->addFeature(CpuInfo::kArmFeatureV8); + } +#else + armPopulateBaselineArm64Features(cpuInfo); + + // `AT_HWCAP` provides ARMv8 related flags. + static const LinuxHWCapMapping hwCapMapping[] = { + { /* HWCAP_ASIMD */ (1 << 1), CpuInfo::kArmFeatureNEON }, + { /* HWCAP_AES */ (1 << 3), CpuInfo::kArmFeatureAES }, + { /* HWCAP_CRC32 */ (1 << 7), CpuInfo::kArmFeatureCRC32 }, + { /* HWCAP_PMULL */ (1 << 4), CpuInfo::kArmFeaturePMULL }, + { /* HWCAP_SHA1 */ (1 << 5), CpuInfo::kArmFeatureSHA1 }, + { /* HWCAP_SHA2 */ (1 << 6), CpuInfo::kArmFeatureSHA256 } + { /* HWCAP_ATOMICS */ (1 << 8), CpuInfo::kArmFeatureAtomics64 } + }; + armDetectHWCaps(cpuInfo, AT_HWCAP, hwCapMapping, ASMJIT_ARRAY_SIZE(hwCapMapping)); + + // `AT_HWCAP2` is not used at the moment. +#endif +} +#endif // ASMJIT_OS_LINUX + +ASMJIT_FAVOR_SIZE static void armDetectCpuInfo(CpuInfo* cpuInfo) noexcept { +#if ASMJIT_OS_WINDOWS + armDetectCpuInfoOnWindows(cpuInfo); +#elif ASMJIT_OS_LINUX + armDetectCpuInfoOnLinux(cpuInfo); +#else +# error "[asmjit] armDetectCpuInfo() - Unsupported OS." +#endif +} +#endif // ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 + +// ============================================================================ +// [asmjit::CpuInfo - Detect X86] +// ============================================================================ + +#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 + +//! \internal +//! +//! X86 CPUID result. +struct CpuIdResult { + uint32_t eax, ebx, ecx, edx; +}; + +//! \internal +//! +//! Content of XCR register, result of XGETBV instruction. +struct XGetBVResult { + uint32_t eax, edx; +}; + +#if ASMJIT_CC_MSC && !ASMJIT_CC_MSC_GE(15, 0, 30729) && ASMJIT_ARCH_X64 +//! \internal +//! +//! HACK: VS2008 or less, 64-bit mode - `__cpuidex` doesn't exist! However, +//! 64-bit calling convention specifies the first parameter to be passed by +//! ECX, so we may be lucky if compiler doesn't move the register, otherwise +//! the result would be wrong. +static void ASMJIT_NOINLINE void x86CallCpuIdWorkaround(uint32_t inEcx, uint32_t inEax, CpuIdResult* result) noexcept { + __cpuid(reinterpret_cast(result), inEax); +} +#endif + +//! \internal +//! +//! Wrapper to call `cpuid` instruction. +static void ASMJIT_INLINE x86CallCpuId(CpuIdResult* result, uint32_t inEax, uint32_t inEcx = 0) noexcept { +#if ASMJIT_CC_MSC && ASMJIT_CC_MSC_GE(15, 0, 30729) + __cpuidex(reinterpret_cast(result), inEax, inEcx); +#elif ASMJIT_CC_MSC && ASMJIT_ARCH_X64 + x86CallCpuIdWorkaround(inEcx, inEax, result); +#elif ASMJIT_CC_MSC && ASMJIT_ARCH_X86 + uint32_t paramEax = inEax; + uint32_t paramEcx = inEcx; + uint32_t* out = reinterpret_cast(result); + + __asm { + mov eax, paramEax + mov ecx, paramEcx + mov edi, out + cpuid + mov dword ptr[edi + 0], eax + mov dword ptr[edi + 4], ebx + mov dword ptr[edi + 8], ecx + mov dword ptr[edi + 12], edx + } +#elif (ASMJIT_CC_GCC || ASMJIT_CC_CLANG) && ASMJIT_ARCH_X86 + __asm__ __volatile__( + "mov %%ebx, %%edi\n" + "cpuid\n" + "xchg %%edi, %%ebx\n" + : "=a"(result->eax), + "=D"(result->ebx), + "=c"(result->ecx), + "=d"(result->edx) + : "a"(inEax), + "c"(inEcx) + ); +#elif (ASMJIT_CC_GCC || ASMJIT_CC_CLANG) && ASMJIT_ARCH_X64 + __asm__ __volatile__( \ + "mov %%rbx, %%rdi\n" + "cpuid\n" + "xchg %%rdi, %%rbx\n" + : "=a"(result->eax), + "=D"(result->ebx), + "=c"(result->ecx), + "=d"(result->edx) + : "a"(inEax), + "c"(inEcx) + ); +#else +# error "[asmjit] x86CallCpuid() - Unsupported compiler." +#endif +} + +//! \internal +//! +//! Wrapper to call `xgetbv` instruction. +static ASMJIT_INLINE void x86CallXGetBV(XGetBVResult* result, uint32_t inEcx) noexcept { +#if ASMJIT_CC_MSC_GE(16, 0, 40219) // 2010SP1+ + uint64_t value = _xgetbv(inEcx); + result->eax = static_cast(value & 0xFFFFFFFFU); + result->edx = static_cast(value >> 32); +#elif ASMJIT_CC_GCC || ASMJIT_CC_CLANG + uint32_t outEax; + uint32_t outEdx; + + // Replaced, because the world is not perfect: + // __asm__ __volatile__("xgetbv" : "=a"(outEax), "=d"(outEdx) : "c"(inEcx)); + __asm__ __volatile__(".byte 0x0F, 0x01, 0xd0" : "=a"(outEax), "=d"(outEdx) : "c"(inEcx)); + + result->eax = outEax; + result->edx = outEdx; +#else + result->eax = 0; + result->edx = 0; +#endif +} + +//! \internal +//! +//! Map a 12-byte vendor string returned by `cpuid` into a `CpuInfo::Vendor` ID. +static ASMJIT_INLINE uint32_t x86GetCpuVendorID(const char* vendorString) noexcept { + struct VendorData { + uint32_t id; + char text[12]; + }; + + static const VendorData vendorList[] = { + { CpuInfo::kVendorIntel , { 'G', 'e', 'n', 'u', 'i', 'n', 'e', 'I', 'n', 't', 'e', 'l' } }, + { CpuInfo::kVendorAMD , { 'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'A', 'M', 'D' } }, + { CpuInfo::kVendorVIA , { 'V', 'I', 'A', 0 , 'V', 'I', 'A', 0 , 'V', 'I', 'A', 0 } }, + { CpuInfo::kVendorVIA , { 'C', 'e', 'n', 't', 'a', 'u', 'r', 'H', 'a', 'u', 'l', 's' } } + }; + + uint32_t dw0 = reinterpret_cast(vendorString)[0]; + uint32_t dw1 = reinterpret_cast(vendorString)[1]; + uint32_t dw2 = reinterpret_cast(vendorString)[2]; + + for (uint32_t i = 0; i < ASMJIT_ARRAY_SIZE(vendorList); i++) { + if (dw0 == reinterpret_cast(vendorList[i].text)[0] && + dw1 == reinterpret_cast(vendorList[i].text)[1] && + dw2 == reinterpret_cast(vendorList[i].text)[2]) + return vendorList[i].id; + } + + return CpuInfo::kVendorNone; +} + +static ASMJIT_INLINE void x86SimplifyBrandString(char* s) noexcept { + // Used to always clear the current character to ensure that the result + // doesn't contain garbage after the new zero terminator. + char* d = s; + + char prev = 0; + char curr = s[0]; + s[0] = '\0'; + + for (;;) { + if (curr == 0) + break; + + if (curr == ' ') { + if (prev == '@' || s[1] == ' ' || s[1] == '@') + goto L_Skip; + } + + d[0] = curr; + d++; + prev = curr; + +L_Skip: + curr = *++s; + s[0] = '\0'; + } + + d[0] = '\0'; +} + +ASMJIT_FAVOR_SIZE static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept { + uint32_t i, maxId; + + CpuIdResult regs; + XGetBVResult xcr0 = { 0, 0 }; + + cpuInfo->_archInfo.init(ArchInfo::kTypeHost); + + // -------------------------------------------------------------------------- + // [CPUID EAX=0x0] + // -------------------------------------------------------------------------- + + // Get vendor string/id. + x86CallCpuId(®s, 0x0); + + maxId = regs.eax; + ::memcpy(cpuInfo->_vendorString + 0, ®s.ebx, 4); + ::memcpy(cpuInfo->_vendorString + 4, ®s.edx, 4); + ::memcpy(cpuInfo->_vendorString + 8, ®s.ecx, 4); + cpuInfo->_vendorId = x86GetCpuVendorID(cpuInfo->_vendorString); + + // -------------------------------------------------------------------------- + // [CPUID EAX=0x1] + // -------------------------------------------------------------------------- + + if (maxId >= 0x1) { + // Get feature flags in ECX/EDX and family/model in EAX. + x86CallCpuId(®s, 0x1); + + // Fill family and model fields. + cpuInfo->_family = (regs.eax >> 8) & 0x0F; + cpuInfo->_model = (regs.eax >> 4) & 0x0F; + cpuInfo->_stepping = (regs.eax ) & 0x0F; + + // Use extended family and model fields. + if (cpuInfo->_family == 0x0F) { + cpuInfo->_family += ((regs.eax >> 20) & 0xFF); + cpuInfo->_model += ((regs.eax >> 16) & 0x0F) << 4; + } + + cpuInfo->_x86Data._processorType = ((regs.eax >> 12) & 0x03); + cpuInfo->_x86Data._brandIndex = ((regs.ebx ) & 0xFF); + cpuInfo->_x86Data._flushCacheLineSize = ((regs.ebx >> 8) & 0xFF) * 8; + cpuInfo->_x86Data._maxLogicalProcessors = ((regs.ebx >> 16) & 0xFF); + + if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE3); + if (regs.ecx & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeaturePCLMULQDQ); + if (regs.ecx & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureMONITOR); + if (regs.ecx & 0x00000200U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSSE3); + if (regs.ecx & 0x00002000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMPXCHG16B); + if (regs.ecx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4_1); + if (regs.ecx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4_2); + if (regs.ecx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMOVBE); + if (regs.ecx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeaturePOPCNT); + if (regs.ecx & 0x02000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAESNI); + if (regs.ecx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVE); + if (regs.ecx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVE_OS); + if (regs.ecx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDRAND); + if (regs.edx & 0x00000010U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDTSC); + if (regs.edx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMPXCHG8B); + if (regs.edx & 0x00008000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMOV); + if (regs.edx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLFLUSH); + if (regs.edx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMMX); + if (regs.edx & 0x01000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFXSR); + if (regs.edx & 0x02000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE) + .addFeature(CpuInfo::kX86FeatureMMX2); + if (regs.edx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE) + .addFeature(CpuInfo::kX86FeatureSSE2); + if (regs.edx & 0x10000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMT); + + // Get the content of XCR0 if supported by CPU and enabled by OS. + if ((regs.ecx & 0x0C000000U) == 0x0C000000U) + x86CallXGetBV(&xcr0, 0); + + // Detect AVX+. + if (regs.ecx & 0x10000000U) { + // - XCR0[2:1] == 11b + // XMM & YMM states need to be enabled by OS. + if ((xcr0.eax & 0x00000006U) == 0x00000006U) { + cpuInfo->addFeature(CpuInfo::kX86FeatureAVX); + + if (regs.ecx & 0x00004000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFMA3); + if (regs.ecx & 0x20000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureF16C); + } + } + } + + // -------------------------------------------------------------------------- + // [CPUID EAX=0x7 ECX=0x0] + // -------------------------------------------------------------------------- + + // Detect new features if the processor supports CPUID-07. + bool maybeMPX = false; + + if (maxId >= 0x7) { + x86CallCpuId(®s, 0x7); + + if (regs.ebx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureFSGSBASE); + if (regs.ebx & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureBMI); + if (regs.ebx & 0x00000010U) cpuInfo->addFeature(CpuInfo::kX86FeatureHLE); + if (regs.ebx & 0x00000080U) cpuInfo->addFeature(CpuInfo::kX86FeatureSMEP); + if (regs.ebx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeatureBMI2); + if (regs.ebx & 0x00000200U) cpuInfo->addFeature(CpuInfo::kX86FeatureERMS); + if (regs.ebx & 0x00000800U) cpuInfo->addFeature(CpuInfo::kX86FeatureRTM); + if (regs.ebx & 0x00004000U) maybeMPX = true; + if (regs.ebx & 0x00040000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDSEED); + if (regs.ebx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureADX); + if (regs.ebx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSMAP); + if (regs.ebx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeaturePCOMMIT); + if (regs.ebx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLFLUSH_OPT); + if (regs.ebx & 0x01000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLWB); + if (regs.ebx & 0x20000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSHA); + if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeaturePREFETCHWT1); + + // Detect AVX2. + if (cpuInfo->hasFeature(CpuInfo::kX86FeatureAVX)) + if (regs.ebx & 0x00000020U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX2); + + // Detect AVX-512+. + if (regs.ebx & 0x00010000U) { + // - XCR0[2:1] == 11b + // XMM/YMM states need to be enabled by OS. + // - XCR0[7:5] == 111b + // Upper 256-bit of ZMM0-XMM15 and ZMM16-ZMM31 need to be enabled by the OS. + if ((xcr0.eax & 0x000000E6U) == 0x000000E6U) { + cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512F); + + if (regs.ebx & 0x00020000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512DQ); + if (regs.ebx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512IFMA); + if (regs.ebx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512PFI); + if (regs.ebx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512ERI); + if (regs.ebx & 0x10000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512CDI); + if (regs.ebx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512BW); + if (regs.ebx & 0x80000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512VL); + if (regs.ecx & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512VBMI); + } + } + } + + // -------------------------------------------------------------------------- + // [CPUID EAX=0xD, ECX=0x0] + // -------------------------------------------------------------------------- + + if (maxId >= 0xD && maybeMPX) { + x86CallCpuId(®s, 0xD); + + // Both CPUID result and XCR0 has to be enabled to have support for MPX. + if (((regs.eax & xcr0.eax) & 0x00000018U) == 0x00000018U) { + cpuInfo->addFeature(CpuInfo::kX86FeatureMPX); + } + } + + // -------------------------------------------------------------------------- + // [CPUID EAX=0x80000000...maxId] + // -------------------------------------------------------------------------- + + // The highest EAX that we understand. + uint32_t kHighestProcessedEAX = 0x80000008U; + + // Several CPUID calls are required to get the whole branc string. It's easy + // to copy one DWORD at a time instead of performing a byte copy. + uint32_t* brand = reinterpret_cast(cpuInfo->_brandString); + + i = maxId = 0x80000000U; + do { + x86CallCpuId(®s, i); + switch (i) { + case 0x80000000U: + maxId = Utils::iMin(regs.eax, kHighestProcessedEAX); + break; + + case 0x80000001U: + if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureLAHF_SAHF); + if (regs.ecx & 0x00000020U) cpuInfo->addFeature(CpuInfo::kX86FeatureLZCNT); + if (regs.ecx & 0x00000040U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4A); + if (regs.ecx & 0x00000080U) cpuInfo->addFeature(CpuInfo::kX86FeatureMSSE); + if (regs.ecx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeaturePREFETCH); + if (regs.ecx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureTBM); + if (regs.edx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureNX); + if (regs.edx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFXSR_OPT); + if (regs.edx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMMX2); + if (regs.edx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDTSCP); + if (regs.edx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86Feature3DNOW2) + .addFeature(CpuInfo::kX86FeatureMMX2); + if (regs.edx & 0x80000000U) cpuInfo->addFeature(CpuInfo::kX86Feature3DNOW); + + if (cpuInfo->hasFeature(CpuInfo::kX86FeatureAVX)) { + if (regs.ecx & 0x00000800U) cpuInfo->addFeature(CpuInfo::kX86FeatureXOP); + if (regs.ecx & 0x00010000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFMA4); + } + break; + + case 0x80000002U: + case 0x80000003U: + case 0x80000004U: + *brand++ = regs.eax; + *brand++ = regs.ebx; + *brand++ = regs.ecx; + *brand++ = regs.edx; + + // Go directly to the last one. + if (i == 0x80000004U) i = 0x80000008U - 1; + break; + + case 0x80000008U: + if (regs.ebx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLZERO); + break; + } + } while (++i <= maxId); + + // Simplify CPU brand string by removing unnecessary spaces. + x86SimplifyBrandString(cpuInfo->_brandString); +} +#endif // ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 + +// ============================================================================ +// [asmjit::CpuInfo - Detect - HWThreadsCount] +// ============================================================================ + +static ASMJIT_INLINE uint32_t cpuDetectHWThreadsCount() noexcept { +#if ASMJIT_OS_WINDOWS + SYSTEM_INFO info; + ::GetSystemInfo(&info); + return info.dwNumberOfProcessors; +#elif ASMJIT_OS_POSIX && defined(_SC_NPROCESSORS_ONLN) + long res = ::sysconf(_SC_NPROCESSORS_ONLN); + if (res <= 0) return 1; + return static_cast(res); +#else + return 1; +#endif +} + +// ============================================================================ +// [asmjit::CpuInfo - Detect] +// ============================================================================ + +ASMJIT_FAVOR_SIZE void CpuInfo::detect() noexcept { + reset(); + +#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 + armDetectCpuInfo(this); +#endif // ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 + +#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 + x86DetectCpuInfo(this); +#endif // ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 + + _hwThreadsCount = cpuDetectHWThreadsCount(); +} + +// ============================================================================ +// [asmjit::CpuInfo - GetHost] +// ============================================================================ + +struct HostCpuInfo : public CpuInfo { + ASMJIT_INLINE HostCpuInfo() noexcept : CpuInfo() { detect(); } +}; + +const CpuInfo& CpuInfo::getHost() noexcept { + static HostCpuInfo host; + return host; +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/cpuinfo.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/cpuinfo.h new file mode 100644 index 00000000..e3fb3793 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/cpuinfo.h @@ -0,0 +1,294 @@ + // [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CPUINFO_H +#define _ASMJIT_BASE_CPUINFO_H + +// [Dependencies] +#include "../base/arch.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::CpuInfo] +// ============================================================================ + +//! CPU information. +class CpuInfo { +public: + //! CPU vendor ID. + ASMJIT_ENUM(Vendor) { + kVendorNone = 0, //!< Generic or unknown. + kVendorIntel = 1, //!< Intel vendor. + kVendorAMD = 2, //!< AMD vendor. + kVendorVIA = 3 //!< VIA vendor. + }; + + //! ARM/ARM64 CPU features. + ASMJIT_ENUM(ArmFeatures) { + kArmFeatureV6, //!< ARMv6 instruction set. + kArmFeatureV7, //!< ARMv7 instruction set. + kArmFeatureV8, //!< ARMv8 instruction set. + kArmFeatureTHUMB, //!< CPU provides THUMB v1 instruction set (ARM only). + kArmFeatureTHUMB2, //!< CPU provides THUMB v2 instruction set (ARM only). + kArmFeatureVFP2, //!< CPU provides VFPv2 instruction set. + kArmFeatureVFP3, //!< CPU provides VFPv3 instruction set. + kArmFeatureVFP4, //!< CPU provides VFPv4 instruction set. + kArmFeatureVFP_D32, //!< CPU provides 32 VFP-D (64-bit) registers. + kArmFeatureNEON, //!< CPU provides NEON instruction set. + kArmFeatureDSP, //!< CPU provides DSP extensions. + kArmFeatureIDIV, //!< CPU provides hardware support for SDIV and UDIV. + kArmFeatureAES, //!< CPU provides AES instructions (ARM64 only). + kArmFeatureCRC32, //!< CPU provides CRC32 instructions (ARM64 only). + kArmFeaturePMULL, //!< CPU provides PMULL instructions (ARM64 only). + kArmFeatureSHA1, //!< CPU provides SHA1 instructions (ARM64 only). + kArmFeatureSHA256, //!< CPU provides SHA256 instructions (ARM64 only). + kArmFeatureAtomics64, //!< CPU provides 64-bit load/store atomics (ARM64 only). + + kArmFeaturesCount //!< Count of ARM/ARM64 CPU features. + }; + + //! X86/X64 CPU features. + ASMJIT_ENUM(X86Features) { + kX86FeatureNX = 0, //!< CPU has Not-Execute-Bit. + kX86FeatureMT, //!< CPU has multi-threading. + kX86FeatureRDTSC, //!< CPU has RDTSC. + kX86FeatureRDTSCP, //!< CPU has RDTSCP. + kX86FeatureCMOV, //!< CPU has CMOV. + kX86FeatureCMPXCHG8B, //!< CPU has CMPXCHG8B. + kX86FeatureCMPXCHG16B, //!< CPU has CMPXCHG16B (x64). + kX86FeatureCLFLUSH, //!< CPU has CLFUSH. + kX86FeatureCLFLUSH_OPT, //!< CPU has CLFUSH (optimized). + kX86FeatureCLWB, //!< CPU has CLWB. + kX86FeatureCLZERO, //!< CPU has CLZERO. + kX86FeaturePCOMMIT, //!< CPU has PCOMMIT. + kX86FeaturePREFETCH, //!< CPU has PREFETCH. + kX86FeaturePREFETCHWT1, //!< CPU has PREFETCHWT1. + kX86FeatureLAHF_SAHF, //!< CPU has LAHF/SAHF. + kX86FeatureFXSR, //!< CPU has FXSAVE/FXRSTOR. + kX86FeatureFXSR_OPT, //!< CPU has FXSAVE/FXRSTOR (optimized). + kX86FeatureMMX, //!< CPU has MMX. + kX86FeatureMMX2, //!< CPU has extended MMX. + kX86Feature3DNOW, //!< CPU has 3dNow! + kX86Feature3DNOW2, //!< CPU has enhanced 3dNow! + kX86FeatureSSE, //!< CPU has SSE. + kX86FeatureSSE2, //!< CPU has SSE2. + kX86FeatureSSE3, //!< CPU has SSE3. + kX86FeatureSSSE3, //!< CPU has SSSE3. + kX86FeatureSSE4A, //!< CPU has SSE4.A. + kX86FeatureSSE4_1, //!< CPU has SSE4.1. + kX86FeatureSSE4_2, //!< CPU has SSE4.2. + kX86FeatureMSSE, //!< CPU has Misaligned SSE (MSSE). + kX86FeatureMONITOR, //!< CPU has MONITOR and MWAIT. + kX86FeatureMOVBE, //!< CPU has MOVBE. + kX86FeaturePOPCNT, //!< CPU has POPCNT. + kX86FeatureLZCNT, //!< CPU has LZCNT. + kX86FeatureAESNI, //!< CPU has AESNI. + kX86FeaturePCLMULQDQ, //!< CPU has PCLMULQDQ. + kX86FeatureRDRAND, //!< CPU has RDRAND. + kX86FeatureRDSEED, //!< CPU has RDSEED. + kX86FeatureSMAP, //!< CPU has SMAP (supervisor-mode access prevention). + kX86FeatureSMEP, //!< CPU has SMEP (supervisor-mode execution prevention). + kX86FeatureSHA, //!< CPU has SHA-1 and SHA-256. + kX86FeatureXSAVE, //!< CPU has XSAVE support - XSAVE/XRSTOR, XSETBV/XGETBV, and XCR0. + kX86FeatureXSAVE_OS, //!< OS has enabled XSAVE, you can call XGETBV to get value of XCR0. + kX86FeatureAVX, //!< CPU has AVX. + kX86FeatureAVX2, //!< CPU has AVX2. + kX86FeatureF16C, //!< CPU has F16C. + kX86FeatureFMA3, //!< CPU has FMA3. + kX86FeatureFMA4, //!< CPU has FMA4. + kX86FeatureXOP, //!< CPU has XOP. + kX86FeatureBMI, //!< CPU has BMI (bit manipulation instructions #1). + kX86FeatureBMI2, //!< CPU has BMI2 (bit manipulation instructions #2). + kX86FeatureADX, //!< CPU has ADX (multi-precision add-carry instruction extensions). + kX86FeatureTBM, //!< CPU has TBM (trailing bit manipulation). + kX86FeatureMPX, //!< CPU has MPX (memory protection extensions). + kX86FeatureHLE, //!< CPU has HLE. + kX86FeatureRTM, //!< CPU has RTM. + kX86FeatureERMS, //!< CPU has ERMS (enhanced REP MOVSB/STOSB). + kX86FeatureFSGSBASE, //!< CPU has FSGSBASE. + kX86FeatureAVX512F, //!< CPU has AVX-512F (foundation). + kX86FeatureAVX512CDI, //!< CPU has AVX-512CDI (conflict detection instructions). + kX86FeatureAVX512PFI, //!< CPU has AVX-512PFI (prefetch instructions). + kX86FeatureAVX512ERI, //!< CPU has AVX-512ERI (exponential and reciprocal instructions). + kX86FeatureAVX512DQ, //!< CPU has AVX-512DQ (DWORD/QWORD). + kX86FeatureAVX512BW, //!< CPU has AVX-512BW (BYTE/WORD). + kX86FeatureAVX512VL, //!< CPU has AVX VL (vector length extensions). + kX86FeatureAVX512IFMA, //!< CPU has AVX IFMA (integer fused multiply add using 52-bit precision). + kX86FeatureAVX512VBMI, //!< CPU has AVX VBMI (vector byte manipulation instructions). + + kX86FeaturesCount //!< Count of X86/X64 CPU features. + }; + + //! \internal + enum { + kFeaturesPerUInt32 = static_cast(sizeof(uint32_t)) * 8 + }; + + // -------------------------------------------------------------------------- + // [ArmInfo] + // -------------------------------------------------------------------------- + + struct ArmData { + }; + + // -------------------------------------------------------------------------- + // [X86Info] + // -------------------------------------------------------------------------- + + struct X86Data { + uint32_t _processorType; //!< Processor type. + uint32_t _brandIndex; //!< Brand index. + uint32_t _flushCacheLineSize; //!< Flush cache line size (in bytes). + uint32_t _maxLogicalProcessors; //!< Maximum number of addressable IDs for logical processors. + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CpuInfo() noexcept { reset(); } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + //! Initialize CpuInfo to the given architecture, see \ArchInfo. + ASMJIT_INLINE void initArch(uint32_t archType, uint32_t archMode = 0) noexcept { + _archInfo.init(archType, archMode); + } + + ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(CpuInfo)); } + + // -------------------------------------------------------------------------- + // [Detect] + // -------------------------------------------------------------------------- + + ASMJIT_API void detect() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get generic architecture information. + ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _archInfo; } + //! Get CPU architecture type, see \ArchInfo::Type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return _archInfo.getType(); } + //! Get CPU architecture sub-type, see \ArchInfo::SubType. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return _archInfo.getSubType(); } + + //! Get CPU vendor string. + ASMJIT_INLINE const char* getVendorString() const noexcept { return _vendorString; } + //! Get CPU brand string. + ASMJIT_INLINE const char* getBrandString() const noexcept { return _brandString; } + + //! Get CPU vendor ID. + ASMJIT_INLINE uint32_t getVendorId() const noexcept { return _vendorId; } + //! Get CPU family ID. + ASMJIT_INLINE uint32_t getFamily() const noexcept { return _family; } + //! Get CPU model ID. + ASMJIT_INLINE uint32_t getModel() const noexcept { return _model; } + //! Get CPU stepping. + ASMJIT_INLINE uint32_t getStepping() const noexcept { return _stepping; } + + //! Get number of hardware threads available. + ASMJIT_INLINE uint32_t getHwThreadsCount() const noexcept { + return _hwThreadsCount; + } + + //! Get whether CPU has a `feature`. + ASMJIT_INLINE bool hasFeature(uint32_t feature) const noexcept { + ASMJIT_ASSERT(feature < sizeof(_features) * 8); + + uint32_t pos = feature / kFeaturesPerUInt32; + uint32_t bit = feature % kFeaturesPerUInt32; + + return static_cast((_features[pos] >> bit) & 0x1); + } + + //! Add a CPU `feature`. + ASMJIT_INLINE CpuInfo& addFeature(uint32_t feature) noexcept { + ASMJIT_ASSERT(feature < sizeof(_features) * 8); + + uint32_t pos = feature / kFeaturesPerUInt32; + uint32_t bit = feature % kFeaturesPerUInt32; + + _features[pos] |= static_cast(1) << bit; + return *this; + } + + // -------------------------------------------------------------------------- + // [Accessors - ARM] + // -------------------------------------------------------------------------- + + // -------------------------------------------------------------------------- + // [Accessors - X86] + // -------------------------------------------------------------------------- + + //! Get processor type. + ASMJIT_INLINE uint32_t getX86ProcessorType() const noexcept { + return _x86Data._processorType; + } + + //! Get brand index. + ASMJIT_INLINE uint32_t getX86BrandIndex() const noexcept { + return _x86Data._brandIndex; + } + + //! Get flush cache line size. + ASMJIT_INLINE uint32_t getX86FlushCacheLineSize() const noexcept { + return _x86Data._flushCacheLineSize; + } + + //! Get maximum logical processors count. + ASMJIT_INLINE uint32_t getX86MaxLogicalProcessors() const noexcept { + return _x86Data._maxLogicalProcessors; + } + + // -------------------------------------------------------------------------- + // [Statics] + // -------------------------------------------------------------------------- + + //! Get the host CPU information. + static ASMJIT_API const CpuInfo& getHost() noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + ArchInfo _archInfo; //!< CPU architecture information. + char _vendorString[16]; //!< CPU vendor string. + char _brandString[64]; //!< CPU brand string. + uint32_t _vendorId; //!< CPU vendor id, see \ref Vendor. + uint32_t _family; //!< CPU family ID. + uint32_t _model; //!< CPU model ID. + uint32_t _stepping; //!< CPU stepping. + uint32_t _hwThreadsCount; //!< Number of hardware threads. + uint32_t _features[8]; //!< CPU features (bit-array). + + // Architecture specific data. + union { + ArmData _armData; + X86Data _x86Data; + }; +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_CPUINFO_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/func.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/func.cpp new file mode 100644 index 00000000..b9fc9b7e --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/func.cpp @@ -0,0 +1,186 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/arch.h" +#include "../base/func.h" + +#if defined(ASMJIT_BUILD_X86) +#include "../x86/x86internal_p.h" +#include "../x86/x86operand.h" +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) +#include "../arm/arminternal_p.h" +#include "../arm/armoperand.h" +#endif // ASMJIT_BUILD_ARM + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::CallConv - Init / Reset] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error CallConv::init(uint32_t ccId) noexcept { + reset(); + +#if defined(ASMJIT_BUILD_X86) + if (CallConv::isX86Family(ccId)) + return X86Internal::initCallConv(*this, ccId); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (CallConv::isArmFamily(ccId)) + return ArmInternal::initCallConv(*this, ccId); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArgument); +} + +// ============================================================================ +// [asmjit::FuncDetail - Init / Reset] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& sign) { + uint32_t ccId = sign.getCallConv(); + CallConv& cc = _callConv; + + uint32_t argCount = sign.getArgCount(); + if (ASMJIT_UNLIKELY(argCount > kFuncArgCount)) + return DebugUtils::errored(kErrorInvalidArgument); + + ASMJIT_PROPAGATE(cc.init(ccId)); + + uint32_t gpSize = (cc.getArchType() == ArchInfo::kTypeX86) ? 4 : 8; + uint32_t deabstractDelta = TypeId::deabstractDeltaOfSize(gpSize); + + const uint8_t* args = sign.getArgs(); + for (uint32_t i = 0; i < static_cast(argCount); i++) { + Value& arg = _args[i]; + arg.initTypeId(TypeId::deabstract(args[i], deabstractDelta)); + } + _argCount = static_cast(argCount); + + uint32_t ret = sign.getRet(); + if (ret != TypeId::kVoid) { + _rets[0].initTypeId(TypeId::deabstract(ret, deabstractDelta)); + _retCount = 1; + } + +#if defined(ASMJIT_BUILD_X86) + if (CallConv::isX86Family(ccId)) + return X86Internal::initFuncDetail(*this, sign, gpSize); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (CallConv::isArmFamily(ccId)) + return ArmInternal::initFuncDetail(*this, sign, gpSize); +#endif // ASMJIT_BUILD_ARM + + // We should never bubble here as if `cc.init()` succeeded then there has to + // be an implementation for the current architecture. However, stay safe. + return DebugUtils::errored(kErrorInvalidArgument); +} + +// ============================================================================ +// [asmjit::FuncFrameLayout - Init / Reset] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error FuncFrameLayout::init(const FuncDetail& func, const FuncFrameInfo& ffi) noexcept { + uint32_t ccId = func.getCallConv().getId(); + +#if defined(ASMJIT_BUILD_X86) + if (CallConv::isX86Family(ccId)) + return X86Internal::initFrameLayout(*this, func, ffi); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (CallConv::isArmFamily(ccId)) + return ArmInternal::initFrameLayout(*this, func, ffi); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArgument); +} + +// ============================================================================ +// [asmjit::FuncArgsMapper] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error FuncArgsMapper::updateFrameInfo(FuncFrameInfo& ffi) const noexcept { + const FuncDetail* func = getFuncDetail(); + if (!func) return DebugUtils::errored(kErrorInvalidState); + + uint32_t ccId = func->getCallConv().getId(); + +#if defined(ASMJIT_BUILD_X86) + if (CallConv::isX86Family(ccId)) + return X86Internal::argsToFrameInfo(*this, ffi); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (CallConv::isArmFamily(ccId)) + return ArmInternal::argsToFrameInfo(*this, ffi); +#endif // ASMJIT_BUILD_X86 + + return DebugUtils::errored(kErrorInvalidArch); +} + +// ============================================================================ +// [asmjit::FuncUtils] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error FuncUtils::emitProlog(CodeEmitter* emitter, const FuncFrameLayout& layout) { +#if defined(ASMJIT_BUILD_X86) + if (emitter->getArchInfo().isX86Family()) + return X86Internal::emitProlog(static_cast(emitter), layout); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (emitter->getArchInfo().isArmFamily()) + return ArmInternal::emitProlog(static_cast(emitter), layout); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArch); +} + +ASMJIT_FAVOR_SIZE Error FuncUtils::emitEpilog(CodeEmitter* emitter, const FuncFrameLayout& layout) { +#if defined(ASMJIT_BUILD_X86) + if (emitter->getArchInfo().isX86Family()) + return X86Internal::emitEpilog(static_cast(emitter), layout); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (emitter->getArchInfo().isArmFamily()) + return ArmInternal::emitEpilog(static_cast(emitter), layout); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArch); +} + +ASMJIT_FAVOR_SIZE Error FuncUtils::allocArgs(CodeEmitter* emitter, const FuncFrameLayout& layout, const FuncArgsMapper& args) { +#if defined(ASMJIT_BUILD_X86) + if (emitter->getArchInfo().isX86Family()) + return X86Internal::allocArgs(static_cast(emitter), layout, args); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (emitter->getArchInfo().isArmFamily()) + return ArmInternal::allocArgs(static_cast(emitter), layout, args); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArch); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/func.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/func.h new file mode 100644 index 00000000..518a8530 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/func.h @@ -0,0 +1,1257 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_FUNC_H +#define _ASMJIT_BASE_FUNC_H + +#include "../asmjit_build.h" + +// [Dependencies] +#include "../base/arch.h" +#include "../base/operand.h" +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class CodeEmitter; + +// ============================================================================ +// [asmjit::CallConv] +// ============================================================================ + +//! Function calling convention. +//! +//! Function calling convention is a scheme that defines how function parameters +//! are passed and how function returns its result. AsmJit defines a variety of +//! architecture and OS specific calling conventions and also provides a compile +//! time detection to make JIT code-generation easier. +struct CallConv { + //! Calling convention id. + ASMJIT_ENUM(Id) { + //! None or invalid (can't be used). + kIdNone = 0, + + // ------------------------------------------------------------------------ + // [X86] + // ------------------------------------------------------------------------ + + //! X86 `__cdecl` calling convention (used by C runtime and libraries). + kIdX86CDecl = 1, + //! X86 `__stdcall` calling convention (used mostly by WinAPI). + kIdX86StdCall = 2, + //! X86 `__thiscall` calling convention (MSVC/Intel). + kIdX86MsThisCall = 3, + //! X86 `__fastcall` convention (MSVC/Intel). + kIdX86MsFastCall = 4, + //! X86 `__fastcall` convention (GCC and Clang). + kIdX86GccFastCall = 5, + //! X86 `regparm(1)` convention (GCC and Clang). + kIdX86GccRegParm1 = 6, + //! X86 `regparm(2)` convention (GCC and Clang). + kIdX86GccRegParm2 = 7, + //! X86 `regparm(3)` convention (GCC and Clang). + kIdX86GccRegParm3 = 8, + + //! X64 calling convention defined by WIN64-ABI. + //! + //! Links: + //! * . + kIdX86Win64 = 16, + //! X64 calling convention used by Unix platforms (SYSV/AMD64-ABI). + kIdX86SysV64 = 17, + + // ------------------------------------------------------------------------ + // [ARM] + // ------------------------------------------------------------------------ + + //! Legacy calling convention, floating point arguments are passed via GP registers. + kIdArm32SoftFP = 32, + //! Modern calling convention, uses VFP registers to pass floating point arguments. + kIdArm32HardFP = 33, + + // ------------------------------------------------------------------------ + // [Internal] + // ------------------------------------------------------------------------ + + _kIdX86Start = 1, //!< \internal + _kIdX86End = 8, //!< \internal + + _kIdX64Start = 16, //!< \internal + _kIdX64End = 17, //!< \internal + + _kIdArmStart = 32, //!< \internal + _kIdArmEnd = 33, //!< \internal + + // ------------------------------------------------------------------------ + // [Host] + // ------------------------------------------------------------------------ + +#if defined(ASMJIT_DOCGEN) + //! Default calling convention based on the current C++ compiler's settings. + //! + //! NOTE: This should be always the same as `kIdHostCDecl`, but some + //! compilers allow to override the default calling convention. Overriding + //! is not detected at the moment. + kIdHost = DETECTED_AT_COMPILE_TIME, + + //! Default CDECL calling convention based on the current C++ compiler's settings. + kIdHostCDecl = DETECTED_AT_COMPILE_TIME, + + //! Default STDCALL calling convention based on the current C++ compiler's settings. + //! + //! NOTE: If not defined by the host then it's the same as `kIdHostCDecl`. + kIdHostStdCall = DETECTED_AT_COMPILE_TIME, + + //! Compatibility for `__fastcall` calling convention. + //! + //! NOTE: If not defined by the host then it's the same as `kIdHostCDecl`. + kIdHostFastCall = DETECTED_AT_COMPILE_TIME +#elif ASMJIT_ARCH_X86 + kIdHost = kIdX86CDecl, + kIdHostCDecl = kIdX86CDecl, + kIdHostStdCall = kIdX86StdCall, + kIdHostFastCall = ASMJIT_CC_MSC ? kIdX86MsFastCall : + ASMJIT_CC_GCC ? kIdX86GccFastCall : + ASMJIT_CC_CLANG ? kIdX86GccFastCall : kIdNone +#elif ASMJIT_ARCH_X64 + kIdHost = ASMJIT_OS_WINDOWS ? kIdX86Win64 : kIdX86SysV64, + kIdHostCDecl = kIdHost, // Doesn't exist, redirected to host. + kIdHostStdCall = kIdHost, // Doesn't exist, redirected to host. + kIdHostFastCall = kIdHost // Doesn't exist, redirected to host. +#elif ASMJIT_ARCH_ARM32 +# if defined(__SOFTFP__) + kIdHost = kIdArm32SoftFP, +# else + kIdHost = kIdArm32HardFP, +# endif + // These don't exist on ARM. + kIdHostCDecl = kIdHost, // Doesn't exist, redirected to host. + kIdHostStdCall = kIdHost, // Doesn't exist, redirected to host. + kIdHostFastCall = kIdHost // Doesn't exist, redirected to host. +#else +# error "[asmjit] Couldn't determine the target's calling convention." +#endif + }; + + //! Calling convention algorithm. + //! + //! This is AsmJit specific. It basically describes how should AsmJit convert + //! the function arguments defined by `FuncSignature` into register ids or + //! stack offsets. The default algorithm is a standard algorithm that assigns + //! registers first, and then assigns stack. The Win64 algorithm does register + //! shadowing as defined by `WIN64` calling convention - it applies to 64-bit + //! calling conventions only. + ASMJIT_ENUM(Algorithm) { + kAlgorithmDefault = 0, //!< Default algorithm (cross-platform). + kAlgorithmWin64 = 1 //!< WIN64 specific algorithm. + }; + + //! Calling convention flags. + ASMJIT_ENUM(Flags) { + kFlagCalleePopsStack = 0x01, //!< Callee is responsible for cleaning up the stack. + kFlagPassFloatsByVec = 0x02, //!< Pass F32 and F64 arguments by VEC128 register. + kFlagVectorCall = 0x04, //!< This is a '__vectorcall' calling convention. + kFlagIndirectVecArgs = 0x08 //!< Pass vector arguments indirectly (as a pointer). + }; + + //! Internal limits of CallConv. + ASMJIT_ENUM(Limits) { + kNumRegKinds = 4, //!< Number of RegKinds handled by CallConv. + kNumRegArgsPerKind = 8 //!< Number of maximum arguments passed in register per RegKind. + }; + + //! Passed registers' order. + struct RegOrder { + uint8_t id[kNumRegArgsPerKind]; //!< Passed registers, ordered. + }; + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE bool isX86Family(uint32_t ccId) noexcept { return ccId >= _kIdX86Start && ccId <= _kIdX64End; } + static ASMJIT_INLINE bool isArmFamily(uint32_t ccId) noexcept { return ccId >= _kIdArmStart && ccId <= _kIdArmEnd; } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_API Error init(uint32_t ccId) noexcept; + + ASMJIT_INLINE void reset() noexcept { + ::memset(this, 0, sizeof(*this)); + ::memset(_passedOrder, 0xFF, sizeof(_passedOrder)); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get calling convention id, see \ref Id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + //! Set calling convention id, see \ref Id. + ASMJIT_INLINE void setId(uint32_t id) noexcept { _id = static_cast(id); } + + //! Get architecture type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return _archType; } + //! Set architecture type. + ASMJIT_INLINE void setArchType(uint32_t archType) noexcept { _archType = static_cast(archType); } + + //! Get calling convention algorithm, see \ref Algorithm. + ASMJIT_INLINE uint32_t getAlgorithm() const noexcept { return _algorithm; } + //! Set calling convention algorithm, see \ref Algorithm. + ASMJIT_INLINE void setAlgorithm(uint32_t algorithm) noexcept { _algorithm = static_cast(algorithm); } + + //! Get if the calling convention has the given `flag` set. + ASMJIT_INLINE bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; } + //! Get calling convention flags, see \ref Flags. + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; } + //! Add calling convention flags, see \ref Flags. + ASMJIT_INLINE void setFlags(uint32_t flag) noexcept { _flags = flag; }; + //! Add calling convention flags, see \ref Flags. + ASMJIT_INLINE void addFlags(uint32_t flag) noexcept { _flags |= flag; }; + + //! Get a natural stack alignment. + ASMJIT_INLINE uint32_t getNaturalStackAlignment() const noexcept { return _naturalStackAlignment; } + + //! Set a natural stack alignment. + //! + //! This function can be used to override the default stack alignment in case + //! that you know that it's alignment is different. For example it allows to + //! implement custom calling conventions that guarantee higher stack alignment. + ASMJIT_INLINE void setNaturalStackAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _naturalStackAlignment = static_cast(value); + } + + //! Get if this calling convention specifies 'SpillZone'. + ASMJIT_INLINE bool hasSpillZone() const noexcept { return _spillZoneSize != 0; } + //! Get size of 'SpillZone'. + ASMJIT_INLINE uint32_t getSpillZoneSize() const noexcept { return _spillZoneSize; } + //! Set size of 'SpillZone'. + ASMJIT_INLINE void setSpillZoneSize(uint32_t size) noexcept { _spillZoneSize = static_cast(size); } + + //! Get if this calling convention specifies 'RedZone'. + ASMJIT_INLINE bool hasRedZone() const noexcept { return _redZoneSize != 0; } + //! Get size of 'RedZone'. + ASMJIT_INLINE uint32_t getRedZoneSize() const noexcept { return _redZoneSize; } + //! Set size of 'RedZone'. + ASMJIT_INLINE void setRedZoneSize(uint32_t size) noexcept { _redZoneSize = static_cast(size); } + + ASMJIT_INLINE const uint8_t* getPassedOrder(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kNumRegKinds); + return _passedOrder[kind].id; + } + + ASMJIT_INLINE uint32_t getPassedRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kNumRegKinds); + return _passedRegs[kind]; + } + + ASMJIT_INLINE void _setPassedPacked(uint32_t kind, uint32_t p0, uint32_t p1) noexcept { + ASMJIT_ASSERT(kind < kNumRegKinds); + + reinterpret_cast(_passedOrder[kind].id)[0] = p0; + reinterpret_cast(_passedOrder[kind].id)[1] = p1; + } + + ASMJIT_INLINE void setPassedToNone(uint32_t kind) noexcept { + ASMJIT_ASSERT(kind < kNumRegKinds); + + _setPassedPacked(kind, ASMJIT_PACK32_4x8(0xFF, 0xFF, 0xFF, 0xFF), + ASMJIT_PACK32_4x8(0xFF, 0xFF, 0xFF, 0xFF)); + _passedRegs[kind] = 0; + } + + ASMJIT_INLINE void setPassedOrder(uint32_t kind, uint32_t a0, uint32_t a1 = 0xFF, uint32_t a2 = 0xFF, uint32_t a3 = 0xFF, uint32_t a4 = 0xFF, uint32_t a5 = 0xFF, uint32_t a6 = 0xFF, uint32_t a7 = 0xFF) noexcept { + ASMJIT_ASSERT(kind < kNumRegKinds); + + _setPassedPacked(kind, ASMJIT_PACK32_4x8(a0, a1, a2, a3), + ASMJIT_PACK32_4x8(a4, a5, a6, a7)); + + // NOTE: This should always be called with all arguments known at compile + // time, so even if it looks scary it should be translated to a single + // instruction. + _passedRegs[kind] = (a0 != 0xFF ? 1U << a0 : 0U) | + (a1 != 0xFF ? 1U << a1 : 0U) | + (a2 != 0xFF ? 1U << a2 : 0U) | + (a3 != 0xFF ? 1U << a3 : 0U) | + (a4 != 0xFF ? 1U << a4 : 0U) | + (a5 != 0xFF ? 1U << a5 : 0U) | + (a6 != 0xFF ? 1U << a6 : 0U) | + (a7 != 0xFF ? 1U << a7 : 0U) ; + } + + ASMJIT_INLINE uint32_t getPreservedRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kNumRegKinds); + return _preservedRegs[kind]; + } + + + ASMJIT_INLINE void setPreservedRegs(uint32_t kind, uint32_t regs) noexcept { + ASMJIT_ASSERT(kind < kNumRegKinds); + _preservedRegs[kind] = regs; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t _id; //!< Calling convention id, see \ref Id. + uint8_t _archType; //!< Architecture type (see \ref ArchInfo::Type). + uint8_t _algorithm; //!< Calling convention algorithm. + uint8_t _flags; //!< Calling convention flags. + + uint8_t _naturalStackAlignment; //!< Natural stack alignment as defined by OS/ABI. + uint8_t _spillZoneSize; //!< Spill zone size (WIN64 == 32 bytes). + uint16_t _redZoneSize; //!< Red zone size (AMD64 == 128 bytes). + + RegOrder _passedOrder[kNumRegKinds]; //!< Passed registers' order, per kind. + uint32_t _passedRegs[kNumRegKinds]; //!< Mask of all passed registers, per kind. + uint32_t _preservedRegs[kNumRegKinds]; //!< Mask of all preserved registers, per kind. +}; + +// ============================================================================ +// [asmjit::FuncArgIndex] +// ============================================================================ + +//! Function argument index (lo/hi). +ASMJIT_ENUM(FuncArgIndex) { + //! Maximum number of function arguments supported by AsmJit. + kFuncArgCount = 16, + //! Extended maximum number of arguments (used internally). + kFuncArgCountLoHi = kFuncArgCount * 2, + + //! Index to the LO part of function argument (default). + //! + //! This value is typically omitted and added only if there is HI argument + //! accessed. + kFuncArgLo = 0, + + //! Index to the HI part of function argument. + //! + //! HI part of function argument depends on target architecture. On x86 it's + //! typically used to transfer 64-bit integers (they form a pair of 32-bit + //! integers). + kFuncArgHi = kFuncArgCount +}; + +// ============================================================================ +// [asmjit::FuncSignature] +// ============================================================================ + +//! Function signature. +//! +//! Contains information about function return type, count of arguments and +//! their TypeIds. Function signature is a low level structure which doesn't +//! contain platform specific or calling convention specific information. +struct FuncSignature { + enum { + //! Doesn't have variable number of arguments (`...`). + kNoVarArgs = 0xFF + }; + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + //! Initialize the function signature. + ASMJIT_INLINE void init(uint32_t ccId, uint32_t ret, const uint8_t* args, uint32_t argCount) noexcept { + ASMJIT_ASSERT(ccId <= 0xFF); + ASMJIT_ASSERT(argCount <= 0xFF); + + _callConv = static_cast(ccId); + _argCount = static_cast(argCount); + _vaIndex = kNoVarArgs; + _ret = ret; + _args = args; + } + + ASMJIT_INLINE void reset() noexcept { + memset(this, 0, sizeof(*this)); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the function's calling convention. + ASMJIT_INLINE uint32_t getCallConv() const noexcept { return _callConv; } + + //! Get if the function has variable number of arguments (...). + ASMJIT_INLINE bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; } + //! Get the variable arguments (...) index, `kNoVarArgs` if none. + ASMJIT_INLINE uint32_t getVAIndex() const noexcept { return _vaIndex; } + + //! Get the number of function arguments. + ASMJIT_INLINE uint32_t getArgCount() const noexcept { return _argCount; } + + ASMJIT_INLINE bool hasRet() const noexcept { return _ret != TypeId::kVoid; } + //! Get the return value type. + ASMJIT_INLINE uint32_t getRet() const noexcept { return _ret; } + + //! Get the type of the argument at index `i`. + ASMJIT_INLINE uint32_t getArg(uint32_t i) const noexcept { + ASMJIT_ASSERT(i < _argCount); + return _args[i]; + } + //! Get the array of function arguments' types. + ASMJIT_INLINE const uint8_t* getArgs() const noexcept { return _args; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t _callConv; //!< Calling convention id. + uint8_t _argCount; //!< Count of arguments. + uint8_t _vaIndex; //!< Index to a first vararg or `kNoVarArgs`. + uint8_t _ret; //!< TypeId of a return value. + const uint8_t* _args; //!< TypeIds of function arguments. +}; + +// ============================================================================ +// [asmjit::FuncSignatureT] +// ============================================================================ + +//! \internal +#define T(TYPE) TypeIdOf::kTypeId + +//! Static function signature (no arguments). +template +class FuncSignature0 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature0(uint32_t ccId = CallConv::kIdHost) noexcept { + init(ccId, T(RET), nullptr, 0); + } +}; + +//! Static function signature (1 argument). +template +class FuncSignature1 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature1(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (2 arguments). +template +class FuncSignature2 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature2(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (3 arguments). +template +class FuncSignature3 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature3(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (4 arguments). +template +class FuncSignature4 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature4(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (5 arguments). +template +class FuncSignature5 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature5(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (6 arguments). +template +class FuncSignature6 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature6(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (7 arguments). +template +class FuncSignature7 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature7(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (8 arguments). +template +class FuncSignature8 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature8(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (9 arguments). +template +class FuncSignature9 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature9(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7), T(A8) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (10 arguments). +template +class FuncSignature10 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature10(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7), T(A8), T(A9) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +#if ASMJIT_CC_HAS_VARIADIC_TEMPLATES +//! Static function signature (variadic). +template +class FuncSignatureT : public FuncSignature { +public: + ASMJIT_INLINE FuncSignatureT(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { (T(ARGS))... }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; +#endif // ASMJIT_CC_HAS_VARIADIC_TEMPLATES + +#undef T + +// ============================================================================ +// [asmjit::FuncSignatureX] +// ============================================================================ + +//! Dynamic function signature. +class FuncSignatureX : public FuncSignature { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE FuncSignatureX(uint32_t ccId = CallConv::kIdHost) noexcept { + init(ccId, TypeId::kVoid, _builderArgList, 0); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void setCallConv(uint32_t ccId) noexcept { + ASMJIT_ASSERT(ccId <= 0xFF); + _callConv = static_cast(ccId); + } + + //! Set the return type to `retType`. + ASMJIT_INLINE void setRet(uint32_t retType) noexcept { _ret = retType; } + //! Set the return type based on `T`. + template + ASMJIT_INLINE void setRetT() noexcept { setRet(TypeIdOf::kTypeId); } + + //! Set the argument at index `i` to the `type` + ASMJIT_INLINE void setArg(uint32_t i, uint32_t type) noexcept { + ASMJIT_ASSERT(i < _argCount); + _builderArgList[i] = type; + } + //! Set the argument at index `i` to the type based on `T`. + template + ASMJIT_INLINE void setArgT(uint32_t i) noexcept { setArg(i, TypeIdOf::kTypeId); } + + //! Append an argument of `type` to the function prototype. + ASMJIT_INLINE void addArg(uint32_t type) noexcept { + ASMJIT_ASSERT(_argCount < kFuncArgCount); + _builderArgList[_argCount++] = static_cast(type); + } + //! Append an argument of type based on `T` to the function prototype. + template + ASMJIT_INLINE void addArgT() noexcept { addArg(TypeIdOf::kTypeId); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t _builderArgList[kFuncArgCount]; +}; + +// ============================================================================ +// [asmjit::FuncDetail] +// ============================================================================ + +//! Function detail - CallConv and expanded FuncSignature. +//! +//! Function details is architecture and OS dependent representation of function. +//! It contains calling convention and expanded function signature so all +//! arguments have assigned either register type & id or stack address. +class FuncDetail { +public: + //! Limits are the same as limits defined by \ref CallConv. + ASMJIT_ENUM(Limits) { + kNumRegKinds = CallConv::kNumRegKinds + }; + + //! Argument or return value as defined by `FuncSignature`, but with register + //! or stack address (and other metadata) assigned. + struct Value { + ASMJIT_ENUM(Parts) { + kTypeIdShift = 24, + kTypeIdMask = 0xFF000000U, + + kRegTypeShift = 8, + kRegTypeMask = 0x0000FF00U, + + kRegIdShift = 0, + kRegIdMask = 0x000000FFU, + + kStackOffsetShift = 0, + kStackOffsetMask = 0x0000FFFFU, + + kIsByReg = 0x00010000U, + kIsByStack = 0x00020000U, + kIsIndirect = 0x00040000U + }; + + //! Get if this value is initialized (i.e. contains a valid data). + ASMJIT_INLINE bool isInitialized() const noexcept { return _value != 0; } + //! Initialize this in/out by a given `typeId`. + ASMJIT_INLINE void initTypeId(uint32_t typeId) noexcept { _value = typeId << kTypeIdShift; } + //! Initialize this in/out by a given `typeId`, `regType`, and `regId`. + ASMJIT_INLINE void initReg(uint32_t typeId, uint32_t regType, uint32_t regId) noexcept { + _value = (typeId << kTypeIdShift) | (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsByReg; + } + //! Initialize this in/out by a given `typeId` and `offset`. + ASMJIT_INLINE void initStack(uint32_t typeId, uint32_t stackOffset) noexcept { + _value = (typeId << kTypeIdShift) | (stackOffset << kStackOffsetShift) | kIsByStack; + } + //! Reset the value to its uninitialized and unassigned state. + ASMJIT_INLINE void reset() noexcept { _value = 0; } + + ASMJIT_INLINE void assignToReg(uint32_t regType, uint32_t regId) noexcept { + ASMJIT_ASSERT(!isAssigned()); + _value |= (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsByReg; + } + + ASMJIT_INLINE void assignToStack(int32_t offset) noexcept { + ASMJIT_ASSERT(!isAssigned()); + _value |= (offset << kStackOffsetShift) | kIsByStack; + } + + //! Get if this argument is passed by register. + ASMJIT_INLINE bool byReg() const noexcept { return (_value & kIsByReg) != 0; } + //! Get if this argument is passed by stack. + ASMJIT_INLINE bool byStack() const noexcept { return (_value & kIsByStack) != 0; } + //! Get if this argument is passed by register. + ASMJIT_INLINE bool isAssigned() const noexcept { return (_value & (kIsByReg | kIsByStack)) != 0; } + //! Get if this argument is passed through a pointer (used by WIN64 to pass XMM|YMM|ZMM). + ASMJIT_INLINE bool isIndirect() const noexcept { return (_value & kIsIndirect) != 0; } + + //! Get virtual type of this argument or return value. + ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _value >> kTypeIdShift; } + //! Get a register type of the register used to pass the argument or return the value. + ASMJIT_INLINE uint32_t getRegType() const noexcept { return (_value & kRegTypeMask) >> kRegTypeShift; } + //! Get a physical id of the register used to pass the argument or return the value. + ASMJIT_INLINE uint32_t getRegId() const noexcept { return (_value & kRegIdMask) >> kRegIdShift; } + //! Get a stack offset of this argument (always positive). + ASMJIT_INLINE int32_t getStackOffset() const noexcept { return (_value & kStackOffsetMask) >> kStackOffsetShift; } + + uint32_t _value; + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE FuncDetail() noexcept { reset(); } + ASMJIT_INLINE FuncDetail(const FuncDetail& other) noexcept { + ::memcpy(this, &other, sizeof(*this)); + } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + //! Initialize this `FuncDetail` to the given signature. + ASMJIT_API Error init(const FuncSignature& sign); + ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); } + + // -------------------------------------------------------------------------- + // [Accessors - Calling Convention] + // -------------------------------------------------------------------------- + + //! Get the function's calling convention, see `CallConv`. + ASMJIT_INLINE const CallConv& getCallConv() const noexcept { return _callConv; } + + //! Get CallConv flags, see \ref CallConv::Flags. + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _callConv.getFlags(); } + //! Check if a CallConv `flag` is set, see \ref CallConv::Flags. + ASMJIT_INLINE bool hasFlag(uint32_t ccFlag) const noexcept { return _callConv.hasFlag(ccFlag); } + + // -------------------------------------------------------------------------- + // [Accessors - Arguments and Return] + // -------------------------------------------------------------------------- + + //! Get count of function return values. + ASMJIT_INLINE uint32_t getRetCount() const noexcept { return _retCount; } + //! Get the number of function arguments. + ASMJIT_INLINE uint32_t getArgCount() const noexcept { return _argCount; } + + //! Get whether the function has a return value. + ASMJIT_INLINE bool hasRet() const noexcept { return _retCount != 0; } + //! Get function return value. + ASMJIT_INLINE Value& getRet(size_t index = 0) noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_rets)); + return _rets[index]; + } + //! Get function return value (const). + ASMJIT_INLINE const Value& getRet(size_t index = 0) const noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_rets)); + return _rets[index]; + } + + //! Get function arguments array. + ASMJIT_INLINE Value* getArgs() noexcept { return _args; } + //! Get function arguments array (const). + ASMJIT_INLINE const Value* getArgs() const noexcept { return _args; } + + ASMJIT_INLINE bool hasArg(size_t index) const noexcept { + ASMJIT_ASSERT(index < kFuncArgCountLoHi); + return _args[index].isInitialized(); + } + + //! Get function argument at index `index`. + ASMJIT_INLINE Value& getArg(size_t index) noexcept { + ASMJIT_ASSERT(index < kFuncArgCountLoHi); + return _args[index]; + } + + //! Get function argument at index `index`. + ASMJIT_INLINE const Value& getArg(size_t index) const noexcept { + ASMJIT_ASSERT(index < kFuncArgCountLoHi); + return _args[index]; + } + + ASMJIT_INLINE void resetArg(size_t index) noexcept { + ASMJIT_ASSERT(index < kFuncArgCountLoHi); + _args[index].reset(); + } + + //! Get if the function passes one or more argument by stack. + ASMJIT_INLINE bool hasStackArgs() const noexcept { return _argStackSize != 0; } + //! Get stack size needed for function arguments passed on the stack. + ASMJIT_INLINE uint32_t getArgStackSize() const noexcept { return _argStackSize; } + + ASMJIT_INLINE uint32_t getNaturalStackAlignment() const noexcept { return _callConv.getNaturalStackAlignment(); } + ASMJIT_INLINE uint32_t getSpillZoneSize() const noexcept { return _callConv.getSpillZoneSize(); } + ASMJIT_INLINE uint32_t getRedZoneSize() const noexcept { return _callConv.getRedZoneSize(); } + + ASMJIT_INLINE uint32_t getPassedRegs(uint32_t kind) const noexcept { return _callConv.getPassedRegs(kind); } + ASMJIT_INLINE uint32_t getPreservedRegs(uint32_t kind) const noexcept { return _callConv.getPreservedRegs(kind); } + + ASMJIT_INLINE uint32_t getUsedRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kNumRegKinds); + return _usedRegs[kind]; + } + + ASMJIT_INLINE void addUsedRegs(uint32_t kind, uint32_t regs) noexcept { + ASMJIT_ASSERT(kind < kNumRegKinds); + _usedRegs[kind] |= regs; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CallConv _callConv; //!< Calling convention. + uint8_t _argCount; //!< Number of function arguments. + uint8_t _retCount; //!< Number of function return values. + uint32_t _usedRegs[kNumRegKinds]; //!< Registers that contains arguments (signature dependent). + uint32_t _argStackSize; //!< Size of arguments passed by stack. + Value _rets[2]; //!< Function return values. + Value _args[kFuncArgCountLoHi]; //!< Function arguments. +}; + +// ============================================================================ +// [asmjit::FuncFrameInfo] +// ============================================================================ + +//! Function-frame information. +//! +//! This structure can be used to create a function frame in a cross-platform +//! way. It contains information about the function's stack to be used and +//! registers to be saved and restored. Based on this information in can +//! calculate the optimal layout of a function as \ref FuncFrameLayout. +struct FuncFrameInfo { + //! Limits are the same as limits defined by \ref CallConv. + ASMJIT_ENUM(Limits) { + kNumRegKinds = CallConv::kNumRegKinds + }; + + //! Attributes. + //! + //! Attributes are designed in a way that all are initially false, and user + //! or function-frame finalizer sets them when necessary. Architecture-specific + //! attributes are prefixed with the architecture name. + ASMJIT_ENUM(Attributes) { + kAttrPreserveFP = 0x00000001U, //!< Preserve frame pointer (EBP|RBP). + kAttrCompactPE = 0x00000002U, //!< Use smaller, but possibly slower prolog/epilog. + kAttrHasCalls = 0x00000004U, //!< Function calls other functions (is not leaf). + + kX86AttrAlignedVecSR = 0x00010000U, //!< Use aligned save/restore of VEC regs. + kX86AttrMmxCleanup = 0x00020000U, //!< Emit EMMS instruction in epilog (X86). + kX86AttrAvxCleanup = 0x00040000U, //!< Emit VZEROUPPER instruction in epilog (X86). + kX86AttrAvxEnabled = 0x00080000U //!< Use AVX instead of SSE for all operations (X86). + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE FuncFrameInfo() noexcept { reset(); } + + ASMJIT_INLINE FuncFrameInfo(const FuncFrameInfo& other) noexcept { + ::memcpy(this, &other, sizeof(*this)); + } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void reset() noexcept { + ::memset(this, 0, sizeof(*this)); + _stackArgsRegId = kInvalidReg; + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get frame-info flags, see \ref Attributes. + ASMJIT_INLINE uint32_t getAttributes() const noexcept { return _attributes; } + //! Check if a frame-info `flag` is set, see \ref Attributes. + ASMJIT_INLINE bool hasAttribute(uint32_t attr) const noexcept { return (_attributes & attr) != 0; } + //! Add `flags` to the frame-info, see \ref Attributes. + ASMJIT_INLINE void addAttributes(uint32_t attrs) noexcept { _attributes |= attrs; } + //! Clear `flags` from the frame-info, see \ref Attributes. + ASMJIT_INLINE void clearAttributes(uint32_t attrs) noexcept { _attributes &= ~attrs; } + + //! Get if the function preserves frame pointer (EBP|ESP on X86). + ASMJIT_INLINE bool hasPreservedFP() const noexcept { return (_attributes & kAttrPreserveFP) != 0; } + //! Enable preserved frame pointer. + ASMJIT_INLINE void enablePreservedFP() noexcept { _attributes |= kAttrPreserveFP; } + //! Disable preserved frame pointer. + ASMJIT_INLINE void disablePreservedFP() noexcept { _attributes &= ~kAttrPreserveFP; } + + //! Get if the function prolog and epilog should be compacted (as small as possible). + ASMJIT_INLINE bool hasCompactPE() const noexcept { return (_attributes & kAttrCompactPE) != 0; } + //! Enable compact prolog/epilog. + ASMJIT_INLINE void enableCompactPE() noexcept { _attributes |= kAttrCompactPE; } + //! Disable compact prolog/epilog. + ASMJIT_INLINE void disableCompactPE() noexcept { _attributes &= ~kAttrCompactPE; } + + //! Get if the function calls other functions. + ASMJIT_INLINE bool hasCalls() const noexcept { return (_attributes & kAttrHasCalls) != 0; } + //! Set `kFlagHasCalls` to true. + ASMJIT_INLINE void enableCalls() noexcept { _attributes |= kAttrHasCalls; } + //! Set `kFlagHasCalls` to false. + ASMJIT_INLINE void disableCalls() noexcept { _attributes &= ~kAttrHasCalls; } + + //! Get if the function contains MMX cleanup - 'emms' instruction in epilog. + ASMJIT_INLINE bool hasMmxCleanup() const noexcept { return (_attributes & kX86AttrMmxCleanup) != 0; } + //! Enable MMX cleanup. + ASMJIT_INLINE void enableMmxCleanup() noexcept { _attributes |= kX86AttrMmxCleanup; } + //! Disable MMX cleanup. + ASMJIT_INLINE void disableMmxCleanup() noexcept { _attributes &= ~kX86AttrMmxCleanup; } + + //! Get if the function contains AVX cleanup - 'vzeroupper' instruction in epilog. + ASMJIT_INLINE bool hasAvxCleanup() const noexcept { return (_attributes & kX86AttrAvxCleanup) != 0; } + //! Enable AVX cleanup. + ASMJIT_INLINE void enableAvxCleanup() noexcept { _attributes |= kX86AttrAvxCleanup; } + //! Disable AVX cleanup. + ASMJIT_INLINE void disableAvxCleanup() noexcept { _attributes &= ~kX86AttrAvxCleanup; } + + //! Get if the function contains AVX cleanup - 'vzeroupper' instruction in epilog. + ASMJIT_INLINE bool isAvxEnabled() const noexcept { return (_attributes & kX86AttrAvxEnabled) != 0; } + //! Enable AVX cleanup. + ASMJIT_INLINE void enableAvx() noexcept { _attributes |= kX86AttrAvxEnabled; } + //! Disable AVX cleanup. + ASMJIT_INLINE void disableAvx() noexcept { _attributes &= ~kX86AttrAvxEnabled; } + + //! Get which registers (by `kind`) are saved/restored in prolog/epilog, respectively. + ASMJIT_INLINE uint32_t getDirtyRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kNumRegKinds); + return _dirtyRegs[kind]; + } + + //! Set which registers (by `kind`) are saved/restored in prolog/epilog, respectively. + ASMJIT_INLINE void setDirtyRegs(uint32_t kind, uint32_t regs) noexcept { + ASMJIT_ASSERT(kind < kNumRegKinds); + _dirtyRegs[kind] = regs; + } + + //! Add registers (by `kind`) to saved/restored registers. + ASMJIT_INLINE void addDirtyRegs(uint32_t kind, uint32_t regs) noexcept { + ASMJIT_ASSERT(kind < kNumRegKinds); + _dirtyRegs[kind] |= regs; + } + + ASMJIT_INLINE void setAllDirty() noexcept { + _dirtyRegs[0] = 0xFFFFFFFFU; + _dirtyRegs[1] = 0xFFFFFFFFU; + _dirtyRegs[2] = 0xFFFFFFFFU; + _dirtyRegs[3] = 0xFFFFFFFFU; + } + + ASMJIT_INLINE void setAllDirty(uint32_t kind) noexcept { + ASMJIT_ASSERT(kind < kNumRegKinds); + _dirtyRegs[kind] = 0xFFFFFFFFU; + } + + //! Get stack-frame size used by the function. + ASMJIT_INLINE uint32_t getStackFrameSize() const noexcept { return _stackFrameSize; } + //! Get call-frame size used by the function. + ASMJIT_INLINE uint32_t getCallFrameSize() const noexcept { return _callFrameSize; } + + //! Get minimum stack-frame alignment required by the function. + ASMJIT_INLINE uint32_t getStackFrameAlignment() const noexcept { return _stackFrameAlignment; } + //! Get minimum call-frame alignment required by the function. + ASMJIT_INLINE uint32_t getCallFrameAlignment() const noexcept { return _callFrameAlignment; } + + ASMJIT_INLINE void setStackFrameSize(uint32_t size) noexcept { _stackFrameSize = size; } + ASMJIT_INLINE void setCallFrameSize(uint32_t size) noexcept { _callFrameSize = size; } + + ASMJIT_INLINE void setStackFrameAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _stackFrameAlignment = static_cast(value); + } + + ASMJIT_INLINE void setCallFrameAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _callFrameAlignment = static_cast(value); + } + + ASMJIT_INLINE void mergeStackFrameSize(uint32_t size) noexcept { _stackFrameSize = Utils::iMax(_stackFrameSize, size); } + ASMJIT_INLINE void mergeCallFrameSize(uint32_t size) noexcept { _callFrameSize = Utils::iMax(_callFrameSize, size); } + + ASMJIT_INLINE void mergeStackFrameAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _stackFrameAlignment = static_cast(Utils::iMax(_stackFrameAlignment, value)); + } + + ASMJIT_INLINE void mergeCallFrameAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _callFrameAlignment = static_cast(Utils::iMax(_callFrameAlignment, value)); + } + + ASMJIT_INLINE bool hasStackArgsRegId() const noexcept { return _stackArgsRegId != kInvalidReg; } + ASMJIT_INLINE uint32_t getStackArgsRegId() const noexcept { return _stackArgsRegId; } + ASMJIT_INLINE void setStackArgsRegId(uint32_t regId) { _stackArgsRegId = regId; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _attributes; //!< Function attributes. + uint32_t _dirtyRegs[kNumRegKinds]; //!< Registers used by the function. + + uint8_t _stackFrameAlignment; //!< Minimum alignment of stack-frame. + uint8_t _callFrameAlignment; //!< Minimum alignment of call-frame. + uint8_t _stackArgsRegId; //!< Register that holds base-address to arguments passed by stack. + + uint32_t _stackFrameSize; //!< Size of a stack-frame used by the function. + uint32_t _callFrameSize; //!< Size of a call-frame (not part of _stackFrameSize). +}; + +// ============================================================================ +// [asmjit::FuncFrameLayout] +// ============================================================================ + +//! Function-frame layout. +//! +//! Function layout is used directly by prolog and epilog insertion helpers. It +//! contains only information necessary to insert proper prolog and epilog, and +//! should be always calculated from \ref FuncDetail and \ref FuncFrameInfo, where +//! \ref FuncDetail defines function's calling convention and signature, and \ref +//! FuncFrameInfo specifies how much stack is used, and which registers are dirty. +struct FuncFrameLayout { + //! Limits are the same as limits defined by \ref CallConv. + ASMJIT_ENUM(Limits) { + kNumRegKinds = CallConv::kNumRegKinds + }; + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_API Error init(const FuncDetail& func, const FuncFrameInfo& ffi) noexcept; + ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool hasPreservedFP() const noexcept { return static_cast(_preservedFP); } + ASMJIT_INLINE bool hasDsaSlotUsed() const noexcept { return static_cast(_dsaSlotUsed); } + ASMJIT_INLINE bool hasAlignedVecSR() const noexcept { return static_cast(_alignedVecSR); } + ASMJIT_INLINE bool hasDynamicAlignment() const noexcept { return static_cast(_dynamicAlignment); } + + ASMJIT_INLINE bool hasMmxCleanup() const noexcept { return static_cast(_mmxCleanup); } + ASMJIT_INLINE bool hasAvxCleanup() const noexcept { return static_cast(_avxCleanup); } + ASMJIT_INLINE bool isAvxEnabled() const noexcept { return static_cast(_avxEnabled); } + + ASMJIT_INLINE uint32_t getSavedRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kNumRegKinds); + return _savedRegs[kind]; + } + + //! Get stack size. + ASMJIT_INLINE uint32_t getStackSize() const noexcept { return _stackSize; } + //! Get stack alignment. + ASMJIT_INLINE uint32_t getStackAlignment() const noexcept { return _stackAlignment; } + //! Get the offset needed to access the function's stack (it skips call-stack). + ASMJIT_INLINE uint32_t getStackBaseOffset() const noexcept { return _stackBaseOffset; } + + //! Get stack size required to save GP registers. + ASMJIT_INLINE uint32_t getGpStackSize() const noexcept { return _gpStackSize; } + //! Get stack size required to save VEC registers. + ASMJIT_INLINE uint32_t getVecStackSize() const noexcept { return _vecStackSize; } + + ASMJIT_INLINE uint32_t getGpStackOffset() const noexcept { return _gpStackOffset; } + ASMJIT_INLINE uint32_t getVecStackOffset() const noexcept { return _vecStackOffset; } + + ASMJIT_INLINE uint32_t getStackArgsRegId() const noexcept { return _stackArgsRegId; } + ASMJIT_INLINE uint32_t getStackArgsOffset() const noexcept { return _stackArgsOffset; } + + ASMJIT_INLINE bool hasStackAdjustment() const noexcept { return _stackAdjustment != 0; } + ASMJIT_INLINE uint32_t getStackAdjustment() const noexcept { return _stackAdjustment; } + + ASMJIT_INLINE bool hasCalleeStackCleanup() const noexcept { return _calleeStackCleanup != 0; } + ASMJIT_INLINE uint32_t getCalleeStackCleanup() const noexcept { return _calleeStackCleanup; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t _stackAlignment; //!< Final stack alignment of the functions. + uint8_t _stackBaseRegId; //!< GP register that holds address of base stack address. + uint8_t _stackArgsRegId; //!< GP register that holds address of the first argument passed by stack. + + uint32_t _savedRegs[kNumRegKinds]; //!< Registers that will be saved/restored in prolog/epilog. + + uint32_t _preservedFP : 1; //!< Function preserves frame-pointer. + uint32_t _dsaSlotUsed : 1; //!< True if `_dsaSlot` contains a valid memory slot/offset. + uint32_t _alignedVecSR : 1; //!< Use instructions that perform aligned ops to save/restore XMM regs. + uint32_t _dynamicAlignment : 1; //!< Function must dynamically align the stack. + + uint32_t _mmxCleanup : 1; //!< Emit 'emms' in epilog (X86). + uint32_t _avxCleanup : 1; //!< Emit 'vzeroupper' in epilog (X86). + uint32_t _avxEnabled : 1; //!< Use AVX instead of SSE for SIMD saves/restores (X86). + + uint32_t _stackSize; //!< Stack size (sum of function's stack and call stack). + uint32_t _stackBaseOffset; //!< Stack offset (non-zero if kFlagHasCalls is set). + uint32_t _stackAdjustment; //!< Stack adjustment in prolog/epilog. + uint32_t _stackArgsOffset; //!< Offset to the first argument passed by stack of _stackArgsRegId. + + uint32_t _dsaSlot; //!< Memory slot where the prolog inserter stores previous (unaligned) ESP. + uint16_t _calleeStackCleanup; //!< How many bytes the callee should add to the stack (X86 STDCALL). + uint16_t _gpStackSize; //!< Stack size required to save GP regs. + uint16_t _vecStackSize; //!< Stack size required to save VEC regs. + uint32_t _gpStackOffset; //!< Offset where saved GP regs are stored. + uint32_t _vecStackOffset; //!< Offset where saved GP regs are stored. +}; + +// ============================================================================ +// [asmjit::FuncArgsMapper] +// ============================================================================ + +//! Assign a physical register to each function argument. +//! +//! This is used to specify where each function argument should be shuffled +//! or allocated (in case it's passed by stack). +class FuncArgsMapper { +public: + struct Value { + // NOTE: The layout is compatible with FuncDetail::Value except stack. + ASMJIT_ENUM(Parts) { + kTypeIdShift = 24, + kTypeIdMask = 0xFF000000U, + + kRegTypeShift = 8, + kRegTypeMask = 0x0000FF00U, + + kRegIdShift = 0, + kRegIdMask = 0x000000FFU, + + kIsAssigned = 0x00010000U + }; + + //! Get if this value is initialized (i.e. contains a valid data). + ASMJIT_INLINE bool isAssigned() const noexcept { return _value != 0; } + //! Initialize this in/out by a given `typeId`, `regType`, and `regId`. + ASMJIT_INLINE void assign(uint32_t typeId, uint32_t regType, uint32_t regId) noexcept { + _value = (typeId << kTypeIdShift) | (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsAssigned; + } + //! Reset the value to its unassigned state. + ASMJIT_INLINE void reset() noexcept { _value = 0; } + + //! Get virtual type of this argument or return value. + ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _value >> kTypeIdShift; } + //! Get a register type of the register used to pass the argument or return the value. + ASMJIT_INLINE uint32_t getRegType() const noexcept { return (_value & kRegTypeMask) >> kRegTypeShift; } + //! Get a physical id of the register used to pass the argument or return the value. + ASMJIT_INLINE uint32_t getRegId() const noexcept { return (_value & kRegIdMask) >> kRegIdShift; } + + uint32_t _value; + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + explicit ASMJIT_INLINE FuncArgsMapper(const FuncDetail* fd) noexcept { reset(fd); } + ASMJIT_INLINE FuncArgsMapper(const FuncArgsMapper& other) noexcept { + ::memcpy(this, &other, sizeof(*this)); + } + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void reset(const FuncDetail* fd = nullptr) noexcept { + _funcDetail = fd; + ::memset(_args, 0, sizeof(_args)); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE const FuncDetail* getFuncDetail() const noexcept { return _funcDetail; } + ASMJIT_INLINE void setFuncDetail(const FuncDetail* fd) noexcept { _funcDetail = fd; } + + ASMJIT_INLINE Value& getArg(size_t index) noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args)); + return _args[index]; + } + ASMJIT_INLINE const Value& getArg(size_t index) const noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args)); + return _args[index]; + } + + ASMJIT_INLINE bool isAssigned(size_t index) const noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args)); + return _args[index].isAssigned(); + } + + ASMJIT_INLINE void assign(size_t index, const Reg& reg, uint32_t typeId = TypeId::kVoid) noexcept { + // Not designed for virtual registers. + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args)); + ASMJIT_ASSERT(reg.isPhysReg()); + + _args[index].assign(typeId, reg.getRegType(), reg.getId()); + } + + // NOTE: All `assignAll()` methods are shortcuts to assign all arguments at + // once, however, since registers are passed all at once these initializers + // don't provide any way to pass TypeId and/or to keep any argument between + // the arguments passed uninitialized. + ASMJIT_INLINE void assignAll(const Reg& a0) noexcept { + assign(0, a0); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1) noexcept { + assign(0, a0); assign(1, a1); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + assign(4, a4); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + assign(4, a4); assign(5, a5); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5, const Reg& a6) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + assign(4, a4); assign(5, a5); assign(6, a6); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5, const Reg& a6, const Reg& a7) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + assign(4, a4); assign(5, a5); assign(6, a6); assign(7, a7); + } + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + //! Update `FuncFrameInfo` accordingly to FuncArgsMapper. + //! + //! This method must be called if you use `FuncArgsMapper` and you plan to + //! use `FuncUtils::allocArgs()` to remap all arguments after the prolog is + //! inserted. + ASMJIT_API Error updateFrameInfo(FuncFrameInfo& ffi) const noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + const FuncDetail* _funcDetail; //!< Function detail. + Value _args[kFuncArgCountLoHi]; //!< Mapping of each function argument. +}; + +// ============================================================================ +// [asmjit::FuncUtils] +// ============================================================================ + +struct FuncUtils { + static ASMJIT_API Error emitProlog(CodeEmitter* emitter, const FuncFrameLayout& layout); + static ASMJIT_API Error emitEpilog(CodeEmitter* emitter, const FuncFrameLayout& layout); + static ASMJIT_API Error allocArgs(CodeEmitter* emitter, const FuncFrameLayout& layout, const FuncArgsMapper& args); +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_FUNC_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/globals.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/globals.cpp new file mode 100644 index 00000000..129ca039 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/globals.cpp @@ -0,0 +1,124 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/globals.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::DebugUtils] +// ============================================================================ + +#if !defined(ASMJIT_DISABLE_TEXT) +static const char errorMessages[] = + "Ok\0" + "No heap memory\0" + "No virtual memory\0" + "Invalid argument\0" + "Invalid state\0" + "Invalid architecture\0" + "Not initialized\0" + "Already initialized\0" + "Slot occupied\0" + "No code generated\0" + "Code too large\0" + "Invalid label\0" + "Label index overflow\0" + "Label already bound\0" + "Label already defined\0" + "Label name too long\0" + "Invalid label name\0" + "Invalid parent label\0" + "Non-local label can't have parent\0" + "Relocation index overflow\0" + "Invalid relocation entry\0" + "Invalid instruction\0" + "Invalid register type\0" + "Invalid register's physical id\0" + "Invalid register's virtual id\0" + "Invalid rex prefix\0" + "Invalid mask, expected {k}\0" + "Invalid use of {k}\0" + "Invalid use of {k}{z}\0" + "Invalid broadcast {1tox}\0" + "Invalid {sae} or {rc} option\0" + "Invalid address\0" + "Invalid address index\0" + "Invalid address scale\0" + "Invalid use of 64-bit address\0" + "Invalid displacement\0" + "Invalid segment\0" + "Operand size mismatch\0" + "Ambiguous operand size\0" + "Invalid type-info\0" + "Invalid use of a low 8-bit GPB register\0" + "Invalid use of a 64-bit GPQ register in 32-bit mode\0" + "Invalid use of an 80-bit float\0" + "No more physical registers\0" + "Overlapping register arguments\0" + "Overlapping register and arguments base-address register\0" + "Unknown error\0"; + +ASMJIT_FAVOR_SIZE static const char* findPackedString(const char* p, uint32_t id, uint32_t maxId) noexcept { + uint32_t i = 0; + + if (id > maxId) + id = maxId; + + while (i < id) { + while (p[0]) + p++; + + p++; + i++; + } + + return p; +} +#endif // ASMJIT_DISABLE_TEXT + +ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept { +#if !defined(ASMJIT_DISABLE_TEXT) + return findPackedString(errorMessages, err, kErrorCount); +#else + static const char noMessage[] = ""; + return noMessage; +#endif +} + +ASMJIT_FAVOR_SIZE void DebugUtils::debugOutput(const char* str) noexcept { +#if ASMJIT_OS_WINDOWS + ::OutputDebugStringA(str); +#else + ::fputs(str, stderr); +#endif +} + +ASMJIT_FAVOR_SIZE void DebugUtils::assertionFailed(const char* file, int line, const char* msg) noexcept { + char str[1024]; + + snprintf(str, 1024, + "[asmjit] Assertion failed at %s (line %d):\n" + "[asmjit] %s\n", file, line, msg); + + // Support buggy `snprintf` implementations. + str[1023] = '\0'; + + debugOutput(str); + ::abort(); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/globals.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/globals.h new file mode 100644 index 00000000..3005927a --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/globals.h @@ -0,0 +1,281 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_GLOBALS_H +#define _ASMJIT_BASE_GLOBALS_H + +// [Dependencies] +#include "../asmjit_build.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::TypeDefs] +// ============================================================================ + +//! AsmJit error core (uint32_t). +typedef uint32_t Error; + +// ============================================================================ +// [asmjit::GlobalDefs] +// ============================================================================ + +//! Invalid index +//! +//! Invalid index is the last possible index that is never used in practice. In +//! AsmJit it is used exclusively with strings to indicate the the length of the +//! string is not known and has to be determined. +static const size_t kInvalidIndex = ~static_cast(0); + +//! Invalid base address. +static const uint64_t kNoBaseAddress = ~static_cast(0); + +//! Global constants. +ASMJIT_ENUM(GlobalDefs) { + //! Invalid instruction. + kInvalidInst = 0, + //! Invalid register id. + kInvalidReg = 0xFF, + //! Invalid value or id. + kInvalidValue = 0xFFFFFFFF, + + //! Host memory allocator overhead. + //! + //! The overhead is decremented from all zone allocators so the operating + //! system doesn't have to allocate one extra virtual page to keep tract of + //! the requested memory block. + //! + //! The number is actually a guess. + kMemAllocOverhead = sizeof(intptr_t) * 4, + + //! Memory grow threshold. + kMemAllocGrowMax = 8192 * 1024 +}; + +// ============================================================================ +// [asmjit::ptr_cast] +// ============================================================================ + +//! Cast designed to cast between function and void* pointers. +template +ASMJIT_INLINE Dst ptr_cast(Src p) noexcept { return (Dst)p; } + +// ============================================================================ +// [asmjit::ErrorCode] +// ============================================================================ + +//! AsmJit error codes. +ASMJIT_ENUM(ErrorCode) { + //! No error (success). + //! + //! This is default state and state you want. + kErrorOk = 0, + + //! Heap memory allocation failed. + kErrorNoHeapMemory, + + //! Virtual memory allocation failed. + kErrorNoVirtualMemory, + + //! Invalid argument. + kErrorInvalidArgument, + + //! Invalid state. + //! + //! If this error is returned it means that either you are doing something + //! wrong or AsmJit caught itself by doing something wrong. This error should + //! not be underestimated. + kErrorInvalidState, + + //! Incompatible architecture. + kErrorInvalidArch, + + //! The object is not initialized. + kErrorNotInitialized, + //! The object is already initialized. + kErrorAlreadyInitialized, + + //! CodeHolder can't have attached more than one \ref Assembler at a time. + kErrorSlotOccupied, + + //! No code generated. + //! + //! Returned by runtime if the \ref CodeHolder contains no code. + kErrorNoCodeGenerated, + //! Code generated is larger than allowed. + kErrorCodeTooLarge, + + //! Attempt to use uninitialized label. + kErrorInvalidLabel, + //! Label index overflow - a single `Assembler` instance can hold more than + //! 2 billion labels (2147483391 to be exact). If there is an attempt to + //! create more labels this error is returned. + kErrorLabelIndexOverflow, + //! Label is already bound. + kErrorLabelAlreadyBound, + //! Label is already defined (named labels). + kErrorLabelAlreadyDefined, + //! Label name is too long. + kErrorLabelNameTooLong, + //! Label must always be local if it's anonymous (without a name). + kErrorInvalidLabelName, + //! Parent id passed to `CodeHolder::newNamedLabelId()` was invalid. + kErrorInvalidParentLabel, + //! Parent id specified for a non-local (global) label. + kErrorNonLocalLabelCantHaveParent, + + //! Relocation index overflow. + kErrorRelocIndexOverflow, + //! Invalid relocation entry. + kErrorInvalidRelocEntry, + + //! Invalid instruction. + kErrorInvalidInstruction, + //! Invalid register type. + kErrorInvalidRegType, + //! Invalid register's physical id. + kErrorInvalidPhysId, + //! Invalid register's virtual id. + kErrorInvalidVirtId, + //! Invalid REX prefix. + kErrorInvalidRexPrefix, + //! Invalid mask register (not 'k'). + kErrorInvalidKMaskReg, + //! Invalid {k} use (not supported by the instruction). + kErrorInvalidKMaskUse, + //! Invalid {k}{z} use (not supported by the instruction). + kErrorInvalidKZeroUse, + //! Invalid broadcast - Currently only related to invalid use of AVX-512 {1tox}. + kErrorInvalidBroadcast, + //! Invalid 'embedded-rounding' {er} or 'suppress-all-exceptions' {sae} (AVX-512). + kErrorInvalidEROrSAE, + //! Invalid address used (not encodable). + kErrorInvalidAddress, + //! Invalid index register used in memory address (not encodable). + kErrorInvalidAddressIndex, + //! Invalid address scale (not encodable). + kErrorInvalidAddressScale, + //! Invalid use of 64-bit address. + kErrorInvalidAddress64Bit, + //! Invalid displacement (not encodable). + kErrorInvalidDisplacement, + //! Invalid segment. + kErrorInvalidSegment, + + //! Mismatching operand size (size of multiple operands doesn't match the operation size). + kErrorOperandSizeMismatch, + //! Ambiguous operand size (memory has zero size while it's required to determine the operation type. + kErrorAmbiguousOperandSize, + + //! Invalid TypeId. + kErrorInvalidTypeId, + //! Invalid use of a 8-bit GPB-HIGH register. + kErrorInvalidUseOfGpbHi, + //! Invalid use of a 64-bit GPQ register in 32-bit mode. + kErrorInvalidUseOfGpq, + //! Invalid use of an 80-bit float (TypeId::kF80). + kErrorInvalidUseOfF80, + + //! AsmJit requires a physical register, but no one is available. + kErrorNoMorePhysRegs, + //! A variable has been assigned more than once to a function argument (CodeCompiler). + kErrorOverlappingRegArgs, + //! Invalid register to hold stack arguments offset. + kErrorOverlappingStackRegWithRegArg, + + //! Count of AsmJit error codes. + kErrorCount +}; + +// ============================================================================ +// [asmjit::Init / NoInit] +// ============================================================================ + +#if !defined(ASMJIT_DOCGEN) +struct _Init {}; +static const _Init Init = {}; + +struct _NoInit {}; +static const _NoInit NoInit = {}; +#endif // !ASMJIT_DOCGEN + +// ============================================================================ +// [asmjit::DebugUtils] +// ============================================================================ + +namespace DebugUtils { + +//! Returns the error `err` passed. +//! +//! Provided for debugging purposes. Putting a breakpoint inside `errored` can +//! help with tracing the origin of any error reported / returned by AsmJit. +static ASMJIT_INLINE Error errored(Error err) noexcept { return err; } + +//! Get a printable version of `asmjit::Error` code. +ASMJIT_API const char* errorAsString(Error err) noexcept; + +//! Called in debug build to output a debugging message caused by assertion +//! failure or tracing (if enabled by ASMJIT_TRACE). +ASMJIT_API void debugOutput(const char* str) noexcept; + +//! Called in debug build on assertion failure. +//! +//! \param file Source file name where it happened. +//! \param line Line in the source file. +//! \param msg Message to display. +//! +//! If you have problems with assertions put a breakpoint at assertionFailed() +//! function (asmjit/base/globals.cpp) and check the call stack to locate the +//! failing code. +ASMJIT_API void ASMJIT_NORETURN assertionFailed(const char* file, int line, const char* msg) noexcept; + +#if defined(ASMJIT_DEBUG) +# define ASMJIT_ASSERT(exp) \ + do { \ + if (ASMJIT_LIKELY(exp)) \ + break; \ + ::asmjit::DebugUtils::assertionFailed(__FILE__, __LINE__, #exp); \ + } while (0) +# define ASMJIT_NOT_REACHED() \ + do { \ + ::asmjit::DebugUtils::assertionFailed(__FILE__, __LINE__, \ + "ASMJIT_NOT_REACHED has been reached"); \ + ASMJIT_ASSUME(0); \ + } while (0) +#else +# define ASMJIT_ASSERT(exp) ASMJIT_NOP +# define ASMJIT_NOT_REACHED() ASMJIT_ASSUME(0) +#endif // DEBUG + +//! \internal +//! +//! Used by AsmJit to propagate a possible `Error` produced by `...` to the caller. +#define ASMJIT_PROPAGATE(...) \ + do { \ + ::asmjit::Error _err = __VA_ARGS__; \ + if (ASMJIT_UNLIKELY(_err)) return _err; \ + } while (0) + +} // DebugUtils namespace + +//! \} + +} // asmjit namespace + +//! \} + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_GLOBALS_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/logging.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/logging.cpp new file mode 100644 index 00000000..32262660 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/logging.cpp @@ -0,0 +1,202 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Guard] +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_LOGGING) + +// [Dependencies] +#include "../base/logging.h" +#include "../base/utils.h" +#include + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::LogUtil] +// ============================================================================ + +Error LogUtil::formatLine(StringBuilder& sb, const uint8_t* binData, size_t binLen, size_t dispLen, size_t imLen, const char* comment) noexcept { + size_t currentLen = sb.getLength(); + size_t commentLen = comment ? Utils::strLen(comment, kMaxCommentLength) : 0; + + ASMJIT_ASSERT(binLen >= dispLen); + + if ((binLen != 0 && binLen != kInvalidIndex) || commentLen) { + size_t align = kMaxInstLength; + char sep = ';'; + + for (size_t i = (binLen == kInvalidIndex); i < 2; i++) { + size_t begin = sb.getLength(); + + // Append align. + if (currentLen < align) + ASMJIT_PROPAGATE(sb.appendChars(' ', align - currentLen)); + + // Append separator. + if (sep) { + ASMJIT_PROPAGATE(sb.appendChar(sep)); + ASMJIT_PROPAGATE(sb.appendChar(' ')); + } + + // Append binary data or comment. + if (i == 0) { + ASMJIT_PROPAGATE(sb.appendHex(binData, binLen - dispLen - imLen)); + ASMJIT_PROPAGATE(sb.appendChars('.', dispLen * 2)); + ASMJIT_PROPAGATE(sb.appendHex(binData + binLen - imLen, imLen)); + if (commentLen == 0) break; + } + else { + ASMJIT_PROPAGATE(sb.appendString(comment, commentLen)); + } + + currentLen += sb.getLength() - begin; + align += kMaxBinaryLength; + sep = '|'; + } + } + + return sb.appendChar('\n'); +} + +// ============================================================================ +// [asmjit::Formatter - Construction / Destruction] +// ============================================================================ + +Formatter::Formatter() noexcept + : _virtRegHandlerFunc(nullptr), + _virtRegHandlerData(nullptr) {} +Formatter::~Formatter() noexcept {} + +// ============================================================================ +// [asmjit::Logger - Construction / Destruction] +// ============================================================================ + +Logger::Logger() noexcept { + _options = 0; + ::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation)); +} +Logger::~Logger() noexcept {} + +// ============================================================================ +// [asmjit::Logger - Logging] +// ============================================================================ + +Error Logger::logf(const char* fmt, ...) noexcept { + Error err; + + va_list ap; + va_start(ap, fmt); + err = logv(fmt, ap); + va_end(ap); + + return err; +} + +Error Logger::logv(const char* fmt, va_list ap) noexcept { + char buf[1024]; + size_t len = vsnprintf(buf, sizeof(buf), fmt, ap); + + if (len >= sizeof(buf)) + len = sizeof(buf) - 1; + return log(buf, len); +} + +Error Logger::logBinary(const void* data, size_t size) noexcept { + static const char prefix[] = ".data "; + static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + const uint8_t* s = static_cast(data); + size_t i = size; + + char buffer[128]; + ::memcpy(buffer, prefix, ASMJIT_ARRAY_SIZE(prefix) - 1); + + while (i) { + uint32_t n = static_cast(Utils::iMin(i, 16)); + char* p = buffer + ASMJIT_ARRAY_SIZE(prefix) - 1; + + i -= n; + do { + uint32_t c = s[0]; + + p[0] = hex[c >> 4]; + p[1] = hex[c & 15]; + + p += 2; + s += 1; + } while (--n); + + *p++ = '\n'; + ASMJIT_PROPAGATE(log(buffer, (size_t)(p - buffer))); + } + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::Logger - Indentation] +// ============================================================================ + +void Logger::setIndentation(const char* indentation) noexcept { + ::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation)); + if (!indentation) + return; + + size_t length = Utils::strLen(indentation, ASMJIT_ARRAY_SIZE(_indentation) - 1); + ::memcpy(_indentation, indentation, length); +} + +// ============================================================================ +// [asmjit::FileLogger - Construction / Destruction] +// ============================================================================ + +FileLogger::FileLogger(FILE* stream) noexcept : _stream(nullptr) { setStream(stream); } +FileLogger::~FileLogger() noexcept {} + +// ============================================================================ +// [asmjit::FileLogger - Logging] +// ============================================================================ + +Error FileLogger::log(const char* buf, size_t len) noexcept { + if (!_stream) + return kErrorOk; + + if (len == kInvalidIndex) + len = strlen(buf); + + fwrite(buf, 1, len, _stream); + return kErrorOk; +} + +// ============================================================================ +// [asmjit::StringLogger - Construction / Destruction] +// ============================================================================ + +StringLogger::StringLogger() noexcept {} +StringLogger::~StringLogger() noexcept {} + +// ============================================================================ +// [asmjit::StringLogger - Logging] +// ============================================================================ + +Error StringLogger::log(const char* buf, size_t len) noexcept { + return _stringBuilder.appendString(buf, len); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_LOGGING diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/logging.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/logging.h new file mode 100644 index 00000000..1e3c2a6b --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/logging.h @@ -0,0 +1,287 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_LOGGING_H +#define _ASMJIT_BASE_LOGGING_H + +#include "../asmjit_build.h" + +// [Dependencies] +#include "../base/string.h" +#include + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +#if !defined(ASMJIT_DISABLE_LOGGING) + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +struct Operand_; +class Reg; + +// ============================================================================ +// [asmjit::LogUtil] +// ============================================================================ + +// Only used by asmjit internals, not available to consumers. +#if defined(ASMJIT_EXPORTS) +struct LogUtil { + enum { + // Has to be big to be able to hold all metadata compiler can assign to a + // single instruction. + kMaxCommentLength = 512, + kMaxInstLength = 40, + kMaxBinaryLength = 26 + }; + + static Error formatLine( + StringBuilder& sb, + const uint8_t* binData, size_t binLen, size_t dispLen, size_t imLen, const char* comment) noexcept; +}; +#endif // ASMJIT_EXPORTS + +// ============================================================================ +// [asmjit::Formatter] +// ============================================================================ + +class ASMJIT_VIRTAPI Formatter { +public: + typedef Error (ASMJIT_CDECL* VirtRegHandlerFunc)( + StringBuilder& out, uint32_t logOptions, const Reg& r, void* handlerData); + + ASMJIT_API Formatter() noexcept; + ASMJIT_API virtual ~Formatter() noexcept; + + virtual Error formatRegister( + StringBuilder& out, + uint32_t logOptions, + uint32_t regType, + uint32_t regId) const noexcept = 0; + + virtual Error formatOperand( + StringBuilder& out, + uint32_t logOptions, + const Operand_& op) const noexcept = 0; + + virtual Error formatInstruction( + StringBuilder& out, + uint32_t logOptions, + uint32_t instId, + uint32_t options, + const Operand_& opExtra, + const Operand_* opArray, uint32_t opCount) const noexcept = 0; + + ASMJIT_INLINE Error formatVirtReg(StringBuilder& out, uint32_t logOptions, const Reg& r) const { + ASMJIT_ASSERT(hasVirtRegHandler()); + return _virtRegHandlerFunc(out, logOptions, r, _virtRegHandlerData); + } + + ASMJIT_INLINE bool hasVirtRegHandler() const noexcept { return _virtRegHandlerFunc != nullptr; } + + ASMJIT_INLINE void setVirtRegHandler(VirtRegHandlerFunc func, void* data) noexcept { + _virtRegHandlerFunc = func; + _virtRegHandlerData = data; + } + ASMJIT_INLINE void resetVirtRegHandler() { setVirtRegHandler(nullptr, nullptr); } + + VirtRegHandlerFunc _virtRegHandlerFunc; + void* _virtRegHandlerData; +}; + +// ============================================================================ +// [asmjit::Logger] +// ============================================================================ + +//! Abstract logging class. +//! +//! This class can be inherited and reimplemented to fit into your logging +//! subsystem. When reimplementing use `Logger::log()` method to log into +//! a custom stream. +class ASMJIT_VIRTAPI Logger { +public: + ASMJIT_NONCOPYABLE(Logger) + + // -------------------------------------------------------------------------- + // [Options] + // -------------------------------------------------------------------------- + + //! Logger options. + ASMJIT_ENUM(Options) { + kOptionBinaryForm = 0x00000001, //! Output instructions also in binary form. + kOptionHexImmediate = 0x00000002, //! Output constants in hexadecimal form. + kOptionHexDisplacement = 0x00000004 //! Output displacements in hexadecimal form. + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a `Logger` instance. + ASMJIT_API Logger() noexcept; + //! Destroy the `Logger` instance. + ASMJIT_API virtual ~Logger() noexcept; + + // -------------------------------------------------------------------------- + // [Logging] + // -------------------------------------------------------------------------- + + //! Log output. + virtual Error log(const char* buf, size_t len = kInvalidIndex) noexcept = 0; + + //! Format the message by using "sprintf()" and then send to `log()`. + ASMJIT_API Error logf(const char* fmt, ...) noexcept; + //! Format the message by using "vsprintf()" and then send to `log()`. + ASMJIT_API Error logv(const char* fmt, va_list ap) noexcept; + //! Log binary data. + ASMJIT_API Error logBinary(const void* data, size_t size) noexcept; + + // -------------------------------------------------------------------------- + // [Options] + // -------------------------------------------------------------------------- + + //! Get all logger options as a single integer. + ASMJIT_INLINE uint32_t getOptions() const noexcept { return _options; } + //! Get the given logger option. + ASMJIT_INLINE bool hasOption(uint32_t option) const noexcept { return (_options & option) != 0; } + ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _options |= options; } + ASMJIT_INLINE void clearOptions(uint32_t options) noexcept { _options &= ~options; } + + // -------------------------------------------------------------------------- + // [Indentation] + // -------------------------------------------------------------------------- + + //! Get indentation. + ASMJIT_INLINE const char* getIndentation() const noexcept { return _indentation; } + //! Set indentation. + ASMJIT_API void setIndentation(const char* indentation) noexcept; + //! Reset indentation. + ASMJIT_INLINE void resetIndentation() noexcept { setIndentation(nullptr); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Options, see \ref LoggerOption. + uint32_t _options; + + //! Indentation. + char _indentation[12]; +}; + +// ============================================================================ +// [asmjit::FileLogger] +// ============================================================================ + +//! Logger that can log to a `FILE*` stream. +class ASMJIT_VIRTAPI FileLogger : public Logger { +public: + ASMJIT_NONCOPYABLE(FileLogger) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `FileLogger` that logs to a `FILE` stream. + ASMJIT_API FileLogger(FILE* stream = nullptr) noexcept; + //! Destroy the `FileLogger`. + ASMJIT_API virtual ~FileLogger() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the logging out put stream or null. + ASMJIT_INLINE FILE* getStream() const noexcept { return _stream; } + + //! Set the logging output stream to `stream` or null. + //! + //! NOTE: If the `stream` is null it will disable logging, but it won't + //! stop calling `log()` unless the logger is detached from the + //! \ref Assembler. + ASMJIT_INLINE void setStream(FILE* stream) noexcept { _stream = stream; } + + // -------------------------------------------------------------------------- + // [Logging] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Error log(const char* buf, size_t len = kInvalidIndex) noexcept override; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! C file stream. + FILE* _stream; +}; + +// ============================================================================ +// [asmjit::StringLogger] +// ============================================================================ + +//! Logger that stores everything in an internal string buffer. +class ASMJIT_VIRTAPI StringLogger : public Logger { +public: + ASMJIT_NONCOPYABLE(StringLogger) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create new `StringLogger`. + ASMJIT_API StringLogger() noexcept; + //! Destroy the `StringLogger`. + ASMJIT_API virtual ~StringLogger() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get `char*` pointer which represents the resulting string. + //! + //! The pointer is owned by `StringLogger`, it can't be modified or freed. + ASMJIT_INLINE const char* getString() const noexcept { return _stringBuilder.getData(); } + //! Clear the resulting string. + ASMJIT_INLINE void clearString() noexcept { _stringBuilder.clear(); } + + //! Get the length of the string returned by `getString()`. + ASMJIT_INLINE size_t getLength() const noexcept { return _stringBuilder.getLength(); } + + // -------------------------------------------------------------------------- + // [Logging] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Error log(const char* buf, size_t len = kInvalidIndex) noexcept override; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Output string. + StringBuilder _stringBuilder; +}; +#else +class Formatter; +class Logger; +#endif // !ASMJIT_DISABLE_LOGGING + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_LOGGER_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/operand.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/operand.cpp new file mode 100644 index 00000000..b907e1a1 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/operand.cpp @@ -0,0 +1,207 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/operand.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::TypeId] +// ============================================================================ + +template +struct TypeIdSizeOf_T { + enum { + kValue = (ID == TypeId::kI8 ) ? 1 : + (ID == TypeId::kU8 ) ? 1 : + (ID == TypeId::kI16 ) ? 2 : + (ID == TypeId::kU16 ) ? 2 : + (ID == TypeId::kI32 ) ? 4 : + (ID == TypeId::kU32 ) ? 4 : + (ID == TypeId::kI64 ) ? 8 : + (ID == TypeId::kU64 ) ? 8 : + (ID == TypeId::kF32 ) ? 4 : + (ID == TypeId::kF64 ) ? 8 : + (ID == TypeId::kF80 ) ? 10 : + (ID == TypeId::kMask8 ) ? 1 : + (ID == TypeId::kMask16) ? 2 : + (ID == TypeId::kMask32) ? 4 : + (ID == TypeId::kMask64) ? 8 : + (ID == TypeId::kMmx32 ) ? 4 : + (ID == TypeId::kMmx64 ) ? 8 : + (ID >= TypeId::_kVec32Start && ID <= TypeId::_kVec32End ) ? 4 : + (ID >= TypeId::_kVec64Start && ID <= TypeId::_kVec64End ) ? 8 : + (ID >= TypeId::_kVec128Start && ID <= TypeId::_kVec128End) ? 16 : + (ID >= TypeId::_kVec256Start && ID <= TypeId::_kVec256End) ? 32 : + (ID >= TypeId::_kVec512Start && ID <= TypeId::_kVec512End) ? 64 : 0 + }; +}; + +template +struct TypeIdElementOf_T { + enum { + kValue = (ID == TypeId::kMask8 ) ? TypeId::kU8 : + (ID == TypeId::kMask16) ? TypeId::kU16 : + (ID == TypeId::kMask32) ? TypeId::kU32 : + (ID == TypeId::kMask64) ? TypeId::kU64 : + (ID == TypeId::kMmx32 ) ? TypeId::kI32 : + (ID == TypeId::kMmx64 ) ? TypeId::kI64 : + (ID >= TypeId::kI8 && ID <= TypeId::kF80 ) ? ID : + (ID >= TypeId::_kVec32Start && ID <= TypeId::_kVec32End ) ? ID - TypeId::_kVec32Start + TypeId::kI8 : + (ID >= TypeId::_kVec64Start && ID <= TypeId::_kVec64End ) ? ID - TypeId::_kVec64Start + TypeId::kI8 : + (ID >= TypeId::_kVec128Start && ID <= TypeId::_kVec128End) ? ID - TypeId::_kVec128Start + TypeId::kI8 : + (ID >= TypeId::_kVec256Start && ID <= TypeId::_kVec256End) ? ID - TypeId::_kVec256Start + TypeId::kI8 : + (ID >= TypeId::_kVec512Start && ID <= TypeId::_kVec512End) ? ID - TypeId::_kVec512Start + TypeId::kI8 : 0 + }; +}; + +#define R(TMPL, I) TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue +ASMJIT_API const TypeId::Info TypeId::_info = { + // SizeOd[128] + { + R(TypeIdSizeOf_T, 0), R(TypeIdSizeOf_T, 16), + R(TypeIdSizeOf_T, 32), R(TypeIdSizeOf_T, 48), + R(TypeIdSizeOf_T, 64), R(TypeIdSizeOf_T, 80), + R(TypeIdSizeOf_T, 96), R(TypeIdSizeOf_T, 112) + }, + + // ElementOf[128] + { + R(TypeIdElementOf_T, 0), R(TypeIdElementOf_T, 16), + R(TypeIdElementOf_T, 32), R(TypeIdElementOf_T, 48), + R(TypeIdElementOf_T, 64), R(TypeIdElementOf_T, 80), + R(TypeIdElementOf_T, 96), R(TypeIdElementOf_T, 112) + } +}; +#undef R + +// ============================================================================ +// [asmjit::Operand - Test] +// ============================================================================ + +#if defined(ASMJIT_TEST) +UNIT(base_operand) { + INFO("Checking operand sizes"); + EXPECT(sizeof(Operand) == 16); + EXPECT(sizeof(Reg) == 16); + EXPECT(sizeof(Mem) == 16); + EXPECT(sizeof(Imm) == 16); + EXPECT(sizeof(Label) == 16); + + INFO("Checking basic functionality of Operand"); + Operand a, b; + Operand dummy; + + EXPECT(a.isNone() == true); + EXPECT(a.isReg() == false); + EXPECT(a.isMem() == false); + EXPECT(a.isImm() == false); + EXPECT(a.isLabel() == false); + EXPECT(a == b); + + EXPECT(a._any.reserved8_4 == 0, "Default constructed Operand should zero its 'reserved8_4' field"); + EXPECT(a._any.reserved12_4 == 0, "Default constructed Operand should zero its 'reserved12_4' field"); + + INFO("Checking basic functionality of Label"); + Label label; + EXPECT(label.isValid() == false); + EXPECT(label.getId() == kInvalidValue); + + INFO("Checking basic functionality of Reg"); + EXPECT(Reg().isValid() == false, + "Default constructed Reg() should not be valid"); + EXPECT(Reg()._any.reserved8_4 == 0, + "A default constructed Reg() should zero its 'reserved8_4' field"); + EXPECT(Reg()._any.reserved12_4 == 0, + "A default constructed Reg() should zero its 'reserved12_4' field"); + + EXPECT(Reg().isReg() == false, + "Default constructed register should not isReg()"); + EXPECT(static_cast(dummy).isValid() == false, + "Default constructed Operand casted to Reg should not be valid"); + + // Create some register (not specific to any architecture). + uint32_t rSig = Operand::makeRegSignature(1, 2, 8); + Reg r1(Reg::fromSignature(rSig, 5)); + + EXPECT(r1.isValid() == true); + EXPECT(r1.isReg() == true); + EXPECT(r1.isReg(1) == true); + EXPECT(r1.isPhysReg() == true); + EXPECT(r1.isVirtReg() == false); + EXPECT(r1.getSignature() == rSig); + EXPECT(r1.getRegType() == 1); + EXPECT(r1.getRegKind() == 2); + EXPECT(r1.getSize() == 8); + EXPECT(r1.getId() == 5); + EXPECT(r1.isReg(1, 5) == true); // RegType and Id. + + EXPECT(r1._any.reserved8_4 == 0, "Reg should have 'reserved8_4' zero"); + EXPECT(r1._any.reserved12_4 == 0, "Reg should have 'reserved12_4' zero"); + + // The same type of register having different id. + Reg r2(r1, 6); + EXPECT(r2.isValid() == true); + EXPECT(r2.isReg() == true); + EXPECT(r2.isReg(1) == true); + EXPECT(r2.isPhysReg() == true); + EXPECT(r2.isVirtReg() == false); + EXPECT(r2.getSignature() == rSig); + EXPECT(r2.getRegType() == r1.getRegType()); + EXPECT(r2.getRegKind() == r1.getRegKind()); + EXPECT(r2.getSize() == r1.getSize()); + EXPECT(r2.getId() == 6); + EXPECT(r2.isReg(1, 6) == true); + + r1.reset(); + EXPECT(!r1.isValid(), + "Reset register should not be valid"); + EXPECT(!r1.isReg(), + "Reset register should not isReg()"); + + INFO("Checking basic functionality of Mem"); + Mem m; + EXPECT(m.isMem() , "Default constructed Mem() should isMem()"); + EXPECT(m == Mem() , "Two default constructed Mem() operands should be equal"); + EXPECT(m.hasBase() == false , "Default constructed Mem() should not have base specified"); + EXPECT(m.hasIndex() == false , "Default constructed Mem() should not have index specified"); + EXPECT(m.has64BitOffset() == true , "Default constructed Mem() should report 64-bit offset"); + EXPECT(m.getOffset() == 0 , "Default constructed Mem() should have be zero offset / address"); + + m.setOffset(-1); + EXPECT(m.getOffsetLo32() == -1 , "Memory operand must hold a 32-bit offset"); + EXPECT(m.getOffset() == -1 , "32-bit offset must be sign extended to 64 bits"); + + int64_t x = int64_t(ASMJIT_UINT64_C(0xFF00FF0000000001)); + m.setOffset(x); + EXPECT(m.getOffset() == x , "Memory operand must hold a 64-bit offset"); + EXPECT(m.getOffsetLo32() == 1 , "Memory operand must return correct low offset DWORD"); + EXPECT(m.getOffsetHi32() == 0xFF00FF00, "Memory operand must return correct high offset DWORD"); + + INFO("Checking basic functionality of Imm"); + EXPECT(Imm(-1).getInt64() == int64_t(-1), + "Immediate values should by default sign-extend to 64-bits"); +} +#endif // ASMJIT_TEST + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/operand.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/operand.h new file mode 100644 index 00000000..8e4785f8 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/operand.h @@ -0,0 +1,1414 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_OPERAND_H +#define _ASMJIT_BASE_OPERAND_H + +// [Dependencies] +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::Operand_] +// ============================================================================ + +//! Constructor-less \ref Operand. +//! +//! Contains no initialization code and can be used safely to define an array +//! of operands that won't be initialized. This is a \ref Operand compatible +//! data structure designed to be statically initialized or `static const`. +struct Operand_ { + // -------------------------------------------------------------------------- + // [Operand Type] + // -------------------------------------------------------------------------- + + //! Operand types that can be encoded in \ref Operand. + ASMJIT_ENUM(OpType) { + kOpNone = 0, //!< Not an operand or not initialized. + kOpReg = 1, //!< Operand is a register. + kOpMem = 2, //!< Operand is a memory. + kOpImm = 3, //!< Operand is an immediate value. + kOpLabel = 4 //!< Operand is a label. + }; + + // -------------------------------------------------------------------------- + // [Operand Id] + // -------------------------------------------------------------------------- + + //! Operand id helpers useful for id <-> index translation. + ASMJIT_ENUM(PackedId) { + kPackedIdMin = 0x00000100U, //!< Minimum valid packed-id. + kPackedIdMax = 0xFFFFFFFEU, //!< Maximum valid packed-id. + kPackedIdCount = kPackedIdMax - kPackedIdMin //!< Count of valid packed-ids. + }; + + // -------------------------------------------------------------------------- + // [Operand Utilities] + // -------------------------------------------------------------------------- + + //! Get if the given `id` is a valid packed-id that can be used by Operand. + //! Packed ids are those equal or greater than `kPackedIdMin` and lesser or + //! equal to `kPackedIdMax`. This concept was created to support virtual + //! registers and to make them distinguishable from physical ones. It allows + //! a single uint32_t to contain either physical register id or virtual + //! register id represented as `packed-id`. This concept is used also for + //! labels to make the API consistent. + static ASMJIT_INLINE bool isPackedId(uint32_t id) noexcept { return id - kPackedIdMin <= kPackedIdCount; } + //! Convert a real-id into a packed-id that can be stored in Operand. + static ASMJIT_INLINE uint32_t packId(uint32_t id) noexcept { return id + kPackedIdMin; } + //! Convert a packed-id back to real-id. + static ASMJIT_INLINE uint32_t unpackId(uint32_t id) noexcept { return id - kPackedIdMin; } + + static ASMJIT_INLINE uint32_t makeSignature(uint32_t opType, uint32_t subType, uint32_t payload, uint32_t size) noexcept { + return Utils::pack32_4x8(opType, subType, payload, size); + } + + static ASMJIT_INLINE uint32_t makeRegSignature(uint32_t regType, uint32_t regKind, uint32_t size) noexcept { + return Utils::pack32_4x8(kOpReg, regType, regKind, size); + } + + // -------------------------------------------------------------------------- + // [Operand Data] + // -------------------------------------------------------------------------- + + //! Any operand. + struct AnyData { + uint8_t op; //!< Type of the operand (see \ref OpType). + uint8_t subType; //!< Subtype - depends on `op`. + uint8_t payload; //!< Payload - depends on `op`. + uint8_t size; //!< Size of the operand (register, address, immediate). + uint32_t id; //!< Operand id or `kInvalidValue`. + uint32_t reserved8_4; //!< \internal + uint32_t reserved12_4; //!< \internal + }; + + //! Register operand data. + struct RegData { + uint8_t op; //!< Type of the operand (always \ref kOpReg). + uint8_t regType; //!< Register type (also operand sub-type). + uint8_t regKind; //!< Register kind. + uint8_t size; //!< Size of the register. + uint32_t id; //!< Physical or virtual register id. + uint32_t reserved8_4; //!< \internal + uint32_t reserved12_4; //!< \internal + }; + + //! Memory operand data. + struct MemData { + uint8_t op; //!< Type of the operand (always \ref kOpMem). + uint8_t baseIndexType; //!< Type of BASE and INDEX registers. + uint8_t flags; //!< Architecture dependent flags. + uint8_t size; //!< Size of the memory operand. + uint32_t index; //!< INDEX register id or `kInvalidValue`. + + // [BASE + OFF32] vs just [OFF64]. + union { + uint64_t offset64; //!< 64-bit offset, combining low and high 32-bit parts. + struct { +#if ASMJIT_ARCH_LE + uint32_t offsetLo32; //!< 32-bit low offset part. + uint32_t base; //!< 32-bit high offset part or BASE. +#else + uint32_t base; //!< 32-bit high offset part or BASE. + uint32_t offsetLo32; //!< 32-bit low offset part. +#endif + }; + }; + }; + + //! Immediate operand data. + struct ImmData { + uint8_t op; //!< Type of the operand (always \ref kOpImm). + uint8_t reserved_1_1; //!< Must be zero. + uint8_t reserved_2_1; //!< Must be zero. + uint8_t size; //!< Size of the immediate (or 0 to autodetect). + uint32_t id; //!< Immediate id (always `kInvalidValue`). + UInt64 value; //!< Immediate value. + }; + + //! Label operand data. + struct LabelData { + uint8_t op; //!< Type of the operand (always \ref kOpLabel). + uint8_t reserved_1_1; //!< Must be zero. + uint8_t reserved_2_1; //!< Must be zero. + uint8_t size; //!< Must be zero. + uint32_t id; //!< Label id (`kInvalidValue` if not initialized). + uint32_t reserved8_4; //!< \internal + uint32_t reserved12_4; //!< \internal + }; + + // -------------------------------------------------------------------------- + // [Init & Copy] + // -------------------------------------------------------------------------- + + //! \internal + //! + //! Initialize the operand to `other` (used by constructors). + ASMJIT_INLINE void _init(const Operand_& other) noexcept { ::memcpy(this, &other, sizeof(Operand_)); } + + //! \internal + ASMJIT_INLINE void _initReg(uint32_t signature, uint32_t id) { + _init_packed_d0_d1(signature, id); + _init_packed_d2_d3(0, 0); + } + + //! \internal + ASMJIT_INLINE void _init_packed_op_b1_b2_sz_id(uint32_t op, uint32_t r0, uint32_t r1, uint32_t size, uint32_t id) noexcept { + // This hack is not for performance, it's to decrease the size of the binary + // generated when constructing AsmJit operands (mostly for third parties). + // Some compilers are not able to join four BYTE writes to a single DWORD + // write. Because the `op`, `r0`, `r1`, and `size` arguments are usually compile + // time constants the compiler can do a really nice job if they are joined + // by using bitwise operations. + _packed[0].setPacked_2x32(makeSignature(op, r0, r1, size), id); + } + + //! \internal + ASMJIT_INLINE void _init_packed_d0_d1(uint32_t d0, uint32_t d1) noexcept { _packed[0].setPacked_2x32(d0, d1); } + //! \internal + ASMJIT_INLINE void _init_packed_d2_d3(uint32_t d2, uint32_t d3) noexcept { _packed[1].setPacked_2x32(d2, d3); } + + //! \internal + //! + //! Initialize the operand from `other` (used by operator overloads). + ASMJIT_INLINE void copyFrom(const Operand_& other) noexcept { ::memcpy(this, &other, sizeof(Operand_)); } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get if the operand matches the given signature `sign`. + ASMJIT_INLINE bool hasSignature(uint32_t signature) const noexcept { return _signature == signature; } + + //! Get if the operand matches a signature composed of `opType`, `subType`, `payload`, and `size`, + ASMJIT_INLINE bool hasSignature(uint32_t opType, uint32_t subType, uint32_t payload, uint32_t size) const noexcept { + return _signature == makeSignature(opType, subType, payload, size); + } + //! Get if the operand matches a signature of the `other` operand. + ASMJIT_INLINE bool hasSignature(const Operand_& other) const noexcept { + return _signature == other.getSignature(); + } + + //! Get a 32-bit operand signature. + //! + //! Signature is first 4 bytes of the operand data. It's used mostly for + //! operand checking as it's much faster to check 4 bytes at once than having + //! to check these bytes individually. + ASMJIT_INLINE uint32_t getSignature() const noexcept { return _signature; } + //! Set the operand signature (see \ref getSignature). + //! + //! Improper use of `setSignature()` can lead to hard-to-debug errors. + ASMJIT_INLINE void setSignature(uint32_t signature) noexcept { _signature = signature; } + //! \overload + ASMJIT_INLINE void setSignature(uint32_t opType, uint32_t subType, uint32_t payload, uint32_t size) noexcept { + _signature = makeSignature(opType, subType, payload, size); + } + + //! Get type of the operand, see \ref OpType. + ASMJIT_INLINE uint32_t getOp() const noexcept { return _any.op; } + //! Get if the operand is none (\ref kOpNone). + ASMJIT_INLINE bool isNone() const noexcept { return _any.op == kOpNone; } + //! Get if the operand is a register (\ref kOpReg). + ASMJIT_INLINE bool isReg() const noexcept { return _any.op == kOpReg; } + //! Get if the operand is a memory location (\ref kOpMem). + ASMJIT_INLINE bool isMem() const noexcept { return _any.op == kOpMem; } + //! Get if the operand is an immediate (\ref kOpImm). + ASMJIT_INLINE bool isImm() const noexcept { return _any.op == kOpImm; } + //! Get if the operand is a label (\ref kOpLabel). + ASMJIT_INLINE bool isLabel() const noexcept { return _any.op == kOpLabel; } + + //! Get if the operand is a physical register. + ASMJIT_INLINE bool isPhysReg() const noexcept { return isReg() && _reg.id < kInvalidReg; } + //! Get if the operand is a virtual register. + ASMJIT_INLINE bool isVirtReg() const noexcept { return isReg() && isPackedId(_reg.id); } + + //! Get if the operand specifies a size (i.e. the size is not zero). + ASMJIT_INLINE bool hasSize() const noexcept { return _any.size != 0; } + //! Get if the size of the operand matches `size`. + ASMJIT_INLINE bool hasSize(uint32_t size) const noexcept { return _any.size == size; } + + //! Get size of the operand (in bytes). + //! + //! The value returned depends on the operand type: + //! * None - Should always return zero size. + //! * Reg - Should always return the size of the register. If the register + //! size depends on architecture (like \ref X86CReg and \ref X86DReg) + //! the size returned should be the greatest possible (so it should + //! return 64-bit size in such case). + //! * Mem - Size is optional and will be in most cases zero. + //! * Imm - Should always return zero size. + //! * Label - Should always return zero size. + ASMJIT_INLINE uint32_t getSize() const noexcept { return _any.size; } + + //! Get the operand id. + //! + //! The value returned should be interpreted accordingly to the operand type: + //! * None - Should be `kInvalidValue`. + //! * Reg - Physical or virtual register id. + //! * Mem - Multiple meanings - BASE address (register or label id), or + //! high value of a 64-bit absolute address. + //! * Imm - Should be `kInvalidValue`. + //! * Label - Label id if it was created by using `newLabel()` or `kInvalidValue` + //! if the label is invalid or uninitialized. + ASMJIT_INLINE uint32_t getId() const noexcept { return _any.id; } + + //! Get if the operand is 100% equal to `other`. + ASMJIT_INLINE bool isEqual(const Operand_& other) const noexcept { + return (_packed[0] == other._packed[0]) & + (_packed[1] == other._packed[1]) ; + } + + //! Get if the operand is a register matching `regType`. + ASMJIT_INLINE bool isReg(uint32_t regType) const noexcept { + const uint32_t kMsk = makeSignature(0xFF , 0xFF , 0, 0); + const uint32_t kSgn = makeSignature(kOpReg, regType, 0, 0); + return (_signature & kMsk) == kSgn; + } + + //! Get whether the operand is register and of `type` and `id`. + ASMJIT_INLINE bool isReg(uint32_t regType, uint32_t id) const noexcept { + return isReg(regType) && getId() == id; + } + + //! Get whether the operand is a register or memory. + ASMJIT_INLINE bool isRegOrMem() const noexcept { + ASMJIT_ASSERT(kOpMem - kOpReg == 1); + return _any.op >= kOpReg && _any.op <= kOpMem; + } + + //! Cast this operand to `T` type. + template + ASMJIT_INLINE T& as() noexcept { return static_cast(*this); } + //! Cast this operand to `T` type (const). + template + ASMJIT_INLINE const T& as() const noexcept { return static_cast(*this); } + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + //! Reset the `Operand` to none. + //! + //! None operand is defined the following way: + //! - Its signature is zero (kOpNone, and the rest zero as well). + //! - Its id is `kInvalidValue`. + //! - The reserved8_4 field is set to `kInvalidValue`. + //! - The reserved12_4 field is set to zero. + //! + //! Reset operand must match the Operand state right after its construction: + //! + //! ``` + //! using namespace asmjit; + //! + //! Operand a; + //! Operand b; + //! assert(a == b); + //! + //! b = x86::eax; + //! assert(a != b); + //! + //! b.reset(); + //! assert(a == b); + //! ``` + ASMJIT_INLINE void reset() noexcept { + _init_packed_op_b1_b2_sz_id(kOpNone, 0, 0, 0, kInvalidValue); + _init_packed_d2_d3(0, 0); + } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + template + ASMJIT_INLINE bool operator==(const T& other) const noexcept { return isEqual(other); } + template + ASMJIT_INLINE bool operator!=(const T& other) const noexcept { return !isEqual(other); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + union { + AnyData _any; //!< Generic data. + RegData _reg; //!< Physical or virtual register data. + MemData _mem; //!< Memory address data. + ImmData _imm; //!< Immediate value data. + LabelData _label; //!< Label data. + + uint32_t _signature; //!< Operand signature (first 32-bits). + UInt64 _packed[2]; //!< Operand packed into two 64-bit integers. + }; +}; + +// ============================================================================ +// [asmjit::Operand] +// ============================================================================ + +//! Operand can contain register, memory location, immediate, or label. +class Operand : public Operand_ { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create an uninitialized operand. + ASMJIT_INLINE Operand() noexcept { reset(); } + //! Create a reference to `other` operand. + ASMJIT_INLINE Operand(const Operand& other) noexcept { _init(other); } + //! Create a reference to `other` operand. + explicit ASMJIT_INLINE Operand(const Operand_& other) noexcept { _init(other); } + //! Create a completely uninitialized operand (dangerous). + explicit ASMJIT_INLINE Operand(const _NoInit&) noexcept {} + + // -------------------------------------------------------------------------- + // [Clone] + // -------------------------------------------------------------------------- + + //! Clone the `Operand`. + ASMJIT_INLINE Operand clone() const noexcept { return Operand(*this); } + + ASMJIT_INLINE Operand& operator=(const Operand_& other) noexcept { copyFrom(other); return *this; } +}; + +// ============================================================================ +// [asmjit::Label] +// ============================================================================ + +//! Label (jump target or data location). +//! +//! Label represents a location in code typically used as a jump target, but +//! may be also a reference to some data or a static variable. Label has to be +//! explicitly created by CodeEmitter. +//! +//! Example of using labels: +//! +//! ~~~ +//! // Create a CodeEmitter (for example X86Assembler). +//! X86Assembler a; +//! +//! // Create Label instance. +//! Label L1 = a.newLabel(); +//! +//! // ... your code ... +//! +//! // Using label. +//! a.jump(L1); +//! +//! // ... your code ... +//! +//! // Bind label to the current position, see `CodeEmitter::bind()`. +//! a.bind(L1); +//! ~~~ +class Label : public Operand { +public: + enum { + kMaxNameLength = 2048 //!< Maximum length of label/symbol name. + }; + + //! Type of the Label. + enum Type { + kTypeAnonymous = 0, //!< Anonymous (unnamed) label. + kTypeLocal = 1, //!< Local label (always has parentId). + kTypeGlobal = 2, //!< Global label (never has parentId). + kTypeCount = 3 //!< Number of label types. + }; + + // TODO: Find a better place, find a better name. + enum { + //! Label tag is used as a sub-type, forming a unique signature across all + //! operand types as 0x1 is never associated with any register (reg-type). + //! This means that a memory operand's BASE register can be constructed + //! from virtually any operand (register vs. label) by just assigning its + //! type (reg type or label-tag) and operand id. + kLabelTag = 0x1 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create new, unassociated label. + ASMJIT_INLINE Label() noexcept : Operand(NoInit) { reset(); } + //! Create a reference to another label. + ASMJIT_INLINE Label(const Label& other) noexcept : Operand(other) {} + + explicit ASMJIT_INLINE Label(uint32_t id) noexcept : Operand(NoInit) { + _init_packed_op_b1_b2_sz_id(kOpLabel, 0, 0, 0, id); + _init_packed_d2_d3(0, 0); + } + + explicit ASMJIT_INLINE Label(const _NoInit&) noexcept : Operand(NoInit) {} + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + // TODO: I think that if operand is reset it shouldn't say it's a Label, it + // should be none like all other operands. + ASMJIT_INLINE void reset() noexcept { + _init_packed_op_b1_b2_sz_id(kOpLabel, 0, 0, 0, kInvalidValue); + _init_packed_d2_d3(0, 0); + } + + // -------------------------------------------------------------------------- + // [Label Specific] + // -------------------------------------------------------------------------- + + //! Get if the label was created by CodeEmitter and has an assigned id. + ASMJIT_INLINE bool isValid() const noexcept { return _label.id != kInvalidValue; } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Label& operator=(const Label& other) noexcept { copyFrom(other); return *this; } +}; + +// ============================================================================ +// [asmjit::RegTraits] +// ============================================================================ + +//! Allows to resolve a signature of `RegT` register at compile-time. +//! +//! Must be provided by an architecture-specific implementation, ambiguous +//! registers like `Reg`, `X86Gp` and `X86Vec` are not resolved by design. +template +struct RegTraits {}; + +// ============================================================================ +// [asmjit::RegInfo] +// ============================================================================ + +//! Structure that contains information about a physical register. +//! +//! This information is stored in first 4 bytes of `Reg` operand. It consists +//! of: +//! * `op` - Operand type, always \ref Operand::kOpReg. +//! * `regType` - Register type - platform specific, see \ref X86Reg::Type. +//! * `regKind` - Register kind - platform specific, see \ref X86Reg::Kind. +//! * `size` - Size of the register. +union RegInfo { + struct { + uint8_t op; //!< Type of the operand (always \ref kOpReg). + uint8_t regType; //!< Register type. + uint8_t regKind; //!< Register kind. + uint8_t size; //!< Size of the register. + }; + uint32_t signature; +}; + + +// ============================================================================ +// [asmjit::Reg] +// ============================================================================ + +//! Physical/Virtual register operand. +class Reg : public Operand { +public: + ASMJIT_ENUM(RegType) { + kRegNone = 0, //!< No register - unused, invalid, multiple meanings. + _kRegStart = 2, //!< Start of register types (must be honored). + kRegRip = _kRegStart, //!< Universal id of RIP register (if supported). + kRegMax = 31 //!< Maximum possible register id of all architectures. + }; + + //! Register kind. + ASMJIT_ENUM(Kind) { + kKindGp = 0 //!< GP register kind, compatible with all architectures. + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a dummy register operand. + ASMJIT_INLINE Reg() noexcept : Operand() {} + //! Create a new register operand which is the same as `other` . + ASMJIT_INLINE Reg(const Reg& other) noexcept : Operand(other) {} + //! Create a new register operand compatible with `other`, but with a different `id`. + ASMJIT_INLINE Reg(const Reg& other, uint32_t id) noexcept : Operand(NoInit) { + _signature = other._signature; + _reg.id = id; + _packed[1] = other._packed[1]; + } + + //! Create a register initialized to `signature` and `id`. + ASMJIT_INLINE Reg(const _Init&, uint32_t signature, uint32_t id) noexcept : Operand(NoInit) { _initReg(signature, id); } + explicit ASMJIT_INLINE Reg(const _NoInit&) noexcept : Operand(NoInit) {} + + //! Create a new register based on `signature` and `id`. + static ASMJIT_INLINE Reg fromSignature(uint32_t signature, uint32_t id) noexcept { return Reg(Init, signature, id); } + + // -------------------------------------------------------------------------- + // [Reg Specific] + // -------------------------------------------------------------------------- + + //! Get if the register is valid (either virtual or physical). + ASMJIT_INLINE bool isValid() const noexcept { return _reg.id != kInvalidValue; } + //! Get if this is a physical register. + ASMJIT_INLINE bool isPhysReg() const noexcept { return _reg.id < kInvalidReg; } + //! Get if this is a virtual register (used by \ref CodeCompiler). + ASMJIT_INLINE bool isVirtReg() const noexcept { return isPackedId(_reg.id); } + + using Operand_::isReg; + + //! Get if the register type matches `type`. + ASMJIT_INLINE bool isReg(uint32_t regType) const noexcept { return _reg.regType == regType; } + //! Get if the register type matches `type` and register id matches `id`. + ASMJIT_INLINE bool isReg(uint32_t regType, uint32_t id) const noexcept { return isReg(regType) && getId() == id; } + + //! Get if this register is the same as `other`. + //! + //! This is just an optimization. Registers by default only use the first + //! 8 bytes of the Operand, so this method takes advantage of this knowledge + //! and only compares these 8 bytes. If both operands were created correctly + //! then `isEqual()` and `isSame()` should give the same answer, however, if + //! some operands contains a garbage or other metadata in the upper 8 bytes + //! then `isSame()` may return `true` in cases where `isEqual()` returns + //! false. However. no such case is known at the moment. + ASMJIT_INLINE bool isSame(const Reg& other) const noexcept { return _packed[0] == _packed[1]; } + + //! Set a 32-bit operand signature based on traits of `T`. + template + ASMJIT_INLINE void setSignatureT() noexcept { _signature = RegTraits::kSignature; } + + //! Get the register type. + ASMJIT_INLINE uint32_t getRegType() const noexcept { return _reg.regType; } + //! Get the register kind. + ASMJIT_INLINE uint32_t getRegKind() const noexcept { return _reg.regKind; } + + //! Clone the register operand. + ASMJIT_INLINE Reg clone() const noexcept { return Reg(*this); } + + //! Cast this register to `RegT` by also changing its signature. + //! + //! NOTE: Improper use of `cloneAs()` can lead to hard-to-debug errors. + template + ASMJIT_INLINE RegT cloneAs() const noexcept { return RegT(Init, RegTraits::kSignature, getId()); } + + //! Cast this register to `other` by also changing its signature. + //! + //! NOTE: Improper use of `cloneAs()` can lead to hard-to-debug errors. + template + ASMJIT_INLINE RegT cloneAs(const RegT& other) const noexcept { return RegT(Init, other.getSignature(), getId()); } + + //! Set the register id to `id`. + ASMJIT_INLINE void setId(uint32_t id) noexcept { _reg.id = id; } + + //! Set register's `signature` and `id`. + ASMJIT_INLINE void setSignatureAndId(uint32_t signature, uint32_t id) noexcept { + _signature = signature; + _reg.id = id; + } + +#define ASMJIT_DEFINE_ABSTRACT_REG(REG, BASE_REG) \ +public: \ + /*! Default constructor doesn't setup anything, it's like `Operand()`. */ \ + ASMJIT_INLINE REG() ASMJIT_NOEXCEPT \ + : BASE_REG() {} \ + \ + /*! Copy the `other` REG register operand. */ \ + ASMJIT_INLINE REG(const REG& other) ASMJIT_NOEXCEPT \ + : BASE_REG(other) {} \ + \ + /*! Copy the `other` REG register operand having its id set to `id` */ \ + ASMJIT_INLINE REG(const Reg& other, uint32_t id) ASMJIT_NOEXCEPT \ + : BASE_REG(other, id) {} \ + \ + /*! Create a REG register operand based on `signature` and `id`. */ \ + ASMJIT_INLINE REG(const _Init& init, uint32_t signature, uint32_t id) ASMJIT_NOEXCEPT \ + : BASE_REG(init, signature, id) {} \ + \ + /*! Create a completely uninitialized REG register operand (garbage). */ \ + explicit ASMJIT_INLINE REG(const _NoInit&) ASMJIT_NOEXCEPT \ + : BASE_REG(NoInit) {} \ + \ + /*! Clone the register operand. */ \ + ASMJIT_INLINE REG clone() const ASMJIT_NOEXCEPT { return REG(*this); } \ + \ + /*! Create a new register from register type and id. */ \ + static ASMJIT_INLINE REG fromTypeAndId(uint32_t regType, uint32_t id) ASMJIT_NOEXCEPT { \ + return REG(Init, signatureOf(regType), id); \ + } \ + \ + /*! Create a new register from signature and id. */ \ + static ASMJIT_INLINE REG fromSignature(uint32_t signature, uint32_t id) ASMJIT_NOEXCEPT { \ + return REG(Init, signature, id); \ + } \ + \ + ASMJIT_INLINE REG& operator=(const REG& other) ASMJIT_NOEXCEPT { copyFrom(other); return *this; } \ + +#define ASMJIT_DEFINE_FINAL_REG(REG, BASE_REG, TRAITS) \ + ASMJIT_DEFINE_ABSTRACT_REG(REG, BASE_REG) \ + \ + /*! Create a REG register with `id`. */ \ + explicit ASMJIT_INLINE REG(uint32_t id) ASMJIT_NOEXCEPT \ + : BASE_REG(Init, kSignature, id) {} \ + \ + enum { \ + kThisType = TRAITS::kType, \ + kThisKind = TRAITS::kKind, \ + kThisSize = TRAITS::kSize, \ + kSignature = TRAITS::kSignature \ + }; +}; + +// ============================================================================ +// [asmjit::Mem] +// ============================================================================ + +//! Base class for all memory operands. +//! +//! NOTE: It's tricky to pack all possible cases that define a memory operand +//! into just 16 bytes. The `Mem` splits data into the following parts: +//! +//! BASE - Base register or label - requires 36 bits total. 4 bits are used +//! to encode the type of the BASE operand (label vs. register type) and +//! the remaining 32 bits define the BASE id, which can be a physical or +//! virtual register index. If BASE type is zero, which is never used as +//! a register-type and label doesn't use it as well then BASE field +//! contains a high DWORD of a possible 64-bit absolute address, which is +//! possible on X64. +//! +//! INDEX - Index register (or theoretically Label, which doesn't make sense). +//! Encoding is similar to BASE - it also requires 36 bits and splits the +//! encoding to INDEX type (4 bits defining the register type) and id (32-bits). +//! +//! OFFSET - A relative offset of the address. Basically if BASE is specified +//! the relative displacement adjusts BASE and an optional INDEX. if BASE is +//! not specified then the OFFSET should be considered as ABSOLUTE address +//! (at least on X86/X64). In that case its low 32 bits are stored in +//! DISPLACEMENT field and the remaining high 32 bits are stored in BASE. +//! +//! OTHER FIELDS - There is rest 8 bits that can be used for whatever purpose. +//! The X86Mem operand uses these bits to store segment override +//! prefix and index shift (scale). +class Mem : public Operand { +public: + //! Memory operand flags. + ASMJIT_ENUM(Flags) { + //! Should be encoded as an absolute memory operand. + kFlagAbs = 0x20, + + //! This memory operand represents a function argument's stack location. + //! + //! This flag is used exclusively by \ref CodeCompiler. + kFlagArgHome = 0x40, + + //! This memory operand represents a virtual register's home-slot. + //! + //! This flag is used exclusively by \ref CodeCompiler. + kFlagRegHome = 0x80 + }; + + //! Helper constants to pack BASE and INDEX register types into `MemData::baseIndexType`. + ASMJIT_ENUM(BaseIndexType) { + kMemBaseTypeShift = 0, + kMemIndexTypeShift = 4, + + kMemBaseTypeMask = 0xF << kMemBaseTypeShift, + kMemIndexTypeMask = 0xF << kMemIndexTypeShift + }; + + static ASMJIT_INLINE uint32_t encodeBaseIndex(uint32_t baseType, uint32_t indexType) noexcept { + return (baseType << kMemBaseTypeShift) | (indexType << kMemIndexTypeShift); + } + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Construct a default `Mem` operand, that points to [0]. + ASMJIT_INLINE Mem() noexcept : Operand(NoInit) { reset(); } + ASMJIT_INLINE Mem(const Mem& other) noexcept : Operand(other) {} + + ASMJIT_INLINE Mem(const _Init&, + uint32_t baseType, uint32_t baseId, + uint32_t indexType, uint32_t indexId, + int32_t off, uint32_t size, uint32_t flags) noexcept : Operand(NoInit) { + + uint32_t baseIndexType = encodeBaseIndex(baseType, indexType); + _init_packed_op_b1_b2_sz_id(kOpMem, baseIndexType, flags, size, indexId); + _mem.base = baseId; + _mem.offsetLo32 = static_cast(off); + } + explicit ASMJIT_INLINE Mem(const _NoInit&) noexcept : Operand(NoInit) {} + + // -------------------------------------------------------------------------- + // [Mem Internal] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE uint32_t _unpackFromFlags(uint32_t shift, uint32_t bits) const noexcept { + return (static_cast(_mem.flags) >> shift) & bits; + } + + ASMJIT_INLINE void _packToFlags(uint32_t value, uint32_t shift, uint32_t bits) noexcept { + ASMJIT_ASSERT(value <= bits); + _mem.flags = static_cast( + (static_cast(_mem.flags) & ~(bits << shift)) | (value << shift)); + } + + // -------------------------------------------------------------------------- + // [Mem Specific] + // -------------------------------------------------------------------------- + + //! Clone `Mem` operand. + ASMJIT_INLINE Mem clone() const noexcept { return Mem(*this); } + + //! Reset the memory operand - after reset the memory points to [0]. + ASMJIT_INLINE void reset() noexcept { + _init_packed_op_b1_b2_sz_id(kOpMem, 0, 0, 0, kInvalidValue); + _init_packed_d2_d3(0, 0); + } + + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _mem.flags; } + ASMJIT_INLINE bool hasFlag(uint32_t flag) const noexcept { return (_mem.flags & flag) != 0; } + ASMJIT_INLINE void addFlags(uint32_t flags) noexcept { _mem.flags |= static_cast(flags); } + ASMJIT_INLINE void clearFlags(uint32_t flags) noexcept { _mem.flags &= ~static_cast(flags); } + + ASMJIT_INLINE bool isAbs() const noexcept { return hasFlag(kFlagAbs); } + ASMJIT_INLINE bool isArgHome() const noexcept { return hasFlag(kFlagArgHome); } + ASMJIT_INLINE bool isRegHome() const noexcept { return hasFlag(kFlagRegHome); } + + ASMJIT_INLINE void setAbs(bool value = false) noexcept { + _mem.flags = static_cast((_mem.flags & ~kFlagAbs) | (value ? kFlagAbs : 0)); + } + ASMJIT_INLINE void markArgHome() noexcept { addFlags(kFlagArgHome); } + ASMJIT_INLINE void markRegHome() noexcept { addFlags(kFlagRegHome); } + + ASMJIT_INLINE void clearAbs(bool value = false) noexcept { _mem.flags &= ~kFlagAbs; } + ASMJIT_INLINE void clearArgHome() noexcept { clearFlags(kFlagArgHome); } + ASMJIT_INLINE void clearRegHome() noexcept { clearFlags(kFlagRegHome); } + + //! Get if the memory operand has a BASE register or label specified. + ASMJIT_INLINE bool hasBase() const noexcept { return getBaseType() != 0; } + //! Get if the memory operand has an INDEX register specified. + ASMJIT_INLINE bool hasIndex() const noexcept { return getIndexType() != 0; } + + //! Get whether the memory operand has BASE and INDEX register. + ASMJIT_INLINE bool hasBaseOrIndex() const noexcept { return _mem.baseIndexType != 0; } + //! Get whether the memory operand has BASE and INDEX register. + ASMJIT_INLINE bool hasBaseAndIndex() const noexcept { return (_mem.baseIndexType & 0xF0U) != 0 && (_mem.baseIndexType & 0x0FU) != 0; } + + //! Get if the BASE operand is a register. + ASMJIT_INLINE bool hasBaseReg() const noexcept { + // It's hacky, but if we know that 0 and 1 reg-type is never used by any + // register we can assume that every reg-type will always have at least + // one of three remaining bits (in a 4-bit nibble) set. This is a good + // optimization as we don't have to unpack the BASE type from `baseIndexType`. + return (_mem.baseIndexType & (0xE << kMemBaseTypeShift)) != 0; + } + //! Get if the BASE operand is a register. + ASMJIT_INLINE bool hasIndexReg() const noexcept { + return (_mem.baseIndexType & (0xE << kMemIndexTypeShift)) != 0; + } + + //! Get if the BASE operand is a label. + ASMJIT_INLINE bool hasBaseLabel() const noexcept { return getBaseType() == Label::kLabelTag; } + + //! Get type of a BASE register (0 if this memory operand doesn't use the BASE register). + //! + //! NOTE: If the returned type is one (a value never associated to a register + //! type) the BASE is not register, but it's a label. One equals to `kLabelTag`. + //! You should always check `hasBaseLabel()` before using `getBaseId()` result. + ASMJIT_INLINE uint32_t getBaseType() const noexcept { return (_mem.baseIndexType >> kMemBaseTypeShift) & 0xF; } + //! Get type of an INDEX register (0 if this memory operand doesn't use the INDEX register). + ASMJIT_INLINE uint32_t getIndexType() const noexcept { return (_mem.baseIndexType >> kMemIndexTypeShift) & 0xF; } + + //! Get both BASE (3:0 bits) and INDEX (7:4 bits) types combined into a single integer. + //! + //! NOTE: This is the way these two are stored. You can use `encodeBaseIndex()` + //! to encode any two types into the format and bit masking to extract the BASE + //! and INDEX types from the packed integer. + ASMJIT_INLINE uint32_t getBaseIndexType() const noexcept { return _mem.baseIndexType; } + + //! Get id of the BASE register or label (if the BASE was specified as label). + ASMJIT_INLINE uint32_t getBaseId() const noexcept { return _mem.base; } + + //! Get id of the INDEX register. + ASMJIT_INLINE uint32_t getIndexId() const noexcept { return _mem.index; } + + ASMJIT_INLINE void _setBase(uint32_t regType, uint32_t id) noexcept { + _mem.baseIndexType = static_cast( + (_mem.baseIndexType & ~encodeBaseIndex(0xF, 0)) | encodeBaseIndex(regType, 0)); + _mem.base = id; + } + + ASMJIT_INLINE void _setIndex(uint32_t regType, uint32_t id) noexcept { + _mem.baseIndexType = static_cast( + (_mem.baseIndexType & ~encodeBaseIndex(0, 0xF)) | encodeBaseIndex(0, regType)); + _mem.index = id; + } + + ASMJIT_INLINE void setBase(const Reg& base) noexcept { return _setBase(base.getRegType(), base.getId()); } + ASMJIT_INLINE void setIndex(const Reg& index) noexcept { return _setIndex(index.getRegType(), index.getId()); } + + //! Reset the memory operand's BASE register / label. + ASMJIT_INLINE void resetBase() noexcept { _setBase(0, 0); } + //! Reset the memory operand's INDEX register. + ASMJIT_INLINE void resetIndex() noexcept { _setIndex(0, kInvalidValue); } + + //! Set memory operand size. + ASMJIT_INLINE void setSize(uint32_t size) noexcept { _mem.size = static_cast(size); } + + ASMJIT_INLINE bool hasOffset() const noexcept { + int32_t lo = static_cast(_mem.offsetLo32); + int32_t hi = static_cast(_mem.base) & -static_cast(getBaseType() == 0); + return (lo | hi) != 0; + } + + //! Get if the memory operand has 64-bit offset or absolute address. + //! + //! If this is true then `hasBase()` must always report false. + ASMJIT_INLINE bool has64BitOffset() const noexcept { return getBaseType() == 0; } + + //! Get a 64-bit offset or absolute address. + ASMJIT_INLINE int64_t getOffset() const noexcept { + return has64BitOffset() + ? static_cast(_mem.offset64) + : static_cast(static_cast(_mem.offsetLo32)); // Sign-Extend. + } + + //! Get a lower part of a 64-bit offset or absolute address. + ASMJIT_INLINE int32_t getOffsetLo32() const noexcept { return static_cast(_mem.offsetLo32); } + //! Get a higher part of a 64-bit offset or absolute address. + //! + //! NOTE: This function is UNSAFE and returns garbage if `has64BitOffset()` + //! returns false. Never use blindly without checking it. + ASMJIT_INLINE int32_t getOffsetHi32() const noexcept { return static_cast(_mem.base); } + + //! Set a 64-bit offset or an absolute address to `offset`. + //! + //! NOTE: This functions attempts to set both high and low parts of a 64-bit + //! offset, however, if the operand has a BASE register it will store only the + //! low 32 bits of the offset / address as there is no way to store both BASE + //! and 64-bit offset, and there is currently no architecture that has such + //! capability targeted by AsmJit. + ASMJIT_INLINE void setOffset(int64_t offset) noexcept { + if (has64BitOffset()) + _mem.offset64 = static_cast(offset); + else + _mem.offsetLo32 = static_cast(offset & 0xFFFFFFFF); + } + //! Adjust the offset by a 64-bit `off`. + ASMJIT_INLINE void addOffset(int64_t off) noexcept { + if (has64BitOffset()) + _mem.offset64 += static_cast(off); + else + _mem.offsetLo32 += static_cast(off & 0xFFFFFFFF); + } + //! Reset the memory offset to zero. + ASMJIT_INLINE void resetOffset() noexcept { setOffset(0); } + + //! Set a low 32-bit offset to `off`. + ASMJIT_INLINE void setOffsetLo32(int32_t off) noexcept { + _mem.offsetLo32 = static_cast(off); + } + //! Adjust the offset by `off`. + //! + //! NOTE: This is a fast function that doesn't use the HI 32-bits of a + //! 64-bit offset. Use it only if you know that there is a BASE register + //! and the offset is only 32 bits anyway. + ASMJIT_INLINE void addOffsetLo32(int32_t off) noexcept { + _mem.offsetLo32 += static_cast(off); + } + //! Reset the memory offset to zero. + ASMJIT_INLINE void resetOffsetLo32() noexcept { setOffsetLo32(0); } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Mem& operator=(const Mem& other) noexcept { copyFrom(other); return *this; } +}; + +// ============================================================================ +// [asmjit::Imm] +// ============================================================================ + +//! Immediate operand. +//! +//! Immediate operand is usually part of instruction itself. It's inlined after +//! or before the instruction opcode. Immediates can be only signed or unsigned +//! integers. +//! +//! To create immediate operand use `imm()` or `imm_u()` non-members or `Imm` +//! constructors. +class Imm : public Operand { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new immediate value (initial value is 0). + Imm() noexcept : Operand(NoInit) { + _init_packed_op_b1_b2_sz_id(kOpImm, 0, 0, 0, kInvalidValue); + _imm.value.i64 = 0; + } + + //! Create a new signed immediate value, assigning the value to `val`. + explicit Imm(int64_t val) noexcept : Operand(NoInit) { + _init_packed_op_b1_b2_sz_id(kOpImm, 0, 0, 0, kInvalidValue); + _imm.value.i64 = val; + } + + //! Create a new immediate value from `other`. + ASMJIT_INLINE Imm(const Imm& other) noexcept : Operand(other) {} + + explicit ASMJIT_INLINE Imm(const _NoInit&) noexcept : Operand(NoInit) {} + + // -------------------------------------------------------------------------- + // [Immediate Specific] + // -------------------------------------------------------------------------- + + //! Clone `Imm` operand. + ASMJIT_INLINE Imm clone() const noexcept { return Imm(*this); } + + //! Get whether the immediate can be casted to 8-bit signed integer. + ASMJIT_INLINE bool isInt8() const noexcept { return Utils::isInt8(_imm.value.i64); } + //! Get whether the immediate can be casted to 8-bit unsigned integer. + ASMJIT_INLINE bool isUInt8() const noexcept { return Utils::isUInt8(_imm.value.i64); } + + //! Get whether the immediate can be casted to 16-bit signed integer. + ASMJIT_INLINE bool isInt16() const noexcept { return Utils::isInt16(_imm.value.i64); } + //! Get whether the immediate can be casted to 16-bit unsigned integer. + ASMJIT_INLINE bool isUInt16() const noexcept { return Utils::isUInt16(_imm.value.i64); } + + //! Get whether the immediate can be casted to 32-bit signed integer. + ASMJIT_INLINE bool isInt32() const noexcept { return Utils::isInt32(_imm.value.i64); } + //! Get whether the immediate can be casted to 32-bit unsigned integer. + ASMJIT_INLINE bool isUInt32() const noexcept { return Utils::isUInt32(_imm.value.i64); } + + //! Get immediate value as 8-bit signed integer. + ASMJIT_INLINE int8_t getInt8() const noexcept { return static_cast(_imm.value.i32Lo & 0xFF); } + //! Get immediate value as 8-bit unsigned integer. + ASMJIT_INLINE uint8_t getUInt8() const noexcept { return static_cast(_imm.value.u32Lo & 0xFFU); } + //! Get immediate value as 16-bit signed integer. + ASMJIT_INLINE int16_t getInt16() const noexcept { return static_cast(_imm.value.i32Lo & 0xFFFF);} + //! Get immediate value as 16-bit unsigned integer. + ASMJIT_INLINE uint16_t getUInt16() const noexcept { return static_cast(_imm.value.u32Lo & 0xFFFFU);} + + //! Get immediate value as 32-bit signed integer. + ASMJIT_INLINE int32_t getInt32() const noexcept { return _imm.value.i32Lo; } + //! Get low 32-bit signed integer. + ASMJIT_INLINE int32_t getInt32Lo() const noexcept { return _imm.value.i32Lo; } + //! Get high 32-bit signed integer. + ASMJIT_INLINE int32_t getInt32Hi() const noexcept { return _imm.value.i32Hi; } + + //! Get immediate value as 32-bit unsigned integer. + ASMJIT_INLINE uint32_t getUInt32() const noexcept { return _imm.value.u32Lo; } + //! Get low 32-bit signed integer. + ASMJIT_INLINE uint32_t getUInt32Lo() const noexcept { return _imm.value.u32Lo; } + //! Get high 32-bit signed integer. + ASMJIT_INLINE uint32_t getUInt32Hi() const noexcept { return _imm.value.u32Hi; } + + //! Get immediate value as 64-bit signed integer. + ASMJIT_INLINE int64_t getInt64() const noexcept { return _imm.value.i64; } + //! Get immediate value as 64-bit unsigned integer. + ASMJIT_INLINE uint64_t getUInt64() const noexcept { return _imm.value.u64; } + + //! Get immediate value as `intptr_t`. + ASMJIT_INLINE intptr_t getIntPtr() const noexcept { + if (sizeof(intptr_t) == sizeof(int64_t)) + return static_cast(getInt64()); + else + return static_cast(getInt32()); + } + + //! Get immediate value as `uintptr_t`. + ASMJIT_INLINE uintptr_t getUIntPtr() const noexcept { + if (sizeof(uintptr_t) == sizeof(uint64_t)) + return static_cast(getUInt64()); + else + return static_cast(getUInt32()); + } + + //! Set immediate value to 8-bit signed integer `val`. + ASMJIT_INLINE void setInt8(int8_t val) noexcept { _imm.value.i64 = static_cast(val); } + //! Set immediate value to 8-bit unsigned integer `val`. + ASMJIT_INLINE void setUInt8(uint8_t val) noexcept { _imm.value.u64 = static_cast(val); } + + //! Set immediate value to 16-bit signed integer `val`. + ASMJIT_INLINE void setInt16(int16_t val) noexcept { _imm.value.i64 = static_cast(val); } + //! Set immediate value to 16-bit unsigned integer `val`. + ASMJIT_INLINE void setUInt16(uint16_t val) noexcept { _imm.value.u64 = static_cast(val); } + + //! Set immediate value to 32-bit signed integer `val`. + ASMJIT_INLINE void setInt32(int32_t val) noexcept { _imm.value.i64 = static_cast(val); } + //! Set immediate value to 32-bit unsigned integer `val`. + ASMJIT_INLINE void setUInt32(uint32_t val) noexcept { _imm.value.u64 = static_cast(val); } + + //! Set immediate value to 64-bit signed integer `val`. + ASMJIT_INLINE void setInt64(int64_t val) noexcept { _imm.value.i64 = val; } + //! Set immediate value to 64-bit unsigned integer `val`. + ASMJIT_INLINE void setUInt64(uint64_t val) noexcept { _imm.value.u64 = val; } + //! Set immediate value to intptr_t `val`. + ASMJIT_INLINE void setIntPtr(intptr_t val) noexcept { _imm.value.i64 = static_cast(val); } + //! Set immediate value to uintptr_t `val`. + ASMJIT_INLINE void setUIntPtr(uintptr_t val) noexcept { _imm.value.u64 = static_cast(val); } + + //! Set immediate value as unsigned type to `val`. + ASMJIT_INLINE void setPtr(void* p) noexcept { setIntPtr((uint64_t)p); } + //! Set immediate value to `val`. + template + ASMJIT_INLINE void setValue(T val) noexcept { setIntPtr((int64_t)val); } + + // -------------------------------------------------------------------------- + // [Float] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void setFloat(float f) noexcept { + _imm.value.f32Lo = f; + _imm.value.u32Hi = 0; + } + + ASMJIT_INLINE void setDouble(double d) noexcept { + _imm.value.f64 = d; + } + + // -------------------------------------------------------------------------- + // [Truncate] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void truncateTo8Bits() noexcept { + if (ASMJIT_ARCH_64BIT) { + _imm.value.u64 &= static_cast(0x000000FFU); + } + else { + _imm.value.u32Lo &= 0x000000FFU; + _imm.value.u32Hi = 0; + } + } + + ASMJIT_INLINE void truncateTo16Bits() noexcept { + if (ASMJIT_ARCH_64BIT) { + _imm.value.u64 &= static_cast(0x0000FFFFU); + } + else { + _imm.value.u32Lo &= 0x0000FFFFU; + _imm.value.u32Hi = 0; + } + } + + ASMJIT_INLINE void truncateTo32Bits() noexcept { _imm.value.u32Hi = 0; } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + //! Assign `other` to the immediate operand. + ASMJIT_INLINE Imm& operator=(const Imm& other) noexcept { copyFrom(other); return *this; } +}; + +//! Create a signed immediate operand. +static ASMJIT_INLINE Imm imm(int64_t val) noexcept { return Imm(val); } +//! Create an unsigned immediate operand. +static ASMJIT_INLINE Imm imm_u(uint64_t val) noexcept { return Imm(static_cast(val)); } +//! Create an immediate operand from `p`. +template +static ASMJIT_INLINE Imm imm_ptr(T p) noexcept { return Imm(static_cast((intptr_t)p)); } + +// ============================================================================ +// [asmjit::TypeId] +// ============================================================================ + +//! Type-id. +//! +//! This is an additional information that can be used to describe a physical +//! or virtual register. it's used mostly by CodeCompiler to describe register +//! representation (the kind of data stored in the register and the width used) +//! and it's also used by APIs that allow to describe and work with function +//! signatures. +struct TypeId { + // -------------------------------------------------------------------------- + // [Id] + // -------------------------------------------------------------------------- + + enum Id { + kVoid = 0, + + _kIntStart = 32, + _kIntEnd = 41, + + kIntPtr = 32, + kUIntPtr = 33, + + kI8 = 34, + kU8 = 35, + kI16 = 36, + kU16 = 37, + kI32 = 38, + kU32 = 39, + kI64 = 40, + kU64 = 41, + + _kFloatStart = 42, + _kFloatEnd = 44, + + kF32 = 42, + kF64 = 43, + kF80 = 44, + + _kMaskStart = 45, + _kMaskEnd = 48, + + kMask8 = 45, + kMask16 = 46, + kMask32 = 47, + kMask64 = 48, + + _kMmxStart = 49, + _kMmxEnd = 50, + + kMmx32 = 49, + kMmx64 = 50, + + _kVec32Start = 51, + _kVec32End = 60, + + kI8x4 = 51, + kU8x4 = 52, + kI16x2 = 53, + kU16x2 = 54, + kI32x1 = 55, + kU32x1 = 56, + kF32x1 = 59, + + _kVec64Start = 61, + _kVec64End = 70, + + kI8x8 = 61, + kU8x8 = 62, + kI16x4 = 63, + kU16x4 = 64, + kI32x2 = 65, + kU32x2 = 66, + kI64x1 = 67, + kU64x1 = 68, + kF32x2 = 69, + kF64x1 = 70, + + _kVec128Start = 71, + _kVec128End = 80, + + kI8x16 = 71, + kU8x16 = 72, + kI16x8 = 73, + kU16x8 = 74, + kI32x4 = 75, + kU32x4 = 76, + kI64x2 = 77, + kU64x2 = 78, + kF32x4 = 79, + kF64x2 = 80, + + _kVec256Start = 81, + _kVec256End = 90, + + kI8x32 = 81, + kU8x32 = 82, + kI16x16 = 83, + kU16x16 = 84, + kI32x8 = 85, + kU32x8 = 86, + kI64x4 = 87, + kU64x4 = 88, + kF32x8 = 89, + kF64x4 = 90, + + _kVec512Start = 91, + _kVec512End = 100, + + kI8x64 = 91, + kU8x64 = 92, + kI16x32 = 93, + kU16x32 = 94, + kI32x16 = 95, + kU32x16 = 96, + kI64x8 = 97, + kU64x8 = 98, + kF32x16 = 99, + kF64x8 = 100, + + kCount = 101 + }; + + // -------------------------------------------------------------------------- + // [TypeName - Used by Templates] + // -------------------------------------------------------------------------- + + struct Int8 {}; //!< int8_t as C++ type-name. + struct UInt8 {}; //!< uint8_t as C++ type-name. + struct Int16 {}; //!< int16_t as C++ type-name. + struct UInt16 {}; //!< uint16_t as C++ type-name. + struct Int32 {}; //!< int32_t as C++ type-name. + struct UInt32 {}; //!< uint32_t as C++ type-name. + struct Int64 {}; //!< int64_t as C++ type-name. + struct UInt64 {}; //!< uint64_t as C++ type-name. + struct IntPtr {}; //!< intptr_t as C++ type-name. + struct UIntPtr {}; //!< uintptr_t as C++ type-name. + struct Float {}; //!< float as C++ type-name. + struct Double {}; //!< double as C++ type-name. + struct MmxReg {}; //!< MMX register as C++ type-name. + struct Vec128 {}; //!< SIMD128/XMM register as C++ type-name. + struct Vec256 {}; //!< SIMD256/YMM register as C++ type-name. + struct Vec512 {}; //!< SIMD512/ZMM register as C++ type-name. + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + struct Info { + uint8_t sizeOf[128]; + uint8_t elementOf[128]; + }; + + ASMJIT_API static const Info _info; + + static ASMJIT_INLINE bool isVoid(uint32_t typeId) noexcept { return typeId == 0; } + static ASMJIT_INLINE bool isValid(uint32_t typeId) noexcept { return typeId >= _kIntStart && typeId <= _kVec512End; } + static ASMJIT_INLINE bool isAbstract(uint32_t typeId) noexcept { return typeId >= kIntPtr && typeId <= kUIntPtr; } + static ASMJIT_INLINE bool isInt(uint32_t typeId) noexcept { return typeId >= _kIntStart && typeId <= _kIntEnd; } + static ASMJIT_INLINE bool isGpb(uint32_t typeId) noexcept { return typeId >= kI8 && typeId <= kU8; } + static ASMJIT_INLINE bool isGpw(uint32_t typeId) noexcept { return typeId >= kI16 && typeId <= kU16; } + static ASMJIT_INLINE bool isGpd(uint32_t typeId) noexcept { return typeId >= kI32 && typeId <= kU32; } + static ASMJIT_INLINE bool isGpq(uint32_t typeId) noexcept { return typeId >= kI64 && typeId <= kU64; } + static ASMJIT_INLINE bool isFloat(uint32_t typeId) noexcept { return typeId >= _kFloatStart && typeId <= _kFloatEnd; } + static ASMJIT_INLINE bool isMask(uint32_t typeId) noexcept { return typeId >= _kMaskStart && typeId <= _kMaskEnd; } + static ASMJIT_INLINE bool isMmx(uint32_t typeId) noexcept { return typeId >= _kMmxStart && typeId <= _kMmxEnd; } + + static ASMJIT_INLINE bool isVec(uint32_t typeId) noexcept { return typeId >= _kVec32Start && typeId <= _kVec512End; } + static ASMJIT_INLINE bool isVec32(uint32_t typeId) noexcept { return typeId >= _kVec32Start && typeId <= _kVec32End; } + static ASMJIT_INLINE bool isVec64(uint32_t typeId) noexcept { return typeId >= _kVec64Start && typeId <= _kVec64End; } + static ASMJIT_INLINE bool isVec128(uint32_t typeId) noexcept { return typeId >= _kVec128Start && typeId <= _kVec128End; } + static ASMJIT_INLINE bool isVec256(uint32_t typeId) noexcept { return typeId >= _kVec256Start && typeId <= _kVec256End; } + static ASMJIT_INLINE bool isVec512(uint32_t typeId) noexcept { return typeId >= _kVec512Start && typeId <= _kVec512End; } + + static ASMJIT_INLINE uint32_t sizeOf(uint32_t typeId) noexcept { + ASMJIT_ASSERT(typeId < ASMJIT_ARRAY_SIZE(_info.sizeOf)); + return _info.sizeOf[typeId]; + } + + static ASMJIT_INLINE uint32_t elementOf(uint32_t typeId) noexcept { + ASMJIT_ASSERT(typeId < ASMJIT_ARRAY_SIZE(_info.elementOf)); + return _info.elementOf[typeId]; + } + + //! Get an offset to convert a `kIntPtr` and `kUIntPtr` TypeId into a + //! type that matches `gpSize` (general-purpose register size). If you + //! find such TypeId it's then only about adding the offset to it. + //! + //! For example: + //! ~~~ + //! uint32_t gpSize = '4' or '8'; + //! uint32_t deabstractDelta = TypeId::deabstractDeltaOfSize(gpSize); + //! + //! uint32_t typeId = 'some type-id'; + //! + //! // Normalize some typeId into a non-abstract typeId. + //! if (TypeId::isAbstract(typeId)) typeId += deabstractDelta; + //! + //! // The same, but by using TypeId::deabstract() function. + //! typeId = TypeId::deabstract(typeId, deabstractDelta); + //! + //! ~~~ + static ASMJIT_INLINE uint32_t deabstractDeltaOfSize(uint32_t gpSize) noexcept { + return gpSize >= 8 ? kI64 - kIntPtr : kI32 - kIntPtr; + } + + static ASMJIT_INLINE uint32_t deabstract(uint32_t typeId, uint32_t deabstractDelta) noexcept { + return TypeId::isAbstract(typeId) ? typeId += deabstractDelta : typeId; + } +}; + +//! TypeIdOf<> template allows to get a TypeId of a C++ type. +template struct TypeIdOf { + // Don't provide anything if not specialized. +}; +template struct TypeIdOf { + enum { kTypeId = TypeId::kUIntPtr }; +}; + +template +struct TypeIdOfInt { + enum { + kSigned = int(~static_cast(0) < static_cast(0)), + kTypeId = (sizeof(T) == 1) ? (int)(kSigned ? TypeId::kI8 : TypeId::kU8 ) : + (sizeof(T) == 2) ? (int)(kSigned ? TypeId::kI16 : TypeId::kU16) : + (sizeof(T) == 4) ? (int)(kSigned ? TypeId::kI32 : TypeId::kU32) : + (sizeof(T) == 8) ? (int)(kSigned ? TypeId::kI64 : TypeId::kU64) : (int)TypeId::kVoid + }; +}; + +#define ASMJIT_DEFINE_TYPE_ID(T, TYPE_INFO) \ + template<> struct TypeIdOf { enum { kTypeId = TYPE_INFO }; } + +ASMJIT_DEFINE_TYPE_ID(signed char , TypeIdOfInt::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned char , TypeIdOfInt::kTypeId); +ASMJIT_DEFINE_TYPE_ID(short , TypeIdOfInt::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned short , TypeIdOfInt::kTypeId); +ASMJIT_DEFINE_TYPE_ID(int , TypeIdOfInt::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned int , TypeIdOfInt::kTypeId); +ASMJIT_DEFINE_TYPE_ID(long , TypeIdOfInt::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned long , TypeIdOfInt::kTypeId); +#if ASMJIT_CC_MSC && !ASMJIT_CC_MSC_GE(16, 0, 0) +ASMJIT_DEFINE_TYPE_ID(__int64 , TypeIdOfInt<__int64>::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned __int64 , TypeIdOfInt::kTypeId); +#else +ASMJIT_DEFINE_TYPE_ID(long long , TypeIdOfInt::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned long long, TypeIdOfInt::kTypeId); +#endif +#if ASMJIT_CC_HAS_NATIVE_CHAR +ASMJIT_DEFINE_TYPE_ID(char , TypeIdOfInt::kTypeId); +#endif +#if ASMJIT_CC_HAS_NATIVE_CHAR16_T +ASMJIT_DEFINE_TYPE_ID(char16_t , TypeIdOfInt::kTypeId); +#endif +#if ASMJIT_CC_HAS_NATIVE_CHAR32_T +ASMJIT_DEFINE_TYPE_ID(char32_t , TypeIdOfInt::kTypeId); +#endif +#if ASMJIT_CC_HAS_NATIVE_WCHAR_T +ASMJIT_DEFINE_TYPE_ID(wchar_t , TypeIdOfInt::kTypeId); +#endif + +ASMJIT_DEFINE_TYPE_ID(void , TypeId::kVoid); +ASMJIT_DEFINE_TYPE_ID(float , TypeId::kF32); +ASMJIT_DEFINE_TYPE_ID(double , TypeId::kF64); + +ASMJIT_DEFINE_TYPE_ID(TypeId::Int8 , TypeId::kI8); +ASMJIT_DEFINE_TYPE_ID(TypeId::UInt8 , TypeId::kU8); +ASMJIT_DEFINE_TYPE_ID(TypeId::Int16 , TypeId::kI16); +ASMJIT_DEFINE_TYPE_ID(TypeId::UInt16 , TypeId::kU16); +ASMJIT_DEFINE_TYPE_ID(TypeId::Int32 , TypeId::kI32); +ASMJIT_DEFINE_TYPE_ID(TypeId::UInt32 , TypeId::kU32); +ASMJIT_DEFINE_TYPE_ID(TypeId::Int64 , TypeId::kI64); +ASMJIT_DEFINE_TYPE_ID(TypeId::UInt64 , TypeId::kU64); +ASMJIT_DEFINE_TYPE_ID(TypeId::IntPtr , TypeId::kIntPtr); +ASMJIT_DEFINE_TYPE_ID(TypeId::UIntPtr , TypeId::kUIntPtr); +ASMJIT_DEFINE_TYPE_ID(TypeId::Float , TypeId::kF32); +ASMJIT_DEFINE_TYPE_ID(TypeId::Double , TypeId::kF64); +ASMJIT_DEFINE_TYPE_ID(TypeId::MmxReg , TypeId::kMmx64); +ASMJIT_DEFINE_TYPE_ID(TypeId::Vec128 , TypeId::kI32x4); +ASMJIT_DEFINE_TYPE_ID(TypeId::Vec256 , TypeId::kI32x8); +ASMJIT_DEFINE_TYPE_ID(TypeId::Vec512 , TypeId::kI32x16); + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_OPERAND_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/osutils.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/osutils.cpp new file mode 100644 index 00000000..e24e4487 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/osutils.cpp @@ -0,0 +1,228 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/osutils.h" +#include "../base/utils.h" + +#if ASMJIT_OS_POSIX +# include +# include +# include +# include +#endif // ASMJIT_OS_POSIX + +#if ASMJIT_OS_MAC +# include +#endif // ASMJIT_OS_MAC + +#if ASMJIT_OS_WINDOWS +# if defined(_MSC_VER) && _MSC_VER >= 1400 +# include +# else +# define _InterlockedCompareExchange InterlockedCompareExchange +# endif // _MSC_VER +#endif // ASMJIT_OS_WINDOWS + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::OSUtils - Virtual Memory] +// ============================================================================ + +// Windows specific implementation using `VirtualAllocEx` and `VirtualFree`. +#if ASMJIT_OS_WINDOWS +static ASMJIT_NOINLINE const VMemInfo& OSUtils_GetVMemInfo() noexcept { + static VMemInfo vmi; + + if (ASMJIT_UNLIKELY(!vmi.hCurrentProcess)) { + SYSTEM_INFO info; + ::GetSystemInfo(&info); + + vmi.pageSize = Utils::alignToPowerOf2(info.dwPageSize); + vmi.pageGranularity = info.dwAllocationGranularity; + vmi.hCurrentProcess = ::GetCurrentProcess(); + } + + return vmi; +}; + +VMemInfo OSUtils::getVirtualMemoryInfo() noexcept { return OSUtils_GetVMemInfo(); } + +void* OSUtils::allocVirtualMemory(size_t size, size_t* allocated, uint32_t flags) noexcept { + return allocProcessMemory(static_cast(0), size, allocated, flags); +} + +Error OSUtils::releaseVirtualMemory(void* p, size_t size) noexcept { + return releaseProcessMemory(static_cast(0), p, size); +} + +void* OSUtils::allocProcessMemory(HANDLE hProcess, size_t size, size_t* allocated, uint32_t flags) noexcept { + if (size == 0) + return nullptr; + + const VMemInfo& vmi = OSUtils_GetVMemInfo(); + if (!hProcess) hProcess = vmi.hCurrentProcess; + + // VirtualAllocEx rounds the allocated size to a page size automatically, + // but we need the `alignedSize` so we can store the real allocated size + // into `allocated` output. + size_t alignedSize = Utils::alignTo(size, vmi.pageSize); + + // Windows XP SP2 / Vista+ allow data-execution-prevention (DEP). + DWORD protectFlags = 0; + + if (flags & kVMExecutable) + protectFlags |= (flags & kVMWritable) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ; + else + protectFlags |= (flags & kVMWritable) ? PAGE_READWRITE : PAGE_READONLY; + + LPVOID mBase = ::VirtualAllocEx(hProcess, nullptr, alignedSize, MEM_COMMIT | MEM_RESERVE, protectFlags); + if (ASMJIT_UNLIKELY(!mBase)) return nullptr; + + ASMJIT_ASSERT(Utils::isAligned(reinterpret_cast(mBase), vmi.pageSize)); + if (allocated) *allocated = alignedSize; + return mBase; +} + +Error OSUtils::releaseProcessMemory(HANDLE hProcess, void* p, size_t size) noexcept { + const VMemInfo& vmi = OSUtils_GetVMemInfo(); + if (!hProcess) hProcess = vmi.hCurrentProcess; + + if (ASMJIT_UNLIKELY(!::VirtualFreeEx(hProcess, p, 0, MEM_RELEASE))) + return DebugUtils::errored(kErrorInvalidState); + + return kErrorOk; +} +#endif // ASMJIT_OS_WINDOWS + +// Posix specific implementation using `mmap()` and `munmap()`. +#if ASMJIT_OS_POSIX + +// Mac uses MAP_ANON instead of MAP_ANONYMOUS. +#if !defined(MAP_ANONYMOUS) +# define MAP_ANONYMOUS MAP_ANON +#endif // MAP_ANONYMOUS + +static const VMemInfo& OSUtils_GetVMemInfo() noexcept { + static VMemInfo vmi; + if (ASMJIT_UNLIKELY(!vmi.pageSize)) { + size_t pageSize = ::getpagesize(); + vmi.pageSize = pageSize; + vmi.pageGranularity = Utils::iMax(pageSize, 65536); + } + return vmi; +}; + +VMemInfo OSUtils::getVirtualMemoryInfo() noexcept { return OSUtils_GetVMemInfo(); } + +void* OSUtils::allocVirtualMemory(size_t size, size_t* allocated, uint32_t flags) noexcept { + const VMemInfo& vmi = OSUtils_GetVMemInfo(); + + size_t alignedSize = Utils::alignTo(size, vmi.pageSize); + int protection = PROT_READ; + + if (flags & kVMWritable ) protection |= PROT_WRITE; + if (flags & kVMExecutable) protection |= PROT_EXEC; + + void* mbase = ::mmap(nullptr, alignedSize, protection, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (ASMJIT_UNLIKELY(mbase == MAP_FAILED)) return nullptr; + + if (allocated) *allocated = alignedSize; + return mbase; +} + +Error OSUtils::releaseVirtualMemory(void* p, size_t size) noexcept { + if (ASMJIT_UNLIKELY(::munmap(p, size) != 0)) + return DebugUtils::errored(kErrorInvalidState); + + return kErrorOk; +} +#endif // ASMJIT_OS_POSIX + +// ============================================================================ +// [asmjit::OSUtils - GetTickCount] +// ============================================================================ + +#if ASMJIT_OS_WINDOWS +static ASMJIT_INLINE uint32_t OSUtils_calcHiRes(const LARGE_INTEGER& now, double freq) noexcept { + return static_cast( + (int64_t)(double(now.QuadPart) / freq) & 0xFFFFFFFF); +} + +uint32_t OSUtils::getTickCount() noexcept { + static volatile uint32_t _hiResTicks; + static volatile double _hiResFreq; + + do { + uint32_t hiResOk = _hiResTicks; + LARGE_INTEGER qpf, now; + + // If for whatever reason this fails, bail to `GetTickCount()`. + if (!::QueryPerformanceCounter(&now)) break; + + // Expected - if we ran through this at least once `hiResTicks` will be + // either 1 or 0xFFFFFFFF. If it's '1' then the Hi-Res counter is available + // and `QueryPerformanceCounter()` can be used. + if (hiResOk == 1) return OSUtils_calcHiRes(now, _hiResFreq); + + // Hi-Res counter is not available, bail to `GetTickCount()`. + if (hiResOk != 0) break; + + // Detect availability of Hi-Res counter, if not available, bail to `GetTickCount()`. + if (!::QueryPerformanceFrequency(&qpf)) { + _InterlockedCompareExchange((LONG*)&_hiResTicks, 0xFFFFFFFF, 0); + break; + } + + double freq = double(qpf.QuadPart) / 1000.0; + _hiResFreq = freq; + + _InterlockedCompareExchange((LONG*)&_hiResTicks, 1, 0); + return OSUtils_calcHiRes(now, freq); + } while (0); + + return ::GetTickCount(); +} +#elif ASMJIT_OS_MAC +uint32_t OSUtils::getTickCount() noexcept { + static mach_timebase_info_data_t _machTime; + + // See Apple's QA1398. + if (ASMJIT_UNLIKELY(_machTime.denom == 0) || mach_timebase_info(&_machTime) != KERN_SUCCESS) + return 0; + + // `mach_absolute_time()` returns nanoseconds, we want milliseconds. + uint64_t t = mach_absolute_time() / 1000000; + + t = t * _machTime.numer / _machTime.denom; + return static_cast(t & 0xFFFFFFFFU); +} +#elif defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 +uint32_t OSUtils::getTickCount() noexcept { + struct timespec ts; + + if (ASMJIT_UNLIKELY(clock_gettime(CLOCK_MONOTONIC, &ts) != 0)) + return 0; + + uint64_t t = (uint64_t(ts.tv_sec ) * 1000) + (uint64_t(ts.tv_nsec) / 1000000); + return static_cast(t & 0xFFFFFFFFU); +} +#else +#error "[asmjit] OSUtils::getTickCount() is not implemented for your target OS." +uint32_t OSUtils::getTickCount() noexcept { return 0; } +#endif + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/osutils.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/osutils.h new file mode 100644 index 00000000..ab7660bc --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/osutils.h @@ -0,0 +1,178 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_OSUTILS_H +#define _ASMJIT_BASE_OSUTILS_H + +// [Dependencies] +#include "../base/globals.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::VMemInfo] +// ============================================================================ + +//! Information about OS virtual memory. +struct VMemInfo { +#if ASMJIT_OS_WINDOWS + HANDLE hCurrentProcess; //!< Handle of the current process (Windows). +#endif // ASMJIT_OS_WINDOWS + size_t pageSize; //!< Virtual memory page size. + size_t pageGranularity; //!< Virtual memory page granularity. +}; + +// ============================================================================ +// [asmjit::OSUtils] +// ============================================================================ + +//! OS utilities. +//! +//! Virtual Memory +//! -------------- +//! +//! Provides functions to allocate and release virtual memory that is required +//! to execute dynamically generated code. If both processor and host OS support +//! data-execution-prevention (DEP) then the only way to run machine code is to +//! allocate virtual memory that has `OSUtils::kVMExecutable` flag enabled. All +//! functions provides by OSUtils use internally platform specific API. +//! +//! Benchmarking +//! ------------ +//! +//! OSUtils also provide a function `getTickCount()` that can be used for +//! benchmarking purposes. It's similar to Windows-only `GetTickCount()`, but +//! it's cross-platform and tries to be the most reliable platform specific +//! calls to make the result usable. +struct OSUtils { + // -------------------------------------------------------------------------- + // [Virtual Memory] + // -------------------------------------------------------------------------- + + //! Virtual memory flags. + ASMJIT_ENUM(VMFlags) { + kVMWritable = 0x00000001U, //!< Virtual memory is writable. + kVMExecutable = 0x00000002U //!< Virtual memory is executable. + }; + + static ASMJIT_API VMemInfo getVirtualMemoryInfo() noexcept; + + //! Allocate virtual memory. + static ASMJIT_API void* allocVirtualMemory(size_t size, size_t* allocated, uint32_t flags) noexcept; + //! Release virtual memory previously allocated by \ref allocVirtualMemory(). + static ASMJIT_API Error releaseVirtualMemory(void* p, size_t size) noexcept; + +#if ASMJIT_OS_WINDOWS + //! Allocate virtual memory of `hProcess` (Windows). + static ASMJIT_API void* allocProcessMemory(HANDLE hProcess, size_t size, size_t* allocated, uint32_t flags) noexcept; + + //! Release virtual memory of `hProcess` (Windows). + static ASMJIT_API Error releaseProcessMemory(HANDLE hProcess, void* p, size_t size) noexcept; +#endif // ASMJIT_OS_WINDOWS + + // -------------------------------------------------------------------------- + // [GetTickCount] + // -------------------------------------------------------------------------- + + //! Get the current CPU tick count, used for benchmarking (1ms resolution). + static ASMJIT_API uint32_t getTickCount() noexcept; +}; + +// ============================================================================ +// [asmjit::Lock] +// ============================================================================ + +//! \internal +//! +//! Lock. +struct Lock { + ASMJIT_NONCOPYABLE(Lock) + + // -------------------------------------------------------------------------- + // [Windows] + // -------------------------------------------------------------------------- + +#if ASMJIT_OS_WINDOWS + typedef CRITICAL_SECTION Handle; + + //! Create a new `Lock` instance. + ASMJIT_INLINE Lock() noexcept { InitializeCriticalSection(&_handle); } + //! Destroy the `Lock` instance. + ASMJIT_INLINE ~Lock() noexcept { DeleteCriticalSection(&_handle); } + + //! Lock. + ASMJIT_INLINE void lock() noexcept { EnterCriticalSection(&_handle); } + //! Unlock. + ASMJIT_INLINE void unlock() noexcept { LeaveCriticalSection(&_handle); } +#endif // ASMJIT_OS_WINDOWS + + // -------------------------------------------------------------------------- + // [Posix] + // -------------------------------------------------------------------------- + +#if ASMJIT_OS_POSIX + typedef pthread_mutex_t Handle; + + //! Create a new `Lock` instance. + ASMJIT_INLINE Lock() noexcept { pthread_mutex_init(&_handle, nullptr); } + //! Destroy the `Lock` instance. + ASMJIT_INLINE ~Lock() noexcept { pthread_mutex_destroy(&_handle); } + + //! Lock. + ASMJIT_INLINE void lock() noexcept { pthread_mutex_lock(&_handle); } + //! Unlock. + ASMJIT_INLINE void unlock() noexcept { pthread_mutex_unlock(&_handle); } +#endif // ASMJIT_OS_POSIX + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Native handle. + Handle _handle; +}; + +// ============================================================================ +// [asmjit::AutoLock] +// ============================================================================ + +//! \internal +//! +//! Scoped lock. +struct AutoLock { + ASMJIT_NONCOPYABLE(AutoLock) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE AutoLock(Lock& target) noexcept : _target(target) { _target.lock(); } + ASMJIT_INLINE ~AutoLock() noexcept { _target.unlock(); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Reference to the `Lock`. + Lock& _target; +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_OSUTILS_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/regalloc.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/regalloc.cpp new file mode 100644 index 00000000..73362a59 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/regalloc.cpp @@ -0,0 +1,603 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Guard] +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_COMPILER) + +// [Dependencies] +#include "../base/regalloc_p.h" +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::RAPass - Construction / Destruction] +// ============================================================================ + +RAPass::RAPass() noexcept : + CBPass("RA"), + _varMapToVaListOffset(0) {} +RAPass::~RAPass() noexcept {} + +// ============================================================================ +// [asmjit::RAPass - Interface] +// ============================================================================ + +Error RAPass::process(Zone* zone) noexcept { + _zone = zone; + _heap.reset(zone); + _emitComments = (cb()->getGlobalOptions() & CodeEmitter::kOptionLoggingEnabled) != 0; + + Error err = kErrorOk; + CBNode* node = cc()->getFirstNode(); + if (!node) return err; + + do { + if (node->getType() == CBNode::kNodeFunc) { + CCFunc* func = static_cast(node); + node = func->getEnd(); + + err = compile(func); + if (err) break; + } + + // Find a function by skipping all nodes that are not `kNodeFunc`. + do { + node = node->getNext(); + } while (node && node->getType() != CBNode::kNodeFunc); + } while (node); + + _heap.reset(nullptr); + _zone = nullptr; + return err; +} + +Error RAPass::compile(CCFunc* func) noexcept { + ASMJIT_PROPAGATE(prepare(func)); + + Error err; + do { + err = fetch(); + if (err) break; + + err = removeUnreachableCode(); + if (err) break; + + err = livenessAnalysis(); + if (err) break; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (cc()->getGlobalOptions() & CodeEmitter::kOptionLoggingEnabled) { + err = annotate(); + if (err) break; + } +#endif // !ASMJIT_DISABLE_LOGGING + + err = translate(); + } while (false); + + cleanup(); + + // We alter the compiler cursor, because it doesn't make sense to reference + // it after compilation - some nodes may disappear and it's forbidden to add + // new code after the compilation is done. + cc()->_setCursor(nullptr); + return err; +} + +Error RAPass::prepare(CCFunc* func) noexcept { + CBNode* end = func->getEnd(); + + _func = func; + _stop = end->getNext(); + _extraBlock = end; + + _unreachableList.reset(); + _returningList.reset(); + _jccList.reset(); + _contextVd.reset(&_heap); + + _memVarCells = nullptr; + _memStackCells = nullptr; + + _mem1ByteVarsUsed = 0; + _mem2ByteVarsUsed = 0; + _mem4ByteVarsUsed = 0; + _mem8ByteVarsUsed = 0; + _mem16ByteVarsUsed = 0; + _mem32ByteVarsUsed = 0; + _mem64ByteVarsUsed = 0; + _memStackCellsUsed = 0; + + _memMaxAlign = 0; + _memVarTotal = 0; + _memStackTotal = 0; + _memAllTotal = 0; + _annotationLength = 12; + + return kErrorOk; +} + +void RAPass::cleanup() noexcept { + VirtReg** virtArray = _contextVd.getData(); + size_t virtCount = _contextVd.getLength(); + + for (size_t i = 0; i < virtCount; i++) { + VirtReg* vreg = virtArray[i]; + vreg->_raId = kInvalidValue; + vreg->resetPhysId(); + } + + _contextVd.reset(nullptr); + _extraBlock = nullptr; +} + +// ============================================================================ +// [asmjit::RAPass - Mem] +// ============================================================================ + +static ASMJIT_INLINE uint32_t RAGetDefaultAlignment(uint32_t size) { + if (size > 32) + return 64; + else if (size > 16) + return 32; + else if (size > 8) + return 16; + else if (size > 4) + return 8; + else if (size > 2) + return 4; + else if (size > 1) + return 2; + else + return 1; +} + +RACell* RAPass::_newVarCell(VirtReg* vreg) { + ASMJIT_ASSERT(vreg->_memCell == nullptr); + + RACell* cell; + uint32_t size = vreg->getSize(); + + if (vreg->isStack()) { + cell = _newStackCell(size, vreg->getAlignment()); + if (ASMJIT_UNLIKELY(!cell)) return nullptr; + } + else { + cell = static_cast(_zone->alloc(sizeof(RACell))); + if (!cell) goto _NoMemory; + + cell->next = _memVarCells; + cell->offset = 0; + cell->size = size; + cell->alignment = size; + + _memVarCells = cell; + _memMaxAlign = Utils::iMax(_memMaxAlign, size); + _memVarTotal += size; + + switch (size) { + case 1: _mem1ByteVarsUsed++ ; break; + case 2: _mem2ByteVarsUsed++ ; break; + case 4: _mem4ByteVarsUsed++ ; break; + case 8: _mem8ByteVarsUsed++ ; break; + case 16: _mem16ByteVarsUsed++; break; + case 32: _mem32ByteVarsUsed++; break; + case 64: _mem64ByteVarsUsed++; break; + + default: + ASMJIT_NOT_REACHED(); + } + } + + vreg->_memCell = cell; + return cell; + +_NoMemory: + cc()->setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; +} + +RACell* RAPass::_newStackCell(uint32_t size, uint32_t alignment) { + RACell* cell = static_cast(_zone->alloc(sizeof(RACell))); + if (ASMJIT_UNLIKELY(!cell)) return nullptr; + + if (alignment == 0) + alignment = RAGetDefaultAlignment(size); + + if (alignment > 64) + alignment = 64; + + ASMJIT_ASSERT(Utils::isPowerOf2(alignment)); + size = Utils::alignTo(size, alignment); + + // Insert it sorted according to the alignment and size. + { + RACell** pPrev = &_memStackCells; + RACell* cur = *pPrev; + + while (cur && ((cur->alignment > alignment) || (cur->alignment == alignment && cur->size > size))) { + pPrev = &cur->next; + cur = *pPrev; + } + + cell->next = cur; + cell->offset = 0; + cell->size = size; + cell->alignment = alignment; + + *pPrev = cell; + _memStackCellsUsed++; + + _memMaxAlign = Utils::iMax(_memMaxAlign, alignment); + _memStackTotal += size; + } + + return cell; +} + +Error RAPass::resolveCellOffsets() { + RACell* varCell = _memVarCells; + RACell* stackCell = _memStackCells; + + uint32_t stackAlignment = 0; + if (stackCell) stackAlignment = stackCell->alignment; + + uint32_t pos64 = 0; + uint32_t pos32 = pos64 + _mem64ByteVarsUsed * 64; + uint32_t pos16 = pos32 + _mem32ByteVarsUsed * 32; + uint32_t pos8 = pos16 + _mem16ByteVarsUsed * 16; + uint32_t pos4 = pos8 + _mem8ByteVarsUsed * 8 ; + uint32_t pos2 = pos4 + _mem4ByteVarsUsed * 4 ; + uint32_t pos1 = pos2 + _mem2ByteVarsUsed * 2 ; + + // Assign home slots. + while (varCell) { + uint32_t size = varCell->size; + uint32_t offset = 0; + + switch (size) { + case 1: offset = pos1 ; pos1 += 1 ; break; + case 2: offset = pos2 ; pos2 += 2 ; break; + case 4: offset = pos4 ; pos4 += 4 ; break; + case 8: offset = pos8 ; pos8 += 8 ; break; + case 16: offset = pos16; pos16 += 16; break; + case 32: offset = pos32; pos32 += 32; break; + case 64: offset = pos64; pos64 += 64; break; + + default: + ASMJIT_NOT_REACHED(); + } + + varCell->offset = static_cast(offset); + varCell = varCell->next; + } + + // Assign stack slots. + uint32_t stackPos = pos1 + _mem1ByteVarsUsed; + while (stackCell) { + uint32_t size = stackCell->size; + uint32_t alignment = stackCell->alignment; + ASMJIT_ASSERT(alignment != 0 && Utils::isPowerOf2(alignment)); + + stackPos = Utils::alignTo(stackPos, alignment); + stackCell->offset = stackPos; + stackCell = stackCell->next; + + stackPos += size; + } + + _memAllTotal = stackPos; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::RAPass - RemoveUnreachableCode] +// ============================================================================ + +Error RAPass::removeUnreachableCode() { + ZoneList::Link* link = _unreachableList.getFirst(); + CBNode* stop = getStop(); + + while (link) { + CBNode* node = link->getValue(); + if (node && node->getPrev() && node != stop) { + // Locate all unreachable nodes. + CBNode* first = node; + do { + if (node->hasPassData()) break; + node = node->getNext(); + } while (node != stop); + + // Remove unreachable nodes that are neither informative nor directives. + if (node != first) { + CBNode* end = node; + node = first; + + // NOTE: The strategy is as follows: + // 1. The algorithm removes everything until it finds a first label. + // 2. After the first label is found it removes only removable nodes. + bool removeEverything = true; + do { + CBNode* next = node->getNext(); + bool remove = node->isRemovable(); + + if (!remove) { + if (node->isLabel()) + removeEverything = false; + remove = removeEverything; + } + + if (remove) { + ASMJIT_TSEC({ + this->_traceNode(this, node, "[REMOVED UNREACHABLE] "); + }); + cc()->removeNode(node); + } + + node = next; + } while (node != end); + } + } + + link = link->getNext(); + } + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::RAPass - Liveness Analysis] +// ============================================================================ + +//! \internal +struct LivenessTarget { + LivenessTarget* prev; //!< Previous target. + CBLabel* node; //!< Target node. + CBJump* from; //!< Jumped from. +}; + +Error RAPass::livenessAnalysis() { + uint32_t bLen = static_cast( + ((_contextVd.getLength() + RABits::kEntityBits - 1) / RABits::kEntityBits)); + + // No variables. + if (bLen == 0) + return kErrorOk; + + CCFunc* func = getFunc(); + CBJump* from = nullptr; + + LivenessTarget* ltCur = nullptr; + LivenessTarget* ltUnused = nullptr; + + ZoneList::Link* retPtr = _returningList.getFirst(); + ASMJIT_ASSERT(retPtr != nullptr); + + CBNode* node = retPtr->getValue(); + RAData* wd; + + size_t varMapToVaListOffset = _varMapToVaListOffset; + RABits* bCur = newBits(bLen); + if (ASMJIT_UNLIKELY(!bCur)) goto NoMem; + + // Allocate bits for code visited first time. +Visit: + for (;;) { + wd = node->getPassData(); + if (wd->liveness) { + if (bCur->_addBitsDelSource(wd->liveness, bCur, bLen)) + goto Patch; + else + goto Done; + } + + RABits* bTmp = copyBits(bCur, bLen); + if (!bTmp) goto NoMem; + + wd = node->getPassData(); + wd->liveness = bTmp; + + uint32_t tiedTotal = wd->tiedTotal; + TiedReg* tiedArray = reinterpret_cast(((uint8_t*)wd) + varMapToVaListOffset); + + for (uint32_t i = 0; i < tiedTotal; i++) { + TiedReg* tied = &tiedArray[i]; + VirtReg* vreg = tied->vreg; + + uint32_t flags = tied->flags; + uint32_t raId = vreg->_raId; + + if ((flags & TiedReg::kWAll) && !(flags & TiedReg::kRAll)) { + // Write-Only. + bTmp->setBit(raId); + bCur->delBit(raId); + } + else { + // Read-Only or Read/Write. + bTmp->setBit(raId); + bCur->setBit(raId); + } + } + + if (node->getType() == CBNode::kNodeLabel) + goto Target; + + if (node == func) + goto Done; + + ASMJIT_ASSERT(node->getPrev()); + node = node->getPrev(); + } + + // Patch already generated liveness bits. +Patch: + for (;;) { + ASMJIT_ASSERT(node->hasPassData()); + ASMJIT_ASSERT(node->getPassData()->liveness != nullptr); + + RABits* bNode = node->getPassData()->liveness; + if (!bNode->_addBitsDelSource(bCur, bLen)) goto Done; + if (node->getType() == CBNode::kNodeLabel) goto Target; + + if (node == func) goto Done; + node = node->getPrev(); + } + +Target: + if (static_cast(node)->getNumRefs() != 0) { + // Push a new LivenessTarget onto the stack if needed. + if (!ltCur || ltCur->node != node) { + // Allocate a new LivenessTarget object (from pool or zone). + LivenessTarget* ltTmp = ltUnused; + + if (ltTmp) { + ltUnused = ltUnused->prev; + } + else { + ltTmp = _zone->allocT( + sizeof(LivenessTarget) - sizeof(RABits) + bLen * sizeof(uintptr_t)); + if (!ltTmp) goto NoMem; + } + + // Initialize and make current - ltTmp->from will be set later on. + ltTmp->prev = ltCur; + ltTmp->node = static_cast(node); + ltCur = ltTmp; + + from = static_cast(node)->getFrom(); + ASMJIT_ASSERT(from != nullptr); + } + else { + from = ltCur->from; + goto JumpNext; + } + + // Visit/Patch. + do { + ltCur->from = from; + bCur->copyBits(node->getPassData()->liveness, bLen); + + if (!from->getPassData()->liveness) { + node = from; + goto Visit; + } + + // Issue #25: Moved 'JumpNext' here since it's important to patch + // code again if there are more live variables than before. +JumpNext: + if (bCur->delBits(from->getPassData()->liveness, bLen)) { + node = from; + goto Patch; + } + + from = from->getJumpNext(); + } while (from); + + // Pop the current LivenessTarget from the stack. + { + LivenessTarget* ltTmp = ltCur; + ltCur = ltCur->prev; + ltTmp->prev = ltUnused; + ltUnused = ltTmp; + } + } + + bCur->copyBits(node->getPassData()->liveness, bLen); + node = node->getPrev(); + if (node->isJmp() || !node->hasPassData()) goto Done; + + wd = node->getPassData(); + if (!wd->liveness) goto Visit; + if (bCur->delBits(wd->liveness, bLen)) goto Patch; + +Done: + if (ltCur) { + node = ltCur->node; + from = ltCur->from; + + goto JumpNext; + } + + retPtr = retPtr->getNext(); + if (retPtr) { + node = retPtr->getValue(); + goto Visit; + } + + return kErrorOk; + +NoMem: + return DebugUtils::errored(kErrorNoHeapMemory); +} + +// ============================================================================ +// [asmjit::RAPass - Annotate] +// ============================================================================ + +Error RAPass::formatInlineComment(StringBuilder& dst, CBNode* node) { +#if !defined(ASMJIT_DISABLE_LOGGING) + RAData* wd = node->getPassData(); + + if (node->hasInlineComment()) + dst.appendString(node->getInlineComment()); + + if (wd && wd->liveness) { + if (dst.getLength() < _annotationLength) + dst.appendChars(' ', _annotationLength - dst.getLength()); + + uint32_t vdCount = static_cast(_contextVd.getLength()); + size_t offset = dst.getLength() + 1; + + dst.appendChar('['); + dst.appendChars(' ', vdCount); + dst.appendChar(']'); + RABits* liveness = wd->liveness; + + uint32_t i; + for (i = 0; i < vdCount; i++) { + if (liveness->getBit(i)) + dst.getData()[offset + i] = '.'; + } + + uint32_t tiedTotal = wd->tiedTotal; + TiedReg* tiedArray = reinterpret_cast(((uint8_t*)wd) + _varMapToVaListOffset); + + for (i = 0; i < tiedTotal; i++) { + TiedReg* tied = &tiedArray[i]; + VirtReg* vreg = tied->vreg; + uint32_t flags = tied->flags; + + char c = 'u'; + if ( (flags & TiedReg::kRAll) && !(flags & TiedReg::kWAll)) c = 'r'; + if (!(flags & TiedReg::kRAll) && (flags & TiedReg::kWAll)) c = 'w'; + if ( (flags & TiedReg::kRAll) && (flags & TiedReg::kWAll)) c = 'x'; + // Uppercase if unused. + if ( (flags & TiedReg::kUnuse)) c -= 'a' - 'A'; + + ASMJIT_ASSERT(offset + vreg->_raId < dst.getLength()); + dst._data[offset + vreg->_raId] = c; + } + } +#endif // !ASMJIT_DISABLE_LOGGING + + return kErrorOk; +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_COMPILER diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/regalloc_p.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/regalloc_p.h new file mode 100644 index 00000000..0aa61a92 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/regalloc_p.h @@ -0,0 +1,578 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_REGALLOC_P_H +#define _ASMJIT_BASE_REGALLOC_P_H + +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_COMPILER) + +// [Dependencies] +#include "../base/codecompiler.h" +#include "../base/zone.h" +#include "../base/zonecontainers.h" +#include "../base/zoneheap.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::TiedReg] +// ============================================================================ + +//! Tied register (CodeCompiler) +//! +//! Tied register is used to describe one ore more register operands that share +//! the same virtual register. Tied register contains all the data that is +//! essential for register allocation. +struct TiedReg { + //! Flags. + ASMJIT_ENUM(Flags) { + kRReg = 0x00000001U, //!< Register read. + kWReg = 0x00000002U, //!< Register write. + kXReg = 0x00000003U, //!< Register read-write. + + kRMem = 0x00000004U, //!< Memory read. + kWMem = 0x00000008U, //!< Memory write. + kXMem = 0x0000000CU, //!< Memory read-write. + + kRDecide = 0x00000010U, //!< RA can decide between reg/mem read. + kWDecide = 0x00000020U, //!< RA can decide between reg/mem write. + kXDecide = 0x00000030U, //!< RA can decide between reg/mem read-write. + + kRFunc = 0x00000100U, //!< Function argument passed in register. + kWFunc = 0x00000200U, //!< Function return value passed into register. + kXFunc = 0x00000300U, //!< Function argument and return value. + kRCall = 0x00000400U, //!< Function call operand. + + kSpill = 0x00000800U, //!< Variable should be spilled. + kUnuse = 0x00001000U, //!< Variable should be unused at the end of the instruction/node. + + kRAll = kRReg | kRMem | kRDecide | kRFunc | kRCall, //!< All in-flags. + kWAll = kWReg | kWMem | kWDecide | kWFunc, //!< All out-flags. + + kRDone = 0x00400000U, //!< Already allocated on the input. + kWDone = 0x00800000U, //!< Already allocated on the output. + + kX86GpbLo = 0x10000000U, + kX86GpbHi = 0x20000000U, + kX86Fld4 = 0x40000000U, + kX86Fld8 = 0x80000000U + }; + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void init(VirtReg* vreg, uint32_t flags = 0, uint32_t inRegs = 0, uint32_t allocableRegs = 0) noexcept { + this->vreg = vreg; + this->flags = flags; + this->refCount = 0; + this->inPhysId = kInvalidReg; + this->outPhysId = kInvalidReg; + this->reserved = 0; + this->inRegs = inRegs; + this->allocableRegs = allocableRegs; + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get whether the variable has to be allocated in a specific input register. + ASMJIT_INLINE uint32_t hasInPhysId() const { return inPhysId != kInvalidReg; } + //! Get whether the variable has to be allocated in a specific output register. + ASMJIT_INLINE uint32_t hasOutPhysId() const { return outPhysId != kInvalidReg; } + + //! Set the input register index. + ASMJIT_INLINE void setInPhysId(uint32_t index) { inPhysId = static_cast(index); } + //! Set the output register index. + ASMJIT_INLINE void setOutPhysId(uint32_t index) { outPhysId = static_cast(index); } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE TiedReg& operator=(const TiedReg& other) { + ::memcpy(this, &other, sizeof(TiedReg)); + return *this; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Pointer to the associated \ref VirtReg. + VirtReg* vreg; + //! Tied flags. + uint32_t flags; + + union { + struct { + //! How many times the variable is used by the instruction/node. + uint8_t refCount; + //! Input register index or `kInvalidReg` if it's not given. + //! + //! Even if the input register index is not given (i.e. it may by any + //! register), register allocator should assign an index that will be + //! used to persist a variable into this specific index. It's helpful + //! in situations where one variable has to be allocated in multiple + //! registers to determine the register which will be persistent. + uint8_t inPhysId; + //! Output register index or `kInvalidReg` if it's not given. + //! + //! Typically `kInvalidReg` if variable is only used on input. + uint8_t outPhysId; + //! \internal + uint8_t reserved; + }; + + //! \internal + //! + //! Packed data #0. + uint32_t packed; + }; + + //! Mandatory input registers. + //! + //! Mandatory input registers are required by the instruction even if + //! there are duplicates. This schema allows us to allocate one variable + //! in one or more register when needed. Required mostly by instructions + //! that have implicit register operands (imul, cpuid, ...) and function + //! call. + uint32_t inRegs; + + //! Allocable input registers. + //! + //! Optional input registers is a mask of all allocable registers for a given + //! variable where we have to pick one of them. This mask is usually not used + //! when _inRegs is set. If both masks are used then the register + //! allocator tries first to find an intersection between these and allocates + //! an extra slot if not found. + uint32_t allocableRegs; +}; + +// ============================================================================ +// [asmjit::RABits] +// ============================================================================ + +//! Fixed size bit-array. +//! +//! Used by variable liveness analysis. +struct RABits { + // -------------------------------------------------------------------------- + // [Enums] + // -------------------------------------------------------------------------- + + enum { + kEntitySize = static_cast(sizeof(uintptr_t)), + kEntityBits = kEntitySize * 8 + }; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE uintptr_t getBit(uint32_t index) const noexcept { + return (data[index / kEntityBits] >> (index % kEntityBits)) & 1; + } + + ASMJIT_INLINE void setBit(uint32_t index) noexcept { + data[index / kEntityBits] |= static_cast(1) << (index % kEntityBits); + } + + ASMJIT_INLINE void delBit(uint32_t index) noexcept { + data[index / kEntityBits] &= ~(static_cast(1) << (index % kEntityBits)); + } + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + //! Copy bits from `s0`, returns `true` if at least one bit is set in `s0`. + ASMJIT_INLINE bool copyBits(const RABits* s0, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t t = s0->data[i]; + data[i] = t; + r |= t; + } + return r != 0; + } + + ASMJIT_INLINE bool addBits(const RABits* s0, uint32_t len) noexcept { + return addBits(this, s0, len); + } + + ASMJIT_INLINE bool addBits(const RABits* s0, const RABits* s1, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t t = s0->data[i] | s1->data[i]; + data[i] = t; + r |= t; + } + return r != 0; + } + + ASMJIT_INLINE bool andBits(const RABits* s1, uint32_t len) noexcept { + return andBits(this, s1, len); + } + + ASMJIT_INLINE bool andBits(const RABits* s0, const RABits* s1, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t t = s0->data[i] & s1->data[i]; + data[i] = t; + r |= t; + } + return r != 0; + } + + ASMJIT_INLINE bool delBits(const RABits* s1, uint32_t len) noexcept { + return delBits(this, s1, len); + } + + ASMJIT_INLINE bool delBits(const RABits* s0, const RABits* s1, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t t = s0->data[i] & ~s1->data[i]; + data[i] = t; + r |= t; + } + return r != 0; + } + + ASMJIT_INLINE bool _addBitsDelSource(RABits* s1, uint32_t len) noexcept { + return _addBitsDelSource(this, s1, len); + } + + ASMJIT_INLINE bool _addBitsDelSource(const RABits* s0, RABits* s1, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t a = s0->data[i]; + uintptr_t b = s1->data[i]; + + this->data[i] = a | b; + b &= ~a; + + s1->data[i] = b; + r |= b; + } + return r != 0; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uintptr_t data[1]; +}; + +// ============================================================================ +// [asmjit::RACell] +// ============================================================================ + +//! Register allocator's (RA) memory cell. +struct RACell { + RACell* next; //!< Next active cell. + int32_t offset; //!< Cell offset, relative to base-offset. + uint32_t size; //!< Cell size. + uint32_t alignment; //!< Cell alignment. +}; + +// ============================================================================ +// [asmjit::RAData] +// ============================================================================ + +//! Register allocator's (RA) data associated with each \ref CBNode. +struct RAData { + ASMJIT_INLINE RAData(uint32_t tiedTotal) noexcept + : liveness(nullptr), + state(nullptr), + tiedTotal(tiedTotal) {} + + RABits* liveness; //!< Liveness bits (populated by liveness-analysis). + RAState* state; //!< Optional saved \ref RAState. + uint32_t tiedTotal; //!< Total count of \ref TiedReg regs. +}; + +// ============================================================================ +// [asmjit::RAState] +// ============================================================================ + +//! Variables' state. +struct RAState {}; + +// ============================================================================ +// [asmjit::RAPass] +// ============================================================================ + +//! \internal +//! +//! Register allocator pipeline used by \ref CodeCompiler. +struct RAPass : public CBPass { +public: + ASMJIT_NONCOPYABLE(RAPass) + + typedef void (ASMJIT_CDECL* TraceNodeFunc)(RAPass* self, CBNode* node_, const char* prefix); + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + RAPass() noexcept; + virtual ~RAPass() noexcept; + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + virtual Error process(Zone* zone) noexcept override; + + //! Run the register allocator for a given function `func`. + virtual Error compile(CCFunc* func) noexcept; + + //! Called by `compile()` to prepare the register allocator to process the + //! given function. It should reset and set-up everything (i.e. no states + //! from a previous compilation should prevail). + virtual Error prepare(CCFunc* func) noexcept; + + //! Called after `compile()` to clean everything up, no matter if it + //! succeeded or failed. + virtual void cleanup() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the associated `CodeCompiler`. + ASMJIT_INLINE CodeCompiler* cc() const noexcept { return static_cast(_cb); } + + //! Get function. + ASMJIT_INLINE CCFunc* getFunc() const noexcept { return _func; } + //! Get stop node. + ASMJIT_INLINE CBNode* getStop() const noexcept { return _stop; } + + //! Get extra block. + ASMJIT_INLINE CBNode* getExtraBlock() const noexcept { return _extraBlock; } + //! Set extra block. + ASMJIT_INLINE void setExtraBlock(CBNode* node) noexcept { _extraBlock = node; } + + // -------------------------------------------------------------------------- + // [State] + // -------------------------------------------------------------------------- + + //! Get current state. + ASMJIT_INLINE RAState* getState() const { return _state; } + + //! Load current state from `target` state. + virtual void loadState(RAState* src) = 0; + + //! Save current state, returning new `RAState` instance. + virtual RAState* saveState() = 0; + + //! Change the current state to `target` state. + virtual void switchState(RAState* src) = 0; + + //! Change the current state to the intersection of two states `a` and `b`. + virtual void intersectStates(RAState* a, RAState* b) = 0; + + // -------------------------------------------------------------------------- + // [Context] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Error assignRAId(VirtReg* vreg) noexcept { + // Likely as a single virtual register would be mostly used more than once, + // this means that each virtual register will hit one bad case (doesn't + // have id) and then all likely cases. + if (ASMJIT_LIKELY(vreg->_raId != kInvalidValue)) return kErrorOk; + + uint32_t raId = static_cast(_contextVd.getLength()); + ASMJIT_PROPAGATE(_contextVd.append(vreg)); + + vreg->_raId = raId; + return kErrorOk; + } + + // -------------------------------------------------------------------------- + // [Mem] + // -------------------------------------------------------------------------- + + RACell* _newVarCell(VirtReg* vreg); + RACell* _newStackCell(uint32_t size, uint32_t alignment); + + ASMJIT_INLINE RACell* getVarCell(VirtReg* vreg) { + RACell* cell = vreg->getMemCell(); + return cell ? cell : _newVarCell(vreg); + } + + virtual Error resolveCellOffsets(); + + // -------------------------------------------------------------------------- + // [Bits] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE RABits* newBits(uint32_t len) { + return static_cast( + _zone->allocZeroed(static_cast(len) * RABits::kEntitySize)); + } + + ASMJIT_INLINE RABits* copyBits(const RABits* src, uint32_t len) { + return static_cast( + _zone->dup(src, static_cast(len) * RABits::kEntitySize)); + } + + // -------------------------------------------------------------------------- + // [Fetch] + // -------------------------------------------------------------------------- + + //! Fetch. + //! + //! Fetch iterates over all nodes and gathers information about all variables + //! used. The process generates information required by register allocator, + //! variable liveness analysis and translator. + virtual Error fetch() = 0; + + // -------------------------------------------------------------------------- + // [Unreachable Code] + // -------------------------------------------------------------------------- + + //! Add unreachable-flow data to the unreachable flow list. + ASMJIT_INLINE Error addUnreachableNode(CBNode* node) { + ZoneList::Link* link = _zone->allocT::Link>(); + if (!link) return DebugUtils::errored(kErrorNoHeapMemory); + + link->setValue(node); + _unreachableList.append(link); + + return kErrorOk; + } + + //! Remove unreachable code. + virtual Error removeUnreachableCode(); + + // -------------------------------------------------------------------------- + // [Code-Flow] + // -------------------------------------------------------------------------- + + //! Add returning node (i.e. node that returns and where liveness analysis + //! should start). + ASMJIT_INLINE Error addReturningNode(CBNode* node) { + ZoneList::Link* link = _zone->allocT::Link>(); + if (!link) return DebugUtils::errored(kErrorNoHeapMemory); + + link->setValue(node); + _returningList.append(link); + + return kErrorOk; + } + + //! Add jump-flow data to the jcc flow list. + ASMJIT_INLINE Error addJccNode(CBNode* node) { + ZoneList::Link* link = _zone->allocT::Link>(); + if (!link) return DebugUtils::errored(kErrorNoHeapMemory); + + link->setValue(node); + _jccList.append(link); + + return kErrorOk; + } + + // -------------------------------------------------------------------------- + // [Analyze] + // -------------------------------------------------------------------------- + + //! Perform variable liveness analysis. + //! + //! Analysis phase iterates over nodes in reverse order and generates a bit + //! array describing variables that are alive at every node in the function. + //! When the analysis start all variables are assumed dead. When a read or + //! read/write operations of a variable is detected the variable becomes + //! alive; when only write operation is detected the variable becomes dead. + //! + //! When a label is found all jumps to that label are followed and analysis + //! repeats until all variables are resolved. + virtual Error livenessAnalysis(); + + // -------------------------------------------------------------------------- + // [Annotate] + // -------------------------------------------------------------------------- + + virtual Error annotate() = 0; + virtual Error formatInlineComment(StringBuilder& dst, CBNode* node); + + // -------------------------------------------------------------------------- + // [Translate] + // -------------------------------------------------------------------------- + + //! Translate code by allocating registers and handling state changes. + virtual Error translate() = 0; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Zone* _zone; //!< Zone passed to `process()`. + ZoneHeap _heap; //!< ZoneHeap that uses `_zone`. + + CCFunc* _func; //!< Function being processed. + CBNode* _stop; //!< Stop node. + CBNode* _extraBlock; //!< Node that is used to insert extra code after the function body. + + TraceNodeFunc _traceNode; //!< Only non-null if ASMJIT_TRACE is enabled. + + //! \internal + //! + //! Offset (how many bytes to add) to `VarMap` to get `TiedReg` array. Used + //! by liveness analysis shared across all backends. This is needed because + //! `VarMap` is a base class for a specialized version that liveness analysis + //! doesn't use, it just needs `TiedReg` array. + uint32_t _varMapToVaListOffset; + + uint8_t _emitComments; //!< Whether to emit comments. + + ZoneList _unreachableList; //!< Unreachable nodes. + ZoneList _returningList; //!< Returning nodes. + ZoneList _jccList; //!< Jump nodes. + + ZoneVector _contextVd; //!< All variables used by the current function. + RACell* _memVarCells; //!< Memory used to spill variables. + RACell* _memStackCells; //!< Memory used to allocate memory on the stack. + + uint32_t _mem1ByteVarsUsed; //!< Count of 1-byte cells. + uint32_t _mem2ByteVarsUsed; //!< Count of 2-byte cells. + uint32_t _mem4ByteVarsUsed; //!< Count of 4-byte cells. + uint32_t _mem8ByteVarsUsed; //!< Count of 8-byte cells. + uint32_t _mem16ByteVarsUsed; //!< Count of 16-byte cells. + uint32_t _mem32ByteVarsUsed; //!< Count of 32-byte cells. + uint32_t _mem64ByteVarsUsed; //!< Count of 64-byte cells. + uint32_t _memStackCellsUsed; //!< Count of stack memory cells. + + uint32_t _memMaxAlign; //!< Maximum memory alignment used by the function. + uint32_t _memVarTotal; //!< Count of bytes used by variables. + uint32_t _memStackTotal; //!< Count of bytes used by stack. + uint32_t _memAllTotal; //!< Count of bytes used by variables and stack after alignment. + + uint32_t _annotationLength; //!< Default length of an annotated instruction. + RAState* _state; //!< Current RA state. +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_COMPILER +#endif // _ASMJIT_BASE_REGALLOC_P_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/runtime.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/runtime.cpp new file mode 100644 index 00000000..f074885a --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/runtime.cpp @@ -0,0 +1,147 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/cpuinfo.h" +#include "../base/runtime.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +static ASMJIT_INLINE void hostFlushInstructionCache(const void* p, size_t size) noexcept { + // Only useful on non-x86 architectures. +#if !ASMJIT_ARCH_X86 && !ASMJIT_ARCH_X64 +# if ASMJIT_OS_WINDOWS + // Windows has a built-in support in kernel32.dll. + ::FlushInstructionCache(_memMgr.getProcessHandle(), p, size); +# endif // ASMJIT_OS_WINDOWS +#else + ASMJIT_UNUSED(p); + ASMJIT_UNUSED(size); +#endif // !ASMJIT_ARCH_X86 && !ASMJIT_ARCH_X64 +} + +static ASMJIT_INLINE uint32_t hostDetectNaturalStackAlignment() noexcept { + // Alignment is assumed to match the pointer-size by default. + uint32_t alignment = sizeof(intptr_t); + + // X86 & X64 + // --------- + // + // - 32-bit X86 requires stack to be aligned to 4 bytes. Modern Linux, Mac + // and UNIX guarantees 16-byte stack alignment even on 32-bit. I'm not + // sure about all other UNIX operating systems, because 16-byte alignment + //! is addition to an older specification. + // - 64-bit X86 requires stack to be aligned to at least 16 bytes. +#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 + int kIsModernOS = ASMJIT_OS_LINUX || // Linux & ANDROID. + ASMJIT_OS_MAC || // OSX and iOS. + ASMJIT_OS_BSD ; // BSD variants. + alignment = ASMJIT_ARCH_X64 || kIsModernOS ? 16 : 4; +#endif + + // ARM32 & ARM64 + // ------------- + // + // - 32-bit ARM requires stack to be aligned to 8 bytes. + // - 64-bit ARM requires stack to be aligned to 16 bytes. +#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 + alignment = ASMJIT_ARCH_ARM32 ? 8 : 16; +#endif + + return alignment; +} + + +// ============================================================================ +// [asmjit::Runtime - Construction / Destruction] +// ============================================================================ + +Runtime::Runtime() noexcept + : _codeInfo(), + _runtimeType(kRuntimeNone), + _allocType(VMemMgr::kAllocFreeable) {} +Runtime::~Runtime() noexcept {} + +// ============================================================================ +// [asmjit::HostRuntime - Construction / Destruction] +// ============================================================================ + +HostRuntime::HostRuntime() noexcept { + _runtimeType = kRuntimeJit; + + // Setup the CodeInfo of this Runtime. + _codeInfo._archInfo = CpuInfo::getHost().getArchInfo(); + _codeInfo._stackAlignment = static_cast(hostDetectNaturalStackAlignment()); + _codeInfo._cdeclCallConv = CallConv::kIdHostCDecl; + _codeInfo._stdCallConv = CallConv::kIdHostStdCall; + _codeInfo._fastCallConv = CallConv::kIdHostFastCall; +} +HostRuntime::~HostRuntime() noexcept {} + +// ============================================================================ +// [asmjit::HostRuntime - Interface] +// ============================================================================ + +void HostRuntime::flush(const void* p, size_t size) noexcept { + hostFlushInstructionCache(p, size); +} + +// ============================================================================ +// [asmjit::JitRuntime - Construction / Destruction] +// ============================================================================ + +JitRuntime::JitRuntime() noexcept {} +JitRuntime::~JitRuntime() noexcept {} + +// ============================================================================ +// [asmjit::JitRuntime - Interface] +// ============================================================================ + +Error JitRuntime::_add(void** dst, CodeHolder* code) noexcept { + size_t codeSize = code->getCodeSize(); + if (ASMJIT_UNLIKELY(codeSize == 0)) { + *dst = nullptr; + return DebugUtils::errored(kErrorNoCodeGenerated); + } + + void* p = _memMgr.alloc(codeSize, getAllocType()); + if (ASMJIT_UNLIKELY(!p)) { + *dst = nullptr; + return DebugUtils::errored(kErrorNoVirtualMemory); + } + + // Relocate the code and release the unused memory back to `VMemMgr`. + size_t relocSize = code->relocate(p); + if (ASMJIT_UNLIKELY(relocSize == 0)) { + *dst = nullptr; + _memMgr.release(p); + return DebugUtils::errored(kErrorInvalidState); + } + + if (relocSize < codeSize) + _memMgr.shrink(p, relocSize); + + flush(p, relocSize); + *dst = p; + + return kErrorOk; +} + +Error JitRuntime::_release(void* p) noexcept { + return _memMgr.release(p); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/runtime.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/runtime.h new file mode 100644 index 00000000..bcec6e7b --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/runtime.h @@ -0,0 +1,198 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_RUNTIME_H +#define _ASMJIT_BASE_RUNTIME_H + +// [Dependencies] +#include "../base/codeholder.h" +#include "../base/vmem.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class CodeHolder; + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::Runtime] +// ============================================================================ + +//! Base runtime. +class ASMJIT_VIRTAPI Runtime { +public: + ASMJIT_NONCOPYABLE(Runtime) + + ASMJIT_ENUM(RuntimeType) { + kRuntimeNone = 0, + kRuntimeJit = 1, + kRuntimeRemote = 2 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a `Runtime` instance. + ASMJIT_API Runtime() noexcept; + //! Destroy the `Runtime` instance. + ASMJIT_API virtual ~Runtime() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get CodeInfo of this runtime. + //! + //! CodeInfo can be used to setup a CodeHolder in case you plan to generate a + //! code compatible and executable by this Runtime. + ASMJIT_INLINE const CodeInfo& getCodeInfo() const noexcept { return _codeInfo; } + + //! Get the Runtime's architecture type, see \ref ArchInfo::Type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return _codeInfo.getArchType(); } + //! Get the Runtime's architecture sub-type, see \ref ArchInfo::SubType. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return _codeInfo.getArchSubType(); } + + //! Get the runtime type, see \ref Type. + ASMJIT_INLINE uint32_t getRuntimeType() const noexcept { return _runtimeType; } + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + // NOTE: To allow passing function pointers to `add()` and `release()` the + // virtual methods are prefixed with `_` and called from templates. + + template + ASMJIT_INLINE Error add(Func* dst, CodeHolder* code) noexcept { + return _add(ptr_cast(dst), code); + } + + template + ASMJIT_INLINE Error release(Func dst) noexcept { + return _release(ptr_cast(dst)); + } + + //! Allocate a memory needed for a code stored in the \ref CodeHolder and + //! relocate it to the target location. + //! + //! The beginning of the memory allocated for the function is returned in + //! `dst`. If failed the \ref Error code is returned and `dst` is set to null + //! (this means that you don't have to set it to null before calling `add()`). + virtual Error _add(void** dst, CodeHolder* code) noexcept = 0; + + //! Release `p` allocated by `add()`. + virtual Error _release(void* p) noexcept = 0; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CodeInfo _codeInfo; //!< Basic information about the Runtime's code. + uint8_t _runtimeType; //!< Type of the runtime. + uint8_t _allocType; //!< Type of the allocator the Runtime uses. + uint8_t _reserved[6]; //!< \internal +}; + +// ============================================================================ +// [asmjit::HostRuntime] +// ============================================================================ + +//! Runtime designed to be used in the same process the code is generated in. +class ASMJIT_VIRTAPI HostRuntime : public Runtime { +public: + ASMJIT_NONCOPYABLE(HostRuntime) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a `HostRuntime` instance. + ASMJIT_API HostRuntime() noexcept; + //! Destroy the `HostRuntime` instance. + ASMJIT_API virtual ~HostRuntime() noexcept; + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + //! Flush an instruction cache. + //! + //! This member function is called after the code has been copied to the + //! destination buffer. It is only useful for JIT code generation as it + //! causes a flush of the processor's cache. + //! + //! Flushing is basically a NOP under X86/X64, but is needed by architectures + //! that do not have a transparent instruction cache like ARM. + //! + //! This function can also be overridden to improve compatibility with tools + //! such as Valgrind, however, it's not an official part of AsmJit. + ASMJIT_API virtual void flush(const void* p, size_t size) noexcept; +}; + +// ============================================================================ +// [asmjit::JitRuntime] +// ============================================================================ + +//! Runtime designed to store and execute code generated at runtime (JIT). +class ASMJIT_VIRTAPI JitRuntime : public HostRuntime { +public: + ASMJIT_NONCOPYABLE(JitRuntime) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a `JitRuntime` instance. + ASMJIT_API JitRuntime() noexcept; + //! Destroy the `JitRuntime` instance. + ASMJIT_API virtual ~JitRuntime() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the type of allocation. + ASMJIT_INLINE uint32_t getAllocType() const noexcept { return _allocType; } + //! Set the type of allocation. + ASMJIT_INLINE void setAllocType(uint32_t allocType) noexcept { _allocType = allocType; } + + //! Get the virtual memory manager. + ASMJIT_INLINE VMemMgr* getMemMgr() const noexcept { return const_cast(&_memMgr); } + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Error _add(void** dst, CodeHolder* code) noexcept override; + ASMJIT_API virtual Error _release(void* p) noexcept override; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Virtual memory manager. + VMemMgr _memMgr; +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_RUNTIME_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/simdtypes.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/simdtypes.h new file mode 100644 index 00000000..09d23af6 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/simdtypes.h @@ -0,0 +1,1075 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_SIMDTYPES_H +#define _ASMJIT_BASE_SIMDTYPES_H + +// [Dependencies] +#include "../base/globals.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::Data64] +// ============================================================================ + +//! 64-bit data useful for creating SIMD constants. +union Data64 { + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Set all eight 8-bit signed integers. + static ASMJIT_INLINE Data64 fromI8(int8_t x0) noexcept { + Data64 self; + self.setI8(x0); + return self; + } + + //! Set all eight 8-bit unsigned integers. + static ASMJIT_INLINE Data64 fromU8(uint8_t x0) noexcept { + Data64 self; + self.setU8(x0); + return self; + } + + //! Set all eight 8-bit signed integers. + static ASMJIT_INLINE Data64 fromI8( + int8_t x0, int8_t x1, int8_t x2, int8_t x3, int8_t x4, int8_t x5, int8_t x6, int8_t x7) noexcept { + + Data64 self; + self.setI8(x0, x1, x2, x3, x4, x5, x6, x7); + return self; + } + + //! Set all eight 8-bit unsigned integers. + static ASMJIT_INLINE Data64 fromU8( + uint8_t x0, uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, uint8_t x5, uint8_t x6, uint8_t x7) noexcept { + + Data64 self; + self.setU8(x0, x1, x2, x3, x4, x5, x6, x7); + return self; + } + + //! Set all four 16-bit signed integers. + static ASMJIT_INLINE Data64 fromI16(int16_t x0) noexcept { + Data64 self; + self.setI16(x0); + return self; + } + + //! Set all four 16-bit unsigned integers. + static ASMJIT_INLINE Data64 fromU16(uint16_t x0) noexcept { + Data64 self; + self.setU16(x0); + return self; + } + + //! Set all four 16-bit signed integers. + static ASMJIT_INLINE Data64 fromI16(int16_t x0, int16_t x1, int16_t x2, int16_t x3) noexcept { + Data64 self; + self.setI16(x0, x1, x2, x3); + return self; + } + + //! Set all four 16-bit unsigned integers. + static ASMJIT_INLINE Data64 fromU16(uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3) noexcept { + Data64 self; + self.setU16(x0, x1, x2, x3); + return self; + } + + //! Set all two 32-bit signed integers. + static ASMJIT_INLINE Data64 fromI32(int32_t x0) noexcept { + Data64 self; + self.setI32(x0); + return self; + } + + //! Set all two 32-bit unsigned integers. + static ASMJIT_INLINE Data64 fromU32(uint32_t x0) noexcept { + Data64 self; + self.setU32(x0); + return self; + } + + //! Set all two 32-bit signed integers. + static ASMJIT_INLINE Data64 fromI32(int32_t x0, int32_t x1) noexcept { + Data64 self; + self.setI32(x0, x1); + return self; + } + + //! Set all two 32-bit unsigned integers. + static ASMJIT_INLINE Data64 fromU32(uint32_t x0, uint32_t x1) noexcept { + Data64 self; + self.setU32(x0, x1); + return self; + } + + //! Set 64-bit signed integer. + static ASMJIT_INLINE Data64 fromI64(int64_t x0) noexcept { + Data64 self; + self.setI64(x0); + return self; + } + + //! Set 64-bit unsigned integer. + static ASMJIT_INLINE Data64 fromU64(uint64_t x0) noexcept { + Data64 self; + self.setU64(x0); + return self; + } + + //! Set all two SP-FP values. + static ASMJIT_INLINE Data64 fromF32(float x0) noexcept { + Data64 self; + self.setF32(x0); + return self; + } + + //! Set all two SP-FP values. + static ASMJIT_INLINE Data64 fromF32(float x0, float x1) noexcept { + Data64 self; + self.setF32(x0, x1); + return self; + } + + //! Set all two SP-FP values. + static ASMJIT_INLINE Data64 fromF64(double x0) noexcept { + Data64 self; + self.setF64(x0); + return self; + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Set all eight 8-bit signed integers. + ASMJIT_INLINE void setI8(int8_t x0) noexcept { + setU8(static_cast(x0)); + } + + //! Set all eight 8-bit unsigned integers. + ASMJIT_INLINE void setU8(uint8_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0101010101010101); + uq[0] = xq; + } + else { + uint32_t xd = static_cast(x0) * static_cast(0x01010101U); + ud[0] = xd; + ud[1] = xd; + } + } + + //! Set all eight 8-bit signed integers. + ASMJIT_INLINE void setI8( + int8_t x0, int8_t x1, int8_t x2, int8_t x3, int8_t x4, int8_t x5, int8_t x6, int8_t x7) noexcept { + + sb[0] = x0; sb[1] = x1; sb[2] = x2; sb[3] = x3; + sb[4] = x4; sb[5] = x5; sb[6] = x6; sb[7] = x7; + } + + //! Set all eight 8-bit unsigned integers. + ASMJIT_INLINE void setU8( + uint8_t x0, uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, uint8_t x5, uint8_t x6, uint8_t x7) noexcept { + + ub[0] = x0; ub[1] = x1; ub[2] = x2; ub[3] = x3; + ub[4] = x4; ub[5] = x5; ub[6] = x6; ub[7] = x7; + } + + //! Set all four 16-bit signed integers. + ASMJIT_INLINE void setI16(int16_t x0) noexcept { + setU16(static_cast(x0)); + } + + //! Set all four 16-bit unsigned integers. + ASMJIT_INLINE void setU16(uint16_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0001000100010001); + uq[0] = xq; + } + else { + uint32_t xd = static_cast(x0) * static_cast(0x00010001U); + ud[0] = xd; + ud[1] = xd; + } + } + + //! Set all four 16-bit signed integers. + ASMJIT_INLINE void setI16(int16_t x0, int16_t x1, int16_t x2, int16_t x3) noexcept { + sw[0] = x0; sw[1] = x1; sw[2] = x2; sw[3] = x3; + } + + //! Set all four 16-bit unsigned integers. + ASMJIT_INLINE void setU16(uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3) noexcept { + uw[0] = x0; uw[1] = x1; uw[2] = x2; uw[3] = x3; + } + + //! Set all two 32-bit signed integers. + ASMJIT_INLINE void setI32(int32_t x0) noexcept { + sd[0] = x0; sd[1] = x0; + } + + //! Set all two 32-bit unsigned integers. + ASMJIT_INLINE void setU32(uint32_t x0) noexcept { + ud[0] = x0; ud[1] = x0; + } + + //! Set all two 32-bit signed integers. + ASMJIT_INLINE void setI32(int32_t x0, int32_t x1) noexcept { + sd[0] = x0; sd[1] = x1; + } + + //! Set all two 32-bit unsigned integers. + ASMJIT_INLINE void setU32(uint32_t x0, uint32_t x1) noexcept { + ud[0] = x0; ud[1] = x1; + } + + //! Set 64-bit signed integer. + ASMJIT_INLINE void setI64(int64_t x0) noexcept { + sq[0] = x0; + } + + //! Set 64-bit unsigned integer. + ASMJIT_INLINE void setU64(uint64_t x0) noexcept { + uq[0] = x0; + } + + //! Set all two SP-FP values. + ASMJIT_INLINE void setF32(float x0) noexcept { + sf[0] = x0; sf[1] = x0; + } + + //! Set all two SP-FP values. + ASMJIT_INLINE void setF32(float x0, float x1) noexcept { + sf[0] = x0; sf[1] = x1; + } + + //! Set all two SP-FP values. + ASMJIT_INLINE void setF64(double x0) noexcept { + df[0] = x0; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Array of eight 8-bit signed integers. + int8_t sb[8]; + //! Array of eight 8-bit unsigned integers. + uint8_t ub[8]; + //! Array of four 16-bit signed integers. + int16_t sw[4]; + //! Array of four 16-bit unsigned integers. + uint16_t uw[4]; + //! Array of two 32-bit signed integers. + int32_t sd[2]; + //! Array of two 32-bit unsigned integers. + uint32_t ud[2]; + //! Array of one 64-bit signed integer. + int64_t sq[1]; + //! Array of one 64-bit unsigned integer. + uint64_t uq[1]; + + //! Array of two SP-FP values. + float sf[2]; + //! Array of one DP-FP value. + double df[1]; +}; + +// ============================================================================ +// [asmjit::Data128] +// ============================================================================ + +//! 128-bit data useful for creating SIMD constants. +union Data128 { + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Set all sixteen 8-bit signed integers. + static ASMJIT_INLINE Data128 fromI8(int8_t x0) noexcept { + Data128 self; + self.setI8(x0); + return self; + } + + //! Set all sixteen 8-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU8(uint8_t x0) noexcept { + Data128 self; + self.setU8(x0); + return self; + } + + //! Set all sixteen 8-bit signed integers. + static ASMJIT_INLINE Data128 fromI8( + int8_t x0 , int8_t x1 , int8_t x2 , int8_t x3 , + int8_t x4 , int8_t x5 , int8_t x6 , int8_t x7 , + int8_t x8 , int8_t x9 , int8_t x10, int8_t x11, + int8_t x12, int8_t x13, int8_t x14, int8_t x15) noexcept { + + Data128 self; + self.setI8(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15); + return self; + } + + //! Set all sixteen 8-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU8( + uint8_t x0 , uint8_t x1 , uint8_t x2 , uint8_t x3 , + uint8_t x4 , uint8_t x5 , uint8_t x6 , uint8_t x7 , + uint8_t x8 , uint8_t x9 , uint8_t x10, uint8_t x11, + uint8_t x12, uint8_t x13, uint8_t x14, uint8_t x15) noexcept { + + Data128 self; + self.setU8(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15); + return self; + } + + //! Set all eight 16-bit signed integers. + static ASMJIT_INLINE Data128 fromI16(int16_t x0) noexcept { + Data128 self; + self.setI16(x0); + return self; + } + + //! Set all eight 16-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU16(uint16_t x0) noexcept { + Data128 self; + self.setU16(x0); + return self; + } + + //! Set all eight 16-bit signed integers. + static ASMJIT_INLINE Data128 fromI16( + int16_t x0, int16_t x1, int16_t x2, int16_t x3, int16_t x4, int16_t x5, int16_t x6, int16_t x7) noexcept { + + Data128 self; + self.setI16(x0, x1, x2, x3, x4, x5, x6, x7); + return self; + } + + //! Set all eight 16-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU16( + uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3, uint16_t x4, uint16_t x5, uint16_t x6, uint16_t x7) noexcept { + + Data128 self; + self.setU16(x0, x1, x2, x3, x4, x5, x6, x7); + return self; + } + + //! Set all four 32-bit signed integers. + static ASMJIT_INLINE Data128 fromI32(int32_t x0) noexcept { + Data128 self; + self.setI32(x0); + return self; + } + + //! Set all four 32-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU32(uint32_t x0) noexcept { + Data128 self; + self.setU32(x0); + return self; + } + + //! Set all four 32-bit signed integers. + static ASMJIT_INLINE Data128 fromI32(int32_t x0, int32_t x1, int32_t x2, int32_t x3) noexcept { + Data128 self; + self.setI32(x0, x1, x2, x3); + return self; + } + + //! Set all four 32-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU32(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) noexcept { + Data128 self; + self.setU32(x0, x1, x2, x3); + return self; + } + + //! Set all two 64-bit signed integers. + static ASMJIT_INLINE Data128 fromI64(int64_t x0) noexcept { + Data128 self; + self.setI64(x0); + return self; + } + + //! Set all two 64-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU64(uint64_t x0) noexcept { + Data128 self; + self.setU64(x0); + return self; + } + + //! Set all two 64-bit signed integers. + static ASMJIT_INLINE Data128 fromI64(int64_t x0, int64_t x1) noexcept { + Data128 self; + self.setI64(x0, x1); + return self; + } + + //! Set all two 64-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU64(uint64_t x0, uint64_t x1) noexcept { + Data128 self; + self.setU64(x0, x1); + return self; + } + + //! Set all four SP-FP floats. + static ASMJIT_INLINE Data128 fromF32(float x0) noexcept { + Data128 self; + self.setF32(x0); + return self; + } + + //! Set all four SP-FP floats. + static ASMJIT_INLINE Data128 fromF32(float x0, float x1, float x2, float x3) noexcept { + Data128 self; + self.setF32(x0, x1, x2, x3); + return self; + } + + //! Set all two DP-FP floats. + static ASMJIT_INLINE Data128 fromF64(double x0) noexcept { + Data128 self; + self.setF64(x0); + return self; + } + + //! Set all two DP-FP floats. + static ASMJIT_INLINE Data128 fromF64(double x0, double x1) noexcept { + Data128 self; + self.setF64(x0, x1); + return self; + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Set all sixteen 8-bit signed integers. + ASMJIT_INLINE void setI8(int8_t x0) noexcept { + setU8(static_cast(x0)); + } + + //! Set all sixteen 8-bit unsigned integers. + ASMJIT_INLINE void setU8(uint8_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0101010101010101); + uq[0] = xq; + uq[1] = xq; + } + else { + uint32_t xd = static_cast(x0) * static_cast(0x01010101U); + ud[0] = xd; + ud[1] = xd; + ud[2] = xd; + ud[3] = xd; + } + } + + //! Set all sixteen 8-bit signed integers. + ASMJIT_INLINE void setI8( + int8_t x0 , int8_t x1 , int8_t x2 , int8_t x3 , + int8_t x4 , int8_t x5 , int8_t x6 , int8_t x7 , + int8_t x8 , int8_t x9 , int8_t x10, int8_t x11, + int8_t x12, int8_t x13, int8_t x14, int8_t x15) noexcept { + + sb[0 ] = x0 ; sb[1 ] = x1 ; sb[2 ] = x2 ; sb[3 ] = x3 ; + sb[4 ] = x4 ; sb[5 ] = x5 ; sb[6 ] = x6 ; sb[7 ] = x7 ; + sb[8 ] = x8 ; sb[9 ] = x9 ; sb[10] = x10; sb[11] = x11; + sb[12] = x12; sb[13] = x13; sb[14] = x14; sb[15] = x15; + } + + //! Set all sixteen 8-bit unsigned integers. + ASMJIT_INLINE void setU8( + uint8_t x0 , uint8_t x1 , uint8_t x2 , uint8_t x3 , + uint8_t x4 , uint8_t x5 , uint8_t x6 , uint8_t x7 , + uint8_t x8 , uint8_t x9 , uint8_t x10, uint8_t x11, + uint8_t x12, uint8_t x13, uint8_t x14, uint8_t x15) noexcept { + + ub[0 ] = x0 ; ub[1 ] = x1 ; ub[2 ] = x2 ; ub[3 ] = x3 ; + ub[4 ] = x4 ; ub[5 ] = x5 ; ub[6 ] = x6 ; ub[7 ] = x7 ; + ub[8 ] = x8 ; ub[9 ] = x9 ; ub[10] = x10; ub[11] = x11; + ub[12] = x12; ub[13] = x13; ub[14] = x14; ub[15] = x15; + } + + //! Set all eight 16-bit signed integers. + ASMJIT_INLINE void setI16(int16_t x0) noexcept { + setU16(static_cast(x0)); + } + + //! Set all eight 16-bit unsigned integers. + ASMJIT_INLINE void setU16(uint16_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0001000100010001); + uq[0] = xq; + uq[1] = xq; + } + else { + uint32_t xd = static_cast(x0) * static_cast(0x00010001U); + ud[0] = xd; + ud[1] = xd; + ud[2] = xd; + ud[3] = xd; + } + } + + //! Set all eight 16-bit signed integers. + ASMJIT_INLINE void setI16( + int16_t x0, int16_t x1, int16_t x2, int16_t x3, int16_t x4, int16_t x5, int16_t x6, int16_t x7) noexcept { + + sw[0] = x0; sw[1] = x1; sw[2] = x2; sw[3] = x3; + sw[4] = x4; sw[5] = x5; sw[6] = x6; sw[7] = x7; + } + + //! Set all eight 16-bit unsigned integers. + ASMJIT_INLINE void setU16( + uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3, uint16_t x4, uint16_t x5, uint16_t x6, uint16_t x7) noexcept { + + uw[0] = x0; uw[1] = x1; uw[2] = x2; uw[3] = x3; + uw[4] = x4; uw[5] = x5; uw[6] = x6; uw[7] = x7; + } + + //! Set all four 32-bit signed integers. + ASMJIT_INLINE void setI32(int32_t x0) noexcept { + setU32(static_cast(x0)); + } + + //! Set all four 32-bit unsigned integers. + ASMJIT_INLINE void setU32(uint32_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t t = (static_cast(x0) << 32) + x0; + uq[0] = t; + uq[1] = t; + } + else { + ud[0] = x0; + ud[1] = x0; + ud[2] = x0; + ud[3] = x0; + } + } + + //! Set all four 32-bit signed integers. + ASMJIT_INLINE void setI32(int32_t x0, int32_t x1, int32_t x2, int32_t x3) noexcept { + sd[0] = x0; sd[1] = x1; sd[2] = x2; sd[3] = x3; + } + + //! Set all four 32-bit unsigned integers. + ASMJIT_INLINE void setU32(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) noexcept { + ud[0] = x0; ud[1] = x1; ud[2] = x2; ud[3] = x3; + } + + //! Set all two 64-bit signed integers. + ASMJIT_INLINE void setI64(int64_t x0) noexcept { + sq[0] = x0; sq[1] = x0; + } + + //! Set all two 64-bit unsigned integers. + ASMJIT_INLINE void setU64(uint64_t x0) noexcept { + uq[0] = x0; uq[1] = x0; + } + + //! Set all two 64-bit signed integers. + ASMJIT_INLINE void setI64(int64_t x0, int64_t x1) noexcept { + sq[0] = x0; sq[1] = x1; + } + + //! Set all two 64-bit unsigned integers. + ASMJIT_INLINE void setU64(uint64_t x0, uint64_t x1) noexcept { + uq[0] = x0; uq[1] = x1; + } + + //! Set all four SP-FP floats. + ASMJIT_INLINE void setF32(float x0) noexcept { + sf[0] = x0; sf[1] = x0; sf[2] = x0; sf[3] = x0; + } + + //! Set all four SP-FP floats. + ASMJIT_INLINE void setF32(float x0, float x1, float x2, float x3) noexcept { + sf[0] = x0; sf[1] = x1; sf[2] = x2; sf[3] = x3; + } + + //! Set all two DP-FP floats. + ASMJIT_INLINE void setF64(double x0) noexcept { + df[0] = x0; df[1] = x0; + } + + //! Set all two DP-FP floats. + ASMJIT_INLINE void setF64(double x0, double x1) noexcept { + df[0] = x0; df[1] = x1; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Array of sixteen 8-bit signed integers. + int8_t sb[16]; + //! Array of sixteen 8-bit unsigned integers. + uint8_t ub[16]; + //! Array of eight 16-bit signed integers. + int16_t sw[8]; + //! Array of eight 16-bit unsigned integers. + uint16_t uw[8]; + //! Array of four 32-bit signed integers. + int32_t sd[4]; + //! Array of four 32-bit unsigned integers. + uint32_t ud[4]; + //! Array of two 64-bit signed integers. + int64_t sq[2]; + //! Array of two 64-bit unsigned integers. + uint64_t uq[2]; + + //! Array of four 32-bit single precision floating points. + float sf[4]; + //! Array of two 64-bit double precision floating points. + double df[2]; +}; + +// ============================================================================ +// [asmjit::Data256] +// ============================================================================ + +//! 256-bit data useful for creating SIMD constants. +union Data256 { + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Set all thirty two 8-bit signed integers. + static ASMJIT_INLINE Data256 fromI8(int8_t x0) noexcept { + Data256 self; + self.setI8(x0); + return self; + } + + //! Set all thirty two 8-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU8(uint8_t x0) noexcept { + Data256 self; + self.setU8(x0); + return self; + } + + //! Set all thirty two 8-bit signed integers. + static ASMJIT_INLINE Data256 fromI8( + int8_t x0 , int8_t x1 , int8_t x2 , int8_t x3 , + int8_t x4 , int8_t x5 , int8_t x6 , int8_t x7 , + int8_t x8 , int8_t x9 , int8_t x10, int8_t x11, + int8_t x12, int8_t x13, int8_t x14, int8_t x15, + int8_t x16, int8_t x17, int8_t x18, int8_t x19, + int8_t x20, int8_t x21, int8_t x22, int8_t x23, + int8_t x24, int8_t x25, int8_t x26, int8_t x27, + int8_t x28, int8_t x29, int8_t x30, int8_t x31) noexcept { + + Data256 self; + self.setI8( + x0, x1 , x2 , x3 , x4 , x5 , x6 , x7 , x8 , x9 , x10, x11, x12, x13, x14, x15, + x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31); + return self; + } + + //! Set all thirty two 8-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU8( + uint8_t x0 , uint8_t x1 , uint8_t x2 , uint8_t x3 , + uint8_t x4 , uint8_t x5 , uint8_t x6 , uint8_t x7 , + uint8_t x8 , uint8_t x9 , uint8_t x10, uint8_t x11, + uint8_t x12, uint8_t x13, uint8_t x14, uint8_t x15, + uint8_t x16, uint8_t x17, uint8_t x18, uint8_t x19, + uint8_t x20, uint8_t x21, uint8_t x22, uint8_t x23, + uint8_t x24, uint8_t x25, uint8_t x26, uint8_t x27, + uint8_t x28, uint8_t x29, uint8_t x30, uint8_t x31) noexcept { + + Data256 self; + self.setU8( + x0, x1 , x2 , x3 , x4 , x5 , x6 , x7 , x8 , x9 , x10, x11, x12, x13, x14, x15, + x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31); + return self; + } + + //! Set all sixteen 16-bit signed integers. + static ASMJIT_INLINE Data256 fromI16(int16_t x0) noexcept { + Data256 self; + self.setI16(x0); + return self; + } + + //! Set all sixteen 16-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU16(uint16_t x0) noexcept { + Data256 self; + self.setU16(x0); + return self; + } + + //! Set all sixteen 16-bit signed integers. + static ASMJIT_INLINE Data256 fromI16( + int16_t x0, int16_t x1, int16_t x2 , int16_t x3 , int16_t x4 , int16_t x5 , int16_t x6 , int16_t x7 , + int16_t x8, int16_t x9, int16_t x10, int16_t x11, int16_t x12, int16_t x13, int16_t x14, int16_t x15) noexcept { + + Data256 self; + self.setI16(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15); + return self; + } + + //! Set all sixteen 16-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU16( + uint16_t x0, uint16_t x1, uint16_t x2 , uint16_t x3 , uint16_t x4 , uint16_t x5 , uint16_t x6 , uint16_t x7 , + uint16_t x8, uint16_t x9, uint16_t x10, uint16_t x11, uint16_t x12, uint16_t x13, uint16_t x14, uint16_t x15) noexcept { + + Data256 self; + self.setU16(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15); + return self; + } + + //! Set all eight 32-bit signed integers. + static ASMJIT_INLINE Data256 fromI32(int32_t x0) noexcept { + Data256 self; + self.setI32(x0); + return self; + } + + //! Set all eight 32-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU32(uint32_t x0) noexcept { + Data256 self; + self.setU32(x0); + return self; + } + + //! Set all eight 32-bit signed integers. + static ASMJIT_INLINE Data256 fromI32( + int32_t x0, int32_t x1, int32_t x2, int32_t x3, + int32_t x4, int32_t x5, int32_t x6, int32_t x7) noexcept { + + Data256 self; + self.setI32(x0, x1, x2, x3, x4, x5, x6, x7); + return self; + } + + //! Set all eight 32-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU32( + uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, + uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7) noexcept { + + Data256 self; + self.setU32(x0, x1, x2, x3, x4, x5, x6, x7); + return self; + } + + //! Set all four 64-bit signed integers. + static ASMJIT_INLINE Data256 fromI64(int64_t x0) noexcept { + Data256 self; + self.setI64(x0); + return self; + } + + //! Set all four 64-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU64(uint64_t x0) noexcept { + Data256 self; + self.setU64(x0); + return self; + } + + //! Set all four 64-bit signed integers. + static ASMJIT_INLINE Data256 fromI64(int64_t x0, int64_t x1, int64_t x2, int64_t x3) noexcept { + Data256 self; + self.setI64(x0, x1, x2, x3); + return self; + } + + //! Set all four 64-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU64(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3) noexcept { + Data256 self; + self.setU64(x0, x1, x2, x3); + return self; + } + + //! Set all eight SP-FP floats. + static ASMJIT_INLINE Data256 fromF32(float x0) noexcept { + Data256 self; + self.setF32(x0); + return self; + } + + //! Set all eight SP-FP floats. + static ASMJIT_INLINE Data256 fromF32( + float x0, float x1, float x2, float x3, + float x4, float x5, float x6, float x7) noexcept { + + Data256 self; + self.setF32(x0, x1, x2, x3, x4, x5, x6, x7); + return self; + } + + //! Set all four DP-FP floats. + static ASMJIT_INLINE Data256 fromF64(double x0) noexcept { + Data256 self; + self.setF64(x0); + return self; + } + + //! Set all four DP-FP floats. + static ASMJIT_INLINE Data256 fromF64(double x0, double x1, double x2, double x3) noexcept { + Data256 self; + self.setF64(x0, x1, x2, x3); + return self; + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Set all thirty two 8-bit signed integers. + ASMJIT_INLINE void setI8(int8_t x0) noexcept { + setU8(static_cast(x0)); + } + + //! Set all thirty two 8-bit unsigned integers. + ASMJIT_INLINE void setU8(uint8_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t xq = static_cast(x0)* ASMJIT_UINT64_C(0x0101010101010101); + uq[0] = xq; + uq[1] = xq; + uq[2] = xq; + uq[3] = xq; + } + else { + uint32_t xd = static_cast(x0)* static_cast(0x01010101U); + ud[0] = xd; + ud[1] = xd; + ud[2] = xd; + ud[3] = xd; + ud[4] = xd; + ud[5] = xd; + ud[6] = xd; + ud[7] = xd; + } + } + + //! Set all thirty two 8-bit signed integers. + ASMJIT_INLINE void setI8( + int8_t x0 , int8_t x1 , int8_t x2 , int8_t x3 , + int8_t x4 , int8_t x5 , int8_t x6 , int8_t x7 , + int8_t x8 , int8_t x9 , int8_t x10, int8_t x11, + int8_t x12, int8_t x13, int8_t x14, int8_t x15, + int8_t x16, int8_t x17, int8_t x18, int8_t x19, + int8_t x20, int8_t x21, int8_t x22, int8_t x23, + int8_t x24, int8_t x25, int8_t x26, int8_t x27, + int8_t x28, int8_t x29, int8_t x30, int8_t x31) noexcept { + + sb[0 ] = x0 ; sb[1 ] = x1 ; sb[2 ] = x2 ; sb[3 ] = x3 ; + sb[4 ] = x4 ; sb[5 ] = x5 ; sb[6 ] = x6 ; sb[7 ] = x7 ; + sb[8 ] = x8 ; sb[9 ] = x9 ; sb[10] = x10; sb[11] = x11; + sb[12] = x12; sb[13] = x13; sb[14] = x14; sb[15] = x15; + sb[16] = x16; sb[17] = x17; sb[18] = x18; sb[19] = x19; + sb[20] = x20; sb[21] = x21; sb[22] = x22; sb[23] = x23; + sb[24] = x24; sb[25] = x25; sb[26] = x26; sb[27] = x27; + sb[28] = x28; sb[29] = x29; sb[30] = x30; sb[31] = x31; + } + + //! Set all thirty two 8-bit unsigned integers. + ASMJIT_INLINE void setU8( + uint8_t x0 , uint8_t x1 , uint8_t x2 , uint8_t x3 , + uint8_t x4 , uint8_t x5 , uint8_t x6 , uint8_t x7 , + uint8_t x8 , uint8_t x9 , uint8_t x10, uint8_t x11, + uint8_t x12, uint8_t x13, uint8_t x14, uint8_t x15, + uint8_t x16, uint8_t x17, uint8_t x18, uint8_t x19, + uint8_t x20, uint8_t x21, uint8_t x22, uint8_t x23, + uint8_t x24, uint8_t x25, uint8_t x26, uint8_t x27, + uint8_t x28, uint8_t x29, uint8_t x30, uint8_t x31) noexcept { + + ub[0 ] = x0 ; ub[1 ] = x1 ; ub[2 ] = x2 ; ub[3 ] = x3 ; + ub[4 ] = x4 ; ub[5 ] = x5 ; ub[6 ] = x6 ; ub[7 ] = x7 ; + ub[8 ] = x8 ; ub[9 ] = x9 ; ub[10] = x10; ub[11] = x11; + ub[12] = x12; ub[13] = x13; ub[14] = x14; ub[15] = x15; + ub[16] = x16; ub[17] = x17; ub[18] = x18; ub[19] = x19; + ub[20] = x20; ub[21] = x21; ub[22] = x22; ub[23] = x23; + ub[24] = x24; ub[25] = x25; ub[26] = x26; ub[27] = x27; + ub[28] = x28; ub[29] = x29; ub[30] = x30; ub[31] = x31; + } + + //! Set all sixteen 16-bit signed integers. + ASMJIT_INLINE void setI16(int16_t x0) noexcept { + setU16(static_cast(x0)); + } + + //! Set all eight 16-bit unsigned integers. + ASMJIT_INLINE void setU16(uint16_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t xq = static_cast(x0)* ASMJIT_UINT64_C(0x0001000100010001); + uq[0] = xq; + uq[1] = xq; + uq[2] = xq; + uq[3] = xq; + } + else { + uint32_t xd = static_cast(x0)* static_cast(0x00010001U); + ud[0] = xd; + ud[1] = xd; + ud[2] = xd; + ud[3] = xd; + ud[4] = xd; + ud[5] = xd; + ud[6] = xd; + ud[7] = xd; + } + } + + //! Set all sixteen 16-bit signed integers. + ASMJIT_INLINE void setI16( + int16_t x0, int16_t x1, int16_t x2 , int16_t x3 , int16_t x4 , int16_t x5 , int16_t x6 , int16_t x7, + int16_t x8, int16_t x9, int16_t x10, int16_t x11, int16_t x12, int16_t x13, int16_t x14, int16_t x15) noexcept { + + sw[0 ] = x0 ; sw[1 ] = x1 ; sw[2 ] = x2 ; sw[3 ] = x3 ; + sw[4 ] = x4 ; sw[5 ] = x5 ; sw[6 ] = x6 ; sw[7 ] = x7 ; + sw[8 ] = x8 ; sw[9 ] = x9 ; sw[10] = x10; sw[11] = x11; + sw[12] = x12; sw[13] = x13; sw[14] = x14; sw[15] = x15; + } + + //! Set all sixteen 16-bit unsigned integers. + ASMJIT_INLINE void setU16( + uint16_t x0, uint16_t x1, uint16_t x2 , uint16_t x3 , uint16_t x4 , uint16_t x5 , uint16_t x6 , uint16_t x7, + uint16_t x8, uint16_t x9, uint16_t x10, uint16_t x11, uint16_t x12, uint16_t x13, uint16_t x14, uint16_t x15) noexcept { + + uw[0 ] = x0 ; uw[1 ] = x1 ; uw[2 ] = x2 ; uw[3 ] = x3 ; + uw[4 ] = x4 ; uw[5 ] = x5 ; uw[6 ] = x6 ; uw[7 ] = x7 ; + uw[8 ] = x8 ; uw[9 ] = x9 ; uw[10] = x10; uw[11] = x11; + uw[12] = x12; uw[13] = x13; uw[14] = x14; uw[15] = x15; + } + + //! Set all eight 32-bit signed integers. + ASMJIT_INLINE void setI32(int32_t x0) noexcept { + setU32(static_cast(x0)); + } + + //! Set all eight 32-bit unsigned integers. + ASMJIT_INLINE void setU32(uint32_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t xq = (static_cast(x0) << 32) + x0; + uq[0] = xq; + uq[1] = xq; + uq[2] = xq; + uq[3] = xq; + } + else { + ud[0] = x0; + ud[1] = x0; + ud[2] = x0; + ud[3] = x0; + ud[4] = x0; + ud[5] = x0; + ud[6] = x0; + ud[7] = x0; + } + } + + //! Set all eight 32-bit signed integers. + ASMJIT_INLINE void setI32( + int32_t x0, int32_t x1, int32_t x2, int32_t x3, + int32_t x4, int32_t x5, int32_t x6, int32_t x7) noexcept { + + sd[0] = x0; sd[1] = x1; sd[2] = x2; sd[3] = x3; + sd[4] = x4; sd[5] = x5; sd[6] = x6; sd[7] = x7; + } + + //! Set all eight 32-bit unsigned integers. + ASMJIT_INLINE void setU32( + uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, + uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7) noexcept { + + ud[0] = x0; ud[1] = x1; ud[2] = x2; ud[3] = x3; + ud[4] = x4; ud[5] = x5; ud[6] = x6; ud[7] = x7; + } + + //! Set all four 64-bit signed integers. + ASMJIT_INLINE void setI64(int64_t x0) noexcept { + sq[0] = x0; sq[1] = x0; sq[2] = x0; sq[3] = x0; + } + + //! Set all four 64-bit unsigned integers. + ASMJIT_INLINE void setU64(uint64_t x0) noexcept { + uq[0] = x0; uq[1] = x0; uq[2] = x0; uq[3] = x0; + } + + //! Set all four 64-bit signed integers. + ASMJIT_INLINE void setI64(int64_t x0, int64_t x1, int64_t x2, int64_t x3) noexcept { + sq[0] = x0; sq[1] = x1; sq[2] = x2; sq[3] = x3; + } + + //! Set all four 64-bit unsigned integers. + ASMJIT_INLINE void setU64(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3) noexcept { + uq[0] = x0; uq[1] = x1; uq[2] = x2; uq[3] = x3; + } + + //! Set all eight SP-FP floats. + ASMJIT_INLINE void setF32(float x0) noexcept { + sf[0] = x0; sf[1] = x0; sf[2] = x0; sf[3] = x0; + sf[4] = x0; sf[5] = x0; sf[6] = x0; sf[7] = x0; + } + + //! Set all eight SP-FP floats. + ASMJIT_INLINE void setF32( + float x0, float x1, float x2, float x3, + float x4, float x5, float x6, float x7) noexcept { + + sf[0] = x0; sf[1] = x1; sf[2] = x2; sf[3] = x3; + sf[4] = x4; sf[5] = x5; sf[6] = x6; sf[7] = x7; + } + + //! Set all four DP-FP floats. + ASMJIT_INLINE void setF64(double x0) noexcept { + df[0] = x0; df[1] = x0; df[2] = x0; df[3] = x0; + } + + //! Set all four DP-FP floats. + ASMJIT_INLINE void setF64(double x0, double x1, double x2, double x3) noexcept { + df[0] = x0; df[1] = x1; df[2] = x2; df[3] = x3; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Array of thirty two 8-bit signed integers. + int8_t sb[32]; + //! Array of thirty two 8-bit unsigned integers. + uint8_t ub[32]; + //! Array of sixteen 16-bit signed integers. + int16_t sw[16]; + //! Array of sixteen 16-bit unsigned integers. + uint16_t uw[16]; + //! Array of eight 32-bit signed integers. + int32_t sd[8]; + //! Array of eight 32-bit unsigned integers. + uint32_t ud[8]; + //! Array of four 64-bit signed integers. + int64_t sq[4]; + //! Array of four 64-bit unsigned integers. + uint64_t uq[4]; + + //! Array of eight 32-bit single precision floating points. + float sf[8]; + //! Array of four 64-bit double precision floating points. + double df[4]; +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_SIMDTYPES_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/string.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/string.cpp new file mode 100644 index 00000000..28064341 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/string.cpp @@ -0,0 +1,353 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/string.h" +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::StringBuilder - Construction / Destruction] +// ============================================================================ + +// Should be placed in read-only memory. +static const char StringBuilder_empty[4] = { 0 }; + +StringBuilder::StringBuilder() noexcept + : _data(const_cast(StringBuilder_empty)), + _length(0), + _capacity(0), + _canFree(false) {} + +StringBuilder::~StringBuilder() noexcept { + if (_canFree) + ASMJIT_FREE(_data); +} + +// ============================================================================ +// [asmjit::StringBuilder - Prepare / Reserve] +// ============================================================================ + +ASMJIT_FAVOR_SIZE char* StringBuilder::prepare(uint32_t op, size_t len) noexcept { + if (op == kStringOpSet) { + // We don't care here, but we can't return a null pointer since it indicates + // failure in memory allocation. + if (len == 0) { + if (_data != StringBuilder_empty) + _data[0] = 0; + + _length = 0; + return _data; + } + + if (_capacity < len) { + if (len >= IntTraits::maxValue() - sizeof(intptr_t) * 2) + return nullptr; + + size_t to = Utils::alignTo(len, sizeof(intptr_t)); + if (to < 256 - sizeof(intptr_t)) + to = 256 - sizeof(intptr_t); + + char* newData = static_cast(ASMJIT_ALLOC(to + sizeof(intptr_t))); + if (!newData) { + clear(); + return nullptr; + } + + if (_canFree) + ASMJIT_FREE(_data); + + _data = newData; + _capacity = to + sizeof(intptr_t) - 1; + _canFree = true; + } + + _data[len] = 0; + _length = len; + + ASMJIT_ASSERT(_length <= _capacity); + return _data; + } + else { + // We don't care here, but we can't return a nullptr pointer since it indicates + // failure in memory allocation. + if (len == 0) + return _data + _length; + + // Overflow. + if (IntTraits::maxValue() - sizeof(intptr_t) * 2 - _length < len) + return nullptr; + + size_t after = _length + len; + if (_capacity < after) { + size_t to = _capacity; + + if (to < 256) + to = 256; + + while (to < 1024 * 1024 && to < after) + to *= 2; + + if (to < after) { + to = after; + if (to < (IntTraits::maxValue() - 1024 * 32)) + to = Utils::alignTo(to, 1024 * 32); + } + + to = Utils::alignTo(to, sizeof(intptr_t)); + char* newData = static_cast(ASMJIT_ALLOC(to + sizeof(intptr_t))); + if (!newData) return nullptr; + + ::memcpy(newData, _data, _length); + if (_canFree) + ASMJIT_FREE(_data); + + _data = newData; + _capacity = to + sizeof(intptr_t) - 1; + _canFree = true; + } + + char* ret = _data + _length; + _data[after] = 0; + _length = after; + + ASMJIT_ASSERT(_length <= _capacity); + return ret; + } +} + +ASMJIT_FAVOR_SIZE Error StringBuilder::reserve(size_t to) noexcept { + if (_capacity >= to) + return kErrorOk; + + if (to >= IntTraits::maxValue() - sizeof(intptr_t) * 2) + return DebugUtils::errored(kErrorNoHeapMemory); + + to = Utils::alignTo(to, sizeof(intptr_t)); + char* newData = static_cast(ASMJIT_ALLOC(to + sizeof(intptr_t))); + + if (!newData) + return DebugUtils::errored(kErrorNoHeapMemory); + + ::memcpy(newData, _data, _length + 1); + if (_canFree) + ASMJIT_FREE(_data); + + _data = newData; + _capacity = to + sizeof(intptr_t) - 1; + _canFree = true; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::StringBuilder - Clear] +// ============================================================================ + +void StringBuilder::clear() noexcept { + if (_data != StringBuilder_empty) + _data[0] = 0; + _length = 0; +} + +// ============================================================================ +// [asmjit::StringBuilder - Methods] +// ============================================================================ + +Error StringBuilder::_opString(uint32_t op, const char* str, size_t len) noexcept { + if (len == kInvalidIndex) + len = str ? ::strlen(str) : static_cast(0); + + char* p = prepare(op, len); + if (!p) return DebugUtils::errored(kErrorNoHeapMemory); + + ::memcpy(p, str, len); + return kErrorOk; +} + +Error StringBuilder::_opChar(uint32_t op, char c) noexcept { + char* p = prepare(op, 1); + if (!p) return DebugUtils::errored(kErrorNoHeapMemory); + + *p = c; + return kErrorOk; +} + +Error StringBuilder::_opChars(uint32_t op, char c, size_t n) noexcept { + char* p = prepare(op, n); + if (!p) return DebugUtils::errored(kErrorNoHeapMemory); + + ::memset(p, c, n); + return kErrorOk; +} + +static const char StringBuilder_numbers[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +Error StringBuilder::_opNumber(uint32_t op, uint64_t i, uint32_t base, size_t width, uint32_t flags) noexcept { + if (base < 2 || base > 36) + base = 10; + + char buf[128]; + char* p = buf + ASMJIT_ARRAY_SIZE(buf); + + uint64_t orig = i; + char sign = '\0'; + + // -------------------------------------------------------------------------- + // [Sign] + // -------------------------------------------------------------------------- + + if ((flags & kStringFormatSigned) != 0 && static_cast(i) < 0) { + i = static_cast(-static_cast(i)); + sign = '-'; + } + else if ((flags & kStringFormatShowSign) != 0) { + sign = '+'; + } + else if ((flags & kStringFormatShowSpace) != 0) { + sign = ' '; + } + + // -------------------------------------------------------------------------- + // [Number] + // -------------------------------------------------------------------------- + + do { + uint64_t d = i / base; + uint64_t r = i % base; + + *--p = StringBuilder_numbers[r]; + i = d; + } while (i); + + size_t numberLength = (size_t)(buf + ASMJIT_ARRAY_SIZE(buf) - p); + + // -------------------------------------------------------------------------- + // [Alternate Form] + // -------------------------------------------------------------------------- + + if ((flags & kStringFormatAlternate) != 0) { + if (base == 8) { + if (orig != 0) + *--p = '0'; + } + if (base == 16) { + *--p = 'x'; + *--p = '0'; + } + } + + // -------------------------------------------------------------------------- + // [Width] + // -------------------------------------------------------------------------- + + if (sign != 0) + *--p = sign; + + if (width > 256) + width = 256; + + if (width <= numberLength) + width = 0; + else + width -= numberLength; + + // -------------------------------------------------------------------------- + // Write] + // -------------------------------------------------------------------------- + + size_t prefixLength = (size_t)(buf + ASMJIT_ARRAY_SIZE(buf) - p) - numberLength; + char* data = prepare(op, prefixLength + width + numberLength); + + if (!data) + return DebugUtils::errored(kErrorNoHeapMemory); + + ::memcpy(data, p, prefixLength); + data += prefixLength; + + ::memset(data, '0', width); + data += width; + + ::memcpy(data, p + prefixLength, numberLength); + return kErrorOk; +} + +Error StringBuilder::_opHex(uint32_t op, const void* data, size_t len) noexcept { + char* dst; + + if (len >= IntTraits::maxValue() / 2 || !(dst = prepare(op, len * 2))) + return DebugUtils::errored(kErrorNoHeapMemory);; + + const char* src = static_cast(data); + for (size_t i = 0; i < len; i++, dst += 2, src++) { + dst[0] = StringBuilder_numbers[(src[0] >> 4) & 0xF]; + dst[1] = StringBuilder_numbers[(src[0] ) & 0xF]; + } + return kErrorOk; +} + +Error StringBuilder::_opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept { + char buf[1024]; + + vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap); + buf[ASMJIT_ARRAY_SIZE(buf) - 1] = '\0'; + + return _opString(op, buf); +} + +Error StringBuilder::setFormat(const char* fmt, ...) noexcept { + bool result; + + va_list ap; + va_start(ap, fmt); + result = _opVFormat(kStringOpSet, fmt, ap); + va_end(ap); + + return result; +} + +Error StringBuilder::appendFormat(const char* fmt, ...) noexcept { + bool result; + + va_list ap; + va_start(ap, fmt); + result = _opVFormat(kStringOpAppend, fmt, ap); + va_end(ap); + + return result; +} + +bool StringBuilder::eq(const char* str, size_t len) const noexcept { + const char* aData = _data; + const char* bData = str; + + size_t aLength = _length; + size_t bLength = len; + + if (bLength == kInvalidIndex) { + size_t i; + for (i = 0; i < aLength; i++) + if (aData[i] != bData[i] || bData[i] == 0) + return false; + return bData[i] == 0; + } + else { + if (aLength != bLength) + return false; + return ::memcmp(aData, bData, aLength) == 0; + } +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/string.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/string.h new file mode 100644 index 00000000..18ebcf81 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/string.h @@ -0,0 +1,289 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_STRING_H +#define _ASMJIT_BASE_STRING_H + +// [Dependencies] +#include "../base/globals.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::SmallString] +// ============================================================================ + +//! Small string is a template that helps to create strings that can be either +//! statically allocated if they are small, or externally allocated in case +//! their length exceed the limit. The `WholeSize` represents the size of the +//! whole `SmallString` structure, based on that size the maximum size of the +//! internal buffer is determined. +template +class SmallString { +public: + enum { kMaxEmbeddedLength = WholeSize - 5 }; + + ASMJIT_INLINE SmallString() noexcept { reset(); } + ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); } + + ASMJIT_INLINE bool isEmpty() const noexcept { return _length == 0; } + ASMJIT_INLINE bool isEmbedded() const noexcept { return _length <= kMaxEmbeddedLength; } + ASMJIT_INLINE bool mustEmbed(size_t len) const noexcept { return len <= kMaxEmbeddedLength; } + + ASMJIT_INLINE uint32_t getLength() const noexcept { return _length; } + ASMJIT_INLINE char* getData() const noexcept { + return _length <= kMaxEmbeddedLength ? const_cast(_embedded) : _external[1]; + } + + ASMJIT_INLINE void setEmbedded(const char* data, size_t len) noexcept { + ASMJIT_ASSERT(len <= kMaxEmbeddedLength); + + _length = static_cast(len); + ::memcpy(_embedded, data, len); + _embedded[len] = '\0'; + } + + ASMJIT_INLINE void setExternal(const char* data, size_t len) noexcept { + ASMJIT_ASSERT(len > kMaxEmbeddedLength); + ASMJIT_ASSERT(len <= ~static_cast(0)); + + _length = static_cast(len); + _external[1] = const_cast(data); + } + + union { + struct { + uint32_t _length; + char _embedded[WholeSize - 4]; + }; + char* _external[2]; + }; +}; + +// ============================================================================ +// [asmjit::StringBuilder] +// ============================================================================ + +//! String builder. +//! +//! String builder was designed to be able to build a string using append like +//! operation to append numbers, other strings, or signle characters. It can +//! allocate it's own buffer or use a buffer created on the stack. +//! +//! String builder contains method specific to AsmJit functionality, used for +//! logging or HTML output. +class StringBuilder { +public: + ASMJIT_NONCOPYABLE(StringBuilder) + + //! \internal + //! + //! String operation. + ASMJIT_ENUM(OpType) { + kStringOpSet = 0, //!< Replace the current string by a given content. + kStringOpAppend = 1 //!< Append a given content to the current string. + }; + + //! \internal + //! + //! String format flags. + ASMJIT_ENUM(StringFormatFlags) { + kStringFormatShowSign = 0x00000001, + kStringFormatShowSpace = 0x00000002, + kStringFormatAlternate = 0x00000004, + kStringFormatSigned = 0x80000000 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_API StringBuilder() noexcept; + ASMJIT_API ~StringBuilder() noexcept; + + ASMJIT_INLINE StringBuilder(const _NoInit&) noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get string builder capacity. + ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; } + //! Get length. + ASMJIT_INLINE size_t getLength() const noexcept { return _length; } + + //! Get null-terminated string data. + ASMJIT_INLINE char* getData() noexcept { return _data; } + //! Get null-terminated string data (const). + ASMJIT_INLINE const char* getData() const noexcept { return _data; } + + // -------------------------------------------------------------------------- + // [Prepare / Reserve] + // -------------------------------------------------------------------------- + + //! Prepare to set/append. + ASMJIT_API char* prepare(uint32_t op, size_t len) noexcept; + + //! Reserve `to` bytes in string builder. + ASMJIT_API Error reserve(size_t to) noexcept; + + // -------------------------------------------------------------------------- + // [Clear] + // -------------------------------------------------------------------------- + + //! Clear the content in String builder. + ASMJIT_API void clear() noexcept; + + // -------------------------------------------------------------------------- + // [Op] + // -------------------------------------------------------------------------- + + ASMJIT_API Error _opString(uint32_t op, const char* str, size_t len = kInvalidIndex) noexcept; + ASMJIT_API Error _opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept; + ASMJIT_API Error _opChar(uint32_t op, char c) noexcept; + ASMJIT_API Error _opChars(uint32_t op, char c, size_t n) noexcept; + ASMJIT_API Error _opNumber(uint32_t op, uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept; + ASMJIT_API Error _opHex(uint32_t op, const void* data, size_t len) noexcept; + + // -------------------------------------------------------------------------- + // [Set] + // -------------------------------------------------------------------------- + + //! Replace the current string with `str` having `len` characters (or `kInvalidIndex` if it's null terminated). + ASMJIT_INLINE Error setString(const char* str, size_t len = kInvalidIndex) noexcept { return _opString(kStringOpSet, str, len); } + //! Replace the current content by a formatted string `fmt`. + ASMJIT_API Error setFormat(const char* fmt, ...) noexcept; + //! Replace the current content by a formatted string `fmt` (va_list version). + ASMJIT_INLINE Error setFormatVA(const char* fmt, va_list ap) noexcept { return _opVFormat(kStringOpSet, fmt, ap); } + + //! Replace the current content by a single `c` character. + ASMJIT_INLINE Error setChar(char c) noexcept { return _opChar(kStringOpSet, c); } + //! Replace the current content by `c` character `n` times. + ASMJIT_INLINE Error setChars(char c, size_t n) noexcept { return _opChars(kStringOpSet, c, n); } + + //! Replace the current content by a formatted integer `i` (signed). + ASMJIT_INLINE Error setInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { + return _opNumber(kStringOpSet, i, base, width, flags | kStringFormatSigned); + } + + //! Replace the current content by a formatted integer `i` (unsigned). + ASMJIT_INLINE Error setUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { + return _opNumber(kStringOpSet, i, base, width, flags); + } + + //! Replace the current content by the given `data` converted to a HEX string. + ASMJIT_INLINE Error setHex(const void* data, size_t len) noexcept { + return _opHex(kStringOpSet, data, len); + } + + // -------------------------------------------------------------------------- + // [Append] + // -------------------------------------------------------------------------- + + //! Append string `str` having `len` characters (or `kInvalidIndex` if it's null terminated). + ASMJIT_INLINE Error appendString(const char* str, size_t len = kInvalidIndex) noexcept { return _opString(kStringOpAppend, str, len); } + //! Append a formatted string `fmt`. + ASMJIT_API Error appendFormat(const char* fmt, ...) noexcept; + //! Append a formatted string `fmt` (va_list version). + ASMJIT_INLINE Error appendFormatVA(const char* fmt, va_list ap) noexcept { return _opVFormat(kStringOpAppend, fmt, ap); } + + //! Append a single `c` character. + ASMJIT_INLINE Error appendChar(char c) noexcept { return _opChar(kStringOpAppend, c); } + //! Append `c` character `n` times. + ASMJIT_INLINE Error appendChars(char c, size_t n) noexcept { return _opChars(kStringOpAppend, c, n); } + + //! Append `i`. + ASMJIT_INLINE Error appendInt(int64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { + return _opNumber(kStringOpAppend, static_cast(i), base, width, flags | kStringFormatSigned); + } + + //! Append `i`. + ASMJIT_INLINE Error appendUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { + return _opNumber(kStringOpAppend, i, base, width, flags); + } + + //! Append the given `data` converted to a HEX string. + ASMJIT_INLINE Error appendHex(const void* data, size_t len) noexcept { + return _opHex(kStringOpAppend, data, len); + } + + // -------------------------------------------------------------------------- + // [Eq] + // -------------------------------------------------------------------------- + + //! Check for equality with other `str` of length `len`. + ASMJIT_API bool eq(const char* str, size_t len = kInvalidIndex) const noexcept; + //! Check for equality with `other`. + ASMJIT_INLINE bool eq(const StringBuilder& other) const noexcept { return eq(other._data); } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool operator==(const StringBuilder& other) const noexcept { return eq(other); } + ASMJIT_INLINE bool operator!=(const StringBuilder& other) const noexcept { return !eq(other); } + + ASMJIT_INLINE bool operator==(const char* str) const noexcept { return eq(str); } + ASMJIT_INLINE bool operator!=(const char* str) const noexcept { return !eq(str); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + char* _data; //!< String data. + size_t _length; //!< String length. + size_t _capacity; //!< String capacity. + size_t _canFree; //!< If the string data can be freed. +}; + +// ============================================================================ +// [asmjit::StringBuilderTmp] +// ============================================================================ + +//! Temporary string builder, has statically allocated `N` bytes. +template +class StringBuilderTmp : public StringBuilder { +public: + ASMJIT_NONCOPYABLE(StringBuilderTmp) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE StringBuilderTmp() noexcept : StringBuilder(NoInit) { + _data = _embeddedData; + _data[0] = 0; + + _length = 0; + _capacity = N; + _canFree = false; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Embedded data. + char _embeddedData[static_cast( + N + 1 + sizeof(intptr_t)) & ~static_cast(sizeof(intptr_t) - 1)]; +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_STRING_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/utils.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/utils.cpp new file mode 100644 index 00000000..10752818 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/utils.cpp @@ -0,0 +1,185 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::Utils - Unit] +// ============================================================================ + +#if defined(ASMJIT_TEST) +UNIT(base_utils) { + uint32_t i; + + INFO("IntTraits<>."); + EXPECT(IntTraits::kIsSigned,"IntTraits should report signed."); + EXPECT(IntTraits::kIsSigned, "IntTraits should report signed."); + EXPECT(IntTraits::kIsSigned, "IntTraits should report signed."); + EXPECT(IntTraits::kIsSigned, "IntTraits should report signed."); + + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned."); + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned."); + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned."); + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned."); + + EXPECT(IntTraits::kIsSigned, "IntTraits should report signed."); + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned."); + + EXPECT(IntTraits::kIsIntPtr, "IntTraits should report intptr_t type."); + EXPECT(IntTraits::kIsIntPtr, "IntTraits should report intptr_t type."); + + INFO("Utils::iMin()/iMax()."); + EXPECT(Utils::iMin( 0, -1) == -1, "Utils::iMin should return a minimum value."); + EXPECT(Utils::iMin(-1, -2) == -2, "Utils::iMin should return a minimum value."); + EXPECT(Utils::iMin( 1, 2) == 1, "Utils::iMin should return a minimum value."); + + EXPECT(Utils::iMax( 0, -1) == 0, "Utils::iMax should return a maximum value."); + EXPECT(Utils::iMax(-1, -2) == -1, "Utils::iMax should return a maximum value."); + EXPECT(Utils::iMax( 1, 2) == 2, "Utils::iMax should return a maximum value."); + + INFO("Utils::inInterval()."); + EXPECT(Utils::inInterval(11 , 10, 20) == true , "Utils::inInterval should return true if inside."); + EXPECT(Utils::inInterval(101, 10, 20) == false, "Utils::inInterval should return false if outside."); + + INFO("Utils::isInt8()."); + EXPECT(Utils::isInt8(-128) == true , "Utils::isInt8<> should return true if inside."); + EXPECT(Utils::isInt8( 127) == true , "Utils::isInt8<> should return true if inside."); + EXPECT(Utils::isInt8(-129) == false, "Utils::isInt8<> should return false if outside."); + EXPECT(Utils::isInt8( 128) == false, "Utils::isInt8<> should return false if outside."); + + INFO("Utils::isInt16()."); + EXPECT(Utils::isInt16(-32768) == true , "Utils::isInt16<> should return true if inside."); + EXPECT(Utils::isInt16( 32767) == true , "Utils::isInt16<> should return true if inside."); + EXPECT(Utils::isInt16(-32769) == false, "Utils::isInt16<> should return false if outside."); + EXPECT(Utils::isInt16( 32768) == false, "Utils::isInt16<> should return false if outside."); + + INFO("Utils::isInt32()."); + EXPECT(Utils::isInt32( 2147483647 ) == true, "Utils::isInt32 should return true if inside."); + EXPECT(Utils::isInt32(-2147483647 - 1) == true, "Utils::isInt32 should return true if inside."); + EXPECT(Utils::isInt32(ASMJIT_UINT64_C(2147483648)) == false, "Utils::isInt32 should return false if outside."); + EXPECT(Utils::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == false, "Utils::isInt32 should return false if outside."); + EXPECT(Utils::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false, "Utils::isInt32 should return false if outside."); + + INFO("Utils::isUInt8()."); + EXPECT(Utils::isUInt8(0) == true , "Utils::isUInt8<> should return true if inside."); + EXPECT(Utils::isUInt8(255) == true , "Utils::isUInt8<> should return true if inside."); + EXPECT(Utils::isUInt8(256) == false, "Utils::isUInt8<> should return false if outside."); + EXPECT(Utils::isUInt8(-1) == false, "Utils::isUInt8<> should return false if negative."); + + INFO("Utils::isUInt12()."); + EXPECT(Utils::isUInt12(0) == true , "Utils::isUInt12<> should return true if inside."); + EXPECT(Utils::isUInt12(4095) == true , "Utils::isUInt12<> should return true if inside."); + EXPECT(Utils::isUInt12(4096) == false, "Utils::isUInt12<> should return false if outside."); + EXPECT(Utils::isUInt12(-1) == false, "Utils::isUInt12<> should return false if negative."); + + INFO("Utils::isUInt16()."); + EXPECT(Utils::isUInt16(0) == true , "Utils::isUInt16<> should return true if inside."); + EXPECT(Utils::isUInt16(65535) == true , "Utils::isUInt16<> should return true if inside."); + EXPECT(Utils::isUInt16(65536) == false, "Utils::isUInt16<> should return false if outside."); + EXPECT(Utils::isUInt16(-1) == false, "Utils::isUInt16<> should return false if negative."); + + INFO("Utils::isUInt32()."); + EXPECT(Utils::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == true, "Utils::isUInt32 should return true if inside."); + EXPECT(Utils::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false, "Utils::isUInt32 should return false if outside."); + EXPECT(Utils::isUInt32(-1) == false, "Utils::isUInt32 should return false if negative."); + + INFO("Utils::isPower2()."); + for (i = 0; i < 64; i++) { + EXPECT(Utils::isPowerOf2(static_cast(1) << i) == true, + "Utils::isPower2() didn't report power of 2."); + EXPECT(Utils::isPowerOf2((static_cast(1) << i) ^ 0x001101) == false, + "Utils::isPower2() didn't report not power of 2."); + } + + INFO("Utils::mask()."); + for (i = 0; i < 32; i++) { + EXPECT(Utils::mask(i) == (1 << i), + "Utils::mask(%u) should return %X.", i, (1 << i)); + } + + INFO("Utils::bits()."); + for (i = 0; i < 32; i++) { + uint32_t expectedBits = 0; + + for (uint32_t b = 0; b < i; b++) + expectedBits |= static_cast(1) << b; + + EXPECT(Utils::bits(i) == expectedBits, + "Utils::bits(%u) should return %X.", i, expectedBits); + } + + INFO("Utils::hasBit()."); + for (i = 0; i < 32; i++) { + EXPECT(Utils::hasBit((1 << i), i) == true, + "Utils::hasBit(%X, %u) should return true.", (1 << i), i); + } + + INFO("Utils::bitCount()."); + for (i = 0; i < 32; i++) { + EXPECT(Utils::bitCount((1 << i)) == 1, + "Utils::bitCount(%X) should return true.", (1 << i)); + } + EXPECT(Utils::bitCount(0x000000F0) == 4, ""); + EXPECT(Utils::bitCount(0x10101010) == 4, ""); + EXPECT(Utils::bitCount(0xFF000000) == 8, ""); + EXPECT(Utils::bitCount(0xFFFFFFF7) == 31, ""); + EXPECT(Utils::bitCount(0x7FFFFFFF) == 31, ""); + + INFO("Utils::findFirstBit()."); + for (i = 0; i < 32; i++) { + EXPECT(Utils::findFirstBit((1 << i)) == i, + "Utils::findFirstBit(%X) should return %u.", (1 << i), i); + } + + INFO("Utils::keepNOnesFromRight()."); + EXPECT(Utils::keepNOnesFromRight(0xF, 1) == 0x1, ""); + EXPECT(Utils::keepNOnesFromRight(0xF, 2) == 0x3, ""); + EXPECT(Utils::keepNOnesFromRight(0xF, 3) == 0x7, ""); + EXPECT(Utils::keepNOnesFromRight(0x5, 2) == 0x5, ""); + EXPECT(Utils::keepNOnesFromRight(0xD, 2) == 0x5, ""); + + INFO("Utils::isAligned()."); + EXPECT(Utils::isAligned(0xFFFF, 4) == false, ""); + EXPECT(Utils::isAligned(0xFFF4, 4) == true , ""); + EXPECT(Utils::isAligned(0xFFF8, 8) == true , ""); + EXPECT(Utils::isAligned(0xFFF0, 16) == true , ""); + + INFO("Utils::alignTo()."); + EXPECT(Utils::alignTo(0xFFFF, 4) == 0x10000, ""); + EXPECT(Utils::alignTo(0xFFF4, 4) == 0x0FFF4, ""); + EXPECT(Utils::alignTo(0xFFF8, 8) == 0x0FFF8, ""); + EXPECT(Utils::alignTo(0xFFF0, 16) == 0x0FFF0, ""); + EXPECT(Utils::alignTo(0xFFF0, 32) == 0x10000, ""); + + INFO("Utils::alignToPowerOf2()."); + EXPECT(Utils::alignToPowerOf2(0xFFFF) == 0x10000, ""); + EXPECT(Utils::alignToPowerOf2(0xF123) == 0x10000, ""); + EXPECT(Utils::alignToPowerOf2(0x0F00) == 0x01000, ""); + EXPECT(Utils::alignToPowerOf2(0x0100) == 0x00100, ""); + EXPECT(Utils::alignToPowerOf2(0x1001) == 0x02000, ""); + + INFO("Utils::alignDiff()."); + EXPECT(Utils::alignDiff(0xFFFF, 4) == 1, ""); + EXPECT(Utils::alignDiff(0xFFF4, 4) == 0, ""); + EXPECT(Utils::alignDiff(0xFFF8, 8) == 0, ""); + EXPECT(Utils::alignDiff(0xFFF0, 16) == 0, ""); + EXPECT(Utils::alignDiff(0xFFF0, 32) == 16, ""); +} +#endif // ASMJIT_TEST + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/utils.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/utils.h new file mode 100644 index 00000000..0333b22f --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/utils.h @@ -0,0 +1,1287 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_UTILS_H +#define _ASMJIT_BASE_UTILS_H + +// [Dependencies] +#include "../base/globals.h" + +#if ASMJIT_CC_MSC_GE(14, 0, 0) +# include +#endif // ASMJIT_OS_WINDOWS + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::IntTraits] +// ============================================================================ + +//! \internal +//! \{ +template +struct IntTraitsPrivate {}; // Let it fail if not specialized! + +template<> struct IntTraitsPrivate<1, 0> { typedef int IntType; typedef int8_t SignedType; typedef uint8_t UnsignedType; }; +template<> struct IntTraitsPrivate<1, 1> { typedef int IntType; typedef int8_t SignedType; typedef uint8_t UnsignedType; }; + +template<> struct IntTraitsPrivate<2, 0> { typedef int IntType; typedef int16_t SignedType; typedef uint16_t UnsignedType; }; +template<> struct IntTraitsPrivate<2, 1> { typedef int IntType; typedef int16_t SignedType; typedef uint16_t UnsignedType; }; + +template<> struct IntTraitsPrivate<4, 0> { typedef int64_t IntType; typedef int32_t SignedType; typedef uint32_t UnsignedType; }; +template<> struct IntTraitsPrivate<4, 1> { typedef int IntType; typedef int32_t SignedType; typedef uint32_t UnsignedType; }; + +template<> struct IntTraitsPrivate<8, 0> { typedef int64_t IntType; typedef int64_t SignedType; typedef uint64_t UnsignedType; }; +template<> struct IntTraitsPrivate<8, 1> { typedef int64_t IntType; typedef int64_t SignedType; typedef uint64_t UnsignedType; }; + +//! \internal +template +struct IntTraits { + enum { + kIsSigned = static_cast(~static_cast(0)) < static_cast(0), + kIsUnsigned = !kIsSigned, + kIs8Bit = sizeof(T) == 1, + kIs16Bit = sizeof(T) == 2, + kIs32Bit = sizeof(T) == 4, + kIs64Bit = sizeof(T) == 8, + kIsIntPtr = sizeof(T) == sizeof(intptr_t) + }; + + typedef typename IntTraitsPrivate::IntType IntType; + typedef typename IntTraitsPrivate::SignedType SignedType; + typedef typename IntTraitsPrivate::UnsignedType UnsignedType; + + //! Get a minimum value of `T`. + static ASMJIT_INLINE T minValue() noexcept { + return kIsSigned ? T((~static_cast(0) >> 1) + static_cast(1)) : T(0); + } + + //! Get a maximum value of `T`. + static ASMJIT_INLINE T maxValue() noexcept { + return kIsSigned ? T(~static_cast(0) >> 1) : ~T(0); + } +}; + +//! \} + +// ============================================================================ +// [asmjit::Utils] +// ============================================================================ + +//! AsmJit utilities - integer, string, etc... +struct Utils { + // -------------------------------------------------------------------------- + // [Float <-> Int] + // -------------------------------------------------------------------------- + + //! \internal + union FloatBits { + int32_t i; + float f; + }; + + //! \internal + union DoubleBits { + int64_t i; + double d; + }; + + //! Bit-cast `float` to a 32-bit integer. + static ASMJIT_INLINE int32_t floatAsInt(float f) noexcept { FloatBits m; m.f = f; return m.i; } + //! Bit-cast 32-bit integer to `float`. + static ASMJIT_INLINE float intAsFloat(int32_t i) noexcept { FloatBits m; m.i = i; return m.f; } + + //! Bit-cast `double` to a 64-bit integer. + static ASMJIT_INLINE int64_t doubleAsInt(double d) noexcept { DoubleBits m; m.d = d; return m.i; } + //! Bit-cast 64-bit integer to `double`. + static ASMJIT_INLINE double intAsDouble(int64_t i) noexcept { DoubleBits m; m.i = i; return m.d; } + + // -------------------------------------------------------------------------- + // [Pack / Unpack] + // -------------------------------------------------------------------------- + + //! Pack four 8-bit integer into a 32-bit integer as it is an array of `{b0,b1,b2,b3}`. + static ASMJIT_INLINE uint32_t pack32_4x8(uint32_t b0, uint32_t b1, uint32_t b2, uint32_t b3) noexcept { + return ASMJIT_PACK32_4x8(b0, b1, b2, b3); + } + + //! Pack two 32-bit integer into a 64-bit integer as it is an array of `{u0,u1}`. + static ASMJIT_INLINE uint64_t pack64_2x32(uint32_t u0, uint32_t u1) noexcept { + return ASMJIT_ARCH_LE ? (static_cast(u1) << 32) + u0 + : (static_cast(u0) << 32) + u1; + } + + // -------------------------------------------------------------------------- + // [Position of byte (in bit-shift)] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE uint32_t byteShiftOfDWordStruct(uint32_t index) noexcept { + if (ASMJIT_ARCH_LE) + return index * 8; + else + return (sizeof(uint32_t) - 1 - index) * 8; + } + + // -------------------------------------------------------------------------- + // [Min/Max] + // -------------------------------------------------------------------------- + + // Some environments declare `min()` and `max()` as preprocessor macros so it + // was decided to use different names to prevent such collision. + + //! Get minimum value of `a` and `b`. + template + static ASMJIT_INLINE T iMin(const T& a, const T& b) noexcept { return a < b ? a : b; } + + //! Get maximum value of `a` and `b`. + template + static ASMJIT_INLINE T iMax(const T& a, const T& b) noexcept { return a > b ? a : b; } + + // -------------------------------------------------------------------------- + // [Lower/Upper] + // -------------------------------------------------------------------------- + + template + static ASMJIT_INLINE T toLower(T c) noexcept { return c ^ (static_cast(c >= T('A') && c <= T('Z')) << 5); } + template + static ASMJIT_INLINE T toUpper(T c) noexcept { return c ^ (static_cast(c >= T('a') && c <= T('z')) << 5); } + + // -------------------------------------------------------------------------- + // [Hash] + // -------------------------------------------------------------------------- + + // \internal + static ASMJIT_INLINE uint32_t hashRound(uint32_t hash, uint32_t c) noexcept { return hash * 65599 + c; } + + // Get a hash of the given string `str` of `len` length. Length must be valid + // as this function doesn't check for a null terminator and allows it in the + // middle of the string. + static ASMJIT_INLINE uint32_t hashString(const char* str, size_t len) noexcept { + uint32_t hVal = 0; + for (uint32_t i = 0; i < len; i++) + hVal = hashRound(hVal, str[i]); + return hVal; + } + + // -------------------------------------------------------------------------- + // [Swap] + // -------------------------------------------------------------------------- + + template + static ASMJIT_INLINE void swap(T& a, T& b) noexcept { + T tmp = a; + a = b; + b = tmp; + } + + // -------------------------------------------------------------------------- + // [InInterval] + // -------------------------------------------------------------------------- + + //! Get whether `x` is greater than or equal to `a` and lesses than or equal to `b`. + template + static ASMJIT_INLINE bool inInterval(T x, T a, T b) noexcept { + return x >= a && x <= b; + } + + // -------------------------------------------------------------------------- + // [AsInt] + // -------------------------------------------------------------------------- + + //! Map an integer `x` of type `T` to `int` or `int64_t` depending on the + //! type. Used internally by AsmJit to dispatch arguments that can be of + //! arbitrary integer type into a function argument that is either `int` or + //! `int64_t`. + template + static ASMJIT_INLINE typename IntTraits::IntType asInt(T x) noexcept { + return static_cast::IntType>(x); + } + + // -------------------------------------------------------------------------- + // [IsInt / IsUInt] + // -------------------------------------------------------------------------- + + //! Get whether the given integer `x` can be casted to an 8-bit signed integer. + template + static ASMJIT_INLINE bool isInt8(T x) noexcept { + typedef typename IntTraits::SignedType SignedType; + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return sizeof(T) <= 1 || inInterval(SignedType(x), -128, 127); + else + return UnsignedType(x) <= UnsignedType(127U); + } + + //! Get whether the given integer `x` can be casted to a 16-bit signed integer. + template + static ASMJIT_INLINE bool isInt16(T x) noexcept { + typedef typename IntTraits::SignedType SignedType; + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return sizeof(T) <= 2 || inInterval(SignedType(x), -32768, 32767); + else + return sizeof(T) <= 1 || UnsignedType(x) <= UnsignedType(32767U); + } + + //! Get whether the given integer `x` can be casted to a 32-bit signed integer. + template + static ASMJIT_INLINE bool isInt32(T x) noexcept { + typedef typename IntTraits::SignedType SignedType; + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return sizeof(T) <= 4 || inInterval(SignedType(x), -2147483647 - 1, 2147483647); + else + return sizeof(T) <= 2 || UnsignedType(x) <= UnsignedType(2147483647U); + } + + //! Get whether the given integer `x` can be casted to an 8-bit unsigned integer. + template + static ASMJIT_INLINE bool isUInt8(T x) noexcept { + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return x >= T(0) && (sizeof(T) <= 1 ? true : x <= T(255)); + else + return sizeof(T) <= 1 || UnsignedType(x) <= UnsignedType(255U); + } + + //! Get whether the given integer `x` can be casted to a 12-bit unsigned integer (ARM specific). + template + static ASMJIT_INLINE bool isUInt12(T x) noexcept { + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return x >= T(0) && (sizeof(T) <= 1 ? true : x <= T(4095)); + else + return sizeof(T) <= 1 || UnsignedType(x) <= UnsignedType(4095U); + } + + //! Get whether the given integer `x` can be casted to a 16-bit unsigned integer. + template + static ASMJIT_INLINE bool isUInt16(T x) noexcept { + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return x >= T(0) && (sizeof(T) <= 2 ? true : x <= T(65535)); + else + return sizeof(T) <= 2 || UnsignedType(x) <= UnsignedType(65535U); + } + + //! Get whether the given integer `x` can be casted to a 32-bit unsigned integer. + template + static ASMJIT_INLINE bool isUInt32(T x) noexcept { + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return x >= T(0) && (sizeof(T) <= 4 ? true : x <= T(4294967295U)); + else + return sizeof(T) <= 4 || UnsignedType(x) <= UnsignedType(4294967295U); + } + + // -------------------------------------------------------------------------- + // [IsPowerOf2] + // -------------------------------------------------------------------------- + + //! Get whether the `n` value is a power of two (only one bit is set). + template + static ASMJIT_INLINE bool isPowerOf2(T n) noexcept { + return n != 0 && (n & (n - 1)) == 0; + } + + // -------------------------------------------------------------------------- + // [Mask] + // -------------------------------------------------------------------------- + + //! Generate a bit-mask that has `x` bit set. + static ASMJIT_INLINE uint32_t mask(uint32_t x) noexcept { + ASMJIT_ASSERT(x < 32); + return static_cast(1) << x; + } + + //! Generate a bit-mask that has `x0` and `x1` bits set. + static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1) noexcept { + return mask(x0) | mask(x1); + } + + //! Generate a bit-mask that has `x0`, `x1` and `x2` bits set. + static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2) noexcept { + return mask(x0, x1) | mask(x2); + } + + //! Generate a bit-mask that has `x0`, `x1`, `x2` and `x3` bits set. + static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) noexcept { + return mask(x0, x1) | mask(x2, x3); + } + + //! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3` and `x4` bits set. + static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4) noexcept { + return mask(x0, x1) | mask(x2, x3) | mask(x4); + } + + //! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4` and `x5` bits set. + static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5) noexcept { + return mask(x0, x1) | mask(x2, x3) | mask(x4, x5); + } + + //! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4`, `x5` and `x6` bits set. + static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6) noexcept { + return mask(x0, x1) | mask(x2, x3) | mask(x4, x5) | mask(x6); + } + + //! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4`, `x5`, `x6` and `x7` bits set. + static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7) noexcept { + return mask(x0, x1) | mask(x2, x3) | mask(x4, x5) | mask(x6, x7); + } + + //! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4`, `x5`, `x6`, `x7` and `x8` bits set. + static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7, uint32_t x8) noexcept { + return mask(x0, x1) | mask(x2, x3) | mask(x4, x5) | mask(x6, x7) | mask(x8); + } + + //! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4`, `x5`, `x6`, `x7`, `x8` and `x9` bits set. + static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7, uint32_t x8, uint32_t x9) noexcept { + return mask(x0, x1) | mask(x2, x3) | mask(x4, x5) | mask(x6, x7) | mask(x8, x9); + } + + // -------------------------------------------------------------------------- + // [Bits] + // -------------------------------------------------------------------------- + + //! Generate a bit-mask that has `x` most significant bits set. + static ASMJIT_INLINE uint32_t bits(uint32_t x) noexcept { + // Shifting more bits than the type has results in undefined behavior. In + // such case asmjit trashes the result by ORing with `overflow` mask, which + // discards the undefined value returned by the shift. + uint32_t overflow = static_cast( + -static_cast(x >= sizeof(uint32_t) * 8)); + + return ((static_cast(1) << x) - 1U) | overflow; + } + + // -------------------------------------------------------------------------- + // [HasBit] + // -------------------------------------------------------------------------- + + //! Get whether `x` has bit `n` set. + template + static ASMJIT_INLINE bool hasBit(T x, Index n) noexcept { + return (x & (static_cast(1) << n)) != 0; + } + + // -------------------------------------------------------------------------- + // [BitCount] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE uint32_t bitCountSlow(uint32_t x) noexcept { + // From: http://graphics.stanford.edu/~seander/bithacks.html + x = x - ((x >> 1) & 0x55555555U); + x = (x & 0x33333333U) + ((x >> 2) & 0x33333333U); + return (((x + (x >> 4)) & 0x0F0F0F0FU) * 0x01010101U) >> 24; + } + + //! Get count of bits in `x`. + static ASMJIT_INLINE uint32_t bitCount(uint32_t x) noexcept { +#if ASMJIT_CC_GCC || ASMJIT_CC_CLANG + return __builtin_popcount(x); +#else + return bitCountSlow(x); +#endif + } + + // -------------------------------------------------------------------------- + // [FindFirstBit] + // -------------------------------------------------------------------------- + + //! \internal + static ASMJIT_INLINE uint32_t findFirstBitSlow(uint32_t mask) noexcept { + // This is a reference (slow) implementation of `findFirstBit()`, used when + // we don't have a C++ compiler support. The implementation speed has been + // improved to check for 2 bits per iteration. + uint32_t i = 1; + + while (mask != 0) { + uint32_t two = mask & 0x3; + if (two != 0x0) + return i - (two & 0x1); + + i += 2; + mask >>= 2; + } + + return 0xFFFFFFFFU; + } + + //! Find a first bit in `mask`. + static ASMJIT_INLINE uint32_t findFirstBit(uint32_t mask) noexcept { +#if ASMJIT_CC_MSC_GE(14, 0, 0) && (ASMJIT_ARCH_X86 || ASMJIT_ARCH_ARM32 || \ + ASMJIT_ARCH_X64 || ASMJIT_ARCH_ARM64) + DWORD i; + if (_BitScanForward(&i, mask)) + return static_cast(i); + else + return 0xFFFFFFFFU; +#elif ASMJIT_CC_GCC_GE(3, 4, 6) || ASMJIT_CC_CLANG + if (mask) + return __builtin_ctz(mask); + else + return 0xFFFFFFFFU; +#else + return findFirstBitSlow(mask); +#endif + } + + // -------------------------------------------------------------------------- + // [Misc] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE uint32_t keepNOnesFromRight(uint32_t mask, uint32_t nBits) noexcept { + uint32_t m = 0x1; + + do { + nBits -= (mask & m) != 0; + m <<= 1; + if (nBits == 0) { + m -= 1; + mask &= m; + break; + } + } while (m); + + return mask; + } + + static ASMJIT_INLINE uint32_t indexNOnesFromRight(uint8_t* dst, uint32_t mask, uint32_t nBits) noexcept { + uint32_t totalBits = nBits; + uint8_t i = 0; + uint32_t m = 0x1; + + do { + if (mask & m) { + *dst++ = i; + if (--nBits == 0) + break; + } + + m <<= 1; + i++; + } while (m); + + return totalBits - nBits; + } + + // -------------------------------------------------------------------------- + // [Alignment] + // -------------------------------------------------------------------------- + + template + static ASMJIT_INLINE bool isAligned(X base, Y alignment) noexcept { + typedef typename IntTraitsPrivate::UnsignedType U; + return ((U)base % (U)alignment) == 0; + } + + template + static ASMJIT_INLINE X alignTo(X x, Y alignment) noexcept { + typedef typename IntTraitsPrivate::UnsignedType U; + return (X)( ((U)x + (U)(alignment - 1)) & ~(static_cast(alignment) - 1) ); + } + + //! Get delta required to align `base` to `alignment`. + template + static ASMJIT_INLINE X alignDiff(X base, Y alignment) noexcept { + return alignTo(base, alignment) - base; + } + + template + static ASMJIT_INLINE T alignToPowerOf2(T base) noexcept { + // Implementation is from "Hacker's Delight" by Henry S. Warren, Jr. + base -= 1; + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4293) +#endif // _MSC_VER + + base = base | (base >> 1); + base = base | (base >> 2); + base = base | (base >> 4); + + // 8/16/32 constants are multiplied by the condition to prevent a compiler + // complaining about the 'shift count >= type width' (GCC). + if (sizeof(T) >= 2) base = base | (base >> ( 8 * (sizeof(T) >= 2))); // Base >> 8. + if (sizeof(T) >= 4) base = base | (base >> (16 * (sizeof(T) >= 4))); // Base >> 16. + if (sizeof(T) >= 8) base = base | (base >> (32 * (sizeof(T) >= 8))); // Base >> 32. + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif // _MSC_VER + + return base + 1; + } + + // -------------------------------------------------------------------------- + // [String] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE size_t strLen(const char* s, size_t maxlen) noexcept { + size_t i; + for (i = 0; i < maxlen; i++) + if (!s[i]) + break; + return i; + } + + // -------------------------------------------------------------------------- + // [BSwap] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE uint32_t byteswap32(uint32_t x) noexcept { +#if ASMJIT_CC_MSC + return static_cast(_byteswap_ulong(x)); +#elif ASMJIT_CC_GCC_GE(4, 3, 0) || ASMJIT_CC_CLANG_GE(2, 6, 0) + return __builtin_bswap32(x); +#else + uint32_t y = x & 0x00FFFF00U; + x = (x << 24) + (x >> 24); + y = (y << 8) + (y >> 8); + return x + (y & 0x00FFFF00U); +#endif + } + + // -------------------------------------------------------------------------- + // [ReadMem] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE uint32_t readU8(const void* p) noexcept { + return static_cast(static_cast(p)[0]); + } + + static ASMJIT_INLINE int32_t readI8(const void* p) noexcept { + return static_cast(static_cast(p)[0]); + } + + template + static ASMJIT_INLINE uint32_t readU16xLE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_LE && (ASMJIT_ARCH_UNALIGNED_16 || Alignment >= 2)) { + return static_cast(static_cast(p)[0]); + } + else { + uint32_t x = static_cast(static_cast(p)[0]); + uint32_t y = static_cast(static_cast(p)[1]); + return x + (y << 8); + } + } + + template + static ASMJIT_INLINE uint32_t readU16xBE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_BE && (ASMJIT_ARCH_UNALIGNED_16 || Alignment >= 2)) { + return static_cast(static_cast(p)[0]); + } + else { + uint32_t x = static_cast(static_cast(p)[0]); + uint32_t y = static_cast(static_cast(p)[1]); + return (x << 8) + y; + } + } + + template + static ASMJIT_INLINE uint32_t readU16x(const void* p) noexcept { + return ASMJIT_ARCH_LE ? readU16xLE(p) : readU16xBE(p); + } + + template + static ASMJIT_INLINE int32_t readI16xLE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_LE && (ASMJIT_ARCH_UNALIGNED_16 || Alignment >= 2)) { + return static_cast(static_cast(p)[0]); + } + else { + int32_t x = static_cast(static_cast(p)[0]); + int32_t y = static_cast(static_cast(p)[1]); + return x + (y << 8); + } + } + + template + static ASMJIT_INLINE int32_t readI16xBE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_BE && (ASMJIT_ARCH_UNALIGNED_16 || Alignment >= 2)) { + return static_cast(static_cast(p)[0]); + } + else { + int32_t x = static_cast(static_cast(p)[0]); + int32_t y = static_cast(static_cast(p)[1]); + return (x << 8) + y; + } + } + + template + static ASMJIT_INLINE int32_t readI16x(const void* p) noexcept { + return ASMJIT_ARCH_LE ? readI16xLE(p) : readI16xBE(p); + } + + static ASMJIT_INLINE uint32_t readU16aLE(const void* p) noexcept { return readU16xLE<2>(p); } + static ASMJIT_INLINE uint32_t readU16uLE(const void* p) noexcept { return readU16xLE<0>(p); } + + static ASMJIT_INLINE uint32_t readU16aBE(const void* p) noexcept { return readU16xBE<2>(p); } + static ASMJIT_INLINE uint32_t readU16uBE(const void* p) noexcept { return readU16xBE<0>(p); } + + static ASMJIT_INLINE uint32_t readU16a(const void* p) noexcept { return readU16x<2>(p); } + static ASMJIT_INLINE uint32_t readU16u(const void* p) noexcept { return readU16x<0>(p); } + + static ASMJIT_INLINE int32_t readI16aLE(const void* p) noexcept { return readI16xLE<2>(p); } + static ASMJIT_INLINE int32_t readI16uLE(const void* p) noexcept { return readI16xLE<0>(p); } + + static ASMJIT_INLINE int32_t readI16aBE(const void* p) noexcept { return readI16xBE<2>(p); } + static ASMJIT_INLINE int32_t readI16uBE(const void* p) noexcept { return readI16xBE<0>(p); } + + static ASMJIT_INLINE int32_t readI16a(const void* p) noexcept { return readI16x<2>(p); } + static ASMJIT_INLINE int32_t readI16u(const void* p) noexcept { return readI16x<0>(p); } + + template + static ASMJIT_INLINE uint32_t readU32xLE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_UNALIGNED_32 || Alignment >= 4) { + uint32_t x = static_cast(p)[0]; + return ASMJIT_ARCH_LE ? x : byteswap32(x); + } + else { + uint32_t x = readU16xLE(static_cast(p) + 0); + uint32_t y = readU16xLE(static_cast(p) + 2); + return x + (y << 16); + } + } + + template + static ASMJIT_INLINE uint32_t readU32xBE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_UNALIGNED_32 || Alignment >= 4) { + uint32_t x = static_cast(p)[0]; + return ASMJIT_ARCH_BE ? x : byteswap32(x); + } + else { + uint32_t x = readU16xBE(static_cast(p) + 0); + uint32_t y = readU16xBE(static_cast(p) + 2); + return (x << 16) + y; + } + } + + template + static ASMJIT_INLINE uint32_t readU32x(const void* p) noexcept { + return ASMJIT_ARCH_LE ? readU32xLE(p) : readU32xBE(p); + } + + template + static ASMJIT_INLINE int32_t readI32xLE(const void* p) noexcept { + return static_cast(readU32xLE(p)); + } + + template + static ASMJIT_INLINE int32_t readI32xBE(const void* p) noexcept { + return static_cast(readU32xBE(p)); + } + + template + static ASMJIT_INLINE int32_t readI32x(const void* p) noexcept { + return ASMJIT_ARCH_LE ? readI32xLE(p) : readI32xBE(p); + } + + static ASMJIT_INLINE uint32_t readU32a(const void* p) noexcept { return readU32x<4>(p); } + static ASMJIT_INLINE uint32_t readU32u(const void* p) noexcept { return readU32x<0>(p); } + + static ASMJIT_INLINE uint32_t readU32aLE(const void* p) noexcept { return readU32xLE<4>(p); } + static ASMJIT_INLINE uint32_t readU32uLE(const void* p) noexcept { return readU32xLE<0>(p); } + + static ASMJIT_INLINE uint32_t readU32aBE(const void* p) noexcept { return readU32xBE<4>(p); } + static ASMJIT_INLINE uint32_t readU32uBE(const void* p) noexcept { return readU32xBE<0>(p); } + + static ASMJIT_INLINE int32_t readI32a(const void* p) noexcept { return readI32x<4>(p); } + static ASMJIT_INLINE int32_t readI32u(const void* p) noexcept { return readI32x<0>(p); } + + static ASMJIT_INLINE int32_t readI32aLE(const void* p) noexcept { return readI32xLE<4>(p); } + static ASMJIT_INLINE int32_t readI32uLE(const void* p) noexcept { return readI32xLE<0>(p); } + + static ASMJIT_INLINE int32_t readI32aBE(const void* p) noexcept { return readI32xBE<4>(p); } + static ASMJIT_INLINE int32_t readI32uBE(const void* p) noexcept { return readI32xBE<0>(p); } + + template + static ASMJIT_INLINE uint64_t readU64xLE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_LE && (ASMJIT_ARCH_UNALIGNED_64 || Alignment >= 8)) { + return static_cast(p)[0]; + } + else { + uint32_t x = readU32xLE(static_cast(p) + 0); + uint32_t y = readU32xLE(static_cast(p) + 4); + return static_cast(x) + (static_cast(y) << 32); + } + } + + template + static ASMJIT_INLINE uint64_t readU64xBE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_BE && (ASMJIT_ARCH_UNALIGNED_64 || Alignment >= 8)) { + return static_cast(p)[0]; + } + else { + uint32_t x = readU32xLE(static_cast(p) + 0); + uint32_t y = readU32xLE(static_cast(p) + 4); + return (static_cast(x) << 32) + static_cast(y); + } + } + + template + static ASMJIT_INLINE uint64_t readU64x(const void* p) noexcept { + return ASMJIT_ARCH_LE ? readU64xLE(p) : readU64xBE(p); + } + + template + static ASMJIT_INLINE int64_t readI64xLE(const void* p) noexcept { + return static_cast(readU64xLE(p)); + } + + template + static ASMJIT_INLINE int64_t readI64xBE(const void* p) noexcept { + return static_cast(readU64xBE(p)); + } + + template + static ASMJIT_INLINE int64_t readI64x(const void* p) noexcept { + return ASMJIT_ARCH_LE ? readI64xLE(p) : readI64xBE(p); + } + + static ASMJIT_INLINE uint64_t readU64a(const void* p) noexcept { return readU64x<8>(p); } + static ASMJIT_INLINE uint64_t readU64u(const void* p) noexcept { return readU64x<0>(p); } + + static ASMJIT_INLINE uint64_t readU64aLE(const void* p) noexcept { return readU64xLE<8>(p); } + static ASMJIT_INLINE uint64_t readU64uLE(const void* p) noexcept { return readU64xLE<0>(p); } + + static ASMJIT_INLINE uint64_t readU64aBE(const void* p) noexcept { return readU64xBE<8>(p); } + static ASMJIT_INLINE uint64_t readU64uBE(const void* p) noexcept { return readU64xBE<0>(p); } + + static ASMJIT_INLINE int64_t readI64a(const void* p) noexcept { return readI64x<8>(p); } + static ASMJIT_INLINE int64_t readI64u(const void* p) noexcept { return readI64x<0>(p); } + + static ASMJIT_INLINE int64_t readI64aLE(const void* p) noexcept { return readI64xLE<8>(p); } + static ASMJIT_INLINE int64_t readI64uLE(const void* p) noexcept { return readI64xLE<0>(p); } + + static ASMJIT_INLINE int64_t readI64aBE(const void* p) noexcept { return readI64xBE<8>(p); } + static ASMJIT_INLINE int64_t readI64uBE(const void* p) noexcept { return readI64xBE<0>(p); } + + // -------------------------------------------------------------------------- + // [WriteMem] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE void writeU8(void* p, uint32_t x) noexcept { + static_cast(p)[0] = static_cast(x & 0xFFU); + } + + static ASMJIT_INLINE void writeI8(void* p, int32_t x) noexcept { + static_cast(p)[0] = static_cast(x & 0xFF); + } + + template + static ASMJIT_INLINE void writeU16xLE(void* p, uint32_t x) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_LE && (ASMJIT_ARCH_UNALIGNED_16 || Alignment >= 2)) { + static_cast(p)[0] = static_cast(x & 0xFFFFU); + } + else { + static_cast(p)[0] = static_cast((x ) & 0xFFU); + static_cast(p)[1] = static_cast((x >> 8) & 0xFFU); + } + } + + template + static ASMJIT_INLINE void writeU16xBE(void* p, uint32_t x) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_BE && (ASMJIT_ARCH_UNALIGNED_16 || Alignment >= 2)) { + static_cast(p)[0] = static_cast(x & 0xFFFFU); + } + else { + static_cast(p)[0] = static_cast((x >> 8) & 0xFFU); + static_cast(p)[1] = static_cast((x ) & 0xFFU); + } + } + + template + static ASMJIT_INLINE void writeU16x(void* p, uint32_t x) noexcept { + if (ASMJIT_ARCH_LE) + writeU16xLE(p, x); + else + writeU16xBE(p, x); + } + + template + static ASMJIT_INLINE void writeI16xLE(void* p, int32_t x) noexcept { + writeU16xLE(p, static_cast(x)); + } + + template + static ASMJIT_INLINE void writeI16xBE(void* p, int32_t x) noexcept { + writeU16xBE(p, static_cast(x)); + } + + template + static ASMJIT_INLINE void writeI16x(void* p, int32_t x) noexcept { + writeU16x(p, static_cast(x)); + } + + static ASMJIT_INLINE void writeU16aLE(void* p, uint32_t x) noexcept { writeU16xLE<2>(p, x); } + static ASMJIT_INLINE void writeU16uLE(void* p, uint32_t x) noexcept { writeU16xLE<0>(p, x); } + + static ASMJIT_INLINE void writeU16aBE(void* p, uint32_t x) noexcept { writeU16xBE<2>(p, x); } + static ASMJIT_INLINE void writeU16uBE(void* p, uint32_t x) noexcept { writeU16xBE<0>(p, x); } + + static ASMJIT_INLINE void writeU16a(void* p, uint32_t x) noexcept { writeU16x<2>(p, x); } + static ASMJIT_INLINE void writeU16u(void* p, uint32_t x) noexcept { writeU16x<0>(p, x); } + + static ASMJIT_INLINE void writeI16aLE(void* p, int32_t x) noexcept { writeI16xLE<2>(p, x); } + static ASMJIT_INLINE void writeI16uLE(void* p, int32_t x) noexcept { writeI16xLE<0>(p, x); } + + static ASMJIT_INLINE void writeI16aBE(void* p, int32_t x) noexcept { writeI16xBE<2>(p, x); } + static ASMJIT_INLINE void writeI16uBE(void* p, int32_t x) noexcept { writeI16xBE<0>(p, x); } + + static ASMJIT_INLINE void writeI16a(void* p, int32_t x) noexcept { writeI16x<2>(p, x); } + static ASMJIT_INLINE void writeI16u(void* p, int32_t x) noexcept { writeI16x<0>(p, x); } + + template + static ASMJIT_INLINE void writeU32xLE(void* p, uint32_t x) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_UNALIGNED_32 || Alignment >= 4) { + static_cast(p)[0] = ASMJIT_ARCH_LE ? x : byteswap32(x); + } + else { + writeU16xLE(static_cast(p) + 0, x >> 16); + writeU16xLE(static_cast(p) + 2, x); + } + } + + template + static ASMJIT_INLINE void writeU32xBE(void* p, uint32_t x) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_UNALIGNED_32 || Alignment >= 4) { + static_cast(p)[0] = ASMJIT_ARCH_BE ? x : byteswap32(x); + } + else { + writeU16xBE(static_cast(p) + 0, x); + writeU16xBE(static_cast(p) + 2, x >> 16); + } + } + + template + static ASMJIT_INLINE void writeU32x(void* p, uint32_t x) noexcept { + if (ASMJIT_ARCH_LE) + writeU32xLE(p, x); + else + writeU32xBE(p, x); + } + + template + static ASMJIT_INLINE void writeI32xLE(void* p, int32_t x) noexcept { + writeU32xLE(p, static_cast(x)); + } + + template + static ASMJIT_INLINE void writeI32xBE(void* p, int32_t x) noexcept { + writeU32xBE(p, static_cast(x)); + } + + template + static ASMJIT_INLINE void writeI32x(void* p, int32_t x) noexcept { + writeU32x(p, static_cast(x)); + } + + static ASMJIT_INLINE void writeU32aLE(void* p, uint32_t x) noexcept { writeU32xLE<4>(p, x); } + static ASMJIT_INLINE void writeU32uLE(void* p, uint32_t x) noexcept { writeU32xLE<0>(p, x); } + + static ASMJIT_INLINE void writeU32aBE(void* p, uint32_t x) noexcept { writeU32xBE<4>(p, x); } + static ASMJIT_INLINE void writeU32uBE(void* p, uint32_t x) noexcept { writeU32xBE<0>(p, x); } + + static ASMJIT_INLINE void writeU32a(void* p, uint32_t x) noexcept { writeU32x<4>(p, x); } + static ASMJIT_INLINE void writeU32u(void* p, uint32_t x) noexcept { writeU32x<0>(p, x); } + + static ASMJIT_INLINE void writeI32aLE(void* p, int32_t x) noexcept { writeI32xLE<4>(p, x); } + static ASMJIT_INLINE void writeI32uLE(void* p, int32_t x) noexcept { writeI32xLE<0>(p, x); } + + static ASMJIT_INLINE void writeI32aBE(void* p, int32_t x) noexcept { writeI32xBE<4>(p, x); } + static ASMJIT_INLINE void writeI32uBE(void* p, int32_t x) noexcept { writeI32xBE<0>(p, x); } + + static ASMJIT_INLINE void writeI32a(void* p, int32_t x) noexcept { writeI32x<4>(p, x); } + static ASMJIT_INLINE void writeI32u(void* p, int32_t x) noexcept { writeI32x<0>(p, x); } + + template + static ASMJIT_INLINE void writeU64xLE(void* p, uint64_t x) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_LE && (ASMJIT_ARCH_UNALIGNED_64 || Alignment >= 8)) { + static_cast(p)[0] = x; + } + else { + writeU32xLE(static_cast(p) + 0, static_cast(x >> 32)); + writeU32xLE(static_cast(p) + 4, static_cast(x & 0xFFFFFFFFU)); + } + } + + template + static ASMJIT_INLINE void writeU64xBE(void* p, uint64_t x) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_BE && (ASMJIT_ARCH_UNALIGNED_64 || Alignment >= 8)) { + static_cast(p)[0] = x; + } + else { + writeU32xBE(static_cast(p) + 0, static_cast(x & 0xFFFFFFFFU)); + writeU32xBE(static_cast(p) + 4, static_cast(x >> 32)); + } + } + + template + static ASMJIT_INLINE void writeU64x(void* p, uint64_t x) noexcept { + if (ASMJIT_ARCH_LE) + writeU64xLE(p, x); + else + writeU64xBE(p, x); + } + + template + static ASMJIT_INLINE void writeI64xLE(void* p, int64_t x) noexcept { + writeU64xLE(p, static_cast(x)); + } + + template + static ASMJIT_INLINE void writeI64xBE(void* p, int64_t x) noexcept { + writeU64xBE(p, static_cast(x)); + } + + template + static ASMJIT_INLINE void writeI64x(void* p, int64_t x) noexcept { + writeU64x(p, static_cast(x)); + } + + static ASMJIT_INLINE void writeU64aLE(void* p, uint64_t x) noexcept { writeU64xLE<8>(p, x); } + static ASMJIT_INLINE void writeU64uLE(void* p, uint64_t x) noexcept { writeU64xLE<0>(p, x); } + + static ASMJIT_INLINE void writeU64aBE(void* p, uint64_t x) noexcept { writeU64xBE<8>(p, x); } + static ASMJIT_INLINE void writeU64uBE(void* p, uint64_t x) noexcept { writeU64xBE<0>(p, x); } + + static ASMJIT_INLINE void writeU64a(void* p, uint64_t x) noexcept { writeU64x<8>(p, x); } + static ASMJIT_INLINE void writeU64u(void* p, uint64_t x) noexcept { writeU64x<0>(p, x); } + + static ASMJIT_INLINE void writeI64aLE(void* p, int64_t x) noexcept { writeI64xLE<8>(p, x); } + static ASMJIT_INLINE void writeI64uLE(void* p, int64_t x) noexcept { writeI64xLE<0>(p, x); } + + static ASMJIT_INLINE void writeI64aBE(void* p, int64_t x) noexcept { writeI64xBE<8>(p, x); } + static ASMJIT_INLINE void writeI64uBE(void* p, int64_t x) noexcept { writeI64xBE<0>(p, x); } + + static ASMJIT_INLINE void writeI64a(void* p, int64_t x) noexcept { writeI64x<8>(p, x); } + static ASMJIT_INLINE void writeI64u(void* p, int64_t x) noexcept { writeI64x<0>(p, x); } +}; + +// ============================================================================ +// [asmjit::UInt64] +// ============================================================================ + +union UInt64 { + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64 fromUInt64(uint64_t val) noexcept { + UInt64 data; + data.setUInt64(val); + return data; + } + + ASMJIT_INLINE UInt64 fromUInt64(const UInt64& val) noexcept { + UInt64 data; + data.setUInt64(val); + return data; + } + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void reset() noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 = 0; + } + else { + u32[0] = 0; + u32[1] = 0; + } + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE uint64_t getUInt64() const noexcept { + return u64; + } + + ASMJIT_INLINE UInt64& setUInt64(uint64_t val) noexcept { + u64 = val; + return *this; + } + + ASMJIT_INLINE UInt64& setUInt64(const UInt64& val) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 = val.u64; + } + else { + u32[0] = val.u32[0]; + u32[1] = val.u32[1]; + } + return *this; + } + + ASMJIT_INLINE UInt64& setPacked_2x32(uint32_t u0, uint32_t u1) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 = Utils::pack64_2x32(u0, u1); + } + else { + u32[0] = u0; + u32[1] = u1; + } + return *this; + } + + // -------------------------------------------------------------------------- + // [Add] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64& add(uint64_t val) noexcept { + u64 += val; + return *this; + } + + ASMJIT_INLINE UInt64& add(const UInt64& val) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 += val.u64; + } + else { + u32[0] += val.u32[0]; + u32[1] += val.u32[1]; + } + return *this; + } + + // -------------------------------------------------------------------------- + // [Sub] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64& sub(uint64_t val) noexcept { + u64 -= val; + return *this; + } + + ASMJIT_INLINE UInt64& sub(const UInt64& val) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 -= val.u64; + } + else { + u32[0] -= val.u32[0]; + u32[1] -= val.u32[1]; + } + return *this; + } + + // -------------------------------------------------------------------------- + // [And] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64& and_(uint64_t val) noexcept { + u64 &= val; + return *this; + } + + ASMJIT_INLINE UInt64& and_(const UInt64& val) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 &= val.u64; + } + else { + u32[0] &= val.u32[0]; + u32[1] &= val.u32[1]; + } + return *this; + } + + // -------------------------------------------------------------------------- + // [AndNot] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64& andNot(uint64_t val) noexcept { + u64 &= ~val; + return *this; + } + + ASMJIT_INLINE UInt64& andNot(const UInt64& val) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 &= ~val.u64; + } + else { + u32[0] &= ~val.u32[0]; + u32[1] &= ~val.u32[1]; + } + return *this; + } + + // -------------------------------------------------------------------------- + // [Or] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64& or_(uint64_t val) noexcept { + u64 |= val; + return *this; + } + + ASMJIT_INLINE UInt64& or_(const UInt64& val) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 |= val.u64; + } + else { + u32[0] |= val.u32[0]; + u32[1] |= val.u32[1]; + } + return *this; + } + + // -------------------------------------------------------------------------- + // [Xor] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64& xor_(uint64_t val) noexcept { + u64 ^= val; + return *this; + } + + ASMJIT_INLINE UInt64& xor_(const UInt64& val) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 ^= val.u64; + } + else { + u32[0] ^= val.u32[0]; + u32[1] ^= val.u32[1]; + } + return *this; + } + + // -------------------------------------------------------------------------- + // [Eq] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isZero() const noexcept { + if (ASMJIT_ARCH_64BIT) + return u64 == 0; + else + return (u32[0] | u32[1]) == 0; + } + + ASMJIT_INLINE bool isNonZero() const noexcept { + if (ASMJIT_ARCH_64BIT) + return u64 != 0; + else + return (u32[0] | u32[1]) != 0; + } + + ASMJIT_INLINE bool eq(uint64_t val) const noexcept { + return u64 == val; + } + + ASMJIT_INLINE bool eq(const UInt64& val) const noexcept { + if (ASMJIT_ARCH_64BIT) + return u64 == val.u64; + else + return u32[0] == val.u32[0] && u32[1] == val.u32[1]; + } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64& operator+=(uint64_t val) noexcept { return add(val); } + ASMJIT_INLINE UInt64& operator+=(const UInt64& val) noexcept { return add(val); } + + ASMJIT_INLINE UInt64& operator-=(uint64_t val) noexcept { return sub(val); } + ASMJIT_INLINE UInt64& operator-=(const UInt64& val) noexcept { return sub(val); } + + ASMJIT_INLINE UInt64& operator&=(uint64_t val) noexcept { return and_(val); } + ASMJIT_INLINE UInt64& operator&=(const UInt64& val) noexcept { return and_(val); } + + ASMJIT_INLINE UInt64& operator|=(uint64_t val) noexcept { return or_(val); } + ASMJIT_INLINE UInt64& operator|=(const UInt64& val) noexcept { return or_(val); } + + ASMJIT_INLINE UInt64& operator^=(uint64_t val) noexcept { return xor_(val); } + ASMJIT_INLINE UInt64& operator^=(const UInt64& val) noexcept { return xor_(val); } + + ASMJIT_INLINE bool operator==(uint64_t val) const noexcept { return eq(val); } + ASMJIT_INLINE bool operator==(const UInt64& val) const noexcept { return eq(val); } + + ASMJIT_INLINE bool operator!=(uint64_t val) const noexcept { return !eq(val); } + ASMJIT_INLINE bool operator!=(const UInt64& val) const noexcept { return !eq(val); } + + ASMJIT_INLINE bool operator<(uint64_t val) const noexcept { return u64 < val; } + ASMJIT_INLINE bool operator<(const UInt64& val) const noexcept { return u64 < val.u64; } + + ASMJIT_INLINE bool operator<=(uint64_t val) const noexcept { return u64 <= val; } + ASMJIT_INLINE bool operator<=(const UInt64& val) const noexcept { return u64 <= val.u64; } + + ASMJIT_INLINE bool operator>(uint64_t val) const noexcept { return u64 > val; } + ASMJIT_INLINE bool operator>(const UInt64& val) const noexcept { return u64 > val.u64; } + + ASMJIT_INLINE bool operator>=(uint64_t val) const noexcept { return u64 >= val; } + ASMJIT_INLINE bool operator>=(const UInt64& val) const noexcept { return u64 >= val.u64; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + int8_t i8[8]; //!< 8-bit signed integer (8x). + uint8_t u8[8]; //!< 8-bit unsigned integer (8x). + + int16_t i16[4]; //!< 16-bit signed integer (4x). + uint16_t u16[4]; //!< 16-bit unsigned integer (4x). + + int32_t i32[2]; //!< 32-bit signed integer (2x). + uint32_t u32[2]; //!< 32-bit unsigned integer (2x). + + int64_t i64; //!< 64-bit signed integer. + uint64_t u64; //!< 64-bit unsigned integer. + + float f32[2]; //!< 32-bit floating point (2x). + double f64; //!< 64-bit floating point. + +#if ASMJIT_ARCH_LE + struct { float f32Lo, f32Hi; }; + struct { int32_t i32Lo, i32Hi; }; + struct { uint32_t u32Lo, u32Hi; }; +#else + struct { float f32Hi, f32Lo; }; + struct { int32_t i32Hi, i32Lo; }; + struct { uint32_t u32Hi, u32Lo; }; +#endif // ASMJIT_ARCH_LE +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_UTILS_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/vmem.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/vmem.cpp new file mode 100644 index 00000000..1f9eec8b --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/vmem.cpp @@ -0,0 +1,1077 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/osutils.h" +#include "../base/utils.h" +#include "../base/vmem.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +// This file contains implementation of virtual memory management for AsmJit +// library. There are several goals I decided to write implementation myself: +// +// - Granularity of allocated blocks is different than granularity for a typical +// C malloc. It is at least 64-bytes so CodeEmitter can guarantee the alignment +// up to 64 bytes, which is the size of a cache-line and it's also required by +// AVX-512 aligned loads and stores. Alignment requirements can grow in the future, +// but at the moment 64 bytes is safe (we may jump to 128 bytes if necessary or +// make it configurable). +// +// - Keep memory manager information outside of the allocated virtual memory +// pages, because these pages allow machine code execution and there should +// be not data required to keep track of these blocks. Another reason is that +// some environments (i.e. iOS) allow to generate and run JIT code, but this +// code has to be set to [Executable, but not Writable]. +// +// - Keep implementation simple and easy to follow. +// +// Implementation is based on bit arrays and binary trees. Bit arrays contain +// information related to allocated and unused blocks of memory. The size of +// a block is described by `MemNode::density`. Count of blocks is stored in +// `MemNode::blocks`. For example if density is 64 and count of blocks is 20, +// memory node contains 64*20 bytes of memory and the smallest possible allocation +// (and also alignment) is 64 bytes. So density is also related to memory +// alignment. Binary trees (RB) are used to enable fast lookup into all addresses +// allocated by memory manager instance. This is used mainly by `VMemPrivate::release()`. +// +// Bit array looks like this (empty = unused, X = used) - Size of block 64: +// +// ------------------------------------------------------------------------- +// | |X|X| | | | | |X|X|X|X|X|X| | | | | | | | | | | | |X| | | | |X|X|X| | | +// ------------------------------------------------------------------------- +// (Maximum continuous block) +// +// These bits show that there are 12 allocated blocks (X) of 64 bytes, so total +// size allocated is 768 bytes. Maximum count of continuous memory is 12 * 64. + +namespace asmjit { + +// ============================================================================ +// [asmjit::VMemMgr - BitOps] +// ============================================================================ + +#define M_DIV(x, y) ((x) / (y)) +#define M_MOD(x, y) ((x) % (y)) + +//! \internal +enum { kBitsPerEntity = (sizeof(size_t) * 8) }; + +//! \internal +//! +//! Set `len` bits in `buf` starting at `index` bit index. +static void _SetBits(size_t* buf, size_t index, size_t len) noexcept { + if (len == 0) + return; + + size_t i = index / kBitsPerEntity; // size_t[] + size_t j = index % kBitsPerEntity; // size_t[][] bit index + + // How many bytes process in the first group. + size_t c = kBitsPerEntity - j; + if (c > len) + c = len; + + // Offset. + buf += i; + + *buf++ |= ((~(size_t)0) >> (kBitsPerEntity - c)) << j; + len -= c; + + while (len >= kBitsPerEntity) { + *buf++ = ~(size_t)0; + len -= kBitsPerEntity; + } + + if (len) + *buf |= ((~(size_t)0) >> (kBitsPerEntity - len)); +} + +// ============================================================================ +// [asmjit::VMemMgr::TypeDefs] +// ============================================================================ + +typedef VMemMgr::RbNode RbNode; +typedef VMemMgr::MemNode MemNode; +typedef VMemMgr::PermanentNode PermanentNode; + +// ============================================================================ +// [asmjit::VMemMgr::RbNode] +// ============================================================================ + +//! \internal +//! +//! Base red-black tree node. +struct VMemMgr::RbNode { + // Implementation is based on article by Julienne Walker (Public Domain), + // including C code and original comments. Thanks for the excellent article. + + RbNode* node[2]; //!< Left[0] and right[1] nodes. + uint8_t* mem; //!< Virtual memory address. + uint32_t red; //!< Node color (red vs. black). +}; + +//! \internal +//! +//! Get if the node is red (nullptr or node with red flag). +static ASMJIT_INLINE bool rbIsRed(RbNode* node) noexcept { + return node && node->red; +} + +//! \internal +//! +//! Check whether the RB tree is valid. +static int rbAssert(RbNode* root) noexcept { + if (!root) return 1; + + RbNode* ln = root->node[0]; + RbNode* rn = root->node[1]; + + // Red violation. + ASMJIT_ASSERT( !(rbIsRed(root) && (rbIsRed(ln) || rbIsRed(rn))) ); + + int lh = rbAssert(ln); + int rh = rbAssert(rn); + + // Invalid btree. + ASMJIT_ASSERT(ln == nullptr || ln->mem < root->mem); + ASMJIT_ASSERT(rn == nullptr || rn->mem > root->mem); + + // Black violation. + ASMJIT_ASSERT(!(lh != 0 && rh != 0 && lh != rh)); + + // Only count black links. + if (lh != 0 && rh != 0) + return rbIsRed(root) ? lh : lh + 1; + else + return 0; +} + +//! \internal +//! +//! Single rotation. +static ASMJIT_INLINE RbNode* rbRotateSingle(RbNode* root, int dir) noexcept { + RbNode* save = root->node[!dir]; + + root->node[!dir] = save->node[dir]; + save->node[dir] = root; + + root->red = 1; + save->red = 0; + + return save; +} + +//! \internal +//! +//! Double rotation. +static ASMJIT_INLINE RbNode* rbRotateDouble(RbNode* root, int dir) noexcept { + root->node[!dir] = rbRotateSingle(root->node[!dir], !dir); + return rbRotateSingle(root, dir); +} + +// ============================================================================ +// [asmjit::VMemMgr::MemNode] +// ============================================================================ + +struct VMemMgr::MemNode : public RbNode { + ASMJIT_INLINE void init(MemNode* other) noexcept { + mem = other->mem; + + size = other->size; + used = other->used; + blocks = other->blocks; + density = other->density; + largestBlock = other->largestBlock; + + baUsed = other->baUsed; + baCont = other->baCont; + } + + // Get available space. + ASMJIT_INLINE size_t getAvailable() const noexcept { return size - used; } + + MemNode* prev; // Prev node in list. + MemNode* next; // Next node in list. + + size_t size; // How many bytes contain this node. + size_t used; // How many bytes are used in this node. + size_t blocks; // How many blocks are here. + size_t density; // Minimum count of allocated bytes in this node (also alignment). + size_t largestBlock; // Contains largest block that can be allocated. + + size_t* baUsed; // Contains bits about used blocks (0 = unused, 1 = used). + size_t* baCont; // Contains bits about continuous blocks (0 = stop , 1 = continue). +}; + +// ============================================================================ +// [asmjit::VMemMgr::PermanentNode] +// ============================================================================ + +//! \internal +//! +//! Permanent node. +struct VMemMgr::PermanentNode { + //! Get available space. + ASMJIT_INLINE size_t getAvailable() const noexcept { return size - used; } + + PermanentNode* prev; // Pointer to prev chunk or nullptr. + uint8_t* mem; // Base pointer (virtual memory address). + size_t size; // Count of bytes allocated. + size_t used; // Count of bytes used. +}; + +// ============================================================================ +// [asmjit::VMemMgr - Private] +// ============================================================================ + +//! \internal +//! +//! Helper to avoid `#ifdef`s in the code. +ASMJIT_INLINE uint8_t* vMemMgrAllocVMem(VMemMgr* self, size_t size, size_t* vSize) noexcept { + uint32_t flags = OSUtils::kVMWritable | OSUtils::kVMExecutable; +#if !ASMJIT_OS_WINDOWS + return static_cast(OSUtils::allocVirtualMemory(size, vSize, flags)); +#else + return static_cast(OSUtils::allocProcessMemory(self->_hProcess, size, vSize, flags)); +#endif +} + +//! \internal +//! +//! Helper to avoid `#ifdef`s in the code. +ASMJIT_INLINE Error vMemMgrReleaseVMem(VMemMgr* self, void* p, size_t vSize) noexcept { +#if !ASMJIT_OS_WINDOWS + return OSUtils::releaseVirtualMemory(p, vSize); +#else + return OSUtils::releaseProcessMemory(self->_hProcess, p, vSize); +#endif +} + +//! \internal +//! +//! Check whether the Red-Black tree is valid. +static bool vMemMgrCheckTree(VMemMgr* self) noexcept { + return rbAssert(self->_root) > 0; +} + +//! \internal +//! +//! Alloc virtual memory including a heap memory needed for `MemNode` data. +//! +//! Returns set-up `MemNode*` or nullptr if allocation failed. +static MemNode* vMemMgrCreateNode(VMemMgr* self, size_t size, size_t density) noexcept { + size_t vSize; + uint8_t* vmem = vMemMgrAllocVMem(self, size, &vSize); + if (!vmem) return nullptr; + + size_t blocks = (vSize / density); + size_t bsize = (((blocks + 7) >> 3) + sizeof(size_t) - 1) & ~(size_t)(sizeof(size_t) - 1); + + MemNode* node = static_cast(ASMJIT_ALLOC(sizeof(MemNode))); + uint8_t* data = static_cast(ASMJIT_ALLOC(bsize * 2)); + + // Out of memory. + if (!node || !data) { + vMemMgrReleaseVMem(self, vmem, vSize); + if (node) ASMJIT_FREE(node); + if (data) ASMJIT_FREE(data); + return nullptr; + } + + // Initialize RbNode data. + node->node[0] = nullptr; + node->node[1] = nullptr; + node->mem = vmem; + node->red = 1; + + // Initialize MemNode data. + node->prev = nullptr; + node->next = nullptr; + + node->size = vSize; + node->used = 0; + node->blocks = blocks; + node->density = density; + node->largestBlock = vSize; + + ::memset(data, 0, bsize * 2); + node->baUsed = reinterpret_cast(data); + node->baCont = reinterpret_cast(data + bsize); + + return node; +} + +static void vMemMgrInsertNode(VMemMgr* self, MemNode* node) noexcept { + if (!self->_root) { + // Empty tree case. + self->_root = node; + } + else { + // False tree root. + RbNode head = { { nullptr, nullptr }, 0, 0 }; + + // Grandparent & parent. + RbNode* g = nullptr; + RbNode* t = &head; + + // Iterator & parent. + RbNode* p = nullptr; + RbNode* q = t->node[1] = self->_root; + + int dir = 0; + int last = 0; // Not needed to initialize, but makes some tools happy. + + // Search down the tree. + for (;;) { + if (!q) { + // Insert new node at the bottom. + q = node; + p->node[dir] = node; + } + else if (rbIsRed(q->node[0]) && rbIsRed(q->node[1])) { + // Color flip. + q->red = 1; + q->node[0]->red = 0; + q->node[1]->red = 0; + } + + // Fix red violation. + if (rbIsRed(q) && rbIsRed(p)) { + int dir2 = t->node[1] == g; + t->node[dir2] = q == p->node[last] ? rbRotateSingle(g, !last) : rbRotateDouble(g, !last); + } + + // Stop if found. + if (q == node) + break; + + last = dir; + dir = q->mem < node->mem; + + // Update helpers. + if (g) t = g; + + g = p; + p = q; + q = q->node[dir]; + } + + // Update root. + self->_root = static_cast(head.node[1]); + } + + // Make root black. + self->_root->red = 0; + + // Link with others. + node->prev = self->_last; + + if (!self->_first) { + self->_first = node; + self->_last = node; + self->_optimal = node; + } + else { + node->prev = self->_last; + self->_last->next = node; + self->_last = node; + } +} + +//! \internal +//! +//! Remove node from Red-Black tree. +//! +//! Returns node that should be freed, but it doesn't have to be necessarily +//! the `node` passed. +static MemNode* vMemMgrRemoveNode(VMemMgr* self, MemNode* node) noexcept { + // False tree root. + RbNode head = { { nullptr, nullptr }, 0, 0 }; + + // Helpers. + RbNode* q = &head; + RbNode* p = nullptr; + RbNode* g = nullptr; + + // Found item. + RbNode* f = nullptr; + int dir = 1; + + // Set up. + q->node[1] = self->_root; + + // Search and push a red down. + while (q->node[dir]) { + int last = dir; + + // Update helpers. + g = p; + p = q; + q = q->node[dir]; + dir = q->mem < node->mem; + + // Save found node. + if (q == node) + f = q; + + // Push the red node down. + if (!rbIsRed(q) && !rbIsRed(q->node[dir])) { + if (rbIsRed(q->node[!dir])) { + p = p->node[last] = rbRotateSingle(q, dir); + } + else if (!rbIsRed(q->node[!dir])) { + RbNode* s = p->node[!last]; + + if (s) { + if (!rbIsRed(s->node[!last]) && !rbIsRed(s->node[last])) { + // Color flip. + p->red = 0; + s->red = 1; + q->red = 1; + } + else { + int dir2 = g->node[1] == p; + + if (rbIsRed(s->node[last])) + g->node[dir2] = rbRotateDouble(p, last); + else if (rbIsRed(s->node[!last])) + g->node[dir2] = rbRotateSingle(p, last); + + // Ensure correct coloring. + q->red = g->node[dir2]->red = 1; + g->node[dir2]->node[0]->red = 0; + g->node[dir2]->node[1]->red = 0; + } + } + } + } + } + + // Replace and remove. + ASMJIT_ASSERT(f != nullptr); + ASMJIT_ASSERT(f != &head); + ASMJIT_ASSERT(q != &head); + + if (f != q) { + ASMJIT_ASSERT(f != &head); + static_cast(f)->init(static_cast(q)); + } + + p->node[p->node[1] == q] = q->node[q->node[0] == nullptr]; + + // Update root and make it black. + self->_root = static_cast(head.node[1]); + if (self->_root) self->_root->red = 0; + + // Unlink. + MemNode* next = static_cast(q)->next; + MemNode* prev = static_cast(q)->prev; + + if (prev) + prev->next = next; + else + self->_first = next; + + if (next) + next->prev = prev; + else + self->_last = prev; + + if (self->_optimal == q) + self->_optimal = prev ? prev : next; + + return static_cast(q); +} + +static MemNode* vMemMgrFindNodeByPtr(VMemMgr* self, uint8_t* mem) noexcept { + MemNode* node = self->_root; + while (node) { + uint8_t* nodeMem = node->mem; + + // Go left. + if (mem < nodeMem) { + node = static_cast(node->node[0]); + continue; + } + + // Go right. + uint8_t* nodeEnd = nodeMem + node->size; + if (mem >= nodeEnd) { + node = static_cast(node->node[1]); + continue; + } + + // Match. + break; + } + return node; +} + +static void* vMemMgrAllocPermanent(VMemMgr* self, size_t vSize) noexcept { + static const size_t permanentAlignment = 32; + static const size_t permanentNodeSize = 32768; + + vSize = Utils::alignTo(vSize, permanentAlignment); + + AutoLock locked(self->_lock); + PermanentNode* node = self->_permanent; + + // Try to find space in allocated chunks. + while (node && vSize > node->getAvailable()) + node = node->prev; + + // Or allocate new node. + if (!node) { + size_t nodeSize = permanentNodeSize; + if (nodeSize < vSize) nodeSize = vSize; + + node = static_cast(ASMJIT_ALLOC(sizeof(PermanentNode))); + if (!node) return nullptr; + + node->mem = vMemMgrAllocVMem(self, nodeSize, &node->size); + if (!node->mem) { + ASMJIT_FREE(node); + return nullptr; + } + + node->used = 0; + node->prev = self->_permanent; + self->_permanent = node; + } + + // Finally, copy function code to our space we reserved for. + uint8_t* result = node->mem + node->used; + + // Update Statistics. + node->used += vSize; + self->_usedBytes += vSize; + + // Code can be null to only reserve space for code. + return static_cast(result); +} + +static void* vMemMgrAllocFreeable(VMemMgr* self, size_t vSize) noexcept { + // Current index. + size_t i; + + // How many we need to be freed. + size_t need; + size_t minVSize; + + // Align to 32 bytes by default. + vSize = Utils::alignTo(vSize, 32); + if (vSize == 0) + return nullptr; + + AutoLock locked(self->_lock); + MemNode* node = self->_optimal; + minVSize = self->_blockSize; + + // Try to find memory block in existing nodes. + while (node) { + // Skip this node? + if ((node->getAvailable() < vSize) || (node->largestBlock < vSize && node->largestBlock != 0)) { + MemNode* next = node->next; + + if (node->getAvailable() < minVSize && node == self->_optimal && next) + self->_optimal = next; + + node = next; + continue; + } + + size_t* up = node->baUsed; // Current ubits address. + size_t ubits; // Current ubits[0] value. + size_t bit; // Current bit mask. + size_t blocks = node->blocks; // Count of blocks in node. + size_t cont = 0; // How many bits are currently freed in find loop. + size_t maxCont = 0; // Largest continuous block (bits count). + size_t j; + + need = M_DIV((vSize + node->density - 1), node->density); + i = 0; + + // Try to find node that is large enough. + while (i < blocks) { + ubits = *up++; + + // Fast skip used blocks. + if (ubits == ~(size_t)0) { + if (cont > maxCont) + maxCont = cont; + cont = 0; + + i += kBitsPerEntity; + continue; + } + + size_t max = kBitsPerEntity; + if (i + max > blocks) + max = blocks - i; + + for (j = 0, bit = 1; j < max; bit <<= 1) { + j++; + if ((ubits & bit) == 0) { + if (++cont == need) { + i += j; + i -= cont; + goto L_Found; + } + + continue; + } + + if (cont > maxCont) maxCont = cont; + cont = 0; + } + + i += kBitsPerEntity; + } + + // Because we traversed the entire node, we can set largest node size that + // will be used to cache next traversing. + node->largestBlock = maxCont * node->density; + + node = node->next; + } + + // If we are here, we failed to find existing memory block and we must + // allocate a new one. + { + size_t blockSize = self->_blockSize; + if (blockSize < vSize) blockSize = vSize; + + node = vMemMgrCreateNode(self, blockSize, self->_blockDensity); + if (!node) return nullptr; + + // Update binary tree. + vMemMgrInsertNode(self, node); + ASMJIT_ASSERT(vMemMgrCheckTree(self)); + + // Alloc first node at start. + i = 0; + need = (vSize + node->density - 1) / node->density; + + // Update statistics. + self->_allocatedBytes += node->size; + } + +L_Found: + // Update bits. + _SetBits(node->baUsed, i, need); + _SetBits(node->baCont, i, need - 1); + + // Update statistics. + { + size_t u = need * node->density; + node->used += u; + node->largestBlock = 0; + self->_usedBytes += u; + } + + // And return pointer to allocated memory. + uint8_t* result = node->mem + i * node->density; + ASMJIT_ASSERT(result >= node->mem && result <= node->mem + node->size - vSize); + return result; +} + +//! \internal +//! +//! Reset the whole `VMemMgr` instance, freeing all heap memory allocated an +//! virtual memory allocated unless `keepVirtualMemory` is true (and this is +//! only used when writing data to a remote process). +static void vMemMgrReset(VMemMgr* self, bool keepVirtualMemory) noexcept { + MemNode* node = self->_first; + + while (node) { + MemNode* next = node->next; + + if (!keepVirtualMemory) + vMemMgrReleaseVMem(self, node->mem, node->size); + + ASMJIT_FREE(node->baUsed); + ASMJIT_FREE(node); + + node = next; + } + + self->_allocatedBytes = 0; + self->_usedBytes = 0; + + self->_root = nullptr; + self->_first = nullptr; + self->_last = nullptr; + self->_optimal = nullptr; +} + +// ============================================================================ +// [asmjit::VMemMgr - Construction / Destruction] +// ============================================================================ + +#if !ASMJIT_OS_WINDOWS +VMemMgr::VMemMgr() noexcept { +#else +VMemMgr::VMemMgr(HANDLE hProcess) noexcept { +#endif + + VMemInfo vm = OSUtils::getVirtualMemoryInfo(); + +#if ASMJIT_OS_WINDOWS + _hProcess = hProcess ? hProcess : vm.hCurrentProcess; +#endif // ASMJIT_OS_WINDOWS + + _blockSize = vm.pageGranularity; + _blockDensity = 64; + + _allocatedBytes = 0; + _usedBytes = 0; + + _root = nullptr; + _first = nullptr; + _last = nullptr; + _optimal = nullptr; + + _permanent = nullptr; + _keepVirtualMemory = false; +} + +VMemMgr::~VMemMgr() noexcept { + // Freeable memory cleanup - Also frees the virtual memory if configured to. + vMemMgrReset(this, _keepVirtualMemory); + + // Permanent memory cleanup - Never frees the virtual memory. + PermanentNode* node = _permanent; + while (node) { + PermanentNode* prev = node->prev; + ASMJIT_FREE(node); + node = prev; + } +} + +// ============================================================================ +// [asmjit::VMemMgr - Reset] +// ============================================================================ + +void VMemMgr::reset() noexcept { + vMemMgrReset(this, false); +} + +// ============================================================================ +// [asmjit::VMemMgr - Alloc / Release] +// ============================================================================ + +void* VMemMgr::alloc(size_t size, uint32_t type) noexcept { + if (type == kAllocPermanent) + return vMemMgrAllocPermanent(this, size); + else + return vMemMgrAllocFreeable(this, size); +} + +Error VMemMgr::release(void* p) noexcept { + if (!p) return kErrorOk; + + AutoLock locked(_lock); + MemNode* node = vMemMgrFindNodeByPtr(this, static_cast(p)); + if (!node) return DebugUtils::errored(kErrorInvalidArgument); + + size_t offset = (size_t)((uint8_t*)p - (uint8_t*)node->mem); + size_t bitpos = M_DIV(offset, node->density); + size_t i = (bitpos / kBitsPerEntity); + + size_t* up = node->baUsed + i; // Current ubits address. + size_t* cp = node->baCont + i; // Current cbits address. + size_t ubits = *up; // Current ubits[0] value. + size_t cbits = *cp; // Current cbits[0] value. + size_t bit = (size_t)1 << (bitpos % kBitsPerEntity); + + size_t cont = 0; + bool stop; + + for (;;) { + stop = (cbits & bit) == 0; + ubits &= ~bit; + cbits &= ~bit; + + bit <<= 1; + cont++; + + if (stop || bit == 0) { + *up = ubits; + *cp = cbits; + if (stop) + break; + + ubits = *++up; + cbits = *++cp; + bit = 1; + } + } + + // If the freed block is fully allocated node then it's needed to + // update 'optimal' pointer in memory manager. + if (node->used == node->size) { + MemNode* cur = _optimal; + + do { + cur = cur->prev; + if (cur == node) { + _optimal = node; + break; + } + } while (cur); + } + + // Statistics. + cont *= node->density; + if (node->largestBlock < cont) + node->largestBlock = cont; + + node->used -= cont; + _usedBytes -= cont; + + // If page is empty, we can free it. + if (node->used == 0) { + // Free memory associated with node (this memory is not accessed + // anymore so it's safe). + vMemMgrReleaseVMem(this, node->mem, node->size); + ASMJIT_FREE(node->baUsed); + + node->baUsed = nullptr; + node->baCont = nullptr; + + // Statistics. + _allocatedBytes -= node->size; + + // Remove node. This function can return different node than + // passed into, but data is copied into previous node if needed. + ASMJIT_FREE(vMemMgrRemoveNode(this, node)); + ASMJIT_ASSERT(vMemMgrCheckTree(this)); + } + + return kErrorOk; +} + +Error VMemMgr::shrink(void* p, size_t used) noexcept { + if (!p) return kErrorOk; + if (used == 0) + return release(p); + + AutoLock locked(_lock); + MemNode* node = vMemMgrFindNodeByPtr(this, (uint8_t*)p); + if (!node) return DebugUtils::errored(kErrorInvalidArgument); + + size_t offset = (size_t)((uint8_t*)p - (uint8_t*)node->mem); + size_t bitpos = M_DIV(offset, node->density); + size_t i = (bitpos / kBitsPerEntity); + + size_t* up = node->baUsed + i; // Current ubits address. + size_t* cp = node->baCont + i; // Current cbits address. + size_t ubits = *up; // Current ubits[0] value. + size_t cbits = *cp; // Current cbits[0] value. + size_t bit = (size_t)1 << (bitpos % kBitsPerEntity); + + size_t cont = 0; + size_t usedBlocks = (used + node->density - 1) / node->density; + + bool stop; + + // Find the first block we can mark as free. + for (;;) { + stop = (cbits & bit) == 0; + if (stop) + return kErrorOk; + + if (++cont == usedBlocks) + break; + + bit <<= 1; + if (bit == 0) { + ubits = *++up; + cbits = *++cp; + bit = 1; + } + } + + // Free the tail blocks. + cont = ~(size_t)0; + goto _EnterFreeLoop; + + for (;;) { + stop = (cbits & bit) == 0; + ubits &= ~bit; + +_EnterFreeLoop: + cbits &= ~bit; + + bit <<= 1; + cont++; + + if (stop || bit == 0) { + *up = ubits; + *cp = cbits; + if (stop) + break; + + ubits = *++up; + cbits = *++cp; + bit = 1; + } + } + + // Statistics. + cont *= node->density; + if (node->largestBlock < cont) + node->largestBlock = cont; + + node->used -= cont; + _usedBytes -= cont; + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::VMem - Test] +// ============================================================================ + +#if defined(ASMJIT_TEST) +static void VMemTest_fill(void* a, void* b, int i) noexcept { + int pattern = rand() % 256; + *(int *)a = i; + *(int *)b = i; + ::memset((char*)a + sizeof(int), pattern, i - sizeof(int)); + ::memset((char*)b + sizeof(int), pattern, i - sizeof(int)); +} + +static void VMemTest_verify(void* a, void* b) noexcept { + int ai = *(int*)a; + int bi = *(int*)b; + + EXPECT(ai == bi, + "The length of 'a' (%d) and 'b' (%d) should be same", ai, bi); + + EXPECT(::memcmp(a, b, ai) == 0, + "Pattern (%p) doesn't match", a); +} + +static void VMemTest_stats(VMemMgr& memmgr) noexcept { + INFO("Used : %u", static_cast(memmgr.getUsedBytes())); + INFO("Allocated: %u", static_cast(memmgr.getAllocatedBytes())); +} + +static void VMemTest_shuffle(void** a, void** b, size_t count) noexcept { + for (size_t i = 0; i < count; ++i) { + size_t si = (size_t)rand() % count; + + void* ta = a[i]; + void* tb = b[i]; + + a[i] = a[si]; + b[i] = b[si]; + + a[si] = ta; + b[si] = tb; + } +} + +UNIT(base_vmem) { + VMemMgr memmgr; + + // Should be predictible. + srand(100); + + int i; + int kCount = 200000; + + INFO("Memory alloc/free test - %d allocations.", static_cast(kCount)); + + void** a = (void**)ASMJIT_ALLOC(sizeof(void*) * kCount); + void** b = (void**)ASMJIT_ALLOC(sizeof(void*) * kCount); + + EXPECT(a != nullptr && b != nullptr, + "Couldn't allocate %u bytes on heap.", kCount * 2); + + INFO("Allocating virtual memory..."); + for (i = 0; i < kCount; i++) { + int r = (rand() % 1000) + 4; + + a[i] = memmgr.alloc(r); + EXPECT(a[i] != nullptr, + "Couldn't allocate %d bytes of virtual memory", r); + ::memset(a[i], 0, r); + } + VMemTest_stats(memmgr); + + INFO("Freeing virtual memory..."); + for (i = 0; i < kCount; i++) { + EXPECT(memmgr.release(a[i]) == kErrorOk, + "Failed to free %p.", b[i]); + } + VMemTest_stats(memmgr); + + INFO("Verified alloc/free test - %d allocations.", static_cast(kCount)); + for (i = 0; i < kCount; i++) { + int r = (rand() % 1000) + 4; + + a[i] = memmgr.alloc(r); + EXPECT(a[i] != nullptr, + "Couldn't allocate %d bytes of virtual memory.", r); + + b[i] = ASMJIT_ALLOC(r); + EXPECT(b[i] != nullptr, + "Couldn't allocate %d bytes on heap.", r); + + VMemTest_fill(a[i], b[i], r); + } + VMemTest_stats(memmgr); + + INFO("Shuffling..."); + VMemTest_shuffle(a, b, kCount); + + INFO("Verify and free..."); + for (i = 0; i < kCount / 2; i++) { + VMemTest_verify(a[i], b[i]); + EXPECT(memmgr.release(a[i]) == kErrorOk, + "Failed to free %p.", a[i]); + ASMJIT_FREE(b[i]); + } + VMemTest_stats(memmgr); + + INFO("Alloc again."); + for (i = 0; i < kCount / 2; i++) { + int r = (rand() % 1000) + 4; + + a[i] = memmgr.alloc(r); + EXPECT(a[i] != nullptr, + "Couldn't allocate %d bytes of virtual memory.", r); + + b[i] = ASMJIT_ALLOC(r); + EXPECT(b[i] != nullptr, + "Couldn't allocate %d bytes on heap."); + + VMemTest_fill(a[i], b[i], r); + } + VMemTest_stats(memmgr); + + INFO("Verify and free..."); + for (i = 0; i < kCount; i++) { + VMemTest_verify(a[i], b[i]); + EXPECT(memmgr.release(a[i]) == kErrorOk, + "Failed to free %p.", a[i]); + ASMJIT_FREE(b[i]); + } + VMemTest_stats(memmgr); + + ASMJIT_FREE(a); + ASMJIT_FREE(b); +} +#endif // ASMJIT_TEST + +} // asmjit namespace diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/vmem.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/vmem.h new file mode 100644 index 00000000..6a1a5133 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/vmem.h @@ -0,0 +1,154 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_VMEM_H +#define _ASMJIT_BASE_VMEM_H + +// [Dependencies] +#include "../base/globals.h" +#include "../base/osutils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::VMemMgr] +// ============================================================================ + +//! Reference implementation of memory manager that uses `VMemUtil` to allocate +//! chunks of virtual memory and bit arrays to manage it. +class VMemMgr { +public: + //! Type of virtual memory allocation, see `VMemMgr::alloc()`. + ASMJIT_ENUM(AllocType) { + //! Normal memory allocation, has to be freed by `VMemMgr::release()`. + kAllocFreeable = 0, + //! Allocate permanent memory, can't be freed. + kAllocPermanent = 1 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + +#if !ASMJIT_OS_WINDOWS + //! Create a `VMemMgr` instance. + ASMJIT_API VMemMgr() noexcept; +#else + //! Create a `VMemMgr` instance. + //! + //! NOTE: When running on Windows it's possible to specify a `hProcess` to + //! be used for memory allocation. Using `hProcess` allows to allocate memory + //! of a remote process. + ASMJIT_API VMemMgr(HANDLE hProcess = static_cast(0)) noexcept; +#endif // ASMJIT_OS_WINDOWS + + //! Destroy the `VMemMgr` instance and free all blocks. + ASMJIT_API ~VMemMgr() noexcept; + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + //! Free all allocated memory. + ASMJIT_API void reset() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + +#if ASMJIT_OS_WINDOWS + //! Get the handle of the process memory manager is bound to. + ASMJIT_INLINE HANDLE getProcessHandle() const noexcept { return _hProcess; } +#endif // ASMJIT_OS_WINDOWS + + //! Get how many bytes are currently allocated. + ASMJIT_INLINE size_t getAllocatedBytes() const noexcept { return _allocatedBytes; } + //! Get how many bytes are currently used. + ASMJIT_INLINE size_t getUsedBytes() const noexcept { return _usedBytes; } + + //! Get whether to keep allocated memory after the `VMemMgr` is destroyed. + //! + //! \sa \ref setKeepVirtualMemory. + ASMJIT_INLINE bool getKeepVirtualMemory() const noexcept { return _keepVirtualMemory; } + //! Set whether to keep allocated memory after the memory manager is destroyed. + //! + //! This method is usable when patching code of remote process. You need to + //! allocate process memory, store generated assembler into it and patch the + //! method you want to redirect (into your code). This method affects only + //! VMemMgr destructor. After destruction all internal + //! structures are freed, only the process virtual memory remains. + //! + //! NOTE: Memory allocated with kAllocPermanent is always kept. + //! + //! \sa \ref getKeepVirtualMemory. + ASMJIT_INLINE void setKeepVirtualMemory(bool val) noexcept { _keepVirtualMemory = val; } + + // -------------------------------------------------------------------------- + // [Alloc / Release] + // -------------------------------------------------------------------------- + + //! Allocate a `size` bytes of virtual memory. + //! + //! Note that if you are implementing your own virtual memory manager then you + //! can quitly ignore type of allocation. This is mainly for AsmJit to memory + //! manager that allocated memory will be never freed. + ASMJIT_API void* alloc(size_t size, uint32_t type = kAllocFreeable) noexcept; + //! Free previously allocated memory at a given `address`. + ASMJIT_API Error release(void* p) noexcept; + //! Free extra memory allocated with `p`. + ASMJIT_API Error shrink(void* p, size_t used) noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + +#if ASMJIT_OS_WINDOWS + HANDLE _hProcess; //!< Process passed to `VirtualAllocEx` and `VirtualFree`. +#endif // ASMJIT_OS_WINDOWS + Lock _lock; //!< Lock to enable thread-safe functionality. + + size_t _blockSize; //!< Default block size. + size_t _blockDensity; //!< Default block density. + bool _keepVirtualMemory; //!< Keep virtual memory after destroyed. + + size_t _allocatedBytes; //!< How many bytes are currently allocated. + size_t _usedBytes; //!< How many bytes are currently used. + + //! \internal + //! \{ + + struct RbNode; + struct MemNode; + struct PermanentNode; + + // Memory nodes root. + MemNode* _root; + // Memory nodes list. + MemNode* _first; + MemNode* _last; + MemNode* _optimal; + // Permanent memory. + PermanentNode* _permanent; + + //! \} +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_VMEM_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/zone.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/zone.cpp new file mode 100644 index 00000000..921a5f29 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/zone.cpp @@ -0,0 +1,200 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/utils.h" +#include "../base/zone.h" +#include + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! Zero size block used by `Zone` that doesn't have any memory allocated. +static const Zone::Block Zone_zeroBlock = { nullptr, nullptr, 0, { 0 } }; + +static ASMJIT_INLINE uint32_t Zone_getAlignmentOffsetFromAlignment(uint32_t x) noexcept { + switch (x) { + default: return 0; + case 0 : return 0; + case 1 : return 0; + case 2 : return 1; + case 4 : return 2; + case 8 : return 3; + case 16: return 4; + case 32: return 5; + case 64: return 6; + } +} + +// ============================================================================ +// [asmjit::Zone - Construction / Destruction] +// ============================================================================ + +Zone::Zone(uint32_t blockSize, uint32_t blockAlignment) noexcept + : _ptr(nullptr), + _end(nullptr), + _block(const_cast(&Zone_zeroBlock)), + _blockSize(blockSize), + _blockAlignmentShift(Zone_getAlignmentOffsetFromAlignment(blockAlignment)) {} + +Zone::~Zone() noexcept { + reset(true); +} + +// ============================================================================ +// [asmjit::Zone - Reset] +// ============================================================================ + +void Zone::reset(bool releaseMemory) noexcept { + Block* cur = _block; + + // Can't be altered. + if (cur == &Zone_zeroBlock) + return; + + if (releaseMemory) { + // Since cur can be in the middle of the double-linked list, we have to + // traverse to both directions `prev` and `next` separately. + Block* next = cur->next; + do { + Block* prev = cur->prev; + ASMJIT_FREE(cur); + cur = prev; + } while (cur); + + cur = next; + while (cur) { + next = cur->next; + ASMJIT_FREE(cur); + cur = next; + } + + _ptr = nullptr; + _end = nullptr; + _block = const_cast(&Zone_zeroBlock); + } + else { + while (cur->prev) + cur = cur->prev; + + _ptr = cur->data; + _end = _ptr + cur->size; + _block = cur; + } +} + +// ============================================================================ +// [asmjit::Zone - Alloc] +// ============================================================================ + +void* Zone::_alloc(size_t size) noexcept { + Block* curBlock = _block; + uint8_t* p; + + size_t blockSize = Utils::iMax(_blockSize, size); + size_t blockAlignment = getBlockAlignment(); + + // The `_alloc()` method can only be called if there is not enough space + // in the current block, see `alloc()` implementation for more details. + ASMJIT_ASSERT(curBlock == &Zone_zeroBlock || getRemainingSize() < size); + + // If the `Zone` has been cleared the current block doesn't have to be the + // last one. Check if there is a block that can be used instead of allocating + // a new one. If there is a `next` block it's completely unused, we don't have + // to check for remaining bytes. + Block* next = curBlock->next; + if (next && next->size >= size) { + p = Utils::alignTo(next->data, blockAlignment); + + _block = next; + _ptr = p + size; + _end = next->data + next->size; + + return static_cast(p); + } + + // Prevent arithmetic overflow. + if (ASMJIT_UNLIKELY(blockSize > (~static_cast(0) - sizeof(Block) - blockAlignment))) + return nullptr; + + blockSize += blockAlignment; + Block* newBlock = static_cast(ASMJIT_ALLOC(sizeof(Block) + blockSize)); + + if (ASMJIT_UNLIKELY(!newBlock)) + return nullptr; + + // Align the pointer to `blockAlignment` and adjust the size of this block + // accordingly. It's the same as using `blockAlignment - Utils::alignDiff()`, + // just written differently. + p = Utils::alignTo(newBlock->data, blockAlignment); + newBlock->prev = nullptr; + newBlock->next = nullptr; + newBlock->size = blockSize; + + if (curBlock != &Zone_zeroBlock) { + newBlock->prev = curBlock; + curBlock->next = newBlock; + + // Does only happen if there is a next block, but the requested memory + // can't fit into it. In this case a new buffer is allocated and inserted + // between the current block and the next one. + if (next) { + newBlock->next = next; + next->prev = newBlock; + } + } + + _block = newBlock; + _ptr = p + size; + _end = newBlock->data + blockSize; + + return static_cast(p); +} + +void* Zone::allocZeroed(size_t size) noexcept { + void* p = alloc(size); + if (ASMJIT_UNLIKELY(!p)) return p; + return ::memset(p, 0, size); +} + +void* Zone::dup(const void* data, size_t size, bool nullTerminate) noexcept { + if (ASMJIT_UNLIKELY(!data || !size)) return nullptr; + + ASMJIT_ASSERT(size != IntTraits::maxValue()); + uint8_t* m = allocT(size + nullTerminate); + if (ASMJIT_UNLIKELY(!m)) return nullptr; + + ::memcpy(m, data, size); + if (nullTerminate) m[size] = '\0'; + + return static_cast(m); +} + +char* Zone::sformat(const char* fmt, ...) noexcept { + if (ASMJIT_UNLIKELY(!fmt)) return nullptr; + + char buf[512]; + size_t len; + + va_list ap; + va_start(ap, fmt); + + len = vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf) - 1, fmt, ap); + buf[len++] = 0; + + va_end(ap); + return static_cast(dup(buf, len)); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/zone.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/zone.h new file mode 100644 index 00000000..0db30171 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/zone.h @@ -0,0 +1,229 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_ZONE_H +#define _ASMJIT_BASE_ZONE_H + +// [Dependencies] +#include "../base/globals.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::Zone] +// ============================================================================ + +//! Memory zone. +//! +//! Zone is an incremental memory allocator that allocates memory by simply +//! incrementing a pointer. It allocates blocks of memory by using standard +//! C `malloc`, but divides these blocks into smaller segments requested by +//! calling `Zone::alloc()` and friends. +//! +//! Zone has no function to release the allocated memory. It has to be released +//! all at once by calling `reset()`. If you need a more friendly allocator that +//! also supports `release()`, consider using \ref Zone with \ref ZoneHeap. +class Zone { +public: + //! \internal + //! + //! A single block of memory. + struct Block { + Block* prev; //!< Link to the previous block. + Block* next; //!< Link to the next block. + size_t size; //!< Size of the block. + uint8_t data[sizeof(void*)]; //!< Data. + }; + + enum { + //! Zone allocator overhead. + kZoneOverhead = kMemAllocOverhead + static_cast(sizeof(Block)) + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new instance of `Zone` allocator. + //! + //! The `blockSize` parameter describes the default size of the block. If the + //! `size` parameter passed to `alloc()` is greater than the default size + //! `Zone` will allocate and use a larger block, but it will not change the + //! default `blockSize`. + //! + //! It's not required, but it's good practice to set `blockSize` to a + //! reasonable value that depends on the usage of `Zone`. Greater block sizes + //! are generally safer and performs better than unreasonably low values. + ASMJIT_API Zone(uint32_t blockSize, uint32_t blockAlignment = 0) noexcept; + + //! Destroy the `Zone` instance. + //! + //! This will destroy the `Zone` instance and release all blocks of memory + //! allocated by it. It performs implicit `reset(true)`. + ASMJIT_API ~Zone() noexcept; + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + //! Reset the `Zone` invalidating all blocks allocated. + //! + //! If `releaseMemory` is true all buffers will be released to the system. + ASMJIT_API void reset(bool releaseMemory = false) noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the default block size. + ASMJIT_INLINE uint32_t getBlockSize() const noexcept { return _blockSize; } + //! Get the default block alignment. + ASMJIT_INLINE uint32_t getBlockAlignment() const noexcept { return (uint32_t)1 << _blockAlignmentShift; } + //! Get remaining size of the current block. + ASMJIT_INLINE size_t getRemainingSize() const noexcept { return (size_t)(_end - _ptr); } + + //! Get the current zone cursor (dangerous). + //! + //! This is a function that can be used to get exclusive access to the current + //! block's memory buffer. + ASMJIT_INLINE uint8_t* getCursor() noexcept { return _ptr; } + //! Get the end of the current zone block, only useful if you use `getCursor()`. + ASMJIT_INLINE uint8_t* getEnd() noexcept { return _end; } + + //! Set the current zone cursor to `p` (must match the current block). + //! + //! This is a counterpart of `getZoneCursor()`. + ASMJIT_INLINE void setCursor(uint8_t* p) noexcept { + ASMJIT_ASSERT(p >= _ptr && p <= _end); + _ptr = p; + } + + // -------------------------------------------------------------------------- + // [Alloc] + // -------------------------------------------------------------------------- + + //! Allocate `size` bytes of memory. + //! + //! Pointer returned is valid until the `Zone` instance is destroyed or reset + //! by calling `reset()`. If you plan to make an instance of C++ from the + //! given pointer use placement `new` and `delete` operators: + //! + //! ~~~ + //! using namespace asmjit; + //! + //! class Object { ... }; + //! + //! // Create Zone with default block size of approximately 65536 bytes. + //! Zone zone(65536 - Zone::kZoneOverhead); + //! + //! // Create your objects using zone object allocating, for example: + //! Object* obj = static_cast( zone.alloc(sizeof(Object)) ); + // + //! if (!obj) { + //! // Handle out of memory error. + //! } + //! + //! // Placement `new` and `delete` operators can be used to instantiate it. + //! new(obj) Object(); + //! + //! // ... lifetime of your objects ... + //! + //! // To destroy the instance (if required). + //! obj->~Object(); + //! + //! // Reset or destroy `Zone`. + //! zone.reset(); + //! ~~~ + ASMJIT_INLINE void* alloc(size_t size) noexcept { + uint8_t* ptr = _ptr; + size_t remainingBytes = (size_t)(_end - ptr); + + if (ASMJIT_UNLIKELY(remainingBytes < size)) + return _alloc(size); + + _ptr += size; + ASMJIT_ASSERT(_ptr <= _end); + + return static_cast(ptr); + } + + //! Allocate `size` bytes without any checks. + //! + //! Can only be called if `getRemainingSize()` returns size at least equal + //! to `size`. + ASMJIT_INLINE void* allocNoCheck(size_t size) noexcept { + ASMJIT_ASSERT((size_t)(_end - _ptr) >= size); + + uint8_t* ptr = _ptr; + _ptr += size; + return static_cast(ptr); + } + + //! Allocate `size` bytes of zeroed memory. + //! + //! See \ref alloc() for more details. + ASMJIT_API void* allocZeroed(size_t size) noexcept; + + //! Like `alloc()`, but the return pointer is casted to `T*`. + template + ASMJIT_INLINE T* allocT(size_t size = sizeof(T)) noexcept { + return static_cast(alloc(size)); + } + + //! Like `allocNoCheck()`, but the return pointer is casted to `T*`. + template + ASMJIT_INLINE T* allocNoCheckT(size_t size = sizeof(T)) noexcept { + return static_cast(allocNoCheck(size)); + } + + //! Like `allocZeroed()`, but the return pointer is casted to `T*`. + template + ASMJIT_INLINE T* allocZeroedT(size_t size = sizeof(T)) noexcept { + return static_cast(allocZeroed(size)); + } + + //! \internal + ASMJIT_API void* _alloc(size_t size) noexcept; + + //! Helper to duplicate data. + ASMJIT_API void* dup(const void* data, size_t size, bool nullTerminate = false) noexcept; + + //! Helper to duplicate formatted string, maximum length is 256 bytes. + ASMJIT_API char* sformat(const char* str, ...) noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t* _ptr; //!< Pointer in the current block's buffer. + uint8_t* _end; //!< End of the current block's buffer. + Block* _block; //!< Current block. + +#if ASMJIT_ARCH_64BIT + uint32_t _blockSize; //!< Default size of a newly allocated block. + uint32_t _blockAlignmentShift; //!< Minimum alignment of each block. +#else + uint32_t _blockSize : 29; //!< Default size of a newly allocated block. + uint32_t _blockAlignmentShift : 3; //!< Minimum alignment of each block. +#endif +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_ZONE_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/zonecontainers.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/zonecontainers.cpp new file mode 100644 index 00000000..0dd73294 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/zonecontainers.cpp @@ -0,0 +1,239 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/utils.h" +#include "../base/zonecontainers.h" +#include "../base/zoneheap.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::ZoneVectorBase - Reset] +// ============================================================================ + +//! Clear vector data and free internal buffer. +void ZoneVectorBase::_reset(size_t sizeOfT, ZoneHeap* heap) noexcept { + if (_data) { + ASMJIT_ASSERT(_heap != nullptr); + _heap->release(_data, _capacity * sizeOfT); + } + + _heap = heap; + _length = 0; + _capacity = 0; + _data = nullptr; +} + +// ============================================================================ +// [asmjit::ZoneVectorBase - Helpers] +// ============================================================================ + +Error ZoneVectorBase::_grow(size_t sizeOfT, size_t n) noexcept { + size_t threshold = kMemAllocGrowMax / sizeOfT; + size_t capacity = _capacity; + size_t after = _length; + + if (IntTraits::maxValue() - n < after) + return DebugUtils::errored(kErrorNoHeapMemory); + + after += n; + if (capacity >= after) return kErrorOk; + + // ZoneVector is used as an array to hold short-lived data structures used + // during code generation. The growing strategy is simple - use small capacity + // at the beginning (very good for ZoneHeap) and then grow quicker to prevent + // successive reallocations. + if (capacity < 4) + capacity = 4; + else if (capacity < 8) + capacity = 8; + else if (capacity < 16) + capacity = 16; + else if (capacity < 64) + capacity = 64; + else if (capacity < 256) + capacity = 256; + + while (capacity < after) { + if (capacity < threshold) + capacity *= 2; + else + capacity += threshold; + } + + return _reserve(sizeOfT, capacity); +} + +Error ZoneVectorBase::_reserve(size_t sizeOfT, size_t n) noexcept { + size_t oldCapacity = _capacity; + if (oldCapacity >= n) return kErrorOk; + + size_t nBytes = n * sizeOfT; + if (nBytes < n) return DebugUtils::errored(kErrorNoHeapMemory); + + size_t allocatedBytes; + uint8_t* newData = static_cast(_heap->alloc(nBytes, allocatedBytes)); + + if (ASMJIT_UNLIKELY(!newData)) + return DebugUtils::errored(kErrorNoHeapMemory); + + void* oldData = _data; + if (_length) + ::memcpy(newData, oldData, _length * sizeOfT); + + if (oldData) + _heap->release(oldData, oldCapacity * sizeOfT); + + _capacity = allocatedBytes / sizeOfT; + ASMJIT_ASSERT(_capacity >= n); + + _data = newData; + return kErrorOk; +} + +Error ZoneVectorBase::_resize(size_t sizeOfT, size_t n) noexcept { + size_t length = _length; + if (_capacity < n) { + ASMJIT_PROPAGATE(_grow(sizeOfT, n - length)); + ASMJIT_ASSERT(_capacity >= n); + } + + if (length < n) + ::memset(static_cast(_data) + length * sizeOfT, 0, (n - length) * sizeOfT); + + _length = n; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::ZoneHashBase - Utilities] +// ============================================================================ + +static uint32_t ZoneHashGetClosestPrime(uint32_t x) noexcept { + static const uint32_t primeTable[] = { + 23, 53, 193, 389, 769, 1543, 3079, 6151, 12289, 24593 + }; + + size_t i = 0; + uint32_t p; + + do { + if ((p = primeTable[i]) > x) + break; + } while (++i < ASMJIT_ARRAY_SIZE(primeTable)); + + return p; +} + +// ============================================================================ +// [asmjit::ZoneHashBase - Reset] +// ============================================================================ + +void ZoneHashBase::reset(ZoneHeap* heap) noexcept { + ZoneHashNode** oldData = _data; + if (oldData != _embedded) + _heap->release(oldData, _bucketsCount * sizeof(ZoneHashNode*)); + + _heap = heap; + _size = 0; + _bucketsCount = 1; + _bucketsGrow = 1; + _data = _embedded; + _embedded[0] = nullptr; +} + +// ============================================================================ +// [asmjit::ZoneHashBase - Rehash] +// ============================================================================ + +void ZoneHashBase::_rehash(uint32_t newCount) noexcept { + ASMJIT_ASSERT(isInitialized()); + + ZoneHashNode** oldData = _data; + ZoneHashNode** newData = reinterpret_cast( + _heap->alloc(static_cast(newCount) * sizeof(ZoneHashNode*))); + + // We can still store nodes into the table, but it will degrade. + if (ASMJIT_UNLIKELY(newData == nullptr)) + return; + + uint32_t i; + uint32_t oldCount = _bucketsCount; + + for (i = 0; i < oldCount; i++) { + ZoneHashNode* node = oldData[i]; + while (node) { + ZoneHashNode* next = node->_hashNext; + uint32_t hMod = node->_hVal % newCount; + + node->_hashNext = newData[hMod]; + newData[hMod] = node; + + node = next; + } + } + + // 90% is the maximum occupancy, can't overflow since the maximum capacity + // is limited to the last prime number stored in the prime table. + if (oldData != _embedded) + _heap->release(oldData, _bucketsCount * sizeof(ZoneHashNode*)); + + _bucketsCount = newCount; + _bucketsGrow = newCount * 9 / 10; + + _data = newData; +} + +// ============================================================================ +// [asmjit::ZoneHashBase - Ops] +// ============================================================================ + +ZoneHashNode* ZoneHashBase::_put(ZoneHashNode* node) noexcept { + uint32_t hMod = node->_hVal % _bucketsCount; + ZoneHashNode* next = _data[hMod]; + + node->_hashNext = next; + _data[hMod] = node; + + if (++_size >= _bucketsGrow && next) { + uint32_t newCapacity = ZoneHashGetClosestPrime(_bucketsCount); + if (newCapacity != _bucketsCount) + _rehash(newCapacity); + } + + return node; +} + +ZoneHashNode* ZoneHashBase::_del(ZoneHashNode* node) noexcept { + uint32_t hMod = node->_hVal % _bucketsCount; + + ZoneHashNode** pPrev = &_data[hMod]; + ZoneHashNode* p = *pPrev; + + while (p) { + if (p == node) { + *pPrev = p->_hashNext; + return node; + } + + pPrev = &p->_hashNext; + p = *pPrev; + } + + return nullptr; +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/zonecontainers.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/zonecontainers.h new file mode 100644 index 00000000..0089f1c8 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/zonecontainers.h @@ -0,0 +1,443 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CONTAINERS_H +#define _ASMJIT_BASE_CONTAINERS_H + +// [Dependencies] +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class ZoneHeap; + +// ============================================================================ +// [asmjit::ZoneList] +// ============================================================================ + +//! \internal +template +class ZoneList { +public: + ASMJIT_NONCOPYABLE(ZoneList) + + // -------------------------------------------------------------------------- + // [Link] + // -------------------------------------------------------------------------- + + struct Link { + //! Get next node. + ASMJIT_INLINE Link* getNext() const noexcept { return _next; } + //! Get value. + ASMJIT_INLINE T getValue() const noexcept { return _value; } + //! Set value to `value`. + ASMJIT_INLINE void setValue(const T& value) noexcept { _value = value; } + + Link* _next; + T _value; + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ZoneList() noexcept : _first(nullptr), _last(nullptr) {} + ASMJIT_INLINE ~ZoneList() noexcept {} + + // -------------------------------------------------------------------------- + // [Data] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isEmpty() const noexcept { return _first != nullptr; } + ASMJIT_INLINE Link* getFirst() const noexcept { return _first; } + ASMJIT_INLINE Link* getLast() const noexcept { return _last; } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void reset() noexcept { + _first = nullptr; + _last = nullptr; + } + + ASMJIT_INLINE void prepend(Link* link) noexcept { + link->_next = _first; + if (!_first) _last = link; + _first = link; + } + + ASMJIT_INLINE void append(Link* link) noexcept { + link->_next = nullptr; + if (!_first) + _first = link; + else + _last->_next = link; + _last = link; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Link* _first; + Link* _last; +}; + +// ============================================================================ +// [asmjit::ZoneVectorBase] +// ============================================================================ + +//! \internal +class ZoneVectorBase { +public: + ASMJIT_NONCOPYABLE(ZoneVectorBase) + +protected: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new instance of `ZoneVectorBase`. + explicit ASMJIT_INLINE ZoneVectorBase(ZoneHeap* heap) noexcept + : _heap(heap), + _length(0), + _capacity(0), + _data(nullptr) {} + + //! Destroy the `ZoneVectorBase` and its data. + ASMJIT_INLINE ~ZoneVectorBase() noexcept {} + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + //! Reset the vector data and set its `length` to zero. + //! + //! If `releaseMemory` is true the vector buffer will be released to the + //! system. + ASMJIT_API void _reset(size_t sizeOfT, ZoneHeap* heap) noexcept; + + // -------------------------------------------------------------------------- + // [Grow / Reserve] + // -------------------------------------------------------------------------- + + ASMJIT_API Error _grow(size_t sizeOfT, size_t n) noexcept; + ASMJIT_API Error _resize(size_t sizeOfT, size_t n) noexcept; + ASMJIT_API Error _reserve(size_t sizeOfT, size_t n) noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + ZoneHeap* _heap; //!< Zone heap used to allocate data. + size_t _length; //!< Length of the vector. + size_t _capacity; //!< Capacity of the vector. + void* _data; //!< Vector data. +}; + +// ============================================================================ +// [asmjit::ZoneVector] +// ============================================================================ + +//! Template used to store and manage array of Zone allocated data. +//! +//! This template has these advantages over other std::vector<>: +//! - Always non-copyable (designed to be non-copyable, we want it). +//! - No copy-on-write (some implementations of STL can use it). +//! - Optimized for working only with POD types. +//! - Uses ZoneHeap, thus small vectors are basically for free. +template +class ZoneVector : public ZoneVectorBase { +public: + ASMJIT_NONCOPYABLE(ZoneVector) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new instance of `ZoneVector`. + explicit ASMJIT_INLINE ZoneVector(ZoneHeap* heap = nullptr) noexcept + : ZoneVectorBase(heap) {} + + //! Destroy the `ZoneVector` and its data. + ASMJIT_INLINE ~ZoneVector() noexcept { _reset(sizeof(T), nullptr); } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + //! Get if this `ZoneVectorBase` has been initialized. + ASMJIT_INLINE bool isInitialized() const noexcept { return _heap != nullptr; } + //! Reset this vector and initialize to use the given ZoneHeap (can be null). + ASMJIT_INLINE void reset(ZoneHeap* heap) noexcept { _reset(sizeof(T), heap); } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get whether the vector is empty. + ASMJIT_INLINE bool isEmpty() const noexcept { return _length == 0; } + + //! Get length. + ASMJIT_INLINE size_t getLength() const noexcept { return _length; } + //! Get capacity. + ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; } + + //! Get data. + ASMJIT_INLINE T* getData() noexcept { return static_cast(_data); } + //! \overload + ASMJIT_INLINE const T* getData() const noexcept { return static_cast(_data); } + + // -------------------------------------------------------------------------- + // [Grow / Reserve] + // -------------------------------------------------------------------------- + + //! Called to grow the buffer to fit at least `n` elements more. + ASMJIT_INLINE Error grow(size_t n) noexcept { return ZoneVectorBase::_grow(sizeof(T), n); } + //! Resize the vector to hold `n` elements. + //! + //! If `n` is greater than the current length then the additional elements' + //! content will be initialized to zero. If `n` is less than the current + //! length then the vector will be truncated to exactly `n` elements. + ASMJIT_INLINE Error resize(size_t n) noexcept { return ZoneVectorBase::_resize(sizeof(T), n); } + //! Realloc internal array to fit at least `n` items. + ASMJIT_INLINE Error reserve(size_t n) noexcept { return ZoneVectorBase::_reserve(sizeof(T), n); } + + ASMJIT_INLINE void truncate(size_t n) noexcept { + ASMJIT_ASSERT(n <= _length); + _length = n; + } + + ASMJIT_INLINE Error willGrow(size_t n) noexcept { + return _capacity - _length < n ? grow(n) : static_cast(kErrorOk); + } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + //! Clears the vector without reseting \ref ZoneHeap. + ASMJIT_INLINE void clear() noexcept { _length = 0; } + + //! Prepend `item` to the vector. + Error prepend(const T& item) noexcept { + if (ASMJIT_UNLIKELY(_length == _capacity)) + ASMJIT_PROPAGATE(grow(1)); + + ::memmove(static_cast(_data) + 1, _data, _length * sizeof(T)); + ::memcpy(_data, &item, sizeof(T)); + + _length++; + return kErrorOk; + } + + //! Insert an `item` at the specified `index`. + Error insert(size_t index, const T& item) noexcept { + ASMJIT_ASSERT(index <= _length); + + if (ASMJIT_UNLIKELY(_length == _capacity)) + ASMJIT_PROPAGATE(grow(1)); + + T* dst = static_cast(_data) + index; + ::memmove(dst + 1, dst, _length - index); + ::memcpy(dst, &item, sizeof(T)); + + _length++; + return kErrorOk; + } + + //! Append `item` to the vector. + Error append(const T& item) noexcept { + if (ASMJIT_UNLIKELY(_length == _capacity)) + ASMJIT_PROPAGATE(grow(1)); + + ::memcpy(static_cast(_data) + _length, &item, sizeof(T)); + + _length++; + return kErrorOk; + } + + //! Append `item` to the vector (unsafe case). + //! + //! Can only be used together with `willGrow()`. If `willGrow(N)` returns + //! `kErrorOk` then N elements can be added to the vector without checking + //! if there is a place for them. Used mostly internally. + ASMJIT_INLINE void appendUnsafe(const T& item) noexcept { + ASMJIT_ASSERT(_length < _capacity); + + ::memcpy(static_cast(_data) + _length, &item, sizeof(T)); + _length++; + } + + //! Get index of `val` or `kInvalidIndex` if not found. + ASMJIT_INLINE size_t indexOf(const T& val) const noexcept { + const T* data = static_cast(_data); + size_t length = _length; + + for (size_t i = 0; i < length; i++) + if (data[i] == val) + return i; + + return kInvalidIndex; + } + + //! Remove item at index `i`. + ASMJIT_INLINE void removeAt(size_t i) noexcept { + ASMJIT_ASSERT(i < _length); + + T* data = static_cast(_data) + i; + _length--; + ::memmove(data, data + 1, _length - i); + } + + //! Swap this pod-vector with `other`. + ASMJIT_INLINE void swap(ZoneVector& other) noexcept { + ASMJIT_ASSERT(_heap == other._heap); + + Utils::swap(_length, other._length); + Utils::swap(_capacity, other._capacity); + Utils::swap(_data, other._data); + } + + //! Get item at index `i`. + ASMJIT_INLINE T& operator[](size_t i) noexcept { + ASMJIT_ASSERT(i < _length); + return getData()[i]; + } + + //! Get item at index `i`. + ASMJIT_INLINE const T& operator[](size_t i) const noexcept { + ASMJIT_ASSERT(i < _length); + return getData()[i]; + } +}; + +// ============================================================================ +// [asmjit::ZoneHashNode] +// ============================================================================ + +//! Node used by \ref ZoneHash<> template. +//! +//! You must provide function `bool eq(const Key& key)` in order to make +//! `ZoneHash::get()` working. +class ZoneHashNode { +public: + ASMJIT_INLINE ZoneHashNode(uint32_t hVal = 0) noexcept + : _hashNext(nullptr), + _hVal(hVal) {} + + //! Next node in the chain, null if it terminates the chain. + ZoneHashNode* _hashNext; + //! Key hash. + uint32_t _hVal; + //! Should be used by Node that inherits ZoneHashNode, it aligns ZoneHashNode. + uint32_t _customData; +}; + +// ============================================================================ +// [asmjit::ZoneHashBase] +// ============================================================================ + +class ZoneHashBase { +public: + ASMJIT_NONCOPYABLE(ZoneHashBase) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ZoneHashBase(ZoneHeap* heap) noexcept { + _heap = heap; + _size = 0; + _bucketsCount = 1; + _bucketsGrow = 1; + _data = _embedded; + _embedded[0] = nullptr; + } + ASMJIT_INLINE ~ZoneHashBase() noexcept { reset(nullptr); } + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isInitialized() const noexcept { return _heap != nullptr; } + ASMJIT_API void reset(ZoneHeap* heap) noexcept; + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE size_t getSize() const noexcept { return _size; } + + ASMJIT_API void _rehash(uint32_t newCount) noexcept; + ASMJIT_API ZoneHashNode* _put(ZoneHashNode* node) noexcept; + ASMJIT_API ZoneHashNode* _del(ZoneHashNode* node) noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + ZoneHeap* _heap; //!< ZoneHeap used to allocate data. + size_t _size; //!< Count of records inserted into the hash table. + uint32_t _bucketsCount; //!< Count of hash buckets. + uint32_t _bucketsGrow; //!< When buckets array should grow. + + ZoneHashNode** _data; //!< Buckets data. + ZoneHashNode* _embedded[1]; //!< Embedded data, used by empty hash tables. +}; + +// ============================================================================ +// [asmjit::ZoneHash] +// ============================================================================ + +//! Low-level hash table specialized for storing string keys and POD values. +//! +//! This hash table allows duplicates to be inserted (the API is so low +//! level that it's up to you if you allow it or not, as you should first +//! `get()` the node and then modify it or insert a new node by using `put()`, +//! depending on the intention). +template +class ZoneHash : public ZoneHashBase { +public: + explicit ASMJIT_INLINE ZoneHash(ZoneHeap* heap = nullptr) noexcept + : ZoneHashBase(heap) {} + ASMJIT_INLINE ~ZoneHash() noexcept {} + + template + ASMJIT_INLINE Node* get(const Key& key) const noexcept { + uint32_t hMod = key.hVal % _bucketsCount; + Node* node = static_cast(_data[hMod]); + + while (node && !key.matches(node)) + node = static_cast(node->_hashNext); + return node; + } + + ASMJIT_INLINE Node* put(Node* node) noexcept { return static_cast(_put(node)); } + ASMJIT_INLINE Node* del(Node* node) noexcept { return static_cast(_del(node)); } +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_CONTAINERS_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/zoneheap.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/base/zoneheap.cpp new file mode 100644 index 00000000..2ba096c9 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/zoneheap.cpp @@ -0,0 +1,180 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/utils.h" +#include "../base/zoneheap.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::ZoneHeap - Helpers] +// ============================================================================ + +static bool ZoneHeap_hasDynamicBlock(ZoneHeap* self, ZoneHeap::DynamicBlock* block) noexcept { + ZoneHeap::DynamicBlock* cur = self->_dynamicBlocks; + while (cur) { + if (cur == block) + return true; + cur = cur->next; + } + return false; +} + +// ============================================================================ +// [asmjit::ZoneHeap - Init / Reset] +// ============================================================================ + +void ZoneHeap::reset(Zone* zone) noexcept { + // Free dynamic blocks. + DynamicBlock* block = _dynamicBlocks; + while (block) { + DynamicBlock* next = block->next; + ASMJIT_FREE(block); + block = next; + } + + // Zero the entire class and initialize to the given `zone`. + ::memset(this, 0, sizeof(*this)); + _zone = zone; +} + +// ============================================================================ +// [asmjit::ZoneHeap - Alloc / Release] +// ============================================================================ + +void* ZoneHeap::_alloc(size_t size, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(isInitialized()); + + // We use our memory pool only if the requested block is of a reasonable size. + uint32_t slot; + if (_getSlotIndex(size, slot, allocatedSize)) { + // Slot reuse. + uint8_t* p = reinterpret_cast(_slots[slot]); + size = allocatedSize; + + if (p) { + _slots[slot] = reinterpret_cast(p)->next; + //printf("ALLOCATED %p of size %d (SLOT %d)\n", p, int(size), slot); + return p; + } + + // So use Zone to allocate a new chunk for us. But before we use it, we + // check if there is enough room for the new chunk in zone, and if not, + // we redistribute the remaining memory in Zone's current block into slots. + Zone* zone = _zone; + p = Utils::alignTo(zone->getCursor(), kBlockAlignment); + size_t remain = (p <= zone->getEnd()) ? (size_t)(zone->getEnd() - p) : size_t(0); + + if (ASMJIT_LIKELY(remain >= size)) { + zone->setCursor(p + size); + //printf("ALLOCATED %p of size %d (SLOT %d)\n", p, int(size), slot); + return p; + } + else { + // Distribute the remaining memory to suitable slots. + if (remain >= kLoGranularity) { + do { + size_t distSize = Utils::iMin(remain, kLoMaxSize); + uint32_t distSlot = static_cast((distSize - kLoGranularity) / kLoGranularity); + ASMJIT_ASSERT(distSlot < kLoCount); + + reinterpret_cast(p)->next = _slots[distSlot]; + _slots[distSlot] = reinterpret_cast(p); + + p += distSize; + remain -= distSize; + } while (remain >= kLoGranularity); + zone->setCursor(p); + } + + p = static_cast(zone->_alloc(size)); + if (ASMJIT_UNLIKELY(!p)) { + allocatedSize = 0; + return nullptr; + } + + //printf("ALLOCATED %p of size %d (SLOT %d)\n", p, int(size), slot); + return p; + } + } + else { + // Allocate a dynamic block. + size_t overhead = sizeof(DynamicBlock) + sizeof(DynamicBlock*) + kBlockAlignment; + + // Handle a possible overflow. + if (ASMJIT_UNLIKELY(overhead >= ~static_cast(0) - size)) + return nullptr; + + void* p = ASMJIT_ALLOC(size + overhead); + if (ASMJIT_UNLIKELY(!p)) { + allocatedSize = 0; + return nullptr; + } + + // Link as first in `_dynamicBlocks` double-linked list. + DynamicBlock* block = static_cast(p); + DynamicBlock* next = _dynamicBlocks; + + if (next) + next->prev = block; + + block->prev = nullptr; + block->next = next; + _dynamicBlocks = block; + + // Align the pointer to the guaranteed alignment and store `DynamicBlock` + // at the end of the memory block, so `_releaseDynamic()` can find it. + p = Utils::alignTo(static_cast(p) + sizeof(DynamicBlock) + sizeof(DynamicBlock*), kBlockAlignment); + reinterpret_cast(p)[-1] = block; + + allocatedSize = size; + //printf("ALLOCATED DYNAMIC %p of size %d\n", p, int(size)); + return p; + } +} + +void* ZoneHeap::_allocZeroed(size_t size, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(isInitialized()); + + void* p = _alloc(size, allocatedSize); + if (ASMJIT_UNLIKELY(!p)) return p; + return ::memset(p, 0, allocatedSize); +} + +void ZoneHeap::_releaseDynamic(void* p, size_t size) noexcept { + ASMJIT_ASSERT(isInitialized()); + //printf("RELEASING DYNAMIC %p of size %d\n", p, int(size)); + + // Pointer to `DynamicBlock` is stored at [-1]. + DynamicBlock* block = reinterpret_cast(p)[-1]; + ASMJIT_ASSERT(ZoneHeap_hasDynamicBlock(this, block)); + + // Unlink and free. + DynamicBlock* prev = block->prev; + DynamicBlock* next = block->next; + + if (prev) + prev->next = next; + else + _dynamicBlocks = next; + + if (next) + next->prev = prev; + + ASMJIT_FREE(block); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/base/zoneheap.h b/components/shared/detours/asmjit/asmjit/src/asmjit/base/zoneheap.h new file mode 100644 index 00000000..413ca36b --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/base/zoneheap.h @@ -0,0 +1,237 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_ZONEHEAP_H +#define _ASMJIT_BASE_ZONEHEAP_H + +// [Dependencies] +#include "../base/utils.h" +#include "../base/zone.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::ZoneHeap] +// ============================================================================ + +//! Zone-based memory allocator that uses an existing \ref Zone and provides +//! a `release()` functionality on top of it. It uses \ref Zone only for chunks +//! that can be pooled, and uses libc `malloc()` for chunks that are large. +//! +//! The advantage of ZoneHeap is that it can allocate small chunks of memory +//! really fast, and these chunks, when released, will be reused by consecutive +//! calls to `alloc()`. Also, since ZoneHeap uses \ref Zone, you can turn any +//! \ref Zone into a \ref ZoneHeap, and use it in your \ref CBPass when necessary. +//! +//! ZoneHeap is used by AsmJit containers to make containers having only +//! few elements fast (and lightweight) and to allow them to grow and use +//! dynamic blocks when require more storage. +class ZoneHeap { + ASMJIT_NONCOPYABLE(ZoneHeap) + + enum { + // In short, we pool chunks of these sizes: + // [32, 64, 96, 128, 192, 256, 320, 384, 448, 512] + + //! How many bytes per a low granularity pool (has to be at least 16). + kLoGranularity = 32, + //! Number of slots of a low granularity pool. + kLoCount = 4, + //! Maximum size of a block that can be allocated in a low granularity pool. + kLoMaxSize = kLoGranularity * kLoCount, + + //! How many bytes per a high granularity pool. + kHiGranularity = 64, + //! Number of slots of a high granularity pool. + kHiCount = 6, + //! Maximum size of a block that can be allocated in a high granularity pool. + kHiMaxSize = kLoMaxSize + kHiGranularity * kHiCount, + + //! Alignment of every pointer returned by `alloc()`. + kBlockAlignment = kLoGranularity + }; + + //! Single-linked list used to store unused chunks. + struct Slot { + //! Link to a next slot in a single-linked list. + Slot* next; + }; + + //! A block of memory that has been allocated dynamically and is not part of + //! block-list used by the allocator. This is used to keep track of all these + //! blocks so they can be freed by `reset()` if not freed explicitly. + struct DynamicBlock { + DynamicBlock* prev; + DynamicBlock* next; + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `ZoneHeap`. + //! + //! NOTE: To use it, you must first `init()` it. + ASMJIT_INLINE ZoneHeap() noexcept { + ::memset(this, 0, sizeof(*this)); + } + //! Create a new `ZoneHeap` initialized to use `zone`. + explicit ASMJIT_INLINE ZoneHeap(Zone* zone) noexcept { + ::memset(this, 0, sizeof(*this)); + _zone = zone; + } + //! Destroy the `ZoneHeap`. + ASMJIT_INLINE ~ZoneHeap() noexcept { reset(); } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + //! Get if the `ZoneHeap` is initialized (i.e. has `Zone`). + ASMJIT_INLINE bool isInitialized() const noexcept { return _zone != nullptr; } + + //! Convenience method to initialize the `ZoneHeap` with `zone`. + //! + //! It's the same as calling `reset(zone)`. + ASMJIT_INLINE void init(Zone* zone) noexcept { reset(zone); } + + //! Reset this `ZoneHeap` and also forget about the current `Zone` which + //! is attached (if any). Reset optionally attaches a new `zone` passed, or + //! keeps the `ZoneHeap` in an uninitialized state, if `zone` is null. + ASMJIT_API void reset(Zone* zone = nullptr) noexcept; + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + //! \internal + //! + //! Get the slot index to be used for `size`. Returns `true` if a valid slot + //! has been written to `slot` and `allocatedSize` has been filled with slot + //! exact size (`allocatedSize` can be equal or slightly greater than `size`). + static ASMJIT_INLINE bool _getSlotIndex(size_t size, uint32_t& slot) noexcept { + ASMJIT_ASSERT(size > 0); + if (size > kHiMaxSize) + return false; + + if (size <= kLoMaxSize) + slot = static_cast((size - 1) / kLoGranularity); + else + slot = static_cast((size - kLoMaxSize - 1) / kHiGranularity) + kLoCount; + + return true; + } + + //! \overload + static ASMJIT_INLINE bool _getSlotIndex(size_t size, uint32_t& slot, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(size > 0); + if (size > kHiMaxSize) + return false; + + if (size <= kLoMaxSize) { + slot = static_cast((size - 1) / kLoGranularity); + allocatedSize = Utils::alignTo(size, kLoGranularity); + } + else { + slot = static_cast((size - kLoMaxSize - 1) / kHiGranularity) + kLoCount; + allocatedSize = Utils::alignTo(size, kHiGranularity); + } + + return true; + } + + // -------------------------------------------------------------------------- + // [Alloc / Release] + // -------------------------------------------------------------------------- + + ASMJIT_API void* _alloc(size_t size, size_t& allocatedSize) noexcept; + ASMJIT_API void* _allocZeroed(size_t size, size_t& allocatedSize) noexcept; + ASMJIT_API void _releaseDynamic(void* p, size_t size) noexcept; + + //! Allocate `size` bytes of memory, ideally from an available pool. + //! + //! NOTE: `size` can't be zero, it will assert in debug mode in such case. + ASMJIT_INLINE void* alloc(size_t size) noexcept { + ASMJIT_ASSERT(isInitialized()); + size_t allocatedSize; + return _alloc(size, allocatedSize); + } + + //! Like `alloc(size)`, but provides a second argument `allocatedSize` that + //! provides a way to know how big the block returned actually is. This is + //! useful for containers to prevent growing too early. + ASMJIT_INLINE void* alloc(size_t size, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(isInitialized()); + return _alloc(size, allocatedSize); + } + + //! Like `alloc()`, but the return pointer is casted to `T*`. + template + ASMJIT_INLINE T* allocT(size_t size = sizeof(T)) noexcept { + return static_cast(alloc(size)); + } + + //! Like `alloc(size)`, but returns zeroed memory. + ASMJIT_INLINE void* allocZeroed(size_t size) noexcept { + ASMJIT_ASSERT(isInitialized()); + + size_t allocatedSize; + return _allocZeroed(size, allocatedSize); + } + + //! Like `alloc(size, allocatedSize)`, but returns zeroed memory. + ASMJIT_INLINE void* allocZeroed(size_t size, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(isInitialized()); + + return _allocZeroed(size, allocatedSize); + } + + //! Like `allocZeroed()`, but the return pointer is casted to `T*`. + template + ASMJIT_INLINE T* allocZeroedT(size_t size = sizeof(T)) noexcept { + return static_cast(allocZeroed(size)); + } + + //! Release the memory previously allocated by `alloc()`. The `size` argument + //! has to be the same as used to call `alloc()` or `allocatedSize` returned + //! by `alloc()`. + ASMJIT_INLINE void release(void* p, size_t size) noexcept { + ASMJIT_ASSERT(isInitialized()); + + ASMJIT_ASSERT(p != nullptr); + ASMJIT_ASSERT(size != 0); + + uint32_t slot; + if (_getSlotIndex(size, slot)) { + //printf("RELEASING %p of size %d (SLOT %u)\n", p, int(size), slot); + static_cast(p)->next = static_cast(_slots[slot]); + _slots[slot] = static_cast(p); + } + else { + _releaseDynamic(p, size); + } + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Zone* _zone; //!< Zone used to allocate memory that fits into slots. + Slot* _slots[kLoCount + kHiCount]; //!< Indexed slots containing released memory. + DynamicBlock* _dynamicBlocks; //!< Dynamic blocks for larger allocations (no slots). +}; + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_ZONEHEAP_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/x86.h b/components/shared/detours/asmjit/asmjit/src/asmjit/x86.h new file mode 100644 index 00000000..33ee1c76 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/x86.h @@ -0,0 +1,24 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_X86_H +#define _ASMJIT_X86_H + +// [Dependencies] +#include "./base.h" + +#include "./x86/x86assembler.h" +#include "./x86/x86builder.h" +#include "./x86/x86compiler.h" +#include "./x86/x86emitter.h" +#include "./x86/x86inst.h" +#include "./x86/x86logging.h" +#include "./x86/x86misc.h" +#include "./x86/x86operand.h" + +// [Guard] +#endif // _ASMJIT_X86_H diff --git a/components/shared/detours/asmjit/asmjit/src/asmjit/x86/x86assembler.cpp b/components/shared/detours/asmjit/asmjit/src/asmjit/x86/x86assembler.cpp new file mode 100644 index 00000000..2c1e9081 --- /dev/null +++ b/components/shared/detours/asmjit/asmjit/src/asmjit/x86/x86assembler.cpp @@ -0,0 +1,4505 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Guard] +#include "../asmjit_build.h" +#if defined(ASMJIT_BUILD_X86) + +// [Dependencies] +#include "../base/cpuinfo.h" +#include "../base/runtime.h" +#include "../base/utils.h" +#include "../base/vmem.h" +#include "../x86/x86assembler.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [FastUInt8] +// ============================================================================ + +#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 +typedef unsigned char FastUInt8; +#else +typedef unsigned int FastUInt8; +#endif + +// ============================================================================ +// [Constants] +// ============================================================================ + +//! \internal +//! +//! X86/X64 bytes used to encode important prefixes. +enum X86Byte { + //! 1-byte REX prefix mask. + kX86ByteRex = 0x40, + + //! 1-byte REX.W component. + kX86ByteRexW = 0x08, + + //! 2-byte VEX prefix: + //! - `[0]` - `0xC5`. + //! - `[1]` - `RvvvvLpp`. + kX86ByteVex2 = 0xC5, + + //! 3-byte VEX prefix. + //! - `[0]` - `0xC4`. + //! - `[1]` - `RXBmmmmm`. + //! - `[2]` - `WvvvvLpp`. + kX86ByteVex3 = 0xC4, + + //! 3-byte XOP prefix. + //! - `[0]` - `0x8F`. + //! - `[1]` - `RXBmmmmm`. + //! - `[2]` - `WvvvvLpp`. + kX86ByteXop3 = 0x8F, + + //! 4-byte EVEX prefix. + //! - `[0]` - `0x62`. + //! - `[1]` - Payload0 or `P[ 7: 0]` - `[R X B R' 0 0 m m]`. + //! - `[2]` - Payload1 or `P[15: 8]` - `[W v v v v 1 p p]`. + //! - `[3]` - Payload2 or `P[23:16]` - `[z L' L b V' a a a]`. + //! + //! Groups: + //! - `P[ 1: 0]` - OPCODE: EVEX.mmmmm, only lowest 2 bits [1:0] used. + //! - `P[ 3: 2]` - ______: Must be 0. + //! - `P[ 4]` - REG-ID: EVEX.R' - 5th bit of 'RRRRR'. + //! - `P[ 5]` - REG-ID: EVEX.B - 4th bit of 'BBBBB'. + //! - `P[ 6]` - REG-ID: EVEX.X - 5th bit of 'BBBBB' or 4th bit of 'XXXX' (with SIB). + //! - `P[ 7]` - REG-ID: EVEX.R - 4th bit of 'RRRRR'. + //! - `P[ 9: 8]` - OPCODE: EVEX.pp. + //! - `P[ 10]` - ______: Must be 1. + //! - `P[14:11]` - REG-ID: 4 bits of 'VVVV'. + //! - `P[ 15]` - OPCODE: EVEX.W. + //! - `P[18:16]` - REG-ID: K register k0...k7 (Merging/Zeroing Vector Ops). + //! - `P[ 19]` - REG-ID: 5th bit of 'VVVVV'. + //! - `P[ 20]` - OPCODE: Broadcast/Rounding Control/SAE bit. + //! - `P[22.21]` - OPCODE: Vector Length (L' and L) / Rounding Control. + //! - `P[ 23]` - OPCODE: Zeroing/Merging. + kX86ByteEvex = 0x62 +}; + +// AsmJit specific (used to encode VVVVV field in XOP/VEX/EVEX). +enum VexVVVVV { + kVexVVVVVShift = 7, + kVexVVVVVMask = 0x1F << kVexVVVVVShift +}; + +//! \internal +//! +//! Instruction 2-byte/3-byte opcode prefix definition. +struct X86OpCodeMM { + uint8_t len; + uint8_t data[3]; +}; + +//! \internal +//! +//! Mandatory prefixes used to encode legacy [66, F3, F2] or [9B] byte. +static const uint8_t x86OpCodePP[8] = { 0x00, 0x66, 0xF3, 0xF2, 0x00, 0x00, 0x00, 0x9B }; + +//! \internal +//! +//! Instruction 2-byte/3-byte opcode prefix data. +static const X86OpCodeMM x86OpCodeMM[] = { + { 0, { 0x00, 0x00, 0 } }, // #00 (0b0000). + { 1, { 0x0F, 0x00, 0 } }, // #01 (0b0001). + { 2, { 0x0F, 0x38, 0 } }, // #02 (0b0010). + { 2, { 0x0F, 0x3A, 0 } }, // #03 (0b0011). + { 2, { 0x0F, 0x01, 0 } }, // #04 (0b0100). + { 0, { 0x00, 0x00, 0 } }, // #05 (0b0101). + { 0, { 0x00, 0x00, 0 } }, // #06 (0b0110). + { 0, { 0x00, 0x00, 0 } }, // #07 (0b0111). + { 0, { 0x00, 0x00, 0 } }, // #08 (0b1000). + { 0, { 0x00, 0x00, 0 } }, // #09 (0b1001). + { 0, { 0x00, 0x00, 0 } }, // #0A (0b1010). + { 0, { 0x00, 0x00, 0 } }, // #0B (0b1011). + { 0, { 0x00, 0x00, 0 } }, // #0C (0b1100). + { 0, { 0x00, 0x00, 0 } }, // #0D (0b1101). + { 0, { 0x00, 0x00, 0 } }, // #0E (0b1110). + { 0, { 0x00, 0x00, 0 } } // #0F (0b1111). +}; + +static const uint8_t x86SegmentPrefix[8] = { 0x00, 0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65, 0x00 }; +static const uint8_t x86OpCodePushSeg[8] = { 0x00, 0x06, 0x0E, 0x16, 0x1E, 0xA0, 0xA8, 0x00 }; +static const uint8_t x86OpCodePopSeg[8] = { 0x00, 0x07, 0x00, 0x17, 0x1F, 0xA1, 0xA9, 0x00 }; + +// ============================================================================ +// [asmjit::X86MemInfo | X86VEXPrefix | X86LLByRegType | X86CDisp8Table] +// ============================================================================ + +#define TABLE(DEF, I) DEF((I*16) + 0), DEF((I*16) + 1), DEF((I*16) + 2), DEF((I*16) + 3), \ + DEF((I*16) + 4), DEF((I*16) + 5), DEF((I*16) + 6), DEF((I*16) + 7), \ + DEF((I*16) + 8), DEF((I*16) + 9), DEF((I*16) + 10), DEF((I*16) + 11), \ + DEF((I*16) + 12), DEF((I*16) + 13), DEF((I*16) + 14), DEF((I*16) + 15) + +//! \internal +//! +//! Memory operand's info bits. +//! +//! A lookup table that contains various information based on the BASE and INDEX +//! information of a memory operand. This is much better and safer than playing +//! with IFs in the code and can check for errors must faster and better. +enum X86MemInfo_Enum { + kX86MemInfo_0 = 0x00, + + kX86MemInfo_BaseGp = 0x01, //!< Has BASE reg, REX.B can be 1, compatible with REX.B byte. + kX86MemInfo_Index = 0x02, //!< Has INDEX reg, REX.X can be 1, compatible with REX.X byte. + + kX86MemInfo_BaseLabel = 0x10, //!< Base is Label. + kX86MemInfo_BaseRip = 0x20, //!< Base is RIP. + + kX86MemInfo_67H_X86 = 0x40, //!< Address-size override in 32-bit mode. + kX86MemInfo_67H_X64 = 0x80, //!< Address-size override in 64-bit mode. + kX86MemInfo_67H_Mask = 0xC0 //!< Contains all address-size override bits. +}; + +#define B(X) (((X) >> Mem::kMemBaseTypeShift ) & 0xF) +#define I(X) (((X) >> Mem::kMemIndexTypeShift) & 0xF) +#define X86MemInfo_T(X) \ + (/* BASE */ ((B(X) >= X86Reg::kRegGpw && B(X) <= X86Reg::kRegGpq ) ? kX86MemInfo_BaseGp : 0) | \ + ((B(X) == X86Reg::kRegRip ) ? kX86MemInfo_BaseRip : 0) | \ + ((B(X) == Label::kLabelTag ) ? kX86MemInfo_BaseLabel : 0) | \ + \ + /* IDX */ ((I(X) >= X86Reg::kRegGpw && I(X) <= X86Reg::kRegGpq ) ? kX86MemInfo_Index : \ + (I(X) >= X86Reg::kRegXmm && I(X) <= X86Reg::kRegZmm ) ? kX86MemInfo_Index : 0) | \ + \ + /* 67H */ ((B(X) == X86Reg::kRegGpw && I(X) == X86Reg::kRegNone) ? kX86MemInfo_67H_X86 : \ + (B(X) == X86Reg::kRegGpd && I(X) == X86Reg::kRegNone) ? kX86MemInfo_67H_X64 : \ + (B(X) == X86Reg::kRegNone && I(X) == X86Reg::kRegGpw ) ? kX86MemInfo_67H_X86 : \ + (B(X) == X86Reg::kRegNone && I(X) == X86Reg::kRegGpd ) ? kX86MemInfo_67H_X64 : \ + (B(X) == X86Reg::kRegGpw && I(X) == X86Reg::kRegGpw ) ? kX86MemInfo_67H_X86 : \ + (B(X) == X86Reg::kRegGpd && I(X) == X86Reg::kRegGpd ) ? kX86MemInfo_67H_X64 : \ + (B(X) == X86Reg::kRegGpw && I(X) == X86Reg::kRegXmm ) ? kX86MemInfo_67H_X86 : \ + (B(X) == X86Reg::kRegGpd && I(X) == X86Reg::kRegXmm ) ? kX86MemInfo_67H_X64 : \ + (B(X) == X86Reg::kRegGpw && I(X) == X86Reg::kRegYmm ) ? kX86MemInfo_67H_X86 : \ + (B(X) == X86Reg::kRegGpd && I(X) == X86Reg::kRegYmm ) ? kX86MemInfo_67H_X64 : \ + (B(X) == X86Reg::kRegGpw && I(X) == X86Reg::kRegZmm ) ? kX86MemInfo_67H_X86 : \ + (B(X) == X86Reg::kRegGpd && I(X) == X86Reg::kRegZmm ) ? kX86MemInfo_67H_X64 : \ + (B(X) == Label::kLabelTag && I(X) == X86Reg::kRegGpw ) ? kX86MemInfo_67H_X86 : \ + (B(X) == Label::kLabelTag && I(X) == X86Reg::kRegGpd ) ? kX86MemInfo_67H_X64 : 0) | 0x04 | 0x08) +// The result stored in the LUT is a combination of +// - 67H - Address override prefix - depends on BASE+INDEX register types and +// the target architecture. +// - REX - A possible combination of REX.[B|X|R|W] bits in REX prefix where +// REX.B and REX.X are possibly masked out, but REX.R and REX.W are +// kept as is. +static const uint8_t x86MemInfo[256] = { + TABLE(X86MemInfo_T, 0), TABLE(X86MemInfo_T, 1), + TABLE(X86MemInfo_T, 2), TABLE(X86MemInfo_T, 3), + TABLE(X86MemInfo_T, 4), TABLE(X86MemInfo_T, 5), + TABLE(X86MemInfo_T, 6), TABLE(X86MemInfo_T, 7), + TABLE(X86MemInfo_T, 8), TABLE(X86MemInfo_T, 9), + TABLE(X86MemInfo_T,10), TABLE(X86MemInfo_T,11), + TABLE(X86MemInfo_T,12), TABLE(X86MemInfo_T,13), + TABLE(X86MemInfo_T,14), TABLE(X86MemInfo_T,15) +}; +#undef X86MemInfo_T +#undef I +#undef B + +// VEX3 or XOP xor bits applied to the opcode before emitted. The index to this +// table is 'mmmmm' value, which contains all we need. This is only used by a +// 3 BYTE VEX and XOP prefixes, 2 BYTE VEX prefix is handled differently. The +// idea is to minimize the difference between VEX3 vs XOP when encoding VEX +// or XOP instruction. This should minimize the code required to emit such +// instructions and should also make it faster as we don't need any branch to +// decide between VEX3 vs XOP. +// +// ____ ___ +// [_OPCODE_|WvvvvLpp|RXBmmmmm|VEX3_XOP] +#define X86VEXPrefix_T(X) \ + ( (((X) & 0x08) ? kX86ByteXop3 : kX86ByteVex3) | (0xF << 19) | (0x7 << 13) ) +static const uint32_t x86VEXPrefix[16] = { TABLE(X86VEXPrefix_T, 0) }; +#undef X86VEXPrefix_T + +// Table that contains LL opcode field addressed by a register size / 16. It's +// used to propagate L.256 or L.512 when YMM or ZMM registers are used, +// respectively. +#define X86LLBySizeDiv16_T(X) \ + (((X) & (64 >> 4)) ? X86Inst::kOpCode_LL_512 : \ + ((X) & (32 >> 4)) ? X86Inst::kOpCode_LL_256 : 0 ) +static const uint32_t x86LLBySizeDiv16[16] = { TABLE(X86LLBySizeDiv16_T, 0) }; +#undef X86LLBySizeDiv16_T + +// Table that contains LL opcode field addressed by a register size / 16. It's +// used to propagate L.256 or L.512 when YMM or ZMM registers are used, +// respectively. +#define X86LLByRegType_T(X) \ + ((X) == X86Reg::kRegZmm ? X86Inst::kOpCode_LL_512 : \ + (X) == X86Reg::kRegYmm ? X86Inst::kOpCode_LL_256 : 0 ) +static const uint32_t x86LLByRegType[16] = { TABLE(X86LLByRegType_T, 0) }; +#undef X86LLByRegType_T + +// Table that contains a scale (shift left) based on 'TTWLL' field and +// the instruction's tuple-type (TT) field. The scale is then applied to +// the BASE-N stored in each opcode to calculate the final compressed +// displacement used by all EVEX encoded instructions. +#define TT(X) (((X) >> 3) << X86Inst::kOpCode_CDTT_Shift) +#define LL(X) (((X) >> 0) & 0x3) +#define W(X) (((X) >> 2) & 0x1) +#define X86CDisp8SHL_T(X) \ + ((TT(X) == X86Inst::kOpCode_CDTT_None ? ((LL(X)==0) ? 0 : (LL(X)==1) ? 0 : 0 ) : \ + TT(X) == X86Inst::kOpCode_CDTT_ByLL ? ((LL(X)==0) ? 0 : (LL(X)==1) ? 1 : 2 ) : \ + TT(X) == X86Inst::kOpCode_CDTT_T1W ? ((LL(X)==0) ? W(X) : (LL(X)==1) ? 1+W(X) : 2+W(X)) : \ + TT(X) == X86Inst::kOpCode_CDTT_DUP ? ((LL(X)==0) ? 0 : (LL(X)==1) ? 2 : 3 ) : 0 ) << X86Inst::kOpCode_CDSHL_Shift) +static const uint32_t x86CDisp8SHL[32] = { + TABLE(X86CDisp8SHL_T, 0), + TABLE(X86CDisp8SHL_T, 1) +}; +#undef X86CDisp8SHL_T +#undef W +#undef TT +#undef LL + +// Table that contains MOD byte of a 16-bit [BASE + disp] address. +// 0xFF == Invalid. +static const uint8_t x86Mod16BaseTable[8] = { + 0xFF, // AX -> Not encodable. + 0xFF, // CX -> Not encodable. + 0xFF, // DX -> Not encodable. + 0x07, // BX -> 111. + 0xFF, // SP -> Not encodable. + 0x06, // BP -> 110. + 0x04, // SI -> 100. + 0x05 // DI -> 101. +}; + +// Table that contains MOD byte of a 16-bit [BASE + INDEX + disp] combination. +// 0xFF == Invalid. +#define IS_BASE_INDEX(X, BASE, INDEX) (((X) >> 3) == X86Gp::kId##BASE && ((X) & 0x7) == X86Gp::kId##INDEX) +#define X86Mod16BaseIndexTable(X) \ + ((IS_BASE_INDEX(X, Bx, Si) || IS_BASE_INDEX(X, Si, Bx)) ? 0x00 : \ + (IS_BASE_INDEX(X, Bx, Di) || IS_BASE_INDEX(X, Di, Bx)) ? 0x01 : \ + (IS_BASE_INDEX(X, Bp, Si) || IS_BASE_INDEX(X, Si, Bp)) ? 0x02 : \ + (IS_BASE_INDEX(X, Bp, Di) || IS_BASE_INDEX(X, Di, Bp)) ? 0x03 : 0xFF) +static const uint8_t x86Mod16BaseIndexTable[64] = { + TABLE(X86Mod16BaseIndexTable, 0), + TABLE(X86Mod16BaseIndexTable, 1), + TABLE(X86Mod16BaseIndexTable, 2), + TABLE(X86Mod16BaseIndexTable, 3) +}; +#undef X86Mod16BaseIndexTable +#undef IS_BASE_INDEX + +#undef TABLE + +// ============================================================================ +// [asmjit::X86Assembler - Helpers] +// ============================================================================ + +static ASMJIT_INLINE bool x86IsJmpOrCall(uint32_t instId) noexcept { + return instId == X86Inst::kIdJmp || + instId == X86Inst::kIdCall; +} + +static ASMJIT_INLINE bool x86IsImplicitMem(const Operand_& op, uint32_t base) noexcept { + return op.isMem() && op.as().getBaseId() == base; +} + +static ASMJIT_INLINE int64_t x86SignExtend32To64(int64_t imm) noexcept { + return static_cast(static_cast(imm & 0xFFFFFFFF)); +} + +static ASMJIT_INLINE uint32_t x86OpCodeLByVMem(const Operand_& op) noexcept { + return x86LLByRegType[static_cast(op).getIndexType()]; +} + +static ASMJIT_INLINE uint32_t x86OpCodeLBySize(uint32_t size) noexcept { + return x86LLBySizeDiv16[size / 16]; +} + +//! Combine `regId` and `vvvvvId` into a single value (used by AVX and AVX-512). +static ASMJIT_INLINE uint32_t x86PackRegAndVvvvv(uint32_t regId, uint32_t vvvvvId) noexcept { + return regId + (vvvvvId << kVexVVVVVShift); +} + +//! Get `O` field of `opCode`. +static ASMJIT_INLINE uint32_t x86ExtractO(uint32_t opCode) noexcept { + return (opCode >> X86Inst::kOpCode_O_Shift) & 0x07; +} + +static ASMJIT_INLINE uint32_t x86ExtractREX(uint32_t opCode, uint32_t options) noexcept { + // kOpCode_REX was designed in a way that when shifted there will be no bytes + // set except REX.[B|X|R|W]. The returned value forms a real REX prefix byte. + // This case is tested by `X86Inst.cpp`. + return (opCode | options) >> X86Inst::kOpCode_REX_Shift; +} + +static ASMJIT_INLINE uint32_t x86ExtractLLMM(uint32_t opCode, uint32_t options) noexcept { + uint32_t x = opCode & (X86Inst::kOpCode_LL_Mask | X86Inst::kOpCode_MM_Mask); + uint32_t y = options & X86Inst::kOptionVex3; + return (x | y) >> X86Inst::kOpCode_MM_Shift; +} + +static ASMJIT_INLINE uint32_t x86ExtractLLPPMM(uint32_t opCode, uint32_t options) noexcept { + uint32_t x = opCode & (X86Inst::kOpCode_LL_Mask | X86Inst::kOpCode_MM_Mask); + uint32_t y = options & X86Inst::kOptionVex3; + return (x | y) >> X86Inst::kOpCode_MM_Shift; +} + +//! Encode MOD byte. +static ASMJIT_INLINE uint32_t x86EncodeMod(uint32_t m, uint32_t o, uint32_t rm) noexcept { + ASMJIT_ASSERT(m <= 3); + ASMJIT_ASSERT(o <= 7); + ASMJIT_ASSERT(rm <= 7); + return (m << 6) + (o << 3) + rm; +} + +//! Encode SIB byte. +static ASMJIT_INLINE uint32_t x86EncodeSib(uint32_t s, uint32_t i, uint32_t b) noexcept { + ASMJIT_ASSERT(s <= 3); + ASMJIT_ASSERT(i <= 7); + ASMJIT_ASSERT(b <= 7); + return (s << 6) + (i << 3) + b; +} + +// ============================================================================ +// [asmjit::X86Assembler - Macros] +// ============================================================================ + +#define EMIT_BYTE(VAL) \ + do { \ + cursor[0] = static_cast((VAL) & 0xFFU); \ + cursor += 1; \ + } while (0) + +#define EMIT_WORD(VAL) \ + do { \ + Utils::writeU16uLE(cursor, \ + static_cast((VAL) & 0xFFFFU)); \ + cursor += 2; \ + } while (0) + +#define EMIT_DWORD(VAL) \ + do { \ + Utils::writeU32uLE(cursor, \ + static_cast((VAL) & 0xFFFFFFFFU)); \ + cursor += 4; \ + } while (0) + +// ============================================================================ +// [asmjit::X86Assembler - Construction / Destruction] +// ============================================================================ + +X86Assembler::X86Assembler(CodeHolder* code) noexcept : Assembler() { + if (code) + code->attach(this); +} +X86Assembler::~X86Assembler() noexcept {} + +// ============================================================================ +// [asmjit::X86Assembler - Events] +// ============================================================================ + +Error X86Assembler::onAttach(CodeHolder* code) noexcept { + uint32_t archType = code->getArchType(); + if (!ArchInfo::isX86Family(archType)) + return DebugUtils::errored(kErrorInvalidArch); + + ASMJIT_PROPAGATE(Base::onAttach(code)); + + if (archType == ArchInfo::kTypeX86) { + // 32 bit architecture - X86. + _setAddressOverrideMask(kX86MemInfo_67H_X86); + _globalOptions |= X86Inst::_kOptionInvalidRex; + _nativeGpArray = x86OpData.gpd; + } + else { + // 64 bit architecture - X64 or X32. + _setAddressOverrideMask(kX86MemInfo_67H_X64); + _nativeGpArray = x86OpData.gpq; + } + + _nativeGpReg = _nativeGpArray[0]; + return kErrorOk; +} + +Error X86Assembler::onDetach(CodeHolder* code) noexcept { + return Base::onDetach(code); +} + +// ============================================================================ +// [asmjit::X86Assembler - Align] +// ============================================================================ + +Error X86Assembler::align(uint32_t mode, uint32_t alignment) { +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) + _code->_logger->logf("%s.align %u\n", _code->_logger->getIndentation(), alignment); +#endif // !ASMJIT_DISABLE_LOGGING + + if (mode > kAlignZero) + return setLastError(DebugUtils::errored(kErrorInvalidArgument)); + + if (alignment <= 1) + return kErrorOk; + + if (!Utils::isPowerOf2(alignment) || alignment > 64) + return setLastError(DebugUtils::errored(kErrorInvalidArgument)); + + uint32_t i = static_cast(Utils::alignDiff(getOffset(), alignment)); + if (i == 0) + return kErrorOk; + + if (getRemainingSpace() < i) { + Error err = _code->growBuffer(&_section->_buffer, i); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + } + + uint8_t* cursor = _bufferPtr; + uint8_t pattern = 0x00; + + switch (mode) { + case kAlignCode: { + if (_globalHints & kHintOptimizedAlign) { + // Intel 64 and IA-32 Architectures Software Developer's Manual - Volume 2B (NOP). + enum { kMaxNopSize = 9 }; + + static const uint8_t nopData[kMaxNopSize][kMaxNopSize] = { + { 0x90 }, + { 0x66, 0x90 }, + { 0x0F, 0x1F, 0x00 }, + { 0x0F, 0x1F, 0x40, 0x00 }, + { 0x0F, 0x1F, 0x44, 0x00, 0x00 }, + { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 }, + { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 }, + { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 } + }; + + do { + uint32_t n = Utils::iMin(i, kMaxNopSize); + const uint8_t* src = nopData[n - 1]; + + i -= n; + do { + EMIT_BYTE(*src++); + } while (--n); + } while (i); + } + + pattern = 0x90; + break; + } + + case kAlignData: pattern = 0xCC; break; + case kAlignZero: break; // Pattern already set to zero. + } + + while (i) { + EMIT_BYTE(pattern); + i--; + } + + _bufferPtr = cursor; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::X86Assembler - Emit Helpers] +// ============================================================================ + +#if !defined(ASMJIT_DISABLE_LOGGING) +static void X86Assembler_logInstruction(X86Assembler* self, + uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, + uint32_t relSize, uint32_t imLen, uint8_t* afterCursor) { + + Logger* logger = self->_code->getLogger(); + ASMJIT_ASSERT(logger != nullptr); + ASMJIT_ASSERT(options & CodeEmitter::kOptionLoggingEnabled); + + StringBuilderTmp<256> sb; + uint32_t logOptions = logger->getOptions(); + + uint8_t* beforeCursor = self->_bufferPtr; + intptr_t emittedSize = (intptr_t)(afterCursor - beforeCursor); + + sb.appendString(logger->getIndentation()); + + Operand_ opArray[6]; + opArray[0].copyFrom(o0); + opArray[1].copyFrom(o1); + opArray[2].copyFrom(o2); + opArray[3].copyFrom(o3); + opArray[4].copyFrom(self->_op4); + opArray[5].copyFrom(self->_op5); + if (!(options & CodeEmitter::kOptionOp4)) opArray[4].reset(); + if (!(options & CodeEmitter::kOptionOp5)) opArray[5].reset(); + + self->_formatter.formatInstruction(sb, logOptions, instId, options, self->_opExtra, opArray, 6); + + if ((logOptions & Logger::kOptionBinaryForm) != 0) + LogUtil::formatLine(sb, self->_bufferPtr, emittedSize, relSize, imLen, self->getInlineComment()); + else + LogUtil::formatLine(sb, nullptr, kInvalidIndex, 0, 0, self->getInlineComment()); + + logger->log(sb.getData(), sb.getLength()); +} + +static Error X86Assembler_failedInstruction( + X86Assembler* self, + uint32_t err, + uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) { + + StringBuilderTmp<256> sb; + sb.appendString(DebugUtils::errorAsString(err)); + sb.appendString(": "); + + Operand_ opArray[6]; + opArray[0].copyFrom(o0); + opArray[1].copyFrom(o1); + opArray[2].copyFrom(o2); + opArray[3].copyFrom(o3); + opArray[4].copyFrom(self->_op4); + opArray[5].copyFrom(self->_op5); + if (!(options & CodeEmitter::kOptionOp4)) opArray[4].reset(); + if (!(options & CodeEmitter::kOptionOp5)) opArray[5].reset(); + + self->_formatter.formatInstruction(sb, 0, instId, options, self->_opExtra, opArray, 6); + + self->resetOptions(); + self->resetInlineComment(); + + return self->setLastError(err, sb.getData()); +} +#else +static ASMJIT_INLINE Error X86Assembler_failedInstruction( + X86Assembler* self, + uint32_t err, + uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) { + + self->resetOptions(); + self->resetInlineComment(); + + return self->setLastError(err); +} +#endif + +#if !defined(ASMJIT_DISABLE_VALIDATION) +static Error X86Assembler_validateInstruction( + X86Assembler* self, + uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) { + + Operand_ opArray[6]; + opArray[0].copyFrom(o0); + opArray[1].copyFrom(o1); + opArray[2].copyFrom(o2); + opArray[3].copyFrom(o3); + opArray[4].copyFrom(self->_op4); + opArray[5].copyFrom(self->_op5); + if (!(options & CodeEmitter::kOptionOp4)) opArray[4].reset(); + if (!(options & CodeEmitter::kOptionOp5)) opArray[5].reset(); + + Error err = X86Inst::validate(self->getArchType(), instId, options, self->getOpExtra(), opArray, 6); + if (err) return X86Assembler_failedInstruction(self, err, instId, options, o0, o1, o2, o3); + + return kErrorOk; +} +#endif // !ASMJIT_DISABLE_VALIDATION + +// ============================================================================ +// [asmjit::X86Assembler - Emit] +// ============================================================================ + +#define ADD_66H_P(EXP) \ + do { \ + opCode |= (static_cast(EXP) << X86Inst::kOpCode_PP_Shift); \ + } while (0) + +#define ADD_66H_P_BY_SIZE(SIZE) \ + do { \ + opCode |= (static_cast((SIZE) & 0x02)) \ + << (X86Inst::kOpCode_PP_Shift - 1); \ + } while (0) + +#define ADD_REX_W(EXP) \ + do { \ + if (EXP) \ + opCode |= X86Inst::kOpCode_W; \ + } while (0) + +#define ADD_REX_W_BY_SIZE(SIZE) \ + do { \ + if ((SIZE) == 8) \ + opCode |= X86Inst::kOpCode_W; \ + } while (0) + +#define ADD_PREFIX_BY_SIZE(SIZE) \ + do { \ + ADD_66H_P_BY_SIZE(SIZE); \ + ADD_REX_W_BY_SIZE(SIZE); \ + } while (0) + +#define ADD_VEX_W(EXP) \ + do { \ + opCode |= static_cast(EXP) << X86Inst::kOpCode_W_Shift; \ + } while (0) + +#define EMIT_PP(OPCODE) \ + do { \ + uint32_t ppIndex = \ + ((OPCODE ) >> X86Inst::kOpCode_PP_Shift) & \ + (X86Inst::kOpCode_PP_FPUMask >> X86Inst::kOpCode_PP_Shift) ; \ + uint8_t ppCode = x86OpCodePP[ppIndex]; \ + \ + cursor[0] = ppCode; \ + cursor += ppIndex != 0; \ + } while (0) + +#define EMIT_MM_OP(OPCODE) \ + do { \ + uint32_t op = OPCODE & (0x00FF | X86Inst::kOpCode_MM_Mask); \ + \ + uint32_t mmIndex = op >> X86Inst::kOpCode_MM_Shift; \ + const X86OpCodeMM& mmCode = x86OpCodeMM[mmIndex]; \ + \ + if (mmIndex) { \ + cursor[0] = mmCode.data[0]; \ + cursor[1] = mmCode.data[1]; \ + cursor += mmCode.len; \ + } \ + \ + EMIT_BYTE(op); \ + } while (0) + +// If the operand is BPL|SPL|SIL|DIL|R8B-15B +// - Force REX prefix +// If the operand is AH|BH|CH|DH +// - patch its index from 0..3 to 4..7 as encoded by X86. +// - Disallow REX prefix. +#define FIXUP_GPB(REG_OP, REG_ID, ...) \ + do { \ + if (static_cast(REG_OP).isGpbLo()) { \ + options |= (REG_ID >= 4) ? X86Inst::kOptionRex : 0; \ + } \ + else { \ + ASMJIT_ASSERT(X86Reg::isGpbHi(REG_OP)); \ + options |= X86Inst::_kOptionInvalidRex; \ + REG_ID += 4; \ + } \ + } while (0) + +#define ENC_OPS1(OP0) ((Operand::kOp##OP0)) +#define ENC_OPS2(OP0, OP1) ((Operand::kOp##OP0) + ((Operand::kOp##OP1) << 3)) +#define ENC_OPS3(OP0, OP1, OP2) ((Operand::kOp##OP0) + ((Operand::kOp##OP1) << 3) + ((Operand::kOp##OP2) << 6)) +#define ENC_OPS4(OP0, OP1, OP2, OP3) ((Operand::kOp##OP0) + ((Operand::kOp##OP1) << 3) + ((Operand::kOp##OP2) << 6) + ((Operand::kOp##OP3) << 9)) +#define ENC_OPS5(OP0, OP1, OP2, OP3, OP4) ((Operand::kOp##OP0) + ((Operand::kOp##OP1) << 3) + ((Operand::kOp##OP2) << 6) + ((Operand::kOp##OP3) << 9) + ((Operand::kOp##OP4) << 12)) + +Error X86Assembler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) { + Error err; + + const Operand_* rmRel; // Memory operand or operand that holds Label|Imm. + uint32_t rmInfo; // Memory operand's info based on x86MemInfo. + uint32_t rbReg; // Memory base or modRM register. + uint32_t rxReg; // Memory index register. + uint32_t opReg; // ModR/M opcode or register id. + uint32_t opCode; // Instruction opcode. + + LabelEntry* label; // Label entry. + RelocEntry* re = nullptr; // Relocation entry. + int32_t relOffset; // Relative offset + FastUInt8 relSize = 0; // Relative size. + + int64_t imVal; // Immediate value (must be 64-bit). + FastUInt8 imLen = 0; // Immediate length. + + const uint32_t kSHR_W_PP = X86Inst::kOpCode_PP_Shift - 16; + const uint32_t kSHR_W_EW = X86Inst::kOpCode_EW_Shift - 23; + + uint8_t* cursor = _bufferPtr; + uint32_t options = static_cast(instId >= X86Inst::_kIdCount) | + static_cast((size_t)(_bufferEnd - cursor) < 16) | + getGlobalOptions() | getOptions(); + + const X86Inst* instData = X86InstDB::instData + instId; + const X86Inst::CommonData* commonData; + + // Handle failure and rare cases first. + const uint32_t kErrorsAndSpecialCases = + CodeEmitter::kOptionMaybeFailureCase | // Error / Buffer check. + CodeEmitter::kOptionStrictValidation | // Strict validation. + X86Inst::kOptionRep | // REP/REPZ prefix. + X86Inst::kOptionRepnz | // REP/REPNZ prefix. + X86Inst::kOptionLock ; // LOCK prefix. + + // Signature of the first 3 operands. Instructions that use more operands + // create `isign4` and `isign5` on-the-fly. + uint32_t isign3 = o0.getOp() + (o1.getOp() << 3) + (o2.getOp() << 6); + + if (ASMJIT_UNLIKELY(options & kErrorsAndSpecialCases)) { + // Don't do anything if we are in error state. + if (_lastError) return _lastError; + + if (options & CodeEmitter::kOptionMaybeFailureCase) { + // Unknown instruction. + if (instId >= X86Inst::_kIdCount) + goto InvalidArgument; + + // Grow request, happens rarely. + if ((size_t)(_bufferEnd - cursor) < 16) { + err = _code->growBuffer(&_section->_buffer, 16); + if (ASMJIT_UNLIKELY(err)) goto Failed; + + cursor = _bufferPtr; + } + } + + // Strict validation. +#if !defined(ASMJIT_DISABLE_VALIDATION) + // It calls `X86Assembler_failedInstruction` on error, no need to call it. + if (options & CodeEmitter::kOptionStrictValidation) + ASMJIT_PROPAGATE(X86Assembler_validateInstruction(this, instId, options, o0, o1, o2, o3)); +#endif // !ASMJIT_DISABLE_VALIDATION + + uint32_t instFlags = instData->getFlags(); + + // LOCK prefix. + if (options & X86Inst::kOptionLock) { + if (ASMJIT_UNLIKELY(!(instFlags & X86Inst::kInstFlagLock))) + goto InvalidInstruction; + EMIT_BYTE(0xF0); + } + + // REP / REPNZ prefix. + if (options & (X86Inst::kOptionRep | X86Inst::kOptionRepnz)) { + if (ASMJIT_UNLIKELY(!(instFlags & (X86Inst::kInstFlagRep | X86Inst::kInstFlagRepnz)))) + goto InvalidInstruction; + + if (!_opExtra.isNone() && ASMJIT_UNLIKELY(!X86Reg::isGp(_opExtra, X86Gp::kIdCx))) + goto InvalidInstruction; + + EMIT_BYTE((options & X86Inst::kOptionRepnz) ? 0xF2 : 0xF3); + } + } + + // -------------------------------------------------------------------------- + // [Encoding Scope] + // -------------------------------------------------------------------------- + + opCode = instData->getMainOpCode(); + opReg = x86ExtractO(opCode); + commonData = &instData->getCommonData(); + + switch (instData->getEncodingType()) { + case X86Inst::kEncodingNone: + goto EmitDone; + + // ------------------------------------------------------------------------ + // [X86] + // ------------------------------------------------------------------------ + + case X86Inst::kEncodingX86Op: + goto EmitX86Op; + + case X86Inst::kEncodingX86Op_O: + rbReg = 0; + goto EmitX86R; + + case X86Inst::kEncodingX86Op_xAX: + if (isign3 == 0) + goto EmitX86Op; + + if (isign3 == ENC_OPS1(Reg) && o0.getId() == X86Gp::kIdAx) + goto EmitX86Op; + break; + + case X86Inst::kEncodingX86Op_xDX_xAX: + if (isign3 == 0) + goto EmitX86Op; + + if (isign3 == ENC_OPS2(Reg, Reg) && o0.getId() == X86Gp::kIdDx && + o1.getId() == X86Gp::kIdAx) + goto EmitX86Op; + break; + + case X86Inst::kEncodingX86Op_ZAX: + if (isign3 == 0) + goto EmitX86Op; + + rmRel = &o0; + if (isign3 == ENC_OPS1(Mem) && x86IsImplicitMem(o0, X86Gp::kIdAx)) + goto EmitX86OpImplicitMem; + + break; + + case X86Inst::kEncodingX86I_xAX: + // Implicit form. + if (isign3 == ENC_OPS1(Imm)) { + imVal = o0.as().getUInt8(); + imLen = 1; + goto EmitX86Op; + } + + // Explicit form. + if (isign3 == ENC_OPS2(Reg, Imm) && o0.getId() == X86Gp::kIdAx) { + imVal = o1.as().getUInt8(); + imLen = 1; + goto EmitX86Op; + } + break; + + case X86Inst::kEncodingX86M: + rbReg = o0.getId(); + ADD_PREFIX_BY_SIZE(o0.getSize()); + + if (isign3 == ENC_OPS1(Reg)) + goto EmitX86R; + + rmRel = &o0; + if (isign3 == ENC_OPS1(Mem)) + goto EmitX86M; + break; + + case X86Inst::kEncodingX86M_GPB_MulDiv: +CaseX86M_GPB_MulDiv: + // Explicit form? + if (isign3 > 0x7) { + // [AX] <- [AX] div|mul r8. + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (ASMJIT_UNLIKELY(!X86Reg::isGpw(o0, X86Gp::kIdAx) || !X86Reg::isGpb(o1))) + goto InvalidInstruction; + + rbReg = o1.getId(); + FIXUP_GPB(o1, rbReg); + goto EmitX86R; + } + + // [AX] <- [AX] div|mul m8. + if (isign3 == ENC_OPS2(Reg, Mem)) { + if (ASMJIT_UNLIKELY(!X86Reg::isGpw(o0, X86Gp::kIdAx))) + goto InvalidInstruction; + + rmRel = &o1; + goto EmitX86M; + } + + // [?DX:?AX] <- [?DX:?AX] div|mul r16|r32|r64 + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + if (ASMJIT_UNLIKELY(o0.getSize() != o1.getSize())) + goto InvalidInstruction; + rbReg = o2.getId(); + + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86R; + } + + // [?DX:?AX] <- [?DX:?AX] div|mul m16|m32|m64 + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + if (ASMJIT_UNLIKELY(o0.getSize() != o1.getSize())) + goto InvalidInstruction; + rmRel = &o2; + + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86M; + } + + goto InvalidInstruction; + } + + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingX86M_GPB: + if (isign3 == ENC_OPS1(Reg)) { + rbReg = o0.getId(); + if (o0.getSize() == 1) { + FIXUP_GPB(o0, rbReg); + goto EmitX86R; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS1(Mem)) { + if (ASMJIT_UNLIKELY(o0.getSize() == 0)) + goto AmbiguousOperandSize; + rmRel = &o0; + + opCode += o0.getSize() != 1; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86M_Only: + if (isign3 == ENC_OPS1(Mem)) { + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Rm: + ADD_PREFIX_BY_SIZE(o0.getSize()); + + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Arith: + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (o0.getSize() != o1.getSize()) + goto OperandSizeMismatch; + + opReg = o0.getId(); + rbReg = o1.getId(); + + if (o0.getSize() == 1) { + opCode += 2; + FIXUP_GPB(o0, opReg); + FIXUP_GPB(o1, rbReg); + + if (!(options & X86Inst::kOptionModMR)) + goto EmitX86R; + + opCode -= 2; + Utils::swap(opReg, rbReg); + goto EmitX86R; + } + else { + opCode += 3; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + if (!(options & X86Inst::kOptionModMR)) + goto EmitX86R; + + opCode -= 2; + Utils::swap(opReg, rbReg); + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, opReg); + opCode += 2; + goto EmitX86M; + } + else { + opCode += 3; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86M; + } + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + + if (o1.getSize() == 1) { + FIXUP_GPB(o1, opReg); + goto EmitX86M; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o1.getSize()); + goto EmitX86M; + } + } + + // The remaining instructions use 0x80 opcode. + opCode = 0x80; + + if (isign3 == ENC_OPS2(Reg, Imm)) { + uint32_t regSize = o0.getSize(); + + rbReg = o0.getId(); + imVal = static_cast(o1).getInt64(); + + if (regSize == 1) { + FIXUP_GPB(o0, rbReg); + imLen = 1; + } + else { + if (regSize == 2) { + ADD_66H_P(1); + } + else if (regSize == 4) { + // Sign extend so isInt8 returns the right result. + imVal = x86SignExtend32To64(imVal); + } + else if (regSize == 8) { + // In 64-bit mode it's not possible to use 64-bit immediate. + if (Utils::isUInt32(imVal)) { + // Zero-extend `and` by using a 32-bit GPD destination instead of a 64-bit GPQ. + if (instId == X86Inst::kIdAnd) + regSize = 4; + else if (!Utils::isInt32(imVal)) + goto InvalidInstruction; + } + ADD_REX_W_BY_SIZE(regSize); + } + + imLen = Utils::iMin(regSize, 4); + if (Utils::isInt8(imVal) && !(options & X86Inst::kOptionLongForm)) + imLen = 1; + } + + // Alternate Form - AL, AX, EAX, RAX. + if (rbReg == 0 && (regSize == 1 || imLen != 1) && !(options & X86Inst::kOptionLongForm)) { + opCode &= X86Inst::kOpCode_PP_66 | X86Inst::kOpCode_W; + opCode |= ((opReg << 3) | (0x04 + (regSize != 1))); + imLen = Utils::iMin(regSize, 4); + goto EmitX86Op; + } + + opCode += regSize != 1 ? (imLen != 1 ? 1 : 3) : 0; + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Mem, Imm)) { + uint32_t memSize = o0.getSize(); + + if (ASMJIT_UNLIKELY(memSize == 0)) + goto AmbiguousOperandSize; + + imVal = static_cast(o1).getInt64(); + imLen = Utils::iMin(memSize, 4); + + // Sign extend so isInt8 returns the right result. + if (memSize == 4) + imVal = x86SignExtend32To64(imVal); + + if (Utils::isInt8(imVal) && !(options & X86Inst::kOptionLongForm)) + imLen = 1; + + opCode += memSize != 1 ? (imLen != 1 ? 1 : 3) : 0; + ADD_PREFIX_BY_SIZE(memSize); + + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Bswap: + if (isign3 == ENC_OPS1(Reg)) { + if (ASMJIT_UNLIKELY(o0.getSize() < 4)) + goto InvalidInstruction; + + opReg = o0.getId(); + ADD_REX_W_BY_SIZE(o0.getSize()); + goto EmitX86OpReg; + } + break; + + case X86Inst::kEncodingX86Bt: + if (isign3 == ENC_OPS2(Reg, Reg)) { + ADD_PREFIX_BY_SIZE(o1.getSize()); + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + ADD_PREFIX_BY_SIZE(o1.getSize()); + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + + // The remaining instructions use the secondary opcode/r. + imVal = static_cast(o1).getInt64(); + imLen = 1; + + opCode = commonData->getAltOpCode(); + opReg = x86ExtractO(opCode); + ADD_PREFIX_BY_SIZE(o0.getSize()); + + if (isign3 == ENC_OPS2(Reg, Imm)) { + rbReg = o0.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Mem, Imm)) { + if (ASMJIT_UNLIKELY(o0.getSize() == 0)) + goto AmbiguousOperandSize; + + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Call: + if (isign3 == ENC_OPS1(Reg)) { + rbReg = o0.getId(); + goto EmitX86R; + } + + rmRel = &o0; + if (isign3 == ENC_OPS1(Mem)) + goto EmitX86M; + + // Call with 32-bit displacement use 0xE8 opcode. Call with 8-bit + // displacement is not encodable so the alternative opcode field + // in X86DB must be zero. + opCode = 0xE8; + goto EmitJmpCall; + + case X86Inst::kEncodingX86Cmpxchg: { + // Convert explicit to implicit. + if (isign3 & (0x7 << 6)) { + if (!X86Reg::isGp(o2) || o2.getId() != X86Gp::kIdAx) + goto InvalidInstruction; + isign3 &= 0x3F; + } + + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (o0.getSize() != o1.getSize()) + goto OperandSizeMismatch; + + rbReg = o0.getId(); + opReg = o1.getId(); + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, rbReg); + FIXUP_GPB(o1, opReg); + goto EmitX86R; + } + else { + ADD_PREFIX_BY_SIZE(o0.getSize()); + opCode++; + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + + if (o1.getSize() == 1) { + FIXUP_GPB(o0, opReg); + goto EmitX86M; + } + else { + ADD_PREFIX_BY_SIZE(o1.getSize()); + opCode++; + goto EmitX86M; + } + } + break; + } + + case X86Inst::kEncodingX86Crc: + opReg = o0.getId(); + + if (isign3 == ENC_OPS2(Reg, Reg)) { + rbReg = o1.getId(); + if (o1.getSize() == 1) { + FIXUP_GPB(o1, rbReg); + goto EmitX86R; + } + else { + // This seems to be the only exception of encoding 66F2 PP prefix. + if (o1.getSize() == 2) EMIT_BYTE(0x66); + + opCode++; + ADD_REX_W_BY_SIZE(o1.getSize()); + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + rmRel = &o1; + if (o1.getSize() == 0) + goto AmbiguousOperandSize; + + // This seems to be the only exception of encoding 66F2 PP prefix. + if (o1.getSize() == 2) EMIT_BYTE(0x66); + + opCode += o1.getSize() != 1; + ADD_REX_W_BY_SIZE(o1.getSize()); + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Enter: + if (isign3 == ENC_OPS2(Imm, Imm)) { + imVal = (static_cast(static_cast(o1).getUInt16()) << 0) | + (static_cast(static_cast(o0).getUInt8()) << 16) ; + imLen = 3; + goto EmitX86Op; + } + break; + + case X86Inst::kEncodingX86Imul: + // First process all forms distinct of `kEncodingX86M_OptB_MulDiv`. + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opCode = 0x6B; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (!Utils::isInt8(imVal) || (options & X86Inst::kOptionLongForm)) { + opCode -= 2; + imLen = o0.getSize() == 2 ? 2 : 4; + } + + opReg = o0.getId(); + rbReg = o1.getId(); + + goto EmitX86R; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + opCode = 0x6B; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + // Sign extend so isInt8 returns the right result. + if (o0.getSize() == 4) + imVal = x86SignExtend32To64(imVal); + + if (!Utils::isInt8(imVal) || (options & X86Inst::kOptionLongForm)) { + opCode -= 2; + imLen = o0.getSize() == 2 ? 2 : 4; + } + + opReg = o0.getId(); + rmRel = &o1; + + goto EmitX86M; + } + + if (isign3 == ENC_OPS2(Reg, Reg)) { + // Must be explicit 'ax, r8' form. + if (o1.getSize() == 1) + goto CaseX86M_GPB_MulDiv; + + if (o0.getSize() != o1.getSize()) + goto OperandSizeMismatch; + + opReg = o0.getId(); + rbReg = o1.getId(); + + opCode = X86Inst::kOpCode_MM_0F | 0xAF; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + // Must be explicit 'ax, m8' form. + if (o1.getSize() == 1) + goto CaseX86M_GPB_MulDiv; + + opReg = o0.getId(); + rmRel = &o1; + + opCode = X86Inst::kOpCode_MM_0F | 0xAF; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86M; + } + + // Shorthand to imul 'reg, reg, imm'. + if (isign3 == ENC_OPS2(Reg, Imm)) { + opCode = 0x6B; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + imVal = static_cast(o1).getInt64(); + imLen = 1; + + // Sign extend so isInt8 returns the right result. + if (o0.getSize() == 4) + imVal = x86SignExtend32To64(imVal); + + if (!Utils::isInt8(imVal) || (options & X86Inst::kOptionLongForm)) { + opCode -= 2; + imLen = o0.getSize() == 2 ? 2 : 4; + } + + opReg = rbReg = o0.getId(); + goto EmitX86R; + } + + // Try implicit form. + goto CaseX86M_GPB_MulDiv; + + case X86Inst::kEncodingX86In: + if (isign3 == ENC_OPS2(Reg, Imm)) { + if (ASMJIT_UNLIKELY(o0.getId() != X86Gp::kIdAx)) + goto InvalidInstruction; + + imVal = o1.as().getUInt8(); + imLen = 1; + + opCode = commonData->getAltOpCode() + (o0.getSize() != 1); + ADD_66H_P_BY_SIZE(o0.getSize()); + goto EmitX86Op; + } + + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (ASMJIT_UNLIKELY(o0.getId() != X86Gp::kIdAx || o1.getId() != X86Gp::kIdDx)) + goto InvalidInstruction; + + opCode += o0.getSize() != 1; + ADD_66H_P_BY_SIZE(o0.getSize()); + goto EmitX86Op; + } + break; + + case X86Inst::kEncodingX86Ins: + if (isign3 == ENC_OPS2(Mem, Reg)) { + if (ASMJIT_UNLIKELY(!x86IsImplicitMem(o0, X86Gp::kIdDi) || o1.getId() != X86Gp::kIdDx)) + goto InvalidInstruction; + + uint32_t size = o0.getSize(); + if (ASMJIT_UNLIKELY(size == 0)) + goto AmbiguousOperandSize; + + rmRel = &o0; + opCode += (size != 1); + + ADD_66H_P_BY_SIZE(size); + goto EmitX86OpImplicitMem; + } + break; + + case X86Inst::kEncodingX86IncDec: + if (isign3 == ENC_OPS1(Reg)) { + rbReg = o0.getId(); + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, rbReg); + goto EmitX86R; + } + + if (is32Bit()) { + // INC r16|r32 is only encodable in 32-bit mode (collides with REX). + opCode = commonData->getAltOpCode() + (rbReg & 0x07); + ADD_66H_P_BY_SIZE(o0.getSize()); + goto EmitX86Op; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS1(Mem)) { + rmRel = &o0; + opCode += o0.getSize() != 1; + + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Int: + if (isign3 == ENC_OPS1(Imm)) { + imVal = static_cast(o0).getInt64(); + imLen = 1; + goto EmitX86Op; + } + break; + + case X86Inst::kEncodingX86Jcc: + if (_globalHints & CodeEmitter::kHintPredictedJumps) { + if (options & X86Inst::kOptionTaken) + EMIT_BYTE(0x3E); + if (options & X86Inst::kOptionNotTaken) + EMIT_BYTE(0x2E); + } + + rmRel = &o0; + goto EmitJmpCall; + + case X86Inst::kEncodingX86JecxzLoop: + rmRel = &o0; + // Explicit jecxz|loop [r|e]cx, dst + if (o0.isReg()) { + if (ASMJIT_UNLIKELY(!X86Reg::isGp(o0, X86Gp::kIdCx))) + goto InvalidInstruction; + + if ((is32Bit() && o0.getSize() == 2) || (is64Bit() && o0.getSize() == 4)) + EMIT_BYTE(0x67); + + rmRel = &o1; + } + goto EmitJmpCall; + + case X86Inst::kEncodingX86Jmp: + if (isign3 == ENC_OPS1(Reg)) { + rbReg = o0.getId(); + goto EmitX86R; + } + + rmRel = &o0; + if (isign3 == ENC_OPS1(Mem)) + goto EmitX86M; + + // Jump encoded with 32-bit displacement use 0xE9 opcode. Jump encoded + // with 8-bit displacement's opcode is stored as an alternative opcode. + opCode = 0xE9; + goto EmitJmpCall; + + case X86Inst::kEncodingX86Lea: + if (isign3 == ENC_OPS2(Reg, Mem)) { + ADD_PREFIX_BY_SIZE(o0.getSize()); + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Mov: + // Reg <- Reg + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + + // Asmjit uses segment registers indexed from 1 to 6, leaving zero as + // "no segment register used". We have to fix this (decrement the index + // of the register) when emitting MOV instructions which move to/from + // a segment register. The segment register is always `opReg`, because + // the MOV instruction uses either RM or MR encoding. + + // GP <- ?? + if (X86Reg::isGp(o0)) { + // GP <- GP + if (X86Reg::isGp(o1)) { + uint32_t size0 = o0.getSize(); + uint32_t size1 = o1.getSize(); + + if (size0 != size1) { + // We allow 'mov r64, r32' as it's basically zero-extend. + if (size0 == 8 && size1 == 4) + size0 = 4; // Zero extend, don't promote to 64-bit. + else + goto InvalidInstruction; + } + + if (size0 == 1) { + FIXUP_GPB(o0, opReg); + FIXUP_GPB(o1, rbReg); + opCode = 0x8A; + + if (!(options & X86Inst::kOptionModMR)) + goto EmitX86R; + + opCode -= 2; + Utils::swap(opReg, rbReg); + goto EmitX86R; + } + else { + opCode = 0x8B; + ADD_PREFIX_BY_SIZE(size0); + + if (!(options & X86Inst::kOptionModMR)) + goto EmitX86R; + + opCode -= 2; + Utils::swap(opReg, rbReg); + goto EmitX86R; + } + } + + opReg = rbReg; + rbReg = o0.getId(); + + // GP <- SEG + if (X86Reg::isSeg(o1)) { + opCode = 0x8C; + opReg--; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86R; + } + + // GP <- CR + if (X86Reg::isCr(o1)) { + opCode = 0x20 | X86Inst::kOpCode_MM_0F; + goto EmitX86R; + } + + // GP <- DR + if (X86Reg::isDr(o1)) { + opCode = 0x21 | X86Inst::kOpCode_MM_0F; + goto EmitX86R; + } + } + else { + // ?? <- GP + if (!X86Reg::isGp(o1)) + goto InvalidInstruction; + + // SEG <- GP + if (X86Reg::isSeg(o0)) { + opCode = 0x8E; + opReg--; + ADD_PREFIX_BY_SIZE(o1.getSize()); + goto EmitX86R; + } + + // CR <- GP + if (X86Reg::isCr(o0)) { + opCode = 0x22 | X86Inst::kOpCode_MM_0F; + goto EmitX86R; + } + + // DR <- GP + if (X86Reg::isDr(o0)) { + opCode = 0x23 | X86Inst::kOpCode_MM_0F; + goto EmitX86R; + } + } + + goto InvalidInstruction; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + + // SEG <- Mem + if (X86Reg::isSeg(o0)) { + opCode = 0x8E; + opReg--; + ADD_PREFIX_BY_SIZE(o1.getSize()); + goto EmitX86M; + } + // Reg <- Mem + else { + if (o0.getSize() == 1) { + opCode = 0; + FIXUP_GPB(o0, opReg); + } + else { + opCode = 1; + ADD_PREFIX_BY_SIZE(o0.getSize()); + } + + // Handle a special form 'mov al|ax|eax|rax, [ptr64]' that doesn't use MOD. + if (o0.getId() == X86Gp::kIdAx && !rmRel->as().hasBaseOrIndex()) { + opCode += 0xA0; + imVal = rmRel->as().getOffset(); + imLen = getGpSize(); + goto EmitX86Op; + } + else { + opCode += 0x8A; + goto EmitX86M; + } + } + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + + // Mem <- SEG + if (X86Reg::isSeg(o1)) { + opCode = 0x8C; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86M; + } + // Mem <- Reg + else { + if (o1.getSize() == 1) { + opCode = 0; + FIXUP_GPB(o1, opReg); + } + else { + opCode = 1; + ADD_PREFIX_BY_SIZE(o1.getSize()); + } + + // Handle a special form 'mov [ptr64], al|ax|eax|rax' that doesn't use MOD. + if (!rmRel->as().hasBaseOrIndex() && o1.getId() == X86Gp::kIdAx) { + opCode += 0xA2; + imVal = rmRel->as().getOffset(); + imLen = getGpSize(); + goto EmitX86Op; + } + else { + opCode += 0x88; + goto EmitX86M; + } + } + } + + if (isign3 == ENC_OPS2(Reg, Imm)) { + opReg = o0.getId(); + imLen = o0.getSize(); + + if (imLen == 1) { + FIXUP_GPB(o0, opReg); + + imVal = static_cast(o1).getUInt8(); + opCode = 0xB0; + goto EmitX86OpReg; + } + else { + // 64-bit immediate in 64-bit mode is allowed. + imVal = static_cast(o1).getInt64(); + + // Optimize the instruction size by using a 32-bit immediate if possible. + if (imLen == 8 && !(options & X86Inst::kOptionLongForm)) { + if (Utils::isUInt32(imVal)) { + // Zero-extend by using a 32-bit GPD destination instead of a 64-bit GPQ. + imLen = 4; + } + else if (Utils::isInt32(imVal)) { + // Sign-extend, uses 'C7 /0' opcode. + rbReg = opReg; + + opCode = 0xC7 | X86Inst::kOpCode_W; + opReg = 0; + + imLen = 4; + goto EmitX86R; + } + } + + opCode = 0xB8; + ADD_PREFIX_BY_SIZE(imLen); + goto EmitX86OpReg; + } + } + + if (isign3 == ENC_OPS2(Mem, Imm)) { + uint32_t memSize = o0.getSize(); + + if (ASMJIT_UNLIKELY(memSize == 0)) + goto AmbiguousOperandSize; + + imVal = static_cast(o1).getInt64(); + imLen = Utils::iMin(memSize, 4); + + opCode = 0xC6 + (memSize != 1); + opReg = 0; + ADD_PREFIX_BY_SIZE(memSize); + + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86MovsxMovzx: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + ADD_PREFIX_BY_SIZE(o0.getSize()); + + if (o1.getSize() == 1) { + FIXUP_GPB(o1, rbReg); + goto EmitX86R; + } + else { + opCode++; + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opCode += o1.getSize() != 1; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Out: + if (isign3 == ENC_OPS2(Imm, Reg)) { + if (ASMJIT_UNLIKELY(o1.getId() != X86Gp::kIdAx)) + goto InvalidInstruction; + + imVal = o0.as().getUInt8(); + imLen = 1; + + opCode = commonData->getAltOpCode() + (o1.getSize() != 1); + ADD_66H_P_BY_SIZE(o1.getSize()); + goto EmitX86Op; + } + + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (ASMJIT_UNLIKELY(o0.getId() != X86Gp::kIdDx || o1.getId() != X86Gp::kIdAx)) + goto InvalidInstruction; + + opCode += o1.getSize() != 1; + ADD_66H_P_BY_SIZE(o1.getSize()); + goto EmitX86Op; + } + break; + + case X86Inst::kEncodingX86Outs: + if (isign3 == ENC_OPS2(Reg, Mem)) { + if (ASMJIT_UNLIKELY(o0.getId() != X86Gp::kIdDx), !x86IsImplicitMem(o1, X86Gp::kIdSi)) + goto InvalidInstruction; + + uint32_t size = o1.getSize(); + if (ASMJIT_UNLIKELY(size == 0)) + goto AmbiguousOperandSize; + + rmRel = &o1; + opCode += (size != 1); + + ADD_66H_P_BY_SIZE(size); + goto EmitX86OpImplicitMem; + } + break; + + case X86Inst::kEncodingX86Push: + if (isign3 == ENC_OPS1(Reg)) { + if (X86Reg::isSeg(o0)) { + uint32_t segment = o0.getId(); + if (ASMJIT_UNLIKELY(segment >= X86Seg::kIdCount)) + goto InvalidSegment; + + if (segment >= X86Seg::kIdFs) + EMIT_BYTE(0x0F); + + EMIT_BYTE(x86OpCodePushSeg[segment]); + goto EmitDone; + } + else { + goto CaseX86Pop_Gp; + } + } + + if (isign3 == ENC_OPS1(Imm)) { + imVal = static_cast(o0).getInt64(); + imLen = 4; + + if (Utils::isInt8(imVal) && !(options & X86Inst::kOptionLongForm)) + imLen = 1; + + opCode = imLen == 1 ? 0x6A : 0x68; + goto EmitX86Op; + } + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingX86Pop: + if (isign3 == ENC_OPS1(Reg)) { + if (X86Reg::isSeg(o0)) { + uint32_t segment = o0.getId(); + if (ASMJIT_UNLIKELY(segment == X86Seg::kIdCs || segment >= X86Seg::kIdCount)) + goto InvalidSegment; + + if (segment >= X86Seg::kIdFs) + EMIT_BYTE(0x0F); + + EMIT_BYTE(x86OpCodePopSeg[segment]); + goto EmitDone; + } + else { +CaseX86Pop_Gp: + // We allow 2 byte, 4 byte, and 8 byte register sizes, although PUSH + // and POP only allow 2 bytes or native size. On 64-bit we simply + // PUSH/POP 64-bit register even if 32-bit register was given. + if (ASMJIT_UNLIKELY(o0.getSize() < 2)) + goto InvalidInstruction; + + opCode = commonData->getAltOpCode(); + opReg = o0.getId(); + + ADD_66H_P_BY_SIZE(o0.getSize()); + goto EmitX86OpReg; + } + } + + if (isign3 == ENC_OPS1(Mem)) { + if (ASMJIT_UNLIKELY(o0.getSize() == 0)) + goto AmbiguousOperandSize; + + if (ASMJIT_UNLIKELY(o0.getSize() != 2 && o0.getSize() != getGpSize())) + goto InvalidInstruction; + + ADD_66H_P_BY_SIZE(o0.getSize()); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Ret: + if (isign3 == 0) { + // 'ret' without immediate, change C2 to C3. + opCode++; + goto EmitX86Op; + } + + if (isign3 == ENC_OPS1(Imm)) { + imVal = static_cast(o0).getInt64(); + if (imVal == 0 && !(options & X86Inst::kOptionLongForm)) { + // 'ret' without immediate, change C2 to C3. + opCode++; + goto EmitX86Op; + } + else { + imLen = 2; + goto EmitX86Op; + } + } + break; + + case X86Inst::kEncodingX86Rot: + if (o0.isReg()) { + rbReg = o0.getId(); + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, rbReg); + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + } + + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (ASMJIT_UNLIKELY(o1.getId() != X86Gp::kIdCx)) + goto InvalidInstruction; + + opCode += 2; + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Imm)) { + imVal = static_cast(o1).getInt64() & 0xFF; + imLen = 0; + + if (imVal == 1 && !(options & X86Inst::kOptionLongForm)) + goto EmitX86R; + + imLen = 1; + opCode -= 0x10; + goto EmitX86R; + } + } + else { + opCode += o0.getSize() != 1; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + if (isign3 == ENC_OPS2(Mem, Reg)) { + if (ASMJIT_UNLIKELY(o1.getId() != X86Gp::kIdCx)) + goto InvalidInstruction; + + opCode += 2; + rmRel = &o0; + goto EmitX86M; + } + + if (isign3 == ENC_OPS2(Mem, Imm)) { + if (ASMJIT_UNLIKELY(o0.getSize() == 0)) + goto AmbiguousOperandSize; + + imVal = static_cast(o1).getInt64() & 0xFF; + imLen = 0; + rmRel = &o0; + + if (imVal == 1 && !(options & X86Inst::kOptionLongForm)) + goto EmitX86M; + + imLen = 1; + opCode -= 0x10; + goto EmitX86M; + } + } + break; + + case X86Inst::kEncodingX86Set: + if (isign3 == ENC_OPS1(Reg)) { + rbReg = o0.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS1(Mem)) { + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86ShldShrd: + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + ADD_PREFIX_BY_SIZE(o0.getSize()); + imVal = static_cast(o2).getInt64(); + imLen = 1; + + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS3(Mem, Reg, Imm)) { + ADD_PREFIX_BY_SIZE(o1.getSize()); + imVal = static_cast(o2).getInt64(); + imLen = 1; + + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + + // The following instructions use opCode + 1. + opCode++; + + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + if (ASMJIT_UNLIKELY(o2.getId() != X86Gp::kIdCx)) + goto InvalidInstruction; + + ADD_PREFIX_BY_SIZE(o0.getSize()); + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS3(Mem, Reg, Reg)) { + if (ASMJIT_UNLIKELY(o2.getId() != X86Gp::kIdCx)) + goto InvalidInstruction; + + ADD_PREFIX_BY_SIZE(o1.getSize()); + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86StrRm: + if (isign3 == ENC_OPS2(Reg, Mem)) { + rmRel = &o1; + if (ASMJIT_UNLIKELY(rmRel->as().getOffsetLo32() || !X86Reg::isGp(o0.as(), X86Gp::kIdAx))) + goto InvalidInstruction; + + uint32_t size = o0.getSize(); + if (o1.hasSize() && ASMJIT_UNLIKELY(o1.getSize() != size)) + goto OperandSizeMismatch; + + ADD_PREFIX_BY_SIZE(size); + opCode += static_cast(size != 1); + + goto EmitX86OpImplicitMem; + } + break; + + case X86Inst::kEncodingX86StrMr: + if (isign3 == ENC_OPS2(Mem, Reg)) { + rmRel = &o0; + if (ASMJIT_UNLIKELY(rmRel->as().getOffsetLo32() || !X86Reg::isGp(o1.as(), X86Gp::kIdAx))) + goto InvalidInstruction; + + uint32_t size = o1.getSize(); + if (o0.hasSize() && ASMJIT_UNLIKELY(o0.getSize() != size)) + goto OperandSizeMismatch; + + ADD_PREFIX_BY_SIZE(size); + opCode += static_cast(size != 1); + + goto EmitX86OpImplicitMem; + } + break; + + case X86Inst::kEncodingX86StrMm: + if (isign3 == ENC_OPS2(Mem, Mem)) { + if (ASMJIT_UNLIKELY(o0.as().getBaseIndexType() != + o1.as().getBaseIndexType())) + goto InvalidInstruction; + + rmRel = &o1; + if (ASMJIT_UNLIKELY(o0.as().hasOffset())) + goto InvalidInstruction; + + uint32_t size = o1.getSize(); + if (ASMJIT_UNLIKELY(size == 0)) + goto AmbiguousOperandSize; + + if (ASMJIT_UNLIKELY(o0.getSize() != size)) + goto OperandSizeMismatch; + + ADD_PREFIX_BY_SIZE(size); + opCode += static_cast(size != 1); + + goto EmitX86OpImplicitMem; + } + break; + + case X86Inst::kEncodingX86Test: + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (o0.getSize() != o1.getSize()) + goto OperandSizeMismatch; + + rbReg = o0.getId(); + opReg = o1.getId(); + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, rbReg); + FIXUP_GPB(o1, opReg); + goto EmitX86R; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + + if (o1.getSize() == 1) { + FIXUP_GPB(o1, opReg); + goto EmitX86M; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o1.getSize()); + goto EmitX86M; + } + } + + // The following instructions use the secondary opcode. + opCode = commonData->getAltOpCode(); + opReg = x86ExtractO(opCode); + + if (isign3 == ENC_OPS2(Reg, Imm)) { + rbReg = o0.getId(); + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, rbReg); + + imVal = static_cast(o1).getUInt8(); + imLen = 1; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + imVal = static_cast(o1).getInt64(); + imLen = Utils::iMin(o0.getSize(), 4); + } + + // Alternate Form - AL, AX, EAX, RAX. + if (o0.getId() == 0 && !(options & X86Inst::kOptionLongForm)) { + opCode &= X86Inst::kOpCode_PP_66 | X86Inst::kOpCode_W; + opCode |= 0xA8 + (o0.getSize() != 1); + goto EmitX86Op; + } + + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Mem, Imm)) { + if (ASMJIT_UNLIKELY(o0.getSize() == 0)) + goto AmbiguousOperandSize; + + imVal = static_cast(o1).getInt64(); + imLen = Utils::iMin(o0.getSize(), 4); + + opCode += (o0.getSize() != 1); + ADD_PREFIX_BY_SIZE(o0.getSize()); + + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Xchg: + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, opReg); + goto EmitX86M; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86M; + } + } + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingX86Xadd: + if (isign3 == ENC_OPS2(Reg, Reg)) { + rbReg = o0.getId(); + opReg = o1.getId(); + + if (o0.getSize() != o1.getSize()) + goto OperandSizeMismatch; + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, rbReg); + FIXUP_GPB(o1, opReg); + goto EmitX86R; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + // Special opcode for 'xchg ?ax, reg'. + if (instId == X86Inst::kIdXchg && (opReg == 0 || rbReg == 0)) { + opCode &= X86Inst::kOpCode_PP_66 | X86Inst::kOpCode_W; + opCode |= 0x90; + // One of `xchg a, b` or `xchg b, a` is AX/EAX/RAX. + opReg += rbReg; + goto EmitX86OpReg; + } + else { + goto EmitX86R; + } + } + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opCode += o1.getSize() != 1; + ADD_PREFIX_BY_SIZE(o1.getSize()); + + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Fence: + rbReg = 0; + goto EmitX86R; + + // ------------------------------------------------------------------------ + // [FPU] + // ------------------------------------------------------------------------ + + case X86Inst::kEncodingFpuOp: + goto EmitFpuOp; + + case X86Inst::kEncodingFpuArith: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + + // We switch to the alternative opcode if the first operand is zero. + if (opReg == 0) { +CaseFpuArith_Reg: + opCode = ((0xD8 << X86Inst::kOpCode_FPU_2B_Shift) ) + + ((opCode >> X86Inst::kOpCode_FPU_2B_Shift) & 0xFF) + rbReg; + goto EmitFpuOp; + } + else if (rbReg == 0) { + rbReg = opReg; + opCode = ((0xDC << X86Inst::kOpCode_FPU_2B_Shift) ) + + ((opCode ) & 0xFF) + rbReg; + goto EmitFpuOp; + } + else { + goto InvalidInstruction; + } + } + + if (isign3 == ENC_OPS1(Mem)) { +CaseFpuArith_Mem: + // 0xD8/0xDC, depends on the size of the memory operand; opReg is valid. + opCode = (o0.getSize() == 4) ? 0xD8 : 0xDC; + // Clear compressed displacement before going to EmitX86M. + opCode &= ~static_cast(X86Inst::kOpCode_CDSHL_Mask); + + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingFpuCom: + if (isign3 == 0) { + rbReg = 1; + goto CaseFpuArith_Reg; + } + + if (isign3 == ENC_OPS1(Reg)) { + rbReg = o0.getId(); + goto CaseFpuArith_Reg; + } + + if (isign3 == ENC_OPS1(Mem)) { + goto CaseFpuArith_Mem; + } + break; + + case X86Inst::kEncodingFpuFldFst: + if (isign3 == ENC_OPS1(Mem)) { + rmRel = &o0; + + if (o0.getSize() == 4 && commonData->hasFlag(X86Inst::kInstFlagFPU_M4)) { + goto EmitX86M; + } + + if (o0.getSize() == 8 && commonData->hasFlag(X86Inst::kInstFlagFPU_M8)) { + opCode += 4; + goto EmitX86M; + } + + if (o0.getSize() == 10 && commonData->hasFlag(X86Inst::kInstFlagFPU_M10)) { + opCode = commonData->getAltOpCode(); + opReg = x86ExtractO(opCode); + goto EmitX86M; + } + } + + if (isign3 == ENC_OPS1(Reg)) { + if (instId == X86Inst::kIdFld ) { opCode = (0xD9 << X86Inst::kOpCode_FPU_2B_Shift) + 0xC0 + o0.getId(); goto EmitFpuOp; } + if (instId == X86Inst::kIdFst ) { opCode = (0xDD << X86Inst::kOpCode_FPU_2B_Shift) + 0xD0 + o0.getId(); goto EmitFpuOp; } + if (instId == X86Inst::kIdFstp) { opCode = (0xDD << X86Inst::kOpCode_FPU_2B_Shift) + 0xD8 + o0.getId(); goto EmitFpuOp; } + } + break; + + case X86Inst::kEncodingFpuM: + if (isign3 == ENC_OPS1(Mem)) { + // Clear compressed displacement before going to EmitX86M. + opCode &= ~static_cast(X86Inst::kOpCode_CDSHL_Mask); + + rmRel = &o0; + if (o0.getSize() == 2 && commonData->hasFlag(X86Inst::kInstFlagFPU_M2)) { + opCode += 4; + goto EmitX86M; + } + + if (o0.getSize() == 4 && commonData->hasFlag(X86Inst::kInstFlagFPU_M4)) { + goto EmitX86M; + } + + if (o0.getSize() == 8 && commonData->hasFlag(X86Inst::kInstFlagFPU_M8)) { + opCode = commonData->getAltOpCode() & ~static_cast(X86Inst::kOpCode_CDSHL_Mask); + opReg = x86ExtractO(opCode); + goto EmitX86M; + } + } + break; + + case X86Inst::kEncodingFpuRDef: + if (isign3 == 0) { + opCode += 1; + goto EmitFpuOp; + } + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingFpuR: + if (isign3 == ENC_OPS1(Reg)) { + opCode += o0.getId(); + goto EmitFpuOp; + } + break; + + case X86Inst::kEncodingFpuStsw: + if (isign3 == ENC_OPS1(Reg)) { + if (ASMJIT_UNLIKELY(o0.getId() != X86Gp::kIdAx)) + goto InvalidInstruction; + + opCode = commonData->getAltOpCode(); + goto EmitFpuOp; + } + + if (isign3 == ENC_OPS1(Mem)) { + // Clear compressed displacement before going to EmitX86M. + opCode &= ~static_cast(X86Inst::kOpCode_CDSHL_Mask); + + rmRel = &o0; + goto EmitX86M; + } + break; + + // ------------------------------------------------------------------------ + // [Ext] + // ------------------------------------------------------------------------ + + case X86Inst::kEncodingExtPextrw: + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + ADD_66H_P(X86Reg::isXmm(o1)); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS3(Mem, Reg, Imm)) { + // Secondary opcode of 'pextrw' instruction (SSE4.1). + opCode = commonData->getAltOpCode(); + ADD_66H_P(X86Reg::isXmm(o1)); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtExtract: + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + ADD_66H_P(X86Reg::isXmm(o1)); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS3(Mem, Reg, Imm)) { + ADD_66H_P(X86Reg::isXmm(o1)); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtMov: + // GP|MMX|XMM <- GP|MMX|XMM + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + + if (!(options & X86Inst::kOptionModMR) || !commonData->hasAltOpCode()) + goto EmitX86R; + + opCode = commonData->getAltOpCode(); + Utils::swap(opReg, rbReg); + goto EmitX86R; + } + + // GP|MMX|XMM <- Mem + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + + // The following instruction uses opCode[1]. + opCode = commonData->getAltOpCode(); + + // Mem <- GP|MMX|XMM + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtMovnti: + if (isign3 == ENC_OPS2(Mem, Reg)) { + ADD_REX_W(X86Reg::isGpq(o1)); + + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtMovbe: + if (isign3 == ENC_OPS2(Reg, Mem)) { + if (o0.getSize() == 1) + goto InvalidInstruction; + + ADD_PREFIX_BY_SIZE(o0.getSize()); + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + + // The following instruction uses the secondary opcode. + opCode = commonData->getAltOpCode(); + + if (isign3 == ENC_OPS2(Mem, Reg)) { + if (o1.getSize() == 1) + goto InvalidInstruction; + + ADD_PREFIX_BY_SIZE(o1.getSize()); + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtMovd: +CaseExtMovd: + opReg = o0.getId(); + ADD_66H_P(X86Reg::isXmm(o0)); + + // MMX/XMM <- Gp + if (isign3 == ENC_OPS2(Reg, Reg) && X86Reg::isGp(o1)) { + rbReg = o1.getId(); + goto EmitX86R; + } + + // MMX/XMM <- Mem + if (isign3 == ENC_OPS2(Reg, Mem)) { + rmRel = &o1; + goto EmitX86M; + } + + // The following instructions use the secondary opcode. + opCode &= X86Inst::kOpCode_W; + opCode |= commonData->getAltOpCode(); + opReg = o1.getId(); + ADD_66H_P(X86Reg::isXmm(o1)); + + // GP <- MMX/XMM + if (isign3 == ENC_OPS2(Reg, Reg) && X86Reg::isGp(o0)) { + rbReg = o0.getId(); + goto EmitX86R; + } + + // Mem <- MMX/XMM + if (isign3 == ENC_OPS2(Mem, Reg)) { + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtMovq: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + + // MMX <- MMX + if (X86Reg::isMm(o0) && X86Reg::isMm(o1)) { + opCode = X86Inst::kOpCode_PP_00 | X86Inst::kOpCode_MM_0F | 0x6F; + + if (!(options & X86Inst::kOptionModMR)) + goto EmitX86R; + + opCode += 0x10; + Utils::swap(opReg, rbReg); + goto EmitX86R; + } + + // XMM <- XMM + if (X86Reg::isXmm(o0) && X86Reg::isXmm(o1)) { + opCode = X86Inst::kOpCode_PP_F3 | X86Inst::kOpCode_MM_0F | 0x7E; + + if (!(options & X86Inst::kOptionModMR)) + goto EmitX86R; + + opCode = X86Inst::kOpCode_PP_66 | X86Inst::kOpCode_MM_0F | 0xD6; + Utils::swap(opReg, rbReg); + goto EmitX86R; + } + + // MMX <- XMM (MOVDQ2Q) + if (X86Reg::isMm(o0) && X86Reg::isXmm(o1)) { + opCode = X86Inst::kOpCode_PP_F2 | X86Inst::kOpCode_MM_0F | 0xD6; + goto EmitX86R; + } + + // XMM <- MMX (MOVQ2DQ) + if (X86Reg::isXmm(o0) && X86Reg::isMm(o1)) { + opCode = X86Inst::kOpCode_PP_F3 | X86Inst::kOpCode_MM_0F | 0xD6; + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + + // MMX <- Mem + if (X86Reg::isMm(o0)) { + opCode = X86Inst::kOpCode_PP_00 | X86Inst::kOpCode_MM_0F | 0x6F; + goto EmitX86M; + } + + // XMM <- Mem + if (X86Reg::isXmm(o0)) { + opCode = X86Inst::kOpCode_PP_F3 | X86Inst::kOpCode_MM_0F | 0x7E; + goto EmitX86M; + } + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + + // Mem <- MMX + if (X86Reg::isMm(o1)) { + opCode = X86Inst::kOpCode_PP_00 | X86Inst::kOpCode_MM_0F | 0x7F; + goto EmitX86M; + } + + // Mem <- XMM + if (X86Reg::isXmm(o1)) { + opCode = X86Inst::kOpCode_PP_66 | X86Inst::kOpCode_MM_0F | 0xD6; + goto EmitX86M; + } + } + + // MOVQ in other case is simply a MOVD instruction promoted to 64-bit. + opCode |= X86Inst::kOpCode_W; + goto CaseExtMovd; + + case X86Inst::kEncodingExtRm_XMM0: + if (ASMJIT_UNLIKELY(!o2.isNone() && !X86Reg::isXmm(o2, 0))) + goto InvalidInstruction; + + isign3 &= 0x3F; + goto CaseExtRm; + + case X86Inst::kEncodingExtRm_ZDI: + if (ASMJIT_UNLIKELY(!o2.isNone() && !x86IsImplicitMem(o2, X86Gp::kIdDi))) + goto InvalidInstruction; + + isign3 &= 0x3F; + goto CaseExtRm; + + case X86Inst::kEncodingExtRm_Wx: + ADD_REX_W(X86Reg::isGpq(o0) || o1.getSize() == 8); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingExtRm: +CaseExtRm: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtRm_P: + if (isign3 == ENC_OPS2(Reg, Reg)) { + ADD_66H_P(X86Reg::isXmm(o0) | X86Reg::isXmm(o1)); + + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + ADD_66H_P(X86Reg::isXmm(o0)); + + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtRmRi: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + + // The following instruction uses the secondary opcode. + opCode = commonData->getAltOpCode(); + opReg = x86ExtractO(opCode); + + if (isign3 == ENC_OPS2(Reg, Imm)) { + imVal = static_cast(o1).getInt64(); + imLen = 1; + + rbReg = o0.getId(); + goto EmitX86R; + } + break; + + case X86Inst::kEncodingExtRmRi_P: + if (isign3 == ENC_OPS2(Reg, Reg)) { + ADD_66H_P(X86Reg::isXmm(o0) | X86Reg::isXmm(o1)); + + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + ADD_66H_P(X86Reg::isXmm(o0)); + + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + + // The following instruction uses the secondary opcode. + opCode = commonData->getAltOpCode(); + opReg = x86ExtractO(opCode); + + if (isign3 == ENC_OPS2(Reg, Imm)) { + ADD_66H_P(X86Reg::isXmm(o0)); + + imVal = static_cast(o1).getInt64(); + imLen = 1; + + rbReg = o0.getId(); + goto EmitX86R; + } + break; + + case X86Inst::kEncodingExtRmi: + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtRmi_P: + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + ADD_66H_P(X86Reg::isXmm(o0) | X86Reg::isXmm(o1)); + + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + ADD_66H_P(X86Reg::isXmm(o0)); + + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + break; + + // ------------------------------------------------------------------------ + // [Extrq / Insertq (SSE4A)] + // ------------------------------------------------------------------------ + + case X86Inst::kEncodingExtExtrq: + opReg = o0.getId(); + rbReg = o1.getId(); + + if (isign3 == ENC_OPS2(Reg, Reg)) + goto EmitX86R; + + // The following instruction uses the secondary opcode. + opCode = commonData->getAltOpCode(); + + if (isign3 == ENC_OPS3(Reg, Imm, Imm)) { + imVal = (static_cast(o1).getUInt32() ) + + (static_cast(o2).getUInt32() << 8) ; + imLen = 2; + + rbReg = x86ExtractO(opCode); + goto EmitX86R; + } + break; + + case X86Inst::kEncodingExtInsertq: { + const uint32_t isign4 = isign3 + (o3.getOp() << 9); + opReg = o0.getId(); + rbReg = o1.getId(); + + if (isign4 == ENC_OPS2(Reg, Reg)) + goto EmitX86R; + + // The following instruction uses the secondary opcode. + opCode = commonData->getAltOpCode(); + + if (isign4 == ENC_OPS4(Reg, Reg, Imm, Imm)) { + imVal = (static_cast(o2).getUInt32() ) + + (static_cast(o3).getUInt32() << 8) ; + imLen = 2; + goto EmitX86R; + } + break; + } + + // ------------------------------------------------------------------------ + // [3dNow] + // ------------------------------------------------------------------------ + + case X86Inst::kEncodingExt3dNow: + // Every 3dNow instruction starts with 0x0F0F and the actual opcode is + // stored as 8-bit immediate. + imVal = opCode & 0xFF; + imLen = 1; + + opCode = X86Inst::kOpCode_MM_0F | 0x0F; + opReg = o0.getId(); + + if (isign3 == ENC_OPS2(Reg, Reg)) { + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + rmRel = &o1; + goto EmitX86M; + } + break; + + // ------------------------------------------------------------------------ + // [VEX/EVEX] + // ------------------------------------------------------------------------ + + case X86Inst::kEncodingVexOp: + goto EmitVexEvexOp; + + case X86Inst::kEncodingVexKmov: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + + // Form 'k, reg'. + if (X86Reg::isGp(o1)) { + opCode = commonData->getAltOpCode(); + goto EmitVexEvexR; + } + + // Form 'reg, k'. + if (X86Reg::isGp(o0)) { + opCode = commonData->getAltOpCode() + 1; + goto EmitVexEvexR; + } + + // Form 'k, k'. + if (!(options & X86Inst::kOptionModMR)) + goto EmitVexEvexR; + + opCode++; + Utils::swap(opReg, rbReg); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + + goto EmitVexEvexM; + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + + opCode++; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexM: + if (isign3 == ENC_OPS1(Mem)) { + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexM_VM: + if (isign3 == ENC_OPS1(Mem)) { + opCode |= x86OpCodeLByVMem(o0); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexMr_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexMr_VM: + if (isign3 == ENC_OPS2(Mem, Reg)) { + opCode |= Utils::iMax(x86OpCodeLByVMem(o0), x86OpCodeLBySize(o1.getSize())); + + opReg = o1.getId(); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexMri_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexMri: + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Mem, Reg, Imm)) { + opReg = o1.getId(); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRm_ZDI: + if (ASMJIT_UNLIKELY(!o2.isNone() && !x86IsImplicitMem(o2, X86Gp::kIdDi))) + goto InvalidInstruction; + + isign3 &= 0x3F; + goto CaseVexRm; + + case X86Inst::kEncodingVexRm_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRm: +CaseVexRm: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRm_VM: + if (isign3 == ENC_OPS2(Reg, Mem)) { + opCode |= Utils::iMax(x86OpCodeLByVMem(o1), x86OpCodeLBySize(o0.getSize())); + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRmi_Wx: + ADD_REX_W(X86Reg::isGpq(o0) | X86Reg::isGpq(o1)); + goto CaseVexRmi; + + case X86Inst::kEncodingVexRmi_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRmi: +CaseVexRmi: + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvm: +CaseVexRvm: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { +CaseVexRvm_R: + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvm_ZDX_Wx: + if (ASMJIT_UNLIKELY(!o3.isNone() && !X86Reg::isGp(o3, X86Gp::kIdDx))) + goto InvalidInstruction; + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRvm_Wx: + ADD_REX_W(X86Reg::isGpq(o0) | X86Reg::isGpq(o1)); + goto CaseVexRvm; + + case X86Inst::kEncodingVexRvm_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + goto CaseVexRvm; + + case X86Inst::kEncodingVexRvmr_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRvmr: { + const uint32_t isign4 = isign3 + (o3.getOp() << 9); + imVal = o3.getId() << 4; + imLen = 1; + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + goto EmitVexEvexR; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Mem, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + goto EmitVexEvexM; + } + break; + } + + case X86Inst::kEncodingVexRvmi_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRvmi: { + const uint32_t isign4 = isign3 + (o3.getOp() << 9); + imVal = static_cast(o3).getInt64(); + imLen = 1; + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Imm)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + goto EmitVexEvexR; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Mem, Imm)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + goto EmitVexEvexM; + } + break; + } + + case X86Inst::kEncodingVexRmv_Wx: + ADD_REX_W(X86Reg::isGpq(o0) | X86Reg::isGpq(o2)); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRmv: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRmvRm_VM: + if (isign3 == ENC_OPS2(Reg, Mem)) { + opCode = commonData->getAltOpCode(); + opCode |= Utils::iMax(x86OpCodeLByVMem(o1), x86OpCodeLBySize(o0.getSize())); + + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRmv_VM: + if (isign3 == ENC_OPS3(Reg, Mem, Reg)) { + opCode |= Utils::iMax(x86OpCodeLByVMem(o1), x86OpCodeLBySize(o0.getSize() | o2.getSize())); + + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + + case X86Inst::kEncodingVexRmvi: { + const uint32_t isign4 = isign3 + (o3.getOp() << 9); + imVal = static_cast(o3).getInt64(); + imLen = 1; + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Imm)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign4 == ENC_OPS4(Reg, Mem, Reg, Imm)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + } + + case X86Inst::kEncodingVexMovdMovq: + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (X86Reg::isGp(o0)) { + opCode = commonData->getAltOpCode(); + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitVexEvexR; + } + + if (X86Reg::isGp(o1)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + } + + // If this is a 'W' version (movq) then allow also vmovq 'xmm|xmm' form. + if (opCode & X86Inst::kOpCode_EW) + goto CaseVexRmMr; + else + goto CaseVexRmMr_AfterRegReg; + + case X86Inst::kEncodingVexRmMr_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRmMr: +CaseVexRmMr: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + +CaseVexRmMr_AfterRegReg: + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + + // The following instruction uses the secondary opcode. + opCode &= X86Inst::kOpCode_LL_Mask; + opCode |= commonData->getAltOpCode(); + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvmRmv: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rbReg = o1.getId(); + + if (!(options & X86Inst::kOptionModMR)) + goto EmitVexEvexR; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + + ADD_VEX_W(true); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rmRel = &o1; + goto EmitVexEvexM; + } + + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + + ADD_VEX_W(true); + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvmRmi_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRvmRmi: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + goto EmitVexEvexM; + } + + // The following instructions use the secondary opcode. + opCode &= X86Inst::kOpCode_LL_Mask; + opCode |= commonData->getAltOpCode(); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvmRmvRmi: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rbReg = o1.getId(); + + if (!(options & X86Inst::kOptionModMR)) + goto EmitVexEvexR; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + + ADD_VEX_W(true); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rmRel = &o1; + + goto EmitVexEvexM; + } + + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + + ADD_VEX_W(true); + goto EmitVexEvexM; + } + + // The following instructions use the secondary opcode. + opCode = commonData->getAltOpCode(); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvmMr: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + goto EmitVexEvexM; + } + + // The following instructions use the secondary opcode. + opCode = commonData->getAltOpCode(); + + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvmMvr_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRvmMvr: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + goto EmitVexEvexM; + } + + // The following instruction uses the secondary opcode. + opCode &= X86Inst::kOpCode_LL_Mask; + opCode |= commonData->getAltOpCode(); + + if (isign3 == ENC_OPS3(Mem, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o2.getId(), o1.getId()); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvmVmi_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRvmVmi: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + goto EmitVexEvexM; + } + + // The following instruction uses the secondary opcode. + opCode &= X86Inst::kOpCode_LL_Mask; + opCode |= commonData->getAltOpCode(); + opReg = x86ExtractO(opCode); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opReg = x86PackRegAndVvvvv(opReg, o0.getId()); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + opReg = x86PackRegAndVvvvv(opReg, o0.getId()); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexVm_Wx: + ADD_REX_W(X86Reg::isGpq(o0) | X86Reg::isGpq(o1)); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexVm: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = x86PackRegAndVvvvv(opReg, o0.getId()); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = x86PackRegAndVvvvv(opReg, o0.getId()); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexEvexVmi_Lx: + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) + opCode |= X86Inst::kOpCode_MM_ForceEvex; + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexVmi_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexVmi: + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opReg = x86PackRegAndVvvvv(opReg, o0.getId()); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + opReg = x86PackRegAndVvvvv(opReg, o0.getId()); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvrmRvmr_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRvrmRvmr: { + const uint32_t isign4 = isign3 + (o3.getOp() << 9); + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Reg)) { + imVal = o3.getId() << 4; + imLen = 1; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + + goto EmitVexEvexR; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Mem)) { + imVal = o2.getId() << 4; + imLen = 1; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o3; + + ADD_VEX_W(true); + goto EmitVexEvexM; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Mem, Reg)) { + imVal = o3.getId() << 4; + imLen = 1; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + + goto EmitVexEvexM; + } + break; + } + + case X86Inst::kEncodingVexRvrmiRvmri_Lx: { + if (!(options & CodeEmitter::kOptionOp4) || !_op4.isImm()) + goto InvalidInstruction; + + const uint32_t isign4 = isign3 + (o3.getOp() << 9); + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize() | o2.getSize() | o3.getSize()); + + imVal = static_cast(_op4).getUInt8() & 0x0F; + imLen = 1; + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Reg)) { + imVal |= o3.getId() << 4; + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + + goto EmitVexEvexR; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Mem)) { + imVal |= o2.getId() << 4; + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o3; + + ADD_VEX_W(true); + goto EmitVexEvexM; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Mem, Reg)) { + imVal |= o3.getId() << 4; + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + + goto EmitVexEvexM; + } + break; + } + + case X86Inst::kEncodingVexMovssMovsd: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + goto CaseVexRvm_R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opCode = commonData->getAltOpCode(); + opReg = o1.getId(); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + // ------------------------------------------------------------------------ + // [FMA4] + // ------------------------------------------------------------------------ + + case X86Inst::kEncodingFma4_Lx: + // It's fine to just check the first operand, second is just for sanity. + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingFma4: { + const uint32_t isign4 = isign3 + (o3.getOp() << 9); + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Reg)) { + imVal = o3.getId() << 4; + imLen = 1; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + + goto EmitVexEvexR; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Mem)) { + imVal = o2.getId() << 4; + imLen = 1; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o3; + + ADD_VEX_W(true); + goto EmitVexEvexM; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Mem, Reg)) { + imVal = o3.getId() << 4; + imLen = 1; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + + goto EmitVexEvexM; + } + break; + } + } + goto InvalidInstruction; + + // -------------------------------------------------------------------------- + // [Emit - X86] + // -------------------------------------------------------------------------- + +EmitX86Op: + // Emit mandatory instruction prefix. + EMIT_PP(opCode); + + // Emit REX prefix (64-bit only). + { + uint32_t rex = x86ExtractREX(opCode, options); + if (rex) { + if (options & X86Inst::_kOptionInvalidRex) + goto InvalidRexPrefix; + EMIT_BYTE(rex | kX86ByteRex); + } + } + + // Emit instruction opcodes. + EMIT_MM_OP(opCode); + + if (imLen != 0) + goto EmitImm; + else + goto EmitDone; + +EmitX86OpReg: + // Emit mandatory instruction prefix. + EMIT_PP(opCode); + + // Emit REX prefix (64-bit only). + { + uint32_t rex = x86ExtractREX(opCode, options) | + (opReg >> 3); // Rex.B (0x01). + if (rex) { + EMIT_BYTE(rex | kX86ByteRex); + if (options & X86Inst::_kOptionInvalidRex) + goto InvalidRexPrefix; + opReg &= 0x7; + } + } + + // Emit instruction opcodes. + opCode += opReg; + EMIT_MM_OP(opCode); + + if (imLen != 0) + goto EmitImm; + else + goto EmitDone; + +EmitX86OpImplicitMem: + // NOTE: Don't change the emit order here, it's compatible with KeyStone/LLVM. + rmInfo = x86MemInfo[rmRel->as().getBaseIndexType()]; + if (ASMJIT_UNLIKELY(rmRel->as().hasOffset() || (rmInfo & kX86MemInfo_Index))) + goto InvalidInstruction; + + // Emit mandatory instruction prefix. + EMIT_PP(opCode); + + // Emit REX prefix (64-bit only). + { + uint32_t rex = x86ExtractREX(opCode, options); + if (rex) { + if (options & X86Inst::_kOptionInvalidRex) + goto InvalidRexPrefix; + EMIT_BYTE(rex | kX86ByteRex); + } + } + + // Segment-override prefix. + if (rmRel->as().hasSegment()) + EMIT_BYTE(x86SegmentPrefix[rmRel->as().getSegmentId()]); + + // Address-override prefix. + if (rmInfo & _getAddressOverrideMask()) + EMIT_BYTE(0x67); + + // Emit instruction opcodes. + EMIT_MM_OP(opCode); + + if (imLen != 0) + goto EmitImm; + else + goto EmitDone; + +EmitX86R: + // Mandatory instruction prefix. + EMIT_PP(opCode); + + // Rex prefix (64-bit only). + { + uint32_t rex = x86ExtractREX(opCode, options) | + ((opReg & 0x08) >> 1) | // REX.R (0x04). + ((rbReg ) >> 3) ; // REX.B (0x01). + if (rex) { + if (options & X86Inst::_kOptionInvalidRex) + goto InvalidRexPrefix; + EMIT_BYTE(rex | kX86ByteRex); + opReg &= 0x07; + rbReg &= 0x07; + } + } + + // Instruction opcodes. + EMIT_MM_OP(opCode); + // ModR. + EMIT_BYTE(x86EncodeMod(3, opReg, rbReg)); + + if (imLen != 0) + goto EmitImm; + else + goto EmitDone; + +EmitX86M: + ASMJIT_ASSERT(rmRel != nullptr); + ASMJIT_ASSERT(rmRel->getOp() == Operand::kOpMem); + rmInfo = x86MemInfo[rmRel->as().getBaseIndexType()]; + + // GP instructions have never compressed displacement specified. + ASMJIT_ASSERT((opCode & X86Inst::kOpCode_CDSHL_Mask) == 0); + + // Segment-override prefix. + if (rmRel->as().hasSegment()) + EMIT_BYTE(x86SegmentPrefix[rmRel->as().getSegmentId()]); + + // Address-override prefix. + if (rmInfo & _getAddressOverrideMask()) + EMIT_BYTE(0x67); + + // Mandatory instruction prefix. + EMIT_PP(opCode); + + rbReg = rmRel->as().getBaseId(); + rxReg = rmRel->as().getIndexId(); + + // REX prefix (64-bit only). + { + uint32_t rex; + + rex = (rbReg >> 3) & 0x01; // REX.B (0x01). + rex |= (rxReg >> 2) & 0x02; // REX.X (0x02). + rex |= (opReg >> 1) & 0x04; // REX.R (0x04). + + rex &= rmInfo; + rex |= x86ExtractREX(opCode, options); + + if (rex) { + if (options & X86Inst::_kOptionInvalidRex) + goto InvalidRexPrefix; + EMIT_BYTE(rex | kX86ByteRex); + opReg &= 0x07; + } + } + + // Instruction opcodes. + EMIT_MM_OP(opCode); + // ... Fall through ... + + // -------------------------------------------------------------------------- + // [Emit - MOD/SIB] + // -------------------------------------------------------------------------- + +EmitModSib: + if (!(rmInfo & (kX86MemInfo_Index | kX86MemInfo_67H_X86))) { + // ==========|> [BASE + DISP8|DISP32]. + if (rmInfo & kX86MemInfo_BaseGp) { + rbReg &= 0x7; + relOffset = rmRel->as().getOffsetLo32(); + + uint32_t mod = x86EncodeMod(0, opReg, rbReg); + if (rbReg == X86Gp::kIdSp) { + // [XSP|R12]. + if (relOffset == 0) { + EMIT_BYTE(mod); + EMIT_BYTE(x86EncodeSib(0, 4, 4)); + } + // [XSP|R12 + DISP8|DISP32]. + else { + uint32_t cdShift = (opCode & X86Inst::kOpCode_CDSHL_Mask) >> X86Inst::kOpCode_CDSHL_Shift; + int32_t cdOffset = relOffset >> cdShift; + + if (Utils::isInt8(cdOffset) && relOffset == (cdOffset << cdShift)) { + EMIT_BYTE(mod + 0x40); // <- MOD(1, opReg, rbReg). + EMIT_BYTE(x86EncodeSib(0, 4, 4)); + EMIT_BYTE(cdOffset & 0xFF); + } + else { + EMIT_BYTE(mod + 0x80); // <- MOD(2, opReg, rbReg). + EMIT_BYTE(x86EncodeSib(0, 4, 4)); + EMIT_DWORD(relOffset); + } + } + } + else if (rbReg != X86Gp::kIdBp && relOffset == 0) { + // [BASE]. + EMIT_BYTE(mod); + } + else { + // [BASE + DISP8|DISP32]. + uint32_t cdShift = (opCode & X86Inst::kOpCode_CDSHL_Mask) >> X86Inst::kOpCode_CDSHL_Shift; + int32_t cdOffset = relOffset >> cdShift; + + if (Utils::isInt8(cdOffset) && relOffset == (cdOffset << cdShift)) { + EMIT_BYTE(mod + 0x40); + EMIT_BYTE(cdOffset & 0xFF); + } + else { + EMIT_BYTE(mod + 0x80); + EMIT_DWORD(relOffset); + } + } + } + // ==========|> [ABSOLUTE | DISP32]. + else if (!(rmInfo & (kX86MemInfo_BaseLabel | kX86MemInfo_BaseRip))) { + if (is32Bit()) { + relOffset = rmRel->as().getOffsetLo32(); + EMIT_BYTE(x86EncodeMod(0, opReg, 5)); + EMIT_DWORD(relOffset); + } + else { + uint64_t baseAddress = getCodeInfo().getBaseAddress(); + relOffset = rmRel->as().getOffsetLo32(); + + // Prefer absolute addressing mode if FS|GS segment override is present. + bool absoluteValid = rmRel->as().getOffsetHi32() == (relOffset >> 31); + bool preferAbsolute = (rmRel->as().getSegmentId() >= X86Seg::kIdFs) || rmRel->as().isAbs(); + + // If we know the base address and the memory operand points to an + // absolute address it's possible to calculate REL32 that can be + // be used as [RIP+REL32] in 64-bit mode. + if (baseAddress != kNoBaseAddress && !preferAbsolute) { + const uint32_t kModRel32Size = 5; + uint64_t rip64 = baseAddress + + static_cast((uintptr_t)(cursor - _bufferData)) + imLen + kModRel32Size; + + uint64_t rel64 = static_cast(rmRel->as().getOffset()) - rip64; + if (Utils::isInt32(static_cast(rel64))) { + EMIT_BYTE(x86EncodeMod(0, opReg, 5)); + EMIT_DWORD(static_cast(rel64 & 0xFFFFFFFFU)); + if (imLen != 0) + goto EmitImm; + else + goto EmitDone; + } + } + + if (ASMJIT_UNLIKELY(!absoluteValid)) + goto InvalidAddress64Bit; + + EMIT_BYTE(x86EncodeMod(0, opReg, 4)); + EMIT_BYTE(x86EncodeSib(0, 4, 5)); + EMIT_DWORD(relOffset); + } + } + // ==========|> [LABEL|RIP + DISP32] + else { + EMIT_BYTE(x86EncodeMod(0, opReg, 5)); + + if (is32Bit()) { +EmitModSib_LabelRip_X86: + if (ASMJIT_UNLIKELY(_code->_relocations.willGrow(1) != kErrorOk)) + goto NoHeapMemory; + + relOffset = rmRel->as().getOffsetLo32(); + if (rmInfo & kX86MemInfo_BaseLabel) { + // [LABEL->ABS]. + label = _code->getLabelEntry(rmRel->as().getBaseId()); + if (!label) goto InvalidLabel; + + err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, 4); + if (ASMJIT_UNLIKELY(err)) goto Failed; + + re->_sourceOffset = static_cast((uintptr_t)(cursor - _bufferData)); + re->_data = static_cast(relOffset); + + if (label->isBound()) { + // Bound label. + re->_data += static_cast(label->getOffset()); + EMIT_DWORD(0); + } + else { + // Non-bound label. + relOffset = -4 - imLen; + relSize = 4; + goto EmitRel; + } + } + else { + // [RIP->ABS]. + err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, 4); + if (ASMJIT_UNLIKELY(err)) goto Failed; + + re->_sourceOffset = static_cast((uintptr_t)(cursor - _bufferData)); + re->_data = re->_sourceOffset + + static_cast(static_cast(relOffset)); + EMIT_DWORD(0); + } + } + else { + relOffset = rmRel->as().getOffsetLo32(); + if (rmInfo & kX86MemInfo_BaseLabel) { + // [RIP]. + label = _code->getLabelEntry(rmRel->as().getBaseId()); + if (!label) goto InvalidLabel; + + relOffset -= (4 + imLen); + if (label->isBound()) { + // Bound label. + relOffset += label->getOffset() - static_cast((intptr_t)(cursor - _bufferData)); + EMIT_DWORD(static_cast(relOffset)); + } + else { + // Non-bound label. + relSize = 4; + goto EmitRel; + } + } + else { + // [RIP]. + EMIT_DWORD(static_cast(relOffset)); + } + } + } + } + else if (!(rmInfo & kX86MemInfo_67H_X86)) { + // ESP|RSP can't be used as INDEX in pure SIB mode, however, VSIB mode + // allows XMM4|YMM4|ZMM4 (that's why the check is before the label). + if (ASMJIT_UNLIKELY(rxReg == X86Gp::kIdSp)) goto InvalidAddressIndex; + +EmitModVSib: + rxReg &= 0x7; + + // ==========|> [BASE + INDEX + DISP8|DISP16|DISP32]. + if (rmInfo & kX86MemInfo_BaseGp) { + rbReg &= 0x7; + relOffset = rmRel->as().getOffsetLo32(); + + uint32_t mod = x86EncodeMod(0, opReg, 4); + uint32_t sib = x86EncodeSib(rmRel->as().getShift(), rxReg, rbReg); + + if (relOffset == 0 && rbReg != X86Gp::kIdBp) { + // [BASE + INDEX << SHIFT]. + EMIT_BYTE(mod); + EMIT_BYTE(sib); + } + else { + uint32_t cdShift = (opCode & X86Inst::kOpCode_CDSHL_Mask) >> X86Inst::kOpCode_CDSHL_Shift; + int32_t cdOffset = relOffset >> cdShift; + + if (Utils::isInt8(cdOffset) && relOffset == (cdOffset << cdShift)) { + // [BASE + INDEX << SHIFT + DISP8]. + EMIT_BYTE(mod + 0x40); // <- MOD(1, opReg, 4). + EMIT_BYTE(sib); + EMIT_BYTE(cdOffset); + } + else { + // [BASE + INDEX << SHIFT + DISP16|DISP32]. + EMIT_BYTE(mod + 0x80); // <- MOD(2, opReg, 4). + EMIT_BYTE(sib); + EMIT_DWORD(relOffset); + } + } + } + // ==========|> [INDEX + DISP16|DISP32]. + else if (!(rmInfo & (kX86MemInfo_BaseLabel | kX86MemInfo_BaseRip))) { + // [INDEX << SHIFT + DISP32]. + EMIT_BYTE(x86EncodeMod(0, opReg, 4)); + EMIT_BYTE(x86EncodeSib(rmRel->as().getShift(), rxReg, 5)); + + relOffset = rmRel->as().getOffsetLo32(); + EMIT_DWORD(relOffset); + } + // ==========|> [LABEL|RIP + INDEX + DISP32]. + else { + if (is32Bit()) { + EMIT_BYTE(x86EncodeMod(0, opReg, 4)); + EMIT_BYTE(x86EncodeSib(rmRel->as().getShift(), rxReg, 5)); + goto EmitModSib_LabelRip_X86; + } + else { + goto InvalidAddress; + } + } + } + else { + // 16-bit address mode (32-bit mode with 67 override prefix). + relOffset = (static_cast(rmRel->as().getOffsetLo32()) << 16) >> 16; + + // NOTE: 16-bit addresses don't use SIB byte and their encoding differs. We + // use a table-based approach to calculate the proper MOD byte as it's easier. + // Also, not all BASE [+ INDEX] combinations are supported in 16-bit mode, so + // this may fail. + const uint32_t kBaseGpIdx = (kX86MemInfo_BaseGp | kX86MemInfo_Index); + + if (rmInfo & kBaseGpIdx) { + // ==========|> [BASE + INDEX + DISP16]. + uint32_t mod; + + rbReg &= 0x7; + rxReg &= 0x7; + + if ((rmInfo & kBaseGpIdx) == kBaseGpIdx) { + uint32_t shf = rmRel->as().getShift(); + if (ASMJIT_UNLIKELY(shf != 0)) + goto InvalidAddress; + mod = x86Mod16BaseIndexTable[(rbReg << 3) + rxReg]; + } + else { + if (rmInfo & kX86MemInfo_Index) + rbReg = rxReg; + mod = x86Mod16BaseTable[rbReg]; + } + + if (ASMJIT_UNLIKELY(mod == 0xFF)) + goto InvalidAddress; + + mod += opReg << 3; + if (relOffset == 0 && mod != 0x06) { + EMIT_BYTE(mod); + } + else if (Utils::isInt8(relOffset)) { + EMIT_BYTE(mod + 0x40); + EMIT_BYTE(relOffset); + } + else { + EMIT_BYTE(mod + 0x80); + EMIT_WORD(relOffset); + } + } + else { + // Not supported in 16-bit addresses. + if (rmInfo & (kX86MemInfo_BaseRip | kX86MemInfo_BaseLabel)) + goto InvalidAddress; + + // ==========|> [DISP16]. + EMIT_BYTE(opReg | 0x06); + EMIT_WORD(relOffset); + } + } + + if (imLen != 0) + goto EmitImm; + else + goto EmitDone; + + // -------------------------------------------------------------------------- + // [Emit - FPU] + // -------------------------------------------------------------------------- + +EmitFpuOp: + // Mandatory instruction prefix. + EMIT_PP(opCode); + + // FPU instructions consist of two opcodes. + EMIT_BYTE(opCode >> X86Inst::kOpCode_FPU_2B_Shift); + EMIT_BYTE(opCode); + goto EmitDone; + + // -------------------------------------------------------------------------- + // [Emit - VEX / EVEX] + // -------------------------------------------------------------------------- + +EmitVexEvexOp: + { + // These don't use immediate. + ASMJIT_ASSERT(imLen == 0); + + // Only 'vzeroall' and 'vzeroupper' instructions use this encoding, they + // don't define 'W' to be '1' so we can just check the 'mmmmm' field. Both + // functions can encode by using VEV2 prefix so VEV3 is basically only used + // when forced from outside. + ASMJIT_ASSERT((opCode & X86Inst::kOpCode_W) == 0); + + uint32_t x = ((opCode & X86Inst::kOpCode_MM_Mask ) >> (X86Inst::kOpCode_MM_Shift )) | + ((opCode & X86Inst::kOpCode_LL_Mask ) >> (X86Inst::kOpCode_LL_Shift - 10)) | + ((opCode & X86Inst::kOpCode_PP_VEXMask) >> (X86Inst::kOpCode_PP_Shift - 8)) | + ((options & X86Inst::kOptionVex3 ) >> (X86Inst::kOpCode_MM_Shift )) ; + if (x & 0x04U) { + x = (x & (0x4 ^ 0xFFFF)) << 8; // [00000000|00000Lpp|0000m0mm|00000000]. + x ^= (kX86ByteVex3) | // [........|00000Lpp|0000m0mm|__VEX3__]. + (0x07U << 13) | // [........|00000Lpp|1110m0mm|__VEX3__]. + (0x0FU << 19) | // [........|01111Lpp|1110m0mm|__VEX3__]. + (opCode << 24) ; // [_OPCODE_|01111Lpp|1110m0mm|__VEX3__]. + + EMIT_DWORD(x); + goto EmitDone; + } + else { + x = ((x >> 8) ^ x) ^ 0xF9; + EMIT_BYTE(kX86ByteVex2); + EMIT_BYTE(x); + EMIT_BYTE(opCode); + goto EmitDone; + } + } + +EmitVexEvexR: + { + // VEX instructions use only 0-1 BYTE immediate. + ASMJIT_ASSERT(imLen <= 1); + + // Construct `x` - a complete EVEX|VEX prefix. + uint32_t x = ((opReg << 4) & 0xF980U) | // [........|........|Vvvvv..R|R.......]. + ((rbReg << 2) & 0x0060U) | // [........|........|........|.BB.....]. + (x86ExtractLLMM(opCode, options)); // [........|.LL.....|Vvvvv..R|RBBmmmmm]. + opReg &= 0x7; + + // Handle AVX512 options by a single branch. + const uint32_t kAvx512Options = X86Inst::kOptionOpExtra | + X86Inst::kOptionKZ | + X86Inst::kOption1ToX | + X86Inst::kOptionSAE | + X86Inst::kOptionER ; + if (options & kAvx512Options) { + // Memory broadcast without a memory operand is invalid. + if (ASMJIT_UNLIKELY(options & X86Inst::kOption1ToX)) + goto InvalidBroadcast; + + // TODO: {sae} and {er} + + // NOTE: We consider a valid construct internally even when {kz} was + // specified without specifying the register. In that case it would be + // `k0` and basically everything should be zeroed. It's valid EVEX. + if (options & X86Inst::kOptionOpExtra) + x |= _opExtra.getId() << 16; + + x |= options & X86Inst::kOptionKZ; // [........|zLL..aaa|Vvvvv..R|RBBmmmmm]. + } + + // Check if EVEX is required by checking bits in `x` : [........|xx...xxx|x......x|.x.x....]. + if (x & 0x00C78150U) { + uint32_t y = ((x << 4) & 0x00080000U) | // [........|....V...|........|........]. + ((x >> 4) & 0x00000010U) ; // [........|....V...|........|...R....]. + x = (x & 0x00FF78E3U) | y; // [........|zLL.Vaaa|0vvvv000|RBBR00mm]. + x = (x << 8) | // [zLL.Vaaa|0vvvv000|RBBR00mm|00000000]. + ((opCode >> kSHR_W_PP) & 0x00830000U) | // [zLL.Vaaa|Wvvvv0pp|RBBR00mm|00000000]. + ((opCode >> kSHR_W_EW) & 0x00800000U) ; // [zLL.Vaaa|Wvvvv0pp|RBBR00mm|00000000] (added EVEX.W). + // _ ____ ____ + x ^= 0x087CF000U | kX86ByteEvex; // [zLL.Vaaa|Wvvvv1pp|RBBR00mm|01100010]. + + EMIT_DWORD(x); + EMIT_BYTE(opCode); + + rbReg &= 0x7; + EMIT_BYTE(x86EncodeMod(3, opReg, rbReg)); + + if (imLen == 0) goto EmitDone; + EMIT_BYTE(imVal & 0xFF); + goto EmitDone; + } + + // Not EVEX, prepare `x` for VEX2 or VEX3: x = [........|00L00000|0vvvv000|R0B0mmmm]. + x |= ((opCode >> (kSHR_W_PP + 8)) & 0x8300U) | // [00000000|00L00000|Wvvvv0pp|R0B0mmmm]. + ((x >> 11 ) & 0x0400U) ; // [00000000|00L00000|WvvvvLpp|R0B0mmmm]. + + // Check if VEX3 is required / forced: [........|........|x.......|..x..x..]. + if (x & 0x0008024U) { + uint32_t xorMsk = x86VEXPrefix[x & 0xF] | (opCode << 24); + + // Clear 'FORCE-VEX3' bit and all high bits. + x = (x & (0x4 ^ 0xFFFF)) << 8; // [00000000|WvvvvLpp|R0B0m0mm|00000000]. + // ____ _ _ + x ^= xorMsk; // [_OPCODE_|WvvvvLpp|R1Bmmmmm|VEX3|XOP]. + EMIT_DWORD(x); + + rbReg &= 0x7; + EMIT_BYTE(x86EncodeMod(3, opReg, rbReg)); + + if (imLen == 0) goto EmitDone; + EMIT_BYTE(imVal & 0xFF); + goto EmitDone; + } + else { + // 'mmmmm' must be '00001'. + ASMJIT_ASSERT((x & 0x1F) == 0x01); + + x = ((x >> 8) ^ x) ^ 0xF9; + EMIT_BYTE(kX86ByteVex2); + EMIT_BYTE(x); + EMIT_BYTE(opCode); + + rbReg &= 0x7; + EMIT_BYTE(x86EncodeMod(3, opReg, rbReg)); + + if (imLen == 0) goto EmitDone; + EMIT_BYTE(imVal & 0xFF); + goto EmitDone; + } + } + +EmitVexEvexM: + ASMJIT_ASSERT(rmRel != nullptr); + ASMJIT_ASSERT(rmRel->getOp() == Operand::kOpMem); + rmInfo = x86MemInfo[rmRel->as().getBaseIndexType()]; + + // Segment-override prefix. + if (rmRel->as().hasSegment()) + EMIT_BYTE(x86SegmentPrefix[rmRel->as().getSegmentId()]); + + // Address-override prefix. + if (rmInfo & _getAddressOverrideMask()) + EMIT_BYTE(0x67); + + rbReg = rmRel->as().hasBaseReg() ? rmRel->as().getBaseId() : uint32_t(0); + rxReg = rmRel->as().hasIndexReg() ? rmRel->as().getIndexId() : uint32_t(0); + + { + // VEX instructions use only 0-1 BYTE immediate. + ASMJIT_ASSERT(imLen <= 1); + + // Construct `x` - a complete EVEX|VEX prefix. + uint32_t x = ((opReg << 4 ) & 0x0000F980U) | // [........|........|Vvvvv..R|R.......]. + ((rxReg << 3 ) & 0x00000040U) | // [........|........|........|.X......]. + ((rxReg << 15) & 0x00080000U) | // [........|....X...|........|........]. + ((rbReg << 2 ) & 0x00000020U) | // [........|........|........|..B.....]. + (x86ExtractLLMM(opCode, options)); // [........|.LL.X...|Vvvvv..R|RXBmmmmm]. + opReg &= 0x07U; + + // Handle AVX512 options by a single branch. + const uint32_t kAvx512Options = X86Inst::kOptionOpExtra | + X86Inst::kOption1ToX | + X86Inst::kOptionKZ | + X86Inst::kOptionSAE | + X86Inst::kOptionER ; + if (options & kAvx512Options) { + // {er} and {sae} are both invalid if memory operand is used. + if (ASMJIT_UNLIKELY(options & (X86Inst::kOptionSAE | X86Inst::kOptionER))) + goto InvalidEROrSAE; + + // NOTE: We consider a valid construct internally even when {kz} was + // specified without specifying the register. In that case it would be + // `k0` and basically everything would be zeroed. It's a valid EVEX. + if (options & X86Inst::kOptionOpExtra) + x |= _opExtra.getId() << 16; + + x |= options & (X86Inst::kOption1ToX | // [........|.LLbXaaa|Vvvvv..R|RXBmmmmm]. + X86Inst::kOptionKZ ); // [........|zLLbXaaa|Vvvvv..R|RXBmmmmm]. + } + + // Check if EVEX is required by checking bits in `x` : [........|xx.xxxxx|x......x|...x....]. + if (x & 0x00DF8110U) { + uint32_t y = ((x << 4) & 0x00080000U) | // [........|....V...|........|........]. + ((x >> 4) & 0x00000010U) ; // [........|....V...|........|...R....]. + x = (x & 0xFFFF78E3U) | y; // [........|zLLbVaaa|0vvvv000|RXBR00mm]. + x = (x << 8) | // [zLLbVaaa|0vvvv000|RBBR00mm|00000000]. + ((opCode >> kSHR_W_PP) & 0x00830000U) | // [zLLbVaaa|Wvvvv0pp|RBBR00mm|00000000]. + ((opCode >> kSHR_W_EW) & 0x00800000U) ; // [zLLbVaaa|Wvvvv0pp|RBBR00mm|00000000] (added EVEX.W). + // _ ____ ____ + x ^= 0x087CF000U | kX86ByteEvex; // [zLLbVaaa|Wvvvv1pp|RBBR00mm|01100010]. + + EMIT_DWORD(x); + EMIT_BYTE(opCode); + + if (opCode & 0x10000000U) { + // Broadcast, change the compressed displacement scale to either x4 (SHL 2) or x8 (SHL 3) + // depending on instruction's W. If 'W' is 1 'SHL' must be 3, otherwise it must be 2. + opCode &=~static_cast(X86Inst::kOpCode_CDSHL_Mask); + opCode |= ((x & 0x00800000U) ? 3 : 2) << X86Inst::kOpCode_CDSHL_Shift; + } + else { + // Add the compressed displacement 'SHF' to the opcode based on 'TTWLL'. + uint32_t TTWLL = ((opCode >> (X86Inst::kOpCode_CDTT_Shift - 3)) & 0x18) + + ((opCode >> (X86Inst::kOpCode_W_Shift - 2)) & 0x04) + + ((x >> 29) & 0x3); + opCode += x86CDisp8SHL[TTWLL]; + } + } + else { + // Not EVEX, prepare `x` for VEX2 or VEX3: x = [........|00L00000|0vvvv000|RXB0mmmm]. + x |= ((opCode >> (kSHR_W_PP + 8)) & 0x8300U) | // [00000000|00L00000|Wvvvv0pp|RXB0mmmm]. + ((x >> 11 ) & 0x0400U) ; // [00000000|00L00000|WvvvvLpp|RXB0mmmm]. + + // Clear a possible CDisp specified by EVEX. + opCode &= ~X86Inst::kOpCode_CDSHL_Mask; + + // Check if VEX3 is required / forced: [........|........|x.......|.xx..x..]. + if (x & 0x0008064U) { + uint32_t xorMsk = x86VEXPrefix[x & 0xF] | (opCode << 24); + + // Clear 'FORCE-VEX3' bit and all high bits. + x = (x & (0x4 ^ 0xFFFF)) << 8; // [00000000|WvvvvLpp|RXB0m0mm|00000000]. + // ____ ___ + x ^= xorMsk; // [_OPCODE_|WvvvvLpp|RXBmmmmm|VEX3_XOP]. + EMIT_DWORD(x); + } + else { + // 'mmmmm' must be '00001'. + ASMJIT_ASSERT((x & 0x1F) == 0x01); + + x = ((x >> 8) ^ x) ^ 0xF9; + EMIT_BYTE(kX86ByteVex2); + EMIT_BYTE(x); + EMIT_BYTE(opCode); + } + } + } + + // MOD|SIB address. + if (!commonData->hasFlag(X86Inst::kInstFlagVM)) + goto EmitModSib; + + // MOD|VSIB address without INDEX is invalid. + if (rmInfo & kX86MemInfo_Index) + goto EmitModVSib; + goto InvalidInstruction; + + // -------------------------------------------------------------------------- + // [Emit - Jmp/Jcc/Call] + // -------------------------------------------------------------------------- + + // TODO: Should be adjusted after there support for multiple sections feature is added. +EmitJmpCall: + { + // Emit REX prefix if asked for (64-bit only). + uint32_t rex = x86ExtractREX(opCode, options); + if (rex) { + if (options & X86Inst::_kOptionInvalidRex) + goto InvalidRexPrefix; + EMIT_BYTE(rex | kX86ByteRex); + } + + uint64_t ip = static_cast((intptr_t)(cursor - _bufferData)); + uint32_t rel32 = 0; + uint32_t opCode8 = commonData->getAltOpCode(); + + uint32_t inst8Size = 1 + 1; // OPCODE + REL8 . + uint32_t inst32Size = 1 + 4; // [PREFIX] OPCODE + REL32. + + // Jcc instructions with 32-bit displacement use 0x0F prefix, + // other instructions don't. No other prefixes are used by X86. + ASMJIT_ASSERT((opCode8 & X86Inst::kOpCode_MM_Mask) == 0); + ASMJIT_ASSERT((opCode & X86Inst::kOpCode_MM_Mask) == 0 || + (opCode & X86Inst::kOpCode_MM_Mask) == X86Inst::kOpCode_MM_0F); + + inst32Size += static_cast( + (opCode & X86Inst::kOpCode_MM_Mask) == X86Inst::kOpCode_MM_0F); + + if (rmRel->isLabel()) { + label = _code->getLabelEntry(rmRel->as