diff --git a/src/decompiler/impl/JadxDecompiler.ts b/src/decompiler/impl/JadxDecompiler.ts index 0972cc9..5af9ef0 100644 --- a/src/decompiler/impl/JadxDecompiler.ts +++ b/src/decompiler/impl/JadxDecompiler.ts @@ -25,6 +25,55 @@ export class JadxDecompiler implements SmaliDecompiler { return join(this.sourceOutputDir, (smaliClassName.includes("/") ? "" : "defpackage/") + smaliClassName + ".java") } + private async collectJavaFiles(directoryPath: string): Promise { + const entries = await fsAsync.readdir(directoryPath, { withFileTypes: true }) + const results: string[] = [] + for (const entry of entries) { + const entryPath = join(directoryPath, entry.name) + if (entry.isDirectory()) { + results.push(...await this.collectJavaFiles(entryPath)) + } else if (entry.isFile() && entry.name.toLowerCase().endsWith(".java")) { + results.push(entryPath) + } + } + return results + } + + private async findGeneratedOutputFilePath(smaliClassName: string): Promise { + const expectedOutputFilePath = this.getOutputFilePath(smaliClassName) + try { + await fsAsync.stat(expectedOutputFilePath) + return expectedOutputFilePath + } catch { + try { + const javaCandidates = await this.collectJavaFiles(this.sourceOutputDir) + const normalizedExpectedOutputFilePath = expectedOutputFilePath.replace(/\\/g, "/") + const normalizedExpectedBaseName = expectedOutputFilePath.split(/[\\/]/).pop()!.toLowerCase() + const suffixMatchedCandidates = javaCandidates.filter((candidatePath) => candidatePath.replace(/\\/g, "/").endsWith(normalizedExpectedOutputFilePath)) + if (suffixMatchedCandidates.length === 1) { + return suffixMatchedCandidates[0] + } + const baseNameMatchedCandidates = javaCandidates.filter((candidatePath) => candidatePath.split(/[\\/]/).pop()!.toLowerCase() === normalizedExpectedBaseName) + if (baseNameMatchedCandidates.length === 1) { + return baseNameMatchedCandidates[0] + } + const jadxPrefixedBaseNameMatchedCandidates = javaCandidates.filter((candidatePath) => candidatePath.split(/[\\/]/).pop()!.toLowerCase() === `c${normalizedExpectedBaseName}`) + if (jadxPrefixedBaseNameMatchedCandidates.length === 1) { + return jadxPrefixedBaseNameMatchedCandidates[0] + } + const suffixWithoutRootDirectoryMatchedCandidates = javaCandidates.filter((candidatePath) => candidatePath.replace(/\\/g, "/").endsWith(`/${normalizedExpectedOutputFilePath.split("/").slice(-2).join("/").replace(normalizedExpectedBaseName, `c${normalizedExpectedBaseName}`)}`)) + if (suffixWithoutRootDirectoryMatchedCandidates.length === 1) { + return suffixWithoutRootDirectoryMatchedCandidates[0] + } + if (javaCandidates.length === 1) { + return javaCandidates[0] + } + } catch { + } + throw new DecompileError(`Error is caught when reading ${expectedOutputFilePath}: file not found`) + } + } + private async loadConfig(): Promise { const config = workspace.getConfiguration("smali2java.decompiler.jadx") return { @@ -39,7 +88,6 @@ export class JadxDecompiler implements SmaliDecompiler { const config = await this.loadConfig() if (!config.path) throw new DecompileError("The jadx executable path has not been configured") if (!(await fsAsync.stat(config.path)).isFile()) throw new DecompileError("Illegal jadx executable path") - const outputFilePath = this.getOutputFilePath(smaliClassName) const { stdout, stderr } = await execAsync(`${await config.path} ${this.quote(smaliFileUri.fsPath)} -ds ${this.quote(this.sourceOutputDir)} ${config.options ?? ""}`) this.outputChannel.append(stdout) if (stderr && stderr.length > 0) { @@ -47,6 +95,7 @@ export class JadxDecompiler implements SmaliDecompiler { this.outputChannel.append(stderr) throw new DecompileError("View the output for details") } + const outputFilePath = await this.findGeneratedOutputFilePath(smaliClassName) try { await fsAsync.stat(outputFilePath) } catch(e) { @@ -54,7 +103,7 @@ export class JadxDecompiler implements SmaliDecompiler { } return Uri.from({ scheme: JavaCodeProvider.scheme, - path: outputFilePath + path: process.platform == "win32" ? outputFilePath.replace(/\\/g, "/") : outputFilePath }) } diff --git a/src/provider/JavaCodeProvider.ts b/src/provider/JavaCodeProvider.ts index bad691e..104a17f 100644 --- a/src/provider/JavaCodeProvider.ts +++ b/src/provider/JavaCodeProvider.ts @@ -12,7 +12,7 @@ export default class JavaCodeProvider implements TextDocumentContentProvider { } async provideTextDocumentContent(uri: Uri): Promise { - const buffer = await fs.readFile(uri.path) + const buffer = await fs.readFile(uri.fsPath) return buffer.toString() } } \ No newline at end of file