From 39ea0b3a13da83cada7b2427230b5f50415356d4 Mon Sep 17 00:00:00 2001 From: thorium <102315529+thorium-cfx@users.noreply.github.com> Date: Thu, 3 Nov 2022 16:37:22 +0100 Subject: [PATCH] tweak(build-tools): required build tools for mono v2 runtime * Clean up of the C# project generation code (removing duplicate code). * Dedicated v2 native code generator. * Native enums now show an obsolete warning for both v1 and v2. * prebuild_natives.bat function for updating native files. * prebuild_natives.bat fix where it would only update files the new and old file were the same, not when there were actual changes. Prerequisite to mono_rt2 --- .gitattributes | 1 + code/client/clrcore-v2/Native/Native.cs | 2 +- code/components/config.lua | 2 + code/premake5.lua | 323 ++++++----- code/tools/ci/build_server_2.sh | 2 + ext/natives/Makefile | 15 +- ext/natives/codegen_out_cs_v2.lua | 722 ++++++++++++++++++++++++ ext/natives/codegen_out_enum.lua | 18 +- prebuild_natives.cmd | 68 ++- 9 files changed, 973 insertions(+), 180 deletions(-) create mode 100644 ext/natives/codegen_out_cs_v2.lua diff --git a/.gitattributes b/.gitattributes index 93659bf0e4..b9b12c608a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,5 @@ *.sh text eol=lf +*.cmd text eol=crlf # helpers for GH language stats *.h linguist-language=C++ diff --git a/code/client/clrcore-v2/Native/Native.cs b/code/client/clrcore-v2/Native/Native.cs index ff2b4a69d5..e5aa9e0834 100644 --- a/code/client/clrcore-v2/Native/Native.cs +++ b/code/client/clrcore-v2/Native/Native.cs @@ -304,7 +304,7 @@ internal override unsafe void PushNativeValue(ref CustomNativeInvoker.CustomInvo internal override unsafe void PullNativeValue(ref CustomNativeInvoker.CustomInvocation ctx) { - m_nativeValue = ctx.m_ctx.retDataPtr[ctx.m_offset++]; + m_nativeValue = ctx.m_ctx.functionDataPtr[ctx.m_offset++]; } } diff --git a/code/components/config.lua b/code/components/config.lua index ca2acd9045..0c28c69e9f 100644 --- a/code/components/config.lua +++ b/code/components/config.lua @@ -21,6 +21,7 @@ if _OPTIONS['game'] == 'server' then end]] component 'citizen-scripting-v8node' component 'citizen-scripting-mono' + component 'citizen-scripting-mono-v2' component 'citizen-server-instance' component 'citizen-server-impl' component 'citizen-server-state-fivesv' @@ -63,6 +64,7 @@ else component 'net' component 'citizen-scripting-mono' + component 'citizen-scripting-mono-v2' if _OPTIONS['game'] ~= 'ny' then component 'citizen-scripting-v8client' diff --git a/code/premake5.lua b/code/premake5.lua index 7e500d5782..c31920002c 100644 --- a/code/premake5.lua +++ b/code/premake5.lua @@ -24,6 +24,16 @@ newoption { description = "Root directory for bin/ files." } +local PROGRAM_DETAILS = { + ['server'] = { publicName = 'Server', cSharp = { nativesFile = 'NativesServer.cs', gameFiles = 'Server/*.cs' } }, + + ['five'] = { publicName = 'FiveM', cSharp = { nativesFile = 'NativesFive.cs', gameFiles = 'Client/FiveM/**.cs' } }, + ['fivem'] = { publicName = 'FiveM', cSharp = { nativesFile = 'NativesFive.cs', gameFiles = 'Client/FiveM/**.cs' } }, + + ['rdr3'] = { publicName = 'RedM', cSharp = { nativesFile = 'NativesRDR3.cs', gameFiles = 'Client/RedM/*.cs' } }, + ['ny'] = { publicName = 'LibertyM', cSharp = { nativesFile = 'NativesNY.cs', gameFiles = 'Client/LibertyM/*.cs' } }, +} + dofile('tools/build/init.lua') -- perform component processing @@ -311,12 +321,12 @@ local function WriteDocumentationFileXml(_premake, _cfg) return end - - if _cfg.project.name ~= 'CitiMono' then - return + + if _cfg.project.name:find('^CitizenFX.') ~= nil then + _premake.w('$(OutputPath)ref\\' .. _cfg.targetname .. '.xml') + elseif _cfg.project.name == 'CitiMono' then + _premake.w('' .. string.gsub(_cfg.buildtarget.relpath, ".dll", ".xml") .. '') end - - _premake.w('' .. string.gsub(_cfg.buildtarget.relpath, ".dll", ".xml") .. '') end premake.override(premake.vstudio.dotnetbase, "propertyGroup", function(base, cfg) @@ -346,10 +356,10 @@ premake.override(premake.vstudio.cs2005, "targets", function(base, prj) _p(1, '') _p(2, '%s', path.getabsolute("client/clrref/" .. _OPTIONS['game'])) _p(2, '$(GenAPITargetDir)\\$(TargetName).cs') - _p(2, '%s', ('--exclude-api-list "%s" --exclude-attributes-list "%s"'):format( + _p(2, '--exclude-api-list "%s" --exclude-attributes-list "%s"', path.getabsolute("client/clrref/exclude_list.txt"), path.getabsolute("client/clrref/exclude_attributes_list.txt") - )) + ) _p(2, 'true') _p(1, '') @@ -358,10 +368,30 @@ premake.override(premake.vstudio.cs2005, "targets", function(base, prj) _p(1, '') _p(2, '') _p(1, '') - end - - if prj.name == 'CitiMonoRef' then + + elseif prj.name == 'CitiMonoRef' then _p(1, '', path.getabsolute("client/clrref/GenFacades.targets")) + + elseif prj.name:find('^CitizenFX.') ~= nil then + _p(1, '') + _p(2, 'true') + _p(1, '') + _p(1, '') + _p(2, '') + _p(2, '') + _p(3, '') + _p(3, '') + _p(2, '') + _p(2, '') + _p(2, '') + _p(3, '') + _p(2, '') + _p(2, '') + _p(2, '') + _p(3, '') + _p(3, '') + _p(2, '') + _p(1, '') end end) @@ -388,55 +418,67 @@ premake.override(premake.vstudio.dotnetbase, "nuGetReferences", function(base, c end) if _OPTIONS['game'] ~= 'launcher' then - project "CitiMono" - targetname "CitizenFX.Core" - language "C#" - kind "SharedLib" - - -- Missing XML comment for publicly visible type or member - disablewarnings 'CS1591' - if os.istarget('windows') then - dependson 'CfxPrebuild' + function csproject(projectName, targetName) + do project(projectName) + targetname(targetName or projectName) + language 'C#' + kind 'SharedLib' + + disablewarnings 'CS1591' -- Missing XML comment for publicly visible type or member + dotnetframework '4.6' + buildoptions '/debug:portable /langversion:7.3' + csversion '7.3' + + links { + 'System.dll', + 'System.Core.dll', + 'Microsoft.CSharp.dll', + '../data/client/citizen/clr2/lib/mono/4.5/System.Reflection.Metadata.dll', + '../data/client/citizen/clr2/lib/mono/4.5/MsgPack.dll' + } + + if os.istarget('windows') then + defines { 'OS_WIN' } + elseif os.istarget('linux') then + defines { 'OS_LINUX' } + end end - - dotnetframework '4.6' - + end + + function cstargets(targetDirectory) + filter { "configurations:Debug" } + targetdir (binroot .. '/debug/citizen/clr2/lib/mono/4.5/'.. (targetDirectory or '')) + + filter { "configurations:Release" } + targetdir (binroot .. '/release/citizen/clr2/lib/mono/4.5/'.. (targetDirectory or '')) + end + + group "mono/v1" + + local csharpCoreAssemblyName = _OPTIONS['game'] == 'server' and 'CitizenFX.Core.Server' or 'CitizenFX.Core.Client' + + do csproject ("CitiMono", "CitizenFX.Core") clr 'Unsafe' - - csversion '7.3' - - files { "client/clrcore/*.cs", "client/clrcore/Math/*.cs" } - - files { "../vendor/ben-demystifier/src/Ben.Demystifier/**.cs" } + files { 'client/clrcore/*.cs', 'client/clrcore/Math/*.cs' } + files { '../vendor/ben-demystifier/src/Ben.Demystifier/**.cs' } - if _OPTIONS['game'] == 'five' then - files { "client/clrcore/NativesFive.cs" } - elseif _OPTIONS['game'] == 'rdr3' then - files { "client/clrcore/NativesRDR3.cs" } - elseif _OPTIONS['game'] == 'server' then - files { "client/clrcore/NativesServer.cs" } - elseif _OPTIONS['game'] == 'ny' then - files { "client/clrcore/NativesNY.cs" } - end - - if _OPTIONS['game'] ~= 'server' then - defines { 'USE_HYPERDRIVE' } - + if _OPTIONS['game'] == 'server' then + files { 'client/clrcore/Server/*.cs' } + else if _OPTIONS['game'] == 'five' then - files { "client/clrcore/External/*.cs" } + files { 'client/clrcore/External/*.cs' } end - else - files { "client/clrcore/Server/*.cs" } + + defines { 'USE_HYPERDRIVE' } end - - if os.istarget('windows') then - nuget { - "Microsoft.DotNet.GenAPI:6.0.0-beta.21063.5", - "Microsoft.DotNet.GenFacades:6.0.0-beta.21063.5", - } - nugetsource "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" - else + + if os.istarget('windows') then + dependson 'CfxPrebuild' + nuget { 'Microsoft.DotNet.GenAPI:6.0.0-beta.21063.5', 'Microsoft.DotNet.GenFacades:6.0.0-beta.21063.5' } + nugetsource 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json' + elseif os.istarget('linux') then + links { '/usr/lib/mono/4.5/Facades/System.Runtime.dll', '/usr/lib/mono/4.5/Facades/System.IO.dll' } postbuildcommands { ("%s '%s' '%s' '%%{cfg.linktarget.abspath}'"):format( path.getabsolute("client/clrref/genapi.sh"), @@ -445,104 +487,121 @@ if _OPTIONS['game'] ~= 'launcher' then ) } end - - links { - "System.dll", - "Microsoft.CSharp.dll", - "System.Core.dll", - "../data/client/citizen/clr2/lib/mono/4.5/System.Reflection.Metadata.dll", - "../data/client/citizen/clr2/lib/mono/4.5/System.Collections.Immutable.dll", - "../data/client/citizen/clr2/lib/mono/4.5/MsgPack.dll" - } - - if os.istarget('linux') then - links { - "/usr/lib/mono/4.5/Facades/System.Runtime.dll", - "/usr/lib/mono/4.5/Facades/System.IO.dll" - } - end - - buildoptions '/debug:portable /langversion:7.3' - - filter { "configurations:Debug" } - targetdir (binroot .. '/debug/citizen/clr2/lib/mono/4.5/') - - filter { "configurations:Release" } - targetdir (binroot .. '/release/citizen/clr2/lib/mono/4.5/') - - if _OPTIONS['game'] ~= 'server' then - project "CitiMonoSystemDrawingStub" - targetname "System.Drawing" - language "C#" - kind "SharedLib" - - links { "CitiMono" } - - files { - "client/clrref/System.Drawing.cs" - } - - filter { "configurations:Debug" } - targetdir (binroot .. '/debug/citizen/clr2/lib/mono/4.5/') - - filter { "configurations:Release" } - targetdir (binroot .. '/release/citizen/clr2/lib/mono/4.5/') + + links { '../data/client/citizen/clr2/lib/mono/4.5/System.Collections.Immutable.dll' } + cstargets '' end - + + -- reference assembly project if os.istarget('windows') then - project "CitiMonoRef" - if _OPTIONS['game'] == 'server' then - targetname "CitizenFX.Core.Server" - else - targetname "CitizenFX.Core.Client" - end - - language "C#" - kind "SharedLib" - - dependson "CitiMono" - - dotnetframework '4.6' + do csproject ("CitiMonoRef", csharpCoreAssemblyName) clr 'Unsafe' - csversion '7.3' - - links { - "System.dll", - "System.Drawing.dll", - "System.Core.dll", - } + files { 'client/clrref/' .. _OPTIONS['game'] .. '/CitizenFX.Core.cs' } + links { 'System.dll', 'System.Drawing.dll', 'System.Core.dll' } + dependson 'CitiMono' + cstargets 'ref/' + end + elseif os.istarget('linux') then + do project "CitiMonoRef" + kind 'Utility' + files { 'client/clrref/CitizenFX.Core.Server.csproj' } + dependson 'CitiMono' - files { "client/clrref/" .. _OPTIONS['game'] .. "/CitizenFX.Core.cs" } + local absMonoRoot = path.getabsolute(binroot) .. '/%{cfg.name:lower()}/citizen/clr2/lib/mono/4.5/' - buildoptions '/debug:portable /langversion:7.3' - targetdir (binroot .. '/%{cfg.name:lower()}/citizen/clr2/lib/mono/4.5/ref/') - else - project "CitiMonoRef" - kind "Utility" - - dependson "CitiMono" - - files { - "client/clrref/CitizenFX.Core.Server.csproj" - } - filter 'files:**.csproj' buildmessage 'Generating facades' - buildinputs { "client/clrref/server/CitizenFX.Core.cs" } + buildinputs { 'client/clrref/server/CitizenFX.Core.cs' } buildcommands { ("dotnet msbuild '%%{file.abspath}' /p:Configuration=%%{cfg.name} /p:FacadeOutPath=%s /t:Restore"):format( - path.getabsolute(binroot) .. '/%{cfg.name:lower()}/citizen/clr2/lib/mono/4.5/' + absMonoRoot ), ("dotnet msbuild '%%{file.abspath}' /p:Configuration=%%{cfg.name} /p:FacadeOutPath=%s"):format( - path.getabsolute(binroot) .. '/%{cfg.name:lower()}/citizen/clr2/lib/mono/4.5/' + absMonoRoot ), ("rm -rf '%s'"):format( - path.getabsolute(binroot) .. '/%{cfg.name:lower()}/citizen/clr2/lib/mono/4.5/ref/' + absMonoRoot .. 'ref/' ) } buildoutputs { binroot .. '/%{cfg.name:lower()}/citizen/clr2/lib/mono/4.5/CitizenFX.Core.Server.dll' } + end + end + + if _OPTIONS['game'] ~= 'server' then + do csproject ("CitiMonoSystemDrawingStub", "System.Drawing") + files { 'client/clrref/System.Drawing.cs' } + links { 'CitiMono' } + cstargets '' + end + end + + group "mono/v2" + + -- Get game name and files + -- '.*' should not load any file, replace it with something else if it does + local program = PROGRAM_DETAILS[_OPTIONS['game']] + if not program then + print('Program "'.._OPTIONS['game']..'" was not found in the PROGRAM_DETAILS list, you either provided an incorrect program or PROGRAM_DETAILS is missing this options, premake5.lua') + program = { publicName = 'Unknown', cSharp = { nativesFile = '.*', gameFiles = '.*' } } + end + + do csproject (csharpCoreAssemblyName) + clr 'Unsafe' + files { + 'client/clrcore-v2/*.cs', + 'client/clrcore-v2/Coroutine/*.cs', + 'client/clrcore-v2/Interop/*.cs', + 'client/clrcore-v2/Math/v2/*.cs', + 'client/clrcore-v2/Native/*.cs', + 'client/clrcore-v2/System/*.cs', + + -- Math, cherry pick from v1 files for now + 'client/clrcore-v2/Math/GameMath.cs', + 'client/clrcore-v2/Math/MathUtil.cs', + 'client/clrcore-v2/Math/Matrix.cs', + 'client/clrcore-v2/Math/Matrix3x3.cs', + 'client/clrcore-v2/Math/Quaternion.cs', + 'client/clrcore-v2/Math/Vector2.cs', + 'client/clrcore-v2/Math/Vector3.cs', + 'client/clrcore-v2/Math/Vector4.cs', + } + + defines { 'MONO_V2' } + + if _OPTIONS['game'] == 'server' then + files { 'client/clrcore-v2/' .. program.cSharp.gameFiles } + defines { 'NATIVE_IMPL_INCLUDE', 'NATIVE_HASHES_INCLUDE', 'NATIVE_WRAPPERS_INCLUDE' } + end + cstargets 'v2' end + + if _OPTIONS['game'] ~= 'server' then + do csproject ("CitizenFX."..program.publicName..".NativeImpl") + clr 'Unsafe' + files { 'client/clrcore-v2/Native/'..program.cSharp.nativesFile } + links { csharpCoreAssemblyName } + defines { 'MONO_V2', 'NATIVE_WRAPPER_USE_VERSION', 'NATIVE_IMPL_INCLUDE' } + cstargets 'v2' + end + + do csproject ("CitizenFX."..program.publicName..".Native") + files { 'client/clrcore-v2/Native/'..program.cSharp.nativesFile, 'client/clrcore-v2/Native/CustomNativeWrapper.cs' } + links { csharpCoreAssemblyName, 'CitizenFX.'..program.publicName..'.NativeImpl' } + defines { 'MONO_V2', 'NATIVE_WRAPPER_USE_VERSION', 'NATIVE_HASHES_INCLUDE', 'NATIVE_WRAPPERS_INCLUDE' } + cstargets 'v2/Native/' + end + + do csproject ("CitizenFX."..program.publicName) + clr 'Unsafe' + files { 'client/clrcore-v2/'..program.cSharp.gameFiles, 'client/clrcore-v2/Game/Shared/*.cs' } + links { csharpCoreAssemblyName, 'CitizenFX.'..program.publicName..'.Native' } + defines { 'MONO_V2' } + cstargets 'v2' + end + end + end + group "" -- TARGET: shared component diff --git a/code/tools/ci/build_server_2.sh b/code/tools/ci/build_server_2.sh index 8bae6068c3..5ea094810c 100644 --- a/code/tools/ci/build_server_2.sh +++ b/code/tools/ci/build_server_2.sh @@ -86,6 +86,8 @@ EOF } #endif EOF + + lua5.3 codegen.lua inp/natives_global.lua cs_v2 server > /src/code/client/clrcore-v2/Natives/NativesServer.cs lua5.3 codegen.lua inp/natives_global.lua rpc server > /opt/cfx-server/citizen/scripting/rpc_natives.json fi diff --git a/ext/natives/Makefile b/ext/natives/Makefile index 9031ff340e..51b5f0276c 100644 --- a/ext/natives/Makefile +++ b/ext/natives/Makefile @@ -99,6 +99,7 @@ export NATIVES_NY_FOOTER all: out \ out/NativesFive.cs out/NativesServer.cs out/NativesRDR3.cs out/NativesNY.cs \ + out/v2/NativesFive.cs out/v2/NativesServer.cs out/v2/NativesRDR3.cs out/v2/NativesNY.cs \ out/rpc_natives.json \ out/natives_server.lua out/natives_server.d.ts out/natives_server.js out/natives_server.zip \ out/Natives.h out/NativesRDR.h out/NativesServer.h out/NativesNY.h out/PASGen.h \ @@ -111,7 +112,7 @@ clean: rm -r out out: - mkdir -p out + mkdir -p out/v2 out/NativesFive.cs: inp/natives_global.lua $(CODEGEN_SCRIPTS) codegen_out_cs.lua codegen_out_enum.lua echo "$$NATIVES_FIVE_HEADER" > $@ @@ -137,6 +138,18 @@ out/NativesServer.cs: $(CODEGEN_SCRIPTS) inp/natives_global.lua codegen_out_cs.l ./lua53 codegen.lua inp/natives_global.lua cs server >> $@ echo "$$NATIVES_SERVER_FOOTER" >> $@ +out/v2/NativesFive.cs: inp/natives_global.lua $(CODEGEN_SCRIPTS) codegen_out_cs_v2.lua codegen_out_enum.lua | inp/natives_global_client_compat.lua + ./lua53 codegen.lua inp/natives_global.lua cs_v2 > $@ + +out/v2/NativesRDR3.cs: inp/natives_rdr3.lua $(CODEGEN_SCRIPTS) codegen_out_cs_v2.lua codegen_out_enum.lua + ./lua53 codegen.lua inp/natives_rdr3.lua cs_v2 > $@ + +out/v2/NativesNY.cs: inp/natives_ny.lua $(CODEGEN_SCRIPTS) codegen_out_cs_v2.lua codegen_out_enum.lua + ./lua53 codegen.lua inp/natives_ny.lua cs_v2 > $@ + +out/v2/NativesServer.cs: inp/natives_global.lua $(CODEGEN_SCRIPTS) codegen_out_cs_v2.lua rpc_spec_natives.lua + ./lua53 codegen.lua inp/natives_global.lua cs_v2 server > $@ + out/Natives.h: inp/natives_global.lua $(CODEGEN_SCRIPTS) codegen_out_native_lua.lua rpc_spec_natives.lua ./lua53 codegen.lua inp/natives_global.lua native_lua > $@ diff --git a/ext/natives/codegen_out_cs_v2.lua b/ext/natives/codegen_out_cs_v2.lua new file mode 100644 index 0000000000..0afdfac658 --- /dev/null +++ b/ext/natives/codegen_out_cs_v2.lua @@ -0,0 +1,722 @@ +-- Products +local PRODUCTS = { + ['server'] = { 'IS_FXSERVER', 'Server' }, + ['global'] = { 'GTA_FIVE', 'FiveM' }, + ['rdr3'] = { 'IS_RDR3', 'RedM' }, + ['ny'] = { 'GTA_NY', 'LibertyM' }, +} + +-- C# language words +local langWords = { + ['abstract'] = '_abstract', + ['as'] = '_as', + ['base'] = '_base', + ['bool'] = '_bool', + ['break'] = '_break', + ['byte'] = '_byte', + ['case'] = '_case', + ['catch'] = '_catch', + ['char'] = '_char', + ['checked'] = '_checked', + ['class'] = '_class', + ['const'] = '_const', + ['continue'] = '_continue', + ['decimal'] = '_decimal', + ['default'] = '_default', + ['delegate'] = '_delegate', + ['do'] = '_do', + ['double'] = '_double', + ['else'] = '_else', + ['enum'] = '_enum', + ['event'] = '_event', + ['explicit'] = '_explicit', + ['extern'] = '_extern', + ['false'] = '_false', + ['finally'] = '_finally', + ['fixed'] = '_fixed', + ['float'] = '_float', + ['for'] = '_for', + ['foreach'] = '_foreach', + ['goto'] = '_goto', + ['if'] = '_if', + ['implicit'] = '_implicit', + ['in'] = '_in', + ['int'] = '_int', + ['interface'] = '_interface', + ['internal'] = '_internal', + ['is'] = '_is', + ['lock'] = '_lock', + ['long'] = '_long', + ['namespace'] = '_namespace', + ['new'] = '_new', + ['null'] = '_null', + ['object'] = '_object', + ['operator'] = '_operator', + ['out'] = '_out', + ['override'] = '_override', + ['params'] = '_params', + ['private'] = '_private', + ['protected'] = '_protected', + ['public'] = '_public', + ['readonly'] = '_readonly', + ['ref'] = '_ref', + ['return'] = '_return', + ['sbyte'] = '_sbyte', + ['sealed'] = '_sealed', + ['short'] = '_short', + ['sizeof'] = '_sizeof', + ['stackalloc'] = '_stackalloc', + ['static'] = '_static', + ['string'] = '_string', + ['struct'] = '_struct', + ['switch'] = '_switch', + ['this'] = '_this', + ['throw'] = '_throw', + ['true'] = '_true', + ['try'] = '_try', + ['typeof'] = '_typeof', + ['uint'] = '_uint', + ['ulong'] = '_ulong', + ['unchecked'] = '_unchecked', + ['unsafe'] = '_unsafe', + ['ushort'] = '_ushort', + ['using'] = '_using', + ['virtual'] = '_virtual', + ['void'] = '_void', + ['volatile'] = '_volatile', + ['while'] = '_while', + ['add'] = '_add', + ['alias'] = '_alias', + ['ascending'] = '_ascending', + ['async'] = '_async', + ['await'] = '_await', + ['descending'] = '_descending', + ['dynamic'] = '_dynamic', + ['from'] = '_from', + ['get'] = '_get', + ['global'] = '_global', + ['group'] = '_group', + ['into'] = '_into', + ['join'] = '_join', + ['let'] = '_let', + ['nameof'] = '_nameof', + ['orderby'] = '_orderby', + ['remove'] = '_remove', + ['select'] = '_select', + ['set'] = '_set', + ['value'] = '_value', + ['var'] = '_var', + ['when'] = '_when', + ['where'] = '_where', + ['yield'] = '_yield' +} + +local usedNatives = {} + +local t1, t2, t3, t4 = '\t', '\t\t', '\t\t\t', '\t\t\t\t' +compatibility = {} -- need to be global +version = { 2, 0, 0 } -- need to be global + +-- default code patterns, changing these can result in bad code generation or type mismatch +local valType = 'ulong' -- used as the default + +local valBase = { 'ulong {0}', '{0}' } +local valWrap = { nil, 'N64.Val({0})', nil } + +local fixed = 'fixed(void* p_{0} = &{0})' -- used to fix parameters and get their pointer +local refBase = { 'ref ulong {0}', '(ulong)p_{0}', fixed } +local refWrap = { 'var _{0} = N64.Val({0})', 'ref _{0}', '{0} = N64.To_{1}(_{0})' } + +-- Explanation: +-- . = takes any character, used for custom code generation +-- %W = any valid character for types and/or methods +-- {0} or {0-1} = input that'll be replaced, '{0}' will be replaced with argument names, '{1}' will be replaced by type names +-- +-- [requested_type] = { +-- [1] = [%W] -- wrapper argument and return type (unless overriden) +-- [2] = -- base method code (invocation) +-- { +-- [1] = [.{0}], [required] -- parameter declaration +-- [2] = [.{0}], [required] -- how will the parameter be passed into the ulong[] buffer, e.g.: for strings we use "N64.Str({0})" +-- [3] = [.{0}], -- pre stack code used to pin/fix memory or a using statement +-- }, +-- [3] = -- wrapper method code +-- { +-- [1] = [.{0}], -- pre base-call code, e.g.: "var _{0} = N64.Val({0})" so we can pass {0} as a ref +-- [2] = [.{0}], [required] -- how will the parameter be passed into the base function, e.g.: "ref _{0}" if using the above pre base-call code +-- [3] = [.{0}] -- post base-call code, e.g.: "{0} = _{0}" to store the retrieved value back into the given parameter +-- }, +-- [4] = -- return overrides, can be used to dereference types, send pointers back as ulong, etc. +-- { +-- [1] = [%W], or "ulong" -- base method return type, e.g.: "int" by dereferencing the "ref int" +-- [2] = [.{0-1]}], or "*({1}*){0}" -- how the base function will interpret the ulong* value(s) +-- [3] = [%W], or "ulong" -- wrapper method return type, e.g.: "object" by deserializing the "OutPacket" +-- [4] = [%W{0-1}] or "N64.To_{1}({0})" -- how wrapper functions will interpret the base function's return value +-- }, +-- typeSize = [0-9] or 1 -- override the amount of 64 bit slots taken for this type +-- compatType = [%W] or wrapperType[4][1] -- use this type for compat type matching instead of the parameter or return types, reduces the amount of types +-- } + +local wrapperTypes = { + [false] = -- by value + { + ['bool'] = { 'bool', valBase, valWrap }, + ['byte'] = { 'byte', valBase, valWrap }, + ['ushort'] = { 'ushort', valBase, valWrap }, + ['uint'] = { 'uint', valBase, valWrap }, + ['ulong'] = { 'ulong', { 'ulong {0}', '{0}' }, { nil, '{0}', nil } }, + ['sbyte'] = { 'sbyte', valBase, valWrap }, + ['short'] = { 'short', valBase, valWrap }, + ['int'] = { 'int', valBase, valWrap }, + ['long'] = { 'long', valBase, valWrap }, + ['float'] = { 'float', valBase, valWrap }, + ['double'] = { 'double', valBase, valWrap }, + + ['Hash'] = { 'uint', valBase, valWrap }, + ['func'] = { 'System.Delegate', + { 'InFunc {0}', '(ulong)p_{0}', 'fixed(void* p_{0} = {0}.value)' }, + { nil, '{0}', nil }, + { 'OutFunc', '*(OutFunc*){0}', 'System.Delegate', '(System.Delegate){0}' }}, + ['string'] = { 'CString', + { 'CString {0}', '(ulong)p_{0}', 'fixed(void* p_{0} = {0}?.value)' }, + { nil, '{0}', nil }, + { 'OutString', '*(OutString*){0}', 'OutString', '{0}' }, compatType = 'string' }, + ['object'] = { 'object', + { 'InPacket {0}', '(ulong)p_{0}, (ulong){0}.value?.LongLength', 'fixed(void* p_{0} = {0}.value)' }, + { nil, 'InPacket.Serialize({0})', nil }, + { 'OutPacket', '*(OutPacket*){0}', 'object', '{0}.Deserialize()' }, typeSize = 2, compatType = 'object' }, + ['Vector3'] = { 'Vector3', + { 'ulong {0}_x, ulong {0}_y, ulong {0}_z', '{0}_x, {0}_y, {0}_z' }, + { nil, 'N64.Val({0}.X), N64.Val({0}.Y), N64.Val({0}.Z)', nil }, + { 'Vector3', '(Vector3)(*(NativeVector3*){0})', 'Vector3', '{0}' }, typeSize = 3 + }, + }, + [true] = -- by ref + { + ['bool'] = { 'ref bool', refBase, refWrap, { 'bool' } }, + ['byte'] = { 'ref byte', refBase, refWrap, { 'byte' } }, + ['ushort'] = { 'ref ushort', refBase, refWrap, { 'ushort' } }, + ['uint'] = { 'ref uint', refBase, refWrap, { 'uint' } }, + ['ulong'] = { 'ref ulong', refBase, { nil, 'ref {0}', nil }, { 'ulong' }}, + ['sbyte'] = { 'ref sbyte', refBase, refWrap, { 'sbyte' } }, + ['short'] = { 'ref short', refBase, refWrap, { 'short' } }, + ['int'] = { 'ref int', refBase, refWrap, { 'int' } }, + ['long'] = { 'ref long', refBase, refWrap, { 'long' } }, + ['float'] = { 'ref float', refBase, refWrap, { 'float' } }, + ['double'] = { 'ref double', refBase, refWrap, { 'double' } }, + + ['Hash'] = { 'ref uint', refBase, refWrap, { 'uint', '*(uint*){0}', '{0}' }, { 'uint' } }, + ['Vector3'] = { 'ref Vector3', { 'ref Vector3 {0}', '(ulong)p_{0}', fixed }, { nil, 'ref {0}', nil }, { 'Vector3', nil, 'Vector3' } }, + } +} + +-- some types have different nativeType values than what we're using in C#, correct them here +-- can be seen as: overrideParamTypes[isPointer][type.name] or type.nativeType +local overrideParamTypes = { + [false] = -- by value + { + ['Hash'] = { 'uint', false }, + ['Any'] = { 'long', false }, + ['Vector3'] = { 'Vector3', false }, + }, + [true] = -- by ref + { + ['Hash'] = { 'uint', true }, + ['Any'] = { 'long', true }, + ['AnyPtr'] = { 'long', true }, + ['Vector3'] = { 'Vector3', true }, + } +} + +local overrideReturnTypes = { + ['Hash'] = 'uint', + ['Any'] = 'long', + ['Any*'] = 'long', + ['AnyPtr'] = 'long', + ['Vector3'] = 'Vector3', + + ['ulong*'] = 'ulong', -- compat file uses ulong* instead of Any* +} + +-- Explanation: +-- +-- compatWrapperTypes[base_method_type][compat_method_type] +-- +-- { +--- [1] = [.{0}], -- pre base-call code, e.g.: "var _{0} = N64.Val({0})" so we can pass {0} as a ref +-- [2] = [.{0}], [required] -- how will the parameter be passed into the base function, e.g.: "ref _{0}" if using the above pre base-call code +-- [3] = [.{0}] -- post base-call code, e.g.: "{0} = _{0}" to store the retrieved value back into the given parameter +-- [4] = [.{0}] or '{0}' -- return value conversion +-- } +-- +-- [*]['default'] are used for the missing arguments to the base method +-- +-- SEGFAULT : (chance of a) Read/Write Access Violation + +local compatWrapperTypes = { + ['string'] = { + ['string'] = { nil, '{0}', nil }, + ['ulong'] = { nil, 'null', nil }, + ['ulong*'] = { nil, 'null', nil }, -- prev: SEGFAULT + ['Vector3'] = { nil, 'null', nil }, -- shouldn't appear + ['Vector3*'] = { nil, 'null', nil }, + ['default'] = { nil, 'null', nil }, + }, + ['ulong'] = { + ['string'] = { nil, '0', nil, '0' }, + ['ulong'] = { nil, '{0}', nil }, + ['ulong*'] = { nil, '{0}', nil }, + ['Vector3'] = { nil, '{0}.X', nil, '*(ulong*)&{0}' }, -- shouldn't appear + ['Vector3*'] = { nil, '{0}.X', nil, '*(ulong*)&{0}' }, + ['default'] = { nil, '0', nil }, + }, + ['ulong*'] = { + ['string'] = { 'ulong _{0}', 'ref _{0}', nil }, -- prev: SEGFAULT + ['ulong'] = { nil, 'ref {0}', nil }, -- prev: SEGFAULT + ['ulong*'] = { nil, 'ref {0}', nil }, + ['Vector3'] = { nil, 'ref *(ulong*)&{0}.X', nil }, -- shouldn't appear, prev: SEGFAULT + ['Vector3*'] = { nil, 'ref *(ulong*)&{0}.X', nil }, + ['default'] = { 'ulong _{0} = 0', 'ref _{0}', nil }, + }, + ['Vector3'] = { + ['string'] = { nil, 'new Vector3(0.0f)', nil, 'default' }, + ['ulong'] = { nil, 'new Vector3(N64.To_float({0}))', nil, 'new Vector3(N64.To_float({0}))' }, + ['ulong*'] = { nil, 'new Vector3({0})', nil }, -- ptr interpreted as the Vec3's float X, ref var doesn't change + ['Vector3'] = { nil, '{0}', nil }, + ['Vector3*'] = { nil, '{0}', nil }, + ['default'] = { nil, 'default(Vector3)', nil }, + }, + ['Vector3*'] = { + ['string'] = { 'Vector3 _{0};\n', 'ref _{0}', nil }, + ['ulong*'] = { 'Vector3 _{0} = new Vector3(N64.To_float({0}))', 'ref _{0}', nil }, + ['ulong*'] = { 'Vector3 _{0} = new Vector3({0})', 'ref _{0}', '{0} = N64.Val(_{0}.X)' }, + ['Vector3'] = { nil, 'ref {0}', nil }, + ['Vector3*'] = { nil, 'ref {0}', nil }, + ['default'] = { 'Vector3 _{0} = default', 'ref _{0}', nil }, + }, +} + +local function GetOrDefault(tbl, key, def) + return (tbl and tbl[key]) or def +end + +local function ToCType(typ) + -- e.g.: turn 'ref uint' into `uint*` + local s1, s2 = typ:match('^(%w+)%s?(%w*)') + return s2 ~= '' and s2..'*' or s1 +end + +local function ArraysEqual(left, right) + local length = #left; + if (length ~= #right) then return false; end + + for i = 1, length do + if (left[i] ~= right[i]) then return false; end + end + + return true; +end + +local function ToFunctionName(name) + return name:lower():gsub('^0x', 'n_0x'):gsub('_(%a)', string.upper):gsub('^(%a)', string.upper) +end + +local function ToUniqueName(nativeName) + local count = usedNatives[nativeName] + if not count then + usedNatives[nativeName] = 1 + return '' + end + + usedNatives[nativeName] = count + 1 + return tostring(count) +end + +local function GetWrapperTypeDirect(typ, isPointer) + local wrapTyp = wrapperTypes[isPointer][typ] + if wrapTyp then return wrapTyp end + + io.stderr:write('type is nil for: ', typ, ' as a ', (isPointer and 'pointer\n' or 'non-pointer\n'), debug.traceback(), '\n') + return wrapperTypes[isPointer][valType] +end + +local function GetWrapperType(typ, isPointer) + local override = overrideParamTypes[isPointer][typ.name] + if override then + return GetWrapperTypeDirect(table.unpack(override)), override[2] + end + + return GetWrapperTypeDirect(typ.nativeType, isPointer), isPointer +end + +local function GetReturnType(typ) + if not typ then return { 'void', {}, {}, {'void'} } end + + local typ, ptr = (overrideReturnTypes[typ.name] or typ.nativeType):match('(%w+)(%*?)') + local isPointer = ptr ~= '' + return GetWrapperTypeDirect(typ, isPointer), isPointer +end + +local function ToNonLanguageName(str) + return langWords[str] or str +end + +local function TrimAndSanitizeXML(str) + return trim(str):gsub('%W', { ['&'] = '&', ['<'] = '<', ['>'] = '>', ['\n'] = '\n' .. t2 .. '/// ' }) +end + +local function FormatDocSectionXML(str, openTag, closeTag, allowEmptyTag) + if (str) then + return t2..'/// '..openTag..'\n' + ..t2..'/// '..TrimAndSanitizeXML(str)..'\n' + ..t2..'/// '..closeTag..'\n'; + end + + return allowEmptyTag and t2 .. '/// ' .. openTag:sub(1, -2) .. ' />\n' or '' +end + +local function FormatDocXML(native) + local d = parseDocString(native) + + if d then + local str = FormatDocSectionXML(d.summary, '', '') + + if d.hasParams then + for _, v in ipairs(d.params) do + str = str .. FormatDocSectionXML(v[2], '', '', true) + end + end + + if d.returns then + str = str .. FormatDocSectionXML(d.returns, '', '') + end + + return str + end + + return '' +end + +local function LoadCompatibilityMethods() + local compatFileName = arg[1]:gsub('.lua', '_'..gApiSet..'_compat.lua') + local compatFile = loadfile(compatFileName) + if compatFile then + assert(compatFile)() + io.stderr:write('\tIncluding compatibility methods, ', compatFileName, ' file is used.\n') + end +end + +local function GetCompatCode(to, from) + local result = compatWrapperTypes[to] + if result then + result = result[from] + if result then return result end + end + + return nil +end + +local function GetAndFormatCompatCode(to, from, index, pattern, value) + local result = GetCompatCode(to, from) + if result then + result = result[index] + if result then + return result:gsub(pattern, value), true + end + end + + return value, false +end + +local function PrintCompatibilityMethods(native, nativeReturnType, latestSignature) + local compatMethods = compatibility[tonumber(native.hash)] + if compatMethods then + local length = #compatMethods + + local toMethodParam = latestSignature + local toMethodParamLength = #latestSignature + local toMethodReturn = latestSignature[1]; + + for c = 1, length do + local curMethod = compatMethods[c] + curMethod[1] = overrideReturnTypes[curMethod[1]] or curMethod[1]; + + if not ArraysEqual(curMethod, latestSignature) then + local hasResult = curMethod[1] ~= 'void' + local wrapRetType = GetReturnType(hasResult and { name = curMethod[1], nativeType = curMethod[1] }) + local resultType = GetOrDefault(wrapRetType[4], 1, valType) + + local init, body, post = '', (native.returns and nativeReturnType..'_' or 'void_')..native.hash..'(', '' + + if hasResult and native.returns then + body = 'var __res = ' .. body + end + + io.stdout:write('\n', t2, '[System.Obsolete("This method is obsolete", true)]\n') + io.stdout:write(t2, 'public static unsafe ', resultType, ' ', (hasResult and resultType..'_' or 'void_'), native.hash, '(') + + -- setup all arguments + local i, argn, comma = 2, math.min(#curMethod, toMethodParamLength), '' + while i <= argn do + local nativeType, toArgType, paramName = curMethod[i], toMethodParam[i], 'p'..tostring(i - 2) + if toArgType then + local compatCode = GetCompatCode(toArgType, nativeType) + if compatCode then + if compatCode[1] then init = init .. t3 .. compatCode[1]:gsub('{0}', paramName) .. ';\n' end + body = body .. comma .. compatCode[2]:gsub('{0}', paramName) + if compatCode[3] then post = post .. t3 .. compatCode[3]:gsub('{0}', paramName) .. ';\n' end + else + body = body .. comma .. paramName + end + else + body = body .. comma .. paramName + end + + local typeName, ptr = nativeType:match('(%w+)(%*?)') + nativeType = GetWrapperTypeDirect(typeName, ptr == '*')[1] + io.stdout:write(comma, nativeType, ' ', paramName) + + comma, i = ', ', i + 1 + end + + -- fill in blanks + while i <= toMethodParamLength do + local fromType, toArgType = 'default', toMethodParam[i] + local compatCode = GetCompatCode(toArgType, fromType) + if compatCode then + if compatCode[1] then init = init .. t3 .. compatCode[1]:gsub('{0}', fromType) .. ';\n' end + body = body .. comma .. compatCode[2]:gsub('{0}', fromType) + if compatCode[3] then post = post .. t3 .. compatCode[3]:gsub('{0}', fromType) .. ';\n' end + else + body = body .. comma .. 'default' + end + comma, i = ', ', i + 1 + end + + io.stdout:write(')\n', t2, '{\n', init, t3, body, ');\n', post) + + -- returning and conversion + if hasResult then + local result, found = 'default', true + if native.returns then + result, found = GetAndFormatCompatCode(wrapRetType.compatType or resultType, toMethodReturn, 4, '{0}', '__res') + if not result then io.stderr:write('No proper conversion found "', nativeReturnType, '" to "', resultType, '" in native ', native.name) end + end + + io.stdout:write(t3, 'return ' .. result .. ';\n') + end + + io.stdout:write(t2, '}\n') + end + end + end +end + +local function PrintNativeMethod(native) + local wrapRetType, writeVector3Out = GetReturnType(native.returns), false + local retType = GetOrDefault(wrapRetType[4], 1, valType) + local hasResult = retType ~= 'void' + local funcSignature = { ToCType(wrapRetType.compatType or retType) } + + io.stdout:write(t2, '[System.Security.SecuritySafeCritical]\n', t2, 'public static unsafe ', retType, ' ', + (hasResult and retType:gsub('ref ', '')..'_' or 'void_'), native.hash, '(') + + -- write parameters and append argument string for the ulong stack + local argn, fixed, stack = 0, '', '' + local comma, argSize = '', #native.arguments + for i = 1, argSize do + local arg = native.arguments[i] + local name, wrapperType, asPointer = ToNonLanguageName(arg.name), GetWrapperType(arg.type, arg.pointer == true) + + io.stdout:write(comma, wrapperType[2][1]:gsub('{0}', name), '') + stack = stack .. comma .. wrapperType[2][2]:gsub('{0}', name) + + if wrapperType[2][3] then fixed = fixed..t3..wrapperType[2][3]:gsub('{0}', name)..'\n' end + + -- used for the compatibility system + local cType = ToCType(wrapperType.compatType or wrapperType[2][1]) + if cType == 'Vector3' then + table.insert(funcSignature, valType) + table.insert(funcSignature, valType) + table.insert(funcSignature, valType) + else + table.insert(funcSignature, cType) + end + + argn = argn + (wrapperType.typeSize or 1) + if wrapperType[1] == 'ref Vector3' then writeVector3Out = true end + + comma = ', ' + end + + io.stdout:write(')\n', t2, '{\n', fixed, t3, '{\n', t4, 'ulong* __data = stackalloc ulong[] { ', stack) + + -- make sure there's enough room to store the return types + local resultSize = hasResult and (wrapRetType.typeSize or 1) or -1 + while argn < resultSize do + io.stdout:write(comma, '0') + comma, argn = ', ', argn + 1 + end + + -- invoke native + io.stdout:write(' };\n', t4, 'ScriptContext.InvokeNative') + if writeVector3Out then io.stdout:write('OutVec3') end -- will call the Vector3 copy function, required to pass on the values + io.stdout:write('(ref s_', native.hash, ', ', native.hash, ', __data, ', argn, ');\n') + + -- return result + if hasResult then + local resultInterpret = GetOrDefault(wrapRetType[4], 2, '*({1}*){0}'); + io.stdout:write(t4, 'return ', resultInterpret:gsub('{%d}', {['{0}'] = '__data', ['{1}'] = retType }), ';\n') + end + + io.stdout:write(t3, '}\n', t2, '}\n\n', t2, 'private static UIntPtr s_', native.hash, ';\n') + PrintCompatibilityMethods(native, retType, funcSignature) + io.stdout:write('\n') +end + +local function PrintWrappers(native) + local parm, init, args, post, forw = '', '', '', '', '', '' + + local comma, argSize = '', #native.arguments; + for i = 1, argSize do + local arg = native.arguments[i]; + local name, wrapperType, ptr = ToNonLanguageName(arg.name), GetWrapperType(arg.type, arg.pointer == true) + + parm = parm .. comma .. wrapperType[1]..' '..name + if wrapperType[3][1] then init = init .. t3 .. wrapperType[3][1]:gsub('{0}', name) .. ';\n' end + args = args .. comma .. wrapperType[3][2]:gsub('{0}', name) + if wrapperType[3][3] then post = post .. t3 .. wrapperType[3][3]:gsub('{%d}', {['{0}'] = name, ['{1}'] = GetOrDefault(wrapperType[4], 1, wrapperType[1]) }) .. ';\n' end + forw = forw .. comma .. (ptr and 'ref ' or '') .. name + + comma = ', ' + end + + local wrapRetType = GetReturnType(native.returns); + local doc, retType = FormatDocXML(native), GetOrDefault(wrapRetType[4], 3, wrapRetType[1]) + local nativeName, calleeRetType = native.displayName .. ToUniqueName(native.displayName), GetOrDefault(wrapRetType[4], 1, valType) + + io.stdout:write(doc, t2, 'public static ', retType, ' ', nativeName, '(', parm, ')\n', t2, '{\n', init, t3) + + if retType ~= 'void' then + io.stdout:write('var __res = ') + local convertType = GetOrDefault(wrapRetType[4], 4, 'N64.To_{1}({0})'):gsub('{.}', { ['{0}'] = '__res', ['{1}'] = retType }) + post = post .. t3 .. 'return ' .. convertType .. ';\n' + end + + io.stdout:write('NativesImpl.', (native.returns and calleeRetType..'_' or 'void_'), native.hash, '(', args, ');\n', post, t2, '}\n\n') + + -- alias wrappers for native + for _, alias in ipairs(native.aliases) do + local aliasName = ToFunctionName(alias) + if native.displayName ~= aliasName then + io.stdout:write(doc, t2, 'public static ', retType, ' ', aliasName, '(', parm, ') => ', nativeName, '(', forw, ');\n\n') + end + end +end + +-- sort the natives table +local _natives, nativeCount = {}, #natives + +for i = 1, nativeCount do + local native = natives[i] + if matchApiSet(native) then + native.displayName = ToFunctionName(native.name) + table.insert(_natives, native) + end +end + +nativeCount = #_natives + +-- sort alphabetically +table.sort(_natives, function(a, b) return a.displayName < b.displayName end) + +LoadCompatibilityMethods() + +-- get top constant +local topConstant = 'Unknown' +local productName = gApiSet == 'server' and 'server' or arg[1]:match('_(%w+)%..+$') +if productName then + local product = PRODUCTS[productName] + if product then + topConstant = product[1]; + productName = product[2]; + else + local err = 'Error, couldn\'t find game "'..productName..'" in the PRODUCTS list, please pick one from the list in "codegen_out_cs_v2.lua"' + topConstant = 'Unknown // '..err + io.stderr:write(err) + end +else + local err = 'Error, couldn\'t get game name from "'..arg[1]..'", doesn\'t match pattern "_(%w+)%..+$"' + topConstant = 'Unknown // '..err + productName = 'Unknown' + io.stderr:write(err) +end + +io.stdout:write('#if ', topConstant, '\nusing System;\nusing CitizenFX.Core;\nusing CitizenFX.Core.Native;\n\n') + +-- ignore missing argument documentation warnings +io.stdout:write('#pragma warning disable CS1573\n\n') + +-- versioning +io.stdout:write('#if NATIVE_WRAPPER_USE_VERSION\n') +io.stdout:write('[assembly: System.Reflection.AssemblyVersion("', table.concat(version, '.'), '")]\n') +io.stdout:write('[assembly: System.Reflection.AssemblyFileVersion("', table.concat(version, '.'), '")]\n') +io.stdout:write('#endif\n\n') + +io.stdout:write('namespace CitizenFX.', productName, '.Native\n{\n') + +-- write enums +dofile("./codegen_out_enum.lua") + +-- native base methods +io.stdout:write('\n#if NATIVE_IMPL_INCLUDE\n', t1, 'public static class NativesImpl\n', t1 ,'{\n') +for i = 1, nativeCount do + PrintNativeMethod(_natives[i]) +end +io.stdout:write(t1, '}\n#endif\n') + +-- native wrappers +io.stdout:write('\n#if NATIVE_WRAPPERS_INCLUDE\n', t1, 'public static partial class Natives\n', t1 ,'{\n') +for i = 1, nativeCount do + PrintWrappers(_natives[i]) +end +io.stdout:write(t1, '}\n#endif\n') + +-- PAS bits +io.stdout:write('#if NATIVE_PTR_CHECKS_INCLUDE\n') +io.stdout:write(t1, 'internal static partial class PointerArgumentSafety\n', t1, '{\n') +io.stdout:write(t2, 'static System.Collections.Generic.Dictionary s_nativeInfo = new System.Collections.Generic.Dictionary\n', t2, '{\n') + +do + local primitive_t = valType; + local func_t = wrapperTypes[false]['func'][4][1]; + local vector3_t = wrapperTypes[false]['Vector3'][4][1]; + local string_t = wrapperTypes[false]['string'][4][1]; + local object_t = wrapperTypes[false]['object'][4][1]; + + for _, native in pairs(_natives) do + if native.returns then + local typ = GetOrDefault(GetReturnType(native.returns)[4], 1, primitive_t) + + io.stdout:write(t3, '[', native.hash, '] = ') + + if native.name == 'INVOKE_FUNCTION_REFERENCE' then + io.stdout:write('0x80') + elseif typ == primitive_t then + io.stdout:write('0x01') + elseif typ == vector3_t then + io.stdout:write('0x03') + elseif typ == string_t then + io.stdout:write('0x40') + elseif typ == object_t then + io.stdout:write('0x20') + else + io.stdout:write('0x00') + end + + io.stdout:write(', // ', (native.ns or 'UNKNOWN'), '/', native.name, '\n') + end + end +end + +io.stdout:write(t2, '};\n', t1, '}\n#endif\n}\n#endif') diff --git a/ext/natives/codegen_out_enum.lua b/ext/natives/codegen_out_enum.lua index b5622d7c25..52223f3862 100644 --- a/ext/natives/codegen_out_enum.lua +++ b/ext/natives/codegen_out_enum.lua @@ -23,8 +23,7 @@ local function processDoc(native) end local hadSet = {} - -print('\tpublic enum Hash : ulong\n\t{') +io.stdout:write('#if !MONO_V2 || NATIVE_HASHES_INCLUDE\n\tpublic enum Hash : ulong\n\t{\n') for _, v in pairs(_natives) do if matchApiSet(v) then @@ -32,12 +31,11 @@ for _, v in pairs(_natives) do if name:sub(1, 2) ~= '0x' then if v.doc ~= nil then - print(processDoc(v)) + io.stdout:write(processDoc(v), '\n') end if not hadSet[name] then - print(string.format(t .. "%s = %s,", name, v.hash)) - + io.stdout:write(t, name, ' = ', v.hash, ',\n') hadSet[name] = true end @@ -50,15 +48,13 @@ for _, v in pairs(_natives) do end end -for _, v in pairs(aliases) do +for _, v in ipairs(aliases) do if not hadSet[v.alias] then - print(string.format(t .. "/// ")) - print(string.format(t .. "/// Deprecated name, use %s instead", v.name)) - print(string.format(t .. "/// ")) - print(string.format(t .. "%s = %s, // %s", v.alias, v.hash, v.name)) + io.stdout:write(t, '[System.Obsolete("Deprecated name, use ', v.name, ' instead")]\n') + io.stdout:write(t, v.alias, ' = ', v.hash, ', // ', v.name, '\n') hadSet[v.alias] = true end end -print('\t}') +io.stdout:write('\t}\n#endif\n') diff --git a/prebuild_natives.cmd b/prebuild_natives.cmd index ffe6febc60..875931557f 100644 --- a/prebuild_natives.cmd +++ b/prebuild_natives.cmd @@ -22,44 +22,13 @@ if errorlevel 1 ( pushd ext\natives\ mkdir inp -%systemroot%\system32\curl -z inp\natives_global.lua -Lo inp\natives_global_new.lua https://runtime.fivem.net/doc/natives.lua -%systemroot%\system32\curl -z inp\natives_rdr3.lua -Lo inp\natives_rdr3_new.lua https://runtime.fivem.net/doc/natives_rdr_tmp.lua -%systemroot%\system32\curl -z inp\natives_ny.lua -Lo inp\natives_ny_new.lua https://runtime.fivem.net/doc/natives_ny_tmp.lua +call:UpdateToLatest inp\natives_global.lua https://runtime.fivem.net/doc/natives.lua +call:UpdateToLatest inp\natives_rdr3.lua https://runtime.fivem.net/doc/natives_rdr_tmp.lua +call:UpdateToLatest inp\natives_ny.lua https://runtime.fivem.net/doc/natives_ny_tmp.lua -if exist inp\natives_global.lua ( - diff inp\natives_global.lua inp\natives_global_new.lua > nul +call:UpdateToLatest inp\natives_global_client_compat.lua https://runtime.fivem.net/doc/natives_global_client_compat.lua - if errorlevel 0 ( - copy /y inp\natives_global_new.lua inp\natives_global.lua - ) -) else ( - copy /y inp\natives_global_new.lua inp\natives_global.lua -) - -if exist inp\natives_rdr3.lua ( - diff inp\natives_rdr3.lua inp\natives_rdr3_new.lua > nul - - if errorlevel 0 ( - copy /y inp\natives_rdr3_new.lua inp\natives_rdr3.lua - ) -) else ( - copy /y inp\natives_rdr3_new.lua inp\natives_rdr3.lua -) - -if exist inp\natives_ny.lua ( - diff inp\natives_ny.lua inp\natives_ny_new.lua > nul - - if errorlevel 0 ( - copy /y inp\natives_ny_new.lua inp\natives_ny.lua - ) -) else ( - copy /y inp\natives_ny_new.lua inp\natives_ny.lua -) - -del inp\natives_global_new.lua -del inp\natives_rdr3_new.lua -del inp\natives_ny_new.lua popd pushd ext\native-doc-gen\ @@ -84,8 +53,37 @@ if errorlevel 1 ( xcopy /y out\*.zip ..\..\data\shared\citizen\scripting\lua xcopy /y out\*.cs ..\..\code\client\clrcore + xcopy /y out\v2\*.cs ..\..\code\client\clrcore-v2\Native xcopy /y out\Natives*.h ..\..\code\components\citizen-scripting-lua\src xcopy /y out\PASGen.h ..\..\code\components\rage-scripting-five\src ) popd +goto :eof + +:: Functions + +:UpdateToLatest +echo Updating %~1 +%systemroot%\system32\curl -z %~1 -Lo %~1.new %~2 + +if not errorlevel 0 ( + echo cURL exited with error code %errorlevel%. +) else ( + if exist %~1.new ( + if exist %~1 ( + diff %~1 %~1.new > nul + + if not errorlevel 1 ( + del %~1.new + exit /B 0 + ) + ) + + move /y %~1.new %~1 + ) else ( + echo File is up-to-date. + ) +) + +exit /B 0