Merge pull request 'Updated to 4.4-rc1 & better justfile' (#1) from Sara/godot-module-template:development into development
Reviewed-on: hertog/godot-module-template#1
This commit is contained in:
		
						commit
						d08586768d
					
				| 
						 | 
				
			
			@ -1,27 +1,52 @@
 | 
			
		|||
# Commented out parameters are those with the same value as base LLVM style.
 | 
			
		||||
# We can uncomment them if we want to change their value, or enforce the
 | 
			
		||||
# chosen value in case the base style changes (last sync: Clang 14.0).
 | 
			
		||||
---
 | 
			
		||||
### General config, applies to all languages ###
 | 
			
		||||
# chosen value in case the base style changes (last sync: Clang 17.0.6).
 | 
			
		||||
BasedOnStyle: LLVM
 | 
			
		||||
AccessModifierOffset: -4
 | 
			
		||||
AlignAfterOpenBracket: DontAlign
 | 
			
		||||
# AlignArrayOfStructures: None
 | 
			
		||||
# AlignConsecutiveMacros: None
 | 
			
		||||
# AlignConsecutiveAssignments: None
 | 
			
		||||
# AlignConsecutiveBitFields: None
 | 
			
		||||
# AlignConsecutiveDeclarations: None
 | 
			
		||||
# AlignConsecutiveAssignments:
 | 
			
		||||
#   Enabled: false
 | 
			
		||||
#   AcrossEmptyLines: false
 | 
			
		||||
#   AcrossComments: false
 | 
			
		||||
#   AlignCompound: false
 | 
			
		||||
#   PadOperators: true
 | 
			
		||||
# AlignConsecutiveBitFields:
 | 
			
		||||
#   Enabled: false
 | 
			
		||||
#   AcrossEmptyLines: false
 | 
			
		||||
#   AcrossComments: false
 | 
			
		||||
#   AlignCompound: false
 | 
			
		||||
#   PadOperators: false
 | 
			
		||||
# AlignConsecutiveDeclarations:
 | 
			
		||||
#   Enabled: false
 | 
			
		||||
#   AcrossEmptyLines: false
 | 
			
		||||
#   AcrossComments: false
 | 
			
		||||
#   AlignCompound: false
 | 
			
		||||
#   PadOperators: false
 | 
			
		||||
# AlignConsecutiveMacros:
 | 
			
		||||
#   Enabled: false
 | 
			
		||||
#   AcrossEmptyLines: false
 | 
			
		||||
#   AcrossComments: false
 | 
			
		||||
#   AlignCompound: false
 | 
			
		||||
#   PadOperators: false
 | 
			
		||||
# AlignConsecutiveShortCaseStatements:
 | 
			
		||||
#   Enabled: false
 | 
			
		||||
#   AcrossEmptyLines: false
 | 
			
		||||
#   AcrossComments: false
 | 
			
		||||
#   AlignCaseColons: false
 | 
			
		||||
# AlignEscapedNewlines: Right
 | 
			
		||||
AlignOperands: DontAlign
 | 
			
		||||
AlignTrailingComments: false
 | 
			
		||||
AlignTrailingComments:
 | 
			
		||||
  Kind: Never
 | 
			
		||||
  OverEmptyLines: 0
 | 
			
		||||
# AllowAllArgumentsOnNextLine: true
 | 
			
		||||
AllowAllParametersOfDeclarationOnNextLine: false
 | 
			
		||||
# AllowShortEnumsOnASingleLine: true
 | 
			
		||||
# AllowShortBlocksOnASingleLine: Never
 | 
			
		||||
# AllowShortCaseLabelsOnASingleLine: false
 | 
			
		||||
# AllowShortFunctionsOnASingleLine: All
 | 
			
		||||
# AllowShortLambdasOnASingleLine: All
 | 
			
		||||
# AllowShortEnumsOnASingleLine: true
 | 
			
		||||
AllowShortFunctionsOnASingleLine: Inline
 | 
			
		||||
# AllowShortIfStatementsOnASingleLine: Never
 | 
			
		||||
# AllowShortLambdasOnASingleLine: All
 | 
			
		||||
# AllowShortLoopsOnASingleLine: false
 | 
			
		||||
# AlwaysBreakAfterDefinitionReturnType: None
 | 
			
		||||
# AlwaysBreakAfterReturnType: None
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +56,7 @@ AllowAllParametersOfDeclarationOnNextLine: false
 | 
			
		|||
#   - __capability
 | 
			
		||||
# BinPackArguments: true
 | 
			
		||||
# BinPackParameters: true
 | 
			
		||||
# BitFieldColonSpacing: Both
 | 
			
		||||
# BraceWrapping:
 | 
			
		||||
#   AfterCaseLabel: false
 | 
			
		||||
#   AfterClass: false
 | 
			
		||||
| 
						 | 
				
			
			@ -50,31 +76,28 @@ AllowAllParametersOfDeclarationOnNextLine: false
 | 
			
		|||
#   SplitEmptyFunction: true
 | 
			
		||||
#   SplitEmptyRecord: true
 | 
			
		||||
#   SplitEmptyNamespace: true
 | 
			
		||||
# BreakAfterAttributes: Never
 | 
			
		||||
# BreakAfterJavaFieldAnnotations: false
 | 
			
		||||
# BreakArrays: true
 | 
			
		||||
# BreakBeforeBinaryOperators: None
 | 
			
		||||
# BreakBeforeConceptDeclarations: true
 | 
			
		||||
# BreakBeforeBraces: Attach
 | 
			
		||||
# BreakBeforeInheritanceComma: false
 | 
			
		||||
# BreakInheritanceList: BeforeColon
 | 
			
		||||
# BreakBeforeConceptDeclarations: Always
 | 
			
		||||
# BreakBeforeInlineASMColon: OnlyMultiline
 | 
			
		||||
# BreakBeforeTernaryOperators: true
 | 
			
		||||
# BreakConstructorInitializersBeforeComma: false
 | 
			
		||||
BreakConstructorInitializers: AfterColon
 | 
			
		||||
# BreakInheritanceList: BeforeColon
 | 
			
		||||
# BreakStringLiterals: true
 | 
			
		||||
ColumnLimit: 0
 | 
			
		||||
# CommentPragmas:  '^ IWYU pragma:'
 | 
			
		||||
# QualifierAlignment: Leave
 | 
			
		||||
# CommentPragmas: "^ IWYU pragma:"
 | 
			
		||||
# CompactNamespaces: false
 | 
			
		||||
ConstructorInitializerIndentWidth: 8
 | 
			
		||||
ContinuationIndentWidth: 8
 | 
			
		||||
Cpp11BracedListStyle: false
 | 
			
		||||
# DeriveLineEnding: true
 | 
			
		||||
# DerivePointerAlignment: false
 | 
			
		||||
# DisableFormat: false
 | 
			
		||||
# EmptyLineAfterAccessModifier: Never
 | 
			
		||||
# EmptyLineBeforeAccessModifier: LogicalBlock
 | 
			
		||||
# ExperimentalAutoDetectBinPacking: false
 | 
			
		||||
# PackConstructorInitializers: BinPack
 | 
			
		||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
 | 
			
		||||
# AllowAllConstructorInitializersOnNextLine: true
 | 
			
		||||
# FixNamespaceComments: true
 | 
			
		||||
# ForEachMacros:
 | 
			
		||||
#   - foreach
 | 
			
		||||
| 
						 | 
				
			
			@ -84,32 +107,59 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
 | 
			
		|||
#   - KJ_IF_MAYBE
 | 
			
		||||
# IncludeBlocks: Preserve
 | 
			
		||||
IncludeCategories:
 | 
			
		||||
  - Regex:           '".*"'
 | 
			
		||||
  - Regex: ^".*"$
 | 
			
		||||
    Priority: 1
 | 
			
		||||
  - Regex:           '^<.*\.h>'
 | 
			
		||||
  - Regex: ^<.*\.h>$
 | 
			
		||||
    Priority: 2
 | 
			
		||||
  - Regex:           '^<.*'
 | 
			
		||||
  - Regex: ^<.*>$
 | 
			
		||||
    Priority: 3
 | 
			
		||||
# IncludeIsMainRegex: '(Test)?$'
 | 
			
		||||
# IncludeIsMainSourceRegex: ''
 | 
			
		||||
# IncludeIsMainRegex: (Test)?$
 | 
			
		||||
# IncludeIsMainSourceRegex: ""
 | 
			
		||||
# IndentAccessModifiers: false
 | 
			
		||||
IndentCaseLabels: true
 | 
			
		||||
# IndentCaseBlocks: false
 | 
			
		||||
IndentCaseLabels: true
 | 
			
		||||
# IndentExternBlock: AfterExternBlock
 | 
			
		||||
# IndentGotoLabels: true
 | 
			
		||||
# IndentPPDirectives: None
 | 
			
		||||
# IndentExternBlock: AfterExternBlock
 | 
			
		||||
# IndentRequires:  false
 | 
			
		||||
# IndentRequiresClause: true
 | 
			
		||||
IndentWidth: 4
 | 
			
		||||
# IndentWrappedFunctionNames: false
 | 
			
		||||
InsertBraces: true
 | 
			
		||||
# InsertNewlineAtEOF: false
 | 
			
		||||
# InsertTrailingCommas: None
 | 
			
		||||
# IntegerLiteralSeparator:
 | 
			
		||||
#   Binary: 0
 | 
			
		||||
#   BinaryMinDigits: 0
 | 
			
		||||
#   Decimal: 0
 | 
			
		||||
#   DecimalMinDigits: 0
 | 
			
		||||
#   Hex: 0
 | 
			
		||||
#   HexMinDigits: 0
 | 
			
		||||
JavaImportGroups:
 | 
			
		||||
  - org.godotengine
 | 
			
		||||
  - android
 | 
			
		||||
  - androidx
 | 
			
		||||
  - com.android
 | 
			
		||||
  - com.google
 | 
			
		||||
  - java
 | 
			
		||||
  - javax
 | 
			
		||||
# JavaScriptQuotes: Leave
 | 
			
		||||
# JavaScriptWrapImports: true
 | 
			
		||||
# KeepEmptyLinesAtEOF: false
 | 
			
		||||
KeepEmptyLinesAtTheStartOfBlocks: false
 | 
			
		||||
# LambdaBodyIndentation: Signature
 | 
			
		||||
# MacroBlockBegin: ''
 | 
			
		||||
# MacroBlockEnd:   ''
 | 
			
		||||
# Language: Cpp
 | 
			
		||||
# LineEnding: DeriveLF
 | 
			
		||||
# MacroBlockBegin: ""
 | 
			
		||||
# MacroBlockEnd: ""
 | 
			
		||||
# MaxEmptyLinesToKeep: 1
 | 
			
		||||
# NamespaceIndentation: None
 | 
			
		||||
# ObjCBinPackProtocolList: Auto
 | 
			
		||||
ObjCBlockIndentWidth: 4
 | 
			
		||||
# ObjCBreakBeforeNestedBlockParam: true
 | 
			
		||||
# ObjCSpaceAfterProperty: false
 | 
			
		||||
# ObjCSpaceBeforeProtocolList: true
 | 
			
		||||
# PPIndentWidth: -1
 | 
			
		||||
PackConstructorInitializers: NextLine
 | 
			
		||||
# PenaltyBreakAssignment: 2
 | 
			
		||||
# PenaltyBreakBeforeFirstCallParameter: 19
 | 
			
		||||
# PenaltyBreakComment: 300
 | 
			
		||||
| 
						 | 
				
			
			@ -118,82 +168,71 @@ KeepEmptyLinesAtTheStartOfBlocks: false
 | 
			
		|||
# PenaltyBreakString: 1000
 | 
			
		||||
# PenaltyBreakTemplateDeclaration: 10
 | 
			
		||||
# PenaltyExcessCharacter: 1000000
 | 
			
		||||
# PenaltyReturnTypeOnItsOwnLine: 60
 | 
			
		||||
# PenaltyIndentedWhitespace: 0
 | 
			
		||||
# PenaltyReturnTypeOnItsOwnLine: 60
 | 
			
		||||
# PointerAlignment: Right
 | 
			
		||||
# PPIndentWidth:   -1
 | 
			
		||||
# QualifierAlignment: Leave
 | 
			
		||||
# ReferenceAlignment: Pointer
 | 
			
		||||
# ReflowComments: true
 | 
			
		||||
# RemoveBracesLLVM: false
 | 
			
		||||
# RemoveParentheses: Leave
 | 
			
		||||
RemoveSemicolon: true
 | 
			
		||||
# RequiresClausePosition: OwnLine
 | 
			
		||||
# RequiresExpressionIndentation: OuterScope
 | 
			
		||||
# SeparateDefinitionBlocks: Leave
 | 
			
		||||
# ShortNamespaceLines: 1
 | 
			
		||||
# SortIncludes: CaseSensitive
 | 
			
		||||
# SortJavaStaticImport: Before
 | 
			
		||||
# SortUsingDeclarations: true
 | 
			
		||||
# SortUsingDeclarations: LexicographicNumeric
 | 
			
		||||
# SpaceAfterCStyleCast: false
 | 
			
		||||
# SpaceAfterLogicalNot: false
 | 
			
		||||
# SpaceAfterTemplateKeyword: true
 | 
			
		||||
# SpaceAroundPointerQualifiers: Default
 | 
			
		||||
# SpaceBeforeAssignmentOperators: true
 | 
			
		||||
# SpaceBeforeCaseColon: false
 | 
			
		||||
# SpaceBeforeCpp11BracedList: false
 | 
			
		||||
# SpaceBeforeCtorInitializerColon: true
 | 
			
		||||
# SpaceBeforeInheritanceColon: true
 | 
			
		||||
# SpaceBeforeJsonColon: false
 | 
			
		||||
# SpaceBeforeParens: ControlStatements
 | 
			
		||||
# SpaceBeforeParensOptions:
 | 
			
		||||
#   AfterControlStatements: true
 | 
			
		||||
#   AfterForeachMacros: true
 | 
			
		||||
#   AfterFunctionDefinitionName: false
 | 
			
		||||
#   AfterFunctionDeclarationName: false
 | 
			
		||||
#   AfterFunctionDefinitionName: false
 | 
			
		||||
#   AfterIfMacros: true
 | 
			
		||||
#   AfterOverloadedOperator: false
 | 
			
		||||
#   AfterRequiresInClause: false
 | 
			
		||||
#   AfterRequiresInExpression: false
 | 
			
		||||
#   BeforeNonEmptyParentheses: false
 | 
			
		||||
# SpaceAroundPointerQualifiers: Default
 | 
			
		||||
# SpaceBeforeRangeBasedForLoopColon: true
 | 
			
		||||
# SpaceBeforeSquareBrackets: false
 | 
			
		||||
# SpaceInEmptyBlock: false
 | 
			
		||||
# SpaceInEmptyParentheses: false
 | 
			
		||||
# SpacesBeforeTrailingComments: 1
 | 
			
		||||
# SpacesInAngles: Never
 | 
			
		||||
# SpacesInConditionalStatement: false
 | 
			
		||||
# SpacesInContainerLiterals: true
 | 
			
		||||
# SpacesInCStyleCastParentheses: false
 | 
			
		||||
## Godot TODO: We'll want to use a min of 1, but we need to see how to fix
 | 
			
		||||
## our comment capitalization at the same time.
 | 
			
		||||
SpacesInLineCommentPrefix:
 | 
			
		||||
  Minimum:         0
 | 
			
		||||
  Minimum: 0 # We want a minimum of 1 for comments, but allow 0 for disabled code.
 | 
			
		||||
  Maximum: -1
 | 
			
		||||
# SpacesInParentheses: false
 | 
			
		||||
# SpacesInParens: Never
 | 
			
		||||
# SpacesInParensOptions:
 | 
			
		||||
#   InConditionalStatements: false
 | 
			
		||||
#   InCStyleCasts: false
 | 
			
		||||
#   InEmptyParentheses: false
 | 
			
		||||
#   Other: false
 | 
			
		||||
# SpacesInSquareBrackets: false
 | 
			
		||||
# SpaceBeforeSquareBrackets: false
 | 
			
		||||
# BitFieldColonSpacing: Both
 | 
			
		||||
Standard: c++20
 | 
			
		||||
# StatementAttributeLikeMacros:
 | 
			
		||||
#   - Q_EMIT
 | 
			
		||||
# StatementMacros:
 | 
			
		||||
#   - Q_UNUSED
 | 
			
		||||
#   - QT_REQUIRE_VERSION
 | 
			
		||||
TabWidth: 4
 | 
			
		||||
# UseCRLF:         false
 | 
			
		||||
UseTab: Always
 | 
			
		||||
# VerilogBreakBetweenInstancePorts: true
 | 
			
		||||
# WhitespaceSensitiveMacros:
 | 
			
		||||
#   - STRINGIZE
 | 
			
		||||
#   - PP_STRINGIZE
 | 
			
		||||
#   - BOOST_PP_STRINGIZE
 | 
			
		||||
#   - NS_SWIFT_NAME
 | 
			
		||||
#   - CF_SWIFT_NAME
 | 
			
		||||
---
 | 
			
		||||
### C++ specific config ###
 | 
			
		||||
Language:        Cpp
 | 
			
		||||
Standard:        c++17
 | 
			
		||||
---
 | 
			
		||||
### ObjC specific config ###
 | 
			
		||||
Language:        ObjC
 | 
			
		||||
# ObjCBinPackProtocolList: Auto
 | 
			
		||||
ObjCBlockIndentWidth: 4
 | 
			
		||||
# ObjCBreakBeforeNestedBlockParam: true
 | 
			
		||||
# ObjCSpaceAfterProperty: false
 | 
			
		||||
# ObjCSpaceBeforeProtocolList: true
 | 
			
		||||
---
 | 
			
		||||
### Java specific config ###
 | 
			
		||||
Language:        Java
 | 
			
		||||
# BreakAfterJavaFieldAnnotations: false
 | 
			
		||||
JavaImportGroups: ['org.godotengine', 'android', 'androidx', 'com.android', 'com.google', 'java', 'javax']
 | 
			
		||||
...
 | 
			
		||||
#   - NS_SWIFT_NAME
 | 
			
		||||
#   - PP_STRINGIZE
 | 
			
		||||
#   - STRINGIZE
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,13 @@
 | 
			
		|||
---
 | 
			
		||||
Checks: >-
 | 
			
		||||
  -*,
 | 
			
		||||
  cppcoreguidelines-pro-type-member-init,
 | 
			
		||||
  modernize-redundant-void-arg,
 | 
			
		||||
  modernize-use-bool-literals,
 | 
			
		||||
  modernize-use-default-member-init,
 | 
			
		||||
  modernize-use-nullptr,
 | 
			
		||||
  readability-braces-around-statements,
 | 
			
		||||
  readability-redundant-member-init
 | 
			
		||||
WarningsAsErrors: ''
 | 
			
		||||
HeaderFileExtensions: ['', h, hh, hpp, hxx, inc, glsl]
 | 
			
		||||
Checks:
 | 
			
		||||
  - -*
 | 
			
		||||
  - cppcoreguidelines-pro-type-member-init
 | 
			
		||||
  - modernize-redundant-void-arg
 | 
			
		||||
  - modernize-use-bool-literals
 | 
			
		||||
  - modernize-use-default-member-init
 | 
			
		||||
  - modernize-use-nullptr
 | 
			
		||||
  - readability-braces-around-statements
 | 
			
		||||
  - readability-redundant-member-init
 | 
			
		||||
HeaderFileExtensions: ["", h, hh, hpp, hxx, inc, glsl]
 | 
			
		||||
ImplementationFileExtensions: [c, cc, cpp, cxx, m, mm, java]
 | 
			
		||||
HeaderFilterRegex: (core|doc|drivers|editor|main|modules|platform|scene|servers|tests)/
 | 
			
		||||
FormatStyle: file
 | 
			
		||||
| 
						 | 
				
			
			@ -19,4 +17,3 @@ CheckOptions:
 | 
			
		|||
  modernize-use-bool-literals.IgnoreMacros: false
 | 
			
		||||
  modernize-use-default-member-init.IgnoreMacros: false
 | 
			
		||||
  modernize-use-default-member-init.UseAssignment: true
 | 
			
		||||
...
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										31
									
								
								engine/.clangd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								engine/.clangd
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
# https://clangd.llvm.org/config
 | 
			
		||||
---
 | 
			
		||||
# Default conditions, apply everywhere.
 | 
			
		||||
 | 
			
		||||
Diagnostics:
 | 
			
		||||
  Includes:
 | 
			
		||||
    IgnoreHeader:
 | 
			
		||||
      - core/typedefs\.h # Our "main" header, featuring transitive includes; allow everywhere.
 | 
			
		||||
      - \.compat\.inc
 | 
			
		||||
---
 | 
			
		||||
# Header-specific conditions.
 | 
			
		||||
 | 
			
		||||
If:
 | 
			
		||||
  PathMatch: .*\.(h|hh|hpp|hxx|inc)
 | 
			
		||||
 | 
			
		||||
# Exclude certain, noisy warnings that lack full context. Replace with lowered severity if/when
 | 
			
		||||
# clangd gets diagnostic severity support. (See: https://github.com/clangd/clangd/issues/1937)
 | 
			
		||||
CompileFlags:
 | 
			
		||||
  Add:
 | 
			
		||||
    - -Wno-unneeded-internal-declaration
 | 
			
		||||
    - -Wno-unused-const-variable
 | 
			
		||||
    - -Wno-unused-function
 | 
			
		||||
    - -Wno-unused-variable
 | 
			
		||||
---
 | 
			
		||||
# Suppress all third-party warnings.
 | 
			
		||||
 | 
			
		||||
If:
 | 
			
		||||
  PathMatch: thirdparty/.*
 | 
			
		||||
 | 
			
		||||
Diagnostics:
 | 
			
		||||
  Suppress: "*"
 | 
			
		||||
| 
						 | 
				
			
			@ -3,24 +3,18 @@ root = true
 | 
			
		|||
[*]
 | 
			
		||||
charset = utf-8
 | 
			
		||||
end_of_line = lf
 | 
			
		||||
indent_size = 4
 | 
			
		||||
indent_style = tab
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
 | 
			
		||||
[*.{cpp,hpp,c,h,mm}]
 | 
			
		||||
max_line_length = 120
 | 
			
		||||
trim_trailing_whitespace = true
 | 
			
		||||
 | 
			
		||||
[{*.gradle,AndroidManifest.xml}]
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 4
 | 
			
		||||
 | 
			
		||||
[{*.py,SConstruct,SCsub}]
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 4
 | 
			
		||||
 | 
			
		||||
# YAML requires indentation with spaces instead of tabs.
 | 
			
		||||
[*.{yml,yaml}]
 | 
			
		||||
indent_style = space
 | 
			
		||||
[{*.{yml,yaml},.clang{-format,-tidy,d}}]
 | 
			
		||||
indent_size = 2
 | 
			
		||||
indent_style = space
 | 
			
		||||
 | 
			
		||||
[*.svg]
 | 
			
		||||
insert_final_newline = false
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,3 +54,15 @@ df61dc4b2bd54a5a40c515493c76f5a458e5b541
 | 
			
		|||
 | 
			
		||||
# Enforce template syntax `typename` over `class`
 | 
			
		||||
9903e6779b70fc03aae70a37b9cf053f4f355b91
 | 
			
		||||
 | 
			
		||||
# Style: Apply new `clang-format` fixes
 | 
			
		||||
b37fc1014abf7adda70dc30b0822d775b3a4433f
 | 
			
		||||
 | 
			
		||||
# Set clang-format `RemoveSemicolon` rule to `true`
 | 
			
		||||
0d350e71086fffce0553811739aae9f6ad66136c
 | 
			
		||||
 | 
			
		||||
# Style: Apply clang-tidy fixes (superficial)
 | 
			
		||||
bb5f390fb9b466be35a5df7651323d7e66afca31
 | 
			
		||||
 | 
			
		||||
# Style: Enforce `AllowShortFunctionsOnASingleLine`
 | 
			
		||||
e06d83860d798b6766b23d6eae48557387a7db85
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										4
									
								
								engine/.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								engine/.gitattributes
									
									
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
# Properly detect languages on Github
 | 
			
		||||
*.h linguist-language=cpp
 | 
			
		||||
*.inc linguist-language=cpp
 | 
			
		||||
*.h linguist-language=C++
 | 
			
		||||
*.inc linguist-language=C++
 | 
			
		||||
thirdparty/* linguist-vendored
 | 
			
		||||
 | 
			
		||||
# Normalize EOL for all files that Git considers text files
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								engine/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								engine/.gitignore
									
									
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -36,8 +36,8 @@ compile_commands.json
 | 
			
		|||
platform/windows/godot_res.res
 | 
			
		||||
 | 
			
		||||
# Ninja build files
 | 
			
		||||
build.ninja
 | 
			
		||||
.ninja
 | 
			
		||||
*.ninja
 | 
			
		||||
.ninja/
 | 
			
		||||
run_ninja_env.bat
 | 
			
		||||
 | 
			
		||||
# Generated by Godot binary
 | 
			
		||||
| 
						 | 
				
			
			@ -77,6 +77,9 @@ venv
 | 
			
		|||
__pycache__/
 | 
			
		||||
*.pyc
 | 
			
		||||
 | 
			
		||||
# Python modules
 | 
			
		||||
.*_cache/
 | 
			
		||||
 | 
			
		||||
# Documentation
 | 
			
		||||
doc/_build/
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -164,9 +167,6 @@ gmon.out
 | 
			
		|||
# Kdevelop
 | 
			
		||||
*.kdev4
 | 
			
		||||
 | 
			
		||||
# Mypy
 | 
			
		||||
.mypy_cache
 | 
			
		||||
 | 
			
		||||
# Qt Creator
 | 
			
		||||
*.config
 | 
			
		||||
*.creator
 | 
			
		||||
| 
						 | 
				
			
			@ -216,6 +216,7 @@ xcuserdata/
 | 
			
		|||
*.xcscmblueprint
 | 
			
		||||
*.xccheckout
 | 
			
		||||
*.xcodeproj/*
 | 
			
		||||
!misc/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
 | 
			
		||||
 | 
			
		||||
##############################
 | 
			
		||||
### Visual Studio specific ###
 | 
			
		||||
| 
						 | 
				
			
			@ -362,6 +363,7 @@ cpp.hint
 | 
			
		|||
# macOS
 | 
			
		||||
.DS_Store
 | 
			
		||||
__MACOSX
 | 
			
		||||
Godot.app
 | 
			
		||||
 | 
			
		||||
# Windows
 | 
			
		||||
# https://github.com/github/gitignore/blob/main/Global/Windows.gitignore
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -84,6 +84,7 @@ Jean-Michel Bernard <jmb462@gmail.com>
 | 
			
		|||
Jérôme Gully <jerome.gully0@gmail.com>
 | 
			
		||||
JFonS <joan.fonssanchez@gmail.com>
 | 
			
		||||
jitspoe <jitspoe@yahoo.com> <jitspoeAyahoooDcom>
 | 
			
		||||
Johan Aires Rastén <johan@oljud.se>
 | 
			
		||||
Juan Linietsky <reduzio@gmail.com>
 | 
			
		||||
Juan Linietsky <reduzio@gmail.com> <juan@godotengine.org>
 | 
			
		||||
Juan Linietsky <reduzio@gmail.com> <juan@okamstudio.com>
 | 
			
		||||
| 
						 | 
				
			
			@ -97,6 +98,7 @@ karroffel <therzog@mail.de> <thomas.herzog@simedis.com>
 | 
			
		|||
Kelly Thomas <kelly.thomas@hotmail.com.au>
 | 
			
		||||
Kongfa Waroros <gongpha@hotmail.com>
 | 
			
		||||
K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com>
 | 
			
		||||
K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com> <fire@users.noreply.github.com>
 | 
			
		||||
kleonc <9283098+kleonc@users.noreply.github.com> <kleonc@users.noreply.github.com>
 | 
			
		||||
Leon Krause <lk@leonkrause.com> <eska@eska.me>
 | 
			
		||||
Leon Krause <lk@leonkrause.com> <eska014@users.noreply.github.com>
 | 
			
		||||
| 
						 | 
				
			
			@ -142,6 +144,7 @@ Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
 | 
			
		|||
Pieter-Jan Briers <pieterjan.briers+git@gmail.com> <pieterjan.briers@gmail.com>
 | 
			
		||||
Poommetee Ketson <poommetee@protonmail.com>
 | 
			
		||||
Przemysław Gołąb (n-pigeon) <golab.przemyslaw@gmail.com>
 | 
			
		||||
Radiant <69520693+RadiantUwU@users.noreply.github.com> <i.like.using.discord@gmail.com>
 | 
			
		||||
Rafał Mikrut <mikrutrafal@protonmail.com>
 | 
			
		||||
Rafał Mikrut <mikrutrafal@protonmail.com> <mikrutrafal54@gmail.com>
 | 
			
		||||
Ralf Hölzemer <r.hoelzemer@posteo.de> <rollenrolm@posteo.de>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,21 +4,25 @@ default_language_version:
 | 
			
		|||
exclude: |
 | 
			
		||||
  (?x)^(
 | 
			
		||||
    .*thirdparty/.*|
 | 
			
		||||
    .*-so_wrap\.(h|c)$
 | 
			
		||||
  )
 | 
			
		||||
    .*-so_wrap\.(h|c)|
 | 
			
		||||
    platform/android/java/editor/src/main/java/com/android/.*|
 | 
			
		||||
    platform/android/java/lib/src/com/google/.*
 | 
			
		||||
  )$
 | 
			
		||||
 | 
			
		||||
repos:
 | 
			
		||||
  - repo: https://github.com/pre-commit/mirrors-clang-format
 | 
			
		||||
    rev: v17.0.6
 | 
			
		||||
    rev: v19.1.3
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: clang-format
 | 
			
		||||
        files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java|glsl)$
 | 
			
		||||
        files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java)$
 | 
			
		||||
        types_or: [text]
 | 
			
		||||
        exclude: |
 | 
			
		||||
          (?x)^(
 | 
			
		||||
            tests/python_build/.*|
 | 
			
		||||
            platform/android/java/lib/src/com/.*
 | 
			
		||||
          )
 | 
			
		||||
        exclude: ^tests/python_build/.*
 | 
			
		||||
      - id: clang-format
 | 
			
		||||
        name: clang-format-glsl
 | 
			
		||||
        files: \.glsl$
 | 
			
		||||
        types_or: [text]
 | 
			
		||||
        exclude: ^tests/python_build/.*
 | 
			
		||||
        args: [-style=file:misc/utility/clang_format_glsl.yml]
 | 
			
		||||
 | 
			
		||||
  - repo: https://github.com/pocc/pre-commit-hooks
 | 
			
		||||
    rev: v1.3.5
 | 
			
		||||
| 
						 | 
				
			
			@ -27,24 +31,24 @@ repos:
 | 
			
		|||
        files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java|glsl)$
 | 
			
		||||
        args: [--fix, --quiet, --use-color]
 | 
			
		||||
        types_or: [text]
 | 
			
		||||
        exclude: |
 | 
			
		||||
          (?x)^(
 | 
			
		||||
            tests/python_build/.*|
 | 
			
		||||
            platform/android/java/lib/src/com/.*
 | 
			
		||||
          )
 | 
			
		||||
        additional_dependencies: [clang-tidy==18.1.1]
 | 
			
		||||
        exclude: ^tests/python_build/.*
 | 
			
		||||
        additional_dependencies: [clang-tidy==19.1.0]
 | 
			
		||||
        require_serial: true
 | 
			
		||||
        stages: [manual] # Not automatically triggered, invoked via `pre-commit run --hook-stage manual clang-tidy`
 | 
			
		||||
 | 
			
		||||
  - repo: https://github.com/astral-sh/ruff-pre-commit
 | 
			
		||||
    rev: v0.4.4
 | 
			
		||||
    rev: v0.9.4
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: ruff
 | 
			
		||||
        args: [--fix]
 | 
			
		||||
        files: (\.py|SConstruct|SCsub)$
 | 
			
		||||
        types_or: [text]
 | 
			
		||||
      - id: ruff-format
 | 
			
		||||
        files: (\.py|SConstruct|SCsub)$
 | 
			
		||||
        types_or: [text]
 | 
			
		||||
 | 
			
		||||
  - repo: https://github.com/pre-commit/mirrors-mypy
 | 
			
		||||
    rev: v0.971
 | 
			
		||||
    rev: v1.14.1
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: mypy
 | 
			
		||||
        files: \.py$
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +84,7 @@ repos:
 | 
			
		|||
        name: doc-status
 | 
			
		||||
        language: python
 | 
			
		||||
        entry: python doc/tools/doc_status.py
 | 
			
		||||
        args: [doc/classes, modules/*/doc_classes, platform/*/doc_classes]
 | 
			
		||||
        args: [doc/classes, modules/*/doc_classes, platform/*/doc_classes, -c]
 | 
			
		||||
        pass_filenames: false
 | 
			
		||||
        files: ^(doc/classes|.*/doc_classes)/.*\.xml$
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -89,16 +93,21 @@ repos:
 | 
			
		|||
        language: node
 | 
			
		||||
        entry: eslint
 | 
			
		||||
        files: ^(platform/web/js/|modules/|misc/dist/html/).*\.(js|html)$
 | 
			
		||||
        args: [--fix, --no-warn-ignored, --no-config-lookup, --config, platform/web/eslint.config.cjs]
 | 
			
		||||
        args:
 | 
			
		||||
          - --fix
 | 
			
		||||
          - --no-warn-ignored
 | 
			
		||||
          - --no-config-lookup
 | 
			
		||||
          - --config
 | 
			
		||||
          - platform/web/eslint.config.cjs
 | 
			
		||||
        additional_dependencies:
 | 
			
		||||
          - '@eslint/js@^9.3.0'
 | 
			
		||||
          - '@html-eslint/eslint-plugin@^0.24.1'
 | 
			
		||||
          - '@html-eslint/parser@^0.24.1'
 | 
			
		||||
          - '@stylistic/eslint-plugin@^2.1.0'
 | 
			
		||||
          - 'eslint@^9.3.0'
 | 
			
		||||
          - 'eslint-plugin-html@^8.1.1'
 | 
			
		||||
          - 'globals@^15.3.0'
 | 
			
		||||
          - 'espree@^10.0.1'
 | 
			
		||||
          - "@eslint/js@^9.3.0"
 | 
			
		||||
          - "@html-eslint/eslint-plugin@^0.24.1"
 | 
			
		||||
          - "@html-eslint/parser@^0.24.1"
 | 
			
		||||
          - "@stylistic/eslint-plugin@^2.1.0"
 | 
			
		||||
          - eslint@^9.3.0
 | 
			
		||||
          - eslint-plugin-html@^8.1.1
 | 
			
		||||
          - globals@^15.3.0
 | 
			
		||||
          - espree@^10.0.1
 | 
			
		||||
 | 
			
		||||
      - id: jsdoc
 | 
			
		||||
        name: jsdoc
 | 
			
		||||
| 
						 | 
				
			
			@ -112,11 +121,11 @@ repos:
 | 
			
		|||
          - platform/web/js/engine/config.js
 | 
			
		||||
          - platform/web/js/engine/features.js
 | 
			
		||||
          - --destination
 | 
			
		||||
          - ''
 | 
			
		||||
          - ""
 | 
			
		||||
          - -d
 | 
			
		||||
          - dry-run
 | 
			
		||||
        pass_filenames: false
 | 
			
		||||
        additional_dependencies: ['jsdoc@^4.0.3']
 | 
			
		||||
        additional_dependencies: [jsdoc@^4.0.3]
 | 
			
		||||
 | 
			
		||||
      - id: svgo
 | 
			
		||||
        name: svgo
 | 
			
		||||
| 
						 | 
				
			
			@ -124,7 +133,7 @@ repos:
 | 
			
		|||
        entry: svgo
 | 
			
		||||
        files: \.svg$
 | 
			
		||||
        args: [--quiet, --config, misc/utility/svgo.config.mjs]
 | 
			
		||||
        additional_dependencies: ["svgo@3.3.2"]
 | 
			
		||||
        additional_dependencies: [svgo@3.3.2]
 | 
			
		||||
 | 
			
		||||
      - id: copyright-headers
 | 
			
		||||
        name: copyright-headers
 | 
			
		||||
| 
						 | 
				
			
			@ -133,20 +142,19 @@ repos:
 | 
			
		|||
        files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java)$
 | 
			
		||||
        exclude: |
 | 
			
		||||
          (?x)^(
 | 
			
		||||
            core/math/bvh_.*\.inc$|
 | 
			
		||||
            core/math/bvh_.*\.inc|
 | 
			
		||||
            platform/(?!android|ios|linuxbsd|macos|web|windows)\w+/.*|
 | 
			
		||||
            platform/android/java/lib/src/com/.*|
 | 
			
		||||
            platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView\.java$|
 | 
			
		||||
            platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper\.java$|
 | 
			
		||||
            platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix\.java$
 | 
			
		||||
          )
 | 
			
		||||
            platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView\.java|
 | 
			
		||||
            platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper\.java|
 | 
			
		||||
            platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix\.java
 | 
			
		||||
          )$
 | 
			
		||||
 | 
			
		||||
      - id: header-guards
 | 
			
		||||
        name: header-guards
 | 
			
		||||
        language: python
 | 
			
		||||
        entry: python misc/scripts/header_guards.py
 | 
			
		||||
        files: \.(h|hpp|hh|hxx)$
 | 
			
		||||
        exclude: ^.*/(thread|platform_config|platform_gl)\.h$
 | 
			
		||||
        exclude: ^.*/(dummy|thread|platform_config|platform_gl)\.h$
 | 
			
		||||
 | 
			
		||||
      - id: file-format
 | 
			
		||||
        name: file-format
 | 
			
		||||
| 
						 | 
				
			
			@ -155,22 +163,22 @@ repos:
 | 
			
		|||
        types_or: [text]
 | 
			
		||||
        exclude: |
 | 
			
		||||
          (?x)^(
 | 
			
		||||
            .*\.test\.txt$|
 | 
			
		||||
            .*\.svg$|
 | 
			
		||||
            .*\.patch$|
 | 
			
		||||
            .*\.out$|
 | 
			
		||||
            modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines\.gd$|
 | 
			
		||||
            modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment\.notest\.gd$|
 | 
			
		||||
            modules/gdscript/tests/scripts/parser/warnings/empty_file_newline\.notest\.gd$|
 | 
			
		||||
            platform/android/java/lib/src/com/google/.*
 | 
			
		||||
          )
 | 
			
		||||
            .*\.test\.txt|
 | 
			
		||||
            .*\.svg|
 | 
			
		||||
            .*\.patch|
 | 
			
		||||
            .*\.out|
 | 
			
		||||
            modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines\.gd|
 | 
			
		||||
            modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment\.norun\.gd|
 | 
			
		||||
            modules/gdscript/tests/scripts/parser/warnings/empty_file_newline\.norun\.gd|
 | 
			
		||||
            tests/data/.*\.bin
 | 
			
		||||
          )$
 | 
			
		||||
 | 
			
		||||
      - id: dotnet-format
 | 
			
		||||
        name: dotnet-format
 | 
			
		||||
        language: python
 | 
			
		||||
        entry: python misc/scripts/dotnet_format.py
 | 
			
		||||
        types_or: [c#]
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# End of upstream Godot pre-commit hooks.
 | 
			
		||||
#
 | 
			
		||||
# Keep this separation to let downstream forks add their own hooks to this file,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,17 @@
 | 
			
		|||
# Godot Engine authors
 | 
			
		||||
 | 
			
		||||
Godot Engine is developed by a community of voluntary contributors who
 | 
			
		||||
contribute code, bug reports, documentation, artwork, support, etc.
 | 
			
		||||
contribute code, bug reports, documentation, translations, support, etc.,
 | 
			
		||||
across multiple repositories.
 | 
			
		||||
 | 
			
		||||
It is impossible to list them all; nevertheless, this file aims at listing
 | 
			
		||||
the developers who contributed significant patches to this MIT licensed
 | 
			
		||||
source code. "Significant" is arbitrarily decided, but should be fair :)
 | 
			
		||||
the developers who contributed significant improvements to the engine code.
 | 
			
		||||
 | 
			
		||||
To keep the list curated, we use a threshold of a minimum of 11 commits.
 | 
			
		||||
 | 
			
		||||
This file does not strictly reflect copyright ownership for the engine
 | 
			
		||||
source code. For this, refer to the Git history to know which contributor
 | 
			
		||||
wrote which part of the codebase.
 | 
			
		||||
 | 
			
		||||
GitHub usernames are indicated in parentheses, or as sole entry when no other
 | 
			
		||||
name is available.
 | 
			
		||||
| 
						 | 
				
			
			@ -25,8 +31,6 @@ name is available.
 | 
			
		|||
 | 
			
		||||
## Developers
 | 
			
		||||
 | 
			
		||||
(in alphabetical order, with over 10 commits excluding merges)
 | 
			
		||||
 | 
			
		||||
    Aaron Franke (aaronfranke)
 | 
			
		||||
    Aaron Pagano (aaronp64)
 | 
			
		||||
    Aaron Record (LightningAA)
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +42,7 @@ name is available.
 | 
			
		|||
    Alfred Reinold Baudisch (alfredbaudisch)
 | 
			
		||||
    Alistair Leslie-Hughes (alesliehughes)
 | 
			
		||||
    Alket Rexhepi (alketii)
 | 
			
		||||
    Alvin Wong (alvinhochun)
 | 
			
		||||
    Andrea Catania (AndreaCatania)
 | 
			
		||||
    Andreia Gaita (shana)
 | 
			
		||||
    Andrii Doroshenko (Xrayez)
 | 
			
		||||
| 
						 | 
				
			
			@ -46,11 +51,13 @@ name is available.
 | 
			
		|||
    Angad Kambli (angad-k)
 | 
			
		||||
    Anilforextra (AnilBK)
 | 
			
		||||
    Anish Bhobe (KidRigger)
 | 
			
		||||
    Anni Ryynänen (anniryynanen)
 | 
			
		||||
    Anton Yabchinskiy (a12n)
 | 
			
		||||
    Anutrix
 | 
			
		||||
    Aren Villanueva (kurikaesu)
 | 
			
		||||
    Ariel Manzur (punto-)
 | 
			
		||||
    Arman Elgudzhyan (puchik)
 | 
			
		||||
    Arseny Kapoulkine (zeux)
 | 
			
		||||
    AThousandShips
 | 
			
		||||
    aXu-AP
 | 
			
		||||
    Bartłomiej T. Listwon (Listwon)
 | 
			
		||||
| 
						 | 
				
			
			@ -72,6 +79,7 @@ name is available.
 | 
			
		|||
    Carter Anderson (cart)
 | 
			
		||||
    ChibiDenDen
 | 
			
		||||
    Chris Bradfield (cbscribe)
 | 
			
		||||
    Chris Cranford (Naros)
 | 
			
		||||
    Christian Kaiser (ckaiser)
 | 
			
		||||
    Clay John (clayjohn)
 | 
			
		||||
    ConteZero
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +94,9 @@ name is available.
 | 
			
		|||
    David Cambré (Gallilus)
 | 
			
		||||
    David Sichma (DavidSichma)
 | 
			
		||||
    David Snopek (dsnopek)
 | 
			
		||||
    derammo
 | 
			
		||||
    Dharkael (lupoDharkael)
 | 
			
		||||
    Dirk Steinmetz (rsjtdrjgfuzkfg)
 | 
			
		||||
    Dmitry Koteroff (Krakean)
 | 
			
		||||
    Dmitry Maganov (vonagam)
 | 
			
		||||
    Dominik Jasiński (dreamsComeTrue)
 | 
			
		||||
| 
						 | 
				
			
			@ -117,6 +127,7 @@ name is available.
 | 
			
		|||
    Geequlim
 | 
			
		||||
    George Marques (vnen)
 | 
			
		||||
    Gerrit Großkopf (Grosskopf)
 | 
			
		||||
    Giganzo
 | 
			
		||||
    Gilles Roudiere (groud)
 | 
			
		||||
    Gordon MacPherson (RevoluPowered)
 | 
			
		||||
    Guilherme Felipe de C. G. da Silva (guilhermefelipecgs)
 | 
			
		||||
| 
						 | 
				
			
			@ -126,7 +137,6 @@ name is available.
 | 
			
		|||
    Hein-Pieter van Braam-Stewart (hpvb)
 | 
			
		||||
    Hendrik Brucker (Geometror)
 | 
			
		||||
    Hilderin
 | 
			
		||||
    hilfazer
 | 
			
		||||
    Hiroshi Ogawa (hi-ogawa)
 | 
			
		||||
    HolonProduction
 | 
			
		||||
    homer666
 | 
			
		||||
| 
						 | 
				
			
			@ -151,6 +161,7 @@ name is available.
 | 
			
		|||
    Jia Jun Chai (SkyLucilfer)
 | 
			
		||||
    jitspoe
 | 
			
		||||
    Joan Fons Sanchez (JFonS)
 | 
			
		||||
    Johan Aires Rastén (JohanAR)
 | 
			
		||||
    Johan Manuel (29jm)
 | 
			
		||||
    Johannes Witt (HaSa1002)
 | 
			
		||||
    Jonathan Nicholl (jtnicholl)
 | 
			
		||||
| 
						 | 
				
			
			@ -163,11 +174,13 @@ name is available.
 | 
			
		|||
    Jummit
 | 
			
		||||
    Justo Delgado (mrcdk)
 | 
			
		||||
    karroffel
 | 
			
		||||
    Kassandra Pucher (PucklaJ)
 | 
			
		||||
    Kelly Thomas (KellyThomas)
 | 
			
		||||
    kleonc
 | 
			
		||||
    Kongfa Waroros (gongpha)
 | 
			
		||||
    Kostadin Damyanov (Max-Might)
 | 
			
		||||
    K. S. Ernest (iFire) Lee (fire)
 | 
			
		||||
    Kyle Eichlin (likeich)
 | 
			
		||||
    lawnjelly
 | 
			
		||||
    Leon Krause (leonkrause)
 | 
			
		||||
    Liz Haas (27thLiz)
 | 
			
		||||
| 
						 | 
				
			
			@ -185,6 +198,7 @@ name is available.
 | 
			
		|||
    Marcus Brummer (mbrlabs)
 | 
			
		||||
    Marcus Elg (MCrafterzz)
 | 
			
		||||
    Mariano Javier Suligoy (MarianoGnu)
 | 
			
		||||
    Mario Liebisch (MarioLiebisch)
 | 
			
		||||
    Mario Schlack (hurikhan)
 | 
			
		||||
    Marios Staikopoulos (marstaik)
 | 
			
		||||
    Marius Hanl (Maran23)
 | 
			
		||||
| 
						 | 
				
			
			@ -198,6 +212,7 @@ name is available.
 | 
			
		|||
    Masoud BH (masoudbh3)
 | 
			
		||||
    Mateo Kuruk Miccino (kuruk-mm)
 | 
			
		||||
    Matias N. Goldberg (darksylinc)
 | 
			
		||||
    Matthew Murphy (mashumafi)
 | 
			
		||||
    Matthew (skyace65)
 | 
			
		||||
    Matthias Hölzl (hoelzl)
 | 
			
		||||
    Max Hilbrunner (mhilbrunner)
 | 
			
		||||
| 
						 | 
				
			
			@ -209,9 +224,10 @@ name is available.
 | 
			
		|||
    MichiRecRoom (LikeLakers2)
 | 
			
		||||
    Micky (Mickeon)
 | 
			
		||||
    Mikael Hermansson (mihe)
 | 
			
		||||
    Mika Viskari (miv391)
 | 
			
		||||
    MinusKube
 | 
			
		||||
    MJacred
 | 
			
		||||
    Morris "Tabor" Arroad (mortarroad)
 | 
			
		||||
    Mounir Tohami (WhalesState)
 | 
			
		||||
    mrezai
 | 
			
		||||
    Muhammad Huri (CakHuri)
 | 
			
		||||
    muiroc
 | 
			
		||||
| 
						 | 
				
			
			@ -234,6 +250,7 @@ name is available.
 | 
			
		|||
    Patrick Dawson (pkdawson)
 | 
			
		||||
    Patrick Exner (FlameLizard)
 | 
			
		||||
    Patrick (firefly2442)
 | 
			
		||||
    patwork
 | 
			
		||||
    Paul Batty (Paulb23)
 | 
			
		||||
    Paul Joannon (paulloz)
 | 
			
		||||
    Paul Trojahn (ptrojahn)
 | 
			
		||||
| 
						 | 
				
			
			@ -245,6 +262,7 @@ name is available.
 | 
			
		|||
    Pieter-Jan Briers (PJB3005)
 | 
			
		||||
    Poommetee Ketson (Noshyaar)
 | 
			
		||||
    Przemysław Gołąb (n-pigeon)
 | 
			
		||||
    Radiant (RadiantUwU)
 | 
			
		||||
    Rafael M. G. (rafallus)
 | 
			
		||||
    Rafał Mikrut (qarmin)
 | 
			
		||||
    Raffaele Picca (RPicster)
 | 
			
		||||
| 
						 | 
				
			
			@ -282,6 +300,7 @@ name is available.
 | 
			
		|||
    Stanislav Labzyuk (DarkMessiah)
 | 
			
		||||
    Stijn Hinlopen (hinlopen)
 | 
			
		||||
    stmSi
 | 
			
		||||
    Stuart Carnie (stuartcarnie)
 | 
			
		||||
    Swarnim Arun (minraws)
 | 
			
		||||
    TC (floppyhammer)
 | 
			
		||||
    TechnoPorg
 | 
			
		||||
| 
						 | 
				
			
			@ -289,6 +308,7 @@ name is available.
 | 
			
		|||
    Thakee Nathees (ThakeeNathees)
 | 
			
		||||
    thebestnom
 | 
			
		||||
    Theo Hallenius (TheoXD)
 | 
			
		||||
    Thomas ten Cate (ttencate)
 | 
			
		||||
    Timo Schwarzer (timoschwarzer)
 | 
			
		||||
    Timothé Bonhoure (ajreckof)
 | 
			
		||||
    Timo (toger5)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,42 +1,40 @@
 | 
			
		|||
# Exhaustive licensing information for files in the Godot Engine repository
 | 
			
		||||
# =========================================================================
 | 
			
		||||
#
 | 
			
		||||
# This file aims at documenting the copyright and license for every source
 | 
			
		||||
# file in the Godot Engine repository, and especially outline the files
 | 
			
		||||
# whose license differs from the MIT/Expat license used by Godot Engine.
 | 
			
		||||
#
 | 
			
		||||
# It is written as a machine-readable format following the debian/copyright
 | 
			
		||||
# specification. Globbing patterns (e.g. "Files: *") mean that they affect
 | 
			
		||||
# all corresponding files (also recursively in subfolders), apart from those
 | 
			
		||||
# with a more explicit copyright statement.
 | 
			
		||||
#
 | 
			
		||||
# Licenses are given with their debian/copyright short name (or SPDX identifier
 | 
			
		||||
# if no standard short name exists) and are all included in plain text at the
 | 
			
		||||
# end of this file (in alphabetical order).
 | 
			
		||||
#
 | 
			
		||||
# Disclaimer for thirdparty libraries:
 | 
			
		||||
# ------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# Licensing details for thirdparty libraries in the 'thirdparty/' directory
 | 
			
		||||
# are given in summarized form, i.e. with only the "main" license described
 | 
			
		||||
# in the library's license statement. Different licenses of single files or
 | 
			
		||||
# code snippets in thirdparty libraries are not documented here.
 | 
			
		||||
# For example:
 | 
			
		||||
#   Files: ./thirdparty/zlib/
 | 
			
		||||
#   Copyright: 1995-2017, Jean-loup Gailly and Mark Adler
 | 
			
		||||
#   License: Zlib
 | 
			
		||||
# The exact copyright for each file in that library *may* differ, and some
 | 
			
		||||
# files or code snippets might be distributed under other compatible licenses
 | 
			
		||||
# (e.g. a public domain dedication), but as far as Godot Engine is concerned
 | 
			
		||||
# the library is considered as a whole under the Zlib license.
 | 
			
		||||
#
 | 
			
		||||
# Note: When linking dynamically against thirdparty libraries instead of
 | 
			
		||||
# building them into the Godot binary, you may remove the corresponding
 | 
			
		||||
# license details from this file.
 | 
			
		||||
 | 
			
		||||
-----------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 | 
			
		||||
Comment:
 | 
			
		||||
 Exhaustive licensing information for files in the Godot Engine repository
 | 
			
		||||
 =========================================================================
 | 
			
		||||
 .
 | 
			
		||||
 This file aims at documenting the copyright and license for every source
 | 
			
		||||
 file in the Godot Engine repository, and especially outline the files
 | 
			
		||||
 whose license differs from the MIT/Expat license used by Godot Engine.
 | 
			
		||||
 .
 | 
			
		||||
 It is written as a machine-readable format following the debian/copyright
 | 
			
		||||
 specification. Globbing patterns (e.g. "Files: *") mean that they affect
 | 
			
		||||
 all corresponding files (also recursively in subfolders), apart from those
 | 
			
		||||
 with a more explicit copyright statement.
 | 
			
		||||
 .
 | 
			
		||||
 Licenses are given with their debian/copyright short name (or SPDX identifier
 | 
			
		||||
 if no standard short name exists) and are all included in plain text at the
 | 
			
		||||
 end of this file (in alphabetical order).
 | 
			
		||||
 .
 | 
			
		||||
 Disclaimer for thirdparty libraries:
 | 
			
		||||
 ------------------------------------
 | 
			
		||||
 .
 | 
			
		||||
 Licensing details for thirdparty libraries in the 'thirdparty/' directory
 | 
			
		||||
 are given in summarized form, i.e. with only the "main" license described
 | 
			
		||||
 in the library's license statement. Different licenses of single files or
 | 
			
		||||
 code snippets in thirdparty libraries are not documented here.
 | 
			
		||||
 For example:
 | 
			
		||||
   Files: thirdparty/zlib/*
 | 
			
		||||
   Copyright: 1995-2017, Jean-loup Gailly and Mark Adler
 | 
			
		||||
   License: Zlib
 | 
			
		||||
 The exact copyright for each file in that library *may* differ, and some
 | 
			
		||||
 files or code snippets might be distributed under other compatible licenses
 | 
			
		||||
 (e.g. a public domain dedication), but as far as Godot Engine is concerned
 | 
			
		||||
 the library is considered as a whole under the Zlib license.
 | 
			
		||||
 .
 | 
			
		||||
 Note: When linking dynamically against thirdparty libraries instead of
 | 
			
		||||
 building them into the Godot binary, you may remove the corresponding
 | 
			
		||||
 license details from this file.
 | 
			
		||||
Upstream-Name: Godot Engine
 | 
			
		||||
Upstream-Contact: Rémi Verschelde <contact@godotengine.org>
 | 
			
		||||
Source: https://github.com/godotengine/godot
 | 
			
		||||
| 
						 | 
				
			
			@ -47,222 +45,244 @@ Copyright: 2014-present, Godot Engine contributors
 | 
			
		|||
 2007-2014, Juan Linietsky, Ariel Manzur
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./icon.png
 | 
			
		||||
 ./icon.svg
 | 
			
		||||
 ./logo.png
 | 
			
		||||
 ./logo.svg
 | 
			
		||||
Files: icon.png
 | 
			
		||||
 icon.svg
 | 
			
		||||
 logo.png
 | 
			
		||||
 logo.svg
 | 
			
		||||
Comment: Godot Engine logo
 | 
			
		||||
Copyright: 2017, Andrea Calabró
 | 
			
		||||
License: CC-BY-4.0
 | 
			
		||||
 | 
			
		||||
Files: ./core/math/convex_hull.cpp
 | 
			
		||||
 ./core/math/convex_hull.h
 | 
			
		||||
Files: core/math/convex_hull.cpp
 | 
			
		||||
 core/math/convex_hull.h
 | 
			
		||||
Comment: Bullet Continuous Collision Detection and Physics Library
 | 
			
		||||
Copyright: 2011, Ole Kniemeyer, MAXON, www.maxon.net
 | 
			
		||||
 2014-present, Godot Engine contributors
 | 
			
		||||
 2007-2014, Juan Linietsky, Ariel Manzur
 | 
			
		||||
License: Expat and Zlib
 | 
			
		||||
 | 
			
		||||
Files: ./modules/lightmapper_rd/lm_compute.glsl
 | 
			
		||||
Comment: Joint Non-Local Means (JNLM) denoiser
 | 
			
		||||
Copyright: 2020, Manuel Prandini
 | 
			
		||||
 2014-present, Godot Engine contributors
 | 
			
		||||
 2007-2014, Juan Linietsky, Ariel Manzur
 | 
			
		||||
Files: modules/betsy/alpha_stitch.glsl
 | 
			
		||||
 modules/betsy/bc1.glsl
 | 
			
		||||
 modules/betsy/bc4.glsl
 | 
			
		||||
 modules/betsy/bc6h.glsl
 | 
			
		||||
 modules/betsy/CrossPlatformSettings_piece_all.glsl
 | 
			
		||||
Comment: Betsy
 | 
			
		||||
Copyright: 2020-2022, Matias N. Goldberg
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./platform/android/java/lib/aidl/com/android/*
 | 
			
		||||
 ./platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml
 | 
			
		||||
 ./platform/android/java/lib/src/com/google/android/*
 | 
			
		||||
 ./platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java
 | 
			
		||||
 ./platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java
 | 
			
		||||
Comment: The Android Open Source Project
 | 
			
		||||
Copyright: 2008-2016, The Android Open Source Project
 | 
			
		||||
 2002, Google, Inc.
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix.java
 | 
			
		||||
Comment: ProcessPhoenix
 | 
			
		||||
Copyright: 2015, Jake Wharton
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./scene/animation/easing_equations.h
 | 
			
		||||
Comment: Robert Penner's Easing Functions
 | 
			
		||||
Copyright: 2001, Robert Penner
 | 
			
		||||
 2014-present, Godot Engine contributors
 | 
			
		||||
 2007-2014, Juan Linietsky, Ariel Manzur
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./servers/physics_2d/godot_joints_2d.cpp
 | 
			
		||||
Files: modules/godot_physics_2d/godot_joints_2d.cpp
 | 
			
		||||
Comment: Chipmunk2D Joint Constraints
 | 
			
		||||
Copyright: 2007, Scott Lembcke
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./servers/physics_3d/collision_solver_3d_sat.cpp
 | 
			
		||||
Comment: Open Dynamics Engine
 | 
			
		||||
Copyright: 2001-2003, Russell L. Smith, Alen Ladavac, Nguyen Binh
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: ./servers/physics_3d/gjk_epa.cpp
 | 
			
		||||
 ./servers/physics_3d/joints/generic_6dof_joint_3d_sw.cpp
 | 
			
		||||
 ./servers/physics_3d/joints/generic_6dof_joint_3d_sw.h
 | 
			
		||||
 ./servers/physics_3d/joints/hinge_joint_3d_sw.cpp
 | 
			
		||||
 ./servers/physics_3d/joints/hinge_joint_3d_sw.h
 | 
			
		||||
 ./servers/physics_3d/joints/jacobian_entry_3d_sw.h
 | 
			
		||||
 ./servers/physics_3d/joints/pin_joint_3d_sw.cpp
 | 
			
		||||
 ./servers/physics_3d/joints/pin_joint_3d_sw.h
 | 
			
		||||
 ./servers/physics_3d/joints/slider_joint_3d_sw.cpp
 | 
			
		||||
 ./servers/physics_3d/joints/slider_joint_3d_sw.h
 | 
			
		||||
 ./servers/physics_3d/soft_body_3d_sw.cpp
 | 
			
		||||
 ./servers/physics_3d/soft_body_3d_sw.h
 | 
			
		||||
 ./servers/physics_3d/shape_3d_sw.cpp
 | 
			
		||||
 ./servers/physics_3d/shape_3d_sw.h
 | 
			
		||||
Files: modules/godot_physics_3d/gjk_epa.cpp
 | 
			
		||||
 modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp
 | 
			
		||||
 modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.h
 | 
			
		||||
 modules/godot_physics_3d/joints/godot_hinge_joint_3d.cpp
 | 
			
		||||
 modules/godot_physics_3d/joints/godot_hinge_joint_3d_sw.h
 | 
			
		||||
 modules/godot_physics_3d/joints/godot_jacobian_entry_3d_sw.h
 | 
			
		||||
 modules/godot_physics_3d/joints/godot_pin_joint_3d.cpp
 | 
			
		||||
 modules/godot_physics_3d/joints/godot_pin_joint_3d.h
 | 
			
		||||
 modules/godot_physics_3d/joints/godot_slider_joint_3d.cpp
 | 
			
		||||
 modules/godot_physics_3d/joints/godot_slider_joint_3d.h
 | 
			
		||||
 modules/godot_physics_3d/godot_soft_body_3d.cpp
 | 
			
		||||
 modules/godot_physics_3d/godot_soft_body_3d.h
 | 
			
		||||
 modules/godot_physics_3d/godot_shape_3d.cpp
 | 
			
		||||
 modules/godot_physics_3d/godot_shape_3d.h
 | 
			
		||||
Comment: Bullet Continuous Collision Detection and Physics Library
 | 
			
		||||
Copyright: 2003-2008, Erwin Coumans
 | 
			
		||||
 2014-present, Godot Engine contributors
 | 
			
		||||
 2007-2014, Juan Linietsky, Ariel Manzur
 | 
			
		||||
License: Expat and Zlib
 | 
			
		||||
 | 
			
		||||
Files: ./servers/physics_3d/joints/cone_twist_joint_3d_sw.cpp
 | 
			
		||||
 ./servers/physics_3d/joints/cone_twist_joint_3d_sw.h
 | 
			
		||||
Files: modules/godot_physics_3d/godot_collision_solver_3d_sat.cpp
 | 
			
		||||
Comment: Open Dynamics Engine
 | 
			
		||||
Copyright: 2001-2003, Russell L. Smith, Alen Ladavac, Nguyen Binh
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: modules/godot_physics_3d/joints/godot_cone_twist_joint_3d.cpp
 | 
			
		||||
 modules/godot_physics_3d/joints/godot_cone_twist_joint_3d.h
 | 
			
		||||
Comment: Bullet Continuous Collision Detection and Physics Library
 | 
			
		||||
Copyright: 2007, Starbreeze Studios
 | 
			
		||||
 2014-present, Godot Engine contributors
 | 
			
		||||
 2007-2014, Juan Linietsky, Ariel Manzur
 | 
			
		||||
License: Expat and Zlib
 | 
			
		||||
 | 
			
		||||
Files: ./servers/rendering/renderer_rd/shaders/ss_effects_downsample.glsl
 | 
			
		||||
 ./servers/rendering/renderer_rd/shaders/ssao_blur.glsl
 | 
			
		||||
 ./servers/rendering/renderer_rd/shaders/ssao_importance_map.glsl
 | 
			
		||||
 ./servers/rendering/renderer_rd/shaders/ssao_interleave.glsl
 | 
			
		||||
 ./servers/rendering/renderer_rd/shaders/ssao.glsl
 | 
			
		||||
 ./servers/rendering/renderer_rd/shaders/ssil_blur.glsl
 | 
			
		||||
 ./servers/rendering/renderer_rd/shaders/ssil_importance_map.glsl
 | 
			
		||||
 ./servers/rendering/renderer_rd/shaders/ssil_interleave.glsl
 | 
			
		||||
 ./servers/rendering/renderer_rd/shaders/ssil.glsl
 | 
			
		||||
Files: modules/jolt_physics/spaces/jolt_temp_allocator.cpp
 | 
			
		||||
Comment: Jolt Physics
 | 
			
		||||
Copyright: 2021, Jorrit Rouwe
 | 
			
		||||
 2014-present, Godot Engine contributors
 | 
			
		||||
 2007-2014, Juan Linietsky, Ariel Manzur
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: modules/lightmapper_rd/lm_compute.glsl
 | 
			
		||||
Comment: Joint Non-Local Means (JNLM) denoiser
 | 
			
		||||
Copyright: 2020, Manuel Prandini
 | 
			
		||||
 2014-present, Godot Engine contributors
 | 
			
		||||
 2007-2014, Juan Linietsky, Ariel Manzur
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: platform/android/java/editor/src/main/java/com/android/*
 | 
			
		||||
 platform/android/java/lib/aidl/com/android/*
 | 
			
		||||
 platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml
 | 
			
		||||
 platform/android/java/lib/src/com/google/android/*
 | 
			
		||||
 platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java
 | 
			
		||||
 platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java
 | 
			
		||||
Comment: The Android Open Source Project
 | 
			
		||||
Copyright: 2008-2016, The Android Open Source Project
 | 
			
		||||
 2002, Google, Inc.
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix.java
 | 
			
		||||
Comment: ProcessPhoenix
 | 
			
		||||
Copyright: 2015, Jake Wharton
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: scene/animation/easing_equations.h
 | 
			
		||||
Comment: Robert Penner's Easing Functions
 | 
			
		||||
Copyright: 2001, Robert Penner
 | 
			
		||||
 2014-present, Godot Engine contributors
 | 
			
		||||
 2007-2014, Juan Linietsky, Ariel Manzur
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: servers/rendering/renderer_rd/shaders/ss_effects_downsample.glsl
 | 
			
		||||
 servers/rendering/renderer_rd/shaders/ssao_blur.glsl
 | 
			
		||||
 servers/rendering/renderer_rd/shaders/ssao_importance_map.glsl
 | 
			
		||||
 servers/rendering/renderer_rd/shaders/ssao_interleave.glsl
 | 
			
		||||
 servers/rendering/renderer_rd/shaders/ssao.glsl
 | 
			
		||||
 servers/rendering/renderer_rd/shaders/ssil_blur.glsl
 | 
			
		||||
 servers/rendering/renderer_rd/shaders/ssil_importance_map.glsl
 | 
			
		||||
 servers/rendering/renderer_rd/shaders/ssil_interleave.glsl
 | 
			
		||||
 servers/rendering/renderer_rd/shaders/ssil.glsl
 | 
			
		||||
Comment: Intel ASSAO and related files
 | 
			
		||||
Copyright: 2016, Intel Corporation
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./servers/rendering/renderer_rd/shaders/taa_resolve.glsl
 | 
			
		||||
Files: servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl
 | 
			
		||||
Comment: Temporal Anti-Aliasing resolve implementation
 | 
			
		||||
Copyright: 2016, Panos Karabelas
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/amd-fsr/
 | 
			
		||||
Files: thirdparty/amd-fsr/*
 | 
			
		||||
Comment: AMD FidelityFX Super Resolution
 | 
			
		||||
Copyright: 2021, Advanced Micro Devices, Inc.
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/amd-fsr2/
 | 
			
		||||
Files: thirdparty/amd-fsr2/*
 | 
			
		||||
Comment: AMD FidelityFX Super Resolution 2
 | 
			
		||||
Copyright: 2022-2023, Advanced Micro Devices, Inc.
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/angle/
 | 
			
		||||
Files: thirdparty/angle/*
 | 
			
		||||
Comment: ANGLE
 | 
			
		||||
Copyright: 2018, The ANGLE Project Authors.
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/astcenc/
 | 
			
		||||
Files: thirdparty/astcenc/*
 | 
			
		||||
Comment: Arm ASTC Encoder
 | 
			
		||||
Copyright: 2011-2024, Arm Limited
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/basis_universal/
 | 
			
		||||
Files: thirdparty/basis_universal/*
 | 
			
		||||
Comment: Basis Universal
 | 
			
		||||
Copyright: 2022, Binomial LLC.
 | 
			
		||||
Copyright: 2019-2024, Binomial LLC.
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/brotli/
 | 
			
		||||
Files: thirdparty/brotli/*
 | 
			
		||||
Comment: Brotli
 | 
			
		||||
Copyright: 2009, 2010, 2013-2016 by the Brotli Authors.
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/certs/ca-certificates.crt
 | 
			
		||||
Files: thirdparty/certs/ca-certificates.crt
 | 
			
		||||
Comment: CA certificates
 | 
			
		||||
Copyright: Mozilla Contributors
 | 
			
		||||
License: MPL-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/clipper2/
 | 
			
		||||
Files: thirdparty/clipper2/*
 | 
			
		||||
Comment: Clipper2
 | 
			
		||||
Copyright: 2010-2023, Angus Johnson
 | 
			
		||||
Copyright: 2010-2024, Angus Johnson
 | 
			
		||||
License: BSL-1.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/cvtt/
 | 
			
		||||
Files: thirdparty/cvtt/*
 | 
			
		||||
Comment: Convection Texture Tools Stand-Alone Kernels
 | 
			
		||||
Copyright: 2018, Eric Lasota
 | 
			
		||||
 2018, Microsoft Corp.
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/d3d12ma/
 | 
			
		||||
Files: thirdparty/d3d12ma/*
 | 
			
		||||
Comment: D3D12 Memory Allocator
 | 
			
		||||
Copyright: 2019-2022 Advanced Micro Devices, Inc.
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/directx_headers/
 | 
			
		||||
Files: thirdparty/directx_headers/*
 | 
			
		||||
Comment: DirectX Headers
 | 
			
		||||
Copyright: Microsoft Corporation
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/doctest/
 | 
			
		||||
Files: thirdparty/doctest/*
 | 
			
		||||
Comment: doctest
 | 
			
		||||
Copyright: 2016-2023, Viktor Kirilov
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/embree/
 | 
			
		||||
Files: thirdparty/embree/*
 | 
			
		||||
Comment: Embree
 | 
			
		||||
Copyright: 2009-2021 Intel Corporation
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/enet/
 | 
			
		||||
Files: thirdparty/enet/*
 | 
			
		||||
Comment: ENet
 | 
			
		||||
Copyright: 2002-2024, Lee Salzman
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/etcpak/
 | 
			
		||||
Files: thirdparty/etcpak/*
 | 
			
		||||
Comment: etcpak
 | 
			
		||||
Copyright: 2013-2022, Bartosz Taudul
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/fonts/DroidSans*.woff2
 | 
			
		||||
Files: thirdparty/fonts/DroidSans*.woff2
 | 
			
		||||
Comment: DroidSans font
 | 
			
		||||
Copyright: 2008, The Android Open Source Project
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/fonts/JetBrainsMono_Regular.woff2
 | 
			
		||||
Files: thirdparty/fonts/JetBrainsMono_Regular.woff2
 | 
			
		||||
Comment: JetBrains Mono font
 | 
			
		||||
Copyright: 2020, JetBrains s.r.o.
 | 
			
		||||
License: OFL-1.1
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/fonts/NotoSans*.woff2
 | 
			
		||||
Files: thirdparty/fonts/NotoSans*.woff2
 | 
			
		||||
Comment: Noto Sans font
 | 
			
		||||
Copyright: 2012, Google Inc.
 | 
			
		||||
License: OFL-1.1
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/freetype/
 | 
			
		||||
Files: thirdparty/fonts/Vazirmatn*.woff2
 | 
			
		||||
Comment: Vazirmatn font
 | 
			
		||||
Copyright: 2015, The Vazirmatn Project Authors.
 | 
			
		||||
License: OFL-1.1
 | 
			
		||||
 | 
			
		||||
Files: thirdparty/freetype/*
 | 
			
		||||
Comment: The FreeType Project
 | 
			
		||||
Copyright: 1996-2023, David Turner, Robert Wilhelm, and Werner Lemberg.
 | 
			
		||||
License: FTL
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/glad/
 | 
			
		||||
Files: thirdparty/glad/*
 | 
			
		||||
Comment: glad
 | 
			
		||||
Copyright: 2013-2022, David Herberth
 | 
			
		||||
 2013-2020, The Khronos Group Inc.
 | 
			
		||||
License: CC0-1.0 and Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/glslang/
 | 
			
		||||
Files: thirdparty/glslang/*
 | 
			
		||||
Comment: glslang
 | 
			
		||||
Copyright: 2015-2020, Google, Inc.
 | 
			
		||||
  2014-2020, The Khronos Group Inc
 | 
			
		||||
  2002, NVIDIA Corporation.
 | 
			
		||||
License: glslang
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/graphite/
 | 
			
		||||
Files: thirdparty/graphite/*
 | 
			
		||||
Comment: Graphite engine
 | 
			
		||||
Copyright: 2010, SIL International
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/harfbuzz/
 | 
			
		||||
Files: thirdparty/harfbuzz/*
 | 
			
		||||
Comment: HarfBuzz text shaping library
 | 
			
		||||
Copyright: 2010-2022, Google, Inc.
 | 
			
		||||
 2015-2020, Ebrahim Byagowi
 | 
			
		||||
| 
						 | 
				
			
			@ -283,248 +303,263 @@ Copyright: 2010-2022, Google, Inc.
 | 
			
		|||
 2013-2015, Alexei Podtelezhnikov
 | 
			
		||||
License: HarfBuzz
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/icu4c/
 | 
			
		||||
Files: thirdparty/icu4c/*
 | 
			
		||||
Comment: International Components for Unicode
 | 
			
		||||
Copyright: 2016-2024, Unicode, Inc.
 | 
			
		||||
License: Unicode
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/jpeg-compressor/
 | 
			
		||||
Files: thirdparty/jolt_physics/*
 | 
			
		||||
Comment: Jolt Physics
 | 
			
		||||
Copyright: 2021, Jorrit Rouwe
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: thirdparty/jpeg-compressor/*
 | 
			
		||||
Comment: jpeg-compressor
 | 
			
		||||
Copyright: 2012, Rich Geldreich
 | 
			
		||||
License: public-domain or Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/libbacktrace/
 | 
			
		||||
Files: thirdparty/libbacktrace/*
 | 
			
		||||
Comment: libbacktrace
 | 
			
		||||
Copyright: 2012-2021, Free Software Foundation, Inc.
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/libktx/
 | 
			
		||||
Files: thirdparty/libktx/*
 | 
			
		||||
Comment: KTX
 | 
			
		||||
Copyright: 2013-2020, Mark Callow
 | 
			
		||||
 2010-2020 The Khronos Group, Inc.
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/libogg/
 | 
			
		||||
Files: thirdparty/libogg/*
 | 
			
		||||
Comment: OggVorbis
 | 
			
		||||
Copyright: 2002, Xiph.org Foundation
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/libpng/
 | 
			
		||||
Files: thirdparty/libpng/*
 | 
			
		||||
Comment: libpng
 | 
			
		||||
Copyright: 1995-2024, The PNG Reference Library Authors.
 | 
			
		||||
 2018-2024, Cosmin Truta.
 | 
			
		||||
Copyright: 1995-2025, The PNG Reference Library Authors.
 | 
			
		||||
 2018-2025, Cosmin Truta.
 | 
			
		||||
 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
 | 
			
		||||
 1996-1997, Andreas Dilger.
 | 
			
		||||
 1995-1996, Guy Eric Schalnat, Group 42, Inc.
 | 
			
		||||
License: Zlib
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/libtheora/
 | 
			
		||||
Files: thirdparty/libtheora/*
 | 
			
		||||
Comment: OggTheora
 | 
			
		||||
Copyright: 2002-2009, Xiph.org Foundation
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/libvorbis/
 | 
			
		||||
Files: thirdparty/libvorbis/*
 | 
			
		||||
Comment: OggVorbis
 | 
			
		||||
Copyright: 2002-2015, Xiph.org Foundation
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/libwebp/
 | 
			
		||||
Files: thirdparty/libwebp/*
 | 
			
		||||
Comment: WebP codec
 | 
			
		||||
Copyright: 2010, Google Inc.
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/mbedtls/
 | 
			
		||||
Files: thirdparty/manifold/*
 | 
			
		||||
Comment: Manifold
 | 
			
		||||
Copyright: 2020-2024, The Manifold Authors
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: thirdparty/mbedtls/*
 | 
			
		||||
Comment: Mbed TLS
 | 
			
		||||
Copyright: The Mbed TLS Contributors
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/meshoptimizer/
 | 
			
		||||
Files: thirdparty/meshoptimizer/*
 | 
			
		||||
Comment: meshoptimizer
 | 
			
		||||
Copyright: 2016-2023, Arseny Kapoulkine
 | 
			
		||||
Copyright: 2016-2024, Arseny Kapoulkine
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/mingw-std-threads/
 | 
			
		||||
Files: thirdparty/mingw-std-threads/*
 | 
			
		||||
Comment: mingw-std-threads
 | 
			
		||||
Copyright: 2016, Mega Limited
 | 
			
		||||
License: BSD-2-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/minimp3/
 | 
			
		||||
Files: thirdparty/minimp3/*
 | 
			
		||||
Comment: MiniMP3
 | 
			
		||||
Copyright: lieff
 | 
			
		||||
License: CC0-1.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/miniupnpc/
 | 
			
		||||
Files: thirdparty/miniupnpc/*
 | 
			
		||||
Comment: MiniUPnP Project
 | 
			
		||||
Copyright: 2005-2024, Thomas Bernard
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/minizip/
 | 
			
		||||
Files: thirdparty/minizip/*
 | 
			
		||||
Comment: MiniZip
 | 
			
		||||
Copyright: 1998-2010, Gilles Vollant
 | 
			
		||||
 2007-2008, Even Rouault
 | 
			
		||||
 2009-2010, Mathias Svensson
 | 
			
		||||
License: Zlib
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/misc/cubemap_coeffs.h
 | 
			
		||||
Files: thirdparty/misc/bcdec.h
 | 
			
		||||
Comment: bcdec
 | 
			
		||||
Copyright: 2022, Sergii Kudlai
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: thirdparty/misc/cubemap_coeffs.h
 | 
			
		||||
Comment: Fast Filtering of Reflection Probes
 | 
			
		||||
Copyright: 2016, Activision Publishing, Inc.
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/misc/fastlz.c
 | 
			
		||||
 ./thirdparty/misc/fastlz.h
 | 
			
		||||
Files: thirdparty/misc/fastlz.c
 | 
			
		||||
 thirdparty/misc/fastlz.h
 | 
			
		||||
Comment: FastLZ
 | 
			
		||||
Copyright: 2005-2020, Ariya Hidayat
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/misc/ifaddrs-android.cc
 | 
			
		||||
 ./thirdparty/misc/ifaddrs-android.h
 | 
			
		||||
Comment: libjingle
 | 
			
		||||
Copyright: 2012-2013, Google Inc.
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/misc/mikktspace.c
 | 
			
		||||
 ./thirdparty/misc/mikktspace.h
 | 
			
		||||
Comment: Tangent Space Normal Maps implementation
 | 
			
		||||
Copyright: 2011, Morten S. Mikkelsen
 | 
			
		||||
License: Zlib
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/misc/ok_color.h
 | 
			
		||||
 ./thirdparty/misc/ok_color_shader.h
 | 
			
		||||
Comment: OK Lab color space
 | 
			
		||||
Copyright: 2021, Björn Ottosson
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/noise/FastNoiseLite.h
 | 
			
		||||
Files: thirdparty/misc/FastNoiseLite.h
 | 
			
		||||
Comment: FastNoise Lite
 | 
			
		||||
Copyright: 2023, Jordan Peck and contributors
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/misc/pcg.cpp
 | 
			
		||||
 ./thirdparty/misc/pcg.h
 | 
			
		||||
Files: thirdparty/misc/ifaddrs-android.cc
 | 
			
		||||
 thirdparty/misc/ifaddrs-android.h
 | 
			
		||||
Comment: libjingle
 | 
			
		||||
Copyright: 2012-2013, Google Inc.
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: thirdparty/misc/mikktspace.c
 | 
			
		||||
 thirdparty/misc/mikktspace.h
 | 
			
		||||
Comment: Tangent Space Normal Maps implementation
 | 
			
		||||
Copyright: 2011, Morten S. Mikkelsen
 | 
			
		||||
License: Zlib
 | 
			
		||||
 | 
			
		||||
Files: thirdparty/misc/nvapi_minimal.h
 | 
			
		||||
Comment: NVIDIA NVAPI (minimal excerpt)
 | 
			
		||||
Copyright: 2019-2022, NVIDIA Corporation
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: thirdparty/misc/ok_color.h
 | 
			
		||||
 thirdparty/misc/ok_color_shader.h
 | 
			
		||||
Comment: OK Lab color space
 | 
			
		||||
Copyright: 2021, Björn Ottosson
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: thirdparty/misc/pcg.cpp
 | 
			
		||||
 thirdparty/misc/pcg.h
 | 
			
		||||
Comment: Minimal PCG32 implementation
 | 
			
		||||
Copyright: 2014, M.E. O'Neill
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/misc/polypartition.cpp
 | 
			
		||||
 ./thirdparty/misc/polypartition.h
 | 
			
		||||
Files: thirdparty/misc/polypartition.cpp
 | 
			
		||||
 thirdparty/misc/polypartition.h
 | 
			
		||||
Comment: PolyPartition / Triangulator
 | 
			
		||||
Copyright: 2011-2021, Ivan Fratric and contributors
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/misc/qoa.h
 | 
			
		||||
Files: thirdparty/misc/qoa.h
 | 
			
		||||
Comment: Quite OK Audio Format
 | 
			
		||||
Copyright: 2023, Dominic Szablewski
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/misc/r128.c
 | 
			
		||||
 ./thirdparty/misc/r128.h
 | 
			
		||||
Files: thirdparty/misc/r128.c
 | 
			
		||||
 thirdparty/misc/r128.h
 | 
			
		||||
Comment: r128 library
 | 
			
		||||
Copyright: Alan Hickman
 | 
			
		||||
License: public-domain or Unlicense
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/misc/smaz.c
 | 
			
		||||
 ./thirdparty/misc/smaz.h
 | 
			
		||||
Files: thirdparty/misc/smaz.c
 | 
			
		||||
 thirdparty/misc/smaz.h
 | 
			
		||||
Comment: SMAZ
 | 
			
		||||
Copyright: 2006-2009, Salvatore Sanfilippo
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/misc/smolv.cpp
 | 
			
		||||
 ./thirdparty/misc/smolv.h
 | 
			
		||||
Files: thirdparty/misc/smolv.cpp
 | 
			
		||||
 thirdparty/misc/smolv.h
 | 
			
		||||
Comment: SMOL-V
 | 
			
		||||
Copyright: 2016-2020, Aras Pranckevicius
 | 
			
		||||
Copyright: 2016-2024, Aras Pranckevicius
 | 
			
		||||
License: public-domain or Unlicense or Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/misc/stb_rect_pack.h
 | 
			
		||||
Files: thirdparty/misc/stb_rect_pack.h
 | 
			
		||||
Comment: stb libraries
 | 
			
		||||
Copyright: Sean Barrett
 | 
			
		||||
License: public-domain or Unlicense or Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/misc/yuv2rgb.h
 | 
			
		||||
Files: thirdparty/misc/yuv2rgb.h
 | 
			
		||||
Comment: YUV2RGB
 | 
			
		||||
Copyright: 2008-2011, Robin Watts
 | 
			
		||||
License: BSD-2-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/msdfgen/
 | 
			
		||||
Files: thirdparty/msdfgen/*
 | 
			
		||||
Comment: Multi-channel signed distance field generator
 | 
			
		||||
Copyright: 2016-2022, Viktor Chlumsky
 | 
			
		||||
Copyright: 2014-2024, Viktor Chlumsky
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/nvapi/nvapi_minimal.h
 | 
			
		||||
Comment: Stripped down version of "nvapi.h" from the NVIDIA NVAPI SDK
 | 
			
		||||
Copyright: 2019-2022, NVIDIA Corporation
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/openxr/
 | 
			
		||||
Files: thirdparty/openxr/*
 | 
			
		||||
Comment: OpenXR Loader
 | 
			
		||||
Copyright: 2020-2023, The Khronos Group Inc.
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/pcre2/
 | 
			
		||||
Files: thirdparty/pcre2/*
 | 
			
		||||
Comment: PCRE2
 | 
			
		||||
Copyright: 1997-2024, University of Cambridge
 | 
			
		||||
 2009-2024, Zoltan Herczeg
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/recastnavigation/
 | 
			
		||||
Files: thirdparty/recastnavigation/*
 | 
			
		||||
Comment: Recast
 | 
			
		||||
Copyright: 2009, Mikko Mononen
 | 
			
		||||
License: Zlib
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/rvo2/
 | 
			
		||||
Files: thirdparty/rvo2/*
 | 
			
		||||
Comment: RVO2
 | 
			
		||||
Copyright: 2016, University of North Carolina at Chapel Hill
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/spirv-reflect/
 | 
			
		||||
Files: thirdparty/spirv-cross/*
 | 
			
		||||
Comment: SPIRV-Cross
 | 
			
		||||
Copyright: 2015-2021, Arm Limited
 | 
			
		||||
License: Apache-2.0 or Expat
 | 
			
		||||
 | 
			
		||||
Files: thirdparty/spirv-reflect/*
 | 
			
		||||
Comment: SPIRV-Reflect
 | 
			
		||||
Copyright: 2017-2022, Google Inc.
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/squish/
 | 
			
		||||
Comment: libSquish
 | 
			
		||||
Copyright: 2006, Simon Brown
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/thorvg/
 | 
			
		||||
Files: thirdparty/thorvg/*
 | 
			
		||||
Comment: ThorVG
 | 
			
		||||
Copyright: 2020-2024, The ThorVG Project
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/tinyexr/
 | 
			
		||||
Files: thirdparty/tinyexr/*
 | 
			
		||||
Comment: TinyEXR
 | 
			
		||||
Copyright: 2014-2021, Syoyo Fujita
 | 
			
		||||
  2002, Industrial Light & Magic, a division of Lucas Digital Ltd. LLC
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/ufbx/
 | 
			
		||||
Files: thirdparty/ufbx/*
 | 
			
		||||
Comment: ufbx
 | 
			
		||||
Copyright: 2020, Samuli Raivio
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/vhacd/
 | 
			
		||||
Files: thirdparty/vhacd/*
 | 
			
		||||
Comment: V-HACD
 | 
			
		||||
Copyright: 2011, Khaled Mamou
 | 
			
		||||
  2003-2009, Erwin Coumans
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/volk/
 | 
			
		||||
Files: thirdparty/volk/*
 | 
			
		||||
Comment: volk
 | 
			
		||||
Copyright: 2018-2024, Arseny Kapoulkine
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/vulkan/
 | 
			
		||||
Files: thirdparty/vulkan/*
 | 
			
		||||
Comment: Vulkan Headers
 | 
			
		||||
Copyright: 2014-2024, The Khronos Group Inc.
 | 
			
		||||
  2014-2024, Valve Corporation
 | 
			
		||||
  2014-2024, LunarG, Inc.
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/vulkan/vk_mem_alloc.h
 | 
			
		||||
Files: thirdparty/vulkan/vk_mem_alloc.h
 | 
			
		||||
Comment: Vulkan Memory Allocator
 | 
			
		||||
Copyright: 2017-2024, Advanced Micro Devices, Inc.
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/wayland/
 | 
			
		||||
Files: thirdparty/wayland/*
 | 
			
		||||
Comment: Wayland core protocol
 | 
			
		||||
Copyright: 2008-2012, Kristian Høgsberg
 | 
			
		||||
  2010-2012, Intel Corporation
 | 
			
		||||
| 
						 | 
				
			
			@ -532,7 +567,7 @@ Copyright: 2008-2012, Kristian Høgsberg
 | 
			
		|||
  2012, Collabora, Ltd.
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/wayland-protocols/
 | 
			
		||||
Files: thirdparty/wayland-protocols/*
 | 
			
		||||
Comment: Wayland protocols that add functionality not available in the core protocol
 | 
			
		||||
Copyright: 2008-2013, Kristian Høgsberg
 | 
			
		||||
  2010-2013, Intel Corporation
 | 
			
		||||
| 
						 | 
				
			
			@ -544,24 +579,24 @@ Copyright: 2008-2013, Kristian Høgsberg
 | 
			
		|||
  2015,      Red Hat Inc.
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/wslay/
 | 
			
		||||
Files: thirdparty/wslay/*
 | 
			
		||||
Comment: Wslay
 | 
			
		||||
Copyright: 2011, 2012, 2015, Tatsuhiro Tsujikawa
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/xatlas/
 | 
			
		||||
Files: thirdparty/xatlas/*
 | 
			
		||||
Comment: xatlas
 | 
			
		||||
Copyright: 2018-2020, Jonathan Young
 | 
			
		||||
  2013, Thekla, Inc
 | 
			
		||||
  2006, NVIDIA Corporation, Ignacio Castano
 | 
			
		||||
License: Expat
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/zlib/
 | 
			
		||||
Files: thirdparty/zlib/*
 | 
			
		||||
Comment: zlib
 | 
			
		||||
Copyright: 1995-2024, Jean-loup Gailly and Mark Adler
 | 
			
		||||
License: Zlib
 | 
			
		||||
 | 
			
		||||
Files: ./thirdparty/zstd/
 | 
			
		||||
Files: thirdparty/zstd/*
 | 
			
		||||
Comment: Zstandard
 | 
			
		||||
Copyright: Meta Platforms, Inc. and affiliates.
 | 
			
		||||
License: BSD-3-clause
 | 
			
		||||
| 
						 | 
				
			
			@ -569,6 +604,196 @@ License: BSD-3-clause
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
License: Apache-2.0
 | 
			
		||||
                               Apache License
 | 
			
		||||
                         Version 2.0, January 2004
 | 
			
		||||
                      http://www.apache.org/licenses/
 | 
			
		||||
 .
 | 
			
		||||
 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
			
		||||
 .
 | 
			
		||||
 1. Definitions.
 | 
			
		||||
 .
 | 
			
		||||
    "License" shall mean the terms and conditions for use, reproduction,
 | 
			
		||||
    and distribution as defined by Sections 1 through 9 of this document.
 | 
			
		||||
 .
 | 
			
		||||
    "Licensor" shall mean the copyright owner or entity authorized by
 | 
			
		||||
    the copyright owner that is granting the License.
 | 
			
		||||
 .
 | 
			
		||||
    "Legal Entity" shall mean the union of the acting entity and all
 | 
			
		||||
    other entities that control, are controlled by, or are under common
 | 
			
		||||
    control with that entity. For the purposes of this definition,
 | 
			
		||||
    "control" means (i) the power, direct or indirect, to cause the
 | 
			
		||||
    direction or management of such entity, whether by contract or
 | 
			
		||||
    otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
			
		||||
    outstanding shares, or (iii) beneficial ownership of such entity.
 | 
			
		||||
 .
 | 
			
		||||
    "You" (or "Your") shall mean an individual or Legal Entity
 | 
			
		||||
    exercising permissions granted by this License.
 | 
			
		||||
 .
 | 
			
		||||
    "Source" form shall mean the preferred form for making modifications,
 | 
			
		||||
    including but not limited to software source code, documentation
 | 
			
		||||
    source, and configuration files.
 | 
			
		||||
 .
 | 
			
		||||
    "Object" form shall mean any form resulting from mechanical
 | 
			
		||||
    transformation or translation of a Source form, including but
 | 
			
		||||
    not limited to compiled object code, generated documentation,
 | 
			
		||||
    and conversions to other media types.
 | 
			
		||||
 .
 | 
			
		||||
    "Work" shall mean the work of authorship, whether in Source or
 | 
			
		||||
    Object form, made available under the License, as indicated by a
 | 
			
		||||
    copyright notice that is included in or attached to the work
 | 
			
		||||
    (an example is provided in the Appendix below).
 | 
			
		||||
 .
 | 
			
		||||
    "Derivative Works" shall mean any work, whether in Source or Object
 | 
			
		||||
    form, that is based on (or derived from) the Work and for which the
 | 
			
		||||
    editorial revisions, annotations, elaborations, or other modifications
 | 
			
		||||
    represent, as a whole, an original work of authorship. For the purposes
 | 
			
		||||
    of this License, Derivative Works shall not include works that remain
 | 
			
		||||
    separable from, or merely link (or bind by name) to the interfaces of,
 | 
			
		||||
    the Work and Derivative Works thereof.
 | 
			
		||||
 .
 | 
			
		||||
    "Contribution" shall mean any work of authorship, including
 | 
			
		||||
    the original version of the Work and any modifications or additions
 | 
			
		||||
    to that Work or Derivative Works thereof, that is intentionally
 | 
			
		||||
    submitted to Licensor for inclusion in the Work by the copyright owner
 | 
			
		||||
    or by an individual or Legal Entity authorized to submit on behalf of
 | 
			
		||||
    the copyright owner. For the purposes of this definition, "submitted"
 | 
			
		||||
    means any form of electronic, verbal, or written communication sent
 | 
			
		||||
    to the Licensor or its representatives, including but not limited to
 | 
			
		||||
    communication on electronic mailing lists, source code control systems,
 | 
			
		||||
    and issue tracking systems that are managed by, or on behalf of, the
 | 
			
		||||
    Licensor for the purpose of discussing and improving the Work, but
 | 
			
		||||
    excluding communication that is conspicuously marked or otherwise
 | 
			
		||||
    designated in writing by the copyright owner as "Not a Contribution."
 | 
			
		||||
 .
 | 
			
		||||
    "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
			
		||||
    on behalf of whom a Contribution has been received by Licensor and
 | 
			
		||||
    subsequently incorporated within the Work.
 | 
			
		||||
 .
 | 
			
		||||
 2. Grant of Copyright License. Subject to the terms and conditions of
 | 
			
		||||
    this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
    worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
    copyright license to reproduce, prepare Derivative Works of,
 | 
			
		||||
    publicly display, publicly perform, sublicense, and distribute the
 | 
			
		||||
    Work and such Derivative Works in Source or Object form.
 | 
			
		||||
 .
 | 
			
		||||
 3. Grant of Patent License. Subject to the terms and conditions of
 | 
			
		||||
    this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
    worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
    (except as stated in this section) patent license to make, have made,
 | 
			
		||||
    use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
			
		||||
    where such license applies only to those patent claims licensable
 | 
			
		||||
    by such Contributor that are necessarily infringed by their
 | 
			
		||||
    Contribution(s) alone or by combination of their Contribution(s)
 | 
			
		||||
    with the Work to which such Contribution(s) was submitted. If You
 | 
			
		||||
    institute patent litigation against any entity (including a
 | 
			
		||||
    cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
			
		||||
    or a Contribution incorporated within the Work constitutes direct
 | 
			
		||||
    or contributory patent infringement, then any patent licenses
 | 
			
		||||
    granted to You under this License for that Work shall terminate
 | 
			
		||||
    as of the date such litigation is filed.
 | 
			
		||||
 .
 | 
			
		||||
 4. Redistribution. You may reproduce and distribute copies of the
 | 
			
		||||
    Work or Derivative Works thereof in any medium, with or without
 | 
			
		||||
    modifications, and in Source or Object form, provided that You
 | 
			
		||||
    meet the following conditions:
 | 
			
		||||
 .
 | 
			
		||||
 (a) You must give any other recipients of the Work or
 | 
			
		||||
     Derivative Works a copy of this License; and
 | 
			
		||||
 .
 | 
			
		||||
 (b) You must cause any modified files to carry prominent notices
 | 
			
		||||
     stating that You changed the files; and
 | 
			
		||||
 .
 | 
			
		||||
 (c) You must retain, in the Source form of any Derivative Works
 | 
			
		||||
     that You distribute, all copyright, patent, trademark, and
 | 
			
		||||
     attribution notices from the Source form of the Work,
 | 
			
		||||
     excluding those notices that do not pertain to any part of
 | 
			
		||||
     the Derivative Works; and
 | 
			
		||||
 .
 | 
			
		||||
 (d) If the Work includes a "NOTICE" text file as part of its
 | 
			
		||||
     distribution, then any Derivative Works that You distribute must
 | 
			
		||||
     include a readable copy of the attribution notices contained
 | 
			
		||||
     within such NOTICE file, excluding those notices that do not
 | 
			
		||||
     pertain to any part of the Derivative Works, in at least one
 | 
			
		||||
     of the following places: within a NOTICE text file distributed
 | 
			
		||||
     as part of the Derivative Works; within the Source form or
 | 
			
		||||
     documentation, if provided along with the Derivative Works; or,
 | 
			
		||||
     within a display generated by the Derivative Works, if and
 | 
			
		||||
     wherever such third-party notices normally appear. The contents
 | 
			
		||||
     of the NOTICE file are for informational purposes only and
 | 
			
		||||
     do not modify the License. You may add Your own attribution
 | 
			
		||||
     notices within Derivative Works that You distribute, alongside
 | 
			
		||||
     or as an addendum to the NOTICE text from the Work, provided
 | 
			
		||||
     that such additional attribution notices cannot be construed
 | 
			
		||||
     as modifying the License.
 | 
			
		||||
 .
 | 
			
		||||
 You may add Your own copyright statement to Your modifications and
 | 
			
		||||
 may provide additional or different license terms and conditions
 | 
			
		||||
 for use, reproduction, or distribution of Your modifications, or
 | 
			
		||||
 for any such Derivative Works as a whole, provided Your use,
 | 
			
		||||
 reproduction, and distribution of the Work otherwise complies with
 | 
			
		||||
 the conditions stated in this License.
 | 
			
		||||
 .
 | 
			
		||||
 5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
			
		||||
    any Contribution intentionally submitted for inclusion in the Work
 | 
			
		||||
    by You to the Licensor shall be under the terms and conditions of
 | 
			
		||||
    this License, without any additional terms or conditions.
 | 
			
		||||
    Notwithstanding the above, nothing herein shall supersede or modify
 | 
			
		||||
    the terms of any separate license agreement you may have executed
 | 
			
		||||
    with Licensor regarding such Contributions.
 | 
			
		||||
 .
 | 
			
		||||
 6. Trademarks. This License does not grant permission to use the trade
 | 
			
		||||
    names, trademarks, service marks, or product names of the Licensor,
 | 
			
		||||
    except as required for reasonable and customary use in describing the
 | 
			
		||||
    origin of the Work and reproducing the content of the NOTICE file.
 | 
			
		||||
 .
 | 
			
		||||
 7. Disclaimer of Warranty. Unless required by applicable law or
 | 
			
		||||
    agreed to in writing, Licensor provides the Work (and each
 | 
			
		||||
    Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
			
		||||
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
			
		||||
    implied, including, without limitation, any warranties or conditions
 | 
			
		||||
    of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
			
		||||
    PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
			
		||||
    appropriateness of using or redistributing the Work and assume any
 | 
			
		||||
    risks associated with Your exercise of permissions under this License.
 | 
			
		||||
 .
 | 
			
		||||
 8. Limitation of Liability. In no event and under no legal theory,
 | 
			
		||||
    whether in tort (including negligence), contract, or otherwise,
 | 
			
		||||
    unless required by applicable law (such as deliberate and grossly
 | 
			
		||||
    negligent acts) or agreed to in writing, shall any Contributor be
 | 
			
		||||
    liable to You for damages, including any direct, indirect, special,
 | 
			
		||||
    incidental, or consequential damages of any character arising as a
 | 
			
		||||
    result of this License or out of the use or inability to use the
 | 
			
		||||
    Work (including but not limited to damages for loss of goodwill,
 | 
			
		||||
    work stoppage, computer failure or malfunction, or any and all
 | 
			
		||||
    other commercial damages or losses), even if such Contributor
 | 
			
		||||
    has been advised of the possibility of such damages.
 | 
			
		||||
 .
 | 
			
		||||
 9. Accepting Warranty or Additional Liability. While redistributing
 | 
			
		||||
    the Work or Derivative Works thereof, You may choose to offer,
 | 
			
		||||
    and charge a fee for, acceptance of support, warranty, indemnity,
 | 
			
		||||
    or other liability obligations and/or rights consistent with this
 | 
			
		||||
    License. However, in accepting such obligations, You may act only
 | 
			
		||||
    on Your own behalf and on Your sole responsibility, not on behalf
 | 
			
		||||
    of any other Contributor, and only if You agree to indemnify,
 | 
			
		||||
    defend, and hold each Contributor harmless for any liability
 | 
			
		||||
    incurred by, or claims asserted against, such Contributor by reason
 | 
			
		||||
    of your accepting any such warranty or additional liability.
 | 
			
		||||
 .
 | 
			
		||||
 END OF TERMS AND CONDITIONS
 | 
			
		||||
 .
 | 
			
		||||
 APPENDIX: How to apply the Apache License to your work.
 | 
			
		||||
 .
 | 
			
		||||
    To apply the Apache License to your work, attach the following
 | 
			
		||||
    boilerplate notice, with the fields enclosed by brackets "[]"
 | 
			
		||||
    replaced with your own identifying information. (Don't include
 | 
			
		||||
    the brackets!)  The text should be enclosed in the appropriate
 | 
			
		||||
    comment syntax for the file format. We also recommend that a
 | 
			
		||||
    file or class name and description of purpose be included on the
 | 
			
		||||
    same "printed page" as the copyright notice for easier
 | 
			
		||||
    identification within third-party archives.
 | 
			
		||||
 .
 | 
			
		||||
 Copyright [yyyy] [name of copyright owner]
 | 
			
		||||
 .
 | 
			
		||||
 Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 you may not use this file except in compliance with the License.
 | 
			
		||||
 You may obtain a copy of the License at
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,12 +12,12 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
 | 
			
		||||
## Patrons
 | 
			
		||||
 | 
			
		||||
    Khronos® Group <https://www.khronos.org/>
 | 
			
		||||
    OSS Capital <https://oss.capital/>
 | 
			
		||||
    Re-Logic <https://re-logic.com/>
 | 
			
		||||
 | 
			
		||||
## Platinum sponsors
 | 
			
		||||
 | 
			
		||||
    Google Play <https://play.google.com/>
 | 
			
		||||
    Google Play <https://play.google.com>
 | 
			
		||||
    Ramatak <https://ramatak.com/>
 | 
			
		||||
    V-Sekai <https://github.com/V-Sekai>
 | 
			
		||||
    W4 Games <https://w4games.com/>
 | 
			
		||||
| 
						 | 
				
			
			@ -25,53 +25,56 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
## Gold sponsors
 | 
			
		||||
 | 
			
		||||
    Mega Crit <https://www.megacrit.com/>
 | 
			
		||||
    Pirate Software <https://gopiratesoftware.com/>
 | 
			
		||||
    Prehensile Tales <https://prehensile-tales.com/>
 | 
			
		||||
    Pirate Software <https://gopiratesoftware.com>
 | 
			
		||||
    Prehensile Tales <https://prehensile-tales.com>
 | 
			
		||||
    Robot Gentleman <http://robotgentleman.com/>
 | 
			
		||||
 | 
			
		||||
## Silver sponsors
 | 
			
		||||
 | 
			
		||||
    Broken Rules <https://brokenrul.es/>
 | 
			
		||||
    Chasing Carrots <https://www.chasing-carrots.com/>
 | 
			
		||||
    Broken Rules <https://brokenrul.es>
 | 
			
		||||
    Chasing Carrots <https://www.chasing-carrots.com>
 | 
			
		||||
    Copia Wealth Studios <https://copiawealthstudios.com/>
 | 
			
		||||
    Indoor Astronaut <https://indoorastronaut.ch/>
 | 
			
		||||
    LoadComplete <https://loadcomplete.com/>
 | 
			
		||||
    Null <https://null.com/>
 | 
			
		||||
    Orbital Knight <https://www.orbitalknight.com/>
 | 
			
		||||
    Playful Studios <https://playfulstudios.com/>
 | 
			
		||||
    Re-Logic <https://re-logic.com/>
 | 
			
		||||
 | 
			
		||||
## Diamond members
 | 
			
		||||
 | 
			
		||||
    Bippinbits <http://domekeepergame.com/>
 | 
			
		||||
    Sealow
 | 
			
		||||
    And 5 anonymous donors
 | 
			
		||||
    Sylv <https://rankith.itch.io/unnamed-space-idle-prototype>
 | 
			
		||||
    And 3 anonymous donors
 | 
			
		||||
 | 
			
		||||
## Titanium members
 | 
			
		||||
 | 
			
		||||
    Adriaan de Jongh <https://adriaan.games/>
 | 
			
		||||
    Anitya Space <https://www.anitya.space/>
 | 
			
		||||
    Adriaan de Jongh <https://adriaan.games>
 | 
			
		||||
    Anitya Space <https://www.anitya.space>
 | 
			
		||||
    Basically Games
 | 
			
		||||
    FDG Entertainment <https://www.fdg-entertainment.com/>
 | 
			
		||||
    Game Dev Artisan <https://gamedevartisan.com/>
 | 
			
		||||
    FDG Entertainment <https://www.fdg-entertainment.com>
 | 
			
		||||
    Game Dev Artisan <https://gamedevartisan.com>
 | 
			
		||||
    Garry Newman
 | 
			
		||||
    Isaiah Smith <https://www.isaiahsmith.dev/>
 | 
			
		||||
    Libretrend <https://libretrend.com/>
 | 
			
		||||
    Kenney <https://kenney.nl/>
 | 
			
		||||
    Libretrend <https://libretrend.com>
 | 
			
		||||
    Life Art Studios <https://lifeartstudios.net/>
 | 
			
		||||
    Lucid Silence Games
 | 
			
		||||
    Matthew Campbell
 | 
			
		||||
    PolyMars <https://polymars.dev/>
 | 
			
		||||
    RPG in a Box <https://www.rpginabox.com/>
 | 
			
		||||
    Razenpok <https://www.youtube.com/watch?v=-QxI-RP6-HM>
 | 
			
		||||
    Smirk Software <https://smirk.gg/>
 | 
			
		||||
    RPG in a Box <https://www.rpginabox.com>
 | 
			
		||||
    Smirk Software <https://smirk.gg>
 | 
			
		||||
    Studio Sunshower <https://www.studiosunshower.com/>
 | 
			
		||||
    TrampolineTales <https://TrampolineTales.com/>
 | 
			
		||||
    粟二华 (Su Erhua)
 | 
			
		||||
    And 6 anonymous donors
 | 
			
		||||
    And 4 anonymous donors
 | 
			
		||||
 | 
			
		||||
## Platinum members
 | 
			
		||||
 | 
			
		||||
    Andy Touch
 | 
			
		||||
    BlockImperiumGames (BIG)
 | 
			
		||||
    Christoph Woinke
 | 
			
		||||
    Christopher Shifflett
 | 
			
		||||
    Christoph Woinke
 | 
			
		||||
    Cody Bentley
 | 
			
		||||
    Darrin Massena
 | 
			
		||||
    Edward Flick
 | 
			
		||||
| 
						 | 
				
			
			@ -79,8 +82,8 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    HP van Braam
 | 
			
		||||
    iCommitGames
 | 
			
		||||
    Jonah Stich
 | 
			
		||||
    Justo Delgado Baudí
 | 
			
		||||
    katnamag
 | 
			
		||||
    Marek Belski
 | 
			
		||||
    Matthew Ekenstedt
 | 
			
		||||
    Memories in 8Bit
 | 
			
		||||
    Mike King
 | 
			
		||||
| 
						 | 
				
			
			@ -97,13 +100,14 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    TigerJ
 | 
			
		||||
    Violin Iliev
 | 
			
		||||
    Vladimír Chvátil
 | 
			
		||||
    And 16 anonymous donors
 | 
			
		||||
    And 13 anonymous donors
 | 
			
		||||
 | 
			
		||||
## Gold members
 | 
			
		||||
 | 
			
		||||
    80px
 | 
			
		||||
    afreytes
 | 
			
		||||
    alMoo Games
 | 
			
		||||
    alMoo Games
 | 
			
		||||
    Alva Majo
 | 
			
		||||
    Antti Vesanen
 | 
			
		||||
    Asher Glick
 | 
			
		||||
| 
						 | 
				
			
			@ -119,6 +123,8 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    Bryce Dixon
 | 
			
		||||
    c64cosmin
 | 
			
		||||
    Carlo del Mundo
 | 
			
		||||
    Carl van der Geest
 | 
			
		||||
    Chocolate Software
 | 
			
		||||
    Cindy Trieu
 | 
			
		||||
    ClarkThyLord
 | 
			
		||||
    Codex404
 | 
			
		||||
| 
						 | 
				
			
			@ -135,17 +141,21 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    dgehrig
 | 
			
		||||
    dhanielk
 | 
			
		||||
    Distorted Realities
 | 
			
		||||
    Donkung
 | 
			
		||||
    Dono
 | 
			
		||||
    Don't You Know Who I Am? Inc.
 | 
			
		||||
    Dustuu
 | 
			
		||||
    Dylan P.
 | 
			
		||||
    Edelweiss
 | 
			
		||||
    Ends
 | 
			
		||||
    Eren Ogrul
 | 
			
		||||
    Eric Brand
 | 
			
		||||
    Eric Phy
 | 
			
		||||
    Faisal Al-Kubaisi (QatariGameDev)
 | 
			
		||||
    Felix Adam
 | 
			
		||||
    FeralBytes
 | 
			
		||||
    Festzeltgaming.de
 | 
			
		||||
    Frozen Fractal
 | 
			
		||||
    Gaudipern
 | 
			
		||||
    GlassBrick
 | 
			
		||||
    Grau
 | 
			
		||||
| 
						 | 
				
			
			@ -153,6 +163,7 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    Hayden Oliver
 | 
			
		||||
    hiulit
 | 
			
		||||
    Illyan
 | 
			
		||||
    Immaculate Lift Studio
 | 
			
		||||
    Ivan Tabashki
 | 
			
		||||
    Jacob (HACKhalo2 Studios)
 | 
			
		||||
    Jam
 | 
			
		||||
| 
						 | 
				
			
			@ -160,13 +171,12 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    Javier Roman
 | 
			
		||||
    Jeff Hungerford
 | 
			
		||||
    Jeronimo Schreyer
 | 
			
		||||
    Joel Martinez
 | 
			
		||||
    Johannes Wuensch
 | 
			
		||||
    John Gabriel
 | 
			
		||||
    Jonas Yamazaki
 | 
			
		||||
    Jonathan
 | 
			
		||||
    José Canepa
 | 
			
		||||
    Joshua Stelly
 | 
			
		||||
    Justin Sasso
 | 
			
		||||
    Kalydi Balázs
 | 
			
		||||
    KAR Games
 | 
			
		||||
    Kiri "ExpiredPopsicle" Artemis
 | 
			
		||||
| 
						 | 
				
			
			@ -181,7 +191,9 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    m1n1ster
 | 
			
		||||
    Manuel Requena
 | 
			
		||||
    Mara Huldra
 | 
			
		||||
    Marek Belski
 | 
			
		||||
    Martin Šenkeřík
 | 
			
		||||
    MHDante
 | 
			
		||||
    Michael Gooch
 | 
			
		||||
    Modus Ponens
 | 
			
		||||
    Moshe Harris
 | 
			
		||||
| 
						 | 
				
			
			@ -190,6 +202,7 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    Nassor Paulino da Silva
 | 
			
		||||
    nezticle
 | 
			
		||||
    Niklas Wahrman
 | 
			
		||||
    Nitzan Bueno
 | 
			
		||||
    Niwl Games
 | 
			
		||||
    NotNet
 | 
			
		||||
    Oathbringer
 | 
			
		||||
| 
						 | 
				
			
			@ -207,6 +220,7 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    re:thinc
 | 
			
		||||
    Richard Ivánek
 | 
			
		||||
    Rudi P
 | 
			
		||||
    Sam Leathers
 | 
			
		||||
    Samuel Judd
 | 
			
		||||
    ScoreSpace
 | 
			
		||||
    Shiny Shinken
 | 
			
		||||
| 
						 | 
				
			
			@ -238,12 +252,12 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    Zhu Li
 | 
			
		||||
    zikes
 | 
			
		||||
    嗯大爷
 | 
			
		||||
    潘彦圣
 | 
			
		||||
 | 
			
		||||
    Alex Khayrullin
 | 
			
		||||
    Algebrute
 | 
			
		||||
    Andriy
 | 
			
		||||
    Antanas Paskauskas
 | 
			
		||||
    anti666
 | 
			
		||||
    Ari
 | 
			
		||||
    Arisaka Mayuki
 | 
			
		||||
    Arthur S. Muszynski
 | 
			
		||||
| 
						 | 
				
			
			@ -269,13 +283,11 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    Liam Smyth
 | 
			
		||||
    LoparPanda
 | 
			
		||||
    Martin Gulliksson
 | 
			
		||||
    Martin Soucek
 | 
			
		||||
    Michael Dürwald
 | 
			
		||||
    Michael Policastro
 | 
			
		||||
    n00sh
 | 
			
		||||
    Nicolás Monner Sans
 | 
			
		||||
    Nikita Rotskov
 | 
			
		||||
    Nikola Whallon
 | 
			
		||||
    Oliver Dick
 | 
			
		||||
    Patrick Wuttke
 | 
			
		||||
    Pete Goodwin
 | 
			
		||||
| 
						 | 
				
			
			@ -298,7 +310,6 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    VoidPointer
 | 
			
		||||
    Yifan Lai
 | 
			
		||||
 | 
			
		||||
    Aaron Mayfield
 | 
			
		||||
    Adam Carr
 | 
			
		||||
    Adam Smeltzer
 | 
			
		||||
    Adisibio
 | 
			
		||||
| 
						 | 
				
			
			@ -315,7 +326,6 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    Ano Nim
 | 
			
		||||
    Arch Toasty
 | 
			
		||||
    Arda Erol
 | 
			
		||||
    A Really Tall Horse
 | 
			
		||||
    Arturo Rosales
 | 
			
		||||
    Ash K
 | 
			
		||||
    Aubrey Falconer
 | 
			
		||||
| 
						 | 
				
			
			@ -343,6 +353,7 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    Dakota Watkins
 | 
			
		||||
    Daniele Tolomelli
 | 
			
		||||
    Daniel Ramos
 | 
			
		||||
    Daren Scot Wilson
 | 
			
		||||
    Dave Jansen
 | 
			
		||||
    Davesnothere
 | 
			
		||||
    David Baker
 | 
			
		||||
| 
						 | 
				
			
			@ -361,12 +372,11 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    Eric Stokes
 | 
			
		||||
    Eric Williams
 | 
			
		||||
    Erkki Seppälä
 | 
			
		||||
    Ewan Holmes
 | 
			
		||||
    Felix Adam
 | 
			
		||||
    Frank
 | 
			
		||||
    Frying☆Pan
 | 
			
		||||
    Game Endeavor
 | 
			
		||||
    gamerminstrel
 | 
			
		||||
    Garrett S
 | 
			
		||||
    Gary Thomas
 | 
			
		||||
    gebba
 | 
			
		||||
    Greyson Richey
 | 
			
		||||
| 
						 | 
				
			
			@ -385,7 +395,6 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    Jamie Massey
 | 
			
		||||
    JARKKO PARVIAINEN
 | 
			
		||||
    Jason Evans
 | 
			
		||||
    Joakim Askenbäck
 | 
			
		||||
    Jonas
 | 
			
		||||
    Jonas Arndt
 | 
			
		||||
    Jonas Yamazaki
 | 
			
		||||
| 
						 | 
				
			
			@ -421,7 +430,6 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    Martin Holas
 | 
			
		||||
    Martin Liška
 | 
			
		||||
    Martin Trbola
 | 
			
		||||
    Matěj Drábek
 | 
			
		||||
    Mathieu
 | 
			
		||||
    Matt Edwards
 | 
			
		||||
    Maverick
 | 
			
		||||
| 
						 | 
				
			
			@ -441,7 +449,6 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    Neofytos Chimonas
 | 
			
		||||
    Nerdforge
 | 
			
		||||
    Nerdyninja
 | 
			
		||||
    Nick Eldrenkamp
 | 
			
		||||
    Nik Rudenko
 | 
			
		||||
    Noel Billig
 | 
			
		||||
    ozrk
 | 
			
		||||
| 
						 | 
				
			
			@ -450,7 +457,6 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    Patrick Nafarrete
 | 
			
		||||
    Paul Black
 | 
			
		||||
    Paul Gieske
 | 
			
		||||
    Paul Mozet
 | 
			
		||||
    Pete
 | 
			
		||||
    Phoenix Jauregui
 | 
			
		||||
    Pierre Caye
 | 
			
		||||
| 
						 | 
				
			
			@ -462,7 +468,6 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    Raghava Kovvali
 | 
			
		||||
    Ragnar Pettersson
 | 
			
		||||
    Rammeow
 | 
			
		||||
    Rebecca H
 | 
			
		||||
    Richard Hayes
 | 
			
		||||
    Riley
 | 
			
		||||
    RobotCritter
 | 
			
		||||
| 
						 | 
				
			
			@ -508,7 +513,7 @@ generous deed immortalized in the next stable release of Godot Engine.
 | 
			
		|||
    ケルベロス
 | 
			
		||||
    貴宏 小松
 | 
			
		||||
 | 
			
		||||
    And 181 anonymous donors
 | 
			
		||||
    And 176 anonymous donors
 | 
			
		||||
 | 
			
		||||
## Silver and bronze donors
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,14 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
from misc.utility.scons_hints import *
 | 
			
		||||
 | 
			
		||||
EnsureSConsVersion(3, 1, 2)
 | 
			
		||||
EnsurePythonVersion(3, 6)
 | 
			
		||||
EnsureSConsVersion(4, 0)
 | 
			
		||||
EnsurePythonVersion(3, 8)
 | 
			
		||||
 | 
			
		||||
# System
 | 
			
		||||
import atexit
 | 
			
		||||
import glob
 | 
			
		||||
import os
 | 
			
		||||
import pickle
 | 
			
		||||
import sys
 | 
			
		||||
import time
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
from importlib.util import module_from_spec, spec_from_file_location
 | 
			
		||||
from types import ModuleType
 | 
			
		||||
| 
						 | 
				
			
			@ -51,37 +50,20 @@ _helper_module("platform_methods", "platform_methods.py")
 | 
			
		|||
_helper_module("version", "version.py")
 | 
			
		||||
_helper_module("core.core_builders", "core/core_builders.py")
 | 
			
		||||
_helper_module("main.main_builders", "main/main_builders.py")
 | 
			
		||||
_helper_module("misc.utility.color", "misc/utility/color.py")
 | 
			
		||||
 | 
			
		||||
# Local
 | 
			
		||||
import gles3_builders
 | 
			
		||||
import glsl_builders
 | 
			
		||||
import methods
 | 
			
		||||
import scu_builders
 | 
			
		||||
from methods import print_error, print_warning
 | 
			
		||||
from platform_methods import architecture_aliases, architectures
 | 
			
		||||
from misc.utility.color import STDERR_COLOR, print_error, print_info, print_warning
 | 
			
		||||
from platform_methods import architecture_aliases, architectures, compatibility_platform_aliases
 | 
			
		||||
 | 
			
		||||
if ARGUMENTS.get("target", "editor") == "editor":
 | 
			
		||||
    _helper_module("editor.editor_builders", "editor/editor_builders.py")
 | 
			
		||||
    _helper_module("editor.template_builders", "editor/template_builders.py")
 | 
			
		||||
 | 
			
		||||
# Enable ANSI escape code support on Windows 10 and later (for colored console output).
 | 
			
		||||
# <https://github.com/python/cpython/issues/73245>
 | 
			
		||||
if sys.stdout.isatty() and sys.platform == "win32":
 | 
			
		||||
    try:
 | 
			
		||||
        from ctypes import WinError, byref, windll  # type: ignore
 | 
			
		||||
        from ctypes.wintypes import DWORD  # type: ignore
 | 
			
		||||
 | 
			
		||||
        stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11))
 | 
			
		||||
        mode = DWORD(0)
 | 
			
		||||
        if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)):
 | 
			
		||||
            raise WinError()
 | 
			
		||||
        mode = DWORD(mode.value | 4)
 | 
			
		||||
        if not windll.kernel32.SetConsoleMode(stdout_handle, mode):
 | 
			
		||||
            raise WinError()
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        methods._colorize = False
 | 
			
		||||
        print_error(f"Failed to enable ANSI escape code support, disabling color output.\n{e}")
 | 
			
		||||
 | 
			
		||||
# Scan possible build platforms
 | 
			
		||||
 | 
			
		||||
platform_list = []  # list of platforms
 | 
			
		||||
| 
						 | 
				
			
			@ -91,8 +73,6 @@ platform_doc_class_path = {}
 | 
			
		|||
platform_exporters = []
 | 
			
		||||
platform_apis = []
 | 
			
		||||
 | 
			
		||||
time_at_start = time.time()
 | 
			
		||||
 | 
			
		||||
for x in sorted(glob.glob("platform/*")):
 | 
			
		||||
    if not os.path.isdir(x) or not os.path.exists(x + "/detect.py"):
 | 
			
		||||
        continue
 | 
			
		||||
| 
						 | 
				
			
			@ -127,34 +107,21 @@ for x in sorted(glob.glob("platform/*")):
 | 
			
		|||
    sys.path.remove(tmppath)
 | 
			
		||||
    sys.modules.pop("detect")
 | 
			
		||||
 | 
			
		||||
custom_tools = ["default"]
 | 
			
		||||
 | 
			
		||||
platform_arg = ARGUMENTS.get("platform", ARGUMENTS.get("p", False))
 | 
			
		||||
 | 
			
		||||
if platform_arg == "android":
 | 
			
		||||
    custom_tools = ["clang", "clang++", "as", "ar", "link"]
 | 
			
		||||
elif platform_arg == "web":
 | 
			
		||||
    # Use generic POSIX build toolchain for Emscripten.
 | 
			
		||||
    custom_tools = ["cc", "c++", "ar", "link", "textfile", "zip"]
 | 
			
		||||
elif os.name == "nt" and methods.get_cmdline_bool("use_mingw", False):
 | 
			
		||||
    custom_tools = ["mingw"]
 | 
			
		||||
 | 
			
		||||
# We let SCons build its default ENV as it includes OS-specific things which we don't
 | 
			
		||||
# want to have to pull in manually.
 | 
			
		||||
# want to have to pull in manually. However we enforce no "tools", which we register
 | 
			
		||||
# further down after parsing our platform-specific configuration.
 | 
			
		||||
# Then we prepend PATH to make it take precedence, while preserving SCons' own entries.
 | 
			
		||||
env = Environment(tools=custom_tools)
 | 
			
		||||
env = Environment(tools=[])
 | 
			
		||||
env.PrependENVPath("PATH", os.getenv("PATH"))
 | 
			
		||||
env.PrependENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
 | 
			
		||||
if "TERM" in os.environ:  # Used for colored output.
 | 
			
		||||
    env["ENV"]["TERM"] = os.environ["TERM"]
 | 
			
		||||
 | 
			
		||||
env.disabled_modules = []
 | 
			
		||||
env.disabled_modules = set()
 | 
			
		||||
env.module_version_string = ""
 | 
			
		||||
env.msvc = False
 | 
			
		||||
env.scons_version = env._get_major_minor_revision(scons_raw_version)
 | 
			
		||||
 | 
			
		||||
env.__class__.disable_module = methods.disable_module
 | 
			
		||||
 | 
			
		||||
env.__class__.add_module_version_string = methods.add_module_version_string
 | 
			
		||||
 | 
			
		||||
env.__class__.add_source_files = methods.add_source_files
 | 
			
		||||
| 
						 | 
				
			
			@ -190,11 +157,7 @@ if profile:
 | 
			
		|||
opts = Variables(customs, ARGUMENTS)
 | 
			
		||||
 | 
			
		||||
# Target build options
 | 
			
		||||
if env.scons_version >= (4, 3):
 | 
			
		||||
    opts.Add(["platform", "p"], "Target platform (%s)" % "|".join(platform_list), "")
 | 
			
		||||
else:
 | 
			
		||||
    opts.Add("platform", "Target platform (%s)" % "|".join(platform_list), "")
 | 
			
		||||
    opts.Add("p", "Alias for 'platform'", "")
 | 
			
		||||
opts.Add((["platform", "p"], "Target platform (%s)" % "|".join(platform_list), ""))
 | 
			
		||||
opts.Add(EnumVariable("target", "Compilation target", "editor", ("editor", "template_release", "template_debug")))
 | 
			
		||||
opts.Add(EnumVariable("arch", "CPU architecture", "auto", ["auto"] + architectures, architecture_aliases))
 | 
			
		||||
opts.Add(BoolVariable("dev_build", "Developer build with dev-only debugging code (DEV_ENABLED)", False))
 | 
			
		||||
| 
						 | 
				
			
			@ -217,11 +180,12 @@ opts.Add(BoolVariable("threads", "Enable threading support", True))
 | 
			
		|||
opts.Add(BoolVariable("deprecated", "Enable compatibility code for deprecated and removed features", True))
 | 
			
		||||
opts.Add(EnumVariable("precision", "Set the floating-point precision level", "single", ("single", "double")))
 | 
			
		||||
opts.Add(BoolVariable("minizip", "Enable ZIP archive support using minizip", True))
 | 
			
		||||
opts.Add(BoolVariable("brotli", "Enable Brotli for decompresson and WOFF2 fonts support", True))
 | 
			
		||||
opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 audio driver", False))
 | 
			
		||||
opts.Add(BoolVariable("brotli", "Enable Brotli for decompression and WOFF2 fonts support", True))
 | 
			
		||||
opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 audio driver on supported platforms", False))
 | 
			
		||||
opts.Add(BoolVariable("vulkan", "Enable the vulkan rendering driver", True))
 | 
			
		||||
opts.Add(BoolVariable("opengl3", "Enable the OpenGL/GLES3 rendering driver", True))
 | 
			
		||||
opts.Add(BoolVariable("d3d12", "Enable the Direct3D 12 rendering driver", False))
 | 
			
		||||
opts.Add(BoolVariable("d3d12", "Enable the Direct3D 12 rendering driver on supported platforms", False))
 | 
			
		||||
opts.Add(BoolVariable("metal", "Enable the Metal rendering driver on supported platforms (Apple arm64 only)", False))
 | 
			
		||||
opts.Add(BoolVariable("openxr", "Enable the OpenXR driver", True))
 | 
			
		||||
opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True))
 | 
			
		||||
opts.Add(BoolVariable("disable_exceptions", "Force disabling exception handling code", True))
 | 
			
		||||
| 
						 | 
				
			
			@ -229,11 +193,22 @@ opts.Add("custom_modules", "A list of comma-separated directory paths containing
 | 
			
		|||
opts.Add(BoolVariable("custom_modules_recursive", "Detect custom modules recursively for each specified path.", True))
 | 
			
		||||
 | 
			
		||||
# Advanced options
 | 
			
		||||
opts.Add(BoolVariable("dev_mode", "Alias for dev options: verbose=yes warnings=extra werror=yes tests=yes", False))
 | 
			
		||||
opts.Add(
 | 
			
		||||
    BoolVariable(
 | 
			
		||||
        "dev_mode", "Alias for dev options: verbose=yes warnings=extra werror=yes tests=yes strict_checks=yes", False
 | 
			
		||||
    )
 | 
			
		||||
)
 | 
			
		||||
opts.Add(BoolVariable("tests", "Build the unit tests", False))
 | 
			
		||||
opts.Add(BoolVariable("fast_unsafe", "Enable unsafe options for faster rebuilds", False))
 | 
			
		||||
opts.Add(BoolVariable("ninja", "Use the ninja backend for faster rebuilds", False))
 | 
			
		||||
opts.Add(BoolVariable("ninja_auto_run", "Run ninja automatically after generating the ninja file", True))
 | 
			
		||||
opts.Add("ninja_file", "Path to the generated ninja file", "build.ninja")
 | 
			
		||||
opts.Add(BoolVariable("compiledb", "Generate compilation DB (`compile_commands.json`) for external tools", False))
 | 
			
		||||
opts.Add(
 | 
			
		||||
    "num_jobs",
 | 
			
		||||
    "Use up to N jobs when compiling (equivalent to `-j N`). Defaults to max jobs - 1. Ignored if -j is used.",
 | 
			
		||||
    "",
 | 
			
		||||
)
 | 
			
		||||
opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False))
 | 
			
		||||
opts.Add(BoolVariable("progress", "Show a progress indicator during compilation", True))
 | 
			
		||||
opts.Add(EnumVariable("warnings", "Level of compilation warnings", "all", ("extra", "all", "moderate", "no")))
 | 
			
		||||
| 
						 | 
				
			
			@ -254,10 +229,13 @@ opts.Add(
 | 
			
		|||
    "",
 | 
			
		||||
)
 | 
			
		||||
opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False))
 | 
			
		||||
opts.Add(BoolVariable("strict_checks", "Enforce stricter checks (debug option)", False))
 | 
			
		||||
opts.Add(BoolVariable("scu_build", "Use single compilation unit build", False))
 | 
			
		||||
opts.Add("scu_limit", "Max includes per SCU file when using scu_build (determines RAM use)", "0")
 | 
			
		||||
opts.Add(BoolVariable("engine_update_check", "Enable engine update checks in the Project Manager", True))
 | 
			
		||||
opts.Add(BoolVariable("steamapi", "Enable minimal SteamAPI integration for usage time tracking (editor only)", False))
 | 
			
		||||
opts.Add("cache_path", "Path to a directory where SCons cache files will be stored. No value disables the cache.", "")
 | 
			
		||||
opts.Add("cache_limit", "Max size (in GiB) for the SCons cache. 0 means no limit.", "0")
 | 
			
		||||
 | 
			
		||||
# Thirdparty libraries
 | 
			
		||||
opts.Add(BoolVariable("builtin_brotli", "Use the built-in Brotli library", True))
 | 
			
		||||
| 
						 | 
				
			
			@ -285,7 +263,6 @@ opts.Add(BoolVariable("builtin_pcre2_with_jit", "Use JIT compiler for the built-
 | 
			
		|||
opts.Add(BoolVariable("builtin_recastnavigation", "Use the built-in Recast navigation library", True))
 | 
			
		||||
opts.Add(BoolVariable("builtin_rvo2_2d", "Use the built-in RVO2 2D library", True))
 | 
			
		||||
opts.Add(BoolVariable("builtin_rvo2_3d", "Use the built-in RVO2 3D library", True))
 | 
			
		||||
opts.Add(BoolVariable("builtin_squish", "Use the built-in squish library", True))
 | 
			
		||||
opts.Add(BoolVariable("builtin_xatlas", "Use the built-in xatlas library", True))
 | 
			
		||||
opts.Add(BoolVariable("builtin_zlib", "Use the built-in zlib library", True))
 | 
			
		||||
opts.Add(BoolVariable("builtin_zstd", "Use the built-in Zstd library", True))
 | 
			
		||||
| 
						 | 
				
			
			@ -309,6 +286,9 @@ opts.Add("rcflags", "Custom flags for Windows resource compiler")
 | 
			
		|||
# in following code (especially platform and custom_modules).
 | 
			
		||||
opts.Update(env)
 | 
			
		||||
 | 
			
		||||
# Setup caching logic early to catch everything.
 | 
			
		||||
methods.prepare_cache(env)
 | 
			
		||||
 | 
			
		||||
# Copy custom environment variables if set.
 | 
			
		||||
if env["import_env_vars"]:
 | 
			
		||||
    for env_var in str(env["import_env_vars"]).split(","):
 | 
			
		||||
| 
						 | 
				
			
			@ -317,10 +297,7 @@ if env["import_env_vars"]:
 | 
			
		|||
 | 
			
		||||
# Platform selection: validate input, and add options.
 | 
			
		||||
 | 
			
		||||
if env.scons_version < (4, 3) and not env["platform"]:
 | 
			
		||||
    env["platform"] = env["p"]
 | 
			
		||||
 | 
			
		||||
if env["platform"] == "":
 | 
			
		||||
if not env["platform"]:
 | 
			
		||||
    # Missing `platform` argument, try to detect platform automatically
 | 
			
		||||
    if (
 | 
			
		||||
        sys.platform.startswith("linux")
 | 
			
		||||
| 
						 | 
				
			
			@ -335,38 +312,29 @@ if env["platform"] == "":
 | 
			
		|||
    elif sys.platform == "win32":
 | 
			
		||||
        env["platform"] = "windows"
 | 
			
		||||
 | 
			
		||||
    if env["platform"] != "":
 | 
			
		||||
        print(f'Automatically detected platform: {env["platform"]}')
 | 
			
		||||
    if env["platform"]:
 | 
			
		||||
        print(f"Automatically detected platform: {env['platform']}")
 | 
			
		||||
 | 
			
		||||
if env["platform"] == "osx":
 | 
			
		||||
    # Deprecated alias kept for compatibility.
 | 
			
		||||
    print_warning('Platform "osx" has been renamed to "macos" in Godot 4. Building for platform "macos".')
 | 
			
		||||
    env["platform"] = "macos"
 | 
			
		||||
# Deprecated aliases kept for compatibility.
 | 
			
		||||
if env["platform"] in compatibility_platform_aliases:
 | 
			
		||||
    alias = env["platform"]
 | 
			
		||||
    platform = compatibility_platform_aliases[alias]
 | 
			
		||||
    print_warning(
 | 
			
		||||
        f'Platform "{alias}" has been renamed to "{platform}" in Godot 4. Building for platform "{platform}".'
 | 
			
		||||
    )
 | 
			
		||||
    env["platform"] = platform
 | 
			
		||||
 | 
			
		||||
if env["platform"] == "iphone":
 | 
			
		||||
    # Deprecated alias kept for compatibility.
 | 
			
		||||
    print_warning('Platform "iphone" has been renamed to "ios" in Godot 4. Building for platform "ios".')
 | 
			
		||||
    env["platform"] = "ios"
 | 
			
		||||
 | 
			
		||||
if env["platform"] in ["linux", "bsd", "x11"]:
 | 
			
		||||
    if env["platform"] == "x11":
 | 
			
		||||
        # Deprecated alias kept for compatibility.
 | 
			
		||||
        print_warning('Platform "x11" has been renamed to "linuxbsd" in Godot 4. Building for platform "linuxbsd".')
 | 
			
		||||
# Alias for convenience.
 | 
			
		||||
if env["platform"] in ["linux", "bsd"]:
 | 
			
		||||
    env["platform"] = "linuxbsd"
 | 
			
		||||
 | 
			
		||||
if env["platform"] == "javascript":
 | 
			
		||||
    # Deprecated alias kept for compatibility.
 | 
			
		||||
    print_warning('Platform "javascript" has been renamed to "web" in Godot 4. Building for platform "web".')
 | 
			
		||||
    env["platform"] = "web"
 | 
			
		||||
 | 
			
		||||
if env["platform"] not in platform_list:
 | 
			
		||||
    text = "The following platforms are available:\n\t{}\n".format("\n\t".join(platform_list))
 | 
			
		||||
    text += "Please run SCons again and select a valid platform: platform=<string>."
 | 
			
		||||
 | 
			
		||||
    if env["platform"] == "list":
 | 
			
		||||
        print(text)
 | 
			
		||||
    elif env["platform"] == "":
 | 
			
		||||
    elif not env["platform"]:
 | 
			
		||||
        print_error("Could not detect platform automatically.\n" + text)
 | 
			
		||||
    else:
 | 
			
		||||
        print_error(f'Invalid target platform "{env["platform"]}".\n' + text)
 | 
			
		||||
| 
						 | 
				
			
			@ -375,8 +343,7 @@ if env["platform"] not in platform_list:
 | 
			
		|||
 | 
			
		||||
# Add platform-specific options.
 | 
			
		||||
if env["platform"] in platform_opts:
 | 
			
		||||
    for opt in platform_opts[env["platform"]]:
 | 
			
		||||
        opts.Add(opt)
 | 
			
		||||
    opts.AddVariables(*platform_opts[env["platform"]])
 | 
			
		||||
 | 
			
		||||
# Platform-specific flags.
 | 
			
		||||
# These can sometimes override default options, so they need to be processed
 | 
			
		||||
| 
						 | 
				
			
			@ -432,12 +399,11 @@ for name, path in modules_detected.items():
 | 
			
		|||
    else:
 | 
			
		||||
        enabled = False
 | 
			
		||||
 | 
			
		||||
    opts.Add(BoolVariable("module_" + name + "_enabled", "Enable module '%s'" % (name,), enabled))
 | 
			
		||||
    opts.Add(BoolVariable(f"module_{name}_enabled", f"Enable module '{name}'", enabled))
 | 
			
		||||
 | 
			
		||||
    # Add module-specific options.
 | 
			
		||||
    try:
 | 
			
		||||
        for opt in config.get_opts(env["platform"]):
 | 
			
		||||
            opts.Add(opt)
 | 
			
		||||
        opts.AddVariables(*config.get_opts(env["platform"]))
 | 
			
		||||
    except AttributeError:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -450,6 +416,23 @@ env.modules_detected = modules_detected
 | 
			
		|||
opts.Update(env, {**ARGUMENTS, **env.Dictionary()})
 | 
			
		||||
Help(opts.GenerateHelpText(env))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# FIXME: Tool assignment happening at this stage is a direct consequence of getting the platform logic AFTER the SCons
 | 
			
		||||
# environment was already been constructed. Fixing this would require a broader refactor where all options are setup
 | 
			
		||||
# ahead of time with native validator/converter functions.
 | 
			
		||||
tmppath = "./platform/" + env["platform"]
 | 
			
		||||
sys.path.insert(0, tmppath)
 | 
			
		||||
import detect
 | 
			
		||||
 | 
			
		||||
custom_tools = ["default"]
 | 
			
		||||
try:  # Platform custom tools are optional
 | 
			
		||||
    custom_tools = detect.get_tools(env)
 | 
			
		||||
except AttributeError:
 | 
			
		||||
    pass
 | 
			
		||||
for tool in custom_tools:
 | 
			
		||||
    env.Tool(tool)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# add default include paths
 | 
			
		||||
 | 
			
		||||
env.Prepend(CPPPATH=["#"])
 | 
			
		||||
| 
						 | 
				
			
			@ -496,14 +479,19 @@ else:
 | 
			
		|||
    # Disable assert() for production targets (only used in thirdparty code).
 | 
			
		||||
    env.Append(CPPDEFINES=["NDEBUG"])
 | 
			
		||||
 | 
			
		||||
# This is not part of fast_unsafe because the only downside it has compared to
 | 
			
		||||
# the default is that SCons won't mark files that were changed in the last second
 | 
			
		||||
# as different. This is unlikely to be a problem in any real situation as just booting
 | 
			
		||||
# up scons takes more than that time.
 | 
			
		||||
# Renamed to `content-timestamp` in SCons >= 4.2, keeping MD5 for compat.
 | 
			
		||||
env.Decider("MD5-timestamp")
 | 
			
		||||
 | 
			
		||||
# SCons speed optimization controlled by the `fast_unsafe` option, which provide
 | 
			
		||||
# more than 10 s speed up for incremental rebuilds.
 | 
			
		||||
# Unsafe as they reduce the certainty of rebuilding all changed files, so it's
 | 
			
		||||
# enabled by default for `debug` builds, and can be overridden from command line.
 | 
			
		||||
# Ref: https://github.com/SCons/scons/wiki/GoFastButton
 | 
			
		||||
if methods.get_cmdline_bool("fast_unsafe", env.dev_build):
 | 
			
		||||
    # Renamed to `content-timestamp` in SCons >= 4.2, keeping MD5 for compat.
 | 
			
		||||
    env.Decider("MD5-timestamp")
 | 
			
		||||
    env.SetOption("implicit_cache", 1)
 | 
			
		||||
    env.SetOption("max_drift", 60)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -526,10 +514,6 @@ if not env["deprecated"]:
 | 
			
		|||
if env["precision"] == "double":
 | 
			
		||||
    env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])
 | 
			
		||||
 | 
			
		||||
tmppath = "./platform/" + env["platform"]
 | 
			
		||||
sys.path.insert(0, tmppath)
 | 
			
		||||
import detect
 | 
			
		||||
 | 
			
		||||
# Default num_jobs to local cpu count if not user specified.
 | 
			
		||||
# SCons has a peculiarity where user-specified options won't be overridden
 | 
			
		||||
# by SetOption, so we can rely on this to know if we should use our default.
 | 
			
		||||
| 
						 | 
				
			
			@ -537,13 +521,19 @@ initial_num_jobs = env.GetOption("num_jobs")
 | 
			
		|||
altered_num_jobs = initial_num_jobs + 1
 | 
			
		||||
env.SetOption("num_jobs", altered_num_jobs)
 | 
			
		||||
if env.GetOption("num_jobs") == altered_num_jobs:
 | 
			
		||||
    num_jobs = env.get("num_jobs", "")
 | 
			
		||||
    if str(num_jobs).isdigit() and int(num_jobs) > 0:
 | 
			
		||||
        env.SetOption("num_jobs", num_jobs)
 | 
			
		||||
    else:
 | 
			
		||||
        cpu_count = os.cpu_count()
 | 
			
		||||
        if cpu_count is None:
 | 
			
		||||
        print_warning("Couldn't auto-detect CPU count to configure build parallelism. Specify it with the -j argument.")
 | 
			
		||||
            print_warning(
 | 
			
		||||
                "Couldn't auto-detect CPU count to configure build parallelism. Specify it with the `-j` or `num_jobs` arguments."
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            safer_cpu_count = cpu_count if cpu_count <= 4 else cpu_count - 1
 | 
			
		||||
            print(
 | 
			
		||||
            "Auto-detected %d CPU cores available for build parallelism. Using %d cores by default. You can override it with the -j argument."
 | 
			
		||||
                "Auto-detected %d CPU cores available for build parallelism. Using %d cores by default. You can override it with the `-j` or `num_jobs` arguments."
 | 
			
		||||
                % (cpu_count, safer_cpu_count)
 | 
			
		||||
            )
 | 
			
		||||
            env.SetOption("num_jobs", safer_cpu_count)
 | 
			
		||||
| 
						 | 
				
			
			@ -566,7 +556,7 @@ env.Append(RCFLAGS=env.get("rcflags", "").split())
 | 
			
		|||
# Feature build profile
 | 
			
		||||
env.disabled_classes = []
 | 
			
		||||
if env["build_profile"] != "":
 | 
			
		||||
    print('Using feature build profile: "{}"'.format(env["build_profile"]))
 | 
			
		||||
    print(f'Using feature build profile: "{env["build_profile"]}"')
 | 
			
		||||
    import json
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
| 
						 | 
				
			
			@ -578,7 +568,7 @@ if env["build_profile"] != "":
 | 
			
		|||
            for c in dbo:
 | 
			
		||||
                env[c] = dbo[c]
 | 
			
		||||
    except json.JSONDecodeError:
 | 
			
		||||
        print_error('Failed to open feature build profile: "{}"'.format(env["build_profile"]))
 | 
			
		||||
        print_error(f'Failed to open feature build profile: "{env["build_profile"]}"')
 | 
			
		||||
        Exit(255)
 | 
			
		||||
 | 
			
		||||
# 'dev_mode' and 'production' are aliases to set default options if they haven't been
 | 
			
		||||
| 
						 | 
				
			
			@ -588,12 +578,18 @@ if env["dev_mode"]:
 | 
			
		|||
    env["warnings"] = ARGUMENTS.get("warnings", "extra")
 | 
			
		||||
    env["werror"] = methods.get_cmdline_bool("werror", True)
 | 
			
		||||
    env["tests"] = methods.get_cmdline_bool("tests", True)
 | 
			
		||||
    env["strict_checks"] = methods.get_cmdline_bool("strict_checks", True)
 | 
			
		||||
if env["production"]:
 | 
			
		||||
    env["use_static_cpp"] = methods.get_cmdline_bool("use_static_cpp", True)
 | 
			
		||||
    env["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", False)
 | 
			
		||||
    if env["platform"] == "android":
 | 
			
		||||
        env["swappy"] = methods.get_cmdline_bool("swappy", True)
 | 
			
		||||
    # LTO "auto" means we handle the preferred option in each platform detect.py.
 | 
			
		||||
    env["lto"] = ARGUMENTS.get("lto", "auto")
 | 
			
		||||
 | 
			
		||||
if env["strict_checks"]:
 | 
			
		||||
    env.Append(CPPDEFINES=["STRICT_CHECKS"])
 | 
			
		||||
 | 
			
		||||
# Run SCU file generation script if in a SCU build.
 | 
			
		||||
if env["scu_build"]:
 | 
			
		||||
    max_includes_per_scu = 8
 | 
			
		||||
| 
						 | 
				
			
			@ -613,28 +609,21 @@ detect.configure(env)
 | 
			
		|||
 | 
			
		||||
print(f'Building for platform "{env["platform"]}", architecture "{env["arch"]}", target "{env["target"]}".')
 | 
			
		||||
if env.dev_build:
 | 
			
		||||
    print("NOTE: Developer build, with debug optimization level and debug symbols (unless overridden).")
 | 
			
		||||
    print_info("Developer build, with debug optimization level and debug symbols (unless overridden).")
 | 
			
		||||
 | 
			
		||||
# Enforce our minimal compiler version requirements
 | 
			
		||||
cc_version = methods.get_compiler_version(env) or {
 | 
			
		||||
    "major": None,
 | 
			
		||||
    "minor": None,
 | 
			
		||||
    "patch": None,
 | 
			
		||||
    "metadata1": None,
 | 
			
		||||
    "metadata2": None,
 | 
			
		||||
    "date": None,
 | 
			
		||||
}
 | 
			
		||||
cc_version_major = int(cc_version["major"] or -1)
 | 
			
		||||
cc_version_minor = int(cc_version["minor"] or -1)
 | 
			
		||||
cc_version_metadata1 = cc_version["metadata1"] or ""
 | 
			
		||||
cc_version = methods.get_compiler_version(env)
 | 
			
		||||
cc_version_major = cc_version["major"]
 | 
			
		||||
cc_version_minor = cc_version["minor"]
 | 
			
		||||
cc_version_metadata1 = cc_version["metadata1"]
 | 
			
		||||
 | 
			
		||||
if methods.using_gcc(env):
 | 
			
		||||
if cc_version_major == -1:
 | 
			
		||||
    print_warning(
 | 
			
		||||
        "Couldn't detect compiler version, skipping version checks. "
 | 
			
		||||
        "Build may fail if the compiler doesn't support C++17 fully."
 | 
			
		||||
    )
 | 
			
		||||
    elif cc_version_major < 9:
 | 
			
		||||
elif methods.using_gcc(env):
 | 
			
		||||
    if cc_version_major < 9:
 | 
			
		||||
        print_error(
 | 
			
		||||
            "Detected GCC version older than 9, which does not fully support "
 | 
			
		||||
            "C++17, or has bugs when compiling Godot. Supported versions are 9 "
 | 
			
		||||
| 
						 | 
				
			
			@ -650,46 +639,69 @@ if methods.using_gcc(env):
 | 
			
		|||
            "to switch to posix threads."
 | 
			
		||||
        )
 | 
			
		||||
        Exit(255)
 | 
			
		||||
    if env["debug_paths_relative"] and cc_version_major < 8:
 | 
			
		||||
        print_warning("GCC < 8 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
 | 
			
		||||
        env["debug_paths_relative"] = False
 | 
			
		||||
elif methods.using_clang(env):
 | 
			
		||||
    if cc_version_major == -1:
 | 
			
		||||
        print_warning(
 | 
			
		||||
            "Couldn't detect compiler version, skipping version checks. "
 | 
			
		||||
            "Build may fail if the compiler doesn't support C++17 fully."
 | 
			
		||||
        )
 | 
			
		||||
    # Apple LLVM versions differ from upstream LLVM version \o/, compare
 | 
			
		||||
    # in https://en.wikipedia.org/wiki/Xcode#Toolchain_versions
 | 
			
		||||
    elif env["platform"] == "macos" or env["platform"] == "ios":
 | 
			
		||||
        vanilla = methods.is_vanilla_clang(env)
 | 
			
		||||
        if vanilla and cc_version_major < 6:
 | 
			
		||||
            print_warning(
 | 
			
		||||
                "Detected Clang version older than 6, which does not fully support "
 | 
			
		||||
                "C++17. Supported versions are Clang 6 and later."
 | 
			
		||||
            )
 | 
			
		||||
            Exit(255)
 | 
			
		||||
        elif not vanilla and cc_version_major < 10:
 | 
			
		||||
    if methods.is_apple_clang(env):
 | 
			
		||||
        if cc_version_major < 10:
 | 
			
		||||
            print_error(
 | 
			
		||||
                "Detected Apple Clang version older than 10, which does not fully "
 | 
			
		||||
                "support C++17. Supported versions are Apple Clang 10 and later."
 | 
			
		||||
            )
 | 
			
		||||
            Exit(255)
 | 
			
		||||
        if env["debug_paths_relative"] and not vanilla and cc_version_major < 12:
 | 
			
		||||
        elif env["debug_paths_relative"] and cc_version_major < 12:
 | 
			
		||||
            print_warning(
 | 
			
		||||
                "Apple Clang < 12 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option."
 | 
			
		||||
            )
 | 
			
		||||
            env["debug_paths_relative"] = False
 | 
			
		||||
    elif cc_version_major < 6:
 | 
			
		||||
    else:
 | 
			
		||||
        if cc_version_major < 6:
 | 
			
		||||
            print_error(
 | 
			
		||||
                "Detected Clang version older than 6, which does not fully support "
 | 
			
		||||
                "C++17. Supported versions are Clang 6 and later."
 | 
			
		||||
            )
 | 
			
		||||
            Exit(255)
 | 
			
		||||
    if env["debug_paths_relative"] and cc_version_major < 10:
 | 
			
		||||
        elif env["debug_paths_relative"] and cc_version_major < 10:
 | 
			
		||||
            print_warning("Clang < 10 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
 | 
			
		||||
            env["debug_paths_relative"] = False
 | 
			
		||||
 | 
			
		||||
elif env.msvc:
 | 
			
		||||
    # Ensure latest minor builds of Visual Studio 2017/2019.
 | 
			
		||||
    # https://github.com/godotengine/godot/pull/94995#issuecomment-2336464574
 | 
			
		||||
    if cc_version_major == 16 and cc_version_minor < 11:
 | 
			
		||||
        print_error(
 | 
			
		||||
            "Detected Visual Studio 2019 version older than 16.11, which has bugs "
 | 
			
		||||
            "when compiling Godot. Use a newer VS2019 version, or VS2022."
 | 
			
		||||
        )
 | 
			
		||||
        Exit(255)
 | 
			
		||||
    if cc_version_major == 15 and cc_version_minor < 9:
 | 
			
		||||
        print_error(
 | 
			
		||||
            "Detected Visual Studio 2017 version older than 15.9, which has bugs "
 | 
			
		||||
            "when compiling Godot. Use a newer VS2017 version, or VS2019/VS2022."
 | 
			
		||||
        )
 | 
			
		||||
        Exit(255)
 | 
			
		||||
    if cc_version_major < 15:
 | 
			
		||||
        print_error(
 | 
			
		||||
            "Detected Visual Studio 2015 or earlier, which is unsupported in Godot. "
 | 
			
		||||
            "Supported versions are Visual Studio 2017 and later."
 | 
			
		||||
        )
 | 
			
		||||
        Exit(255)
 | 
			
		||||
 | 
			
		||||
# Default architecture flags.
 | 
			
		||||
if env["arch"] == "x86_32":
 | 
			
		||||
    if env.msvc:
 | 
			
		||||
        env.Append(CCFLAGS=["/arch:SSE2"])
 | 
			
		||||
    else:
 | 
			
		||||
        env.Append(CCFLAGS=["-msse2"])
 | 
			
		||||
 | 
			
		||||
# Explicitly specify colored output.
 | 
			
		||||
if methods.using_gcc(env):
 | 
			
		||||
    env.AppendUnique(CCFLAGS=["-fdiagnostics-color" if STDERR_COLOR else "-fno-diagnostics-color"])
 | 
			
		||||
elif methods.using_clang(env) or methods.using_emcc(env):
 | 
			
		||||
    env.AppendUnique(CCFLAGS=["-fcolor-diagnostics" if STDERR_COLOR else "-fno-color-diagnostics"])
 | 
			
		||||
    if sys.platform == "win32":
 | 
			
		||||
        env.AppendUnique(CCFLAGS=["-fansi-escape-codes"])
 | 
			
		||||
 | 
			
		||||
# Set optimize and debug_symbols flags.
 | 
			
		||||
# "custom" means do nothing and let users set their own optimization flags.
 | 
			
		||||
# Needs to happen after configure to have `env.msvc` defined.
 | 
			
		||||
| 
						 | 
				
			
			@ -712,6 +724,12 @@ if env.msvc:
 | 
			
		|||
        env.Append(CCFLAGS=["/Od"])
 | 
			
		||||
else:
 | 
			
		||||
    if env["debug_symbols"]:
 | 
			
		||||
        if env["platform"] == "windows":
 | 
			
		||||
            if methods.using_clang(env):
 | 
			
		||||
                env.Append(CCFLAGS=["-gdwarf-4"])  # clang dwarf-5 symbols are broken on Windows.
 | 
			
		||||
            else:
 | 
			
		||||
                env.Append(CCFLAGS=["-gdwarf-5"])  # For gcc, only dwarf-5 symbols seem usable by libbacktrace.
 | 
			
		||||
        else:
 | 
			
		||||
            # Adding dwarf-4 explicitly makes stacktraces work with clang builds,
 | 
			
		||||
            # otherwise addr2line doesn't understand them
 | 
			
		||||
            env.Append(CCFLAGS=["-gdwarf-4"])
 | 
			
		||||
| 
						 | 
				
			
			@ -730,7 +748,7 @@ else:
 | 
			
		|||
            project_path = Dir("#").abspath
 | 
			
		||||
            env.Append(CCFLAGS=[f"-ffile-prefix-map={project_path}=."])
 | 
			
		||||
    else:
 | 
			
		||||
        if methods.using_clang(env) and not methods.is_vanilla_clang(env):
 | 
			
		||||
        if methods.is_apple_clang(env):
 | 
			
		||||
            # Apple Clang, its linker doesn't like -s.
 | 
			
		||||
            env.Append(LINKFLAGS=["-Wl,-S", "-Wl,-x", "-Wl,-dead_strip"])
 | 
			
		||||
        else:
 | 
			
		||||
| 
						 | 
				
			
			@ -766,13 +784,23 @@ if env["lto"] != "none":
 | 
			
		|||
# This needs to come after `configure`, otherwise we don't have env.msvc.
 | 
			
		||||
if not env.msvc:
 | 
			
		||||
    # Specifying GNU extensions support explicitly, which are supported by
 | 
			
		||||
    # both GCC and Clang. Both currently default to gnu11 and gnu++14.
 | 
			
		||||
    env.Prepend(CFLAGS=["-std=gnu11"])
 | 
			
		||||
    # both GCC and Clang. Both currently default to gnu17 and gnu++17.
 | 
			
		||||
    env.Prepend(CFLAGS=["-std=gnu17"])
 | 
			
		||||
    env.Prepend(CXXFLAGS=["-std=gnu++17"])
 | 
			
		||||
else:
 | 
			
		||||
    # MSVC doesn't have clear C standard support, /std only covers C++.
 | 
			
		||||
    # We apply it to CCFLAGS (both C and C++ code) in case it impacts C features.
 | 
			
		||||
    env.Prepend(CCFLAGS=["/std:c++17"])
 | 
			
		||||
    # MSVC started offering C standard support with Visual Studio 2019 16.8, which covers all
 | 
			
		||||
    # of our supported VS2019 & VS2022 versions; VS2017 will only pass the C++ standard.
 | 
			
		||||
    env.Prepend(CXXFLAGS=["/std:c++17"])
 | 
			
		||||
    if cc_version_major < 16:
 | 
			
		||||
        print_warning("Visual Studio 2017 cannot specify a C-Standard.")
 | 
			
		||||
    else:
 | 
			
		||||
        env.Prepend(CFLAGS=["/std:c17"])
 | 
			
		||||
    # MSVC is non-conforming with the C++ standard by default, so we enable more conformance.
 | 
			
		||||
    # Note that this is still not complete conformance, as certain Windows-related headers
 | 
			
		||||
    # don't compile under complete conformance.
 | 
			
		||||
    env.Prepend(CCFLAGS=["/permissive-"])
 | 
			
		||||
    # Allow use of `__cplusplus` macro to determine C++ standard universally.
 | 
			
		||||
    env.Prepend(CXXFLAGS=["/Zc:__cplusplus"])
 | 
			
		||||
 | 
			
		||||
# Disable exception handling. Godot doesn't use exceptions anywhere, and this
 | 
			
		||||
# saves around 20% of binary size and very significant build time (GH-80513).
 | 
			
		||||
| 
						 | 
				
			
			@ -785,21 +813,9 @@ elif env.msvc:
 | 
			
		|||
    env.Append(CXXFLAGS=["/EHsc"])
 | 
			
		||||
 | 
			
		||||
# Configure compiler warnings
 | 
			
		||||
if env.msvc:  # MSVC
 | 
			
		||||
    if env["warnings"] == "no":
 | 
			
		||||
        env.Append(CCFLAGS=["/w"])
 | 
			
		||||
    else:
 | 
			
		||||
        if env["warnings"] == "extra":
 | 
			
		||||
            env.Append(CCFLAGS=["/W4"])
 | 
			
		||||
        elif env["warnings"] == "all":
 | 
			
		||||
            # C4458 is like -Wshadow. Part of /W4 but let's apply it for the default /W3 too.
 | 
			
		||||
            env.Append(CCFLAGS=["/W3", "/w34458"])
 | 
			
		||||
        elif env["warnings"] == "moderate":
 | 
			
		||||
            env.Append(CCFLAGS=["/W2"])
 | 
			
		||||
if env.msvc and not methods.using_clang(env):  # MSVC
 | 
			
		||||
    # Disable warnings which we don't plan to fix.
 | 
			
		||||
 | 
			
		||||
        env.Append(
 | 
			
		||||
            CCFLAGS=[
 | 
			
		||||
    disabled_warnings = [
 | 
			
		||||
        "/wd4100",  # C4100 (unreferenced formal parameter): Doesn't play nice with polymorphism.
 | 
			
		||||
        "/wd4127",  # C4127 (conditional expression is constant)
 | 
			
		||||
        "/wd4201",  # C4201 (non-standard nameless struct/union): Only relevant for C89.
 | 
			
		||||
| 
						 | 
				
			
			@ -807,22 +823,32 @@ if env.msvc:  # MSVC
 | 
			
		|||
        "/wd4245",
 | 
			
		||||
        "/wd4267",
 | 
			
		||||
        "/wd4305",  # C4305 (truncation): double to float or real_t, too hard to avoid.
 | 
			
		||||
        "/wd4324",  # C4820 (structure was padded due to alignment specifier)
 | 
			
		||||
        "/wd4514",  # C4514 (unreferenced inline function has been removed)
 | 
			
		||||
        "/wd4714",  # C4714 (function marked as __forceinline not inlined)
 | 
			
		||||
        "/wd4820",  # C4820 (padding added after construct)
 | 
			
		||||
    ]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if env["warnings"] == "extra":
 | 
			
		||||
        env.Append(CCFLAGS=["/W4"] + disabled_warnings)
 | 
			
		||||
    elif env["warnings"] == "all":
 | 
			
		||||
        # C4458 is like -Wshadow. Part of /W4 but let's apply it for the default /W3 too.
 | 
			
		||||
        env.Append(CCFLAGS=["/W3", "/w34458"] + disabled_warnings)
 | 
			
		||||
    elif env["warnings"] == "moderate":
 | 
			
		||||
        env.Append(CCFLAGS=["/W2"] + disabled_warnings)
 | 
			
		||||
    else:  # 'no'
 | 
			
		||||
        # C4267 is particularly finicky & needs to be explicitly disabled.
 | 
			
		||||
        env.Append(CCFLAGS=["/w", "/wd4267"])
 | 
			
		||||
 | 
			
		||||
    if env["werror"]:
 | 
			
		||||
        env.Append(CCFLAGS=["/WX"])
 | 
			
		||||
        env.Append(LINKFLAGS=["/WX"])
 | 
			
		||||
 | 
			
		||||
else:  # GCC, Clang
 | 
			
		||||
    common_warnings = []
 | 
			
		||||
 | 
			
		||||
    if methods.using_gcc(env):
 | 
			
		||||
        common_warnings += ["-Wshadow", "-Wno-misleading-indentation"]
 | 
			
		||||
        if cc_version_major == 7:  # Bogus warning fixed in 8+.
 | 
			
		||||
            common_warnings += ["-Wno-strict-overflow"]
 | 
			
		||||
        if cc_version_major < 11:
 | 
			
		||||
            # Regression in GCC 9/10, spams so much in our variadic templates
 | 
			
		||||
            # that we need to outright disable it.
 | 
			
		||||
| 
						 | 
				
			
			@ -835,8 +861,11 @@ else:  # GCC, Clang
 | 
			
		|||
        # for putting them in `Set` or `Map`. We don't mind about unreliable ordering.
 | 
			
		||||
        common_warnings += ["-Wno-ordered-compare-function-pointers"]
 | 
			
		||||
 | 
			
		||||
    # clang-cl will interpret `-Wall` as `-Weverything`, workaround with compatibility cast
 | 
			
		||||
    W_ALL = "-Wall" if not env.msvc else "-W3"
 | 
			
		||||
 | 
			
		||||
    if env["warnings"] == "extra":
 | 
			
		||||
        env.Append(CCFLAGS=["-Wall", "-Wextra", "-Wwrite-strings", "-Wno-unused-parameter"] + common_warnings)
 | 
			
		||||
        env.Append(CCFLAGS=[W_ALL, "-Wextra", "-Wwrite-strings", "-Wno-unused-parameter"] + common_warnings)
 | 
			
		||||
        env.Append(CXXFLAGS=["-Wctor-dtor-privacy", "-Wnon-virtual-dtor"])
 | 
			
		||||
        if methods.using_gcc(env):
 | 
			
		||||
            env.Append(
 | 
			
		||||
| 
						 | 
				
			
			@ -858,9 +887,9 @@ else:  # GCC, Clang
 | 
			
		|||
        elif methods.using_clang(env) or methods.using_emcc(env):
 | 
			
		||||
            env.Append(CCFLAGS=["-Wimplicit-fallthrough"])
 | 
			
		||||
    elif env["warnings"] == "all":
 | 
			
		||||
        env.Append(CCFLAGS=["-Wall"] + common_warnings)
 | 
			
		||||
        env.Append(CCFLAGS=[W_ALL] + common_warnings)
 | 
			
		||||
    elif env["warnings"] == "moderate":
 | 
			
		||||
        env.Append(CCFLAGS=["-Wall", "-Wno-unused"] + common_warnings)
 | 
			
		||||
        env.Append(CCFLAGS=[W_ALL, "-Wno-unused"] + common_warnings)
 | 
			
		||||
    else:  # 'no'
 | 
			
		||||
        env.Append(CCFLAGS=["-w"])
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -895,7 +924,7 @@ env.module_icons_paths = []
 | 
			
		|||
env.doc_class_path = platform_doc_class_path
 | 
			
		||||
 | 
			
		||||
for name, path in modules_detected.items():
 | 
			
		||||
    if not env["module_" + name + "_enabled"]:
 | 
			
		||||
    if not env[f"module_{name}_enabled"]:
 | 
			
		||||
        continue
 | 
			
		||||
    sys.path.insert(0, path)
 | 
			
		||||
    env.current_module = name
 | 
			
		||||
| 
						 | 
				
			
			@ -932,7 +961,7 @@ methods.sort_module_list(env)
 | 
			
		|||
 | 
			
		||||
if env.editor_build:
 | 
			
		||||
    # Add editor-specific dependencies to the dependency graph.
 | 
			
		||||
    env.module_add_dependencies("editor", ["freetype", "svg"])
 | 
			
		||||
    env.module_add_dependencies("editor", ["freetype", "regex", "svg"])
 | 
			
		||||
 | 
			
		||||
    # And check if they are met.
 | 
			
		||||
    if not env.module_check_dependencies("editor"):
 | 
			
		||||
| 
						 | 
				
			
			@ -969,8 +998,7 @@ if env["disable_3d"]:
 | 
			
		|||
if env["disable_advanced_gui"]:
 | 
			
		||||
    if env.editor_build:
 | 
			
		||||
        print_error(
 | 
			
		||||
            "Build option `disable_advanced_gui=yes` cannot be used for editor builds, "
 | 
			
		||||
            "only for export template builds."
 | 
			
		||||
            "Build option `disable_advanced_gui=yes` cannot be used for editor builds, only for export template builds."
 | 
			
		||||
        )
 | 
			
		||||
        Exit(255)
 | 
			
		||||
    else:
 | 
			
		||||
| 
						 | 
				
			
			@ -1002,36 +1030,22 @@ GLSL_BUILDERS = {
 | 
			
		|||
}
 | 
			
		||||
env.Append(BUILDERS=GLSL_BUILDERS)
 | 
			
		||||
 | 
			
		||||
scons_cache_path = os.environ.get("SCONS_CACHE")
 | 
			
		||||
if scons_cache_path is not None:
 | 
			
		||||
    CacheDir(scons_cache_path)
 | 
			
		||||
    print("Scons cache enabled... (path: '" + scons_cache_path + "')")
 | 
			
		||||
 | 
			
		||||
if env["vsproj"]:
 | 
			
		||||
    env.vs_incs = []
 | 
			
		||||
    env.vs_srcs = []
 | 
			
		||||
 | 
			
		||||
if env["compiledb"]:
 | 
			
		||||
    if env.scons_version < (4, 0, 0):
 | 
			
		||||
        # Generating the compilation DB (`compile_commands.json`) requires SCons 4.0.0 or later.
 | 
			
		||||
        print_error("The `compiledb=yes` option requires SCons 4.0 or later, but your version is %s." % scons_raw_version)
 | 
			
		||||
        Exit(255)
 | 
			
		||||
 | 
			
		||||
    env.Tool("compilation_db")
 | 
			
		||||
    env.Alias("compiledb", env.CompilationDatabase())
 | 
			
		||||
    env.NoCache(env.CompilationDatabase())
 | 
			
		||||
    if not env["verbose"]:
 | 
			
		||||
        env["COMPILATIONDB_COMSTR"] = "$GENCOMSTR"
 | 
			
		||||
 | 
			
		||||
if env["ninja"]:
 | 
			
		||||
    if env.scons_version < (4, 2, 0):
 | 
			
		||||
        print_error("The `ninja=yes` option requires SCons 4.2 or later, but your version is %s." % scons_raw_version)
 | 
			
		||||
        print_error(f"The `ninja=yes` option requires SCons 4.2 or later, but your version is {scons_raw_version}.")
 | 
			
		||||
        Exit(255)
 | 
			
		||||
 | 
			
		||||
    SetOption("experimental", "ninja")
 | 
			
		||||
    env.Tool("ninja")
 | 
			
		||||
 | 
			
		||||
    # By setting this we allow the user to run ninja by themselves with all
 | 
			
		||||
    # the flags they need, as apparently automatically running from scons
 | 
			
		||||
    # is way slower.
 | 
			
		||||
    SetOption("disable_execute_ninja", True)
 | 
			
		||||
    env["NINJA_FILE_NAME"] = env["ninja_file"]
 | 
			
		||||
    env["NINJA_DISABLE_AUTO_RUN"] = not env["ninja_auto_run"]
 | 
			
		||||
    env.Tool("ninja", env["ninja_file"])
 | 
			
		||||
 | 
			
		||||
# Threads
 | 
			
		||||
if env["threads"]:
 | 
			
		||||
| 
						 | 
				
			
			@ -1070,35 +1084,9 @@ if "check_c_headers" in env:
 | 
			
		|||
            env.AppendUnique(CPPDEFINES=[headers[header]])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# FIXME: This method mixes both cosmetic progress stuff and cache handling...
 | 
			
		||||
methods.show_progress(env)
 | 
			
		||||
# TODO: replace this with `env.Dump(format="json")`
 | 
			
		||||
# once we start requiring SCons 4.0 as min version.
 | 
			
		||||
methods.dump(env)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def print_elapsed_time():
 | 
			
		||||
    elapsed_time_sec = round(time.time() - time_at_start, 2)
 | 
			
		||||
    time_centiseconds = round((elapsed_time_sec % 1) * 100)
 | 
			
		||||
    print(
 | 
			
		||||
        "{}[Time elapsed: {}.{:02}]{}".format(
 | 
			
		||||
            methods.ANSI.GRAY,
 | 
			
		||||
            time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)),
 | 
			
		||||
            time_centiseconds,
 | 
			
		||||
            methods.ANSI.RESET,
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
atexit.register(print_elapsed_time)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def purge_flaky_files():
 | 
			
		||||
    paths_to_keep = ["ninja.build"]
 | 
			
		||||
    for build_failure in GetBuildFailures():
 | 
			
		||||
        path = build_failure.node.path
 | 
			
		||||
        if os.path.isfile(path) and path not in paths_to_keep:
 | 
			
		||||
            os.remove(path)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
atexit.register(purge_flaky_files)
 | 
			
		||||
methods.prepare_purge(env)
 | 
			
		||||
methods.prepare_timer()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
from misc.utility.scons_hints import *
 | 
			
		||||
 | 
			
		||||
Import("env")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -98,6 +99,8 @@ if env["builtin_zlib"]:
 | 
			
		|||
    env.Prepend(CPPPATH=[thirdparty_zlib_dir])
 | 
			
		||||
    if env.dev_build:
 | 
			
		||||
        env_thirdparty.Append(CPPDEFINES=["ZLIB_DEBUG"])
 | 
			
		||||
        # Affects headers so it should also be defined for Godot code
 | 
			
		||||
        env.Append(CPPDEFINES=["ZLIB_DEBUG"])
 | 
			
		||||
 | 
			
		||||
    env_thirdparty.add_source_files(thirdparty_obj, thirdparty_zlib_sources)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -140,7 +143,7 @@ if env["builtin_zstd"]:
 | 
			
		|||
        "decompress/zstd_decompress_block.c",
 | 
			
		||||
        "decompress/zstd_decompress.c",
 | 
			
		||||
    ]
 | 
			
		||||
    if env["platform"] in ["android", "ios", "linuxbsd", "macos"]:
 | 
			
		||||
    if env["platform"] in ["android", "ios", "linuxbsd", "macos"] and env["arch"] == "x86_64":
 | 
			
		||||
        # Match platforms with ZSTD_ASM_SUPPORTED in common/portability_macros.h
 | 
			
		||||
        thirdparty_zstd_sources.append("decompress/huf_decompress_amd64.S")
 | 
			
		||||
    thirdparty_zstd_sources = [thirdparty_zstd_dir + file for file in thirdparty_zstd_sources]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
from misc.utility.scons_hints import *
 | 
			
		||||
 | 
			
		||||
Import("env")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,7 @@
 | 
			
		|||
#include "core/license.gen.h"
 | 
			
		||||
#include "core/variant/typed_array.h"
 | 
			
		||||
#include "core/version.h"
 | 
			
		||||
#include "servers/rendering/rendering_device.h"
 | 
			
		||||
 | 
			
		||||
void Engine::set_physics_ticks_per_second(int p_ips) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(p_ips <= 0, "Engine iterations per second must be greater than 0.");
 | 
			
		||||
| 
						 | 
				
			
			@ -68,6 +69,11 @@ double Engine::get_physics_jitter_fix() const {
 | 
			
		|||
 | 
			
		||||
void Engine::set_max_fps(int p_fps) {
 | 
			
		||||
	_max_fps = p_fps > 0 ? p_fps : 0;
 | 
			
		||||
 | 
			
		||||
	RenderingDevice *rd = RenderingDevice::get_singleton();
 | 
			
		||||
	if (rd) {
 | 
			
		||||
		rd->_set_max_fps(_max_fps);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Engine::get_max_fps() const {
 | 
			
		||||
| 
						 | 
				
			
			@ -110,6 +116,10 @@ void Engine::set_time_scale(double p_scale) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
double Engine::get_time_scale() const {
 | 
			
		||||
	return freeze_time_scale ? 0 : _time_scale;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double Engine::get_unfrozen_time_scale() const {
 | 
			
		||||
	return _time_scale;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -238,6 +248,9 @@ String Engine::get_architecture_name() const {
 | 
			
		|||
	return "ppc";
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#elif defined(__loongarch64)
 | 
			
		||||
	return "loongarch64";
 | 
			
		||||
 | 
			
		||||
#elif defined(__wasm__)
 | 
			
		||||
#if defined(__wasm64__)
 | 
			
		||||
	return "wasm64";
 | 
			
		||||
| 
						 | 
				
			
			@ -263,6 +276,24 @@ bool Engine::is_generate_spirv_debug_info_enabled() const {
 | 
			
		|||
	return generate_spirv_debug_info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Engine::is_extra_gpu_memory_tracking_enabled() const {
 | 
			
		||||
	return extra_gpu_memory_tracking;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
 | 
			
		||||
bool Engine::is_accurate_breadcrumbs_enabled() const {
 | 
			
		||||
	return accurate_breadcrumbs;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void Engine::set_print_to_stdout(bool p_enabled) {
 | 
			
		||||
	CoreGlobals::print_line_enabled = p_enabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Engine::is_printing_to_stdout() const {
 | 
			
		||||
	return CoreGlobals::print_line_enabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Engine::set_print_error_messages(bool p_enabled) {
 | 
			
		||||
	CoreGlobals::print_error_enabled = p_enabled;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -380,6 +411,18 @@ bool Engine::notify_frame_server_synced() {
 | 
			
		|||
	return server_syncs > SERVER_SYNC_FRAME_COUNT_WARNING;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Engine::set_freeze_time_scale(bool p_frozen) {
 | 
			
		||||
	freeze_time_scale = p_frozen;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Engine::set_embedded_in_editor(bool p_enabled) {
 | 
			
		||||
	embedded_in_editor = p_enabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Engine::is_embedded_in_editor() const {
 | 
			
		||||
	return embedded_in_editor;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Engine::Engine() {
 | 
			
		||||
	singleton = this;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,6 @@
 | 
			
		|||
#include "core/os/main_loop.h"
 | 
			
		||||
#include "core/string/ustring.h"
 | 
			
		||||
#include "core/templates/list.h"
 | 
			
		||||
#include "core/templates/vector.h"
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
class TypedArray;
 | 
			
		||||
| 
						 | 
				
			
			@ -72,6 +71,10 @@ private:
 | 
			
		|||
	bool abort_on_gpu_errors = false;
 | 
			
		||||
	bool use_validation_layers = false;
 | 
			
		||||
	bool generate_spirv_debug_info = false;
 | 
			
		||||
	bool extra_gpu_memory_tracking = false;
 | 
			
		||||
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
 | 
			
		||||
	bool accurate_breadcrumbs = false;
 | 
			
		||||
#endif
 | 
			
		||||
	int32_t gpu_idx = -1;
 | 
			
		||||
 | 
			
		||||
	uint64_t _process_frames = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -83,6 +86,8 @@ private:
 | 
			
		|||
	bool editor_hint = false;
 | 
			
		||||
	bool project_manager_hint = false;
 | 
			
		||||
	bool extension_reloading = false;
 | 
			
		||||
	bool embedded_in_editor = false;
 | 
			
		||||
	bool recovery_mode_hint = false;
 | 
			
		||||
 | 
			
		||||
	bool _print_header = true;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -95,6 +100,8 @@ private:
 | 
			
		|||
	int server_syncs = 0;
 | 
			
		||||
	bool frame_server_synced = false;
 | 
			
		||||
 | 
			
		||||
	bool freeze_time_scale = false;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static Engine *get_singleton();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -126,6 +133,10 @@ public:
 | 
			
		|||
 | 
			
		||||
	void set_time_scale(double p_scale);
 | 
			
		||||
	double get_time_scale() const;
 | 
			
		||||
	double get_unfrozen_time_scale() const;
 | 
			
		||||
 | 
			
		||||
	void set_print_to_stdout(bool p_enabled);
 | 
			
		||||
	bool is_printing_to_stdout() const;
 | 
			
		||||
 | 
			
		||||
	void set_print_error_messages(bool p_enabled);
 | 
			
		||||
	bool is_printing_error_messages() const;
 | 
			
		||||
| 
						 | 
				
			
			@ -152,6 +163,9 @@ public:
 | 
			
		|||
 | 
			
		||||
	_FORCE_INLINE_ void set_extension_reloading_enabled(bool p_enabled) { extension_reloading = p_enabled; }
 | 
			
		||||
	_FORCE_INLINE_ bool is_extension_reloading_enabled() const { return extension_reloading; }
 | 
			
		||||
 | 
			
		||||
	_FORCE_INLINE_ void set_recovery_mode_hint(bool p_enabled) { recovery_mode_hint = p_enabled; }
 | 
			
		||||
	_FORCE_INLINE_ bool is_recovery_mode_hint() const { return recovery_mode_hint; }
 | 
			
		||||
#else
 | 
			
		||||
	_FORCE_INLINE_ void set_editor_hint(bool p_enabled) {}
 | 
			
		||||
	_FORCE_INLINE_ bool is_editor_hint() const { return false; }
 | 
			
		||||
| 
						 | 
				
			
			@ -161,6 +175,9 @@ public:
 | 
			
		|||
 | 
			
		||||
	_FORCE_INLINE_ void set_extension_reloading_enabled(bool p_enabled) {}
 | 
			
		||||
	_FORCE_INLINE_ bool is_extension_reloading_enabled() const { return false; }
 | 
			
		||||
 | 
			
		||||
	_FORCE_INLINE_ void set_recovery_mode_hint(bool p_enabled) {}
 | 
			
		||||
	_FORCE_INLINE_ bool is_recovery_mode_hint() const { return false; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	Dictionary get_version_info() const;
 | 
			
		||||
| 
						 | 
				
			
			@ -181,11 +198,19 @@ public:
 | 
			
		|||
	bool is_abort_on_gpu_errors_enabled() const;
 | 
			
		||||
	bool is_validation_layers_enabled() const;
 | 
			
		||||
	bool is_generate_spirv_debug_info_enabled() const;
 | 
			
		||||
	bool is_extra_gpu_memory_tracking_enabled() const;
 | 
			
		||||
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
 | 
			
		||||
	bool is_accurate_breadcrumbs_enabled() const;
 | 
			
		||||
#endif
 | 
			
		||||
	int32_t get_gpu_index() const;
 | 
			
		||||
 | 
			
		||||
	void increment_frames_drawn();
 | 
			
		||||
	bool notify_frame_server_synced();
 | 
			
		||||
 | 
			
		||||
	void set_freeze_time_scale(bool p_frozen);
 | 
			
		||||
	void set_embedded_in_editor(bool p_enabled);
 | 
			
		||||
	bool is_embedded_in_editor() const;
 | 
			
		||||
 | 
			
		||||
	Engine();
 | 
			
		||||
	virtual ~Engine();
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,7 +39,6 @@
 | 
			
		|||
#include "core/io/marshalls.h"
 | 
			
		||||
#include "core/io/resource_uid.h"
 | 
			
		||||
#include "core/object/script_language.h"
 | 
			
		||||
#include "core/os/keyboard.h"
 | 
			
		||||
#include "core/templates/rb_set.h"
 | 
			
		||||
#include "core/variant/typed_array.h"
 | 
			
		||||
#include "core/variant/variant_parser.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -194,7 +193,7 @@ String ProjectSettings::localize_path(const String &p_path) const {
 | 
			
		|||
 | 
			
		||||
		return cwd.replace_first(res_path, "res://");
 | 
			
		||||
	} else {
 | 
			
		||||
		int sep = path.rfind("/");
 | 
			
		||||
		int sep = path.rfind_char('/');
 | 
			
		||||
		if (sep == -1) {
 | 
			
		||||
			return "res://" + path;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -214,36 +213,36 @@ String ProjectSettings::localize_path(const String &p_path) const {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void ProjectSettings::set_initial_value(const String &p_name, const Variant &p_value) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + ".");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name));
 | 
			
		||||
 | 
			
		||||
	// Duplicate so that if value is array or dictionary, changing the setting will not change the stored initial value.
 | 
			
		||||
	props[p_name].initial = p_value.duplicate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProjectSettings::set_restart_if_changed(const String &p_name, bool p_restart) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + ".");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name));
 | 
			
		||||
	props[p_name].restart_if_changed = p_restart;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProjectSettings::set_as_basic(const String &p_name, bool p_basic) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + ".");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name));
 | 
			
		||||
	props[p_name].basic = p_basic;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProjectSettings::set_as_internal(const String &p_name, bool p_internal) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + ".");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name));
 | 
			
		||||
	props[p_name].internal = p_internal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProjectSettings::set_ignore_value_in_docs(const String &p_name, bool p_ignore) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + ".");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name));
 | 
			
		||||
#ifdef DEBUG_METHODS_ENABLED
 | 
			
		||||
	props[p_name].ignore_value_in_docs = p_ignore;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ProjectSettings::get_ignore_value_in_docs(const String &p_name) const {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!props.has(p_name), false, "Request for nonexistent project setting: " + p_name + ".");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!props.has(p_name), false, vformat("Request for nonexistent project setting: '%s'.", p_name));
 | 
			
		||||
#ifdef DEBUG_METHODS_ENABLED
 | 
			
		||||
	return props[p_name].ignore_value_in_docs;
 | 
			
		||||
#else
 | 
			
		||||
| 
						 | 
				
			
			@ -262,6 +261,12 @@ String ProjectSettings::globalize_path(const String &p_path) const {
 | 
			
		|||
			return p_path.replace("res:/", resource_path);
 | 
			
		||||
		}
 | 
			
		||||
		return p_path.replace("res://", "");
 | 
			
		||||
	} else if (p_path.begins_with("uid://")) {
 | 
			
		||||
		const String path = ResourceUID::uid_to_path(p_path);
 | 
			
		||||
		if (!resource_path.is_empty()) {
 | 
			
		||||
			return path.replace("res:/", resource_path);
 | 
			
		||||
		}
 | 
			
		||||
		return path.replace("res://", "");
 | 
			
		||||
	} else if (p_path.begins_with("user://")) {
 | 
			
		||||
		String data_dir = OS::get_singleton()->get_user_data_dir();
 | 
			
		||||
		if (!data_dir.is_empty()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -300,7 +305,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		{ // Feature overrides.
 | 
			
		||||
			int dot = p_name.operator String().find(".");
 | 
			
		||||
			int dot = p_name.operator String().find_char('.');
 | 
			
		||||
			if (dot != -1) {
 | 
			
		||||
				Vector<String> s = p_name.operator String().split(".");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -348,7 +353,6 @@ bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const {
 | 
			
		|||
	_THREAD_SAFE_METHOD_
 | 
			
		||||
 | 
			
		||||
	if (!props.has(p_name)) {
 | 
			
		||||
		WARN_PRINT("Property not found: " + String(p_name));
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	r_ret = props[p_name].variant;
 | 
			
		||||
| 
						 | 
				
			
			@ -372,7 +376,7 @@ Variant ProjectSettings::get_setting_with_override(const StringName &p_name) con
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if (!props.has(name)) {
 | 
			
		||||
		WARN_PRINT("Property not found: " + String(name));
 | 
			
		||||
		WARN_PRINT(vformat("Property not found: '%s'.", String(name)));
 | 
			
		||||
		return Variant();
 | 
			
		||||
	}
 | 
			
		||||
	return props[name].variant;
 | 
			
		||||
| 
						 | 
				
			
			@ -436,7 +440,7 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const {
 | 
			
		|||
 | 
			
		||||
	for (const _VCSort &E : vclist) {
 | 
			
		||||
		String prop_info_name = E.name;
 | 
			
		||||
		int dot = prop_info_name.find(".");
 | 
			
		||||
		int dot = prop_info_name.find_char('.');
 | 
			
		||||
		if (dot != -1 && !custom_prop_info.has(prop_info_name)) {
 | 
			
		||||
			prop_info_name = prop_info_name.substr(0, dot);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -468,13 +472,30 @@ void ProjectSettings::_emit_changed() {
 | 
			
		|||
	emit_signal("settings_changed");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset) {
 | 
			
		||||
bool ProjectSettings::load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset) {
 | 
			
		||||
	return ProjectSettings::_load_resource_pack(p_pack, p_replace_files, p_offset, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset, bool p_main_pack) {
 | 
			
		||||
	if (PackedData::get_singleton()->is_disabled()) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool ok = PackedData::get_singleton()->add_pack(p_pack, p_replace_files, p_offset) == OK;
 | 
			
		||||
	if (p_pack == "res://") {
 | 
			
		||||
		// Loading the resource directory as a pack source is reserved for internal use only.
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!p_main_pack && !using_datapack && !OS::get_singleton()->get_resource_dir().is_empty()) {
 | 
			
		||||
		// Add the project's resource file system to PackedData so directory access keeps working when
 | 
			
		||||
		// the game is running without a main pack, like in the editor or on Android.
 | 
			
		||||
		PackedData::get_singleton()->add_pack_source(memnew(PackedSourceDirectory));
 | 
			
		||||
		PackedData::get_singleton()->add_pack("res://", false, 0);
 | 
			
		||||
		DirAccess::make_default<DirAccessPack>(DirAccess::ACCESS_RESOURCES);
 | 
			
		||||
		using_datapack = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool ok = PackedData::get_singleton()->add_pack(p_pack, p_replace_files, p_offset) == OK;
 | 
			
		||||
	if (!ok) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -487,14 +508,17 @@ bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_f
 | 
			
		|||
		ResourceUID::get_singleton()->load_from_cache(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//if data.pck is found, all directory access will be from here
 | 
			
		||||
	// If the data pack was found, all directory access will be from here.
 | 
			
		||||
	if (!using_datapack) {
 | 
			
		||||
		DirAccess::make_default<DirAccessPack>(DirAccess::ACCESS_RESOURCES);
 | 
			
		||||
		using_datapack = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProjectSettings::_convert_to_last_version(int p_from_version) {
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
	if (p_from_version <= 3) {
 | 
			
		||||
		// Converts the actions from array to dictionary (array of events to dictionary with deadzone + events)
 | 
			
		||||
		for (KeyValue<StringName, ProjectSettings::VariantContainer> &E : props) {
 | 
			
		||||
| 
						 | 
				
			
			@ -508,6 +532,7 @@ void ProjectSettings::_convert_to_last_version(int p_from_version) {
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -548,8 +573,8 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
 | 
			
		|||
	// Attempt with a user-defined main pack first
 | 
			
		||||
 | 
			
		||||
	if (!p_main_pack.is_empty()) {
 | 
			
		||||
		bool ok = _load_resource_pack(p_main_pack);
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(!ok, ERR_CANT_OPEN, "Cannot open resource pack '" + p_main_pack + "'.");
 | 
			
		||||
		bool ok = _load_resource_pack(p_main_pack, false, 0, true);
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(!ok, ERR_CANT_OPEN, vformat("Cannot open resource pack '%s'.", p_main_pack));
 | 
			
		||||
 | 
			
		||||
		Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary");
 | 
			
		||||
		if (err == OK && !p_ignore_override) {
 | 
			
		||||
| 
						 | 
				
			
			@ -567,7 +592,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
 | 
			
		|||
		// and if so, we attempt loading it at the end.
 | 
			
		||||
 | 
			
		||||
		// Attempt with PCK bundled into executable.
 | 
			
		||||
		bool found = _load_resource_pack(exec_path);
 | 
			
		||||
		bool found = _load_resource_pack(exec_path, false, 0, true);
 | 
			
		||||
 | 
			
		||||
		// Attempt with exec_name.pck.
 | 
			
		||||
		// (This is the usual case when distributing a Godot game.)
 | 
			
		||||
| 
						 | 
				
			
			@ -583,20 +608,20 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
 | 
			
		|||
#ifdef MACOS_ENABLED
 | 
			
		||||
		if (!found) {
 | 
			
		||||
			// Attempt to load PCK from macOS .app bundle resources.
 | 
			
		||||
			found = _load_resource_pack(OS::get_singleton()->get_bundle_resource_dir().path_join(exec_basename + ".pck")) || _load_resource_pack(OS::get_singleton()->get_bundle_resource_dir().path_join(exec_filename + ".pck"));
 | 
			
		||||
			found = _load_resource_pack(OS::get_singleton()->get_bundle_resource_dir().path_join(exec_basename + ".pck"), false, 0, true) || _load_resource_pack(OS::get_singleton()->get_bundle_resource_dir().path_join(exec_filename + ".pck"), false, 0, true);
 | 
			
		||||
		}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		if (!found) {
 | 
			
		||||
			// Try to load data pack at the location of the executable.
 | 
			
		||||
			// As mentioned above, we have two potential names to attempt.
 | 
			
		||||
			found = _load_resource_pack(exec_dir.path_join(exec_basename + ".pck")) || _load_resource_pack(exec_dir.path_join(exec_filename + ".pck"));
 | 
			
		||||
			found = _load_resource_pack(exec_dir.path_join(exec_basename + ".pck"), false, 0, true) || _load_resource_pack(exec_dir.path_join(exec_filename + ".pck"), false, 0, true);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!found) {
 | 
			
		||||
			// If we couldn't find them next to the executable, we attempt
 | 
			
		||||
			// the current working directory. Same story, two tests.
 | 
			
		||||
			found = _load_resource_pack(exec_basename + ".pck") || _load_resource_pack(exec_filename + ".pck");
 | 
			
		||||
			found = _load_resource_pack(exec_basename + ".pck", false, 0, true) || _load_resource_pack(exec_filename + ".pck", false, 0, true);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If we opened our package, try and load our project.
 | 
			
		||||
| 
						 | 
				
			
			@ -624,11 +649,33 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
 | 
			
		|||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef MACOS_ENABLED
 | 
			
		||||
	// Attempt to load project file from macOS .app bundle resources.
 | 
			
		||||
	resource_path = OS::get_singleton()->get_bundle_resource_dir();
 | 
			
		||||
	if (!resource_path.is_empty()) {
 | 
			
		||||
		if (resource_path[resource_path.length() - 1] == '/') {
 | 
			
		||||
			resource_path = resource_path.substr(0, resource_path.length() - 1); // Chop end.
 | 
			
		||||
		}
 | 
			
		||||
		Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(d.is_null(), ERR_CANT_CREATE, vformat("Cannot create DirAccess for path '%s'.", resource_path));
 | 
			
		||||
		d->change_dir(resource_path);
 | 
			
		||||
 | 
			
		||||
		Error err;
 | 
			
		||||
 | 
			
		||||
		err = _load_settings_text_or_binary(resource_path.path_join("project.godot"), resource_path.path_join("project.binary"));
 | 
			
		||||
		if (err == OK && !p_ignore_override) {
 | 
			
		||||
			// Optional, we don't mind if it fails.
 | 
			
		||||
			_load_settings_text(resource_path.path_join("override.cfg"));
 | 
			
		||||
			return err;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// Nothing was found, try to find a project file in provided path (`p_path`)
 | 
			
		||||
	// or, if requested (`p_upwards`) in parent directories.
 | 
			
		||||
 | 
			
		||||
	Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(d.is_null(), ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_path + "'.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(d.is_null(), ERR_CANT_CREATE, vformat("Cannot create DirAccess for path '%s'.", p_path));
 | 
			
		||||
	d->change_dir(p_path);
 | 
			
		||||
 | 
			
		||||
	String current_dir = d->get_current_dir();
 | 
			
		||||
| 
						 | 
				
			
			@ -724,7 +771,7 @@ Error ProjectSettings::_load_settings_binary(const String &p_path) {
 | 
			
		|||
		cs[slen] = 0;
 | 
			
		||||
		f->get_buffer((uint8_t *)cs.ptr(), slen);
 | 
			
		||||
		String key;
 | 
			
		||||
		key.parse_utf8(cs.ptr());
 | 
			
		||||
		key.parse_utf8(cs.ptr(), slen);
 | 
			
		||||
 | 
			
		||||
		uint32_t vlen = f->get_32();
 | 
			
		||||
		Vector<uint8_t> d;
 | 
			
		||||
| 
						 | 
				
			
			@ -732,7 +779,7 @@ Error ProjectSettings::_load_settings_binary(const String &p_path) {
 | 
			
		|||
		f->get_buffer(d.ptrw(), vlen);
 | 
			
		||||
		Variant value;
 | 
			
		||||
		err = decode_variant(value, d.ptr(), d.size(), nullptr, true);
 | 
			
		||||
		ERR_CONTINUE_MSG(err != OK, "Error decoding property: " + key + ".");
 | 
			
		||||
		ERR_CONTINUE_MSG(err != OK, vformat("Error decoding property: '%s'.", key));
 | 
			
		||||
		set(key, value);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -774,7 +821,7 @@ Error ProjectSettings::_load_settings_text(const String &p_path) {
 | 
			
		|||
			last_save_time = FileAccess::get_modified_time(get_resource_path().path_join("project.godot"));
 | 
			
		||||
			return OK;
 | 
			
		||||
		}
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(err != OK, err, "Error parsing " + p_path + " at line " + itos(lines) + ": " + error_text + " File might be corrupted.");
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error parsing '%s' at line %d: %s File might be corrupted.", p_path, lines, error_text));
 | 
			
		||||
 | 
			
		||||
		if (!assign.is_empty()) {
 | 
			
		||||
			if (section.is_empty() && assign == "config_version") {
 | 
			
		||||
| 
						 | 
				
			
			@ -800,7 +847,7 @@ Error ProjectSettings::_load_settings_text_or_binary(const String &p_text_path,
 | 
			
		|||
		return OK;
 | 
			
		||||
	} else if (err != ERR_FILE_NOT_FOUND) {
 | 
			
		||||
		// If the file exists but can't be loaded, we want to know it.
 | 
			
		||||
		ERR_PRINT("Couldn't load file '" + p_bin_path + "', error code " + itos(err) + ".");
 | 
			
		||||
		ERR_PRINT(vformat("Couldn't load file '%s', error code %d.", p_bin_path, err));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Fallback to text-based project.godot file if binary was not found.
 | 
			
		||||
| 
						 | 
				
			
			@ -808,7 +855,7 @@ Error ProjectSettings::_load_settings_text_or_binary(const String &p_text_path,
 | 
			
		|||
	if (err == OK) {
 | 
			
		||||
		return OK;
 | 
			
		||||
	} else if (err != ERR_FILE_NOT_FOUND) {
 | 
			
		||||
		ERR_PRINT("Couldn't load file '" + p_text_path + "', error code " + itos(err) + ".");
 | 
			
		||||
		ERR_PRINT(vformat("Couldn't load file '%s', error code %d.", p_text_path, err));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return err;
 | 
			
		||||
| 
						 | 
				
			
			@ -822,17 +869,17 @@ Error ProjectSettings::load_custom(const String &p_path) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
int ProjectSettings::get_order(const String &p_name) const {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!props.has(p_name), -1, "Request for nonexistent project setting: " + p_name + ".");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!props.has(p_name), -1, vformat("Request for nonexistent project setting: '%s'.", p_name));
 | 
			
		||||
	return props[p_name].order;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProjectSettings::set_order(const String &p_name, int p_order) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + ".");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name));
 | 
			
		||||
	props[p_name].order = p_order;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProjectSettings::set_builtin_order(const String &p_name) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + ".");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name));
 | 
			
		||||
	if (props[p_name].order >= NO_BUILTIN_ORDER_BASE) {
 | 
			
		||||
		props[p_name].order = last_builtin_order++;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -840,12 +887,12 @@ void ProjectSettings::set_builtin_order(const String &p_name) {
 | 
			
		|||
 | 
			
		||||
bool ProjectSettings::is_builtin_setting(const String &p_name) const {
 | 
			
		||||
	// Return true because a false negative is worse than a false positive.
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!props.has(p_name), true, "Request for nonexistent project setting: " + p_name + ".");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!props.has(p_name), true, vformat("Request for nonexistent project setting: '%s'.", p_name));
 | 
			
		||||
	return props[p_name].order < NO_BUILTIN_ORDER_BASE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProjectSettings::clear(const String &p_name) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + ".");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name));
 | 
			
		||||
	props.erase(p_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -860,7 +907,7 @@ Error ProjectSettings::save() {
 | 
			
		|||
Error ProjectSettings::_save_settings_binary(const String &p_file, const RBMap<String, List<String>> &p_props, const CustomMap &p_custom, const String &p_custom_features) {
 | 
			
		||||
	Error err;
 | 
			
		||||
	Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, err, "Couldn't save project.binary at " + p_file + ".");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Couldn't save project.binary at '%s'.", p_file));
 | 
			
		||||
 | 
			
		||||
	uint8_t hdr[4] = { 'E', 'C', 'F', 'G' };
 | 
			
		||||
	file->store_buffer(hdr, 4);
 | 
			
		||||
| 
						 | 
				
			
			@ -873,7 +920,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const RBMap<S
 | 
			
		|||
 | 
			
		||||
	if (!p_custom_features.is_empty()) {
 | 
			
		||||
		// Store how many properties are saved, add one for custom features, which must always go first.
 | 
			
		||||
		file->store_32(count + 1);
 | 
			
		||||
		file->store_32(uint32_t(count + 1));
 | 
			
		||||
		String key = CoreStringName(_custom_features);
 | 
			
		||||
		file->store_pascal_string(key);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -886,12 +933,12 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const RBMap<S
 | 
			
		|||
 | 
			
		||||
		err = encode_variant(p_custom_features, buff.ptrw(), len, false);
 | 
			
		||||
		ERR_FAIL_COND_V(err != OK, err);
 | 
			
		||||
		file->store_32(len);
 | 
			
		||||
		file->store_32(uint32_t(len));
 | 
			
		||||
		file->store_buffer(buff.ptr(), buff.size());
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		// Store how many properties are saved.
 | 
			
		||||
		file->store_32(count);
 | 
			
		||||
		file->store_32(uint32_t(count));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (const KeyValue<String, List<String>> &E : p_props) {
 | 
			
		||||
| 
						 | 
				
			
			@ -918,7 +965,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const RBMap<S
 | 
			
		|||
 | 
			
		||||
			err = encode_variant(value, buff.ptrw(), len, true);
 | 
			
		||||
			ERR_FAIL_COND_V_MSG(err != OK, ERR_INVALID_DATA, "Error when trying to encode Variant.");
 | 
			
		||||
			file->store_32(len);
 | 
			
		||||
			file->store_32(uint32_t(len));
 | 
			
		||||
			file->store_buffer(buff.ptr(), buff.size());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -930,7 +977,7 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const RBMap<Str
 | 
			
		|||
	Error err;
 | 
			
		||||
	Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err);
 | 
			
		||||
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, err, "Couldn't save project.godot - " + p_file + ".");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Couldn't save project.godot - %s.", p_file));
 | 
			
		||||
 | 
			
		||||
	file->store_line("; Engine configuration file.");
 | 
			
		||||
	file->store_line("; It's best edited using the editor UI and not directly,");
 | 
			
		||||
| 
						 | 
				
			
			@ -1016,7 +1063,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Check for the existence of a csproj file.
 | 
			
		||||
	if (_csproj_exists(p_path.get_base_dir())) {
 | 
			
		||||
	if (_csproj_exists(get_resource_path())) {
 | 
			
		||||
		// If there is a csproj file, add the C# feature if it doesn't already exist.
 | 
			
		||||
		if (!project_features.has("C#")) {
 | 
			
		||||
			project_features.append("C#");
 | 
			
		||||
| 
						 | 
				
			
			@ -1076,7 +1123,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
 | 
			
		|||
		String category = E.name;
 | 
			
		||||
		String name = E.name;
 | 
			
		||||
 | 
			
		||||
		int div = category.find("/");
 | 
			
		||||
		int div = category.find_char('/');
 | 
			
		||||
 | 
			
		||||
		if (div < 0) {
 | 
			
		||||
			category = "";
 | 
			
		||||
| 
						 | 
				
			
			@ -1103,7 +1150,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
 | 
			
		|||
	} else if (p_path.ends_with(".binary")) {
 | 
			
		||||
		return _save_settings_binary(p_path, save_props, p_custom, save_features);
 | 
			
		||||
	} else {
 | 
			
		||||
		ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unknown config file format: " + p_path);
 | 
			
		||||
		ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, vformat("Unknown config file format: '%s'.", p_path));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1168,23 +1215,17 @@ bool ProjectSettings::is_project_loaded() const {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
bool ProjectSettings::_property_can_revert(const StringName &p_name) const {
 | 
			
		||||
	if (!props.has(p_name)) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return props[p_name].initial != props[p_name].variant;
 | 
			
		||||
	return props.has(p_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ProjectSettings::_property_get_revert(const StringName &p_name, Variant &r_property) const {
 | 
			
		||||
	if (!props.has(p_name)) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Duplicate so that if value is array or dictionary, changing the setting will not change the stored initial value.
 | 
			
		||||
	r_property = props[p_name].initial.duplicate();
 | 
			
		||||
 | 
			
		||||
	const RBMap<StringName, ProjectSettings::VariantContainer>::Element *value = props.find(p_name);
 | 
			
		||||
	if (value) {
 | 
			
		||||
		r_property = value->value().initial.duplicate();
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProjectSettings::set_setting(const String &p_setting, const Variant &p_value) {
 | 
			
		||||
	set(p_setting, p_value);
 | 
			
		||||
| 
						 | 
				
			
			@ -1204,10 +1245,10 @@ void ProjectSettings::refresh_global_class_list() {
 | 
			
		|||
	Array script_classes = get_global_class_list();
 | 
			
		||||
	for (int i = 0; i < script_classes.size(); i++) {
 | 
			
		||||
		Dictionary c = script_classes[i];
 | 
			
		||||
		if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) {
 | 
			
		||||
		if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base") || !c.has("is_abstract") || !c.has("is_tool")) {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		ScriptServer::add_global_class(c["class"], c["base"], c["language"], c["path"]);
 | 
			
		||||
		ScriptServer::add_global_class(c["class"], c["base"], c["language"], c["path"], c["is_abstract"], c["is_tool"]);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1378,7 +1419,7 @@ void ProjectSettings::_bind_methods() {
 | 
			
		|||
	ClassDB::bind_method(D_METHOD("localize_path", "path"), &ProjectSettings::localize_path);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("globalize_path", "path"), &ProjectSettings::globalize_path);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("save"), &ProjectSettings::save);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("load_resource_pack", "pack", "replace_files", "offset"), &ProjectSettings::_load_resource_pack, DEFVAL(true), DEFVAL(0));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("load_resource_pack", "pack", "replace_files", "offset"), &ProjectSettings::load_resource_pack, DEFVAL(true), DEFVAL(0));
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("save_custom", "file"), &ProjectSettings::_save_custom_bnd);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1398,7 +1439,7 @@ void ProjectSettings::_add_builtin_input_map() {
 | 
			
		|||
			}
 | 
			
		||||
 | 
			
		||||
			Dictionary action;
 | 
			
		||||
			action["deadzone"] = Variant(0.5f);
 | 
			
		||||
			action["deadzone"] = Variant(InputMap::DEFAULT_DEADZONE);
 | 
			
		||||
			action["events"] = events;
 | 
			
		||||
 | 
			
		||||
			String action_name = "input/" + E.key;
 | 
			
		||||
| 
						 | 
				
			
			@ -1467,15 +1508,12 @@ ProjectSettings::ProjectSettings() {
 | 
			
		|||
	GLOBAL_DEF("display/window/size/transparent", false);
 | 
			
		||||
	GLOBAL_DEF("display/window/size/extend_to_title", false);
 | 
			
		||||
	GLOBAL_DEF("display/window/size/no_focus", false);
 | 
			
		||||
	GLOBAL_DEF("display/window/size/sharp_corners", false);
 | 
			
		||||
 | 
			
		||||
	GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_width_override", PROPERTY_HINT_RANGE, "0,7680,1,or_greater"), 0); // 8K resolution
 | 
			
		||||
	GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"), 0); // 8K resolution
 | 
			
		||||
 | 
			
		||||
	GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true);
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	GLOBAL_DEF("display/window/energy_saving/keep_screen_on.editor_hint", false);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	GLOBAL_DEF("animation/warnings/check_invalid_track_paths", true);
 | 
			
		||||
	GLOBAL_DEF("animation/warnings/check_angle_interpolation_type_conflicting", true);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1489,15 +1527,6 @@ ProjectSettings::ProjectSettings() {
 | 
			
		|||
	GLOBAL_DEF(PropertyInfo(Variant::INT, "audio/general/ios/session_category", PROPERTY_HINT_ENUM, "Ambient,Multi Route,Play and Record,Playback,Record,Solo Ambient"), 0);
 | 
			
		||||
	GLOBAL_DEF("audio/general/ios/mix_with_others", false);
 | 
			
		||||
 | 
			
		||||
	PackedStringArray extensions;
 | 
			
		||||
	extensions.push_back("gd");
 | 
			
		||||
	if (ClassDB::class_exists("CSharpScript")) {
 | 
			
		||||
		extensions.push_back("cs");
 | 
			
		||||
	}
 | 
			
		||||
	extensions.push_back("gdshader");
 | 
			
		||||
 | 
			
		||||
	GLOBAL_DEF(PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions"), extensions);
 | 
			
		||||
 | 
			
		||||
	_add_builtin_input_map();
 | 
			
		||||
 | 
			
		||||
	// Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum.
 | 
			
		||||
| 
						 | 
				
			
			@ -1505,7 +1534,15 @@ ProjectSettings::ProjectSettings() {
 | 
			
		|||
	GLOBAL_DEF("display/window/subwindows/embed_subwindows", true);
 | 
			
		||||
	// Keep the enum values in sync with the `DisplayServer::VSyncMode` enum.
 | 
			
		||||
	custom_prop_info["display/window/vsync/vsync_mode"] = PropertyInfo(Variant::INT, "display/window/vsync/vsync_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Adaptive,Mailbox");
 | 
			
		||||
	custom_prop_info["rendering/driver/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/driver/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded");
 | 
			
		||||
 | 
			
		||||
	GLOBAL_DEF("display/window/frame_pacing/android/enable_frame_pacing", true);
 | 
			
		||||
	GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/frame_pacing/android/swappy_mode", PROPERTY_HINT_ENUM, "pipeline_forced_on,auto_fps_pipeline_forced_on,auto_fps_auto_pipeline"), 2);
 | 
			
		||||
 | 
			
		||||
#ifdef DISABLE_DEPRECATED
 | 
			
		||||
	custom_prop_info["rendering/driver/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/driver/threads/thread_model", PROPERTY_HINT_ENUM, "Safe:1,Separate");
 | 
			
		||||
#else
 | 
			
		||||
	custom_prop_info["rendering/driver/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/driver/threads/thread_model", PROPERTY_HINT_ENUM, "Unsafe (deprecated),Safe,Separate");
 | 
			
		||||
#endif
 | 
			
		||||
	GLOBAL_DEF("physics/2d/run_on_separate_thread", false);
 | 
			
		||||
	GLOBAL_DEF("physics/3d/run_on_separate_thread", false);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1548,6 +1585,7 @@ ProjectSettings::ProjectSettings() {
 | 
			
		|||
	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/block_size_kb", PROPERTY_HINT_RANGE, "4,2048,1,or_greater"), 256);
 | 
			
		||||
	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/max_size_mb", PROPERTY_HINT_RANGE, "1,1024,1,or_greater"), 128);
 | 
			
		||||
	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/texture_upload_region_size_px", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64);
 | 
			
		||||
	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/texture_download_region_size_px", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64);
 | 
			
		||||
	GLOBAL_DEF_RST(PropertyInfo(Variant::BOOL, "rendering/rendering_device/pipeline_cache/enable"), true);
 | 
			
		||||
	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/rendering_device/pipeline_cache/save_chunk_size_mb", PROPERTY_HINT_RANGE, "0.000001,64.0,0.001,or_greater"), 3.0);
 | 
			
		||||
	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/vulkan/max_descriptors_per_pool", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64);
 | 
			
		||||
| 
						 | 
				
			
			@ -1563,13 +1601,18 @@ ProjectSettings::ProjectSettings() {
 | 
			
		|||
	// installed by the scripts provided in the repository
 | 
			
		||||
	// (check `misc/scripts/install_d3d12_sdk_windows.py`).
 | 
			
		||||
	// For example, if the script installs 1.613.3, the default value must be 613.
 | 
			
		||||
	GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/rendering_device/d3d12/agility_sdk_version"), 613);
 | 
			
		||||
	GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/rendering_device/d3d12/agility_sdk_version", PROPERTY_HINT_RANGE, "0,10000,1,or_greater,hide_slider"), 613);
 | 
			
		||||
 | 
			
		||||
	GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/textures/canvas_textures/default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Linear Mipmap,Nearest Mipmap"), 1);
 | 
			
		||||
	GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/textures/canvas_textures/default_texture_repeat", PROPERTY_HINT_ENUM, "Disable,Enable,Mirror"), 0);
 | 
			
		||||
 | 
			
		||||
	GLOBAL_DEF("collada/use_ambient", false);
 | 
			
		||||
 | 
			
		||||
	// Input settings
 | 
			
		||||
	GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false);
 | 
			
		||||
	GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false);
 | 
			
		||||
	GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "input_devices/pointing/android/rotary_input_scroll_axis", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1);
 | 
			
		||||
 | 
			
		||||
	// These properties will not show up in the dialog. If you want to exclude whole groups, use add_hidden_prefix().
 | 
			
		||||
	GLOBAL_DEF_INTERNAL("application/config/features", PackedStringArray());
 | 
			
		||||
	GLOBAL_DEF_INTERNAL("internationalization/locale/translation_remaps", PackedStringArray());
 | 
			
		||||
| 
						 | 
				
			
			@ -1582,6 +1625,7 @@ ProjectSettings::ProjectSettings() {
 | 
			
		|||
 | 
			
		||||
ProjectSettings::ProjectSettings(const String &p_path) {
 | 
			
		||||
	if (load_custom(p_path) == OK) {
 | 
			
		||||
		resource_path = p_path.get_base_dir();
 | 
			
		||||
		project_loaded = true;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,10 +47,8 @@ public:
 | 
			
		|||
	typedef HashMap<String, Variant> CustomMap;
 | 
			
		||||
	static const String PROJECT_DATA_DIR_NAME_SUFFIX;
 | 
			
		||||
 | 
			
		||||
	enum {
 | 
			
		||||
	// Properties that are not for built in values begin from this value, so builtin ones are displayed first.
 | 
			
		||||
		NO_BUILTIN_ORDER_BASE = 1 << 16
 | 
			
		||||
	};
 | 
			
		||||
	constexpr static const int32_t NO_BUILTIN_ORDER_BASE = 1 << 16;
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	const static PackedStringArray get_required_features();
 | 
			
		||||
| 
						 | 
				
			
			@ -137,7 +135,8 @@ protected:
 | 
			
		|||
 | 
			
		||||
	void _convert_to_last_version(int p_from_version);
 | 
			
		||||
 | 
			
		||||
	bool _load_resource_pack(const String &p_pack, bool p_replace_files = true, int p_offset = 0);
 | 
			
		||||
	bool load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset);
 | 
			
		||||
	bool _load_resource_pack(const String &p_pack, bool p_replace_files = true, int p_offset = 0, bool p_main_pack = false);
 | 
			
		||||
 | 
			
		||||
	void _add_property_info_bind(const Dictionary &p_info);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										62
									
								
								engine/core/core_bind.compat.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								engine/core/core_bind.compat.inc
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,62 @@
 | 
			
		|||
/**************************************************************************/
 | 
			
		||||
/*  core_bind.compat.inc                                                  */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
 | 
			
		||||
namespace core_bind {
 | 
			
		||||
 | 
			
		||||
// Semaphore
 | 
			
		||||
 | 
			
		||||
void Semaphore::_post_bind_compat_93605() {
 | 
			
		||||
	post(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Semaphore::_bind_compatibility_methods() {
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("post"), &Semaphore::_post_bind_compat_93605);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OS
 | 
			
		||||
 | 
			
		||||
String OS::_read_string_from_stdin_bind_compat_91201() {
 | 
			
		||||
	return read_string_from_stdin(1024);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Dictionary OS::_execute_with_pipe_bind_compat_94434(const String &p_path, const Vector<String> &p_arguments) {
 | 
			
		||||
	return execute_with_pipe(p_path, p_arguments, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OS::_bind_compatibility_methods() {
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("read_string_from_stdin"), &OS::_read_string_from_stdin_bind_compat_91201);
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("execute_with_pipe", "path", "arguments"), &OS::_execute_with_pipe_bind_compat_94434);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace core_bind
 | 
			
		||||
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
| 
						 | 
				
			
			@ -29,13 +29,12 @@
 | 
			
		|||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "core_bind.h"
 | 
			
		||||
#include "core_bind.compat.inc"
 | 
			
		||||
 | 
			
		||||
#include "core/config/project_settings.h"
 | 
			
		||||
#include "core/crypto/crypto_core.h"
 | 
			
		||||
#include "core/debugger/engine_debugger.h"
 | 
			
		||||
#include "core/debugger/script_debugger.h"
 | 
			
		||||
#include "core/io/file_access_compressed.h"
 | 
			
		||||
#include "core/io/file_access_encrypted.h"
 | 
			
		||||
#include "core/io/marshalls.h"
 | 
			
		||||
#include "core/math/geometry_2d.h"
 | 
			
		||||
#include "core/math/geometry_3d.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -56,8 +55,11 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String &
 | 
			
		|||
ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const String &p_path, Array r_progress) {
 | 
			
		||||
	float progress = 0;
 | 
			
		||||
	::ResourceLoader::ThreadLoadStatus tls = ::ResourceLoader::load_threaded_get_status(p_path, &progress);
 | 
			
		||||
	// Default array should never be modified, it causes the hash of the method to change.
 | 
			
		||||
	if (!ClassDB::is_default_array_arg(r_progress)) {
 | 
			
		||||
		r_progress.resize(1);
 | 
			
		||||
		r_progress[0] = progress;
 | 
			
		||||
	}
 | 
			
		||||
	return (ThreadLoadStatus)tls;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +73,7 @@ Ref<Resource> ResourceLoader::load(const String &p_path, const String &p_type_hi
 | 
			
		|||
	Error err = OK;
 | 
			
		||||
	Ref<Resource> ret = ::ResourceLoader::load(p_path, p_type_hint, ResourceFormatLoader::CacheMode(p_cache_mode), &err);
 | 
			
		||||
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, ret, "Error loading resource: '" + p_path + "'.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, ret, vformat("Error loading resource: '%s'.", p_path));
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -111,10 +113,15 @@ PackedStringArray ResourceLoader::get_dependencies(const String &p_path) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
bool ResourceLoader::has_cached(const String &p_path) {
 | 
			
		||||
	String local_path = ProjectSettings::get_singleton()->localize_path(p_path);
 | 
			
		||||
	String local_path = ::ResourceLoader::_validate_local_path(p_path);
 | 
			
		||||
	return ResourceCache::has(local_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<Resource> ResourceLoader::get_cached_ref(const String &p_path) {
 | 
			
		||||
	String local_path = ::ResourceLoader::_validate_local_path(p_path);
 | 
			
		||||
	return ResourceCache::get_ref(local_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) {
 | 
			
		||||
	return ::ResourceLoader::exists(p_path, p_type_hint);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -123,9 +130,13 @@ ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) {
 | 
			
		|||
	return ::ResourceLoader::get_resource_uid(p_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Vector<String> ResourceLoader::list_directory(const String &p_directory) {
 | 
			
		||||
	return ::ResourceLoader::list_directory(p_directory);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResourceLoader::_bind_methods() {
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("load_threaded_request", "path", "type_hint", "use_sub_threads", "cache_mode"), &ResourceLoader::load_threaded_request, DEFVAL(""), DEFVAL(false), DEFVAL(CACHE_MODE_REUSE));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &ResourceLoader::load_threaded_get_status, DEFVAL(Array()));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &ResourceLoader::load_threaded_get_status, DEFVAL_ARRAY);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("load_threaded_get", "path"), &ResourceLoader::load_threaded_get);
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("load", "path", "type_hint", "cache_mode"), &ResourceLoader::load, DEFVAL(""), DEFVAL(CACHE_MODE_REUSE));
 | 
			
		||||
| 
						 | 
				
			
			@ -135,8 +146,10 @@ void ResourceLoader::_bind_methods() {
 | 
			
		|||
	ClassDB::bind_method(D_METHOD("set_abort_on_missing_resources", "abort"), &ResourceLoader::set_abort_on_missing_resources);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_dependencies", "path"), &ResourceLoader::get_dependencies);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("has_cached", "path"), &ResourceLoader::has_cached);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_cached_ref", "path"), &ResourceLoader::get_cached_ref);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &ResourceLoader::exists, DEFVAL(""));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_resource_uid", "path"), &ResourceLoader::get_resource_uid);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("list_directory", "directory_path"), &ResourceLoader::list_directory);
 | 
			
		||||
 | 
			
		||||
	BIND_ENUM_CONSTANT(THREAD_LOAD_INVALID_RESOURCE);
 | 
			
		||||
	BIND_ENUM_CONSTANT(THREAD_LOAD_IN_PROGRESS);
 | 
			
		||||
| 
						 | 
				
			
			@ -174,6 +187,10 @@ void ResourceSaver::remove_resource_format_saver(Ref<ResourceFormatSaver> p_form
 | 
			
		|||
	::ResourceSaver::remove_resource_format_saver(p_format_saver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResourceUID::ID ResourceSaver::get_resource_id_for_path(const String &p_path, bool p_generate) {
 | 
			
		||||
	return ::ResourceSaver::get_resource_id_for_path(p_path, p_generate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResourceSaver *ResourceSaver::singleton = nullptr;
 | 
			
		||||
 | 
			
		||||
void ResourceSaver::_bind_methods() {
 | 
			
		||||
| 
						 | 
				
			
			@ -181,6 +198,7 @@ void ResourceSaver::_bind_methods() {
 | 
			
		|||
	ClassDB::bind_method(D_METHOD("get_recognized_extensions", "type"), &ResourceSaver::get_recognized_extensions);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("add_resource_format_saver", "format_saver", "at_front"), &ResourceSaver::add_resource_format_saver, DEFVAL(false));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("remove_resource_format_saver", "format_saver"), &ResourceSaver::remove_resource_format_saver);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_resource_id_for_path", "path", "generate"), &ResourceSaver::get_resource_id_for_path, DEFVAL(false));
 | 
			
		||||
 | 
			
		||||
	BIND_BITFIELD_FLAG(FLAG_NONE);
 | 
			
		||||
	BIND_BITFIELD_FLAG(FLAG_RELATIVE_PATHS);
 | 
			
		||||
| 
						 | 
				
			
			@ -288,8 +306,24 @@ Error OS::shell_show_in_file_manager(const String &p_path, bool p_open_folder) {
 | 
			
		|||
	return ::OS::get_singleton()->shell_show_in_file_manager(p_path, p_open_folder);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String OS::read_string_from_stdin() {
 | 
			
		||||
	return ::OS::get_singleton()->get_stdin_string();
 | 
			
		||||
String OS::read_string_from_stdin(int64_t p_buffer_size) {
 | 
			
		||||
	return ::OS::get_singleton()->get_stdin_string(p_buffer_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PackedByteArray OS::read_buffer_from_stdin(int64_t p_buffer_size) {
 | 
			
		||||
	return ::OS::get_singleton()->get_stdin_buffer(p_buffer_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OS::StdHandleType OS::get_stdin_type() const {
 | 
			
		||||
	return (OS::StdHandleType)::OS::get_singleton()->get_stdin_type();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OS::StdHandleType OS::get_stdout_type() const {
 | 
			
		||||
	return (OS::StdHandleType)::OS::get_singleton()->get_stdout_type();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OS::StdHandleType OS::get_stderr_type() const {
 | 
			
		||||
	return (OS::StdHandleType)::OS::get_singleton()->get_stderr_type();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r_output, bool p_read_stderr, bool p_open_console) {
 | 
			
		||||
| 
						 | 
				
			
			@ -300,19 +334,22 @@ int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r
 | 
			
		|||
	String pipe;
 | 
			
		||||
	int exitcode = 0;
 | 
			
		||||
	Error err = ::OS::get_singleton()->execute(p_path, args, &pipe, &exitcode, p_read_stderr, nullptr, p_open_console);
 | 
			
		||||
	// Default array should never be modified, it causes the hash of the method to change.
 | 
			
		||||
	if (!ClassDB::is_default_array_arg(r_output)) {
 | 
			
		||||
		r_output.push_back(pipe);
 | 
			
		||||
	}
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	return exitcode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Dictionary OS::execute_with_pipe(const String &p_path, const Vector<String> &p_arguments) {
 | 
			
		||||
Dictionary OS::execute_with_pipe(const String &p_path, const Vector<String> &p_arguments, bool p_blocking) {
 | 
			
		||||
	List<String> args;
 | 
			
		||||
	for (const String &arg : p_arguments) {
 | 
			
		||||
		args.push_back(arg);
 | 
			
		||||
	}
 | 
			
		||||
	return ::OS::get_singleton()->execute_with_pipe(p_path, args);
 | 
			
		||||
	return ::OS::get_singleton()->execute_with_pipe(p_path, args, p_blocking);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int OS::create_instance(const Vector<String> &p_arguments) {
 | 
			
		||||
| 
						 | 
				
			
			@ -385,6 +422,10 @@ String OS::get_version() const {
 | 
			
		|||
	return ::OS::get_singleton()->get_version();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String OS::get_version_alias() const {
 | 
			
		||||
	return ::OS::get_singleton()->get_version_alias();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Vector<String> OS::get_video_adapter_driver_info() const {
 | 
			
		||||
	return ::OS::get_singleton()->get_video_adapter_driver_info();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -450,11 +491,11 @@ Error OS::set_thread_name(const String &p_name) {
 | 
			
		|||
 | 
			
		||||
::Thread::ID OS::get_thread_caller_id() const {
 | 
			
		||||
	return ::Thread::get_caller_id();
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
::Thread::ID OS::get_main_thread_id() const {
 | 
			
		||||
	return ::Thread::get_main_id();
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool OS::has_feature(const String &p_feature) const {
 | 
			
		||||
	const bool *value_ptr = feature_cache.getptr(p_feature);
 | 
			
		||||
| 
						 | 
				
			
			@ -538,6 +579,11 @@ String OS::get_cache_dir() const {
 | 
			
		|||
	return ::OS::get_singleton()->get_cache_path();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String OS::get_temp_dir() const {
 | 
			
		||||
	// Exposed as `get_temp_dir()` instead of `get_temp_path()` for consistency with other exposed OS methods.
 | 
			
		||||
	return ::OS::get_singleton()->get_temp_path();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool OS::is_debug_build() const {
 | 
			
		||||
#ifdef DEBUG_ENABLED
 | 
			
		||||
	return true;
 | 
			
		||||
| 
						 | 
				
			
			@ -610,9 +656,15 @@ void OS::_bind_methods() {
 | 
			
		|||
	ClassDB::bind_method(D_METHOD("get_system_font_path", "font_name", "weight", "stretch", "italic"), &OS::get_system_font_path, DEFVAL(400), DEFVAL(100), DEFVAL(false));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_system_font_path_for_text", "font_name", "text", "locale", "script", "weight", "stretch", "italic"), &OS::get_system_font_path_for_text, DEFVAL(String()), DEFVAL(String()), DEFVAL(400), DEFVAL(100), DEFVAL(false));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("read_string_from_stdin"), &OS::read_string_from_stdin);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL(Array()), DEFVAL(false), DEFVAL(false));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("execute_with_pipe", "path", "arguments"), &OS::execute_with_pipe);
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("read_string_from_stdin", "buffer_size"), &OS::read_string_from_stdin);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("read_buffer_from_stdin", "buffer_size"), &OS::read_buffer_from_stdin);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_stdin_type"), &OS::get_stdin_type);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_stdout_type"), &OS::get_stdout_type);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_stderr_type"), &OS::get_stderr_type);
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL_ARRAY, DEFVAL(false), DEFVAL(false));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("execute_with_pipe", "path", "arguments", "blocking"), &OS::execute_with_pipe, DEFVAL(true));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("kill", "pid"), &OS::kill);
 | 
			
		||||
| 
						 | 
				
			
			@ -630,6 +682,7 @@ void OS::_bind_methods() {
 | 
			
		|||
	ClassDB::bind_method(D_METHOD("get_name"), &OS::get_name);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_distribution_name"), &OS::get_distribution_name);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_version"), &OS::get_version);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_version_alias"), &OS::get_version_alias);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_cmdline_args"), &OS::get_cmdline_args);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_cmdline_user_args"), &OS::get_cmdline_user_args);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -660,6 +713,7 @@ void OS::_bind_methods() {
 | 
			
		|||
	ClassDB::bind_method(D_METHOD("get_config_dir"), &OS::get_config_dir);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_data_dir"), &OS::get_data_dir);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_cache_dir"), &OS::get_cache_dir);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_temp_dir"), &OS::get_temp_dir);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_unique_id"), &OS::get_unique_id);
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_keycode_string", "code"), &OS::get_keycode_string);
 | 
			
		||||
| 
						 | 
				
			
			@ -692,6 +746,7 @@ void OS::_bind_methods() {
 | 
			
		|||
	BIND_ENUM_CONSTANT(RENDERING_DRIVER_VULKAN);
 | 
			
		||||
	BIND_ENUM_CONSTANT(RENDERING_DRIVER_OPENGL3);
 | 
			
		||||
	BIND_ENUM_CONSTANT(RENDERING_DRIVER_D3D12);
 | 
			
		||||
	BIND_ENUM_CONSTANT(RENDERING_DRIVER_METAL);
 | 
			
		||||
 | 
			
		||||
	BIND_ENUM_CONSTANT(SYSTEM_DIR_DESKTOP);
 | 
			
		||||
	BIND_ENUM_CONSTANT(SYSTEM_DIR_DCIM);
 | 
			
		||||
| 
						 | 
				
			
			@ -701,6 +756,12 @@ void OS::_bind_methods() {
 | 
			
		|||
	BIND_ENUM_CONSTANT(SYSTEM_DIR_MUSIC);
 | 
			
		||||
	BIND_ENUM_CONSTANT(SYSTEM_DIR_PICTURES);
 | 
			
		||||
	BIND_ENUM_CONSTANT(SYSTEM_DIR_RINGTONES);
 | 
			
		||||
 | 
			
		||||
	BIND_ENUM_CONSTANT(STD_HANDLE_INVALID);
 | 
			
		||||
	BIND_ENUM_CONSTANT(STD_HANDLE_CONSOLE);
 | 
			
		||||
	BIND_ENUM_CONSTANT(STD_HANDLE_FILE);
 | 
			
		||||
	BIND_ENUM_CONSTANT(STD_HANDLE_PIPE);
 | 
			
		||||
	BIND_ENUM_CONSTANT(STD_HANDLE_UNKNOWN);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
////// Geometry2D //////
 | 
			
		||||
| 
						 | 
				
			
			@ -901,6 +962,19 @@ Dictionary Geometry2D::make_atlas(const Vector<Size2> &p_rects) {
 | 
			
		|||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TypedArray<Point2i> Geometry2D::bresenham_line(const Point2i &p_from, const Point2i &p_to) {
 | 
			
		||||
	Vector<Point2i> points = ::Geometry2D::bresenham_line(p_from, p_to);
 | 
			
		||||
 | 
			
		||||
	TypedArray<Point2i> result;
 | 
			
		||||
	result.resize(points.size());
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < points.size(); i++) {
 | 
			
		||||
		result[i] = points[i];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Geometry2D::_bind_methods() {
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("is_point_in_circle", "point", "circle_position", "circle_radius"), &Geometry2D::is_point_in_circle);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("segment_intersects_circle", "segment_from", "segment_to", "circle_position", "circle_radius"), &Geometry2D::segment_intersects_circle);
 | 
			
		||||
| 
						 | 
				
			
			@ -935,6 +1009,8 @@ void Geometry2D::_bind_methods() {
 | 
			
		|||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &Geometry2D::make_atlas);
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("bresenham_line", "from", "to"), &Geometry2D::bresenham_line);
 | 
			
		||||
 | 
			
		||||
	BIND_ENUM_CONSTANT(OPERATION_UNION);
 | 
			
		||||
	BIND_ENUM_CONSTANT(OPERATION_DIFFERENCE);
 | 
			
		||||
	BIND_ENUM_CONSTANT(OPERATION_INTERSECTION);
 | 
			
		||||
| 
						 | 
				
			
			@ -1209,14 +1285,15 @@ bool Semaphore::try_wait() {
 | 
			
		|||
	return semaphore.try_wait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Semaphore::post() {
 | 
			
		||||
	semaphore.post();
 | 
			
		||||
void Semaphore::post(int p_count) {
 | 
			
		||||
	ERR_FAIL_COND(p_count <= 0);
 | 
			
		||||
	semaphore.post(p_count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Semaphore::_bind_methods() {
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("wait"), &Semaphore::wait);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("try_wait"), &Semaphore::try_wait);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("post"), &Semaphore::post);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("post", "count"), &Semaphore::post, DEFVAL(1));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
////// Mutex //////
 | 
			
		||||
| 
						 | 
				
			
			@ -1280,7 +1357,7 @@ void Thread::_start_func(void *ud) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if (ce.error != Callable::CallError::CALL_OK) {
 | 
			
		||||
		ERR_FAIL_MSG("Could not call function '" + func_name + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, nullptr, 0, ce) + ".");
 | 
			
		||||
		ERR_FAIL_MSG(vformat("Could not call function '%s' to start thread %d: %s.", func_name, t->get_id(), Variant::get_callable_error_text(t->target_callable, nullptr, 0, ce)));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1404,6 +1481,11 @@ Variant ClassDB::instantiate(const StringName &p_class) const {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ClassDB::APIType ClassDB::class_get_api_type(const StringName &p_class) const {
 | 
			
		||||
	::ClassDB::APIType api_type = ::ClassDB::get_api_type(p_class);
 | 
			
		||||
	return (APIType)api_type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ClassDB::class_has_signal(const StringName &p_class, const StringName &p_signal) const {
 | 
			
		||||
	return ::ClassDB::has_signal(p_class, p_signal);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1440,6 +1522,14 @@ TypedArray<Dictionary> ClassDB::class_get_property_list(const StringName &p_clas
 | 
			
		|||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StringName ClassDB::class_get_property_getter(const StringName &p_class, const StringName &p_property) {
 | 
			
		||||
	return ::ClassDB::get_property_getter(p_class, p_property);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StringName ClassDB::class_get_property_setter(const StringName &p_class, const StringName &p_property) {
 | 
			
		||||
	return ::ClassDB::get_property_setter(p_class, p_property);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Variant ClassDB::class_get_property(Object *p_object, const StringName &p_property) const {
 | 
			
		||||
	Variant ret;
 | 
			
		||||
	::ClassDB::get_property(p_object, p_property, ret);
 | 
			
		||||
| 
						 | 
				
			
			@ -1492,6 +1582,23 @@ TypedArray<Dictionary> ClassDB::class_get_method_list(const StringName &p_class,
 | 
			
		|||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Variant ClassDB::class_call_static(const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) {
 | 
			
		||||
	if (p_argcount < 2) {
 | 
			
		||||
		r_call_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
 | 
			
		||||
		return Variant::NIL;
 | 
			
		||||
	}
 | 
			
		||||
	if (!p_arguments[0]->is_string() || !p_arguments[1]->is_string()) {
 | 
			
		||||
		r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
 | 
			
		||||
		return Variant::NIL;
 | 
			
		||||
	}
 | 
			
		||||
	StringName class_ = *p_arguments[0];
 | 
			
		||||
	StringName method = *p_arguments[1];
 | 
			
		||||
	const MethodBind *bind = ::ClassDB::get_method(class_, method);
 | 
			
		||||
	ERR_FAIL_NULL_V_MSG(bind, Variant::NIL, "Cannot find static method.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!bind->is_static(), Variant::NIL, "Method is not static.");
 | 
			
		||||
	return bind->call(nullptr, p_arguments + 2, p_argcount - 2, r_call_error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PackedStringArray ClassDB::class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance) const {
 | 
			
		||||
	List<String> constants;
 | 
			
		||||
	::ClassDB::get_integer_constant_list(p_class, &constants, p_no_inheritance);
 | 
			
		||||
| 
						 | 
				
			
			@ -1575,7 +1682,7 @@ void ClassDB::get_argument_options(const StringName &p_function, int p_idx, List
 | 
			
		|||
				pf == "class_has_method" || pf == "class_get_method_list" ||
 | 
			
		||||
				pf == "class_get_integer_constant_list" || pf == "class_has_integer_constant" || pf == "class_get_integer_constant" ||
 | 
			
		||||
				pf == "class_has_enum" || pf == "class_get_enum_list" || pf == "class_get_enum_constants" || pf == "class_get_integer_constant_enum" ||
 | 
			
		||||
				pf == "is_class_enabled" || pf == "is_class_enum_bitfield");
 | 
			
		||||
				pf == "is_class_enabled" || pf == "is_class_enum_bitfield" || pf == "class_get_api_type");
 | 
			
		||||
	}
 | 
			
		||||
	if (first_argument_is_class || pf == "is_parent_class") {
 | 
			
		||||
		for (const String &E : get_class_list()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1596,11 +1703,15 @@ void ClassDB::_bind_methods() {
 | 
			
		|||
	::ClassDB::bind_method(D_METHOD("can_instantiate", "class"), &ClassDB::can_instantiate);
 | 
			
		||||
	::ClassDB::bind_method(D_METHOD("instantiate", "class"), &ClassDB::instantiate);
 | 
			
		||||
 | 
			
		||||
	::ClassDB::bind_method(D_METHOD("class_get_api_type", "class"), &ClassDB::class_get_api_type);
 | 
			
		||||
 | 
			
		||||
	::ClassDB::bind_method(D_METHOD("class_has_signal", "class", "signal"), &ClassDB::class_has_signal);
 | 
			
		||||
	::ClassDB::bind_method(D_METHOD("class_get_signal", "class", "signal"), &ClassDB::class_get_signal);
 | 
			
		||||
	::ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &ClassDB::class_get_signal_list, DEFVAL(false));
 | 
			
		||||
 | 
			
		||||
	::ClassDB::bind_method(D_METHOD("class_get_property_list", "class", "no_inheritance"), &ClassDB::class_get_property_list, DEFVAL(false));
 | 
			
		||||
	::ClassDB::bind_method(D_METHOD("class_get_property_getter", "class", "property"), &ClassDB::class_get_property_getter);
 | 
			
		||||
	::ClassDB::bind_method(D_METHOD("class_get_property_setter", "class", "property"), &ClassDB::class_get_property_setter);
 | 
			
		||||
	::ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &ClassDB::class_get_property);
 | 
			
		||||
	::ClassDB::bind_method(D_METHOD("class_set_property", "object", "property", "value"), &ClassDB::class_set_property);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1612,6 +1723,8 @@ void ClassDB::_bind_methods() {
 | 
			
		|||
 | 
			
		||||
	::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::class_get_method_list, DEFVAL(false));
 | 
			
		||||
 | 
			
		||||
	::ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "class_call_static", &ClassDB::class_call_static, MethodInfo("class_call_static", PropertyInfo(Variant::STRING_NAME, "class"), PropertyInfo(Variant::STRING_NAME, "method")));
 | 
			
		||||
 | 
			
		||||
	::ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &ClassDB::class_get_integer_constant_list, DEFVAL(false));
 | 
			
		||||
 | 
			
		||||
	::ClassDB::bind_method(D_METHOD("class_has_integer_constant", "class", "name"), &ClassDB::class_has_integer_constant);
 | 
			
		||||
| 
						 | 
				
			
			@ -1625,6 +1738,12 @@ void ClassDB::_bind_methods() {
 | 
			
		|||
	::ClassDB::bind_method(D_METHOD("is_class_enum_bitfield", "class", "enum", "no_inheritance"), &ClassDB::is_class_enum_bitfield, DEFVAL(false));
 | 
			
		||||
 | 
			
		||||
	::ClassDB::bind_method(D_METHOD("is_class_enabled", "class"), &ClassDB::is_class_enabled);
 | 
			
		||||
 | 
			
		||||
	BIND_ENUM_CONSTANT(API_CORE);
 | 
			
		||||
	BIND_ENUM_CONSTANT(API_EDITOR);
 | 
			
		||||
	BIND_ENUM_CONSTANT(API_EXTENSION);
 | 
			
		||||
	BIND_ENUM_CONSTANT(API_EDITOR_EXTENSION);
 | 
			
		||||
	BIND_ENUM_CONSTANT(API_NONE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace special
 | 
			
		||||
| 
						 | 
				
			
			@ -1737,8 +1856,8 @@ Object *Engine::get_singleton_object(const StringName &p_name) const {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void Engine::register_singleton(const StringName &p_name, Object *p_object) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(has_singleton(p_name), "Singleton already registered: " + String(p_name));
 | 
			
		||||
	ERR_FAIL_COND_MSG(!String(p_name).is_valid_identifier(), "Singleton name is not a valid identifier: " + p_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(has_singleton(p_name), vformat("Singleton already registered: '%s'.", String(p_name)));
 | 
			
		||||
	ERR_FAIL_COND_MSG(!String(p_name).is_valid_ascii_identifier(), vformat("Singleton name is not a valid identifier: '%s'.", p_name));
 | 
			
		||||
	::Engine::Singleton s;
 | 
			
		||||
	s.class_name = p_name;
 | 
			
		||||
	s.name = p_name;
 | 
			
		||||
| 
						 | 
				
			
			@ -1748,8 +1867,8 @@ void Engine::register_singleton(const StringName &p_name, Object *p_object) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void Engine::unregister_singleton(const StringName &p_name) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!has_singleton(p_name), "Attempt to remove unregistered singleton: " + String(p_name));
 | 
			
		||||
	ERR_FAIL_COND_MSG(!::Engine::get_singleton()->is_singleton_user_created(p_name), "Attempt to remove non-user created singleton: " + String(p_name));
 | 
			
		||||
	ERR_FAIL_COND_MSG(!has_singleton(p_name), vformat("Attempt to remove unregistered singleton: '%s'.", String(p_name)));
 | 
			
		||||
	ERR_FAIL_COND_MSG(!::Engine::get_singleton()->is_singleton_user_created(p_name), vformat("Attempt to remove non-user created singleton: '%s'.", String(p_name)));
 | 
			
		||||
	::Engine::get_singleton()->remove_singleton(p_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1787,10 +1906,22 @@ bool Engine::is_editor_hint() const {
 | 
			
		|||
	return ::Engine::get_singleton()->is_editor_hint();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Engine::is_embedded_in_editor() const {
 | 
			
		||||
	return ::Engine::get_singleton()->is_embedded_in_editor();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String Engine::get_write_movie_path() const {
 | 
			
		||||
	return ::Engine::get_singleton()->get_write_movie_path();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Engine::set_print_to_stdout(bool p_enabled) {
 | 
			
		||||
	::Engine::get_singleton()->set_print_to_stdout(p_enabled);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Engine::is_printing_to_stdout() const {
 | 
			
		||||
	return ::Engine::get_singleton()->is_printing_to_stdout();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Engine::set_print_error_messages(bool p_enabled) {
 | 
			
		||||
	::Engine::get_singleton()->set_print_error_messages(p_enabled);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1856,13 +1987,18 @@ void Engine::_bind_methods() {
 | 
			
		|||
	ClassDB::bind_method(D_METHOD("get_script_language", "index"), &Engine::get_script_language);
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("is_editor_hint"), &Engine::is_editor_hint);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("is_embedded_in_editor"), &Engine::is_embedded_in_editor);
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_write_movie_path"), &Engine::get_write_movie_path);
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("set_print_to_stdout", "enabled"), &Engine::set_print_to_stdout);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("is_printing_to_stdout"), &Engine::is_printing_to_stdout);
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("set_print_error_messages", "enabled"), &Engine::set_print_error_messages);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("is_printing_error_messages"), &Engine::is_printing_error_messages);
 | 
			
		||||
 | 
			
		||||
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "print_error_messages"), "set_print_error_messages", "is_printing_error_messages");
 | 
			
		||||
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "print_to_stdout"), "set_print_to_stdout", "is_printing_to_stdout");
 | 
			
		||||
	ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_ticks_per_second"), "set_physics_ticks_per_second", "get_physics_ticks_per_second");
 | 
			
		||||
	ADD_PROPERTY(PropertyInfo(Variant::INT, "max_physics_steps_per_frame"), "set_max_physics_steps_per_frame", "get_max_physics_steps_per_frame");
 | 
			
		||||
	ADD_PROPERTY(PropertyInfo(Variant::INT, "max_fps"), "set_max_fps", "get_max_fps");
 | 
			
		||||
| 
						 | 
				
			
			@ -1881,14 +2017,14 @@ bool EngineDebugger::is_active() {
 | 
			
		|||
void EngineDebugger::register_profiler(const StringName &p_name, Ref<EngineProfiler> p_profiler) {
 | 
			
		||||
	ERR_FAIL_COND(p_profiler.is_null());
 | 
			
		||||
	ERR_FAIL_COND_MSG(p_profiler->is_bound(), "Profiler already registered.");
 | 
			
		||||
	ERR_FAIL_COND_MSG(profilers.has(p_name) || has_profiler(p_name), "Profiler name already in use: " + p_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(profilers.has(p_name) || has_profiler(p_name), vformat("Profiler name already in use: '%s'.", p_name));
 | 
			
		||||
	Error err = p_profiler->bind(p_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(err != OK, "Profiler failed to register with error: " + itos(err));
 | 
			
		||||
	ERR_FAIL_COND_MSG(err != OK, vformat("Profiler failed to register with error: %d.", err));
 | 
			
		||||
	profilers.insert(p_name, p_profiler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EngineDebugger::unregister_profiler(const StringName &p_name) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + p_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(!profilers.has(p_name), vformat("Profiler not registered: '%s'.", p_name));
 | 
			
		||||
	profilers[p_name]->unbind();
 | 
			
		||||
	profilers.erase(p_name);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1912,7 +2048,7 @@ void EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, c
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void EngineDebugger::register_message_capture(const StringName &p_name, const Callable &p_callable) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(captures.has(p_name) || has_capture(p_name), "Capture already registered: " + p_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(captures.has(p_name) || has_capture(p_name), vformat("Capture already registered: '%s'.", p_name));
 | 
			
		||||
	captures.insert(p_name, p_callable);
 | 
			
		||||
	Callable &c = captures[p_name];
 | 
			
		||||
	::EngineDebugger::Capture capture(&c, &EngineDebugger::call_capture);
 | 
			
		||||
| 
						 | 
				
			
			@ -1920,7 +2056,7 @@ void EngineDebugger::register_message_capture(const StringName &p_name, const Ca
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void EngineDebugger::unregister_message_capture(const StringName &p_name) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!captures.has(p_name), "Capture not registered: " + p_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(!captures.has(p_name), vformat("Capture not registered: '%s'.", p_name));
 | 
			
		||||
	::EngineDebugger::unregister_message_capture(p_name);
 | 
			
		||||
	captures.erase(p_name);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1954,8 +2090,8 @@ Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Arra
 | 
			
		|||
	Variant retval;
 | 
			
		||||
	Callable::CallError err;
 | 
			
		||||
	capture.callp(args, 2, retval, err);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err.error != Callable::CallError::CALL_OK, FAILED, "Error calling 'capture' to callable: " + Variant::get_callable_error_text(capture, args, 2, err));
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(retval.get_type() != Variant::BOOL, FAILED, "Error calling 'capture' to callable: " + String(capture) + ". Return type is not bool.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err.error != Callable::CallError::CALL_OK, FAILED, vformat("Error calling 'capture' to callable: %s.", Variant::get_callable_error_text(capture, args, 2, err)));
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(retval.get_type() != Variant::BOOL, FAILED, vformat("Error calling 'capture' to callable: '%s'. Return type is not bool.", String(capture)));
 | 
			
		||||
	r_captured = retval;
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,11 +32,8 @@
 | 
			
		|||
#define CORE_BIND_H
 | 
			
		||||
 | 
			
		||||
#include "core/debugger/engine_profiler.h"
 | 
			
		||||
#include "core/io/image.h"
 | 
			
		||||
#include "core/io/resource_loader.h"
 | 
			
		||||
#include "core/io/resource_saver.h"
 | 
			
		||||
#include "core/object/script_language.h"
 | 
			
		||||
#include "core/os/os.h"
 | 
			
		||||
#include "core/os/semaphore.h"
 | 
			
		||||
#include "core/os/thread.h"
 | 
			
		||||
#include "core/templates/safe_refcount.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +70,7 @@ public:
 | 
			
		|||
	static ResourceLoader *get_singleton() { return singleton; }
 | 
			
		||||
 | 
			
		||||
	Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, CacheMode p_cache_mode = CACHE_MODE_REUSE);
 | 
			
		||||
	ThreadLoadStatus load_threaded_get_status(const String &p_path, Array r_progress = Array());
 | 
			
		||||
	ThreadLoadStatus load_threaded_get_status(const String &p_path, Array r_progress = ClassDB::default_array_arg);
 | 
			
		||||
	Ref<Resource> load_threaded_get(const String &p_path);
 | 
			
		||||
 | 
			
		||||
	Ref<Resource> load(const String &p_path, const String &p_type_hint = "", CacheMode p_cache_mode = CACHE_MODE_REUSE);
 | 
			
		||||
| 
						 | 
				
			
			@ -83,9 +80,12 @@ public:
 | 
			
		|||
	void set_abort_on_missing_resources(bool p_abort);
 | 
			
		||||
	PackedStringArray get_dependencies(const String &p_path);
 | 
			
		||||
	bool has_cached(const String &p_path);
 | 
			
		||||
	Ref<Resource> get_cached_ref(const String &p_path);
 | 
			
		||||
	bool exists(const String &p_path, const String &p_type_hint = "");
 | 
			
		||||
	ResourceUID::ID get_resource_uid(const String &p_path);
 | 
			
		||||
 | 
			
		||||
	Vector<String> list_directory(const String &p_directory);
 | 
			
		||||
 | 
			
		||||
	ResourceLoader() { singleton = this; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -115,6 +115,8 @@ public:
 | 
			
		|||
	void add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front);
 | 
			
		||||
	void remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver);
 | 
			
		||||
 | 
			
		||||
	ResourceUID::ID get_resource_id_for_path(const String &p_path, bool p_generate = false);
 | 
			
		||||
 | 
			
		||||
	ResourceSaver() { singleton = this; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -127,16 +129,32 @@ protected:
 | 
			
		|||
	static void _bind_methods();
 | 
			
		||||
	static OS *singleton;
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
	Dictionary _execute_with_pipe_bind_compat_94434(const String &p_path, const Vector<String> &p_arguments);
 | 
			
		||||
 | 
			
		||||
	String _read_string_from_stdin_bind_compat_91201();
 | 
			
		||||
	static void _bind_compatibility_methods();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	enum RenderingDriver {
 | 
			
		||||
		RENDERING_DRIVER_VULKAN,
 | 
			
		||||
		RENDERING_DRIVER_OPENGL3,
 | 
			
		||||
		RENDERING_DRIVER_D3D12,
 | 
			
		||||
		RENDERING_DRIVER_METAL,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	PackedByteArray get_entropy(int p_bytes);
 | 
			
		||||
	String get_system_ca_certificates();
 | 
			
		||||
 | 
			
		||||
	enum StdHandleType {
 | 
			
		||||
		STD_HANDLE_INVALID,
 | 
			
		||||
		STD_HANDLE_CONSOLE,
 | 
			
		||||
		STD_HANDLE_FILE,
 | 
			
		||||
		STD_HANDLE_PIPE,
 | 
			
		||||
		STD_HANDLE_UNKNOWN,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	virtual PackedStringArray get_connected_midi_inputs();
 | 
			
		||||
	virtual void open_midi_inputs();
 | 
			
		||||
	virtual void close_midi_inputs();
 | 
			
		||||
| 
						 | 
				
			
			@ -157,9 +175,15 @@ public:
 | 
			
		|||
	String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const;
 | 
			
		||||
	Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const;
 | 
			
		||||
	String get_executable_path() const;
 | 
			
		||||
	String read_string_from_stdin();
 | 
			
		||||
	int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false, bool p_open_console = false);
 | 
			
		||||
	Dictionary execute_with_pipe(const String &p_path, const Vector<String> &p_arguments);
 | 
			
		||||
 | 
			
		||||
	String read_string_from_stdin(int64_t p_buffer_size = 1024);
 | 
			
		||||
	PackedByteArray read_buffer_from_stdin(int64_t p_buffer_size = 1024);
 | 
			
		||||
	StdHandleType get_stdin_type() const;
 | 
			
		||||
	StdHandleType get_stdout_type() const;
 | 
			
		||||
	StdHandleType get_stderr_type() const;
 | 
			
		||||
 | 
			
		||||
	int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = ClassDB::default_array_arg, bool p_read_stderr = false, bool p_open_console = false);
 | 
			
		||||
	Dictionary execute_with_pipe(const String &p_path, const Vector<String> &p_arguments, bool p_blocking = true);
 | 
			
		||||
	int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false);
 | 
			
		||||
	int create_instance(const Vector<String> &p_arguments);
 | 
			
		||||
	Error kill(int p_pid);
 | 
			
		||||
| 
						 | 
				
			
			@ -182,6 +206,7 @@ public:
 | 
			
		|||
	String get_name() const;
 | 
			
		||||
	String get_distribution_name() const;
 | 
			
		||||
	String get_version() const;
 | 
			
		||||
	String get_version_alias() const;
 | 
			
		||||
	Vector<String> get_cmdline_args();
 | 
			
		||||
	Vector<String> get_cmdline_user_args();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -236,6 +261,7 @@ public:
 | 
			
		|||
	String get_config_dir() const;
 | 
			
		||||
	String get_data_dir() const;
 | 
			
		||||
	String get_cache_dir() const;
 | 
			
		||||
	String get_temp_dir() const;
 | 
			
		||||
 | 
			
		||||
	Error set_thread_name(const String &p_name);
 | 
			
		||||
	::Thread::ID get_thread_caller_id() const;
 | 
			
		||||
| 
						 | 
				
			
			@ -315,6 +341,8 @@ public:
 | 
			
		|||
 | 
			
		||||
	Dictionary make_atlas(const Vector<Size2> &p_rects);
 | 
			
		||||
 | 
			
		||||
	TypedArray<Point2i> bresenham_line(const Point2i &p_from, const Point2i &p_to);
 | 
			
		||||
 | 
			
		||||
	Geometry2D() { singleton = this; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -389,12 +417,17 @@ class Semaphore : public RefCounted {
 | 
			
		|||
	GDCLASS(Semaphore, RefCounted);
 | 
			
		||||
	::Semaphore semaphore;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
	void _post_bind_compat_93605();
 | 
			
		||||
	static void _bind_compatibility_methods();
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	void wait();
 | 
			
		||||
	bool try_wait();
 | 
			
		||||
	void post();
 | 
			
		||||
	void post(int p_count = 1);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Thread : public RefCounted {
 | 
			
		||||
| 
						 | 
				
			
			@ -434,6 +467,14 @@ protected:
 | 
			
		|||
	static void _bind_methods();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	enum APIType {
 | 
			
		||||
		API_CORE,
 | 
			
		||||
		API_EDITOR,
 | 
			
		||||
		API_EXTENSION,
 | 
			
		||||
		API_EDITOR_EXTENSION,
 | 
			
		||||
		API_NONE,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	PackedStringArray get_class_list() const;
 | 
			
		||||
	PackedStringArray get_inheriters_from_class(const StringName &p_class) const;
 | 
			
		||||
	StringName get_parent_class(const StringName &p_class) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -442,11 +483,14 @@ public:
 | 
			
		|||
	bool can_instantiate(const StringName &p_class) const;
 | 
			
		||||
	Variant instantiate(const StringName &p_class) const;
 | 
			
		||||
 | 
			
		||||
	APIType class_get_api_type(const StringName &p_class) const;
 | 
			
		||||
	bool class_has_signal(const StringName &p_class, const StringName &p_signal) const;
 | 
			
		||||
	Dictionary class_get_signal(const StringName &p_class, const StringName &p_signal) const;
 | 
			
		||||
	TypedArray<Dictionary> class_get_signal_list(const StringName &p_class, bool p_no_inheritance = false) const;
 | 
			
		||||
 | 
			
		||||
	TypedArray<Dictionary> class_get_property_list(const StringName &p_class, bool p_no_inheritance = false) const;
 | 
			
		||||
	StringName class_get_property_getter(const StringName &p_class, const StringName &p_property);
 | 
			
		||||
	StringName class_get_property_setter(const StringName &p_class, const StringName &p_property);
 | 
			
		||||
	Variant class_get_property(Object *p_object, const StringName &p_property) const;
 | 
			
		||||
	Error class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -457,6 +501,7 @@ public:
 | 
			
		|||
	int class_get_method_argument_count(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false) const;
 | 
			
		||||
 | 
			
		||||
	TypedArray<Dictionary> class_get_method_list(const StringName &p_class, bool p_no_inheritance = false) const;
 | 
			
		||||
	Variant class_call_static(const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error);
 | 
			
		||||
 | 
			
		||||
	PackedStringArray class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const;
 | 
			
		||||
	bool class_has_integer_constant(const StringName &p_class, const StringName &p_name) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -539,9 +584,14 @@ public:
 | 
			
		|||
	void set_editor_hint(bool p_enabled);
 | 
			
		||||
	bool is_editor_hint() const;
 | 
			
		||||
 | 
			
		||||
	bool is_embedded_in_editor() const;
 | 
			
		||||
 | 
			
		||||
	// `set_write_movie_path()` is not exposed to the scripting API as changing it at run-time has no effect.
 | 
			
		||||
	String get_write_movie_path() const;
 | 
			
		||||
 | 
			
		||||
	void set_print_to_stdout(bool p_enabled);
 | 
			
		||||
	bool is_printing_to_stdout() const;
 | 
			
		||||
 | 
			
		||||
	void set_print_error_messages(bool p_enabled);
 | 
			
		||||
	bool is_printing_error_messages() const;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -611,6 +661,7 @@ VARIANT_BITFIELD_CAST(core_bind::ResourceSaver::SaverFlags);
 | 
			
		|||
 | 
			
		||||
VARIANT_ENUM_CAST(core_bind::OS::RenderingDriver);
 | 
			
		||||
VARIANT_ENUM_CAST(core_bind::OS::SystemDir);
 | 
			
		||||
VARIANT_ENUM_CAST(core_bind::OS::StdHandleType);
 | 
			
		||||
 | 
			
		||||
VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyBooleanOperation);
 | 
			
		||||
VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyJoinType);
 | 
			
		||||
| 
						 | 
				
			
			@ -618,4 +669,6 @@ VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyEndType);
 | 
			
		|||
 | 
			
		||||
VARIANT_ENUM_CAST(core_bind::Thread::Priority);
 | 
			
		||||
 | 
			
		||||
VARIANT_ENUM_CAST(core_bind::special::ClassDB::APIType);
 | 
			
		||||
 | 
			
		||||
#endif // CORE_BIND_H
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -200,8 +200,8 @@ def make_license_header(target, source, env):
 | 
			
		|||
            tag, content = reader.next_tag()
 | 
			
		||||
            if tag in ("Files", "Copyright", "License"):
 | 
			
		||||
                part[tag] = content[:]
 | 
			
		||||
            elif tag == "Comment":
 | 
			
		||||
                # attach part to named project
 | 
			
		||||
            elif tag == "Comment" and part:
 | 
			
		||||
                # attach non-empty part to named project
 | 
			
		||||
                projects[content[0]] = projects.get(content[0], []) + [part]
 | 
			
		||||
 | 
			
		||||
            if not tag or not reader.current:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,7 +33,6 @@
 | 
			
		|||
#include "core/input/input_event.h"
 | 
			
		||||
#include "core/object/class_db.h"
 | 
			
		||||
#include "core/os/keyboard.h"
 | 
			
		||||
#include "core/templates/hash_set.h"
 | 
			
		||||
#include "core/variant/variant.h"
 | 
			
		||||
 | 
			
		||||
struct _CoreConstant {
 | 
			
		||||
| 
						 | 
				
			
			@ -671,11 +670,14 @@ void register_global_constants() {
 | 
			
		|||
	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_OBJECTID);
 | 
			
		||||
	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_POINTER);
 | 
			
		||||
	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ARRAY_TYPE);
 | 
			
		||||
	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_DICTIONARY_TYPE);
 | 
			
		||||
	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LOCALE_ID);
 | 
			
		||||
	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LOCALIZABLE_STRING);
 | 
			
		||||
	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_TYPE);
 | 
			
		||||
	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_HIDE_QUATERNION_EDIT);
 | 
			
		||||
	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PASSWORD);
 | 
			
		||||
	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_TOOL_BUTTON);
 | 
			
		||||
	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ONESHOT);
 | 
			
		||||
	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX);
 | 
			
		||||
 | 
			
		||||
	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NONE);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@
 | 
			
		|||
#define CORE_CONSTANTS_H
 | 
			
		||||
 | 
			
		||||
#include "core/string/string_name.h"
 | 
			
		||||
#include "core/templates/hash_set.h"
 | 
			
		||||
#include "core/templates/hash_map.h"
 | 
			
		||||
 | 
			
		||||
class CoreConstants {
 | 
			
		||||
public:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,61 +34,56 @@
 | 
			
		|||
#include "core/string/string_name.h"
 | 
			
		||||
 | 
			
		||||
class CoreStringNames {
 | 
			
		||||
	friend void register_core_types();
 | 
			
		||||
	friend void unregister_core_types();
 | 
			
		||||
	inline static CoreStringNames *singleton = nullptr;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static void create() { singleton = memnew(CoreStringNames); }
 | 
			
		||||
	static void free() {
 | 
			
		||||
		memdelete(singleton);
 | 
			
		||||
		singleton = nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CoreStringNames();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	_FORCE_INLINE_ static CoreStringNames *get_singleton() { return singleton; }
 | 
			
		||||
 | 
			
		||||
	static CoreStringNames *singleton;
 | 
			
		||||
	const StringName free_ = StaticCString::create("free"); // free would conflict with C++ keyword.
 | 
			
		||||
	const StringName changed = StaticCString::create("changed");
 | 
			
		||||
	const StringName script = StaticCString::create("script");
 | 
			
		||||
	const StringName script_changed = StaticCString::create("script_changed");
 | 
			
		||||
	const StringName _iter_init = StaticCString::create("_iter_init");
 | 
			
		||||
	const StringName _iter_next = StaticCString::create("_iter_next");
 | 
			
		||||
	const StringName _iter_get = StaticCString::create("_iter_get");
 | 
			
		||||
	const StringName get_rid = StaticCString::create("get_rid");
 | 
			
		||||
	const StringName _to_string = StaticCString::create("_to_string");
 | 
			
		||||
	const StringName _custom_features = StaticCString::create("_custom_features");
 | 
			
		||||
 | 
			
		||||
	StringName free_; // "free", conflict with C++ keyword.
 | 
			
		||||
	StringName changed;
 | 
			
		||||
	StringName script;
 | 
			
		||||
	StringName script_changed;
 | 
			
		||||
	StringName _iter_init;
 | 
			
		||||
	StringName _iter_next;
 | 
			
		||||
	StringName _iter_get;
 | 
			
		||||
	StringName get_rid;
 | 
			
		||||
	StringName _to_string;
 | 
			
		||||
	StringName _custom_features;
 | 
			
		||||
	const StringName x = StaticCString::create("x");
 | 
			
		||||
	const StringName y = StaticCString::create("y");
 | 
			
		||||
	const StringName z = StaticCString::create("z");
 | 
			
		||||
	const StringName w = StaticCString::create("w");
 | 
			
		||||
	const StringName r = StaticCString::create("r");
 | 
			
		||||
	const StringName g = StaticCString::create("g");
 | 
			
		||||
	const StringName b = StaticCString::create("b");
 | 
			
		||||
	const StringName a = StaticCString::create("a");
 | 
			
		||||
	const StringName position = StaticCString::create("position");
 | 
			
		||||
	const StringName size = StaticCString::create("size");
 | 
			
		||||
	const StringName end = StaticCString::create("end");
 | 
			
		||||
	const StringName basis = StaticCString::create("basis");
 | 
			
		||||
	const StringName origin = StaticCString::create("origin");
 | 
			
		||||
	const StringName normal = StaticCString::create("normal");
 | 
			
		||||
	const StringName d = StaticCString::create("d");
 | 
			
		||||
	const StringName h = StaticCString::create("h");
 | 
			
		||||
	const StringName s = StaticCString::create("s");
 | 
			
		||||
	const StringName v = StaticCString::create("v");
 | 
			
		||||
	const StringName r8 = StaticCString::create("r8");
 | 
			
		||||
	const StringName g8 = StaticCString::create("g8");
 | 
			
		||||
	const StringName b8 = StaticCString::create("b8");
 | 
			
		||||
	const StringName a8 = StaticCString::create("a8");
 | 
			
		||||
 | 
			
		||||
	StringName x;
 | 
			
		||||
	StringName y;
 | 
			
		||||
	StringName z;
 | 
			
		||||
	StringName w;
 | 
			
		||||
	StringName r;
 | 
			
		||||
	StringName g;
 | 
			
		||||
	StringName b;
 | 
			
		||||
	StringName a;
 | 
			
		||||
	StringName position;
 | 
			
		||||
	StringName size;
 | 
			
		||||
	StringName end;
 | 
			
		||||
	StringName basis;
 | 
			
		||||
	StringName origin;
 | 
			
		||||
	StringName normal;
 | 
			
		||||
	StringName d;
 | 
			
		||||
	StringName h;
 | 
			
		||||
	StringName s;
 | 
			
		||||
	StringName v;
 | 
			
		||||
	StringName r8;
 | 
			
		||||
	StringName g8;
 | 
			
		||||
	StringName b8;
 | 
			
		||||
	StringName a8;
 | 
			
		||||
 | 
			
		||||
	StringName call;
 | 
			
		||||
	StringName call_deferred;
 | 
			
		||||
	StringName bind;
 | 
			
		||||
	StringName notification;
 | 
			
		||||
	StringName property_list_changed;
 | 
			
		||||
	const StringName call = StaticCString::create("call");
 | 
			
		||||
	const StringName call_deferred = StaticCString::create("call_deferred");
 | 
			
		||||
	const StringName bind = StaticCString::create("bind");
 | 
			
		||||
	const StringName notification = StaticCString::create("notification");
 | 
			
		||||
	const StringName property_list_changed = StaticCString::create("property_list_changed");
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define CoreStringName(m_name) CoreStringNames::get_singleton()->m_name
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
from misc.utility.scons_hints import *
 | 
			
		||||
 | 
			
		||||
Import("env")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ class AESContext : public RefCounted {
 | 
			
		|||
	GDCLASS(AESContext, RefCounted);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	enum Mode {
 | 
			
		||||
	enum Mode : int32_t {
 | 
			
		||||
		MODE_ECB_ENCRYPT,
 | 
			
		||||
		MODE_ECB_DECRYPT,
 | 
			
		||||
		MODE_CBC_ENCRYPT,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,16 +30,12 @@
 | 
			
		|||
 | 
			
		||||
#include "crypto.h"
 | 
			
		||||
 | 
			
		||||
#include "core/config/engine.h"
 | 
			
		||||
#include "core/io/certs_compressed.gen.h"
 | 
			
		||||
#include "core/io/compression.h"
 | 
			
		||||
 | 
			
		||||
/// Resources
 | 
			
		||||
 | 
			
		||||
CryptoKey *(*CryptoKey::_create)() = nullptr;
 | 
			
		||||
CryptoKey *CryptoKey::create() {
 | 
			
		||||
CryptoKey *(*CryptoKey::_create)(bool p_notify_postinitialize) = nullptr;
 | 
			
		||||
CryptoKey *CryptoKey::create(bool p_notify_postinitialize) {
 | 
			
		||||
	if (_create) {
 | 
			
		||||
		return _create();
 | 
			
		||||
		return _create(p_notify_postinitialize);
 | 
			
		||||
	}
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -52,10 +48,10 @@ void CryptoKey::_bind_methods() {
 | 
			
		|||
	ClassDB::bind_method(D_METHOD("load_from_string", "string_key", "public_only"), &CryptoKey::load_from_string, DEFVAL(false));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
X509Certificate *(*X509Certificate::_create)() = nullptr;
 | 
			
		||||
X509Certificate *X509Certificate::create() {
 | 
			
		||||
X509Certificate *(*X509Certificate::_create)(bool p_notify_postinitialize) = nullptr;
 | 
			
		||||
X509Certificate *X509Certificate::create(bool p_notify_postinitialize) {
 | 
			
		||||
	if (_create) {
 | 
			
		||||
		return _create();
 | 
			
		||||
		return _create(p_notify_postinitialize);
 | 
			
		||||
	}
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -116,10 +112,10 @@ void HMACContext::_bind_methods() {
 | 
			
		|||
	ClassDB::bind_method(D_METHOD("finish"), &HMACContext::finish);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HMACContext *(*HMACContext::_create)() = nullptr;
 | 
			
		||||
HMACContext *HMACContext::create() {
 | 
			
		||||
HMACContext *(*HMACContext::_create)(bool p_notify_postinitialize) = nullptr;
 | 
			
		||||
HMACContext *HMACContext::create(bool p_notify_postinitialize) {
 | 
			
		||||
	if (_create) {
 | 
			
		||||
		return _create();
 | 
			
		||||
		return _create(p_notify_postinitialize);
 | 
			
		||||
	}
 | 
			
		||||
	ERR_FAIL_V_MSG(nullptr, "HMACContext is not available when the mbedtls module is disabled.");
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -127,10 +123,10 @@ HMACContext *HMACContext::create() {
 | 
			
		|||
/// Crypto
 | 
			
		||||
 | 
			
		||||
void (*Crypto::_load_default_certificates)(const String &p_path) = nullptr;
 | 
			
		||||
Crypto *(*Crypto::_create)() = nullptr;
 | 
			
		||||
Crypto *Crypto::create() {
 | 
			
		||||
Crypto *(*Crypto::_create)(bool p_notify_postinitialize) = nullptr;
 | 
			
		||||
Crypto *Crypto::create(bool p_notify_postinitialize) {
 | 
			
		||||
	if (_create) {
 | 
			
		||||
		return _create();
 | 
			
		||||
		return _create(p_notify_postinitialize);
 | 
			
		||||
	}
 | 
			
		||||
	ERR_FAIL_V_MSG(nullptr, "Crypto is not available when the mbedtls module is disabled.");
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -240,7 +236,7 @@ Error ResourceFormatSaverCrypto::save(const Ref<Resource> &p_resource, const Str
 | 
			
		|||
	} else {
 | 
			
		||||
		ERR_FAIL_V(ERR_INVALID_PARAMETER);
 | 
			
		||||
	}
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save Crypto resource to file '" + p_path + "'.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot save Crypto resource to file '%s'.", p_path));
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,10 +42,10 @@ class CryptoKey : public Resource {
 | 
			
		|||
 | 
			
		||||
protected:
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
	static CryptoKey *(*_create)();
 | 
			
		||||
	static CryptoKey *(*_create)(bool p_notify_postinitialize);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static CryptoKey *create();
 | 
			
		||||
	static CryptoKey *create(bool p_notify_postinitialize = true);
 | 
			
		||||
	virtual Error load(const String &p_path, bool p_public_only = false) = 0;
 | 
			
		||||
	virtual Error save(const String &p_path, bool p_public_only = false) = 0;
 | 
			
		||||
	virtual String save_to_string(bool p_public_only = false) = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -58,10 +58,10 @@ class X509Certificate : public Resource {
 | 
			
		|||
 | 
			
		||||
protected:
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
	static X509Certificate *(*_create)();
 | 
			
		||||
	static X509Certificate *(*_create)(bool p_notify_postinitialize);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static X509Certificate *create();
 | 
			
		||||
	static X509Certificate *create(bool p_notify_postinitialize = true);
 | 
			
		||||
	virtual Error load(const String &p_path) = 0;
 | 
			
		||||
	virtual Error load_from_memory(const uint8_t *p_buffer, int p_len) = 0;
 | 
			
		||||
	virtual Error save(const String &p_path) = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -106,10 +106,10 @@ class HMACContext : public RefCounted {
 | 
			
		|||
 | 
			
		||||
protected:
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
	static HMACContext *(*_create)();
 | 
			
		||||
	static HMACContext *(*_create)(bool p_notify_postinitialize);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static HMACContext *create();
 | 
			
		||||
	static HMACContext *create(bool p_notify_postinitialize = true);
 | 
			
		||||
 | 
			
		||||
	virtual Error start(HashingContext::HashType p_hash_type, const PackedByteArray &p_key) = 0;
 | 
			
		||||
	virtual Error update(const PackedByteArray &p_data) = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -124,11 +124,11 @@ class Crypto : public RefCounted {
 | 
			
		|||
 | 
			
		||||
protected:
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
	static Crypto *(*_create)();
 | 
			
		||||
	static Crypto *(*_create)(bool p_notify_postinitialize);
 | 
			
		||||
	static void (*_load_default_certificates)(const String &p_path);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static Crypto *create();
 | 
			
		||||
	static Crypto *create(bool p_notify_postinitialize = true);
 | 
			
		||||
	static void load_default_certificates(const String &p_path);
 | 
			
		||||
 | 
			
		||||
	virtual PackedByteArray generate_random_bytes(int p_bytes) = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -155,6 +155,10 @@ public:
 | 
			
		|||
	virtual void get_recognized_extensions(List<String> *p_extensions) const override;
 | 
			
		||||
	virtual bool handles_type(const String &p_type) const override;
 | 
			
		||||
	virtual String get_resource_type(const String &p_path) const override;
 | 
			
		||||
 | 
			
		||||
	// Treat certificates as text files, do not generate a `*.{crt,key,pub}.uid` file.
 | 
			
		||||
	virtual ResourceUID::ID get_resource_uid(const String &p_path) const override { return ResourceUID::INVALID_ID; }
 | 
			
		||||
	virtual bool has_custom_uid_support() const override { return true; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ResourceFormatSaverCrypto : public ResourceFormatSaver {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -70,7 +70,7 @@ int CryptoCore::RandomGenerator::_entropy_poll(void *p_data, unsigned char *r_bu
 | 
			
		|||
Error CryptoCore::RandomGenerator::init() {
 | 
			
		||||
	int ret = mbedtls_ctr_drbg_seed((mbedtls_ctr_drbg_context *)ctx, mbedtls_entropy_func, (mbedtls_entropy_context *)entropy, nullptr, 0);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(ret, FAILED, " failed\n  ! mbedtls_ctr_drbg_seed returned an error" + itos(ret));
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(ret, FAILED, vformat(" failed\n  ! mbedtls_ctr_drbg_seed returned an error %d.", ret));
 | 
			
		||||
	}
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +78,7 @@ Error CryptoCore::RandomGenerator::init() {
 | 
			
		|||
Error CryptoCore::RandomGenerator::get_random_bytes(uint8_t *r_buffer, size_t p_bytes) {
 | 
			
		||||
	ERR_FAIL_NULL_V(ctx, ERR_UNCONFIGURED);
 | 
			
		||||
	int ret = mbedtls_ctr_drbg_random((mbedtls_ctr_drbg_context *)ctx, r_buffer, p_bytes);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(ret, FAILED, " failed\n  ! mbedtls_ctr_drbg_seed returned an error" + itos(ret));
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(ret, FAILED, vformat(" failed\n  ! mbedtls_ctr_drbg_seed returned an error %d.", ret));
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,7 +37,7 @@ class HashingContext : public RefCounted {
 | 
			
		|||
	GDCLASS(HashingContext, RefCounted);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	enum HashType {
 | 
			
		||||
	enum HashType : int32_t {
 | 
			
		||||
		HASH_MD5,
 | 
			
		||||
		HASH_SHA1,
 | 
			
		||||
		HASH_SHA256
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
from misc.utility.scons_hints import *
 | 
			
		||||
 | 
			
		||||
Import("env")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -147,3 +147,37 @@ bool DebuggerMarshalls::OutputError::deserialize(const Array &p_arr) {
 | 
			
		|||
	CHECK_END(p_arr, idx, "OutputError");
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Array DebuggerMarshalls::serialize_key_shortcut(const Ref<Shortcut> &p_shortcut) {
 | 
			
		||||
	ERR_FAIL_COND_V(p_shortcut.is_null(), Array());
 | 
			
		||||
	Array keys;
 | 
			
		||||
	for (const Ref<InputEvent> ev : p_shortcut->get_events()) {
 | 
			
		||||
		const Ref<InputEventKey> kev = ev;
 | 
			
		||||
		ERR_CONTINUE(kev.is_null());
 | 
			
		||||
		if (kev->get_physical_keycode() != Key::NONE) {
 | 
			
		||||
			keys.push_back(true);
 | 
			
		||||
			keys.push_back(kev->get_physical_keycode_with_modifiers());
 | 
			
		||||
		} else {
 | 
			
		||||
			keys.push_back(false);
 | 
			
		||||
			keys.push_back(kev->get_keycode_with_modifiers());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return keys;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<Shortcut> DebuggerMarshalls::deserialize_key_shortcut(const Array &p_keys) {
 | 
			
		||||
	Array key_events;
 | 
			
		||||
	ERR_FAIL_COND_V(p_keys.size() % 2 != 0, Ref<Shortcut>());
 | 
			
		||||
	for (int i = 0; i < p_keys.size(); i += 2) {
 | 
			
		||||
		ERR_CONTINUE(p_keys[i].get_type() != Variant::BOOL);
 | 
			
		||||
		ERR_CONTINUE(p_keys[i + 1].get_type() != Variant::INT);
 | 
			
		||||
		key_events.push_back(InputEventKey::create_reference((Key)p_keys[i + 1].operator int(), p_keys[i].operator bool()));
 | 
			
		||||
	}
 | 
			
		||||
	if (key_events.is_empty()) {
 | 
			
		||||
		return Ref<Shortcut>();
 | 
			
		||||
	}
 | 
			
		||||
	Ref<Shortcut> shortcut;
 | 
			
		||||
	shortcut.instantiate();
 | 
			
		||||
	shortcut->set_events(key_events);
 | 
			
		||||
	return shortcut;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,7 @@
 | 
			
		|||
#ifndef DEBUGGER_MARSHALLS_H
 | 
			
		||||
#define DEBUGGER_MARSHALLS_H
 | 
			
		||||
 | 
			
		||||
#include "core/input/shortcut.h"
 | 
			
		||||
#include "core/object/script_language.h"
 | 
			
		||||
 | 
			
		||||
struct DebuggerMarshalls {
 | 
			
		||||
| 
						 | 
				
			
			@ -68,6 +69,9 @@ struct DebuggerMarshalls {
 | 
			
		|||
		Array serialize();
 | 
			
		||||
		bool deserialize(const Array &p_arr);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	static Array serialize_key_shortcut(const Ref<Shortcut> &p_shortcut);
 | 
			
		||||
	static Ref<Shortcut> deserialize_key_shortcut(const Array &p_keys);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // DEBUGGER_MARSHALLS_H
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,12 +46,12 @@ HashMap<String, EngineDebugger::CreatePeerFunc> EngineDebugger::protocols;
 | 
			
		|||
void (*EngineDebugger::allow_focus_steal_fn)();
 | 
			
		||||
 | 
			
		||||
void EngineDebugger::register_profiler(const StringName &p_name, const Profiler &p_func) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(profilers.has(p_name), "Profiler already registered: " + p_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(profilers.has(p_name), vformat("Profiler already registered: '%s'.", p_name));
 | 
			
		||||
	profilers.insert(p_name, p_func);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EngineDebugger::unregister_profiler(const StringName &p_name) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + p_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(!profilers.has(p_name), vformat("Profiler not registered: '%s'.", p_name));
 | 
			
		||||
	Profiler &p = profilers[p_name];
 | 
			
		||||
	if (p.active && p.toggle) {
 | 
			
		||||
		p.toggle(p.data, false, Array());
 | 
			
		||||
| 
						 | 
				
			
			@ -61,22 +61,22 @@ void EngineDebugger::unregister_profiler(const StringName &p_name) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void EngineDebugger::register_message_capture(const StringName &p_name, Capture p_func) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(captures.has(p_name), "Capture already registered: " + p_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(captures.has(p_name), vformat("Capture already registered: '%s'.", p_name));
 | 
			
		||||
	captures.insert(p_name, p_func);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EngineDebugger::unregister_message_capture(const StringName &p_name) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!captures.has(p_name), "Capture not registered: " + p_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(!captures.has(p_name), vformat("Capture not registered: '%s'.", p_name));
 | 
			
		||||
	captures.erase(p_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EngineDebugger::register_uri_handler(const String &p_protocol, CreatePeerFunc p_func) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(protocols.has(p_protocol), "Protocol handler already registered: " + p_protocol);
 | 
			
		||||
	ERR_FAIL_COND_MSG(protocols.has(p_protocol), vformat("Protocol handler already registered: '%s'.", p_protocol));
 | 
			
		||||
	protocols.insert(p_protocol, p_func);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!profilers.has(p_name), "Can't change profiler state, no profiler: " + p_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(!profilers.has(p_name), vformat("Can't change profiler state, no profiler: '%s'.", p_name));
 | 
			
		||||
	Profiler &p = profilers[p_name];
 | 
			
		||||
	if (p.toggle) {
 | 
			
		||||
		p.toggle(p.data, p_enabled, p_opts);
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +85,7 @@ void EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, c
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void EngineDebugger::profiler_add_frame_data(const StringName &p_name, const Array &p_data) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!profilers.has(p_name), "Can't add frame data, no profiler: " + p_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(!profilers.has(p_name), vformat("Can't add frame data, no profiler: '%s'.", p_name));
 | 
			
		||||
	Profiler &p = profilers[p_name];
 | 
			
		||||
	if (p.add) {
 | 
			
		||||
		p.add(p.data, p_data);
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +106,7 @@ bool EngineDebugger::has_capture(const StringName &p_name) {
 | 
			
		|||
 | 
			
		||||
Error EngineDebugger::capture_parse(const StringName &p_name, const String &p_msg, const Array &p_args, bool &r_captured) {
 | 
			
		||||
	r_captured = false;
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!captures.has(p_name), ERR_UNCONFIGURED, "Capture not registered: " + p_name);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!captures.has(p_name), ERR_UNCONFIGURED, vformat("Capture not registered: '%s'.", p_name));
 | 
			
		||||
	const Capture &cap = captures[p_name];
 | 
			
		||||
	return cap.capture(cap.data, p_msg, p_args, r_captured);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -163,8 +163,8 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, co
 | 
			
		|||
 | 
			
		||||
	for (int i = 0; i < p_breakpoints.size(); i++) {
 | 
			
		||||
		const String &bp = p_breakpoints[i];
 | 
			
		||||
		int sp = bp.rfind(":");
 | 
			
		||||
		ERR_CONTINUE_MSG(sp == -1, "Invalid breakpoint: '" + bp + "', expected file:line format.");
 | 
			
		||||
		int sp = bp.rfind_char(':');
 | 
			
		||||
		ERR_CONTINUE_MSG(sp == -1, vformat("Invalid breakpoint: '%s', expected file:line format.", bp));
 | 
			
		||||
 | 
			
		||||
		singleton_script_debugger->insert_breakpoint(bp.substr(sp + 1, bp.length()).to_int(), bp.substr(0, sp));
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,7 +36,6 @@
 | 
			
		|||
#include "core/templates/hash_map.h"
 | 
			
		||||
#include "core/templates/vector.h"
 | 
			
		||||
#include "core/variant/array.h"
 | 
			
		||||
#include "core/variant/variant.h"
 | 
			
		||||
 | 
			
		||||
class RemoteDebuggerPeer;
 | 
			
		||||
class ScriptDebugger;
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +105,7 @@ public:
 | 
			
		|||
	_FORCE_INLINE_ static EngineDebugger *get_singleton() { return singleton; }
 | 
			
		||||
	_FORCE_INLINE_ static bool is_active() { return singleton != nullptr && script_debugger != nullptr; }
 | 
			
		||||
 | 
			
		||||
	_FORCE_INLINE_ static ScriptDebugger *get_script_debugger() { return script_debugger; };
 | 
			
		||||
	_FORCE_INLINE_ static ScriptDebugger *get_script_debugger() { return script_debugger; }
 | 
			
		||||
 | 
			
		||||
	static void initialize(const String &p_uri, bool p_skip_breakpoints, const Vector<String> &p_breakpoints, void (*p_allow_focus_steal_fn)());
 | 
			
		||||
	static void deinitialize();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -171,7 +171,7 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
 | 
			
		|||
 | 
			
		||||
			} else {
 | 
			
		||||
				String key_value = line.get_slicec(' ', 1);
 | 
			
		||||
				int value_pos = key_value.find("=");
 | 
			
		||||
				int value_pos = key_value.find_char('=');
 | 
			
		||||
 | 
			
		||||
				if (value_pos < 0) {
 | 
			
		||||
					print_line("Error: Invalid set format. Use: set key=value");
 | 
			
		||||
| 
						 | 
				
			
			@ -208,10 +208,10 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
 | 
			
		|||
			print_variables(members, values, variable_prefix);
 | 
			
		||||
 | 
			
		||||
		} else if (line.begins_with("p") || line.begins_with("print")) {
 | 
			
		||||
			if (line.get_slice_count(" ") <= 1) {
 | 
			
		||||
				print_line("Usage: print <expre>");
 | 
			
		||||
			if (line.find_char(' ') < 0) {
 | 
			
		||||
				print_line("Usage: print <expression>");
 | 
			
		||||
			} else {
 | 
			
		||||
				String expr = line.get_slicec(' ', 2);
 | 
			
		||||
				String expr = line.split(" ", true, 1)[1];
 | 
			
		||||
				String res = script_lang->debug_parse_stack_level_expression(current_frame, expr);
 | 
			
		||||
				print_line(res);
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -344,7 +344,7 @@ Pair<String, int> LocalDebugger::to_breakpoint(const String &p_line) {
 | 
			
		|||
	String breakpoint_part = p_line.get_slicec(' ', 1);
 | 
			
		||||
	Pair<String, int> breakpoint;
 | 
			
		||||
 | 
			
		||||
	int last_colon = breakpoint_part.rfind(":");
 | 
			
		||||
	int last_colon = breakpoint_part.rfind_char(':');
 | 
			
		||||
	if (last_colon < 0) {
 | 
			
		||||
		print_line("Error: Invalid breakpoint format. Expected [source:line]");
 | 
			
		||||
		return breakpoint;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,7 @@
 | 
			
		|||
#include "core/debugger/script_debugger.h"
 | 
			
		||||
#include "core/input/input.h"
 | 
			
		||||
#include "core/io/resource_loader.h"
 | 
			
		||||
#include "core/math/expression.h"
 | 
			
		||||
#include "core/object/script_language.h"
 | 
			
		||||
#include "core/os/os.h"
 | 
			
		||||
#include "servers/display_server.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +79,7 @@ public:
 | 
			
		|||
		for (int i = 0; i < custom_monitor_names.size(); i++) {
 | 
			
		||||
			Variant monitor_value = performance->call("get_custom_monitor", custom_monitor_names[i]);
 | 
			
		||||
			if (!monitor_value.is_num()) {
 | 
			
		||||
				ERR_PRINT("Value of custom monitor '" + String(custom_monitor_names[i]) + "' is not a number");
 | 
			
		||||
				ERR_PRINT(vformat("Value of custom monitor '%s' is not a number.", String(custom_monitor_names[i])));
 | 
			
		||||
				arr[i + max] = Variant();
 | 
			
		||||
			} else {
 | 
			
		||||
				arr[i + max] = monitor_value;
 | 
			
		||||
| 
						 | 
				
			
			@ -337,7 +338,7 @@ void RemoteDebugger::_send_stack_vars(List<String> &p_names, List<Variant> &p_va
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
Error RemoteDebugger::_try_capture(const String &p_msg, const Array &p_data, bool &r_captured) {
 | 
			
		||||
	const int idx = p_msg.find(":");
 | 
			
		||||
	const int idx = p_msg.find_char(':');
 | 
			
		||||
	r_captured = false;
 | 
			
		||||
	if (idx < 0) { // No prefix, unknown message.
 | 
			
		||||
		return OK;
 | 
			
		||||
| 
						 | 
				
			
			@ -529,11 +530,46 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
 | 
			
		|||
			} else if (command == "set_skip_breakpoints") {
 | 
			
		||||
				ERR_FAIL_COND(data.is_empty());
 | 
			
		||||
				script_debugger->set_skip_breakpoints(data[0]);
 | 
			
		||||
			} else if (command == "evaluate") {
 | 
			
		||||
				String expression_str = data[0];
 | 
			
		||||
				int frame = data[1];
 | 
			
		||||
 | 
			
		||||
				ScriptInstance *breaked_instance = script_debugger->get_break_language()->debug_get_stack_level_instance(frame);
 | 
			
		||||
				if (!breaked_instance) {
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				List<String> locals;
 | 
			
		||||
				List<Variant> local_vals;
 | 
			
		||||
 | 
			
		||||
				script_debugger->get_break_language()->debug_get_stack_level_locals(frame, &locals, &local_vals);
 | 
			
		||||
				ERR_FAIL_COND(locals.size() != local_vals.size());
 | 
			
		||||
 | 
			
		||||
				PackedStringArray locals_vector;
 | 
			
		||||
				for (const String &S : locals) {
 | 
			
		||||
					locals_vector.append(S);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				Array local_vals_array;
 | 
			
		||||
				for (const Variant &V : local_vals) {
 | 
			
		||||
					local_vals_array.append(V);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				Expression expression;
 | 
			
		||||
				expression.parse(expression_str, locals_vector);
 | 
			
		||||
				const Variant return_val = expression.execute(local_vals_array, breaked_instance->get_owner());
 | 
			
		||||
 | 
			
		||||
				DebuggerMarshalls::ScriptStackVariable stvar;
 | 
			
		||||
				stvar.name = expression_str;
 | 
			
		||||
				stvar.value = return_val;
 | 
			
		||||
				stvar.type = 3;
 | 
			
		||||
 | 
			
		||||
				send_message("evaluation_return", stvar.serialize());
 | 
			
		||||
			} else {
 | 
			
		||||
				bool captured = false;
 | 
			
		||||
				ERR_CONTINUE(_try_capture(command, data, captured) != OK);
 | 
			
		||||
				if (!captured) {
 | 
			
		||||
					WARN_PRINT("Unknown message received from debugger: " + command);
 | 
			
		||||
					WARN_PRINT(vformat("Unknown message received from debugger: %s.", command));
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -574,7 +610,7 @@ void RemoteDebugger::poll_events(bool p_is_idle) {
 | 
			
		|||
		ERR_CONTINUE(arr[1].get_type() != Variant::ARRAY);
 | 
			
		||||
 | 
			
		||||
		const String cmd = arr[0];
 | 
			
		||||
		const int idx = cmd.find(":");
 | 
			
		||||
		const int idx = cmd.find_char(':');
 | 
			
		||||
		bool parsed = false;
 | 
			
		||||
		if (idx < 0) { // Not prefix, use scripts capture.
 | 
			
		||||
			capture_parse("core", cmd, arr[1], parsed);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -144,9 +144,8 @@ void RemoteDebuggerPeerTCP::_read_in() {
 | 
			
		|||
			Error err = decode_variant(var, buf, in_pos, &read);
 | 
			
		||||
			ERR_CONTINUE(read != in_pos || err != OK);
 | 
			
		||||
			ERR_CONTINUE_MSG(var.get_type() != Variant::ARRAY, "Malformed packet received, not an Array.");
 | 
			
		||||
			mutex.lock();
 | 
			
		||||
			MutexLock lock(mutex);
 | 
			
		||||
			in_queue.push_back(var);
 | 
			
		||||
			mutex.unlock();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -174,12 +173,12 @@ Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_po
 | 
			
		|||
		} else {
 | 
			
		||||
			const int ms = waits[i];
 | 
			
		||||
			OS::get_singleton()->delay_usec(ms * 1000);
 | 
			
		||||
			print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec.");
 | 
			
		||||
			print_verbose("Remote Debugger: Connection failed with status: '" + String::num_int64(tcp_client->get_status()) + "', retrying in " + String::num_int64(ms) + " msec.");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
 | 
			
		||||
		ERR_PRINT("Remote Debugger: Unable to connect. Status: " + String::num(tcp_client->get_status()) + ".");
 | 
			
		||||
		ERR_PRINT(vformat("Remote Debugger: Unable to connect. Status: %s.", String::num_int64(tcp_client->get_status())));
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
	connected = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -224,8 +223,8 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) {
 | 
			
		|||
	String debug_host = p_uri.replace("tcp://", "");
 | 
			
		||||
	uint16_t debug_port = 6007;
 | 
			
		||||
 | 
			
		||||
	if (debug_host.contains(":")) {
 | 
			
		||||
		int sep_pos = debug_host.rfind(":");
 | 
			
		||||
	if (debug_host.contains_char(':')) {
 | 
			
		||||
		int sep_pos = debug_host.rfind_char(':');
 | 
			
		||||
		debug_port = debug_host.substr(sep_pos + 1).to_int();
 | 
			
		||||
		debug_host = debug_host.substr(0, sep_pos);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,6 @@
 | 
			
		|||
#include "core/object/script_language.h"
 | 
			
		||||
#include "core/string/string_name.h"
 | 
			
		||||
#include "core/templates/hash_set.h"
 | 
			
		||||
#include "core/templates/rb_map.h"
 | 
			
		||||
#include "core/templates/vector.h"
 | 
			
		||||
 | 
			
		||||
class ScriptDebugger {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,8 @@
 | 
			
		|||
String DocData::get_default_value_string(const Variant &p_value) {
 | 
			
		||||
	if (p_value.get_type() == Variant::ARRAY) {
 | 
			
		||||
		return Variant(Array(p_value, 0, StringName(), Variant())).get_construct_string().replace("\n", " ");
 | 
			
		||||
	} else if (p_value.get_type() == Variant::DICTIONARY) {
 | 
			
		||||
		return Variant(Dictionary(p_value, 0, StringName(), Variant(), 0, StringName(), Variant())).get_construct_string().replace("\n", " ");
 | 
			
		||||
	} else {
 | 
			
		||||
		return p_value.get_construct_string().replace("\n", " ");
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -57,6 +59,8 @@ void DocData::return_doc_from_retinfo(DocData::MethodDoc &p_method, const Proper
 | 
			
		|||
		p_method.return_type = p_retinfo.class_name;
 | 
			
		||||
	} else if (p_retinfo.type == Variant::ARRAY && p_retinfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
 | 
			
		||||
		p_method.return_type = p_retinfo.hint_string + "[]";
 | 
			
		||||
	} else if (p_retinfo.type == Variant::DICTIONARY && p_retinfo.hint == PROPERTY_HINT_DICTIONARY_TYPE) {
 | 
			
		||||
		p_method.return_type = "Dictionary[" + p_retinfo.hint_string.replace(";", ", ") + "]";
 | 
			
		||||
	} else if (p_retinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
 | 
			
		||||
		p_method.return_type = p_retinfo.hint_string;
 | 
			
		||||
	} else if (p_retinfo.type == Variant::NIL && p_retinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
 | 
			
		||||
| 
						 | 
				
			
			@ -89,6 +93,8 @@ void DocData::argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const
 | 
			
		|||
		p_argument.type = p_arginfo.class_name;
 | 
			
		||||
	} else if (p_arginfo.type == Variant::ARRAY && p_arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
 | 
			
		||||
		p_argument.type = p_arginfo.hint_string + "[]";
 | 
			
		||||
	} else if (p_arginfo.type == Variant::DICTIONARY && p_arginfo.hint == PROPERTY_HINT_DICTIONARY_TYPE) {
 | 
			
		||||
		p_argument.type = "Dictionary[" + p_arginfo.hint_string.replace(";", ", ") + "]";
 | 
			
		||||
	} else if (p_arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
 | 
			
		||||
		p_argument.type = p_arginfo.hint_string;
 | 
			
		||||
	} else if (p_arginfo.type == Variant::NIL) {
 | 
			
		||||
| 
						 | 
				
			
			@ -99,28 +105,6 @@ void DocData::argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DocData::property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_property, const ScriptMemberInfo &p_memberinfo) {
 | 
			
		||||
	p_property.name = p_memberinfo.propinfo.name;
 | 
			
		||||
	p_property.description = p_memberinfo.doc_string;
 | 
			
		||||
 | 
			
		||||
	if (p_memberinfo.propinfo.type == Variant::OBJECT) {
 | 
			
		||||
		p_property.type = p_memberinfo.propinfo.class_name;
 | 
			
		||||
	} else if (p_memberinfo.propinfo.type == Variant::NIL && p_memberinfo.propinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
 | 
			
		||||
		p_property.type = "Variant";
 | 
			
		||||
	} else {
 | 
			
		||||
		p_property.type = Variant::get_type_name(p_memberinfo.propinfo.type);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p_property.setter = p_memberinfo.setter;
 | 
			
		||||
	p_property.getter = p_memberinfo.getter;
 | 
			
		||||
 | 
			
		||||
	if (p_memberinfo.has_default_value && p_memberinfo.default_value.get_type() != Variant::OBJECT) {
 | 
			
		||||
		p_property.default_value = get_default_value_string(p_memberinfo.default_value);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p_property.overridden = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DocData::method_doc_from_methodinfo(DocData::MethodDoc &p_method, const MethodInfo &p_methodinfo, const String &p_desc) {
 | 
			
		||||
	p_method.name = p_methodinfo.name;
 | 
			
		||||
	p_method.description = p_desc;
 | 
			
		||||
| 
						 | 
				
			
			@ -164,14 +148,3 @@ void DocData::method_doc_from_methodinfo(DocData::MethodDoc &p_method, const Met
 | 
			
		|||
		p_method.arguments.push_back(argument);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DocData::constant_doc_from_variant(DocData::ConstantDoc &p_const, const StringName &p_name, const Variant &p_value, const String &p_desc) {
 | 
			
		||||
	p_const.name = p_name;
 | 
			
		||||
	p_const.value = p_value;
 | 
			
		||||
	p_const.is_value_valid = (p_value.get_type() != Variant::OBJECT);
 | 
			
		||||
	p_const.description = p_desc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DocData::signal_doc_from_methodinfo(DocData::MethodDoc &p_signal, const MethodInfo &p_methodinfo, const String &p_desc) {
 | 
			
		||||
	return method_doc_from_methodinfo(p_signal, p_methodinfo, p_desc);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,16 +34,6 @@
 | 
			
		|||
#include "core/io/xml_parser.h"
 | 
			
		||||
#include "core/variant/variant.h"
 | 
			
		||||
 | 
			
		||||
struct ScriptMemberInfo {
 | 
			
		||||
	PropertyInfo propinfo;
 | 
			
		||||
	String doc_string;
 | 
			
		||||
	StringName setter;
 | 
			
		||||
	StringName getter;
 | 
			
		||||
 | 
			
		||||
	bool has_default_value = false;
 | 
			
		||||
	Variant default_value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DocData {
 | 
			
		||||
public:
 | 
			
		||||
	struct ArgumentDoc {
 | 
			
		||||
| 
						 | 
				
			
			@ -276,6 +266,7 @@ public:
 | 
			
		|||
		String name;
 | 
			
		||||
		String value;
 | 
			
		||||
		bool is_value_valid = false;
 | 
			
		||||
		String type;
 | 
			
		||||
		String enumeration;
 | 
			
		||||
		bool is_bitfield = false;
 | 
			
		||||
		String description;
 | 
			
		||||
| 
						 | 
				
			
			@ -302,6 +293,10 @@ public:
 | 
			
		|||
				doc.is_value_valid = p_dict["is_value_valid"];
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (p_dict.has("type")) {
 | 
			
		||||
				doc.type = p_dict["type"];
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (p_dict.has("enumeration")) {
 | 
			
		||||
				doc.enumeration = p_dict["enumeration"];
 | 
			
		||||
				if (p_dict.has("is_bitfield")) {
 | 
			
		||||
| 
						 | 
				
			
			@ -352,6 +347,8 @@ public:
 | 
			
		|||
 | 
			
		||||
			dict["is_value_valid"] = p_doc.is_value_valid;
 | 
			
		||||
 | 
			
		||||
			dict["type"] = p_doc.type;
 | 
			
		||||
 | 
			
		||||
			if (!p_doc.enumeration.is_empty()) {
 | 
			
		||||
				dict["enumeration"] = p_doc.enumeration;
 | 
			
		||||
				dict["is_bitfield"] = p_doc.is_bitfield;
 | 
			
		||||
| 
						 | 
				
			
			@ -522,6 +519,10 @@ public:
 | 
			
		|||
		String type;
 | 
			
		||||
		String data_type;
 | 
			
		||||
		String description;
 | 
			
		||||
		bool is_deprecated = false;
 | 
			
		||||
		String deprecated_message;
 | 
			
		||||
		bool is_experimental = false;
 | 
			
		||||
		String experimental_message;
 | 
			
		||||
		String default_value;
 | 
			
		||||
		String keywords;
 | 
			
		||||
		bool operator<(const ThemeItemDoc &p_theme_item) const {
 | 
			
		||||
| 
						 | 
				
			
			@ -550,6 +551,16 @@ public:
 | 
			
		|||
				doc.description = p_dict["description"];
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (p_dict.has("deprecated")) {
 | 
			
		||||
				doc.is_deprecated = true;
 | 
			
		||||
				doc.deprecated_message = p_dict["deprecated"];
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (p_dict.has("experimental")) {
 | 
			
		||||
				doc.is_experimental = true;
 | 
			
		||||
				doc.experimental_message = p_dict["experimental"];
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (p_dict.has("default_value")) {
 | 
			
		||||
				doc.default_value = p_dict["default_value"];
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -579,6 +590,14 @@ public:
 | 
			
		|||
				dict["description"] = p_doc.description;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (p_doc.is_deprecated) {
 | 
			
		||||
				dict["deprecated"] = p_doc.deprecated_message;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (p_doc.is_experimental) {
 | 
			
		||||
				dict["experimental"] = p_doc.experimental_message;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (!p_doc.default_value.is_empty()) {
 | 
			
		||||
				dict["default_value"] = p_doc.default_value;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -959,10 +978,7 @@ public:
 | 
			
		|||
 | 
			
		||||
	static void return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo);
 | 
			
		||||
	static void argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const PropertyInfo &p_arginfo);
 | 
			
		||||
	static void property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_property, const ScriptMemberInfo &p_memberinfo);
 | 
			
		||||
	static void method_doc_from_methodinfo(DocData::MethodDoc &p_method, const MethodInfo &p_methodinfo, const String &p_desc);
 | 
			
		||||
	static void constant_doc_from_variant(DocData::ConstantDoc &p_const, const StringName &p_name, const Variant &p_value, const String &p_desc);
 | 
			
		||||
	static void signal_doc_from_methodinfo(DocData::MethodDoc &p_signal, const MethodInfo &p_methodinfo, const String &p_desc);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // DOC_DATA_H
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
from misc.utility.scons_hints import *
 | 
			
		||||
 | 
			
		||||
Import("env")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,6 +41,7 @@
 | 
			
		|||
 * - Are added to the Error enum in core/error/error_list.h
 | 
			
		||||
 * - Have a description added to error_names in core/error/error_list.cpp
 | 
			
		||||
 * - Are bound with BIND_CORE_ENUM_CONSTANT() in core/core_constants.cpp
 | 
			
		||||
 * - Have a matching Android version in platform/android/java/lib/src/org/godotengine/godot/error/Error.kt
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
enum Error {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,6 +34,12 @@
 | 
			
		|||
#include "core/os/os.h"
 | 
			
		||||
#include "core/string/ustring.h"
 | 
			
		||||
 | 
			
		||||
// Optional physics interpolation warnings try to include the path to the relevant node.
 | 
			
		||||
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
 | 
			
		||||
#include "core/config/project_settings.h"
 | 
			
		||||
#include "scene/main/node.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static ErrorHandlerList *error_handler_list = nullptr;
 | 
			
		||||
 | 
			
		||||
void add_error_handler(ErrorHandlerList *p_handler) {
 | 
			
		||||
| 
						 | 
				
			
			@ -101,6 +107,28 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co
 | 
			
		|||
	_global_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// For printing errors when we may crash at any point, so we must flush ASAP a lot of lines
 | 
			
		||||
// but we don't want to make it noisy by printing lots of file & line info (because it's already
 | 
			
		||||
// been printing by a preceding _err_print_error).
 | 
			
		||||
void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type) {
 | 
			
		||||
	if (OS::get_singleton()) {
 | 
			
		||||
		OS::get_singleton()->printerr("ERROR: %s\n", p_error.utf8().get_data());
 | 
			
		||||
	} else {
 | 
			
		||||
		// Fallback if errors happen before OS init or after it's destroyed.
 | 
			
		||||
		const char *err_details = p_error.utf8().get_data();
 | 
			
		||||
		fprintf(stderr, "ERROR: %s\n", err_details);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_global_lock();
 | 
			
		||||
	ErrorHandlerList *l = error_handler_list;
 | 
			
		||||
	while (l) {
 | 
			
		||||
		l->errfunc(l->userdata, "", "", 0, p_error.utf8().get_data(), "", false, p_type);
 | 
			
		||||
		l = l->next;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_global_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Errors with message. (All combinations of p_error and p_message as String or char*.)
 | 
			
		||||
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) {
 | 
			
		||||
	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message, p_editor_notify, p_type);
 | 
			
		||||
| 
						 | 
				
			
			@ -128,3 +156,48 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
 | 
			
		|||
void _err_flush_stdout() {
 | 
			
		||||
	fflush(stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Prevent error spam by limiting the warnings to a certain frequency.
 | 
			
		||||
void _physics_interpolation_warning(const char *p_function, const char *p_file, int p_line, ObjectID p_id, const char *p_warn_string) {
 | 
			
		||||
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
 | 
			
		||||
	const uint32_t warn_max = 2048;
 | 
			
		||||
	const uint32_t warn_timeout_seconds = 15;
 | 
			
		||||
 | 
			
		||||
	static uint32_t warn_count = warn_max;
 | 
			
		||||
	static uint32_t warn_timeout = warn_timeout_seconds;
 | 
			
		||||
 | 
			
		||||
	uint32_t time_now = UINT32_MAX;
 | 
			
		||||
 | 
			
		||||
	if (warn_count) {
 | 
			
		||||
		warn_count--;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!warn_count) {
 | 
			
		||||
		time_now = OS::get_singleton()->get_ticks_msec() / 1000;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ((warn_count == 0) && (time_now >= warn_timeout)) {
 | 
			
		||||
		warn_count = warn_max;
 | 
			
		||||
		warn_timeout = time_now + warn_timeout_seconds;
 | 
			
		||||
 | 
			
		||||
		if (GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
 | 
			
		||||
			// UINT64_MAX means unused.
 | 
			
		||||
			if (p_id.operator uint64_t() == UINT64_MAX) {
 | 
			
		||||
				_err_print_error(p_function, p_file, p_line, "[Physics interpolation] " + String(p_warn_string) + " (possibly benign).", false, ERR_HANDLER_WARNING);
 | 
			
		||||
			} else {
 | 
			
		||||
				String node_name;
 | 
			
		||||
				if (p_id.is_valid()) {
 | 
			
		||||
					Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id));
 | 
			
		||||
					if (node && node->is_inside_tree()) {
 | 
			
		||||
						node_name = "\"" + String(node->get_path()) + "\"";
 | 
			
		||||
					} else {
 | 
			
		||||
						node_name = "\"unknown\"";
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				_err_print_error(p_function, p_file, p_line, "[Physics interpolation] " + String(p_warn_string) + ": " + node_name + " (possibly benign).", false, ERR_HANDLER_WARNING);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,7 @@
 | 
			
		|||
#ifndef ERROR_MACROS_H
 | 
			
		||||
#define ERROR_MACROS_H
 | 
			
		||||
 | 
			
		||||
#include "core/object/object_id.h"
 | 
			
		||||
#include "core/typedefs.h"
 | 
			
		||||
 | 
			
		||||
#include <atomic> // We'd normally use safe_refcount.h, but that would cause circular includes.
 | 
			
		||||
| 
						 | 
				
			
			@ -67,10 +68,13 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co
 | 
			
		|||
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
 | 
			
		||||
void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
 | 
			
		||||
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
 | 
			
		||||
void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
 | 
			
		||||
void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool p_editor_notify = false, bool fatal = false);
 | 
			
		||||
void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify = false, bool fatal = false);
 | 
			
		||||
void _err_flush_stdout();
 | 
			
		||||
 | 
			
		||||
void _physics_interpolation_warning(const char *p_function, const char *p_file, int p_line, ObjectID p_id, const char *p_warn_string);
 | 
			
		||||
 | 
			
		||||
#ifdef __GNUC__
 | 
			
		||||
//#define FUNCTION_STR __PRETTY_FUNCTION__ - too annoying
 | 
			
		||||
#define FUNCTION_STR __FUNCTION__
 | 
			
		||||
| 
						 | 
				
			
			@ -832,4 +836,14 @@ void _err_flush_stdout();
 | 
			
		|||
#define DEV_CHECK_ONCE(m_cond)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Physics Interpolation warnings.
 | 
			
		||||
 * These are spam protection warnings.
 | 
			
		||||
 */
 | 
			
		||||
#define PHYSICS_INTERPOLATION_NODE_WARNING(m_object_id, m_string) \
 | 
			
		||||
	_physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, m_object_id, m_string)
 | 
			
		||||
 | 
			
		||||
#define PHYSICS_INTERPOLATION_WARNING(m_string) \
 | 
			
		||||
	_physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, ObjectID(UINT64_MAX), m_string)
 | 
			
		||||
 | 
			
		||||
#endif // ERROR_MACROS_H
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
from misc.utility.scons_hints import *
 | 
			
		||||
 | 
			
		||||
Import("env")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@
 | 
			
		|||
 | 
			
		||||
#include "core/config/engine.h"
 | 
			
		||||
#include "core/core_constants.h"
 | 
			
		||||
#include "core/extension/gdextension_compat_hashes.h"
 | 
			
		||||
#include "core/extension/gdextension_special_compat_hashes.h"
 | 
			
		||||
#include "core/io/file_access.h"
 | 
			
		||||
#include "core/io/json.h"
 | 
			
		||||
#include "core/templates/pair.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -60,6 +60,9 @@ static String get_property_info_type_name(const PropertyInfo &p_info) {
 | 
			
		|||
	if (p_info.type == Variant::ARRAY && (p_info.hint == PROPERTY_HINT_ARRAY_TYPE)) {
 | 
			
		||||
		return String("typedarray::") + p_info.hint_string;
 | 
			
		||||
	}
 | 
			
		||||
	if (p_info.type == Variant::DICTIONARY && (p_info.hint == PROPERTY_HINT_DICTIONARY_TYPE)) {
 | 
			
		||||
		return String("typeddictionary::") + p_info.hint_string;
 | 
			
		||||
	}
 | 
			
		||||
	if (p_info.type == Variant::INT && (p_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM))) {
 | 
			
		||||
		return String("enum::") + String(p_info.class_name);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +88,7 @@ static String get_property_info_type_name(const PropertyInfo &p_info) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static String get_type_meta_name(const GodotTypeInfo::Metadata metadata) {
 | 
			
		||||
	static const char *argmeta[11] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double" };
 | 
			
		||||
	static const char *argmeta[13] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double", "char16", "char32" };
 | 
			
		||||
	return argmeta[metadata];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1014,9 +1017,22 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
 | 
			
		|||
						d2["name"] = String(method_name);
 | 
			
		||||
						d2["is_const"] = (F.flags & METHOD_FLAG_CONST) ? true : false;
 | 
			
		||||
						d2["is_static"] = (F.flags & METHOD_FLAG_STATIC) ? true : false;
 | 
			
		||||
						d2["is_required"] = (F.flags & METHOD_FLAG_VIRTUAL_REQUIRED) ? true : false;
 | 
			
		||||
						d2["is_vararg"] = false;
 | 
			
		||||
						d2["is_virtual"] = true;
 | 
			
		||||
						// virtual functions have no hash since no MethodBind is involved
 | 
			
		||||
						d2["hash"] = mi.get_compatibility_hash();
 | 
			
		||||
 | 
			
		||||
						Vector<uint32_t> compat_hashes = ClassDB::get_virtual_method_compatibility_hashes(class_name, method_name);
 | 
			
		||||
						Array compatibility;
 | 
			
		||||
						if (compat_hashes.size()) {
 | 
			
		||||
							for (int i = 0; i < compat_hashes.size(); i++) {
 | 
			
		||||
								compatibility.push_back(compat_hashes[i]);
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						if (compatibility.size() > 0) {
 | 
			
		||||
							d2["hash_compatibility"] = compatibility;
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						bool has_return = mi.return_val.type != Variant::NIL || (mi.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT);
 | 
			
		||||
						if (has_return) {
 | 
			
		||||
							PropertyInfo pinfo = mi.return_val;
 | 
			
		||||
| 
						 | 
				
			
			@ -1090,7 +1106,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
 | 
			
		|||
						}
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
						GDExtensionCompatHashes::get_legacy_hashes(class_name, method_name, compatibility);
 | 
			
		||||
						GDExtensionSpecialCompatHashes::get_legacy_hashes(class_name, method_name, compatibility);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
						if (compatibility.size() > 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1201,7 +1217,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
 | 
			
		|||
					if (F.name.begins_with("_")) {
 | 
			
		||||
						continue; //hidden property
 | 
			
		||||
					}
 | 
			
		||||
					if (F.name.contains("/")) {
 | 
			
		||||
					if (F.name.contains_char('/')) {
 | 
			
		||||
						// Ignore properties with '/' (slash) in the name. These are only meant for use in the inspector.
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
| 
						 | 
				
			
			@ -1359,7 +1375,7 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_
 | 
			
		|||
		return true; // May just not have this array and its still good. Probably added recently.
 | 
			
		||||
	}
 | 
			
		||||
	bool failed = false;
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!p_new_api.has(p_base_array), false, "New API lacks base array: " + p_base_array);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!p_new_api.has(p_base_array), false, vformat("New API lacks base array: %s", p_base_array));
 | 
			
		||||
	Array new_api = p_new_api[p_base_array];
 | 
			
		||||
	HashMap<String, Dictionary> new_api_assoc;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1367,6 +1383,9 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_
 | 
			
		|||
		Dictionary elem = var;
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(!elem.has(p_name_field), false, vformat("Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug.", base_array, p_name_field));
 | 
			
		||||
		String name = elem[p_name_field];
 | 
			
		||||
		if (name.is_valid_float()) {
 | 
			
		||||
			name = name.trim_suffix(".0"); // Make "integers" stringified as integers.
 | 
			
		||||
		}
 | 
			
		||||
		if (p_compare_operators && elem.has("right_type")) {
 | 
			
		||||
			name += " " + String(elem["right_type"]);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -1382,6 +1401,9 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_
 | 
			
		|||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		String name = old_elem[p_name_field];
 | 
			
		||||
		if (name.is_valid_float()) {
 | 
			
		||||
			name = name.trim_suffix(".0"); // Make "integers" stringified as integers.
 | 
			
		||||
		}
 | 
			
		||||
		if (p_compare_operators && old_elem.has("right_type")) {
 | 
			
		||||
			name += " " + String(old_elem["right_type"]);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -1463,8 +1485,8 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_
 | 
			
		|||
 | 
			
		||||
		if (p_compare_hashes) {
 | 
			
		||||
			if (!old_elem.has("hash")) {
 | 
			
		||||
				if (old_elem.has("is_virtual") && bool(old_elem["is_virtual"]) && !new_elem.has("hash")) {
 | 
			
		||||
					continue; // No hash for virtual methods, go on.
 | 
			
		||||
				if (old_elem.has("is_virtual") && bool(old_elem["is_virtual"]) && !old_elem.has("hash")) {
 | 
			
		||||
					continue; // Virtual methods didn't use to have hashes, so skip check if it's missing in the old file.
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				failed = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -1511,7 +1533,7 @@ static bool compare_sub_dict_array(HashSet<String> &r_removed_classes_registered
 | 
			
		|||
		return true; // May just not have this array and its still good. Probably added recently or optional.
 | 
			
		||||
	}
 | 
			
		||||
	bool failed = false;
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!p_new_api.has(p_outer), false, "New API lacks base array: " + p_outer);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!p_new_api.has(p_outer), false, vformat("New API lacks base array: %s", p_outer));
 | 
			
		||||
	Array new_api = p_new_api[p_outer];
 | 
			
		||||
	HashMap<String, Dictionary> new_api_assoc;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,11 +32,9 @@
 | 
			
		|||
#include "gdextension.compat.inc"
 | 
			
		||||
 | 
			
		||||
#include "core/config/project_settings.h"
 | 
			
		||||
#include "core/io/dir_access.h"
 | 
			
		||||
#include "core/object/class_db.h"
 | 
			
		||||
#include "core/object/method_bind.h"
 | 
			
		||||
#include "core/os/os.h"
 | 
			
		||||
#include "core/version.h"
 | 
			
		||||
#include "gdextension_library_loader.h"
 | 
			
		||||
#include "gdextension_manager.h"
 | 
			
		||||
 | 
			
		||||
extern void gdextension_setup_interface();
 | 
			
		||||
| 
						 | 
				
			
			@ -48,146 +46,6 @@ String GDExtension::get_extension_list_config_file() {
 | 
			
		|||
	return ProjectSettings::get_singleton()->get_project_data_path().path_join("extension_list.cfg");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Vector<SharedObject> GDExtension::find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature) {
 | 
			
		||||
	Vector<SharedObject> dependencies_shared_objects;
 | 
			
		||||
	if (p_config->has_section("dependencies")) {
 | 
			
		||||
		List<String> config_dependencies;
 | 
			
		||||
		p_config->get_section_keys("dependencies", &config_dependencies);
 | 
			
		||||
 | 
			
		||||
		for (const String &dependency : config_dependencies) {
 | 
			
		||||
			Vector<String> dependency_tags = dependency.split(".");
 | 
			
		||||
			bool all_tags_met = true;
 | 
			
		||||
			for (int i = 0; i < dependency_tags.size(); i++) {
 | 
			
		||||
				String tag = dependency_tags[i].strip_edges();
 | 
			
		||||
				if (!p_has_feature(tag)) {
 | 
			
		||||
					all_tags_met = false;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (all_tags_met) {
 | 
			
		||||
				Dictionary dependency_value = p_config->get_value("dependencies", dependency);
 | 
			
		||||
				for (const Variant *key = dependency_value.next(nullptr); key; key = dependency_value.next(key)) {
 | 
			
		||||
					String dependency_path = *key;
 | 
			
		||||
					String target_path = dependency_value[*key];
 | 
			
		||||
					if (dependency_path.is_relative_path()) {
 | 
			
		||||
						dependency_path = p_path.get_base_dir().path_join(dependency_path);
 | 
			
		||||
					}
 | 
			
		||||
					dependencies_shared_objects.push_back(SharedObject(dependency_path, dependency_tags, target_path));
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dependencies_shared_objects;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String GDExtension::find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags) {
 | 
			
		||||
	// First, check the explicit libraries.
 | 
			
		||||
	if (p_config->has_section("libraries")) {
 | 
			
		||||
		List<String> libraries;
 | 
			
		||||
		p_config->get_section_keys("libraries", &libraries);
 | 
			
		||||
 | 
			
		||||
		// Iterate the libraries, finding the best matching tags.
 | 
			
		||||
		String best_library_path;
 | 
			
		||||
		Vector<String> best_library_tags;
 | 
			
		||||
		for (const String &E : libraries) {
 | 
			
		||||
			Vector<String> tags = E.split(".");
 | 
			
		||||
			bool all_tags_met = true;
 | 
			
		||||
			for (int i = 0; i < tags.size(); i++) {
 | 
			
		||||
				String tag = tags[i].strip_edges();
 | 
			
		||||
				if (!p_has_feature(tag)) {
 | 
			
		||||
					all_tags_met = false;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (all_tags_met && tags.size() > best_library_tags.size()) {
 | 
			
		||||
				best_library_path = p_config->get_value("libraries", E);
 | 
			
		||||
				best_library_tags = tags;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!best_library_path.is_empty()) {
 | 
			
		||||
			if (best_library_path.is_relative_path()) {
 | 
			
		||||
				best_library_path = p_path.get_base_dir().path_join(best_library_path);
 | 
			
		||||
			}
 | 
			
		||||
			if (r_tags != nullptr) {
 | 
			
		||||
				r_tags->append_array(best_library_tags);
 | 
			
		||||
			}
 | 
			
		||||
			return best_library_path;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Second, try to autodetect
 | 
			
		||||
	String autodetect_library_prefix;
 | 
			
		||||
	if (p_config->has_section_key("configuration", "autodetect_library_prefix")) {
 | 
			
		||||
		autodetect_library_prefix = p_config->get_value("configuration", "autodetect_library_prefix");
 | 
			
		||||
	}
 | 
			
		||||
	if (!autodetect_library_prefix.is_empty()) {
 | 
			
		||||
		String autodetect_path = autodetect_library_prefix;
 | 
			
		||||
		if (autodetect_path.is_relative_path()) {
 | 
			
		||||
			autodetect_path = p_path.get_base_dir().path_join(autodetect_path);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Find the folder and file parts of the prefix.
 | 
			
		||||
		String folder;
 | 
			
		||||
		String file_prefix;
 | 
			
		||||
		if (DirAccess::dir_exists_absolute(autodetect_path)) {
 | 
			
		||||
			folder = autodetect_path;
 | 
			
		||||
		} else if (DirAccess::dir_exists_absolute(autodetect_path.get_base_dir())) {
 | 
			
		||||
			folder = autodetect_path.get_base_dir();
 | 
			
		||||
			file_prefix = autodetect_path.get_file();
 | 
			
		||||
		} else {
 | 
			
		||||
			ERR_FAIL_V_MSG(String(), vformat("Error in extension: %s. Could not find folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Open the folder.
 | 
			
		||||
		Ref<DirAccess> dir = DirAccess::open(folder);
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(!dir.is_valid(), String(), vformat("Error in extension: %s. Could not open folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
 | 
			
		||||
 | 
			
		||||
		// Iterate the files and check the prefixes, finding the best matching file.
 | 
			
		||||
		String best_file;
 | 
			
		||||
		Vector<String> best_file_tags;
 | 
			
		||||
		dir->list_dir_begin();
 | 
			
		||||
		String file_name = dir->_get_next();
 | 
			
		||||
		while (file_name != "") {
 | 
			
		||||
			if (!dir->current_is_dir() && file_name.begins_with(file_prefix)) {
 | 
			
		||||
				// Check if the files matches all requested feature tags.
 | 
			
		||||
				String tags_str = file_name.trim_prefix(file_prefix);
 | 
			
		||||
				tags_str = tags_str.trim_suffix(tags_str.get_extension());
 | 
			
		||||
 | 
			
		||||
				Vector<String> tags = tags_str.split(".", false);
 | 
			
		||||
				bool all_tags_met = true;
 | 
			
		||||
				for (int i = 0; i < tags.size(); i++) {
 | 
			
		||||
					String tag = tags[i].strip_edges();
 | 
			
		||||
					if (!p_has_feature(tag)) {
 | 
			
		||||
						all_tags_met = false;
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// If all tags are found in the feature list, and we found more tags than before, use this file.
 | 
			
		||||
				if (all_tags_met && tags.size() > best_file_tags.size()) {
 | 
			
		||||
					best_file_tags = tags;
 | 
			
		||||
					best_file = file_name;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			file_name = dir->_get_next();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!best_file.is_empty()) {
 | 
			
		||||
			String library_path = folder.path_join(best_file);
 | 
			
		||||
			if (r_tags != nullptr) {
 | 
			
		||||
				r_tags->append_array(best_file_tags);
 | 
			
		||||
			}
 | 
			
		||||
			return library_path;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return String();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class GDExtensionMethodBind : public MethodBind {
 | 
			
		||||
	GDExtensionClassMethodCall call_func;
 | 
			
		||||
	GDExtensionClassMethodValidatedCall validated_call_func;
 | 
			
		||||
| 
						 | 
				
			
			@ -296,7 +154,7 @@ public:
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	virtual bool is_vararg() const override {
 | 
			
		||||
		return false;
 | 
			
		||||
		return vararg;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			@ -382,11 +240,12 @@ public:
 | 
			
		|||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs) {
 | 
			
		||||
	const GDExtensionClassCreationInfo3 class_info3 = {
 | 
			
		||||
	const GDExtensionClassCreationInfo4 class_info4 = {
 | 
			
		||||
		p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
 | 
			
		||||
		p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
 | 
			
		||||
		true, // GDExtensionBool is_exposed;
 | 
			
		||||
		false, // GDExtensionBool is_runtime;
 | 
			
		||||
		nullptr, // GDExtensionConstStringPtr icon_path;
 | 
			
		||||
		p_extension_funcs->set_func, // GDExtensionClassSet set_func;
 | 
			
		||||
		p_extension_funcs->get_func, // GDExtensionClassGet get_func;
 | 
			
		||||
		p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
 | 
			
		||||
| 
						 | 
				
			
			@ -398,29 +257,33 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library
 | 
			
		|||
		p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
 | 
			
		||||
		p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
 | 
			
		||||
		p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
 | 
			
		||||
		p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */
 | 
			
		||||
		nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */
 | 
			
		||||
		p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
 | 
			
		||||
		nullptr, // GDExtensionClassRecreateInstance recreate_instance_func;
 | 
			
		||||
		p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
 | 
			
		||||
		nullptr, // GDExtensionClassGetVirtual get_virtual_func;
 | 
			
		||||
		nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
 | 
			
		||||
		nullptr, // GDExtensionClassCallVirtualWithData call_virtual_func;
 | 
			
		||||
		p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
 | 
			
		||||
		p_extension_funcs->class_userdata, // void *class_userdata;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const ClassCreationDeprecatedInfo legacy = {
 | 
			
		||||
		p_extension_funcs->notification_func, // GDExtensionClassNotification notification_func;
 | 
			
		||||
		p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
 | 
			
		||||
		p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func;
 | 
			
		||||
		p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
 | 
			
		||||
		p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
 | 
			
		||||
		nullptr,
 | 
			
		||||
	};
 | 
			
		||||
	_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy);
 | 
			
		||||
	_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs) {
 | 
			
		||||
	const GDExtensionClassCreationInfo3 class_info3 = {
 | 
			
		||||
	const GDExtensionClassCreationInfo4 class_info4 = {
 | 
			
		||||
		p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
 | 
			
		||||
		p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
 | 
			
		||||
		p_extension_funcs->is_exposed, // GDExtensionBool is_exposed;
 | 
			
		||||
		false, // GDExtensionBool is_runtime;
 | 
			
		||||
		nullptr, // GDExtensionConstStringPtr icon_path;
 | 
			
		||||
		p_extension_funcs->set_func, // GDExtensionClassSet set_func;
 | 
			
		||||
		p_extension_funcs->get_func, // GDExtensionClassGet get_func;
 | 
			
		||||
		p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
 | 
			
		||||
| 
						 | 
				
			
			@ -432,35 +295,77 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar
 | 
			
		|||
		p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
 | 
			
		||||
		p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
 | 
			
		||||
		p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
 | 
			
		||||
		p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */
 | 
			
		||||
		nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */
 | 
			
		||||
		p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
 | 
			
		||||
		p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func;
 | 
			
		||||
		p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
 | 
			
		||||
		p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
 | 
			
		||||
		nullptr, // GDExtensionClassGetVirtual get_virtual_func;
 | 
			
		||||
		nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
 | 
			
		||||
		p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func;
 | 
			
		||||
		p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
 | 
			
		||||
		p_extension_funcs->class_userdata, // void *class_userdata;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const ClassCreationDeprecatedInfo legacy = {
 | 
			
		||||
		nullptr, // GDExtensionClassNotification notification_func;
 | 
			
		||||
		p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
 | 
			
		||||
		p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func;
 | 
			
		||||
		p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
 | 
			
		||||
		p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
 | 
			
		||||
		p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtual get_virtual_func;
 | 
			
		||||
	};
 | 
			
		||||
	_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy);
 | 
			
		||||
	_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy);
 | 
			
		||||
}
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
 | 
			
		||||
void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs) {
 | 
			
		||||
	const GDExtensionClassCreationInfo4 class_info4 = {
 | 
			
		||||
		p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
 | 
			
		||||
		p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
 | 
			
		||||
		p_extension_funcs->is_exposed, // GDExtensionBool is_exposed;
 | 
			
		||||
		p_extension_funcs->is_runtime, // GDExtensionBool is_runtime;
 | 
			
		||||
		nullptr, // GDExtensionConstStringPtr icon_path;
 | 
			
		||||
		p_extension_funcs->set_func, // GDExtensionClassSet set_func;
 | 
			
		||||
		p_extension_funcs->get_func, // GDExtensionClassGet get_func;
 | 
			
		||||
		p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
 | 
			
		||||
		p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
 | 
			
		||||
		p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func;
 | 
			
		||||
		p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func;
 | 
			
		||||
		p_extension_funcs->validate_property_func, // GDExtensionClassValidateProperty validate_property_func;
 | 
			
		||||
		p_extension_funcs->notification_func, // GDExtensionClassNotification2 notification_func;
 | 
			
		||||
		p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
 | 
			
		||||
		p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
 | 
			
		||||
		p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
 | 
			
		||||
		nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */
 | 
			
		||||
		p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
 | 
			
		||||
		p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func;
 | 
			
		||||
		nullptr, // GDExtensionClassGetVirtual get_virtual_func;
 | 
			
		||||
		nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
 | 
			
		||||
		p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func;
 | 
			
		||||
		p_extension_funcs->class_userdata, // void *class_userdata;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const ClassCreationDeprecatedInfo legacy = {
 | 
			
		||||
		nullptr, // GDExtensionClassNotification notification_func;
 | 
			
		||||
		nullptr, // GDExtensionClassFreePropertyList free_property_list_func;
 | 
			
		||||
		p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance2 create_instance_func;
 | 
			
		||||
		p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
 | 
			
		||||
		p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
 | 
			
		||||
		p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtual get_virtual_func;
 | 
			
		||||
	};
 | 
			
		||||
	_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
 | 
			
		||||
void GDExtension::_register_extension_class4(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs) {
 | 
			
		||||
	_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, p_extension_funcs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) {
 | 
			
		||||
void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) {
 | 
			
		||||
	GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
 | 
			
		||||
 | 
			
		||||
	StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
 | 
			
		||||
	StringName parent_class_name = *reinterpret_cast<const StringName *>(p_parent_class_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(!String(class_name).is_valid_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier.");
 | 
			
		||||
	ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), "Attempt to register extension class '" + class_name + "', which appears to be already registered.");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!String(class_name).is_valid_unicode_identifier(), vformat("Attempt to register extension class '%s', which is not a valid class identifier.", class_name));
 | 
			
		||||
	ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), vformat("Attempt to register extension class '%s', which appears to be already registered.", class_name));
 | 
			
		||||
 | 
			
		||||
	Extension *parent_extension = nullptr;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -474,7 +379,7 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
 | 
			
		|||
			//inheriting from engine class
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		ERR_FAIL_MSG("Attempt to register an extension class '" + String(class_name) + "' using non-existing parent class '" + String(parent_class_name) + "'.");
 | 
			
		||||
		ERR_FAIL_MSG(vformat("Attempt to register an extension class '%s' using non-existing parent class '%s'.", String(class_name), String(parent_class_name)));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			@ -530,6 +435,10 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
 | 
			
		|||
	if (p_deprecated_funcs) {
 | 
			
		||||
		extension->gdextension.notification = p_deprecated_funcs->notification_func;
 | 
			
		||||
		extension->gdextension.free_property_list = p_deprecated_funcs->free_property_list_func;
 | 
			
		||||
		extension->gdextension.create_instance = p_deprecated_funcs->create_instance_func;
 | 
			
		||||
		extension->gdextension.get_rid = p_deprecated_funcs->get_rid_func;
 | 
			
		||||
		extension->gdextension.get_virtual = p_deprecated_funcs->get_virtual_func;
 | 
			
		||||
		extension->gdextension.get_virtual_call_data = p_deprecated_funcs->get_virtual_call_data_func;
 | 
			
		||||
	}
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
	extension->gdextension.notification2 = p_extension_funcs->notification_func;
 | 
			
		||||
| 
						 | 
				
			
			@ -537,13 +446,12 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
 | 
			
		|||
	extension->gdextension.reference = p_extension_funcs->reference_func;
 | 
			
		||||
	extension->gdextension.unreference = p_extension_funcs->unreference_func;
 | 
			
		||||
	extension->gdextension.class_userdata = p_extension_funcs->class_userdata;
 | 
			
		||||
	extension->gdextension.create_instance = p_extension_funcs->create_instance_func;
 | 
			
		||||
	extension->gdextension.create_instance2 = p_extension_funcs->create_instance_func;
 | 
			
		||||
	extension->gdextension.free_instance = p_extension_funcs->free_instance_func;
 | 
			
		||||
	extension->gdextension.recreate_instance = p_extension_funcs->recreate_instance_func;
 | 
			
		||||
	extension->gdextension.get_virtual = p_extension_funcs->get_virtual_func;
 | 
			
		||||
	extension->gdextension.get_virtual_call_data = p_extension_funcs->get_virtual_call_data_func;
 | 
			
		||||
	extension->gdextension.get_virtual2 = p_extension_funcs->get_virtual_func;
 | 
			
		||||
	extension->gdextension.get_virtual_call_data2 = p_extension_funcs->get_virtual_call_data_func;
 | 
			
		||||
	extension->gdextension.call_virtual_with_data = p_extension_funcs->call_virtual_with_data_func;
 | 
			
		||||
	extension->gdextension.get_rid = p_extension_funcs->get_rid_func;
 | 
			
		||||
 | 
			
		||||
	extension->gdextension.reloadable = self->reloadable;
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			@ -559,6 +467,13 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
	ClassDB::register_extension_class(&extension->gdextension);
 | 
			
		||||
 | 
			
		||||
	if (p_extension_funcs->icon_path != nullptr) {
 | 
			
		||||
		const String icon_path = *reinterpret_cast<const String *>(p_extension_funcs->icon_path);
 | 
			
		||||
		if (!icon_path.is_empty()) {
 | 
			
		||||
			self->class_icon_paths[class_name] = icon_path;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GDExtension::_register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info) {
 | 
			
		||||
| 
						 | 
				
			
			@ -566,7 +481,7 @@ void GDExtension::_register_extension_class_method(GDExtensionClassLibraryPtr p_
 | 
			
		|||
 | 
			
		||||
	StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
 | 
			
		||||
	StringName method_name = *reinterpret_cast<const StringName *>(p_method_info->name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension method '" + String(method_name) + "' for unexisting class '" + class_name + "'.");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension method '%s' for unexisting class '%s'.", String(method_name), class_name));
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	Extension *extension = &self->extension_classes[class_name];
 | 
			
		||||
| 
						 | 
				
			
			@ -616,7 +531,7 @@ void GDExtension::_register_extension_class_integer_constant(GDExtensionClassLib
 | 
			
		|||
	StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
 | 
			
		||||
	StringName enum_name = *reinterpret_cast<const StringName *>(p_enum_name);
 | 
			
		||||
	StringName constant_name = *reinterpret_cast<const StringName *>(p_constant_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension constant '" + constant_name + "' for unexisting class '" + class_name + "'.");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension constant '%s' for unexisting class '%s'.", constant_name, class_name));
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	// If the extension is still marked as reloading, that means it failed to register again.
 | 
			
		||||
| 
						 | 
				
			
			@ -640,7 +555,7 @@ void GDExtension::_register_extension_class_property_indexed(GDExtensionClassLib
 | 
			
		|||
	StringName setter = *reinterpret_cast<const StringName *>(p_setter);
 | 
			
		||||
	StringName getter = *reinterpret_cast<const StringName *>(p_getter);
 | 
			
		||||
	String property_name = *reinterpret_cast<const StringName *>(p_info->name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property '" + property_name + "' for unexisting class '" + class_name + "'.");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension class property '%s' for unexisting class '%s'.", property_name, class_name));
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	// If the extension is still marked as reloading, that means it failed to register again.
 | 
			
		||||
| 
						 | 
				
			
			@ -661,7 +576,7 @@ void GDExtension::_register_extension_class_property_group(GDExtensionClassLibra
 | 
			
		|||
	StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
 | 
			
		||||
	String group_name = *reinterpret_cast<const String *>(p_group_name);
 | 
			
		||||
	String prefix = *reinterpret_cast<const String *>(p_prefix);
 | 
			
		||||
	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property group '" + group_name + "' for unexisting class '" + class_name + "'.");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension class property group '%s' for unexisting class '%s'.", group_name, class_name));
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	// If the extension is still marked as reloading, that means it failed to register again.
 | 
			
		||||
| 
						 | 
				
			
			@ -680,7 +595,7 @@ void GDExtension::_register_extension_class_property_subgroup(GDExtensionClassLi
 | 
			
		|||
	StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
 | 
			
		||||
	String subgroup_name = *reinterpret_cast<const String *>(p_subgroup_name);
 | 
			
		||||
	String prefix = *reinterpret_cast<const String *>(p_prefix);
 | 
			
		||||
	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property subgroup '" + subgroup_name + "' for unexisting class '" + class_name + "'.");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension class property subgroup '%s' for unexisting class '%s'.", subgroup_name, class_name));
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	// If the extension is still marked as reloading, that means it failed to register again.
 | 
			
		||||
| 
						 | 
				
			
			@ -698,7 +613,7 @@ void GDExtension::_register_extension_class_signal(GDExtensionClassLibraryPtr p_
 | 
			
		|||
 | 
			
		||||
	StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
 | 
			
		||||
	StringName signal_name = *reinterpret_cast<const StringName *>(p_signal_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class signal '" + signal_name + "' for unexisting class '" + class_name + "'.");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to register extension class signal '%s' for unexisting class '%s'.", signal_name, class_name));
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	// If the extension is still marked as reloading, that means it failed to register again.
 | 
			
		||||
| 
						 | 
				
			
			@ -721,7 +636,7 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra
 | 
			
		|||
	GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
 | 
			
		||||
 | 
			
		||||
	StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
 | 
			
		||||
	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to unregister unexisting extension class '" + class_name + "'.");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), vformat("Attempt to unregister unexisting extension class '%s'.", class_name));
 | 
			
		||||
 | 
			
		||||
	Extension *ext = &self->extension_classes[class_name];
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			@ -729,7 +644,7 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra
 | 
			
		|||
		self->_clear_extension(ext);
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
	ERR_FAIL_COND_MSG(ext->gdextension.children.size(), "Attempt to unregister class '" + class_name + "' while other extension classes inherit from it.");
 | 
			
		||||
	ERR_FAIL_COND_MSG(ext->gdextension.children.size(), vformat("Attempt to unregister class '%s' while other extension classes inherit from it.", class_name));
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	ClassDB::unregister_extension_class(class_name, !ext->is_reloading);
 | 
			
		||||
| 
						 | 
				
			
			@ -755,80 +670,54 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra
 | 
			
		|||
void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringPtr r_path) {
 | 
			
		||||
	GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
 | 
			
		||||
 | 
			
		||||
	memnew_placement(r_path, String(self->library_path));
 | 
			
		||||
	Ref<GDExtensionLibraryLoader> library_loader = self->loader;
 | 
			
		||||
	String library_path;
 | 
			
		||||
	if (library_loader.is_valid()) {
 | 
			
		||||
		library_path = library_loader->library_path;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memnew_placement(r_path, String(library_path));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HashMap<StringName, GDExtensionInterfaceFunctionPtr> GDExtension::gdextension_interface_functions;
 | 
			
		||||
 | 
			
		||||
void GDExtension::register_interface_function(const StringName &p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), "Attempt to register interface function '" + p_function_name + "', which appears to be already registered.");
 | 
			
		||||
	ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), vformat("Attempt to register interface function '%s', which appears to be already registered.", p_function_name));
 | 
			
		||||
	gdextension_interface_functions.insert(p_function_name, p_function_pointer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(const StringName &p_function_name) {
 | 
			
		||||
	GDExtensionInterfaceFunctionPtr *function = gdextension_interface_functions.getptr(p_function_name);
 | 
			
		||||
	ERR_FAIL_NULL_V_MSG(function, nullptr, "Attempt to get non-existent interface function: " + String(p_function_name) + ".");
 | 
			
		||||
	ERR_FAIL_NULL_V_MSG(function, nullptr, vformat("Attempt to get non-existent interface function: '%s'.", String(p_function_name)));
 | 
			
		||||
	return *function;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol, Vector<SharedObject> *p_dependencies) {
 | 
			
		||||
	String abs_path = ProjectSettings::get_singleton()->globalize_path(p_path);
 | 
			
		||||
Error GDExtension::open_library(const String &p_path, const Ref<GDExtensionLoader> &p_loader) {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(p_loader.is_null(), FAILED, "Can't open GDExtension without a loader.");
 | 
			
		||||
	loader = p_loader;
 | 
			
		||||
 | 
			
		||||
	Vector<String> abs_dependencies_paths;
 | 
			
		||||
	if (p_dependencies != nullptr && !p_dependencies->is_empty()) {
 | 
			
		||||
		for (const SharedObject &dependency : *p_dependencies) {
 | 
			
		||||
			abs_dependencies_paths.push_back(ProjectSettings::get_singleton()->globalize_path(dependency.path));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	Error err = loader->open_library(p_path);
 | 
			
		||||
 | 
			
		||||
	String actual_lib_path;
 | 
			
		||||
	OS::GDExtensionData data = {
 | 
			
		||||
		true, // also_set_library_path
 | 
			
		||||
		&actual_lib_path, // r_resolved_path
 | 
			
		||||
		Engine::get_singleton()->is_editor_hint(), // generate_temp_files
 | 
			
		||||
		&abs_dependencies_paths, // library_dependencies
 | 
			
		||||
	};
 | 
			
		||||
	Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, &data);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, vformat("GDExtension dynamic library not found: '%s'.", p_path));
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Can't open GDExtension dynamic library: '%s'.", p_path));
 | 
			
		||||
 | 
			
		||||
	if (actual_lib_path.get_file() != abs_path.get_file()) {
 | 
			
		||||
		// If temporary files are generated, let's change the library path to point at the original,
 | 
			
		||||
		// because that's what we want to check to see if it's changed.
 | 
			
		||||
		library_path = actual_lib_path.get_base_dir().path_join(p_path.get_file());
 | 
			
		||||
	} else {
 | 
			
		||||
		library_path = actual_lib_path;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + abs_path);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + abs_path);
 | 
			
		||||
 | 
			
		||||
	void *entry_funcptr = nullptr;
 | 
			
		||||
 | 
			
		||||
	err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, p_entry_symbol, entry_funcptr, false);
 | 
			
		||||
	err = loader->initialize(&gdextension_get_proc_address, this, &initialization);
 | 
			
		||||
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		ERR_PRINT("GDExtension entry point '" + p_entry_symbol + "' not found in library " + abs_path);
 | 
			
		||||
		OS::get_singleton()->close_dynamic_library(library);
 | 
			
		||||
		// Errors already logged in initialize().
 | 
			
		||||
		loader->close_library();
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr;
 | 
			
		||||
	GDExtensionBool ret = initialization_function(&gdextension_get_proc_address, this, &initialization);
 | 
			
		||||
 | 
			
		||||
	if (ret) {
 | 
			
		||||
	level_initialized = -1;
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
	} else {
 | 
			
		||||
		ERR_PRINT("GDExtension initialization function '" + p_entry_symbol + "' returned an error.");
 | 
			
		||||
		OS::get_singleton()->close_dynamic_library(library);
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GDExtension::close_library() {
 | 
			
		||||
	ERR_FAIL_NULL(library);
 | 
			
		||||
	OS::get_singleton()->close_dynamic_library(library);
 | 
			
		||||
	ERR_FAIL_COND(!is_library_open());
 | 
			
		||||
	loader->close_library();
 | 
			
		||||
 | 
			
		||||
	library = nullptr;
 | 
			
		||||
	class_icon_paths.clear();
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			@ -837,16 +726,16 @@ void GDExtension::close_library() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
bool GDExtension::is_library_open() const {
 | 
			
		||||
	return library != nullptr;
 | 
			
		||||
	return loader.is_valid() && loader->is_library_open();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GDExtension::InitializationLevel GDExtension::get_minimum_library_initialization_level() const {
 | 
			
		||||
	ERR_FAIL_NULL_V(library, INITIALIZATION_LEVEL_CORE);
 | 
			
		||||
	ERR_FAIL_COND_V(!is_library_open(), INITIALIZATION_LEVEL_CORE);
 | 
			
		||||
	return InitializationLevel(initialization.minimum_initialization_level);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GDExtension::initialize_library(InitializationLevel p_level) {
 | 
			
		||||
	ERR_FAIL_NULL(library);
 | 
			
		||||
	ERR_FAIL_COND(!is_library_open());
 | 
			
		||||
	ERR_FAIL_COND_MSG(p_level <= int32_t(level_initialized), vformat("Level '%d' must be higher than the current level '%d'", p_level, level_initialized));
 | 
			
		||||
 | 
			
		||||
	level_initialized = int32_t(p_level);
 | 
			
		||||
| 
						 | 
				
			
			@ -856,7 +745,7 @@ void GDExtension::initialize_library(InitializationLevel p_level) {
 | 
			
		|||
	initialization.initialize(initialization.userdata, GDExtensionInitializationLevel(p_level));
 | 
			
		||||
}
 | 
			
		||||
void GDExtension::deinitialize_library(InitializationLevel p_level) {
 | 
			
		||||
	ERR_FAIL_NULL(library);
 | 
			
		||||
	ERR_FAIL_COND(!is_library_open());
 | 
			
		||||
	ERR_FAIL_COND(p_level > int32_t(level_initialized));
 | 
			
		||||
 | 
			
		||||
	level_initialized = int32_t(p_level) - 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -880,7 +769,7 @@ GDExtension::GDExtension() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
GDExtension::~GDExtension() {
 | 
			
		||||
	if (library != nullptr) {
 | 
			
		||||
	if (is_library_open()) {
 | 
			
		||||
		close_library();
 | 
			
		||||
	}
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			@ -897,8 +786,9 @@ void GDExtension::initialize_gdextensions() {
 | 
			
		|||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
	register_interface_function("classdb_register_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class);
 | 
			
		||||
	register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2);
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
	register_interface_function("classdb_register_extension_class3", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class3);
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
	register_interface_function("classdb_register_extension_class4", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class4);
 | 
			
		||||
	register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method);
 | 
			
		||||
	register_interface_function("classdb_register_extension_class_virtual_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_virtual_method);
 | 
			
		||||
	register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant);
 | 
			
		||||
| 
						 | 
				
			
			@ -918,142 +808,15 @@ void GDExtension::finalize_gdextensions() {
 | 
			
		|||
Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, Ref<GDExtension> &p_extension) {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(p_extension.is_valid() && p_extension->is_library_open(), ERR_ALREADY_IN_USE, "Cannot load GDExtension resource into already opened library.");
 | 
			
		||||
 | 
			
		||||
	Ref<ConfigFile> config;
 | 
			
		||||
	config.instantiate();
 | 
			
		||||
	GDExtensionManager *extension_manager = GDExtensionManager::get_singleton();
 | 
			
		||||
 | 
			
		||||
	Error err = config->load(p_path);
 | 
			
		||||
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		ERR_PRINT("Error loading GDExtension configuration file: " + p_path);
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!config->has_section_key("configuration", "entry_symbol")) {
 | 
			
		||||
		ERR_PRINT("GDExtension configuration file must contain a \"configuration/entry_symbol\" key: " + p_path);
 | 
			
		||||
		return ERR_INVALID_DATA;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	String entry_symbol = config->get_value("configuration", "entry_symbol");
 | 
			
		||||
 | 
			
		||||
	uint32_t compatibility_minimum[3] = { 0, 0, 0 };
 | 
			
		||||
	if (config->has_section_key("configuration", "compatibility_minimum")) {
 | 
			
		||||
		String compat_string = config->get_value("configuration", "compatibility_minimum");
 | 
			
		||||
		Vector<int> parts = compat_string.split_ints(".");
 | 
			
		||||
		for (int i = 0; i < parts.size(); i++) {
 | 
			
		||||
			if (i >= 3) {
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			if (parts[i] >= 0) {
 | 
			
		||||
				compatibility_minimum[i] = parts[i];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		ERR_PRINT("GDExtension configuration file must contain a \"configuration/compatibility_minimum\" key: " + p_path);
 | 
			
		||||
		return ERR_INVALID_DATA;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (compatibility_minimum[0] < 4 || (compatibility_minimum[0] == 4 && compatibility_minimum[1] == 0)) {
 | 
			
		||||
		ERR_PRINT(vformat("GDExtension's compatibility_minimum (%d.%d.%d) must be at least 4.1.0: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
 | 
			
		||||
		return ERR_INVALID_DATA;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool compatible = true;
 | 
			
		||||
	// Check version lexicographically.
 | 
			
		||||
	if (VERSION_MAJOR != compatibility_minimum[0]) {
 | 
			
		||||
		compatible = VERSION_MAJOR > compatibility_minimum[0];
 | 
			
		||||
	} else if (VERSION_MINOR != compatibility_minimum[1]) {
 | 
			
		||||
		compatible = VERSION_MINOR > compatibility_minimum[1];
 | 
			
		||||
	} else {
 | 
			
		||||
		compatible = VERSION_PATCH >= compatibility_minimum[2];
 | 
			
		||||
	}
 | 
			
		||||
	if (!compatible) {
 | 
			
		||||
		ERR_PRINT(vformat("GDExtension only compatible with Godot version %d.%d.%d or later: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
 | 
			
		||||
		return ERR_INVALID_DATA;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Optionally check maximum compatibility.
 | 
			
		||||
	if (config->has_section_key("configuration", "compatibility_maximum")) {
 | 
			
		||||
		uint32_t compatibility_maximum[3] = { 0, 0, 0 };
 | 
			
		||||
		String compat_string = config->get_value("configuration", "compatibility_maximum");
 | 
			
		||||
		Vector<int> parts = compat_string.split_ints(".");
 | 
			
		||||
		for (int i = 0; i < 3; i++) {
 | 
			
		||||
			if (i < parts.size() && parts[i] >= 0) {
 | 
			
		||||
				compatibility_maximum[i] = parts[i];
 | 
			
		||||
			} else {
 | 
			
		||||
				// If a version part is missing, set the maximum to an arbitrary high value.
 | 
			
		||||
				compatibility_maximum[i] = 9999;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		compatible = true;
 | 
			
		||||
		if (VERSION_MAJOR != compatibility_maximum[0]) {
 | 
			
		||||
			compatible = VERSION_MAJOR < compatibility_maximum[0];
 | 
			
		||||
		} else if (VERSION_MINOR != compatibility_maximum[1]) {
 | 
			
		||||
			compatible = VERSION_MINOR < compatibility_maximum[1];
 | 
			
		||||
		}
 | 
			
		||||
#if VERSION_PATCH
 | 
			
		||||
		// #if check to avoid -Wtype-limits warning when 0.
 | 
			
		||||
		else {
 | 
			
		||||
			compatible = VERSION_PATCH <= compatibility_maximum[2];
 | 
			
		||||
		}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		if (!compatible) {
 | 
			
		||||
			ERR_PRINT(vformat("GDExtension only compatible with Godot version %s or earlier: %s", compat_string, p_path));
 | 
			
		||||
			return ERR_INVALID_DATA;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	String library_path = GDExtension::find_extension_library(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
 | 
			
		||||
 | 
			
		||||
	if (library_path.is_empty()) {
 | 
			
		||||
		const String os_arch = OS::get_singleton()->get_name().to_lower() + "." + Engine::get_singleton()->get_architecture_name();
 | 
			
		||||
		ERR_PRINT(vformat("No GDExtension library found for current OS and architecture (%s) in configuration file: %s", os_arch, p_path));
 | 
			
		||||
		return ERR_FILE_NOT_FOUND;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool is_static_library = library_path.ends_with(".a") || library_path.ends_with(".xcframework");
 | 
			
		||||
 | 
			
		||||
	if (!library_path.is_resource_file() && !library_path.is_absolute_path()) {
 | 
			
		||||
		library_path = p_path.get_base_dir().path_join(library_path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (p_extension.is_null()) {
 | 
			
		||||
		p_extension.instantiate();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	p_extension->set_reloadable(config->get_value("configuration", "reloadable", false) && Engine::get_singleton()->is_extension_reloading_enabled());
 | 
			
		||||
 | 
			
		||||
	p_extension->update_last_modified_time(
 | 
			
		||||
			FileAccess::get_modified_time(p_path),
 | 
			
		||||
			FileAccess::get_modified_time(library_path));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	Vector<SharedObject> library_dependencies = GDExtension::find_extension_dependencies(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
 | 
			
		||||
	err = p_extension->open_library(is_static_library ? String() : library_path, entry_symbol, &library_dependencies);
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		// Unreference the extension so that this loading can be considered a failure.
 | 
			
		||||
		p_extension.unref();
 | 
			
		||||
 | 
			
		||||
		// Errors already logged in open_library()
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Handle icons if any are specified.
 | 
			
		||||
	if (config->has_section("icons")) {
 | 
			
		||||
		List<String> keys;
 | 
			
		||||
		config->get_section_keys("icons", &keys);
 | 
			
		||||
		for (const String &key : keys) {
 | 
			
		||||
			String icon_path = config->get_value("icons", key);
 | 
			
		||||
			if (icon_path.is_relative_path()) {
 | 
			
		||||
				icon_path = p_path.get_base_dir().path_join(icon_path);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			p_extension->class_icon_paths[key] = icon_path;
 | 
			
		||||
		}
 | 
			
		||||
	GDExtensionManager::LoadStatus status = extension_manager->load_extension(p_path);
 | 
			
		||||
	if (status != GDExtensionManager::LOAD_STATUS_OK && status != GDExtensionManager::LOAD_STATUS_ALREADY_LOADED) {
 | 
			
		||||
		// Errors already logged in load_extension().
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p_extension = extension_manager->get_extension(p_path);
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1094,16 +857,7 @@ String GDExtensionResourceLoader::get_resource_type(const String &p_path) const
 | 
			
		|||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
bool GDExtension::has_library_changed() const {
 | 
			
		||||
	// Check only that the last modified time is different (rather than checking
 | 
			
		||||
	// that it's newer) since some OS's (namely Windows) will preserve the modified
 | 
			
		||||
	// time by default when copying files.
 | 
			
		||||
	if (FileAccess::get_modified_time(get_path()) != resource_last_modified_time) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	if (FileAccess::get_modified_time(library_path) != library_last_modified_time) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
	return loader->has_library_changed();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GDExtension::prepare_reload() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,13 +31,11 @@
 | 
			
		|||
#ifndef GDEXTENSION_H
 | 
			
		||||
#define GDEXTENSION_H
 | 
			
		||||
 | 
			
		||||
#include <functional>
 | 
			
		||||
 | 
			
		||||
#include "core/extension/gdextension_interface.h"
 | 
			
		||||
#include "core/extension/gdextension_loader.h"
 | 
			
		||||
#include "core/io/config_file.h"
 | 
			
		||||
#include "core/io/resource_loader.h"
 | 
			
		||||
#include "core/object/ref_counted.h"
 | 
			
		||||
#include "core/os/shared_object.h"
 | 
			
		||||
 | 
			
		||||
class GDExtensionMethodBind;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -46,8 +44,8 @@ class GDExtension : public Resource {
 | 
			
		|||
 | 
			
		||||
	friend class GDExtensionManager;
 | 
			
		||||
 | 
			
		||||
	void *library = nullptr; // pointer if valid,
 | 
			
		||||
	String library_path;
 | 
			
		||||
	Ref<GDExtensionLoader> loader;
 | 
			
		||||
 | 
			
		||||
	bool reloadable = false;
 | 
			
		||||
 | 
			
		||||
	struct Extension {
 | 
			
		||||
| 
						 | 
				
			
			@ -72,15 +70,20 @@ class GDExtension : public Resource {
 | 
			
		|||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
		GDExtensionClassNotification notification_func = nullptr;
 | 
			
		||||
		GDExtensionClassFreePropertyList free_property_list_func = nullptr;
 | 
			
		||||
		GDExtensionClassCreateInstance create_instance_func = nullptr;
 | 
			
		||||
		GDExtensionClassGetRID get_rid_func = nullptr;
 | 
			
		||||
		GDExtensionClassGetVirtual get_virtual_func = nullptr;
 | 
			
		||||
		GDExtensionClassGetVirtualCallData get_virtual_call_data_func = nullptr;
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
	static void _register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs);
 | 
			
		||||
	static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs);
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
	static void _register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs);
 | 
			
		||||
	static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr);
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
	static void _register_extension_class4(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs);
 | 
			
		||||
	static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr);
 | 
			
		||||
	static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info);
 | 
			
		||||
	static void _register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info);
 | 
			
		||||
	static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield);
 | 
			
		||||
| 
						 | 
				
			
			@ -96,8 +99,6 @@ class GDExtension : public Resource {
 | 
			
		|||
	int32_t level_initialized = -1;
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	uint64_t resource_last_modified_time = 0;
 | 
			
		||||
	uint64_t library_last_modified_time = 0;
 | 
			
		||||
	bool is_reloading = false;
 | 
			
		||||
	Vector<GDExtensionMethodBind *> invalid_methods;
 | 
			
		||||
	Vector<ObjectID> instance_bindings;
 | 
			
		||||
| 
						 | 
				
			
			@ -124,11 +125,12 @@ public:
 | 
			
		|||
	virtual bool editor_can_reload_from_file() override { return false; } // Reloading is handled in a special way.
 | 
			
		||||
 | 
			
		||||
	static String get_extension_list_config_file();
 | 
			
		||||
	static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr);
 | 
			
		||||
	static Vector<SharedObject> find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature);
 | 
			
		||||
 | 
			
		||||
	Error open_library(const String &p_path, const String &p_entry_symbol, Vector<SharedObject> *p_dependencies = nullptr);
 | 
			
		||||
	const Ref<GDExtensionLoader> get_loader() const { return loader; }
 | 
			
		||||
 | 
			
		||||
	Error open_library(const String &p_path, const Ref<GDExtensionLoader> &p_loader);
 | 
			
		||||
	void close_library();
 | 
			
		||||
	bool is_library_open() const;
 | 
			
		||||
 | 
			
		||||
	enum InitializationLevel {
 | 
			
		||||
		INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
 | 
			
		||||
| 
						 | 
				
			
			@ -146,17 +148,11 @@ protected:
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	bool is_library_open() const;
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	bool is_reloadable() const { return reloadable; }
 | 
			
		||||
	void set_reloadable(bool p_reloadable) { reloadable = p_reloadable; }
 | 
			
		||||
 | 
			
		||||
	bool has_library_changed() const;
 | 
			
		||||
	void update_last_modified_time(uint64_t p_resource_last_modified_time, uint64_t p_library_last_modified_time) {
 | 
			
		||||
		resource_last_modified_time = p_resource_last_modified_time;
 | 
			
		||||
		library_last_modified_time = p_library_last_modified_time;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void track_instance_binding(Object *p_object);
 | 
			
		||||
	void untrack_instance_binding(Object *p_object);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,8 +32,9 @@
 | 
			
		|||
 | 
			
		||||
#include "core/config/engine.h"
 | 
			
		||||
#include "core/extension/gdextension.h"
 | 
			
		||||
#include "core/extension/gdextension_compat_hashes.h"
 | 
			
		||||
#include "core/extension/gdextension_special_compat_hashes.h"
 | 
			
		||||
#include "core/io/file_access.h"
 | 
			
		||||
#include "core/io/image.h"
 | 
			
		||||
#include "core/io/xml_parser.h"
 | 
			
		||||
#include "core/object/class_db.h"
 | 
			
		||||
#include "core/object/script_language_extension.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -507,6 +508,14 @@ static GDExtensionBool gdextension_variant_has_key(GDExtensionConstVariantPtr p_
 | 
			
		|||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GDObjectInstanceID gdextension_variant_get_object_instance_id(GDExtensionConstVariantPtr p_self) {
 | 
			
		||||
	const Variant *self = (const Variant *)p_self;
 | 
			
		||||
	if (likely(self->get_type() == Variant::OBJECT)) {
 | 
			
		||||
		return self->operator ObjectID();
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gdextension_variant_get_type_name(GDExtensionVariantType p_type, GDExtensionUninitializedVariantPtr r_ret) {
 | 
			
		||||
	String name = Variant::get_type_name((Variant::Type)p_type);
 | 
			
		||||
	memnew_placement(r_ret, String(name));
 | 
			
		||||
| 
						 | 
				
			
			@ -691,6 +700,91 @@ static GDExtensionTypeFromVariantConstructorFunc gdextension_get_variant_to_type
 | 
			
		|||
	ERR_FAIL_V_MSG(nullptr, "Getting Variant conversion function with invalid type");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GDExtensionVariantGetInternalPtrFunc gdextension_variant_get_ptr_internal_getter(GDExtensionVariantType p_type) {
 | 
			
		||||
	switch (p_type) {
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_BOOL:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<bool *(*)(Variant *)>(VariantInternal::get_bool));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_INT:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<int64_t *(*)(Variant *)>(VariantInternal::get_int));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_FLOAT:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<double *(*)(Variant *)>(VariantInternal::get_float));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_STRING:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<String *(*)(Variant *)>(VariantInternal::get_string));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_VECTOR2:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Vector2 *(*)(Variant *)>(VariantInternal::get_vector2));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_VECTOR2I:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Vector2i *(*)(Variant *)>(VariantInternal::get_vector2i));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_RECT2:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Rect2 *(*)(Variant *)>(VariantInternal::get_rect2));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_RECT2I:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Rect2i *(*)(Variant *)>(VariantInternal::get_rect2i));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_VECTOR3:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Vector3 *(*)(Variant *)>(VariantInternal::get_vector3));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_VECTOR3I:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Vector3i *(*)(Variant *)>(VariantInternal::get_vector3i));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_TRANSFORM2D:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Transform2D *(*)(Variant *)>(VariantInternal::get_transform2d));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_VECTOR4:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Vector4 *(*)(Variant *)>(VariantInternal::get_vector4));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_VECTOR4I:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Vector4i *(*)(Variant *)>(VariantInternal::get_vector4i));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_PLANE:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Plane *(*)(Variant *)>(VariantInternal::get_plane));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_QUATERNION:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Quaternion *(*)(Variant *)>(VariantInternal::get_quaternion));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_AABB:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<AABB *(*)(Variant *)>(VariantInternal::get_aabb));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_BASIS:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Basis *(*)(Variant *)>(VariantInternal::get_basis));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_TRANSFORM3D:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Transform3D *(*)(Variant *)>(VariantInternal::get_transform));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_PROJECTION:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Projection *(*)(Variant *)>(VariantInternal::get_projection));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_COLOR:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Color *(*)(Variant *)>(VariantInternal::get_color));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_STRING_NAME:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<StringName *(*)(Variant *)>(VariantInternal::get_string_name));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_NODE_PATH:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<NodePath *(*)(Variant *)>(VariantInternal::get_node_path));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_RID:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<RID *(*)(Variant *)>(VariantInternal::get_rid));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_OBJECT:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Object **(*)(Variant *)>(VariantInternal::get_object));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_CALLABLE:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Callable *(*)(Variant *)>(VariantInternal::get_callable));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_SIGNAL:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Signal *(*)(Variant *)>(VariantInternal::get_signal));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_DICTIONARY:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Dictionary *(*)(Variant *)>(VariantInternal::get_dictionary));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_ARRAY:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<Array *(*)(Variant *)>(VariantInternal::get_array));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_PACKED_BYTE_ARRAY:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedByteArray *(*)(Variant *)>(VariantInternal::get_byte_array));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_PACKED_INT32_ARRAY:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedInt32Array *(*)(Variant *)>(VariantInternal::get_int32_array));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_PACKED_INT64_ARRAY:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedInt64Array *(*)(Variant *)>(VariantInternal::get_int64_array));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_PACKED_FLOAT32_ARRAY:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedFloat32Array *(*)(Variant *)>(VariantInternal::get_float32_array));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_PACKED_FLOAT64_ARRAY:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedFloat64Array *(*)(Variant *)>(VariantInternal::get_float64_array));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_PACKED_STRING_ARRAY:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedStringArray *(*)(Variant *)>(VariantInternal::get_string_array));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR2_ARRAY:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedVector2Array *(*)(Variant *)>(VariantInternal::get_vector2_array));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR3_ARRAY:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedVector3Array *(*)(Variant *)>(VariantInternal::get_vector3_array));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_PACKED_COLOR_ARRAY:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedColorArray *(*)(Variant *)>(VariantInternal::get_color_array));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR4_ARRAY:
 | 
			
		||||
			return reinterpret_cast<GDExtensionVariantGetInternalPtrFunc>(static_cast<PackedVector4Array *(*)(Variant *)>(VariantInternal::get_vector4_array));
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_NIL:
 | 
			
		||||
		case GDEXTENSION_VARIANT_TYPE_VARIANT_MAX:
 | 
			
		||||
			ERR_FAIL_V_MSG(nullptr, "Getting Variant get internal pointer function with invalid type.");
 | 
			
		||||
	}
 | 
			
		||||
	ERR_FAIL_V_MSG(nullptr, "Getting Variant get internal pointer function with invalid type.");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ptrcalls
 | 
			
		||||
static GDExtensionPtrOperatorEvaluator gdextension_variant_get_ptr_operator_evaluator(GDExtensionVariantOperator p_operator, GDExtensionVariantType p_type_a, GDExtensionVariantType p_type_b) {
 | 
			
		||||
	return (GDExtensionPtrOperatorEvaluator)Variant::get_ptr_operator_evaluator(Variant::Operator(p_operator), Variant::Type(p_type_a), Variant::Type(p_type_b));
 | 
			
		||||
| 
						 | 
				
			
			@ -1199,6 +1293,15 @@ static GDExtensionVariantPtr gdextension_dictionary_operator_index_const(GDExten
 | 
			
		|||
	return (GDExtensionVariantPtr)&self->operator[](*(const Variant *)p_key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gdextension_dictionary_set_typed(GDExtensionTypePtr p_self, GDExtensionVariantType p_key_type, GDExtensionConstStringNamePtr p_key_class_name, GDExtensionConstVariantPtr p_key_script, GDExtensionVariantType p_value_type, GDExtensionConstStringNamePtr p_value_class_name, GDExtensionConstVariantPtr p_value_script) {
 | 
			
		||||
	Dictionary *self = reinterpret_cast<Dictionary *>(p_self);
 | 
			
		||||
	const StringName *key_class_name = reinterpret_cast<const StringName *>(p_key_class_name);
 | 
			
		||||
	const Variant *key_script = reinterpret_cast<const Variant *>(p_key_script);
 | 
			
		||||
	const StringName *value_class_name = reinterpret_cast<const StringName *>(p_value_class_name);
 | 
			
		||||
	const Variant *value_script = reinterpret_cast<const Variant *>(p_value_script);
 | 
			
		||||
	self->set_typed((uint32_t)p_key_type, *key_class_name, *key_script, (uint32_t)p_value_type, *value_class_name, *value_script);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* OBJECT API */
 | 
			
		||||
 | 
			
		||||
static void gdextension_object_method_bind_call(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1299,7 +1402,7 @@ static void gdextension_object_call_script_method(GDExtensionObjectPtr p_object,
 | 
			
		|||
	const StringName method = *reinterpret_cast<const StringName *>(p_method);
 | 
			
		||||
	const Variant **args = (const Variant **)p_args;
 | 
			
		||||
 | 
			
		||||
	Callable::CallError error;
 | 
			
		||||
	Callable::CallError error; // TODO: Check `error`?
 | 
			
		||||
	memnew_placement(r_return, Variant);
 | 
			
		||||
	*(Variant *)r_return = o->callp(method, args, p_argument_count, error);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1501,24 +1604,31 @@ static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionC
 | 
			
		|||
	// If lookup failed, see if this is one of the broken hashes from issue #81386.
 | 
			
		||||
	if (!mb && exists) {
 | 
			
		||||
		uint32_t mapped_hash;
 | 
			
		||||
		if (GDExtensionCompatHashes::lookup_current_hash(classname, methodname, p_hash, &mapped_hash)) {
 | 
			
		||||
		if (GDExtensionSpecialCompatHashes::lookup_current_hash(classname, methodname, p_hash, &mapped_hash)) {
 | 
			
		||||
			mb = ClassDB::get_method_with_compatibility(classname, methodname, mapped_hash, &exists);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (!mb && exists) {
 | 
			
		||||
		ERR_PRINT("Method '" + classname + "." + methodname + "' has changed and no compatibility fallback has been provided. Please open an issue.");
 | 
			
		||||
		ERR_PRINT(vformat("Method '%s.%s' has changed and no compatibility fallback has been provided. Please open an issue.", classname, methodname));
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
	ERR_FAIL_NULL_V(mb, nullptr);
 | 
			
		||||
	return (GDExtensionMethodBindPtr)mb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
static GDExtensionObjectPtr gdextension_classdb_construct_object(GDExtensionConstStringNamePtr p_classname) {
 | 
			
		||||
	const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
 | 
			
		||||
	return (GDExtensionObjectPtr)ClassDB::instantiate_no_placeholders(classname);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static GDExtensionObjectPtr gdextension_classdb_construct_object2(GDExtensionConstStringNamePtr p_classname) {
 | 
			
		||||
	const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
 | 
			
		||||
	return (GDExtensionObjectPtr)ClassDB::instantiate_without_postinitialization(classname);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *gdextension_classdb_get_class_tag(GDExtensionConstStringNamePtr p_classname) {
 | 
			
		||||
	const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
 | 
			
		||||
| 
						 | 
				
			
			@ -1594,11 +1704,13 @@ void gdextension_setup_interface() {
 | 
			
		|||
	REGISTER_INTERFACE_FUNC(variant_has_method);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(variant_has_member);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(variant_has_key);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(variant_get_object_instance_id);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(variant_get_type_name);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(variant_can_convert);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(variant_can_convert_strict);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(get_variant_from_type_constructor);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(get_variant_to_type_constructor);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(variant_get_ptr_internal_getter);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(variant_get_ptr_operator_evaluator);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(variant_get_ptr_builtin_method);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(variant_get_ptr_constructor);
 | 
			
		||||
| 
						 | 
				
			
			@ -1672,6 +1784,7 @@ void gdextension_setup_interface() {
 | 
			
		|||
	REGISTER_INTERFACE_FUNC(array_set_typed);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(dictionary_operator_index);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(dictionary_operator_index_const);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(dictionary_set_typed);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(object_method_bind_call);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(object_method_bind_ptrcall);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(object_destroy);
 | 
			
		||||
| 
						 | 
				
			
			@ -1701,7 +1814,10 @@ void gdextension_setup_interface() {
 | 
			
		|||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(callable_custom_create2);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(callable_custom_get_userdata);
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(classdb_construct_object);
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(classdb_construct_object2);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(classdb_get_class_tag);
 | 
			
		||||
	REGISTER_INTERFACE_FUNC(editor_add_plugin);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -198,6 +198,7 @@ typedef struct {
 | 
			
		|||
 | 
			
		||||
typedef void (*GDExtensionVariantFromTypeConstructorFunc)(GDExtensionUninitializedVariantPtr, GDExtensionTypePtr);
 | 
			
		||||
typedef void (*GDExtensionTypeFromVariantConstructorFunc)(GDExtensionUninitializedTypePtr, GDExtensionVariantPtr);
 | 
			
		||||
typedef void *(*GDExtensionVariantGetInternalPtrFunc)(GDExtensionVariantPtr);
 | 
			
		||||
typedef void (*GDExtensionPtrOperatorEvaluator)(GDExtensionConstTypePtr p_left, GDExtensionConstTypePtr p_right, GDExtensionTypePtr r_result);
 | 
			
		||||
typedef void (*GDExtensionPtrBuiltInMethod)(GDExtensionTypePtr p_base, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_return, int p_argument_count);
 | 
			
		||||
typedef void (*GDExtensionPtrConstructor)(GDExtensionUninitializedTypePtr p_base, const GDExtensionConstTypePtr *p_args);
 | 
			
		||||
| 
						 | 
				
			
			@ -268,10 +269,13 @@ typedef void (*GDExtensionClassReference)(GDExtensionClassInstancePtr p_instance
 | 
			
		|||
typedef void (*GDExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance);
 | 
			
		||||
typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
 | 
			
		||||
typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_class_userdata);
 | 
			
		||||
typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance2)(void *p_class_userdata, GDExtensionBool p_notify_postinitialize);
 | 
			
		||||
typedef void (*GDExtensionClassFreeInstance)(void *p_class_userdata, GDExtensionClassInstancePtr p_instance);
 | 
			
		||||
typedef GDExtensionClassInstancePtr (*GDExtensionClassRecreateInstance)(void *p_class_userdata, GDExtensionObjectPtr p_object);
 | 
			
		||||
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
 | 
			
		||||
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual2)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash);
 | 
			
		||||
typedef void *(*GDExtensionClassGetVirtualCallData)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
 | 
			
		||||
typedef void *(*GDExtensionClassGetVirtualCallData2)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash);
 | 
			
		||||
typedef void (*GDExtensionClassCallVirtualWithData)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, void *p_virtual_call_userdata, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -292,7 +296,7 @@ typedef struct {
 | 
			
		|||
	GDExtensionClassGetVirtual get_virtual_func; // Queries a virtual function by name and returns a callback to invoke the requested virtual function.
 | 
			
		||||
	GDExtensionClassGetRID get_rid_func;
 | 
			
		||||
	void *class_userdata; // Per-class user data, later accessible in instance bindings.
 | 
			
		||||
} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo3 instead.
 | 
			
		||||
} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo4 instead.
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
	GDExtensionBool is_virtual;
 | 
			
		||||
| 
						 | 
				
			
			@ -325,7 +329,7 @@ typedef struct {
 | 
			
		|||
	GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
 | 
			
		||||
	GDExtensionClassGetRID get_rid_func;
 | 
			
		||||
	void *class_userdata; // Per-class user data, later accessible in instance bindings.
 | 
			
		||||
} GDExtensionClassCreationInfo2; // Deprecated. Use GDExtensionClassCreationInfo3 instead.
 | 
			
		||||
} GDExtensionClassCreationInfo2; // Deprecated. Use GDExtensionClassCreationInfo4 instead.
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
	GDExtensionBool is_virtual;
 | 
			
		||||
| 
						 | 
				
			
			@ -359,7 +363,41 @@ typedef struct {
 | 
			
		|||
	GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
 | 
			
		||||
	GDExtensionClassGetRID get_rid_func;
 | 
			
		||||
	void *class_userdata; // Per-class user data, later accessible in instance bindings.
 | 
			
		||||
} GDExtensionClassCreationInfo3;
 | 
			
		||||
} GDExtensionClassCreationInfo3; // Deprecated. Use GDExtensionClassCreationInfo4 instead.
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
	GDExtensionBool is_virtual;
 | 
			
		||||
	GDExtensionBool is_abstract;
 | 
			
		||||
	GDExtensionBool is_exposed;
 | 
			
		||||
	GDExtensionBool is_runtime;
 | 
			
		||||
	GDExtensionConstStringPtr icon_path;
 | 
			
		||||
	GDExtensionClassSet set_func;
 | 
			
		||||
	GDExtensionClassGet get_func;
 | 
			
		||||
	GDExtensionClassGetPropertyList get_property_list_func;
 | 
			
		||||
	GDExtensionClassFreePropertyList2 free_property_list_func;
 | 
			
		||||
	GDExtensionClassPropertyCanRevert property_can_revert_func;
 | 
			
		||||
	GDExtensionClassPropertyGetRevert property_get_revert_func;
 | 
			
		||||
	GDExtensionClassValidateProperty validate_property_func;
 | 
			
		||||
	GDExtensionClassNotification2 notification_func;
 | 
			
		||||
	GDExtensionClassToString to_string_func;
 | 
			
		||||
	GDExtensionClassReference reference_func;
 | 
			
		||||
	GDExtensionClassUnreference unreference_func;
 | 
			
		||||
	GDExtensionClassCreateInstance2 create_instance_func; // (Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract.
 | 
			
		||||
	GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory.
 | 
			
		||||
	GDExtensionClassRecreateInstance recreate_instance_func;
 | 
			
		||||
	// Queries a virtual function by name and returns a callback to invoke the requested virtual function.
 | 
			
		||||
	GDExtensionClassGetVirtual2 get_virtual_func;
 | 
			
		||||
	// Paired with `call_virtual_with_data_func`, this is an alternative to `get_virtual_func` for extensions that
 | 
			
		||||
	// need or benefit from extra data when calling virtual functions.
 | 
			
		||||
	// Returns user data that will be passed to `call_virtual_with_data_func`.
 | 
			
		||||
	// Returning `NULL` from this function signals to Godot that the virtual function is not overridden.
 | 
			
		||||
	// Data returned from this function should be managed by the extension and must be valid until the extension is deinitialized.
 | 
			
		||||
	// You should supply either `get_virtual_func`, or `get_virtual_call_data_func` with `call_virtual_with_data_func`.
 | 
			
		||||
	GDExtensionClassGetVirtualCallData2 get_virtual_call_data_func;
 | 
			
		||||
	// Used to call virtual functions when `get_virtual_call_data_func` is not null.
 | 
			
		||||
	GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
 | 
			
		||||
	void *class_userdata; // Per-class user data, later accessible in instance bindings.
 | 
			
		||||
} GDExtensionClassCreationInfo4;
 | 
			
		||||
 | 
			
		||||
typedef void *GDExtensionClassLibraryPtr;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -386,7 +424,9 @@ typedef enum {
 | 
			
		|||
	GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32,
 | 
			
		||||
	GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64,
 | 
			
		||||
	GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT,
 | 
			
		||||
	GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE
 | 
			
		||||
	GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE,
 | 
			
		||||
	GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_CHAR16,
 | 
			
		||||
	GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_CHAR32,
 | 
			
		||||
} GDExtensionClassMethodArgumentMetadata;
 | 
			
		||||
 | 
			
		||||
typedef void (*GDExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
 | 
			
		||||
| 
						 | 
				
			
			@ -805,7 +845,7 @@ typedef void (*GDExtensionInterfaceMemFree)(void *p_ptr);
 | 
			
		|||
 *
 | 
			
		||||
 * Logs an error to Godot's built-in debugger and to the OS terminal.
 | 
			
		||||
 *
 | 
			
		||||
 * @param p_description The code trigging the error.
 | 
			
		||||
 * @param p_description The code triggering the error.
 | 
			
		||||
 * @param p_function The function name where the error occurred.
 | 
			
		||||
 * @param p_file The file where the error occurred.
 | 
			
		||||
 * @param p_line The line where the error occurred.
 | 
			
		||||
| 
						 | 
				
			
			@ -819,7 +859,7 @@ typedef void (*GDExtensionInterfacePrintError)(const char *p_description, const
 | 
			
		|||
 *
 | 
			
		||||
 * Logs an error with a message to Godot's built-in debugger and to the OS terminal.
 | 
			
		||||
 *
 | 
			
		||||
 * @param p_description The code trigging the error.
 | 
			
		||||
 * @param p_description The code triggering the error.
 | 
			
		||||
 * @param p_message The message to show along with the error.
 | 
			
		||||
 * @param p_function The function name where the error occurred.
 | 
			
		||||
 * @param p_file The file where the error occurred.
 | 
			
		||||
| 
						 | 
				
			
			@ -834,7 +874,7 @@ typedef void (*GDExtensionInterfacePrintErrorWithMessage)(const char *p_descript
 | 
			
		|||
 *
 | 
			
		||||
 * Logs a warning to Godot's built-in debugger and to the OS terminal.
 | 
			
		||||
 *
 | 
			
		||||
 * @param p_description The code trigging the warning.
 | 
			
		||||
 * @param p_description The code triggering the warning.
 | 
			
		||||
 * @param p_function The function name where the warning occurred.
 | 
			
		||||
 * @param p_file The file where the warning occurred.
 | 
			
		||||
 * @param p_line The line where the warning occurred.
 | 
			
		||||
| 
						 | 
				
			
			@ -848,7 +888,7 @@ typedef void (*GDExtensionInterfacePrintWarning)(const char *p_description, cons
 | 
			
		|||
 *
 | 
			
		||||
 * Logs a warning with a message to Godot's built-in debugger and to the OS terminal.
 | 
			
		||||
 *
 | 
			
		||||
 * @param p_description The code trigging the warning.
 | 
			
		||||
 * @param p_description The code triggering the warning.
 | 
			
		||||
 * @param p_message The message to show along with the warning.
 | 
			
		||||
 * @param p_function The function name where the warning occurred.
 | 
			
		||||
 * @param p_file The file where the warning occurred.
 | 
			
		||||
| 
						 | 
				
			
			@ -863,7 +903,7 @@ typedef void (*GDExtensionInterfacePrintWarningWithMessage)(const char *p_descri
 | 
			
		|||
 *
 | 
			
		||||
 * Logs a script error to Godot's built-in debugger and to the OS terminal.
 | 
			
		||||
 *
 | 
			
		||||
 * @param p_description The code trigging the error.
 | 
			
		||||
 * @param p_description The code triggering the error.
 | 
			
		||||
 * @param p_function The function name where the error occurred.
 | 
			
		||||
 * @param p_file The file where the error occurred.
 | 
			
		||||
 * @param p_line The line where the error occurred.
 | 
			
		||||
| 
						 | 
				
			
			@ -877,7 +917,7 @@ typedef void (*GDExtensionInterfacePrintScriptError)(const char *p_description,
 | 
			
		|||
 *
 | 
			
		||||
 * Logs a script error with a message to Godot's built-in debugger and to the OS terminal.
 | 
			
		||||
 *
 | 
			
		||||
 * @param p_description The code trigging the error.
 | 
			
		||||
 * @param p_description The code triggering the error.
 | 
			
		||||
 * @param p_message The message to show along with the error.
 | 
			
		||||
 * @param p_function The function name where the error occurred.
 | 
			
		||||
 * @param p_file The file where the error occurred.
 | 
			
		||||
| 
						 | 
				
			
			@ -1271,6 +1311,21 @@ typedef GDExtensionBool (*GDExtensionInterfaceVariantHasMember)(GDExtensionVaria
 | 
			
		|||
 */
 | 
			
		||||
typedef GDExtensionBool (*GDExtensionInterfaceVariantHasKey)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionBool *r_valid);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @name variant_get_object_instance_id
 | 
			
		||||
 * @since 4.4
 | 
			
		||||
 *
 | 
			
		||||
 * Gets the object instance ID from a variant of type GDEXTENSION_VARIANT_TYPE_OBJECT.
 | 
			
		||||
 *
 | 
			
		||||
 * If the variant isn't of type GDEXTENSION_VARIANT_TYPE_OBJECT, then zero will be returned.
 | 
			
		||||
 * The instance ID will be returned even if the object is no longer valid - use `object_get_instance_by_id()` to check if the object is still valid.
 | 
			
		||||
 *
 | 
			
		||||
 * @param p_self A pointer to the Variant.
 | 
			
		||||
 *
 | 
			
		||||
 * @return The instance ID for the contained object.
 | 
			
		||||
 */
 | 
			
		||||
typedef GDObjectInstanceID (*GDExtensionInterfaceVariantGetObjectInstanceId)(GDExtensionConstVariantPtr p_self);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @name variant_get_type_name
 | 
			
		||||
 * @since 4.1
 | 
			
		||||
| 
						 | 
				
			
			@ -1332,6 +1387,23 @@ typedef GDExtensionVariantFromTypeConstructorFunc (*GDExtensionInterfaceGetVaria
 | 
			
		|||
 */
 | 
			
		||||
typedef GDExtensionTypeFromVariantConstructorFunc (*GDExtensionInterfaceGetVariantToTypeConstructor)(GDExtensionVariantType p_type);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @name variant_get_ptr_internal_getter
 | 
			
		||||
 * @since 4.4
 | 
			
		||||
 *
 | 
			
		||||
 * Provides a function pointer for retrieving a pointer to a variant's internal value.
 | 
			
		||||
 * Access to a variant's internal value can be used to modify it in-place, or to retrieve its value without the overhead of variant conversion functions.
 | 
			
		||||
 * It is recommended to cache the getter for all variant types in a function table to avoid retrieval overhead upon use.
 | 
			
		||||
 *
 | 
			
		||||
 * @note Each function assumes the variant's type has already been determined and matches the function.
 | 
			
		||||
 * Invoking the function with a variant of a mismatched type has undefined behavior, and may lead to a segmentation fault.
 | 
			
		||||
 *
 | 
			
		||||
 * @param p_type The Variant type.
 | 
			
		||||
 *
 | 
			
		||||
 * @return A pointer to a type-specific function that returns a pointer to the internal value of a variant. Check the implementation of this function (gdextension_variant_get_ptr_internal_getter) for pointee type info of each variant type.
 | 
			
		||||
 */
 | 
			
		||||
typedef GDExtensionVariantGetInternalPtrFunc (*GDExtensionInterfaceGetVariantGetInternalPtrFunc)(GDExtensionVariantType p_type);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @name variant_get_ptr_operator_evaluator
 | 
			
		||||
 * @since 4.1
 | 
			
		||||
| 
						 | 
				
			
			@ -2337,6 +2409,22 @@ typedef GDExtensionVariantPtr (*GDExtensionInterfaceDictionaryOperatorIndex)(GDE
 | 
			
		|||
 */
 | 
			
		||||
typedef GDExtensionVariantPtr (*GDExtensionInterfaceDictionaryOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionConstVariantPtr p_key);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @name dictionary_set_typed
 | 
			
		||||
 * @since 4.4
 | 
			
		||||
 *
 | 
			
		||||
 * Makes a Dictionary into a typed Dictionary.
 | 
			
		||||
 *
 | 
			
		||||
 * @param p_self A pointer to the Dictionary.
 | 
			
		||||
 * @param p_key_type The type of Variant the Dictionary key will store.
 | 
			
		||||
 * @param p_key_class_name A pointer to a StringName with the name of the object (if p_key_type is GDEXTENSION_VARIANT_TYPE_OBJECT).
 | 
			
		||||
 * @param p_key_script A pointer to a Script object (if p_key_type is GDEXTENSION_VARIANT_TYPE_OBJECT and the base class is extended by a script).
 | 
			
		||||
 * @param p_value_type The type of Variant the Dictionary value will store.
 | 
			
		||||
 * @param p_value_class_name A pointer to a StringName with the name of the object (if p_value_type is GDEXTENSION_VARIANT_TYPE_OBJECT).
 | 
			
		||||
 * @param p_value_script A pointer to a Script object (if p_value_type is GDEXTENSION_VARIANT_TYPE_OBJECT and the base class is extended by a script).
 | 
			
		||||
 */
 | 
			
		||||
typedef void (*GDExtensionInterfaceDictionarySetTyped)(GDExtensionTypePtr p_self, GDExtensionVariantType p_key_type, GDExtensionConstStringNamePtr p_key_class_name, GDExtensionConstVariantPtr p_key_script, GDExtensionVariantType p_value_type, GDExtensionConstStringNamePtr p_value_class_name, GDExtensionConstVariantPtr p_value_script);
 | 
			
		||||
 | 
			
		||||
/* INTERFACE: Object */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -2680,6 +2768,7 @@ typedef void *(*GDExtensionInterfaceCallableCustomGetUserData)(GDExtensionConstT
 | 
			
		|||
/**
 | 
			
		||||
 * @name classdb_construct_object
 | 
			
		||||
 * @since 4.1
 | 
			
		||||
 * @deprecated in Godot 4.4. Use `classdb_construct_object2` instead.
 | 
			
		||||
 *
 | 
			
		||||
 * Constructs an Object of the requested class.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -2691,6 +2780,22 @@ typedef void *(*GDExtensionInterfaceCallableCustomGetUserData)(GDExtensionConstT
 | 
			
		|||
 */
 | 
			
		||||
typedef GDExtensionObjectPtr (*GDExtensionInterfaceClassdbConstructObject)(GDExtensionConstStringNamePtr p_classname);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @name classdb_construct_object2
 | 
			
		||||
 * @since 4.4
 | 
			
		||||
 *
 | 
			
		||||
 * Constructs an Object of the requested class.
 | 
			
		||||
 *
 | 
			
		||||
 * The passed class must be a built-in godot class, or an already-registered extension class. In both cases, object_set_instance() should be called to fully initialize the object.
 | 
			
		||||
 *
 | 
			
		||||
 * "NOTIFICATION_POSTINITIALIZE" must be sent after construction.
 | 
			
		||||
 *
 | 
			
		||||
 * @param p_classname A pointer to a StringName with the class name.
 | 
			
		||||
 *
 | 
			
		||||
 * @return A pointer to the newly created Object.
 | 
			
		||||
 */
 | 
			
		||||
typedef GDExtensionObjectPtr (*GDExtensionInterfaceClassdbConstructObject2)(GDExtensionConstStringNamePtr p_classname);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @name classdb_get_method_bind
 | 
			
		||||
 * @since 4.1
 | 
			
		||||
| 
						 | 
				
			
			@ -2722,7 +2827,7 @@ typedef void *(*GDExtensionInterfaceClassdbGetClassTag)(GDExtensionConstStringNa
 | 
			
		|||
/**
 | 
			
		||||
 * @name classdb_register_extension_class
 | 
			
		||||
 * @since 4.1
 | 
			
		||||
 * @deprecated in Godot 4.2. Use `classdb_register_extension_class3` instead.
 | 
			
		||||
 * @deprecated in Godot 4.2. Use `classdb_register_extension_class4` instead.
 | 
			
		||||
 *
 | 
			
		||||
 * Registers an extension class in the ClassDB.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -2738,7 +2843,7 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass)(GDExtensionCla
 | 
			
		|||
/**
 | 
			
		||||
 * @name classdb_register_extension_class2
 | 
			
		||||
 * @since 4.2
 | 
			
		||||
 * @deprecated in Godot 4.3. Use `classdb_register_extension_class3` instead.
 | 
			
		||||
 * @deprecated in Godot 4.3. Use `classdb_register_extension_class4` instead.
 | 
			
		||||
 *
 | 
			
		||||
 * Registers an extension class in the ClassDB.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -2754,6 +2859,7 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionCl
 | 
			
		|||
/**
 | 
			
		||||
 * @name classdb_register_extension_class3
 | 
			
		||||
 * @since 4.3
 | 
			
		||||
 * @deprecated in Godot 4.4. Use `classdb_register_extension_class4` instead.
 | 
			
		||||
 *
 | 
			
		||||
 * Registers an extension class in the ClassDB.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -2766,6 +2872,21 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionCl
 | 
			
		|||
 */
 | 
			
		||||
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass3)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @name classdb_register_extension_class4
 | 
			
		||||
 * @since 4.4
 | 
			
		||||
 *
 | 
			
		||||
 * Registers an extension class in the ClassDB.
 | 
			
		||||
 *
 | 
			
		||||
 * Provided struct can be safely freed once the function returns.
 | 
			
		||||
 *
 | 
			
		||||
 * @param p_library A pointer the library received by the GDExtension's entry point function.
 | 
			
		||||
 * @param p_class_name A pointer to a StringName with the class name.
 | 
			
		||||
 * @param p_parent_class_name A pointer to a StringName with the parent class name.
 | 
			
		||||
 * @param p_extension_funcs A pointer to a GDExtensionClassCreationInfo2 struct.
 | 
			
		||||
 */
 | 
			
		||||
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass4)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @name classdb_register_extension_class_method
 | 
			
		||||
 * @since 4.1
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										394
									
								
								engine/core/extension/gdextension_library_loader.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										394
									
								
								engine/core/extension/gdextension_library_loader.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,394 @@
 | 
			
		|||
/**************************************************************************/
 | 
			
		||||
/*  gdextension_library_loader.cpp                                        */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "gdextension_library_loader.h"
 | 
			
		||||
 | 
			
		||||
#include "core/config/project_settings.h"
 | 
			
		||||
#include "core/io/dir_access.h"
 | 
			
		||||
#include "core/version.h"
 | 
			
		||||
#include "gdextension.h"
 | 
			
		||||
 | 
			
		||||
Vector<SharedObject> GDExtensionLibraryLoader::find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature) {
 | 
			
		||||
	Vector<SharedObject> dependencies_shared_objects;
 | 
			
		||||
	if (p_config->has_section("dependencies")) {
 | 
			
		||||
		List<String> config_dependencies;
 | 
			
		||||
		p_config->get_section_keys("dependencies", &config_dependencies);
 | 
			
		||||
 | 
			
		||||
		for (const String &dependency : config_dependencies) {
 | 
			
		||||
			Vector<String> dependency_tags = dependency.split(".");
 | 
			
		||||
			bool all_tags_met = true;
 | 
			
		||||
			for (int i = 0; i < dependency_tags.size(); i++) {
 | 
			
		||||
				String tag = dependency_tags[i].strip_edges();
 | 
			
		||||
				if (!p_has_feature(tag)) {
 | 
			
		||||
					all_tags_met = false;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (all_tags_met) {
 | 
			
		||||
				Dictionary dependency_value = p_config->get_value("dependencies", dependency);
 | 
			
		||||
				for (const Variant *key = dependency_value.next(nullptr); key; key = dependency_value.next(key)) {
 | 
			
		||||
					String dependency_path = *key;
 | 
			
		||||
					String target_path = dependency_value[*key];
 | 
			
		||||
					if (dependency_path.is_relative_path()) {
 | 
			
		||||
						dependency_path = p_path.get_base_dir().path_join(dependency_path);
 | 
			
		||||
					}
 | 
			
		||||
					dependencies_shared_objects.push_back(SharedObject(dependency_path, dependency_tags, target_path));
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dependencies_shared_objects;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String GDExtensionLibraryLoader::find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags) {
 | 
			
		||||
	// First, check the explicit libraries.
 | 
			
		||||
	if (p_config->has_section("libraries")) {
 | 
			
		||||
		List<String> libraries;
 | 
			
		||||
		p_config->get_section_keys("libraries", &libraries);
 | 
			
		||||
 | 
			
		||||
		// Iterate the libraries, finding the best matching tags.
 | 
			
		||||
		String best_library_path;
 | 
			
		||||
		Vector<String> best_library_tags;
 | 
			
		||||
		for (const String &E : libraries) {
 | 
			
		||||
			Vector<String> tags = E.split(".");
 | 
			
		||||
			bool all_tags_met = true;
 | 
			
		||||
			for (int i = 0; i < tags.size(); i++) {
 | 
			
		||||
				String tag = tags[i].strip_edges();
 | 
			
		||||
				if (!p_has_feature(tag)) {
 | 
			
		||||
					all_tags_met = false;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (all_tags_met && tags.size() > best_library_tags.size()) {
 | 
			
		||||
				best_library_path = p_config->get_value("libraries", E);
 | 
			
		||||
				best_library_tags = tags;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!best_library_path.is_empty()) {
 | 
			
		||||
			if (best_library_path.is_relative_path()) {
 | 
			
		||||
				best_library_path = p_path.get_base_dir().path_join(best_library_path);
 | 
			
		||||
			}
 | 
			
		||||
			if (r_tags != nullptr) {
 | 
			
		||||
				r_tags->append_array(best_library_tags);
 | 
			
		||||
			}
 | 
			
		||||
			return best_library_path;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Second, try to autodetect.
 | 
			
		||||
	String autodetect_library_prefix;
 | 
			
		||||
	if (p_config->has_section_key("configuration", "autodetect_library_prefix")) {
 | 
			
		||||
		autodetect_library_prefix = p_config->get_value("configuration", "autodetect_library_prefix");
 | 
			
		||||
	}
 | 
			
		||||
	if (!autodetect_library_prefix.is_empty()) {
 | 
			
		||||
		String autodetect_path = autodetect_library_prefix;
 | 
			
		||||
		if (autodetect_path.is_relative_path()) {
 | 
			
		||||
			autodetect_path = p_path.get_base_dir().path_join(autodetect_path);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Find the folder and file parts of the prefix.
 | 
			
		||||
		String folder;
 | 
			
		||||
		String file_prefix;
 | 
			
		||||
		if (DirAccess::dir_exists_absolute(autodetect_path)) {
 | 
			
		||||
			folder = autodetect_path;
 | 
			
		||||
		} else if (DirAccess::dir_exists_absolute(autodetect_path.get_base_dir())) {
 | 
			
		||||
			folder = autodetect_path.get_base_dir();
 | 
			
		||||
			file_prefix = autodetect_path.get_file();
 | 
			
		||||
		} else {
 | 
			
		||||
			ERR_FAIL_V_MSG(String(), vformat("Error in extension: %s. Could not find folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Open the folder.
 | 
			
		||||
		Ref<DirAccess> dir = DirAccess::open(folder);
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(dir.is_null(), String(), vformat("Error in extension: %s. Could not open folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
 | 
			
		||||
 | 
			
		||||
		// Iterate the files and check the prefixes, finding the best matching file.
 | 
			
		||||
		String best_file;
 | 
			
		||||
		Vector<String> best_file_tags;
 | 
			
		||||
		dir->list_dir_begin();
 | 
			
		||||
		String file_name = dir->_get_next();
 | 
			
		||||
		while (file_name != "") {
 | 
			
		||||
			if (!dir->current_is_dir() && file_name.begins_with(file_prefix)) {
 | 
			
		||||
				// Check if the files matches all requested feature tags.
 | 
			
		||||
				String tags_str = file_name.trim_prefix(file_prefix);
 | 
			
		||||
				tags_str = tags_str.trim_suffix(tags_str.get_extension());
 | 
			
		||||
 | 
			
		||||
				Vector<String> tags = tags_str.split(".", false);
 | 
			
		||||
				bool all_tags_met = true;
 | 
			
		||||
				for (int i = 0; i < tags.size(); i++) {
 | 
			
		||||
					String tag = tags[i].strip_edges();
 | 
			
		||||
					if (!p_has_feature(tag)) {
 | 
			
		||||
						all_tags_met = false;
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// If all tags are found in the feature list, and we found more tags than before, use this file.
 | 
			
		||||
				if (all_tags_met && tags.size() > best_file_tags.size()) {
 | 
			
		||||
					best_file_tags = tags;
 | 
			
		||||
					best_file = file_name;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			file_name = dir->_get_next();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!best_file.is_empty()) {
 | 
			
		||||
			String library_path = folder.path_join(best_file);
 | 
			
		||||
			if (r_tags != nullptr) {
 | 
			
		||||
				r_tags->append_array(best_file_tags);
 | 
			
		||||
			}
 | 
			
		||||
			return library_path;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return String();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error GDExtensionLibraryLoader::open_library(const String &p_path) {
 | 
			
		||||
	Error err = parse_gdextension_file(p_path);
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path);
 | 
			
		||||
 | 
			
		||||
	Vector<String> abs_dependencies_paths;
 | 
			
		||||
	if (!library_dependencies.is_empty()) {
 | 
			
		||||
		for (const SharedObject &dependency : library_dependencies) {
 | 
			
		||||
			abs_dependencies_paths.push_back(ProjectSettings::get_singleton()->globalize_path(dependency.path));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	OS::GDExtensionData data = {
 | 
			
		||||
		true, // also_set_library_path
 | 
			
		||||
		&library_path, // r_resolved_path
 | 
			
		||||
		Engine::get_singleton()->is_editor_hint(), // generate_temp_files
 | 
			
		||||
		&abs_dependencies_paths, // library_dependencies
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	err = OS::get_singleton()->open_dynamic_library(is_static_library ? String() : abs_path, library, &data);
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error GDExtensionLibraryLoader::initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) {
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	p_extension->set_reloadable(is_reloadable && Engine::get_singleton()->is_extension_reloading_enabled());
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	for (const KeyValue<String, String> &icon : class_icon_paths) {
 | 
			
		||||
		p_extension->class_icon_paths[icon.key] = icon.value;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void *entry_funcptr = nullptr;
 | 
			
		||||
 | 
			
		||||
	Error err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, entry_symbol, entry_funcptr, false);
 | 
			
		||||
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		ERR_PRINT(vformat("GDExtension entry point '%s' not found in library %s.", entry_symbol, library_path));
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr;
 | 
			
		||||
 | 
			
		||||
	GDExtensionBool ret = initialization_function(p_get_proc_address, p_extension.ptr(), r_initialization);
 | 
			
		||||
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		return OK;
 | 
			
		||||
	} else {
 | 
			
		||||
		ERR_PRINT(vformat("GDExtension initialization function '%s' returned an error.", entry_symbol));
 | 
			
		||||
		return FAILED;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GDExtensionLibraryLoader::close_library() {
 | 
			
		||||
	OS::get_singleton()->close_dynamic_library(library);
 | 
			
		||||
	library = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GDExtensionLibraryLoader::is_library_open() const {
 | 
			
		||||
	return library != nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GDExtensionLibraryLoader::has_library_changed() const {
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	// Check only that the last modified time is different (rather than checking
 | 
			
		||||
	// that it's newer) since some OS's (namely Windows) will preserve the modified
 | 
			
		||||
	// time by default when copying files.
 | 
			
		||||
	if (FileAccess::get_modified_time(resource_path) != resource_last_modified_time) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	if (FileAccess::get_modified_time(library_path) != library_last_modified_time) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GDExtensionLibraryLoader::library_exists() const {
 | 
			
		||||
	return FileAccess::exists(resource_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error GDExtensionLibraryLoader::parse_gdextension_file(const String &p_path) {
 | 
			
		||||
	resource_path = p_path;
 | 
			
		||||
 | 
			
		||||
	Ref<ConfigFile> config;
 | 
			
		||||
	config.instantiate();
 | 
			
		||||
 | 
			
		||||
	Error err = config->load(p_path);
 | 
			
		||||
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		ERR_PRINT(vformat("Error loading GDExtension configuration file: '%s'.", p_path));
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!config->has_section_key("configuration", "entry_symbol")) {
 | 
			
		||||
		ERR_PRINT(vformat("GDExtension configuration file must contain a \"configuration/entry_symbol\" key: '%s'.", p_path));
 | 
			
		||||
		return ERR_INVALID_DATA;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	entry_symbol = config->get_value("configuration", "entry_symbol");
 | 
			
		||||
 | 
			
		||||
	uint32_t compatibility_minimum[3] = { 0, 0, 0 };
 | 
			
		||||
	if (config->has_section_key("configuration", "compatibility_minimum")) {
 | 
			
		||||
		String compat_string = config->get_value("configuration", "compatibility_minimum");
 | 
			
		||||
		Vector<int> parts = compat_string.split_ints(".");
 | 
			
		||||
		for (int i = 0; i < parts.size(); i++) {
 | 
			
		||||
			if (i >= 3) {
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			if (parts[i] >= 0) {
 | 
			
		||||
				compatibility_minimum[i] = parts[i];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		ERR_PRINT(vformat("GDExtension configuration file must contain a \"configuration/compatibility_minimum\" key: '%s'.", p_path));
 | 
			
		||||
		return ERR_INVALID_DATA;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (compatibility_minimum[0] < 4 || (compatibility_minimum[0] == 4 && compatibility_minimum[1] == 0)) {
 | 
			
		||||
		ERR_PRINT(vformat("GDExtension's compatibility_minimum (%d.%d.%d) must be at least 4.1.0: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
 | 
			
		||||
		return ERR_INVALID_DATA;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool compatible = true;
 | 
			
		||||
	// Check version lexicographically.
 | 
			
		||||
	if (VERSION_MAJOR != compatibility_minimum[0]) {
 | 
			
		||||
		compatible = VERSION_MAJOR > compatibility_minimum[0];
 | 
			
		||||
	} else if (VERSION_MINOR != compatibility_minimum[1]) {
 | 
			
		||||
		compatible = VERSION_MINOR > compatibility_minimum[1];
 | 
			
		||||
	} else {
 | 
			
		||||
		compatible = VERSION_PATCH >= compatibility_minimum[2];
 | 
			
		||||
	}
 | 
			
		||||
	if (!compatible) {
 | 
			
		||||
		ERR_PRINT(vformat("GDExtension only compatible with Godot version %d.%d.%d or later: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
 | 
			
		||||
		return ERR_INVALID_DATA;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Optionally check maximum compatibility.
 | 
			
		||||
	if (config->has_section_key("configuration", "compatibility_maximum")) {
 | 
			
		||||
		uint32_t compatibility_maximum[3] = { 0, 0, 0 };
 | 
			
		||||
		String compat_string = config->get_value("configuration", "compatibility_maximum");
 | 
			
		||||
		Vector<int> parts = compat_string.split_ints(".");
 | 
			
		||||
		for (int i = 0; i < 3; i++) {
 | 
			
		||||
			if (i < parts.size() && parts[i] >= 0) {
 | 
			
		||||
				compatibility_maximum[i] = parts[i];
 | 
			
		||||
			} else {
 | 
			
		||||
				// If a version part is missing, set the maximum to an arbitrary high value.
 | 
			
		||||
				compatibility_maximum[i] = 9999;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		compatible = true;
 | 
			
		||||
		if (VERSION_MAJOR != compatibility_maximum[0]) {
 | 
			
		||||
			compatible = VERSION_MAJOR < compatibility_maximum[0];
 | 
			
		||||
		} else if (VERSION_MINOR != compatibility_maximum[1]) {
 | 
			
		||||
			compatible = VERSION_MINOR < compatibility_maximum[1];
 | 
			
		||||
		}
 | 
			
		||||
#if VERSION_PATCH
 | 
			
		||||
		// #if check to avoid -Wtype-limits warning when 0.
 | 
			
		||||
		else {
 | 
			
		||||
			compatible = VERSION_PATCH <= compatibility_maximum[2];
 | 
			
		||||
		}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		if (!compatible) {
 | 
			
		||||
			ERR_PRINT(vformat("GDExtension only compatible with Godot version %s or earlier: %s", compat_string, p_path));
 | 
			
		||||
			return ERR_INVALID_DATA;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	library_path = find_extension_library(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
 | 
			
		||||
 | 
			
		||||
	if (library_path.is_empty()) {
 | 
			
		||||
		const String os_arch = OS::get_singleton()->get_name().to_lower() + "." + Engine::get_singleton()->get_architecture_name();
 | 
			
		||||
		ERR_PRINT(vformat("No GDExtension library found for current OS and architecture (%s) in configuration file: %s", os_arch, p_path));
 | 
			
		||||
		return ERR_FILE_NOT_FOUND;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	is_static_library = library_path.ends_with(".a") || library_path.ends_with(".xcframework");
 | 
			
		||||
 | 
			
		||||
	if (!library_path.is_resource_file() && !library_path.is_absolute_path()) {
 | 
			
		||||
		library_path = p_path.get_base_dir().path_join(library_path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	is_reloadable = config->get_value("configuration", "reloadable", false);
 | 
			
		||||
 | 
			
		||||
	update_last_modified_time(
 | 
			
		||||
			FileAccess::get_modified_time(resource_path),
 | 
			
		||||
			FileAccess::get_modified_time(library_path));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	library_dependencies = find_extension_dependencies(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
 | 
			
		||||
 | 
			
		||||
	// Handle icons if any are specified.
 | 
			
		||||
	if (config->has_section("icons")) {
 | 
			
		||||
		List<String> keys;
 | 
			
		||||
		config->get_section_keys("icons", &keys);
 | 
			
		||||
		for (const String &key : keys) {
 | 
			
		||||
			String icon_path = config->get_value("icons", key);
 | 
			
		||||
			if (icon_path.is_relative_path()) {
 | 
			
		||||
				icon_path = p_path.get_base_dir().path_join(icon_path);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			class_icon_paths[key] = icon_path;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										85
									
								
								engine/core/extension/gdextension_library_loader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								engine/core/extension/gdextension_library_loader.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,85 @@
 | 
			
		|||
/**************************************************************************/
 | 
			
		||||
/*  gdextension_library_loader.h                                          */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef GDEXTENSION_LIBRARY_LOADER_H
 | 
			
		||||
#define GDEXTENSION_LIBRARY_LOADER_H
 | 
			
		||||
 | 
			
		||||
#include <functional>
 | 
			
		||||
 | 
			
		||||
#include "core/extension/gdextension_loader.h"
 | 
			
		||||
#include "core/io/config_file.h"
 | 
			
		||||
#include "core/os/shared_object.h"
 | 
			
		||||
 | 
			
		||||
class GDExtensionLibraryLoader : public GDExtensionLoader {
 | 
			
		||||
	friend class GDExtensionManager;
 | 
			
		||||
	friend class GDExtension;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	String resource_path;
 | 
			
		||||
 | 
			
		||||
	void *library = nullptr; // pointer if valid.
 | 
			
		||||
	String library_path;
 | 
			
		||||
	String entry_symbol;
 | 
			
		||||
 | 
			
		||||
	bool is_static_library = false;
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	bool is_reloadable = false;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	Vector<SharedObject> library_dependencies;
 | 
			
		||||
 | 
			
		||||
	HashMap<String, String> class_icon_paths;
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	uint64_t resource_last_modified_time = 0;
 | 
			
		||||
	uint64_t library_last_modified_time = 0;
 | 
			
		||||
 | 
			
		||||
	void update_last_modified_time(uint64_t p_resource_last_modified_time, uint64_t p_library_last_modified_time) {
 | 
			
		||||
		resource_last_modified_time = p_resource_last_modified_time;
 | 
			
		||||
		library_last_modified_time = p_library_last_modified_time;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr);
 | 
			
		||||
	static Vector<SharedObject> find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature);
 | 
			
		||||
 | 
			
		||||
	virtual Error open_library(const String &p_path) override;
 | 
			
		||||
	virtual Error initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) override;
 | 
			
		||||
	virtual void close_library() override;
 | 
			
		||||
	virtual bool is_library_open() const override;
 | 
			
		||||
	virtual bool has_library_changed() const override;
 | 
			
		||||
	virtual bool library_exists() const override;
 | 
			
		||||
 | 
			
		||||
	Error parse_gdextension_file(const String &p_path);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // GDEXTENSION_LIBRARY_LOADER_H
 | 
			
		||||
							
								
								
									
										48
									
								
								engine/core/extension/gdextension_loader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								engine/core/extension/gdextension_loader.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
/**************************************************************************/
 | 
			
		||||
/*  gdextension_loader.h                                                  */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef GDEXTENSION_LOADER_H
 | 
			
		||||
#define GDEXTENSION_LOADER_H
 | 
			
		||||
 | 
			
		||||
#include "core/object/ref_counted.h"
 | 
			
		||||
 | 
			
		||||
class GDExtension;
 | 
			
		||||
 | 
			
		||||
class GDExtensionLoader : public RefCounted {
 | 
			
		||||
public:
 | 
			
		||||
	virtual Error open_library(const String &p_path) = 0;
 | 
			
		||||
	virtual Error initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) = 0;
 | 
			
		||||
	virtual void close_library() = 0;
 | 
			
		||||
	virtual bool is_library_open() const = 0;
 | 
			
		||||
	virtual bool has_library_changed() const = 0;
 | 
			
		||||
	virtual bool library_exists() const = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // GDEXTENSION_LOADER_H
 | 
			
		||||
| 
						 | 
				
			
			@ -30,16 +30,21 @@
 | 
			
		|||
 | 
			
		||||
#include "gdextension_manager.h"
 | 
			
		||||
 | 
			
		||||
#include "core/extension/gdextension_compat_hashes.h"
 | 
			
		||||
#include "core/extension/gdextension_library_loader.h"
 | 
			
		||||
#include "core/extension/gdextension_special_compat_hashes.h"
 | 
			
		||||
#include "core/io/dir_access.h"
 | 
			
		||||
#include "core/io/file_access.h"
 | 
			
		||||
#include "core/object/script_language.h"
 | 
			
		||||
 | 
			
		||||
GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension) {
 | 
			
		||||
GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension, bool p_first_load) {
 | 
			
		||||
	if (level >= 0) { // Already initialized up to some level.
 | 
			
		||||
		int32_t minimum_level = p_extension->get_minimum_library_initialization_level();
 | 
			
		||||
		int32_t minimum_level = 0;
 | 
			
		||||
		if (!p_first_load) {
 | 
			
		||||
			minimum_level = p_extension->get_minimum_library_initialization_level();
 | 
			
		||||
			if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
 | 
			
		||||
				return LOAD_STATUS_NEEDS_RESTART;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// Initialize up to current level.
 | 
			
		||||
		for (int32_t i = minimum_level; i <= level; i++) {
 | 
			
		||||
			p_extension->initialize_library(GDExtension::InitializationLevel(i));
 | 
			
		||||
| 
						 | 
				
			
			@ -50,10 +55,20 @@ GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(cons
 | 
			
		|||
		gdextension_class_icon_paths[kv.key] = kv.value;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	// Signals that a new extension is loaded so GDScript can register new class names.
 | 
			
		||||
	emit_signal("extension_loaded", p_extension);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return LOAD_STATUS_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(const Ref<GDExtension> &p_extension) {
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	// Signals that a new extension is unloading so GDScript can unregister class names.
 | 
			
		||||
	emit_signal("extension_unloading", p_extension);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (level >= 0) { // Already initialized up to some level.
 | 
			
		||||
		// Deinitialize down from current level.
 | 
			
		||||
		for (int32_t i = level; i >= GDExtension::INITIALIZATION_LEVEL_CORE; i--) {
 | 
			
		||||
| 
						 | 
				
			
			@ -69,19 +84,35 @@ GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(co
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String &p_path) {
 | 
			
		||||
	if (gdextension_map.has(p_path)) {
 | 
			
		||||
		return LOAD_STATUS_ALREADY_LOADED;
 | 
			
		||||
	}
 | 
			
		||||
	Ref<GDExtension> extension = ResourceLoader::load(p_path);
 | 
			
		||||
	if (extension.is_null()) {
 | 
			
		||||
	if (Engine::get_singleton()->is_recovery_mode_hint()) {
 | 
			
		||||
		return LOAD_STATUS_FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LoadStatus status = _load_extension_internal(extension);
 | 
			
		||||
	Ref<GDExtensionLibraryLoader> loader;
 | 
			
		||||
	loader.instantiate();
 | 
			
		||||
	return GDExtensionManager::get_singleton()->load_extension_with_loader(p_path, loader);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GDExtensionManager::LoadStatus GDExtensionManager::load_extension_with_loader(const String &p_path, const Ref<GDExtensionLoader> &p_loader) {
 | 
			
		||||
	DEV_ASSERT(p_loader.is_valid());
 | 
			
		||||
 | 
			
		||||
	if (gdextension_map.has(p_path)) {
 | 
			
		||||
		return LOAD_STATUS_ALREADY_LOADED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Ref<GDExtension> extension;
 | 
			
		||||
	extension.instantiate();
 | 
			
		||||
	Error err = extension->open_library(p_path, p_loader);
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		return LOAD_STATUS_FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LoadStatus status = _load_extension_internal(extension, true);
 | 
			
		||||
	if (status != LOAD_STATUS_OK) {
 | 
			
		||||
		return status;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	extension->set_path(p_path);
 | 
			
		||||
	gdextension_map[p_path] = extension;
 | 
			
		||||
	return LOAD_STATUS_OK;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -92,6 +123,10 @@ GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String
 | 
			
		|||
#else
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!Engine::get_singleton()->is_extension_reloading_enabled(), LOAD_STATUS_FAILED, "GDExtension reloading is disabled.");
 | 
			
		||||
 | 
			
		||||
	if (Engine::get_singleton()->is_recovery_mode_hint()) {
 | 
			
		||||
		return LOAD_STATUS_FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!gdextension_map.has(p_path)) {
 | 
			
		||||
		return LOAD_STATUS_NOT_LOADED;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -117,12 +152,12 @@ GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String
 | 
			
		|||
		extension->close_library();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Error err = GDExtensionResourceLoader::load_gdextension_resource(p_path, extension);
 | 
			
		||||
	Error err = extension->open_library(p_path, extension->loader);
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		return LOAD_STATUS_FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	status = _load_extension_internal(extension);
 | 
			
		||||
	status = _load_extension_internal(extension, false);
 | 
			
		||||
	if (status != LOAD_STATUS_OK) {
 | 
			
		||||
		return status;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -134,6 +169,10 @@ GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
GDExtensionManager::LoadStatus GDExtensionManager::unload_extension(const String &p_path) {
 | 
			
		||||
	if (Engine::get_singleton()->is_recovery_mode_hint()) {
 | 
			
		||||
		return LOAD_STATUS_FAILED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!gdextension_map.has(p_path)) {
 | 
			
		||||
		return LOAD_STATUS_NOT_LOADED;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -180,14 +219,28 @@ String GDExtensionManager::class_get_icon_path(const String &p_class) const {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void GDExtensionManager::initialize_extensions(GDExtension::InitializationLevel p_level) {
 | 
			
		||||
	if (Engine::get_singleton()->is_recovery_mode_hint()) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ERR_FAIL_COND(int32_t(p_level) - 1 != level);
 | 
			
		||||
	for (KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
 | 
			
		||||
		E.value->initialize_library(p_level);
 | 
			
		||||
 | 
			
		||||
		if (p_level == GDExtension::INITIALIZATION_LEVEL_EDITOR) {
 | 
			
		||||
			for (const KeyValue<String, String> &kv : E.value->class_icon_paths) {
 | 
			
		||||
				gdextension_class_icon_paths[kv.key] = kv.value;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	level = p_level;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GDExtensionManager::deinitialize_extensions(GDExtension::InitializationLevel p_level) {
 | 
			
		||||
	if (Engine::get_singleton()->is_recovery_mode_hint()) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ERR_FAIL_COND(int32_t(p_level) != level);
 | 
			
		||||
	for (KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
 | 
			
		||||
		E.value->deinitialize_library(p_level);
 | 
			
		||||
| 
						 | 
				
			
			@ -226,12 +279,16 @@ void GDExtensionManager::_reload_all_scripts() {
 | 
			
		|||
#endif // TOOLS_ENABLED
 | 
			
		||||
 | 
			
		||||
void GDExtensionManager::load_extensions() {
 | 
			
		||||
	if (Engine::get_singleton()->is_recovery_mode_hint()) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Ref<FileAccess> f = FileAccess::open(GDExtension::get_extension_list_config_file(), FileAccess::READ);
 | 
			
		||||
	while (f.is_valid() && !f->eof_reached()) {
 | 
			
		||||
		String s = f->get_line().strip_edges();
 | 
			
		||||
		if (!s.is_empty()) {
 | 
			
		||||
			LoadStatus err = load_extension(s);
 | 
			
		||||
			ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, "Error loading extension: " + s);
 | 
			
		||||
			ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, vformat("Error loading extension: '%s'.", s));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -240,6 +297,9 @@ void GDExtensionManager::load_extensions() {
 | 
			
		|||
 | 
			
		||||
void GDExtensionManager::reload_extensions() {
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	if (Engine::get_singleton()->is_recovery_mode_hint()) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	bool reloaded = false;
 | 
			
		||||
	for (const KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
 | 
			
		||||
		if (!E.value->is_reloadable()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -261,6 +321,72 @@ void GDExtensionManager::reload_extensions() {
 | 
			
		|||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GDExtensionManager::ensure_extensions_loaded(const HashSet<String> &p_extensions) {
 | 
			
		||||
	Vector<String> extensions_added;
 | 
			
		||||
	Vector<String> extensions_removed;
 | 
			
		||||
 | 
			
		||||
	for (const String &E : p_extensions) {
 | 
			
		||||
		if (!is_extension_loaded(E)) {
 | 
			
		||||
			extensions_added.push_back(E);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Vector<String> loaded_extensions = get_loaded_extensions();
 | 
			
		||||
	for (const String &loaded_extension : loaded_extensions) {
 | 
			
		||||
		if (!p_extensions.has(loaded_extension)) {
 | 
			
		||||
			// The extension may not have a .gdextension file.
 | 
			
		||||
			const Ref<GDExtension> extension = GDExtensionManager::get_singleton()->get_extension(loaded_extension);
 | 
			
		||||
			if (!extension->get_loader()->library_exists()) {
 | 
			
		||||
				extensions_removed.push_back(loaded_extension);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	String extension_list_config_file = GDExtension::get_extension_list_config_file();
 | 
			
		||||
	if (p_extensions.size()) {
 | 
			
		||||
		if (extensions_added.size() || extensions_removed.size()) {
 | 
			
		||||
			// Extensions were added or removed.
 | 
			
		||||
			Ref<FileAccess> f = FileAccess::open(extension_list_config_file, FileAccess::WRITE);
 | 
			
		||||
			for (const String &E : p_extensions) {
 | 
			
		||||
				f->store_line(E);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if (loaded_extensions.size() || FileAccess::exists(extension_list_config_file)) {
 | 
			
		||||
			// Extensions were removed.
 | 
			
		||||
			Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
 | 
			
		||||
			da->remove(extension_list_config_file);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool needs_restart = false;
 | 
			
		||||
	for (const String &extension : extensions_added) {
 | 
			
		||||
		GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->load_extension(extension);
 | 
			
		||||
		if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
 | 
			
		||||
			needs_restart = true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (const String &extension : extensions_removed) {
 | 
			
		||||
		GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->unload_extension(extension);
 | 
			
		||||
		if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
 | 
			
		||||
			needs_restart = true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	if (extensions_added.size() || extensions_removed.size()) {
 | 
			
		||||
		// Emitting extensions_reloaded so EditorNode can reload Inspector and regenerate documentation.
 | 
			
		||||
		emit_signal("extensions_reloaded");
 | 
			
		||||
 | 
			
		||||
		// Reload all scripts to clear out old references.
 | 
			
		||||
		callable_mp_static(&GDExtensionManager::_reload_all_scripts).call_deferred();
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return needs_restart;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GDExtensionManager *GDExtensionManager::get_singleton() {
 | 
			
		||||
	return singleton;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -281,6 +407,8 @@ void GDExtensionManager::_bind_methods() {
 | 
			
		|||
	BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART);
 | 
			
		||||
 | 
			
		||||
	ADD_SIGNAL(MethodInfo("extensions_reloaded"));
 | 
			
		||||
	ADD_SIGNAL(MethodInfo("extension_loaded", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension")));
 | 
			
		||||
	ADD_SIGNAL(MethodInfo("extension_unloading", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension")));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GDExtensionManager *GDExtensionManager::singleton = nullptr;
 | 
			
		||||
| 
						 | 
				
			
			@ -290,7 +418,7 @@ GDExtensionManager::GDExtensionManager() {
 | 
			
		|||
	singleton = this;
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
	GDExtensionCompatHashes::initialize();
 | 
			
		||||
	GDExtensionSpecialCompatHashes::initialize();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -299,6 +427,6 @@ GDExtensionManager::~GDExtensionManager() {
 | 
			
		|||
		singleton = nullptr;
 | 
			
		||||
	}
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
	GDExtensionCompatHashes::finalize();
 | 
			
		||||
	GDExtensionSpecialCompatHashes::finalize();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,7 +54,7 @@ public:
 | 
			
		|||
	};
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	LoadStatus _load_extension_internal(const Ref<GDExtension> &p_extension);
 | 
			
		||||
	LoadStatus _load_extension_internal(const Ref<GDExtension> &p_extension, bool p_first_load);
 | 
			
		||||
	LoadStatus _unload_extension_internal(const Ref<GDExtension> &p_extension);
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			@ -63,6 +63,7 @@ private:
 | 
			
		|||
 | 
			
		||||
public:
 | 
			
		||||
	LoadStatus load_extension(const String &p_path);
 | 
			
		||||
	LoadStatus load_extension_with_loader(const String &p_path, const Ref<GDExtensionLoader> &p_loader);
 | 
			
		||||
	LoadStatus reload_extension(const String &p_path);
 | 
			
		||||
	LoadStatus unload_extension(const String &p_path);
 | 
			
		||||
	bool is_extension_loaded(const String &p_path) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -84,6 +85,7 @@ public:
 | 
			
		|||
 | 
			
		||||
	void load_extensions();
 | 
			
		||||
	void reload_extensions();
 | 
			
		||||
	bool ensure_extensions_loaded(const HashSet<String> &p_extensions);
 | 
			
		||||
 | 
			
		||||
	GDExtensionManager();
 | 
			
		||||
	~GDExtensionManager();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/**************************************************************************/
 | 
			
		||||
/*  gdextension_compat_hashes.cpp                                         */
 | 
			
		||||
/*  gdextension_special_compat_hashes.cpp                                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
| 
						 | 
				
			
			@ -28,16 +28,16 @@
 | 
			
		|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "gdextension_compat_hashes.h"
 | 
			
		||||
#include "gdextension_special_compat_hashes.h"
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
 | 
			
		||||
#include "core/object/class_db.h"
 | 
			
		||||
#include "core/variant/variant.h"
 | 
			
		||||
 | 
			
		||||
HashMap<StringName, LocalVector<GDExtensionCompatHashes::Mapping>> GDExtensionCompatHashes::mappings;
 | 
			
		||||
HashMap<StringName, LocalVector<GDExtensionSpecialCompatHashes::Mapping>> GDExtensionSpecialCompatHashes::mappings;
 | 
			
		||||
 | 
			
		||||
bool GDExtensionCompatHashes::lookup_current_hash(const StringName &p_class, const StringName &p_method, uint32_t p_legacy_hash, uint32_t *r_current_hash) {
 | 
			
		||||
bool GDExtensionSpecialCompatHashes::lookup_current_hash(const StringName &p_class, const StringName &p_method, uint32_t p_legacy_hash, uint32_t *r_current_hash) {
 | 
			
		||||
	LocalVector<Mapping> *methods = mappings.getptr(p_class);
 | 
			
		||||
	if (!methods) {
 | 
			
		||||
		return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -53,7 +53,7 @@ bool GDExtensionCompatHashes::lookup_current_hash(const StringName &p_class, con
 | 
			
		|||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes, bool p_check_valid) {
 | 
			
		||||
bool GDExtensionSpecialCompatHashes::get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes, bool p_check_valid) {
 | 
			
		||||
	LocalVector<Mapping> *methods = mappings.getptr(p_class);
 | 
			
		||||
	if (!methods) {
 | 
			
		||||
		return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +65,7 @@ bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const
 | 
			
		|||
			if (p_check_valid) {
 | 
			
		||||
				MethodBind *mb = ClassDB::get_method_with_compatibility(p_class, p_method, mapping.current_hash);
 | 
			
		||||
				if (!mb) {
 | 
			
		||||
					WARN_PRINT(vformat("Compatibility hash %d for %s::%s() mapped to non-existent hash %d. Please update gdextension_compat_hashes.cpp.", mapping.legacy_hash, p_class, p_method, mapping.current_hash));
 | 
			
		||||
					WARN_PRINT(vformat("Compatibility hash %d for %s::%s() mapped to non-existent hash %d. Please update gdextension_special_compat_hashes.cpp.", mapping.legacy_hash, p_class, p_method, mapping.current_hash));
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +77,7 @@ bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const
 | 
			
		|||
	return found;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GDExtensionCompatHashes::initialize() {
 | 
			
		||||
void GDExtensionSpecialCompatHashes::initialize() {
 | 
			
		||||
	// clang-format off
 | 
			
		||||
	mappings.insert("AESContext", {
 | 
			
		||||
		{ "start", 3167574919, 3122411423 },
 | 
			
		||||
| 
						 | 
				
			
			@ -103,6 +103,14 @@ void GDExtensionCompatHashes::initialize() {
 | 
			
		|||
	mappings.insert("AcceptDialog", {
 | 
			
		||||
		{ "add_button", 4158837846, 3328440682 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("AnimatedSprite2D", {
 | 
			
		||||
		{ "play", 2372066587, 3269405555 },
 | 
			
		||||
		{ "play_backwards", 1421762485, 3323268493 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("AnimatedSprite3D", {
 | 
			
		||||
		{ "play", 2372066587, 3269405555 },
 | 
			
		||||
		{ "play_backwards", 1421762485, 3323268493 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("Animation", {
 | 
			
		||||
		{ "add_track", 2393815928, 3843682357 },
 | 
			
		||||
		{ "track_insert_key", 1985425300, 808952278 },
 | 
			
		||||
| 
						 | 
				
			
			@ -146,6 +154,12 @@ void GDExtensionCompatHashes::initialize() {
 | 
			
		|||
		{ "travel", 3683006648, 3823612587 },
 | 
			
		||||
		{ "start", 3683006648, 3823612587 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("AnimationPlayer", {
 | 
			
		||||
		{ "play", 3697947785, 3118260607 },
 | 
			
		||||
		{ "play", 2221377757, 3118260607 },
 | 
			
		||||
		{ "play_backwards", 3890664824, 2787282401 },
 | 
			
		||||
		{ "play_with_capture", 3180464118, 1572969103 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("ArrayMesh", {
 | 
			
		||||
		{ "add_surface_from_arrays", 172284304, 1796411378 },
 | 
			
		||||
	});
 | 
			
		||||
| 
						 | 
				
			
			@ -241,22 +255,31 @@ void GDExtensionCompatHashes::initialize() {
 | 
			
		|||
#endif
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("DirAccess", {
 | 
			
		||||
		{ "list_dir_begin", 2018049411, 2610976713 },
 | 
			
		||||
		{ "list_dir_begin", 2018049411, 166280745 },
 | 
			
		||||
		{ "list_dir_begin", 2610976713, 166280745 },
 | 
			
		||||
		{ "copy", 198434953, 1063198817 },
 | 
			
		||||
		{ "copy_absolute", 198434953, 1063198817 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("DisplayServer", {
 | 
			
		||||
		{ "global_menu_add_submenu_item", 3806306913, 2828985934 },
 | 
			
		||||
		{ "global_menu_add_item", 3415468211, 3401266716 },
 | 
			
		||||
		{ "global_menu_add_check_item", 3415468211, 3401266716 },
 | 
			
		||||
		{ "global_menu_add_icon_item", 1700867534, 4245856523 },
 | 
			
		||||
		{ "global_menu_add_icon_check_item", 1700867534, 4245856523 },
 | 
			
		||||
		{ "global_menu_add_radio_check_item", 3415468211, 3401266716 },
 | 
			
		||||
		{ "global_menu_add_icon_radio_check_item", 1700867534, 4245856523 },
 | 
			
		||||
		{ "global_menu_add_multistate_item", 635750054, 3431222859 },
 | 
			
		||||
		{ "global_menu_add_item", 3415468211, 3616842746 },
 | 
			
		||||
		{ "global_menu_add_item", 3401266716, 3616842746 },
 | 
			
		||||
		{ "global_menu_add_check_item", 3415468211, 3616842746 },
 | 
			
		||||
		{ "global_menu_add_check_item", 3401266716, 3616842746 },
 | 
			
		||||
		{ "global_menu_add_icon_item", 1700867534, 3867083847 },
 | 
			
		||||
		{ "global_menu_add_icon_item", 4245856523, 3867083847 },
 | 
			
		||||
		{ "global_menu_add_icon_check_item", 1700867534, 3867083847 },
 | 
			
		||||
		{ "global_menu_add_icon_check_item", 4245856523, 3867083847 },
 | 
			
		||||
		{ "global_menu_add_radio_check_item", 3415468211, 3616842746 },
 | 
			
		||||
		{ "global_menu_add_radio_check_item", 3401266716, 3616842746 },
 | 
			
		||||
		{ "global_menu_add_icon_radio_check_item", 1700867534, 3867083847 },
 | 
			
		||||
		{ "global_menu_add_icon_radio_check_item", 4245856523, 3867083847 },
 | 
			
		||||
		{ "global_menu_add_multistate_item", 635750054, 3297554655 },
 | 
			
		||||
		{ "global_menu_add_multistate_item", 3431222859, 3297554655 },
 | 
			
		||||
		{ "global_menu_add_separator", 1041533178, 3214812433 },
 | 
			
		||||
		{ "tts_speak", 3741216677, 903992738 },
 | 
			
		||||
		{ "is_touchscreen_available", 4162880507, 3323674545 },
 | 
			
		||||
		{ "is_touchscreen_available", 4162880507, 36873697 },
 | 
			
		||||
		{ "is_touchscreen_available", 3323674545, 36873697 },
 | 
			
		||||
		{ "screen_set_orientation", 2629526904, 2211511631 },
 | 
			
		||||
		{ "window_get_native_handle", 2709193271, 1096425680 },
 | 
			
		||||
		{ "window_set_title", 3043792800, 441246282 },
 | 
			
		||||
| 
						 | 
				
			
			@ -286,6 +309,12 @@ void GDExtensionCompatHashes::initialize() {
 | 
			
		|||
		{ "virtual_keyboard_show", 860410478, 3042891259 },
 | 
			
		||||
#endif
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("EditorExportPlatform", {
 | 
			
		||||
		{ "export_project_files", 425454869, 1063735070 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("EditorProperty", {
 | 
			
		||||
		{ "emit_changed", 3069422438, 1822500399 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("ENetConnection", {
 | 
			
		||||
		{ "create_host_bound", 866250949, 1515002313 },
 | 
			
		||||
		{ "connect_to_host", 385984708, 2171300490 },
 | 
			
		||||
| 
						 | 
				
			
			@ -453,18 +482,35 @@ void GDExtensionCompatHashes::initialize() {
 | 
			
		|||
	mappings.insert("MultiplayerAPI", {
 | 
			
		||||
		{ "rpc", 1833408346, 2077486355 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("NativeMenu", {
 | 
			
		||||
		{ "add_item", 2553375659, 980552939 },
 | 
			
		||||
		{ "add_check_item", 2553375659, 980552939 },
 | 
			
		||||
		{ "add_icon_item", 2987595282, 1372188274 },
 | 
			
		||||
		{ "add_icon_check_item", 2987595282, 1372188274 },
 | 
			
		||||
		{ "add_radio_check_item", 2553375659, 980552939 },
 | 
			
		||||
		{ "add_icon_radio_check_item", 2987595282, 1372188274 },
 | 
			
		||||
		{ "add_multistate_item", 1558592568, 2674635658 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("NavigationMeshGenerator", {
 | 
			
		||||
		{ "parse_source_geometry_data", 3703028813, 685862123 },
 | 
			
		||||
		{ "bake_from_source_geometry_data", 3669016597, 2469318639 },
 | 
			
		||||
		{ "parse_source_geometry_data", 3703028813, 3172802542 },
 | 
			
		||||
		{ "parse_source_geometry_data", 685862123, 3172802542 },
 | 
			
		||||
		{ "bake_from_source_geometry_data", 3669016597, 1286748856 },
 | 
			
		||||
		{ "bake_from_source_geometry_data", 2469318639, 1286748856 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("NavigationServer2D", {
 | 
			
		||||
		{ "map_get_path", 56240621, 3146466012 },
 | 
			
		||||
		{ "parse_source_geometry_data", 1176164995, 1766905497 },
 | 
			
		||||
		{ "bake_from_source_geometry_data", 2909414286, 2179660022 },
 | 
			
		||||
		{ "bake_from_source_geometry_data_async", 2909414286, 2179660022 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("NavigationServer3D", {
 | 
			
		||||
		{ "map_get_path", 2121045993, 1187418690 },
 | 
			
		||||
		{ "parse_source_geometry_data", 3703028813, 685862123 },
 | 
			
		||||
		{ "bake_from_source_geometry_data", 3669016597, 2469318639 },
 | 
			
		||||
		{ "bake_from_source_geometry_data_async", 3669016597, 2469318639 },
 | 
			
		||||
		{ "parse_source_geometry_data", 3703028813, 3172802542 },
 | 
			
		||||
		{ "parse_source_geometry_data", 685862123, 3172802542 },
 | 
			
		||||
		{ "bake_from_source_geometry_data", 3669016597, 1286748856 },
 | 
			
		||||
		{ "bake_from_source_geometry_data", 2469318639, 1286748856 },
 | 
			
		||||
		{ "bake_from_source_geometry_data_async", 3669016597, 1286748856 },
 | 
			
		||||
		{ "bake_from_source_geometry_data_async", 2469318639, 1286748856 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("Node", {
 | 
			
		||||
		{ "add_child", 3070154285, 3863233950 },
 | 
			
		||||
| 
						 | 
				
			
			@ -504,6 +550,9 @@ void GDExtensionCompatHashes::initialize() {
 | 
			
		|||
		{ "tr", 2475554935, 1195764410 },
 | 
			
		||||
		{ "tr_n", 4021311862, 162698058 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("OpenXRAPIExtension", {
 | 
			
		||||
		{ "transform_from_pose", 3255299855, 2963875352 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("OptionButton", {
 | 
			
		||||
		{ "add_item", 3043792800, 2697778442 },
 | 
			
		||||
		{ "add_icon_item", 3944051090, 3781678508 },
 | 
			
		||||
| 
						 | 
				
			
			@ -631,6 +680,11 @@ void GDExtensionCompatHashes::initialize() {
 | 
			
		|||
	mappings.insert("ProjectSettings", {
 | 
			
		||||
		{ "load_resource_pack", 3001721055, 708980503 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("RDShaderFile", {
 | 
			
		||||
		{ "bake_from_source_geometry_data_async", 2469318639, 1286748856 },
 | 
			
		||||
		{ "set_bytecode", 1558064255, 1526857008 },
 | 
			
		||||
		{ "get_spirv", 3340165340, 2689310080 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("RegEx", {
 | 
			
		||||
		{ "search", 4087180739, 3365977994 },
 | 
			
		||||
		{ "search_all", 3354100289, 849021363 },
 | 
			
		||||
| 
						 | 
				
			
			@ -722,7 +776,7 @@ void GDExtensionCompatHashes::initialize() {
 | 
			
		|||
		{ "push_paragraph", 3218895358, 3089306873 },
 | 
			
		||||
		{ "push_list", 4036303897, 3017143144 },
 | 
			
		||||
		{ "push_table", 1125058220, 2623499273 },
 | 
			
		||||
		{ "set_table_column_expand", 4132157579, 2185176273 },
 | 
			
		||||
		{ "set_table_column_expand", 4258957458, 2185176273 },
 | 
			
		||||
#ifdef REAL_T_IS_DOUBLE
 | 
			
		||||
		{ "add_image", 3346058748, 1507062345 },
 | 
			
		||||
		{ "push_dropcap", 981432822, 763534173 },
 | 
			
		||||
| 
						 | 
				
			
			@ -863,6 +917,9 @@ void GDExtensionCompatHashes::initialize() {
 | 
			
		|||
		{ "set_cells_terrain_path", 3072115677, 3578627656 },
 | 
			
		||||
		{ "get_used_cells_by_id", 4152068407, 2931012785 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("TileMapLayer", {
 | 
			
		||||
		{ "notify_runtime_tile_data_update", 2275361663, 3218959716 },
 | 
			
		||||
	});
 | 
			
		||||
	mappings.insert("TileMapPattern", {
 | 
			
		||||
		{ "set_cell", 634000503, 2224802556 },
 | 
			
		||||
	});
 | 
			
		||||
| 
						 | 
				
			
			@ -964,7 +1021,7 @@ void GDExtensionCompatHashes::initialize() {
 | 
			
		|||
	// clang-format on
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GDExtensionCompatHashes::finalize() {
 | 
			
		||||
void GDExtensionSpecialCompatHashes::finalize() {
 | 
			
		||||
	mappings.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/**************************************************************************/
 | 
			
		||||
/*  gdextension_compat_hashes.h                                           */
 | 
			
		||||
/*  gdextension_special_compat_hashes.h                                   */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
| 
						 | 
				
			
			@ -28,8 +28,8 @@
 | 
			
		|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef GDEXTENSION_COMPAT_HASHES_H
 | 
			
		||||
#define GDEXTENSION_COMPAT_HASHES_H
 | 
			
		||||
#ifndef GDEXTENSION_SPECIAL_COMPAT_HASHES_H
 | 
			
		||||
#define GDEXTENSION_SPECIAL_COMPAT_HASHES_H
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +37,11 @@
 | 
			
		|||
#include "core/templates/hash_map.h"
 | 
			
		||||
#include "core/templates/local_vector.h"
 | 
			
		||||
 | 
			
		||||
class GDExtensionCompatHashes {
 | 
			
		||||
// Note: In most situations, compatibility methods should be registered via ClassDB::bind_compatibility_method().
 | 
			
		||||
//       This class is only meant to be used in exceptional circumstances, for example, when Godot's hashing
 | 
			
		||||
//       algorithm changes and registering compatibility methods for all affect methods would be onerous.
 | 
			
		||||
 | 
			
		||||
class GDExtensionSpecialCompatHashes {
 | 
			
		||||
	struct Mapping {
 | 
			
		||||
		StringName method;
 | 
			
		||||
		uint32_t legacy_hash;
 | 
			
		||||
| 
						 | 
				
			
			@ -55,4 +59,4 @@ public:
 | 
			
		|||
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
 | 
			
		||||
#endif // GDEXTENSION_COMPAT_HASHES_H
 | 
			
		||||
#endif // GDEXTENSION_SPECIAL_COMPAT_HASHES_H
 | 
			
		||||
| 
						 | 
				
			
			@ -55,10 +55,10 @@ def generate_mod_version(argcount, const=False, returns=False):
 | 
			
		|||
 | 
			
		||||
proto_ex = """
 | 
			
		||||
#define EXBIND$VER($RETTYPE m_name$ARG) \\
 | 
			
		||||
GDVIRTUAL$VER($RETTYPE_##m_name$ARG)\\
 | 
			
		||||
GDVIRTUAL$VER_REQUIRED($RETTYPE_##m_name$ARG)\\
 | 
			
		||||
virtual $RETVAL m_name($FUNCARGS) $CONST override { \\
 | 
			
		||||
    $RETPRE\\
 | 
			
		||||
    GDVIRTUAL_REQUIRED_CALL(_##m_name$CALLARGS$RETREF);\\
 | 
			
		||||
    GDVIRTUAL_CALL(_##m_name$CALLARGS$RETREF);\\
 | 
			
		||||
    $RETPOST\\
 | 
			
		||||
}
 | 
			
		||||
"""
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
from misc.utility.scons_hints import *
 | 
			
		||||
 | 
			
		||||
Import("env")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -8,7 +8,7 @@ __XINPUT_DEVICE__,XInput Gamepad,a:b12,b:b13,x:b14,y:b15,start:b4,guide:b10,back
 | 
			
		|||
Default Android Gamepad,Default Controller,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a4,x:b2,dpup:h0.1,back:b4,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Android,
 | 
			
		||||
 | 
			
		||||
# Web
 | 
			
		||||
standard,Standard Gamepad Mapping,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftstick:b10,rightstick:b11,platform:Web,
 | 
			
		||||
standard,Standard Gamepad Mapping,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:+a4,righttrigger:+a5,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftstick:b10,rightstick:b11,platform:Web,
 | 
			
		||||
Linux24c6581a,PowerA Xbox One Cabled,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
 | 
			
		||||
Linux0e6f0301,Logic 3 Controller (xbox compatible),a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
 | 
			
		||||
Linux045e028e,Microsoft X-Box 360 pad,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,6 +77,10 @@ Input *Input::singleton = nullptr;
 | 
			
		|||
 | 
			
		||||
void (*Input::set_mouse_mode_func)(Input::MouseMode) = nullptr;
 | 
			
		||||
Input::MouseMode (*Input::get_mouse_mode_func)() = nullptr;
 | 
			
		||||
void (*Input::set_mouse_mode_override_func)(Input::MouseMode) = nullptr;
 | 
			
		||||
Input::MouseMode (*Input::get_mouse_mode_override_func)() = nullptr;
 | 
			
		||||
void (*Input::set_mouse_mode_override_enabled_func)(bool) = nullptr;
 | 
			
		||||
bool (*Input::is_mouse_mode_override_enabled_func)() = nullptr;
 | 
			
		||||
void (*Input::warp_mouse_func)(const Vector2 &p_position) = nullptr;
 | 
			
		||||
Input::CursorShape (*Input::get_current_cursor_shape_func)() = nullptr;
 | 
			
		||||
void (*Input::set_custom_mouse_cursor_func)(const Ref<Resource> &, Input::CursorShape, const Vector2 &) = nullptr;
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +90,7 @@ Input *Input::get_singleton() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void Input::set_mouse_mode(MouseMode p_mode) {
 | 
			
		||||
	ERR_FAIL_INDEX((int)p_mode, 5);
 | 
			
		||||
	ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
 | 
			
		||||
	set_mouse_mode_func(p_mode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -94,6 +98,23 @@ Input::MouseMode Input::get_mouse_mode() const {
 | 
			
		|||
	return get_mouse_mode_func();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Input::set_mouse_mode_override(MouseMode p_mode) {
 | 
			
		||||
	ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
 | 
			
		||||
	set_mouse_mode_override_func(p_mode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Input::MouseMode Input::get_mouse_mode_override() const {
 | 
			
		||||
	return get_mouse_mode_override_func();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Input::set_mouse_mode_override_enabled(bool p_override_enabled) {
 | 
			
		||||
	set_mouse_mode_override_enabled_func(p_override_enabled);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Input::is_mouse_mode_override_enabled() {
 | 
			
		||||
	return is_mouse_mode_override_enabled_func();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Input::_bind_methods() {
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("is_anything_pressed"), &Input::is_anything_pressed);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed);
 | 
			
		||||
| 
						 | 
				
			
			@ -160,6 +181,7 @@ void Input::_bind_methods() {
 | 
			
		|||
	BIND_ENUM_CONSTANT(MOUSE_MODE_CAPTURED);
 | 
			
		||||
	BIND_ENUM_CONSTANT(MOUSE_MODE_CONFINED);
 | 
			
		||||
	BIND_ENUM_CONSTANT(MOUSE_MODE_CONFINED_HIDDEN);
 | 
			
		||||
	BIND_ENUM_CONSTANT(MOUSE_MODE_MAX);
 | 
			
		||||
 | 
			
		||||
	BIND_ENUM_CONSTANT(CURSOR_ARROW);
 | 
			
		||||
	BIND_ENUM_CONSTANT(CURSOR_IBEAM);
 | 
			
		||||
| 
						 | 
				
			
			@ -197,7 +219,7 @@ void Input::get_argument_options(const StringName &p_function, int p_idx, List<S
 | 
			
		|||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length());
 | 
			
		||||
			String name = pi.name.substr(pi.name.find_char('/') + 1, pi.name.length());
 | 
			
		||||
			r_options->push_back(name.quote());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -252,6 +274,10 @@ Input::VelocityTrack::VelocityTrack() {
 | 
			
		|||
bool Input::is_anything_pressed() const {
 | 
			
		||||
	_THREAD_SAFE_METHOD_
 | 
			
		||||
 | 
			
		||||
	if (disable_input) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!keys_pressed.is_empty() || !joy_buttons_pressed.is_empty() || !mouse_button_mask.is_empty()) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -265,23 +291,63 @@ bool Input::is_anything_pressed() const {
 | 
			
		|||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Input::is_anything_pressed_except_mouse() const {
 | 
			
		||||
	_THREAD_SAFE_METHOD_
 | 
			
		||||
 | 
			
		||||
	if (disable_input) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!keys_pressed.is_empty() || !joy_buttons_pressed.is_empty()) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (const KeyValue<StringName, Input::ActionState> &E : action_states) {
 | 
			
		||||
		if (E.value.cache.pressed) {
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Input::is_key_pressed(Key p_keycode) const {
 | 
			
		||||
	_THREAD_SAFE_METHOD_
 | 
			
		||||
 | 
			
		||||
	if (disable_input) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return keys_pressed.has(p_keycode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Input::is_physical_key_pressed(Key p_keycode) const {
 | 
			
		||||
	_THREAD_SAFE_METHOD_
 | 
			
		||||
 | 
			
		||||
	if (disable_input) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return physical_keys_pressed.has(p_keycode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Input::is_key_label_pressed(Key p_keycode) const {
 | 
			
		||||
	_THREAD_SAFE_METHOD_
 | 
			
		||||
 | 
			
		||||
	if (disable_input) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return key_label_pressed.has(p_keycode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Input::is_mouse_button_pressed(MouseButton p_button) const {
 | 
			
		||||
	_THREAD_SAFE_METHOD_
 | 
			
		||||
 | 
			
		||||
	if (disable_input) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return mouse_button_mask.has_flag(mouse_button_to_mask(p_button));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -295,11 +361,21 @@ static JoyButton _combine_device(JoyButton p_value, int p_device) {
 | 
			
		|||
 | 
			
		||||
bool Input::is_joy_button_pressed(int p_device, JoyButton p_button) const {
 | 
			
		||||
	_THREAD_SAFE_METHOD_
 | 
			
		||||
 | 
			
		||||
	if (disable_input) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return joy_buttons_pressed.has(_combine_device(p_button, p_device));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
 | 
			
		||||
 | 
			
		||||
	if (disable_input) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
 | 
			
		||||
	if (!E) {
 | 
			
		||||
		return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -310,6 +386,11 @@ bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const {
 | 
			
		|||
 | 
			
		||||
bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
 | 
			
		||||
 | 
			
		||||
	if (disable_input) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
 | 
			
		||||
	if (!E) {
 | 
			
		||||
		return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -331,6 +412,11 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con
 | 
			
		|||
 | 
			
		||||
bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
 | 
			
		||||
 | 
			
		||||
	if (disable_input) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
 | 
			
		||||
	if (!E) {
 | 
			
		||||
		return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -352,6 +438,11 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co
 | 
			
		|||
 | 
			
		||||
float Input::get_action_strength(const StringName &p_action, bool p_exact) const {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action));
 | 
			
		||||
 | 
			
		||||
	if (disable_input) {
 | 
			
		||||
		return 0.0f;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
 | 
			
		||||
	if (!E) {
 | 
			
		||||
		return 0.0f;
 | 
			
		||||
| 
						 | 
				
			
			@ -366,6 +457,11 @@ float Input::get_action_strength(const StringName &p_action, bool p_exact) const
 | 
			
		|||
 | 
			
		||||
float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) const {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action));
 | 
			
		||||
 | 
			
		||||
	if (disable_input) {
 | 
			
		||||
		return 0.0f;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
 | 
			
		||||
	if (!E) {
 | 
			
		||||
		return 0.0f;
 | 
			
		||||
| 
						 | 
				
			
			@ -410,6 +506,11 @@ Vector2 Input::get_vector(const StringName &p_negative_x, const StringName &p_po
 | 
			
		|||
 | 
			
		||||
float Input::get_joy_axis(int p_device, JoyAxis p_axis) const {
 | 
			
		||||
	_THREAD_SAFE_METHOD_
 | 
			
		||||
 | 
			
		||||
	if (disable_input) {
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	JoyAxis c = _combine_device(p_axis, p_device);
 | 
			
		||||
	if (_joy_axis.has(c)) {
 | 
			
		||||
		return _joy_axis[c];
 | 
			
		||||
| 
						 | 
				
			
			@ -491,10 +592,9 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_
 | 
			
		|||
		for (int i = 0; i < map_db.size(); i++) {
 | 
			
		||||
			if (js.uid == map_db[i].uid) {
 | 
			
		||||
				mapping = i;
 | 
			
		||||
				js.name = map_db[i].name;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		js.mapping = mapping;
 | 
			
		||||
		_set_joypad_mapping(js, mapping);
 | 
			
		||||
	} else {
 | 
			
		||||
		js.connected = false;
 | 
			
		||||
		for (int i = 0; i < (int)JoyButton::MAX; i++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -513,21 +613,49 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_
 | 
			
		|||
 | 
			
		||||
Vector3 Input::get_gravity() const {
 | 
			
		||||
	_THREAD_SAFE_METHOD_
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_ENABLED
 | 
			
		||||
	if (!gravity_enabled) {
 | 
			
		||||
		WARN_PRINT_ONCE("`input_devices/sensors/enable_gravity` is not enabled in project settings.");
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return gravity;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Vector3 Input::get_accelerometer() const {
 | 
			
		||||
	_THREAD_SAFE_METHOD_
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_ENABLED
 | 
			
		||||
	if (!accelerometer_enabled) {
 | 
			
		||||
		WARN_PRINT_ONCE("`input_devices/sensors/enable_accelerometer` is not enabled in project settings.");
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return accelerometer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Vector3 Input::get_magnetometer() const {
 | 
			
		||||
	_THREAD_SAFE_METHOD_
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_ENABLED
 | 
			
		||||
	if (!magnetometer_enabled) {
 | 
			
		||||
		WARN_PRINT_ONCE("`input_devices/sensors/enable_magnetometer` is not enabled in project settings.");
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return magnetometer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Vector3 Input::get_gyroscope() const {
 | 
			
		||||
	_THREAD_SAFE_METHOD_
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_ENABLED
 | 
			
		||||
	if (!gyroscope_enabled) {
 | 
			
		||||
		WARN_PRINT_ONCE("`input_devices/sensors/enable_gyroscope` is not enabled in project settings.");
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return gyroscope;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -662,6 +790,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
 | 
			
		|||
				button_event->set_canceled(st->is_canceled());
 | 
			
		||||
				button_event->set_button_index(MouseButton::LEFT);
 | 
			
		||||
				button_event->set_double_click(st->is_double_tap());
 | 
			
		||||
				button_event->set_window_id(st->get_window_id());
 | 
			
		||||
 | 
			
		||||
				BitField<MouseButtonMask> ev_bm = mouse_button_mask;
 | 
			
		||||
				if (st->is_pressed()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -699,6 +828,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
 | 
			
		|||
			motion_event->set_velocity(sd->get_velocity());
 | 
			
		||||
			motion_event->set_screen_velocity(sd->get_screen_velocity());
 | 
			
		||||
			motion_event->set_button_mask(mouse_button_mask);
 | 
			
		||||
			motion_event->set_window_id(sd->get_window_id());
 | 
			
		||||
 | 
			
		||||
			_parse_input_event_impl(motion_event, true);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -906,7 +1036,7 @@ void Input::action_release(const StringName &p_action) {
 | 
			
		|||
 | 
			
		||||
	// Create or retrieve existing action.
 | 
			
		||||
	ActionState &action_state = action_states[p_action];
 | 
			
		||||
	action_state.cache.pressed = 0;
 | 
			
		||||
	action_state.cache.pressed = false;
 | 
			
		||||
	action_state.cache.strength = 0.0;
 | 
			
		||||
	action_state.cache.raw_strength = 0.0;
 | 
			
		||||
	// As input may come in part way through a physics tick, the earliest we can react to it is the next physics tick.
 | 
			
		||||
| 
						 | 
				
			
			@ -1455,9 +1585,6 @@ void Input::parse_mapping(const String &p_mapping) {
 | 
			
		|||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CharString uid;
 | 
			
		||||
	uid.resize(17);
 | 
			
		||||
 | 
			
		||||
	mapping.uid = entry[0];
 | 
			
		||||
	mapping.name = entry[1];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1558,25 +1685,93 @@ void Input::add_joy_mapping(const String &p_mapping, bool p_update_existing) {
 | 
			
		|||
		for (KeyValue<int, Joypad> &E : joy_names) {
 | 
			
		||||
			Joypad &joy = E.value;
 | 
			
		||||
			if (joy.uid == uid) {
 | 
			
		||||
				joy.mapping = map_db.size() - 1;
 | 
			
		||||
				_set_joypad_mapping(joy, map_db.size() - 1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Input::remove_joy_mapping(const String &p_guid) {
 | 
			
		||||
	// One GUID can exist multiple times in `map_db`, and
 | 
			
		||||
	// `add_joy_mapping` can choose not to update the existing mapping,
 | 
			
		||||
	// so the indices can be all over the place. Therefore we need to remember them.
 | 
			
		||||
	Vector<int> removed_idx;
 | 
			
		||||
	int min_removed_idx = -1;
 | 
			
		||||
	int max_removed_idx = -1;
 | 
			
		||||
	int fallback_mapping_offset = 0;
 | 
			
		||||
 | 
			
		||||
	for (int i = map_db.size() - 1; i >= 0; i--) {
 | 
			
		||||
		if (p_guid == map_db[i].uid) {
 | 
			
		||||
			map_db.remove_at(i);
 | 
			
		||||
 | 
			
		||||
			if (max_removed_idx == -1) {
 | 
			
		||||
				max_removed_idx = i;
 | 
			
		||||
			}
 | 
			
		||||
			min_removed_idx = i;
 | 
			
		||||
			removed_idx.push_back(i);
 | 
			
		||||
 | 
			
		||||
			if (i < fallback_mapping) {
 | 
			
		||||
				fallback_mapping_offset++;
 | 
			
		||||
			} else if (i == fallback_mapping) {
 | 
			
		||||
				fallback_mapping = -1;
 | 
			
		||||
				WARN_PRINT_ONCE(vformat("Removed fallback joypad input mapping \"%s\". This could lead to joypads not working as intended.", p_guid));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (min_removed_idx == -1) {
 | 
			
		||||
		return; // Nothing removed.
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (fallback_mapping > 0) {
 | 
			
		||||
		// Fix the shifted index.
 | 
			
		||||
		fallback_mapping -= fallback_mapping_offset;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int removed_idx_size = removed_idx.size();
 | 
			
		||||
 | 
			
		||||
	// Update joypad mapping references: some
 | 
			
		||||
	// * should use the fallback_mapping (if set; if not, they get unmapped), or
 | 
			
		||||
	// * need their mapping reference fixed, because the deletion(s) offset them.
 | 
			
		||||
	for (KeyValue<int, Joypad> &E : joy_names) {
 | 
			
		||||
		Joypad &joy = E.value;
 | 
			
		||||
		if (joy.uid == p_guid) {
 | 
			
		||||
			joy.mapping = -1;
 | 
			
		||||
		if (joy.mapping < min_removed_idx) {
 | 
			
		||||
			continue; // Not affected.
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (joy.mapping > max_removed_idx) {
 | 
			
		||||
			_set_joypad_mapping(joy, joy.mapping - removed_idx_size);
 | 
			
		||||
			continue; // Simple offset fix.
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// removed_idx is in reverse order (ie. high to low), because the first loop is in reverse order.
 | 
			
		||||
		for (int i = 0; i < removed_idx.size(); i++) {
 | 
			
		||||
			if (removed_idx[i] == joy.mapping) {
 | 
			
		||||
				// Set to fallback_mapping, if defined, else unmap the joypad.
 | 
			
		||||
				// Currently, the fallback_mapping is only set internally, and only for Android.
 | 
			
		||||
				_set_joypad_mapping(joy, fallback_mapping);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			if (removed_idx[i] < joy.mapping) {
 | 
			
		||||
				// Complex offset fix:
 | 
			
		||||
				// This mapping was shifted by `(removed_idx_size - i)` deletions.
 | 
			
		||||
				_set_joypad_mapping(joy, joy.mapping - (removed_idx_size - i));
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Input::_set_joypad_mapping(Joypad &p_js, int p_map_index) {
 | 
			
		||||
	if (p_map_index != fallback_mapping && p_map_index >= 0 && p_map_index < map_db.size() && p_js.uid != "__XINPUT_DEVICE__") {
 | 
			
		||||
		// Prefer the joypad name defined in the mapping.
 | 
			
		||||
		// Exceptions:
 | 
			
		||||
		// * On Windows for XInput devices the mapping would change the joypad's name to a collective name.
 | 
			
		||||
		// * A fallback mapping is not allowed to override the joypad's name.
 | 
			
		||||
		p_js.name = map_db[p_map_index].name;
 | 
			
		||||
	}
 | 
			
		||||
	p_js.mapping = p_map_index;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Input::set_fallback_mapping(const String &p_guid) {
 | 
			
		||||
	for (int i = 0; i < map_db.size(); i++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1634,6 +1829,14 @@ int Input::get_unused_joy_id() {
 | 
			
		|||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Input::set_disable_input(bool p_disable) {
 | 
			
		||||
	disable_input = p_disable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Input::is_input_disabled() const {
 | 
			
		||||
	return disable_input;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Input::Input() {
 | 
			
		||||
	singleton = this;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1683,6 +1886,11 @@ Input::Input() {
 | 
			
		|||
		// Always use standard behavior in the editor.
 | 
			
		||||
		legacy_just_pressed_behavior = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	accelerometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_accelerometer", false);
 | 
			
		||||
	gravity_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gravity", false);
 | 
			
		||||
	gyroscope_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gyroscope", false);
 | 
			
		||||
	magnetometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_magnetometer", false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Input::~Input() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,12 +47,14 @@ class Input : public Object {
 | 
			
		|||
	static constexpr uint64_t MAX_EVENT = 32;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	// Keep synced with "DisplayServer::MouseMode" enum.
 | 
			
		||||
	enum MouseMode {
 | 
			
		||||
		MOUSE_MODE_VISIBLE,
 | 
			
		||||
		MOUSE_MODE_HIDDEN,
 | 
			
		||||
		MOUSE_MODE_CAPTURED,
 | 
			
		||||
		MOUSE_MODE_CONFINED,
 | 
			
		||||
		MOUSE_MODE_CONFINED_HIDDEN,
 | 
			
		||||
		MOUSE_MODE_MAX,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
#undef CursorShape
 | 
			
		||||
| 
						 | 
				
			
			@ -92,13 +94,18 @@ private:
 | 
			
		|||
	RBSet<JoyButton> joy_buttons_pressed;
 | 
			
		||||
	RBMap<JoyAxis, float> _joy_axis;
 | 
			
		||||
	//RBMap<StringName,int> custom_action_press;
 | 
			
		||||
	bool gravity_enabled = false;
 | 
			
		||||
	Vector3 gravity;
 | 
			
		||||
	bool accelerometer_enabled = false;
 | 
			
		||||
	Vector3 accelerometer;
 | 
			
		||||
	bool magnetometer_enabled = false;
 | 
			
		||||
	Vector3 magnetometer;
 | 
			
		||||
	bool gyroscope_enabled = false;
 | 
			
		||||
	Vector3 gyroscope;
 | 
			
		||||
	Vector2 mouse_pos;
 | 
			
		||||
	int64_t mouse_window = 0;
 | 
			
		||||
	bool legacy_just_pressed_behavior = false;
 | 
			
		||||
	bool disable_input = false;
 | 
			
		||||
 | 
			
		||||
	struct ActionState {
 | 
			
		||||
		uint64_t pressed_physics_frame = UINT64_MAX;
 | 
			
		||||
| 
						 | 
				
			
			@ -175,7 +182,7 @@ private:
 | 
			
		|||
 | 
			
		||||
	HashSet<uint32_t> ignored_device_ids;
 | 
			
		||||
 | 
			
		||||
	int fallback_mapping = -1;
 | 
			
		||||
	int fallback_mapping = -1; // Index of the guid in map_db.
 | 
			
		||||
 | 
			
		||||
	CursorShape default_shape = CURSOR_ARROW;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -236,6 +243,8 @@ private:
 | 
			
		|||
 | 
			
		||||
	Vector<JoyDeviceMapping> map_db;
 | 
			
		||||
 | 
			
		||||
	void _set_joypad_mapping(Joypad &p_js, int p_map_index);
 | 
			
		||||
 | 
			
		||||
	JoyEvent _get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button);
 | 
			
		||||
	JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value, JoyAxisRange &r_range);
 | 
			
		||||
	void _get_mapped_hat_events(const JoyDeviceMapping &mapping, HatDir p_hat, JoyEvent r_events[(size_t)HatDir::MAX]);
 | 
			
		||||
| 
						 | 
				
			
			@ -257,6 +266,10 @@ private:
 | 
			
		|||
 | 
			
		||||
	static void (*set_mouse_mode_func)(MouseMode);
 | 
			
		||||
	static MouseMode (*get_mouse_mode_func)();
 | 
			
		||||
	static void (*set_mouse_mode_override_func)(MouseMode);
 | 
			
		||||
	static MouseMode (*get_mouse_mode_override_func)();
 | 
			
		||||
	static void (*set_mouse_mode_override_enabled_func)(bool);
 | 
			
		||||
	static bool (*is_mouse_mode_override_enabled_func)();
 | 
			
		||||
	static void (*warp_mouse_func)(const Vector2 &p_position);
 | 
			
		||||
 | 
			
		||||
	static CursorShape (*get_current_cursor_shape_func)();
 | 
			
		||||
| 
						 | 
				
			
			@ -275,6 +288,10 @@ protected:
 | 
			
		|||
public:
 | 
			
		||||
	void set_mouse_mode(MouseMode p_mode);
 | 
			
		||||
	MouseMode get_mouse_mode() const;
 | 
			
		||||
	void set_mouse_mode_override(MouseMode p_mode);
 | 
			
		||||
	MouseMode get_mouse_mode_override() const;
 | 
			
		||||
	void set_mouse_mode_override_enabled(bool p_override_enabled);
 | 
			
		||||
	bool is_mouse_mode_override_enabled();
 | 
			
		||||
 | 
			
		||||
#ifdef TOOLS_ENABLED
 | 
			
		||||
	void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
 | 
			
		||||
| 
						 | 
				
			
			@ -283,6 +300,7 @@ public:
 | 
			
		|||
	static Input *get_singleton();
 | 
			
		||||
 | 
			
		||||
	bool is_anything_pressed() const;
 | 
			
		||||
	bool is_anything_pressed_except_mouse() const;
 | 
			
		||||
	bool is_key_pressed(Key p_keycode) const;
 | 
			
		||||
	bool is_physical_key_pressed(Key p_keycode) const;
 | 
			
		||||
	bool is_key_label_pressed(Key p_keycode) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -376,6 +394,9 @@ public:
 | 
			
		|||
 | 
			
		||||
	void set_event_dispatch_function(EventDispatchFunc p_function);
 | 
			
		||||
 | 
			
		||||
	void set_disable_input(bool p_disable);
 | 
			
		||||
	bool is_input_disabled() const;
 | 
			
		||||
 | 
			
		||||
	Input();
 | 
			
		||||
	~Input();
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,7 +33,7 @@ def make_default_controller_mappings(target, source, env):
 | 
			
		|||
                    guid = line_parts[0]
 | 
			
		||||
                    if guid in platform_mappings[current_platform]:
 | 
			
		||||
                        g.write(
 | 
			
		||||
                            "// WARNING - DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format(
 | 
			
		||||
                            "// WARNING: DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format(
 | 
			
		||||
                                src_path, current_platform, platform_mappings[current_platform][guid]
 | 
			
		||||
                            )
 | 
			
		||||
                        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,8 @@
 | 
			
		|||
#ifndef INPUT_ENUMS_H
 | 
			
		||||
#define INPUT_ENUMS_H
 | 
			
		||||
 | 
			
		||||
#include "core/error/error_macros.h"
 | 
			
		||||
 | 
			
		||||
enum class HatDir {
 | 
			
		||||
	UP = 0,
 | 
			
		||||
	RIGHT = 1,
 | 
			
		||||
| 
						 | 
				
			
			@ -131,6 +133,8 @@ enum class MouseButtonMask {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
inline MouseButtonMask mouse_button_to_mask(MouseButton button) {
 | 
			
		||||
	ERR_FAIL_COND_V(button == MouseButton::NONE, MouseButtonMask::NONE);
 | 
			
		||||
 | 
			
		||||
	return MouseButtonMask(1 << ((int)button - 1));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -754,6 +754,8 @@ Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, co
 | 
			
		|||
	mb->set_factor(factor);
 | 
			
		||||
	mb->set_button_index(button_index);
 | 
			
		||||
 | 
			
		||||
	mb->merge_meta_from(this);
 | 
			
		||||
 | 
			
		||||
	return mb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -974,6 +976,8 @@ Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, co
 | 
			
		|||
	mm->set_velocity(p_xform.basis_xform(get_velocity()));
 | 
			
		||||
	mm->set_screen_velocity(get_screen_velocity());
 | 
			
		||||
 | 
			
		||||
	mm->merge_meta_from(this);
 | 
			
		||||
 | 
			
		||||
	return mm;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1088,7 +1092,7 @@ void InputEventMouseMotion::_bind_methods() {
 | 
			
		|||
///////////////////////////////////
 | 
			
		||||
 | 
			
		||||
void InputEventJoypadMotion::set_axis(JoyAxis p_axis) {
 | 
			
		||||
	ERR_FAIL_COND(p_axis < JoyAxis::LEFT_X || p_axis > JoyAxis::MAX);
 | 
			
		||||
	ERR_FAIL_COND(p_axis < JoyAxis::INVALID || p_axis > JoyAxis::MAX);
 | 
			
		||||
 | 
			
		||||
	axis = p_axis;
 | 
			
		||||
	emit_changed();
 | 
			
		||||
| 
						 | 
				
			
			@ -1100,7 +1104,7 @@ JoyAxis InputEventJoypadMotion::get_axis() const {
 | 
			
		|||
 | 
			
		||||
void InputEventJoypadMotion::set_axis_value(float p_value) {
 | 
			
		||||
	axis_value = p_value;
 | 
			
		||||
	pressed = Math::abs(axis_value) >= 0.5f;
 | 
			
		||||
	pressed = Math::abs(axis_value) >= InputMap::DEFAULT_DEADZONE;
 | 
			
		||||
	emit_changed();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1366,6 +1370,8 @@ Ref<InputEvent> InputEventScreenTouch::xformed_by(const Transform2D &p_xform, co
 | 
			
		|||
	st->set_canceled(canceled);
 | 
			
		||||
	st->set_double_tap(double_tap);
 | 
			
		||||
 | 
			
		||||
	st->merge_meta_from(this);
 | 
			
		||||
 | 
			
		||||
	return st;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1494,6 +1500,8 @@ Ref<InputEvent> InputEventScreenDrag::xformed_by(const Transform2D &p_xform, con
 | 
			
		|||
	sd->set_velocity(p_xform.basis_xform(velocity));
 | 
			
		||||
	sd->set_screen_velocity(get_screen_velocity());
 | 
			
		||||
 | 
			
		||||
	sd->merge_meta_from(this);
 | 
			
		||||
 | 
			
		||||
	return sd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1705,6 +1713,8 @@ Ref<InputEvent> InputEventMagnifyGesture::xformed_by(const Transform2D &p_xform,
 | 
			
		|||
	ev->set_position(p_xform.xform(get_position() + p_local_ofs));
 | 
			
		||||
	ev->set_factor(get_factor());
 | 
			
		||||
 | 
			
		||||
	ev->merge_meta_from(this);
 | 
			
		||||
 | 
			
		||||
	return ev;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1745,6 +1755,8 @@ Ref<InputEvent> InputEventPanGesture::xformed_by(const Transform2D &p_xform, con
 | 
			
		|||
	ev->set_position(p_xform.xform(get_position() + p_local_ofs));
 | 
			
		||||
	ev->set_delta(get_delta());
 | 
			
		||||
 | 
			
		||||
	ev->merge_meta_from(this);
 | 
			
		||||
 | 
			
		||||
	return ev;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										41
									
								
								engine/core/input/input_map.compat.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								engine/core/input/input_map.compat.inc
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
/**************************************************************************/
 | 
			
		||||
/*  input_map.compat.inc                                                  */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
 | 
			
		||||
void InputMap::_add_action_bind_compat_97281(const StringName &p_action, float p_deadzone) {
 | 
			
		||||
	add_action(p_action, p_deadzone);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void InputMap::_bind_compatibility_methods() {
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::_add_action_bind_compat_97281, DEFVAL(0.5f));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +29,7 @@
 | 
			
		|||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "input_map.h"
 | 
			
		||||
#include "input_map.compat.inc"
 | 
			
		||||
 | 
			
		||||
#include "core/config/project_settings.h"
 | 
			
		||||
#include "core/input/input.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +44,7 @@ int InputMap::ALL_DEVICES = -1;
 | 
			
		|||
void InputMap::_bind_methods() {
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("has_action", "action"), &InputMap::has_action);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_actions"), &InputMap::_get_actions);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(0.5f));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(DEFAULT_DEADZONE));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action);
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("action_set_deadzone", "action", "deadzone"), &InputMap::action_set_deadzone);
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +106,7 @@ void InputMap::get_argument_options(const StringName &p_function, int p_idx, Lis
 | 
			
		|||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length());
 | 
			
		||||
			String name = pi.name.substr(pi.name.find_char('/') + 1, pi.name.length());
 | 
			
		||||
			r_options->push_back(name.quote());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -115,7 +116,7 @@ void InputMap::get_argument_options(const StringName &p_function, int p_idx, Lis
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
void InputMap::add_action(const StringName &p_action, float p_deadzone) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(input_map.has(p_action), "InputMap already has action \"" + String(p_action) + "\".");
 | 
			
		||||
	ERR_FAIL_COND_MSG(input_map.has(p_action), vformat("InputMap already has action \"%s\".", String(p_action)));
 | 
			
		||||
	input_map[p_action] = Action();
 | 
			
		||||
	static int last_id = 1;
 | 
			
		||||
	input_map[p_action].id = last_id;
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +158,7 @@ List<StringName> InputMap::get_actions() const {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength, int *r_event_index) const {
 | 
			
		||||
	ERR_FAIL_COND_V(!p_event.is_valid(), nullptr);
 | 
			
		||||
	ERR_FAIL_COND_V(p_event.is_null(), nullptr);
 | 
			
		||||
 | 
			
		||||
	int i = 0;
 | 
			
		||||
	for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -253,8 +254,8 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName
 | 
			
		|||
 | 
			
		||||
int InputMap::event_get_index(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match) const {
 | 
			
		||||
	int index = -1;
 | 
			
		||||
	event_get_action_status(p_event, p_action, p_exact_match, nullptr, nullptr, nullptr, &index);
 | 
			
		||||
	return index;
 | 
			
		||||
	bool valid = event_get_action_status(p_event, p_action, p_exact_match, nullptr, nullptr, nullptr, &index);
 | 
			
		||||
	return valid ? index : -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength, int *r_event_index) const {
 | 
			
		||||
| 
						 | 
				
			
			@ -303,10 +304,10 @@ void InputMap::load_from_project_settings() {
 | 
			
		|||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length());
 | 
			
		||||
		String name = pi.name.substr(pi.name.find_char('/') + 1, pi.name.length());
 | 
			
		||||
 | 
			
		||||
		Dictionary action = GLOBAL_GET(pi.name);
 | 
			
		||||
		float deadzone = action.has("deadzone") ? (float)action["deadzone"] : 0.5f;
 | 
			
		||||
		float deadzone = action.has("deadzone") ? (float)action["deadzone"] : DEFAULT_DEADZONE;
 | 
			
		||||
		Array events = action["events"];
 | 
			
		||||
 | 
			
		||||
		add_action(name, deadzone);
 | 
			
		||||
| 
						 | 
				
			
			@ -400,6 +401,7 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
 | 
			
		|||
    { "ui_filedialog_refresh",                         TTRC("Refresh") },
 | 
			
		||||
    { "ui_filedialog_show_hidden",                     TTRC("Show Hidden") },
 | 
			
		||||
    { "ui_swap_input_direction ",                      TTRC("Swap Input Direction") },
 | 
			
		||||
    { "ui_unicode_start",                              TTRC("Start Unicode Character Input") },
 | 
			
		||||
    { "",                                              ""}
 | 
			
		||||
	/* clang-format on */
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -517,12 +519,15 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
 | 
			
		|||
	default_builtin_cache.insert("ui_text_completion_query", inputs);
 | 
			
		||||
 | 
			
		||||
	inputs = List<Ref<InputEvent>>();
 | 
			
		||||
	inputs.push_back(InputEventKey::create_reference(Key::ENTER));
 | 
			
		||||
	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));
 | 
			
		||||
	inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::TAB));
 | 
			
		||||
	inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::ENTER));
 | 
			
		||||
	inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::KP_ENTER));
 | 
			
		||||
	default_builtin_cache.insert("ui_text_completion_accept", inputs);
 | 
			
		||||
 | 
			
		||||
	inputs = List<Ref<InputEvent>>();
 | 
			
		||||
	inputs.push_back(InputEventKey::create_reference(Key::TAB));
 | 
			
		||||
	inputs.push_back(InputEventKey::create_reference(Key::ENTER));
 | 
			
		||||
	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));
 | 
			
		||||
	default_builtin_cache.insert("ui_text_completion_replace", inputs);
 | 
			
		||||
 | 
			
		||||
	// Newlines
 | 
			
		||||
| 
						 | 
				
			
			@ -532,7 +537,6 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
 | 
			
		|||
	default_builtin_cache.insert("ui_text_newline", inputs);
 | 
			
		||||
 | 
			
		||||
	inputs = List<Ref<InputEvent>>();
 | 
			
		||||
 | 
			
		||||
	inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::CMD_OR_CTRL));
 | 
			
		||||
	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER | KeyModifierMask::CMD_OR_CTRL));
 | 
			
		||||
	default_builtin_cache.insert("ui_text_newline_blank", inputs);
 | 
			
		||||
| 
						 | 
				
			
			@ -754,6 +758,10 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
 | 
			
		|||
	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));
 | 
			
		||||
	default_builtin_cache.insert("ui_text_submit", inputs);
 | 
			
		||||
 | 
			
		||||
	inputs = List<Ref<InputEvent>>();
 | 
			
		||||
	inputs.push_back(InputEventKey::create_reference(Key::U | KeyModifierMask::CTRL | KeyModifierMask::SHIFT));
 | 
			
		||||
	default_builtin_cache.insert("ui_unicode_start", inputs);
 | 
			
		||||
 | 
			
		||||
	// ///// UI Graph Shortcuts /////
 | 
			
		||||
 | 
			
		||||
	inputs = List<Ref<InputEvent>>();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,6 +54,8 @@ public:
 | 
			
		|||
		List<Ref<InputEvent>> inputs;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	static constexpr float DEFAULT_DEADZONE = 0.2f;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	static InputMap *singleton;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -69,12 +71,17 @@ private:
 | 
			
		|||
protected:
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
	void _add_action_bind_compat_97281(const StringName &p_action, float p_deadzone = 0.5);
 | 
			
		||||
	static void _bind_compatibility_methods();
 | 
			
		||||
#endif // DISABLE_DEPRECATED
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static _FORCE_INLINE_ InputMap *get_singleton() { return singleton; }
 | 
			
		||||
 | 
			
		||||
	bool has_action(const StringName &p_action) const;
 | 
			
		||||
	List<StringName> get_actions() const;
 | 
			
		||||
	void add_action(const StringName &p_action, float p_deadzone = 0.5);
 | 
			
		||||
	void add_action(const StringName &p_action, float p_deadzone = DEFAULT_DEADZONE);
 | 
			
		||||
	void erase_action(const StringName &p_action);
 | 
			
		||||
 | 
			
		||||
	float action_get_deadzone(const StringName &p_action);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,7 +29,6 @@
 | 
			
		|||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "shortcut.h"
 | 
			
		||||
#include "core/os/keyboard.h"
 | 
			
		||||
 | 
			
		||||
void Shortcut::set_events(const Array &p_events) {
 | 
			
		||||
	for (int i = 0; i < p_events.size(); i++) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
from misc.utility.scons_hints import *
 | 
			
		||||
 | 
			
		||||
Import("env")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,7 +43,7 @@ public:
 | 
			
		|||
	static int zstd_window_log_size;
 | 
			
		||||
	static int gzip_chunk;
 | 
			
		||||
 | 
			
		||||
	enum Mode {
 | 
			
		||||
	enum Mode : int32_t {
 | 
			
		||||
		MODE_FASTLZ,
 | 
			
		||||
		MODE_DEFLATE,
 | 
			
		||||
		MODE_ZSTD,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,7 +31,6 @@
 | 
			
		|||
#include "config_file.h"
 | 
			
		||||
 | 
			
		||||
#include "core/io/file_access_encrypted.h"
 | 
			
		||||
#include "core/os/keyboard.h"
 | 
			
		||||
#include "core/string/string_builder.h"
 | 
			
		||||
#include "core/variant/variant_parser.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,8 +32,8 @@
 | 
			
		|||
 | 
			
		||||
#include "core/config/project_settings.h"
 | 
			
		||||
#include "core/io/file_access.h"
 | 
			
		||||
#include "core/os/memory.h"
 | 
			
		||||
#include "core/os/os.h"
 | 
			
		||||
#include "core/os/time.h"
 | 
			
		||||
#include "core/templates/local_vector.h"
 | 
			
		||||
 | 
			
		||||
thread_local Error DirAccess::last_dir_open_error = OK;
 | 
			
		||||
| 
						 | 
				
			
			@ -155,9 +155,9 @@ Error DirAccess::make_dir_recursive(const String &p_dir) {
 | 
			
		|||
	} else if (full_dir.begins_with("user://")) {
 | 
			
		||||
		base = "user://";
 | 
			
		||||
	} else if (full_dir.is_network_share_path()) {
 | 
			
		||||
		int pos = full_dir.find("/", 2);
 | 
			
		||||
		int pos = full_dir.find_char('/', 2);
 | 
			
		||||
		ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
 | 
			
		||||
		pos = full_dir.find("/", pos + 1);
 | 
			
		||||
		pos = full_dir.find_char('/', pos + 1);
 | 
			
		||||
		ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
 | 
			
		||||
		base = full_dir.substr(0, pos + 1);
 | 
			
		||||
	} else if (full_dir.begins_with("/")) {
 | 
			
		||||
| 
						 | 
				
			
			@ -177,7 +177,7 @@ Error DirAccess::make_dir_recursive(const String &p_dir) {
 | 
			
		|||
		curpath = curpath.path_join(subdirs[i]);
 | 
			
		||||
		Error err = make_dir(curpath);
 | 
			
		||||
		if (err != OK && err != ERR_ALREADY_EXISTS) {
 | 
			
		||||
			ERR_FAIL_V_MSG(err, "Could not create directory: " + curpath);
 | 
			
		||||
			ERR_FAIL_V_MSG(err, vformat("Could not create directory: '%s'.", curpath));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -239,7 +239,7 @@ Ref<DirAccess> DirAccess::create_for_path(const String &p_path) {
 | 
			
		|||
 | 
			
		||||
Ref<DirAccess> DirAccess::open(const String &p_path, Error *r_error) {
 | 
			
		||||
	Ref<DirAccess> da = create_for_path(p_path);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(da.is_null(), nullptr, "Cannot create DirAccess for path '" + p_path + "'.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(da.is_null(), nullptr, vformat("Cannot create DirAccess for path '%s'.", p_path));
 | 
			
		||||
	Error err = da->change_dir(p_path);
 | 
			
		||||
	if (r_error) {
 | 
			
		||||
		*r_error = err;
 | 
			
		||||
| 
						 | 
				
			
			@ -323,6 +323,80 @@ Ref<DirAccess> DirAccess::create(AccessType p_access) {
 | 
			
		|||
	return da;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<DirAccess> DirAccess::create_temp(const String &p_prefix, bool p_keep, Error *r_error) {
 | 
			
		||||
	const String ERROR_COMMON_PREFIX = "Error while creating temporary directory";
 | 
			
		||||
 | 
			
		||||
	if (!p_prefix.is_valid_filename()) {
 | 
			
		||||
		*r_error = ERR_FILE_BAD_PATH;
 | 
			
		||||
		ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: "%s" is not a valid prefix.)", ERROR_COMMON_PREFIX, p_prefix));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Ref<DirAccess> dir_access = DirAccess::open(OS::get_singleton()->get_temp_path());
 | 
			
		||||
 | 
			
		||||
	uint32_t suffix_i = 0;
 | 
			
		||||
	String path;
 | 
			
		||||
	while (true) {
 | 
			
		||||
		String datetime = Time::get_singleton()->get_datetime_string_from_system().replace("-", "").replace("T", "").replace(":", "");
 | 
			
		||||
		datetime += itos(Time::get_singleton()->get_ticks_usec());
 | 
			
		||||
		String suffix = datetime + (suffix_i > 0 ? itos(suffix_i) : "");
 | 
			
		||||
		path = (p_prefix.is_empty() ? "" : p_prefix + "-") + suffix;
 | 
			
		||||
		if (!path.is_valid_filename()) {
 | 
			
		||||
			*r_error = ERR_FILE_BAD_PATH;
 | 
			
		||||
			return Ref<DirAccess>();
 | 
			
		||||
		}
 | 
			
		||||
		if (!DirAccess::exists(path)) {
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		suffix_i += 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Error err = dir_access->make_dir(path);
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		*r_error = err;
 | 
			
		||||
		ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: "%s" couldn't create directory "%s".)", ERROR_COMMON_PREFIX, path));
 | 
			
		||||
	}
 | 
			
		||||
	err = dir_access->change_dir(path);
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		*r_error = err;
 | 
			
		||||
		return Ref<DirAccess>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dir_access->_is_temp = true;
 | 
			
		||||
	dir_access->_temp_keep_after_free = p_keep;
 | 
			
		||||
	dir_access->_temp_path = dir_access->get_current_dir();
 | 
			
		||||
 | 
			
		||||
	*r_error = OK;
 | 
			
		||||
	return dir_access;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<DirAccess> DirAccess::_create_temp(const String &p_prefix, bool p_keep) {
 | 
			
		||||
	return create_temp(p_prefix, p_keep, &last_dir_open_error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DirAccess::_delete_temp() {
 | 
			
		||||
	if (!_is_temp || _temp_keep_after_free) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!DirAccess::exists(_temp_path)) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Error err;
 | 
			
		||||
	{
 | 
			
		||||
		Ref<DirAccess> dir_access = DirAccess::open(_temp_path, &err);
 | 
			
		||||
		if (err != OK) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		err = dir_access->erase_contents_recursive();
 | 
			
		||||
		if (err != OK) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DirAccess::remove_absolute(_temp_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error DirAccess::get_open_error() {
 | 
			
		||||
	return last_dir_open_error;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -345,10 +419,10 @@ Error DirAccess::copy(const String &p_from, const String &p_to, int p_chmod_flag
 | 
			
		|||
	Error err;
 | 
			
		||||
	{
 | 
			
		||||
		Ref<FileAccess> fsrc = FileAccess::open(p_from, FileAccess::READ, &err);
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to open " + p_from);
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to open '%s'.", p_from));
 | 
			
		||||
 | 
			
		||||
		Ref<FileAccess> fdst = FileAccess::open(p_to, FileAccess::WRITE, &err);
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to open " + p_to);
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to open '%s'.", p_to));
 | 
			
		||||
 | 
			
		||||
		const size_t copy_buffer_limit = 65536; // 64 KB
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -444,11 +518,11 @@ Error DirAccess::_copy_dir(Ref<DirAccess> &p_target_da, const String &p_to, int
 | 
			
		|||
		String target_dir = p_to + rel_path;
 | 
			
		||||
		if (!p_target_da->dir_exists(target_dir)) {
 | 
			
		||||
			Error err = p_target_da->make_dir(target_dir);
 | 
			
		||||
			ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + target_dir + "'.");
 | 
			
		||||
			ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot create directory '%s'.", target_dir));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Error err = change_dir(rel_path);
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + rel_path + "'.");
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot change current directory to '%s'.", rel_path));
 | 
			
		||||
 | 
			
		||||
		err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags, p_copy_links);
 | 
			
		||||
		if (err) {
 | 
			
		||||
| 
						 | 
				
			
			@ -466,11 +540,11 @@ Error DirAccess::copy_dir(const String &p_from, String p_to, int p_chmod_flags,
 | 
			
		|||
	ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist.");
 | 
			
		||||
 | 
			
		||||
	Ref<DirAccess> target_da = DirAccess::create_for_path(p_to);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(target_da.is_null(), ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_to + "'.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(target_da.is_null(), ERR_CANT_CREATE, vformat("Cannot create DirAccess for path '%s'.", p_to));
 | 
			
		||||
 | 
			
		||||
	if (!target_da->dir_exists(p_to)) {
 | 
			
		||||
		Error err = target_da->make_dir_recursive(p_to);
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + p_to + "'.");
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot create directory '%s'.", p_to));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!p_to.ends_with("/")) {
 | 
			
		||||
| 
						 | 
				
			
			@ -555,8 +629,9 @@ bool DirAccess::is_case_sensitive(const String &p_path) const {
 | 
			
		|||
void DirAccess::_bind_methods() {
 | 
			
		||||
	ClassDB::bind_static_method("DirAccess", D_METHOD("open", "path"), &DirAccess::_open);
 | 
			
		||||
	ClassDB::bind_static_method("DirAccess", D_METHOD("get_open_error"), &DirAccess::get_open_error);
 | 
			
		||||
	ClassDB::bind_static_method("DirAccess", D_METHOD("create_temp", "prefix", "keep"), &DirAccess::_create_temp, DEFVAL(""), DEFVAL(false));
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("list_dir_begin"), &DirAccess::list_dir_begin, DEFVAL(false), DEFVAL(false));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("list_dir_begin"), &DirAccess::list_dir_begin);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_next"), &DirAccess::_get_next);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("current_is_dir"), &DirAccess::current_is_dir);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("list_dir_end"), &DirAccess::list_dir_end);
 | 
			
		||||
| 
						 | 
				
			
			@ -588,6 +663,8 @@ void DirAccess::_bind_methods() {
 | 
			
		|||
	ClassDB::bind_method(D_METHOD("read_link", "path"), &DirAccess::read_link);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("create_link", "source", "target"), &DirAccess::create_link);
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("is_bundle", "path"), &DirAccess::is_bundle);
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("set_include_navigational", "enable"), &DirAccess::set_include_navigational);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_include_navigational"), &DirAccess::get_include_navigational);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("set_include_hidden", "enable"), &DirAccess::set_include_hidden);
 | 
			
		||||
| 
						 | 
				
			
			@ -598,3 +675,7 @@ void DirAccess::_bind_methods() {
 | 
			
		|||
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_navigational"), "set_include_navigational", "get_include_navigational");
 | 
			
		||||
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DirAccess::~DirAccess() {
 | 
			
		||||
	_delete_temp();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,7 +40,7 @@ class DirAccess : public RefCounted {
 | 
			
		|||
	GDCLASS(DirAccess, RefCounted);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	enum AccessType {
 | 
			
		||||
	enum AccessType : int32_t {
 | 
			
		||||
		ACCESS_RESOURCES,
 | 
			
		||||
		ACCESS_USERDATA,
 | 
			
		||||
		ACCESS_FILESYSTEM,
 | 
			
		||||
| 
						 | 
				
			
			@ -61,6 +61,13 @@ private:
 | 
			
		|||
	bool include_navigational = false;
 | 
			
		||||
	bool include_hidden = false;
 | 
			
		||||
 | 
			
		||||
	bool _is_temp = false;
 | 
			
		||||
	bool _temp_keep_after_free = false;
 | 
			
		||||
	String _temp_path;
 | 
			
		||||
	void _delete_temp();
 | 
			
		||||
 | 
			
		||||
	static Ref<DirAccess> _create_temp(const String &p_prefix = "", bool p_keep = false);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -96,8 +103,8 @@ public:
 | 
			
		|||
 | 
			
		||||
	virtual bool file_exists(String p_file) = 0;
 | 
			
		||||
	virtual bool dir_exists(String p_dir) = 0;
 | 
			
		||||
	virtual bool is_readable(String p_dir) { return true; };
 | 
			
		||||
	virtual bool is_writable(String p_dir) { return true; };
 | 
			
		||||
	virtual bool is_readable(String p_dir) { return true; }
 | 
			
		||||
	virtual bool is_writable(String p_dir) { return true; }
 | 
			
		||||
	static bool exists(const String &p_dir);
 | 
			
		||||
	virtual uint64_t get_space_left() = 0;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -116,10 +123,10 @@ public:
 | 
			
		|||
		Ref<DirAccess> da = create(ACCESS_FILESYSTEM);
 | 
			
		||||
		if (da->file_exists(p_path)) {
 | 
			
		||||
			if (da->remove(p_path) != OK) {
 | 
			
		||||
				ERR_FAIL_MSG("Cannot remove file or directory: " + p_path);
 | 
			
		||||
				ERR_FAIL_MSG(vformat("Cannot remove file or directory: '%s'.", p_path));
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			ERR_FAIL_MSG("Cannot remove non-existent file or directory: " + p_path);
 | 
			
		||||
			ERR_FAIL_MSG(vformat("Cannot remove non-existent file or directory: '%s'.", p_path));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -136,6 +143,7 @@ public:
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	static Ref<DirAccess> open(const String &p_path, Error *r_error = nullptr);
 | 
			
		||||
	static Ref<DirAccess> create_temp(const String &p_prefix = "", bool p_keep = false, Error *r_error = nullptr);
 | 
			
		||||
 | 
			
		||||
	static int _get_drive_count();
 | 
			
		||||
	static String get_drive_name(int p_idx);
 | 
			
		||||
| 
						 | 
				
			
			@ -160,9 +168,11 @@ public:
 | 
			
		|||
	bool get_include_hidden() const;
 | 
			
		||||
 | 
			
		||||
	virtual bool is_case_sensitive(const String &p_path) const;
 | 
			
		||||
	virtual bool is_bundle(const String &p_file) const { return false; }
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	DirAccess() {}
 | 
			
		||||
	virtual ~DirAccess() {}
 | 
			
		||||
	virtual ~DirAccess();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // DIR_ACCESS_H
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,15 +30,12 @@
 | 
			
		|||
 | 
			
		||||
#include "dtls_server.h"
 | 
			
		||||
 | 
			
		||||
#include "core/config/project_settings.h"
 | 
			
		||||
#include "core/io/file_access.h"
 | 
			
		||||
 | 
			
		||||
DTLSServer *(*DTLSServer::_create)() = nullptr;
 | 
			
		||||
DTLSServer *(*DTLSServer::_create)(bool p_notify_postinitialize) = nullptr;
 | 
			
		||||
bool DTLSServer::available = false;
 | 
			
		||||
 | 
			
		||||
DTLSServer *DTLSServer::create() {
 | 
			
		||||
DTLSServer *DTLSServer::create(bool p_notify_postinitialize) {
 | 
			
		||||
	if (_create) {
 | 
			
		||||
		return _create();
 | 
			
		||||
		return _create(p_notify_postinitialize);
 | 
			
		||||
	}
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,14 +38,14 @@ class DTLSServer : public RefCounted {
 | 
			
		|||
	GDCLASS(DTLSServer, RefCounted);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	static DTLSServer *(*_create)();
 | 
			
		||||
	static DTLSServer *(*_create)(bool p_notify_postinitialize);
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
 | 
			
		||||
	static bool available;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static bool is_available();
 | 
			
		||||
	static DTLSServer *create();
 | 
			
		||||
	static DTLSServer *create(bool p_notify_postinitialize = true);
 | 
			
		||||
 | 
			
		||||
	virtual Error setup(Ref<TLSOptions> p_options) = 0;
 | 
			
		||||
	virtual void stop() = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										112
									
								
								engine/core/io/file_access.compat.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								engine/core/io/file_access.compat.inc
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,112 @@
 | 
			
		|||
/**************************************************************************/
 | 
			
		||||
/*  file_access.compat.inc                                                */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/*                         This file is part of:                          */
 | 
			
		||||
/*                             GODOT ENGINE                               */
 | 
			
		||||
/*                        https://godotengine.org                         */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
 | 
			
		||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* Permission is hereby granted, free of charge, to any person obtaining  */
 | 
			
		||||
/* a copy of this software and associated documentation files (the        */
 | 
			
		||||
/* "Software"), to deal in the Software without restriction, including    */
 | 
			
		||||
/* without limitation the rights to use, copy, modify, merge, publish,    */
 | 
			
		||||
/* distribute, sublicense, and/or sell copies of the Software, and to     */
 | 
			
		||||
/* permit persons to whom the Software is furnished to do so, subject to  */
 | 
			
		||||
/* the following conditions:                                              */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* The above copyright notice and this permission notice shall be         */
 | 
			
		||||
/* included in all copies or substantial portions of the Software.        */
 | 
			
		||||
/*                                                                        */
 | 
			
		||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
 | 
			
		||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
 | 
			
		||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
 | 
			
		||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
 | 
			
		||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
 | 
			
		||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
 | 
			
		||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 | 
			
		||||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
 | 
			
		||||
Ref<FileAccess> FileAccess::_open_encrypted_bind_compat_98918(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) {
 | 
			
		||||
	return open_encrypted(p_path, p_mode_flags, p_key, Vector<uint8_t>());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_8_bind_compat_78289(uint8_t p_dest) {
 | 
			
		||||
	store_8(p_dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_16_bind_compat_78289(uint16_t p_dest) {
 | 
			
		||||
	store_16(p_dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_32_bind_compat_78289(uint32_t p_dest) {
 | 
			
		||||
	store_32(p_dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_64_bind_compat_78289(uint64_t p_dest) {
 | 
			
		||||
	store_64(p_dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_buffer_bind_compat_78289(const Vector<uint8_t> &p_buffer) {
 | 
			
		||||
	store_buffer(p_buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_var_bind_compat_78289(const Variant &p_var, bool p_full_objects) {
 | 
			
		||||
	store_var(p_var, p_full_objects);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_half_bind_compat_78289(float p_dest) {
 | 
			
		||||
	store_half(p_dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_float_bind_compat_78289(float p_dest) {
 | 
			
		||||
	store_float(p_dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_double_bind_compat_78289(double p_dest) {
 | 
			
		||||
	store_double(p_dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_real_bind_compat_78289(real_t p_real) {
 | 
			
		||||
	store_real(p_real);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_string_bind_compat_78289(const String &p_string) {
 | 
			
		||||
	store_string(p_string);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_line_bind_compat_78289(const String &p_line) {
 | 
			
		||||
	store_line(p_line);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_csv_line_bind_compat_78289(const Vector<String> &p_values, const String &p_delim) {
 | 
			
		||||
	store_csv_line(p_values, p_delim);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_pascal_string_bind_compat_78289(const String &p_string) {
 | 
			
		||||
	store_pascal_string(p_string);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::_bind_compatibility_methods() {
 | 
			
		||||
	ClassDB::bind_compatibility_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key"), &FileAccess::_open_encrypted_bind_compat_98918);
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("store_8", "value"), &FileAccess::store_8_bind_compat_78289);
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("store_16", "value"), &FileAccess::store_16_bind_compat_78289);
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("store_32", "value"), &FileAccess::store_32_bind_compat_78289);
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("store_64", "value"), &FileAccess::store_64_bind_compat_78289);
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("store_half", "value"), &FileAccess::store_half_bind_compat_78289);
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("store_float", "value"), &FileAccess::store_float_bind_compat_78289);
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("store_double", "value"), &FileAccess::store_double_bind_compat_78289);
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("store_real", "value"), &FileAccess::store_real_bind_compat_78289);
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("store_buffer", "buffer"), &FileAccess::store_buffer_bind_compat_78289);
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("store_line", "line"), &FileAccess::store_line_bind_compat_78289);
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("store_csv_line", "values", "delim"), &FileAccess::store_csv_line_bind_compat_78289, DEFVAL(","));
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("store_string", "string"), &FileAccess::store_string_bind_compat_78289);
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("store_var", "value", "full_objects"), &FileAccess::store_var_bind_compat_78289, DEFVAL(false));
 | 
			
		||||
	ClassDB::bind_compatibility_method(D_METHOD("store_pascal_string", "string"), &FileAccess::store_pascal_string_bind_compat_78289);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +29,7 @@
 | 
			
		|||
/**************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "file_access.h"
 | 
			
		||||
#include "file_access.compat.inc"
 | 
			
		||||
 | 
			
		||||
#include "core/config/project_settings.h"
 | 
			
		||||
#include "core/crypto/crypto_core.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -37,6 +38,7 @@
 | 
			
		|||
#include "core/io/file_access_pack.h"
 | 
			
		||||
#include "core/io/marshalls.h"
 | 
			
		||||
#include "core/os/os.h"
 | 
			
		||||
#include "core/os/time.h"
 | 
			
		||||
 | 
			
		||||
FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = {};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -59,11 +61,9 @@ bool FileAccess::exists(const String &p_name) {
 | 
			
		|||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Ref<FileAccess> f = open(p_name, READ);
 | 
			
		||||
	if (f.is_null()) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
	// Using file_exists because it's faster than trying to open the file.
 | 
			
		||||
	Ref<FileAccess> ret = create_for_path(p_name);
 | 
			
		||||
	return ret->file_exists(p_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::_set_access_type(AccessType p_access) {
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +72,7 @@ void FileAccess::_set_access_type(AccessType p_access) {
 | 
			
		|||
 | 
			
		||||
Ref<FileAccess> FileAccess::create_for_path(const String &p_path) {
 | 
			
		||||
	Ref<FileAccess> ret;
 | 
			
		||||
	if (p_path.begins_with("res://")) {
 | 
			
		||||
	if (p_path.begins_with("res://") || p_path.begins_with("uid://")) {
 | 
			
		||||
		ret = create(ACCESS_RESOURCES);
 | 
			
		||||
	} else if (p_path.begins_with("user://")) {
 | 
			
		||||
		ret = create(ACCESS_USERDATA);
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +85,79 @@ Ref<FileAccess> FileAccess::create_for_path(const String &p_path) {
 | 
			
		|||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<FileAccess> FileAccess::create_temp(int p_mode_flags, const String &p_prefix, const String &p_extension, bool p_keep, Error *r_error) {
 | 
			
		||||
	const String ERROR_COMMON_PREFIX = "Error while creating temporary file";
 | 
			
		||||
 | 
			
		||||
	if (!p_prefix.is_valid_filename()) {
 | 
			
		||||
		*r_error = ERR_FILE_BAD_PATH;
 | 
			
		||||
		ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: "%s" is not a valid prefix.)", ERROR_COMMON_PREFIX, p_prefix));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!p_extension.is_valid_filename()) {
 | 
			
		||||
		*r_error = ERR_FILE_BAD_PATH;
 | 
			
		||||
		ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: "%s" is not a valid extension.)", ERROR_COMMON_PREFIX, p_extension));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const String TEMP_DIR = OS::get_singleton()->get_temp_path();
 | 
			
		||||
	String extension = p_extension.trim_prefix(".");
 | 
			
		||||
 | 
			
		||||
	uint32_t suffix_i = 0;
 | 
			
		||||
	String path;
 | 
			
		||||
	while (true) {
 | 
			
		||||
		String datetime = Time::get_singleton()->get_datetime_string_from_system().replace("-", "").replace("T", "").replace(":", "");
 | 
			
		||||
		datetime += itos(Time::get_singleton()->get_ticks_usec());
 | 
			
		||||
		String suffix = datetime + (suffix_i > 0 ? itos(suffix_i) : "");
 | 
			
		||||
		path = TEMP_DIR.path_join((p_prefix.is_empty() ? "" : p_prefix + "-") + suffix + (extension.is_empty() ? "" : "." + extension));
 | 
			
		||||
		if (!DirAccess::exists(path)) {
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		suffix_i += 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Error err;
 | 
			
		||||
	{
 | 
			
		||||
		// Create file first with WRITE mode.
 | 
			
		||||
		// Otherwise, it would fail to open with a READ mode.
 | 
			
		||||
		Ref<FileAccess> ret = FileAccess::open(path, FileAccess::ModeFlags::WRITE, &err);
 | 
			
		||||
		if (err != OK) {
 | 
			
		||||
			*r_error = err;
 | 
			
		||||
			ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: could not create "%s".)", ERROR_COMMON_PREFIX, path));
 | 
			
		||||
		}
 | 
			
		||||
		ret->flush();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Open then the temp file with the correct mode flag.
 | 
			
		||||
	Ref<FileAccess> ret = FileAccess::open(path, p_mode_flags, &err);
 | 
			
		||||
	if (err != OK) {
 | 
			
		||||
		*r_error = err;
 | 
			
		||||
		ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: could not open "%s".)", ERROR_COMMON_PREFIX, path));
 | 
			
		||||
	}
 | 
			
		||||
	if (ret.is_valid()) {
 | 
			
		||||
		ret->_is_temp_file = true;
 | 
			
		||||
		ret->_temp_keep_after_use = p_keep;
 | 
			
		||||
		ret->_temp_path = ret->get_path_absolute();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*r_error = OK;
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<FileAccess> FileAccess::_create_temp(int p_mode_flags, const String &p_prefix, const String &p_extension, bool p_keep) {
 | 
			
		||||
	return create_temp(p_mode_flags, p_prefix, p_extension, p_keep, &last_file_open_error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::_delete_temp() {
 | 
			
		||||
	if (!_is_temp_file || _temp_keep_after_use) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!FileAccess::exists(_temp_path)) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DirAccess::remove_absolute(_temp_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error FileAccess::reopen(const String &p_path, int p_mode_flags) {
 | 
			
		||||
	return open_internal(p_path, p_mode_flags);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -126,7 +199,7 @@ Ref<FileAccess> FileAccess::_open(const String &p_path, ModeFlags p_mode_flags)
 | 
			
		|||
	return fa;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) {
 | 
			
		||||
Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key, const Vector<uint8_t> &p_iv) {
 | 
			
		||||
	Ref<FileAccess> fa = _open(p_path, p_mode_flags);
 | 
			
		||||
	if (fa.is_null()) {
 | 
			
		||||
		return fa;
 | 
			
		||||
| 
						 | 
				
			
			@ -134,7 +207,7 @@ Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mod
 | 
			
		|||
 | 
			
		||||
	Ref<FileAccessEncrypted> fae;
 | 
			
		||||
	fae.instantiate();
 | 
			
		||||
	Error err = fae->open_and_parse(fa, p_key, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ);
 | 
			
		||||
	Error err = fae->open_and_parse(fa, p_key, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ, true, p_iv);
 | 
			
		||||
	last_file_open_error = err;
 | 
			
		||||
	if (err) {
 | 
			
		||||
		return Ref<FileAccess>();
 | 
			
		||||
| 
						 | 
				
			
			@ -184,13 +257,17 @@ FileAccess::AccessType FileAccess::get_access_type() const {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
String FileAccess::fix_path(const String &p_path) const {
 | 
			
		||||
	//helper used by file accesses that use a single filesystem
 | 
			
		||||
	// Helper used by file accesses that use a single filesystem.
 | 
			
		||||
 | 
			
		||||
	String r_path = p_path.replace("\\", "/");
 | 
			
		||||
 | 
			
		||||
	switch (_access_type) {
 | 
			
		||||
		case ACCESS_RESOURCES: {
 | 
			
		||||
			if (ProjectSettings::get_singleton()) {
 | 
			
		||||
				if (r_path.begins_with("uid://")) {
 | 
			
		||||
					r_path = ResourceUID::uid_to_path(r_path);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (r_path.begins_with("res://")) {
 | 
			
		||||
					String resource_path = ProjectSettings::get_singleton()->get_resource_path();
 | 
			
		||||
					if (!resource_path.is_empty()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -225,59 +302,48 @@ String FileAccess::fix_path(const String &p_path) const {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/* these are all implemented for ease of porting, then can later be optimized */
 | 
			
		||||
uint8_t FileAccess::get_8() const {
 | 
			
		||||
	uint8_t data = 0;
 | 
			
		||||
	get_buffer(&data, sizeof(uint8_t));
 | 
			
		||||
 | 
			
		||||
uint16_t FileAccess::get_16() const {
 | 
			
		||||
	uint16_t res;
 | 
			
		||||
	uint8_t a, b;
 | 
			
		||||
 | 
			
		||||
	a = get_8();
 | 
			
		||||
	b = get_8();
 | 
			
		||||
 | 
			
		||||
	if (big_endian) {
 | 
			
		||||
		SWAP(a, b);
 | 
			
		||||
	return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	res = b;
 | 
			
		||||
	res <<= 8;
 | 
			
		||||
	res |= a;
 | 
			
		||||
uint16_t FileAccess::get_16() const {
 | 
			
		||||
	uint16_t data = 0;
 | 
			
		||||
	get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint16_t));
 | 
			
		||||
 | 
			
		||||
	return res;
 | 
			
		||||
	if (big_endian) {
 | 
			
		||||
		data = BSWAP16(data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t FileAccess::get_32() const {
 | 
			
		||||
	uint32_t res;
 | 
			
		||||
	uint16_t a, b;
 | 
			
		||||
 | 
			
		||||
	a = get_16();
 | 
			
		||||
	b = get_16();
 | 
			
		||||
	uint32_t data = 0;
 | 
			
		||||
	get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint32_t));
 | 
			
		||||
 | 
			
		||||
	if (big_endian) {
 | 
			
		||||
		SWAP(a, b);
 | 
			
		||||
		data = BSWAP32(data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res = b;
 | 
			
		||||
	res <<= 16;
 | 
			
		||||
	res |= a;
 | 
			
		||||
 | 
			
		||||
	return res;
 | 
			
		||||
	return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t FileAccess::get_64() const {
 | 
			
		||||
	uint64_t res;
 | 
			
		||||
	uint32_t a, b;
 | 
			
		||||
 | 
			
		||||
	a = get_32();
 | 
			
		||||
	b = get_32();
 | 
			
		||||
	uint64_t data = 0;
 | 
			
		||||
	get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint64_t));
 | 
			
		||||
 | 
			
		||||
	if (big_endian) {
 | 
			
		||||
		SWAP(a, b);
 | 
			
		||||
		data = BSWAP64(data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res = b;
 | 
			
		||||
	res <<= 32;
 | 
			
		||||
	res |= a;
 | 
			
		||||
	return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	return res;
 | 
			
		||||
float FileAccess::get_half() const {
 | 
			
		||||
	return Math::half_to_float(get_16());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float FileAccess::get_float() const {
 | 
			
		||||
| 
						 | 
				
			
			@ -317,7 +383,7 @@ double FileAccess::get_double() const {
 | 
			
		|||
String FileAccess::get_token() const {
 | 
			
		||||
	CharString token;
 | 
			
		||||
 | 
			
		||||
	char32_t c = get_8();
 | 
			
		||||
	uint8_t c = get_8();
 | 
			
		||||
 | 
			
		||||
	while (!eof_reached()) {
 | 
			
		||||
		if (c <= ' ') {
 | 
			
		||||
| 
						 | 
				
			
			@ -325,7 +391,7 @@ String FileAccess::get_token() const {
 | 
			
		|||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			token += c;
 | 
			
		||||
			token += char(c);
 | 
			
		||||
		}
 | 
			
		||||
		c = get_8();
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -382,14 +448,14 @@ public:
 | 
			
		|||
String FileAccess::get_line() const {
 | 
			
		||||
	CharBuffer line;
 | 
			
		||||
 | 
			
		||||
	char32_t c = get_8();
 | 
			
		||||
	uint8_t c = get_8();
 | 
			
		||||
 | 
			
		||||
	while (!eof_reached()) {
 | 
			
		||||
		if (c == '\n' || c == '\0') {
 | 
			
		||||
		if (c == '\n' || c == '\0' || get_error() != OK) {
 | 
			
		||||
			line.push_back(0);
 | 
			
		||||
			return String::utf8(line.get_data());
 | 
			
		||||
		} else if (c != '\r') {
 | 
			
		||||
			line.push_back(c);
 | 
			
		||||
			line.push_back(char(c));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c = get_8();
 | 
			
		||||
| 
						 | 
				
			
			@ -467,17 +533,6 @@ String FileAccess::get_as_text(bool p_skip_cr) const {
 | 
			
		|||
	return text;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
 | 
			
		||||
	ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
 | 
			
		||||
 | 
			
		||||
	uint64_t i = 0;
 | 
			
		||||
	for (i = 0; i < p_length && !eof_reached(); i++) {
 | 
			
		||||
		p_dst[i] = get_8();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Vector<uint8_t> FileAccess::get_buffer(int64_t p_length) const {
 | 
			
		||||
	Vector<uint8_t> data;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -487,10 +542,10 @@ Vector<uint8_t> FileAccess::get_buffer(int64_t p_length) const {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	Error err = data.resize(p_length);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, data, "Can't resize data to " + itos(p_length) + " elements.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, data, vformat("Can't resize data to %d elements.", p_length));
 | 
			
		||||
 | 
			
		||||
	uint8_t *w = data.ptrw();
 | 
			
		||||
	int64_t len = get_buffer(&w[0], p_length);
 | 
			
		||||
	int64_t len = get_buffer(w, p_length);
 | 
			
		||||
 | 
			
		||||
	if (len < p_length) {
 | 
			
		||||
		data.resize(len);
 | 
			
		||||
| 
						 | 
				
			
			@ -510,70 +565,60 @@ String FileAccess::get_as_utf8_string(bool p_skip_cr) const {
 | 
			
		|||
	w[len] = 0;
 | 
			
		||||
 | 
			
		||||
	String s;
 | 
			
		||||
	s.parse_utf8((const char *)w, -1, p_skip_cr);
 | 
			
		||||
	s.parse_utf8((const char *)w, len, p_skip_cr);
 | 
			
		||||
	return s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_16(uint16_t p_dest) {
 | 
			
		||||
	uint8_t a, b;
 | 
			
		||||
 | 
			
		||||
	a = p_dest & 0xFF;
 | 
			
		||||
	b = p_dest >> 8;
 | 
			
		||||
bool FileAccess::store_8(uint8_t p_dest) {
 | 
			
		||||
	return store_buffer(&p_dest, sizeof(uint8_t));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FileAccess::store_16(uint16_t p_dest) {
 | 
			
		||||
	if (big_endian) {
 | 
			
		||||
		SWAP(a, b);
 | 
			
		||||
		p_dest = BSWAP16(p_dest);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	store_8(a);
 | 
			
		||||
	store_8(b);
 | 
			
		||||
	return store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint16_t));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_32(uint32_t p_dest) {
 | 
			
		||||
	uint16_t a, b;
 | 
			
		||||
 | 
			
		||||
	a = p_dest & 0xFFFF;
 | 
			
		||||
	b = p_dest >> 16;
 | 
			
		||||
 | 
			
		||||
bool FileAccess::store_32(uint32_t p_dest) {
 | 
			
		||||
	if (big_endian) {
 | 
			
		||||
		SWAP(a, b);
 | 
			
		||||
		p_dest = BSWAP32(p_dest);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	store_16(a);
 | 
			
		||||
	store_16(b);
 | 
			
		||||
	return store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint32_t));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_64(uint64_t p_dest) {
 | 
			
		||||
	uint32_t a, b;
 | 
			
		||||
 | 
			
		||||
	a = p_dest & 0xFFFFFFFF;
 | 
			
		||||
	b = p_dest >> 32;
 | 
			
		||||
 | 
			
		||||
bool FileAccess::store_64(uint64_t p_dest) {
 | 
			
		||||
	if (big_endian) {
 | 
			
		||||
		SWAP(a, b);
 | 
			
		||||
		p_dest = BSWAP64(p_dest);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	store_32(a);
 | 
			
		||||
	store_32(b);
 | 
			
		||||
	return store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint64_t));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_real(real_t p_real) {
 | 
			
		||||
bool FileAccess::store_real(real_t p_real) {
 | 
			
		||||
	if constexpr (sizeof(real_t) == 4) {
 | 
			
		||||
		store_float(p_real);
 | 
			
		||||
		return store_float(p_real);
 | 
			
		||||
	} else {
 | 
			
		||||
		store_double(p_real);
 | 
			
		||||
		return store_double(p_real);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_float(float p_dest) {
 | 
			
		||||
bool FileAccess::store_half(float p_dest) {
 | 
			
		||||
	return store_16(Math::make_half_float(p_dest));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FileAccess::store_float(float p_dest) {
 | 
			
		||||
	MarshallFloat m;
 | 
			
		||||
	m.f = p_dest;
 | 
			
		||||
	store_32(m.i);
 | 
			
		||||
	return store_32(m.i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_double(double p_dest) {
 | 
			
		||||
bool FileAccess::store_double(double p_dest) {
 | 
			
		||||
	MarshallDouble m;
 | 
			
		||||
	m.d = p_dest;
 | 
			
		||||
	store_64(m.l);
 | 
			
		||||
	return store_64(m.l);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t FileAccess::get_modified_time(const String &p_file) {
 | 
			
		||||
| 
						 | 
				
			
			@ -582,7 +627,7 @@ uint64_t FileAccess::get_modified_time(const String &p_file) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	Ref<FileAccess> fa = create_for_path(p_file);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "Cannot create FileAccess for path '" + p_file + "'.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(fa.is_null(), 0, vformat("Cannot create FileAccess for path '%s'.", p_file));
 | 
			
		||||
 | 
			
		||||
	uint64_t mt = fa->_get_modified_time(p_file);
 | 
			
		||||
	return mt;
 | 
			
		||||
| 
						 | 
				
			
			@ -594,7 +639,7 @@ BitField<FileAccess::UnixPermissionFlags> FileAccess::get_unix_permissions(const
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	Ref<FileAccess> fa = create_for_path(p_file);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "Cannot create FileAccess for path '" + p_file + "'.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(fa.is_null(), 0, vformat("Cannot create FileAccess for path '%s'.", p_file));
 | 
			
		||||
 | 
			
		||||
	return fa->_get_unix_permissions(p_file);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -605,7 +650,7 @@ Error FileAccess::set_unix_permissions(const String &p_file, BitField<FileAccess
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	Ref<FileAccess> fa = create_for_path(p_file);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file));
 | 
			
		||||
 | 
			
		||||
	Error err = fa->_set_unix_permissions(p_file, p_permissions);
 | 
			
		||||
	return err;
 | 
			
		||||
| 
						 | 
				
			
			@ -617,7 +662,7 @@ bool FileAccess::get_hidden_attribute(const String &p_file) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	Ref<FileAccess> fa = create_for_path(p_file);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("Cannot create FileAccess for path '%s'.", p_file));
 | 
			
		||||
 | 
			
		||||
	return fa->_get_hidden_attribute(p_file);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -628,7 +673,7 @@ Error FileAccess::set_hidden_attribute(const String &p_file, bool p_hidden) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	Ref<FileAccess> fa = create_for_path(p_file);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file));
 | 
			
		||||
 | 
			
		||||
	Error err = fa->_set_hidden_attribute(p_file, p_hidden);
 | 
			
		||||
	return err;
 | 
			
		||||
| 
						 | 
				
			
			@ -640,7 +685,7 @@ bool FileAccess::get_read_only_attribute(const String &p_file) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	Ref<FileAccess> fa = create_for_path(p_file);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("Cannot create FileAccess for path '%s'.", p_file));
 | 
			
		||||
 | 
			
		||||
	return fa->_get_read_only_attribute(p_file);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -651,25 +696,24 @@ Error FileAccess::set_read_only_attribute(const String &p_file, bool p_ro) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	Ref<FileAccess> fa = create_for_path(p_file);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file));
 | 
			
		||||
 | 
			
		||||
	Error err = fa->_set_read_only_attribute(p_file, p_ro);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_string(const String &p_string) {
 | 
			
		||||
bool FileAccess::store_string(const String &p_string) {
 | 
			
		||||
	if (p_string.length() == 0) {
 | 
			
		||||
		return;
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CharString cs = p_string.utf8();
 | 
			
		||||
	store_buffer((uint8_t *)&cs[0], cs.length());
 | 
			
		||||
	return store_buffer((uint8_t *)&cs[0], cs.length());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_pascal_string(const String &p_string) {
 | 
			
		||||
bool FileAccess::store_pascal_string(const String &p_string) {
 | 
			
		||||
	CharString cs = p_string.utf8();
 | 
			
		||||
	store_32(cs.length());
 | 
			
		||||
	store_buffer((uint8_t *)&cs[0], cs.length());
 | 
			
		||||
	return store_32(cs.length()) && store_buffer((uint8_t *)&cs[0], cs.length());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String FileAccess::get_pascal_string() {
 | 
			
		||||
| 
						 | 
				
			
			@ -680,24 +724,23 @@ String FileAccess::get_pascal_string() {
 | 
			
		|||
	cs[sl] = 0;
 | 
			
		||||
 | 
			
		||||
	String ret;
 | 
			
		||||
	ret.parse_utf8(cs.ptr());
 | 
			
		||||
	ret.parse_utf8(cs.ptr(), sl);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_line(const String &p_line) {
 | 
			
		||||
	store_string(p_line);
 | 
			
		||||
	store_8('\n');
 | 
			
		||||
bool FileAccess::store_line(const String &p_line) {
 | 
			
		||||
	return store_string(p_line) && store_8('\n');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_delim) {
 | 
			
		||||
	ERR_FAIL_COND(p_delim.length() != 1);
 | 
			
		||||
bool FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_delim) {
 | 
			
		||||
	ERR_FAIL_COND_V(p_delim.length() != 1, false);
 | 
			
		||||
 | 
			
		||||
	String line = "";
 | 
			
		||||
	int size = p_values.size();
 | 
			
		||||
	for (int i = 0; i < size; ++i) {
 | 
			
		||||
		String value = p_values[i];
 | 
			
		||||
 | 
			
		||||
		if (value.contains("\"") || value.contains(p_delim) || value.contains("\n")) {
 | 
			
		||||
		if (value.contains_char('"') || value.contains(p_delim) || value.contains_char('\n')) {
 | 
			
		||||
			value = "\"" + value.replace("\"", "\"\"") + "\"";
 | 
			
		||||
		}
 | 
			
		||||
		if (i < size - 1) {
 | 
			
		||||
| 
						 | 
				
			
			@ -707,41 +750,43 @@ void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_
 | 
			
		|||
		line += value;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	store_line(line);
 | 
			
		||||
	return store_line(line);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) {
 | 
			
		||||
	ERR_FAIL_COND(!p_src && p_length > 0);
 | 
			
		||||
	for (uint64_t i = 0; i < p_length; i++) {
 | 
			
		||||
		store_8(p_src[i]);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_buffer(const Vector<uint8_t> &p_buffer) {
 | 
			
		||||
bool FileAccess::store_buffer(const Vector<uint8_t> &p_buffer) {
 | 
			
		||||
	uint64_t len = p_buffer.size();
 | 
			
		||||
	if (len == 0) {
 | 
			
		||||
		return;
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const uint8_t *r = p_buffer.ptr();
 | 
			
		||||
 | 
			
		||||
	store_buffer(&r[0], len);
 | 
			
		||||
	return store_buffer(r, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccess::store_var(const Variant &p_var, bool p_full_objects) {
 | 
			
		||||
bool FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) {
 | 
			
		||||
	ERR_FAIL_COND_V(!p_src && p_length > 0, false);
 | 
			
		||||
	for (uint64_t i = 0; i < p_length; i++) {
 | 
			
		||||
		if (unlikely(!store_8(p_src[i]))) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FileAccess::store_var(const Variant &p_var, bool p_full_objects) {
 | 
			
		||||
	int len;
 | 
			
		||||
	Error err = encode_variant(p_var, nullptr, len, p_full_objects);
 | 
			
		||||
	ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, false, "Error when trying to encode Variant.");
 | 
			
		||||
 | 
			
		||||
	Vector<uint8_t> buff;
 | 
			
		||||
	buff.resize(len);
 | 
			
		||||
 | 
			
		||||
	uint8_t *w = buff.ptrw();
 | 
			
		||||
	err = encode_variant(p_var, &w[0], len, p_full_objects);
 | 
			
		||||
	ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(err != OK, false, "Error when trying to encode Variant.");
 | 
			
		||||
 | 
			
		||||
	store_32(len);
 | 
			
		||||
	store_buffer(buff);
 | 
			
		||||
	return store_32(uint32_t(len)) && store_buffer(buff);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Vector<uint8_t> FileAccess::get_file_as_bytes(const String &p_path, Error *r_error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -750,7 +795,7 @@ Vector<uint8_t> FileAccess::get_file_as_bytes(const String &p_path, Error *r_err
 | 
			
		|||
		if (r_error) { // if error requested, do not throw error
 | 
			
		||||
			return Vector<uint8_t>();
 | 
			
		||||
		}
 | 
			
		||||
		ERR_FAIL_V_MSG(Vector<uint8_t>(), "Can't open file from path '" + String(p_path) + "'.");
 | 
			
		||||
		ERR_FAIL_V_MSG(Vector<uint8_t>(), vformat("Can't open file from path '%s'.", String(p_path)));
 | 
			
		||||
	}
 | 
			
		||||
	Vector<uint8_t> data;
 | 
			
		||||
	data.resize(f->get_length());
 | 
			
		||||
| 
						 | 
				
			
			@ -768,7 +813,7 @@ String FileAccess::get_file_as_string(const String &p_path, Error *r_error) {
 | 
			
		|||
		if (r_error) {
 | 
			
		||||
			return String();
 | 
			
		||||
		}
 | 
			
		||||
		ERR_FAIL_V_MSG(String(), "Can't get file as string from path '" + String(p_path) + "'.");
 | 
			
		||||
		ERR_FAIL_V_MSG(String(), vformat("Can't get file as string from path '%s'.", String(p_path)));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	String ret;
 | 
			
		||||
| 
						 | 
				
			
			@ -859,10 +904,11 @@ String FileAccess::get_sha256(const String &p_file) {
 | 
			
		|||
 | 
			
		||||
void FileAccess::_bind_methods() {
 | 
			
		||||
	ClassDB::bind_static_method("FileAccess", D_METHOD("open", "path", "flags"), &FileAccess::_open);
 | 
			
		||||
	ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key"), &FileAccess::open_encrypted);
 | 
			
		||||
	ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key", "iv"), &FileAccess::open_encrypted, DEFVAL(Vector<uint8_t>()));
 | 
			
		||||
	ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted_with_pass", "path", "mode_flags", "pass"), &FileAccess::open_encrypted_pass);
 | 
			
		||||
	ClassDB::bind_static_method("FileAccess", D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &FileAccess::open_compressed, DEFVAL(0));
 | 
			
		||||
	ClassDB::bind_static_method("FileAccess", D_METHOD("get_open_error"), &FileAccess::get_open_error);
 | 
			
		||||
	ClassDB::bind_static_method("FileAccess", D_METHOD("create_temp", "mode_flags", "prefix", "extension", "keep"), &FileAccess::_create_temp, DEFVAL(""), DEFVAL(""), DEFVAL(false));
 | 
			
		||||
 | 
			
		||||
	ClassDB::bind_static_method("FileAccess", D_METHOD("get_file_as_bytes", "path"), &FileAccess::_get_file_as_bytes);
 | 
			
		||||
	ClassDB::bind_static_method("FileAccess", D_METHOD("get_file_as_string", "path"), &FileAccess::_get_file_as_string);
 | 
			
		||||
| 
						 | 
				
			
			@ -881,6 +927,7 @@ void FileAccess::_bind_methods() {
 | 
			
		|||
	ClassDB::bind_method(D_METHOD("get_16"), &FileAccess::get_16);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_32"), &FileAccess::get_32);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_64"), &FileAccess::get_64);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_half"), &FileAccess::get_half);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_float"), &FileAccess::get_float);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_double"), &FileAccess::get_double);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("get_real"), &FileAccess::get_real);
 | 
			
		||||
| 
						 | 
				
			
			@ -899,10 +946,11 @@ void FileAccess::_bind_methods() {
 | 
			
		|||
	ClassDB::bind_method(D_METHOD("store_16", "value"), &FileAccess::store_16);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("store_32", "value"), &FileAccess::store_32);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("store_64", "value"), &FileAccess::store_64);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("store_half", "value"), &FileAccess::store_half);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("store_float", "value"), &FileAccess::store_float);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("store_double", "value"), &FileAccess::store_double);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("store_real", "value"), &FileAccess::store_real);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), (void(FileAccess::*)(const Vector<uint8_t> &)) & FileAccess::store_buffer);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), (bool(FileAccess::*)(const Vector<uint8_t> &)) & FileAccess::store_buffer);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("store_line", "line"), &FileAccess::store_line);
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("store_csv_line", "values", "delim"), &FileAccess::store_csv_line, DEFVAL(","));
 | 
			
		||||
	ClassDB::bind_method(D_METHOD("store_string", "string"), &FileAccess::store_string);
 | 
			
		||||
| 
						 | 
				
			
			@ -950,3 +998,7 @@ void FileAccess::_bind_methods() {
 | 
			
		|||
	BIND_BITFIELD_FLAG(UNIX_SET_GROUP_ID);
 | 
			
		||||
	BIND_BITFIELD_FLAG(UNIX_RESTRICTED_DELETE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FileAccess::~FileAccess() {
 | 
			
		||||
	_delete_temp();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ class FileAccess : public RefCounted {
 | 
			
		|||
	GDCLASS(FileAccess, RefCounted);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	enum AccessType {
 | 
			
		||||
	enum AccessType : int32_t {
 | 
			
		||||
		ACCESS_RESOURCES,
 | 
			
		||||
		ACCESS_USERDATA,
 | 
			
		||||
		ACCESS_FILESYSTEM,
 | 
			
		||||
| 
						 | 
				
			
			@ -54,14 +54,14 @@ public:
 | 
			
		|||
		ACCESS_MAX
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum ModeFlags {
 | 
			
		||||
	enum ModeFlags : int32_t {
 | 
			
		||||
		READ = 1,
 | 
			
		||||
		WRITE = 2,
 | 
			
		||||
		READ_WRITE = 3,
 | 
			
		||||
		WRITE_READ = 7,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum UnixPermissionFlags {
 | 
			
		||||
	enum UnixPermissionFlags : int32_t {
 | 
			
		||||
		UNIX_EXECUTE_OTHER = 0x001,
 | 
			
		||||
		UNIX_WRITE_OTHER = 0x002,
 | 
			
		||||
		UNIX_READ_OTHER = 0x004,
 | 
			
		||||
| 
						 | 
				
			
			@ -76,7 +76,7 @@ public:
 | 
			
		|||
		UNIX_SET_USER_ID = 0x800,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum CompressionMode {
 | 
			
		||||
	enum CompressionMode : int32_t {
 | 
			
		||||
		COMPRESSION_FASTLZ = Compression::MODE_FASTLZ,
 | 
			
		||||
		COMPRESSION_DEFLATE = Compression::MODE_DEFLATE,
 | 
			
		||||
		COMPRESSION_ZSTD = Compression::MODE_ZSTD,
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +109,27 @@ protected:
 | 
			
		|||
 | 
			
		||||
	static FileCloseFailNotify close_fail_notify;
 | 
			
		||||
 | 
			
		||||
#ifndef DISABLE_DEPRECATED
 | 
			
		||||
	static Ref<FileAccess> _open_encrypted_bind_compat_98918(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key);
 | 
			
		||||
 | 
			
		||||
	void store_8_bind_compat_78289(uint8_t p_dest);
 | 
			
		||||
	void store_16_bind_compat_78289(uint16_t p_dest);
 | 
			
		||||
	void store_32_bind_compat_78289(uint32_t p_dest);
 | 
			
		||||
	void store_64_bind_compat_78289(uint64_t p_dest);
 | 
			
		||||
	void store_buffer_bind_compat_78289(const Vector<uint8_t> &p_buffer);
 | 
			
		||||
	void store_var_bind_compat_78289(const Variant &p_var, bool p_full_objects = false);
 | 
			
		||||
	void store_half_bind_compat_78289(float p_dest);
 | 
			
		||||
	void store_float_bind_compat_78289(float p_dest);
 | 
			
		||||
	void store_double_bind_compat_78289(double p_dest);
 | 
			
		||||
	void store_real_bind_compat_78289(real_t p_real);
 | 
			
		||||
	void store_string_bind_compat_78289(const String &p_string);
 | 
			
		||||
	void store_line_bind_compat_78289(const String &p_line);
 | 
			
		||||
	void store_csv_line_bind_compat_78289(const Vector<String> &p_values, const String &p_delim = ",");
 | 
			
		||||
	void store_pascal_string_bind_compat_78289(const String &p_string);
 | 
			
		||||
 | 
			
		||||
	static void _bind_compatibility_methods();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	static bool backup_save;
 | 
			
		||||
	thread_local static Error last_file_open_error;
 | 
			
		||||
| 
						 | 
				
			
			@ -122,6 +143,13 @@ private:
 | 
			
		|||
 | 
			
		||||
	static Ref<FileAccess> _open(const String &p_path, ModeFlags p_mode_flags);
 | 
			
		||||
 | 
			
		||||
	bool _is_temp_file = false;
 | 
			
		||||
	bool _temp_keep_after_use = false;
 | 
			
		||||
	String _temp_path;
 | 
			
		||||
	void _delete_temp();
 | 
			
		||||
 | 
			
		||||
	static Ref<FileAccess> _create_temp(int p_mode_flags, const String &p_prefix = "", const String &p_extension = "", bool p_keep = false);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static void set_file_close_fail_notify_callback(FileCloseFailNotify p_cbk) { close_fail_notify = p_cbk; }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -137,18 +165,19 @@ public:
 | 
			
		|||
 | 
			
		||||
	virtual bool eof_reached() const = 0; ///< reading passed EOF
 | 
			
		||||
 | 
			
		||||
	virtual uint8_t get_8() const = 0; ///< get a byte
 | 
			
		||||
	virtual uint8_t get_8() const; ///< get a byte
 | 
			
		||||
	virtual uint16_t get_16() const; ///< get 16 bits uint
 | 
			
		||||
	virtual uint32_t get_32() const; ///< get 32 bits uint
 | 
			
		||||
	virtual uint64_t get_64() const; ///< get 64 bits uint
 | 
			
		||||
 | 
			
		||||
	virtual float get_half() const;
 | 
			
		||||
	virtual float get_float() const;
 | 
			
		||||
	virtual double get_double() const;
 | 
			
		||||
	virtual real_t get_real() const;
 | 
			
		||||
 | 
			
		||||
	Variant get_var(bool p_allow_objects = false) const;
 | 
			
		||||
 | 
			
		||||
	virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const; ///< get an array of bytes
 | 
			
		||||
	virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const = 0; ///< get an array of bytes, needs to be overwritten by children.
 | 
			
		||||
	Vector<uint8_t> get_buffer(int64_t p_length) const;
 | 
			
		||||
	virtual String get_line() const;
 | 
			
		||||
	virtual String get_token() const;
 | 
			
		||||
| 
						 | 
				
			
			@ -157,6 +186,7 @@ public:
 | 
			
		|||
	virtual String get_as_utf8_string(bool p_skip_cr = false) const;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
 | 
			
		||||
	 * Use this for files WRITTEN in _big_ endian machines (ie, amiga/mac)
 | 
			
		||||
	 * It's not about the current CPU type but file formats.
 | 
			
		||||
	 * This flag gets reset to `false` (little endian) on each open.
 | 
			
		||||
| 
						 | 
				
			
			@ -168,26 +198,27 @@ public:
 | 
			
		|||
 | 
			
		||||
	virtual Error resize(int64_t p_length) = 0;
 | 
			
		||||
	virtual void flush() = 0;
 | 
			
		||||
	virtual void store_8(uint8_t p_dest) = 0; ///< store a byte
 | 
			
		||||
	virtual void store_16(uint16_t p_dest); ///< store 16 bits uint
 | 
			
		||||
	virtual void store_32(uint32_t p_dest); ///< store 32 bits uint
 | 
			
		||||
	virtual void store_64(uint64_t p_dest); ///< store 64 bits uint
 | 
			
		||||
	virtual bool store_8(uint8_t p_dest); ///< store a byte
 | 
			
		||||
	virtual bool store_16(uint16_t p_dest); ///< store 16 bits uint
 | 
			
		||||
	virtual bool store_32(uint32_t p_dest); ///< store 32 bits uint
 | 
			
		||||
	virtual bool store_64(uint64_t p_dest); ///< store 64 bits uint
 | 
			
		||||
 | 
			
		||||
	virtual void store_float(float p_dest);
 | 
			
		||||
	virtual void store_double(double p_dest);
 | 
			
		||||
	virtual void store_real(real_t p_real);
 | 
			
		||||
	virtual bool store_half(float p_dest);
 | 
			
		||||
	virtual bool store_float(float p_dest);
 | 
			
		||||
	virtual bool store_double(double p_dest);
 | 
			
		||||
	virtual bool store_real(real_t p_real);
 | 
			
		||||
 | 
			
		||||
	virtual void store_string(const String &p_string);
 | 
			
		||||
	virtual void store_line(const String &p_line);
 | 
			
		||||
	virtual void store_csv_line(const Vector<String> &p_values, const String &p_delim = ",");
 | 
			
		||||
	virtual bool store_string(const String &p_string);
 | 
			
		||||
	virtual bool store_line(const String &p_line);
 | 
			
		||||
	virtual bool store_csv_line(const Vector<String> &p_values, const String &p_delim = ",");
 | 
			
		||||
 | 
			
		||||
	virtual void store_pascal_string(const String &p_string);
 | 
			
		||||
	virtual bool store_pascal_string(const String &p_string);
 | 
			
		||||
	virtual String get_pascal_string();
 | 
			
		||||
 | 
			
		||||
	virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes
 | 
			
		||||
	void store_buffer(const Vector<uint8_t> &p_buffer);
 | 
			
		||||
	virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) = 0; ///< store an array of bytes, needs to be overwritten by children.
 | 
			
		||||
	bool store_buffer(const Vector<uint8_t> &p_buffer);
 | 
			
		||||
 | 
			
		||||
	void store_var(const Variant &p_var, bool p_full_objects = false);
 | 
			
		||||
	bool store_var(const Variant &p_var, bool p_full_objects = false);
 | 
			
		||||
 | 
			
		||||
	virtual void close() = 0;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -198,8 +229,9 @@ public:
 | 
			
		|||
	static Ref<FileAccess> create(AccessType p_access); /// Create a file access (for the current platform) this is the only portable way of accessing files.
 | 
			
		||||
	static Ref<FileAccess> create_for_path(const String &p_path);
 | 
			
		||||
	static Ref<FileAccess> open(const String &p_path, int p_mode_flags, Error *r_error = nullptr); /// Create a file access (for the current platform) this is the only portable way of accessing files.
 | 
			
		||||
	static Ref<FileAccess> create_temp(int p_mode_flags, const String &p_prefix = "", const String &p_extension = "", bool p_keep = false, Error *r_error = nullptr);
 | 
			
		||||
 | 
			
		||||
	static Ref<FileAccess> open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key);
 | 
			
		||||
	static Ref<FileAccess> open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key, const Vector<uint8_t> &p_iv = Vector<uint8_t>());
 | 
			
		||||
	static Ref<FileAccess> open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass);
 | 
			
		||||
	static Ref<FileAccess> open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode = COMPRESSION_FASTLZ);
 | 
			
		||||
	static Error get_open_error();
 | 
			
		||||
| 
						 | 
				
			
			@ -215,8 +247,8 @@ public:
 | 
			
		|||
	static bool get_read_only_attribute(const String &p_file);
 | 
			
		||||
	static Error set_read_only_attribute(const String &p_file, bool p_ro);
 | 
			
		||||
 | 
			
		||||
	static void set_backup_save(bool p_enable) { backup_save = p_enable; };
 | 
			
		||||
	static bool is_backup_save_enabled() { return backup_save; };
 | 
			
		||||
	static void set_backup_save(bool p_enable) { backup_save = p_enable; }
 | 
			
		||||
	static bool is_backup_save_enabled() { return backup_save; }
 | 
			
		||||
 | 
			
		||||
	static String get_md5(const String &p_file);
 | 
			
		||||
	static String get_sha256(const String &p_file);
 | 
			
		||||
| 
						 | 
				
			
			@ -233,8 +265,9 @@ public:
 | 
			
		|||
		create_func[p_access] = _create_builtin<T>;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	FileAccess() {}
 | 
			
		||||
	virtual ~FileAccess() {}
 | 
			
		||||
	virtual ~FileAccess();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
VARIANT_ENUM_CAST(FileAccess::CompressionMode);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,8 +30,6 @@
 | 
			
		|||
 | 
			
		||||
#include "file_access_compressed.h"
 | 
			
		||||
 | 
			
		||||
#include "core/string/print_string.h"
 | 
			
		||||
 | 
			
		||||
void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_mode, uint32_t p_block_size) {
 | 
			
		||||
	magic = p_magic.ascii().get_data();
 | 
			
		||||
	magic = (magic + "    ").substr(0, 4);
 | 
			
		||||
| 
						 | 
				
			
			@ -40,25 +38,13 @@ void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_
 | 
			
		|||
	block_size = p_block_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define WRITE_FIT(m_bytes)                                  \
 | 
			
		||||
	{                                                       \
 | 
			
		||||
		if (write_pos + (m_bytes) > write_max) {            \
 | 
			
		||||
			write_max = write_pos + (m_bytes);              \
 | 
			
		||||
		}                                                   \
 | 
			
		||||
		if (write_max > write_buffer_size) {                \
 | 
			
		||||
			write_buffer_size = next_power_of_2(write_max); \
 | 
			
		||||
			buffer.resize(write_buffer_size);               \
 | 
			
		||||
			write_ptr = buffer.ptrw();                      \
 | 
			
		||||
		}                                                   \
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
Error FileAccessCompressed::open_after_magic(Ref<FileAccess> p_base) {
 | 
			
		||||
	f = p_base;
 | 
			
		||||
	cmode = (Compression::Mode)f->get_32();
 | 
			
		||||
	block_size = f->get_32();
 | 
			
		||||
	if (block_size == 0) {
 | 
			
		||||
		f.unref();
 | 
			
		||||
		ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Can't open compressed file '" + p_base->get_path() + "' with block size 0, it is corrupted.");
 | 
			
		||||
		ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, vformat("Can't open compressed file '%s' with block size 0, it is corrupted.", p_base->get_path()));
 | 
			
		||||
	}
 | 
			
		||||
	read_total = f->get_32();
 | 
			
		||||
	uint32_t bc = (read_total / block_size) + 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -137,7 +123,7 @@ void FileAccessCompressed::_close() {
 | 
			
		|||
		f->store_buffer((const uint8_t *)mgc.get_data(), mgc.length()); //write header 4
 | 
			
		||||
		f->store_32(cmode); //write compression mode 4
 | 
			
		||||
		f->store_32(block_size); //write block size 4
 | 
			
		||||
		f->store_32(write_max); //max amount of data written 4
 | 
			
		||||
		f->store_32(uint32_t(write_max)); //max amount of data written 4
 | 
			
		||||
		uint32_t bc = (write_max / block_size) + 1;
 | 
			
		||||
 | 
			
		||||
		for (uint32_t i = 0; i < bc; i++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +145,7 @@ void FileAccessCompressed::_close() {
 | 
			
		|||
 | 
			
		||||
		f->seek(16); //ok write block sizes
 | 
			
		||||
		for (uint32_t i = 0; i < bc; i++) {
 | 
			
		||||
			f->store_32(block_sizes[i]);
 | 
			
		||||
			f->store_32(uint32_t(block_sizes[i]));
 | 
			
		||||
		}
 | 
			
		||||
		f->seek_end();
 | 
			
		||||
		f->store_buffer((const uint8_t *)mgc.get_data(), mgc.length()); //magic at the end too
 | 
			
		||||
| 
						 | 
				
			
			@ -260,38 +246,6 @@ bool FileAccessCompressed::eof_reached() const {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t FileAccessCompressed::get_8() const {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode.");
 | 
			
		||||
 | 
			
		||||
	if (at_end) {
 | 
			
		||||
		read_eof = true;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uint8_t ret = read_ptr[read_pos];
 | 
			
		||||
 | 
			
		||||
	read_pos++;
 | 
			
		||||
	if (read_pos >= read_block_size) {
 | 
			
		||||
		read_block++;
 | 
			
		||||
 | 
			
		||||
		if (read_block < read_block_count) {
 | 
			
		||||
			//read another block of compressed data
 | 
			
		||||
			f->get_buffer(comp_buffer.ptrw(), read_blocks[read_block].csize);
 | 
			
		||||
			int total = Compression::decompress(buffer.ptrw(), read_blocks.size() == 1 ? read_total : block_size, comp_buffer.ptr(), read_blocks[read_block].csize, cmode);
 | 
			
		||||
			ERR_FAIL_COND_V_MSG(total == -1, 0, "Compressed file is corrupt.");
 | 
			
		||||
			read_block_size = read_block == read_block_count - 1 ? read_total % block_size : block_size;
 | 
			
		||||
			read_pos = 0;
 | 
			
		||||
 | 
			
		||||
		} else {
 | 
			
		||||
			read_block--;
 | 
			
		||||
			at_end = true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t FileAccessCompressed::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
 | 
			
		||||
	ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(f.is_null(), -1, "File must be opened before use.");
 | 
			
		||||
| 
						 | 
				
			
			@ -341,12 +295,25 @@ void FileAccessCompressed::flush() {
 | 
			
		|||
	// compressed files keep data in memory till close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccessCompressed::store_8(uint8_t p_dest) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use.");
 | 
			
		||||
	ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode.");
 | 
			
		||||
bool FileAccessCompressed::store_buffer(const uint8_t *p_src, uint64_t p_length) {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(f.is_null(), false, "File must be opened before use.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!writing, false, "File has not been opened in write mode.");
 | 
			
		||||
 | 
			
		||||
	WRITE_FIT(1);
 | 
			
		||||
	write_ptr[write_pos++] = p_dest;
 | 
			
		||||
	if (write_pos + (p_length) > write_max) {
 | 
			
		||||
		write_max = write_pos + (p_length);
 | 
			
		||||
	}
 | 
			
		||||
	if (write_max > write_buffer_size) {
 | 
			
		||||
		write_buffer_size = next_power_of_2(write_max);
 | 
			
		||||
		ERR_FAIL_COND_V(buffer.resize(write_buffer_size) != OK, false);
 | 
			
		||||
		write_ptr = buffer.ptrw();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (p_length) {
 | 
			
		||||
		memcpy(write_ptr + write_pos, p_src, p_length);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	write_pos += p_length;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FileAccessCompressed::file_exists(const String &p_name) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,14 +83,13 @@ public:
 | 
			
		|||
 | 
			
		||||
	virtual bool eof_reached() const override; ///< reading passed EOF
 | 
			
		||||
 | 
			
		||||
	virtual uint8_t get_8() const override; ///< get a byte
 | 
			
		||||
	virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
 | 
			
		||||
 | 
			
		||||
	virtual Error get_error() const override; ///< get last error
 | 
			
		||||
 | 
			
		||||
	virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual void flush() override;
 | 
			
		||||
	virtual void store_8(uint8_t p_dest) override; ///< store a byte
 | 
			
		||||
	virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override;
 | 
			
		||||
 | 
			
		||||
	virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,13 +31,10 @@
 | 
			
		|||
#include "file_access_encrypted.h"
 | 
			
		||||
 | 
			
		||||
#include "core/crypto/crypto_core.h"
 | 
			
		||||
#include "core/string/print_string.h"
 | 
			
		||||
#include "core/variant/variant.h"
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic) {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(file != nullptr, ERR_ALREADY_IN_USE, "Can't open file while another file from path '" + file->get_path_absolute() + "' is open.");
 | 
			
		||||
Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic, const Vector<uint8_t> &p_iv) {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(file.is_valid(), ERR_ALREADY_IN_USE, vformat("Can't open file while another file from path '%s' is open.", file->get_path_absolute()));
 | 
			
		||||
	ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER);
 | 
			
		||||
 | 
			
		||||
	pos = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -49,6 +46,16 @@ Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<u
 | 
			
		|||
		writing = true;
 | 
			
		||||
		file = p_base;
 | 
			
		||||
		key = p_key;
 | 
			
		||||
		if (p_iv.is_empty()) {
 | 
			
		||||
			iv.resize(16);
 | 
			
		||||
			CryptoCore::RandomGenerator rng;
 | 
			
		||||
			ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator.");
 | 
			
		||||
			Error err = rng.get_random_bytes(iv.ptrw(), 16);
 | 
			
		||||
			ERR_FAIL_COND_V(err != OK, err);
 | 
			
		||||
		} else {
 | 
			
		||||
			ERR_FAIL_COND_V(p_iv.size() != 16, ERR_INVALID_PARAMETER);
 | 
			
		||||
			iv = p_iv;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} else if (p_mode == MODE_READ) {
 | 
			
		||||
		writing = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -63,10 +70,8 @@ Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<u
 | 
			
		|||
		p_base->get_buffer(md5d, 16);
 | 
			
		||||
		length = p_base->get_64();
 | 
			
		||||
 | 
			
		||||
		unsigned char iv[16];
 | 
			
		||||
		for (int i = 0; i < 16; i++) {
 | 
			
		||||
			iv[i] = p_base->get_8();
 | 
			
		||||
		}
 | 
			
		||||
		iv.resize(16);
 | 
			
		||||
		p_base->get_buffer(iv.ptrw(), 16);
 | 
			
		||||
 | 
			
		||||
		base = p_base->get_position();
 | 
			
		||||
		ERR_FAIL_COND_V(p_base->get_length() < base + length, ERR_FILE_CORRUPT);
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +88,7 @@ Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<u
 | 
			
		|||
			CryptoCore::AESContext ctx;
 | 
			
		||||
 | 
			
		||||
			ctx.set_encode_key(key.ptrw(), 256); // Due to the nature of CFB, same key schedule is used for both encryption and decryption!
 | 
			
		||||
			ctx.decrypt_cfb(ds, iv, data.ptrw(), data.ptrw());
 | 
			
		||||
			ctx.decrypt_cfb(ds, iv.ptrw(), data.ptrw(), data.ptrw());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		data.resize(length);
 | 
			
		||||
| 
						 | 
				
			
			@ -145,14 +150,9 @@ void FileAccessEncrypted::_close() {
 | 
			
		|||
 | 
			
		||||
		file->store_buffer(hash, 16);
 | 
			
		||||
		file->store_64(data.size());
 | 
			
		||||
		file->store_buffer(iv.ptr(), 16);
 | 
			
		||||
 | 
			
		||||
		unsigned char iv[16];
 | 
			
		||||
		for (int i = 0; i < 16; i++) {
 | 
			
		||||
			iv[i] = Math::rand() % 256;
 | 
			
		||||
			file->store_8(iv[i]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ctx.encrypt_cfb(len, iv, compressed.ptrw(), compressed.ptrw());
 | 
			
		||||
		ctx.encrypt_cfb(len, iv.ptrw(), compressed.ptrw(), compressed.ptrw());
 | 
			
		||||
 | 
			
		||||
		file->store_buffer(compressed.ptr(), compressed.size());
 | 
			
		||||
		data.clear();
 | 
			
		||||
| 
						 | 
				
			
			@ -162,7 +162,7 @@ void FileAccessEncrypted::_close() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
bool FileAccessEncrypted::is_open() const {
 | 
			
		||||
	return file != nullptr;
 | 
			
		||||
	return file.is_valid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String FileAccessEncrypted::get_path() const {
 | 
			
		||||
| 
						 | 
				
			
			@ -206,26 +206,19 @@ bool FileAccessEncrypted::eof_reached() const {
 | 
			
		|||
	return eofed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t FileAccessEncrypted::get_8() const {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode.");
 | 
			
		||||
	if (pos >= get_length()) {
 | 
			
		||||
		eofed = true;
 | 
			
		||||
uint64_t FileAccessEncrypted::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(writing, -1, "File has not been opened in read mode.");
 | 
			
		||||
 | 
			
		||||
	if (!p_length) {
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uint8_t b = data[pos];
 | 
			
		||||
	pos++;
 | 
			
		||||
	return b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t FileAccessEncrypted::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
 | 
			
		||||
	ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(writing, -1, "File has not been opened in read mode.");
 | 
			
		||||
	ERR_FAIL_NULL_V(p_dst, -1);
 | 
			
		||||
 | 
			
		||||
	uint64_t to_copy = MIN(p_length, get_length() - pos);
 | 
			
		||||
	for (uint64_t i = 0; i < to_copy; i++) {
 | 
			
		||||
		p_dst[i] = data[pos++];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memcpy(p_dst, data.ptr() + pos, to_copy);
 | 
			
		||||
	pos += to_copy;
 | 
			
		||||
 | 
			
		||||
	if (to_copy < p_length) {
 | 
			
		||||
		eofed = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -238,21 +231,23 @@ Error FileAccessEncrypted::get_error() const {
 | 
			
		|||
	return eofed ? ERR_FILE_EOF : OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccessEncrypted::store_buffer(const uint8_t *p_src, uint64_t p_length) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode.");
 | 
			
		||||
	ERR_FAIL_COND(!p_src && p_length > 0);
 | 
			
		||||
bool FileAccessEncrypted::store_buffer(const uint8_t *p_src, uint64_t p_length) {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!writing, false, "File has not been opened in write mode.");
 | 
			
		||||
 | 
			
		||||
	if (pos < get_length()) {
 | 
			
		||||
		for (uint64_t i = 0; i < p_length; i++) {
 | 
			
		||||
			store_8(p_src[i]);
 | 
			
		||||
	if (!p_length) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	} else if (pos == get_length()) {
 | 
			
		||||
		data.resize(pos + p_length);
 | 
			
		||||
		for (uint64_t i = 0; i < p_length; i++) {
 | 
			
		||||
			data.write[pos + i] = p_src[i];
 | 
			
		||||
 | 
			
		||||
	ERR_FAIL_NULL_V(p_src, false);
 | 
			
		||||
 | 
			
		||||
	if (pos + p_length >= get_length()) {
 | 
			
		||||
		ERR_FAIL_COND_V(data.resize(pos + p_length) != OK, false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memcpy(data.ptrw() + pos, p_src, p_length);
 | 
			
		||||
	pos += p_length;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccessEncrypted::flush() {
 | 
			
		||||
| 
						 | 
				
			
			@ -261,18 +256,6 @@ void FileAccessEncrypted::flush() {
 | 
			
		|||
	// encrypted files keep data in memory till close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccessEncrypted::store_8(uint8_t p_dest) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode.");
 | 
			
		||||
 | 
			
		||||
	if (pos < get_length()) {
 | 
			
		||||
		data.write[pos] = p_dest;
 | 
			
		||||
		pos++;
 | 
			
		||||
	} else if (pos == get_length()) {
 | 
			
		||||
		data.push_back(p_dest);
 | 
			
		||||
		pos++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FileAccessEncrypted::file_exists(const String &p_name) {
 | 
			
		||||
	Ref<FileAccess> fa = FileAccess::open(p_name, FileAccess::READ);
 | 
			
		||||
	if (fa.is_null()) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,13 +37,14 @@
 | 
			
		|||
 | 
			
		||||
class FileAccessEncrypted : public FileAccess {
 | 
			
		||||
public:
 | 
			
		||||
	enum Mode {
 | 
			
		||||
	enum Mode : int32_t {
 | 
			
		||||
		MODE_READ,
 | 
			
		||||
		MODE_WRITE_AES256,
 | 
			
		||||
		MODE_MAX
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Vector<uint8_t> iv;
 | 
			
		||||
	Vector<uint8_t> key;
 | 
			
		||||
	bool writing = false;
 | 
			
		||||
	Ref<FileAccess> file;
 | 
			
		||||
| 
						 | 
				
			
			@ -57,9 +58,11 @@ private:
 | 
			
		|||
	void _close();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	Error open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic = true);
 | 
			
		||||
	Error open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic = true, const Vector<uint8_t> &p_iv = Vector<uint8_t>());
 | 
			
		||||
	Error open_and_parse_password(Ref<FileAccess> p_base, const String &p_key, Mode p_mode);
 | 
			
		||||
 | 
			
		||||
	Vector<uint8_t> get_iv() const { return iv; }
 | 
			
		||||
 | 
			
		||||
	virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
 | 
			
		||||
	virtual bool is_open() const override; ///< true when file is open
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -73,15 +76,13 @@ public:
 | 
			
		|||
 | 
			
		||||
	virtual bool eof_reached() const override; ///< reading passed EOF
 | 
			
		||||
 | 
			
		||||
	virtual uint8_t get_8() const override; ///< get a byte
 | 
			
		||||
	virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
 | 
			
		||||
 | 
			
		||||
	virtual Error get_error() const override; ///< get last error
 | 
			
		||||
 | 
			
		||||
	virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual void flush() override;
 | 
			
		||||
	virtual void store_8(uint8_t p_dest) override; ///< store a byte
 | 
			
		||||
	virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
 | 
			
		||||
	virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
 | 
			
		||||
 | 
			
		||||
	virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,8 +31,6 @@
 | 
			
		|||
#include "file_access_memory.h"
 | 
			
		||||
 | 
			
		||||
#include "core/config/project_settings.h"
 | 
			
		||||
#include "core/io/dir_access.h"
 | 
			
		||||
#include "core/templates/rb_map.h"
 | 
			
		||||
 | 
			
		||||
static HashMap<String, Vector<uint8_t>> *files = nullptr;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +83,7 @@ Error FileAccessMemory::open_internal(const String &p_path, int p_mode_flags) {
 | 
			
		|||
	//name = DirAccess::normalize_path(name);
 | 
			
		||||
 | 
			
		||||
	HashMap<String, Vector<uint8_t>>::Iterator E = files->find(name);
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!E, ERR_FILE_NOT_FOUND, "Can't find file '" + p_path + "'.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!E, ERR_FILE_NOT_FOUND, vformat("Can't find file '%s'.", p_path));
 | 
			
		||||
 | 
			
		||||
	data = E->value.ptrw();
 | 
			
		||||
	length = E->value.size();
 | 
			
		||||
| 
						 | 
				
			
			@ -122,18 +120,12 @@ bool FileAccessMemory::eof_reached() const {
 | 
			
		|||
	return pos >= length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t FileAccessMemory::get_8() const {
 | 
			
		||||
	uint8_t ret = 0;
 | 
			
		||||
	if (pos < length) {
 | 
			
		||||
		ret = data[pos];
 | 
			
		||||
	}
 | 
			
		||||
	++pos;
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t FileAccessMemory::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
 | 
			
		||||
	ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
 | 
			
		||||
	if (!p_length) {
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ERR_FAIL_NULL_V(p_dst, -1);
 | 
			
		||||
	ERR_FAIL_NULL_V(data, -1);
 | 
			
		||||
 | 
			
		||||
	uint64_t left = length - pos;
 | 
			
		||||
| 
						 | 
				
			
			@ -157,20 +149,20 @@ void FileAccessMemory::flush() {
 | 
			
		|||
	ERR_FAIL_NULL(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccessMemory::store_8(uint8_t p_byte) {
 | 
			
		||||
	ERR_FAIL_NULL(data);
 | 
			
		||||
	ERR_FAIL_COND(pos >= length);
 | 
			
		||||
	data[pos++] = p_byte;
 | 
			
		||||
bool FileAccessMemory::store_buffer(const uint8_t *p_src, uint64_t p_length) {
 | 
			
		||||
	if (!p_length) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
void FileAccessMemory::store_buffer(const uint8_t *p_src, uint64_t p_length) {
 | 
			
		||||
	ERR_FAIL_COND(!p_src && p_length > 0);
 | 
			
		||||
	ERR_FAIL_NULL_V(p_src, false);
 | 
			
		||||
 | 
			
		||||
	uint64_t left = length - pos;
 | 
			
		||||
	uint64_t write = MIN(p_length, left);
 | 
			
		||||
	if (write < p_length) {
 | 
			
		||||
		WARN_PRINT("Writing less data than requested");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memcpy(&data[pos], p_src, write);
 | 
			
		||||
	pos += write;
 | 
			
		||||
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(write < p_length, false, "Writing less data than requested.");
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,16 +55,13 @@ public:
 | 
			
		|||
 | 
			
		||||
	virtual bool eof_reached() const override; ///< reading passed EOF
 | 
			
		||||
 | 
			
		||||
	virtual uint8_t get_8() const override; ///< get a byte
 | 
			
		||||
 | 
			
		||||
	virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; ///< get an array of bytes
 | 
			
		||||
 | 
			
		||||
	virtual Error get_error() const override; ///< get last error
 | 
			
		||||
 | 
			
		||||
	virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual void flush() override;
 | 
			
		||||
	virtual void store_8(uint8_t p_byte) override; ///< store a byte
 | 
			
		||||
	virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
 | 
			
		||||
	virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
 | 
			
		||||
 | 
			
		||||
	virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,8 +35,6 @@
 | 
			
		|||
#include "core/os/os.h"
 | 
			
		||||
#include "core/version.h"
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) {
 | 
			
		||||
	for (int i = 0; i < sources.size(); i++) {
 | 
			
		||||
		if (sources[i]->try_open_pack(p_path, p_replace_files, p_offset)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -48,7 +46,7 @@ Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted) {
 | 
			
		||||
	String simplified_path = p_path.simplify_path();
 | 
			
		||||
	String simplified_path = p_path.simplify_path().trim_prefix("res://");
 | 
			
		||||
	PathMD5 pmd5(simplified_path.md5_buffer());
 | 
			
		||||
 | 
			
		||||
	bool exists = files.has(pmd5);
 | 
			
		||||
| 
						 | 
				
			
			@ -68,13 +66,11 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if (!exists) {
 | 
			
		||||
		//search for dir
 | 
			
		||||
		String p = simplified_path.replace_first("res://", "");
 | 
			
		||||
		// Search for directory.
 | 
			
		||||
		PackedDir *cd = root;
 | 
			
		||||
 | 
			
		||||
		if (p.contains("/")) { //in a subdir
 | 
			
		||||
 | 
			
		||||
			Vector<String> ds = p.get_base_dir().split("/");
 | 
			
		||||
		if (simplified_path.contains_char('/')) { // In a subdirectory.
 | 
			
		||||
			Vector<String> ds = simplified_path.get_base_dir().split("/");
 | 
			
		||||
 | 
			
		||||
			for (int j = 0; j < ds.size(); j++) {
 | 
			
		||||
				if (!cd->subdirs.has(ds[j])) {
 | 
			
		||||
| 
						 | 
				
			
			@ -89,19 +85,79 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
		String filename = simplified_path.get_file();
 | 
			
		||||
		// Don't add as a file if the path points to a directory
 | 
			
		||||
		// Don't add as a file if the path points to a directory.
 | 
			
		||||
		if (!filename.is_empty()) {
 | 
			
		||||
			cd->files.insert(filename);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PackedData::remove_path(const String &p_path) {
 | 
			
		||||
	String simplified_path = p_path.simplify_path().trim_prefix("res://");
 | 
			
		||||
	PathMD5 pmd5(simplified_path.md5_buffer());
 | 
			
		||||
	if (!files.has(pmd5)) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Search for directory.
 | 
			
		||||
	PackedDir *cd = root;
 | 
			
		||||
 | 
			
		||||
	if (simplified_path.contains_char('/')) { // In a subdirectory.
 | 
			
		||||
		Vector<String> ds = simplified_path.get_base_dir().split("/");
 | 
			
		||||
 | 
			
		||||
		for (int j = 0; j < ds.size(); j++) {
 | 
			
		||||
			if (!cd->subdirs.has(ds[j])) {
 | 
			
		||||
				return; // Subdirectory does not exist, do not bother creating.
 | 
			
		||||
			} else {
 | 
			
		||||
				cd = cd->subdirs[ds[j]];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cd->files.erase(simplified_path.get_file());
 | 
			
		||||
 | 
			
		||||
	files.erase(pmd5);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PackedData::add_pack_source(PackSource *p_source) {
 | 
			
		||||
	if (p_source != nullptr) {
 | 
			
		||||
		sources.push_back(p_source);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t *PackedData::get_file_hash(const String &p_path) {
 | 
			
		||||
	String simplified_path = p_path.simplify_path().trim_prefix("res://");
 | 
			
		||||
	PathMD5 pmd5(simplified_path.md5_buffer());
 | 
			
		||||
	HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5);
 | 
			
		||||
	if (!E) {
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return E->value.md5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HashSet<String> PackedData::get_file_paths() const {
 | 
			
		||||
	HashSet<String> file_paths;
 | 
			
		||||
	_get_file_paths(root, root->name, file_paths);
 | 
			
		||||
	return file_paths;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PackedData::_get_file_paths(PackedDir *p_dir, const String &p_parent_dir, HashSet<String> &r_paths) const {
 | 
			
		||||
	for (const String &E : p_dir->files) {
 | 
			
		||||
		r_paths.insert(p_parent_dir.path_join(E));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (const KeyValue<String, PackedDir *> &E : p_dir->subdirs) {
 | 
			
		||||
		_get_file_paths(E.value, p_parent_dir.path_join(E.key), r_paths);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PackedData::clear() {
 | 
			
		||||
	files.clear();
 | 
			
		||||
	_free_packed_dirs(root);
 | 
			
		||||
	root = memnew(PackedDir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PackedData *PackedData::singleton = nullptr;
 | 
			
		||||
 | 
			
		||||
PackedData::PackedData() {
 | 
			
		||||
| 
						 | 
				
			
			@ -207,8 +263,8 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files,
 | 
			
		|||
	uint32_t ver_minor = f->get_32();
 | 
			
		||||
	f->get_32(); // patch number, not used for validation.
 | 
			
		||||
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(version != PACK_FORMAT_VERSION, false, "Pack version unsupported: " + itos(version) + ".");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), false, "Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor) + ".");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(version != PACK_FORMAT_VERSION, false, vformat("Pack version unsupported: %d.", version));
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), false, vformat("Pack created with a newer version of the engine: %d.%d.", ver_major, ver_minor));
 | 
			
		||||
 | 
			
		||||
	uint32_t pack_flags = f->get_32();
 | 
			
		||||
	uint64_t file_base = f->get_64();
 | 
			
		||||
| 
						 | 
				
			
			@ -251,15 +307,19 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files,
 | 
			
		|||
		cs[sl] = 0;
 | 
			
		||||
 | 
			
		||||
		String path;
 | 
			
		||||
		path.parse_utf8(cs.ptr());
 | 
			
		||||
		path.parse_utf8(cs.ptr(), sl);
 | 
			
		||||
 | 
			
		||||
		uint64_t ofs = file_base + f->get_64();
 | 
			
		||||
		uint64_t ofs = f->get_64();
 | 
			
		||||
		uint64_t size = f->get_64();
 | 
			
		||||
		uint8_t md5[16];
 | 
			
		||||
		f->get_buffer(md5, 16);
 | 
			
		||||
		uint32_t flags = f->get_32();
 | 
			
		||||
 | 
			
		||||
		PackedData::get_singleton()->add_path(p_path, path, ofs + p_offset, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED));
 | 
			
		||||
		if (flags & PACK_FILE_REMOVAL) { // The file was removed.
 | 
			
		||||
			PackedData::get_singleton()->remove_path(path);
 | 
			
		||||
		} else {
 | 
			
		||||
			PackedData::get_singleton()->add_path(p_path, path, file_base + ofs + p_offset, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
| 
						 | 
				
			
			@ -271,6 +331,44 @@ Ref<FileAccess> PackedSourcePCK::get_file(const String &p_path, PackedData::Pack
 | 
			
		|||
 | 
			
		||||
//////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
bool PackedSourceDirectory::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) {
 | 
			
		||||
	// Load with offset feature only supported for PCK files.
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(p_offset != 0, false, "Invalid PCK data. Note that loading files with a non-zero offset isn't supported with directories.");
 | 
			
		||||
 | 
			
		||||
	if (p_path != "res://") {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	add_directory(p_path, p_replace_files);
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<FileAccess> PackedSourceDirectory::get_file(const String &p_path, PackedData::PackedFile *p_file) {
 | 
			
		||||
	Ref<FileAccess> ret = FileAccess::create_for_path(p_path);
 | 
			
		||||
	ret->reopen(p_path, FileAccess::READ);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PackedSourceDirectory::add_directory(const String &p_path, bool p_replace_files) {
 | 
			
		||||
	Ref<DirAccess> da = DirAccess::open(p_path);
 | 
			
		||||
	if (da.is_null()) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	da->set_include_hidden(true);
 | 
			
		||||
 | 
			
		||||
	for (const String &file_name : da->get_files()) {
 | 
			
		||||
		String file_path = p_path.path_join(file_name);
 | 
			
		||||
		uint8_t md5[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 | 
			
		||||
		PackedData::get_singleton()->add_path(p_path, file_path, 0, 0, md5, this, p_replace_files, false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (const String &sub_dir_name : da->get_directories()) {
 | 
			
		||||
		String sub_dir_path = p_path.path_join(sub_dir_name);
 | 
			
		||||
		add_directory(sub_dir_path, p_replace_files);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
Error FileAccessPack::open_internal(const String &p_path, int p_mode_flags) {
 | 
			
		||||
	ERR_PRINT("Can't open pack-referenced file.");
 | 
			
		||||
	return ERR_UNAVAILABLE;
 | 
			
		||||
| 
						 | 
				
			
			@ -313,17 +411,6 @@ bool FileAccessPack::eof_reached() const {
 | 
			
		|||
	return eof;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t FileAccessPack::get_8() const {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use.");
 | 
			
		||||
	if (pos >= pf.size) {
 | 
			
		||||
		eof = true;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pos++;
 | 
			
		||||
	return f->get_8();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t FileAccessPack::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(f.is_null(), -1, "File must be opened before use.");
 | 
			
		||||
	ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
 | 
			
		||||
| 
						 | 
				
			
			@ -366,12 +453,8 @@ void FileAccessPack::flush() {
 | 
			
		|||
	ERR_FAIL();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccessPack::store_8(uint8_t p_dest) {
 | 
			
		||||
	ERR_FAIL();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccessPack::store_buffer(const uint8_t *p_src, uint64_t p_length) {
 | 
			
		||||
	ERR_FAIL();
 | 
			
		||||
bool FileAccessPack::store_buffer(const uint8_t *p_src, uint64_t p_length) {
 | 
			
		||||
	ERR_FAIL_V(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FileAccessPack::file_exists(const String &p_name) {
 | 
			
		||||
| 
						 | 
				
			
			@ -385,7 +468,7 @@ void FileAccessPack::close() {
 | 
			
		|||
FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) :
 | 
			
		||||
		pf(p_file),
 | 
			
		||||
		f(FileAccess::open(pf.pack, FileAccess::READ)) {
 | 
			
		||||
	ERR_FAIL_COND_MSG(f.is_null(), "Can't open pack-referenced file '" + String(pf.pack) + "'.");
 | 
			
		||||
	ERR_FAIL_COND_MSG(f.is_null(), vformat("Can't open pack-referenced file '%s'.", String(pf.pack)));
 | 
			
		||||
 | 
			
		||||
	f->seek(pf.offset);
 | 
			
		||||
	off = pf.offset;
 | 
			
		||||
| 
						 | 
				
			
			@ -393,7 +476,7 @@ FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFil
 | 
			
		|||
	if (pf.encrypted) {
 | 
			
		||||
		Ref<FileAccessEncrypted> fae;
 | 
			
		||||
		fae.instantiate();
 | 
			
		||||
		ERR_FAIL_COND_MSG(fae.is_null(), "Can't open encrypted pack-referenced file '" + String(pf.pack) + "'.");
 | 
			
		||||
		ERR_FAIL_COND_MSG(fae.is_null(), vformat("Can't open encrypted pack-referenced file '%s'.", String(pf.pack)));
 | 
			
		||||
 | 
			
		||||
		Vector<uint8_t> key;
 | 
			
		||||
		key.resize(32);
 | 
			
		||||
| 
						 | 
				
			
			@ -402,7 +485,7 @@ FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFil
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false);
 | 
			
		||||
		ERR_FAIL_COND_MSG(err, "Can't open encrypted pack-referenced file '" + String(pf.pack) + "'.");
 | 
			
		||||
		ERR_FAIL_COND_MSG(err, vformat("Can't open encrypted pack-referenced file '%s'.", String(pf.pack)));
 | 
			
		||||
		f = fae;
 | 
			
		||||
		off = 0;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -543,8 +626,6 @@ String DirAccessPack::get_current_dir(bool p_include_drive) const {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
bool DirAccessPack::file_exists(String p_file) {
 | 
			
		||||
	p_file = fix_path(p_file);
 | 
			
		||||
 | 
			
		||||
	PackedData::PackedDir *pd = _find_dir(p_file.get_base_dir());
 | 
			
		||||
	if (!pd) {
 | 
			
		||||
		return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -553,8 +634,6 @@ bool DirAccessPack::file_exists(String p_file) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
bool DirAccessPack::dir_exists(String p_dir) {
 | 
			
		||||
	p_dir = fix_path(p_dir);
 | 
			
		||||
 | 
			
		||||
	return _find_dir(p_dir) != nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,7 +36,6 @@
 | 
			
		|||
#include "core/string/print_string.h"
 | 
			
		||||
#include "core/templates/hash_set.h"
 | 
			
		||||
#include "core/templates/list.h"
 | 
			
		||||
#include "core/templates/rb_map.h"
 | 
			
		||||
 | 
			
		||||
// Godot's packed file magic header ("GDPC" in ASCII).
 | 
			
		||||
#define PACK_HEADER_MAGIC 0x43504447
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +48,8 @@ enum PackFlags {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
enum PackFileFlags {
 | 
			
		||||
	PACK_FILE_ENCRYPTED = 1 << 0
 | 
			
		||||
	PACK_FILE_ENCRYPTED = 1 << 0,
 | 
			
		||||
	PACK_FILE_REMOVAL = 1 << 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PackSource;
 | 
			
		||||
| 
						 | 
				
			
			@ -107,10 +107,14 @@ private:
 | 
			
		|||
	bool disabled = false;
 | 
			
		||||
 | 
			
		||||
	void _free_packed_dirs(PackedDir *p_dir);
 | 
			
		||||
	void _get_file_paths(PackedDir *p_dir, const String &p_parent_dir, HashSet<String> &r_paths) const;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	void add_pack_source(PackSource *p_source);
 | 
			
		||||
	void add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false); // for PackSource
 | 
			
		||||
	void remove_path(const String &p_path);
 | 
			
		||||
	uint8_t *get_file_hash(const String &p_path);
 | 
			
		||||
	HashSet<String> get_file_paths() const;
 | 
			
		||||
 | 
			
		||||
	void set_disabled(bool p_disabled) { disabled = p_disabled; }
 | 
			
		||||
	_FORCE_INLINE_ bool is_disabled() const { return disabled; }
 | 
			
		||||
| 
						 | 
				
			
			@ -118,6 +122,8 @@ public:
 | 
			
		|||
	static PackedData *get_singleton() { return singleton; }
 | 
			
		||||
	Error add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset);
 | 
			
		||||
 | 
			
		||||
	void clear();
 | 
			
		||||
 | 
			
		||||
	_FORCE_INLINE_ Ref<FileAccess> try_open_path(const String &p_path);
 | 
			
		||||
	_FORCE_INLINE_ bool has_path(const String &p_path);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -141,6 +147,14 @@ public:
 | 
			
		|||
	virtual Ref<FileAccess> get_file(const String &p_path, PackedData::PackedFile *p_file) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PackedSourceDirectory : public PackSource {
 | 
			
		||||
	void add_directory(const String &p_path, bool p_replace_files);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) override;
 | 
			
		||||
	virtual Ref<FileAccess> get_file(const String &p_path, PackedData::PackedFile *p_file) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class FileAccessPack : public FileAccess {
 | 
			
		||||
	PackedData::PackedFile pf;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -169,8 +183,6 @@ public:
 | 
			
		|||
 | 
			
		||||
	virtual bool eof_reached() const override;
 | 
			
		||||
 | 
			
		||||
	virtual uint8_t get_8() const override;
 | 
			
		||||
 | 
			
		||||
	virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
 | 
			
		||||
 | 
			
		||||
	virtual void set_big_endian(bool p_big_endian) override;
 | 
			
		||||
| 
						 | 
				
			
			@ -179,9 +191,7 @@ public:
 | 
			
		|||
 | 
			
		||||
	virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual void flush() override;
 | 
			
		||||
	virtual void store_8(uint8_t p_dest) override;
 | 
			
		||||
 | 
			
		||||
	virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override;
 | 
			
		||||
	virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override;
 | 
			
		||||
 | 
			
		||||
	virtual bool file_exists(const String &p_name) override;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -191,21 +201,18 @@ public:
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
Ref<FileAccess> PackedData::try_open_path(const String &p_path) {
 | 
			
		||||
	String simplified_path = p_path.simplify_path();
 | 
			
		||||
	String simplified_path = p_path.simplify_path().trim_prefix("res://");
 | 
			
		||||
	PathMD5 pmd5(simplified_path.md5_buffer());
 | 
			
		||||
	HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5);
 | 
			
		||||
	if (!E) {
 | 
			
		||||
		return nullptr; //not found
 | 
			
		||||
	}
 | 
			
		||||
	if (E->value.offset == 0) {
 | 
			
		||||
		return nullptr; //was erased
 | 
			
		||||
		return nullptr; // Not found.
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return E->value.src->get_file(p_path, &E->value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PackedData::has_path(const String &p_path) {
 | 
			
		||||
	return files.has(PathMD5(p_path.simplify_path().md5_buffer()));
 | 
			
		||||
	return files.has(PathMD5(p_path.simplify_path().trim_prefix("res://").md5_buffer()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PackedData::has_directory(const String &p_path) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -116,7 +116,7 @@ void ZipArchive::close_handle(unzFile p_file) const {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
unzFile ZipArchive::get_file_handle(const String &p_file) const {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!file_exists(p_file), nullptr, "File '" + p_file + " doesn't exist.");
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(!file_exists(p_file), nullptr, vformat("File '%s' doesn't exist.", p_file));
 | 
			
		||||
	File file = files[p_file];
 | 
			
		||||
 | 
			
		||||
	zlib_filefunc_def io;
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +136,7 @@ unzFile ZipArchive::get_file_handle(const String &p_file) const {
 | 
			
		|||
	io.free_mem = godot_free;
 | 
			
		||||
 | 
			
		||||
	unzFile pkg = unzOpen2(packages[file.package].filename.utf8().get_data(), &io);
 | 
			
		||||
	ERR_FAIL_NULL_V_MSG(pkg, nullptr, "Cannot open file '" + packages[file.package].filename + "'.");
 | 
			
		||||
	ERR_FAIL_NULL_V_MSG(pkg, nullptr, vformat("Cannot open file '%s'.", packages[file.package].filename));
 | 
			
		||||
	int unz_err = unzGoToFilePos(pkg, &file.file_pos);
 | 
			
		||||
	if (unz_err != UNZ_OK || unzOpenCurrentFile(pkg) != UNZ_OK) {
 | 
			
		||||
		unzClose(pkg);
 | 
			
		||||
| 
						 | 
				
			
			@ -291,12 +291,6 @@ bool FileAccessZip::eof_reached() const {
 | 
			
		|||
	return at_eof;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t FileAccessZip::get_8() const {
 | 
			
		||||
	uint8_t ret = 0;
 | 
			
		||||
	get_buffer(&ret, 1);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t FileAccessZip::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
 | 
			
		||||
	ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
 | 
			
		||||
	ERR_FAIL_NULL_V(zfile, -1);
 | 
			
		||||
| 
						 | 
				
			
			@ -328,8 +322,8 @@ void FileAccessZip::flush() {
 | 
			
		|||
	ERR_FAIL();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FileAccessZip::store_8(uint8_t p_dest) {
 | 
			
		||||
	ERR_FAIL();
 | 
			
		||||
bool FileAccessZip::store_buffer(const uint8_t *p_src, uint64_t p_length) {
 | 
			
		||||
	ERR_FAIL_V(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FileAccessZip::file_exists(const String &p_name) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,12 +34,9 @@
 | 
			
		|||
#ifdef MINIZIP_ENABLED
 | 
			
		||||
 | 
			
		||||
#include "core/io/file_access_pack.h"
 | 
			
		||||
#include "core/templates/rb_map.h"
 | 
			
		||||
 | 
			
		||||
#include "thirdparty/minizip/unzip.h"
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
class ZipArchive : public PackSource {
 | 
			
		||||
public:
 | 
			
		||||
	struct File {
 | 
			
		||||
| 
						 | 
				
			
			@ -95,14 +92,13 @@ public:
 | 
			
		|||
 | 
			
		||||
	virtual bool eof_reached() const override; ///< reading passed EOF
 | 
			
		||||
 | 
			
		||||
	virtual uint8_t get_8() const override; ///< get a byte
 | 
			
		||||
	virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
 | 
			
		||||
 | 
			
		||||
	virtual Error get_error() const override; ///< get last error
 | 
			
		||||
 | 
			
		||||
	virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
 | 
			
		||||
	virtual void flush() override;
 | 
			
		||||
	virtual void store_8(uint8_t p_dest) override; ///< store a byte
 | 
			
		||||
	virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override;
 | 
			
		||||
 | 
			
		||||
	virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,9 +42,9 @@ const char *HTTPClient::_methods[METHOD_MAX] = {
 | 
			
		|||
	"PATCH"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
HTTPClient *HTTPClient::create() {
 | 
			
		||||
HTTPClient *HTTPClient::create(bool p_notify_postinitialize) {
 | 
			
		||||
	if (_create) {
 | 
			
		||||
		return _create();
 | 
			
		||||
		return _create(p_notify_postinitialize);
 | 
			
		||||
	}
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -100,9 +100,9 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) {
 | 
			
		|||
Error HTTPClient::verify_headers(const Vector<String> &p_headers) {
 | 
			
		||||
	for (int i = 0; i < p_headers.size(); i++) {
 | 
			
		||||
		String sanitized = p_headers[i].strip_edges();
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(sanitized.is_empty(), ERR_INVALID_PARAMETER, "Invalid HTTP header at index " + itos(i) + ": empty.");
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(sanitized.find(":") < 1, ERR_INVALID_PARAMETER,
 | 
			
		||||
				"Invalid HTTP header at index " + itos(i) + ": String must contain header-value pair, delimited by ':', but was: " + p_headers[i]);
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(sanitized.is_empty(), ERR_INVALID_PARAMETER, vformat("Invalid HTTP header at index %d: empty.", i));
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(sanitized.find_char(':') < 1, ERR_INVALID_PARAMETER,
 | 
			
		||||
				vformat("Invalid HTTP header at index %d: String must contain header-value pair, delimited by ':', but was: '%s'.", i, p_headers[i]));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return OK;
 | 
			
		||||
| 
						 | 
				
			
			@ -113,7 +113,7 @@ Dictionary HTTPClient::_get_response_headers_as_dictionary() {
 | 
			
		|||
	get_response_headers(&rh);
 | 
			
		||||
	Dictionary ret;
 | 
			
		||||
	for (const String &s : rh) {
 | 
			
		||||
		int sp = s.find(":");
 | 
			
		||||
		int sp = s.find_char(':');
 | 
			
		||||
		if (sp == -1) {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -158,12 +158,12 @@ protected:
 | 
			
		|||
	Error _request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body);
 | 
			
		||||
	Error _request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body = String());
 | 
			
		||||
 | 
			
		||||
	static HTTPClient *(*_create)();
 | 
			
		||||
	static HTTPClient *(*_create)(bool p_notify_postinitialize);
 | 
			
		||||
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static HTTPClient *create();
 | 
			
		||||
	static HTTPClient *create(bool p_notify_postinitialize = true);
 | 
			
		||||
 | 
			
		||||
	String query_string_from_dict(const Dictionary &p_dict);
 | 
			
		||||
	Error verify_headers(const Vector<String> &p_headers);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,8 +35,8 @@
 | 
			
		|||
#include "core/io/stream_peer_tls.h"
 | 
			
		||||
#include "core/version.h"
 | 
			
		||||
 | 
			
		||||
HTTPClient *HTTPClientTCP::_create_func() {
 | 
			
		||||
	return memnew(HTTPClientTCP);
 | 
			
		||||
HTTPClient *HTTPClientTCP::_create_func(bool p_notify_postinitialize) {
 | 
			
		||||
	return static_cast<HTTPClient *>(ClassDB::creator<HTTPClientTCP>(p_notify_postinitialize));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, Ref<TLSOptions> p_options) {
 | 
			
		||||
| 
						 | 
				
			
			@ -484,7 +484,7 @@ Error HTTPClientTCP::poll() {
 | 
			
		|||
					// End of response, parse.
 | 
			
		||||
					response_str.push_back(0);
 | 
			
		||||
					String response;
 | 
			
		||||
					response.parse_utf8((const char *)response_str.ptr());
 | 
			
		||||
					response.parse_utf8((const char *)response_str.ptr(), response_str.size());
 | 
			
		||||
					Vector<String> responses = response.split("\n");
 | 
			
		||||
					body_size = -1;
 | 
			
		||||
					chunked = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -508,11 +508,11 @@ Error HTTPClientTCP::poll() {
 | 
			
		|||
							continue;
 | 
			
		||||
						}
 | 
			
		||||
						if (s.begins_with("content-length:")) {
 | 
			
		||||
							body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int();
 | 
			
		||||
							body_size = s.substr(s.find_char(':') + 1, s.length()).strip_edges().to_int();
 | 
			
		||||
							body_left = body_size;
 | 
			
		||||
 | 
			
		||||
						} else if (s.begins_with("transfer-encoding:")) {
 | 
			
		||||
							String encoding = header.substr(header.find(":") + 1, header.length()).strip_edges();
 | 
			
		||||
							String encoding = header.substr(header.find_char(':') + 1, header.length()).strip_edges();
 | 
			
		||||
							if (encoding == "chunked") {
 | 
			
		||||
								chunked = true;
 | 
			
		||||
							}
 | 
			
		||||
| 
						 | 
				
			
			@ -662,15 +662,16 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() {
 | 
			
		|||
				chunk_left -= rec;
 | 
			
		||||
 | 
			
		||||
				if (chunk_left == 0) {
 | 
			
		||||
					if (chunk[chunk.size() - 2] != '\r' || chunk[chunk.size() - 1] != '\n') {
 | 
			
		||||
					const int chunk_size = chunk.size();
 | 
			
		||||
					if (chunk[chunk_size - 2] != '\r' || chunk[chunk_size - 1] != '\n') {
 | 
			
		||||
						ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)");
 | 
			
		||||
						status = STATUS_CONNECTION_ERROR;
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					ret.resize(chunk.size() - 2);
 | 
			
		||||
					ret.resize(chunk_size - 2);
 | 
			
		||||
					uint8_t *w = ret.ptrw();
 | 
			
		||||
					memcpy(w, chunk.ptr(), chunk.size() - 2);
 | 
			
		||||
					memcpy(w, chunk.ptr(), chunk_size - 2);
 | 
			
		||||
					chunk.clear();
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -792,6 +793,6 @@ HTTPClientTCP::HTTPClientTCP() {
 | 
			
		|||
	request_buffer.instantiate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HTTPClient *(*HTTPClient::_create)() = HTTPClientTCP::_create_func;
 | 
			
		||||
HTTPClient *(*HTTPClient::_create)(bool p_notify_postinitialize) = HTTPClientTCP::_create_func;
 | 
			
		||||
 | 
			
		||||
#endif // WEB_ENABLED
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -76,7 +76,7 @@ private:
 | 
			
		|||
	Error _get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static HTTPClient *_create_func();
 | 
			
		||||
	static HTTPClient *_create_func(bool p_notify_postinitialize);
 | 
			
		||||
 | 
			
		||||
	Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) override;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -33,7 +33,6 @@
 | 
			
		|||
 | 
			
		||||
#include "core/io/resource.h"
 | 
			
		||||
#include "core/math/color.h"
 | 
			
		||||
#include "core/math/rect2.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Image storage class. This is used to store an image in user memory, as well as
 | 
			
		||||
| 
						 | 
				
			
			@ -43,12 +42,17 @@
 | 
			
		|||
 | 
			
		||||
class Image;
 | 
			
		||||
 | 
			
		||||
// Function pointer prototypes.
 | 
			
		||||
 | 
			
		||||
typedef Error (*SavePNGFunc)(const String &p_path, const Ref<Image> &p_img);
 | 
			
		||||
typedef Vector<uint8_t> (*SavePNGBufferFunc)(const Ref<Image> &p_img);
 | 
			
		||||
 | 
			
		||||
typedef Error (*SaveJPGFunc)(const String &p_path, const Ref<Image> &p_img, float p_quality);
 | 
			
		||||
typedef Vector<uint8_t> (*SaveJPGBufferFunc)(const Ref<Image> &p_img, float p_quality);
 | 
			
		||||
 | 
			
		||||
typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size);
 | 
			
		||||
typedef Ref<Image> (*ScalableImageMemLoadFunc)(const uint8_t *p_data, int p_size, float p_scale);
 | 
			
		||||
 | 
			
		||||
typedef Error (*SaveWebPFunc)(const String &p_path, const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
 | 
			
		||||
typedef Vector<uint8_t> (*SaveWebPBufferFunc)(const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -59,57 +63,48 @@ class Image : public Resource {
 | 
			
		|||
	GDCLASS(Image, Resource);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static SavePNGFunc save_png_func;
 | 
			
		||||
	static SaveJPGFunc save_jpg_func;
 | 
			
		||||
	static SaveEXRFunc save_exr_func;
 | 
			
		||||
	static SavePNGBufferFunc save_png_buffer_func;
 | 
			
		||||
	static SaveEXRBufferFunc save_exr_buffer_func;
 | 
			
		||||
	static SaveJPGBufferFunc save_jpg_buffer_func;
 | 
			
		||||
	static SaveWebPFunc save_webp_func;
 | 
			
		||||
	static SaveWebPBufferFunc save_webp_buffer_func;
 | 
			
		||||
 | 
			
		||||
	enum {
 | 
			
		||||
		MAX_WIDTH = (1 << 24), // force a limit somehow
 | 
			
		||||
		MAX_HEIGHT = (1 << 24), // force a limit somehow
 | 
			
		||||
		MAX_PIXELS = 268435456
 | 
			
		||||
		MAX_WIDTH = (1 << 24), // Force a limit somehow.
 | 
			
		||||
		MAX_HEIGHT = (1 << 24), // Force a limit somehow.
 | 
			
		||||
		MAX_PIXELS = 268435456 // 16384 ^ 2
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum Format {
 | 
			
		||||
		FORMAT_L8, //luminance
 | 
			
		||||
		FORMAT_LA8, //luminance-alpha
 | 
			
		||||
	enum Format : int32_t {
 | 
			
		||||
		FORMAT_L8, // Luminance
 | 
			
		||||
		FORMAT_LA8, // Luminance-Alpha
 | 
			
		||||
		FORMAT_R8,
 | 
			
		||||
		FORMAT_RG8,
 | 
			
		||||
		FORMAT_RGB8,
 | 
			
		||||
		FORMAT_RGBA8,
 | 
			
		||||
		FORMAT_RGBA4444,
 | 
			
		||||
		FORMAT_RGB565,
 | 
			
		||||
		FORMAT_RF, //float
 | 
			
		||||
		FORMAT_RF, // Float
 | 
			
		||||
		FORMAT_RGF,
 | 
			
		||||
		FORMAT_RGBF,
 | 
			
		||||
		FORMAT_RGBAF,
 | 
			
		||||
		FORMAT_RH, //half float
 | 
			
		||||
		FORMAT_RH, // Half
 | 
			
		||||
		FORMAT_RGH,
 | 
			
		||||
		FORMAT_RGBH,
 | 
			
		||||
		FORMAT_RGBAH,
 | 
			
		||||
		FORMAT_RGBE9995,
 | 
			
		||||
		FORMAT_DXT1, //s3tc bc1
 | 
			
		||||
		FORMAT_DXT3, //bc2
 | 
			
		||||
		FORMAT_DXT5, //bc3
 | 
			
		||||
		FORMAT_RGTC_R,
 | 
			
		||||
		FORMAT_RGTC_RG,
 | 
			
		||||
		FORMAT_BPTC_RGBA, //btpc bc7
 | 
			
		||||
		FORMAT_BPTC_RGBF, //float bc6h
 | 
			
		||||
		FORMAT_BPTC_RGBFU, //unsigned float bc6hu
 | 
			
		||||
		FORMAT_ETC, //etc1
 | 
			
		||||
		FORMAT_ETC2_R11, //etc2
 | 
			
		||||
		FORMAT_ETC2_R11S, //signed, NOT srgb.
 | 
			
		||||
		FORMAT_DXT1, // BC1
 | 
			
		||||
		FORMAT_DXT3, // BC2
 | 
			
		||||
		FORMAT_DXT5, // BC3
 | 
			
		||||
		FORMAT_RGTC_R, // BC4
 | 
			
		||||
		FORMAT_RGTC_RG, // BC5
 | 
			
		||||
		FORMAT_BPTC_RGBA, // BC7
 | 
			
		||||
		FORMAT_BPTC_RGBF, // BC6 Signed
 | 
			
		||||
		FORMAT_BPTC_RGBFU, // BC6 Unsigned
 | 
			
		||||
		FORMAT_ETC, // ETC1
 | 
			
		||||
		FORMAT_ETC2_R11,
 | 
			
		||||
		FORMAT_ETC2_R11S, // Signed, NOT srgb.
 | 
			
		||||
		FORMAT_ETC2_RG11,
 | 
			
		||||
		FORMAT_ETC2_RG11S,
 | 
			
		||||
		FORMAT_ETC2_RG11S, // Signed, NOT srgb.
 | 
			
		||||
		FORMAT_ETC2_RGB8,
 | 
			
		||||
		FORMAT_ETC2_RGBA8,
 | 
			
		||||
		FORMAT_ETC2_RGB8A1,
 | 
			
		||||
		FORMAT_ETC2_RA_AS_RG, //used to make basis universal happy
 | 
			
		||||
		FORMAT_DXT5_RA_AS_RG, //used to make basis universal happy
 | 
			
		||||
		FORMAT_ETC2_RA_AS_RG, // ETC2 RGBA with a RA-RG swizzle for normal maps.
 | 
			
		||||
		FORMAT_DXT5_RA_AS_RG, // BC3 with a RA-RG swizzle for normal maps.
 | 
			
		||||
		FORMAT_ASTC_4x4,
 | 
			
		||||
		FORMAT_ASTC_4x4_HDR,
 | 
			
		||||
		FORMAT_ASTC_8x8,
 | 
			
		||||
| 
						 | 
				
			
			@ -118,17 +113,18 @@ public:
 | 
			
		|||
	};
 | 
			
		||||
 | 
			
		||||
	static const char *format_names[FORMAT_MAX];
 | 
			
		||||
 | 
			
		||||
	enum Interpolation {
 | 
			
		||||
		INTERPOLATE_NEAREST,
 | 
			
		||||
		INTERPOLATE_BILINEAR,
 | 
			
		||||
		INTERPOLATE_CUBIC,
 | 
			
		||||
		INTERPOLATE_TRILINEAR,
 | 
			
		||||
		INTERPOLATE_LANCZOS,
 | 
			
		||||
		/* INTERPOLATE_TRICUBIC, */
 | 
			
		||||
		/* INTERPOLATE GAUSS */
 | 
			
		||||
		// INTERPOLATE_TRICUBIC,
 | 
			
		||||
		// INTERPOLATE_GAUSS
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	//this is used for compression
 | 
			
		||||
	// Used for obtaining optimal compression quality.
 | 
			
		||||
	enum UsedChannels {
 | 
			
		||||
		USED_CHANNELS_L,
 | 
			
		||||
		USED_CHANNELS_LA,
 | 
			
		||||
| 
						 | 
				
			
			@ -137,13 +133,66 @@ public:
 | 
			
		|||
		USED_CHANNELS_RGB,
 | 
			
		||||
		USED_CHANNELS_RGBA,
 | 
			
		||||
	};
 | 
			
		||||
	//some functions provided by something else
 | 
			
		||||
 | 
			
		||||
	// ASTC supports block formats other than 4x4.
 | 
			
		||||
	enum ASTCFormat {
 | 
			
		||||
		ASTC_FORMAT_4x4,
 | 
			
		||||
		ASTC_FORMAT_8x8,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum RoughnessChannel {
 | 
			
		||||
		ROUGHNESS_CHANNEL_R,
 | 
			
		||||
		ROUGHNESS_CHANNEL_G,
 | 
			
		||||
		ROUGHNESS_CHANNEL_B,
 | 
			
		||||
		ROUGHNESS_CHANNEL_A,
 | 
			
		||||
		ROUGHNESS_CHANNEL_L,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum Image3DValidateError {
 | 
			
		||||
		VALIDATE_3D_OK,
 | 
			
		||||
		VALIDATE_3D_ERR_IMAGE_EMPTY,
 | 
			
		||||
		VALIDATE_3D_ERR_MISSING_IMAGES,
 | 
			
		||||
		VALIDATE_3D_ERR_EXTRA_IMAGES,
 | 
			
		||||
		VALIDATE_3D_ERR_IMAGE_SIZE_MISMATCH,
 | 
			
		||||
		VALIDATE_3D_ERR_IMAGE_FORMAT_MISMATCH,
 | 
			
		||||
		VALIDATE_3D_ERR_IMAGE_HAS_MIPMAPS,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum CompressMode {
 | 
			
		||||
		COMPRESS_S3TC,
 | 
			
		||||
		COMPRESS_ETC,
 | 
			
		||||
		COMPRESS_ETC2,
 | 
			
		||||
		COMPRESS_BPTC,
 | 
			
		||||
		COMPRESS_ASTC,
 | 
			
		||||
		COMPRESS_MAX,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum CompressSource {
 | 
			
		||||
		COMPRESS_SOURCE_GENERIC,
 | 
			
		||||
		COMPRESS_SOURCE_SRGB,
 | 
			
		||||
		COMPRESS_SOURCE_NORMAL,
 | 
			
		||||
		COMPRESS_SOURCE_MAX,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum AlphaMode {
 | 
			
		||||
		ALPHA_NONE,
 | 
			
		||||
		ALPHA_BIT,
 | 
			
		||||
		ALPHA_BLEND
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// External saver function pointers.
 | 
			
		||||
 | 
			
		||||
	static SavePNGFunc save_png_func;
 | 
			
		||||
	static SaveJPGFunc save_jpg_func;
 | 
			
		||||
	static SaveEXRFunc save_exr_func;
 | 
			
		||||
	static SaveWebPFunc save_webp_func;
 | 
			
		||||
	static SavePNGBufferFunc save_png_buffer_func;
 | 
			
		||||
	static SaveEXRBufferFunc save_exr_buffer_func;
 | 
			
		||||
	static SaveJPGBufferFunc save_jpg_buffer_func;
 | 
			
		||||
	static SaveWebPBufferFunc save_webp_buffer_func;
 | 
			
		||||
 | 
			
		||||
	// External loader function pointers.
 | 
			
		||||
 | 
			
		||||
	static ImageMemLoadFunc _png_mem_loader_func;
 | 
			
		||||
	static ImageMemLoadFunc _png_mem_unpacker_func;
 | 
			
		||||
	static ImageMemLoadFunc _jpg_mem_loader_func;
 | 
			
		||||
| 
						 | 
				
			
			@ -153,30 +202,37 @@ public:
 | 
			
		|||
	static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func;
 | 
			
		||||
	static ImageMemLoadFunc _ktx_mem_loader_func;
 | 
			
		||||
 | 
			
		||||
	// External VRAM compression function pointers.
 | 
			
		||||
 | 
			
		||||
	static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels);
 | 
			
		||||
	static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels);
 | 
			
		||||
	static void (*_image_compress_etc1_func)(Image *);
 | 
			
		||||
	static void (*_image_compress_etc2_func)(Image *, UsedChannels p_channels);
 | 
			
		||||
	static void (*_image_compress_astc_func)(Image *, ASTCFormat p_format);
 | 
			
		||||
 | 
			
		||||
	static Error (*_image_compress_bptc_rd_func)(Image *, UsedChannels p_channels);
 | 
			
		||||
	static Error (*_image_compress_bc_rd_func)(Image *, UsedChannels p_channels);
 | 
			
		||||
 | 
			
		||||
	// External VRAM decompression function pointers.
 | 
			
		||||
 | 
			
		||||
	static void (*_image_decompress_bc)(Image *);
 | 
			
		||||
	static void (*_image_decompress_bptc)(Image *);
 | 
			
		||||
	static void (*_image_decompress_etc1)(Image *);
 | 
			
		||||
	static void (*_image_decompress_etc2)(Image *);
 | 
			
		||||
	static void (*_image_decompress_astc)(Image *);
 | 
			
		||||
 | 
			
		||||
	// External packer function pointers.
 | 
			
		||||
 | 
			
		||||
	static Vector<uint8_t> (*webp_lossy_packer)(const Ref<Image> &p_image, float p_quality);
 | 
			
		||||
	static Vector<uint8_t> (*webp_lossless_packer)(const Ref<Image> &p_image);
 | 
			
		||||
	static Ref<Image> (*webp_unpacker)(const Vector<uint8_t> &p_buffer);
 | 
			
		||||
	static Vector<uint8_t> (*png_packer)(const Ref<Image> &p_image);
 | 
			
		||||
	static Ref<Image> (*png_unpacker)(const Vector<uint8_t> &p_buffer);
 | 
			
		||||
	static Vector<uint8_t> (*basis_universal_packer)(const Ref<Image> &p_image, UsedChannels p_channels);
 | 
			
		||||
 | 
			
		||||
	static Ref<Image> (*webp_unpacker)(const Vector<uint8_t> &p_buffer);
 | 
			
		||||
	static Ref<Image> (*png_unpacker)(const Vector<uint8_t> &p_buffer);
 | 
			
		||||
	static Ref<Image> (*basis_universal_unpacker)(const Vector<uint8_t> &p_buffer);
 | 
			
		||||
	static Ref<Image> (*basis_universal_unpacker_ptr)(const uint8_t *p_data, int p_size);
 | 
			
		||||
 | 
			
		||||
	_FORCE_INLINE_ Color _get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const;
 | 
			
		||||
	_FORCE_INLINE_ void _set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	static void _bind_methods();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -187,15 +243,12 @@ private:
 | 
			
		|||
	int height = 0;
 | 
			
		||||
	bool mipmaps = false;
 | 
			
		||||
 | 
			
		||||
	void _copy_internals_from(const Image &p_image) {
 | 
			
		||||
		format = p_image.format;
 | 
			
		||||
		width = p_image.width;
 | 
			
		||||
		height = p_image.height;
 | 
			
		||||
		mipmaps = p_image.mipmaps;
 | 
			
		||||
		data = p_image.data;
 | 
			
		||||
	}
 | 
			
		||||
	void _copy_internals_from(const Image &p_image);
 | 
			
		||||
 | 
			
		||||
	_FORCE_INLINE_ void _get_mipmap_offset_and_size(int p_mipmap, int64_t &r_offset, int &r_width, int &r_height) const; //get where the mipmap begins in data
 | 
			
		||||
	_FORCE_INLINE_ Color _get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const;
 | 
			
		||||
	_FORCE_INLINE_ void _set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color);
 | 
			
		||||
 | 
			
		||||
	_FORCE_INLINE_ void _get_mipmap_offset_and_size(int p_mipmap, int64_t &r_offset, int &r_width, int &r_height) const; // Get where the mipmap begins in data.
 | 
			
		||||
 | 
			
		||||
	static int64_t _get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps = -1, int *r_mm_width = nullptr, int *r_mm_height = nullptr);
 | 
			
		||||
	bool _can_modify(Format p_format) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -212,6 +265,8 @@ private:
 | 
			
		|||
 | 
			
		||||
	Error _load_from_buffer(const Vector<uint8_t> &p_array, ImageMemLoadFunc p_loader);
 | 
			
		||||
 | 
			
		||||
	_FORCE_INLINE_ void _generate_mipmap_from_format(Image::Format p_format, const uint8_t *p_src, uint8_t *p_dst, uint32_t p_width, uint32_t p_height, bool p_renormalize = false);
 | 
			
		||||
 | 
			
		||||
	static void average_4_uint8(uint8_t &p_out, const uint8_t &p_a, const uint8_t &p_b, const uint8_t &p_c, const uint8_t &p_d);
 | 
			
		||||
	static void average_4_float(float &p_out, const float &p_a, const float &p_b, const float &p_c, const float &p_d);
 | 
			
		||||
	static void average_4_half(uint16_t &p_out, const uint16_t &p_a, const uint16_t &p_b, const uint16_t &p_c, const uint16_t &p_d);
 | 
			
		||||
| 
						 | 
				
			
			@ -222,52 +277,32 @@ private:
 | 
			
		|||
	static void renormalize_rgbe9995(uint32_t *p_rgb);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	int get_width() const; ///< Get image width
 | 
			
		||||
	int get_height() const; ///< Get image height
 | 
			
		||||
	int get_width() const;
 | 
			
		||||
	int get_height() const;
 | 
			
		||||
	Size2i get_size() const;
 | 
			
		||||
	bool has_mipmaps() const;
 | 
			
		||||
	int get_mipmap_count() const;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Convert the image to another format, conversion only to raw byte format
 | 
			
		||||
	 */
 | 
			
		||||
	// Convert the image to another format, conversion only to raw byte format.
 | 
			
		||||
	void convert(Format p_new_format);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Get the current image format.
 | 
			
		||||
	 */
 | 
			
		||||
	Format get_format() const;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Get where the mipmap begins in data.
 | 
			
		||||
	 */
 | 
			
		||||
	// Get where the mipmap begins in data.
 | 
			
		||||
	int64_t get_mipmap_offset(int p_mipmap) const;
 | 
			
		||||
	void get_mipmap_offset_and_size(int p_mipmap, int64_t &r_ofs, int64_t &r_size) const;
 | 
			
		||||
	void get_mipmap_offset_size_and_dimensions(int p_mipmap, int64_t &r_ofs, int64_t &r_size, int &w, int &h) const;
 | 
			
		||||
 | 
			
		||||
	enum Image3DValidateError {
 | 
			
		||||
		VALIDATE_3D_OK,
 | 
			
		||||
		VALIDATE_3D_ERR_IMAGE_EMPTY,
 | 
			
		||||
		VALIDATE_3D_ERR_MISSING_IMAGES,
 | 
			
		||||
		VALIDATE_3D_ERR_EXTRA_IMAGES,
 | 
			
		||||
		VALIDATE_3D_ERR_IMAGE_SIZE_MISMATCH,
 | 
			
		||||
		VALIDATE_3D_ERR_IMAGE_FORMAT_MISMATCH,
 | 
			
		||||
		VALIDATE_3D_ERR_IMAGE_HAS_MIPMAPS,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	static Image3DValidateError validate_3d_image(Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_images);
 | 
			
		||||
	static String get_3d_image_validation_error_text(Image3DValidateError p_error);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Resize the image, using the preferred interpolation method.
 | 
			
		||||
	 */
 | 
			
		||||
	// Resize the image, using the preferred interpolation method.
 | 
			
		||||
	void resize_to_po2(bool p_square = false, Interpolation p_interpolation = INTERPOLATE_BILINEAR);
 | 
			
		||||
	void resize(int p_width, int p_height, Interpolation p_interpolation = INTERPOLATE_BILINEAR);
 | 
			
		||||
	void shrink_x2();
 | 
			
		||||
	bool is_size_po2() const;
 | 
			
		||||
	/**
 | 
			
		||||
	 * Crop the image to a specific size, if larger, then the image is filled by black
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	// Crop the image to a specific size, if larger, then the image is filled by black.
 | 
			
		||||
	void crop_from_point(int p_x, int p_y, int p_width, int p_height);
 | 
			
		||||
	void crop(int p_width, int p_height);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -277,34 +312,20 @@ public:
 | 
			
		|||
	void flip_x();
 | 
			
		||||
	void flip_y();
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Generate a mipmap to an image (creates an image 1/4 the size, with averaging of 4->1)
 | 
			
		||||
	 */
 | 
			
		||||
	// Generate a mipmap chain of an image (creates an image 1/4 the size, with averaging of 4->1).
 | 
			
		||||
	Error generate_mipmaps(bool p_renormalize = false);
 | 
			
		||||
 | 
			
		||||
	enum RoughnessChannel {
 | 
			
		||||
		ROUGHNESS_CHANNEL_R,
 | 
			
		||||
		ROUGHNESS_CHANNEL_G,
 | 
			
		||||
		ROUGHNESS_CHANNEL_B,
 | 
			
		||||
		ROUGHNESS_CHANNEL_A,
 | 
			
		||||
		ROUGHNESS_CHANNEL_L,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Error generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map);
 | 
			
		||||
 | 
			
		||||
	void clear_mipmaps();
 | 
			
		||||
	void normalize(); //for normal maps
 | 
			
		||||
	void normalize();
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates new internal image data of a given size and format. Current image will be lost.
 | 
			
		||||
	 */
 | 
			
		||||
	// Creates new internal image data of a given size and format. Current image will be lost.
 | 
			
		||||
	void initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format);
 | 
			
		||||
	void initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data);
 | 
			
		||||
	void initialize_data(const char **p_xpm);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * returns true when the image is empty (0,0) in size
 | 
			
		||||
	 */
 | 
			
		||||
	// Returns true when the image is empty (0,0) in size.
 | 
			
		||||
	bool is_empty() const;
 | 
			
		||||
 | 
			
		||||
	Vector<uint8_t> get_data() const;
 | 
			
		||||
| 
						 | 
				
			
			@ -324,27 +345,14 @@ public:
 | 
			
		|||
	static Ref<Image> create_from_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data);
 | 
			
		||||
	void set_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * create an empty image
 | 
			
		||||
	 */
 | 
			
		||||
	Image() {}
 | 
			
		||||
	/**
 | 
			
		||||
	 * create an empty image of a specific size and format
 | 
			
		||||
	 */
 | 
			
		||||
	Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format);
 | 
			
		||||
	/**
 | 
			
		||||
	 * import an image of a specific size and format from a pointer
 | 
			
		||||
	 */
 | 
			
		||||
	Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const Vector<uint8_t> &p_data);
 | 
			
		||||
	Image() = default; // Create an empty image.
 | 
			
		||||
	Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format); // Create an empty image of a specific size and format.
 | 
			
		||||
	Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const Vector<uint8_t> &p_data); // Import an image of a specific size and format from a byte vector.
 | 
			
		||||
	Image(const uint8_t *p_mem_png_jpg, int p_len = -1); // Import either a png or jpg from a pointer.
 | 
			
		||||
	Image(const char **p_xpm); // Import an XPM image.
 | 
			
		||||
 | 
			
		||||
	~Image() {}
 | 
			
		||||
 | 
			
		||||
	enum AlphaMode {
 | 
			
		||||
		ALPHA_NONE,
 | 
			
		||||
		ALPHA_BIT,
 | 
			
		||||
		ALPHA_BLEND
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	AlphaMode detect_alpha() const;
 | 
			
		||||
	bool is_invisible() const;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -359,21 +367,6 @@ public:
 | 
			
		|||
	static int64_t get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap);
 | 
			
		||||
	static int64_t get_image_mipmap_offset_and_dimensions(int p_width, int p_height, Format p_format, int p_mipmap, int &r_w, int &r_h);
 | 
			
		||||
 | 
			
		||||
	enum CompressMode {
 | 
			
		||||
		COMPRESS_S3TC,
 | 
			
		||||
		COMPRESS_ETC,
 | 
			
		||||
		COMPRESS_ETC2,
 | 
			
		||||
		COMPRESS_BPTC,
 | 
			
		||||
		COMPRESS_ASTC,
 | 
			
		||||
		COMPRESS_MAX,
 | 
			
		||||
	};
 | 
			
		||||
	enum CompressSource {
 | 
			
		||||
		COMPRESS_SOURCE_GENERIC,
 | 
			
		||||
		COMPRESS_SOURCE_SRGB,
 | 
			
		||||
		COMPRESS_SOURCE_NORMAL,
 | 
			
		||||
		COMPRESS_SOURCE_MAX,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, ASTCFormat p_astc_format = ASTC_FORMAT_4x4);
 | 
			
		||||
	Error compress_from_channels(CompressMode p_mode, UsedChannels p_channels, ASTCFormat p_astc_format = ASTC_FORMAT_4x4);
 | 
			
		||||
	Error decompress();
 | 
			
		||||
| 
						 | 
				
			
			@ -383,11 +376,14 @@ public:
 | 
			
		|||
	void fix_alpha_edges();
 | 
			
		||||
	void premultiply_alpha();
 | 
			
		||||
	void srgb_to_linear();
 | 
			
		||||
	void linear_to_srgb();
 | 
			
		||||
	void normal_map_to_xy();
 | 
			
		||||
	Ref<Image> rgbe_to_srgb();
 | 
			
		||||
	Ref<Image> get_image_from_mipmap(int p_mipmap) const;
 | 
			
		||||
	void bump_map_to_normal_map(float bump_scale = 1.0);
 | 
			
		||||
 | 
			
		||||
	bool detect_signed(bool p_include_mips = true) const;
 | 
			
		||||
 | 
			
		||||
	void blit_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest);
 | 
			
		||||
	void blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2i &p_src_rect, const Point2i &p_dest);
 | 
			
		||||
	void blend_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest);
 | 
			
		||||
| 
						 | 
				
			
			@ -398,9 +394,8 @@ public:
 | 
			
		|||
	Rect2i get_used_rect() const;
 | 
			
		||||
	Ref<Image> get_region(const Rect2i &p_area) const;
 | 
			
		||||
 | 
			
		||||
	static void set_compress_bc_func(void (*p_compress_func)(Image *, UsedChannels));
 | 
			
		||||
	static void set_compress_bptc_func(void (*p_compress_func)(Image *, UsedChannels));
 | 
			
		||||
	static String get_format_name(Format p_format);
 | 
			
		||||
	static uint32_t get_format_component_mask(Format p_format);
 | 
			
		||||
 | 
			
		||||
	Error load_png_from_buffer(const Vector<uint8_t> &p_array);
 | 
			
		||||
	Error load_jpg_from_buffer(const Vector<uint8_t> &p_array);
 | 
			
		||||
| 
						 | 
				
			
			@ -416,9 +411,6 @@ public:
 | 
			
		|||
	void convert_ra_rgba8_to_rg();
 | 
			
		||||
	void convert_rgba8_to_bgra8();
 | 
			
		||||
 | 
			
		||||
	Image(const uint8_t *p_mem_png_jpg, int p_len = -1);
 | 
			
		||||
	Image(const char **p_xpm);
 | 
			
		||||
 | 
			
		||||
	virtual Ref<Resource> duplicate(bool p_subresources = false) const override;
 | 
			
		||||
 | 
			
		||||
	UsedChannels detect_used_channels(CompressSource p_source = COMPRESS_SOURCE_GENERIC) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -437,14 +429,7 @@ public:
 | 
			
		|||
 | 
			
		||||
	void set_as_black();
 | 
			
		||||
 | 
			
		||||
	void copy_internals_from(const Ref<Image> &p_image) {
 | 
			
		||||
		ERR_FAIL_COND_MSG(p_image.is_null(), "Cannot copy image internals: invalid Image object.");
 | 
			
		||||
		format = p_image->format;
 | 
			
		||||
		width = p_image->width;
 | 
			
		||||
		height = p_image->height;
 | 
			
		||||
		mipmaps = p_image->mipmaps;
 | 
			
		||||
		data = p_image->data;
 | 
			
		||||
	}
 | 
			
		||||
	void copy_internals_from(const Ref<Image> &p_image);
 | 
			
		||||
 | 
			
		||||
	Dictionary compute_image_metrics(const Ref<Image> p_compared_image, bool p_luma_metric = true);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,8 +30,6 @@
 | 
			
		|||
 | 
			
		||||
#include "image_loader.h"
 | 
			
		||||
 | 
			
		||||
#include "core/string/print_string.h"
 | 
			
		||||
 | 
			
		||||
void ImageFormatLoader::_bind_methods() {
 | 
			
		||||
	BIND_BITFIELD_FLAG(FLAG_NONE);
 | 
			
		||||
	BIND_BITFIELD_FLAG(FLAG_FORCE_LINEAR);
 | 
			
		||||
| 
						 | 
				
			
			@ -82,15 +80,16 @@ void ImageFormatLoaderExtension::_bind_methods() {
 | 
			
		|||
 | 
			
		||||
Error ImageLoader::load_image(const String &p_file, Ref<Image> p_image, Ref<FileAccess> p_custom, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
 | 
			
		||||
	ERR_FAIL_COND_V_MSG(p_image.is_null(), ERR_INVALID_PARAMETER, "Can't load an image: invalid Image object.");
 | 
			
		||||
	const String file = ResourceUID::ensure_path(p_file);
 | 
			
		||||
 | 
			
		||||
	Ref<FileAccess> f = p_custom;
 | 
			
		||||
	if (f.is_null()) {
 | 
			
		||||
		Error err;
 | 
			
		||||
		f = FileAccess::open(p_file, FileAccess::READ, &err);
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(f.is_null(), err, "Error opening file '" + p_file + "'.");
 | 
			
		||||
		f = FileAccess::open(file, FileAccess::READ, &err);
 | 
			
		||||
		ERR_FAIL_COND_V_MSG(f.is_null(), err, vformat("Error opening file '%s'.", file));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	String extension = p_file.get_extension();
 | 
			
		||||
	String extension = file.get_extension();
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < loader.size(); i++) {
 | 
			
		||||
		if (!loader[i]->recognize(extension)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +97,7 @@ Error ImageLoader::load_image(const String &p_file, Ref<Image> p_image, Ref<File
 | 
			
		|||
		}
 | 
			
		||||
		Error err = loader.write[i]->load_image(p_image, f, p_flags, p_scale);
 | 
			
		||||
		if (err != OK) {
 | 
			
		||||
			ERR_PRINT("Error loading image: " + p_file);
 | 
			
		||||
			ERR_PRINT(vformat("Error loading image: '%s'.", file));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (err != ERR_FILE_UNRECOGNIZED) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
		Reference in a new issue