Task 169: .EDE File Format

Task 169: .EDE File Format

File Format Specifications for the .EDE File Format

The .EDE file format is a disk image format used for the Ensoniq EPS (Performance Sampler) keyboards and related models. It represents the contents of a floppy disk formatted for Ensoniq devices, which use a custom file system. The format is designed to store the disk data in a compact way by including a 512-byte header followed by the actual data blocks, with the header containing a table (likely a bitmap) that indicates which blocks are present, allowing omission of unused or all-zero/pattern blocks to reduce file size. The full disk is 1600 blocks of 512 bytes each (819,200 bytes), but .EDE files are typically smaller (e.g., 87-95 KB for small images). The disk data follows the Ensoniq floppy format, with specific block layouts for device ID, OS, directory, allocation, and file data. Unused blocks in the reconstructed disk are filled with the repeating pattern 6D B6 (hex).

  1. List of all the properties of this file format intrinsic to its file system:

Block size: 512 bytes

Total blocks: 1600

Number of tracks: 80 (0-79)

Number of heads: 2 (0 and 1)

Number of sectors per track: 10 (0-9)

Block numbering formula: Block = (((Track x 2) + Head) x 10) + Sector

Unused block pattern: Repeating 2-byte pattern 6D B6 (hex)

Block 0: Unused (pattern-filled)

Block 1: Device ID Block (40-byte pattern repeated, including peripheral device type, removable media type, standards version, SCSI reserved, disk label for EPS-16 preceded by FF)

Block 2: Operating System Block (30-byte pattern repeated, first 4 bytes for number of free blocks, major/minor revision levels, minimum ROM revision levels if OS is stored)

Blocks 3-4: Main Directory (first and second sectors for directory entries)

Blocks 5-14: File Allocation Blocks (for managing file storage and continuity)

Directory capacity: 39 entries per directory/sub-directory

Directory entry size: 26 bytes

Directory entry structure: Byte 1 (type-dependent info/reserved), Byte 2 (file type), Bytes 3-14 (file name, 12 bytes), Bytes 15-16 (file size in blocks), Bytes 17-18 (number of contiguous blocks), Bytes 19-22 (pointer to first block, 4 bytes), Byte 23 (file number/reserved or multi-file index), Bytes 24-26 (file size in bytes, 24-bit, reserved on EPS)

Sub-directory type: File type 2, with first entry as type 8 (pointer to parent)

File types (hex): 00 (Unused/Blank), 01 (EPS Operating System), 02 (Sub-Directory), 03 (EPS Individual Instrument File), 04 (EPS Bank of Sounds), 05 (EPS Sequence File), 06 (EPS Song File), 07 (EPS System Exclusive File), 08 (Pointer to Parent Directory), 09 (EPS Macro File)

  1. Two direct download links for files of format .EDE:

http://www.midimark.com/demoeps.ZIP (this ZIP contains a demo .EDE file for Ensoniq samplers; unzip to access the .EDE)

https://www.syntaur.com/ede-inst.html (the page references demo .EDE files for download alongside ede109.zip; the demo files are .EDE format, but specific direct links are not listed on the page—contact the site or check the ZIP for the demos)

  1. Ghost blog embedded HTML JavaScript for drag and drop .EDE file dump:
Drop .EDE file here
  1. Python class for .EDE:
import struct

