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