Task 620: .RS File Format

Task 620: .RS File Format

1. List of Properties for the .RS File Format

The .RS file format refers to the Sun Raster image format (also known as Sun Rasterfile), a bitmap format developed by Sun Microsystems. It is a binary format with a fixed 32-byte header (big-endian), an optional color map, and image data that may be uncompressed or RLE-compressed. The properties intrinsic to the format (i.e., the core metadata fields from the header that define the file's structure and content) are:

  • Magic Number: A 4-byte identifier always set to 0x59a66a95 (in hexadecimal) to confirm it's a Sun Raster file.
  • Width: A 4-byte unsigned integer representing the image width in pixels. Scan lines are padded to multiples of 16 bits.
  • Height: A 4-byte unsigned integer representing the image height in pixels.
  • Depth: A 4-byte unsigned integer indicating bits per pixel (common values: 1 for monochrome, 8 for paletted/grayscale, 24 or 32 for true color; 32-bit includes an alpha or pad byte).
  • Length: A 4-byte unsigned integer for the size of the image data section in bytes (excludes header and color map; may need calculation in older files).
  • Type: A 4-byte unsigned integer specifying the encoding/version (e.g., 0 = old/uncompressed, 1 = standard/uncompressed, 2 = RLE-compressed, 3 = RGB order, others for experimental or converted formats).
  • Color Map Type: A 4-byte unsigned integer indicating color map presence (0 = none, 1 = RGB, 2 = raw/other).
  • Color Map Length: A 4-byte unsigned integer for the size of the color map in bytes (0 if no map; for RGB maps, typically a multiple of 3 for red/green/blue planes).

These properties are extracted from the header. The color map (if present) and image data follow, but they are not "properties" per se—rather, variable content based on the header.

3. HTML/JavaScript for Drag-and-Drop .RS File Dumper (Embeddable in a Ghost Blog or Similar)

Here's a self-contained HTML page with embedded JavaScript. It allows dragging and dropping a .RS file, parses the header, and displays the properties on screen. Embed this in a blog post using an HTML block.

.RS File Properties Dumper
Drag and drop a .RS file here

    

4. Python Class for .RS File Handling

import struct
import os

class RSFile:
    HEADER_FORMAT = '>8I'  # Big-endian, 8 unsigned ints (32 bytes)
    MAGIC = 0x59a66a95

    def __init__(self, filepath):
        self.filepath = filepath
        self.magic = None
        self.width = None
        self.height = None
        self.depth = None
        self.length = None
        self.type = None
        self.map_type = None
        self.map_length = None
        self.color_map = b''
        self.image_data = b''

    def read(self):
        with open(self.filepath, 'rb') as f:
            header = f.read(32)
            if len(header) < 32:
                raise ValueError("File too small for header")
            unpacked = struct.unpack(self.HEADER_FORMAT, header)
            self.magic, self.width, self.height, self.depth, self.length, self.type, self.map_type, self.map_length = unpacked
            if self.magic != self.MAGIC:
                raise ValueError("Invalid magic number")
            self.color_map = f.read(self.map_length)
            self.image_data = f.read(self.length)  # Note: Does not decode RLE if present

    def print_properties(self):
        if self.magic is None:
            raise ValueError("File not read yet")
        print(f"Magic Number: 0x{self.magic:08x}")
        print(f"Width: {self.width}")
        print(f"Height: {self.height}")
        print(f"Depth: {self.depth}")
        print(f"Length: {self.length}")
        print(f"Type: {self.type}")
        print(f"Color Map Type: {self.map_type}")
        print(f"Color Map Length: {self.map_length}")

    def write(self, new_filepath=None):
        if self.magic is None:
            raise ValueError("No data to write")
        filepath = new_filepath or self.filepath
        with open(filepath, 'wb') as f:
            header = struct.pack(self.HEADER_FORMAT, self.magic, self.width, self.height, self.depth,
                                 self.length, self.type, self.map_type, self.map_length)
            f.write(header)
            f.write(self.color_map)
            f.write(self.image_data)

# Example usage:
# rs = RSFile('example.rs')
# rs.read()
# rs.print_properties()
# rs.write('modified.rs')

This class reads the file (decodes header, loads map and data), prints properties, and writes back (without modifying data; RLE encoding not implemented for simplicity).

5. Java Class for .RS File Handling

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

public class RSFile {
    private static final int MAGIC = 0x59a66a95;
    private static final int HEADER_SIZE = 32;

    private String filepath;
    private int magic;
    private int width;
    private int height;
    private int depth;
    private int length;
    private int type;
    private int mapType;
    private int mapLength;
    private byte[] colorMap;
    private byte[] imageData;

    public RSFile(String filepath) {
        this.filepath = filepath;
    }

    public void read() throws IOException {
        try (RandomAccessFile raf = new RandomAccessFile(filepath, "r")) {
            FileChannel channel = raf.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE).order(ByteOrder.BIG_ENDIAN);
            channel.read(buffer);
            buffer.flip();
            magic = buffer.getInt();
            if (magic != MAGIC) {
                throw new IOException("Invalid magic number");
            }
            width = buffer.getInt();
            height = buffer.getInt();
            depth = buffer.getInt();
            length = buffer.getInt();
            type = buffer.getInt();
            mapType = buffer.getInt();
            mapLength = buffer.getInt();

            colorMap = new byte[mapLength];
            buffer = ByteBuffer.allocate(mapLength);
            channel.read(buffer);
            buffer.flip();
            buffer.get(colorMap);

            imageData = new byte[length];
            buffer = ByteBuffer.allocate(length);
            channel.read(buffer);
            buffer.flip();
            buffer.get(imageData);
        }
    }

    public void printProperties() {
        System.out.printf("Magic Number: 0x%08X%n", magic);
        System.out.println("Width: " + width);
        System.out.println("Height: " + height);
        System.out.println("Depth: " + depth);
        System.out.println("Length: " + length);
        System.out.println("Type: " + type);
        System.out.println("Color Map Type: " + mapType);
        System.out.println("Color Map Length: " + mapLength);
    }

    public void write(String newFilepath) throws IOException {
        String outPath = (newFilepath != null) ? newFilepath : filepath;
        try (RandomAccessFile raf = new RandomAccessFile(outPath, "rw")) {
            FileChannel channel = raf.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE).order(ByteOrder.BIG_ENDIAN);
            buffer.putInt(magic);
            buffer.putInt(width);
            buffer.putInt(height);
            buffer.putInt(depth);
            buffer.putInt(length);
            buffer.putInt(type);
            buffer.putInt(mapType);
            buffer.putInt(mapLength);
            buffer.flip();
            channel.write(buffer);

            buffer = ByteBuffer.wrap(colorMap);
            channel.write(buffer);

            buffer = ByteBuffer.wrap(imageData);
            channel.write(buffer);
        }
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     RSFile rs = new RSFile("example.rs");
    //     rs.read();
    //     rs.printProperties();
    //     rs.write("modified.rs");
    // }
}

