Task 645: .SCR File Format
Task 645: .SCR File Format
File Format Specifications for .SCR (ZX Spectrum Screen Format)
The .SCR file format is a raster image format primarily associated with the ZX Spectrum home computer, used for storing screenshots or screen dumps. It represents a direct memory dump of the ZX Spectrum's video RAM, capturing both pixel bitmap data and color attribute information. The format is simple, headerless, and fixed-size, with no compression or variable metadata. It supports a fixed resolution of 256×192 pixels in monochrome (1 bit per pixel) for the bitmap, augmented by an 8×8 block-based attribute map that defines colors, brightness, and flashing effects from a fixed 15-color palette (8 base colors with bright variants).
Key aspects of the format:
- Total file size: Exactly 6912 bytes.
- Bitmap data: First 6144 bytes (256×192 pixels, 1 bpp, row-major order with 32 bytes per scanline; each byte's bits are MSB-first, left-to-right).
- Attribute data: Last 768 bytes (32×24 grid of 8×8 pixel blocks; each byte encodes foreground/background colors (bits 0–3/4–7), brightness (bit 6), and flash (bit 7)).
- Palette: Fixed ZX Spectrum colors (black=0, blue=1, red=2, magenta=3, green=4, cyan=5, yellow=6, white=7; bits 0–2 select color, bit 3 unused for base, bright mode shifts to 8–15 equivalents).
- No endianness issues: Byte order is as stored in memory.
- Validation: Files must be precisely 6912 bytes; deviations indicate invalid format.
This format is uncompressed and self-contained, ideal for emulation and retro computing applications. It lacks advanced features like transparency or multiple layers but efficiently captures the ZX Spectrum's unique attribute-clash rendering.
1. List of Properties Intrinsic to the File Format
The following properties are inherent to the .SCR format's structure, derived from its memory-dump design. These are fixed and non-variable, forming the "file system" of the format (i.e., its rigid layout without headers or extensible sections).
| Property | Description | Value/Type |
|---|---|---|
| File Size | Total length of the valid file | 6912 bytes (fixed) |
| Bitmap Offset | Starting position of pixel data | 0 bytes |
| Bitmap Size | Length of the monochrome pixel bitmap | 6144 bytes |
| Pixel Width | Horizontal resolution in pixels | 256 pixels |
| Pixel Height | Vertical resolution in pixels | 192 pixels |
| Bits per Pixel | Depth of the bitmap data | 1 bit |
| Bytes per Scanline | Bytes required per horizontal line in the bitmap | 32 bytes |
| Scanline Order | Layout of pixel lines | Row-major (sequential) |
| Bit Order in Byte | Pixel bit positioning within each byte | MSB-first (leftmost pixel) |
| Attribute Offset | Starting position of color attribute data | 6144 bytes |
| Attribute Size | Length of the attribute map | 768 bytes |
| Attribute Columns | Horizontal blocks (each covering 8 pixels) | 32 |
| Attribute Rows | Vertical blocks (each covering 8 pixels) | 24 |
| Foreground Color Bits | Bits defining ink (text/draw) color in each attribute byte | Bits 0–3 (0–7) |
| Background Color Bits | Bits defining paper (background) color in each attribute byte | Bits 4–7 (0–7) |
| Brightness Flag | Bit indicating bright color mode | Bit 6 (0=normal, 1=bright) |
| Flash Flag | Bit indicating inverse color swapping | Bit 7 (0=off, 1=on) |
| Palette Size | Number of base colors available | 8 (with 7 bright variants for 15 total) |
| Compression | Data encoding method | None (raw binary) |
| Header Presence | Existence of metadata header | None |
These properties ensure compatibility across ZX Spectrum emulators and tools, with no optional fields.
2. Two Direct Download Links for .SCR Files
- Example 1: "Load screen" by Pak-Zer0 (2013) – https://zxart.ee/file/id:18285/filename:Pak-Zer0_-Load_screen%282013%29_%28II_MICROCOMPO_ZX_1-BIT_2013%2C_5%29.scr
- Example 2: "3.scr" by A-Graph (1997) – https://zxart.ee/file/id:53658/filename:A-Graph_-3.scr%281997%29.scr
3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .SCR Property Dump
The following is a self-contained HTML snippet with embedded JavaScript, suitable for embedding in a Ghost blog post via the HTML card. It enables drag-and-drop of a .SCR file onto a designated area, validates the format, decodes the structural properties, and dumps them to the screen in a formatted list. No external dependencies are required; it uses the File API for browser compatibility.
4. Python Class for .SCR Handling
This class opens a .SCR file, decodes and validates the properties, prints them to console, and supports writing a new valid .SCR file (e.g., blank screen). It uses built-in struct and bytes for binary handling.
import os
class SCRDecoder:
FILE_SIZE = 6912
BITMAP_SIZE = 6144
BITMAP_OFFSET = 0
ATTR_OFFSET = 6144
WIDTH = 256
HEIGHT = 192
BYTES_PER_LINE = 32
ATTR_COLS = 32
ATTR_ROWS = 24
def __init__(self, filepath=None):
self.filepath = filepath
self.data = None
if filepath:
self.load()
def load(self):
if not os.path.exists(self.filepath):
raise FileNotFoundError(f".SCR file not found: {self.filepath}")
with open(self.filepath, 'rb') as f:
self.data = f.read()
if len(self.data) != self.FILE_SIZE:
raise ValueError(f"Invalid .SCR: Expected {self.FILE_SIZE} bytes, got {len(self.data)}")
def print_properties(self):
if not self.data:
print("No data loaded. Call load() first.")
return
props = {
'File Size': f"{len(self.data)} bytes (valid)",
'Bitmap Offset': f"{self.BITMAP_OFFSET} bytes",
'Bitmap Size': f"{self.BITMAP_SIZE} bytes",
'Pixel Width': f"{self.WIDTH} pixels",
'Pixel Height': f"{self.HEIGHT} pixels",
'Bits per Pixel': "1 bit",
'Bytes per Scanline': f"{self.BYTES_PER_LINE} bytes",
'Attribute Offset': f"{self.ATTR_OFFSET} bytes",
'Attribute Size': f"{self.FILE_SIZE - self.ATTR_OFFSET} bytes",
'Attribute Columns': f"{self.ATTR_COLS}",
'Attribute Rows': f"{self.ATTR_ROWS}",
'Palette Size': "8 base colors (15 total with bright)",
'Compression': "None",
'Header Presence': "None"
}
for key, value in props.items():
print(f"{key}: {value}")
def write(self, output_path, bitmap_data=None, attr_data=None):
if bitmap_data is None:
bitmap_data = b'\x00' * self.BITMAP_SIZE # Blank bitmap
if attr_data is None:
attr_data = b'\x00' * (self.FILE_SIZE - self.BITMAP_SIZE) # Default attributes (black on black)
if len(bitmap_data) != self.BITMAP_SIZE or len(attr_data) != (self.FILE_SIZE - self.BITMAP_SIZE):
raise ValueError("Invalid data sizes for write.")
with open(output_path, 'wb') as f:
f.write(bitmap_data + attr_data)
print(f"Wrote valid .SCR to {output_path}")
# Example usage:
# decoder = SCRDecoder('example.scr')
# decoder.print_properties()
# decoder.write('blank.scr')
5. Java Class for .SCR Handling
This class uses java.io for file I/O, decodes properties upon loading, prints them to console via System.out, and supports writing a new .SCR file. Compile with javac SCRDecoder.java and run with java SCRDecoder example.scr.
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class SCRDecoder {
private static final int FILE_SIZE = 6912;
private static final int BITMAP_SIZE = 6144;
private static final int BITMAP_OFFSET = 0;
private static final int ATTR_OFFSET = 6144;
private static final int WIDTH = 256;
private static final int HEIGHT = 192;
private static final int BYTES_PER_LINE = 32;
private static final int ATTR_COLS = 32;
private static final int ATTR_ROWS = 24;
private byte[] data;
private String filepath;
public SCRDecoder(String filepath) throws IOException {
this.filepath = filepath;
load();
}
private void load() throws IOException {
this.data = Files.readAllBytes(Paths.get(filepath));
if (data.length != FILE_SIZE) {
throw new IOException("Invalid .SCR: Expected " + FILE_SIZE + " bytes, got " + data.length);
}
}
public void printProperties() {
if (data == null) {
System.out.println("No data loaded.");
return;
}
Map<String, String> props = new HashMap<>();
props.put("File Size", data.length + " bytes (valid)");
props.put("Bitmap Offset", BITMAP_OFFSET + " bytes");
props.put("Bitmap Size", BITMAP_SIZE + " bytes");
props.put("Pixel Width", WIDTH + " pixels");
props.put("Pixel Height", HEIGHT + " pixels");
props.put("Bits per Pixel", "1 bit");
props.put("Bytes per Scanline", BYTES_PER_LINE + " bytes");
props.put("Attribute Offset", ATTR_OFFSET + " bytes");
props.put("Attribute Size", (FILE_SIZE - ATTR_OFFSET) + " bytes");
props.put("Attribute Columns", Integer.toString(ATTR_COLS));
props.put("Attribute Rows", Integer.toString(ATTR_ROWS));
props.put("Palette Size", "8 base colors (15 total with bright)");
props.put("Compression", "None");
props.put("Header Presence", "None");
props.forEach((key, value) -> System.out.println(key + ": " + value));
}
public void write(String outputPath, byte[] bitmapData, byte[] attrData) throws IOException {
if (bitmapData.length != BITMAP_SIZE || attrData.length != (FILE_SIZE - BITMAP_SIZE)) {
throw new IllegalArgumentException("Invalid data sizes for write.");
}
try (FileOutputStream fos = new FileOutputStream(outputPath)) {
fos.write(bitmapData);
fos.write(attrData);
}
System.out.println("Wrote valid .SCR to " + outputPath);
}
public static void main(String[] args) throws IOException {
if (args.length == 0) {
System.out.println("Usage: java SCRDecoder <scr-file>");
return;
}
SCRDecoder decoder = new SCRDecoder(args[0]);
decoder.printProperties();
// Example write (blank):
// decoder.write("blank.scr", new byte[BITMAP_SIZE], new byte[FILE_SIZE - BITMAP_SIZE]);
}
}
6. JavaScript Class for .SCR Handling
This Node.js-compatible class uses the fs module to open files, decode properties, print to console, and write new files. Run with node scr-decoder.js example.scr. For browser use, adapt fs to File API.
const fs = require('fs');
class SCRDecoder {
constructor(filepath = null) {
this.FILE_SIZE = 6912;
this.BITMAP_SIZE = 6144;
this.BITMAP_OFFSET = 0;
this.ATTR_OFFSET = 6144;
this.WIDTH = 256;
this.HEIGHT = 192;
this.BYTES_PER_LINE = 32;
this.ATTR_COLS = 32;
this.ATTR_ROWS = 24;
this.filepath = filepath;
this.data = null;
if (filepath) {
this.load();
}
}
load() {
if (!fs.existsSync(this.filepath)) {
throw new Error(`.SCR file not found: ${this.filepath}`);
}
this.data = fs.readFileSync(this.filepath);
if (this.data.length !== this.FILE_SIZE) {
throw new Error(`Invalid .SCR: Expected ${this.FILE_SIZE} bytes, got ${this.data.length}`);
}
}
printProperties() {
if (!this.data) {
console.log('No data loaded. Call load() first.');
return;
}
const props = {
'File Size': `${this.data.length} bytes (valid)`,
'Bitmap Offset': `${this.BITMAP_OFFSET} bytes`,
'Bitmap Size': `${this.BITMAP_SIZE} bytes`,
'Pixel Width': `${this.WIDTH} pixels`,
'Pixel Height': `${this.HEIGHT} pixels`,
'Bits per Pixel': '1 bit',
'Bytes per Scanline': `${this.BYTES_PER_LINE} bytes`,
'Attribute Offset': `${this.ATTR_OFFSET} bytes`,
'Attribute Size': `${this.FILE_SIZE - this.ATTR_OFFSET} bytes`,
'Attribute Columns': `${this.ATTR_COLS}`,
'Attribute Rows': `${this.ATTR_ROWS}`,
'Palette Size': '8 base colors (15 total with bright)',
'Compression': 'None',
'Header Presence': 'None'
};
Object.entries(props).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
}
write(outputPath, bitmapData = null, attrData = null) {
if (bitmapData === null) bitmapData = Buffer.alloc(this.BITMAP_SIZE, 0); // Blank
if (attrData === null) attrData = Buffer.alloc(this.FILE_SIZE - this.BITMAP_SIZE, 0); // Default
if (bitmapData.length !== this.BITMAP_SIZE || attrData.length !== (this.FILE_SIZE - this.BITMAP_SIZE)) {
throw new Error('Invalid data sizes for write.');
}
fs.writeFileSync(outputPath, Buffer.concat([bitmapData, attrData]));
console.log(`Wrote valid .SCR to ${outputPath}`);
}
// Example usage:
// const decoder = new SCRDecoder('example.scr');
// decoder.printProperties();
// decoder.write('blank.scr');
7. C Implementation for .SCR Handling
This C program defines functions equivalent to a class (load, print properties, write), using stdio.h and stdlib.h for file I/O. Compile with gcc scr_decoder.c -o scr_decoder and run ./scr_decoder example.scr. It validates and prints properties; write creates a blank file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FILE_SIZE 6912
#define BITMAP_SIZE 6144
#define BITMAP_OFFSET 0
#define ATTR_OFFSET 6144
#define WIDTH 256
#define HEIGHT 192
#define BYTES_PER_LINE 32
#define ATTR_COLS 32
#define ATTR_ROWS 24
unsigned char *data = NULL;
int load_scr(const char *filepath) {
FILE *fp = fopen(filepath, "rb");
if (!fp) {
perror("File not found");
return 0;
}
data = (unsigned char *)malloc(FILE_SIZE);
if (fread(data, 1, FILE_SIZE, fp) != FILE_SIZE) {
free(data);
data = NULL;
fclose(fp);
fprintf(stderr, "Invalid .SCR: Expected %d bytes\n", FILE_SIZE);
return 0;
}
fclose(fp);
return 1;
}
void print_properties() {
if (!data) {
printf("No data loaded.\n");
return;
}
printf("ZX Spectrum .SCR Properties:\n");
printf("File Size: %d bytes (valid)\n", FILE_SIZE);
printf("Bitmap Offset: %d bytes\n", BITMAP_OFFSET);
printf("Bitmap Size: %d bytes\n", BITMAP_SIZE);
printf("Pixel Width: %d pixels\n", WIDTH);
printf("Pixel Height: %d pixels\n", HEIGHT);
printf("Bits per Pixel: 1 bit\n");
printf("Bytes per Scanline: %d bytes\n", BYTES_PER_LINE);
printf("Attribute Offset: %d bytes\n", ATTR_OFFSET);
printf("Attribute Size: %d bytes\n", FILE_SIZE - ATTR_OFFSET);
printf("Attribute Columns: %d\n", ATTR_COLS);
printf("Attribute Rows: %d\n", ATTR_ROWS);
printf("Palette Size: 8 base colors (15 total with bright)\n");
printf("Compression: None\n");
printf("Header Presence: None\n");
}
void write_scr(const char *output_path) {
FILE *fp = fopen(output_path, "wb");
if (!fp) {
perror("Write failed");
return;
}
// Blank data
unsigned char blank[FILE_SIZE];
memset(blank, 0, FILE_SIZE);
if (fwrite(blank, 1, FILE_SIZE, fp) != FILE_SIZE) {
perror("Write incomplete");
} else {
printf("Wrote valid .SCR to %s\n", output_path);
}
fclose(fp);
}
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: %s <scr-file>\n", argv[0]);
return 1;
}
if (!load_scr(argv[1])) {
return 1;
}
print_properties();
// Example write: write_scr("blank.scr");
free(data);
return 0;
}