Task 269: .GREXLI File Format
Task 269: .GREXLI File Format
.GREXLI File Format Specifications
After researching available sources, including Wikipedia's list of file extensions, the .GREXLI format appears to be an obscure, proprietary format described as an "uncompressed folder as a file" used by the WinGrex/GrexliLib software (likely an old or niche archiving tool). No official public specification document was found, and it seems to function as a simple binary container for multiple files without compression, similar to a basic TAR variant but in a custom binary header structure. Based on the description and common patterns for such formats, I've reverse-engineered a plausible specification by assuming a minimalistic binary layout that encodes a directory of files (headers + concatenated data). This allows for a file-system-like structure intrinsic to the format.
If this is incorrect, further proprietary docs from WinGrex would be needed, but none are publicly available.
1. List of All Properties Intrinsic to Its File System
The .GREXLI format treats the file as a self-contained file system with a flat directory (no subfolders). The intrinsic properties are the structural elements that define the archive's metadata and content:
- Magic Bytes: 6 bytes identifying the format ("GREXLI" in ASCII).
- Version: 1 byte (uint8, little-endian), indicating the format version (e.g., 0x01 for v1).
- Number of Files: 4 bytes (uint32, little-endian), the count of files in the archive (0 to 2^32-1).
- Per-File Properties (repeated for each file):
- File Name Length: 1 byte (uint8), length of the filename (0-255 bytes; 0 indicates end if num_files=0).
- Filename: Variable bytes (UTF-8 string, exactly the length specified, no null terminator).
- File Size: 8 bytes (uint64, little-endian), the size of the file data in bytes (0 for empty files).
- File Data: Variable bytes (raw binary content, exactly the size specified).
These properties enable reading the entire "file system" sequentially: headers provide metadata, followed by raw data blocks. No padding, encryption, or compression. Total file size = header (11 bytes) + (num_files * (1 + var_name + 8)) + sum(file sizes).
2. Two Direct Download Links for Files of Format .GREXLI
Extensive searches across web, GitHub, archives, and file-sharing sites yielded no public samples or direct downloads. The format appears proprietary/rare, with no known open repositories. For testing the code below, you can generate samples using the provided Python script (see part 4) or similar tools in other languages.
3. Ghost Blog Embedded HTML JavaScript for Drag-n-Drop Parsing
Embed this full HTML snippet into a Ghost blog post (via HTML card). It creates a drag-and-drop zone that reads a dropped .GREXLI file as binary, parses it per the spec, and dumps all properties to a <pre>
block on screen. Uses FileReader
and DataView
for binary parsing (browser-compatible).
4. Python Class for .GREXLI Handling
This class uses struct
for binary parsing/packing. read
opens a file, decodes, prints properties to console. write
creates a new .GREXLI from a dict of {filename: data_bytes}, prints properties, and saves to file.
import struct
import os
class GrexliArchive:
MAGIC = b'GREXLI'
VERSION = 1
@classmethod
def read(cls, filepath):
with open(filepath, 'rb') as f:
data = f.read()
return cls._parse(data)
@classmethod
def _parse(cls, data):
offset = 0
magic = data[offset:offset+6]
if magic != cls.MAGIC:
raise ValueError('Invalid magic bytes')
offset += 6
version = struct.unpack_from('<B', data, offset)[0]
offset += 1
if version != cls.VERSION:
raise ValueError(f'Unsupported version: {version}')
num_files = struct.unpack_from('<I', data, offset)[0]
offset += 4
properties = {'magic': magic.decode(), 'version': version, 'num_files': num_files, 'files': []}
for _ in range(num_files):
name_len = struct.unpack_from('<B', data, offset)[0]
offset += 1
filename = data[offset:offset+name_len].decode('utf-8')
offset += name_len
file_size = struct.unpack_from('<Q', data, offset)[0]
offset += 8
file_data = data[offset:offset+file_size]
offset += file_size
properties['files'].append({'filename': filename, 'file_size': file_size, 'data_preview': file_data[:20]}) # Preview for print
# Full data in real use: store file_data
cls._print_properties(properties)
return properties
@classmethod
def write(cls, filepath, files_dict):
# files_dict: {filename: bytes}
properties = {'magic': cls.MAGIC.decode(), 'version': cls.VERSION, 'num_files': len(files_dict), 'files': []}
packed = cls.MAGIC + struct.pack('<B', cls.VERSION) + struct.pack('<I', len(files_dict))
for filename, file_data in files_dict.items():
name_len = len(filename)
file_size = len(file_data)
properties['files'].append({'filename': filename, 'file_size': file_size})
packed += struct.pack('<B', name_len) + filename.encode('utf-8') + struct.pack('<Q', file_size) + file_data
with open(filepath, 'wb') as f:
f.write(packed)
cls._print_properties(properties)
print(f'Written to {filepath}')
@staticmethod
def _print_properties(props):
print('GREXLI Properties:')
print(f'Magic: {props["magic"]}')
print(f'Version: {props["version"]}')
print(f'Num Files: {props["num_files"]}')
for f in props['files']:
print(f' File: {f["filename"]} (size: {f["file_size"]} bytes)')
if 'data_preview' in f:
print(f' Preview: {f["data_preview"]}')
print('---')
# Example usage:
# props = GrexliArchive.read('example.grexli')
# GrexliArchive.write('output.grexli', {'test.txt': b'Hello World'})
5. Java Class for .GREXLI Handling
Uses DataInputStream
/DataOutputStream
for binary I/O. read
parses file, prints to console. write
builds from Map<String, byte[]>, prints, saves.
import java.io.*;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
public class GrexliArchive {
private static final String MAGIC = "GREXLI";
private static final byte VERSION = 1;
public static void read(String filepath) throws IOException {
try (DataInputStream dis = new DataInputStream(new FileInputStream(filepath))) {
byte[] magicBytes = new byte[6];
dis.readFully(magicBytes);
String magic = new String(magicBytes, StandardCharsets.US_ASCII);
if (!MAGIC.equals(magic)) {
throw new IOException("Invalid magic bytes");
}
byte version = dis.readByte();
if (version != VERSION) {
throw new IOException("Unsupported version: " + version);
}
int numFiles = dis.readInt(); // Little-endian assumed via DataInputStream (JVM default, but confirm)
System.out.println("GREXLI Properties:");
System.out.println("Magic: " + magic);
System.out.println("Version: " + version);
System.out.println("Num Files: " + numFiles);
for (int i = 0; i < numFiles; i++) {
int nameLen = dis.readUnsignedByte();
byte[] nameBytes = new byte[nameLen];
dis.readFully(nameBytes);
String filename = new String(nameBytes, StandardCharsets.UTF_8);
long fileSize = dis.readLong(); // uint64 as long
byte[] fileData = new byte[(int) fileSize];
dis.readFully(fileData);
System.out.println(" File: " + filename + " (size: " + fileSize + " bytes)");
System.out.println(" Preview: " + new String(fileData, 0, Math.min(20, fileData.length), StandardCharsets.UTF_8));
}
System.out.println("---");
}
}
public static void write(String filepath, Map<String, byte[]> files) throws IOException {
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(filepath))) {
dos.write(MAGIC.getBytes(StandardCharsets.US_ASCII));
dos.writeByte(VERSION);
dos.writeInt(files.size());
System.out.println("GREXLI Properties (writing):");
System.out.println("Magic: " + MAGIC);
System.out.println("Version: " + VERSION);
System.out.println("Num Files: " + files.size());
for (Map.Entry<String, byte[]> entry : files.entrySet()) {
String filename = entry.getKey();
byte[] fileData = entry.getValue();
byte[] nameBytes = filename.getBytes(StandardCharsets.UTF_8);
dos.writeByte(nameBytes.length);
dos.write(nameBytes);
dos.writeLong(fileData.length);
dos.write(fileData);
System.out.println(" File: " + filename + " (size: " + fileData.length + " bytes)");
}
System.out.println("---");
}
System.out.println("Written to " + filepath);
}
// Example: GrexliArchive.read("example.grexli");
// Map<String, byte[]> files = new HashMap<>(); files.put("test.txt", "Hello World".getBytes()); GrexliArchive.write("output.grexli", files);
}
6. JavaScript Class for .GREXLI Handling (Node.js)
Uses fs
and Buffer
for I/O. read
parses file, prints to console. write
builds from object {filename: Buffer}, prints, saves. Run with Node.js.
const fs = require('fs');
class GrexliArchive {
static MAGIC = Buffer.from('GREXLI');
static VERSION = 1;
static read(filepath) {
const data = fs.readFileSync(filepath);
let offset = 0;
const magic = data.slice(offset, offset + 6).toString();
offset += 6;
if (magic !== 'GREXLI') throw new Error('Invalid magic bytes');
const version = data.readUInt8(offset);
offset += 1;
if (version !== this.VERSION) throw new Error(`Unsupported version: ${version}`);
const numFiles = data.readUInt32LE(offset);
offset += 4;
const properties = { magic, version, numFiles, files: [] };
for (let i = 0; i < numFiles; i++) {
const nameLen = data.readUInt8(offset);
offset += 1;
const filename = data.slice(offset, offset + nameLen).toString('utf8');
offset += nameLen;
const fileSize = data.readBigUInt64LE(offset);
offset += 8;
const fileData = data.slice(offset, offset + fileSize);
offset += Number(fileSize);
properties.files.push({ filename, fileSize: Number(fileSize), dataPreview: fileData.slice(0, 20).toString('utf8') });
}
this.printProperties(properties);
return properties;
}
static write(filepath, filesObj) {
// filesObj: { filename: Buffer }
const numFiles = Object.keys(filesObj).length;
const properties = { magic: 'GREXLI', version: this.VERSION, numFiles, files: [] };
let packed = Buffer.concat([
this.MAGIC,
Buffer.from([this.VERSION]),
Buffer.alloc(4).writeUInt32LE(numFiles, 0)
]);
for (const [filename, fileData] of Object.entries(filesObj)) {
const nameBuf = Buffer.from(filename, 'utf8');
const fileSize = BigInt(fileData.length);
properties.files.push({ filename, fileSize: fileData.length });
const entryBuf = Buffer.concat([
Buffer.from([nameBuf.length]),
nameBuf,
Buffer.alloc(8).writeBigUInt64LE(fileSize, 0),
fileData
]);
packed = Buffer.concat([packed, entryBuf]);
}
fs.writeFileSync(filepath, packed);
this.printProperties(properties);
console.log(`Written to ${filepath}`);
}
static printProperties(props) {
console.log('GREXLI Properties:');
console.log(`Magic: ${props.magic}`);
console.log(`Version: ${props.version}`);
console.log(`Num Files: ${props.numFiles}`);
props.files.forEach(f => {
console.log(` File: ${f.filename} (size: ${f.fileSize} bytes)`);
console.log(` Preview: ${f.dataPreview}`);
});
console.log('---');
}
}
// Example:
// const props = GrexliArchive.read('example.grexli');
// const files = { 'test.txt': Buffer.from('Hello World') };
// GrexliArchive.write('output.grexli', files);
7. C Class (Struct) for .GREXLI Handling
Basic C implementation using fopen
/fread
/fwrite
. Compile with gcc -o grexli main.c
. read
parses file, prints to stdout. write
builds from array of structs, prints, saves. Assumes little-endian; add byte-swapping if needed.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define MAGIC "GREXLI"
#define VERSION 1
typedef struct {
char filename[256]; // Max 255 + null
uint64_t file_size;
uint8_t* data; // Dynamic alloc
} GrexliFile;
typedef struct {
char magic[7]; // Null-terminated
uint8_t version;
uint32_t num_files;
GrexliFile* files;
} GrexliArchive;
void print_properties(GrexliArchive* arch) {
printf("GREXLI Properties:\n");
printf("Magic: %s\n", arch->magic);
printf("Version: %u\n", arch->version);
printf("Num Files: %u\n", arch->num_files);
for (uint32_t i = 0; i < arch->num_files; i++) {
printf(" File: %s (size: %lu bytes)\n", arch->files[i].filename, arch->files[i].file_size);
if (arch->files[i].data) {
printf(" Preview: %.*s\n", (int)sizeof(arch->files[i].data), arch->files[i].data);
}
}
printf("---\n");
}
int read_grexli(const char* filepath, GrexliArchive* arch) {
FILE* fp = fopen(filepath, "rb");
if (!fp) return -1;
fread(arch->magic, 1, 6, fp);
arch->magic[6] = '\0';
if (strncmp(arch->magic, MAGIC, 6) != 0) {
fclose(fp);
return -1;
}
fread(&arch->version, 1, 1, fp);
if (arch->version != VERSION) {
fclose(fp);
return -1;
}
fread(&arch->num_files, 1, 4, fp); // Assume little-endian
arch->files = malloc(arch->num_files * sizeof(GrexliFile));
for (uint32_t i = 0; i < arch->num_files; i++) {
uint8_t name_len;
fread(&name_len, 1, 1, fp);
fread(arch->files[i].filename, 1, name_len, fp);
arch->files[i].filename[name_len] = '\0';
fread(&arch->files[i].file_size, 1, 8, fp);
arch->files[i].data = malloc(arch->files[i].file_size);
fread(arch->files[i].data, 1, arch->files[i].file_size, fp);
}
fclose(fp);
print_properties(arch);
return 0;
}
int write_grexli(const char* filepath, GrexliArchive* arch) {
FILE* fp = fopen(filepath, "wb");
if (!fp) return -1;
fwrite(MAGIC, 1, 6, fp);
fwrite(&VERSION, 1, 1, fp);
fwrite(&arch->num_files, 1, 4, fp); // Little-endian
print_properties(arch);
for (uint32_t i = 0; i < arch->num_files; i++) {
uint8_t name_len = strlen(arch->files[i].filename);
fwrite(&name_len, 1, 1, fp);
fwrite(arch->files[i].filename, 1, name_len, fp);
fwrite(&arch->files[i].file_size, 1, 8, fp);
if (arch->files[i].data) {
fwrite(arch->files[i].data, 1, arch->files[i].file_size, fp);
}
}
fclose(fp);
printf("Written to %s\n", filepath);
return 0;
}
void free_archive(GrexliArchive* arch) {
for (uint32_t i = 0; i < arch->num_files; i++) {
free(arch->files[i].data);
}
free(arch->files);
}
// Example usage in main:
// GrexliArchive arch = {0};
// read_grexli("example.grexli", &arch);
// free_archive(&arch);