Task 653: .SED File Format
Task 653: .SED File Format
File Format Specifications for .SED
The .SED file format refers to the IExpress Self Extraction Directive format, a proprietary text-based configuration file used by Microsoft's IExpress Wizard (iexpress.exe) to define instructions for building self-extracting executable (EXE) installer packages on Windows systems. These files are INI-style configuration files that specify package metadata, file inclusions, installation behaviors, and user interactions. They are primarily employed in software distribution to bundle files (e.g., executables, INF files, or data) into a single, compressed EXE that can self-extract and run setup routines.
The format is simple and human-readable, consisting of sections marked by square brackets (e.g., [Version], [Options], [Strings], [FileList]) followed by key-value pairs. Values can include wildcards, paths, and placeholders. The file is typically ASCII-encoded, with line endings in CRLF (Windows standard). It does not define a hierarchical file system like ZIP or TAR; instead, it acts as a directive for creating one during extraction. The resulting EXE uses cabinet (CAB) compression internally, but the .SED itself is uncompressed text.
Detailed specifications are not formally documented in Microsoft's public API (as IExpress is a legacy tool), but the structure can be derived from the IExpress Wizard's output and reverse-engineering examples. Key references include Microsoft's IExpress documentation and community analyses (e.g., SS64.com and MDGX.com). The SEDVersion header typically indicates version 3 (common in Windows XP through 11).
1. List of Properties Intrinsic to the .SED File Format
The properties below are core structural and behavioral elements defined within a .SED file. These are "intrinsic" as they directly influence the file's role in the IExpress "file system" (i.e., the extraction and installation process it orchestrates). They are grouped by section for clarity. Not all properties are mandatory; the format allows flexibility, but omitting critical ones (e.g., TargetName) will cause IExpress to fail.
[Version] Section (Metadata for Format Compatibility)
- Class: Fixed value "IExpress" (identifies the file type).
- SEDVersion: Integer version (e.g., 3); determines supported features.
[Options] Section (Extraction and Execution Behavior)
- ExtractOnly: Boolean (0 or 1); if 1, extracts files without running post-extraction commands.
- ExtractTitle: String; custom title for the extraction dialog.
- Uninstallable: Boolean (0 or 1); enables uninstall support if 1.
- ShowInstallProgramWindow: Boolean (0 or 1); controls visibility of the install program's window.
- HideExtractProgress: Boolean (0 or 1); suppresses the extraction progress UI.
- UseLongFileName: Boolean (1 recommended); preserves long filenames during extraction.
- CheckAdminRights: Boolean (0 or 1); requires administrator privileges.
- RestartIfNeededByRunApp: Boolean (0 or 1); prompts for restart if the launched app requires it.
- TargetDirectory: String; extraction path (e.g., %TEMP%\Extract).
- AdminQuietInstCmd: String; silent install command for admin mode (used with /Q flag).
[Strings] Section (User-Facing Text and Paths)
- Title: String; main window title for the installer.
- AppLaunched: String; primary command to run after extraction (e.g., "setup.exe" or "#Setup" for INF-based).
- PostInstallCmd: String; additional command after primary launch.
- AppLaunched2/AppLaunched3/AppLaunched4: Strings; secondary commands (up to 3 additional).
- FriendlyName: String; display name for the package.
- AppModified: String; optional modifier for the launched app.
- DirName: String; default extraction directory name.
- TargetName: String; output EXE filename (e.g., "MyInstaller.exe").
- ReadmeFile: String; path to a README file shown during extraction.
- ReadmeText: String; inline README content if no file.
- LicenseFile: String; path to LICENSE file for agreement prompt.
- LicenseText: String; inline license content.
- FinishedMessage: String; message after successful installation.
- AbortMessage: String; message on failure.
- DefaultPackageName: String; fallback package name.
[FileList] Section (File System Contents)
- File1 to File99: Strings; paths or wildcards to include files (e.g., "C:\source*.exe", "\server\share\data.txt"). These define the "file system" payload, supporting up to 99 entries. Relative paths are resolved from the .SED location.
These properties ensure the .SED acts as a blueprint for a self-contained file system in the resulting EXE, handling compression, extraction paths, and execution flow. For full interoperability, SEDVersion=3 is standard.
2. Two Direct Download Links for .SED Files
Based on publicly available resources, here are two direct links to sample .SED files. These are example configurations generated via IExpress Wizard and shared for educational purposes:
Sample 1: Basic installer directive (from SS64.com documentation).
https://ss64.com/nt/syntax_sed_sample1.txt (Rename to .sed after download.)
Sample 2: Advanced multi-file extraction example (from MDGX INF reference site).
https://www.mdgx.com/files/iexpress_sample.sed (Direct archive link; extract the .sed.)
These samples include standard sections and can be opened in any text editor for verification.
3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .SED Property Dump
Ghost (the blogging platform) supports embedding custom HTML/JavaScript in posts via the "HTML" card or code blocks. Below is a self-contained <script> block that creates a drag-and-drop zone. It parses the dropped .SED file as text, extracts properties by section using regex, and dumps them to a <pre> element below the drop zone. Embed this in a Ghost post by pasting it into an HTML card (enable "Code mode" in the editor). It uses the File API for client-side reading—no server needed.
Drag and drop a .SED file here to dump its properties.
This script handles basic parsing; for complex values (e.g., multi-line), it treats post-equals as the value.
4. Python Class for .SED Handling
The following Python class (SEDHandler) opens a .SED file, parses its properties into a dictionary, prints them to console, supports writing a modified version back to disk, and includes basic read/write methods. It uses configparser for INI-style parsing, which aligns perfectly with .SED structure.
import configparser
import os
class SEDHandler:
def __init__(self, filepath):
self.filepath = filepath
self.config = configparser.ConfigParser(allow_no_value=True)
self.load()
def load(self):
"""Read and parse the .SED file."""
if not os.path.exists(self.filepath):
raise FileNotFoundError(f".SED file not found: {self.filepath}")
self.config.read(self.filepath, encoding='utf-8')
def dump_properties(self):
"""Print all properties to console."""
print("SED Properties Dump:")
for section in self.config.sections():
print(f"[{section}]")
for key, value in self.config.items(section):
print(f" {key}: {value}")
print()
def write(self, output_path=None):
"""Write properties to a new .SED file."""
if output_path is None:
output_path = self.filepath
with open(output_path, 'w', encoding='utf-8') as f:
self.config.write(f)
print(f"Written to: {output_path}")
# Example usage:
# handler = SEDHandler("sample.sed")
# handler.dump_properties()
# handler.config.set("Strings", "Title", "New Title")
# handler.write("modified.sed")
To use: Instantiate with a file path, call dump_properties() for console output, modify via self.config, and call write() to save.
5. Java Class for .SED Handling
This Java class (SEDHandler) uses Properties for section-based parsing (treating each section as a separate Properties instance) and BufferedReader for reading. It prints properties to console, supports modifications, and writes back. Compile with javac SEDHandler.java and run with java SEDHandler <filepath>.
import java.io.*;
import java.util.*;
public class SEDHandler {
private String filepath;
private Map<String, Properties> sections = new HashMap<>();
public SEDHandler(String filepath) throws IOException {
this.filepath = filepath;
load();
}
private void load() throws IOException {
sections.clear();
String currentSection = null;
Properties currentProps = null;
try (BufferedReader reader = new BufferedReader(new FileReader(filepath))) {
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.startsWith("[") && line.endsWith("]")) {
currentSection = line.substring(1, line.length() - 1);
currentProps = new Properties();
sections.put(currentSection, currentProps);
} else if (currentSection != null && line.contains("=") && currentProps != null) {
String[] parts = line.split("=", 2);
currentProps.setProperty(parts[0].trim(), parts[1].trim());
}
}
}
}
public void dumpProperties() {
System.out.println("SED Properties Dump:");
for (Map.Entry<String, Properties> entry : sections.entrySet()) {
String section = entry.getKey();
Properties props = entry.getValue();
System.out.println("[" + section + "]");
for (Map.Entry<Object, Object> prop : props.entrySet()) {
System.out.println(" " + prop.getKey() + ": " + prop.getValue());
}
System.out.println();
}
}
public void write(String outputPath) throws IOException {
try (PrintWriter writer = new PrintWriter(new FileWriter(outputPath))) {
for (Map.Entry<String, Properties> entry : sections.entrySet()) {
writer.println("[" + entry.getKey() + "]");
for (Map.Entry<Object, Object> prop : entry.getValue().entrySet()) {
writer.println(prop.getKey() + "=" + prop.getValue());
}
writer.println();
}
}
System.out.println("Written to: " + outputPath);
}
public static void main(String[] args) throws IOException {
if (args.length != 1) {
System.err.println("Usage: java SEDHandler <sed-file>");
return;
}
SEDHandler handler = new SEDHandler(args[0]);
handler.dumpProperties();
// Example modification: handler.sections.get("Strings").setProperty("Title", "New Title");
// handler.write("modified.sed");
}
}
To use: Run from command line; modify via sections.get("Section").setProperty(key, value) before writing.
6. JavaScript Class for .SED Handling
This Node.js-compatible JavaScript class (SEDHandler) uses the fs module to read/parse/write .SED files. It prints properties to console (via console.log). Run with Node.js: node sed_handler.js sample.sed. For browser use, adapt fs to FileReader.
const fs = require('fs');
class SEDHandler {
constructor(filepath) {
this.filepath = filepath;
this.sections = new Map();
this.load();
}
load() {
if (!fs.existsSync(this.filepath)) {
throw new Error(`.SED file not found: ${this.filepath}`);
}
const content = fs.readFileSync(this.filepath, 'utf8');
let currentSection = null;
content.split('\n').forEach(line => {
line = line.trim();
if (line.startsWith('[') && line.endsWith(']')) {
currentSection = line.slice(1, -1);
this.sections.set(currentSection, new Map());
} else if (currentSection && line.includes('=')) {
const [key, ...valueParts] = line.split('=');
const value = valueParts.join('=').trim();
this.sections.get(currentSection).set(key.trim(), value);
}
});
}
dumpProperties() {
console.log('SED Properties Dump:');
for (const [section, props] of this.sections) {
console.log(`[${section}]`);
for (const [key, value] of props) {
console.log(` ${key}: ${value}`);
}
console.log('');
}
}
write(outputPath = this.filepath) {
let output = '';
for (const [section, props] of this.sections) {
output += `[${section}]\n`;
for (const [key, value] of props) {
output += `${key}=${value}\n`;
}
output += '\n';
}
fs.writeFileSync(outputPath, output.trim(), 'utf8');
console.log(`Written to: ${outputPath}`);
}
}
// Example usage:
// const handler = new SEDHandler('sample.sed');
// handler.dumpProperties();
// handler.sections.get('Strings').set('Title', 'New Title');
// handler.write('modified.sed');
To use: Require in a script; modify maps before writing.
7. C Class (Struct with Functions) for .SED Handling
This C implementation uses a struct (SEDHandler) with functions for loading, dumping, and writing. It parses line-by-line with string functions. Compile with gcc -o sed_handler sed_handler.c and run ./sed_handler sample.sed. It assumes <stdio.h>, <stdlib.h>, <string.h> for basics.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE 256
#define MAX_SECTIONS 100
#define MAX_PROPS 100
typedef struct {
char *key;
char *value;
} Property;
typedef struct {
char *name;
Property props[MAX_PROPS];
int prop_count;
} Section;
typedef struct {
char *filepath;
Section sections[MAX_SECTIONS];
int section_count;
} SEDHandler;
void load_sed(SEDHandler *handler) {
FILE *file = fopen(handler->filepath, "r");
if (!file) {
perror("File open error");
return;
}
char line[MAX_LINE];
char *current_section = NULL;
int sec_idx = -1;
while (fgets(line, sizeof(line), file)) {
line[strcspn(line, "\n")] = 0; // Trim newline
char *trimmed = line;
while (*trimmed == ' ' || *trimmed == '\t') trimmed++; // Trim leading whitespace
if (trimmed[0] == '[' && strrchr(trimmed, ']')) {
current_section = strdup(trimmed + 1);
current_section[strlen(current_section) - 1] = 0; // Remove ]
sec_idx = handler->section_count++;
handler->sections[sec_idx].name = current_section;
handler->sections[sec_idx].prop_count = 0;
} else if (current_section && strchr(trimmed, '=')) {
char *eq_pos = strchr(trimmed, '=');
*eq_pos = 0;
char *key = strdup(trimmed);
char *value = strdup(eq_pos + 1);
// Trim value trailing spaces
char *end = value + strlen(value) - 1;
while (end > value && (*end == ' ' || *end == '\t')) end--;
*(end + 1) = 0;
int prop_idx = handler->sections[sec_idx].prop_count++;
handler->sections[sec_idx].props[prop_idx].key = key;
handler->sections[sec_idx].props[prop_idx].value = value;
}
}
fclose(file);
}
void dump_properties(SEDHandler *handler) {
printf("SED Properties Dump:\n");
for (int i = 0; i < handler->section_count; i++) {
Section *sec = &handler->sections[i];
printf("[%s]\n", sec->name);
for (int j = 0; j < sec->prop_count; j++) {
printf(" %s: %s\n", sec->props[j].key, sec->props[j].value);
}
printf("\n");
}
}
void write_sed(SEDHandler *handler, const char *output_path) {
FILE *file = fopen(output_path, "w");
if (!file) {
perror("Write error");
return;
}
for (int i = 0; i < handler->section_count; i++) {
Section *sec = &handler->sections[i];
fprintf(file, "[%s]\n", sec->name);
for (int j = 0; j < sec->prop_count; j++) {
fprintf(file, "%s=%s\n", sec->props[j].key, sec->props[j].value);
}
fprintf(file, "\n");
}
fclose(file);
printf("Written to: %s\n", output_path);
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <sed-file>\n", argv[0]);
return 1;
}
SEDHandler handler = { .filepath = argv[1], .section_count = 0 };
load_sed(&handler);
dump_properties(&handler);
// Example modification: strcpy(handler.sections[0].props[0].value, "New Value");
// write_sed(&handler, "modified.sed");
// Free memory here in production
return 0;
}
To use: Compile and run; modifications require manual array updates (extend for dynamic allocation in production). This handles reading/writing and console printing as specified.