-
Notifications
You must be signed in to change notification settings - Fork 0
/
FindDir.cs
223 lines (204 loc) · 9.37 KB
/
FindDir.cs
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
using System.Buffers;
using static Unpde.DataType;
using static Unpde.PdeTool;
namespace Unpde {
/// <summary>
/// 查找未解码目录类
/// </summary>
internal class FindDir {
/// <summary>
/// 缓冲区大小
/// </summary>
private const int BufferSize = 0x1000000;
/// <summary>
/// FindDirPDE文件地址
/// </summary>
private static string FindDirPDEPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ThisPdeName.Name, "FindDirPDE.hex");
/// <summary>
/// 原始PDE文件地址
/// </summary>
private static string PDEPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ThisPdeName.FullName);
/// <summary>
/// 记录偏移值覆盖信息
/// </summary>
/// <param name="Offset">偏移值</param>
/// <param name="Size">大小</param>
public static void Rec(uint Offset, uint Size) {
// FindDirPDE 不存在则创建
if (!File.Exists(FindDirPDEPath)) {
Console.WriteLine(" √FindDirPDE文件不存在,正在创建...");
using FileStream fs = File.Create(FindDirPDEPath);
// 设置文件长度为PdeSize
fs.SetLength(PdeSize);
}
// 将已覆盖区域标记为1
using (FileStream fs = new(FindDirPDEPath, FileMode.Open, FileAccess.ReadWrite)) {
fs.Seek(Offset, SeekOrigin.Begin);
byte[] buffer = Enumerable.Repeat((byte)1, (int)Size).ToArray();
fs.Write(buffer, 0, buffer.Length);
}
}
/// <summary>
/// 查找未解码目录
/// </summary>
public static void Find() {
Console.WriteLine(" !开始查找未解码目录...");
Console.WriteLine(" !TagsHash长度:" GVar.TagsHash.Count.ToString());
try {
using var fs = new FileStream(FindDirPDEPath, FileMode.Open, FileAccess.Read);
// 读取FindDirPDE,查找未覆盖区域,找到文件或文件夹标记区域
List<UnDecodedDir> undecodedBlocks = FindUndecodedBlocks(fs);
fs.Close();
// 存在目录或文件
if (undecodedBlocks.Count > 0) {
Console.WriteLine(" !本次总共找到 " undecodedBlocks.Count " 个未解码区域");
Console.WriteLine(" !前往解包");
// 尝试解包
TryUnpack(undecodedBlocks);
} else {
Console.WriteLine(" √未找到未解码目录");
}
} catch (Exception ex) {
Console.WriteLine(" !查找未解码目录失败: " ex.Message);
}
}
/// <summary>
/// 尝试解包
/// </summary>
/// <param name="unrecordedOffsets">未记录的目录偏移列表</param>
private static void TryUnpack(List<UnDecodedDir> unrecordedOffsets) {
foreach (UnDecodedDir unOffset in unrecordedOffsets) {
uint nowOffset = (uint)unOffset.OffSet;
uint NewSize = (uint)(unOffset.Size < 0x1000 ? unOffset.Size : 0x1000);
// 创建目录
var tryDir = new DirStr { UpDir = ThisPdeName.Name "/Other/", NowDir = ThisPdeName.Name "/Other/" unOffset.OffSet "/" };
// 记录日志,一定要记录
if (GVar.NeedOffsetLog) {
OffsetLog.Rec(nowOffset, nowOffset, nowOffset, NewSize, ThisPdeName.Name "/Other/", 2, ThisPdeName.Name "/Other/" unOffset.OffSet "/");
}
// 尝试解包
Unpack.Try(nowOffset, NewSize, tryDir, false);
}
Console.WriteLine(" √此次解包完成,再次查找目录!");
unrecordedOffsets.Clear();
// 再次查找
Find();
}
/// <summary>
/// 查找未解码目录
/// </summary>
/// <param name="fs">FindDirPDE文件流</param>
/// <returns>未解码目录列表</returns>
private static List<UnDecodedDir> FindUndecodedBlocks(FileStream fs) {
// 未解码目录列表
var undecodedBlocks = new List<UnDecodedDir>();
// Buffer
var buffer = ArrayPool<byte>.Shared.Rent(BufferSize);
try {
int bytesRead;
long currentOffset = 0;
while ((bytesRead = fs.Read(buffer, 0, BufferSize)) > 0) {
var span = new ReadOnlySpan<byte>(buffer, 0, bytesRead);
int startIndex = 0;
while (startIndex < bytesRead) {
int nonZeroIndex = span[startIndex..].IndexOfAnyExcept(new byte[] { 0 });
if (nonZeroIndex == -1) {
break;
}
startIndex = nonZeroIndex;
int endIndex = startIndex 1;
while (endIndex < bytesRead && span[endIndex] == 0) {
endIndex ;
}
long size = endIndex - startIndex;
// 0x1000 or 0x80
// 一般一个目录或目录块大小是0x1000,0x80 是最小单元,也就是只能存一个目录或文件
if (size >= 0x80) {
long offset = currentOffset startIndex;
// 记录未解码区域
UnDecodedDir ThisUnDecodedDir = new() { OffSet = 0, Size = 0 };
// 读取原始PDE区域
using var blockStream = new FileStream(PDEPath, FileMode.Open, FileAccess.Read);
blockStream.Position = offset;
var blockData = new byte[size];
blockStream.Read(blockData, 0, (int)size);
var fIndex = FindHexPattern(blockData, GVar.TagsHash);
if (fIndex != -1) {
//Console.WriteLine(" 找到16进制字符串: " fIndex.ToString("X"));
//Console.WriteLine(" 偏移地址: " offset.ToString("X"));
//Console.WriteLine(" 大小: " size.ToString("X"));
// 计算新偏移地址
uint NewOffset = (uint)(offset (fIndex - 0x74));
// 记录偏移值覆盖信息
ThisUnDecodedDir.OffSet = NewOffset;
ThisUnDecodedDir.Size = size;
}
// 判断是否存在文件或文件夹
if (ThisUnDecodedDir.Size != 0) {
// 记录未解码区域
undecodedBlocks.Add(ThisUnDecodedDir);
}
}
startIndex = endIndex;
}
currentOffset = bytesRead;
}
} finally {
ArrayPool<byte>.Shared.Return(buffer);
}
// 返回
return undecodedBlocks;
}
/// <summary>
/// 查找文件夹或文件特殊标记是否存在
/// </summary>
/// <param name="byteData">原始字节数据</param>
/// <param name="fdKey">文件夹或文件特殊标记</param>
/// <returns>-1: 不存在, 其他: 偏移地址 </returns>
private static int FindHexPattern(byte[] byteData, HashSet<string> fdKey) {
foreach (var hexPattern in fdKey) {
var patternBytes = StringToByteArray(hexPattern);
var index = FindByteArray(byteData, patternBytes);
if (index != -1) {
return index;
}
}
return -1;
}
/// <summary>
/// 字符串转字节数组
/// </summary>
/// <param name="hex"> 16进制字符串 </param>
/// <returns> 字节数组 </returns>
private static byte[] StringToByteArray(string hex) {
int length = hex.Length / 2;
var bytes = new byte[length];
for (int i = 0; i < length; i ) {
bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
}
return bytes;
}
/// <summary>
/// 查找字节数组
/// </summary>
/// <param name="data"> 原始字节数据 </param>
/// <param name="pattern"> 查找字节数组 </param>
/// <returns> -1: 不存在, 其他: 偏移地址 </returns>
private static int FindByteArray(byte[] data, byte[] pattern) {
int patternLength = pattern.Length;
for (int i = 0; i <= data.Length - patternLength; i ) {
bool match = true;
for (int j = 0; j < patternLength; j ) {
if (data[i j] != pattern[j]) {
match = false;
break;
}
}
if (match) {
return i;
}
}
return -1;
}
}
}