Task 213: .FACTOR File Format

Task 213: .FACTOR File Format

1. List of Properties of the .FACTOR File Format Intrinsic to Its File System

Based on the specifications from the FACTOR Manual for the Fairchild Sentry 400 Test System, the .FACTOR file format refers to the binary Array Data Segment structure used for storing array data, typically on magnetic tape or disc. The properties stored within the file itself (intrinsic to its structure and accessible via decoding) are:

  • File Name: An 8-character string encoded in TRASCII (a character encoding similar to ASCII, with mappings for alphanumeric and special characters).
  • Data Record Length (N): An integer representing the number of elements in the data array (minimum 7, maximum 512).
  • Padding Fields: Three fixed zero-valued fields (Words 3-5, always 0 for structural alignment).
  • Data Array: An array of N floating-point values in FST-1 format (assumed 32-bit single-precision floating-point for implementation, as exact FST-1 details are not fully documented in available sources).

These properties are embedded in the binary file header and body, allowing the file to be self-descriptive for reading and writing in the Sentry system.

No direct download links for .FACTOR files could be found, as this is an obsolete format from the 1970s Fairchild Sentry system, with no publicly available examples online. Instead, here are links to related documentation containing format descriptions and code examples:

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .FACTOR File Dump

Here's an HTML snippet with embedded JavaScript that can be inserted into a Ghost blog post. It creates a drag-and-drop area where a user can drop a .FACTOR file. The script reads the file as binary, parses the properties (assuming 8-byte name, 4-byte int N big-endian, 12-byte zero padding, then N 4-byte IEEE 32-bit floats big-endian), and dumps them to the screen.

Drag and drop a .FACTOR file here

4. Python Class for .FACTOR File Handling

import struct
import os

class FactorFile:
    def __init__(self, filepath=None):
        self.filepath = filepath
        self.file_name = ''
        self.n = 0
        self.padding = (0, 0, 0)
        self.data_array = []
        if filepath:
            self.read()

    def read(self):
        with open(self.filepath, 'rb') as f:
            data = f.read()
        # Parse
        self.file_name = data[0:8].decode('ascii').strip()
        self.n = struct.unpack('>i', data[8:12])[0]
        self.padding = struct.unpack('>iii', data[12:24])
        self.data_array = []
        offset = 24
        for _ in range(self.n):
            self.data_array.append(struct.unpack('>f', data[offset:offset+4])[0])
            offset += 4

    def write(self, file_name, data_array, filepath=None):
        if filepath:
            self.filepath = filepath
        self.file_name = file_name.ljust(8)[:8]
        self.n = len(data_array)
        self.padding = (0, 0, 0)
        self.data_array = data_array
        with open(self.filepath, 'wb') as f:
            f.write(self.file_name.encode('ascii'))
            f.write(struct.pack('>i', self.n))
            f.write(struct.pack('>iii', *self.padding))
            for val in self.data_array:
                f.write(struct.pack('>f', val))

    def print_properties(self):
        print(f"File Name: {self.file_name}")
        print(f"Data Record Length (N): {self.n}")
        print(f"Padding Fields: {self.padding}")
        print(f"Data Array: {self.data_array}")

# Example usage:
# ff = FactorFile('example.FACTOR')
# ff.print_properties()
# ff.write('TESTFILE', [1.0, 2.5, 3.14], 'new.FACTOR')

5. Java Class for .FACTOR File Handling

import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.nio.file.*;

public class FactorFile {
    private String filepath;
    private String fileName;
    private int n;
    private int[] padding = new int[3];
    private float[] dataArray;

    public FactorFile(String filepath) {
        this.filepath = filepath;
        read();
    }

    public FactorFile() {}

