From 280f128a849e7ccebe54771ae56701d86649f0d3 Mon Sep 17 00:00:00 2001 From: Chilor <1275282135@qq.com> Date: Sat, 2 Aug 2025 01:55:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChunithmAssetsTools.sln | 31 +++ DDSExtractor/DDSExtractor.vbproj | 9 + DDSExtractor/Program.vb | 232 +++++++++++++++++++ DDSPatcher/DDSPatcher.vbproj | 9 + DDSPatcher/Program.vb | 385 +++++++++++++++++++++++++++++++ 5 files changed, 666 insertions(+) create mode 100644 ChunithmAssetsTools.sln create mode 100644 DDSExtractor/DDSExtractor.vbproj create mode 100644 DDSExtractor/Program.vb create mode 100644 DDSPatcher/DDSPatcher.vbproj create mode 100644 DDSPatcher/Program.vb 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