本帖最后由 冥界3大法王 于 2026-7-23 19:14 编辑
编个TotalCommander的*.jar、*.class搜索插件配合组织行动!
![]()
故事情节是这样的:
我要汉化这个Ghidra 12.1.2
可是没个像样的汉化工具来汉化Java程序(*.jar特殊的ZIP文件,解包后会得到*.class文件)
用这个老得掉渣的工具是过时的,落伍的。。没有效率的体现!
既然电脑里安装了VS2022、Delphi、Rust。。等强力后援就得自己开发像样的工具,体现穷人翻身做主DIY没枪没炮自己造的精神!
是不是开发了工具感觉高大上了许多? 我还能调用AI快速汉化呢,但是这样极易引起Java程序报错,因此开发了纠错和状态列、撤销、备份与恢复功能。{:301_997:}
突然发现我的TotalCommander竟然只能搜索到未汉化前的英文
汉化之后无论是*.class 还是*.html内的词条都无法找到,这是不能忍受的!{:301_1006:}
[Delphi] 纯文本查看 复制代码 library JavaSearch;
uses
SysUtils,
Classes,
Windows,
System.Zip,
AnsiStrings;
const
// TC WDX 插件返回值常量
FT_NOMOREFIELDS = 0;
FT_NUMERIC_32 = 1;
FT_STRING = 8;
FT_FULLTEXT = 9;
FT_FILEERROR = -1;
FT_FIELD_EMPTY = -2;
// ------------------------------------------------------------------
// 辅助函数 1:将 Java .properties 中的 \uXXXX 转义字符还原为真实中文字符
// ------------------------------------------------------------------
function UnescapeJavaUnicode(const Input: string): string;
var
I, Len: Integer;
HexCode: string;
ChVal: Integer;
begin
Result := '';
Len := Length(Input);
I := 1;
while I <= Len do
begin
if (Input[I] = '\') and (I + 5 <= Len) and (LowerCase(Input[I + 1]) = 'u') then
begin
HexCode := Copy(Input, I + 2, 4);
ChVal := StrToIntDef('$' + HexCode, -1);
if ChVal <> -1 then
begin
Result := Result + Char(ChVal);
Inc(I, 6);
Continue;
end;
end;
Result := Result + Input[I];
Inc(I);
end;
end;
// ------------------------------------------------------------------
// 辅助函数 2:把中文/字符串转换为 \uXXXX 格式(用于备选匹配)
// ------------------------------------------------------------------
function StringToUnicodeEscape(const S: string): string;
var
I: Integer;
begin
Result := '';
for I := 1 to Length(S) do
begin
if Ord(S[I]) > 127 then
Result := Result + '\u' + Format('%.4x', [Ord(S[I])])
else
Result := Result + S[I];
end;
end;
// ------------------------------------------------------------------
// TC 导出函数 1:定义插件支持的字段
// ------------------------------------------------------------------
function ContentGetSupportedField(FieldIndex: Integer; FieldName: PAnsiChar;
Units: PAnsiChar; MaxLen: Integer): Integer; stdcall;
begin
if FieldIndex = 0 then
begin
AnsiStrings.StrLCopy(FieldName, 'ContainsText', MaxLen);
Result := FT_STRING; // 声明为字符串字段
end
else
Result := FT_NOMOREFIELDS;
end;
// ------------------------------------------------------------------
// TC 导出函数 2:获取文件对应字段的值(核心搜索逻辑)
// ------------------------------------------------------------------
function ContentGetValue(FileName: PAnsiChar; FieldIndex: Integer;
UnitIndex: Integer; FieldValue: PByte; MaxLen: Integer; Flags: Integer): Integer; stdcall;
var
FilePath: string;
SearchQuery: string;
EscapedQueryLower: string;
EscapedQueryUpper: string;
JarFile: TZipFile;
I: Integer;
ZipHeader: TZipHeader;
Stream: TStream;
StringList: TStringList;
Ext: string;
FileContent: string;
Bytes: TBytes;
Matched: Boolean;
begin
if FieldIndex <> 0 then
begin
Result := FT_FIELD_EMPTY;
Exit;
end;
FilePath := string(FileName);
// 仅处理 .jar 压缩包
if not (SameText(ExtractFileExt(FilePath), '.jar') and TZipFile.IsValid(FilePath)) then
begin
Result := FT_FIELD_EMPTY;
Exit;
end;
// 获取 TC 在【插件】选项卡搜索框传入的搜索关键字(如果有)
SearchQuery := '';
if Assigned(FieldValue) and (PAnsiChar(FieldValue)^ <> #0) then
begin
try
SearchQuery := string(PAnsiChar(FieldValue));
except
SearchQuery := '';
end;
end;
// 生成 \uXXXX 格式的转义词供对比
if SearchQuery <> '' then
begin
EscapedQueryLower := LowerCase(StringToUnicodeEscape(SearchQuery));
EscapedQueryUpper := UpperCase(StringToUnicodeEscape(SearchQuery));
end;
Matched := False;
JarFile := TZipFile.Create;
try
try
JarFile.Open(FilePath, zmRead);
for I := 0 to JarFile.FileCount - 1 do
begin
Ext := ExtractFileExt(JarFile.FileName[I]);
// 1. 处理 .properties 属性文件
if SameText(Ext, '.properties') then
begin
JarFile.Read(I, Stream, ZipHeader);
if Assigned(Stream) then
begin
StringList := TStringList.Create;
try
StringList.LoadFromStream(Stream, TEncoding.UTF8);
FileContent := StringList.Text;
// 对内容进行 \uXXXX 解码,还原中文文本
FileContent := UnescapeJavaUnicode(FileContent);
// 如果 TC 传入了搜索词,内部直接判断
if SearchQuery <> '' then
begin
if (Pos(SearchQuery, FileContent) > 0) or
(Pos(EscapedQueryLower, LowerCase(StringList.Text)) > 0) or
(Pos(EscapedQueryUpper, UpperCase(StringList.Text)) > 0) then
begin
Matched := True;
Break;
end;
end
else
begin
// 未传入搜索词时,默认返回解密后的部分文本给 TC 匹配
if Length(FileContent) > MaxLen - 1 then
FileContent := Copy(FileContent, 1, MaxLen - 1);
AnsiStrings.StrLCopy(PAnsiChar(FieldValue), PAnsiChar(AnsiString(FileContent)), MaxLen);
Result := FT_STRING;
Exit;
end;
finally
StringList.Free;
Stream.Free;
end;
end;
end
// 2. 处理 .class 二进制文件(按 UTF-8 匹配关键字)
else if SameText(Ext, '.class') then
begin
if SearchQuery <> '' then
begin
JarFile.Read(I, Stream, ZipHeader);
if Assigned(Stream) and (Stream.Size > 0) then
begin
try
SetLength(Bytes, Stream.Size);
Stream.Position := 0;
Stream.Read(Bytes[0], Stream.Size);
// 直接将 .class 中的字节转换为 UTF-8 文本搜索
FileContent := TEncoding.UTF8.GetString(Bytes);
if Pos(SearchQuery, FileContent) > 0 then
begin
Matched := True;
Break;
end;
finally
Stream.Free;
end;
end;
end;
end;
end;
except
Matched := False;
end;
finally
JarFile.Free;
end;
// 返回匹配结果
if Matched then
begin
AnsiStrings.StrLCopy(PAnsiChar(FieldValue), PAnsiChar(AnsiString(SearchQuery)), MaxLen);
Result := FT_STRING;
end
else
Result := FT_FIELD_EMPTY;
end;
exports
ContentGetSupportedField,
ContentGetValue;
begin
end.
将编译好的DLL改名,复制到TC插件目录下
添加进来
这样就能开心的搜索到无论是*.jar内的*.class还是修改后的*.html {:301_987:}
|