6. JavaScript Class for .RS File Handling

class RSFile {
    constructor(filepath) {
        this.filepath = filepath; // Note: In browser, use File object; here assuming Node.js for fs
        this.magic = null;
        this.width = null;
        this.height = null;
        this.depth = null;
        this.length = null;
        this.type = null;
        this.mapType = null;
        this.mapLength = null;
        this.colorMap = null;
        this.imageData = null;
    }

    async read() {
        const fs = require('fs/promises');
        const buffer = await fs.readFile(this.filepath);
        const view = new DataView(buffer.buffer);
        if (buffer.length < 32) {
            throw new Error('File too small for header');
        }
        this.magic = view.getUint32(0, false); // Big-endian
        if (this.magic !== 0x59a66a95) {
            throw new Error('Invalid magic number');
        }
        this.width = view.getUint32(4, false);
        this.height = view.getUint32(8, false);
        this.depth = view.getUint32(12, false);
        this.length = view.getUint32(16, false);
        this.type = view.getUint32(20, false);
        this.mapType = view.getUint32(24, false);
        this.mapLength = view.getUint32(28, false);
        this.colorMap = buffer.slice(32, 32 + this.mapLength);
        this.imageData = buffer.slice(32 + this.mapLength, 32 + this.mapLength + this.length);
    }

    printProperties() {
        if (this.magic === null) {
            throw new Error('File not read yet');
        }
        console.log(`Magic Number: 0x${this.magic.toString(16)}`);
        console.log(`Width: ${this.width}`);
        console.log(`Height: ${this.height}`);
        console.log(`Depth: ${this.depth}`);
        console.log(`Length: ${this.length}`);
        console.log(`Type: ${this.type}`);
        console.log(`Color Map Type: ${this.mapType}`);
        console.log(`Color Map Length: ${this.mapLength}`);
    }

