├── .gitattributes ├── .gitignore ├── Analysis ├── AIAnalyzer.vb ├── AnalyzerManager.vb ├── ArtAnalyzer.vb ├── CrashAnalyzer.vb ├── CrashItem.vb ├── INIAnalyzeInfo.vb ├── INIAnalyzeResult.vb ├── INIAnalyzer.vb ├── Ra2IniAnalyzer.vb └── RulesAnalyzer.vb ├── Collections └── IndexedList.vb ├── Colorize ├── CodeColorBase.vb ├── CodeColorBlock.vb └── CodeColors.vb ├── Document ├── DocumentCategories.vb ├── DocumentLoadOptions.vb ├── IniBlock.vb ├── IniCommentBlock.vb ├── IniCommentSyntax.vb ├── IniCommentSyntaxTrivia.vb ├── IniControlCharacterSyntaxTrivia.vb ├── IniDocument.vb ├── IniKeyValuePairSyntax.vb ├── IniMainKeySyntax.vb ├── IniNewlineSyntaxTrivia.vb ├── IniRecordBlock.vb ├── IniRecordSyntax.vb ├── IniSyntax.vb ├── IniSyntaxTrivia.vb ├── IniWhitespaceSyntaxTrivia.vb ├── IniWordSyntaxTrivia.vb ├── IniWrongSyntax.vb └── IniWrongSyntaxTrivia.vb ├── Entities ├── EntityInferContext.vb ├── IniFileItem.vb ├── NamedIniAnalyzer.vb └── VBProjectWriter.vb ├── Help ├── AIHelpProvider.vb ├── ArtHelpProvider.vb ├── EmptyHelpProvider.vb ├── HelpDataProvider.vb ├── HelpProvider.vb ├── HelpProviderManager.vb ├── IHelpProvider.vb ├── IniNamespace.vb ├── IniNamespaceFile.vb ├── IniNamespaceItem.vb └── RulesHelpProvider.vb ├── Imaging ├── IRegisterable.vb ├── IniImagingAnalizer.vb ├── IniTreeNode.vb ├── KeyTreeNode.vb ├── MainKeyTreeNode.vb ├── MainKeyTreeNodeWriter.vb ├── RegisterTreeNode.vb ├── SubValueTreeNode.vb ├── Synchronization.vb └── ValueTreeNode.vb ├── Input ├── CodeSnippet.vb ├── ImeBase.vb ├── IniValueMainKeyRenamer.vb ├── QuickFix.vb ├── QuickFixSuggestion.vb └── RenameOptions.vb ├── Linq └── StringExtension.vb ├── My Project └── AssemblyInfo.vb ├── Primitives └── Percentage.vb ├── Ra2CodeAnalysis.vbproj ├── Tools └── SymbolFinder.vb ├── Utility ├── DataGridDisplayNameAttribute.vb ├── DocumentExtensions.vb ├── ExpressionCalculator.vb ├── Parallel.vb ├── ParameterizedProperty.vb ├── ResetableDelayTask.vb ├── TextExtensions.vb ├── ValueConverter.vb └── WritableKeyValuePair.vb └── ViewModels ├── AnalizeSourceViewModel.vb ├── CodeGenerateLogViewModel.vb ├── CodeSnippetGroup.vb ├── CodeSnippetsViewModel.vb ├── DocumentsViewModel.vb ├── FindSymbolResult.vb ├── IniFilesViewModel.vb ├── ProjectGenerateViewModel.vb ├── ProjectSetupViewModel.vb ├── SingleInstance.vb └── WidenIniAnalysisInfo.vb /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.vb diff=visualbasic 7 | 8 | # Standard to msysgit 9 | *.doc diff=astextplain 10 | *.DOC diff=astextplain 11 | *.docx diff=astextplain 12 | *.DOCX diff=astextplain 13 | *.dot diff=astextplain 14 | *.DOT diff=astextplain 15 | *.pdf diff=astextplain 16 | *.PDF diff=astextplain 17 | *.rtf diff=astextplain 18 | *.RTF diff=astextplain 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # Build results 11 | [Dd]ebug/ 12 | [Dd]ebugPublic/ 13 | [Rr]elease/ 14 | [Rr]eleases/ 15 | x64/ 16 | x86/ 17 | build/ 18 | bld/ 19 | [Bb]in/ 20 | [Oo]bj/ 21 | 22 | # Roslyn cache directories 23 | *.ide/ 24 | 25 | # MSTest test Results 26 | [Tt]est[Rr]esult*/ 27 | [Bb]uild[Ll]og.* 28 | 29 | #NUNIT 30 | *.VisualState.xml 31 | TestResult.xml 32 | 33 | # Build Results of an ATL Project 34 | [Dd]ebugPS/ 35 | [Rr]eleasePS/ 36 | dlldata.c 37 | 38 | *_i.c 39 | *_p.c 40 | *_i.h 41 | *.ilk 42 | *.meta 43 | *.obj 44 | *.pch 45 | *.pdb 46 | *.pgc 47 | *.pgd 48 | *.rsp 49 | *.sbr 50 | *.tlb 51 | *.tli 52 | *.tlh 53 | *.tmp 54 | *.tmp_proj 55 | *.log 56 | *.vspscc 57 | *.vssscc 58 | .builds 59 | *.pidb 60 | *.svclog 61 | *.scc 62 | 63 | # Chutzpah Test files 64 | _Chutzpah* 65 | 66 | # Visual C++ cache files 67 | ipch/ 68 | *.aps 69 | *.ncb 70 | *.opensdf 71 | *.sdf 72 | *.cachefile 73 | 74 | # Visual Studio profiler 75 | *.psess 76 | *.vsp 77 | *.vspx 78 | 79 | # TFS 2012 Local Workspace 80 | $tf/ 81 | 82 | # Guidance Automation Toolkit 83 | *.gpState 84 | 85 | # ReSharper is a .NET coding add-in 86 | _ReSharper*/ 87 | *.[Rr]e[Ss]harper 88 | *.DotSettings.user 89 | 90 | # JustCode is a .NET coding addin-in 91 | .JustCode 92 | 93 | # TeamCity is a build add-in 94 | _TeamCity* 95 | 96 | # DotCover is a Code Coverage Tool 97 | *.dotCover 98 | 99 | # NCrunch 100 | _NCrunch_* 101 | .*crunch*.local.xml 102 | 103 | # MightyMoose 104 | *.mm.* 105 | AutoTest.Net/ 106 | 107 | # Web workbench (sass) 108 | .sass-cache/ 109 | 110 | # Installshield output folder 111 | [Ee]xpress/ 112 | 113 | # DocProject is a documentation generator add-in 114 | DocProject/buildhelp/ 115 | DocProject/Help/*.HxT 116 | DocProject/Help/*.HxC 117 | DocProject/Help/*.hhc 118 | DocProject/Help/*.hhk 119 | DocProject/Help/*.hhp 120 | DocProject/Help/Html2 121 | DocProject/Help/html 122 | 123 | # Click-Once directory 124 | publish/ 125 | 126 | # Publish Web Output 127 | *.[Pp]ublish.xml 128 | *.azurePubxml 129 | # TODO: Comment the next line if you want to checkin your web deploy settings 130 | # but database connection strings (with potential passwords) will be unencrypted 131 | *.pubxml 132 | *.publishproj 133 | 134 | # NuGet Packages 135 | *.nupkg 136 | # The packages folder can be ignored because of Package Restore 137 | **/packages/* 138 | # except build/, which is used as an MSBuild target. 139 | !**/packages/build/ 140 | # If using the old MSBuild-Integrated Package Restore, uncomment this: 141 | #!**/packages/repositories.config 142 | 143 | # Windows Azure Build Output 144 | csx/ 145 | *.build.csdef 146 | 147 | # Windows Store app package directory 148 | AppPackages/ 149 | 150 | # Others 151 | sql/ 152 | *.Cache 153 | ClientBin/ 154 | [Ss]tyle[Cc]op.* 155 | ~$* 156 | *~ 157 | *.dbmdl 158 | *.dbproj.schemaview 159 | *.pfx 160 | *.publishsettings 161 | node_modules/ 162 | 163 | # RIA/Silverlight projects 164 | Generated_Code/ 165 | 166 | # Backup & report files from converting an old project file 167 | # to a newer Visual Studio version. Backup files are not needed, 168 | # because we have git ;-) 169 | _UpgradeReport_Files/ 170 | Backup*/ 171 | UpgradeLog*.XML 172 | UpgradeLog*.htm 173 | 174 | # SQL Server files 175 | *.mdf 176 | *.ldf 177 | 178 | # Business Intelligence projects 179 | *.rdl.data 180 | *.bim.layout 181 | *.bim_*.settings 182 | 183 | # Microsoft Fakes 184 | FakesAssemblies/ 185 | 186 | # ========================= 187 | # Operating System Files 188 | # ========================= 189 | 190 | # OSX 191 | # ========================= 192 | 193 | .DS_Store 194 | .AppleDouble 195 | .LSOverride 196 | 197 | # Thumbnails 198 | ._* 199 | 200 | # Files that might appear on external disk 201 | .Spotlight-V100 202 | .Trashes 203 | 204 | # Directories potentially created on remote AFP share 205 | .AppleDB 206 | .AppleDesktop 207 | Network Trash Folder 208 | Temporary Items 209 | .apdisk 210 | 211 | # Windows 212 | # ========================= 213 | 214 | # Windows image file caches 215 | Thumbs.db 216 | ehthumbs.db 217 | 218 | # Folder config file 219 | Desktop.ini 220 | 221 | # Recycle Bin used on file shares 222 | $RECYCLE.BIN/ 223 | 224 | # Windows Installer files 225 | *.cab 226 | *.msi 227 | *.msm 228 | *.msp 229 | 230 | # Windows shortcuts 231 | *.lnk 232 | -------------------------------------------------------------------------------- /Analysis/AIAnalyzer.vb: -------------------------------------------------------------------------------- 1 | Imports Nukepayload2.Ra2CodeAnalysis.AnalysisHelper 2 | Public Class AIAnalyzer 3 | Inherits Ra2IniAnalyzer 4 | Dim rules As RulesAnalyzer 5 | Sub New(INIText As StreamReader, rules As RulesAnalyzer) 6 | MyBase.New(INIText) 7 | Me.rules = rules 8 | End Sub 9 | Sub New(INIText As String, rules As RulesAnalyzer) 10 | MyBase.New(INIText) 11 | Me.rules = rules 12 | End Sub 13 | 14 | Public Overrides ReadOnly Property Name As String = "AI" 15 | 16 | Public Overrides Function Check() As INIAnalyzeResult 17 | Dim AdvResult As New INIAnalyzeResult 18 | SyncLock Result 19 | AdvResult = AdvResult.Concat(Result) 20 | End SyncLock 21 | Try 22 | Dim RulesUnitsCache As New List(Of String) 23 | RulesUnitsCache.AddRange(From i In rules.Values("InfantryTypes") Select i.Value.Item1) 24 | RulesUnitsCache.AddRange(From v In rules.Values("VehicleTypes") Select v.Value.Item1) 25 | RulesUnitsCache.AddRange(From a In rules.Values("AircraftTypes") Select a.Value.Item1) 26 | For Each Record In Values 27 | If Record.Key = "AITriggerTypes" Then 28 | For Each r In Record.Value 29 | Dim vals = r.Value.Item1.Split(","c) 30 | If vals.Count <> 18 Then 31 | AdvResult.Fault.Add(New INIAnalyzeInfo(r.Value.Item2, "AI触发参数数量错误,可导致运行时AccessViolation异常", r.Value.Item1, "AITriggerTypes")) 32 | Else 33 | ValueRegistryCheck("TeamTypes", "AI触发使用了未注册的作战小队1,可导致运行时AccessViolation异常", "AITriggerTypes", vals(1), r.Value.Item2, AdvResult.Fault, Me) 34 | If Not vals(2).Trim.StartsWith("<") Then 35 | ValueRegistryCheck("Countries", "AI触发使用了未注册的国家,可导致运行时AccessViolation异常", "AITriggerTypes", vals(2), r.Value.Item2, AdvResult.Fault, rules) 36 | End If 37 | TypeCheck(vals, {3, 4, 10, 11, 12, 13, 15, 16, 17}, Function(s) s.Trim.IsUInteger, "AI触发参数第{0}个的类型错误,应为正整数,可导致AI行为异常", AdvResult.Message, r.Value.Item2, "AITriggerTypes") 38 | If Not vals(5).Trim.StartsWith("<") Then 39 | If Not SpecialBuildings.Contains(vals(5)) AndAlso Not RulesUnitsCache.Contains(vals(5)) Then 40 | ValueRegistryCheck("BuildingTypes", "AI触发使用了未注册的建筑/单位,可导致运行时AccessViolation异常", "AITriggerTypes", vals(5), r.Value.Item2, AdvResult.Fault, rules) 41 | End If 42 | End If 43 | TypeCheck(vals, {6}, Function(s) s.IsUInteger AndAlso s.Length = 64, "AI触发参数第{0}个的类型错误, 应为长度为64的数字, 可导致运行时AccessViolation异常", AdvResult.Fault, r.Value.Item2, "AITriggerTypes") 44 | TypeCheck(vals, {7, 8, 9}, Function(s) s.Trim.IsUFraction, "AI触发参数第{0}个的类型错误, 应为展开写的正小数, 可导致AI行为异常", AdvResult.Message, r.Value.Item2, "AITriggerTypes") 45 | If Not vals(14).Trim.StartsWith("<") Then 46 | ValueRegistryCheck("TeamTypes", "AI触发使用了未注册的作战小队2, 可导致运行时AccessViolation异常", "AITriggerTypes", vals(14), r.Value.Item2, AdvResult.Fault, Me) 47 | End If 48 | End If 49 | Next 50 | ElseIf Record.Key = "TaskForces" 51 | For Each r In Record.Value 52 | If Values.ContainsKey(r.Value.Item1) Then 53 | For Each vs In Values(r.Value.Item1) 54 | If vs.Key.IsUInteger Then 55 | Try 56 | Dim tmp = CUInt(vs.Key) 57 | Catch ex As OverflowException 58 | AdvResult.Fault.Add(New INIAnalyzeInfo(r.Value.Item2, "特遣小队成员ID严重超出规定范围, 可能导致AI行为混乱或崩溃", r.Value.Item1, Record.Key)) 59 | Continue For 60 | End Try 61 | If CUInt(vs.Key) > 6 Then 62 | AdvResult.Warning.Add(New INIAnalyzeInfo(r.Value.Item2, "特遣小队成员ID大于6, 此成员会被忽略", r.Value.Item1, Record.Key)) 63 | Else 64 | Dim team = vs.Value.Item1.Split(","c) 65 | If team.Count <> 2 Then 66 | AdvResult.Fault.Add(New INIAnalyzeInfo(r.Value.Item2, "特遣小队成员格式无效, 应为数量, 单位, 可能导致运行时AccessViolation异常", r.Value.Item1, Record.Key)) 67 | ElseIf team(0).IsUInteger Then 68 | If Not RulesUnitsCache.Contains(team(1)) Then 69 | AdvResult.Fault.Add(New INIAnalyzeInfo(r.Value.Item2, "特遣小队成员单位未注册, 可能导致运行时AccessViolation异常", r.Value.Item1, Record.Key)) 70 | End If 71 | Else 72 | AdvResult.Fault.Add(New INIAnalyzeInfo(r.Value.Item2, "特遣小队成员数量格式无效, 应为正整数, 可能导致运行时AccessViolation异常", r.Value.Item1, Record.Key)) 73 | End If 74 | End If 75 | End If 76 | Next 77 | Else 78 | AdvResult.Warning.Add(New INIAnalyzeInfo(r.Value.Item2, "特遣注册了, 但是没有定义, 此注册会被忽略", r.Value.Item1, Record.Key)) 79 | End If 80 | Next 81 | ElseIf Record.Key = "ScriptTypes" 82 | For Each r In Record.Value 83 | If Values.ContainsKey(r.Value.Item1) Then 84 | Dim Has490 As Boolean = False 85 | For Each vs In Values(r.Value.Item1) 86 | If vs.Key.IsUInteger Then 87 | Dim s = vs.Value.Item1.Split(","c) 88 | If s.Count = 2 Then 89 | If s(0).Trim = "49" AndAlso s(1).Trim = "0" Then 90 | Has490 = True 91 | ElseIf s(0).Trim = "16" 92 | AdvResult.Warning.Add(New INIAnalyzeInfo(r.Value.Item2, "AI.ini的脚本中使用指令16(巡逻路径点),可能导致AI行为异常", r.Value.Item1, "ScriptTypes")) 93 | End If 94 | Else 95 | AdvResult.Fault.Add(New INIAnalyzeInfo(r.Value.Item2, "脚本内容的两个值格式错误,可能导致运行时AccessViolation异常", r.Value.Item1, "ScriptTypes")) 96 | End If 97 | End If 98 | Next 99 | If Not Has490 Then 100 | AdvResult.Warning.Add(New INIAnalyzeInfo(r.Value.Item2, "脚本没有49,0,可能导致AI行为异常", r.Value.Item1, "ScriptTypes")) 101 | End If 102 | Else 103 | AdvResult.Warning.Add(New INIAnalyzeInfo(r.Value.Item2, "脚本注册了,但是没有定义,此注册会被忽略", r.Value.Item1, "ScriptTypes")) 104 | End If 105 | Next 106 | Else 107 | For Each r In Record.Value 108 | Select Case r.Key 109 | Case "TaskForce" 110 | ValueRegistryCheck("TaskForces", "特遣小队未注册", Record.Key, r, AdvResult.Fault) 111 | Case "Script" 112 | MainKeyRegistryCheck("TeamTypes", "作战小队未注册", Record.Key, r, AdvResult.Fault) 113 | ValueRegistryCheck("ScriptTypes", "脚本未注册", Record.Key, r, AdvResult.Fault) 114 | End Select 115 | Next 116 | End If 117 | Next 118 | Catch ex As KeyNotFoundException 119 | AdvResult.Fault.Add(New INIAnalyzeInfo(0, "关键的主键或键未找到,可能在运行时发生AccessViolation异常", "(未收集)", "(未收集)")) 120 | End Try 121 | 122 | Return AdvResult 123 | End Function 124 | End Class 125 | -------------------------------------------------------------------------------- /Analysis/AnalyzerManager.vb: -------------------------------------------------------------------------------- 1 | Imports System.Threading 2 | 3 | Public Class AnalyzerManager 4 | WithEvents DataSource As AnalizeSourceViewModel 5 | Public ReadOnly Property Rules As RulesAnalyzer 6 | Public ReadOnly Property Art As ArtAnalyzer 7 | Public ReadOnly Property AI As AIAnalyzer 8 | Public ReadOnly Property Ra2 As INIAnalyzer 9 | Public ReadOnly Property Updating As Boolean 10 | Dim _UpdateDelay As New TimeSpan(0, 0, 1) 11 | Public Property UpdateDelay As TimeSpan 12 | Get 13 | Return _UpdateDelay 14 | End Get 15 | Set(value As TimeSpan) 16 | _UpdateDelay = value 17 | DelayTimer.Change(1, DelayTime) 18 | DelayTimer.Change(-1, DelayTime) 19 | End Set 20 | End Property 21 | Private ReadOnly Property DelayTime As Integer 22 | Get 23 | Return CInt(_UpdateDelay.TotalMilliseconds) 24 | End Get 25 | End Property 26 | Dim Update As TimerCallback = Async Sub(state) Await UpdateNowAsync() 27 | Dim DelayTimer As New Timer(Update, Nothing, Timeout.Infinite, DelayTime) 28 | Public Sub DelayedUpdateAnalizeDataRequest() 29 | DelayTimer.Change(DelayTime, DelayTime) 30 | End Sub 31 | ''' 32 | ''' 强制检查分析结果的更新。如果正在更新则会不进行更新并返回False。 33 | ''' 34 | ''' 是否成功更新(没遇到正在更新的情况) 35 | Public Async Function UpdateNowAsync() As Task(Of Boolean) 36 | If _Updating Then 37 | Return False 38 | Else 39 | _Updating = True 40 | Await Task.WhenAll(EnsureRulesUpdatedAsync(), EnsureArtUpdatedAsync(), EnsureAIUpdatedAsync(), EnsureRa2UpdatedAsync()) 41 | UpdateDelay = UpdateDelay 42 | _Updating = False 43 | Return True 44 | End If 45 | End Function 46 | Sub New(DataSource As AnalizeSourceViewModel) 47 | With DataSource 48 | Rules = New RulesAnalyzer(.RulesText) 49 | Art = New ArtAnalyzer(.ArtText, Rules) 50 | AI = New AIAnalyzer(.AIText, Rules) 51 | Ra2 = New INIAnalyzer(.Ra2Text) 52 | End With 53 | Me.DataSource = DataSource 54 | End Sub 55 | Public Async Function EnsureRulesUpdatedAsync() As Task 56 | If DataSource.IsRulesInvalid Then 57 | Await Rules.ReloadAsync(DataSource.RulesText) 58 | DataSource.IsRulesInvalid = False 59 | End If 60 | End Function 61 | Public Async Function EnsureArtUpdatedAsync() As Task 62 | If DataSource.IsArtInvalid Then 63 | Await Art.ReloadAsync(DataSource.ArtText) 64 | DataSource.IsArtInvalid = False 65 | End If 66 | End Function 67 | Public Async Function EnsureAIUpdatedAsync() As Task 68 | If DataSource.IsAIInvalid Then 69 | Await AI.ReloadAsync(DataSource.AIText) 70 | DataSource.IsAIInvalid = False 71 | End If 72 | End Function 73 | Public Async Function EnsureRa2UpdatedAsync() As Task 74 | If DataSource.IsRa2Invalid Then 75 | Await Ra2.ReloadAsync(DataSource.Ra2Text) 76 | DataSource.IsRa2Invalid = False 77 | End If 78 | End Function 79 | 80 | Private Sub DataSource_DataChanged() Handles DataSource.DataChanged 81 | DelayedUpdateAnalizeDataRequest() 82 | End Sub 83 | End Class -------------------------------------------------------------------------------- /Analysis/ArtAnalyzer.vb: -------------------------------------------------------------------------------- 1 | Imports Nukepayload2.Ra2CodeAnalysis.AnalysisHelper 2 | 3 | Public Class ArtAnalyzer 4 | Inherits Ra2IniAnalyzer 5 | Public Overrides ReadOnly Property Name As String = "Art" 6 | Dim rules As RulesAnalyzer 7 | Sub New(INIText As StreamReader, rules As RulesAnalyzer) 8 | MyBase.New(INIText) 9 | Me.rules = rules 10 | End Sub 11 | Sub New(INIText As String, rules As RulesAnalyzer) 12 | MyBase.New(INIText) 13 | Me.rules = rules 14 | End Sub 15 | Public Overrides Function Check() As INIAnalyzeResult 16 | Dim AdvResult As New INIAnalyzeResult 17 | SyncLock Result 18 | AdvResult = AdvResult.Concat(Result) 19 | End SyncLock 20 | Try 21 | 22 | For Each Record In Values 23 | For Each r In Record.Value 24 | Select Case r.Key 25 | Case "Sequence" 26 | If Not Values.ContainsKey(r.Value.Item1) Then 27 | AdvResult.Fault.Add(New INIAnalyzeInfo(r.Value.Item2, "使用了不存在的动作序列,可导致运行时AccessViolation异常。", r.Value.Item1, Record.Key)) 28 | End If 29 | Case "Trailer", "Next" 30 | ValueRegistryCheck("Animations", "使用了未注册的动画,动画不会生效。", Record.Key, r.Value.Item1, r.Value.Item2, AdvResult.Warning, rules, "VoxelAnims") 31 | Case "ExpireAnim", "Spawns" 32 | EachValueRegistryCheck(r, AdvResult.Warning, r.Key, "使用了未注册的动画,动画不会生效。", "Animations", rules, "VoxelAnims") 33 | Case "ToOverlay" 34 | ValueRegistryCheck("OverlayTypes", "使用了未注册的覆盖物,可导致运行时AccessViolation异常。", Record.Key, r.Value.Item1, r.Value.Item2, AdvResult.Fault, rules) 35 | Case "TiberiumSpawnType" 36 | ValueRegistryCheck("OverlayTypes", "使用了未注册的覆盖物,可导致运行时AccessViolation异常。", Record.Key, r.Value.Item1, r.Value.Item2, AdvResult.Fault, rules) 37 | If r.Value.Item1.Length <= 2 OrElse Not r.Value.Item1.Substring(r.Value.Item1.Length - 2).IsUInteger Then 38 | AdvResult.Fault.Add(New INIAnalyzeInfo(r.Value.Item2, "产生的覆盖物名的结尾不是两位数字,可导致运行时AccessViolation异常。", r.Value.Item1, Record.Key)) 39 | End If 40 | Case "Warhead" 41 | If Not rules.Values.Keys.Contains(r.Value.Item1) Then 42 | AdvResult.Fault.Add(New INIAnalyzeInfo(r.Value.Item2, "使用了不存在的弹头,可导致运行时AccessViolation异常。", r.Value.Item1, Record.Key)) 43 | End If 44 | Case "SpawnsParticle" 45 | ValueRegistryCheck("Particles", "使用了未注册的粒子动画,可导致运行时AccessViolation异常。", Record.Key, r.Value.Item1, r.Value.Item2, AdvResult.Fault, rules) 46 | End Select 47 | Next 48 | Next 49 | Catch ex As KeyNotFoundException 50 | AdvResult.Fault.Add(New INIAnalyzeInfo(0, "关键的主键或键未找到,可能在运行时发生AccessViolation异常", "(未收集)", "(未收集)")) 51 | End Try 52 | Return AdvResult 53 | End Function 54 | End Class 55 | -------------------------------------------------------------------------------- /Analysis/CrashAnalyzer.vb: -------------------------------------------------------------------------------- 1 | Public Class CrashAnalyzer 2 | Enum TSPlatformVersions 3 | TiberianSun203 4 | FireStrom 5 | Ra2Original 6 | Ra21006 7 | YuriOriginal 8 | Yuri1001 9 | End Enum 10 | Sub New(Version As TSPlatformVersions) 11 | If Version < TSPlatformVersions.YuriOriginal Then 12 | Throw New NotSupportedException($"不支持{Version}的游戏平台") 13 | End If 14 | End Sub 15 | Public Function FindEIP(Except As String) As String 16 | Dim reg As New Text.RegularExpressions.Regex("(?<=Eip:)[0-9A-F]{8}") 17 | If reg.IsMatch(Except) Then 18 | Return reg.Match(Except).Value 19 | Else 20 | Return Nothing 21 | End If 22 | End Function 23 | Public Function TryGetDetail(Except As String) As CrashItem 24 | Dim cureip = FindEIP(Except) 25 | If cureip IsNot Nothing Then 26 | Return TryGetHelpText(cureip) 27 | Else 28 | Return Nothing 29 | End If 30 | End Function 31 | Private Function TryGetHelpText(UpperEIPText As String) As CrashItem 32 | If EIPTable.ContainsKey(UpperEIPText) Then 33 | Dim s = EIPTable(UpperEIPText) 34 | Return New CrashItem(UpperEIPText, s(0), s(1)) 35 | Else 36 | Return Nothing 37 | End If 38 | End Function 39 | 40 | Dim EIPTable As New Dictionary(Of String, String()) From { 41 | {"004145BD", {"Art", "An AircraftType has a corrupted HVA. 42 | Shows the file type as ""unknown"" in the XCC Mixer. Also an AircraftType has image tag missing or no artmd section. 43 | This could also be caused from an aircraft with no name under AircraftTypes or the name is in the wrong section."}}, 44 | {"004157E3", {"Weapon", "An AircraftType used as spy plane has no Primary weapon."}}, 45 | {"00417D05", {"Weapon", "An AircraftType has fired a weapon which has Suicide=yes set."}}, 46 | {"004242DB", {"Animation", "Using a TrailerAnim on an Animation but not setting a TrailerSeperation (or setting TrailerSeperation=0). This is because the default TrailerSeperation is zero, and that number is used as a divisor."}}, 47 | {"00424A14", {"Animation", "An Animation with MakeInfantry=X set was played, where X was greater than the number of list entries in AnimToInfantry or less than 0."}}, 48 | {"0042E7AF", {"AI", "A Construction Yard does not have AIBuildThis=yes set and the owning side's AI was present in the game."}}, 49 | {"00441C28", {"Misc", "You have set [AudioVisual]→ShakeScreen= to zero."}}, 50 | {"004593BB", {"Misc", "See Tank Bunker / Sell Unit IEs"}}, 51 | {"0045EC90", {"Misc", "Multiple reasons, depending on the stack dump in the except: 52 | If 00506115 appears near the top of the stack dump - some country (present in the game at the moment, controlled by AI) cannot build anything from [General]→Shipyard=."}}, 53 | {"0045ED69", {"Misc", "The [General]→PadAircraft= list is empty."}}, 54 | {"0045ED71", {"Misc", "The first AircraftType in the [General]→PadAircraft= list doesn't have at least one BuildingType listed as its Dock."}}, 55 | {"0046650D", {"Weapon", "A unit's shrapnel weapon does not exist (see broken-reference causes, below)."}}, 56 | {"00471CA4", {"Weapon", "A unit's initial primary (Primary or Weapon1) weapon's Warhead does not have MindControl=yes set, but a weapon in some other weapon slot does. Triggered by one of the following events: 57 | Unit was selected by the user and the user moused-over a potential target. 58 | Unit was considering potential targets on its own (e.g. the unit was about to fire automatically at a nearby enemy unit). 59 | An IFV or urban combat building gains a mind-control weapon via passenger entry or garrison."}}, 60 | {"00482096", {"Unit crates", "Your [AI]→BuildRefinery= is either invalid, missing or blank thereby the game can't find the harvesters for deciding the unit."}}, 61 | {"004895C7", {"Warhead", "You have a warhead with a CellSpread greater than 11."}}, 62 | {"004D5108", {"Weapon", "A unit's secondary weapon does not exist (see broken-reference causes, below), or the weapon's Warhead tag is missing or set to blank. Examples: 63 | The offending weapon is the unit's Secondary weapon, and the unit just finished constructing. 64 | The offending weapon is the unit's EliteSecondary weapon, and the unit just got promoted to Elite status."}}, 65 | {"004F8CCD", {"AI", "[AI]→BuildConst= lists less than 3 BuildingTypes and your last [listed] Construction Yard was destroyed or sold while you were on low power, or you went into low power without owning any Construction Yards."}}, 66 | {"004F65BF", {"Misc", "Some House cannot build anything from [General]→BaseUnit= ."}}, 67 | {"00505E41", {"Misc", "An AI-controlled House which, due to rules(md).ini configuration, is unable to construct a base, received a Construction Yard thus triggering the AI base planning routine. Triggered by the following events: 68 | If a player's MCV was mind-controlled by an enemy, that player is killed, and the MCV is then released from mind-control to the neutral side. The only workaround is to make MCVs immune to mind-control (this is done in the UMP). 69 | A neutral Engineer (there's an official multiplayer map that has neutral Psychic Sensors which, on rare occasions, can leave an Engineer as a survivor) captures a Construction Yard. 70 | More detail: The AI base planning logic kicks in at the moment a player receives a Construction Yard and generates a plan of what buildings to build, in what order. However, the game makes an assumption that any country that can actually start base construction will be able to build at least 3 different BuildingTypes. When that assumption fails (a Construction Yard is received by the civilian house, who cannot build anything), everything goes haywire. For more info on how base planning logic works, refer to the AI Base Planning System article. 71 | Interestingly, the civilian house acquiring a Construction Yard via relinquished mind-control (in the same way as for an MCV) does not cause an IE. Clearly this effect was not taken into consideration when mind-control was added to the engine and the AI base planning routine is only called when a Construction Yard is captured or is first created."}}, 72 | {"0050CD20", {"AI", "The AI is trying to pick a target for the Nuclear Missile or Weather Storm superweapon but is lacking target weighting values for a certain object that exists on the map. You need to uncomment/restore one of the [General]→AIIonCannonXValue= lines. - An InfantryType With Engineer=yes exists On the map. AIIonCannonEngineerValue needs restoring."}}, 73 | {"0050CD44", {"AI", "The AI is trying to pick a target for the Nuclear Missile or Weather Storm superweapon but is lacking target weighting values for a certain object that exists on the map. You need to uncomment/restore one of the [General]→AIIonCannonXValue= lines. - An InfantryType with VehicleThief=yes exists on the map. AIIonCannonThiefValue needs restoring."}}, 74 | {"0050CD79", {"AI", "The AI is trying to pick a target for the Nuclear Missile or Weather Storm superweapon but is lacking target weighting values for a certain object that exists on the map. You need to uncomment/restore one of the [General]→AIIonCannonXValue= lines. - A BuildingType With Factory=BuildingType exists On the map. AIIonCannonConYardValue needs restoring."}}, 75 | {"0050CDA2", {"AI", "The AI is trying to pick a target for the Nuclear Missile or Weather Storm superweapon but is lacking target weighting values for a certain object that exists on the map. You need to uncomment/restore one of the [General]→AIIonCannonXValue= lines. - A BuildingType with Factory=UnitType and Naval=no exists on the map. AIIonCannonWarFactoryValue needs restoring."}}, 76 | {"0050CDCC", {"AI", "The AI is trying to pick a target for the Nuclear Missile or Weather Storm superweapon but is lacking target weighting values for a certain object that exists on the map. You need to uncomment/restore one of the [General]→AIIonCannonXValue= lines. - A BuildingType With a positive Power value exists On the map. AIIonCannonPowerValue needs restoring."}}, 77 | {"0050CDF0", {"AI", "The AI is trying to pick a target for the Nuclear Missile or Weather Storm superweapon but is lacking target weighting values for a certain object that exists on the map. You need to uncomment/restore one of the [General]→AIIonCannonXValue= lines. - A BuildingType with IsBaseDefense=yes exists on the map. AIIonCannonBaseDefenseValue needs restoring."}}, 78 | {"0050CE14", {"AI", "The AI is trying to pick a target for the Nuclear Missile or Weather Storm superweapon but is lacking target weighting values for a certain object that exists on the map. You need to uncomment/restore one of the [General]→AIIonCannonXValue= lines. - A BuildingType With IsPlug=yes exists On the map. AIIonCannonPlugValue needs restoring."}}, 79 | {"0050CE38", {"AI", "The AI is trying to pick a target for the Nuclear Missile or Weather Storm superweapon but is lacking target weighting values for a certain object that exists on the map. You need to uncomment/restore one of the [General]→AIIonCannonXValue= lines. - A BuildingType with IsTemple=yes exists on the map. AIIonCannonTempleValue needs restoring."}}, 80 | {"0050CE5C", {"AI", "The AI is trying to pick a target for the Nuclear Missile or Weather Storm superweapon but is lacking target weighting values for a certain object that exists on the map. You need to uncomment/restore one of the [General]→AIIonCannonXValue= lines. - A BuildingType With HoverPad=yes exists On the map. AIIonCannonHelipadValue needs restoring."}}, 81 | {"0050CEA2", {"AI", "The AI is trying to pick a target for the Nuclear Missile or Weather Storm superweapon but is lacking target weighting values for a certain object that exists on the map. You need to uncomment/restore one of the [General]→AIIonCannonXValue= lines. - A BuildingType listed in [AI] → BuildTech exists on the map. AIIonCannonTechCenterValue needs restoring."}}, 82 | {"0050CECC", {"AI", "The AI is trying to pick a target for the Nuclear Missile or Weather Storm superweapon but is lacking target weighting values for a certain object that exists on the map. You need to uncomment/restore one of the [General]→AIIonCannonXValue= lines. - A VehicleType With Harvester=yes exists On the map. AIIonCannonHarvesterValue needs restoring."}}, 83 | {"0050CF15", {"AI", "The AI is trying to pick a target for the Nuclear Missile or Weather Storm superweapon but is lacking target weighting values for a certain object that exists on the map. You need to uncomment/restore one of the [General]→AIIonCannonXValue= lines. - A VehicleType with a positive Passengers value exists on the map. AIIonCannonAPCValue needs restoring."}}, 84 | {"0050CF2A", {"AI", "The AI is trying to pick a target for the Nuclear Missile or Weather Storm superweapon but is lacking target weighting values for a certain object that exists on the map. You need to uncomment/restore one of the [General]→AIIonCannonXValue= lines. - A VehicleType which DeploysInto a BuildingType listed in [AI] → BuildConst exists on the map. AIIonCannonMCVValue needs restoring."}}, 85 | {"00518369", {"Warhead", "An InfantryType Is taking damage from a non-existant warhead."}}, '{"0053A15B", {"Unknown", "map error"}}, 86 | {"0054AF0E", {"Object", "A unit's Secondary points to a non-existent weapon."}}, 87 | {"00567B43", {"Object", "An Object has a negative sight."}}, 88 | {"0056D388", {"Object", "An InfantryType or VehicleType does not have a valid movement zone."}}, 89 | {"005D7387", {"Misc", "Not having at least one valid InfantryType with AllowedToStartInMultiplayer=yes (default) for each house."}}, 90 | {"005DA453", {"Network", "A crash occurred with the network code around an object called FirewallHelper."}}, 91 | {"005F4F88", {"Animation", "An Animation has TiberiumSpawnType=SOME_OVERLAY and SOME_OVERLAY is one of the three last entries in [OverlayTypes].Link to original report"}}, 92 | {"005F5155", {"Misc", "You tried to construct a BuildingType with HasSpotlight=yes. Yuri's Revenge 1.001 doesn't support this, only patched versions do."}}, 93 | {"0062B662", {"Animation", "Having an animation with SpawnsParticle which does not point to a valid ParticleSystem (see broken-reference causes, below)."}}, 94 | {"0062DCD2", {"Misc", "An overlay type with Explodes=yes set has been destroyed, random dice roll determined that the particle specified in BarrelParticle should be displayed, but that flag is blank OR you're firing a weapon with UseSparkParticles=yes/UseFireParticles=yes/IsRailgun=yes without a valid AttachedParticleSystem= set."}}, 95 | {"0064003C", {"Misc", "If you have a buildable Construction Yard, start its construction, and then cancel it, an IE will occur. Construction Yards should not be buildable - they should only be deployed from vehicles."}}, 96 | {"006407A6", {"Misc", "You've made a MapShot ""Not ScreenShot"" that was saved as Map****.yrm and is in your Directory. Game is trying to load map during initial startup. Remove these MapShots from the directory to prevent this from happening."}}, 97 | {"0065B73F", {"Weapon", "You have a Weapon whose RadLevel is less than [Radiation]→RadLightDelay=. (Cause: Integer division is performed on those two values, which yields zero in this case, and then another value is divided by the result - division by zero.)"}}, 98 | {"0069ACC6", {"Map", "A PKT file's [MultiMaps] section declares a map which doesn't have its own section to define the parameters, or lacks the CD entry in the section."}}, 99 | {"00697F29", {"Misc", "The game can't find a valid gamemode. Your mpmodesmd.ini is corrupted."}}, 100 | {"006AEBB8", {"Misc", "Your ra2md.ini file lists a combination of game mode/map which the game cannot satisfy. This can happen if the range of valid combinations changes between one game session and the next (i.e. because you changed what maps were valid for which game modes in your mod, or you activated a different mod to the one that was active previously). 101 | LaunchBase works around this by saving and restoring the game's configuration on a per-mod basis."}}, 102 | {"006B7D30", {"Object", "An object has a weapon with Spawner=yes, but it doesn't have Spawns=yes. The latter part is what tells the game to initialize the Spawn Manager for this unit when it's instantiated, the former part tells the game to try and access the Spawn Manager (and it doesn't check if it's been initialized properly)."}}, 103 | {"006B7718", {"Object", "A Spawned=yes attempted to fly over the map border instead of landing, thus crashing the game. A known reason for this issue is Selectable=no on AircraftTypes that use aircraft Locomotor."}}, 104 | {"006EA6AE", {"AI/Map", "You coerced the game into creating an instance of a TeamType that is not defined, either through Map Triggers or through AI Script Actions."}}, 105 | {"006F1FC6", {"AI programming", "A TeamType has been defined without assigning it a TaskForce, or a TeamType is being referenced without being defined at all."}}, 106 | {"006F352E", {"Weapon", "A unit has an ElitePrimary weapon specified which does not exist (see broken-reference causes, below), or the weapon's Warhead tag is missing or set to blank, and that unit just got promoted to Elite status."}}, 107 | {"006F72EF", {"Weapon", "A unit has an ElitePrimary weapon specified which does not exist (see broken-reference causes, below), or the weapon's Warhead tag is missing or set to blank, and that unit just got promoted to Elite status."}}, 108 | {"006F40A2", {"Weapon", "Started construction of a unit whose Primary weapon does not exist (see broken-reference causes, below), or the weapon's Warhead tag is missing or set to blank."}}, 109 | {"0070031E", {"Weapon", "A unit has a weapon specified which does not exist in the INI (see broken-reference causes, below), or the weapon's Warhead tag is missing or set to blank. (Common reason - that unit just got promoted to Elite status and one of the Elite weapons is misdefined.)"}}, 110 | {"00702330", {"Anim", "A building has missing or non-existing DebrisTypes. Like DebrisTypes=CRYSTAL1."}}, 111 | {"007120F7", {"Misc", "You have a BuildingType (which is click-repairable) with Strength=0 or Strength below [General]→RepairStep=."}}, 112 | {"0071AF4D", {"Warhead", "Detonating a Temporal=yes warhead under one of the following conditions: 113 | Firing weapon was a shrapnel weapon. 114 | Firing weapon was an urban combat weapon (fired from an occupied building). 115 | Warhead has a non-zero CellSpread set."}}, 116 | {"0071B173", {"Warhead", "Firing a death weapon using a Temporal=yes warhead."}}, 117 | {"00442832", {"Warhead", "Using Sparky=yes warhead without three valid animations defined in [AudioVisual]→OnFire=. Crash happens when damaging a building into yellow or red health."}}, 118 | {"0071C661", {"Warhead", "Using Sparky=yes warhead without two valid animations defined in [AudioVisual]→TreeFire=. Crash happens when damaging a wooden terrain object."}}, 119 | {"0072652D", {"Map", "There is a trigger in the map that wants to change a house's non-existant object to another house."}}, 120 | {"00684E55", {"Map", "There is a trigger which requires a house, either because its events, actions or attached triggers require a house. The house on the trigger is not set."}}, 121 | {"007387EB", {"Art", "[AudioVisual]→ShakeScreen= is missing or set to zero."}}, 122 | {"0073B0C9", {"Misc", "The concept known as ""Infantry Linking"" can result in an IE, occuring when the linked infantry was modified in a subsequent game mode override file or a map and a human player scrolls their battlefield view to a place on the map where an AI-owned War Factory is located. Don't do Infantry Linking."}}, 123 | {"0073C762", {"Art", "The artmd.ini entry specified by a Voxel-using VehicleType's Image tag is missing - the game defaults to Voxel=no in this case and attempts to load and draw a non-existant SHP."}}, 124 | {"00756B2D", {"Art", "The specified ShadowIndex on a voxel VehicleType adresses a section that does not exist."}}, 125 | {"00772A98", {"Weapon", "A unit has an ElitePrimary weapon specified which does not exist (see broken-reference causes, below), or the weapon's Warhead tag is missing or set to blank, and that unit just got promoted to Elite status. 126 | Also reported to be due to ""firing a weapon that has no projectile"". 127 | This needs testing - missing projectile may be an alternative reason to all 'missing weapon' IEs."}}, 128 | {"007C9B92", {"Malformed input", "Multiple reasons, depending on the stack dump in the except: 129 | If 006DD5D2 or 006DD009 appear near the top of the stack dump - Map contains a malformed Map Action, while parsing it, a number was expected, something else or end-of-line was found instead. Map Actions expect well-formed input. 130 | If 00843EEC appears in the stack - RefinerySmokeOffsetThree is not set to a valid value."}}, 131 | {"007CAF66", {"Malformed input", "Multiple reasons, depending on the stack dump in the except: 132 | If 0075DE19 appears near the top of the stack dump - A Warhead whose Verses could not be processed. EBP value says how many values remained to be parsed when an error occurred - (11 - EBP) is the 0-based index of the problematic value."}}, 133 | {"007CFD30", {"Misc - memory management", "If line 20 of the stack dump includes 61108B, and line 23 includes 610CA0, you are likely trying to use RockPatch's ""Place Urban Areas"" feature without applying the necessary snowmd.ini fix. Please check the RockPatch Help for more info."}}, 134 | {"0051BB7D", {"Warhead", "A unit was being erased by a chrono weapon but the object that started the erase process no longer exists. When a unit is being erased, an instance of the TemporalClass is linked to it. This class references the object that is doing the erasing. If the object breaks the link under 'normal' circumstances (e.g. the firer is destroyed or moved) then the attack order is cancelled and the TemporalClass is removed. In some rare cases the link to the firer's TemporalClass is not removed and therefore points to garbage memory. 135 | Examples of how the IE may occur: 136 | Ore harvester with a chrono weapon started an attack but then transformed whilst unloading ore at a refinery before the target was destroyed. Don't give ore harvesters chrono weapons. 137 | A vehicle was being erased but then got picked up by a carryall. This is very difficult to replicate - it has only been reported once. 138 | The IE occurs when the unit would have been erased."}}, 139 | {"00520FC8", {"Warhead", "A building was being erased by a chrono weapon after infantry recieved the order to occupy it. When the building is erased before the infantry reaches it, an IE occurs. This is because the TemporalClass removes the building without marking it as dead, thus the occupying infantry is not informed its destination is gone. The IE occurs when the infantry unit updates its position. There is another TemporalClass related bug similar to this one – EIP address 00521BB6, which lies inside the same function, can be seen near the top on a stack of this exception."}}, 140 | {"90900004", {"Misc", "Generic exception, for example, raised when you are missing the snowmd.ini median fix."}}, 141 | {"FEEEFEEE", {"Misc", "Multiple reasons, depending on the stack dump in the except: 142 | If the stack dump starts with 006B771E then it's a variant of 006B7718."}}} 143 | 144 | End Class 145 | -------------------------------------------------------------------------------- /Analysis/CrashItem.vb: -------------------------------------------------------------------------------- 1 | Public Class CrashItem 2 | Public EIP, Category, Description As String 3 | Sub New(EIP As String, Category As String, Desc As String) 4 | Me.EIP = EIP 5 | Me.Category = Category 6 | Description = Desc 7 | End Sub 8 | End Class -------------------------------------------------------------------------------- /Analysis/INIAnalyzeInfo.vb: -------------------------------------------------------------------------------- 1 | Public Class INIAnalyzeInfo 2 | 3 | Public ReadOnly Property LineNumber As Integer 4 | 5 | Public ReadOnly Property Description As String 6 | 7 | Public ReadOnly Property LineText As String 8 | 9 | Public ReadOnly Property MainKey As String 10 | Sub New(Ln As Integer, Desc As String, Text As String, MK As String) 11 | LineNumber = Ln 12 | Description = Desc 13 | LineText = Text 14 | MainKey = MK 15 | End Sub 16 | End Class -------------------------------------------------------------------------------- /Analysis/INIAnalyzeResult.vb: -------------------------------------------------------------------------------- 1 |  2 | Public Class INIAnalyzeResult 3 | Public ReadOnly Property Message As New List(Of INIAnalyzeInfo) 4 | Public ReadOnly Property Warning As New List(Of INIAnalyzeInfo) 5 | Public ReadOnly Property Fault As New List(Of INIAnalyzeInfo) 6 | 7 | Sub New() 8 | 9 | End Sub 10 | Sub New(Msg As List(Of INIAnalyzeInfo), Warn As List(Of INIAnalyzeInfo), Fau As List(Of INIAnalyzeInfo)) 11 | Message = Msg 12 | Warning = Warn 13 | Fault = Fau 14 | End Sub 15 | ''' 16 | ''' 返回被合并的两个结果 17 | ''' 18 | Public Function Concat(AnotherResult As INIAnalyzeResult) As INIAnalyzeResult 19 | Dim tmp As New INIAnalyzeResult(Message, Warning, Fault) 20 | tmp.Message.AddRange(AnotherResult.Message) 21 | tmp.Warning.AddRange(AnotherResult.Warning) 22 | tmp.Fault.AddRange(AnotherResult.Fault) 23 | Return tmp 24 | End Function 25 | End Class -------------------------------------------------------------------------------- /Analysis/INIAnalyzer.vb: -------------------------------------------------------------------------------- 1 | ''' 2 | ''' 对未拓展的Ini进行分析 3 | ''' 4 | Public Class INIAnalyzer 5 | ''' 6 | ''' 不加载任何信息。只能在关于类型的情况使用 7 | ''' 8 | Sub New() 9 | 10 | End Sub 11 | Public Overridable Function Check() As INIAnalyzeResult 12 | Return Result 13 | End Function 14 | Public Overridable ReadOnly Property Result As New INIAnalyzeResult 15 | ''' 16 | ''' 主键,键和值。如果主键重复注册则合并,如果键重复注册则进入ConflictValues。 17 | ''' 18 | ''' 19 | Public ReadOnly Property Values As New Dictionary(Of String, Dictionary(Of String, Tuple(Of String, Integer))) 20 | ''' 21 | ''' 重复注册的值的多余的部分 22 | ''' 23 | ''' 24 | Public ReadOnly Property ConflictValues As New Dictionary(Of String, List(Of KeyValuePair(Of String, String))) 25 | Protected Sub LineProc(tx As String, ln As Integer, ByRef curMK As String) 26 | If tx.Contains(";") Then 27 | tx = tx.Substring(0, tx.IndexOf(";"c)).Trim 28 | End If 29 | If Not String.IsNullOrEmpty(tx) Then 30 | If tx.StartsWith("[") Then 31 | If tx.Length = 1 Then 32 | Result.Fault.Add(New INIAnalyzeInfo(ln, "语法错误:空的主键", tx, curMK)) 33 | Return 34 | End If 35 | curMK = tx.Substring(1, tx.Length - 2).Trim 36 | If String.IsNullOrWhiteSpace(curMK) Then 37 | Result.Fault.Add(New INIAnalyzeInfo(ln, "语法错误:空的主键", tx, curMK)) 38 | End If 39 | If Not Values.ContainsKey(curMK) Then 40 | Values.Add(curMK, New Dictionary(Of String, Tuple(Of String, Integer))) 41 | End If 42 | ElseIf tx.Contains("=") 43 | If Values.ContainsKey(curMK) Then 44 | Dim spl As Integer = tx.IndexOf("="c) 45 | Dim left = tx.Substring(0, spl).Trim 46 | Dim rig = tx.Substring(spl + 1, tx.Length - 1 - spl).Trim 47 | If String.IsNullOrEmpty(left) Then 48 | Result.Warning.Add(New INIAnalyzeInfo(ln, "空的左值(键),此记录会被忽略", tx, curMK)) 49 | Else 50 | If Values(curMK).ContainsKey(left) Then 51 | Result.Warning.Add(New INIAnalyzeInfo(ln, "重复注册,此记录会被忽略", tx, curMK)) 52 | If ConflictValues.ContainsKey(curMK) Then 53 | ConflictValues(curMK).Add(New KeyValuePair(Of String, String)(left, rig)) 54 | Else 55 | ConflictValues.Add(curMK, New List(Of KeyValuePair(Of String, String))) 56 | End If 57 | Else 58 | Values(curMK).Add(left, New Tuple(Of String, Integer)(rig, ln)) 59 | End If 60 | End If 61 | Else 62 | Result.Warning.Add(New INIAnalyzeInfo(ln, "内容无效:记录应包括在主键内", tx, "(没有主键)")) 63 | End If 64 | Else 65 | Result.Warning.Add(New INIAnalyzeInfo(ln, "内容无效:注释应该以;开头", tx, curMK)) 66 | End If 67 | End If 68 | End Sub 69 | Public Async Function ReloadAsync(IniText As String) As Task 70 | Await Task.Run(Sub() Reload(IniText)) 71 | End Function 72 | Public Sub Reload(IniText As String) 73 | Values.Clear() 74 | ConflictValues.Clear() 75 | Result.Fault.Clear() 76 | Result.Message.Clear() 77 | Result.Warning.Clear() 78 | Load(IniText) 79 | End Sub 80 | Protected Overridable Sub Load(IniText As String) 81 | Dim curMK As String = String.Empty 82 | Dim txs = IniText.Split(CChar(vbLf)) 83 | SyncLock New Object 84 | For ln As Integer = 0 To txs.Length - 1 85 | Dim tx = txs(ln).Trim 86 | LineProc(tx, ln, curMK) 87 | Next 88 | End SyncLock 89 | End Sub 90 | Sub New(INIText As String) 91 | INIText = If(INIText, "") 92 | Load(INIText) 93 | Debug.WriteLine(Me.GetType.Name & " Initialized.") 94 | End Sub 95 | Sub New(INIText As StreamReader) 96 | Dim curMK As String = String.Empty 97 | Dim ln As Integer = 0 98 | SyncLock New Object 99 | Do While Not INIText.EndOfStream 100 | Dim tx = INIText.ReadLine.Trim 101 | LineProc(tx, ln, curMK) 102 | ln += 1 103 | Loop 104 | End SyncLock 105 | End Sub 106 | ''' 107 | ''' 把分析的INI重新写出来,这样能去除重复注册,删除所有注释。 108 | ''' 109 | ''' 110 | Public Overrides Function ToString() As String 111 | Dim sb As New Text.StringBuilder 112 | sb.AppendLine(";由Nukepayload2.Ra2CodeAnalysis生成") 113 | SyncLock Me 'Protect Values 114 | For Each ks In Values.Keys 115 | sb.AppendLine() 116 | sb.AppendLine("[" & ks & "]") 117 | For Each vs In Values(ks) 118 | sb.AppendLine(vs.Key & "=" & vs.Value.Item1) 119 | Next 120 | Next 121 | End SyncLock 122 | Return sb.ToString 123 | End Function 124 | End Class 125 | -------------------------------------------------------------------------------- /Analysis/Ra2IniAnalyzer.vb: -------------------------------------------------------------------------------- 1 | ''' 2 | ''' 包含专门分析Ra2/Ts/Fs/Yr平台的Ini的函数 3 | ''' 4 | Public MustInherit Class Ra2IniAnalyzer 5 | Inherits INIAnalyzer 6 | Public MustOverride ReadOnly Property Name As String 7 | Public MustOverride Overrides Function Check() As INIAnalyzeResult 8 | Sub New(INIText As StreamReader) 9 | MyBase.New(INIText) 10 | End Sub 11 | Sub New(INIText As String) 12 | MyBase.New(INIText) 13 | End Sub 14 | Protected Sub TypeCheck(Arr As IEnumerable(Of String), ids As IEnumerable(Of Integer), chk As Func(Of String, Boolean), errtxt As String, lst As IList(Of INIAnalyzeInfo), Linenum As Integer, MKName As String) 15 | For Each n In ids 16 | If Not chk.Invoke(Arr(n)) Then 17 | lst.Add(New INIAnalyzeInfo(Linenum, String.Format(errtxt, n), Arr(n), MKName)) 18 | End If 19 | Next 20 | End Sub 21 | Protected Sub ValueRegistryCheck(RegistryMainKeyName As String, FailText As String, MainKeyOfCheck As String, Value As String, LineNumber As Integer, lst As IList(Of INIAnalyzeInfo), ExternalValuesSource As INIAnalyzer, Optional SecondaryMainKey As String = Nothing) 22 | If 0 = Aggregate c In From v In ExternalValuesSource.Values(RegistryMainKeyName) Where v.Value.Item1 = Value Into Count Then 23 | If String.IsNullOrEmpty(SecondaryMainKey) Then 24 | lst.Add(New INIAnalyzeInfo(LineNumber, FailText, Value, MainKeyOfCheck)) 25 | Else 26 | ValueRegistryCheck(SecondaryMainKey, FailText, MainKeyOfCheck, Value, LineNumber, lst, ExternalValuesSource) 27 | End If 28 | End If 29 | End Sub 30 | ''' 31 | ''' 值不包含在指定的主键的值则添加指定的列表 32 | ''' 33 | ''' 主键的名字 34 | ''' 如果要添加错误,指定错误的文本 35 | ''' 正在扫描的记录 36 | ''' 目标列表 37 | Protected Sub ValueRegistryCheck(MainKeyName As String, FailText As String, MainKeyOfCheck As String, Record As KeyValuePair(Of String, Tuple(Of String, Integer)), lst As IList(Of INIAnalyzeInfo), Optional SecondaryMainKey As String = Nothing) 38 | ValueRegistryCheck(MainKeyName, FailText, MainKeyOfCheck, Record.Value.Item1, Record.Value.Item2, lst, Me, SecondaryMainKey) 39 | End Sub 40 | ''' 41 | ''' 主键不包含在指定的主键的值则添加指定的列表 42 | ''' 43 | ''' 主键的名字 44 | ''' 如果要添加错误,指定错误的文本 45 | ''' 正在扫描的记录 46 | ''' 目标列表 47 | Protected Sub MainKeyRegistryCheck(MainKeyName As String, FailText As String, MainKeyOfCheckingKV As String, Record As KeyValuePair(Of String, Tuple(Of String, Integer)), lst As IList(Of INIAnalyzeInfo), Optional SecodaryMainKeyName As String = Nothing) 48 | Dim Value = Record.Value 49 | If 0 = Aggregate c In From v In Values(MainKeyName) Where v.Value.Item1 = MainKeyOfCheckingKV Into Count Then 50 | If String.IsNullOrEmpty(SecodaryMainKeyName) Then 51 | lst.Add(New INIAnalyzeInfo(Value.Item2, FailText, Value.Item1, MainKeyOfCheckingKV)) 52 | Else 53 | MainKeyRegistryCheck(SecodaryMainKeyName, FailText, MainKeyOfCheckingKV, Record, lst) 54 | End If 55 | End If 56 | End Sub 57 | ''' 58 | ''' 对用逗号分隔开的所有值进行注册检查 59 | ''' 60 | ''' 要检查的键值对 61 | ''' 要写入的记录 62 | ''' 注册用的主键 63 | ''' 失败时的文本 64 | ''' 被检查的键值对的主键 65 | ''' 备用的注册用的主键 66 | Protected Sub EachValueRegistryCheck(r As KeyValuePair(Of String, Tuple(Of String, Integer)), ls As List(Of INIAnalyzeInfo), MK As String, Tx As String, FindMKey As String, Optional SecondaryMainKey As String = Nothing) 67 | For Each val In r.Value.Item1.Split(","c) 68 | If String.IsNullOrEmpty(val) Then 69 | ls.Add(New INIAnalyzeInfo(r.Value.Item2, "语法错误:','前后应为数值", r.Value.Item1, MK)) 70 | Else 71 | ValueRegistryCheck(FindMKey, Tx, MK, New KeyValuePair(Of String, Tuple(Of String, Integer))(r.Key, New Tuple(Of String, Integer)(val, r.Value.Item2)), ls, SecondaryMainKey) 72 | End If 73 | Next 74 | End Sub 75 | Protected Sub EachValueRegistryCheck(r As KeyValuePair(Of String, Tuple(Of String, Integer)), ls As List(Of INIAnalyzeInfo), MK As String, Tx As String, FindMKey As String, ExternalValuesSource As INIAnalyzer, Optional SecondaryMainKey As String = Nothing) 76 | For Each val In r.Value.Item1.Split(","c) 77 | If String.IsNullOrEmpty(val) Then 78 | ls.Add(New INIAnalyzeInfo(r.Value.Item2, "语法错误:','前后应为数值", r.Value.Item1, MK)) 79 | Else 80 | ValueRegistryCheck(FindMKey, Tx, MK, val, r.Value.Item2, ls, ExternalValuesSource, SecondaryMainKey) 81 | End If 82 | Next 83 | End Sub 84 | Public Shared ReadOnly Property SpecialBuildings As ICollection(Of String) 85 | Get 86 | Return {"BARRACKS", "RADAR", "TECH", "PROC", "POWER", "FACTORY"} 87 | End Get 88 | End Property 89 | Public Shared ReadOnly SpecialTags As ICollection(Of String) = {"", ""} 90 | End Class 91 | -------------------------------------------------------------------------------- /Analysis/RulesAnalyzer.vb: -------------------------------------------------------------------------------- 1 | Imports Nukepayload2.Ra2CodeAnalysis.AnalysisHelper 2 | 3 | ''' 4 | ''' 分析Rules.ini或它的等效ini 5 | ''' 6 | Public Class RulesAnalyzer 7 | Inherits Ra2IniAnalyzer 8 | 9 | Public Overrides ReadOnly Property Name As String = "Rules" 10 | 11 | ''' 12 | ''' 分析结果,包括基类提供的语法检查 13 | ''' 14 | ''' 15 | Public Overrides Function Check() As INIAnalyzeResult 16 | Return CheckInternal() 17 | End Function 18 | Sub New(INIText As StreamReader) 19 | MyBase.New(INIText) 20 | End Sub 21 | Sub New(INIText As String) 22 | MyBase.New(INIText) 23 | End Sub 24 | Public Shared Function IsWeaponKey(Key As String) As Boolean 25 | Return {"OccupyWeapon", "EliteOccupyWeapon", "Primary", "Secondary", "ElitePrimary", "EliteSecondary", "DeathWeapon"}.Contains(Key) OrElse Key.StartsWith("Weapon") OrElse Key.StartsWith("EliteWeapon") 26 | End Function 27 | Protected Function CheckInternal() As INIAnalyzeResult 28 | Dim AdvResult As New INIAnalyzeResult 29 | SyncLock Result 30 | AdvResult = AdvResult.Concat(Result) 31 | End SyncLock 32 | Dim UsedWeapons As New List(Of String) 33 | Dim LoadWeapons As New List(Of Tuple(Of String, Tuple(Of String, Integer))) 34 | 35 | Try 36 | Dim DeadBodies, MetallicDebris As IEnumerable(Of String) 37 | 38 | DeadBodies = From d In Values("General")("DeadBodies").Item1.Split(","c) Select d.Trim 39 | MetallicDebris = From d In Values("General")("MetallicDebris").Item1.Split(","c) Select d.Trim 40 | For Each Record In Values 41 | For Each r In Record.Value 42 | Dim wpchk As Action = Sub() 43 | If Values.ContainsKey(r.Value.Item1) Then 44 | UsedWeapons.Add(r.Value.Item1) 45 | ElseIf r.Value.Item1.ToLower <> "none" Then 46 | AdvResult.Fault.Add(New INIAnalyzeInfo(r.Value.Item2, "武器未定义,可导致单位无法开火或建造时发生AccessViolation", r.Value.Item1, Record.Key)) 47 | End If 48 | End Sub 49 | Select Case r.Key 50 | Case "SuperWeapon", "SuperWeapon2" 'SW 51 | ValueRegistryCheck("SuperWeaponTypes", "超级武器未注册或注册失败,可能导致运行时AccessViolation异常", Record.Key, r, AdvResult.Fault) 52 | Case "Warhead" 'WH 53 | ValueRegistryCheck("Warheads", "弹头未注册或注册失败.", Record.Key, r, AdvResult.Message) 54 | Case "DeadBodies" 55 | If DeadBodies.ContainsEachTrim(r.Value.Item1.Split(","c)) Then 56 | Exit Select 57 | End If 58 | EachValueRegistryCheck(r, AdvResult.Warning, Record.Key, "尸体动画未正确注册,可能导致尸体消失", "Animations", "VoxelAnims") 59 | Case "DebrisAnim", "DebrisAnims" 60 | If MetallicDebris.ContainsEachTrim(r.Value.Item1.Split(","c)) Then 61 | Exit Select 62 | End If 63 | EachValueRegistryCheck(r, AdvResult.Message, Record.Key, "碎片动画未正确注册.", "Animations", "VoxelAnims") 64 | Case "AnimList", "Anim", "MetallicDebris", "ExpireAnim", "TrailerAnim", "Explosion" 65 | EachValueRegistryCheck(r, AdvResult.Message, Record.Key, "一般的动画未注册或注册失败.", "Animations", "VoxelAnims") 66 | Case "Next" 67 | ValueRegistryCheck("Animations", "关键动画未注册或注册失败,可导致运行时AccessViolation异常", Record.Key, New KeyValuePair(Of String, Tuple(Of String, Integer))(r.Key, New Tuple(Of String, Integer)(r.Value.Item1, r.Value.Item2)), AdvResult.Fault) 68 | Case "Owner", "RequiredHouses", "FactoryOwners", "ForbiddenHouses" 69 | EachValueRegistryCheck(r, AdvResult.Warning, Record.Key, "国家未正确注册,可能导致建造结果不是预期的", "Countries") 70 | Case "Category" 'Unit 71 | Select Case r.Value.Item1 72 | Case "AirLift", "AirPower", "AirSupport" 73 | MainKeyRegistryCheck("AircraftTypes", "Category错误或战机未正确注册,可能导致无法正常建造或运行时AccessViolation异常", Record.Key, r, AdvResult.Fault, "VehicleTypes") 74 | Case "VIP", "Soldier", "Civilian" 75 | MainKeyRegistryCheck("InfantryTypes", "Category错误或步兵未正确注册,可能导致无法正常建造或运行时AccessViolation异常", Record.Key, r, AdvResult.Fault) 76 | Case Else 77 | MainKeyRegistryCheck("VehicleTypes", "Category错误或战车未正确注册,可能导致无法正常建造或运行时AccessViolation异常", Record.Key, r, AdvResult.Fault) 78 | End Select 79 | Case "Prerequisite", "Prerequisite2" 80 | If SpecialBuildings.ContainsEachTrim(r.Value.Item1.Split(","c)) Then 81 | Exit Select 82 | End If 83 | EachValueRegistryCheck(r, AdvResult.Warning, Record.Key, "建筑未正确注册,可能导致建造结果不是预期的", "BuildingTypes") 84 | Case "BuildCat" 'Building 85 | MainKeyRegistryCheck("BuildingTypes", "建筑或覆盖物建筑未注册或注册失败,可能导致建筑无法建造或运行时AccessViolation异常", Record.Key, r, AdvResult.Fault, "OverlayTypes") 86 | Case "TiberiumSpawnType" 'Overlay 87 | ValueRegistryCheck("OverlayTypes", "覆盖物未注册或注册失败,可能导致覆盖物无法产生或运行时AccessViolation异常", Record.Key, r, AdvResult.Fault) 88 | Case "OccupyWeapon", "EliteOccupyWeapon", "Primary", "Secondary", "ElitePrimary", "EliteSecondary", "DeathWeapon" 89 | wpchk() 90 | Case "Verses" 91 | Dim spl = r.Value.Item1.Split(","c) 92 | If 0 = Aggregate c In From s In spl Where Not s.Contains("%") Into Count Then 93 | If spl.Count <> 11 Then 94 | AdvResult.Fault.Add(New INIAnalyzeInfo(r.Value.Item2, "弹头百分比数量错误,可能导致运行时AccessViolation异常", r.Value.Item1, Record.Key)) 95 | End If 96 | Else 97 | AdvResult.Fault.Add(New INIAnalyzeInfo(r.Value.Item2, "弹头百分比有语法错误,可能导致运行时AccessViolation异常", r.Value.Item1, Record.Key)) 98 | End If 99 | Case "ShrapnelWeapon", "AirburstWeapon" 100 | LoadWeapons.Add(New Tuple(Of String, Tuple(Of String, Integer))(Record.Key, r.Value)) 101 | Case "HoldsWhat" 102 | MainKeyRegistryCheck("ParticleSystems", "粒子系统未注册或注册失败,可能导致运行时AccessViolation异常", Record.Key, r, AdvResult.Fault) 103 | ValueRegistryCheck("Particles", "粒子未注册或注册失败,可能导致运行时AccessViolation异常", Record.Key, r, AdvResult.Fault) 104 | Case Else 105 | If (r.Key.StartsWith("EliteWeapon") AndAlso r.Key.Length > 11 AndAlso r.Key.Substring(11).IsUInteger) OrElse (r.Key.StartsWith("Weapon") AndAlso r.Key.Length > 6 AndAlso r.Key.Substring(6).IsUInteger) Then 106 | wpchk() 107 | End If 108 | End Select 109 | Next 110 | Next 111 | 112 | Debug.WriteLine("Generic Check complete.Weapon check begins") 113 | Dim wp = From u In UsedWeapons Distinct 114 | Dim lo = From l In LoadWeapons Distinct 115 | For Each wea In lo 116 | If Not wp.Contains(wea.Item2.Item1) Then 117 | AdvResult.Fault.Add(New INIAnalyzeInfo(wea.Item2.Item2, "武器未挂载,可能导致运行时AccessViolation异常", wea.Item2.Item1, wea.Item1)) 118 | End If 119 | Next 120 | For Each w In wp 121 | If Values.ContainsKey(w) Then 122 | Dim wpref = Values(w) 123 | If wpref.Count > 0 Then 124 | Dim fir = wpref.First 125 | For Each BasicKey In {"Damage", "Projectile", "Warhead", "Report", "ROF", "Speed", "Range"} 126 | Dim Rec = If({"Projectile", "Warhead"}.Contains(BasicKey), AdvResult.Fault, AdvResult.Warning) 127 | If wpref.ContainsKey(BasicKey) Then 128 | If String.IsNullOrWhiteSpace(wpref(BasicKey).Item1) Then 129 | AdvResult.Fault.Add(New INIAnalyzeInfo(fir.Value.Item2, $"武器的{BasicKey}值为空,可能导致运行时AccessViolation异常", BasicKey, w)) 130 | End If 131 | Select Case BasicKey 132 | Case "Warhead", "Projectile" 133 | If Not Values.ContainsKey(w) AndAlso w.ToLower <> "none" Then 134 | AdvResult.Fault.Add(New INIAnalyzeInfo(fir.Value.Item2, $"武器的{BasicKey}值不存在于rules中,可能导致运行时AccessViolation异常", BasicKey, w)) 135 | End If 136 | End Select 137 | Else 138 | Rec.Add(New INIAnalyzeInfo(fir.Value.Item2, $"武器没有{BasicKey},可能导致运行时AccessViolation异常", BasicKey, w)) 139 | End If 140 | Next 141 | Else 142 | AdvResult.Fault.Add(New INIAnalyzeInfo(0, $"武器{w}为空,可能导致运行时AccessViolation异常", w, w)) 143 | End If 144 | Else 145 | AdvResult.Fault.Add(New INIAnalyzeInfo(0, $"Internal Check Error: 没有武器{w},注册信息,但是当作已经注册的武器", w, w)) 146 | End If 147 | Next 148 | Catch ex As KeyNotFoundException 149 | AdvResult.Fault.Add(New INIAnalyzeInfo(0, "关键的键或主键没有找到,可导致运行时AccessViolation异常", "(未收集)", "(无)")) 150 | End Try 151 | 152 | Debug.WriteLine("Mission Accomplished") 153 | Return AdvResult 154 | End Function 155 | 156 | End Class 157 | -------------------------------------------------------------------------------- /Collections/IndexedList.vb: -------------------------------------------------------------------------------- 1 | ''' 2 | ''' 对列表某一成员建立唯一索引的列表。它与的区别是:不通过 TKey 查找 TValue。 3 | ''' 4 | Public Class IndexedList(Of TIndex, TValue) 5 | ''' 6 | ''' 对列表某一成员建立唯一索引的列表 7 | ''' 8 | ''' 获取需要索引的成员 9 | ''' 索引是否是聚簇的 10 | Sub New(getIndex As Func(Of TValue, TIndex), clustered As Boolean) 11 | Me.getIndex = getIndex 12 | Index = If(clustered, DirectCast(New SortedSet(Of TIndex), ISet(Of TIndex)), New HashSet(Of TIndex)) 13 | End Sub 14 | 15 | Dim getIndex As Func(Of TValue, TIndex) 16 | Public ReadOnly Property Index As ISet(Of TIndex) 17 | Public ReadOnly Property Values As New List(Of TValue) 18 | ''' 19 | ''' 添加项目 20 | ''' 21 | Public Sub Add(item As TValue) 22 | Values.Add(item) 23 | Index.Add(getIndex(item)) 24 | End Sub 25 | ''' 26 | ''' 删除项目 27 | ''' 28 | Public Sub Remove(item As TValue) 29 | Values.Remove(item) 30 | Index.Remove(getIndex(item)) 31 | End Sub 32 | ''' 33 | ''' 确定被索引的成员是否包含指定项目。对于聚簇索引,使用折半查找。对于非聚簇索引,使用哈希查找。 34 | ''' 35 | Public Function Contains(key As TIndex) As Boolean 36 | Return Index.Contains(key) 37 | End Function 38 | End Class -------------------------------------------------------------------------------- /Colorize/CodeColorBase.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text.RegularExpressions 2 | 3 | Public MustInherit Class CodeColorBase(Of Brush) 4 | Dim KWLst As IEnumerable(Of String) = {"(?<=\s)As(?=\s)", 5 | "(?<=<)(bool|float|object|int|string)(?=>)", 6 | "(?<=(As|Of)\s+)(Boolean|Integer|Single|String|Object)", 7 | "(bool|float|const|struct|object|int|string|class)(?=\s)", 8 | "(Dim|Const|Structure|Of|Class)(?=\s)", 9 | "(?<==\s*)(Nothing|null|True|true|False|false)"} 10 | Dim SecKWLst As IList(Of String) = {"^\w+(?=\(Of)", 'VB样式的泛型左侧 11 | "\w+(?=<)", 'c#样式的泛型左侧 12 | "(?<=<)\w+(?=>)", 'c#样式的泛型右侧 13 | "(?<=:)\w+(?=\s+[^=]+;)", 'c#样式的单变量声明 14 | "(?<=(As|Of)\s+)\w+", 'VB样式的单变量声明+泛型右 15 | "(?<=\w\.)\w+(?=(\s|;))" '最右侧的成员 16 | } 17 | Dim Comment As New Regex("^[^用].*$", RegexOptions.Multiline) 18 | Protected MustOverride Sub SetColor(Color As Brush, Index As Integer, Length As Integer) 19 | Protected MustOverride Sub ClearEffects() 20 | Protected Overridable Sub EndColorInternal() 21 | 22 | End Sub 23 | Public MustOverride Sub Color() 24 | Protected Sub ColorInternal(Code As String, Instructions As Brush, Types As Brush, Comments As Brush) 25 | ClearEffects() 26 | Dim ProcessedIns As New List(Of Integer) 27 | For Each b In KWLst 28 | Dim reg As New Regex(b) 29 | For Each m As Match In reg.Matches(Code) 30 | ProcessedIns.Add(m.Index) 31 | SetColor(Instructions, m.Index, m.Length) 32 | Next 33 | Next 34 | For Each g In SecKWLst 35 | Dim reg As New Regex(g) 36 | For Each m As Match In reg.Matches(Code) 37 | If Not ProcessedIns.Contains(m.Index) Then 38 | SetColor(Types, m.Index, m.Length) 39 | End If 40 | Next 41 | Next 42 | For Each m As Match In Comment.Matches(Code) 43 | SetColor(Comments, m.Index, m.Length) 44 | Next 45 | EndColorInternal() 46 | End Sub 47 | End Class 48 | -------------------------------------------------------------------------------- /Colorize/CodeColorBlock.vb: -------------------------------------------------------------------------------- 1 | Public Class CodeColorBlock(Of TColor) 2 | Public StartIndex As Integer 3 | Public Length As Integer 4 | Public Color As TColor 5 | Sub New(Index As Integer, Length As Integer, Color As TColor) 6 | StartIndex = Index 7 | Me.Length = Length 8 | Me.Color = Color 9 | End Sub 10 | End Class 11 | -------------------------------------------------------------------------------- /Colorize/CodeColors.vb: -------------------------------------------------------------------------------- 1 | Public Class CodeColors 2 | 3 | Public Shared ReadOnly Property AliceBlue As Integer 4 | Get 5 | Return &HFFF0F8FF 6 | End Get 7 | End Property 8 | 9 | Public Shared ReadOnly Property AntiqueWhite As Integer 10 | Get 11 | Return &HFFFAEBD7 12 | End Get 13 | End Property 14 | 15 | Public Shared ReadOnly Property Aqua As Integer 16 | Get 17 | Return &HFF00FFFF 18 | End Get 19 | End Property 20 | 21 | Public Shared ReadOnly Property Aquamarine As Integer 22 | Get 23 | Return &HFF7FFFD4 24 | End Get 25 | End Property 26 | 27 | Public Shared ReadOnly Property Azure As Integer 28 | Get 29 | Return &HFFF0FFFF 30 | End Get 31 | End Property 32 | 33 | Public Shared ReadOnly Property Beige As Integer 34 | Get 35 | Return &HFFF5F5DC 36 | End Get 37 | End Property 38 | 39 | Public Shared ReadOnly Property Bisque As Integer 40 | Get 41 | Return &HFFFFE4C4 42 | End Get 43 | End Property 44 | 45 | Public Shared ReadOnly Property Black As Integer 46 | Get 47 | Return &HFF000000 48 | End Get 49 | End Property 50 | 51 | Public Shared ReadOnly Property BlanchedAlmond As Integer 52 | Get 53 | Return &HFFFFEBCD 54 | End Get 55 | End Property 56 | 57 | Public Shared ReadOnly Property Blue As Integer 58 | Get 59 | Return &HFF0000FF 60 | End Get 61 | End Property 62 | 63 | Public Shared ReadOnly Property BlueViolet As Integer 64 | Get 65 | Return &HFF8A2BE2 66 | End Get 67 | End Property 68 | 69 | Public Shared ReadOnly Property Brown As Integer 70 | Get 71 | Return &HFFA52A2A 72 | End Get 73 | End Property 74 | 75 | Public Shared ReadOnly Property BurlyWood As Integer 76 | Get 77 | Return &HFFDEB887 78 | End Get 79 | End Property 80 | 81 | Public Shared ReadOnly Property CadetBlue As Integer 82 | Get 83 | Return &HFF5F9EA0 84 | End Get 85 | End Property 86 | 87 | Public Shared ReadOnly Property Chartreuse As Integer 88 | Get 89 | Return &HFF7FFF00 90 | End Get 91 | End Property 92 | 93 | Public Shared ReadOnly Property Chocolate As Integer 94 | Get 95 | Return &HFFD2691E 96 | End Get 97 | End Property 98 | 99 | Public Shared ReadOnly Property Coral As Integer 100 | Get 101 | Return &HFFFF7F50 102 | End Get 103 | End Property 104 | 105 | Public Shared ReadOnly Property CornflowerBlue As Integer 106 | Get 107 | Return &HFF6495ED 108 | End Get 109 | End Property 110 | 111 | Public Shared ReadOnly Property Cornsilk As Integer 112 | Get 113 | Return &HFFFFF8DC 114 | End Get 115 | End Property 116 | 117 | Public Shared ReadOnly Property Crimson As Integer 118 | Get 119 | Return &HFFDC143C 120 | End Get 121 | End Property 122 | 123 | Public Shared ReadOnly Property Cyan As Integer 124 | Get 125 | Return &HFF00FFFF 126 | End Get 127 | End Property 128 | 129 | Public Shared ReadOnly Property DarkBlue As Integer 130 | Get 131 | Return &HFF00008B 132 | End Get 133 | End Property 134 | 135 | Public Shared ReadOnly Property DarkCyan As Integer 136 | Get 137 | Return &HFF008B8B 138 | End Get 139 | End Property 140 | 141 | Public Shared ReadOnly Property DarkGoldenrod As Integer 142 | Get 143 | Return &HFFB8860B 144 | End Get 145 | End Property 146 | 147 | Public Shared ReadOnly Property DarkGray As Integer 148 | Get 149 | Return &HFFA9A9A9 150 | End Get 151 | End Property 152 | 153 | Public Shared ReadOnly Property DarkGreen As Integer 154 | Get 155 | Return &HFF006400 156 | End Get 157 | End Property 158 | 159 | Public Shared ReadOnly Property DarkKhaki As Integer 160 | Get 161 | Return &HFFBDB76B 162 | End Get 163 | End Property 164 | 165 | Public Shared ReadOnly Property DarkMagenta As Integer 166 | Get 167 | Return &HFF8B008B 168 | End Get 169 | End Property 170 | 171 | Public Shared ReadOnly Property DarkOliveGreen As Integer 172 | Get 173 | Return &HFF556B2F 174 | End Get 175 | End Property 176 | 177 | Public Shared ReadOnly Property DarkOrange As Integer 178 | Get 179 | Return &HFFFF8C00 180 | End Get 181 | End Property 182 | 183 | Public Shared ReadOnly Property DarkOrchid As Integer 184 | Get 185 | Return &HFF9932CC 186 | End Get 187 | End Property 188 | 189 | Public Shared ReadOnly Property DarkRed As Integer 190 | Get 191 | Return &HFF8B0000 192 | End Get 193 | End Property 194 | 195 | Public Shared ReadOnly Property DarkSalmon As Integer 196 | Get 197 | Return &HFFE9967A 198 | End Get 199 | End Property 200 | 201 | Public Shared ReadOnly Property DarkSeaGreen As Integer 202 | Get 203 | Return &HFF8FBC8F 204 | End Get 205 | End Property 206 | 207 | Public Shared ReadOnly Property DarkSlateBlue As Integer 208 | Get 209 | Return &HFF483D8B 210 | End Get 211 | End Property 212 | 213 | Public Shared ReadOnly Property DarkSlateGray As Integer 214 | Get 215 | Return &HFF2F4F4F 216 | End Get 217 | End Property 218 | 219 | Public Shared ReadOnly Property DarkTurquoise As Integer 220 | Get 221 | Return &HFF00CED1 222 | End Get 223 | End Property 224 | 225 | Public Shared ReadOnly Property DarkViolet As Integer 226 | Get 227 | Return &HFF9400D3 228 | End Get 229 | End Property 230 | 231 | Public Shared ReadOnly Property DeepPink As Integer 232 | Get 233 | Return &HFFFF1493 234 | End Get 235 | End Property 236 | 237 | Public Shared ReadOnly Property DeepSkyBlue As Integer 238 | Get 239 | Return &HFF00BFFF 240 | End Get 241 | End Property 242 | 243 | Public Shared ReadOnly Property DimGray As Integer 244 | Get 245 | Return &HFF696969 246 | End Get 247 | End Property 248 | 249 | Public Shared ReadOnly Property DodgerBlue As Integer 250 | Get 251 | Return &HFF1E90FF 252 | End Get 253 | End Property 254 | 255 | Public Shared ReadOnly Property Firebrick As Integer 256 | Get 257 | Return &HFFB22222 258 | End Get 259 | End Property 260 | 261 | Public Shared ReadOnly Property FloralWhite As Integer 262 | Get 263 | Return &HFFFFFAF0 264 | End Get 265 | End Property 266 | 267 | Public Shared ReadOnly Property ForestGreen As Integer 268 | Get 269 | Return &HFF228B22 270 | End Get 271 | End Property 272 | 273 | Public Shared ReadOnly Property Fuchsia As Integer 274 | Get 275 | Return &HFFFF00FF 276 | End Get 277 | End Property 278 | 279 | Public Shared ReadOnly Property Gainsboro As Integer 280 | Get 281 | Return &HFFDCDCDC 282 | End Get 283 | End Property 284 | 285 | Public Shared ReadOnly Property GhostWhite As Integer 286 | Get 287 | Return &HFFF8F8FF 288 | End Get 289 | End Property 290 | 291 | Public Shared ReadOnly Property Gold As Integer 292 | Get 293 | Return &HFFFFD700 294 | End Get 295 | End Property 296 | 297 | Public Shared ReadOnly Property Goldenrod As Integer 298 | Get 299 | Return &HFFDAA520 300 | End Get 301 | End Property 302 | 303 | Public Shared ReadOnly Property Gray As Integer 304 | Get 305 | Return &HFF808080 306 | End Get 307 | End Property 308 | 309 | Public Shared ReadOnly Property Green As Integer 310 | Get 311 | Return &HFF008000 312 | End Get 313 | End Property 314 | 315 | Public Shared ReadOnly Property GreenYellow As Integer 316 | Get 317 | Return &HFFADFF2F 318 | End Get 319 | End Property 320 | 321 | Public Shared ReadOnly Property Honeydew As Integer 322 | Get 323 | Return &HFFF0FFF0 324 | End Get 325 | End Property 326 | 327 | Public Shared ReadOnly Property HotPink As Integer 328 | Get 329 | Return &HFFFF69B4 330 | End Get 331 | End Property 332 | 333 | Public Shared ReadOnly Property IndianRed As Integer 334 | Get 335 | Return &HFFCD5C5C 336 | End Get 337 | End Property 338 | 339 | Public Shared ReadOnly Property Indigo As Integer 340 | Get 341 | Return &HFF4B0082 342 | End Get 343 | End Property 344 | 345 | Public Shared ReadOnly Property Ivory As Integer 346 | Get 347 | Return &HFFFFFFF0 348 | End Get 349 | End Property 350 | 351 | Public Shared ReadOnly Property Khaki As Integer 352 | Get 353 | Return &HFFF0E68C 354 | End Get 355 | End Property 356 | 357 | Public Shared ReadOnly Property Lavender As Integer 358 | Get 359 | Return &HFFE6E6FA 360 | End Get 361 | End Property 362 | 363 | Public Shared ReadOnly Property LavenderBlush As Integer 364 | Get 365 | Return &HFFFFF0F5 366 | End Get 367 | End Property 368 | 369 | Public Shared ReadOnly Property LawnGreen As Integer 370 | Get 371 | Return &HFF7CFC00 372 | End Get 373 | End Property 374 | 375 | Public Shared ReadOnly Property LemonChiffon As Integer 376 | Get 377 | Return &HFFFFFACD 378 | End Get 379 | End Property 380 | 381 | Public Shared ReadOnly Property LightBlue As Integer 382 | Get 383 | Return &HFFADD8E6 384 | End Get 385 | End Property 386 | 387 | Public Shared ReadOnly Property LightCoral As Integer 388 | Get 389 | Return &HFFF08080 390 | End Get 391 | End Property 392 | 393 | Public Shared ReadOnly Property LightCyan As Integer 394 | Get 395 | Return &HFFE0FFFF 396 | End Get 397 | End Property 398 | 399 | Public Shared ReadOnly Property LightGoldenrodYellow As Integer 400 | Get 401 | Return &HFFFAFAD2 402 | End Get 403 | End Property 404 | 405 | Public Shared ReadOnly Property LightGray As Integer 406 | Get 407 | Return &HFFD3D3D3 408 | End Get 409 | End Property 410 | 411 | Public Shared ReadOnly Property LightGreen As Integer 412 | Get 413 | Return &HFF90EE90 414 | End Get 415 | End Property 416 | 417 | Public Shared ReadOnly Property LightPink As Integer 418 | Get 419 | Return &HFFFFB6C1 420 | End Get 421 | End Property 422 | 423 | Public Shared ReadOnly Property LightSalmon As Integer 424 | Get 425 | Return &HFFFFA07A 426 | End Get 427 | End Property 428 | 429 | Public Shared ReadOnly Property LightSeaGreen As Integer 430 | Get 431 | Return &HFF20B2AA 432 | End Get 433 | End Property 434 | 435 | Public Shared ReadOnly Property LightSkyBlue As Integer 436 | Get 437 | Return &HFF87CEFA 438 | End Get 439 | End Property 440 | 441 | Public Shared ReadOnly Property LightSlateGray As Integer 442 | Get 443 | Return &HFF778899 444 | End Get 445 | End Property 446 | 447 | Public Shared ReadOnly Property LightSteelBlue As Integer 448 | Get 449 | Return &HFFB0C4DE 450 | End Get 451 | End Property 452 | 453 | Public Shared ReadOnly Property LightYellow As Integer 454 | Get 455 | Return &HFFFFFFE0 456 | End Get 457 | End Property 458 | 459 | Public Shared ReadOnly Property Lime As Integer 460 | Get 461 | Return &HFF00FF00 462 | End Get 463 | End Property 464 | 465 | Public Shared ReadOnly Property LimeGreen As Integer 466 | Get 467 | Return &HFF32CD32 468 | End Get 469 | End Property 470 | 471 | Public Shared ReadOnly Property Linen As Integer 472 | Get 473 | Return &HFFFAF0E6 474 | End Get 475 | End Property 476 | 477 | Public Shared ReadOnly Property Magenta As Integer 478 | Get 479 | Return &HFFFF00FF 480 | End Get 481 | End Property 482 | 483 | Public Shared ReadOnly Property Maroon As Integer 484 | Get 485 | Return &HFF800000 486 | End Get 487 | End Property 488 | 489 | Public Shared ReadOnly Property MediumAquamarine As Integer 490 | Get 491 | Return &HFF66CDAA 492 | End Get 493 | End Property 494 | 495 | Public Shared ReadOnly Property MediumBlue As Integer 496 | Get 497 | Return &HFF0000CD 498 | End Get 499 | End Property 500 | 501 | Public Shared ReadOnly Property MediumOrchid As Integer 502 | Get 503 | Return &HFFBA55D3 504 | End Get 505 | End Property 506 | 507 | Public Shared ReadOnly Property MediumPurple As Integer 508 | Get 509 | Return &HFF9370DB 510 | End Get 511 | End Property 512 | 513 | Public Shared ReadOnly Property MediumSeaGreen As Integer 514 | Get 515 | Return &HFF3CB371 516 | End Get 517 | End Property 518 | 519 | Public Shared ReadOnly Property MediumSlateBlue As Integer 520 | Get 521 | Return &HFF7B68EE 522 | End Get 523 | End Property 524 | 525 | Public Shared ReadOnly Property MediumSpringGreen As Integer 526 | Get 527 | Return &HFF00FA9A 528 | End Get 529 | End Property 530 | 531 | Public Shared ReadOnly Property MediumTurquoise As Integer 532 | Get 533 | Return &HFF48D1CC 534 | End Get 535 | End Property 536 | 537 | Public Shared ReadOnly Property MediumVioletRed As Integer 538 | Get 539 | Return &HFFC71585 540 | End Get 541 | End Property 542 | 543 | Public Shared ReadOnly Property MidnightBlue As Integer 544 | Get 545 | Return &HFF191970 546 | End Get 547 | End Property 548 | 549 | Public Shared ReadOnly Property MintCream As Integer 550 | Get 551 | Return &HFFF5FFFA 552 | End Get 553 | End Property 554 | 555 | Public Shared ReadOnly Property MistyRose As Integer 556 | Get 557 | Return &HFFFFE4E1 558 | End Get 559 | End Property 560 | 561 | Public Shared ReadOnly Property Moccasin As Integer 562 | Get 563 | Return &HFFFFE4B5 564 | End Get 565 | End Property 566 | 567 | Public Shared ReadOnly Property NavajoWhite As Integer 568 | Get 569 | Return &HFFFFDEAD 570 | End Get 571 | End Property 572 | 573 | Public Shared ReadOnly Property Navy As Integer 574 | Get 575 | Return &HFF000080 576 | End Get 577 | End Property 578 | 579 | Public Shared ReadOnly Property OldLace As Integer 580 | Get 581 | Return &HFFFDF5E6 582 | End Get 583 | End Property 584 | 585 | Public Shared ReadOnly Property Olive As Integer 586 | Get 587 | Return &HFF808000 588 | End Get 589 | End Property 590 | 591 | Public Shared ReadOnly Property OliveDrab As Integer 592 | Get 593 | Return &HFF6B8E23 594 | End Get 595 | End Property 596 | 597 | Public Shared ReadOnly Property Orange As Integer 598 | Get 599 | Return &HFFFFA500 600 | End Get 601 | End Property 602 | 603 | Public Shared ReadOnly Property OrangeRed As Integer 604 | Get 605 | Return &HFFFF4500 606 | End Get 607 | End Property 608 | 609 | Public Shared ReadOnly Property Orchid As Integer 610 | Get 611 | Return &HFFDA70D6 612 | End Get 613 | End Property 614 | 615 | Public Shared ReadOnly Property PaleGoldenrod As Integer 616 | Get 617 | Return &HFFEEE8AA 618 | End Get 619 | End Property 620 | 621 | Public Shared ReadOnly Property PaleGreen As Integer 622 | Get 623 | Return &HFF98FB98 624 | End Get 625 | End Property 626 | 627 | Public Shared ReadOnly Property PaleTurquoise As Integer 628 | Get 629 | Return &HFFAFEEEE 630 | End Get 631 | End Property 632 | 633 | Public Shared ReadOnly Property PaleVioletRed As Integer 634 | Get 635 | Return &HFFDB7093 636 | End Get 637 | End Property 638 | 639 | Public Shared ReadOnly Property PapayaWhip As Integer 640 | Get 641 | Return &HFFFFEFD5 642 | End Get 643 | End Property 644 | 645 | Public Shared ReadOnly Property PeachPuff As Integer 646 | Get 647 | Return &HFFFFDAB9 648 | End Get 649 | End Property 650 | 651 | Public Shared ReadOnly Property Peru As Integer 652 | Get 653 | Return &HFFCD853F 654 | End Get 655 | End Property 656 | 657 | Public Shared ReadOnly Property Pink As Integer 658 | Get 659 | Return &HFFFFC0CB 660 | End Get 661 | End Property 662 | 663 | Public Shared ReadOnly Property Plum As Integer 664 | Get 665 | Return &HFFDDA0DD 666 | End Get 667 | End Property 668 | 669 | Public Shared ReadOnly Property PowderBlue As Integer 670 | Get 671 | Return &HFFB0E0E6 672 | End Get 673 | End Property 674 | 675 | Public Shared ReadOnly Property Purple As Integer 676 | Get 677 | Return &HFF800080 678 | End Get 679 | End Property 680 | 681 | Public Shared ReadOnly Property Red As Integer 682 | Get 683 | Return &HFFFF0000 684 | End Get 685 | End Property 686 | 687 | Public Shared ReadOnly Property RosyBrown As Integer 688 | Get 689 | Return &HFFBC8F8F 690 | End Get 691 | End Property 692 | 693 | Public Shared ReadOnly Property RoyalBlue As Integer 694 | Get 695 | Return &HFF4169E1 696 | End Get 697 | End Property 698 | 699 | Public Shared ReadOnly Property SaddleBrown As Integer 700 | Get 701 | Return &HFF8B4513 702 | End Get 703 | End Property 704 | 705 | Public Shared ReadOnly Property Salmon As Integer 706 | Get 707 | Return &HFFFA8072 708 | End Get 709 | End Property 710 | 711 | Public Shared ReadOnly Property SandyBrown As Integer 712 | Get 713 | Return &HFFF4A460 714 | End Get 715 | End Property 716 | 717 | Public Shared ReadOnly Property SeaGreen As Integer 718 | Get 719 | Return &HFF2E8B57 720 | End Get 721 | End Property 722 | 723 | Public Shared ReadOnly Property SeaShell As Integer 724 | Get 725 | Return &HFFFFF5EE 726 | End Get 727 | End Property 728 | 729 | Public Shared ReadOnly Property Sienna As Integer 730 | Get 731 | Return &HFFA0522D 732 | End Get 733 | End Property 734 | 735 | Public Shared ReadOnly Property Silver As Integer 736 | Get 737 | Return &HFFC0C0C0 738 | End Get 739 | End Property 740 | 741 | Public Shared ReadOnly Property SkyBlue As Integer 742 | Get 743 | Return &HFF87CEEB 744 | End Get 745 | End Property 746 | 747 | Public Shared ReadOnly Property SlateBlue As Integer 748 | Get 749 | Return &HFF6A5ACD 750 | End Get 751 | End Property 752 | 753 | Public Shared ReadOnly Property SlateGray As Integer 754 | Get 755 | Return &HFF708090 756 | End Get 757 | End Property 758 | 759 | Public Shared ReadOnly Property Snow As Integer 760 | Get 761 | Return &HFFFFFAFA 762 | End Get 763 | End Property 764 | 765 | Public Shared ReadOnly Property SpringGreen As Integer 766 | Get 767 | Return &HFF00FF7F 768 | End Get 769 | End Property 770 | 771 | Public Shared ReadOnly Property SteelBlue As Integer 772 | Get 773 | Return &HFF4682B4 774 | End Get 775 | End Property 776 | 777 | Public Shared ReadOnly Property Tan As Integer 778 | Get 779 | Return &HFFD2B48C 780 | End Get 781 | End Property 782 | 783 | Public Shared ReadOnly Property Teal As Integer 784 | Get 785 | Return &HFF008080 786 | End Get 787 | End Property 788 | 789 | Public Shared ReadOnly Property Thistle As Integer 790 | Get 791 | Return &HFFD8BFD8 792 | End Get 793 | End Property 794 | 795 | Public Shared ReadOnly Property Tomato As Integer 796 | Get 797 | Return &HFFFF6347 798 | End Get 799 | End Property 800 | 801 | Public Shared ReadOnly Property Transparent As Integer 802 | Get 803 | Return &HFFFFFF 804 | End Get 805 | End Property 806 | 807 | Public Shared ReadOnly Property Turquoise As Integer 808 | Get 809 | Return &HFF40E0D0 810 | End Get 811 | End Property 812 | 813 | Public Shared ReadOnly Property Violet As Integer 814 | Get 815 | Return &HFFEE82EE 816 | End Get 817 | End Property 818 | 819 | Public Shared ReadOnly Property Wheat As Integer 820 | Get 821 | Return &HFFF5DEB3 822 | End Get 823 | End Property 824 | 825 | Public Shared ReadOnly Property White As Integer 826 | Get 827 | Return -1 828 | End Get 829 | End Property 830 | 831 | Public Shared ReadOnly Property WhiteSmoke As Integer 832 | Get 833 | Return &HFFF5F5F5 834 | End Get 835 | End Property 836 | 837 | Public Shared ReadOnly Property Yellow As Integer 838 | Get 839 | Return &HFFFFFF00 840 | End Get 841 | End Property 842 | 843 | Public Shared ReadOnly Property YellowGreen As Integer 844 | Get 845 | Return &HFF9ACD32 846 | End Get 847 | End Property 848 | 849 | End Class 850 | -------------------------------------------------------------------------------- /Document/DocumentCategories.vb: -------------------------------------------------------------------------------- 1 | Public Class DocumentCategories 2 | Public Const Rules = "Rules" 3 | Public Const Art = "Art" 4 | Public Const AI = "AI" 5 | Public Const Ra2 = "Ra2" 6 | End Class 7 | -------------------------------------------------------------------------------- /Document/DocumentLoadOptions.vb: -------------------------------------------------------------------------------- 1 | Public Enum DocumentLoadOptions 2 | Store 3 | SeparateBlocksOnly 4 | FullAnalysis 5 | End Enum 6 | -------------------------------------------------------------------------------- /Document/IniBlock.vb: -------------------------------------------------------------------------------- 1 | Namespace Document 2 | Public MustInherit Class IniBlock 3 | Public Property StartIndex As Integer 4 | Protected _Text$ = "" 5 | Public MustOverride Property Text As String 6 | Public ReadOnly Property Children As New List(Of IniSyntax) 7 | Sub New(Text$, StartIndex%) 8 | Me.Text = Text 9 | Me.StartIndex = StartIndex 10 | End Sub 11 | End Class 12 | End Namespace -------------------------------------------------------------------------------- /Document/IniCommentBlock.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | 3 | Namespace Document 4 | Public Class IniCommentBlock 5 | Inherits IniBlock 6 | Sub New(Text As String, StartIndex%) 7 | MyBase.New(Text, StartIndex) 8 | End Sub 9 | 10 | Public Overrides Property Text As String 11 | Get 12 | If CurrentLoadOption = DocumentLoadOptions.SeparateBlocksOnly Then 13 | Return _Text 14 | Else 15 | Return Children.JoinText(Function(s) s.Text) 16 | End If 17 | End Get 18 | Set 19 | If CurrentLoadOption = DocumentLoadOptions.SeparateBlocksOnly Then 20 | _Text = Value 21 | Children.Clear() 22 | Else 23 | If Children.Count = 0 Then 24 | Children.Add(New IniCommentSyntax(Value, 0)) 25 | Else 26 | Children.Item(0).Text = Value 27 | End If 28 | End If 29 | End Set 30 | End Property 31 | End Class 32 | 33 | End Namespace 34 | -------------------------------------------------------------------------------- /Document/IniCommentSyntax.vb: -------------------------------------------------------------------------------- 1 | Namespace Document 2 | Public Class IniCommentSyntax 3 | Inherits IniSyntax 4 | Sub New(Text As String, StartIndex%) 5 | MyBase.New(Text, StartIndex) 6 | End Sub 7 | 8 | Public Overrides Property Text As String 9 | Get 10 | Return Children.JoinText(Function(s) s.Text) 11 | End Get 12 | Set 13 | Select Case Children.Count 14 | Case 0 15 | Children.Add(New IniCommentSyntaxTrivia(Value, 0)) 16 | Case 1 17 | If Value.EndsWith(vbCrLf) Then 18 | Value = Value.Substring(0, Value.Length - 2) 19 | Children(0).Text = Value 20 | Children.Add(New IniNewlineSyntaxTrivia(Value.Length)) 21 | Else 22 | Children(0).Text = Value 23 | End If 24 | Case Else 25 | If Value.EndsWith(vbCrLf) Then 26 | Value = Value.Substring(0, Value.Length - 2) 27 | Children(0).Text = Value 28 | Else 29 | Children.RemoveAt(1) 30 | Children(0).Text = Value 31 | End If 32 | End Select 33 | End Set 34 | End Property 35 | End Class 36 | End Namespace 37 | -------------------------------------------------------------------------------- /Document/IniCommentSyntaxTrivia.vb: -------------------------------------------------------------------------------- 1 | Namespace Document 2 | Public Class IniCommentSyntaxTrivia 3 | Inherits IniSyntaxTrivia 4 | Public Overrides ReadOnly Property ColorARGB As Integer = CodeColors.ForestGreen 5 | Sub New(Text As String, StartIndex%) 6 | MyBase.New(Text, StartIndex) 7 | End Sub 8 | End Class 9 | End Namespace 10 | -------------------------------------------------------------------------------- /Document/IniControlCharacterSyntaxTrivia.vb: -------------------------------------------------------------------------------- 1 | Namespace Document 2 | Public Class IniControlCharacterSyntaxTrivia 3 | Inherits IniSyntaxTrivia 4 | 5 | Public Overrides ReadOnly Property ColorARGB As Integer = CodeColors.DarkRed 6 | Sub New(Text As String, StartIndex%) 7 | MyBase.New(Text, StartIndex) 8 | End Sub 9 | End Class 10 | End Namespace 11 | -------------------------------------------------------------------------------- /Document/IniDocument.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | 3 | Namespace Document 4 | Friend Module LoadSetting 5 | Public CurrentLoadOption As DocumentLoadOptions 6 | End Module 7 | ''' 8 | ''' 表示完整的Ini文档信息, 以便使用 Linq 对它进行分析。 9 | ''' 10 | Public Class IniDocument(Of TObservable As {IList(Of IniBlock), New}) 11 | Public Property Children As New TObservable 12 | Dim _Text$ = "" 13 | Sub New() 14 | CurrentLoadOption = DocumentLoadOptions.FullAnalysis 15 | End Sub 16 | Sub New(Text$, Optional LoadOption As DocumentLoadOptions = DocumentLoadOptions.FullAnalysis) 17 | Me.Text = Text 18 | CurrentLoadOption = LoadOption 19 | End Sub 20 | Public Overridable Property Text As String 21 | Get 22 | Select Case CurrentLoadOption 23 | Case DocumentLoadOptions.Store 24 | Return _Text 25 | Case Else 26 | Return Children.JoinLine(Function(s) s.Text) 27 | End Select 28 | End Get 29 | Set(value As String) 30 | Select Case CurrentLoadOption 31 | Case DocumentLoadOptions.Store 32 | _Text = value 33 | Children.Clear() 34 | Case Else 35 | ReloadBlocks(value) 36 | End Select 37 | End Set 38 | End Property 39 | Protected Function GetNewBlocks(Value$) As TObservable 40 | Dim NewBlocks As New TObservable 41 | Dim ln = Value.Split({vbCrLf}, StringSplitOptions.None) 42 | Dim StartIndexCounter = 0 43 | If ln.Length >= 1 Then 44 | Dim CurBlk As New StringBuilder 45 | Dim LastComment = Not ln(0).Trim.StartsWith("[") 46 | Dim BlockIsComment = LastComment 47 | CurBlk.AppendLine(ln(0)) 48 | For i = 1 To ln.Length - 1 49 | Dim lt = ln(i).Trim 50 | Dim CurIsRecordStart = lt.StartsWith("[") 51 | Dim CurComment = Not (CurIsRecordStart OrElse lt.Split(";"c)(0).Contains("=")) 52 | If BlockIsComment Then 53 | If Not CurComment Then 54 | If CurBlk.Length > 0 Then CurBlk.Remove(CurBlk.Length - 2, 2) 55 | NewBlocks.Add(New IniCommentBlock(CurBlk.ToString, StartIndexCounter)) 56 | StartIndexCounter += CurBlk.Length 57 | CurBlk.Clear() 58 | BlockIsComment = False 59 | End If 60 | CurBlk.AppendLine(ln(i)) 61 | Else 62 | If CurComment Then 63 | Dim j% 64 | For j = i + 1 To ln.Length - 1 65 | Dim ltp = ln(j).TrimStart 66 | Dim PeekIsRecordStart = ltp.StartsWith("[") 67 | Dim PeekIsRecord = ltp.Split(";"c)(0).Contains("=") 68 | If PeekIsRecordStart Then 69 | If CurBlk.Length > 0 Then CurBlk.Remove(CurBlk.Length - 2, 2) 70 | NewBlocks.Add(New IniRecordBlock(CurBlk.ToString, StartIndexCounter)) 71 | StartIndexCounter += CurBlk.Length 72 | CurBlk.Clear() 73 | CurBlk.AppendLine(ln(i)) 74 | BlockIsComment = True 75 | Exit For 76 | ElseIf PeekIsRecord Then 77 | CurBlk.AppendLine(ln(i)) 78 | Exit For 79 | End If 80 | Next 81 | If j = ln.Length Then 82 | CurBlk.AppendLine(ln(i)) 83 | End If 84 | Else 85 | CurBlk.AppendLine(ln(i)) 86 | End If 87 | End If 88 | Next 89 | If CurBlk.Length > 0 Then 90 | CurBlk.Remove(CurBlk.Length - 2, 2) 91 | StartIndexCounter += CurBlk.Length 92 | NewBlocks.Add(New IniRecordBlock(CurBlk.ToString, StartIndexCounter)) 93 | End If 94 | End If 95 | Return NewBlocks 96 | End Function 97 | Public Sub ReloadBlocks(Value$) 98 | Dim Blk = GetNewBlocks(Value) 99 | Children.ReloadContent(Blk, Function(s) s.Text) 100 | End Sub 101 | End Class 102 | End Namespace 103 | -------------------------------------------------------------------------------- /Document/IniKeyValuePairSyntax.vb: -------------------------------------------------------------------------------- 1 | Namespace Document 2 | Public Class IniKeyValuePairSyntax 3 | Inherits IniRecordSyntax 4 | Sub New(Text As String, StartIndex%) 5 | MyBase.New(Text, StartIndex) 6 | End Sub 7 | End Class 8 | End Namespace 9 | -------------------------------------------------------------------------------- /Document/IniMainKeySyntax.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | 3 | Namespace Document 4 | Public Class IniMainKeySyntax 5 | Inherits IniRecordSyntax 6 | Sub New(Text As String, StartIndex%) 7 | MyBase.New(Text, StartIndex) 8 | End Sub 9 | End Class 10 | End Namespace 11 | -------------------------------------------------------------------------------- /Document/IniNewlineSyntaxTrivia.vb: -------------------------------------------------------------------------------- 1 | Namespace Document 2 | Public Class IniNewlineSyntaxTrivia 3 | Inherits IniSyntaxTrivia 4 | 5 | Public Overrides ReadOnly Property ColorARGB As Integer = CodeColors.Black 6 | Sub New(StartIndex%) 7 | MyBase.New(vbCrLf, StartIndex) 8 | End Sub 9 | End Class 10 | End Namespace 11 | -------------------------------------------------------------------------------- /Document/IniRecordBlock.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | 3 | Namespace Document 4 | Public Class IniRecordBlock 5 | Inherits IniBlock 6 | 7 | Sub New(Text As String, StartIndex%) 8 | MyBase.New(Text, StartIndex) 9 | End Sub 10 | 11 | Public Overrides Property Text As String 12 | Get 13 | If CurrentLoadOption = DocumentLoadOptions.SeparateBlocksOnly Then 14 | Return _Text 15 | Else 16 | Return Children.JoinText(Function(s) s.Text) 17 | End If 18 | End Get 19 | Set 20 | If CurrentLoadOption = DocumentLoadOptions.SeparateBlocksOnly Then 21 | _Text = Value 22 | Children.Clear() 23 | Else 24 | Dim Children As New List(Of IniSyntax) 25 | Dim NewBlocks = Value.Split({vbCrLf}, StringSplitOptions.None) 26 | Dim Position% = 0 27 | If NewBlocks.Count > 0 Then 28 | Dim FirstBlk = NewBlocks(0) 29 | Dim MainKeyPart = FirstBlk.ExceptComments 30 | Dim TrimedMKPart = MainKeyPart.Trim 31 | If NewBlocks.Count > 1 Then 32 | FirstBlk &= vbCrLf 33 | End If 34 | If TrimedMKPart.StartsWith("[") AndAlso TrimedMKPart.EndsWith("]") Then 35 | Children.Add(New IniMainKeySyntax(FirstBlk, 0)) 36 | Else 37 | Children.Add(New IniWrongSyntax(FirstBlk, 0, "主键没有']'")) 38 | End If 39 | Position += FirstBlk.Length 40 | For i = 1 To NewBlocks.Length - 1 41 | Dim CurBlk = NewBlocks(i) 42 | Dim MainPart = CurBlk.ExceptComments 43 | If i < NewBlocks.Length - 1 Then 44 | CurBlk &= vbCrLf 45 | End If 46 | If MainPart.Contains("=") Then 47 | Children.Add(New IniKeyValuePairSyntax(CurBlk, Position)) 48 | ElseIf MainPart.TrimStart.StartsWith(";") 49 | Children.Add(New IniCommentSyntax(CurBlk, Position)) 50 | Else 51 | Children.Add(New IniWrongSyntax(CurBlk, Position, "注释没有;")) 52 | End If 53 | Position += CurBlk.Length 54 | Next 55 | End If 56 | Me.Children.ReloadContent(Children, Function(s) s.Text) 57 | End If 58 | End Set 59 | End Property 60 | End Class 61 | End Namespace 62 | -------------------------------------------------------------------------------- /Document/IniRecordSyntax.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | 3 | Namespace Document 4 | Public Class IniRecordSyntax 5 | Inherits IniSyntax 6 | Sub New(Text As String, StartIndex%) 7 | MyBase.New(Text, StartIndex) 8 | End Sub 9 | 10 | Public Overrides Property Text As String 11 | Get 12 | Return Children.JoinText(Function(s) s.Text) 13 | End Get 14 | Set 15 | Dim IsWhiteSpace = False 16 | Dim sb As New StringBuilder 17 | Dim i% 18 | Dim NonComment = Value.ExceptComments 19 | Dim Comment = If(NonComment.Length = Value.Length, "", Value.Substring(NonComment.Length)) 20 | For i = 0 To NonComment.Length - 2 21 | Dim ch = NonComment(i) 22 | If Char.IsSymbol(ch) OrElse Char.IsPunctuation(ch) Then 23 | If sb.Length > 0 Then 24 | If IsWhiteSpace Then 25 | Children.Add(New IniWhitepaceSyntaxTrivia(sb.ToString, i - sb.Length)) 26 | Else 27 | Children.Add(New IniWordSyntaxTrivia(sb.ToString, i - sb.Length)) 28 | End If 29 | sb.Clear() 30 | End If 31 | Children.Add(New IniControlCharacterSyntaxTrivia(ch.ToString, i)) 32 | ElseIf ch = vbCr 33 | ElseIf ch = vbLf 34 | Children.Add(New IniNewlineSyntaxTrivia(i - 1)) 35 | ElseIf Char.IsWhiteSpace(ch) 36 | If sb.Length > 0 AndAlso Not IsWhiteSpace Then 37 | Children.Add(New IniWordSyntaxTrivia(sb.ToString, i - sb.Length)) 38 | sb.Clear() 39 | End If 40 | IsWhiteSpace = True 41 | sb.Append(ch) 42 | Else 43 | If sb.Length > 0 AndAlso IsWhiteSpace Then 44 | Children.Add(New IniWhitepaceSyntaxTrivia(sb.ToString, i - sb.Length)) 45 | sb.Clear() 46 | End If 47 | sb.Append(ch) 48 | IsWhiteSpace = False 49 | End If 50 | Next 51 | i = NonComment.Length - 1 52 | If Not String.IsNullOrWhiteSpace(NonComment(i)) Then 53 | sb.Append(NonComment(i)) 54 | End If 55 | If sb.Length > 0 Then 56 | If IsWhiteSpace Then 57 | Children.Add(New IniWhitepaceSyntaxTrivia(sb.ToString, i - sb.Length)) 58 | sb.Clear() 59 | Else 60 | Children.Add(New IniWordSyntaxTrivia(sb.ToString, i - sb.Length)) 61 | sb.Clear() 62 | End If 63 | End If 64 | If i > 0 AndAlso Value(i) = vbLf Then 65 | Children.Add(New IniNewlineSyntaxTrivia(i - 1)) 66 | End If 67 | If Not String.IsNullOrEmpty(Comment) Then 68 | Children.Add(New IniCommentSyntaxTrivia(Comment, NonComment.Length)) 69 | End If 70 | End Set 71 | End Property 72 | End Class 73 | End Namespace 74 | -------------------------------------------------------------------------------- /Document/IniSyntax.vb: -------------------------------------------------------------------------------- 1 | Namespace Document 2 | Public MustInherit Class IniSyntax 3 | Public Property StartIndex As Integer 4 | Public MustOverride Property Text As String 5 | Public ReadOnly Property Children As New List(Of IniSyntaxTrivia) 6 | Sub New(Text$, StartIndex%) 7 | Me.Text = Text 8 | Me.StartIndex = StartIndex 9 | End Sub 10 | End Class 11 | End Namespace 12 | -------------------------------------------------------------------------------- /Document/IniSyntaxTrivia.vb: -------------------------------------------------------------------------------- 1 | Namespace Document 2 | Public MustInherit Class IniSyntaxTrivia 3 | Public Property StartIndex As Integer 4 | Public Property Text As String 5 | Public MustOverride ReadOnly Property ColorARGB As Integer 6 | Sub New(Text$, StartIndex%) 7 | Me.Text = Text 8 | Me.StartIndex = StartIndex 9 | End Sub 10 | End Class 11 | End Namespace 12 | -------------------------------------------------------------------------------- /Document/IniWhitespaceSyntaxTrivia.vb: -------------------------------------------------------------------------------- 1 | Namespace Document 2 | Public Class IniWhitepaceSyntaxTrivia 3 | Inherits IniSyntaxTrivia 4 | 5 | Public Overrides ReadOnly Property ColorARGB As Integer = CodeColors.Black 6 | Sub New(Text As String, StartIndex%) 7 | MyBase.New(Text, StartIndex) 8 | End Sub 9 | End Class 10 | End Namespace 11 | -------------------------------------------------------------------------------- /Document/IniWordSyntaxTrivia.vb: -------------------------------------------------------------------------------- 1 | Namespace Document 2 | Public Class IniWordSyntaxTrivia 3 | Inherits IniSyntaxTrivia 4 | Public Overrides ReadOnly Property ColorARGB As Integer = CodeColors.Black 5 | Sub New(Text As String, StartIndex%) 6 | MyBase.New(Text, StartIndex) 7 | End Sub 8 | End Class 9 | End Namespace 10 | -------------------------------------------------------------------------------- /Document/IniWrongSyntax.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | 3 | Namespace Document 4 | Public Class IniWrongSyntax 5 | Inherits IniSyntax 6 | Public ReadOnly Property Description$ 7 | 8 | Public Overrides Property Text As String 9 | Get 10 | Return Children.JoinText(Function(s) s.Text) 11 | End Get 12 | Set 13 | If Children.Count = 0 Then 14 | Children.Add(New IniWrongSyntaxTrivia(Value, 0)) 15 | Else 16 | Children(0).Text = Value 17 | End If 18 | End Set 19 | End Property 20 | 21 | Sub New(Text As String, StartIndex%, Description$) 22 | MyBase.New(Text, StartIndex) 23 | Me.Description = Description 24 | End Sub 25 | End Class 26 | End Namespace 27 | -------------------------------------------------------------------------------- /Document/IniWrongSyntaxTrivia.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | 3 | Namespace Document 4 | Public Class IniWrongSyntaxTrivia 5 | Inherits IniSyntaxTrivia 6 | Sub New(Text As String, StartIndex%) 7 | MyBase.New(Text, StartIndex) 8 | End Sub 9 | Public Overrides ReadOnly Property ColorARGB As Integer = CodeColors.Red 10 | End Class 11 | End Namespace 12 | 13 | -------------------------------------------------------------------------------- /Entities/EntityInferContext.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | Imports Nukepayload2.CodeAnalysis 3 | Imports Nukepayload2.Ra2CodeAnalysis.AnalysisHelper 4 | 5 | Public Class EntityInferContext 6 | Sub New(analyzer As NamedIniAnalyzer, helpProvider As HelpProvider, namespaceBuilder As IndentStringBuilder) 7 | NamedAnalyzer = analyzer 8 | Me.NamespaceBuilder = New VBNamespaceBuilder(namespaceBuilder, analyzer.FileNameWithoutExt) 9 | Me.HelpProvider = helpProvider 10 | ProcessData() 11 | End Sub 12 | 13 | Protected Sub ProcessData() 14 | FillDataHead() 15 | InferNewInterface() 16 | InferPossibleBaseClass() 17 | CleanInterface() 18 | MergePossibleBaseClass() 19 | CleanClass() 20 | CorrectMemberName() 21 | End Sub 22 | ''' 23 | ''' 清除与基类重复的声明,同时更新类型推断和数据 24 | ''' 25 | Private Sub CleanClass() 26 | For Each cls In ClassIndex.Values 27 | Dim baseClass = cls.InheritsClass 28 | Dim curProperties = cls.Properties 29 | If baseClass IsNot Nothing Then 30 | Dim baseProperties = baseClass.Properties 31 | If cls.ImplementInterfaces.Count > 0 Then 32 | For Each impl In cls.ImplementInterfaces 33 | baseClass.ImplementInterfaces.Add(impl) 34 | For Each prop In impl.Properties 35 | baseClass.Properties(prop.Key).ImplementsInterface.Add(impl) 36 | Next 37 | Next 38 | cls.ImplementInterfaces.Clear() 39 | End If 40 | For Each prop In baseProperties 41 | Dim curKey = prop.Key 42 | If curProperties.ContainsKey(curKey) Then 43 | Dim curProp = curProperties(curKey) 44 | Dim basicInformation = curProp.BasicInformation 45 | Dim typeNameOverride = basicInformation.TypeNameOverride 46 | '纠正类型推断 47 | If typeNameOverride IsNot Nothing Then 48 | baseProperties(curKey).BasicInformation.TypeNameOverride = typeNameOverride 49 | Dim initExpr = Aggregate tp In cls.BasePropertyInitialization 50 | Where tp.PropertyBasicInformation.Name = basicInformation.Name 51 | Into FirstOrDefault 52 | If initExpr IsNot Nothing Then 53 | Dim init = initExpr.InitialValue 54 | If init.StartsWith("""") AndAlso init.EndsWith("""") Then 55 | initExpr.InitialValue = init.Substring(1, init.Length - 2) 56 | If initExpr.InitialValue.Contains(",") Then 57 | initExpr.PropertyBasicInformation.IsQueryable = True 58 | ElseIf ClassIndex.ContainsKey(initExpr.InitialValue) Then 59 | Dim refCls = ClassIndex(initExpr.InitialValue) 60 | If refCls Is cls Then 61 | initExpr.InitialValue = init 62 | initExpr.PropertyBasicInformation.TypeNameOverride = Nothing 63 | Else 64 | initExpr.PropertyBasicInformation.TypeNameOverride = refCls 65 | End If 66 | End If 67 | Else 68 | initExpr.InitialValue = $"{NamespaceBuilder.Name}Context.{initExpr.PropertyBasicInformation.RuntimeTypeName}.Find({init})" 69 | End If 70 | End If 71 | End If 72 | '删除多余的属性定义 73 | curProperties.Remove(curKey) 74 | Else 75 | If prop.Value.IsPrimaryKey Then 76 | Dim baseProp = prop.Value 77 | cls.BasePropertyInitialization.Add(New VBPropertyAssignmentDeclaration(baseProp.BasicInformation, SurroundInitExpr(cls.Name, "String"))) 78 | End If 79 | End If 80 | Next 81 | Else 82 | Dim vals = NamedAnalyzer.Analyzer.Values 83 | If vals.ContainsKey(cls.Name) Then 84 | Dim clsData = vals(cls.Name) 85 | For Each curProp In curProperties 86 | Dim data = clsData(curProp.Key) 87 | curProp.Value.InitialValue = SurroundInitExpr(data.Item1, curProp.Value.BasicInformation.RuntimeTypeName) 88 | Next 89 | End If 90 | End If 91 | Next 92 | End Sub 93 | 94 | Private Sub CorrectMemberName() 95 | For Each itf In InterfaceIndex.Values 96 | itf.Name = RenameForVBName(itf.Name) 97 | Dim newProps As New Dictionary(Of String, VBPropertyDeclarationSilm) 98 | For Each prop In itf.Properties.Values 99 | prop.Name = RenameForVBName(prop.Name) 100 | newProps.Add(prop.Name, prop) 101 | Next 102 | itf.Properties = newProps 103 | Next 104 | For Each cls In ClassIndex.Values 105 | cls.Name = RenameForVBName(cls.Name) 106 | Dim newProps As New Dictionary(Of String, VBPropertyDeclaration) 107 | For Each prop In cls.Properties.Values 108 | prop.BasicInformation.Name = RenameForVBName(prop.BasicInformation.Name) 109 | newProps.Add(prop.BasicInformation.Name, prop) 110 | Next 111 | cls.Properties = newProps 112 | Next 113 | 114 | End Sub 115 | 116 | Private Shared Function RenameForVBName(name As String) As String 117 | If Not String.IsNullOrEmpty(name) Then 118 | If Char.IsNumber(name(0)) Then 119 | name = "_" + name 120 | End If 121 | name = name.Replace("-"c, "_"c).Replace(" "c, "_") 122 | End If 123 | If VBKeyWordTranslator.KeywordTable.ContainsKey(name) Then 124 | name = "[" + name + "]" 125 | End If 126 | Return name 127 | End Function 128 | 129 | Private Sub MergePossibleBaseClass() 130 | For Each curCls In ClassIndex.Values 131 | Dim implementInterfaces = curCls.ImplementInterfaces 132 | If implementInterfaces.Count > 0 Then 133 | Dim firstInterface = implementInterfaces.First 134 | Dim firstClass = firstInterface.PossibleBaseClass 135 | If implementInterfaces.Count > 1 Then 136 | For Each impl In implementInterfaces.Skip(1) 137 | Dim mergeClass = impl.PossibleBaseClass 138 | Dim firstProperties = firstClass.Properties 139 | For Each newProp In From p In mergeClass.Properties Where Not firstProperties.ContainsKey(p.Key) 140 | Dim prop = newProp.Value 141 | prop.ImplementsInterface.Add(firstInterface) 142 | firstProperties.Add(newProp.Key, prop) 143 | Next 144 | impl.PossibleBaseClass = firstClass 145 | Next 146 | End If 147 | curCls.InheritsClass = firstClass 148 | End If 149 | Next 150 | End Sub 151 | ''' 152 | ''' 通过值提取接口 153 | ''' 154 | Private Sub InferNewInterface() 155 | For Each cls In ClassData 156 | Dim cdata = cls.Item2 157 | For Each kv In cdata 158 | Dim key = kv.Key 159 | Dim values = kv.Value.Item1 160 | Dim itf As VBInterfaceBuilder = Nothing 161 | Dim valueArray = values.Split(","c) 162 | For Each value In valueArray 163 | value = value.Trim 164 | If ClassIndex.ContainsKey(value) Then 165 | Dim refCls = ClassIndex(value) 166 | If InterfaceIndex.ContainsKey("I" + key) Then 167 | itf = InterfaceIndex("I" + key) 168 | Else 169 | itf = New VBInterfaceBuilder(NamespaceBuilder, key, Indent) 170 | AddInterface(itf) 171 | If Not refCls.ImplementInterfaces.Contains(itf) Then 172 | ImplementInterface(refCls, itf) 173 | End If 174 | End If 175 | FillInterfaceImplInformation(refCls, itf) 176 | End If 177 | Next 178 | Dim helptext = TrimHelp(HelpProvider.GetHelpText(key)) 179 | cls.Item1.Properties.Add(key, New VBPropertyDeclaration(NamespaceBuilder.BaseStringBuilder, helptext, helptext.Contains("<已"), New VBPropertyDeclarationSilm(key, HelpProvider.TempAnalizeUsage(values)) With {.TypeNameOverride = itf, .IsQueryable = valueArray.Length > 1}, Nothing, False)) 180 | Next 181 | Next 182 | End Sub 183 | 184 | Private Shared Function TrimHelp(helptext As String) As String 185 | If helptext IsNot Nothing AndAlso helptext.Contains("用法") Then helptext = helptext.Substring(0, helptext.IndexOf("用法")) 186 | Return helptext 187 | End Function 188 | 189 | Private Sub ImplementInterface(curCls As VBClassBuilder, itf As VBInterfaceBuilder) 190 | If Aggregate i In curCls.ImplementInterfaces Select i.Name = itf.Name Into Any Then 191 | Return 192 | End If 193 | curCls.ImplementInterfaces.Add(itf) 194 | InterfaceImplementationIndex(itf).Add(curCls) 195 | End Sub 196 | 197 | ''' 198 | ''' 删除接口中冗余的部分 199 | ''' 200 | Private Sub CleanInterface() 201 | For Each curItf In InterfaceIndex.Values 202 | For Each curImpl In InterfaceImplementationIndex(curItf) 203 | Dim dels = Aggregate prop In curItf.Properties.Values Where Not curImpl.Properties.ContainsKey(prop.Name) Into ToArray 204 | For i = dels.Count - 1 To 0 Step -1 205 | Dim curDel = dels(i) 206 | If curItf.Properties.ContainsKey(curDel.Name) Then 207 | curItf.Properties.Remove(curDel.Name) 208 | End If 209 | curItf.PossibleBaseClass.Properties(curDel.Name).ImplementsInterface.Clear() 210 | Next 211 | Next 212 | Next 213 | End Sub 214 | 215 | ''' 216 | ''' 在类添加实现的接口, 向接口和可能的基类填充初步推断了类型的数据, 向数据类填充数据。 217 | ''' 218 | Private Sub InferPossibleBaseClass() 219 | For Each itf In InterfaceData 220 | Dim curItf = itf.Item1 221 | Dim implList = InterfaceImplementationIndex(curItf) 222 | Dim curBase = curItf.PossibleBaseClass 223 | ClassIndex.Add(curBase.Name, curBase) 224 | '可能的基类的主键 225 | Dim itfName = curItf.Name 226 | Dim pkSilm As New VBPropertyDeclarationSilm(itfName + "Id", "String") 227 | Dim pk As New VBPropertyDeclaration(NamespaceBuilder.BaseStringBuilder, $"用于在Ini中索引{itfName}数据", False, pkSilm, Nothing, True) 228 | curBase.Properties.Add(pkSilm.Name, pk) 229 | If itf.Item2 Is Nothing Then Continue For 230 | '查阅接口记录注册了哪些类 231 | For Each clsName In From l In itf.Item2 Select l.Value.Item1 232 | If ClassIndex.ContainsKey(clsName) Then 233 | Dim curCls = ClassIndex(clsName) 234 | If Not Aggregate i In curCls.ImplementInterfaces Where i.Name = curItf.Name Into Any Then 235 | '让数据类实现接口 236 | ImplementInterface(curCls, curItf) 237 | '从数据类提取信息 238 | FillInterfaceImplInformation(curCls, curItf) 239 | End If 240 | End If 241 | Next 242 | Next 243 | End Sub 244 | 245 | Private Sub FillInterfaceImplInformation(registeredClass As VBClassBuilder, curItf As VBInterfaceBuilder) 246 | Dim curBase = curItf.PossibleBaseClass 247 | For Each line In NamedAnalyzer.Analyzer.Values(registeredClass.Name) 248 | Dim key = line.Key 249 | If Not curBase.Properties.ContainsKey(key) Then 250 | Dim declSilm = New VBPropertyDeclarationSilm(key, HelpProvider.TempAnalizeUsage(line.Value.Item1)) 251 | Dim helpText = TrimHelp(HelpProvider.GetHelpText(key)) 252 | '向数据类增加初始值 253 | Dim initValue = line.Value.Item1 254 | Dim typeName = declSilm.TypeName 255 | initValue = SurroundInitExpr(initValue, typeName) 256 | registeredClass.BasePropertyInitialization.Add(New VBPropertyAssignmentDeclaration(declSilm, initValue)) 257 | '向接口添加冗余的临时属性 258 | curItf.Properties.Add(key, declSilm) 259 | '向可能的基类添加属性 260 | Dim decl As New VBPropertyDeclaration(NamespaceBuilder.BaseStringBuilder, helpText, helpText.Contains("<已"), declSilm, Nothing, False) 261 | curBase.Properties.Add(key, decl) 262 | End If 263 | Next 264 | End Sub 265 | 266 | Private Shared Function SurroundInitExpr(initValue As String, typeName As String) As String 267 | Const enumerStr As String = "IEnumerable" 268 | If typeName.StartsWith(enumerStr) Then 269 | Const ofStr As String = "(Of " 270 | Dim ofIdx = typeName.IndexOf(ofStr) 271 | If ofIdx > 0 AndAlso typeName.EndsWith(")") Then 272 | Dim innerTypeName = typeName.Substring(ofIdx + ofStr.Length, typeName.Length - enumerStr.Length - ofStr.Length - 1) 273 | If innerTypeName.Length > 0 Then 274 | Dim sb As New StringBuilder("{") 275 | For Each value In initValue.Split(","c) 276 | sb.Append(SurroundInitExpr(value, innerTypeName)).Append(", ") 277 | Next 278 | sb.Remove(sb.Length - 2, 2).Append("}") 279 | Return sb.ToString 280 | End If 281 | End If 282 | Return $"{{{initValue}}}" 283 | End If 284 | Select Case typeName 285 | Case "String" 286 | Return """" + initValue + """" 287 | Case "Guid" 288 | Return $"New {typeName}(""{initValue}"")" 289 | Case "Percentage", "BigInteger" 290 | Return $"{typeName}.Parse(""{initValue}"")" 291 | Case "Single" 292 | Return initValue & "F" 293 | Case "Long" 294 | Return initValue & "L" 295 | Case "ULong" 296 | Return initValue & "UL" 297 | Case "Decimal" 298 | Return initValue & "D" 299 | Case Else 300 | Return initValue 301 | End Select 302 | End Function 303 | 304 | ''' 305 | ''' 将ini数据整理, 仅填充头部数据和索引, 不填充属性 306 | ''' 307 | Private Sub FillDataHead() 308 | For Each k In NamedAnalyzer.Analyzer.Values 309 | If k.Value.All(Function(rec) rec.Key.IsInteger) Then 310 | Dim itfb As New VBInterfaceBuilder(NamespaceBuilder, k.Key, Indent) 311 | AddInterface(itfb, k) 312 | Else 313 | Dim clsb = New VBClassBuilder(NamespaceBuilder, k.Key, Indent) 314 | ClassData.Add(New Tuple(Of VBClassBuilder, Dictionary(Of String, Tuple(Of String, Integer)))(clsb, k.Value)) 315 | ClassIndex.Add(k.Key, clsb) 316 | End If 317 | Next 318 | End Sub 319 | 320 | Private Sub AddInterface(itfb As VBInterfaceBuilder, Optional data As KeyValuePair(Of String, Dictionary(Of String, Tuple(Of String, Integer))) = Nothing) 321 | InterfaceIndex.Add(itfb.Name, itfb) 322 | InterfaceImplementationIndex.Add(itfb, New List(Of VBClassBuilder)) 323 | InterfaceData.Add(New Tuple(Of VBInterfaceBuilder, Dictionary(Of String, Tuple(Of String, Integer)))(itfb, data.Value)) 324 | End Sub 325 | 326 | Public ReadOnly Property HelpProvider As HelpProvider 327 | Public ReadOnly Property NamespaceBuilder As VBNamespaceBuilder 328 | Public ReadOnly Property Text As New StringBuilder 329 | Public ReadOnly Property Indent As New StrongBox(Of Integer)(0) 330 | Public ReadOnly Property NamedAnalyzer As NamedIniAnalyzer 331 | Public ReadOnly Property InterfaceData As New List(Of Tuple(Of VBInterfaceBuilder, Dictionary(Of String, Tuple(Of String, Integer)))) 332 | Public ReadOnly Property InterfaceIndex As New Dictionary(Of String, VBInterfaceBuilder) 333 | Public ReadOnly Property InterfaceImplementationIndex As New Dictionary(Of VBInterfaceBuilder, List(Of VBClassBuilder)) 334 | Public ReadOnly Property ClassData As New List(Of Tuple(Of VBClassBuilder, Dictionary(Of String, Tuple(Of String, Integer)))) 335 | Public ReadOnly Property ClassIndex As New Dictionary(Of String, VBClassBuilder) 336 | ''' 337 | ''' 从另一个实体信息推断上下文完善推断 338 | ''' 339 | Public Sub Infer(another As EntityInferContext) 340 | Throw New NotImplementedException 341 | End Sub 342 | End Class -------------------------------------------------------------------------------- /Entities/IniFileItem.vb: -------------------------------------------------------------------------------- 1 | Public Class IniFileItem 2 | Sub New(filePath As String) 3 | Me.FilePath = filePath 4 | FileName = Path.GetFileNameWithoutExtension(filePath) 5 | End Sub 6 | 7 | Public Property FilePath As String 8 | Public Property FileName As String 9 | Public Property CachedContent As String 10 | Public Property CachedAnalyzer As NamedIniAnalyzer 11 | End Class 12 | -------------------------------------------------------------------------------- /Entities/NamedIniAnalyzer.vb: -------------------------------------------------------------------------------- 1 | Public Class NamedIniAnalyzer 2 | Sub New(fileNameWithoutExt As String, analyzer As INIAnalyzer) 3 | Me.FileNameWithoutExt = fileNameWithoutExt 4 | Me.Analyzer = analyzer 5 | End Sub 6 | 7 | Public Property FileNameWithoutExt As String 8 | Public Property Analyzer As INIAnalyzer 9 | End Class 10 | -------------------------------------------------------------------------------- /Entities/VBProjectWriter.vb: -------------------------------------------------------------------------------- 1 | Imports System.Reflection 2 | Imports System.Text 3 | Imports Nukepayload2.CodeAnalysis 4 | Imports Nukepayload2.CodeAnalysis.Linq 5 | 6 | Public Module VBProjectWriterExtension 7 | 8 | 9 | Public Function GenerateVBFileFromIni(d As NamedIniAnalyzer) As GeneratedCodeFile 10 | Dim hlp As HelpProvider = New EmptyHelpProvider 11 | Select Case d.FileNameWithoutExt.ToLower 12 | Case "rules", "rulesmd" 13 | hlp = New RulesHelpProvider 14 | Case "art", "artmd" 15 | hlp = New ArtHelpProvider 16 | Case "ai", "aimd" 17 | hlp = New AIHelpProvider 18 | End Select 19 | Dim sb As New IndentStringBuilder 20 | Dim entityContext As New EntityInferContext(d, hlp, sb) 21 | Dim ns As New VBNamespaceBuilder(sb, d.FileNameWithoutExt) 22 | ns.BeginBlock() 23 | For Each itf In entityContext.InterfaceIndex.Values 24 | itf.BeginBlock() 25 | itf.EndBlock() 26 | Next 27 | For Each cls In entityContext.ClassIndex.Values 28 | cls.BeginBlock() 29 | cls.EndBlock() 30 | Next 31 | ns.EndBlock() 32 | Return New GeneratedCodeFile(d.FileNameWithoutExt + ".vb", sb.ToString) 33 | End Function 34 | End Module -------------------------------------------------------------------------------- /Help/AIHelpProvider.vb: -------------------------------------------------------------------------------- 1 |  2 | Public Class AIHelpProvider 3 | Inherits HelpProvider 4 | 5 | Public Overrides Function GetHelpText(code As String) As String 6 | Return GetHelpTextWithUsage(code, AIHelp, AITypes) 7 | End Function 8 | 9 | Public Shared AIHelp As New Dictionary(Of String, String) From {{"VeteranLevel", "<已过时> 地图文件中指定此小队出现在地图时的兵种等级"}, 10 | {"MindControlDecision", "<不明确> 遭心灵控制后的动作,0自动,1加入控制者作战小队,2送入部队回收厂,3送入生化反应炉,4搜索敌人,5什么也不做"}, 11 | {"Loadable", "<不明确> 可装载"}, 12 | {"Full", "<不明确> <已过时> 创建小队成员的时候,如果小队中有运载工具,其他成员是否应该位于运载工具的内部"}, 13 | {"Annoyance", "<不明确> 烦恼效果,作用不明"}, 14 | {"GuardSlower", "<不明确> 降低反应速度,作用不明"}, 15 | {"Recruiter", "强制重组优先级较低的小队的成员,如果它们是可以重组的"}, 16 | {"Autocreate", "<不明确> 自动建造"}, 17 | {"Prebuild", "<不明确> 预先建造,作用不明"}, 18 | {"Reinforce", "<不明确> <已过时> 增援部队"}, 19 | {"Droppod", "<已过时> 地图文件中指定使用空降进入战区"}, 20 | {"UseTransportOrigin", "<不明确> 使用原始的运载工具"}, 21 | {"Whiner", "<不明确> 哀叫效果,作用不明"}, 22 | {"LooseRecruit", "<不明确> 解散新兵,作用不明"}, 23 | {"Aggressive", "侵略性的,电脑在防御达到一定数量之后再开始建造该小队"}, 24 | {"Suicide", "自毁式攻击,受到攻击不允许还击"}, 25 | {"Priority", "优先级"}, 26 | {"Max", "同一作战方至多允许同时存在的该类型小队个数"}, 27 | {"TechLevel", "<不明确> <已过时> 科技等级,疑似无效"}, 28 | {"OnTransOnly", "<不明确> 只能运输,作用不明"}, 29 | {"AvoidThreats", "规避威胁,小队成员在移动的过程中将会试图规避途中可能遭遇的敌方单位"}, 30 | {"IonImmune", "<已过时> 离子风暴发生时仍然建造"}, 31 | {"TransportsReturnOnUnload", "运载工具卸载后返回基地"}, 32 | {"AreTeamMembersRecruitable", "小队成员是否允许重组"}, 33 | {"IsBaseDefense", "基地防御小队"}, 34 | {"OnlyTargetHouseEnemy", "<不明确> 只攻击敌方单位,作用不明"}, 35 | {"Script", "指定脚本"}, {"House", "指定国家"}, {"TaskForce", "指定特遣小队"}, 36 | {"Group", "指定分组,对阵营使用的AI触发一般填-1"}, {"Name", "此主键的注释"} 37 | } 38 | 39 | Private Shared AITypes As New Dictionary(Of String, String) From {{"Name", "String"}, 40 | {"Group", "Integer"}, 41 | {"VeteranLevel", "Boolean"}, 42 | {"MindControlDecision", "Integer"}, 43 | {"Loadable", "Boolean"}, 44 | {"Full", "Boolean"}, 45 | {"Annoyance", "Boolean"}, 46 | {"GuardSlower", "Boolean"}, 47 | {"House", "String"}, 48 | {"Recruiter", "Boolean"}, 49 | {"Autocreate", "Boolean"}, 50 | {"Prebuild", "Boolean"}, 51 | {"Reinforce", "Boolean"}, 52 | {"Droppod", "Boolean"}, 53 | {"UseTransportOrigin", "Boolean"}, 54 | {"Whiner", "Boolean"}, 55 | {"LooseRecruit", "Boolean"}, 56 | {"Aggressive", "Boolean"}, 57 | {"Suicide", "Boolean"}, 58 | {"Priority", "Integer"}, 59 | {"Max", "Integer"}, 60 | {"TechLevel", "Integer"}, 61 | {"OnTransOnly", "Boolean"}, 62 | {"AvoidThreats", "Boolean"}, 63 | {"IonImmune", "Boolean"}, 64 | {"TransportsReturnOnUnload", "Boolean"}, 65 | {"AreTeamMembersRecruitable", "Boolean"}, 66 | {"IsBaseDefense", "Boolean"}, 67 | {"OnlyTargetHouseEnemy", "Boolean"}, 68 | {"Script", "String"}, 69 | {"TaskForce", "String"} 70 | } 71 | End Class -------------------------------------------------------------------------------- /Help/EmptyHelpProvider.vb: -------------------------------------------------------------------------------- 1 | Public Class EmptyHelpProvider 2 | Inherits HelpProvider 3 | 4 | Public Overrides Function GetHelpText(code As String) As String 5 | Return String.Empty 6 | End Function 7 | End Class 8 | -------------------------------------------------------------------------------- /Help/HelpDataProvider.vb: -------------------------------------------------------------------------------- 1 | Imports Nukepayload2.Ra2CodeAnalysis.AnalysisHelper 2 | 3 | Public Class HelpDataProvider 4 | 5 | Protected Function GetHelpTextFromDic(code As String, dic As Dictionary(Of String, String)) As String 6 | If String.IsNullOrEmpty(code) Then Return "(空)" 7 | If dic.ContainsKey(code) Then 8 | Return dic(code) 9 | Else 10 | Return "" 11 | End If 12 | End Function 13 | 14 | Dim vbcs As New Dictionary(Of String, String) From {{"String", "string"}, {"Integer", "int"}, 15 | {"Single", "float"}, {"Boolean", "bool"}, {"Structure", "struct"}, {"Object", "object"}} 16 | ''' 17 | ''' 格式化用法为vb代码和c#代码 18 | ''' 19 | ''' 用于推断基本类型的代码 20 | ''' 推断出的类型 21 | ''' 22 | Public Function FormatUsage(Code As String, tp As String, Optional DisableTypeJudgeFormat As Boolean = True) As String 23 | Dim cs As String 24 | If vbcs.ContainsKey(tp) Then 25 | cs = vbcs(tp) 26 | Else 27 | cs = tp.Replace("(Of ", "<").Replace(")", ">") 28 | For Each k In vbcs.Keys 29 | cs = cs.Replace(k, vbcs(k)) 30 | Next 31 | End If 32 | If String.IsNullOrEmpty(Code) Then 33 | Return String.Empty 34 | End If 35 | If Not DisableTypeJudgeFormat Then 36 | If tp.Contains("IEnumerable(Of") Then 37 | Return "表示多个可枚举的" & tp.Substring(15, tp.Length - 16) & vbCrLf & "用法1:Dim As " & tp & vbCrLf & "用法2:" & cs & " ;" 38 | End If 39 | End If 40 | If Code.IsInteger Then 41 | Return "整数常量" & vbCrLf & "用法1:Structure System.Int32" & vbCrLf & "用法2:struct System.Int32;" 42 | ElseIf Code.IsNumeric Then 43 | Return "小数常量" & vbCrLf & "用法1:Structure System.Single" & vbCrLf & "用法2:struct System.Single;" 44 | ElseIf Code.Replace("%", "").IsNumeric Then 45 | Return "百分数常量" & vbCrLf & "用法1:Structure Nukepayload2.CodeAnalysis.Percentage" & vbCrLf & "用法2:struct Nukepayload2.CodeAnalysis.Percentage;" 46 | ElseIf Code.Chars(0).IsNumeric Then 47 | Code = "_" & Code 48 | ElseIf Code.Contains(".") Then 49 | Code = Code.Replace("."c, "_"c) 50 | ElseIf {"yes", "no"}.Contains(Code.ToLowerInvariant) Then 51 | Return "布尔值常量" & vbCrLf & "用法1:Const " & Code & " As " & tp & " = " & If(Code.ToLowerInvariant = "yes", "True", "False") & vbCrLf & "用法2:const " & cs & " " & Code & " = " & If(Code.ToLowerInvariant = "yes", "true", "false") & ";" 52 | ElseIf {"true", "false"}.Contains(Code.ToLowerInvariant) Then 53 | Return "表示布尔值" & vbCrLf & "用法1:Structure System.Boolean" & vbCrLf & "用法2:struct System.Boolean;" 54 | ElseIf Code.ToLowerInvariant = "none" Then 55 | Return "表示空值或空引用" & vbCrLf & "用法1:Const " & Code & " As Object = Nothing" & vbCrLf & "用法2:const object " & Code & " = null;" 56 | End If 57 | Return vbCrLf & "用法1:Dim " & Code & " As " & tp & vbCrLf & "用法2:" & cs & " " & Code & ";" 58 | End Function 59 | 60 | Protected Function GetHelpTextWithUsage(code As String, text As Dictionary(Of String, String), usage As Dictionary(Of String, String)) As String 61 | If String.IsNullOrEmpty(code) Then Return "(空)" 62 | Dim tx = GetHelpTextFromDic(code, text) 63 | If usage.ContainsKey(code) Then 64 | If String.IsNullOrEmpty(tx) Then 65 | tx = "确定的用法:" 66 | End If 67 | tx &= FormatUsage(code, usage(code)) 68 | End If 69 | Return tx 70 | End Function 71 | 72 | Public Function TempAnalizeFormatUsage(Key As String, Value As String) As String 73 | Return FormatUsage(Key, TempAnalizeUsage(Value)) 74 | End Function 75 | Public Function GetRulesUsageForIme(Word As String, Helper As RulesHelpProvider, ini As RulesAnalyzer) As String 76 | Dim hlp = Helper.GetHelpText(Word) 77 | If String.IsNullOrEmpty(hlp) Then 78 | hlp = DeepAnalizeFormatUsage(Word, Word, ini) 79 | End If 80 | Return hlp 81 | End Function 82 | Public Function GetUsageForIme(Word As String, Helper As IHelpProvider, ini As INIAnalyzer) As String 83 | Dim hlp = Helper.GetHelpText(Word) 84 | If String.IsNullOrEmpty(hlp) Then 85 | hlp = DeepAnalizeFormatUsage(Word, Word, ini) 86 | End If 87 | Return hlp 88 | End Function 89 | Public Function DeepAnalizeType(Value As String, ini As INIAnalyzer) As String 90 | Dim tp = TempAnalizeUsage(Value) 91 | If tp = "String" Then 92 | For Each mkv In ini.Values 93 | If mkv.Key = "AITriggerTypes" Then 94 | If ini.Values(mkv.Key).ContainsKey(Value) Then 95 | Return mkv.Key 96 | End If 97 | Else 98 | For Each kv In mkv.Value 99 | For Each Name In {"Warhead", "Sequence"} 100 | If kv.Key = Name AndAlso Value = kv.Value.Item1 Then 101 | Return Name 102 | End If 103 | Next 104 | Next 105 | For Each kv In mkv.Value 106 | If Not kv.Key.IsNumeric Then 107 | Exit For 108 | End If 109 | If kv.Value.Item1 = Value Then 110 | Return mkv.Key 111 | End If 112 | Next 113 | End If 114 | Next 115 | End If 116 | Return tp 117 | End Function 118 | Public Function DeepAnalizeFormatUsage(Key As String, Value As String, ini As INIAnalyzer) As String 119 | Return FormatUsage(Value, DeepAnalizeType(Value, ini)) 120 | End Function 121 | 122 | Public Function DeepAnalizeType(Value As String, ini As INIAnalyzer, textcomp As Boolean, Optional CsOverloadTemp As Object = Nothing) As String 123 | Return String.Empty 124 | End Function 125 | Public Function DeepAnalizeType(Value As String, ini As RulesAnalyzer, Optional CsOverloadTemp As Object = Nothing) As String 126 | Dim tp = TempAnalizeUsage(Value) 127 | If tp = "String" Then 128 | For Each mkv In ini.Values 129 | For Each kv In mkv.Value 130 | For Each v In kv.Value.Item1.Split(","c) 131 | If v.Trim = Value Then 132 | If kv.Key.IsNumeric Then 133 | Return mkv.Key 134 | ElseIf RulesAnalyzer.IsWeaponKey(kv.Key) Then 135 | Return "Weapon" 136 | Else 137 | For Each Name In {"Warhead", "Projectile", "MetallicDebris", "DeadBodies"} 138 | If kv.Key = Name AndAlso Value = kv.Value.Item1 Then 139 | Return Name 140 | End If 141 | Next 142 | End If 143 | End If 144 | Next 145 | Next 146 | Next 147 | End If 148 | Return tp 149 | End Function 150 | 151 | Public Function DeepAnalizeFormatUsage(Key As String, Value As String, ini As RulesAnalyzer, textcomp As Boolean, Optional CsOverloadTemp As Object = Nothing) As String 152 | Return String.Empty 153 | End Function 154 | Public Function DeepAnalizeFormatUsage(Key As String, Value As String, ini As RulesAnalyzer, Optional CsOverloadTemp As Object = Nothing) As String 155 | Return FormatUsage(Value, DeepAnalizeType(Value, ini)) 156 | End Function 157 | Public Function TempAnalizeUsage(Value As String) As String 158 | Dim rig = Value 159 | If String.IsNullOrEmpty(rig) OrElse rig.ToLower = "none" Then 160 | Return "Object" 161 | End If 162 | If rig.Contains(",") Then 163 | Dim spa = rig.Split(","c) 164 | Dim sp = spa.First.Trim 165 | If sp.IsInteger Then 166 | For Each tp In spa.Skip(1) 167 | If Not tp.Trim.IsInteger Then Return "IEnumerable(Of String)" 168 | Next 169 | Return $"IEnumerable(Of {FilterIntegerTypes(sp)})" 170 | ElseIf sp.IsFraction Then 171 | For Each tp In spa.Skip(1) 172 | If Not tp.Trim.IsFraction Then Return "IEnumerable(Of String)" 173 | Next 174 | Return $"IEnumerable(Of {FilterFractionTypes(sp)})" 175 | ElseIf sp.Replace("%", "").IsInteger Then 176 | Return "IEnumerable(Of Percentage)" 177 | Else 178 | Return "IEnumerable(Of String)" 179 | End If 180 | Else 181 | If rig.IsInteger Then 182 | Return FilterIntegerTypes(rig) 183 | ElseIf rig.IsFraction Then 184 | Return FilterFractionTypes(rig) 185 | ElseIf {"true", "false", "yes", "no"}.Contains(rig.ToLowerInvariant) Then 186 | Return "Boolean" 187 | ElseIf rig.Replace("%", "").IsInteger Then 188 | Return "Percentage" 189 | ElseIf rig.StartsWith("{") AndAlso rig.EndsWith("}") AndAlso rig.Contains("-") Then 190 | Return "Guid" 191 | Else 192 | Return "String" 193 | End If 194 | End If 195 | End Function 196 | 197 | Private Shared Function FilterFractionTypes(rig As String) As String 198 | Dim sng = 0F, dbl = 0#, dec = 0D 199 | If Single.TryParse(rig, sng) AndAlso Double.TryParse(rig, dbl) AndAlso Decimal.TryParse(rig, dec) Then 200 | Dim sdbl = dbl.ToString 201 | If sdbl = sng.ToString Then 202 | Return "Single" 203 | ElseIf dec.ToString = sdbl Then 204 | Return "Double" 205 | Else 206 | Return "Decimal" 207 | End If 208 | Else 209 | Return "String" 210 | End If 211 | End Function 212 | 213 | Private Shared Function FilterIntegerTypes(rig As String) As String 214 | If Integer.TryParse(rig, 0) Then 215 | Return "Integer" 216 | ElseIf Long.TryParse(rig, 0) Then 217 | Return "Long" 218 | ElseIf ULong.TryParse(rig, 0) Then 219 | Return "ULong" 220 | Else 221 | Return "String" 222 | End If 223 | End Function 224 | End Class 225 | -------------------------------------------------------------------------------- /Help/HelpProvider.vb: -------------------------------------------------------------------------------- 1 | Public MustInherit Class HelpProvider 2 | Inherits HelpDataProvider 3 | Implements IHelpProvider 4 | 5 | Public MustOverride Function GetHelpText(code As String) As String Implements IHelpProvider.GetHelpText 6 | End Class 7 | -------------------------------------------------------------------------------- /Help/HelpProviderManager.vb: -------------------------------------------------------------------------------- 1 | Public Class HelpProviderManager 2 | Public ReadOnly Property Rules As New RulesHelpProvider() 3 | Public ReadOnly Property Art As New ArtHelpProvider() 4 | Public ReadOnly Property AI As New AIHelpProvider() 5 | End Class 6 | -------------------------------------------------------------------------------- /Help/IHelpProvider.vb: -------------------------------------------------------------------------------- 1 |  2 | Public Interface IHelpProvider 3 | Function GetHelpText(code As String) As String 4 | End Interface -------------------------------------------------------------------------------- /Help/IniNamespace.vb: -------------------------------------------------------------------------------- 1 | Imports Nukepayload2.Ra2CodeAnalysis.Imaging 2 | ''' 3 | ''' inins功能支持。像xmlns那样。这样可以提供类型检查和IME支持。 4 | ''' 5 | Public Class IniNamespace 6 | Public Items As New List(Of IniNamespaceItem) 7 | Public Const Rules = "http://schemas.nukepayload2.com/ra2codeanalysis/rules" 8 | Public Const AI = "http://schemas.nukepayload2.com/ra2codeanalysis/ai" 9 | Public Const Art = "http://schemas.nukepayload2.com/ra2codeanalysis/art" 10 | 11 | Sub New(INITree As IEnumerable(Of MainKeyTreeNode), ini As INIAnalyzer, HelpProvider As IHelpProvider) 12 | Dim hd As New HelpDataProvider 13 | Dim KeyNames As New List(Of String) 14 | For Each mk In INITree 15 | For Each kv In mk.KeyValues 16 | Dim tp = hd.DeepAnalizeType(kv.Value.Text, ini) 17 | Items.Add(New IniNamespaceItem(kv.Key.Text, HelpProvider.GetHelpText(kv.Key.Text), tp)) 18 | KeyNames.Add(kv.Key.Text) 19 | Next 20 | Next 21 | End Sub 22 | End Class 23 | -------------------------------------------------------------------------------- /Help/IniNamespaceFile.vb: -------------------------------------------------------------------------------- 1 | Public Structure IniNamespaceFile 2 | Dim TypeID As String 3 | Dim Version As Integer 4 | Dim Author As String 5 | Dim SubIniNsCount As Integer 6 | Dim SubIniNsDatas As IniNamespace() 7 | 8 | End Structure -------------------------------------------------------------------------------- /Help/IniNamespaceItem.vb: -------------------------------------------------------------------------------- 1 | Public Class IniNamespaceItem 2 | Public KeyName As String 3 | Public Usage As List(Of DescTypePair) 4 | Sub New(Key As String, Desc As String, TpName As String) 5 | KeyName = Key 6 | Usage = New List(Of DescTypePair) From {New DescTypePair(Desc, TpName)} 7 | End Sub 8 | End Class 9 | Public Class DescTypePair 10 | Public Description As String 11 | Public TypeName As String 12 | Sub New(Desc As String, TpName As String) 13 | Description = Desc 14 | TypeName = TpName 15 | End Sub 16 | End Class -------------------------------------------------------------------------------- /Imaging/IRegisterable.vb: -------------------------------------------------------------------------------- 1 |  2 | Namespace Imaging 3 | ''' 4 | ''' 能被注册到主键或键 5 | ''' 6 | Public Interface IRegisterable 7 | ReadOnly Property Text As String 8 | ReadOnly Property RegisteredIn As IEnumerable(Of RegisterTreeNode) 9 | Sub RegisterToInternal(Node As RegisterTreeNode) 10 | Sub UnRegisterFromInternal(Node As RegisterTreeNode) 11 | End Interface 12 | 13 | End Namespace -------------------------------------------------------------------------------- /Imaging/IniImagingAnalizer.vb: -------------------------------------------------------------------------------- 1 | Imports System.Reflection 2 | Imports System.Text 3 | Imports Nukepayload2.Ra2CodeAnalysis.AnalysisHelper 4 | 5 | Namespace Imaging 6 | ''' 7 | ''' 用于产生分析图的ini分析器。Values保存着撤销全部更改的结果。 8 | ''' 9 | Public Class IniImagingAnalizer 10 | Inherits INIAnalyzer 11 | Sub New(IniText As String) 12 | MyBase.New(IniText) 13 | End Sub 14 | Protected TreeRoot As List(Of MainKeyTreeNode) 15 | ''' 16 | ''' 注意:修改Root会导致分析结果无效 17 | ''' 18 | ''' 19 | Public ReadOnly Property Root As List(Of MainKeyTreeNode) = TreeRoot 20 | 21 | ''' 22 | ''' 将ini分析结果保存为String 23 | ''' 24 | ''' 25 | Public Overrides Function ToString() As String 26 | Return Root.GetText(Me.GetType) 27 | End Function 28 | 29 | Protected Overrides Sub Load(IniText As String) 30 | MyBase.Load(IniText) 31 | TreeRoot = New List(Of MainKeyTreeNode) 32 | For Each mk In Values.Keys 33 | Dim CurBranch As New MainKeyTreeNode(mk) 34 | TreeRoot.Add(CurBranch) 35 | For Each kv In Values(mk) 36 | Dim va = New ValueTreeNode(kv.Value.Item1) 37 | Dim Rec As New KeyValuePair(Of KeyTreeNode, ValueTreeNode)(New KeyTreeNode(kv.Key, va), va) 38 | CurBranch.KeyValues.Add(Rec) 39 | If Rec.Key.IsRegisterKey Then 40 | For Each v In Rec.Value.Values 41 | CurBranch.Register(v) 42 | Next 43 | End If 44 | Next 45 | Next 46 | For Each r In TreeRoot 47 | For i As Integer = r.RegisteredValues.Count - 1 To 0 Step -1 48 | Dim v = r.RegisteredValues(i) 49 | For Each r1 In TreeRoot 50 | If r1.Text = v.Text Then 51 | r.Register(r1) 52 | Exit For 53 | End If 54 | Next 55 | Next 56 | Next 57 | End Sub 58 | End Class 59 | 60 | End Namespace 61 | -------------------------------------------------------------------------------- /Imaging/IniTreeNode.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | Namespace Imaging 3 | ''' 4 | ''' INI树节点 5 | ''' 6 | Public MustInherit Class IniTreeNode 7 | ReadOnly Property Text As String 8 | Sub New(Text As String) 9 | Me.Text = Text 10 | End Sub 11 | Friend Sub RenameInternal(Name As String) 12 | _Text = Name 13 | End Sub 14 | Public Sub Rename(Root As IEnumerable(Of MainKeyTreeNode), NewName As String) 15 | For Each MK In Root 16 | For Each kv In MK.KeyValues.Keys 17 | Dim Vals As New StringBuilder 18 | Dim vs = MK.KeyValues(kv).Values 19 | Dim co = vs.Count 20 | For i As Integer = 0 To co - 2 21 | Dim v = vs(i) 22 | Dim nam2 = If(Text = v.Text, NewName, v.Text) 23 | v.RenameInternal(nam2) 24 | Vals.Append(nam2) 25 | Vals.Append(","c) 26 | Next 27 | Dim Last = vs(co - 1).Text 28 | Dim nam = If(Text = Last, NewName, Last) 29 | vs.Last.RenameInternal(nam) 30 | Vals.Append(nam) 31 | MK.KeyValues(kv).RenameInternal(Vals.ToString) 32 | Next 33 | Next 34 | RenameInternal(NewName) 35 | End Sub 36 | End Class 37 | End Namespace -------------------------------------------------------------------------------- /Imaging/KeyTreeNode.vb: -------------------------------------------------------------------------------- 1 |  2 | Imports Nukepayload2.Ra2CodeAnalysis.AnalysisHelper 3 | 4 | Namespace Imaging 5 | ''' 6 | ''' 键 7 | ''' 8 | Public Class KeyTreeNode 9 | Inherits RegisterTreeNode 10 | Public Property ValueNode As ValueTreeNode 11 | Public ReadOnly Property IsRegisterKey As Boolean 12 | Get 13 | Return Text.IsUInteger 14 | End Get 15 | End Property 16 | Sub New(Text As String, ValueNode As ValueTreeNode) 17 | MyBase.New(Text) 18 | Me.ValueNode = ValueNode 19 | End Sub 20 | 21 | Public Overrides Sub RegisterAndModify(Item As IRegisterable) 22 | Register(Item) 23 | If Not (From v In ValueNode.Values Select v.Text).Contains(Item.Text) Then 24 | ValueNode.AddValue(New SubValueTreeNode(Item.Text)) 25 | End If 26 | End Sub 27 | 28 | Public Overrides Sub UnRegisterAndModify(Item As IRegisterable) 29 | UnRegister(Item) 30 | If (From v In ValueNode.Values Select v.Text).Contains(Item.Text) Then 31 | ValueNode.RemoveValue(New SubValueTreeNode(Item.Text)) 32 | End If 33 | End Sub 34 | End Class 35 | 36 | End Namespace -------------------------------------------------------------------------------- /Imaging/MainKeyTreeNode.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | 3 | Namespace Imaging 4 | ''' 5 | ''' 主键 6 | ''' 7 | Public Class MainKeyTreeNode 8 | Inherits RegisterTreeNode 9 | Implements IRegisterable 10 | ''' 11 | ''' 把当前主键的信息转换回ini文本 12 | ''' 13 | ''' 14 | Public Overrides Function ToString() As String 15 | With New StringBuilder 16 | .Append("["c) 17 | .Append(Text) 18 | .AppendLine("]"c) 19 | For Each kv In KeyValues 20 | .Append(kv.Key.Text) 21 | .Append("="c) 22 | .AppendLine(kv.Value.Text) 23 | Next 24 | Return .ToString 25 | End With 26 | End Function 27 | ReadOnly Property KeyValues As IDictionary(Of KeyTreeNode, ValueTreeNode) = 28 | New Dictionary(Of KeyTreeNode, ValueTreeNode) 29 | Protected RegIn As New List(Of RegisterTreeNode) 30 | Public ReadOnly Property RegisteredIn As IEnumerable(Of RegisterTreeNode) = RegIn Implements IRegisterable.RegisteredIn 31 | 32 | Private ReadOnly Property IRegisterable_Text As String Implements IRegisterable.Text 33 | Get 34 | Return Text 35 | End Get 36 | End Property 37 | 38 | Sub New(Text As String) 39 | MyBase.New(Text) 40 | End Sub 41 | 42 | Friend Sub RegisterToInternal(Node As RegisterTreeNode) Implements IRegisterable.RegisterToInternal 43 | RegIn.Add(Node) 44 | End Sub 45 | 46 | Friend Sub UnRegisterFromInternal(Node As RegisterTreeNode) Implements IRegisterable.UnRegisterFromInternal 47 | RegIn.Remove(Node) 48 | End Sub 49 | 50 | Public Overrides Sub RegisterAndModify(Item As IRegisterable) 51 | If Not IsRegistered(Item) Then 52 | Register(Item) 53 | Dim keys = From k In KeyValues Select k.Key.Text 54 | For i As Integer = 1 To Integer.MaxValue 55 | If Not keys.Contains(i.ToString) Then 56 | Dim v = New ValueTreeNode(Item.Text) 57 | KeyValues.Add(New KeyTreeNode(i.ToString, v), v) 58 | Return 59 | End If 60 | Next 61 | Throw New InvalidOperationException("注册已满") 62 | End If 63 | End Sub 64 | 65 | Public Overrides Sub UnRegisterAndModify(Item As IRegisterable) 66 | If IsRegistered(Item) Then 67 | UnRegister(Item) 68 | For Each kv In KeyValues 69 | If kv.Value.Text = Item.Text Then 70 | KeyValues.Remove(kv) 71 | Return 72 | End If 73 | Next 74 | End If 75 | End Sub 76 | End Class 77 | End Namespace -------------------------------------------------------------------------------- /Imaging/MainKeyTreeNodeWriter.vb: -------------------------------------------------------------------------------- 1 |  2 | Imports System.Text 3 | 4 | Namespace Imaging 5 | Public Class MainKeyTreeNodeWriter 6 | Dim mkt As MainKeyTreeNode 7 | Protected Sub Load(ReadonlyTree As MainKeyTreeNode) 8 | mkt = ReadonlyTree 9 | Name = mkt.Text 10 | For Each itm In mkt.KeyValues 11 | KeyValues.Add(New WritableKeyValuePair(itm.Key.Text, itm.Value.Text)) 12 | Next 13 | End Sub 14 | Sub New(ReadonlyTree As MainKeyTreeNode) 15 | Load(ReadonlyTree) 16 | End Sub 17 | Property Name As String 18 | Get 19 | Return mkt.Text 20 | End Get 21 | Set(value As String) 22 | mkt.RenameInternal(value) 23 | End Set 24 | End Property 25 | Property KeyValues As New List(Of WritableKeyValuePair) 26 | Public Sub Rename(Root As IEnumerable(Of MainKeyTreeNode), NewName As String) 27 | mkt.Rename(Root, NewName) 28 | End Sub 29 | Public Sub SaveWithoutRename() 30 | mkt.KeyValues.Clear() 31 | For Each kv In KeyValues 32 | Dim va = New ValueTreeNode(kv.Value) 33 | mkt.KeyValues.Add(New KeyValuePair(Of KeyTreeNode, ValueTreeNode)(New KeyTreeNode(kv.Key, va), va)) 34 | Next 35 | End Sub 36 | End Class 37 | End Namespace 38 | -------------------------------------------------------------------------------- /Imaging/RegisterTreeNode.vb: -------------------------------------------------------------------------------- 1 |  2 | Namespace Imaging 3 | ''' 4 | ''' 主键和键是能注册其它节点的 5 | ''' 6 | Public MustInherit Class RegisterTreeNode 7 | Inherits IniTreeNode 8 | Protected RegValues As New List(Of IRegisterable) 9 | Public ReadOnly Property RegisteredValues As IEnumerable(Of IRegisterable) = RegValues 10 | Public MustOverride Sub RegisterAndModify(Item As IRegisterable) 11 | Public MustOverride Sub UnRegisterAndModify(Item As IRegisterable) 12 | Protected Function IsRegistered(Item As IRegisterable) As Boolean 13 | Return (From r In Item.RegisteredIn Select r.Text).Contains(Text) 14 | End Function 15 | Public Sub Register(Item As IRegisterable) 16 | If Not IsRegistered(Item) Then 17 | RegValues.Add(Item) 18 | Item.RegisterToInternal(Me) 19 | End If 20 | End Sub 21 | Public Sub UnRegister(Item As IRegisterable) 22 | RegValues.Remove(Item) 23 | Item.UnRegisterFromInternal(Me) 24 | End Sub 25 | Sub New(Text As String) 26 | MyBase.New(Text) 27 | End Sub 28 | End Class 29 | End Namespace -------------------------------------------------------------------------------- /Imaging/SubValueTreeNode.vb: -------------------------------------------------------------------------------- 1 |  2 | Namespace Imaging 3 | ''' 4 | ''' 值列表 5 | ''' 6 | Public Class SubValueTreeNode 7 | Inherits IniTreeNode 8 | Implements IRegisterable 9 | Protected RegIn As New List(Of RegisterTreeNode) 10 | Public ReadOnly Property RegisteredIn As IEnumerable(Of RegisterTreeNode) = RegIn Implements IRegisterable.RegisteredIn 11 | 12 | Private ReadOnly Property IRegisterable_Text As String Implements IRegisterable.Text 13 | Get 14 | Return Text 15 | End Get 16 | End Property 17 | 18 | 19 | Friend Sub RegisterToInternal(Node As RegisterTreeNode) Implements IRegisterable.RegisterToInternal 20 | RegIn.Add(Node) 21 | End Sub 22 | Sub New(Text As String) 23 | MyBase.New(Text) 24 | End Sub 25 | 26 | Friend Sub UnRegisterFromInternal(Node As RegisterTreeNode) Implements IRegisterable.UnRegisterFromInternal 27 | RegIn.Remove(Node) 28 | End Sub 29 | End Class 30 | End Namespace -------------------------------------------------------------------------------- /Imaging/Synchronization.vb: -------------------------------------------------------------------------------- 1 | Namespace Synchronization 2 | Public Module Events 3 | Public Event ReloadAllDataFromRecord() 4 | Public Sub ReloadAllDataFromRecordRequest() 5 | RaiseEvent ReloadAllDataFromRecord() 6 | End Sub 7 | End Module 8 | End Namespace 9 | 10 | 11 | -------------------------------------------------------------------------------- /Imaging/ValueTreeNode.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | 3 | Namespace Imaging 4 | 5 | ''' 6 | ''' 值 7 | ''' 8 | Public Class ValueTreeNode 9 | Inherits IniTreeNode 10 | Protected Vals As New List(Of SubValueTreeNode) 11 | ReadOnly Property Values As IEnumerable(Of SubValueTreeNode) = Vals 12 | Public Sub AddValue(Item As SubValueTreeNode) 13 | RenameInternal(Text & "," & Item.Text) 14 | Vals.Add(Item) 15 | End Sub 16 | Public Sub RemoveValue(Item As SubValueTreeNode) 17 | Vals.Remove(Item) 18 | Dim sb As New StringBuilder 19 | For Each v In Vals 20 | sb.Append(v.Text) 21 | sb.Append(","c) 22 | Next 23 | sb.Remove(sb.Length - 2, 1) 24 | End Sub 25 | Sub New(Text As String) 26 | MyBase.New(Text) 27 | For Each txs In Text.Split(","c) 28 | Vals.Add(New SubValueTreeNode(txs.Trim)) 29 | Next 30 | End Sub 31 | End Class 32 | 33 | End Namespace -------------------------------------------------------------------------------- /Input/CodeSnippet.vb: -------------------------------------------------------------------------------- 1 | Public Class CodeSnippet 2 | Public ReadOnly Property Shortcut As String 3 | Public ReadOnly Property Text As String 4 | Public ReadOnly Property Description As String 5 | Sub New(Shortcut As String, Text As String, Description As String) 6 | Me.Shortcut = Shortcut 7 | Me.Text = Text 8 | Me.Description = Description 9 | End Sub 10 | End Class 11 | -------------------------------------------------------------------------------- /Input/ImeBase.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | Imports Nukepayload2.Ra2CodeAnalysis.AnalysisHelper 3 | 4 | Public Class ImeItem 5 | Public Property Word As String 6 | Sub New(Wrd As String) 7 | Word = Wrd 8 | End Sub 9 | End Class 10 | Public MustInherit Class ImeBase 11 | Protected MustOverride ReadOnly Property SelectPosition As Integer 12 | MustOverride Property ImeListBinding As IEnumerable 13 | MustOverride Property ListVisible As Boolean 14 | Protected MustOverride Property TextInBox As String 15 | Protected MustOverride Sub SelectAndSetText(Start As Integer, Length As Integer, Text As String) 16 | 17 | Dim LeftVals As New List(Of String) 18 | Dim RightVals As New List(Of String) 19 | Dim MainKeys As New List(Of String) 20 | 21 | Public Sub AutoCompleteFast(CompleteText As String) 22 | Dim StartPos As Integer = 0 23 | Dim Selected = SelectText(False, StartPos) 24 | SelectAndSetText(StartPos, Selected.Length, CompleteText) 25 | End Sub 26 | Dim LockViewList As New Object 27 | Public Async Sub ViewList() 28 | ImeListBinding = Nothing 29 | Dim tmp As New List(Of ImeItem) 30 | Dim Left As Boolean = False 31 | Dim sea = GenerateSearch(Left) 32 | Await Task.Run( 33 | Sub() 34 | SyncLock LockViewList 35 | Dim ls = If(Left, LeftVals, RightVals) 36 | Dim Que = From i In ls Where i.ToLowerInvariant.StartsWith(sea.ToLowerInvariant) Order By i 37 | If Left Then 38 | For Each it In Que '使用Like触发测试版vbc的bug 39 | If Not it.IsUInteger Then 40 | tmp.Add(New ImeItem(it & "=")) 41 | End If 42 | Next 43 | Else 44 | For Each it In Que '使用Like触发测试版vbc的bug 45 | If Not it.IsUInteger Then 46 | tmp.Add(New ImeItem(it)) 47 | End If 48 | Next 49 | End If 50 | End SyncLock 51 | End Sub) 52 | Dim OldItems = TryCast(ImeListBinding, List(Of ImeItem)) 53 | If OldItems IsNot Nothing AndAlso OldItems.Count = tmp.Count Then 54 | For i = 0 To OldItems.Count - 1 55 | If OldItems(i).Word <> tmp(i).Word Then 56 | OldItems(i) = tmp(i) 57 | End If 58 | Next 59 | Else 60 | ImeListBinding = tmp 61 | End If 62 | ListVisible = True 63 | End Sub 64 | Protected Function SelectText(ByRef IsLeft As Boolean, ByRef StartPos As Integer) As String 65 | StartPos = Me.SelectPosition 66 | IsLeft = True 67 | Dim tx = TextInBox 68 | If String.IsNullOrEmpty(tx) Then 69 | Return String.Empty 70 | End If 71 | If StartPos > tx.Length Then 72 | Throw New ArgumentOutOfRangeException("Position", "位置位于字符串外侧") 73 | End If 74 | If StartPos = 0 Then 75 | Return String.Empty 76 | Else 77 | Dim wrd As New List(Of Char) 78 | Do Until StartPos = 0 79 | StartPos -= 1 80 | Dim ch = tx.Chars(StartPos) 81 | If ch = "=" OrElse ch = "," Then IsLeft = False 82 | If ch.IsRegisterableChar Then 83 | wrd.Add(ch) 84 | ElseIf ch <> " " Then 85 | Exit Do 86 | End If 87 | Loop 88 | wrd.Reverse() 89 | Return New String(wrd.ToArray) 90 | End If 91 | End Function 92 | Protected Function GenerateSearch(ByRef IsLeft As Boolean) As String 93 | Return SelectText(IsLeft, 0) 94 | End Function 95 | Dim Lock As New Object 96 | Protected Sub ReloadSync(Ini As INIAnalyzer) 97 | MainKeys.Clear() 98 | LeftVals.Clear() 99 | RightVals.Clear() 100 | SyncLock Lock 101 | For Each mk In Ini.Values 102 | If Not MainKeys.Contains(mk.Key) Then 103 | MainKeys.Add(mk.Key) 104 | End If 105 | For Each kv In mk.Value 106 | If Not LeftVals.Contains(kv.Key) Then 107 | LeftVals.Add(kv.Key) 108 | End If 109 | For Each v In kv.Value.Item1.Split(","c) 110 | v = v.Trim 111 | If Not RightVals.Contains(v) AndAlso Not String.IsNullOrWhiteSpace(v) Then 112 | RightVals.Add(v) 113 | End If 114 | Next 115 | Next 116 | Next 117 | End SyncLock 118 | End Sub 119 | Public Async Function Reload(Ini As INIAnalyzer) As Task 120 | Await Task.Run(Sub() ReloadSync(Ini)) 121 | End Function 122 | Sub New(Ini As INIAnalyzer) 123 | ReloadSync(Ini) 124 | End Sub 125 | End Class 126 | -------------------------------------------------------------------------------- /Input/IniValueMainKeyRenamer.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | 3 | Public Class IniValueMainKeyRenamer 4 | Sub New() 5 | 6 | End Sub 7 | Public Async Function RenameAsync(MatchItem As String, Replacement As String, RenameOption As RenameOptions, DataSource As IEnumerable(Of ParameterizedProperty(Of String))) As Task 8 | If CBool(RenameOption And RenameOptions.MainKey) Then 9 | Await ForEachAsync(DataSource, 10 | Sub(prop) 11 | Dim ini = prop.Value 12 | If MatchItem.StartsWith("<") AndAlso MatchItem.EndsWith(">") Then 13 | prop.Value = prop.Value.Replace(MatchItem, Replacement) 14 | Return 15 | End If 16 | Dim txs = ini.Split({vbCrLf}, StringSplitOptions.None) 17 | Dim sb As New StringBuilder 18 | For Each tx In txs 19 | Dim Original = tx 20 | If tx.Contains(";") Then 21 | tx = tx.Substring(0, tx.IndexOf(";"c)).Trim 22 | End If 23 | If tx.Contains("[") Then 24 | sb.Append(Original.Substring(0, Original.IndexOf("["c) + 1)) 25 | If tx.Length > 1 Then 26 | Dim MK = tx.Substring(1, tx.Length - 2).Trim 27 | If MK = MatchItem Then 28 | MK = Replacement 29 | End If 30 | sb.Append(MK).AppendLine(Original.Substring(Original.IndexOf("]"c))) 31 | End If 32 | Else 33 | sb.AppendLine(Original) 34 | End If 35 | Next 36 | prop.Value = sb.ToString 37 | End Sub) 38 | End If 39 | If CBool(RenameOption And RenameOptions.Value) Then 40 | Await ForEachAsync(DataSource, 41 | Sub(prop) 42 | Dim ini = prop.Value 43 | Dim txs = ini.Split({vbCrLf}, StringSplitOptions.None) 44 | Dim sb As New StringBuilder 45 | For Each tx In txs 46 | Dim Original = tx 47 | If tx.Contains(";") Then 48 | tx = tx.Substring(0, tx.IndexOf(";"c)).Trim 49 | End If 50 | If tx.Contains("=") Then 51 | Dim txEq = tx.Split("="c) 52 | sb.Append(txEq(0)).Append("=") 53 | Dim txRights = txEq(1).Split(","c) 54 | Dim newRights = Aggregate s In txRights Select If(s = MatchItem, Replacement, s) Into ToArray 55 | For i = 0 To newRights.Length - 2 56 | sb.Append(newRights(i)).Append(",") 57 | Next 58 | sb.AppendLine(newRights.Last) 59 | Else 60 | sb.AppendLine(Original) 61 | End If 62 | Next 63 | prop.Value = sb.ToString 64 | End Sub) 65 | End If 66 | End Function 67 | End Class -------------------------------------------------------------------------------- /Input/QuickFix.vb: -------------------------------------------------------------------------------- 1 | Imports Nukepayload2.Ra2CodeAnalysis.AnalysisHelper 2 | Imports Nukepayload2.Ra2CodeAnalysis.Document 3 | ''' 4 | ''' 修正ini文档的常见错误 5 | ''' 6 | Public Class QuickFix 7 | 8 | End Class 9 | -------------------------------------------------------------------------------- /Input/QuickFixSuggestion.vb: -------------------------------------------------------------------------------- 1 | Public MustInherit Class QuickFixSuggestion 2 | Public Message As String 3 | Public Fix As [Delegate] 4 | Public PreviewAddedText As String 5 | Public PreviewRemovedText As String 6 | End Class 7 | -------------------------------------------------------------------------------- /Input/RenameOptions.vb: -------------------------------------------------------------------------------- 1 | Public Enum RenameOptions 2 | None 3 | MainKey 4 | Value 5 | Both 6 | End Enum 7 | -------------------------------------------------------------------------------- /Linq/StringExtension.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | 3 | Namespace Linq 4 | Public Module StringAggregation 5 | ''' 6 | ''' 带缩进和 Windows 格式 换行的合并 7 | ''' 8 | 9 | Public Function Join(source As IEnumerable(Of String), space As Func(Of String, Integer)) As String 10 | Dim sb As New StringBuilder 11 | For Each blk In source 12 | sb.Append(" "c, space(blk)).AppendLine(blk) 13 | Next 14 | sb.Remove(sb.Length - 2, 2) 15 | Return sb.ToString 16 | End Function 17 | ''' 18 | ''' 带单字符分隔符的合并 19 | ''' 20 | 21 | Public Function Join(source As IEnumerable(Of String), separator As Func(Of String, String)) As String 22 | Dim sb As New StringBuilder 23 | For Each blk In source 24 | sb.Append(blk).Append(separator(blk)) 25 | Next 26 | Dim len = separator(String.Empty).Length 27 | If len > 0 Then sb.Remove(sb.Length - len, len) 28 | Return sb.ToString 29 | End Function 30 | End Module 31 | End Namespace 32 | -------------------------------------------------------------------------------- /My Project/AssemblyInfo.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Resources 3 | Imports System.Reflection 4 | 5 | ' 有关程序集的常规信息通过以下特性集 6 | ' 控制。更改这些特性值可修改 7 | ' 与程序集关联的信息。 8 | 9 | ' 检查程序集特性的值 10 | 11 | 12 | 13 | ' 程序集的版本信息由以下四个值组成: 14 | ' 15 | ' 主版本 16 | ' 次版本 17 | ' 生成号 18 | ' 修订号 19 | ' 20 | ' 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 21 | ' 方法是按如下所示使用“*”: 22 | ' 23 | -------------------------------------------------------------------------------- /Primitives/Percentage.vb: -------------------------------------------------------------------------------- 1 | ''' 2 | ''' 表示单精度的百分比 3 | ''' 4 | Public Structure Percentage 5 | Implements IEquatable(Of Percentage), IComparable(Of Percentage), IFormattable 6 | 7 | Dim SingleValue As Single 8 | 9 | Sub New(singleValue As Single) 10 | Me.SingleValue = singleValue 11 | End Sub 12 | 13 | Public Shared Function Parse(text As String) As Percentage 14 | Dim perc As New Percentage 15 | If TryParse(text, perc) Then 16 | Return perc 17 | End If 18 | If String.IsNullOrEmpty(text) Then 19 | text = "空字符串" 20 | End If 21 | Throw New InvalidCastException($"无法将 {text} 转换为 {NameOf(Percentage)} 类型") 22 | End Function 23 | 24 | Public Shared Function TryParse(text As String, ByRef value As Percentage) As Boolean 25 | If text IsNot Nothing AndAlso text.Length > 1 AndAlso text.EndsWith("%") Then 26 | Dim numberPart = text.Substring(0, text.Length - 1) 27 | Dim sng = 0F 28 | If Single.TryParse(numberPart, sng) Then 29 | value.SingleValue = sng 30 | Return True 31 | End If 32 | End If 33 | Return False 34 | End Function 35 | Public Function CompareTo(other As Percentage) As Integer Implements IComparable(Of Percentage).CompareTo 36 | Return SingleValue.CompareTo(other.SingleValue) 37 | End Function 38 | 39 | Public Overloads Function Equals(other As Percentage) As Boolean Implements IEquatable(Of Percentage).Equals 40 | Return SingleValue = other.SingleValue 41 | End Function 42 | 43 | Public Overrides Function Equals(obj As Object) As Boolean 44 | If TypeOf obj Is Percentage Then 45 | Return SingleValue = DirectCast(obj, Percentage).SingleValue 46 | End If 47 | Return False 48 | End Function 49 | Public Overrides Function GetHashCode() As Integer 50 | Return SingleValue.GetHashCode() 51 | End Function 52 | Public Overrides Function ToString() As String 53 | Return (SingleValue * 100).ToString("#") & "%" 54 | End Function 55 | Public Overloads Function ToString(format As String, formatProvider As IFormatProvider) As String Implements IFormattable.ToString 56 | Return (SingleValue * 100).ToString(format, formatProvider) & "%" 57 | End Function 58 | 59 | Public Shared Operator =(left As Percentage, right As Percentage) As Boolean 60 | Return left.SingleValue = right.SingleValue 61 | End Operator 62 | 63 | Public Shared Operator <>(left As Percentage, right As Percentage) As Boolean 64 | Return left.SingleValue <> right.SingleValue 65 | End Operator 66 | 67 | Public Shared Operator >(left As Percentage, right As Percentage) As Boolean 68 | Return left.SingleValue > right.SingleValue 69 | End Operator 70 | 71 | Public Shared Operator <(left As Percentage, right As Percentage) As Boolean 72 | Return left.SingleValue < right.SingleValue 73 | End Operator 74 | 75 | Public Shared Operator >=(left As Percentage, right As Percentage) As Boolean 76 | Return left.SingleValue >= right.SingleValue 77 | End Operator 78 | 79 | Public Shared Operator <=(left As Percentage, right As Percentage) As Boolean 80 | Return left.SingleValue <= right.SingleValue 81 | End Operator 82 | 83 | End Structure 84 | Module Constants 85 | Public Const Yes As Boolean = True 86 | Public Const No As Boolean = False 87 | Public Const None As Object = Nothing 88 | End Module -------------------------------------------------------------------------------- /Ra2CodeAnalysis.vbproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard1.4 5 | Nukepayload2.Ra2CodeAnalysis 6 | Nukepayload2.Ra2CodeAnalysis 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | {11d14cb7-c9fe-4044-aba0-9be40efbc301} 40 | Nukepayload2.CodeAnalysis 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Tools/SymbolFinder.vb: -------------------------------------------------------------------------------- 1 | Imports Nukepayload2.Ra2CodeAnalysis.AnalysisHelper 2 | 3 | Public Class SymbolFinder 4 | Public Async Function Find(MatchText As String, Rules As RulesAnalyzer, Art As ArtAnalyzer, AI As AIAnalyzer) As Task(Of IEnumerable(Of FindSymbolResult)) 5 | Dim Results = Await ForEachAsync(Of Ra2IniAnalyzer, IEnumerable(Of FindSymbolResult))({Rules, Art, AI}, 6 | Function(ini) 7 | Dim ls As New List(Of FindSymbolResult) 8 | For Each mk In ini.Values 9 | If MatchText = mk.Key Then 10 | Dim ln = mk.Value.FirstOrDefault.Value.Item2 11 | If ln = 0 Then ln = 1 12 | ls.Add(New FindSymbolResult(ini.Name, mk.Key, ln - 1, mk.Key, "定义")) 13 | End If 14 | For Each kv In mk.Value 15 | For Each v In kv.Value.Item1.Split(","c) 16 | If v.Trim = MatchText Then 17 | Dim Comment As String 18 | If kv.Key.IsNumeric() Then 19 | Comment = "注册" 20 | Else 21 | Comment = "引用" 22 | End If 23 | ls.Add(New FindSymbolResult(ini.Name, v, kv.Value.Item2, v, Comment)) 24 | End If 25 | Next 26 | Next 27 | Next 28 | Return ls 29 | End Function) 30 | Dim Heads = Results.First 31 | For i = 1 To 2 32 | Heads = Heads.Concat(Results(i)) 33 | Next 34 | Return Heads 35 | End Function 36 | End Class 37 | -------------------------------------------------------------------------------- /Utility/DataGridDisplayNameAttribute.vb: -------------------------------------------------------------------------------- 1 | Imports System.Reflection 2 | 3 | 4 | Public NotInheritable Class DataGridDisplayNameAttribute 5 | Inherits Attribute 6 | Public ReadOnly Name As String 7 | Public Shared Function GetValueDicFromType(tp As Type) As Dictionary(Of String, String) 8 | Dim NameDic As New Dictionary(Of String, String) 9 | For Each m As MemberInfo In tp.GetRuntimeProperties 10 | Dim Attrib = m.GetCustomAttribute(Of DataGridDisplayNameAttribute) 11 | If Attrib IsNot Nothing Then 12 | NameDic.Add(m.Name, DirectCast(Attrib, DataGridDisplayNameAttribute).Name) 13 | Else 14 | NameDic.Add(m.Name, m.Name) 15 | End If 16 | Next 17 | Return NameDic 18 | End Function 19 | Sub New(Name As String) 20 | MyBase.New 21 | Me.Name = Name 22 | End Sub 23 | End Class 24 | -------------------------------------------------------------------------------- /Utility/DocumentExtensions.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | 3 | Public Module DocumentExtensions 4 | ''' 5 | ''' 去除Ini注释;后面的的内容 6 | ''' 7 | 8 | Public Function ExceptComments(Source$) As String 9 | Return If(Source.Contains(";"), Source.Substring(0, Source.IndexOf(";"c)), Source) 10 | End Function 11 | ''' 12 | ''' 把每个内容按换行符分割存入字符串 13 | ''' 14 | 15 | Public Function JoinLine(Of T)(Source As IEnumerable(Of T), GetText As Func(Of T, String)) As String 16 | Dim sb As New StringBuilder 17 | For Each blk In Source 18 | sb.AppendLine(GetText(blk)) 19 | Next 20 | Return sb.ToString 21 | End Function 22 | ''' 23 | ''' 把每个内容无分割存入字符串 24 | ''' 25 | 26 | Public Function JoinText(Of T)(Source As IEnumerable(Of T), GetText As Func(Of T, String)) As String 27 | Dim sb As New StringBuilder 28 | For Each blk In Source 29 | sb.Append(GetText(blk)) 30 | Next 31 | Return sb.ToString 32 | End Function 33 | ''' 34 | ''' 如果数目一样则改写,不一样就删掉重新添加。 35 | ''' 36 | ''' 37 | ''' 38 | ''' 39 | ''' 40 | 41 | Public Sub ReloadContent(Of T)(Dest As IList(Of T), Source As IList(Of T), GetText As Func(Of T, String)) 42 | If Source.Count = Dest.Count Then 43 | For i = 0 To Source.Count - 1 44 | If GetText(Dest(i)) <> GetText(Source(i)) Then 45 | Dest(i) = Source(i) 46 | End If 47 | Next 48 | Else 49 | Dest.Clear() 50 | For Each b In Source 51 | Dest.Add(b) 52 | Next 53 | End If 54 | End Sub 55 | End Module 56 | -------------------------------------------------------------------------------- /Utility/ExpressionCalculator.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | 3 | Namespace AnalysisHelper 4 | ''' 5 | ''' 用于计算ini中的数值 6 | ''' 7 | Public Class ExpressionCalculator 8 | Protected Numbers As New Stack(Of Decimal) 9 | Protected Operators As New Stack(Of String) 10 | Protected ReadOnly Ops As New Dictionary(Of String, Func(Of Decimal, Decimal, Decimal)) From { 11 | {"+", Function(a, b) a + b}, 12 | {"-", Function(a, b) a - b}, 13 | {"*", Function(a, b) a * b}, 14 | {"x", Function(a, b) a * b}, 15 | {"/", Function(a, b) a / b}, 16 | {"\", Function(a, b) CLng(a) \ CLng(b)}, 17 | {"÷", Function(a, b) a / b}, 18 | {"^", Function(a, b) CDec(a ^ b)}, 19 | {"mod", Function(a, b) a Mod b}, 20 | {"%", Function(a, b) a Mod b}, 21 | {"not", Function(a, b) Not CLng(b)}, 22 | {"!", Function(a, b) Not CLng(b)}, 23 | {"and", Function(a, b) CLng(a) And CLng(b)}, 24 | {"or", Function(a, b) CLng(a) Or CLng(b)}, 25 | {"xor", Function(a, b) CLng(a) Xor CLng(b)}, 26 | {"<<", Function(a, b) CLng(a) << CInt(b)}, 27 | {">>", Function(a, b) CLng(a) >> CInt(b)}, 28 | {"<<<", Function(a, b) CLng(a) << CInt(b)}, 29 | {">>>", Function(a, b) CLng(a).BitToUInt64 >> CInt(b)}, 30 | {"sal", Function(a, b) CLng(a) << CInt(b)}, 31 | {"shl", Function(a, b) CLng(a) << CInt(b)}, 32 | {"sar", Function(a, b) CLng(a) >> CInt(b)}, 33 | {"shr", Function(a, b) CLng(a).BitToUInt64 >> CInt(b)}, 34 | {"ror", Function(a, b) 35 | Dim num As ULong = CLng(a).BitToUInt64, Count As Integer = CInt(b) 36 | Count = Count Mod 64 37 | Dim lo = num >> Count 38 | Dim hi = num << 64 - Count 39 | Return BitToInt64(hi Or lo) 40 | End Function}, 41 | {"rol", Function(a, b) 42 | Dim num As ULong = CLng(a).BitToUInt64, Count As Integer = CInt(b) 43 | Count = Count Mod 64 44 | Dim lo = num >> 64 - Count 45 | Dim hi = num << Count 46 | Return BitToInt64(hi Or lo) 47 | End Function} 48 | } 49 | Public ReadOnly Property SupportedOperators As ICollection(Of String) 50 | Get 51 | Return Ops.Keys 52 | End Get 53 | End Property 54 | Protected Overridable Function CalculateBlock(Num2 As Decimal, Num1 As Decimal, OpCode As String) As Decimal 55 | If OpCode.Length > 1 Then OpCode = OpCode.ToLowerInvariant 56 | If Not Ops.ContainsKey(OpCode) Then 57 | Throw New ArgumentException(String.Format("运算符{0}不支持", OpCode)) 58 | End If 59 | Return Ops(OpCode).Invoke(Num1, Num2) 60 | End Function 61 | Protected Function Seperate(chrs As IEnumerable(Of Char)) As IEnumerable(Of String) 62 | Dim Expression = New String(chrs.ToArray) 63 | Dim Result As New List(Of String) 64 | If chrs.CountOf("("c) <> chrs.CountOf(")"c) Then 65 | Throw New ArgumentException("表达式中的(的数量不等于)的数量") 66 | End If 67 | For pos As Integer = 0 To chrs.Count - 1 68 | If IsNumeric(chrs(pos)) OrElse (chrs(pos) = "-"c AndAlso (pos = 0 OrElse Not chrs(pos - 1).IsNumeric)) Then 69 | Dim LastStr As New StringBuilder 70 | Do 71 | LastStr.Append(chrs(pos)) 72 | pos += 1 73 | Loop While IsNumeric(chrs(pos)) OrElse chrs(pos) = "." 74 | pos -= 1 75 | Result.Add(LastStr.ToString) 76 | Else 77 | If chrs(pos) = "(" OrElse chrs(pos) = ")" Then 78 | Result.Add(chrs(pos)) 79 | Else 80 | Dim p As Integer = 0 81 | Dim pm As Integer 82 | Dim LongStr As String = "" 83 | Dim ssb As New StringBuilder 84 | Do 85 | ssb.Append(chrs(pos + p)) 86 | If Ops.Keys.Contains(ssb.ToString) Then 87 | LongStr = ssb.ToString 88 | pm = p 89 | End If 90 | p += 1 91 | If pos + p >= chrs.Count - 1 Then 92 | Exit Do 93 | End If 94 | Loop Until IsNumeric(chrs(pos + p)) 95 | Result.Add(LongStr) 96 | pos += pm 97 | End If 98 | End If 99 | Next 100 | Return Result 101 | End Function 102 | 103 | Private Function GetPriority(s As String) As Integer 104 | Select Case s 105 | Case "(" 106 | Return 0 107 | Case "<<", ">>", "shr", "shl", "rol", "ror", "<<<", ">>>", "sar", "sal" 108 | Return 1 109 | Case "xor", "or", "and" 110 | Return 2 111 | Case "+", "-" 112 | Return 3 113 | Case "mod", "%" 114 | Return 4 115 | Case "\" 116 | Return 5 117 | Case "*", "x", "/", "÷" 118 | Return 6 119 | Case "^" 120 | Return 7 121 | Case ")" 122 | Return 8 123 | Case Else 124 | Throw New ArgumentException("不支持的运算符" & s) 125 | End Select 126 | End Function 127 | Public Function Eval(Expression As String) As Decimal 128 | Numbers.Clear() 129 | Operators.Clear() 130 | Dim Seped = Seperate(Expression.Replace("(", "(").Replace(")", ")").Replace(" ", "").ToCharArray) 131 | For i As Integer = 0 To Seped.Count - 1 132 | Dim current = Seped(i) 133 | If current.IsNumeric Then 134 | Numbers.Push(CDec(current)) 135 | Else 136 | If current <> ")" Then 137 | If current <> "(" AndAlso Operators.Count > 0 Then 138 | Do While GetPriority(Operators.Peek) >= GetPriority(current) 139 | Numbers.Push(CalculateBlock(Numbers.Pop, Numbers.Pop, Operators.Pop)) 140 | If Operators.Count = 0 Then Exit Do 141 | If Operators.Peek = "(" Then Exit Do 142 | Loop 143 | End If 144 | Operators.Push(current) 145 | ElseIf GetPriority(Operators.Peek) < GetPriority(current) Then 146 | If Operators.Peek <> "(" Then 147 | Do Until Operators.Peek = "(" 148 | Numbers.Push(CalculateBlock(Numbers.Pop, Numbers.Pop, Operators.Pop)) 149 | Loop 150 | End If 151 | Operators.Pop() 152 | Else 153 | Numbers.Push(CalculateBlock(Numbers.Pop, Numbers.Pop, Operators.Pop)) 154 | End If 155 | End If 156 | Next 157 | Do While Operators.Count > 0 158 | Numbers.Push(CalculateBlock(Numbers.Pop, Numbers.Pop, Operators.Pop)) 159 | Loop 160 | If Numbers.Count = 1 Then 161 | Return Numbers.First 162 | Else 163 | Throw New ArgumentException("不能省略*或x运算符") 164 | Dim tmp As Decimal = 1 165 | For Each n In Numbers 166 | tmp *= n 167 | Next 168 | Return tmp 169 | End If 170 | End Function 171 | Public Function EvalAsync(Expression As String) As Task(Of Decimal) 172 | Return New Task(Of Decimal)(Function() Eval(Expression)) 173 | End Function 174 | End Class 175 | End Namespace -------------------------------------------------------------------------------- /Utility/Parallel.vb: -------------------------------------------------------------------------------- 1 | Public Module AsyncParallel 2 | Public Async Function ForEachAsync(Of T)(DataSource As IEnumerable(Of T), Proc As Action(Of T)) As Task 3 | Await Task.WhenAll(From d In DataSource Select Task.Run(Sub() Proc(d))) 4 | End Function 5 | Public Async Function ForEachAsync(Of T, TResult)(DataSource As IEnumerable(Of T), Proc As Func(Of T, TResult)) As Task(Of TResult()) 6 | Return Await Task.WhenAll(From d In DataSource Select Task.Run(Function() Proc(d))) 7 | End Function 8 | End Module 9 | -------------------------------------------------------------------------------- /Utility/ParameterizedProperty.vb: -------------------------------------------------------------------------------- 1 | ''' 2 | ''' 用于传递属性和为值获取和设置提供通知 3 | ''' 4 | ''' 5 | Public Class ParameterizedProperty(Of T) 6 | Public ReadOnly Property Getter As Func(Of T) 7 | Public ReadOnly Property Setter As Action(Of T) 8 | Public ReadOnly Property IsReadOnly As Boolean 9 | Get 10 | Return Getter IsNot Nothing AndAlso Setter Is Nothing 11 | End Get 12 | End Property 13 | Public ReadOnly Property IsWriteOnly As Boolean 14 | Get 15 | Return Getter Is Nothing AndAlso Setter IsNot Nothing 16 | End Get 17 | End Property 18 | Public Property Value As T 19 | Get 20 | Return Getter().Invoke 21 | End Get 22 | Set 23 | Setter.Invoke(Value) 24 | End Set 25 | End Property 26 | Public Shared Narrowing Operator CType(Prop As ParameterizedProperty(Of T)) As T 27 | Return Prop.Value 28 | End Operator 29 | Sub New(Getter As Func(Of T), Setter As Action(Of T)) 30 | Me.Getter = Getter 31 | Me.Setter = Setter 32 | End Sub 33 | End Class 34 | -------------------------------------------------------------------------------- /Utility/ResetableDelayTask.vb: -------------------------------------------------------------------------------- 1 | Imports System.Threading 2 | 3 | Public Class ResetableDelayTask 4 | Dim DelayMilsec As Integer 5 | Public ReadOnly Property IsStopped As Boolean = False 6 | Dim CurrentDelay As Integer = 0 7 | Public Sub Reset() 8 | SyncLock Me 9 | CurrentDelay = 0 10 | End SyncLock 11 | End Sub 12 | Public Async Function Run(ac As Action) As Task 13 | If IsStopped Then 14 | Reset() 15 | Return 16 | End If 17 | SyncLock Me 18 | _IsStopped = True 19 | End SyncLock 20 | Do While CurrentDelay < DelayMilsec 21 | Await Task.Delay(16) 22 | If Not IsStopped Then Return 23 | Interlocked.Add(CurrentDelay, 16) 24 | Loop 25 | Await Task.Run(ac) 26 | SyncLock Me 27 | _IsStopped = False 28 | End SyncLock 29 | End Function 30 | 31 | Sub New(DelayTime As TimeSpan) 32 | DelayMilsec = CInt(DelayTime.TotalMilliseconds) 33 | End Sub 34 | End Class 35 | -------------------------------------------------------------------------------- /Utility/TextExtensions.vb: -------------------------------------------------------------------------------- 1 | Imports System.Reflection 2 | Imports System.Text 3 | Imports System.Text.RegularExpressions 4 | Imports Nukepayload2.Ra2CodeAnalysis.Imaging 5 | Imports Ra2CodeAnalysis.Imaging 6 | 7 | Namespace AnalysisHelper 8 | Public Module Extensions 9 | 10 | Public Function GetText(Root As IEnumerable(Of Imaging.MainKeyTreeNode), Generator As Type) As String 11 | Dim sb As New StringBuilder(";<生成的代码(生成器:=" + If(Generator?.FullName, "null") + ",版本:=" + If(Generator.GetTypeInfo.Assembly.GetCustomAttribute(Of AssemblyFileVersionAttribute)?.Version, "null").ToString + ")>" & vbCrLf) 12 | For Each s In Root 13 | sb.AppendLine(s.ToString) 14 | Next 15 | Return sb.ToString 16 | End Function 17 | 18 | Public Function IsComment(Text As String, Index As Integer) As Boolean 19 | Dim StartPos As Integer = Index 20 | If String.IsNullOrEmpty(Text) Then 21 | Return True 22 | End If 23 | If StartPos > Text.Length Then 24 | Throw New ArgumentOutOfRangeException("Position", "位置位于字符串外侧") 25 | End If 26 | If StartPos = 0 Then 27 | Return False 28 | Else 29 | Do Until StartPos = 0 30 | StartPos -= 1 31 | Dim ch = Text.Chars(StartPos) 32 | If ch = ";"c Then 33 | Return True 34 | Else 35 | If ch = vbCr OrElse ch = vbLf Then 36 | Exit Do 37 | End If 38 | End If 39 | Loop 40 | Return False 41 | End If 42 | End Function 43 | 44 | Public Function FindOne(Of T As IniTreeNode)(RT As IEnumerable(Of T), ObjName As String) As T 45 | For Each mk In RT 46 | If mk.Text = ObjName Then Return mk 47 | Next 48 | Return Nothing 49 | End Function 50 | 51 | Public Function SelectLine(Text As String, index As Integer) As String 52 | If index = -1 Then 53 | Return String.Empty 54 | End If 55 | Dim StartPos As Integer = index 56 | If String.IsNullOrEmpty(Text) Then 57 | Return String.Empty 58 | End If 59 | If StartPos > Text.Length Then 60 | Throw New ArgumentOutOfRangeException("Position", "位置位于字符串外侧") 61 | End If 62 | Dim wrd As New List(Of Char) 63 | Do Until StartPos = 0 64 | StartPos -= 1 65 | Dim ch = Text.Chars(StartPos) 66 | If ch <> vbCr AndAlso ch <> vbLf Then 67 | wrd.Add(ch) 68 | Else 69 | Exit Do 70 | End If 71 | Loop 72 | wrd.Reverse() 73 | Dim LeftPart As New String(wrd.ToArray) 74 | Dim RightPart As New StringBuilder 75 | Do Until index >= Text.Length 76 | Dim ch = Text.Chars(index) 77 | If ch <> vbCr AndAlso ch <> vbLf Then 78 | RightPart.Append(ch) 79 | Else 80 | Exit Do 81 | End If 82 | index += 1 83 | Loop 84 | Return LeftPart + RightPart.ToString 85 | End Function 86 | 87 | Public Function IsValidValueChar(Text As Char) As Boolean 88 | Return Not {ChrW(10), ChrW(13), "="c, ";"c}.Contains(Text) 89 | End Function 90 | 91 | Public Function IsValidSingleWordValueChar(Text As Char) As Boolean 92 | Return Char.IsLetterOrDigit(Text) OrElse Text = "{"c OrElse Text = "}"c 93 | End Function 94 | 95 | Public Function IsValidTemplatedWordValueChar(Text As Char) As Boolean 96 | Return Char.IsLetterOrDigit(Text) OrElse Text = "<"c OrElse Text = ">"c OrElse Text = "_"c OrElse Text = "-"c 97 | End Function 98 | 99 | Public Function SelectWord(Text As String, index As Integer, Optional Filter As Func(Of Char, Boolean) = Nothing) As String 100 | If Filter Is Nothing Then 101 | Filter = AddressOf IsValidValueChar 102 | End If 103 | Dim StartPos As Integer = index 104 | If String.IsNullOrEmpty(Text) Then 105 | Return String.Empty 106 | End If 107 | If StartPos > Text.Length Then 108 | Throw New ArgumentOutOfRangeException("Position", "位置位于字符串外侧") 109 | End If 110 | If StartPos = 0 Then 111 | Return String.Empty 112 | Else 113 | Dim wrd As New List(Of Char) 114 | Dim Terminating = False 115 | Do Until StartPos = 0 116 | StartPos -= 1 117 | Dim ch = Text.Chars(StartPos) 118 | If Filter(ch) AndAlso Not Terminating Then 119 | If ch = "<" Then Terminating = True 120 | wrd.Add(ch) 121 | Else 122 | Exit Do 123 | End If 124 | Loop 125 | Terminating = False 126 | wrd.Reverse() 127 | Dim LeftPart As New String(wrd.ToArray) 128 | Dim RightPart As New StringBuilder 129 | Do Until index >= Text.Length 130 | Dim ch = Text.Chars(index) 131 | If Filter(ch) AndAlso Not Terminating Then 132 | If ch = ">" Then Terminating = True 133 | RightPart.Append(ch) 134 | Else 135 | Exit Do 136 | End If 137 | index += 1 138 | Loop 139 | Return LeftPart.Trim + RightPart.ToString.Trim 140 | End If 141 | End Function 142 | ''' 143 | ''' 判断是否是\w,_,%和. 144 | ''' 145 | ''' 146 | ''' 147 | 148 | Function IsRegisterableChar(ch As Char) As Boolean 149 | Return New Regex("(\w|_|\.|%|-)").IsMatch(ch) 150 | End Function 151 | 152 | Function BitToInt64(n As ULong) As Long 153 | Return BitConverter.ToInt64(BitConverter.GetBytes(n), 0) 154 | End Function 155 | 156 | Function BitToUInt64(n As Long) As ULong 157 | Return BitConverter.ToUInt64(BitConverter.GetBytes(n), 0) 158 | End Function 159 | 160 | Function BitToInt32(n As UInteger) As Integer 161 | Return BitConverter.ToInt32(BitConverter.GetBytes(n), 0) 162 | End Function 163 | 164 | Function BitToUInt32(n As Integer) As UInteger 165 | Return BitConverter.ToUInt32(BitConverter.GetBytes(n), 0) 166 | End Function 167 | 168 | Function CountOf(Of T)(txt As IEnumerable(Of T), chr As T) As Integer 169 | Return Aggregate co In From ch In txt Where ch.Equals(chr) Into Count 170 | End Function 171 | 172 | Function CountOf(txt As String, chr As Char) As Integer 173 | Return Aggregate co In From ch In txt.ToCharArray Where ch = chr Into Count 174 | End Function 175 | 176 | Function IsNumeric(t As Char) As Boolean 177 | Return t >= "0"c AndAlso t <= "9"c 178 | End Function 179 | 180 | Function IsInteger(txt As String) As Boolean 181 | If txt.StartsWith("-") OrElse txt.StartsWith("+") Then txt = txt.Substring(1) 182 | If txt.Length = 0 Then Return False 183 | Return txt.Length = Aggregate c In From t In txt.ToCharArray Take While t >= "0"c AndAlso t <= "9"c Into Count 184 | End Function 185 | 186 | Function IsFraction(txt As String) As Boolean 187 | If txt.StartsWith("-") OrElse txt.StartsWith("+") Then txt = txt.Substring(1) 188 | If txt.CountOf("."c) <> 1 Then Return False 189 | Return txt.Length = Aggregate c In From t In txt.ToCharArray Take While t >= "0"c AndAlso t <= "9"c OrElse t = "." Into Count 190 | End Function 191 | 192 | Function IsUInteger(txt As String) As Boolean 193 | If txt.Length = 0 Then Return False 194 | Return txt.Length = Aggregate c In From t In txt.ToCharArray Take While t >= "0"c AndAlso t <= "9"c Into Count 195 | End Function 196 | 197 | Function IsUFraction(txt As String) As Boolean 198 | If txt.CountOf("."c) <> 1 Then Return False 199 | Return txt.Length = Aggregate c In From t In txt.ToCharArray Take While t >= "0"c AndAlso t <= "9"c OrElse t = "." Into Count 200 | End Function 201 | 202 | Function IsNumeric(txt As String) As Boolean 203 | If txt.StartsWith("-") OrElse txt.StartsWith("+") Then txt = txt.Substring(1) 204 | If txt.Length = 0 Then Return False 205 | Return txt.Length = Aggregate c In From t In txt.ToCharArray Take While t >= "0"c AndAlso t <= "9"c OrElse t = "." Into Count 206 | End Function 207 | 208 | Function ContainsEachTrim(Main As IEnumerable(Of String), Second As IEnumerable(Of String)) As Boolean 209 | For Each s In Second 210 | If Main.Contains(s.Trim) Then 211 | Return True 212 | End If 213 | Next 214 | Return False 215 | End Function 216 | End Module 217 | End Namespace 218 | -------------------------------------------------------------------------------- /Utility/ValueConverter.vb: -------------------------------------------------------------------------------- 1 | Public Module ValueConverter 2 | 3 | Public Function ToYesNo(Value As Boolean) As String 4 | Return If(Value, "yes", "no") 5 | End Function 6 | End Module 7 | -------------------------------------------------------------------------------- /Utility/WritableKeyValuePair.vb: -------------------------------------------------------------------------------- 1 | Public Class WritableKeyValuePair 2 | Public Property Key As String 3 | Public Property Value As String 4 | Sub New(K As String, V As String) 5 | Key = K 6 | Value = V 7 | End Sub 8 | End Class 9 | -------------------------------------------------------------------------------- /ViewModels/AnalizeSourceViewModel.vb: -------------------------------------------------------------------------------- 1 | Public Class AnalizeSourceViewModel 2 | Public Event DataChanged(DocumentCategory$, NewData$) 3 | Dim _RulesText$ = "", _ArtText$ = "", _AIText$ = "", _Ra2Text$ = "" 4 | Public Property RulesText$ 5 | Get 6 | Return _RulesText 7 | End Get 8 | Set 9 | _RulesText = Value.Replace(vbCr, "").Replace(vbLf, vbCrLf) 10 | IsRulesInvalid = True 11 | RaiseEvent DataChanged("Rules", Value) 12 | End Set 13 | End Property 14 | Public Property Ra2Text$ 15 | Get 16 | Return _Ra2Text 17 | End Get 18 | Set 19 | _Ra2Text = Value.Replace(vbCr, "").Replace(vbLf, vbCrLf) 20 | IsRa2Invalid = True 21 | RaiseEvent DataChanged("Ra2", Value) 22 | End Set 23 | End Property 24 | Public Property ArtText$ 25 | Get 26 | Return _ArtText 27 | End Get 28 | Set 29 | _ArtText = Value.Replace(vbCr, "").Replace(vbLf, vbCrLf) 30 | IsArtInvalid = True 31 | RaiseEvent DataChanged("Art", Value) 32 | End Set 33 | End Property 34 | Public Property AIText$ 35 | Get 36 | Return _AIText 37 | End Get 38 | Set 39 | _AIText = Value.Replace(vbCr, "").Replace(vbLf, vbCrLf) 40 | IsAIInvalid = True 41 | RaiseEvent DataChanged("AI", Value) 42 | End Set 43 | End Property 44 | Public Property IsRulesInvalid As Boolean = True 45 | Public Property IsArtInvalid As Boolean = True 46 | Public Property IsAIInvalid As Boolean = True 47 | Public Property IsRa2Invalid As Boolean = True 48 | Sub New() 49 | 50 | End Sub 51 | Sub New(RulesText$, ArtText$, AIText$, Ra2Text$) 52 | Me.RulesText = RulesText 53 | Me.Ra2Text = Ra2Text 54 | Me.ArtText = ArtText 55 | Me.AIText = AIText 56 | End Sub 57 | End Class 58 | -------------------------------------------------------------------------------- /ViewModels/CodeGenerateLogViewModel.vb: -------------------------------------------------------------------------------- 1 | Imports System.Collections.ObjectModel 2 | Public Class CodeGenerateLogViewModel 3 | Inherits SingleInstance(Of CodeGenerateLogViewModel) 4 | Public Property IniDiagnostics As New ObservableCollection(Of WidenIniAnalysisInfo) 5 | Public Property GenerateLog As New ObservableCollection(Of String) 6 | End Class 7 | -------------------------------------------------------------------------------- /ViewModels/CodeSnippetGroup.vb: -------------------------------------------------------------------------------- 1 | Public Class CodeSnippetGroup 2 | Public ReadOnly Property Name$ 3 | Public ReadOnly Property Group As IList(Of CodeSnippet) 4 | Sub New(Name$, Group As IList(Of CodeSnippet)) 5 | Me.Name = Name 6 | Me.Group = Group 7 | End Sub 8 | End Class 9 | -------------------------------------------------------------------------------- /ViewModels/DocumentsViewModel.vb: -------------------------------------------------------------------------------- 1 | Namespace Document 2 | Public MustInherit Class DocumentsViewModel(Of TObservable As {IList(Of IniBlock), New}) 3 | Public ReadOnly Property RulesDocument As IniDocument(Of TObservable) 4 | Public ReadOnly Property ArtDocument As IniDocument(Of TObservable) 5 | Public ReadOnly Property AIDocument As IniDocument(Of TObservable) 6 | Public ReadOnly Property Ra2Document As IniDocument(Of TObservable) 7 | Public ReadOnly Property Help As New HelpProviderManager 8 | Public ReadOnly Property HelpUtil As New HelpDataProvider 9 | WithEvents ViewSource As AnalizeSourceViewModel 10 | Sub New(ViewSource As AnalizeSourceViewModel) 11 | Me.ViewSource = ViewSource 12 | RulesDocument = New IniDocument(Of TObservable)(ViewSource.RulesText) 13 | ArtDocument = New IniDocument(Of TObservable)(ViewSource.ArtText) 14 | AIDocument = New IniDocument(Of TObservable)(ViewSource.AIText) 15 | Ra2Document = New IniDocument(Of TObservable)(ViewSource.Ra2Text) 16 | End Sub 17 | 18 | Private Sub ViewSource_DataChanged(DocumentCategory As String, NewData As String) Handles ViewSource.DataChanged 19 | Select Case DocumentCategory 20 | Case DocumentCategories.Rules 21 | RulesDocument.Text = NewData 22 | Case DocumentCategories.AI 23 | AIDocument.Text = NewData 24 | Case DocumentCategories.Art 25 | ArtDocument.Text = NewData 26 | Case DocumentCategories.Ra2 27 | Ra2Document.Text = NewData 28 | End Select 29 | End Sub 30 | End Class 31 | End Namespace 32 | -------------------------------------------------------------------------------- /ViewModels/FindSymbolResult.vb: -------------------------------------------------------------------------------- 1 | Public Class FindSymbolResult 2 | Public ReadOnly Property FileName$ 3 | Public ReadOnly Property MainKey$ 4 | Public ReadOnly Property LineNumber% 5 | Public ReadOnly Property Text$ 6 | Public ReadOnly Property Remark$ 7 | Sub New(FileName$, MainKey$, LineNumber%, Text$, Remark$) 8 | Me.FileName = FileName 9 | Me.MainKey = MainKey 10 | Me.LineNumber = LineNumber 11 | Me.Text = Text 12 | Me.Remark = Remark 13 | End Sub 14 | End Class 15 | -------------------------------------------------------------------------------- /ViewModels/IniFilesViewModel.vb: -------------------------------------------------------------------------------- 1 | Imports System.Collections.ObjectModel 2 | Imports Nukepayload2.Ra2CodeAnalysis 3 | 4 | Public Class IniFilesViewModel 5 | Inherits SingleInstance(Of IniFilesViewModel) 6 | Public Property IniFileItems As New ObservableCollection(Of IniFileItem) 7 | End Class 8 | -------------------------------------------------------------------------------- /ViewModels/ProjectGenerateViewModel.vb: -------------------------------------------------------------------------------- 1 | Public Class ProjectGenerateViewModel 2 | 3 | End Class 4 | -------------------------------------------------------------------------------- /ViewModels/ProjectSetupViewModel.vb: -------------------------------------------------------------------------------- 1 | Imports Nukepayload2.CodeAnalysis 2 | 3 | Public Class ProjectSetupViewModel 4 | Inherits SingleInstance(Of ProjectSetupViewModel) 5 | Public Property ProjectBasicInformation As New ProjectBasicInformation 6 | End Class 7 | -------------------------------------------------------------------------------- /ViewModels/SingleInstance.vb: -------------------------------------------------------------------------------- 1 | Public Class SingleInstance(Of T As SingleInstance(Of T)) 2 | Public Shared ReadOnly Property Instance As T 3 | Sub New() 4 | _Instance = CType(Me, T) 5 | End Sub 6 | End Class 7 | -------------------------------------------------------------------------------- /ViewModels/WidenIniAnalysisInfo.vb: -------------------------------------------------------------------------------- 1 | Imports System.Text 2 | Public Enum ErrorFilters 3 | None 4 | Message 5 | Warning 6 | MessageWarning 7 | Fault 8 | MessageFault 9 | WarningFault 10 | All 11 | End Enum 12 | Public Enum SeverityLevels 13 | Message 14 | Warning 15 | [Error] 16 | End Enum 17 | 18 | Public Class WidenIniAnalysisInfo 19 | Public Enum FileNames 20 | Rules 21 | Art 22 | AI 23 | Ra2 24 | End Enum 25 | Public Shared Function GetWidenIniAnalysisInfo(Result As INIAnalyzeResult, FileName As String, Optional AggOpt As Integer = ErrorFilters.All) As IEnumerable(Of WidenIniAnalysisInfo) 26 | Dim tmp As New List(Of WidenIniAnalysisInfo) 27 | If CBool(AggOpt And ErrorFilters.Message) Then 28 | For Each tp In Result.Message 29 | tmp.Add(New WidenIniAnalysisInfo(SeverityLevels.Message, tp.LineNumber, tp.Description, tp.LineText, tp.MainKey, FileName)) 30 | Next 31 | End If 32 | If CBool(AggOpt And ErrorFilters.Warning) Then 33 | For Each tp In Result.Warning 34 | tmp.Add(New WidenIniAnalysisInfo(SeverityLevels.Warning, tp.LineNumber, tp.Description, tp.LineText, tp.MainKey, FileName)) 35 | Next 36 | End If 37 | If CBool(AggOpt And ErrorFilters.Fault) Then 38 | For Each tp In Result.Fault 39 | tmp.Add(New WidenIniAnalysisInfo(SeverityLevels.Error, tp.LineNumber, tp.Description, tp.LineText, tp.MainKey, FileName)) 40 | Next 41 | End If 42 | Return tmp 43 | End Function 44 | 45 | Public ReadOnly Property InfoType As SeverityLevels 46 | 47 | Public ReadOnly Property LineNumber As Integer 48 | 49 | Public ReadOnly Property Description As String 50 | 51 | Public ReadOnly Property Text As String 52 | 53 | Public ReadOnly Property MainKey As String 54 | 55 | Public ReadOnly Property FileName As String 56 | Sub New(InfoType As SeverityLevels, LineNumber As Integer, Description As String, Text As String, MainKey As String, FileName As String) 57 | Me.InfoType = InfoType 58 | Me.LineNumber = LineNumber 59 | Me.Description = Description 60 | Me.Text = Text 61 | Me.MainKey = MainKey 62 | Me.FileName = FileName 63 | End Sub 64 | End Class 65 | --------------------------------------------------------------------------------