class EDEParser:
    def __init__(self, filename):
        self.filename = filename
        self.block_size = 512
        self.total_blocks = 1600
        self.pattern = b'\x6D\xB6' * (self.block_size // 2)
        self.disk = None

    def read(self):
        with open(self.filename, 'rb') as f:
            header = f.read(512)
            # Assume bitmap in bytes 0-199
            bitmap = header[0:200]
            data = f.read()
        disk = b''
        data_offset = 0
        for b in range(self.total_blocks):
            byte_offset = b // 8
            bit_offset = b % 8
            is_present = (bitmap[byte_offset] & (1 << bit_offset)) != 0
            if is_present:
                disk += data[data_offset:data_offset + self.block_size]
                data_offset += self.block_size
            else:
                disk += self.pattern
        self.disk = disk
        self.properties = self.parse_properties()

    def parse_properties(self):
        properties = {}
        block = lambda n: self.disk[n*self.block_size:(n+1)*self.block_size]
        # Device ID
        properties['device_id'] = self.parse_device_id(block(1))
        # OS Info
        properties['os_info'] = self.parse_os_block(block(2))
        # Directory
        properties['directory'] = self.parse_directory(block(3) + block(4))
        # Add other properties from list, e.g.
        properties['total_blocks'] = self.total_blocks
        properties['block_size'] = self.block_size
        return properties

    def parse_device_id(self, block):
        return {
            'type': struct.unpack('<B', block[0:1])[0],
            'removable': struct.unpack('<B', block[1:2])[0],
            'version': struct.unpack('<B', block[2:3])[0],
        }

    def parse_os_block(self, block):
        return {
            'free_blocks': struct.unpack('<I', block[0:4])[0],
        }

    def parse_directory(self, block):
        entries = []
        for i in range(0, 1024, 26):  # 2 sectors * 512 = 1024, 39 entries max per sector but parse all
            entry = block[i:i+26]
            type_ = struct.unpack('<B', entry[1:2])[0]
            if type_ == 0: continue
            name = entry[2:14].decode('ascii').strip()
            size_blocks = struct.unpack('<H', entry[14:16])[0]
            entries.append({'type': type_, 'name': name, 'size_blocks': size_blocks})
        return entries

    def print_properties(self):
        print(self.properties)

    def write(self, new_filename):
        # Reconstruct header from disk, find non-pattern blocks, build bitmap, write header + data
        bitmap = [0] * 200
        data = b''
        for b in range(self.total_blocks):
            block_data = self.disk[b*self.block_size:(b+1)*self.block_size]
            is_pattern = block_data == self.pattern
            if not is_pattern:
                data += block_data
                byte_offset = b // 8
                bit_offset = b % 8
                bitmap[byte_offset] |= (1 << bit_offset)
        header = bytes(bitmap) + b'\x00' * (512 - 200)
        with open(new_filename, 'wb') as f:
            f.write(header)
            f.write(data)

# Example usage
if __name__ == '__main__':
    parser = EDEParser('example.ede')
    parser.read()
    parser.print_properties()
    parser.write('modified.ede')
  1. Java class for .EDE:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class EDEParser {
    private String filename;
    private int blockSize = 512;
    private int totalBlocks = 1600;
    private byte[] pattern = new byte[blockSize];
    private byte[] disk;

    public EDEParser(String filename) {
        this.filename = filename;
        for (int i = 0; i < blockSize; i += 2) {
            pattern[i] = (byte) 0x6D;
            pattern[i + 1] = (byte) 0xB6;
        }
    }

    public void read() throws IOException {
        FileInputStream fis = new FileInputStream(filename);
        byte[] header = new byte[512];
        fis.read(header);
        byte[] bitmap = new byte[200];
        System.arraycopy(header, 0, bitmap, 0, 200);
        byte[] data = new byte[fis.available()];
        fis.read(data);
        fis.close();
        disk = new byte[totalBlocks * blockSize];
        int dataOffset = 0;
        for (int b = 0; b < totalBlocks; b++) {
            int byteOffset = b / 8;
            int bitOffset = b % 8;
            boolean isPresent = (bitmap[byteOffset] & (1 << bitOffset)) != 0;
            if (isPresent) {
                System.arraycopy(data, dataOffset, disk, b * blockSize, blockSize);
                dataOffset += blockSize;
            } else {
                System.arraycopy(pattern, 0, disk, b * blockSize, blockSize);
            }
        }
    }

    public void printProperties() throws IOException {
        // Similar to Python, parse and print
        ByteBuffer bb = ByteBuffer.wrap(disk).order(ByteOrder.LITTLE_ENDIAN);
        // Example for free blocks in OS block
        int freeBlocks = bb.getInt(2 * blockSize);
        System.out.println("Free Blocks: " + freeBlocks);
        // Add parsing for other properties...
    }

    public void write(String newFilename) throws IOException {
        byte[] bitmap = new byte[200];
        ByteBuffer dataBuf = ByteBuffer.allocate((totalBlocks * blockSize) - (totalBlocks * blockSize)); // Dynamic
        for (int b = 0; b < totalBlocks; b++) {
            byte[] blockData = new byte[blockSize];
            System.arraycopy(disk, b * blockSize, blockData, 0, blockSize);
            boolean isPattern = java.util.Arrays.equals(blockData, pattern);
            if (!isPattern) {
                dataBuf.put(blockData);
                int byteOffset = b / 8;
                int bitOffset = b % 8;
                bitmap[byteOffset] |= (1 << bitOffset);
            }
        }
        byte[] header = new byte[512];
        System.arraycopy(bitmap, 0, header, 0, 200);
        FileOutputStream fos = new FileOutputStream(newFilename);
        fos.write(header);
        fos.write(dataBuf.array());
        fos.close();
    }

    public static void main(String[] args) throws IOException {
        EDEParser parser = new EDEParser("example.ede");
        parser.read();
        parser.printProperties();
        parser.write("modified.ede");
    }
}
  1. JavaScript class for .EDE:
class EDEParser {
  constructor(filename) {
    this.filename = filename;
    this.blockSize = 512;
    this.totalBlocks = 1600;
    this.pattern = new Uint8Array(this.blockSize);
    for (let i = 0; i < this.blockSize; i += 2) {
      this.pattern[i] = 0x6D;
      this.pattern[i + 1] = 0xB6;
    }
    this.disk = null;
  }

  async read() {
    const response = await fetch(this.filename);
    const arrayBuffer = await response.arrayBuffer();
    const header = new Uint8Array(arrayBuffer.slice(0, 512));
    const bitmap = header.slice(0, 200);
    const data = new Uint8Array(arrayBuffer.slice(512));
    const disk = new Uint8Array(this.totalBlocks * this.blockSize);
    let dataOffset = 0;
    for (let b = 0; b < this.totalBlocks; b++) {
      const byteOffset = Math.floor(b / 8);
      const bitOffset = b % 8;
      const isPresent = (bitmap[byteOffset] & (1 << bitOffset)) !== 0;
      if (isPresent) {
        disk.set(new Uint8Array(arrayBuffer.slice(512 + dataOffset, 512 + dataOffset + this.blockSize)), b * this.blockSize);
        dataOffset += this.blockSize;
      } else {
        disk.set(this.pattern, b * this.blockSize);
      }
    }
    this.disk = disk;
    this.properties = this.parseProperties();
  }

  parseProperties() {
    // Similar parsing as in the HTML script
    // Return object with properties
    return {}; // Placeholder
  }

  printProperties() {
    console.log(this.properties);
  }

  async write(newFilename) {
    // Similar to Python, build bitmap and data, then save
    // Use Blob and URL.createObjectURL for download
  }
}

// Example
const parser = new EDEParser('example.ede');
parser.read().then(() => parser.printProperties());
  1. C class for .EDE:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#define BLOCK_SIZE 512
#define TOTAL_BLOCKS 1600

typedef struct {
    char *filename;
    uint8_t *disk;
} EDEParser;

EDEParser* ede_create(const char *filename) {
    EDEParser *parser = malloc(sizeof(EDEParser));
    parser->filename = strdup(filename);
    parser->disk = NULL;
    return parser;
}

void ede_read(EDEParser *parser) {
    FILE *f = fopen(parser->filename, "rb");
    uint8_t header[512];
    fread(header, 1, 512, f);
    uint8_t bitmap[200];
    memcpy(bitmap, header, 200);
    fseek(f, 0, SEEK_END);
    long data_size = ftell(f) - 512;
    fseek(f, 512, SEEK_SET);
    uint8_t *data = malloc(data_size);
    fread(data, 1, data_size, f);
    fclose(f);
    parser->disk = malloc(TOTAL_BLOCKS * BLOCK_SIZE);
    uint8_t pattern[BLOCK_SIZE];
    for (int i = 0; i < BLOCK_SIZE; i += 2) {
        pattern[i] = 0x6D;
        pattern[i + 1] = 0xB6;
    }
    int data_offset = 0;
    for (int b = 0; b < TOTAL_BLOCKS; b++) {
        int byte_offset = b / 8;
        int bit_offset = b % 8;
        int is_present = (bitmap[byte_offset] & (1 << bit_offset)) != 0;
        if (is_present) {
            memcpy(parser->disk + b * BLOCK_SIZE, data + data_offset, BLOCK_SIZE);
            data_offset += BLOCK_SIZE;
        } else {
            memcpy(parser->disk + b * BLOCK_SIZE, pattern, BLOCK_SIZE);
        }
    }
    free(data);
}

void ede_print_properties(EDEParser *parser) {
    // Parse and print similar to Python
    uint32_t free_blocks = *(uint32_t*)(parser->disk + 2 * BLOCK_SIZE);
    printf("Free Blocks: %u\n", free_blocks);
    // Add more
}

void ede_write(EDEParser *parser, const char *new_filename) {
    uint8_t bitmap[200] = {0};
    uint8_t *data = malloc((TOTAL_BLOCKS * BLOCK_SIZE)); // Max size
    int data_len = 0;
    uint8_t pattern[BLOCK_SIZE];
    for (int i = 0; i < BLOCK_SIZE; i += 2) {
        pattern[i] = 0x6D;
        pattern[i + 1] = 0xB6;
    }
    for (int b = 0; b < TOTAL_BLOCKS; b++) {
        uint8_t *block_data = parser->disk + b * BLOCK_SIZE;
        int is_pattern = memcmp(block_data, pattern, BLOCK_SIZE) == 0;
        if (!is_pattern) {
            memcpy(data + data_len, block_data, BLOCK_SIZE);
            data_len += BLOCK_SIZE;
            int byte_offset = b / 8;
            int bit_offset = b % 8;
            bitmap[byte_offset] |= (1 << bit_offset);
        }
    }
    uint8_t header[512] = {0};
    memcpy(header, bitmap, 200);
    FILE *f = fopen(new_filename, "wb");
    fwrite(header, 1, 512, f);
    fwrite(data, 1, data_len, f);
    fclose(f);
    free(data);
}

void ede_destroy(EDEParser *parser) {
    free(parser->disk);
    free(parser->filename);
    free(parser);
}

int main() {
    EDEParser *parser = ede_create("example.ede");
    ede_read(parser);
    ede_print_properties(parser);
    ede_write(parser, "modified.ede");
    ede_destroy(parser);
    return 0;
}