    async write(newFilepath = this.filepath) {
        const fs = require('fs/promises');
        const header = new ArrayBuffer(32);
        const view = new DataView(header);
        view.setUint32(0, this.magic, false);
        view.setUint32(4, this.width, false);
        view.setUint32(8, this.height, false);
        view.setUint32(12, this.depth, false);
        view.setUint32(16, this.length, false);
        view.setUint32(20, this.type, false);
        view.setUint32(24, this.mapType, false);
        view.setUint32(28, this.mapLength, false);
        const fullBuffer = Buffer.concat([Buffer.from(header), this.colorMap, this.imageData]);
        await fs.writeFile(newFilepath, fullBuffer);
    }
}

// Example usage (Node.js):
// const rs = new RSFile('example.rs');
// await rs.read();
// rs.printProperties();
// await rs.write('modified.rs');

(Note: This assumes Node.js for file I/O; for browser, adapt with FileReader similar to the drag-drop script.)

7. C "Class" (Struct and Functions) for .RS File Handling

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <arpa/inet.h> // For htonl/ntohl (big-endian conversion)

#define MAGIC 0x59a66a95
#define HEADER_SIZE 32

typedef struct {
    uint32_t magic;
    uint32_t width;
    uint32_t height;
    uint32_t depth;
    uint32_t length;
    uint32_t type;
    uint32_t map_type;
    uint32_t map_length;
    uint8_t *color_map;
    uint8_t *image_data;
} RSFile;

RSFile* rsfile_create(const char *filepath) {
    RSFile *rs = malloc(sizeof(RSFile));
    if (rs == NULL) return NULL;
    rs->color_map = NULL;
    rs->image_data = NULL;
    // Read immediately or separately; here we leave for read function
    return rs;
}

void rsfile_read(RSFile *rs, const char *filepath) {
    FILE *f = fopen(filepath, "rb");
    if (!f) {
        perror("Failed to open file");
        return;
    }
    uint32_t header[8];
    if (fread(header, sizeof(uint32_t), 8, f) != 8) {
        fprintf(stderr, "File too small for header\n");
        fclose(f);
        return;
    }
    // Convert from big-endian
    for (int i = 0; i < 8; i++) {
        header[i] = ntohl(header[i]);
    }
    rs->magic = header[0];
    if (rs->magic != MAGIC) {
        fprintf(stderr, "Invalid magic number\n");
        fclose(f);
        return;
    }
    rs->width = header[1];
    rs->height = header[2];
    rs->depth = header[3];
    rs->length = header[4];
    rs->type = header[5];
    rs->map_type = header[6];
    rs->map_length = header[7];

    rs->color_map = malloc(rs->map_length);
    if (rs->color_map && fread(rs->color_map, 1, rs->map_length, f) != rs->map_length) {
        free(rs->color_map);
        rs->color_map = NULL;
    }

    rs->image_data = malloc(rs->length);
    if (rs->image_data && fread(rs->image_data, 1, rs->length, f) != rs->length) {
        free(rs->image_data);
        rs->image_data = NULL;
    }
    fclose(f);
}

void rsfile_print_properties(const RSFile *rs) {
    if (rs->magic == 0) {
        fprintf(stderr, "File not read yet\n");
        return;
    }
    printf("Magic Number: 0x%08x\n", rs->magic);
    printf("Width: %u\n", rs->width);
    printf("Height: %u\n", rs->height);
    printf("Depth: %u\n", rs->depth);
    printf("Length: %u\n", rs->length);
    printf("Type: %u\n", rs->type);
    printf("Color Map Type: %u\n", rs->map_type);
    printf("Color Map Length: %u\n", rs->map_length);
}

void rsfile_write(const RSFile *rs, const char *new_filepath) {
    if (rs->magic == 0) {
        fprintf(stderr, "No data to write\n");
        return;
    }
    FILE *f = fopen(new_filepath, "wb");
    if (!f) {
        perror("Failed to open file for writing");
        return;
    }
    uint32_t header[8] = {
        htonl(rs->magic), htonl(rs->width), htonl(rs->height), htonl(rs->depth),
        htonl(rs->length), htonl(rs->type), htonl(rs->map_type), htonl(rs->map_length)
    };
    fwrite(header, sizeof(uint32_t), 8, f);
    if (rs->color_map) fwrite(rs->color_map, 1, rs->map_length, f);
    if (rs->image_data) fwrite(rs->image_data, 1, rs->length, f);
    fclose(f);
}

void rsfile_destroy(RSFile *rs) {
    free(rs->color_map);
    free(rs->image_data);
    free(rs);
}

// Example usage:
// int main() {
//     RSFile *rs = rsfile_create("example.rs");
//     rsfile_read(rs, "example.rs");
//     rsfile_print_properties(rs);
//     rsfile_write(rs, "modified.rs");
//     rsfile_destroy(rs);
//     return 0;
// }