Importing Modules
Patterns can be split into multiple files to better separate pattern code into logical sections. To import other files into the current file, pattern language provides two facilities: the #include
directive and the import
statement. Both will search for a given file inside the includes
folder in pattern search paths. The default search paths are:
The ImHex installation directory.
The
AppData/Local/imhex
directory.Additional search paths. To add additional search paths go to
Extras > Settings > Folders
menu.
#include
directive
#include
directiveA preprocessor directive. The preprocessor replaces this directive with all the lexical tokens from the included file including all the preprocessor defines. On inclusion, this system maintains its own list of files marked with #pragma once
.
To use the #include
directive, specify a path to the file to include followed by the file extension. The path can be enclosed in double quotes ("path/filename.extension"
) or pointy braces (<path/filename.extension>
). The extension is optional. The #include
directive looks for a file with extensions .pat
and .hexpat
if none is specified. However, by convention, the extension is specified when including files. Both of the following are valid ways to use the #include
directive:
import
statement
import
statementThe import
statement is processed during the parsing stage. Once the parser encounters this statement, a separate parser is created to parse the imported file as a separate compilation unit. It's then inserted into the abstract syntax tree (AST) of the current file. The preprocessor defines don't propagate when using the import
statement.
The import
keyword is followed by the path to the file to include with the dot (.
) as the folder separator. The file extension will be resolved automatically. The statement looks for a file with extensions .pat
and .hexpat
. As with any other language statement, the line must end with a semicolon (;
).
import as
statement
import as
statementLibraries generally tend to place all their content under a specific namespace to prevent name collisions between different libraries. This however makes the code pretty verbose often.
To work around this, it's possible to change the namespace the types and functions are being imported into using the as
keyword.
Importing other Patterns
When working with file formats, it sometimes happens that other files or formats are nested inside of the current data. For example, in an Archive file there might be a header and file table followed by data in other formats such as if the Archive contains some executables and images side by side.
In cases like this, it's often useful to import other patterns into the current one so that the code for these other formats can still be used separately.
To accomplish this, an import * from <Pattern File> as <Type Name>
statement may be used.
This code creates a new struct type called Executable
from the content of the pe.hexpat
Pattern file. Whenever the Executable
type is placed anywhere in the current pattern now, the code in the imported pattern is evaluated instead, as the content was copy-pasted into the current file with all offsets adjusted.
The following implementation details might be interesting to know:
When placing the created type at address
0x1000
for example, the imported pattern will see the file as if it started at address0x1000
. It cannot access any bytes before0x1000
and it will think address0x1000
in the file is address0x00
.If there's only a single global placement in the imported pattern file, the content that pattern will be inlined into the newly created struct to not create another unnecessary indirection. If there's multiple global placements, they will be put in the created struct individually.
auto namespace
auto namespace
Since a single library file can contain more than one namespace and it's not always desirable to dump the content of all these namespaces into the same renamed namespace, it's possible to specify which namespace should be the one imported and renamed if needed.
To do this, mark the namespace as auto
:
Include guards
#pragma once
directive
#pragma once
directiveTo prevent duplicate declarations, files meant for importing to other files can use the #pragma once
directive to prevent multiple inclusions. Important, both #include
directive and import
statements keep their own list of files marked with #pragma once
. That means when a file is included in the current file, and then transitively imported by importing a different file, the current file would get duplicate declarations. In other words, a file should only ever be included by using one of the systems: the #include
directive or the import
statement to prevent duplicate declarations.
Manual include guards
When using #include
, you can write include guards manually using #ifndef
and #define
directives:
Importing standard library headers
The standard library headers all use the import
statement internally, and as such should be imported using import
.
Note however, that this only pertains to standard library headers. Custom patterns can still import standard library headers while using #include
for files that are part of the client project.
Forward declarations.
The pattern language supports forward declarations.
Last updated
Was this helpful?