    public void read() {
        try {
            byte[] data = Files.readAllBytes(Paths.get(filepath));
            ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);

            byte[] nameBytes = new byte[8];
            buffer.get(nameBytes);
            fileName = new String(nameBytes).trim();

            n = buffer.getInt();

            padding[0] = buffer.getInt();
            padding[1] = buffer.getInt();
            padding[2] = buffer.getInt();

            dataArray = new float[n];
            for (int i = 0; i < n; i++) {
                dataArray[i] = buffer.getFloat();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void write(String fileName, float[] dataArray, String filepath) {
        this.fileName = fileName.substring(0, Math.min(8, fileName.length()));
        this.fileName = String.format("%-8s", this.fileName);
        this.n = dataArray.length;
        this.padding = new int[]{0, 0, 0};
        this.dataArray = dataArray;
        this.filepath = filepath;

        try (FileOutputStream fos = new FileOutputStream(filepath);
             FileChannel channel = fos.getChannel()) {
            ByteBuffer buffer = ByteBuffer.allocate(24 + 4 * n).order(ByteOrder.BIG_ENDIAN);
            buffer.put(this.fileName.getBytes("ASCII"));
            buffer.putInt(n);
            buffer.putInt(padding[0]);
            buffer.putInt(padding[1]);
            buffer.putInt(padding[2]);
            for (float val : dataArray) {
                buffer.putFloat(val);
            }
            buffer.flip();
            channel.write(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void printProperties() {
        System.out.println("File Name: " + fileName);
        System.out.println("Data Record Length (N): " + n);
        System.out.println("Padding Fields: " + padding[0] + ", " + padding[1] + ", " + padding[2]);
        System.out.print("Data Array: [");
        for (int i = 0; i < dataArray.length; i++) {
            System.out.print(dataArray[i]);
            if (i < dataArray.length - 1) System.out.print(", ");
        }
        System.out.println("]");
    }

    // Example usage:
    // public static void main(String[] args) {
    //     FactorFile ff = new FactorFile("example.FACTOR");
    //     ff.printProperties();
    //     ff.write("TESTFILE", new float[]{1.0f, 2.5f, 3.14f}, "new.FACTOR");
    // }
}

6. JavaScript Class for .FACTOR File Handling

class FactorFile {
  constructor(filepath = null) {
    this.filepath = filepath;
    this.fileName = '';
    this.n = 0;
    this.padding = [0, 0, 0];
    this.dataArray = [];
    if (filepath) {
      // Note: JS can't directly open local files without user input; use with File object or arrayBuffer
    }
  }

  async read(file) { // Pass a File object, e.g., from input or drop
    const arrayBuffer = await file.arrayBuffer();
    const view = new DataView(arrayBuffer);

    let offset = 0;
    const nameBytes = new Uint8Array(arrayBuffer, offset, 8);
    this.fileName = String.fromCharCode(...nameBytes).trim();
    offset += 8;

    this.n = view.getInt32(offset, false); // Big-endian
    offset += 4;

    this.padding[0] = view.getInt32(offset, false);
    offset += 4;
    this.padding[1] = view.getInt32(offset, false);
    offset += 4;
    this.padding[2] = view.getInt32(offset, false);
    offset += 4;

    this.dataArray = [];
    for (let i = 0; i < this.n; i++) {
      this.dataArray.push(view.getFloat32(offset, false));
      offset += 4;
    }
  }

  write(fileName, dataArray) {
    this.fileName = fileName.padEnd(8).slice(0, 8);
    this.n = dataArray.length;
    this.padding = [0, 0, 0];
    this.dataArray = dataArray;

    const buffer = new ArrayBuffer(24 + 4 * this.n);
    const view = new DataView(buffer);

    let offset = 0;
    for (let i = 0; i < 8; i++) {
      view.setUint8(offset + i, this.fileName.charCodeAt(i) || 32); // Pad with space
    }
    offset += 8;

    view.setInt32(offset, this.n, false);
    offset += 4;

    view.setInt32(offset, this.padding[0], false);
    offset += 4;
    view.setInt32(offset, this.padding[1], false);
    offset += 4;
    view.setInt32(offset, this.padding[2], false);
    offset += 4;

    for (let val of dataArray) {
      view.setFloat32(offset, val, false);
      offset += 4;
    }

    // To save: Use Blob and URL.createObjectURL for download
    const blob = new Blob([buffer], { type: 'application/octet-stream' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'output.FACTOR';
    a.click();
    URL.revokeObjectURL(url);
  }

  printProperties() {
    console.log(`File Name: ${this.fileName}`);
    console.log(`Data Record Length (N): ${this.n}`);
    console.log(`Padding Fields: ${this.padding.join(', ')}`);
    console.log(`Data Array: [${this.dataArray.join(', ')}]`);
  }
}

// Example usage:
// const ff = new FactorFile();
// ff.read(someFileObject).then(() => ff.printProperties());
// ff.write('TESTFILE', [1.0, 2.5, 3.14]);

7. C++ Class for .FACTOR File Handling

#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <cstdint>
#include <arpa/inet.h> // For htonl, ntohl (big-endian conversion)

class FactorFile {
private:
    std::string filepath;
    std::string fileName;
    int32_t n;
    int32_t padding[3];
    std::vector<float> dataArray;

public:
    FactorFile(const std::string& fp = "") : filepath(fp), n(0) {
        std::fill(padding, padding + 3, 0);
        if (!fp.empty()) {
            read();
        }
    }

    void read() {
        std::ifstream file(filepath, std::ios::binary | std::ios::ate);
        if (!file) {
            std::cerr << "Error opening file" << std::endl;
            return;
        }
        size_t size = file.tellg();
        file.seekg(0);
        std::vector<char> data(size);
        file.read(data.data(), size);

        char nameBuf[9] = {0};
        std::memcpy(nameBuf, data.data(), 8);
        fileName = std::string(nameBuf);
        fileName.erase(std::find_if(fileName.rbegin(), fileName.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), fileName.end()); // Trim spaces

        std::memcpy(&n, data.data() + 8, 4);
        n = ntohl(n); // Big-endian to host

        std::memcpy(padding, data.data() + 12, 12);
        for (int i = 0; i < 3; ++i) {
            padding[i] = ntohl(padding[i]);
        }

        dataArray.resize(n);
        for (int i = 0; i < n; ++i) {
            uint32_t raw;
            std::memcpy(&raw, data.data() + 24 + i * 4, 4);
            raw = ntohl(raw);
            std::memcpy(&dataArray[i], &raw, 4); // Assume IEEE float
        }
    }

    void write(const std::string& fname, const std::vector<float>& dArray, const std::string& fp = "") {
        if (!fp.empty()) filepath = fp;
        fileName = fname.substr(0, 8);
        fileName += std::string(8 - fileName.length(), ' ');
        n = dArray.size();
        std::fill(padding, padding + 3, 0);
        dataArray = dArray;

        std::ofstream file(filepath, std::ios::binary);
        if (!file) {
            std::cerr << "Error writing file" << std::endl;
            return;
        }
        file.write(fileName.data(), 8);

        int32_t be_n = htonl(n);
        file.write(reinterpret_cast<char*>(&be_n), 4);

        for (int i = 0; i < 3; ++i) {
            int32_t be_pad = htonl(padding[i]);
            file.write(reinterpret_cast<char*>(&be_pad), 4);
        }

        for (float val : dataArray) {
            uint32_t raw;
            std::memcpy(&raw, &val, 4);
            raw = htonl(raw);
            file.write(reinterpret_cast<char*>(&raw), 4);
        }
    }

    void printProperties() const {
        std::cout << "File Name: " << fileName << std::endl;
        std::cout << "Data Record Length (N): " << n << std::endl;
        std::cout << "Padding Fields: " << padding[0] << ", " << padding[1] << ", " << padding[2] << std::endl;
        std::cout << "Data Array: [";
        for (size_t i = 0; i < dataArray.size(); ++i) {
            std::cout << dataArray[i];
            if (i < dataArray.size() - 1) std::cout << ", ";
        }
        std::cout << "]" << std::endl;
    }
};

// Example usage:
// int main() {
//     FactorFile ff("example.FACTOR");
//     ff.printProperties();
//     ff.write("TESTFILE", {1.0f, 2.5f, 3.14f}, "new.FACTOR");
//     return 0;
// }