diff --git a/ChunithmAssetsTools.sln b/ChunithmAssetsTools.sln
new file mode 100644
index 0000000..3eb608d
--- /dev/null
+++ b/ChunithmAssetsTools.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.14.36301.6 d17.14
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "DDSExtractor", "DDSExtractor\DDSExtractor.vbproj", "{0DD02AD4-2897-1D77-5B9B-8CCC916BBF1E}"
+EndProject
+Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "DDSPatcher", "DDSPatcher\DDSPatcher.vbproj", "{D6481E89-DAEE-6DB9-D614-6F5A6FEA610C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {0DD02AD4-2897-1D77-5B9B-8CCC916BBF1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0DD02AD4-2897-1D77-5B9B-8CCC916BBF1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0DD02AD4-2897-1D77-5B9B-8CCC916BBF1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0DD02AD4-2897-1D77-5B9B-8CCC916BBF1E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D6481E89-DAEE-6DB9-D614-6F5A6FEA610C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6481E89-DAEE-6DB9-D614-6F5A6FEA610C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6481E89-DAEE-6DB9-D614-6F5A6FEA610C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6481E89-DAEE-6DB9-D614-6F5A6FEA610C}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {2B6624E1-E624-495A-9AA4-A8FB25EF041D}
+ EndGlobalSection
+EndGlobal
diff --git a/DDSExtractor/DDSExtractor.vbproj b/DDSExtractor/DDSExtractor.vbproj
new file mode 100644
index 0000000..981965a
--- /dev/null
+++ b/DDSExtractor/DDSExtractor.vbproj
@@ -0,0 +1,9 @@
+
+
+
+ Exe
+ DDSExtractor
+ net8.0
+
+
+
diff --git a/DDSExtractor/Program.vb b/DDSExtractor/Program.vb
new file mode 100644
index 0000000..ec67af3
--- /dev/null
+++ b/DDSExtractor/Program.vb
@@ -0,0 +1,232 @@
+Imports System
+Imports System.IO
+Imports System.Collections.Generic
+Imports System.Diagnostics
+
+Module DdsExtractor
+ ' DDS ÎļþÍ·±êʶ
+ Private ReadOnly DDS_HEADER As Byte() = {&H44, &H44, &H53, &H20} ' "DDS "
+ Private ReadOnly POF_MARKER As String = "POF"
+ Public Const Version As String = "v1.1.2"
+ Dim currentPath As String = AppDomain.CurrentDomain.BaseDirectory
+ Dim targetExePath As String = Path.Combine(currentPath, "DDSPatcher.exe")
+
+ Sub Main()
+ Console.ForegroundColor = ConsoleColor.DarkCyan
+ Console.WriteLine($"DDS ÎļþÌáÈ¡¹¤¾ß {Version} by ChilorXN.")
+ Console.ForegroundColor = ConsoleColor.DarkYellow
+ Console.WriteLine("ÇëÍÏ·ÅÒª´¦ÀíµÄ .afb »ò .svo Îļþµ½´°¿Ú£¬»òÊäÈëÎļþ·¾¶(Ö§³Ö¶à¸öÎļþ)")
+ Console.ForegroundColor = ConsoleColor.White
+ Console.WriteLine("ÊäÈë 'Patcher' Æô¶¯Í¬Ä¿Â¼ÏµÄDDSÐÞ²¹¹¤¾ß")
+ Console.WriteLine("ÊäÈë 'exit' Í˳ö³ÌÐò")
+
+ ' ³ÖÐø´¦ÀíÑ»·
+ While True
+ Console.WriteLine()
+ Console.ForegroundColor = ConsoleColor.Blue
+ Console.Write("[Extractor]")
+ Console.ForegroundColor = ConsoleColor.White
+ Console.Write("> ")
+ Dim input As String = Console.ReadLine()
+
+ ' ¼ì²éÌØÊâÃüÁî
+ Select Case input.Trim().ToLower()
+ Case "patcher"
+ Console.WriteLine($"µ±Ç°Â·¾¶£º{currentPath}")
+ If File.Exists(targetExePath) Then
+ Console.ForegroundColor = ConsoleColor.Green
+ Console.WriteLine("ÕýÔÚÆô¶¯...")
+ Console.ForegroundColor = ConsoleColor.White
+ Try
+ ' ʹÓÃProcessÆô¶¯³ÌÐò£¨²»µÈ´ýÍ˳ö£©
+ Dim processInfo As New ProcessStartInfo() With {
+ .FileName = targetExePath,
+ .UseShellExecute = True ' ʹÓÃShellÖ´ÐпÉÒÔ±ÜÃâ×èÈû
+ }
+
+ Process.Start(processInfo)
+ Console.WriteLine("Òѳ¢ÊÔÆô¶¯DDSPatcher")
+ Catch ex As Exception
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine($"Æô¶¯DDSÐÞ²¹¹¤¾ßʱ³ö´í£º{ex.Message}")
+ Console.ForegroundColor = ConsoleColor.White
+ End Try
+ Else
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine("´íÎó£ºÔÚµ±Ç°Ä¿Â¼ÏÂδÕÒµ½DDSPatcher.exe£¬ÇëÈ·ÈÏÄúÊÇ·ñÒѾ½«Æä·ÅÈëDDSExtractorËùÔÚµÄÎļþ¼ÐÄÚ")
+ Console.ForegroundColor = ConsoleColor.White
+ End If
+ Continue While
+ Case "clear"
+ Console.Clear()
+ Continue While
+ Case "help", "about", "version"
+ Console.ForegroundColor = ConsoleColor.DarkCyan
+ Console.WriteLine($"DDS ÎļþÌáÈ¡¹¤¾ß {Version} by ChilorXN.")
+ Console.ForegroundColor = ConsoleColor.DarkYellow
+ Console.WriteLine("ÇëÍÏ·ÅÒª´¦ÀíµÄ .afb »ò .svo Îļþµ½´°¿Ú£¬»òÊäÈëÎļþ·¾¶(Ö§³Ö¶à¸öÎļþ)")
+ Console.ForegroundColor = ConsoleColor.White
+ Console.WriteLine("ÊäÈë 'Patcher' Æô¶¯Í¬Ä¿Â¼ÏµÄDDSÐÞ²¹¹¤¾ß")
+ Console.WriteLine("ÊäÈë 'clear' Çå¿ÕÆÁÄ»")
+ Console.WriteLine("ÊäÈë 'help' Ôٴβ鿴°ïÖú")
+ Console.WriteLine("ÊäÈë 'exit' Í˳ö³ÌÐò")
+ Continue While
+ Case "exit", "quit"
+ Exit While
+ End Select
+
+ ' ´¦ÀíÊäÈëµÄÎļþ
+ ProcessInputFiles(input)
+ End While
+
+ Console.WriteLine("³ÌÐòÒÑÍ˳ö")
+ End Sub
+
+ Private Sub ProcessInputFiles(input As String)
+ ' ´¦ÀíÍϷŵÄÎļþ·¾¶(WindowsÖÕ¶Ë»áÓÃÒýºÅ°ü¹ü´ø¿Õ¸ñµÄÎļþ·¾¶)
+ Dim filePaths As New List(Of String)()
+ Dim inQuotes As Boolean = False
+ Dim currentPath As New System.Text.StringBuilder()
+
+ For Each c As Char In input
+ If c = """"c Then
+ If inQuotes Then
+ ' ½áÊøÒýºÅ°ü¹üµÄ·¾¶
+ filePaths.Add(currentPath.ToString())
+ currentPath.Clear()
+ inQuotes = False
+ Else
+ ' ¿ªÊ¼ÒýºÅ°ü¹üµÄ·¾¶
+ inQuotes = True
+ End If
+ ElseIf Not inQuotes AndAlso Char.IsWhiteSpace(c) Then
+ ' ·ÇÒýºÅ°ü¹üµÄ¿Õ¸ñ·Ö¸ô·û
+ If currentPath.Length > 0 Then
+ filePaths.Add(currentPath.ToString())
+ currentPath.Clear()
+ End If
+ Else
+ ' Ìí¼Óµ½µ±Ç°Â·¾¶
+ currentPath.Append(c)
+ End If
+ Next
+
+ ' Ìí¼Ó×îºóÒ»¸ö·¾¶
+ If currentPath.Length > 0 Then
+ filePaths.Add(currentPath.ToString())
+ End If
+
+ ' ´¦Àíÿ¸öÎļþ
+ For Each filePath In filePaths
+ If Not String.IsNullOrWhiteSpace(filePath) Then
+ Try
+ ProcessFile(filePath)
+ Catch ex As Exception
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine($"´¦ÀíÎļþ {filePath} ʱ³ö´í: {ex.Message}")
+ Console.ForegroundColor = ConsoleColor.White
+ End Try
+ End If
+ Next
+ End Sub
+
+ Private Sub ProcessFile(filePath As String)
+ If Not File.Exists(filePath) Then
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine($"Îļþ²»´æÔÚ: {filePath}")
+ Console.ForegroundColor = ConsoleColor.White
+ Return
+ End If
+
+ Dim extension As String = Path.GetExtension(filePath).ToLower()
+ If extension <> ".afb" AndAlso extension <> ".svo" Then
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine($"²»Ö§³ÖµÄÎļþÀàÐÍ: {filePath} (½öÖ§³Ö .afb ºÍ .svo)")
+ Console.ForegroundColor = ConsoleColor.White
+ Return
+ End If
+
+ Console.WriteLine($"ÕýÔÚ´¦ÀíÎļþ: {filePath}")
+
+ Dim fileData As Byte() = File.ReadAllBytes(filePath)
+ Dim ddsList As List(Of Byte()) = ExtractDdsFiles(fileData, extension = ".afb")
+
+ Console.WriteLine($"ÕÒµ½ {ddsList.Count} ¸ö DDS Îļþ")
+
+ ' ±£´æÌáÈ¡µÄ DDS Îļþ
+ Dim baseName As String = Path.GetFileNameWithoutExtension(filePath)
+ Dim outputDir As String = Path.Combine(Path.GetDirectoryName(filePath), $"{baseName}_extracted")
+
+ Directory.CreateDirectory(outputDir)
+
+ For i As Integer = 0 To ddsList.Count - 1
+ Dim outputPath As String = Path.Combine(outputDir, $"{baseName}_{i + 1}.dds")
+ File.WriteAllBytes(outputPath, ddsList(i))
+ Console.ForegroundColor = ConsoleColor.Green
+ Console.WriteLine($"Òѱ£´æ: {outputPath}")
+ Console.ForegroundColor = ConsoleColor.White
+ Next
+ End Sub
+
+ Private Function ExtractDdsFiles(fileData As Byte(), isAfbFile As Boolean) As List(Of Byte())
+ Dim ddsFiles As New List(Of Byte())()
+ Dim position As Integer = 0
+
+ While position < fileData.Length - 4
+ ' ¼ì²éÊÇ·ñÊÇ DDS ÎļþÍ·
+ If fileData(position) = DDS_HEADER(0) AndAlso
+ fileData(position + 1) = DDS_HEADER(1) AndAlso
+ fileData(position + 2) = DDS_HEADER(2) AndAlso
+ fileData(position + 3) = DDS_HEADER(3) Then
+
+ ' ²éÕÒÏÂÒ»¸ö DDS ÎļþÍ·»ò½áÊø±ê¼Ç
+ Dim nextDdsPos As Integer = FindNextDdsHeader(fileData, position + 4)
+ Dim endPos As Integer = If(nextDdsPos <> -1, nextDdsPos, fileData.Length)
+
+ ' ¶ÔÓÚ AFB Îļþ£¬¼ì²éÊÇ·ñÓÐ POF ±ê¼Ç
+ If isAfbFile AndAlso nextDdsPos = -1 Then
+ Dim pofPos As Integer = FindPofMarker(fileData, position + 4)
+ If pofPos <> -1 Then
+ endPos = pofPos
+ End If
+ End If
+
+ ' ÌáÈ¡ DDS Êý¾Ý
+ Dim ddsLength As Integer = endPos - position
+ Dim ddsData(ddsLength - 1) As Byte
+ Array.Copy(fileData, position, ddsData, 0, ddsLength)
+ ddsFiles.Add(ddsData)
+
+ position = endPos
+ Else
+ position += 1
+ End If
+ End While
+
+ Return ddsFiles
+ End Function
+
+ Private Function FindNextDdsHeader(data As Byte(), startPos As Integer) As Integer
+ For i As Integer = startPos To data.Length - 4
+ If data(i) = DDS_HEADER(0) AndAlso
+ data(i + 1) = DDS_HEADER(1) AndAlso
+ data(i + 2) = DDS_HEADER(2) AndAlso
+ data(i + 3) = DDS_HEADER(3) Then
+ Return i
+ End If
+ Next
+ Return -1
+ End Function
+
+ Private Function FindPofMarker(data As Byte(), startPos As Integer) As Integer
+ ' POF ±ê¼ÇÊÇ ASCII ×Ö·û´® "POF"
+ For i As Integer = startPos To data.Length - 3
+ If data(i) = AscW("P"c) AndAlso
+ data(i + 1) = AscW("O"c) AndAlso
+ data(i + 2) = AscW("F"c) Then
+ Return i
+ End If
+ Next
+ Return -1
+ End Function
+End Module
\ No newline at end of file
diff --git a/DDSPatcher/DDSPatcher.vbproj b/DDSPatcher/DDSPatcher.vbproj
new file mode 100644
index 0000000..ac0c431
--- /dev/null
+++ b/DDSPatcher/DDSPatcher.vbproj
@@ -0,0 +1,9 @@
+
+
+
+ Exe
+ DDSPatcher
+ net8.0
+
+
+
diff --git a/DDSPatcher/Program.vb b/DDSPatcher/Program.vb
new file mode 100644
index 0000000..1c3543f
--- /dev/null
+++ b/DDSPatcher/Program.vb
@@ -0,0 +1,385 @@
+Imports System
+Imports System.IO
+Imports System.Text
+Imports System.Threading
+Imports System.Diagnostics
+
+Module DdsPatcher
+ ' DDS ÎļþÍ·±êʶ
+ Private ReadOnly DDS_HEADER As Byte() = {&H44, &H44, &H53, &H20} ' "DDS "
+ Private ReadOnly POF_MARKER As String = "POF"
+ Private autoPatchMode As Boolean = False
+ Private ignoreWarn As Boolean = False
+ Public Const Version As String = "v1.2.2"
+ Dim currentPath As String = AppDomain.CurrentDomain.BaseDirectory
+ Dim targetExePath As String = Path.Combine(currentPath, "DDSExtractor.exe")
+
+ Sub Main()
+ Console.ForegroundColor = ConsoleColor.DarkCyan
+ Console.WriteLine($"DDS ÎļþÐÞ²¹¹¤¾ß {Version} by ChilorXN.")
+ Console.ForegroundColor = ConsoleColor.DarkYellow
+ Console.WriteLine("ʹÓÃ˵Ã÷: Ô´Îļþ·¾¶ Ð޸ĵÄDDS·¾¶ DDSÐòºÅ(´Ó1¿ªÊ¼)")
+ Console.WriteLine("ʾÀý: ""C:\files\model.afb"" ""C:\modified\dds_1.dds"" 1")
+ Console.ForegroundColor = ConsoleColor.White
+ Console.WriteLine("ÊäÈë 'EnableAutoPatch' Ìø¹ý¶þ´ÎÈ·ÈÏ")
+ Console.WriteLine("ÊäÈë 'DisableAutoPatch' »Ö¸´¶þ´ÎÈ·ÈÏ")
+ Console.WriteLine("ÊäÈë 'Extractor' Æô¶¯Í¬Ä¿Â¼ÏµÄDDSÌáÈ¡Æ÷")
+ Console.WriteLine("ÊäÈë 'exit' Í˳ö³ÌÐò")
+
+ ' ³ÖÐø´¦ÀíÑ»·
+ While True
+ Console.WriteLine()
+ If autoPatchMode Then
+ If Not ignoreWarn Then
+ Console.ForegroundColor = ConsoleColor.DarkYellow
+ Console.WriteLine("ÒÑÆôÓÃ×Ô¶¯ÐÞ²¹Ä£Ê½£¬½«Ìø¹ýÈ·ÈÏÖ±½Ó½øÐÐÇ¿ÖÆÐÞ²¹£¡")
+ Console.WriteLine("ÈôÄúûÓÐʹÓøù¦ÄܵıØÒª£¬ÇëʹÓà 'DisableAutoPatch' ÃüÁî¹Ø±Õ¸Ã¹¦ÄÜ")
+ End If
+ Console.ForegroundColor = ConsoleColor.Red
+ Else
+ Console.ForegroundColor = ConsoleColor.Green
+ End If
+ Console.Write($"[{(If(autoPatchMode, "×Ô¶¯ÐÞ²¹Ä£Ê½", "Õý³£Ä£Ê½"))}]")
+ Console.ForegroundColor = ConsoleColor.White
+ Console.Write("> ")
+ Dim input As String = Console.ReadLine()
+
+ ' ¼ì²éÌØÊâÃüÁî
+ Select Case input.Trim().ToLower()
+ Case "exit", "quit"
+ Exit While
+ Case "enableautopatch"
+ autoPatchMode = True
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine("ÒÑÆôÓÃ×Ô¶¯ÐÞ²¹Ä£Ê½£¬½«Ìø¹ýÈ·ÈÏÖ±½Ó½øÐÐÇ¿ÖÆÐÞ²¹£¡")
+ Console.ForegroundColor = ConsoleColor.White
+ Continue While
+ Case "disableautopatch"
+ autoPatchMode = False
+ Console.ForegroundColor = ConsoleColor.Green
+ Console.WriteLine("ÒÑÍ£ÓÃ×Ô¶¯ÐÞ²¹Ä£Ê½£¬½«»Ö¸´¶þ´ÎÈ·ÈÏÁ÷³Ì")
+ Console.ForegroundColor = ConsoleColor.White
+ Continue While
+ Case "extractor"
+ Console.WriteLine($"µ±Ç°Â·¾¶£º{currentPath}")
+ If File.Exists(targetExePath) Then
+ Console.ForegroundColor = ConsoleColor.Green
+ Console.WriteLine("ÕýÔÚÆô¶¯...")
+ Console.ForegroundColor = ConsoleColor.White
+ Try
+ ' ʹÓÃProcessÆô¶¯³ÌÐò£¨²»µÈ´ýÍ˳ö£©
+ Dim processInfo As New ProcessStartInfo() With {
+ .FileName = targetExePath,
+ .UseShellExecute = True ' ʹÓÃShellÖ´ÐпÉÒÔ±ÜÃâ×èÈû
+ }
+
+ Process.Start(processInfo)
+ Console.WriteLine("Òѳ¢ÊÔÆô¶¯DDSExtractor")
+ Catch ex As Exception
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine($"Æô¶¯DDSÌáÈ¡Æ÷ʱ³ö´í£º{ex.Message}")
+ Console.ForegroundColor = ConsoleColor.White
+ End Try
+ Else
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine("´íÎó£ºÔÚµ±Ç°Ä¿Â¼ÏÂδÕÒµ½DDSExtractor.exe£¬ÇëÈ·ÈÏÄúÊÇ·ñÒѾ½«Æä·ÅÈëDDSPatcherËùÔÚµÄÎļþ¼ÐÄÚ")
+ Console.ForegroundColor = ConsoleColor.White
+ End If
+ Continue While
+ Case "ignorewarn"
+ ignoreWarn = True
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine("ÒÑÆôÓúöÂÔ¾¯¸æ¹¦ÄÜ£¬´Ë´ÎʹÓý«²»»áÔٴγöÏÖ×Ô¶¯ÐÞ²¹Ä£Ê½µÄÌáʾ£¡°´ÏÂEnterǰÇëÔÙÈýÈ·ÈÏ£¡£¡£¡")
+ Console.ForegroundColor = ConsoleColor.DarkYellow
+ Console.WriteLine("ÈçÐèÍ˳ö£¬ÇëÊäÈë 'Reset' »òÖØÆô³ÌÐò")
+ Console.ForegroundColor = ConsoleColor.White
+ Continue While
+ Case "clear"
+ Console.Clear()
+ Continue While
+ Case "reset"
+ autoPatchMode = False
+ ignoreWarn = False
+ Console.ForegroundColor = ConsoleColor.DarkCyan
+ Console.WriteLine("ÉèÖÃÒÑÖØÖ㬽«ÔÚ3ÃëºóÇåÆÁ...")
+ Console.ForegroundColor = ConsoleColor.White
+ Thread.Sleep(3000)
+ Console.Clear()
+ Continue While
+ Case "help", "about", "version"
+ Console.ForegroundColor = ConsoleColor.DarkCyan
+ Console.WriteLine($"DDS ÎļþÐÞ²¹¹¤¾ß {Version} by ChilorXN.")
+ Console.ForegroundColor = ConsoleColor.DarkYellow
+ Console.WriteLine("ʹÓÃ˵Ã÷: Ô´Îļþ·¾¶ Ð޸ĵÄDDS·¾¶ DDSÐòºÅ(´Ó1¿ªÊ¼)")
+ Console.WriteLine("ʾÀý: ""C:\files\model.afb"" ""C:\modified\dds_1.dds"" 1")
+ Console.ForegroundColor = ConsoleColor.White
+ Console.WriteLine("ÊäÈë 'EnableAutoPatch' Ìø¹ý¶þ´ÎÈ·ÈÏ")
+ Console.WriteLine("ÊäÈë 'DisableAutoPatch' »Ö¸´¶þ´ÎÈ·ÈÏ")
+ Console.WriteLine("ÊäÈë 'Extractor' Æô¶¯Í¬Ä¿Â¼ÏµÄDDSÌáÈ¡Æ÷(ÈôÓÐ)")
+ Console.WriteLine("ÊäÈë 'clear' Çå¿ÕÆÁÄ»")
+ Console.WriteLine("ÊäÈë 'reset' ÖØÖÃÉ趨")
+ Console.WriteLine("ÊäÈë 'help' Ôٴβ鿴°ïÖú")
+ Console.WriteLine("ÊäÈë 'exit' Í˳ö³ÌÐò")
+ Continue While
+ End Select
+
+ ' ´¦ÀíÊäÈë
+ ProcessPatchCommand(input)
+ End While
+
+ Console.WriteLine("³ÌÐòÒÑÍ˳ö")
+ End Sub
+
+ Private Sub ProcessPatchCommand(input As String)
+ Dim args As String() = ParseCommandLine(input)
+
+ If args.Length <> 3 Then
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine("´íÎó: ÐèÒª3¸ö²ÎÊý - Ô´Îļþ·¾¶ Ð޸ĵÄDDS·¾¶ DDSÐòºÅ")
+ Console.ForegroundColor = ConsoleColor.White
+ Return
+ End If
+
+ Dim sourceFile As String = args(0)
+ Dim modifiedDdsFile As String = args(1)
+ Dim ddsIndex As Integer
+
+ ' ÑéÖ¤DDSÐòºÅ
+ If Not Integer.TryParse(args(2), ddsIndex) OrElse ddsIndex < 1 Then
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine("´íÎó: DDSÐòºÅ±ØÐëÊÇ´óÓÚ0µÄÕûÊý")
+ Console.ForegroundColor = ConsoleColor.White
+ Return
+ End If
+
+ ' ÑéÖ¤Îļþ´æÔÚ
+ If Not File.Exists(sourceFile) Then
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine($"´íÎó: Ô´Îļþ²»´æÔÚ - {sourceFile}")
+ Console.ForegroundColor = ConsoleColor.White
+ Return
+ End If
+
+ If Not File.Exists(modifiedDdsFile) Then
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine($"´íÎó: Ð޸ĵÄDDSÎļþ²»´æÔÚ - {modifiedDdsFile}")
+ Console.ForegroundColor = ConsoleColor.White
+ Return
+ End If
+
+ ' »ñÈ¡ÎļþÀ©Õ¹Ãû
+ Dim extension As String = Path.GetExtension(sourceFile).ToLower()
+ If extension <> ".afb" AndAlso extension <> ".svo" Then
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine($"´íÎó: ²»Ö§³ÖµÄÎļþÀàÐÍ - {extension} (½öÖ§³Ö .afb ºÍ .svo)")
+ Console.ForegroundColor = ConsoleColor.White
+ Return
+ End If
+
+ Try
+ PatchDdsFile(sourceFile, modifiedDdsFile, ddsIndex, extension = ".afb")
+ Catch ex As Exception
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine($"ÐÞ²¹¹ý³ÌÖгö´í: {ex.Message}")
+ Console.ForegroundColor = ConsoleColor.White
+ End Try
+ End Sub
+
+ Private Sub PatchDdsFile(sourceFile As String, modifiedDdsFile As String, ddsIndex As Integer, isAfbFile As Boolean)
+ ' ¶ÁȡԴÎļþ
+ Dim sourceData As Byte() = File.ReadAllBytes(sourceFile)
+
+ ' ²éÕÒËùÓÐDDSλÖÃ
+ Dim ddsPositions As List(Of DdsInfo) = LocateAllDdsFiles(sourceData, isAfbFile)
+
+ ' ¼ì²éÇëÇóµÄDDSË÷ÒýÊÇ·ñÓÐЧ
+ If ddsIndex > ddsPositions.Count Then
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine($"´íÎó: ÎļþÖÐÖ»°üº¬ {ddsPositions.Count} ¸öDDSÎļþ£¬ÎÞ·¨·ÃÎÊµÚ {ddsIndex} ¸ö")
+ Console.ForegroundColor = ConsoleColor.White
+ Return
+ End If
+
+ Dim targetDds As DdsInfo = ddsPositions(ddsIndex - 1)
+
+ ' ¶ÁÈ¡Ð޸ĺóµÄDDSÎļþ
+ Dim modifiedDdsData As Byte() = File.ReadAllBytes(modifiedDdsFile)
+
+ ' ÑéÖ¤DDSÍ·
+ If modifiedDdsData.Length < 4 OrElse
+ Not (modifiedDdsData(0) = DDS_HEADER(0) AndAlso
+ modifiedDdsData(1) = DDS_HEADER(1) AndAlso
+ modifiedDdsData(2) = DDS_HEADER(2) AndAlso
+ modifiedDdsData(3) = DDS_HEADER(3)) Then
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine("´íÎó: Ð޸ĵÄDDSÎļþûÓÐÓÐЧµÄDDSÍ·")
+ Console.ForegroundColor = ConsoleColor.White
+ Return
+ End If
+
+ ' ÑéÖ¤´óС
+ If modifiedDdsData.Length <> targetDds.Length Then
+ Console.ForegroundColor = ConsoleColor.DarkYellow
+ Console.WriteLine($"¾¯¸æ: DDS´óС²»Æ¥Åä (Ô: {targetDds.Length} ×Ö½Ú, ÐÂ: {modifiedDdsData.Length} ×Ö½Ú, Ïà²î {modifiedDdsData.Length - targetDds.Length} ×Ö½Ú)")
+ Console.WriteLine($"ÔDDSλÖÃ: ÎļþÆ«ÒÆ 0x{targetDds.StartOffset:X8}")
+ Console.ForegroundColor = ConsoleColor.White
+
+ ' Èç¹ûÐÂDDS±ÈÔDDS´ó£¬Ö±½Ó¾Ü¾ø
+ If modifiedDdsData.Length > targetDds.Length Then
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine("´íÎó: ÐÂDDS±ÈÔDDS´ó£¬ÎÞ·¨ÐÞ²¹")
+ Console.ForegroundColor = ConsoleColor.White
+ Return
+ End If
+
+ ' Èç¹ûÐÂDDS±ÈÔDDSС£¬¸ù¾Ýģʽ´¦Àí
+ If Not autoPatchMode Then
+ Console.WriteLine("ÐÅÏ¢: ¼ì²âµ½ÐÂDDS±ÈÔDDSС£¬¿É³¢ÊÔʹÓÃÇ¿ÖÆÐÞ²¹")
+ Console.ForegroundColor = ConsoleColor.DarkYellow
+ Console.WriteLine("¾¯¸æ: Ç¿ÖÆÐÞ²¹¿ÉÄܻᵼÖÂÎÊÌâ!")
+ Console.ForegroundColor = ConsoleColor.White
+ Console.WriteLine("ÊÇ·ñҪʹÓÃÇ¿ÖÆÐÞ²¹? (y/n)")
+ Dim response As String = Console.ReadLine().Trim().ToLower()
+
+ If response <> "y" AndAlso response <> "yes" Then
+ Console.WriteLine("ÐÞ²¹ÒÑÈ¡Ïû")
+ Return
+ End If
+
+ ' ¶þ´ÎÈ·ÈÏ
+ Console.ForegroundColor = ConsoleColor.Red
+ Console.WriteLine("È·¶¨ÒªÊ¹ÓÃÇ¿ÖÆÐÞ²¹Âð? Õâ¿ÉÄÜ»áÆÆ»µÎļþ½á¹¹! (yes/no)")
+ Console.ForegroundColor = ConsoleColor.White
+ Dim confirm As String = Console.ReadLine().Trim().ToLower()
+
+ If confirm <> "yes" Then
+ Console.WriteLine("ÐÞ²¹ÒÑÈ¡Ïû")
+ Return
+ End If
+ Else
+ Console.ForegroundColor = ConsoleColor.DarkYellow
+ Console.WriteLine("¾¯¸æ£º¼ì²âµ½ÒÑÆôÓÃ×Ô¶¯ÐÞ²¹Ä£Ê½£¬½«Ìø¹ý¶þ´ÎÈ·ÈÏÖ±½Ó½øÐÐÇ¿ÖÆÐÞ²¹£¡")
+ Console.ForegroundColor = ConsoleColor.White
+ End If
+ End If
+
+ ' ´´½¨±¸·Ý
+ Dim backupFile As String = sourceFile & ".bak"
+ If Not File.Exists(backupFile) Then
+ File.Copy(sourceFile, backupFile)
+ Console.ForegroundColor = ConsoleColor.Green
+ Console.WriteLine($"ÒÑ´´½¨±¸·ÝÎļþ: {backupFile}")
+ Console.ForegroundColor = ConsoleColor.White
+ End If
+
+ ' Ö´ÐÐÐÞ²¹
+ If modifiedDdsData.Length < targetDds.Length Then
+ ' ½ö¸²¸ÇÐ޸ĺóµÄDDS²¿·Ö£¬±£ÁôÊ£Óಿ·Ö²»±ä
+ Array.Copy(modifiedDdsData, 0, sourceData, targetDds.StartOffset, modifiedDdsData.Length)
+ Console.ForegroundColor = ConsoleColor.DarkYellow
+ Console.WriteLine($"¾¯¸æ: ½öÐÞ²¹ÁËǰ {modifiedDdsData.Length} ×Ö½Ú£¬±£ÁôÁËÔDDSµÄ {targetDds.Length - modifiedDdsData.Length} ×Ö½ÚδÐÞ¸Ä")
+ Console.ForegroundColor = ConsoleColor.White
+ Else
+ ' ÍêÈ«Ìæ»»
+ Array.Copy(modifiedDdsData, 0, sourceData, targetDds.StartOffset, targetDds.Length)
+ End If
+
+ ' ±£´æÐ޸ĺóµÄÎļþ
+ File.WriteAllBytes(sourceFile, sourceData)
+ Console.ForegroundColor = ConsoleColor.Green
+ Console.WriteLine($"³É¹¦½«µÚ {ddsIndex} ¸öDDSÐÞ²¹µ½ {sourceFile}")
+ Console.ForegroundColor = ConsoleColor.White
+ End Sub
+
+ Private Function LocateAllDdsFiles(fileData As Byte(), isAfbFile As Boolean) As List(Of DdsInfo)
+ Dim ddsList As New List(Of DdsInfo)()
+ Dim position As Integer = 0
+
+ While position < fileData.Length - 4
+ ' ¼ì²éÊÇ·ñÊÇ DDS ÎļþÍ·
+ If fileData(position) = DDS_HEADER(0) AndAlso
+ fileData(position + 1) = DDS_HEADER(1) AndAlso
+ fileData(position + 2) = DDS_HEADER(2) AndAlso
+ fileData(position + 3) = DDS_HEADER(3) Then
+
+ ' ²éÕÒÏÂÒ»¸ö DDS ÎļþÍ·»ò½áÊø±ê¼Ç
+ Dim nextDdsPos As Integer = FindNextDdsHeader(fileData, position + 4)
+ Dim endPos As Integer = If(nextDdsPos <> -1, nextDdsPos, fileData.Length)
+
+ ' ¶ÔÓÚ AFB Îļþ£¬¼ì²éÊÇ·ñÓÐ POF ±ê¼Ç
+ If isAfbFile AndAlso nextDdsPos = -1 Then
+ Dim pofPos As Integer = FindPofMarker(fileData, position + 4)
+ If pofPos <> -1 Then
+ endPos = pofPos
+ End If
+ End If
+
+ ' ¼Ç¼DDSÐÅÏ¢
+ ddsList.Add(New DdsInfo With {
+ .StartOffset = position,
+ .Length = endPos - position
+ })
+
+ position = endPos
+ Else
+ position += 1
+ End If
+ End While
+
+ Return ddsList
+ End Function
+
+ Private Function FindNextDdsHeader(data As Byte(), startPos As Integer) As Integer
+ For i As Integer = startPos To data.Length - 4
+ If data(i) = DDS_HEADER(0) AndAlso
+ data(i + 1) = DDS_HEADER(1) AndAlso
+ data(i + 2) = DDS_HEADER(2) AndAlso
+ data(i + 3) = DDS_HEADER(3) Then
+ Return i
+ End If
+ Next
+ Return -1
+ End Function
+
+ Private Function FindPofMarker(data As Byte(), startPos As Integer) As Integer
+ ' POF ±ê¼ÇÊÇ ASCII ×Ö·û´® "POF"
+ For i As Integer = startPos To data.Length - 3
+ If data(i) = AscW("P"c) AndAlso
+ data(i + 1) = AscW("O"c) AndAlso
+ data(i + 2) = AscW("F"c) Then
+ Return i
+ End If
+ Next
+ Return -1
+ End Function
+
+ Private Function ParseCommandLine(input As String) As String()
+ Dim args As New List(Of String)()
+ Dim currentArg As New StringBuilder()
+ Dim inQuotes As Boolean = False
+
+ For Each c As Char In input
+ If c = """"c Then
+ inQuotes = Not inQuotes
+ ElseIf Not inQuotes AndAlso Char.IsWhiteSpace(c) Then
+ If currentArg.Length > 0 Then
+ args.Add(currentArg.ToString())
+ currentArg.Clear()
+ End If
+ Else
+ currentArg.Append(c)
+ End If
+ Next
+
+ If currentArg.Length > 0 Then
+ args.Add(currentArg.ToString())
+ End If
+
+ Return args.ToArray()
+ End Function
+
+ Private Structure DdsInfo
+ Public StartOffset As Integer
+ Public Length As Integer
+ End Structure
+End Module
\ No newline at end of file