Task 607: .RC File Format

Task 607: .RC File Format

File Format Specifications for the .RC File Format

The .RC file format is a text-based resource script used in Windows development to define application resources such as icons, menus, dialog boxes, and strings. It is processed by the Resource Compiler (rc.exe) to produce a binary .RES file that is linked into executables (.EXE or .DLL). The format supports C-style preprocessor directives (#include, #define, #if, etc.) and resource-definition statements. The syntax is case-insensitive for keywords, and files are typically encoded in ANSI or UTF-8/UTF-16, with CRLF line endings. The specifications are documented in Microsoft docs, including resource types, controls, and statements.

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

The .RC file is a plain text format, so it has no binary magic number or fixed structure. Intrinsic properties when stored in a file system include standard file metadata (e.g., on NTFS or FAT32 in Windows):

  • File extension: .rc
  • MIME type: text/plain or application/x-resource-script
  • Encoding: ANSI (default), UTF-8, or UTF-16 (with BOM if Unicode)
  • Line endings: CRLF (Windows standard)
  • File size: Variable (text-based)
  • Creation time
  • Last modification time
  • Last access time
  • File attributes (e.g., read-only, hidden, system)
  • Owner SID
  • Group SID
  • Permissions (ACLs in NTFS)
  • Inode/File ID (if applicable in the file system)

Resource-specific properties extracted from content (via parsing):

  • Resource types: ACCELERATORS, BITMAP, CURSOR, DIALOG, DIALOGEX, FONT, HTML, ICON, MENU, MENUEX, MESSAGETABLE, POPUP, PLUGPLAY, RCDATA, STRINGTABLE, TEXTINCLUDE, TYPELIB, User-Defined, VERSIONINFO, VXD
  • Control types (for dialogs/menus): AUTO3STATE, AUTOCHECKBOX, AUTORADIOBUTTON, CHECKBOX, COMBOBOX, CONTROL, CTEXT, DEFPUSHBUTTON, EDITTEXT, GROUPBOX, ICON, LISTBOX, LTEXT, PUSHBOX, PUSHBUTTON, RADIOBUTTON, RTEXT, SCROLLBAR, STATE3
  • Statement properties: CAPTION, CHARACTERISTICS, CLASS, EXSTYLE, FONT, LANGUAGE, MENU, MENUITEM, STYLE, VERSION
  1. Two direct download links for files of format .RC:
  1. Ghost blog embedded HTML JavaScript for drag and drop .RC file dump:
.RC File Properties Dumper
Drag and drop a .RC file here
  1. Python class for .RC file:
import re
import os

class RCFileHandler:
    def __init__(self, filename):
        self.filename = filename
        self.content = None
        self.properties = []
        self.read_and_decode()

    def read_and_decode(self):
        with open(self.filename, 'r', encoding='utf-8', errors='ignore') as f:
            self.content = f.read()
        self.properties = self.parse()

    def parse(self):
        resource_types = ['ACCELERATORS', 'BITMAP', 'CURSOR', 'DIALOG', 'DIALOGEX', 'FONT', 'HTML', 'ICON', 'MENU', 'MENUEX', 'MESSAGETABLE', 'POPUP', 'PLUGPLAY', 'RCDATA', 'STRINGTABLE', 'TEXTINCLUDE', 'TYPELIB', 'VERSIONINFO', 'VXD']
        lines = self.content.splitlines()
        properties = []
        in_block = False
        current_prop = None
        for line in lines:
            line = line.strip()
            if not line or line.startswith('//') or line.startswith('#'):
                continue
            words = re.split(r'\s+', line)
            if words[0].upper() in resource_types:
                current_prop = {'type': words[0].upper(), 'name': words[1] if len(words) > 1 else '', 'params': ' '.join(words[2:])}
                properties.append(current_prop)
                if 'BEGIN' in line.upper():
                    in_block = True
            elif in_block:
                if 'END' in line.upper():
                    in_block = False
                elif current_prop:
                    current_prop['params'] += f'\n{line}'
        return properties

    def print_properties(self):
        for prop in self.properties:
            print(f"Type: {prop['type']}, Name: {prop['name']}, Params: {prop['params']}")

    def write(self, new_filename):
        with open(new_filename, 'w', encoding='utf-8') as f:
            f.write(self.content)

# Example usage:
# handler = RCFileHandler('example.rc')
# handler.print_properties()
# handler.write('output.rc')
  1. Java class for .RC file:
import java.io.*;
import java.util.*;

public class RCFileHandler {
    private String filename;
    private String content;
    private List<Map<String, String>> properties;

    public RCFileHandler(String filename) {
        this.filename = filename;
        this.properties = new ArrayList<>();
        readAndDecode();
    }

    private void readAndDecode() {
        StringBuilder sb = new StringBuilder();
        try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line).append("\n");
            }
            content = sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        parse();
    }

    private void parse() {
        String[] resourceTypes = {"ACCELERATORS", "BITMAP", "CURSOR", "DIALOG", "DIALOGEX", "FONT", "HTML", "ICON", "MENU", "MENUEX", "MESSAGETABLE", "POPUP", "PLUGPLAY", "RCDATA", "STRINGTABLE", "TEXTINCLUDE", "TYPELIB", "VERSIONINFO", "VXD"};
        Set<String> typeSet = new HashSet<>(Arrays.asList(resourceTypes));
        String[] lines = content.split("\n");
        boolean inBlock = false;
        Map<String, String> currentProp = null;
        StringBuilder params = new StringBuilder();
        for (String line : lines) {
            line = line.trim();
            if (line.isEmpty() || line.startsWith("//") || line.startsWith("#")) continue;
            String[] words = line.split("\\s+");
            if (typeSet.contains(words[0].toUpperCase())) {
                if (currentProp != null) {
                    currentProp.put("params", params.toString().trim());
                    properties.add(currentProp);
                }
                currentProp = new HashMap<>();
                currentProp.put("type", words[0].toUpperCase());
                currentProp.put("name", words.length > 1 ? words[1] : "");
                params = new StringBuilder(String.join(" ", Arrays.copyOfRange(words, 2, words.length)));
                if (line.toUpperCase().contains("BEGIN")) inBlock = true;
            } else if (inBlock) {
                if (line.toUpperCase().contains("END")) {
                    inBlock = false;
                } else if (currentProp != null) {
                    params.append("\n").append(line);
                }
            }
        }
        if (currentProp != null) {
            currentProp.put("params", params.toString().trim());
            properties.add(currentProp);
        }
    }

    public void printProperties() {
        for (Map<String, String> prop : properties) {
            System.out.println("Type: " + prop.get("type") + ", Name: " + prop.get("name") + ", Params: " + prop.get("params"));
        }
    }

    public void write(String newFilename) {
        try (PrintWriter pw = new PrintWriter(new File(newFilename))) {
            pw.print(content);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    // Example usage:
    // public static void main(String[] args) {
    //     RCFileHandler handler = new RCFileHandler("example.rc");
    //     handler.printProperties();
    //     handler.write("output.rc");
    // }
}
  1. JavaScript class for .RC file:
class RCFileHandler {
    constructor(content) {
        this.content = content;
        this.properties = this.parse();
    }

    parse() {
        const resourceTypes = ['ACCELERATORS', 'BITMAP', 'CURSOR', 'DIALOG', 'DIALOGEX', 'FONT', 'HTML', 'ICON', 'MENU', 'MENUEX', 'MESSAGETABLE', 'POPUP', 'PLUGPLAY', 'RCDATA', 'STRINGTABLE', 'TEXTINCLUDE', 'TYPELIB', 'VERSIONINFO', 'VXD'];
        const lines = this.content.split('\n');
        const properties = [];
        let inBlock = false;
        let currentProp = null;
        let params = '';
        lines.forEach(line => {
            line = line.trim();
            if (!line || line.startsWith('//') || line.startsWith('#')) return;
            const words = line.split(/\s+/);
            if (resourceTypes.includes(words[0].toUpperCase())) {
                if (currentProp) {
                    currentProp.params = params.trim();
                    properties.push(currentProp);
                }
                currentProp = { type: words[0].toUpperCase(), name: words[1] || '', params: '' };
                params = words.slice(2).join(' ');
                if (line.toUpperCase().includes('BEGIN')) inBlock = true;
            } else if (inBlock) {
                if (line.toUpperCase().includes('END')) {
                    inBlock = false;
                } else if (currentProp) {
                    params += `\n${line}`;
                }
            }
        });
        if (currentProp) {
            currentProp.params = params.trim();
            properties.push(currentProp);
        }
        return properties;
    }

    printProperties() {
        this.properties.forEach(prop => {
            console.log(`Type: ${prop.type}, Name: ${prop.name}, Params: ${prop.params}`);
        });
    }

    write() {
        // For Node.js, use fs.writeFileSync('output.rc', this.content);
        // For browser, return content for download.
        return this.content;
    }
}

// Example usage (Node.js):
// const fs = require('fs');
// const content = fs.readFileSync('example.rc', 'utf8');
// const handler = new RCFileHandler(content);
// handler.printProperties();
// fs.writeFileSync('output.rc', handler.write());
  1. C class (using C++ for class support) for .RC file:
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <algorithm>
#include <string>

class RCFileHandler {
private:
    std::string filename;
    std::string content;
    std::vector<std::map<std::string, std::string>> properties;

    void readAndDecode() {
        std::ifstream file(filename);
        if (!file.is_open()) {
            std::cerr << "Error opening file" << std::endl;
            return;
        }
        std::stringstream ss;
        ss << file.rdbuf();
        content = ss.str();
        file.close();
        parse();
    }

    void parse() {
        std::vector<std::string> resourceTypes = {"ACCELERATORS", "BITMAP", "CURSOR", "DIALOG", "DIALOGEX", "FONT", "HTML", "ICON", "MENU", "MENUEX", "MESSAGETABLE", "POPUP", "PLUGPLAY", "RCDATA", "STRINGTABLE", "TEXTINCLUDE", "TYPELIB", "VERSIONINFO", "VXD"};
        std::stringstream ss(content);
        std::string line;
        bool inBlock = false;
        std::map<std::string, std::string> currentProp;
        std::string params;
        while (std::getline(ss, line)) {
            line.erase(0, line.find_first_not_of(" \t"));
            line.erase(line.find_last_not_of(" \t") + 1);
            if (line.empty() || line.rfind("//", 0) == 0 || line.rfind("#", 0) == 0) continue;
            std::stringstream wordStream(line);
            std::string firstWord;
            wordStream >> firstWord;
            std::transform(firstWord.begin(), firstWord.end(), firstWord.begin(), ::toupper);
            if (std::find(resourceTypes.begin(), resourceTypes.end(), firstWord) != resourceTypes.end()) {
                if (!currentProp.empty()) {
                    currentProp["params"] = params;
                    properties.push_back(currentProp);
                    currentProp.clear();
                    params.clear();
                }
                std::string name, temp;
                wordStream >> name;
                while (wordStream >> temp) params += temp + " ";
                currentProp["type"] = firstWord;
                currentProp["name"] = name;
                if (line.find("BEGIN") != std::string::npos || line.find("begin") != std::string::npos) inBlock = true;
            } else if (inBlock) {
                if (line.find("END") != std::string::npos || line.find("end") != std::string::npos) {
                    inBlock = false;
                } else {
                    params += line + "\n";
                }
            }
        }
        if (!currentProp.empty()) {
            currentProp["params"] = params;
            properties.push_back(currentProp);
        }
    }

public:
    RCFileHandler(const std::string& fn) : filename(fn) {
        readAndDecode();
    }

    void printProperties() {
        for (const auto& prop : properties) {
            std::cout << "Type: " << prop.at("type") << ", Name: " << prop.at("name") << ", Params: " << prop.at("params") << std::endl;
        }
    }

    void write(const std::string& newFilename) {
        std::ofstream outFile(newFilename);
        if (outFile.is_open()) {
            outFile << content;
            outFile.close();
        } else {
            std::cerr << "Error writing file" << std::endl;
        }
    }
};

// Example usage:
// int main() {
//     RCFileHandler handler("example.rc");
//     handler.printProperties();
//     handler.write("output.rc");
//     return 0;
// }