-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSfxProcessor.cs
More file actions
166 lines (144 loc) · 6.94 KB
/
SfxProcessor.cs
File metadata and controls
166 lines (144 loc) · 6.94 KB
1
2
3
4
5
6
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
using System;
using System.Diagnostics;
using System.IO;
namespace SSPBundleTool
{
public static class SfxProcessor
{
private static readonly byte[] SevenZipSignature = { 0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C };
private static readonly string[] SevenZipCandidates =
{
@"C:\Program Files\7-Zip\7z.exe",
@"C:\Program Files (x86)\7-Zip\7z.exe",
};
public static void AddNarToSfx(
string inputSfxPath,
string narFilePath,
string outputSfxPath,
IProgress<string> progress)
{
string sevenZipExe = FindSevenZip();
if (sevenZipExe == null)
throw new FileNotFoundException(
"7-Zip (7z.exe) が見つかりませんでした。\n" +
"https://www.7-zip.org/ から7-Zipをインストールしてください。");
progress.Report($"7-Zip を使用: {sevenZipExe}");
progress.Report("入力ファイルを読み込み中...");
byte[] inputBytes = File.ReadAllBytes(inputSfxPath);
progress.Report("7zシグネチャを検索中...");
int sigOffset = FindSignature(inputBytes, SevenZipSignature);
if (sigOffset < 0)
throw new InvalidDataException("7zシグネチャが見つかりませんでした。入力ファイルが正しいSFXファイルか確認してください。");
progress.Report($"7zアーカイブ開始オフセット: {sigOffset} bytes");
byte[] prefix = new byte[sigOffset];
Array.Copy(inputBytes, 0, prefix, 0, sigOffset);
string tempFile = Path.GetTempFileName();
// 拡張子を .7z にしないと 7z.exe が形式を認識しない場合があるため改名
string temp7z = tempFile + ".7z";
// アーカイブ内でのファイル名を initial_install.nar に固定するための準備。
// データコピーを避けるため、ファイル名が異なる場合は同ディレクトリ内でリネームして追加し
// finally で必ず元の名前に戻す。ただし同名ファイルが既に存在する場合は一時ディレクトリへの
// コピーにフォールバックする。
string narDir = Path.GetDirectoryName(Path.GetFullPath(narFilePath));
bool alreadyCorrectName = string.Equals(
Path.GetFileName(narFilePath), "initial_install.nar",
StringComparison.OrdinalIgnoreCase);
string narPathForArchive = null;
string narWorkDir = null;
string renamedOriginalPath = null; // リネームした元のパス(finallyで戻すため)
try
{
progress.Report("7zアーカイブ部分を一時ファイルに書き出し中...");
File.Delete(tempFile);
using (var fs = File.Create(temp7z))
{
fs.Write(inputBytes, sigOffset, inputBytes.Length - sigOffset);
}
if (alreadyCorrectName)
{
// ファイル名が既に initial_install.nar なのでそのまま使用
narPathForArchive = narFilePath;
narWorkDir = narDir;
}
else
{
string targetPath = Path.Combine(narDir, "initial_install.nar");
if (File.Exists(targetPath))
throw new IOException(
$"リネーム先のファイルが既に存在します。削除してから再試行してください。\n{targetPath}");
// 同ディレクトリ内でリネーム(データコピーなし)
File.Move(narFilePath, targetPath);
renamedOriginalPath = narFilePath;
narPathForArchive = targetPath;
narWorkDir = narDir;
}
progress.Report("initial_install.nar をアーカイブに追加中...");
// -w で作業ディレクトリを指定し、ファイル名のみがパスに使われるようにする
RunSevenZip(sevenZipExe, $"a \"{temp7z}\" \"{narPathForArchive}\" -w\"{narWorkDir}\"", progress);
progress.Report($"出力ファイルを書き込み中: {outputSfxPath}");
byte[] newArchiveBytes = File.ReadAllBytes(temp7z);
using (var outStream = File.Create(outputSfxPath))
{
outStream.Write(prefix, 0, prefix.Length);
outStream.Write(newArchiveBytes, 0, newArchiveBytes.Length);
}
progress.Report("完了しました!");
}
finally
{
if (File.Exists(temp7z))
File.Delete(temp7z);
// リネームしていた場合は元のファイル名に戻す
if (renamedOriginalPath != null && File.Exists(narPathForArchive))
File.Move(narPathForArchive, renamedOriginalPath);
}
}
private static void RunSevenZip(string exe, string arguments, IProgress<string> progress)
{
var psi = new ProcessStartInfo(exe, arguments)
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
};
using (var proc = Process.Start(psi))
{
string stdout = proc.StandardOutput.ReadToEnd();
string stderr = proc.StandardError.ReadToEnd();
proc.WaitForExit();
if (!string.IsNullOrWhiteSpace(stdout))
foreach (var line in stdout.Split('\n'))
if (!string.IsNullOrWhiteSpace(line))
progress.Report(line.TrimEnd());
if (proc.ExitCode != 0)
throw new Exception($"7z.exe がエラーで終了しました (exit code {proc.ExitCode})。\n{stderr}");
}
}
private static string FindSevenZip()
{
foreach (var path in SevenZipCandidates)
if (File.Exists(path))
return path;
return null;
}
private static int FindSignature(byte[] data, byte[] signature)
{
for (int i = 0; i <= data.Length - signature.Length; i++)
{
bool match = true;
for (int j = 0; j < signature.Length; j++)
{
if (data[i + j] != signature[j])
{
match = false;
break;
}
}
if (match)
return i;
}
return -1;
}
}
}