diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Library.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Library.java new file mode 100644 index 0000000000..c1cf7643a2 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Library.java @@ -0,0 +1,72 @@ +/* ### + * IP: GHIDRA + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.omf.omf51; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.format.omf.OmfException; +import ghidra.app.util.bin.format.omf.OmfString; + +public class Omf51Library { + + private List members = new ArrayList<>(); + + public record MemberHeader(long offset, long size, String name) {} + + /** + * Creates a new {@link Omf51Library} + * + * @param factory A {@link Omf51RecordFactory} + * @throws IOException if an IO-related error occurred + * @throws OmfException if the required OMF-51 records could not be read + */ + public Omf51Library(Omf51RecordFactory factory) throws OmfException, IOException { + if (!(factory.readNextRecord() instanceof Omf51LibraryHeaderRecord libraryHeader)) { + throw new OmfException("Unable to read library header record"); + } + + factory.getReader().setPointerIndex(libraryHeader.getModNamesOffset()); + + if (!(factory.readNextRecord() instanceof Omf51LibraryModuleNamesRecord modNamesRecord)) { + throw new OmfException("Unable to read library module names record"); + } + + if (!(factory.readNextRecord() instanceof Omf51LibraryModuleLocationsRecord modLocations)) { + throw new OmfException("Unable to read library module locations record"); + } + + List locations = modLocations.getLocations(); + + int index = 0; + for (OmfString moduleName : modNamesRecord.getNames()) { + int currentOffset = locations.get(index).getOffset(); + int nextOffset = index + 1 < locations.size() ? locations.get(index + 1).getOffset() + : libraryHeader.getModNamesOffset(); + int size = nextOffset - currentOffset; + members.add(new MemberHeader(currentOffset, size, moduleName.str())); + index++; + } + } + + /** + * {@return the list of members} + */ + public List getMembers() { + return members; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryDictionaryRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryDictionaryRecord.java new file mode 100644 index 0000000000..48a2f67cd6 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryDictionaryRecord.java @@ -0,0 +1,85 @@ +/* ### + * IP: GHIDRA + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.omf.omf51; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.omf.*; +import ghidra.program.model.data.*; +import ghidra.util.exception.DuplicateNameException; + +public class Omf51LibraryDictionaryRecord extends OmfRecord { + + private List> moduleSymbolMap = new ArrayList<>(); + + /** + * Creates a new {@link Omf51LibraryDictionaryRecord} + * + * @param reader A {@link BinaryReader} positioned at the start of the record + * @throws IOException if an IO-related error occurred + */ + public Omf51LibraryDictionaryRecord(BinaryReader reader) throws IOException { + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + List symbols = new ArrayList<>(); + + while (dataReader.getPointerIndex() < dataEnd) { + byte len = dataReader.peekNextByte(); + if (len == 0) { + dataReader.readNextByte(); + moduleSymbolMap.add(symbols); + symbols = new ArrayList<>(); + } + else { + symbols.add(OmfUtils.readString(dataReader)); + } + } + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType struct = new StructureDataType(Omf51RecordTypes.getName(recordType), 0); + struct.add(BYTE, "type", null); + struct.add(WORD, "length", null); + Integer moduleIndex = 0; + for (List symbols : moduleSymbolMap) { + for (OmfString symbol : symbols) { + struct.add(symbol.toDataType(), symbol.getDataTypeSize(), + "symbol%d".formatted(moduleIndex), null); + } + + struct.add(BYTE, "terminator%d".formatted(moduleIndex), null); + moduleIndex++; + } + struct.add(BYTE, "checksum", null); + + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } + + /** + * {@return the symbol names partitioned by module} + */ + public List> getModuleSymbolMap() { + return moduleSymbolMap; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryHeaderRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryHeaderRecord.java new file mode 100644 index 0000000000..cef6fc758a --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryHeaderRecord.java @@ -0,0 +1,91 @@ +/* ### + * IP: GHIDRA + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.omf.omf51; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.omf.*; +import ghidra.program.model.data.*; +import ghidra.util.exception.DuplicateNameException; + +public class Omf51LibraryHeaderRecord extends OmfRecord { + + public static final int BLOCK_SIZE = 128; + + private short moduleCount; + private short blockNumber; + private short byteNumber; + + /** + * Creates a new {@link Omf51LibraryHeaderRecord} record + * + * @param reader A {@link BinaryReader} positioned at the start of the record + * @throws IOException if an IO-related error occurred + */ + public Omf51LibraryHeaderRecord(BinaryReader reader) throws IOException { + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + moduleCount = dataReader.readNextShort(); + blockNumber = dataReader.readNextShort(); + byteNumber = dataReader.readNextShort(); + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType struct = new StructureDataType(Omf51RecordTypes.getName(recordType), 0); + struct.add(BYTE, "type", null); + struct.add(WORD, "length", null); + struct.add(WORD, "moduleCount", null); + struct.add(WORD, "blockNumber", null); + struct.add(WORD, "byteNumber", null); + struct.add(BYTE, "checksum", null); + + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } + + /** + * {@return the module count} + */ + public short getModuleCount() { + return moduleCount; + } + + /** + * {@return the module names block number} + */ + public short getModNamesBlockNumber() { + return blockNumber; + } + + /** + * {@return the module names byte number} + */ + public short getModNamesByteNumber() { + return byteNumber; + } + + /** + * {@return the module names file offset} + */ + public int getModNamesOffset() { + return (blockNumber * BLOCK_SIZE) + byteNumber; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocation.java new file mode 100644 index 0000000000..13e1a81fcf --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocation.java @@ -0,0 +1,71 @@ +/* ### + * IP: GHIDRA + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.omf.omf51; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.StructConverter; +import ghidra.app.util.bin.format.omf.OmfUtils; +import ghidra.program.model.data.*; + +public class Omf51LibraryModuleLocation { + + public static final int BLOCK_SIZE = 128; + + private int blockNumber; + private int byteNumber; + + /** + * Creates a new {@link Omf51LibraryModuleLocation} + * + * @param reader A {@link BinaryReader} positioned at the start of the segment definition + * @throws IOException if an IO-related error occurred + */ + public Omf51LibraryModuleLocation(BinaryReader reader) throws IOException { + blockNumber = reader.readNextUnsignedShort(); + byteNumber = reader.readNextUnsignedShort(); + } + + /** + * {@return the block number} + */ + public int getBlockNumber() { + return blockNumber; + } + + /** + * {@return the byte number} + */ + public int getByteNumber() { + return byteNumber; + } + + /** + * {@return the offset into the library} + */ + public int getOffset() { + return (blockNumber * BLOCK_SIZE) + byteNumber; + } + + public static DataType toDataType() { + StructureDataType struct = new StructureDataType("Omf51LibraryModuleLocation", 0); + struct.add(StructConverter.WORD, "blockNumber", null); + struct.add(StructConverter.WORD, "byteNumber", null); + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocationsRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocationsRecord.java new file mode 100644 index 0000000000..9632f4eafd --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocationsRecord.java @@ -0,0 +1,67 @@ +/* ### + * IP: GHIDRA + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.omf.omf51; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.omf.*; +import ghidra.program.model.data.*; +import ghidra.util.exception.DuplicateNameException; + +public class Omf51LibraryModuleLocationsRecord extends OmfRecord { + + private List locations = new ArrayList<>(); + + /** + * Creates a new {@link Omf51LibraryModuleLocationsRecord} record + * + * @param reader A {@link BinaryReader} positioned at the start of the record + * @throws IOException if an IO-related error occurred + */ + public Omf51LibraryModuleLocationsRecord(BinaryReader reader) throws IOException { + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + while (dataReader.getPointerIndex() < dataEnd) { + locations.add(new Omf51LibraryModuleLocation(dataReader)); + } + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType struct = new StructureDataType(Omf51RecordTypes.getName(recordType), 0); + struct.add(BYTE, "type", null); + struct.add(WORD, "length", null); + struct.add(new ArrayDataType(Omf51LibraryModuleLocation.toDataType(), locations.size()), + "locations", null); + struct.add(BYTE, "checksum", null); + + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } + + /** + * {@return the list of module locations} + */ + public List getLocations() { + return locations; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleNamesRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleNamesRecord.java new file mode 100644 index 0000000000..f42e2d3492 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleNamesRecord.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.omf.omf51; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.omf.*; +import ghidra.program.model.data.*; +import ghidra.util.exception.DuplicateNameException; + +public class Omf51LibraryModuleNamesRecord extends OmfRecord { + + private List names = new ArrayList<>(); + + /** + * Creates a new {@link Omf51LibraryModuleNamesRecord} record + * + * @param reader A {@link BinaryReader} positioned at the start of the record + * @throws IOException if an IO-related error occurred + */ + public Omf51LibraryModuleNamesRecord(BinaryReader reader) throws IOException { + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + while (dataReader.getPointerIndex() < dataEnd) { + names.add(OmfUtils.readString(dataReader)); + } + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType struct = new StructureDataType(Omf51RecordTypes.getName(recordType), 0); + struct.add(BYTE, "type", null); + struct.add(WORD, "length", null); + for (OmfString name : names) { + struct.add(name.toDataType(), name.getDataTypeSize(), "name", null); + } + struct.add(BYTE, "checksum", null); + + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } + + /** + * {@return the list of module names} + */ + public List getNames() { + return names; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51RecordFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51RecordFactory.java index 4bee399546..b8efb24aa3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51RecordFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51RecordFactory.java @@ -70,12 +70,16 @@ public class Omf51RecordFactory extends AbstractOmfRecordFactory { yield new Omf51ExternalDefsRecord(reader, false); case KeilExternalDEF: yield new Omf51ExternalDefsRecord(reader, true); + case LibModLocs: + yield new Omf51LibraryModuleLocationsRecord(reader); + case LibModNames: + yield new Omf51LibraryModuleNamesRecord(reader); + case LibDictionary: + yield new Omf51LibraryDictionaryRecord(reader); + case LibHeader: + yield new Omf51LibraryHeaderRecord(reader); case ScopeDEF: case DebugItem: - case LibModLocs: - case LibModNames: - case LibDictionary: - case LibHeader: yield new OmfUnsupportedRecord(reader, Omf51RecordTypes.class); default: yield new OmfUnknownRecord(reader); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/Omf51Loader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/Omf51Loader.java index e33732b2ff..66edcdb1d2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/Omf51Loader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/Omf51Loader.java @@ -338,7 +338,6 @@ public class Omf51Loader extends AbstractProgramWrapperLoader { if (record instanceof Omf51PublicDefsRecord publicDefRec) { for (Omf51PublicDef def : publicDefRec.getDefinitions()) { if (def.getUsageType() == Omf51PublicDef.NUMBER) { - log.appendMsg("Skipping NUMBER public def"); continue; } Address segmentAddr = segmentToAddr.get(def.getSegId()); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java new file mode 100644 index 0000000000..09d1ca92b4 --- /dev/null +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java @@ -0,0 +1,95 @@ +/* ### + * IP: GHIDRA + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.file.formats.omf51; + +import static ghidra.formats.gfilesystem.fileinfo.FileAttributeType.*; + +import java.io.IOException; +import java.util.List; + +import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.bin.ByteProviderWrapper; +import ghidra.app.util.bin.format.omf.OmfException; +import ghidra.app.util.bin.format.omf.omf51.Omf51Library; +import ghidra.app.util.bin.format.omf.omf51.Omf51RecordFactory; +import ghidra.formats.gfilesystem.*; +import ghidra.formats.gfilesystem.annotations.FileSystemInfo; +import ghidra.formats.gfilesystem.fileinfo.FileAttributes; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +@FileSystemInfo( + type = "omf51", + description = "OMF51 Library", + factory = Omf51ArchiveFileSystemFactory.class +) +public class Omf51ArchiveFileSystem extends AbstractFileSystem { + + private ByteProvider provider; + + public Omf51ArchiveFileSystem(FSRLRoot fsFSRL, ByteProvider provider) { + super(fsFSRL, FileSystemService.getInstance()); + this.provider = provider; + } + + public void mount(TaskMonitor monitor) throws IOException, OmfException, CancelledException { + monitor.setMessage("Opening OMF51 library..."); + Omf51RecordFactory factory = new Omf51RecordFactory(provider); + List members = new Omf51Library(factory).getMembers(); + + monitor.initialize(members.size(), "Opening OMF51 library..."); + for (Omf51Library.MemberHeader member : members) { + monitor.increment(); + fsIndex.storeFile(member.name(), fsIndex.getFileCount(), false, member.size(), member); + } + } + + @Override + public void close() throws IOException { + refManager.onClose(); + if (provider != null) { + provider.close(); + provider = null; + } + fsIndex.clear(); + } + + @Override + public boolean isClosed() { + return provider == null; + } + + @Override + public ByteProvider getByteProvider(GFile file, TaskMonitor monitor) { + Omf51Library.MemberHeader member = fsIndex.getMetadata(file); + return (member != null) + ? new ByteProviderWrapper(provider, member.offset(), member.size(), + file.getFSRL()) + : null; + } + + @Override + public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) { + FileAttributes result = new FileAttributes(); + + Omf51Library.MemberHeader entry = fsIndex.getMetadata(file); + if (entry != null) { + result.add(NAME_ATTR, entry.name()); + result.add(SIZE_ATTR, entry.size()); + } + return result; + } +} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystemFactory.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystemFactory.java new file mode 100644 index 0000000000..6ded7fa3b6 --- /dev/null +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystemFactory.java @@ -0,0 +1,67 @@ +/* ### + * IP: GHIDRA + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.file.formats.omf51; + +import java.io.IOException; + +import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.bin.format.omf.AbstractOmfRecordFactory; +import ghidra.app.util.bin.format.omf.OmfException; +import ghidra.app.util.bin.format.omf.omf51.Omf51LibraryHeaderRecord; +import ghidra.app.util.bin.format.omf.omf51.Omf51RecordFactory; +import ghidra.app.util.opinion.Omf51Loader; +import ghidra.formats.gfilesystem.FSRLRoot; +import ghidra.formats.gfilesystem.FileSystemService; +import ghidra.formats.gfilesystem.factory.GFileSystemFactoryByteProvider; +import ghidra.formats.gfilesystem.factory.GFileSystemProbeByteProvider; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +public class Omf51ArchiveFileSystemFactory implements + GFileSystemFactoryByteProvider, GFileSystemProbeByteProvider { + + @Override + public Omf51ArchiveFileSystem create(FSRLRoot targetFSRL, ByteProvider byteProvider, + FileSystemService fsService, TaskMonitor monitor) + throws IOException, CancelledException { + + Omf51ArchiveFileSystem fs = new Omf51ArchiveFileSystem(targetFSRL, byteProvider); + try { + fs.mount(monitor); + } + catch (OmfException e) { + throw new IOException(e); + } + return fs; + } + + @Override + public boolean probe(ByteProvider byteProvider, FileSystemService fsService, + TaskMonitor monitor) throws IOException, CancelledException { + + if (byteProvider.length() < Omf51Loader.MIN_BYTE_LENGTH) { + return false; + } + + try { + AbstractOmfRecordFactory factory = new Omf51RecordFactory(byteProvider); + return factory.readNextRecord() instanceof Omf51LibraryHeaderRecord; + } + catch (OmfException | IOException e) { + return false; + } + } +}