Merge remote-tracking branch

'origin/GP-5128_ryanmkurtz_extensions--SQUASHED' (Closes #7128)
This commit is contained in:
Ryan Kurtz
2026-07-27 14:11:31 -04:00
33 changed files with 260 additions and 49 deletions

View File

@@ -827,6 +827,11 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
return COFF_NAME;
}
@Override
public Collection<String> getAssociatedFileExtensions() {
return List.of("o", "obj");
}
class CoffPair {
public long offset;
public long size;

View File

@@ -51,6 +51,11 @@ public class ComLoader extends AbstractLibrarySupportLoader {
return COM_NAME;
}
@Override
public Collection<String> getAssociatedFileExtensions() {
return List.of("com");
}
@Override
public boolean isFallback() {
return true;

View File

@@ -193,6 +193,11 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
return ELF_NAME;
}
@Override
public Collection<String> getAssociatedFileExtensions() {
return List.of("so", "o");
}
/**
* Attempts to detect a more specific compiler from the ELF
*

View File

@@ -289,4 +289,9 @@ public class IntelHexLoader extends AbstractProgramLoader {
public String getName() {
return INTEL_HEX_NAME;
}
@Override
public Collection<String> getAssociatedFileExtensions() {
return List.of("hex");
}
}

View File

@@ -297,6 +297,13 @@ public interface Loader extends ExtensionPoint, Comparable<Loader> {
return COMMAND_LINE_ARG_PREFIX + arg;
}
/**
* {@return a {@link Collection} of file extensions associated with this {@link Loader}}
*/
public default Collection<String> getAssociatedFileExtensions() {
return List.of();
}
@Override
public default int compareTo(Loader o) {
int compareTiers = getTier().compareTo(o.getTier());

View File

@@ -168,7 +168,7 @@ public class LoaderService {
* @return An instance of every known {@link Loader}. The {@link Loader} instances are sorted
* according to their {@link Loader#compareTo(Loader) natural ordering}.
*/
private synchronized static Collection<Loader> getAllLoaders() {
public synchronized static Collection<Loader> getAllLoaders() {
List<Loader> loaders = new ArrayList<>(ClassSearcher.getInstances(Loader.class));
Collections.sort(loaders);
return loaders;

View File

@@ -178,6 +178,11 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
return MACH_O_NAME;
}
@Override
public Collection<String> getAssociatedFileExtensions() {
return List.of("dylib");
}
@Override
protected boolean isValidSearchPath(FSRL fsrl, ImporterSettings settings)
throws CancelledException {

View File

@@ -829,4 +829,9 @@ public class NeLoader extends AbstractOrdinalSupportLoader {
return NE_NAME;
}
@Override
public Collection<String> getAssociatedFileExtensions() {
return List.of("exe", "dll");
}
}

View File

@@ -480,4 +480,9 @@ public class Omf51Loader extends AbstractProgramWrapperLoader {
public String getName() {
return OMF51_NAME;
}
@Override
public Collection<String> getAssociatedFileExtensions() {
return List.of("obj");
}
}

View File

@@ -107,6 +107,11 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
return OMF_NAME;
}
@Override
public Collection<String> getAssociatedFileExtensions() {
return List.of("obj");
}
@Override
protected void load(Program program, ImporterSettings settings)
throws IOException, CancelledException {

View File

@@ -870,6 +870,11 @@ public class PeLoader extends AbstractPeDebugLoader {
return PE_NAME;
}
@Override
public Collection<String> getAssociatedFileExtensions() {
return List.of("exe", "dll", "sys");
}
public static class CompilerOpinion {
static final char[] errString_borland =
"This program must be run under Win32\r\n$".toCharArray();

View File

@@ -79,6 +79,13 @@ public interface GFileSystem extends Closeable, Iterable<GFile>, ExtensionPoint
return FSUtilities.getFilesystemDescriptionFromClass(this.getClass());
}
/**
* {@return a {@link Collection} of file extensions associated with this {@link GFileSystem}}
*/
public default Collection<String> getAssociatedFileExtensions() {
return List.of();
}
/**
* File system's FSRL
*

View File

@@ -45,6 +45,11 @@ public @interface FileSystemInfo {
*/
String description() default "";
/**
* {@return an array of file extensions associated with this filesystem}
*/
public String[] extensions() default {};
/**
* The {@link GFileSystemFactory} class that will be responsible for probing and
* creating instances of this filesystem.

View File

@@ -17,6 +17,7 @@ package ghidra.formats.gfilesystem.factory;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import ghidra.app.util.bin.ByteProvider;
import ghidra.formats.gfilesystem.*;
@@ -116,6 +117,18 @@ public class FileSystemFactoryMgr {
//@formatter:on
}
/**
* {@return a map of associated file system extensions keyed by file system description}
*/
public Map<String, String[]> getFilesystemExtensionsByDescription() {
return sortedFactories
.stream()
.collect(
Collectors.toMap(FileSystemInfoRec::getDescription,
FileSystemInfoRec::getExtensions));
}
/**
* Returns the file system type of the specified {@link GFileSystem} class.
*

View File

@@ -36,6 +36,7 @@ public class FileSystemInfoRec {
private final int priority;
private final Class<? extends GFileSystem> fsClass;
private final GFileSystemFactory<?> factory;
private final String[] extensions;
/**
* A static {@link Comparator} that will order {@link FileSystemInfoRec} by their
@@ -54,6 +55,7 @@ public class FileSystemInfoRec {
* @return new {@link FileSystemInfoRec}, or null if the class doesn't have
* valid file system meta data.
*/
@SuppressWarnings("unchecked")
public static FileSystemInfoRec fromClass(Class<? extends GFileSystem> fsClazz) {
FileSystemInfo fsi = fsClazz.getAnnotation(FileSystemInfo.class);
if (fsi == null) {
@@ -81,24 +83,26 @@ public class FileSystemInfoRec {
// Hack to allow GFileSystemBaseFactory to know which fsclass is using it
// so instances can be created by the single GFileSystemBaseFactory impl.
if (factory instanceof GFileSystemBaseFactory) {
((GFileSystemBaseFactory) factory).setFileSystemClass(
(Class<? extends GFileSystemBase>) fsClazz);
if (factory instanceof GFileSystemBaseFactory base) {
base.setFileSystemClass((Class<? extends GFileSystemBase>) fsClazz);
}
FileSystemInfoRec fsir =
new FileSystemInfoRec(fsType, fsi.description(), fsi.priority(), fsClazz, factory);
new FileSystemInfoRec(fsType, fsi.description(), fsi.priority(), fsClazz, factory,
fsi.extensions());
return fsir;
}
private FileSystemInfoRec(String type, String description, int priority,
Class<? extends GFileSystem> fsClass, GFileSystemFactory<?> factory) {
Class<? extends GFileSystem> fsClass, GFileSystemFactory<?> factory,
String[] extensions) {
this.type = type;
this.description = description;
this.priority = priority;
this.fsClass = fsClass;
this.factory = factory;
this.extensions = extensions;
}
/**
@@ -148,4 +152,11 @@ public class FileSystemInfoRec {
public GFileSystemFactory<?> getFactory() {
return factory;
}
/**
* {@return the associated file extensions for this filesystem}
*/
public String[] getExtensions() {
return extensions;
}
}

View File

@@ -465,8 +465,8 @@ public class ImporterPlugin extends Plugin
private void initializeChooser(String title, String buttonText, boolean multiSelect) {
if (chooser == null) {
chooser = new GhidraFileChooser(tool.getActiveWindow());
chooser.addFileFilter(ImporterUtilities.LOADABLE_FILES_FILTER);
chooser.addFileFilter(ImporterUtilities.CONTAINER_FILES_FILTER);
ImporterUtilities.addLoadableFileFilters(chooser);
ImporterUtilities.addFileSystemFileFilters(chooser);
chooser.setSelectedFileFilter(GhidraFileFilter.ALL);
}
chooser.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY);

View File

@@ -20,6 +20,7 @@ import java.io.IOException;
import java.util.*;
import docking.widgets.OptionDialog;
import docking.widgets.filechooser.GhidraFileChooser;
import ghidra.app.events.ProgramAddedToPluginEvent;
import ghidra.app.plugin.core.help.AboutDomainObjectUtils;
import ghidra.app.services.FileSystemBrowserService;
@@ -31,6 +32,7 @@ import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.*;
import ghidra.app.util.opinion.Loader.ImporterSettings;
import ghidra.formats.gfilesystem.*;
import ghidra.formats.gfilesystem.factory.FileSystemFactoryMgr;
import ghidra.framework.main.AppInfo;
import ghidra.framework.main.FrontEndTool;
import ghidra.framework.model.*;
@@ -46,7 +48,6 @@ import ghidra.util.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.CryptoException;
import ghidra.util.filechooser.ExtensionFileFilter;
import ghidra.util.filechooser.GhidraFileFilter;
import ghidra.util.task.TaskLauncher;
import ghidra.util.task.TaskMonitor;
import util.CollectionUtils;
@@ -60,20 +61,44 @@ import util.CollectionUtils;
public class ImporterUtilities {
/**
* File extension filter for well known 'loadable' files for GhidraFileChoosers.
* Adds file filters for associated {@link Loader} file extensions to the given
* {@link GhidraFileChooser}
*
* @param chooser The {@link GhidraFileChooser} to add extension file filters to
*/
public static final GhidraFileFilter LOADABLE_FILES_FILTER = ExtensionFileFilter.forExtensions(
"Loadable files", "exe", "dll", "obj", "drv", "bin", "hex", "o", "a", "so", "class", "lib",
"dylib");
public static void addLoadableFileFilters(GhidraFileChooser chooser) {
Set<String> all = new HashSet<>();
for (Loader loader : LoaderService.getAllLoaders()) {
Collection<String> exts = loader.getAssociatedFileExtensions();
if (!exts.isEmpty()) {
chooser.addFileFilter(new ExtensionFileFilter(exts, loader.getName()));
all.addAll(exts);
}
}
if (!all.isEmpty()) {
chooser.addFileFilter(new ExtensionFileFilter(all, "Loadable files"));
}
}
/**
* File extension filter for well known 'container' files for GhidraFileChoosers.
* Adds file filters for associated {@link GFileSystem} file extensions to the given
* {@link GhidraFileChooser}
*
* @param chooser The {@link GhidraFileChooser} to add extension file filters to
*/
public static final GhidraFileFilter CONTAINER_FILES_FILTER =
ExtensionFileFilter.forExtensions("Container files", "zip", "tar", "tgz", "jar", "gz",
"ipsw", "img3", "dmg", "apk", "cpio", "rpm", "lib");
private static final FileSystemService fsService = FileSystemService.getInstance();
public static void addFileSystemFileFilters(GhidraFileChooser chooser) {
Set<String> all = new HashSet<>();
FileSystemFactoryMgr mgr = FileSystemFactoryMgr.getInstance();
mgr.getFilesystemExtensionsByDescription().forEach((description, exts) -> {
if (exts.length > 0) {
chooser.addFileFilter(new ExtensionFileFilter(exts, description));
Collections.addAll(all, exts);
}
});
if (!all.isEmpty()) {
chooser.addFileFilter(new ExtensionFileFilter(all, "Container files"));
}
}
static List<LanguageCompilerSpecPair> getPairs(Collection<LoadSpec> loadSpecs) {
Set<LanguageCompilerSpecPair> pairs = new HashSet<>();
@@ -133,6 +158,8 @@ public class ImporterUtilities {
Objects.requireNonNull(monitor);
FileSystemService fsService = FileSystemService.getInstance();
try (RefdFile referencedFile = fsService.getRefdFile(fsrl, monitor)) {
if (!ensureFileImportable(referencedFile, monitor)) {
return;
@@ -222,6 +249,7 @@ public class ImporterUtilities {
}
try {
FileSystemService fsService = FileSystemService.getInstance();
ByteProvider provider = fsService.getByteProvider(fsrl, false, monitor);
if (provider.length() == 0) {
Msg.showWarn(null, null, "Error opening " + fsrl.getName(),
@@ -319,6 +347,7 @@ public class ImporterUtilities {
TaskMonitor monitor) {
try {
FileSystemService fsService = FileSystemService.getInstance();
ByteProvider provider = fsService.getByteProvider(fsrl, true, monitor);
LoaderMap loaderMap = LoaderService.getAllSupportedLoadSpecs(provider, monitor);
@@ -421,6 +450,8 @@ public class ImporterUtilities {
Objects.requireNonNull(monitor);
FileSystemService fsService = FileSystemService.getInstance();
try (ByteProvider bp = fsService.getByteProvider(fsrl, false, monitor)) {
MessageLog messageLog = new MessageLog();
@@ -511,6 +542,7 @@ public class ImporterUtilities {
Objects.requireNonNull(monitor);
FileSystemService fsService = FileSystemService.getInstance();
MessageLog messageLog = new MessageLog();
Object consumer = new Object();
program.addConsumer(consumer);

View File

@@ -203,8 +203,8 @@ public class FileSystemBrowserPlugin extends Plugin
private void openChooser(String title, String buttonText, boolean multiSelect) {
if (chooserOpen == null) {
chooserOpen = new GhidraFileChooser(tool.getActiveWindow());
chooserOpen.addFileFilter(ImporterUtilities.LOADABLE_FILES_FILTER);
chooserOpen.addFileFilter(ImporterUtilities.CONTAINER_FILES_FILTER);
ImporterUtilities.addLoadableFileFilters(chooserOpen);
ImporterUtilities.addFileSystemFileFilters(chooserOpen);
chooserOpen.setSelectedFileFilter(GhidraFileFilter.ALL);
}
chooserOpen.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY);

View File

@@ -464,8 +464,8 @@ public class BatchImportDialog extends DialogComponentProvider {
chooser.setTitle("Choose File to Batch Import");
chooser.setApproveButtonText("Select files");
chooser.setFileSelectionMode(GhidraFileChooserMode.FILES_AND_DIRECTORIES);
chooser.addFileFilter(ImporterUtilities.LOADABLE_FILES_FILTER);
chooser.addFileFilter(ImporterUtilities.CONTAINER_FILES_FILTER);
ImporterUtilities.addLoadableFileFilters(chooser);
ImporterUtilities.addFileSystemFileFilters(chooser);
chooser.setSelectedFileFilter(GhidraFileFilter.ALL);
chooser.setLastDirectoryPreference(LAST_IMPORT_DIR);

View File

@@ -133,6 +133,11 @@ public class DyldCacheExtractLoader extends MachoLoader {
return DYLD_CACHE_EXTRACT_NAME;
}
@Override
public Collection<String> getAssociatedFileExtensions() {
return List.of();
}
@Override
public int getTierPriority() {
return 49; // Higher priority than MachoLoader

View File

@@ -104,6 +104,11 @@ public class MachoFileSetExtractLoader extends MachoLoader {
return MACHO_FILESET_EXTRACT_NAME;
}
@Override
public Collection<String> getAssociatedFileExtensions() {
return List.of();
}
@Override
public int getTierPriority() {
return 49; // Higher priority than MachoLoader

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -33,7 +33,12 @@ import ghidra.util.task.TaskMonitor;
* APK is really just a ZIP file.
*
*/
@FileSystemInfo(type = "apk", description = "Android APK", factory = GFileSystemBaseFactory.class)
@FileSystemInfo(
type = "apk",
description = "Android APK",
factory = GFileSystemBaseFactory.class,
extensions = { "apk" }
)
public class ApkFileSystem extends GFileSystemBase {
public ApkFileSystem(String fileSystemName, ByteProvider provider) {

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -30,7 +30,12 @@ import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
import ghidra.formats.gfilesystem.fileinfo.FileAttributes;
import ghidra.util.task.TaskMonitor;
@FileSystemInfo(type = "coff", description = "COFF Archive", factory = CoffArchiveFileSystemFactory.class)
@FileSystemInfo(
type = "coff",
description = "COFF Archive",
factory = CoffArchiveFileSystemFactory.class,
extensions = { "a" }
)
public class CoffArchiveFileSystem extends AbstractFileSystem<CoffArchiveMemberHeader> {
private ByteProvider provider;

View File

@@ -32,7 +32,12 @@ import ghidra.formats.gfilesystem.fileinfo.FileType;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@FileSystemInfo(type = "cpio", description = "CPIO", factory = CpioFileSystemFactory.class)
@FileSystemInfo(
type = "cpio",
description = "CPIO",
factory = CpioFileSystemFactory.class,
extensions = { "cpio" }
)
public class CpioFileSystem extends AbstractFileSystem<CpioArchiveEntry> {
private static final int MAX_SANE_SYMLINK = 64 * 1024;

View File

@@ -28,7 +28,13 @@ import ghidra.formats.gfilesystem.fileinfo.FileAttributes;
* If the filename can be recovered from the embedded metadata, it will be used as the
* name of the singleton file, otherwise the name "gzip_decompressed" will be used.
*/
@FileSystemInfo(type = "gzip", description = "GZIP", priority = FileSystemInfo.PRIORITY_LOW, factory = GZipFileSystemFactory.class)
@FileSystemInfo(
type = "gzip",
description = "GZIP",
priority = FileSystemInfo.PRIORITY_LOW,
factory = GZipFileSystemFactory.class,
extensions = { "gz" }
)
public class GZipFileSystem extends AbstractSinglePayloadFileSystem {
public GZipFileSystem(FSRLRoot fsFSRL, ByteProvider payloadProvider, String payloadFilename,

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -44,7 +44,12 @@ import ghidra.util.task.*;
* DMG server process seems to leak memory. Currently it is killed every N commands and
* restarted.
*/
@FileSystemInfo(type = "dmg", description = "iOS Disk Image (DMG)", factory = DmgClientFileSystemFactory.class)
@FileSystemInfo(
type = "dmg",
description = "iOS Disk Image (DMG)",
factory = DmgClientFileSystemFactory.class,
extensions = { "dmg" }
)
public class DmgClientFileSystem extends AbstractFileSystem<Object> {
private File decryptedDmgFile;

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -28,8 +28,12 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.exception.CryptoException;
import ghidra.util.task.TaskMonitor;
@FileSystemInfo(type = "img3", description = "iOS " +
Img3Constants.IMG3_SIGNATURE, factory = Img3FileSystemFactory.class)
@FileSystemInfo(
type = "img3",
description = "iOS " + Img3Constants.IMG3_SIGNATURE,
factory = Img3FileSystemFactory.class,
extensions = { "img3" }
)
public class Img3FileSystem extends AbstractFileSystem<DataTag> {
private ByteProvider provider;

View File

@@ -34,7 +34,12 @@ import ghidra.util.task.TaskMonitor;
* <p>
* This implementation is currently unused (isValid always returns false).
*/
@FileSystemInfo(type = "ipsw", description = "iOS Firmware (IPSW)", factory = GFileSystemBaseFactory.class)
@FileSystemInfo(
type = "ipsw",
description = "iOS Firmware (IPSW)",
factory = GFileSystemBaseFactory.class,
extensions = { "ipsw" }
)
public class IpswFileSystem extends GFileSystemBase {
public IpswFileSystem(String fileSystemName, ByteProvider provider) {

View File

@@ -46,7 +46,12 @@ import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
* WARNING: care is taken to synchronize access to the underlying sevenzip library methods as
* some race conditions have been encountered that cause the entire jdk to core dump.
*/
@FileSystemInfo(type = "7zip", description = "7Zip", factory = SevenZipFileSystemFactory.class)
@FileSystemInfo(
type = "7zip",
description = "7Zip",
factory = SevenZipFileSystemFactory.class,
extensions = { "7z", "rpm" }
)
public class SevenZipFileSystem extends AbstractFileSystem<ISimpleInArchiveItem> {
private Map<Integer, String> passwords = new HashMap<>();
@@ -203,7 +208,7 @@ public class SevenZipFileSystem extends AbstractFileSystem<ISimpleInArchiveItem>
// that file will not be readable unless a password is found for it (see
// getPasswordForFile()).
try (CryptoSession cryptoSession = fsService.newCryptoSession()) {
try (CryptoSession _ = fsService.newCryptoSession()) {
List<ISimpleInArchiveItem> encryptedItems = getEncryptedItemsWithoutPasswords();
ISimpleInArchiveItem encryptedItem = null;
while ((encryptedItem = getFirstItemWithoutPassword(encryptedItems)) != null &&

View File

@@ -36,7 +36,13 @@ import ghidra.util.task.TaskMonitor;
* The factory supports detecting both compressed (gz) and uncompressed tar files,
* and keys both on the tar filename extension as well as the data in the file.
*/
@FileSystemInfo(type = "tar", description = "TAR", priority = FileSystemInfo.PRIORITY_HIGH, factory = TarFileSystemFactory.class)
@FileSystemInfo(
type = "tar",
description = "TAR",
priority = FileSystemInfo.PRIORITY_HIGH,
factory = TarFileSystemFactory.class,
extensions = { "tar", "tgz" }
)
public class TarFileSystem extends AbstractFileSystem<TarMetadata> {
private ByteProvider provider;

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -25,7 +25,13 @@ import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
* <p>
* 7Zip's features are superior to the native java zip handling (ie. passwords)
*/
@FileSystemInfo(type = "zip", description = "ZIP", factory = ZipFileSystemFactory.class, priority = FileSystemInfo.PRIORITY_HIGH)
@FileSystemInfo(
type = "zip",
description = "ZIP",
factory = ZipFileSystemFactory.class,
priority = FileSystemInfo.PRIORITY_HIGH,
extensions = { "zip", "jar" }
)
public class ZipFileSystem extends SevenZipFileSystem {
public ZipFileSystem(FSRLRoot fsrl, FileSystemService fsService) {

View File

@@ -19,6 +19,7 @@ import java.io.File;
import java.io.FileFilter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import ghidra.util.Msg;
import utilities.util.reflection.ReflectionUtilities;
@@ -56,6 +57,7 @@ public class ExtensionFileFilter implements GhidraFileFilter {
/**
* Creates a file filter that accepts the given file type.
* <p>
* Example: new ExtensionFileFilter("jpg", "JPEG Images");
*
* @param extension file extension to match, without leading dot
@@ -67,16 +69,38 @@ public class ExtensionFileFilter implements GhidraFileFilter {
/**
* Creates a file filter from the given string array and description.
* <p>
* Example: new ExtensionFileFilter(String {"gif", "jpg"}, "Gif and JPG Images");
*
* @param filters array of file name extensions, each without a leading dot
* @param description descriptive string of the filter
*/
public ExtensionFileFilter(String[] filters, String description) {
this.extensions = Arrays.asList(filters)
.stream()
.map(ExtensionFileFilter::clean)
.collect(Collectors.toList());
this(Arrays.stream(filters), description);
}
/**
* Creates a file filter from the given string {@link Collection} and description.
* <p>
* Example: new ExtensionFileFilter(List.of("gif", "jpg"), "Gif and JPG Images");
*
* @param filters array of file name extensions, each without a leading dot
* @param description descriptive string of the filter
*/
public ExtensionFileFilter(Collection<String> filters, String description) {
this(filters.stream(), description);
}
/**
* Creates a file filter from the given string {@link Stream} and description.
* <p>
* Example: new ExtensionFileFilter(Stream.of("gif", "jpg"), "Gif and JPG Images");
*
* @param filters {@link List} of file name extensions, each without a leading dot
* @param description descriptive string of the filter
*/
private ExtensionFileFilter(Stream<String> filters, String description) {
this.extensions = filters.map(ExtensionFileFilter::clean).collect(Collectors.toList());
this.description = description;
}

View File

@@ -86,6 +86,11 @@ public class JavaLoader extends AbstractProgramWrapperLoader {
return JAVA_NAME;
}
@Override
public Collection<String> getAssociatedFileExtensions() {
return List.of("class");
}
@Override
public void load(Program program, ImporterSettings settings) throws IOException {
try {