diff --git a/src/tools/rc/Doxyfile b/src/tools/rc/Doxyfile new file mode 100644 index 0000000000..6d8fd36ce3 --- /dev/null +++ b/src/tools/rc/Doxyfile @@ -0,0 +1,188 @@ +# Doxyfile 1.2.17 + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = "librdef API version 1" +PROJECT_NUMBER = +OUTPUT_DIRECTORY = +OUTPUT_LANGUAGE = English +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_CLASSES = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +INTERNAL_DOCS = NO +STRIP_CODE_COMMENTS = YES +CASE_SENSE_NAMES = YES +SHORT_NAMES = NO +HIDE_SCOPE_NAMES = NO +VERBATIM_HEADERS = YES +SHOW_INCLUDE_FILES = YES +JAVADOC_AUTOBRIEF = YES +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 4 +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +ALIASES = +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +SHOW_USED_FILES = YES +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = . +FILE_PATTERNS = rdef.h +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 1 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO +CGI_NAME = search.cgi +CGI_URL = +DOC_URL = +DOC_ABSPATH = +BIN_ABSPATH = /usr/local/bin/ +EXT_DOC_PATHS = diff --git a/src/tools/rc/Makefile b/src/tools/rc/Makefile new file mode 100644 index 0000000000..6bfe5d1806 --- /dev/null +++ b/src/tools/rc/Makefile @@ -0,0 +1,29 @@ + +# settings for debug builds: +#CXXFLAGS = -g -O0 -DDEBUG -Wall -Wno-multichar + +# settings for release builds: +CXXFLAGS = -O2 -Wall -Wno-multichar -Wno-sign-compare -Wno-unused + +all: rc librdef.so + +parser.cpp: parser.y + bison -d -o$@ $^ + +lexer.cpp: lexer.l parser.cpp + flex -o$@ lexer.l + +librdef.so: compile.cpp decompile.cpp lexer.cpp parser.cpp rdef.cpp + $(CXX) $(CXXFLAGS) -D_BUILDING_RDEF -lbe -lstdc++.r4 -o $@ $^ -nostart -Xlinker -soname=$@ + mimeset -f $@ + +rc: rc.cpp librdef.so + $(CXX) $(CXXFLAGS) -L./ -lrdef -o $@ rc.cpp + mimeset -f $@ + +clean: + rm -f *.o rc librdef.so lexer.cpp parser.cpp parser.cpp.h + +install: + cp rc ~/config/bin + cp librdef.so ~/config/lib diff --git a/src/tools/rc/compile.cpp b/src/tools/rc/compile.cpp new file mode 100644 index 0000000000..af608e2ca6 --- /dev/null +++ b/src/tools/rc/compile.cpp @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2003 Matthijs Hollemans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "rdef.h" +#include "compile.h" +#include "private.h" +#include "parser.cpp.h" + +char lexfile[B_PATH_NAME_LENGTH]; + +static BEntry entry; +static BFile file; +BResources rsrc; +const char* rsrc_file; + +static jmp_buf abort_jmp; // for aborting compilation + +// When it encounters an error, the parser immediately aborts (we don't try +// error recovery). But its parse stack may still contain malloc'ed objects. +// We keep track of these memory blocks in the mem_list, so we can properly +// free them in case of an error (which is the polite thing to do since we +// are a shared library). If the compilation was successful, then mem_list +// should be empty. In DEBUG mode we dump some statistics to verify that the +// parser properly frees up objects when it is done with them. + +#ifdef DEBUG +struct mem_t +{ + void* ptr; + char* file; + int32 line; +}; + +typedef std::list mem_list_t; +typedef mem_list_t::iterator mem_iter_t; + +static mem_list_t mem_list; +#else +static ptr_list_t mem_list; +#endif + +//------------------------------------------------------------------------------ + +void* alloc_mem(size_t size) +{ + void* ptr = malloc(size); // can be 0 + if (ptr == NULL) + { + abort_compile(B_NO_MEMORY, "out of memory"); + } + +#ifdef DEBUG + mem_t mem; + mem.ptr = ptr; + mem.file = strdup(lexfile); + mem.line = yylineno; + mem_list.push_front(mem); +#else + mem_list.push_front(ptr); +#endif + + return ptr; +} + +//------------------------------------------------------------------------------ + +void free_mem(void* ptr) +{ + if (ptr != NULL) + { +#ifdef DEBUG + for (mem_iter_t i = mem_list.begin(); i != mem_list.end(); ++i) + { + if (i->ptr == ptr) + { + free(i->ptr); + free(i->file); + mem_list.erase(i); + return; + } + } +#else + mem_list.remove(ptr); + free(ptr); +#endif + } +} + +//------------------------------------------------------------------------------ + +static void clean_up_mem() +{ +#ifdef DEBUG + printf("mem_list leaks %ld objects\n", mem_list.size()); + for (mem_iter_t i = mem_list.begin(); i != mem_list.end(); ) + { + printf("%p allocated at %s:%ld\n", i->ptr, i->file, i->line); + free(i->ptr); + free(i->file); + i = mem_list.erase(i); + } +#else + free_ptr_list(mem_list); +#endif +} + +//------------------------------------------------------------------------------ + +void abort_compile(status_t err, const char* format, ...) +{ + va_list ap; + + rdef_err = err; + rdef_err_line = yylineno; + strcpy(rdef_err_file, lexfile); + + va_start(ap, format); + vsprintf(rdef_err_msg, format, ap); + va_end(ap); + + abort_compile(); +} + +//------------------------------------------------------------------------------ + +void abort_compile() +{ + longjmp(abort_jmp, 1); +} + +//------------------------------------------------------------------------------ + +static void compile_file(char* file) +{ + strcpy(lexfile, file); + + yyin = fopen(lexfile, "r"); + if (yyin == NULL) + { + strcpy(rdef_err_file, lexfile); + rdef_err = RDEF_FILE_NOT_FOUND; + return; + } + + init_lexer(); + init_parser(); + + if (setjmp(abort_jmp) == 0) + { + yyparse(); + } + + // About error handling: If the bison-generated parser encounters + // a syntax error, it calls yyerror(), aborts parsing, and returns + // from yyparse(). For other kinds of errors (semantics, problem + // writing to BResources, etc), we bail out with a longjmp(). From + // then on, we can tell success or failure by looking at rdef_err. + + clean_up_lexer(); + clean_up_parser(); + clean_up_mem(); +} + +//------------------------------------------------------------------------------ + +static status_t open_output_file() +{ + status_t err = entry.SetTo(rsrc_file, true); + if (err == B_OK) + { + uint32 openMode = B_READ_WRITE | B_CREATE_FILE; + bool clobber = false; + + if (!(flags & RDEF_MERGE_RESOURCES)) + { + openMode |= B_ERASE_FILE; + clobber = true; + } + + err = file.SetTo(&entry, openMode); + if (err == B_OK) + { + err = rsrc.SetTo(&file, clobber); + } + } + + return err; +} + +//------------------------------------------------------------------------------ + +static void close_output_file() +{ + if ((rdef_err == B_OK) || (flags & RDEF_MERGE_RESOURCES)) + { + rsrc.Sync(); + } + else + { + entry.Remove(); // throw away output file + } + + file.Unset(); + entry.Unset(); +} + +//------------------------------------------------------------------------------ + +status_t rdef_compile(const char* output_file) +{ + clear_error(); + + if ((output_file == NULL) || (output_file[0] == '\0')) + { + rdef_err = B_BAD_VALUE; + return rdef_err; + } + + rsrc_file = output_file; + rdef_err = open_output_file(); + if (rdef_err != B_OK) + { + return rdef_err; + } + + for (ptr_iter_t i = input_files.begin(); + (i != input_files.end()) && (rdef_err == B_OK); ++i) + { + compile_file((char*) *i); + } + + close_output_file(); + return rdef_err; +} + +//------------------------------------------------------------------------------ diff --git a/src/tools/rc/compile.h b/src/tools/rc/compile.h new file mode 100644 index 0000000000..9ba0026dd4 --- /dev/null +++ b/src/tools/rc/compile.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2003 Matthijs Hollemans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef COMPILE_H +#define COMPILE_H + +#include +#include + +// Handle of file we're currently parsing. +extern FILE* yyin; + +// Name of the file we're currently parsing. +extern char lexfile[]; + +// The line we're currently parsing. +extern int yylineno; + +struct field_t; + +// Describes a data type. +struct type_t +{ + type_code code; // type code + char* name; // name of this type + int32 count; // how many fields + field_t* fields; // field definitions + int32 def_id; // default resource ID + char* def_name; // default resource name +}; + +// Used by the lexer and parser to pass around resource data. The rdef +// format allows string literals to contain embedded '\0' chars, so we +// can't use strlen() to find their length; instead we look at the size +// field for that (size includes the final '\0' too). +struct data_t +{ + type_t type; // data type + char* name; // name (only if this is a field) + size_t size; // byte size of data + void* ptr; // the actual data +}; + +// Describes a data field in a user-defined type. +struct field_t +{ + type_t type; // data type + char* name; // name of this field + data_t data; // default value +}; + +// Describes an array of data_t or field_t objects. +struct list_t +{ + int32 count; + void* items; // cast to data_t* or field_t* +}; + +// Used by the parser to pass around resource IDs. +struct id_t +{ + bool has_id; + bool has_name; + int32 id; + char* name; +}; + +// The output file we add resources to. +extern BResources rsrc; +extern const char* rsrc_file; + +int yylex(); +int yyparse(); + +void init_lexer(); +void clean_up_lexer(); + +void init_parser(); +void clean_up_parser(); + +void* alloc_mem(size_t size); +void free_mem(void* ptr); + +// Returns the data type with the specified name. +type_t get_type(char* name); + +void abort_compile(status_t err, const char* format, ...); +void abort_compile(); + +#endif // COMPILE_H diff --git a/src/tools/rc/decompile.cpp b/src/tools/rc/decompile.cpp new file mode 100644 index 0000000000..174c67adce --- /dev/null +++ b/src/tools/rc/decompile.cpp @@ -0,0 +1,947 @@ +/* + * Copyright (c) 2003 Matthijs Hollemans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "rdef.h" +#include "private.h" + +// What we add to the front of enum symbols. +#define PREFIX "R_" + +static const char* rdef_name; +static char header_name[B_PATH_NAME_LENGTH + 1]; + +static FILE* rdef_file; +static FILE* header_file; + +// Level of indentation (how many tabs). +static int32 tabs; + +static void examine_data(const char*, type_code, const void*, size_t); + +//------------------------------------------------------------------------------ + +static void indent() +{ + for (int32 t = 0; t < tabs; ++t) + { + fprintf(rdef_file, "\t"); + } +} + +//------------------------------------------------------------------------------ + +void open_brace() +{ +#ifdef PUSSY_CODER_STYLE + fprintf(rdef_file, " {\n"); +#else + fprintf(rdef_file, "\n"); + indent(); + fprintf(rdef_file, "{\n"); +#endif + + ++tabs; +} + +//------------------------------------------------------------------------------ + +void close_brace() +{ + --tabs; + + fprintf(rdef_file, "\n"); + indent(); + fprintf(rdef_file, "}"); +} + +//------------------------------------------------------------------------------ + +static bool make_code(uint32 value, char* code) +{ + code[0] = (value >> 24) & 0xFF; + if (isprint(code[0])) + { + code[1] = (value >> 16) & 0xFF; + if (isprint(code[1])) + { + code[2] = (value >> 8) & 0xFF; + if (isprint(code[2])) + { + code[3] = value & 0xFF; + if (isprint(code[3])) + { + code[4] = '\0'; + return true; + } + } + } + } + + return false; +} + +//------------------------------------------------------------------------------ + +static void write_code(uint32 value) +{ + char code[5]; + if (make_code(value, code)) + { + fprintf(rdef_file, "'%s'", code); + } + else + { + fprintf(rdef_file, "%lu", value); + } +} + +//------------------------------------------------------------------------------ + +static void write_field_name(const char* name) +{ + // We call this from the write_xxx() functions to properly align a + // field's type code (which goes to the left of the field name) and + // the field's data (to right of the name). If we are not currently + // writing a field (but the entire resource), name is NULL. + + if (name != NULL) + { + fprintf(rdef_file, "\"%s\" = ", name); + } +} + +//------------------------------------------------------------------------------ + +static bool is_ident(const char* name) +{ + if ((name[0] != '_') && !isalpha(name[0])) { return false; } + + for (size_t t = 1; t < strlen(name); ++t) + { + if ((name[t] != '_') && !isalnum(name[t])) { return false; } + } + + return true; +} + +//------------------------------------------------------------------------------ + +static bool has_prefix(const char* name) +{ + size_t name_len = strlen(name); + size_t prefix_len = strlen(PREFIX); + + if (name_len > prefix_len) + { + if (strncmp(name, PREFIX, prefix_len) == 0) + { + return true; + } + } + + return false; +} + +//------------------------------------------------------------------------------ + +static void write_rsrc(type_code type, int32 id, const char* name) +{ + if (name[0] == '\0') + { + fprintf(rdef_file, "resource(%ld) ", id); + } + else if ((flags & RDEF_AUTO_NAMES) && is_ident(name)) + { + char code[5]; + if (has_prefix(name)) + { + fprintf(rdef_file, "resource(%s) ", name); + fprintf(header_file, "\t%s = %ld,\n", name, id); + } + else if (make_code(type, code)) + { + fprintf(rdef_file, "resource(%s%s_%s) ", PREFIX, code, name); + fprintf(header_file, "\t%s%s_%s = %ld,\n", PREFIX, code, name, id); + } + else + { + fprintf(rdef_file, "resource(%s%ld_%s) ", PREFIX, type, name); + fprintf(header_file, "\t%s%ld_%s = %ld,\n", PREFIX, type, name, id); + } + } + else + { + fprintf(rdef_file, "resource(%ld, \"%s\") ", id, name); + } +} + +//------------------------------------------------------------------------------ + +static uint8* write_raw_line(uint8* ptr, uint8* end, size_t bytes_per_line) +{ + uint32 count = 0; + + fprintf(rdef_file, "$\""); + + while ((ptr < end) && (count < bytes_per_line)) + { + fprintf(rdef_file, "%02X", *ptr++); + ++count; + } + + fprintf(rdef_file, "\""); + + return ptr; +} + +//------------------------------------------------------------------------------ + +static void write_raw( + const char* name, type_code type, const void* data, size_t length, + size_t bytes_per_line = 32) +{ + uint8* ptr = (uint8*) data; + uint8* end = ptr + length; + + if (length > bytes_per_line) + { + if (type != B_RAW_TYPE) + { + fprintf(rdef_file, "#"); + write_code(type); + fprintf(rdef_file, " "); + } + + write_field_name(name); + fprintf(rdef_file, "array"); + + open_brace(); + + int32 item = 0; + while (ptr < end) + { + if (item++ > 0) { fprintf(rdef_file, "\n"); } + indent(); + ptr = write_raw_line(ptr, end, bytes_per_line); + } + + close_brace(); + } + else + { + if (type != B_RAW_TYPE) + { + fprintf(rdef_file, "#"); + write_code(type); + fprintf(rdef_file, " "); + } + + write_field_name(name); + write_raw_line(ptr, end, bytes_per_line); + } +} + +//------------------------------------------------------------------------------ + +static void write_bool(const char* name, const void* data, size_t length) +{ + if (length != sizeof(bool)) + { + write_raw(name, B_BOOL_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "%s", *((bool*) data) ? "true" : "false"); + } +} + +//------------------------------------------------------------------------------ + +static void write_int8(const char* name, const void* data, size_t length) +{ + if (length != sizeof(int8)) + { + write_raw(name, B_INT8_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "(int8)%d", *((int8*) data)); + } +} + +//------------------------------------------------------------------------------ + +static void write_int16(const char* name, const void* data, size_t length) +{ + if (length != sizeof(int16)) + { + write_raw(name, B_INT16_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "(int16)%d", *((int16*) data)); + } +} + +//------------------------------------------------------------------------------ + +static void write_int32(const char* name, const void* data, size_t length) +{ + if (length != sizeof(int32)) + { + write_raw(name, B_INT32_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "%ld", *((int32*) data)); + } +} + +//------------------------------------------------------------------------------ + +static void write_int64(const char* name, const void* data, size_t length) +{ + if (length != sizeof(int64)) + { + write_raw(name, B_INT64_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "(int64)%lld", *((int64*) data)); + } +} + +//------------------------------------------------------------------------------ + +static void write_uint8(const char* name, const void* data, size_t length) +{ + if (length != sizeof(uint8)) + { + write_raw(name, B_UINT8_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "(uint8)%u", *((uint8*) data)); + } +} + +//------------------------------------------------------------------------------ + +static void write_uint16(const char* name, const void* data, size_t length) +{ + if (length != sizeof(uint16)) + { + write_raw(name, B_UINT16_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "(uint16)%u", *((uint16*) data)); + } +} + +//------------------------------------------------------------------------------ + +static void write_uint32(const char* name, const void* data, size_t length) +{ + if (length != sizeof(uint32)) + { + write_raw(name, B_UINT32_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "(uint32)%lu", *((uint32*) data)); + } +} + +//------------------------------------------------------------------------------ + +static void write_uint64(const char* name, const void* data, size_t length) +{ + if (length != sizeof(uint64)) + { + write_raw(name, B_UINT64_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "(uint64)%llu", *((uint64*) data)); + } +} + +//------------------------------------------------------------------------------ + +static void write_float(const char* name, const void* data, size_t length) +{ + if (length != sizeof(float)) + { + write_raw(name, B_FLOAT_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "%#g", *((float*) data)); + } +} + +//------------------------------------------------------------------------------ + +static void write_double(const char* name, const void* data, size_t length) +{ + if (length != sizeof(double)) + { + write_raw(name, B_DOUBLE_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "(double)%#g", *((double*) data)); + } +} + +//------------------------------------------------------------------------------ + +static void write_size(const char* name, const void* data, size_t length) +{ + if (length != sizeof(size_t)) + { + write_raw(name, B_SIZE_T_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "(size_t)%lu", *((size_t*) data)); + } +} + +//------------------------------------------------------------------------------ + +static void write_ssize(const char* name, const void* data, size_t length) +{ + if (length != sizeof(ssize_t)) + { + write_raw(name, B_SSIZE_T_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "(ssize_t)%ld", *((ssize_t*) data)); + } +} + +//------------------------------------------------------------------------------ + +static void write_off(const char* name, const void* data, size_t length) +{ + if (length != sizeof(off_t)) + { + write_raw(name, B_OFF_T_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "(off_t)%lld", *((off_t*) data)); + } +} + +//------------------------------------------------------------------------------ + +static void write_time(const char* name, const void* data, size_t length) +{ + if (length != sizeof(time_t)) + { + write_raw(name, B_TIME_TYPE, data, length); + } + else + { + write_field_name(name); + fprintf(rdef_file, "(time_t)%ld", *((time_t*) data)); + } +} + +//------------------------------------------------------------------------------ + +static void write_point(const char* name, const void* data) +{ + ///TODO: using built-in type table + + write_field_name(name); + float* f = (float*) data; + fprintf(rdef_file, "point { %#g, %#g }", f[0], f[1]); +} + +//------------------------------------------------------------------------------ + +static void write_rect(const char* name, const void* data) +{ + ///TODO: using built-in type table + + write_field_name(name); + float* f = (float*) data; + fprintf(rdef_file, "rect { %#g, %#g, %#g, %#g }", f[0], f[1], f[2], f[3]); +} + +//------------------------------------------------------------------------------ + +static void write_rgb(const char* name, const void* data) +{ + ///TODO: using built-in type table + + write_field_name(name); + uint8* b = (uint8*) data; + + fprintf( + rdef_file, "rgb_color { 0x%02X, 0x%02X, 0x%02X, 0x%02X }", + b[0], b[1], b[2], b[3]); +} + +//------------------------------------------------------------------------------ + +static char* write_string_line(char* ptr, char* end, size_t chars_per_line) +{ + uint32 count = 0; + bool end_of_item = false; + + fprintf(rdef_file, "\""); + + while ((ptr < end) && (count < chars_per_line) && !end_of_item) + { + char c = *ptr++; + + switch (c) + { + case '\b': fprintf(rdef_file, "\\b"); count += 2; break; + case '\f': fprintf(rdef_file, "\\f"); count += 2; break; + case '\n': fprintf(rdef_file, "\\n"); count += 2; break; + case '\r': fprintf(rdef_file, "\\r"); count += 2; break; + case '\t': fprintf(rdef_file, "\\t"); count += 2; break; + case '\v': fprintf(rdef_file, "\\v"); count += 2; break; + case '\"': fprintf(rdef_file, "\\\""); count += 2; break; + case '\\': fprintf(rdef_file, "\\\\"); count += 2; break; + + case '\0': end_of_item = true; break; + + default: + { + if ((((uint8) c) < 128) && !isprint(c)) + { + fprintf(rdef_file, "\\0x%02X", (uint8) c); count += 5; + } + else + { + fprintf(rdef_file, "%c", c); ++count; + } + } + } + } + + fprintf(rdef_file, "\""); + + if (end_of_item && (ptr < end)) + { + fprintf(rdef_file, ","); + } + + return ptr; +} + +//------------------------------------------------------------------------------ + +static void write_string( + const char* name, type_code type, const void* data, size_t length) +{ + char* ptr = (char*) data; + char* end = ptr + length; + size_t chars_per_line = 64; + + // We write an "array" resource if the string has more than 64 + // characters. A string resource may also be comprised of multiple + // substrings, each terminated by a '\0' char. In that case, we + // must write an "array" resource as well. Sneaky as we are, we use + // strlen() to check for that, because it also looks for a '\0'. + + if ((length > chars_per_line) || (strlen(ptr) < length - 1)) + { + fprintf(rdef_file, "#"); + write_code(type); + fprintf(rdef_file, " array"); + + if (name != NULL) + { + fprintf(rdef_file, " "); + write_field_name(name); + fprintf(rdef_file, " array"); + } + + open_brace(); + + int32 item = 0; + while (ptr < end) + { + if (item++ > 0) { fprintf(rdef_file, "\n"); } + indent(); + ptr = write_string_line(ptr, end, chars_per_line); + } + + close_brace(); + } + else + { + if (type != B_STRING_TYPE) + { + fprintf(rdef_file, "#"); + write_code(type); + fprintf(rdef_file, " "); + } + + write_field_name(name); + write_string_line(ptr, end, chars_per_line); + } +} + +//------------------------------------------------------------------------------ + +static void write_fields(BMessage& msg) +{ + int32 t = 0; + int32 item = 0; + + char* name; + type_code type; + int32 count; + const void* data; + size_t length; + + open_brace(); + + while (msg.GetInfo(B_ANY_TYPE, t, &name, &type, &count) == B_OK) + { + for (int32 k = 0; k < count; ++k) + { + if (msg.FindData(name, type, k, &data, (ssize_t*) &length) == B_OK) + { + if (item++ > 0) { fprintf(rdef_file, ",\n"); } + indent(); + examine_data(name, type, data, length); + } + } + + ++t; + } + + close_brace(); +} + +//------------------------------------------------------------------------------ + +static void write_message(const char* name, BMessage& msg, type_code type) +{ + if (type != B_MESSAGE_TYPE) + { + fprintf(rdef_file, "#"); + write_code(type); + fprintf(rdef_file, " "); + } + + write_field_name(name); + + const char* class_; + if (msg.FindString("class", &class_) == B_OK) + { + fprintf(rdef_file, "archive"); + + const char* add_on; + if (msg.FindString("add_on", &add_on) == B_OK) + { + fprintf(rdef_file, "(\"%s\"", add_on); + if (msg.what != 0) + { + fprintf(rdef_file, ", "); + write_code(msg.what); + } + fprintf(rdef_file, ")"); + + msg.RemoveName("add_on"); + } + else if (msg.what != 0) + { + fprintf(rdef_file, "(, "); + write_code(msg.what); + fprintf(rdef_file, ")"); + } + + fprintf(rdef_file, " %s", class_); + msg.RemoveName("class"); + } + else if (msg.what == 0) + { + fprintf(rdef_file, "message"); + } + else + { + fprintf(rdef_file, "message("); + write_code(msg.what); + fprintf(rdef_file, ")"); + } + + if (msg.CountNames(B_ANY_TYPE) > 0) + { + write_fields(msg); + } +} + +//------------------------------------------------------------------------------ + +static bool is_string(const void* data, size_t length) +{ + // We consider the buffer a string if it contains only human readable + // characters. The buffer should also end with a '\0'. Although the + // compiler allows string literals to contain embedded '\0' chars as + // well, we don't allow them here (because they may cause false hits). + + if (length == 0) { return false; } + + char* ptr = (char*) data; + + for (size_t t = 0; t < length - 1; ++t) + { + if (!isprint(*ptr++)) + { + return false; + } + } + + return (*ptr == '\0'); +} + +//------------------------------------------------------------------------------ + +static void write_other( + const char* name, type_code type, const void* data, size_t length) +{ + BMessage msg; + if (msg.Unflatten((const char*) data) == B_OK) + { + write_message(name, msg, type); + } + else if (is_string(data, length)) + { + write_string(name, type, data, length); + } + else + { + write_raw(name, type, data, length); + } +} + +//------------------------------------------------------------------------------ + +static void examine_data( + const char* name, type_code type, const void* data, size_t length) +{ + switch (type) + { + case B_BOOL_TYPE: write_bool(name, data, length); break; + case B_INT8_TYPE: write_int8(name, data, length); break; + case B_INT16_TYPE: write_int16(name, data, length); break; + case B_INT32_TYPE: write_int32(name, data, length); break; + case B_INT64_TYPE: write_int64(name, data, length); break; + case B_UINT8_TYPE: write_uint8(name, data, length); break; + case B_UINT16_TYPE: write_uint16(name, data, length); break; + case B_UINT32_TYPE: write_uint32(name, data, length); break; + case B_UINT64_TYPE: write_uint64(name, data, length); break; + case B_FLOAT_TYPE: write_float(name, data, length); break; + case B_DOUBLE_TYPE: write_double(name, data, length); break; + case B_SIZE_T_TYPE: write_size(name, data, length); break; + case B_SSIZE_T_TYPE: write_ssize(name, data, length); break; + case B_OFF_T_TYPE: write_off(name, data, length); break; + case B_TIME_TYPE: write_time(name, data, length); break; + + case B_POINT_TYPE: write_point(name, data); break; + case B_RECT_TYPE: write_rect(name, data); break; + case B_RGB_COLOR_TYPE: write_rgb(name, data); break; + + case B_MIME_STRING_TYPE: write_string(name, type, data, length); break; + case B_STRING_TYPE: write_string(name, type, data, length); break; + + case B_POINTER_TYPE: write_raw(name, type, data, length); break; + case 'MICN': write_raw(name, type, data, length, 16); break; + case 'ICON': write_raw(name, type, data, length); break; + + default: write_other(name, type, data, length); break; + } +} + +//------------------------------------------------------------------------------ + +static void examine_file(char* file_name) +{ + BFile file(file_name, B_READ_ONLY); + if (file.InitCheck() != B_OK) + { + strcpy(rdef_err_file, file_name); + rdef_err = RDEF_FILE_NOT_FOUND; + return; + } + + BResources res; + if (res.SetTo(&file) != B_OK) + { + strcpy(rdef_err_file, file_name); + rdef_err = RDEF_NO_RESOURCES; + return; + } + + int32 t = 0; + type_code type; + int32 id; + const char* name; + size_t length; + const void* data; + + while (res.GetResourceInfo(t, &type, &id, &name, &length)) + { + tabs = 0; + + data = res.LoadResource(type, id, NULL); + if (data != NULL) + { + fprintf(rdef_file, "\n"); + write_rsrc(type, id, name); + examine_data(NULL, type, data, length); + fprintf(rdef_file, ";\n"); + } + + ++t; + } +} + +//------------------------------------------------------------------------------ + +static status_t open_output_files() +{ + rdef_file = fopen(rdef_name, "w"); + if (rdef_file == NULL) + { + strcpy(rdef_err_msg, strerror(errno)); + strcpy(rdef_err_file, rdef_name); + return RDEF_WRITE_ERR; + } + + if (flags & RDEF_AUTO_NAMES) + { + header_file = fopen(header_name, "w"); + if (header_file == NULL) + { + strcpy(rdef_err_msg, strerror(errno)); + strcpy(rdef_err_file, header_name); + fclose(rdef_file); + return RDEF_WRITE_ERR; + } + + fprintf(rdef_file, "\n#include \"%s\"\n", header_name); + +#ifdef PUSSY_CODER_STYLE + fprintf(header_file, "\nenum {\n"); +#else + fprintf(header_file, "\nenum\n{\n"); +#endif + } + + return B_OK; +} + +//------------------------------------------------------------------------------ + +static void close_output_files() +{ + if (flags & RDEF_AUTO_NAMES) + { + fprintf(header_file, "};\n"); + fclose(header_file); + + if (rdef_err != B_OK) + { + unlink(header_name); + } + } + + fclose(rdef_file); + + if (rdef_err != B_OK) + { + unlink(rdef_name); + } +} + +//------------------------------------------------------------------------------ + +status_t rdef_decompile(const char* output_file) +{ + clear_error(); + + if ((output_file == NULL) || (output_file[0] == '\0')) + { + rdef_err = B_BAD_VALUE; + return rdef_err; + } + + rdef_name = output_file; + if (flags & RDEF_AUTO_NAMES) + { + sprintf(header_name, "%s.h", output_file); + } + + rdef_err = open_output_files(); + if (rdef_err != B_OK) + { + return rdef_err; + } + + for (ptr_iter_t i = input_files.begin(); + (i != input_files.end()) && (rdef_err == B_OK); ++i) + { + examine_file((char*) *i); + } + + close_output_files(); + return rdef_err; +} + +//------------------------------------------------------------------------------ diff --git a/src/tools/rc/docs/beres-deres.html b/src/tools/rc/docs/beres-deres.html new file mode 100644 index 0000000000..9e6fb4c73c --- /dev/null +++ b/src/tools/rc/docs/beres-deres.html @@ -0,0 +1,103 @@ + + +Differences with beres/deres + + + +

Differences with beres/deres

+ +

Some of your old rdef files may fail to compile with rc. Besides different +command line arguments, rc also has slightly different syntax and semantics. +These changes were made to allow for the future expansion of the rdef grammar +and to make resource scripts easier to write and understand. The compiler +should be backwards compatible with the majority of old scripts, but in some +cases it was necessary to break stuff.

+ +

Differences with beres

+ +
    + +
  • We allow the \0 escape in string literals.
  • + +
  • Our handling of the minus sign is more restrictive; we don't treat it like +a unary minus operator, which means you may only put one minus in front of a +number to negate it (i.e. "- -10" is an error).
  • + +
  • beres allows #define statements, but apparently ignores them. The compiler +reports no error, but any symbols you define are not recognized later on. We +don't allow #defines (yet).
  • + +
  • beres allows you to put enum symbols in resource data, and replaces the +symbol with its value. For some reason, it lets you assign any kind of data to +enum symbols, such as strings and floats. Since that is not valid C/C++ syntax, +librdef does not allow this. As a result, it does not make much sense for us +to support enum symbols in resource data.
  • + +
  • We only allow a few type casts, but enough to be backwards compatible. +beres allows you to cast from bool to all other types, between strings and +numeric types, etc. The usefulness of this is limited, so to keep things simple +librdef doesn't allow that.
  • + +
  • There is no need to put parentheses around a type code, even for simple +data; beres sometimes requires the parens and sometimes it doesn't. With rc, +they are always optional (preferred notation is without the parens).
  • + +
  • beres allows multiple type codes and/or type casts per resource +statement/data field, but we allow only one of each. Because we don't associate +type codes with data (but only with the resource itself or with individual +message fields), we don't allow the shortcut notation that lets you put a type +code and cast inside one set of parentheses.
  • + +
  • We do not allow arrays to have named fields, nor do we allow messages (and +archives) to have unnamed fields. beres apparently treats all compound data the +same, but we don't, because not all compound data is the same.
  • + +
  • When specifying array data, the "array" keyword is required. beres allows +you to omit the array keyword and just put the data between braces, but that +conflicts with our handling of user-defined types.
  • + +
  • Field names in message resources may only be string literals. beres allows +identifiers but it converts them to string literals anyway. Just like normal +data, you may cast the field's data to a different type, but not to a different +type code. Specifying a non-standard type code is fine, but it goes in front of +the type name. Just like beres, rc considers "array", "message", and "archive" +to be valid data type names, but "message" and "archive" cannot be followed by +a "what" code (beres ignores that anyway). When you cast an archive to a +message, we don't strip the "class" field (in fact, we don't perform any +conversion).
  • + +
  • We allow users to define their own types. The built-in types from beres +(point, rect, and rgb_color) are re-implemented as such user-defined types. The +syntax for these three built-ins should still be backwards compatible with old +scripts.
  • + +
  • beres automatically adds names for resources that are specified as +"resource(R_Symbol)", unless the "explicit names only" option is set. We do +that the other way around: we don't automatically add names unless the user +sets the "auto names" option.
  • + +
+ +

Differences with deres:

+ +
    + +
  • We do not write the "generated at" time into the files.
  • + +
  • We put opening braces on the next line (heh heh).
  • + +
  • Type codes that cannot be represented as #'xxxx' are written as #num where +num is a decimal number, not hexadecimal.
  • + +
  • Floats and doubles have a bunch of extra zeroes.
  • + +
  • Hex numbers are always uppercase.
  • + +
  • We may render string literals somewhat differently.
  • + +
  • We only write a header file if the "auto names" option is set.
  • + +
+ + + diff --git a/src/tools/rc/docs/grammar.html b/src/tools/rc/docs/grammar.html new file mode 100644 index 0000000000..d66000bce6 --- /dev/null +++ b/src/tools/rc/docs/grammar.html @@ -0,0 +1,437 @@ + + +rdef grammar + + + + +

rdef grammar

+ +

This is the (somewhat boring) specification of the rdef file format as it is +understood by librdef. It also describes to a certain extent how the compiler +works. You don't need to read this unless you want to hack librdef. Knowledge +of compiler theory and lex/yacc is assumed.

+ +

The lexer

+ +

Like any compiler, librdef contains a lexer (aka scanner) and a parser. The +lexer reads the input file and chops it up into tokens. The lexer ignores +single-line // comments and /* ... */ multi-line comments. It also ignores +whitespace and newlines.

+ +

The lexer recognizes the following tokens:

+ +
+ + + + + + + + + + + + + + + + +
BOOLtrue or false
INTEGERYou can specify integers as decimal numbers, +hexadecimal numbers (starting with 0x or 0X, alpha digits are case +insensitive), octal numbers (starting with a leading 0), binary numbers +(starting with 0b or 0B), or as a four character code ('CCCC'). Valid range is +64 bits. At this point, numbers are always unsigned. The minus sign is treated +as a separate token, and is dealt with by the parser.
FLOATA floating point literal. Must contain a decimal point, +may contain a signed exponent. Stored internally as a double.
STRINGUTF-8 compatible string literal, enclosed by double +quotes. Can contain escape sequences (\b \f \n \r \t \v \" \\ \0), octal +escapes (\000) and hex escapes (\0x00 or \x00). May not span more than one +line, although you are allowed to specify multiple string literals in a row and +the lexer will automatically concatenate them. There is no maximum length.
RAWHexadecimal representation of raw data, enclosed by double +quotes, and prefixed by a dollar sign: $"12FFAB". Each byte is represented by +two hex characters, so there must be an even number of characters between the +quotes. The alpha digits are not case sensitive. Like STRING, a RAW token may +not span more than one line, but multiple consecutive RAW tokens are +automatically concatenated. No maximum length.
IDENTC/C++ identifier. First character is alphabetic or +underscore. Other characters are alphanumeric or underscore.
TYPECODEA hash sign followed by a 32-bit unsigned decimal +number, hex number, or four character code. Examples: #200, #0x00C8, +#'MIMS'
+
+ +

The following are treated as keywords and special symbols:

+ +
enum resource array message archive type { } ( ) , ; = - +
+ +

The lexer also deals with #include statements, which look like: #include +"filename"\n. When you #include a file, the lexer expects it to contain valid +rdef syntax. So even though the include file is probably a C/C++ header, it +should not contain anything but the enum statement and/or comments. The lexer +only looks for include files in the include search paths that you have +specified, so if you want it to look in the current working directory you have +to explicitly specify that. You may nest #includes.

+ +

A note about UTF-8. Since the double quote (hex 0x22) is never part of the +second or third byte of a UTF-8 character, the lexer can safely deal with UTF-8 +characters inside string literals. That is also the reason that the decompiler +does not escape characters that are not human-readable (except the ones in the +7-bit ASCII range), because they could be part of a UTF-8 encoding. The current +version of librdef does not handle L"..." (wide char) strings, but nobody uses +them anyway.

+ +

The parser

+ +

The parser takes the tokens from the lexer and matches them against the +rules of the grammar. What follows is the grammar in a simplified variation of +BNF, so the actual bison source file may look a little different. Legend:

+ +
+ + + + + + + + +
[ a ]match a 0 or 1 times
{ b }match b 0 or more times
c | dmatch either c or d
( e f )group e and f together
lowercasenonterminal
UPPERtoken from the lexer
'c'token from the lexer
+
+ +

The rdef grammar consists of the following rules:

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
script{enum | typedef | resource}
enum ENUM '{' [symboldef {',' symboldef} [',']] '}' +';'
symboldefIDENT ['=' integer]
typedefTYPE [id] [TYPECODE] IDENT '{' fielddef {',' fieldef} +'}' ';'
fielddefdatatype IDENT ['=' data]
resourceRESOURCE [id] [typecode] data ';'
id'(' [(integer | IDENT) [',' STRING] | STRING] ')'
typecode['('] TYPECODE [')']
data[typecast] (BOOL | integer | float | STRING | RAW | array +| message | archive | type)
typecast['(' datatype ')']
datatypeARRAY | MESSAGE | ARCHIVE IDENT | IDENT
integer['-'] INTEGER
float['-'] FLOAT
arrayARRAY ['{' [data {',' data}] '}']
messageMESSAGE ['(' integer ')'] ['{' [msgfield {',' +msgfield}] '}']
msgfield[TYPECODE] [datatype] STRING '=' data
archiveARCHIVE [archiveid] IDENT '{' msgfield {',' msgfield} +'}'
archiveid'(' [STRING] [',' integer] ')'
typeIDENT [data | '{' [typefield {',' typefield}] +'}']
typefield[IDENT '='] data
+
+ +

Semantics

+ +

Resource names

+ +

There are several different ways to specify the ID and name of a new +resource:

+ +
+ + + + + + + + + + + + + + + + +
resourceThe resource is assigned the default name +and ID of its data type.
resource()The resource is assigned the default name and ID of +its data type.
resource(1)The resource is assigned the numeric ID 1, and the +default name of its data type.
resource("xxx")The resource is assigned the name "xxx" and the +default ID of its data type.
resource(1, "xxx")The resource is assigned the numeric ID 1, +and the name "xxx".
resource(sss)The resource is assigned the numeric ID that +corresponds with the symbol sss, which should have been defined in an enum +earlier. If the "auto names" option is passed to the compiler, the resource is +also given the name "sss", otherwise the default name from its data type is +used.
resource(sss, "xxx")The resource is assigned the numeric ID +that corresponds with the symbol sss, and the name "xxx".
+
+ +

Data types and type casts

+ +

Resources (and message fields) have a type code and a data type. The data +type determines the format the data is stored in, while the type code tells the +user how to interpret the data. Typically, there is some kind of relation +between the two, otherwise the resource will be a little hard to read.

+ +

The following table lists the compiler's built-in data types. (Users can +also define their own types; this is described in a later section.)

+ +
+ +
+ + + + + + + + + + + + + +
boolB_BOOL_TYPE
int8B_INT8_TYPE
uint8B_UINT8_TYPE
int16B_INT16_TYPE
uint16B_UINT16_TYPE
int32B_INT32_TYPE
uint32B_UINT32_TYPE
int64B_INT64_TYPE
uint64B_UINT64_TYPE
size_tB_SIZE_T_TYPE
ssize_tB_SSIZE_T_TYPE
+ +
+ + + + + + + + + + + + +
off_tB_OFF_T_TYPE
time_tB_TIME_TYPE
floatB_FLOAT_TYPE
doubleB_DOUBLE_TYPE
stringB_STRING_TYPE
rawB_RAW_TYPE
arrayB_RAW_TYPE
bufferB_RAW_TYPE
messageB_MESSAGE_TYPE
archiveB_MESSAGE_TYPE
+ +
+
+ +

The type code has no effect on how the data is stored. For example, if you +do this: "resource(x) #'LONG' true", then the data will not automatically be +stored as a 32-bit number! If you don't specify an explicit type code, the +compiler uses the type of the data for that.

+ +

You can change the data type with a type cast. The following casts are +allowed:

+ +
+ + + + + + + + + + + + + + + + +
boolYou cannot cast bool data.
integerYou can cast to all numeric data types. Casts to +smaller datatypes will truncate the number. Casting negative numbers to +unsigned datatypes (and vice versa) will wrap them, i.e. (uint8) -1 becomes +255.
floating pointYou can only cast to float or double.
stringYou cannot cast string data.
raw, buffer, arrayYou can cast anything to raw, but not the +other way around.
message, archiveYou cannot cast message data.
typeYou cannot cast user-defined types.
+
+ +

In addition to the "simple" built-in data types, the compiler also natively +supports several data structures from the BeOS API (point, rect, rgb_color) and +a few convenience types (app_signature, app_flags, etc). These types all follow +the same rules as user-defined types.

+ +

Arrays

+ +

The following definitions are semantically +equivalent:

+ +
resource(x) $"AABB";
+resource(x) array { $"AA" $"BB" };
+resource(x) array { $"AA", $"BB" };
+ +

The comma is optional and simply concatenates the two literals. When you +decompile this code, it always looks like:

+ +
resource(x) $"AABB";
+ +

Strings behave differently. The following two definitions are equivalent, +and concatenate the two literals into one string:

+ +
resource(x) "AA" "BB";
+resource(x) #'CSTR' array { "AA" "BB" };
+ +

However, if you put a comma between the the strings, the compiler will still +glue them together but with a '\0' character in the middle. Now the resource +contains two strings: "AA" and "BB". You can also specify the '\0' +character yourself:

+ +
resource(x) "AA\0BB";
+resource(x) #'CSTR' array { "AA", "BB" };
+ +

The following is not proper grammar; use an array instead:

+ +
resource(x) "AA", "BB";
+resource(x) $"AA", $"BB";
+ +

Note that the data type of an array is always raw data, no matter how you +specify its contents. Because raw literals may be empty ($""), so may +arrays.

+ +

Messages and archives

+ +

A message resource is a flattened BMessage. By default it has the data type +B_MESSAGE_TYPE and corresponding type code #'MSGG'. If you don't specify a +"what" code for the message, it defaults to 0.

+ +

Message fields assume the type of their data, unless you specify a different +type in front of the field name. (Normal casting rules apply here.) You can +also give the field a different type code, which tells the BMessage how to +interpret the data, but not how it is stored in the message. This type code +also goes in front of the field name. You can give the same name to multiple +fields, provided that they all have the same type. (The data of these fields +does not have to be the same size.) A message may be empty; it is still a valid +BMessage, but it contains no fields.

+ +

An archive is also a flattened BMessage, but one that was made by +Archive()'ing a BArchivable class, such as BBitmap. The name of the archive, in +this case BBitmap, is automatically added to the message in a field called +"class". The "archive" keyword is optionally followed by a set of parentheses +that enclose a string and/or an integer. The int is the "what" code, the string +is stored in a field called "add_on" (used for dynamic loading of +BArchivables). Other than that, archives and messages are identical. The +compiler does not check whether the contents of the archive actually make +sense, so if you don't structure the data properly you may be unable to +unarchive the object later. Unlike a message, an archive may not be empty, +because that is pointless.

+ +

User-defined types

+ +

We allow users to define their own types. A "type" is just a fancy array, +because the data from the various fields is simply concatenated into one big +block of bytes. The difference is that user-defined types are much easier to +fill in.

+ +

A user-defined type has a symbolic name, a type code, and a number of data +fields. After all the fields have been concatenated, the type code is applied +to the whole block. So, the data type of this resource is always the same as +its type code (unlike arrays, which are always raw data). If no type code is +specified, it defaults to B_RAW_TYPE.

+ +

The data fields always have a default value. For simple fields this is +typically 0 (numeric types) or empty (string, raw, message). The default value +of a user-defined type as a whole is the combination of the default values of +its fields. Of course, the user can specify other defaults. (When a user +creates a new resource that uses such a type, he is basically overriding the +default values with his own.)

+ +

The user may fill in the data fields by name, by order, or using a +combination of both. Every time the compiler sees an unnamed data item, it +stuffs it into the next available field. Named data items are simply assigned +to the field with the same name, and may overwrite a value that was previously +put there "by order". Any fields that are not filled in keep their default +value. For example:

+ +
type vector { int32 x, int32 y, int32 z, int32 w = 4 };
+resource(1) vector { 1, 3, x = 2 };
+ +

Here, x is first set to 1, y is set to 3, x is now overwritten by the value +2, z is given the default value 0, and w defaults to 4.

+ +

Note: if a user-defined type contains string, raw, or message fields, the +size of the type depends on the data that the user puts into it, because these +fields have a variable size. In a future version we will add "constraints" that +let you force the size of a field to a fixed number of bytes. (And possibly +other constraints as well, such as a valid data range.)

+ +

A type definition may also contain a default resource ID and name. The +default ID of built-in types is usually 1 and the name is empty (NULL). For +example:

+ +
type(10, "MyName") mytype { int32 a };
+resource mytype 123;
+ +

The resource is now called "MyName" and has ID 10. Obviously you can only do +this once or you will receive a duplicate resource error. If this type is used +inside an array or other compound type, the default ID and resource name are +ignored. Note: this feature introduces a shift/reduce conflict in the +compiler:

+ +
resource (int8) 123;
+ +

This probably doesn't do what you expect. The compiler now considers the +"(int8)" to be the resource ID, not a typecast. If you did not declare "int8" +in an enum (probably not), this gives a compiler error. Not a big problem, +because it is unlikely that you will ever do this. Here is a workaround:

+ +
resource() (int8) 123;
+ +

Symbol table

+ +

The compiler uses two symbol tables: one for the enum symbols, and one with +the data type definitions. We need those tables to find the numeric ID/type +definition that corresponds with an identifier, and to make sure that there are +no duplicate or missing identifiers. These two symbol tables are independent, +so you may use the same identifier both as an enum symbol and a type name.

+ +

The compiler does not need to keep a symbol table for the resources. +Although the combination of a resource's ID and its type code must be unique, +we can use the BResources class to check for this when we add a resource. There +is no point in duplicating this functionality in the compiler. (However, if we +are merging the new resources into an existing resource file, we will simply +overwrite duplicates.)

+ +

Misc remarks

+ +
    + +
  • As the grammar shows, the last field in an enum statement may be +followed by a comma. This is valid C/C++ syntax, and since the enum will +typically end up in a header file, we support that feature as well. For +anything else between braces, the last item may not be followed by a +comma.

  • + +
  • The type code that follows the "resource" keyword may be enclosed by +parens for historical reasons. The preferred notation is without, just like in +front of field names (where no parens are allowed).

  • + +
  • Even though "ARCHIVE IDENT" is a valid data type, we simply ignore the +identifier for now. Maybe later we will support casting between different +archives or whatever. For now, casting to an archive is the same as casting to +a message, since an archive is really a message.

  • + +
+ + + diff --git a/src/tools/rc/docs/history.html b/src/tools/rc/docs/history.html new file mode 100644 index 0000000000..e5d41ff1ba --- /dev/null +++ b/src/tools/rc/docs/history.html @@ -0,0 +1,18 @@ + + +History + + + +

History

+ +

This is the change history of the rc resource compiler and librdef.

+ +

Version 1.0 (February 16, 2003):

+ +
    +
  • Initial release
  • +
+ + + diff --git a/src/tools/rc/docs/librdef.html b/src/tools/rc/docs/librdef.html new file mode 100644 index 0000000000..41e2843c93 --- /dev/null +++ b/src/tools/rc/docs/librdef.html @@ -0,0 +1,88 @@ + + +The librdef library + + + +

The librdef library

+ +

Of course, it would be cool if other applications (such as GUI resource +editors) could also import and export rdef files. That is why the bulk of rc's +functionality is implemented in a separate shared library, librdef.so.

+ +

Using the library in your own applications is very simple. Here are some +quick instructions to get you started:

+ +
    +
  1. #include "rdef.h" in your sources
  2. +
  3. link your app to librdef.so
  4. +
+ +

The API is rather bare-bones, but it gets the job done. The library uses +files to transfer data to and from your application. This may seem odd, but it +is actually a big advantage. After calling the API functions to compile an rdef +file, you can use the standard BResources class to read the resources from the +output file. Chances are high that your application already knows how to do +this.

+ +

To compile a resource file, the steps are typically this:

+ +
    + +
  1. Call rdef_add_include_dir() one or more times to add include +file search paths.
  2. + +
  3. Call rdef_add_input_file() one or more times to add the rdef +files that you want to compile.
  4. + +
  5. Call rdef_set_flags() to toggle compiler options.
  6. + +
  7. Call rdef_compile() with the name of the output file. This +performs the actual compilation.
  8. + +
  9. Call rdef_free_input_files() to clear the list of input files +that you added earlier.
  10. + +
  11. Call rdef_free_include_dirs() to clear the list of include +directories that you added earlier.
  12. + +
+ +

Decompiling is very similar, although include directories are not used +here:

+ +
    + +
  1. Call rdef_add_input_file() one or more times to add the +resource files that you want to decompile.
  2. + +
  3. Call rdef_set_flags() to toggle compiler options.
  4. + +
  5. Call rdef_decompile() with the name of the output file. The +name of the header file (if any) will be automatically constructed by appending +".h" to the output file name.
  6. + +
  7. Call rdef_free_input_files() to clear the list of input files +that you added earlier.
  8. + +
+ +

If one of these functions returns something other than B_OK, an error +occurred. You can look at the following variables to find out more about the +error, and construct meaningul error messages:

+ +
    +
  • rdef_err The error code that was returned.
  • +
  • rdef_err_line The line number where compilation failed.
  • +
  • rdef_err_file The file where the error occurred.
  • +
  • rdef_err_msg The error message from the compiler.
  • +
+ +

For more information about using librdef, see "rdef.h", which explains the +available functions and data structures in more depth. For a real-world +example, take a look at "rc.cpp", which contains the complete implementation of +the rc compiler. As you'll see, there really isn't much to it, because librdef +already does all the work.

+ + + diff --git a/src/tools/rc/docs/rc.html b/src/tools/rc/docs/rc.html new file mode 100644 index 0000000000..c64aebeb33 --- /dev/null +++ b/src/tools/rc/docs/rc.html @@ -0,0 +1,472 @@ + + +Resource Compiler + + + +

The rc resource compiler

+ +

Table of contents

+ + + + + + + +

Introduction

+ +

In the world of BeOS programming, a "resource" is data that is bundled with +your application. Typical examples are the application's icons and its +signature, but you can attach any data you want (bitmaps, text, cursors, etc). +You stuff this data into a .rsrc file that will be linked to your application +when it is compiled.

+ +

Because .rsrc files have a binary file format, you need to use special tools +to edit them, such as FileTypes, QuickRes, or Resourcer. Alternatively, you can +use a "resource compiler". This is a command line tool that takes a text-based +resource script and turns it into a .rsrc file.

+ +

With a resource compiler, you express your resources as ASCII text using a +special definition language, which makes the resource files much easier to edit +and maintain. You no longer need separate GUI tools to build your .rsrc files, +and you can even automate the whole process by calling the compiler from your +Makefile or Jamfile. Resource scripts will also make your life easier if you +use CVS, because CVS doesn't handle .rsrc files very well.

+ +

BeOS R5 comes with an (experimental) resource compiler called "beres", and a +corresponding decompiler called "deres". rc is an open source replacement (and +enhancement) of these tools. It is (mostly) backwards compatible, so you should +be able to compile your old .rdef files without any problems.

+ + + + + +

How to install

+ +
    +
  1. Copy rc into /boot/home/config/bin
  2. +
  3. Copy librdef.so into /boot/home/config/lib
  4. +
  5. Let's party!
  6. +
+ + + + + +

Writing resource scripts

+ +

Writing resource scripts is not difficult, although the syntax may take some +getting used to. A resource script is a plain text file with one or more +resource definition statements. In addition, it may contain C or C++ style +comments. By convention, resource script files have the extension ".rdef".

+ +

Here is an example of a simple resource script:

+ +
resource(1) true;      /* this is a comment */
+resource(2) 123;       // and so is this
+resource(3) 3.14;
+resource(4) "hello world";
+resource(5) $"0123456789ABCDEF";
+ +

When compiled, this script produces a resource file with five resources. The +above example also illustrates the types of data that resources are allowed to +have: boolean, integer, floating point, character string (UTF-8), and raw data +buffer (hexadecimal).

+ +

By default, integer data is stored as a 32-bit int, and floating point data +is stored as a 4-byte float. If you want to change the way the data is stored, +you have to cast it:

+ +
resource(6) (int8) 123;
+resource(7) (double) 3.14;
+ +

You can cast integer data to the following types: int8, uint8, int16, +uint16, int32, uint32, int64, uint64, ssize_t, size_t, off_t, time_t, float, +double, raw. You can cast floating point data to: float, double, raw. You are +not allowed to cast boolean, string, or raw data to other types.

+ +

In addition to casting, you can also change the resource's type code. This +does not change the way the data is stored, only what it is called. To change +the type code of a resource:

+ +
resource(8) #'dude' 123;
+ +

This changes the type of resource 8 to the four-character-code 'dude'. If +you did not change it, it would be 'LONG', which stands for 32-bit integer. By +changing the type code, you assign a new meaning to the resource. You can also +specify type codes as decimal or hexadecimal numbers:

+ +
resource(9) #200 123;
+resource(10) #0xC8 123;
+ +

For historical reasons, you may also enclose the type code in parens, but +that is not the preferred notation. Type casts and type codes can be +combined:

+ +
resource(11) #'dude' (int8) 123;
+resource(11) (#'dude') (int8) 123;
+ +

In the above examples, we have given the resources numeric IDs. The +combination of ID and type code must be unique in the resource file; you cannot +have two int32 resources with ID 1, for example. However, it is perfectly fine +(but not necessarily a good idea) to do the following, because the data types +are different:

+ +
resource(12) 123;
+resource(12) "w00t!";
+ +

For your own convenience, you can also name resources. Unlike the ID/type +code combination, names do not have to be unique.

+ +
resource(13, "Friday") "Bad Luck";
+ +

Since it is likely that you will be using these resources from a C/C++ +program, it may be convenient to refer to them by symbolic names instead of +hardcoded numeric ID's. The rdef format allows you to do this:

+ +
enum
+{
+    R_AppName = 1,
+    R_SomeOtherThing = 2
+};
+
+resource(R_AppName) "MyKillerApp";
+ +

The compiler will automatically substitute the symbol R_AppName with the +number 1. (You don't have to start these symbol names with the prefix R_, but +it is somewhat of a convention.)

+ +

Now how do you tell your C/C++ app about these symbolic names? You simply +put the enum into a header file that you include both from your application's +source code and your rdef file. The header file, which we'll call +"myresources.h", now looks like this:

+ +
enum
+{
+    R_AppName = 1,
+    R_SomeOtherThing = 2
+};
+ +

And the rdef file becomes this:

+ +
#include "myresources.h"
+
+resource(R_AppName) "MyKillerApp";
+ +

Don't let the .h suffix fool you: the header file is still considered to be +an rdef file, and must contain valid rdef syntax. If you add any other C/C++ +code, your resource script will fail to compile. Of course, you shouldn't add +any other rdef syntax to the header either (unless you want your C++ compiler +to start complaining). Besides comments, the only safe thing to put in that +header file is the enum statement, because both rdef and C/C++ understand +it.

+ +

Just like IDs, symbolic identifiers can be combined with a name:

+ +
resource(R_AppName, "AppName") "MyKillerApp";
+ +

If you don't specify a name, and invoke the compiler with the +--auto-names option, it automatically uses the symbolic ID for the +name as well. So the ID of the following resource is 1 (because that is what +R_AppName corresponds to) and its name becomes "R_AppName":

+ +
resource(R_AppName) "MyKillerApp";
+ + + + + +

Big fat resources

+ +

The resources we have made so far consisted of a single data item, but you +can also supply a collection of data values. The simplest of these compound +data structures is the array:

+ +
resource(20) array { 1234, 5678 };
+ +

An array is nothing more than a raw buffer. The above statement takes the +two 32-bit integers 1234 and 5678 and stuffs them into a new 64-bit buffer. You +can put any kind of data into an array, even other arrays:

+ +
resource(21) array
+{
+    "hello",
+    3.14,
+    true,
+    array { "a", "nested", "array" },
+    $"AABB"
+};
+ +

It is up to you to remember the structure of this array, because array +resources don't keep track of what kind of values you put into them and where +you put these values. For that, we have messages. A message resource is a +flattened BMessage:

+ +
resource(22) message('blah')
+{
+    "Name" = "Santa Claus",
+    "Number" = 3.14,
+    "Array" = array { "a", "nested", "array" },
+    "Other Msg" = message { "field" = "value" }
+};
+ +

A message has an optional "what" code, in this case 'blah', and one or more +fields. A field has a name (between double quotes), a value, and a data type. +By default, the field assumes the type of its data, but you can also specify an +explicit data type and type code in front of the field name:

+ +
resource(23) message('bla2')
+{
+    "integer1" = (int8) 123,             // use cast to change data type
+    int16 "integer2" = 12345,            // specify data type
+    #'dude' "buffer1" = $"aabbccdd",     // specify a new type code
+    #'dude' raw "buffer2" = $"aabbccdd"  // you can also combine them
+};
+ +

A special type of message is the "archive". The BeOS API allows you to take +a BArchivable class and flatten it into a BMessage. You can also add such +archives to your resource scripts:

+ +
resource(24) #'BBMP' archive BBitmap
+{
+    "_frame" = rect { 0.0, 0.0, 63.0, 31.0 },
+    "_cspace" = 8200,
+    "_bmflags" = 1,
+    "_rowbytes" = 256,
+    "_data" =  array
+    {
+        ... /* here goes the bitmap data */ ...
+    }
+};
+ +

So what's this "rect" thing in the "_frame" field? Besides arrays and +messages, the compiler also supports a number of other data structures from the +BeAPI:

+ +
+ + + + +
typecorresponds tofields
pointBPoint, B_POINT_TYPEfloat x, y
rectBRect, B_RECT_TYPEfloat left, top, right, bottom
rgb_colorrgb_color, B_RGB_COLOR_TYPEuint8 red, green, blue, alpha
+ +

To add a color resource to your script, you can do:

+ +
resource(25) rgb_color { 255, 128, 0, 0 };
+ +

Or you can use the field names, in which case the order of the fields does +not matter:

+ +
resource(26) rgb_color
+{
+    blue = 0, green = 128, alpha = 0, red = 255
+};
+ +

You can also make your own data structures, or as we refer to them, +"user-defined types". Suppose that your application wants to store its GUI +elements in the resources:

+ +
type #'menu' menu
+{
+    string name,
+    int32 count,  // how many items
+    array items   // the menu items
+};
+
+type #'item' menuitem
+{
+    string name,
+    message msg,
+    bool enabled = true  // default value is "true"
+};
+ +

A type has a name, an optional type code, and one or more fields. You are +advised not to pick a type code that already belongs to one of the built-in +types, to avoid any confusion. Each field has a data type, a name, and a +default value. If you don't specify a default, it is typically 0 or empty. To +create a new menu resource using the types from the above example, you might +do:

+ +
resource(27) menu
+{
+    name = "File",
+    count = 3,
+    items = array
+    {
+       menuitem { "New...",   message('fnew') },
+       menuitem { "Print...", message('fprt'), false },
+       menuitem { "Exit",     message('_QRQ') }
+    }
+};
+ +

Like an array, a type resource doesn't remember its internal structure. You +can regard types as fancy arrays that are easier to fill in, a template if you +will. User-defined types work under the same rules as the built-in types point, +rect, and rgb_color, so you can specify the fields in order or by their names. +If you don't specify a field, its default value will be used.

+ +

Types can also have a default resource ID and/or name. If you omit to give +the resource an ID or a name, it uses the defaults from its data type. For +example, this:

+ +
type myint { int32 i };
+resource(10, "MyName") myint { 123 };
+ +

Is equivalent to this:

+ +
type(10, "MyName") myint { int32 i };
+resource myint { 123 };
+ +

And to save you even more typing, simple types that have only one field can +also be specified as:

+ +
resource myint 123;
+ + + + + +

Application resources

+ +

All BeOS applications (except command line apps) have a basic set of +resources, such as a MIME signature, launch flags, icons, and a few others. +Adding these kinds of resources is easy, because rc also has a number of +built-in types for that:

+ +
+ + + + + + + + + + + + + + +
typecorresponds tofields
app_signaturethe app's MIME signaturestring +signature
app_flagsapplication launch flagsuint32 +flags
app_versionversion informationuint32 major, middle, +minor, variety, internal
string short_info, long_info
large_icon32x32 iconarray of 1024 bytes
mini_icon16x16 iconarray of 256 bytes
file_typessupported file typesmessage
+ + + +

Filling in these application resources is a little tricky with the current +version of rc. A future version will make this easier, when the user-defined +types mechanism becomes more powerful. For now, see the file +tests/builtin.rdef for more info.

+ + + + + +

Compiling

+ +

rc is a command line tool, which means you must run it from a Terminal +window. Typical usage example:

+ +
rc -o things.rsrc things.rdef
+ +

This tells rc that you wish to compile the script "things.rdef" to the +resource file "things.rsrc". The default name for the output file is +"out.rsrc", but you can change that with the -o or +--output switch, just like we did here.

+ +

You can specify multiple rdef files if you wish, and they will all be +compiled into one big resource file. If your rdef files #include files that are +not in the current working directory, you can add include paths with the +-I or --include option. For a complete list of +options, type rc --help.

+ +

If your project uses a Makefile, you can have rc automatically generate +the resource file for you:

+ +
things.rsrc: things.rdef
+	rc -o $@ $^
+ + + + + + + +

Decompiling

+ +

Of course you can write the resource scripts by hand, but if you already +have a .rsrc file you can tell rc to decompile it. This will produce a +ready-to-go rdef script, and save you some trouble. (Although in some cases +it may be necessary to edit the script a little to suit your needs.) Note that +rc isn't limited to just .rsrc files; you can decompile any file that has +resources, including applications.

+ +

For example, to decompile the file "things.rsrc" into "things.rdef", do:

+ +
rc --decompile -o things.rdef things.rsrc
+ +

The decompiler produces an rdef resource script with the name "out.rdef", +but you can change that name with the -o or --output +switches. If you specify the --auto-names option, rc will also +write a C/C++ header file. Any resources whose name is a valid C/C++ +identifier will be added to the header file. Now your program can access the +resource using this symbolic name.

+ +

Note: Even though rc can decompile multiple .rsrc files into one script, it +does not detect conflicts in resource names or IDs. In such cases, the +resulting .rdef and/or .h files may give errors when you try to compile +them.

+ + + + + +

Authors

+ +

The rc resource compiler and its companion library librdef were written by +Matthijs Hollemans for the +OpenBeOS project. Comments, bug reports, +suggestions, and patches are welcome!

+ + + + + +

License

+ +

Copyright (c) 2003 Matthijs Hollemans

+ +

Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions:

+ +

The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software.

+ +

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE.

+ + + diff --git a/src/tools/rc/docs/todo.html b/src/tools/rc/docs/todo.html new file mode 100644 index 0000000000..875c8bd7fd --- /dev/null +++ b/src/tools/rc/docs/todo.html @@ -0,0 +1,44 @@ + + +Planned features + + + +

Planned features

+ +
    + +
  • Simple integer expressions + - * / | & ~ ()

  • + +
  • Make user-defined types more powerful. Some ideas: bitmask fields (for +app_flags), choice fields (for app_version), specify boolean fields as "name" +(true) or "!name" (false), conditional constructs, fixed-size strings and +arrays.

  • + +
  • Importing files. Right now, you must specify the resource data in the +rdef script, which is messy for (large) binary files. The "import" feature lets +you copy the contents of such files (pictures, icons, etc.) straight into your +resources.

  • + +
  • Attributes, so you can also use rdef scripts to add attributes to your +files instead of (or in addition to) resources.

  • + +
  • In "auto names" mode, the decompiler currently does not use the enum +symbol table. So if two resources have the same name and that name is a valid +C/C++ identifier, the decompiler will add two conflicting symbols to the enum +statement. This can also happen when multiple input files have conflicting +resource IDs.

  • + +
  • Support for the built-in types point, rect, and rgb_color is hardcoded +into the decompiler. The other types, app_flags etc, are not supported at all. +It would be better to use the type symbol table for this as well. Then the +decompiler can also support user-defined types (although these type definitions +must be provided to the decompiler somehow.)

  • + +
  • Right now, archives are treated as messages. Maybe we should give them +their own type, B_ARCHIVED_OBJECT (type code 'ARCV').

  • + +
+ + + diff --git a/src/tools/rc/lexer.l b/src/tools/rc/lexer.l new file mode 100644 index 0000000000..c756265529 --- /dev/null +++ b/src/tools/rc/lexer.l @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2003 Matthijs Hollemans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +%{ +//------------------------------------------------------------------------------ + +#include +#include + +#include "rdef.h" +#include "compile.h" +#include "private.h" +#include "parser.cpp.h" + +#define LEXERROR(msg) abort_compile(RDEF_COMPILE_ERR, msg); + +// Initial size (and increment) of lexbuf. +#define LEX_BUF_SIZE (64*1024) + +// Temporary buffer that the lexer uses to parse string and raw literals. +// The buffer will grow if necessary, to accommodate large data blocks. +static uint8* lexbuf; + +static uint8* lexptr; // current write position in lexbuf +static size_t lexsize; // current size of the lex buffer +static size_t lexcnt; // how full lexbuf currently is + +static void resetbuf(); // resets lexptr and lexcnt +static void addbuf(uint8); // appends byte to lexbuf + +// When we encounter an #include directive, we push the current +// buffer, filename, and line number on the include stack, so we +// can resume lexing that file when we're done with the include. +struct include_t +{ + YY_BUFFER_STATE buffer; + char* filename; + int lineno; +}; + +static std::stack include_stack; + +static void open_include(); +static void close_include(); + +//------------------------------------------------------------------------------ +%} + +%option noyywrap +%option yylineno + +LETTER [a-zA-Z] +BIN [01] +OCT [0-7] +DEC [0-9] +HEX [0-9a-fA-F] +IDENT [a-zA-Z_][a-zA-Z0-9_]* +WSPACE [ \r\t\n\f] +EXP [eE][+-]?{DEC}+ + +%x COMMENT +%x STRDATA +%x RAWDATA +%x INCLUDE + +%% + +enum return ENUM; +resource return RESOURCE; +array return ARRAY; +message return MESSAGE; +archive return ARCHIVE; +type return TYPE; + +false yylval.b = false; return BOOL; +true yylval.b = true; return BOOL; + +0[xX]{HEX}{1,16} { yylval.i = strtoull(yytext + 2, NULL, 16); + return INTEGER; } +0{OCT}{1,24} { yylval.i = strtoull(yytext, NULL, 8); + return INTEGER; } +0[bB]{BIN}{1,64} { yylval.i = strtoull(yytext + 2, NULL, 2); + return INTEGER; } +{DEC}+ { yylval.i = strtoull(yytext, NULL, 10); + return INTEGER; } +'....' { yylval.i = (yytext[1] << 24) + | (yytext[2] << 16) + | (yytext[3] << 8) + | yytext[4]; + return INTEGER; } + +{DEC}+{EXP} yylval.f = strtod(yytext, NULL); return FLOAT; +{DEC}*\.{DEC}+{EXP}? yylval.f = strtod(yytext, NULL); return FLOAT; +{DEC}+\.{DEC}*{EXP}? yylval.f = strtod(yytext, NULL); return FLOAT; + +#{DEC}+ { yylval.t = strtoul(yytext + 1, NULL, 10); + return TYPECODE; } +#0[xX]{HEX}{1,8} { yylval.t = strtoul(yytext + 3, NULL, 16); + return TYPECODE; } +#'....' { yylval.t = (yytext[2] << 24) + | (yytext[3] << 16) + | (yytext[4] << 8) + | yytext[5]; + return TYPECODE; } + +{IDENT} { yylval.I = (char*) alloc_mem(yyleng + 1); + memcpy(yylval.I, yytext, yyleng + 1); + return IDENT; } + +\" BEGIN(STRDATA); resetbuf(); +\"{WSPACE}+\" /* concatenate two literals */ +\" { BEGIN(INITIAL); + addbuf('\0'); + yylval.d.type = get_type("string"); + yylval.d.size = lexcnt; + yylval.d.ptr = alloc_mem(lexcnt); + memcpy(yylval.d.ptr, lexbuf, lexcnt); + return STRING; } +\n LEXERROR("string not terminated") +\\{OCT}{3} addbuf(strtol(yytext + 1, NULL, 8)); +\\0[xX]{HEX}{2} addbuf(strtol(yytext + 3, NULL, 16)); +\\[xX]{HEX}{2} addbuf(strtol(yytext + 2, NULL, 16)); +\\b addbuf('\b'); +\\f addbuf('\f'); +\\n addbuf('\n'); +\\r addbuf('\r'); +\\t addbuf('\t'); +\\v addbuf('\v'); +\\0 addbuf('\0'); +\\. addbuf(yytext[1]); +. addbuf(yytext[0]); + +$\" BEGIN(RAWDATA); resetbuf(); +\"{WSPACE}+$\" /* concatenate two literals */ +\" { BEGIN(INITIAL); + yylval.d.type = get_type("raw"); + yylval.d.size = lexcnt; + yylval.d.ptr = alloc_mem(lexcnt); + memcpy(yylval.d.ptr, lexbuf, lexcnt); + return RAW; } +\n LEXERROR("raw data not terminated") +{HEX}{2} addbuf(strtol(yytext, NULL, 16)); +{HEX} LEXERROR("number of characters must be even") +. LEXERROR("invalid character in raw data") + +"/*" BEGIN(COMMENT); /* eat multi-line comment */ +[^*\n]* /* eat anything that is not a '*' */ +"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ +\n +"*"+"/" BEGIN(INITIAL); +<> LEXERROR("forgot to close /*..*/ comment") + +"//"[^\n]* /* eat single-line comment */ +{WSPACE}+ /* eat whitespace */ + +\#include[ \t]+\" BEGIN(INCLUDE); +[ \t]* /* eat the whitespace */ +[^ \t\n\"]+\" open_include(); +\n LEXERROR("error in include statement") +<> LEXERROR("error in include statement") +<> { if (include_stack.empty()) + yyterminate(); + else + close_include(); } + +. return yytext[0]; + +%% +//------------------------------------------------------------------------------ + +void resetbuf() +{ + lexptr = lexbuf; + lexcnt = 0; +} + +//------------------------------------------------------------------------------ + +void addbuf(uint8 b) +{ + if (lexcnt == lexsize) + { + lexsize += LEX_BUF_SIZE; + lexbuf = (uint8*) realloc(lexbuf, lexsize); + if (lexbuf == NULL) + { + abort_compile(B_NO_MEMORY, "out of memory"); + } + + lexptr = lexbuf + lexcnt; + } + + *lexptr++ = b; + ++lexcnt; +} + +//------------------------------------------------------------------------------ + +void open_include() +{ + yytext[yyleng - 1] = '\0'; // remove trailing " quote + + for (ptr_iter_t i = include_dirs.begin(); i != include_dirs.end(); ++i) + { + char tmpname[B_PATH_NAME_LENGTH]; + strcpy(tmpname, (char*) *i); + strcat(tmpname, yytext); + + yyin = fopen(tmpname, "r"); + if (yyin != NULL) + { + include_t incl; + incl.buffer = YY_CURRENT_BUFFER; + incl.lineno = yylineno; + incl.filename = strdup(lexfile); + include_stack.push(incl); + + strcpy(lexfile, tmpname); + yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); + yylineno = 1; + + BEGIN(INITIAL); + return; + } + } + + abort_compile(RDEF_COMPILE_ERR, "cannot open include %s", yytext); +} + +//------------------------------------------------------------------------------ + +void close_include() +{ + fclose(yyin); + yy_delete_buffer(YY_CURRENT_BUFFER); + + include_t incl = include_stack.top(); + include_stack.pop(); + + yy_switch_to_buffer(incl.buffer); + yylineno = incl.lineno; + strcpy(lexfile, incl.filename); + + free(incl.filename); +} + +//------------------------------------------------------------------------------ + +void init_lexer() +{ + lexsize = LEX_BUF_SIZE; + lexbuf = (uint8*) malloc(lexsize); + if (lexbuf == NULL) + { + abort_compile(B_NO_MEMORY, "out of memory"); + } + + yyrestart(yyin); // necessary for multiple input files +} + +//------------------------------------------------------------------------------ + +void clean_up_lexer() +{ + while (!include_stack.empty()) + { + close_include(); + } + + fclose(yyin); + yy_delete_buffer(YY_CURRENT_BUFFER); + + free(lexbuf); +} + +//------------------------------------------------------------------------------ diff --git a/src/tools/rc/parser.y b/src/tools/rc/parser.y new file mode 100644 index 0000000000..9995aa315a --- /dev/null +++ b/src/tools/rc/parser.y @@ -0,0 +1,1483 @@ +/* + * Copyright (c) 2003 Matthijs Hollemans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +%{ +//------------------------------------------------------------------------------ + +#include + +#include +#include +#include +#include + +#include "rdef.h" +#include "compile.h" +#include "private.h" + +#define YYERROR_VERBOSE + +static void yyerror(char*); + +struct ident_compare_t // allows the maps to compare identifier names +{ + bool operator () (char* s1, char* s2) const + { + return strcmp(s1, s2) < 0; + } +}; + +typedef std::map sym_tab_t; +typedef sym_tab_t::iterator sym_iter_t; + +static sym_tab_t symbol_table; // symbol table for enums +static int32 enum_cnt; // counter for enum symbols without id + +static void add_symbol(char*, int32); +static int32 get_symbol(char*); + +typedef std::map type_tab_t; +typedef type_tab_t::iterator type_iter_t; + +static type_tab_t type_table; // symbol table for data types + +static void add_user_type(id_t, type_code, char*, list_t); + +static data_t make_data(size_t, type_t); +static data_t make_bool(bool); +static data_t make_int(uint64); +static data_t make_float(double); + +static BMessage* make_msg(list_t); +static data_t flatten_msg(BMessage*); + +static data_t make_default(type_t); +static data_t make_type(char*, list_t); + +static list_t make_field_list(field_t); +static list_t concat_field_list(list_t, field_t); +static list_t make_data_list(data_t); +static list_t concat_data_list(list_t, data_t); +static data_t concat_data(data_t, data_t); + +static data_t cast(type_t, data_t); + +static void add_resource(id_t, type_code, data_t); + +//------------------------------------------------------------------------------ +%} + +%expect 1 + +%union +{ + bool b; + uint64 i; + double f; + char* I; + type_code t; + id_t id; + data_t d; + list_t l; + field_t F; + type_t T; +} + +%token ENUM RESOURCE ARCHIVE ARRAY MESSAGE TYPE + +%token BOOL +%token INTEGER +%token FLOAT +%token STRING RAW +%token IDENT +%token TYPECODE + +%type integer +%type float +%type id +%type data array arrayfields message msgfield archive type typefield +%type msgfields typefields typedeffields +%type typedeffield +%type datatype typecast + +%% + +script + : /* empty */ + | script enum + | script typedef + | script resource + ; + +enum + : enumstart '{' '}' ';' + | enumstart '{' symbols '}' ';' + | enumstart '{' symbols ',' '}' ';' + ; + +enumstart + : ENUM { enum_cnt = 0; } + ; + +symbols + : symbols ',' symboldef + | symboldef + ; + +symboldef + : IDENT + { + add_symbol($1, enum_cnt); + ++enum_cnt; + } + | IDENT '=' integer + { + int32 id = (int32) $3; + add_symbol($1, id); + enum_cnt = id + 1; + } + ; + +typedef + : TYPE id TYPECODE IDENT '{' typedeffields '}' ';' + { + add_user_type($2, $3, $4, $6); + } + | TYPE id IDENT '{' typedeffields '}' ';' + { + add_user_type($2, B_RAW_TYPE, $3, $5); + } + ; + +typedeffields + : typedeffields ',' typedeffield { $$ = concat_field_list($1, $3); } + | typedeffield { $$ = make_field_list($1); } + ; + +typedeffield + : datatype IDENT + { + $$.type = $1; + $$.name = $2; + $$.data = make_default($1); + } + | datatype IDENT '=' data + { + $$.type = $1; + $$.name = $2; + $$.data = cast($1, $4); + } + ; + +resource + : RESOURCE id data ';' + { + add_resource($2, $3.type.code, $3); + } + | RESOURCE id TYPECODE data ';' + { + add_resource($2, $3, $4); + } + | RESOURCE id '(' TYPECODE ')' data ';' + { + add_resource($2, $4, $6); + } + ; + +id + : /* empty (causes shift/reduce conflict with typecast) */ + { + $$.has_id = false; $$.has_name = false; $$.name = NULL; + } + | '(' ')' + { + $$.has_id = false; $$.has_name = false; $$.name = NULL; + } + | '(' integer ')' + { + $$.has_id = true; $$.id = (int32) $2; + $$.has_name = false; $$.name = NULL; + } + | '(' integer ',' STRING ')' + { + $$.has_id = true; $$.id = (int32) $2; + $$.has_name = true; $$.name = (char*) $4.ptr; + } + | '(' IDENT ')' + { + $$.has_id = true; $$.id = get_symbol($2); + + if (flags & RDEF_AUTO_NAMES) + { + $$.has_name = true; $$.name = $2; + } + else + { + $$.has_name = false; $$.name = NULL; + free_mem($2); + } + } + | '(' IDENT ',' STRING ')' + { + $$.has_id = true; $$.id = get_symbol($2); + $$.has_name = true; $$.name = (char*) $4.ptr; + free_mem($2); + } + | '(' STRING ')' + { + $$.has_id = false; + $$.has_name = true; $$.name = (char*) $2.ptr; + } + ; + +array + : ARRAY '{' arrayfields '}' { $$ = $3; } + | ARRAY '{' '}' { $$ = make_data(0, get_type("raw")); } + | ARRAY { $$ = make_data(0, get_type("raw")); } + ; + +arrayfields + : arrayfields ',' data { $$ = concat_data($1, $3); } + | data { $$ = $1; $$.type = get_type("raw"); } + ; + +message + : MESSAGE '(' integer ')' '{' msgfields '}' + { + BMessage* msg = make_msg($6); + msg->what = (int32) $3; + $$ = flatten_msg(msg); + } + | MESSAGE '(' integer ')' '{' '}' + { + BMessage* msg = new BMessage; + msg->what = (int32) $3; + $$ = flatten_msg(msg); + } + | MESSAGE '(' integer ')' + { + BMessage* msg = new BMessage; + msg->what = (int32) $3; + $$ = flatten_msg(msg); + } + | MESSAGE '{' msgfields '}' { $$ = flatten_msg(make_msg($3)); } + | MESSAGE '{' '}' { $$ = flatten_msg(new BMessage); } + | MESSAGE { $$ = flatten_msg(new BMessage); } + ; + +msgfields + : msgfields ',' msgfield { $$ = concat_data_list($1, $3); } + | msgfield { $$ = make_data_list($1); } + ; + +msgfield + : STRING '=' data + { + $$ = $3; + $$.name = (char*) $1.ptr; + } + | datatype STRING '=' data + { + $$ = cast($1, $4); + $$.name = (char*) $2.ptr; + } + | TYPECODE STRING '=' data + { + $$ = $4; + $$.type.code = $1; + $$.name = (char*) $2.ptr; + } + | TYPECODE datatype STRING '=' data + { + $$ = cast($2, $5); + $$.type.code = $1; + $$.name = (char*) $3.ptr; + } + ; + +archive + : ARCHIVE IDENT '{' msgfields '}' + { + BMessage* msg = make_msg($4); + msg->AddString("class", $2); + free_mem($2); + $$ = flatten_msg(msg); + } + | ARCHIVE '(' STRING ')' IDENT '{' msgfields '}' + { + BMessage* msg = make_msg($7); + msg->AddString("class", $5); + msg->AddString("add_on", (char*) $3.ptr); + free_mem($5); + free_mem($3.ptr); + $$ = flatten_msg(msg); + } + | ARCHIVE '(' ',' integer ')' IDENT '{' msgfields '}' + { + BMessage* msg = make_msg($8); + msg->what = (int32) $4; + msg->AddString("class", $6); + free_mem($6); + $$ = flatten_msg(msg); + } + | ARCHIVE '(' STRING ',' integer ')' IDENT '{' msgfields '}' + { + BMessage* msg = make_msg($9); + msg->what = (int32) $5; + msg->AddString("class", $7); + msg->AddString("add_on", (char*) $3.ptr); + free_mem($7); + free_mem($3.ptr); + $$ = flatten_msg(msg); + } + ; + +type + : IDENT '{' typefields '}' { $$ = make_type($1, $3); } + | IDENT '{' '}' + { + list_t list; list.count = 0; list.items = NULL; + $$ = make_type($1, list); + } + | IDENT data + { + $$ = make_type($1, make_data_list($2)); + } + | IDENT + { + list_t list; list.count = 0; list.items = NULL; + $$ = make_type($1, list); + } + ; + +typefields + : typefields ',' typefield { $$ = concat_data_list($1, $3); } + | typefield { $$ = make_data_list($1); } + ; + +typefield + : IDENT '=' data { $$ = $3; $$.name = $1; } + | data { $$ = $1; } + ; + +data + : BOOL { $$ = cast(get_type("bool"), make_bool($1)); } + | integer { $$ = cast(get_type("int32"), make_int($1)); } + | float { $$ = cast(get_type("float"), make_float($1)); } + | STRING { $$ = cast($1.type, $1); } + | RAW { $$ = cast($1.type, $1); } + | array { $$ = cast($1.type, $1); } + | message { $$ = cast($1.type, $1); } + | archive { $$ = cast($1.type, $1); } + | type { $$ = cast($1.type, $1); } + | typecast BOOL { $$ = cast($1, make_bool($2)); } + | typecast integer { $$ = cast($1, make_int($2)); } + | typecast float { $$ = cast($1, make_float($2)); } + | typecast STRING { $$ = cast($1, $2); } + | typecast RAW { $$ = cast($1, $2); } + | typecast array { $$ = cast($1, $2); } + | typecast message { $$ = cast($1, $2); } + | typecast archive { $$ = cast($1, $2); } + | typecast type { $$ = cast($1, $2); } + ; + +typecast + : '(' datatype ')' { $$ = $2; } + ; + +datatype + : ARRAY { $$ = get_type("raw"); } + | MESSAGE { $$ = get_type("message"); } + | ARCHIVE IDENT { $$ = get_type("message"); free_mem($2); } + | IDENT { $$ = get_type($1); free_mem($1); } + ; + +integer + : INTEGER { $$ = $1; } + | '-' INTEGER { $$ = -($2); } + ; + +float + : FLOAT { $$ = $1; } + | '-' FLOAT { $$ = -($2); } + ; + +%% +//------------------------------------------------------------------------------ + +void yyerror(char* msg) +{ + // This function is called by the parser when it encounters + // an error, after which it aborts parsing and returns from + // yyparse(). We never call yyerror() directly. + + rdef_err = RDEF_COMPILE_ERR; + rdef_err_line = yylineno; + strcpy(rdef_err_file, lexfile); + strcpy(rdef_err_msg, msg); +} + +//------------------------------------------------------------------------------ + +void add_symbol(char* name, int32 id) +{ + if (symbol_table.find(name) != symbol_table.end()) + { + abort_compile(RDEF_COMPILE_ERR, "duplicate symbol %s", name); + } + + symbol_table.insert(make_pair(name, id)); +} + +//------------------------------------------------------------------------------ + +int32 get_symbol(char* name) +{ + sym_iter_t i = symbol_table.find(name); + + if (i == symbol_table.end()) + { + abort_compile(RDEF_COMPILE_ERR, "unknown symbol %s", name); + } + + return i->second; +} + +//------------------------------------------------------------------------------ + +static void add_builtin_type(type_code code, char* name) +{ + type_t type; + type.code = code; + type.name = name; + type.count = 0; + type.fields = NULL; + type.def_id = 1; + type.def_name = NULL; + + type_table.insert(make_pair(name, type)); +} + +//------------------------------------------------------------------------------ + +void add_user_type(id_t id, type_code code, char* name, list_t list) +{ + if (type_table.find(name) != type_table.end()) + { + abort_compile(RDEF_COMPILE_ERR, "duplicate type %s", name); + } + + type_t type; + type.code = code; + type.name = name; + type.count = list.count; + type.fields = (field_t*) list.items; + type.def_id = 1; + type.def_name = NULL; + + if (id.has_id) + { + type.def_id = id.id; + } + + if (id.has_name) + { + type.def_name = id.name; + } + + type_table.insert(make_pair(name, type)); +} + +//------------------------------------------------------------------------------ + +static bool is_builtin_type(type_t type) +{ + return (type.count == 0); +} + +//------------------------------------------------------------------------------ + +static bool same_type(type_t type1, type_t type2) +{ + return (type1.name == type2.name); // no need for strcmp +} + +//------------------------------------------------------------------------------ + +type_t get_type(char* name) +{ + type_iter_t i = type_table.find(name); + + if (i == type_table.end()) + { + abort_compile(RDEF_COMPILE_ERR, "unknown type %s", name); + } + + return i->second; +} + +//------------------------------------------------------------------------------ + +data_t make_data(size_t size, type_t type) +{ + data_t out; + out.type = type; + out.name = NULL; + out.size = size; + out.ptr = alloc_mem(size); + return out; +} + +//------------------------------------------------------------------------------ + +data_t make_bool(bool b) +{ + data_t out = make_data(sizeof(bool), get_type("bool")); + *((bool*) out.ptr) = b; + return out; +} + +//------------------------------------------------------------------------------ + +data_t make_int(uint64 i) +{ + data_t out = make_data(sizeof(uint64), get_type("uint64")); + *((uint64*) out.ptr) = i; + return out; +} + +//------------------------------------------------------------------------------ + +data_t make_float(double f) +{ + data_t out = make_data(sizeof(double), get_type("double")); + *((double*) out.ptr) = f; + return out; +} + +//------------------------------------------------------------------------------ + +BMessage* make_msg(list_t list) +{ + BMessage* msg = new BMessage; + + for (int32 t = 0; t < list.count; ++t) + { + data_t data = ((data_t*) list.items)[t]; + msg->AddData(data.name, data.type.code, data.ptr, data.size, false); + free_mem(data.name); + free_mem(data.ptr); + } + + free_mem(list.items); + return msg; +} + +//------------------------------------------------------------------------------ + +data_t flatten_msg(BMessage* msg) +{ + data_t out = make_data(msg->FlattenedSize(), get_type("message")); + msg->Flatten((char*) out.ptr, out.size); + delete msg; + return out; +} + +//------------------------------------------------------------------------------ + +data_t make_default(type_t type) +{ + data_t out; + + if (is_builtin_type(type)) + { + switch (type.code) + { + case B_BOOL_TYPE: + out = make_data(sizeof(bool), type); + *((bool*) out.ptr) = false; + break; + + case B_INT8_TYPE: + case B_UINT8_TYPE: + out = make_data(sizeof(uint8), type); + *((uint8*) out.ptr) = 0; + break; + + case B_INT16_TYPE: + case B_UINT16_TYPE: + out = make_data(sizeof(uint16), type); + *((uint16*) out.ptr) = 0; + break; + + case B_INT32_TYPE: + case B_UINT32_TYPE: + case B_SIZE_T_TYPE: + case B_SSIZE_T_TYPE: + case B_TIME_TYPE: + out = make_data(sizeof(uint32), type); + *((uint32*) out.ptr) = 0; + break; + + case B_INT64_TYPE: + case B_UINT64_TYPE: + case B_OFF_T_TYPE: + out = make_data(sizeof(uint64), type); + *((uint64*) out.ptr) = 0; + break; + + case B_FLOAT_TYPE: + out = make_data(sizeof(float), type); + *((float*) out.ptr) = 0.0f; + break; + + case B_DOUBLE_TYPE: + out = make_data(sizeof(double), type); + *((double*) out.ptr) = 0.0; + break; + + case B_STRING_TYPE: + out = make_data(sizeof(char), type); + *((char*) out.ptr) = '\0'; + break; + + case B_RAW_TYPE: + out = make_data(0, type); + break; + + case B_MESSAGE_TYPE: + out = flatten_msg(new BMessage); + break; + } + } + else // user-defined type + { + size_t size = 0; + for (int32 t = 0; t < type.count; ++t) + { + size += type.fields[t].data.size; + } + + out = make_data(size, type); + + uint8* ptr = (uint8*) out.ptr; + for (int32 t = 0; t < type.count; ++t) + { + data_t field_data = type.fields[t].data; + memcpy(ptr, field_data.ptr, field_data.size); + ptr += field_data.size; + } + } + + return out; +} + +//------------------------------------------------------------------------------ + +static data_t* fill_slots(type_t type, list_t list) +{ + data_t* slots = (data_t*) alloc_mem(type.count * sizeof(data_t)); + memset(slots, 0, type.count * sizeof(data_t)); + + for (int32 t = 0; t < list.count; ++t) + { + data_t data = ((data_t*) list.items)[t]; + + if (data.name == NULL) + { + bool found = false; + for (int32 k = 0; k < type.count; ++k) + { + if (slots[k].ptr == NULL) + { + slots[k] = cast(type.fields[k].type, data); + found = true; + break; + } + } + + if (!found) + { + abort_compile(RDEF_COMPILE_ERR, "too many fields"); + } + } + else // named field + { + bool found = false; + for (int32 k = 0; k < type.count; ++k) + { + if (strcmp(type.fields[k].name, data.name) == 0) + { + if (slots[k].ptr != NULL) + { + free_mem(slots[k].ptr); + } + + slots[k] = cast(type.fields[k].type, data); + free_mem(data.name); + found = true; + break; + } + } + + if (!found) + { + abort_compile(RDEF_COMPILE_ERR, "unknown field %s", data.name); + } + } + } + + return slots; +} + +//------------------------------------------------------------------------------ + +static data_t convert_slots(type_t type, data_t* slots) +{ + size_t size = 0; + for (int32 k = 0; k < type.count; ++k) + { + if (slots[k].ptr == NULL) + { + size += type.fields[k].data.size; + } + else + { + size += slots[k].size; + } + } + + data_t out = make_data(size, type); + uint8* ptr = (uint8*) out.ptr; + + for (int32 k = 0; k < type.count; ++k) + { + if (slots[k].ptr == NULL) + { + memcpy(ptr, type.fields[k].data.ptr, type.fields[k].data.size); + ptr += type.fields[k].data.size; + } + else + { + memcpy(ptr, slots[k].ptr, slots[k].size); + ptr += slots[k].size; + free_mem(slots[k].ptr); + } + } + + free_mem(slots); + return out; +} + +//------------------------------------------------------------------------------ + +data_t make_type(char* name, list_t list) +{ + // Some explanation is in order. The "list" contains zero or more data_t + // items. Each of these items corresponds to a data field that the user + // specified, but not necessarily to a field from the type definition. + // So here we have to figure out which data item goes where. It is fairly + // obvious where names items should go, but for items without a name we + // simply use the first available slot. For any fields that the user did + // not fill in we use the default value from the type definition. This + // algorithm allows for variable size fields, such as strings and arrays. + + type_t type = get_type(name); + + data_t* slots = fill_slots(type, list); + data_t out = convert_slots(type, slots); + + free_mem(name); + free_mem(list.items); + return out; +} + +//------------------------------------------------------------------------------ + +list_t make_field_list(field_t field) +{ + list_t out; + out.count = 1; + out.items = alloc_mem(sizeof(field_t)); + *((field_t*) out.items) = field; + return out; +} + +//------------------------------------------------------------------------------ + +list_t concat_field_list(list_t list, field_t field) +{ + list_t out; + out.count = list.count + 1; + out.items = alloc_mem(out.count * sizeof(field_t)); + + memcpy(out.items, list.items, list.count * sizeof(field_t)); + memcpy((field_t*) out.items + list.count, &field, sizeof(field_t)); + + free_mem(list.items); + return out; +} + +//------------------------------------------------------------------------------ + +list_t make_data_list(data_t data) +{ + list_t out; + out.count = 1; + out.items = alloc_mem(sizeof(data_t)); + *((data_t*) out.items) = data; + return out; +} + +//------------------------------------------------------------------------------ + +list_t concat_data_list(list_t list, data_t data) +{ + list_t out; + out.count = list.count + 1; + out.items = (data_t*) alloc_mem(out.count * sizeof(data_t)); + + memcpy(out.items, list.items, list.count * sizeof(data_t)); + memcpy((data_t*) out.items + list.count, &data, sizeof(data_t)); + + free_mem(list.items); + return out; +} + +//------------------------------------------------------------------------------ + +data_t concat_data(data_t data1, data_t data2) +{ + data_t out = make_data(data1.size + data2.size, get_type("raw")); + + memcpy(out.ptr, data1.ptr, data1.size); + memcpy((uint8*) out.ptr + data1.size, data2.ptr, data2.size); + + free_mem(data1.ptr); + free_mem(data2.ptr); + return out; +} + +//------------------------------------------------------------------------------ + +static data_t cast_to_uint8(type_t new_type, data_t data) +{ + data_t out = make_data(sizeof(uint8), new_type); + + switch (data.type.code) + { + case B_INT8_TYPE: + case B_UINT8_TYPE: + *((uint8*) out.ptr) = (uint8) *((uint8*) data.ptr); break; + + case B_INT16_TYPE: + case B_UINT16_TYPE: + *((uint8*) out.ptr) = (uint8) *((uint16*) data.ptr); break; + + case B_INT32_TYPE: + case B_UINT32_TYPE: + case B_SIZE_T_TYPE: + case B_SSIZE_T_TYPE: + case B_TIME_TYPE: + *((uint8*) out.ptr) = (uint8) *((uint32*) data.ptr); break; + + case B_INT64_TYPE: + case B_UINT64_TYPE: + case B_OFF_T_TYPE: + *((uint8*) out.ptr) = (uint8) *((uint64*) data.ptr); break; + + default: + abort_compile(RDEF_COMPILE_ERR, "cannot cast to this type"); + } + + free_mem(data.ptr); + return out; +} + +//------------------------------------------------------------------------------ + +static data_t cast_to_uint16(type_t new_type, data_t data) +{ + data_t out = make_data(sizeof(uint16), new_type); + + switch (data.type.code) + { + case B_INT8_TYPE: + case B_UINT8_TYPE: + *((uint16*) out.ptr) = (uint16) *((uint8*) data.ptr); break; + + case B_INT16_TYPE: + case B_UINT16_TYPE: + *((uint16*) out.ptr) = (uint16) *((uint16*) data.ptr); break; + + case B_INT32_TYPE: + case B_UINT32_TYPE: + case B_SIZE_T_TYPE: + case B_SSIZE_T_TYPE: + case B_TIME_TYPE: + *((uint16*) out.ptr) = (uint16) *((uint32*) data.ptr); break; + + case B_INT64_TYPE: + case B_UINT64_TYPE: + case B_OFF_T_TYPE: + *((uint16*) out.ptr) = (uint16) *((uint64*) data.ptr); break; + + default: + abort_compile(RDEF_COMPILE_ERR, "cannot cast to this type"); + } + + free_mem(data.ptr); + return out; +} + +//------------------------------------------------------------------------------ + +static data_t cast_to_uint32(type_t new_type, data_t data) +{ + data_t out = make_data(sizeof(uint32), new_type); + + switch (data.type.code) + { + case B_INT8_TYPE: + case B_UINT8_TYPE: + *((uint32*) out.ptr) = (uint32) *((uint8*) data.ptr); break; + + case B_INT16_TYPE: + case B_UINT16_TYPE: + *((uint32*) out.ptr) = (uint32) *((uint16*) data.ptr); break; + + case B_INT32_TYPE: + case B_UINT32_TYPE: + case B_SIZE_T_TYPE: + case B_SSIZE_T_TYPE: + case B_TIME_TYPE: + *((uint32*) out.ptr) = (uint32) *((uint32*) data.ptr); break; + + case B_INT64_TYPE: + case B_UINT64_TYPE: + case B_OFF_T_TYPE: + *((uint32*) out.ptr) = (uint32) *((uint64*) data.ptr); break; + + default: + abort_compile(RDEF_COMPILE_ERR, "cannot cast to this type"); + } + + free_mem(data.ptr); + return out; +} + +//------------------------------------------------------------------------------ + +static data_t cast_to_uint64(type_t new_type, data_t data) +{ + data_t out = make_data(sizeof(uint64), new_type); + + switch (data.type.code) + { + case B_INT8_TYPE: + case B_UINT8_TYPE: + *((uint64*) out.ptr) = (uint64) *((uint8*) data.ptr); break; + + case B_INT16_TYPE: + case B_UINT16_TYPE: + *((uint64*) out.ptr) = (uint64) *((uint16*) data.ptr); break; + + case B_INT32_TYPE: + case B_UINT32_TYPE: + case B_SIZE_T_TYPE: + case B_SSIZE_T_TYPE: + case B_TIME_TYPE: + *((uint64*) out.ptr) = (uint64) *((uint32*) data.ptr); break; + + case B_INT64_TYPE: + case B_UINT64_TYPE: + case B_OFF_T_TYPE: + *((uint64*) out.ptr) = (uint64) *((uint64*) data.ptr); break; + + default: + abort_compile(RDEF_COMPILE_ERR, "cannot cast to this type"); + } + + free_mem(data.ptr); + return out; +} + +//------------------------------------------------------------------------------ + +static data_t cast_to_float(type_t new_type, data_t data) +{ + data_t out = make_data(sizeof(float), new_type); + + switch (data.type.code) + { + case B_INT8_TYPE: + case B_UINT8_TYPE: + *((float*) out.ptr) = (float) *((uint8*) data.ptr); break; + + case B_INT16_TYPE: + case B_UINT16_TYPE: + *((float*) out.ptr) = (float) *((uint16*) data.ptr); break; + + case B_INT32_TYPE: + case B_UINT32_TYPE: + case B_SIZE_T_TYPE: + case B_SSIZE_T_TYPE: + case B_TIME_TYPE: + *((float*) out.ptr) = (float) *((uint32*) data.ptr); break; + + case B_INT64_TYPE: + case B_UINT64_TYPE: + case B_OFF_T_TYPE: + *((float*) out.ptr) = (float) *((uint64*) data.ptr); break; + + case B_DOUBLE_TYPE: + *((float*) out.ptr) = (float) *((double*) data.ptr); break; + + default: + abort_compile(RDEF_COMPILE_ERR, "cannot cast to this type"); + } + + free_mem(data.ptr); + return out; +} + +//------------------------------------------------------------------------------ + +static data_t cast_to_double(type_t new_type, data_t data) +{ + data_t out = make_data(sizeof(double), new_type); + + switch (data.type.code) + { + case B_INT8_TYPE: + case B_UINT8_TYPE: + *((double*) out.ptr) = (double) *((uint8*) data.ptr); break; + + case B_INT16_TYPE: + case B_UINT16_TYPE: + *((double*) out.ptr) = (double) *((uint16*) data.ptr); break; + + case B_INT32_TYPE: + case B_UINT32_TYPE: + case B_SIZE_T_TYPE: + case B_SSIZE_T_TYPE: + case B_TIME_TYPE: + *((double*) out.ptr) = (double) *((uint32*) data.ptr); break; + + case B_INT64_TYPE: + case B_UINT64_TYPE: + case B_OFF_T_TYPE: + *((double*) out.ptr) = (double) *((uint64*) data.ptr); break; + + case B_FLOAT_TYPE: + *((double*) out.ptr) = (double) *((float*) data.ptr); break; + + default: + abort_compile(RDEF_COMPILE_ERR, "cannot cast to this type"); + } + + free_mem(data.ptr); + return out; +} + +//------------------------------------------------------------------------------ + +data_t cast(type_t new_type, data_t data) +{ + if (same_type(new_type, data.type)) // you can't cast bool, string, + { // message, or user-defined type + return data; // to another type, only to same + } + + if (is_builtin_type(new_type)) + { + switch (new_type.code) + { + case B_INT8_TYPE: + case B_UINT8_TYPE: + return cast_to_uint8(new_type, data); + + case B_INT16_TYPE: + case B_UINT16_TYPE: + return cast_to_uint16(new_type, data); + + case B_INT32_TYPE: + case B_UINT32_TYPE: + case B_SIZE_T_TYPE: + case B_SSIZE_T_TYPE: + case B_TIME_TYPE: + return cast_to_uint32(new_type, data); + + case B_INT64_TYPE: + case B_UINT64_TYPE: + case B_OFF_T_TYPE: + return cast_to_uint64(new_type, data); + + case B_FLOAT_TYPE: + return cast_to_float(new_type, data); + + case B_DOUBLE_TYPE: + return cast_to_double(new_type, data); + + case B_RAW_TYPE: + data.type = new_type; // you can always cast + return data; // anything to raw + } + } + + abort_compile(RDEF_COMPILE_ERR, "cannot cast to this type"); + return data; +} + +//------------------------------------------------------------------------------ + +void add_resource(id_t id, type_code code, data_t data) +{ + if (!id.has_id) + { + id.id = data.type.def_id; + } + + if (!id.has_name) + { + id.name = data.type.def_name; + } + + if (!(flags & RDEF_MERGE_RESOURCES) && rsrc.HasResource(code, id.id)) + { + abort_compile(RDEF_COMPILE_ERR, "duplicate resource"); + } + + status_t err = rsrc.AddResource(code, id.id, data.ptr, data.size, id.name); + + if (err != B_OK) + { + rdef_err = RDEF_WRITE_ERR; + rdef_err_line = 0; + strcpy(rdef_err_file, rsrc_file); + sprintf(rdef_err_msg, "cannot add resource (%s)", strerror(err)); + abort_compile(); + } + + if (id.has_name) + { + free_mem(id.name); + } + + free_mem(data.ptr); +} + +//------------------------------------------------------------------------------ + +static void add_point_type() +{ + field_t* fields = (field_t*) alloc_mem(2 * sizeof(field_t)); + fields[0].type = get_type("float"); + fields[0].name = "x"; + fields[0].data = make_default(fields[0].type); + fields[1].type = get_type("float"); + fields[1].name = "y"; + fields[1].data = make_default(fields[1].type); + + type_t type; + type.code = B_POINT_TYPE; + type.name = "point"; + type.fields = fields; + type.count = 2; + type.def_id = 1; + type.def_name = NULL; + + type_table.insert(make_pair(type.name, type)); +} + +//------------------------------------------------------------------------------ + +static void add_rect_type() +{ + field_t* fields = (field_t*) alloc_mem(4 * sizeof(field_t)); + fields[0].type = get_type("float"); + fields[0].name = "left"; + fields[0].data = make_default(fields[0].type); + fields[1].type = get_type("float"); + fields[1].name = "top"; + fields[1].data = make_default(fields[1].type); + fields[2].type = get_type("float"); + fields[2].name = "right"; + fields[2].data = make_default(fields[2].type); + fields[3].type = get_type("float"); + fields[3].name = "bottom"; + fields[3].data = make_default(fields[3].type); + + type_t type; + type.code = B_RECT_TYPE; + type.name = "rect"; + type.fields = fields; + type.count = 4; + type.def_id = 1; + type.def_name = NULL; + + type_table.insert(make_pair(type.name, type)); +} + +//------------------------------------------------------------------------------ + +static void add_rgb_color_type() +{ + field_t* fields = (field_t*) alloc_mem(4 * sizeof(field_t)); + fields[0].type = get_type("uint8"); + fields[0].name = "red"; + fields[0].data = make_default(fields[0].type); + fields[1].type = get_type("uint8"); + fields[1].name = "green"; + fields[1].data = make_default(fields[1].type); + fields[2].type = get_type("uint8"); + fields[2].name = "blue"; + fields[2].data = make_default(fields[2].type); + fields[3].type = get_type("uint8"); + fields[3].name = "alpha"; + fields[3].data = make_default(fields[3].type); + *((uint8*) fields[3].data.ptr) = 255; + + type_t type; + type.code = B_RGB_COLOR_TYPE; + type.name = "rgb_color"; + type.fields = fields; + type.count = 4; + type.def_id = 1; + type.def_name = NULL; + + type_table.insert(make_pair(type.name, type)); +} + +//------------------------------------------------------------------------------ + +static void add_app_signature_type() +{ + field_t* fields = (field_t*) alloc_mem(1 * sizeof(field_t)); + fields[0].type = get_type("string"); + fields[0].name = "signature"; + fields[0].data = make_default(fields[0].type); + + type_t type; + type.code = 'MIMS'; + type.name = "app_signature"; + type.fields = fields; + type.count = 1; + type.def_id = 1; + type.def_name = "BEOS:APP_SIG"; + + type_table.insert(make_pair(type.name, type)); +} + +//------------------------------------------------------------------------------ + +static void add_app_flags() +{ + field_t* fields = (field_t*) alloc_mem(1 * sizeof(field_t)); + fields[0].type = get_type("uint32"); + fields[0].name = "flags"; + fields[0].data = make_default(fields[0].type); + + type_t type; + type.code = 'APPF'; + type.name = "app_flags"; + type.fields = fields; + type.count = 1; + type.def_id = 1; + type.def_name = "BEOS:APP_FLAGS"; + + type_table.insert(make_pair(type.name, type)); +} + +//------------------------------------------------------------------------------ + +static void add_app_version() +{ + field_t* fields = (field_t*) alloc_mem(7 * sizeof(field_t)); + fields[0].type = get_type("uint32"); + fields[0].name = "major"; + fields[0].data = make_default(fields[0].type); + fields[1].type = get_type("uint32"); + fields[1].name = "middle"; + fields[1].data = make_default(fields[1].type); + fields[2].type = get_type("uint32"); + fields[2].name = "minor"; + fields[2].data = make_default(fields[2].type); + fields[3].type = get_type("uint32"); + fields[3].name = "variety"; + fields[3].data = make_default(fields[3].type); + fields[4].type = get_type("uint32"); + fields[4].name = "internal"; + fields[4].data = make_default(fields[4].type); + fields[5].type = get_type("string"); + fields[5].name = "short_info"; + fields[5].data = make_data(64, fields[5].type); + memset(fields[5].data.ptr, '\0', fields[5].data.size); + fields[6].type = get_type("string"); + fields[6].name = "long_info"; + fields[6].data = make_data(256, fields[6].type); + memset(fields[6].data.ptr, '\0', fields[6].data.size); + + type_t type; + type.code = 'APPV'; + type.name = "app_version"; + type.fields = fields; + type.count = 7; + type.def_id = 1; + type.def_name = "BEOS:APP_VERSION"; + + type_table.insert(make_pair(type.name, type)); +} + +//------------------------------------------------------------------------------ + +static void add_large_icon() +{ + field_t* fields = (field_t*) alloc_mem(1 * sizeof(field_t)); + fields[0].type = get_type("raw"); + fields[0].name = "icon"; + fields[0].data = make_default(fields[0].type); + + type_t type; + type.code = 'ICON'; + type.name = "large_icon"; + type.fields = fields; + type.count = 1; + type.def_id = 101; + type.def_name = "BEOS:L:STD_ICON"; + + type_table.insert(make_pair(type.name, type)); +} + +//------------------------------------------------------------------------------ + +static void add_mini_icon() +{ + field_t* fields = (field_t*) alloc_mem(1 * sizeof(field_t)); + fields[0].type = get_type("raw"); + fields[0].name = "icon"; + fields[0].data = make_default(fields[0].type); + + type_t type; + type.code = 'MICN'; + type.name = "mini_icon"; + type.fields = fields; + type.count = 1; + type.def_id = 101; + type.def_name = "BEOS:M:STD_ICON"; + + type_table.insert(make_pair(type.name, type)); +} + +//------------------------------------------------------------------------------ + +static void add_file_types() +{ + field_t* fields = (field_t*) alloc_mem(1 * sizeof(field_t)); + fields[0].type = get_type("message"); + fields[0].name = "types"; + fields[0].data = make_default(fields[0].type); + + type_t type; + type.code = 'MSGG'; + type.name = "file_types"; + type.fields = fields; + type.count = 1; + type.def_id = 1; + type.def_name = "BEOS:FILE_TYPES"; + + type_table.insert(make_pair(type.name, type)); +} + +//------------------------------------------------------------------------------ + +void init_parser() +{ + add_builtin_type(B_BOOL_TYPE, "bool"); + add_builtin_type(B_INT8_TYPE, "int8"); + add_builtin_type(B_UINT8_TYPE, "uint8"); + add_builtin_type(B_INT16_TYPE, "int16"); + add_builtin_type(B_UINT16_TYPE, "uint16"); + add_builtin_type(B_INT32_TYPE, "int32"); + add_builtin_type(B_UINT32_TYPE, "uint32"); + add_builtin_type(B_SIZE_T_TYPE, "size_t"); + add_builtin_type(B_SSIZE_T_TYPE, "ssize_t"); + add_builtin_type(B_TIME_TYPE, "time_t"); + add_builtin_type(B_INT64_TYPE, "int64"); + add_builtin_type(B_UINT64_TYPE, "uint64"); + add_builtin_type(B_OFF_T_TYPE, "off_t"); + add_builtin_type(B_FLOAT_TYPE, "float"); + add_builtin_type(B_DOUBLE_TYPE, "double"); + add_builtin_type(B_STRING_TYPE, "string"); + add_builtin_type(B_RAW_TYPE, "raw"); + add_builtin_type(B_RAW_TYPE, "buffer"); + add_builtin_type(B_MESSAGE_TYPE, "message"); + + add_point_type(); + add_rect_type(); + add_rgb_color_type(); + add_app_signature_type(); + add_app_flags(); + add_app_version(); + add_large_icon(); + add_mini_icon(); + add_file_types(); +} + +//------------------------------------------------------------------------------ + +void clean_up_parser() +{ + // The symbol table entries have several malloc'ed objects associated + // with them (such as their name). They were allocated with alloc_mem(), + // so we don't need to free them here; compile.cpp already does that + // when it cleans up. However, we do need to remove the entries from + // the tables, otherwise they will still be around the next time we are + // asked to compile something. In DEBUG mode, however, we do free these + // items, so they don't show up in the mem leak statistics. + +#ifdef DEBUG + for (sym_iter_t i = symbol_table.begin(); i != symbol_table.end(); ++i) + { + free_mem(i->first); + } + + for (type_iter_t i = type_table.begin(); i != type_table.end(); ++i) + { + free_mem(i->first); + type_t type = i->second; + for (int32 t = 0; t < type.count; ++t) + { + free_mem(type.fields[t].name); + free_mem(type.fields[t].data.ptr); + } + free_mem(type.fields); + free_mem(type.name); + free_mem(type.def_name); + } +#endif + + symbol_table.clear(); + type_table.clear(); +} + +//------------------------------------------------------------------------------ diff --git a/src/tools/rc/private.h b/src/tools/rc/private.h new file mode 100644 index 0000000000..55867dbed9 --- /dev/null +++ b/src/tools/rc/private.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2003 Matthijs Hollemans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef PRIVATE_H +#define PRIVATE_H + +#include + +typedef std::list ptr_list_t; +typedef ptr_list_t::iterator ptr_iter_t; + +// Configuration options for the (de)compiler. +extern uint32 flags; + +// Where to look for #include files. +extern ptr_list_t include_dirs; + +// The list of input files. +extern ptr_list_t input_files; + +// free()'s all the elements from a list. +void free_ptr_list(ptr_list_t& list); + +// Resets the rdef_err_* variables. +void clear_error(); + +#endif // PRIVATE_H diff --git a/src/tools/rc/rc.cpp b/src/tools/rc/rc.cpp new file mode 100644 index 0000000000..018f719d81 --- /dev/null +++ b/src/tools/rc/rc.cpp @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2003 Matthijs Hollemans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "rdef.h" + +#define TITLE "OpenBeOS Resource Compiler 1.0" + +#define DEFAULT_OUT_RSRC "out.rsrc" +#define DEFAULT_OUT_RDEF "out.rdef" + +static char* prog_name; + +static bool quiet = false; +static bool should_decompile = false; +static uint32 flags = 0; + +static char output_file[B_PATH_NAME_LENGTH] = { 0 }; + +//------------------------------------------------------------------------------ + +void warn(const char* format, ...) +{ + va_list ap; + + if (!quiet) + { + fprintf(stderr, "Warning! "); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fprintf(stderr, "\n"); + } +} + +//------------------------------------------------------------------------------ + +void error(const char* format, ...) +{ + va_list ap; + + if (!quiet) + { + fprintf(stderr, "Error! "); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fprintf(stderr, "\n"); + } + + exit(EXIT_FAILURE); +} + +//------------------------------------------------------------------------------ + +static void usage() +{ + printf( + +"%s\n\n" +"To compile an rdef script into a resource file:\n" +" %s [options] [-o ] ...\n\n" +"To convert a resource file back into an rdef script:\n" +" %s [options] [-o ] -d ...\n\n" +"Options:\n" +" -d --decompile create an rdef script from a resource file\n" +" --auto-names construct resource names from ID symbols\n" +" -h --help show this message\n" +" -I --include add to the list of include paths\n" +" -m --merge do not erase existing contents of output file\n" +" -o --output specify output file name, default is out.xxx\n" +" -q --quiet do not display any error messages\n" +" -V --version show software version and license\n", + + TITLE, prog_name, prog_name); + + exit(EXIT_SUCCESS); +} + +//------------------------------------------------------------------------------ + +static void version() +{ + printf( + "%s\n\n" + "Copyright (c) 2003 Matthijs Hollemans\n\n" + +"Permission is hereby granted, free of charge, to any person obtaining a\n" +"copy of this software and associated documentation files (the \"Software\"),\n" +"to deal in the Software without restriction, including without limitation\n" +"the rights to use, copy, modify, merge, publish, distribute, sublicense,\n" +"and/or sell copies of the Software, and to permit persons to whom the\n" +"Software is furnished to do so, subject to the following conditions:\n\n" + +"The above copyright notice and this permission notice shall be included in\n" +"all copies or substantial portions of the Software.\n\n" + +"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" +"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" +"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" +"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" +"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n" +"FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n" +"DEALINGS IN THE SOFTWARE.\n", + + TITLE); + + exit(EXIT_SUCCESS); +} + +//------------------------------------------------------------------------------ + +static void get_program_name(char* argv[]) +{ + char* p; + prog_name = ((p = strrchr(argv[0], '/')) ? p + 1 : *argv); +} + +//------------------------------------------------------------------------------ + +static void parse_options(int argc, char* argv[]) +{ + int32 args_left = argc - 1; + + for (int32 i = 1; i < argc; ++i) + { + if ((strcmp(argv[i], "-o") == 0) + || (strcmp(argv[i], "--output") == 0)) + { + if (i + 1 >= argc) + { + error("%s should be followed by a file name", argv[i]); + } + + strcpy(output_file, argv[i + 1]); + + argv[i] = NULL; + argv[i + 1] = NULL; + ++i; args_left -= 2; + } + else if ((strcmp(argv[i], "-I") == 0) + || (strcmp(argv[i], "--include") == 0)) + { + if (i + 1 >= argc) + { + error("%s should be followed by a directory name", argv[i]); + } + + rdef_add_include_dir(argv[i + 1]); + + argv[i] = NULL; + argv[i + 1] = NULL; + ++i; args_left -= 2; + } + else if ((strcmp(argv[i], "-d") == 0) + || (strcmp(argv[i], "--decompile") == 0)) + { + should_decompile = true; + argv[i] = NULL; + --args_left; + } + else if ((strcmp(argv[i], "-m") == 0) + || (strcmp(argv[i], "--merge") == 0)) + { + flags |= RDEF_MERGE_RESOURCES; + argv[i] = NULL; + --args_left; + } + else if (strcmp(argv[i], "--auto-names") == 0) + { + flags |= RDEF_AUTO_NAMES; + argv[i] = NULL; + --args_left; + } + else if ((strcmp(argv[i], "-q") == 0) + || (strcmp(argv[i], "--quiet") == 0)) + { + quiet = true; + argv[i] = NULL; + --args_left; + } + else if ((strcmp(argv[i], "-h") == 0) + || (strcmp(argv[i], "--help") == 0)) + { + usage(); + } + else if ((strcmp(argv[i], "-V") == 0) + || (strcmp(argv[i], "--version") == 0)) + { + version(); + } + else if (argv[i][0] == '-') + { + error("unknown option %s", argv[i]); + argv[i] = NULL; + --args_left; + } + } + + if (args_left < 1) + { + error("no input files"); + } + + for (int i = 1; i < argc; ++i) + { + if (argv[i] != NULL) + { + rdef_add_input_file(argv[i]); + } + } +} + +//------------------------------------------------------------------------------ + +static bool has_extension(char* str, char* ext) +{ + size_t str_len = strlen(str); + size_t ext_len = strlen(ext); + + if (str_len > ext_len) + { + return (strcmp(str + str_len - ext_len, ext) == 0); + } + + return false; +} + +//------------------------------------------------------------------------------ + +static void compile() +{ + if (output_file[0] == '\0') + { + rdef_compile(DEFAULT_OUT_RSRC); + } + else + { + if (!has_extension(output_file, ".rsrc")) + { + strcat(output_file, ".rsrc"); + } + + rdef_compile(output_file); + } +} + +//------------------------------------------------------------------------------ + +static void decompile() +{ + if (output_file[0] == '\0') + { + rdef_decompile(DEFAULT_OUT_RDEF); + } + else + { + if (!has_extension(output_file, ".rdef")) + { + strcat(output_file, ".rdef"); + } + + rdef_decompile(output_file); + } +} + +//------------------------------------------------------------------------------ + +static void report_error() +{ + switch (rdef_err) + { + case RDEF_COMPILE_ERR: + error("%s:%ld %s", rdef_err_file, rdef_err_line, rdef_err_msg); + break; + + case RDEF_FILE_NOT_FOUND: + error("%s not found", rdef_err_file); + break; + + case RDEF_NO_RESOURCES: + error("%s is not a resource file", rdef_err_file); + break; + + case RDEF_WRITE_ERR: + error("error writing to %s (%s)", rdef_err_file, rdef_err_msg); + break; + + case B_NO_MEMORY: + error("out of memory"); + break; + + case B_ERROR: + default: + error("unknown error: %lx (%s)", rdef_err, strerror(rdef_err)); + break; + } +} + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + rdef_add_include_dir("."); // look in current dir first + + get_program_name(argv); + parse_options(argc, argv); + + rdef_set_flags(flags); + + if (should_decompile) + { + decompile(); + } + else + { + compile(); + } + + rdef_free_input_files(); + rdef_free_include_dirs(); + + if (rdef_err != B_OK) + { + report_error(); + } + + return EXIT_SUCCESS; +} + +//------------------------------------------------------------------------------ diff --git a/src/tools/rc/rdef.cpp b/src/tools/rc/rdef.cpp new file mode 100644 index 0000000000..ed9697825e --- /dev/null +++ b/src/tools/rc/rdef.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2003 Matthijs Hollemans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "rdef.h" +#include "private.h" + +ptr_list_t include_dirs; +ptr_list_t input_files; + +uint32 flags = 0; + +status_t rdef_err; +int32 rdef_err_line; +char rdef_err_file[B_PATH_NAME_LENGTH]; +char rdef_err_msg[1024]; + +//------------------------------------------------------------------------------ + +void free_ptr_list(ptr_list_t& list) +{ + for (ptr_iter_t i = list.begin(); i != list.end(); ++i) + { + free(*i); + } + + list.clear(); +} + +//------------------------------------------------------------------------------ + +int32 rdef_get_version() +{ + return 1; +} + +//------------------------------------------------------------------------------ + +status_t rdef_add_include_dir(const char* dir) +{ + clear_error(); + + char* temp = (char*) malloc(strlen(dir) + 2); + if (temp == NULL) + { + rdef_err = B_NO_MEMORY; + return B_NO_MEMORY; + } + + strcpy(temp, dir); + strcat(temp, "/"); + + include_dirs.push_back(temp); + return B_OK; +} + +//------------------------------------------------------------------------------ + +void rdef_free_include_dirs() +{ + free_ptr_list(include_dirs); +} + +//------------------------------------------------------------------------------ + +status_t rdef_add_input_file(const char* file) +{ + clear_error(); + + char* temp = strdup(file); + if (temp == NULL) + { + rdef_err = B_NO_MEMORY; + return B_NO_MEMORY; + } + + input_files.push_back(temp); + return B_OK; +} + +//------------------------------------------------------------------------------ + +void rdef_free_input_files() +{ + free_ptr_list(input_files); +} + +//------------------------------------------------------------------------------ + +void rdef_set_flags(uint32 flags_) +{ + flags = flags_; +} + +//------------------------------------------------------------------------------ + +void rdef_clear_flags() +{ + flags = 0; +} + +//------------------------------------------------------------------------------ + +void clear_error() +{ + rdef_err = B_OK; + rdef_err_line = 0; + rdef_err_file[0] = '\0'; + rdef_err_msg[0] = '\0'; +} + +//------------------------------------------------------------------------------ diff --git a/src/tools/rc/rdef.h b/src/tools/rc/rdef.h new file mode 100644 index 0000000000..0bb43224b6 --- /dev/null +++ b/src/tools/rc/rdef.h @@ -0,0 +1,171 @@ +/** + * @file rdef.h + * + * Public header file for the librdef shared library. Programs that want to + * use librdef should include this file, and link to librdef.so. + * + * @author Copyright (c) 2003 Matthijs Hollemans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef RDEF_H +#define RDEF_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if __INTEL__ +# ifdef _BUILDING_RDEF +# define _IMPEXP_RDEF __declspec(dllexport) +# else +# define _IMPEXP_RDEF __declspec(dllimport) +# endif +#else +# define _IMPEXP_RDEF +#endif + +/** + * Whether to overwrite or merge with the output file. With this flag, the + * compiler will add the resources to the output file (if it exists). When + * something goes wrong, the output file is not deleted (although it may + * already contain some of the new resources). The default action is to clobber + * the contents of the output file if it already existed, and the file is + * deleted in case of an error. Not used for decompiling. + */ +#define RDEF_MERGE_RESOURCES (1 << 0) + +/** + * Whether to generate resource names from symbolic identifiers, or vice versa. + * With this flag, the compiler will use symbolic names in statements such as + * "resource(R_Symbol)" to generate the resource name, in this case "R_Symbol". + * Otherwise, you must explicitly specify a name, such as "resource(R_Symbol, + * "Name") ..." If this option is set when decompiling, the decompiler will put + * resource names that are valid C/C++ identifiers in an enum statement in a + * new header file. + */ +#define RDEF_AUTO_NAMES (1 << 1) + +/** Error codes returned by the librdef functions. */ +enum +{ + /** Syntax error or other compiler error. */ + RDEF_COMPILE_ERR = B_ERRORS_END + 1, + + /** Could not find one of the input files. */ + RDEF_FILE_NOT_FOUND, + + /** One of the input files has nothing to decompile. */ + RDEF_NO_RESOURCES, + + /** Could not write the output file. */ + RDEF_WRITE_ERR, + + /* B_OK */ + /* B_ERROR */ + /* B_NO_MEMORY */ +}; + +/** + * The result of the most recent (de)compilation. B_OK if the operation + * was successful, a negative error code otherwise. This is the same as + * the value returned by any of the rdef_xxx functions. + */ +_IMPEXP_RDEF extern status_t rdef_err; + +/** + * The line number where compilation failed. Valid line numbers start + * at 1. This is 0 if the error did not happen on a specific line. + */ +_IMPEXP_RDEF extern int32 rdef_err_line; + +/** + * The file where the error occurred. This is an empty string if the + * error did not happen in a specific file. + */ +_IMPEXP_RDEF extern char rdef_err_file[]; + +/** + * The error message from the compiler. This is an empty string if there + * was no additional information to report. + */ +_IMPEXP_RDEF extern char rdef_err_msg[]; + +/** + * Returns the version number of the librdef API. You can use this to + * check which functions and variables are available. + */ +_IMPEXP_RDEF int32 rdef_get_version(); + +/** + * Adds a directory where the compiler will look for include files. + * Typically, you want to add the current directory to this list. + * Not used for decompilation. + */ +_IMPEXP_RDEF status_t rdef_add_include_dir(const char* dir); + +/** + * Frees the list of include directories. If you call rdef_add_include_dir(), + * you should always call rdef_free_include_dirs() when the compiler is done. + */ +_IMPEXP_RDEF void rdef_free_include_dirs(); + +/** + * Adds an input file for the compiler or decompiler. Input files are not + * required to have a specific extension. + */ +_IMPEXP_RDEF status_t rdef_add_input_file(const char* file); + +/** + * Frees the list of input files. If you call rdef_add_input_file(), you + * should always call rdef_free_input_files() when the compiler is done. + */ +_IMPEXP_RDEF void rdef_free_input_files(); + +/** Changes the configuration of the compiler or decompiler. */ +_IMPEXP_RDEF void rdef_set_flags(uint32 flags); + +/** Resets all the configuration options to their default values. */ +_IMPEXP_RDEF void rder_clear_flags(); + +/** + * Invokes the rdef-to-rsrc compiler. Before you call rdef_compile(), you must + * have specified at least one input file with rdef_add_input_file(), and one + * include search path with rdef_add_include_dir(). + */ +_IMPEXP_RDEF status_t rdef_compile(const char* output_file); + +/** + * Invokes the rsrc-to-rdef decompiler. Just as with rdef_compile(), you must + * first add at least one input file with rdef_add_input_file(). Include dirs + * are not necessary, because the decompiler does not use them. The decompiler + * writes at least an rdef script file. In addition, if the "auto names" option + * is enabled it also writes a C/C++ header file. If these files already exist, + * they will be overwritten. + */ +_IMPEXP_RDEF status_t rdef_decompile(const char* output_file); + +#ifdef __cplusplus +} +#endif + +#endif /* RDEF_H */ diff --git a/src/tools/rc/tests/all.rdef b/src/tools/rc/tests/all.rdef new file mode 100644 index 0000000000..fab4d55436 --- /dev/null +++ b/src/tools/rc/tests/all.rdef @@ -0,0 +1,11 @@ +/* + * Master test script for the resource compiler. + */ + +#include "enum.rdef" +#include "simple.rdef" +#include "array.rdef" +#include "message.rdef" +#include "type.rdef" + +//#include "defaults.rdef" // test separately diff --git a/src/tools/rc/tests/array.rdef b/src/tools/rc/tests/array.rdef new file mode 100644 index 0000000000..dae4c7bd10 --- /dev/null +++ b/src/tools/rc/tests/array.rdef @@ -0,0 +1,127 @@ + +resource(301) array { $"41" }; +resource(302) array { $"40" $"41" }; +resource(303) array { $"42", $"43" }; +resource(304) array { }; +resource(305) array; +//resource(306) { }; // syntax error +//resource(307) { $"40" }; // syntax error +//resource(308) array { 100, }; // syntax error + +resource(310) array { 1234, 5678 }; +resource(311) array { (int16) 1234 }; +resource(312) array { "wassup" }; +resource(313) array { (string) "wassup" }; + +resource(320) #'CSTR' array { "wassup" }; +resource(321) (#'CSTR') array { "wassup" }; +resource(322) (buffer) array { "hello" }; +resource(323) (raw) array { "there" }; +//resource(324) (string) array { "wassup" }; // invalid cast +//resource(325) (#'CSTR' string) array { "wassup" }; // invalid cast + +resource(330) array { "w00t", "dude" }; +resource(331) #'CSTR' array { "w00t", "dude" }; +resource(332) #'CSTR' array { "blah", "" }; +resource(333) #'CSTR' array { "w00t" "dude" }; +resource(334) #'CSTR' array { "blah" "" }; +resource(335) array { 1234, "text", true, $"4041", 3.14e-1 }; +resource(336) #'LLNG' array { (int64) 1234 }; // decompiles to (int64)1234 + +// The decompiler should not turn this back into a string, because +// it contains a non-human-readable character. +resource(337) array { "hey\0x01there" }; + +// Should decompile to $"405051416070" +resource(340) array +{ + $"40", array { $"50", $"51" }, $"41", array { $"60", array { $"70" }} +}; + +resource(341) array { $"40", message { "field" = "value" }}; + +enum { + R_BeOSBitmap = 100, + R_Icon = 100, + R_Cursor = 100, +}; + +resource(1) #'PNG ' array { + $"89504E470D0A1A0A0000000D49484452000000100000001008060000001FF3FF" + $"61000000096F464673000000000000000000DA2AB6CE00000009704859730000" + $"0FA000000FA001A06A8C770000019E49444154789CA593BF6BDB4018861FFD58" + $"8E8B34F51FE812E3D164C968E3C94BF0D041A55BC0244B372BD666BC092BD0B5" + $"2590ADD44307378BA7108F598247932CFD07324939B4482883CFE2126208E499" + $"8EE37DBFFB78EFFBACD17084499CC427401B686569DA00F07CFF1E5801CB288C" + $"7E997A6B5B204EE21670A6940AA494BC85520A29E50C984661B402704D33103C" + $"3E557CFFF18FDB878CBC7400104EC9E1BEC778D0454A02ED994661B4B275F133" + $"20B8BCBAA317CEB959E7E4A50B5880455EBADCAC737AE19CCBAB3B80407BB0AA" + $"AA3A514AFD7C7CAAE885736DDCF0FF7700C0E76FB3FA4E38058BA4CFA73D0B29" + $"E5A90DB4A5944C2EAE5F987791972E938B6B744E6D176801DC3E64DB486ACC97" + $"4D365A005A6E96A60DCFF7EBC05EB7FF56A1AD364BD386CD0E76BDFE1A5B0F09" + $"C229DF6530B59EEFDFDB6C268CC37DEFDD050CEDCA06964A29C6832EC2296A91" + $"998179164EC178D0452905B0B446C3117112FF410FD2F9DFF5CEEF144EC1F04B" + $"93E3A303805914465FB7214E81D9F1D1018BA44FA72974371550219C824E53B0" + $"48FAB5597B3EBE4CD647D7F919F31AB8B0697BD6410000000049454E44AE4260" + $"82" +}; + +resource(R_Cursor) #'CURS' array { + $"1001070707C0183027C859345014A10AA00AB45AA00AA10A5014593427C81830" + $"07C0000007C01FF03FF8793C701CE10EE00EF45EE00EE10E701C793C3FF81FF0" + $"07C00000" +}; + +resource(R_Icon) #'ICON' array { + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +}; + +resource(1) #'MICN' array { + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +}; diff --git a/src/tools/rc/tests/builtin.rdef b/src/tools/rc/tests/builtin.rdef new file mode 100644 index 0000000000..485a1225ff --- /dev/null +++ b/src/tools/rc/tests/builtin.rdef @@ -0,0 +1,91 @@ +/* + * Definitions for the types that are built into the compiler. + */ + +/* BPoint object. */ +type #'BPNT' point +{ + float x, + float y +}; + +/* BRect object. */ +type #'RECT' rect +{ + float left, + float top, + float right, + float bottom +}; + +/* rgb_color object. */ +type #'RGBC' rgb_color +{ + uint8 red, + uint8 green, + uint8 blue, + uint8 alpha = 0xFF +}; + +/* Application signature. */ +type(1, "BEOS:APP_SIG") #'MIMS' app_signature +{ + string signature // max 240 bytes (including null) +}; + +/* Application launch flags */ +type(1, "BEOS:APP_FLAGS") #'APPF' app_flags +{ + // 0x00000000 B_SINGLE_LAUNCH + // 0x01000000 B_MULTIPLE_LAUNCH + // 0x02000000 B_EXCLUSIVE_LAUNCH + // 0x04000000 B_BACKGROUND_APP + // 0x08000000 B_ARGV_ONLY + uint32 flags +}; + +/* + * Application version info. Should always be 340 bytes. The system + * will automatically append 340 additional bytes with "system info". + */ +type(1, "BEOS:APP_VERSION") #'APPV' app_version +{ + uint32 major, + uint32 middle, + uint32 minor, + + // 0 = development 1 = alpha 2 = beta + // 3 = gamma 4 = golden master 5 = final + uint32 variety, + + uint32 internal, + + string short_info = // must be exactly 64 bytes (including null) + " ", + + string long_info = // must be exactly 256 bytes (including null) + " " + " " + " " + " " +}; + +/* Large (32x32, 8-bit) application icon. */ +type(101, "BEOS:L:STD_ICON") #'ICON' large_icon +{ + array icon // 1024 bytes +}; + +/* Small (16x16, 8-bit) application icon. */ +type(101, "BEOS:M:STD_ICON") #'MICN' mini_icon +{ + array icon // 256 bytes +}; + +/* Supported file types. */ +type(1, "BEOS:FILE_TYPES") #'MSGG' file_types +{ + // contains one or more fields called "types", each of which + // has a MIME type value, for example: "types" = "text/plain" + message types +}; diff --git a/src/tools/rc/tests/defaults.rdef b/src/tools/rc/tests/defaults.rdef new file mode 100644 index 0000000000..18540cca29 --- /dev/null +++ b/src/tools/rc/tests/defaults.rdef @@ -0,0 +1,70 @@ + +enum { R_Type1 = 30, R_Type2 }; + +type #'CSTR' teep1 { string x }; +type(20) #'LONG' teep2 { int32 x }; +type(21, "def name1") #'LONG' teep3 { int32 x }; +type(R_Type1) #'LONG' teep4 { int32 x }; +type(R_Type2, "def name2") #'LONG' teep5 { int32 x }; +type("def name3") #'RAWT' teep6 { array x }; + +enum +{ + R_Res1 = 4, R_Res2, R_Res3, R_Res4, R_Res5, R_Res6, + R_Res7, R_Res8, R_Res9, R_Res10, R_Res11, R_Res12 +}; + +resource 123; // id 1, no name +resource(2) 123; // id 2, no name +resource(3, "over ride") 123; // id 3, name "over ride" +resource(R_Res1) 123; // id 4, no name +resource(R_Res2, "over ride") 123; // id 5, name "over ride" +resource("over ride") 10.0; // id 1, name "over ride" + +resource teep1 "blah"; // id 1, no name +resource(2) teep1 "blah"; // id 2, no name +resource(3, "over ride") teep1 "blah"; // id 3, name "over ride" +resource(R_Res1) teep1 "blah"; // id 4, no name +resource(R_Res2, "over ride") teep1 "blah"; // id 5, name "over ride" +//resource("over ride") teep1 "blah"; // duplicate resource (1) + +resource teep2 123; // id 20, no name +resource(40) teep2 123; // id 40, no name +resource(41, "over ride") teep2 123; // id 41, name "over ride" +resource(R_Res3) teep2 123; // id 6, no name +resource(R_Res4, "over ride") teep2 123; // id 7, name "over ride" +//resource("over ride") teep2 123; // duplicate resource (20) + +resource teep3 123; // id 21, name "def name1" +resource(42) teep3 123; // id 42, name "def name1" +resource(43, "over ride") teep3 123; // id 43, name "over ride" +resource(R_Res5) teep3 123; // id 8, name "def name1" / R_Res5 +resource(R_Res6, "over ride") teep3 123; // id 9, name "over ride" +//resource("over ride") teep3 123; // duplicate resource (21) + +resource teep4 123; // id 30, no name / R_Type1 +resource(44) teep4 123; // id 44, no name / R_Type1 +resource(45, "over ride") teep4 123; // id 45, name "over ride" +resource(R_Res7) teep4 123; // id 10, no name +resource(R_Res8, "over ride") teep4 123; // id 11, name "over ride" +//resource("over ride") teep4 123; // duplicate resource (30) + +resource teep5 123; // id 31, name "def name2" +resource(46) teep5 123; // id 46, name "def name2" +resource(47, "over ride") teep5 123; // id 47, name "over ride" +resource(R_Res9) teep5 123; // id 12, name "def name2" / R_Res9 +resource(R_Res10, "over ride") teep5 123; // id 13, name "over ride" +//resource("over ride") teep5 123; // duplicate resource (31) + +resource teep6 123; // id 1, name "def name3" +resource(48) teep6 123; // id 48, name "def name3" +resource(49, "over ride") teep6 123; // id 49, name "over ride" +resource(R_Res11) teep6 123; // id 14, name "def name3" / R_Res11 +resource(R_Res12, "over ride") teep6 123; // id 15, name "over ride" +//resource("over ride") teep6 123; // duplicate resource (1) + +//resource (int8) 123; // unknown type int8 +resource() (int8) 123; // a possible workaround + +type(0) teep7 { int32 x }; +resource teep7 (int8) 123; diff --git a/src/tools/rc/tests/enum.rdef b/src/tools/rc/tests/enum.rdef new file mode 100644 index 0000000000..748e43af70 --- /dev/null +++ b/src/tools/rc/tests/enum.rdef @@ -0,0 +1,34 @@ + +// Although useless, enums may be empty. +enum { }; + +enum +{ + R_Test1, // starts at 0 + R_Test2 +}; + +enum +{ + R_Test10, // is also 0 + R_Test11 = 1000, + R_Test12 = 1111, + R_Test13, // should be 1112 + R_Test14 = 999, + R_Test15 = -999, // may have comma +}; + +//enum { R_Test10 = 7 }; // duplicate symbol + +resource(R_Test1) 123; +//resource(R_Test10) 123; // duplicate resource +resource(R_Test11) 123; +resource(R_Test15) 123; +resource(R_Test15) "123"; +//resource(R_NotExists) 123; // unknown symbol + +resource(R_Test12) 123; +resource(R_Test13) 123; // should have ID 1112 +resource(R_Test13, "yeah") $"4041"; // R_RAWT_yeah +resource(R_Test13, "yeah\0what") 10.0; // R_FLOT_yeah +//resource(R_Test13, "yeah\0what") $"4041"; // duplicate resource diff --git a/src/tools/rc/tests/message.rdef b/src/tools/rc/tests/message.rdef new file mode 100644 index 0000000000..0b2744917a --- /dev/null +++ b/src/tools/rc/tests/message.rdef @@ -0,0 +1,208 @@ + +// "what" code should default to 0. +resource(401) message { "field1" = "value1" }; + +// Fields with casts, type codes, and nested compound data. +resource(402) message(1000) +{ + "small1" = (int8) 257, // 1 + int8 "small2" = 257, // 1 + int8 "small3" = (int8) 257, // 1 + uint8 "small4" = (int8) -1, // (uint8)255 + int8 "small5" = (uint8) -1, // -1 + int8 "small6" = (int8) -1, // -1 + uint8 "small7" = (uint8) -1, // (uint8)255 + + "large1" = (int64) 3, + int64 "large2" = 3, + int64 "large3" = (int32) 3, + int32 "large4" = (int64) 3, + + "string1" = "some text", +// string "string2" = 123, // invalid cast +// int32 "string3" = "text", // invalid cast + + #'BYTE' "typecode1" = 1234, // does not become "(int8)-46" + #'blah' int16 "typecode2" = 1234, // becomes 16-bit, but raw encoded + + "array1" = array { $"40", $"41" }, + buffer "array2" = array { $"50" $"51" }, + raw "array3" = array { "w00t", "dude" }, + array "array4" = array { "w00t", 1234 }, + array "array5" = (array) array { "w00t", 1234 }, + //raw "array3" = { "w00t", "dude" }, // syntax error + + "msg1" = message { "field" = "value" }, + message "msg2" = message(2000) { "field" = "value" }, + message "msg3" = (message) message(2000) { "field" = "value" }, + #'blah' message "msg4" = message { "field" = "value" } +// ,message(2000) "msg5" = message(2000) { "fld" = "val" } // not supported +}; + +// "Arrays" of fields, i.e. multiple fields with the same name. +resource(403) message('blah') +{ + "integer" = 1, + "integer" = 3, + "integer" = 2 +// ,"integer" = "hi there" // type conflict +}; + +// Tests whether the decompiler correctly outputs nested messages. +resource(404) message('blah') +{ + "sub1" = message('bla1') { "string1" = "value1" }, + "sub1" = message('bla2') { "string2" = "value2" }, + "sub1" = message('bla2') { "string2" = "value2", "string3" = "value3" }, + "SomeField" = 2, + "sub2" = message('bla3') { "string3" = "value3" } +}; + +// Another comma test for the decompiler. +resource(405) #'w00t' message('blah') +{ + "integer1" = 1, + "sub" = message { "string1" = "woot1" }, + "integer2" = 2 +}; + +// The decompiler should still recognize this as a message. +resource(406) #'RAWT' message { "field" = "value" }; + +//resource(407) (int32) message { "field" = "value" }; // invalid cast +//resource(407) (buffer) message { "field" = "value" }; // invalid cast + +// beres allows you to specify a what code inside the cast, +// but we don't (beres ignores it anyway): +//resource(407) (message('blah')) message { "field" = "value"; } + +// Decompiler turns the very long string into an array. +resource(408) message +{ + "longstring" = "alongstring-alongstringg\000-alongstringg-alongstringg-alongstringgalongstringg-alongstringg-alongstringg-alongstringg-alongstring", + "longarray" = array { $"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabb" }, + #'blah' "longblah" = array { $"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabb" }, + "textarray" = array { "pompiedom" } +}; + +// Note: negative "what" values are allowed, but the decompiler converts +// them to unsigned, just like type codes (which cannot be negative). +resource(409) message(-1) { "field" = "value" }; +resource(410) message(0x80000000) { "field" = "value" }; +resource(411) message(0x7FFFFFFF) { "field" = "value" }; +resource(412) message(-100001) { "field" = "value" }; +resource(413) #100001 message(-100001) { "field" = "value" }; +//resource(414) #-100001 message { "f" = "v" }; // syntax error + +resource(415) message { }; +resource(416) message; +resource(417) message('blah') { }; +resource(418) message('blah'); + +// Decompiles to an empty message. +resource(419) $"31424F460100001212000000000000000100"; + +resource(420) message('blah') { + "sub" = message('bla1') { "string1" = "woot1" }, + "sub" = message('bla2') { "string2" = "woot2" }, + int8 "small integer" = 1, + "integer" = 2, + "integer" = 3, + "large integer" = (int64)3, + "string" = "some text", + "sub2" = message('bla3') { "string3" = "woot3" } +}; + +resource(421) #'w00t' message('blah') { + "integer1" = 1, + "sub" = message('bla1') { "string1" = "woot1" }, + "integer2" = 2 +}; + +//------------------------------------------------------------------------------ + +resource(701) #'BBMP' archive BSomeClass { "data" = 123 }; +resource(702) archive BSomeClass { "data" = 123 }; +resource(703) archive("dynamic", 'blah') BSomeClass { "data" = 123 }; +resource(704) archive("dynamic") BSomeClass { "data" = 123 }; +resource(705) archive(, 200) BSomeClass { "data" = 123 }; +resource(706) (archive BSomeClass) archive BSomeClass { "data" = 123 }; +resource(707) archive(, -1) BSomeClass { "data" = 123 }; +resource(700) archive BSomeClass { "" = $"" }; // empty archive trick + +// When you cast an archive to a message, beres strips the +// "class" field from the message (so it will no longer be +// recognized as an archive), but we don't. +resource(708) (message) archive BSomeClass { "data" = 123 }; + +// beres allows you to specify the add_on name and what code +// inside the cast, but we don't (beres ignores it anyway). +//resource(709) (archive("t", 1) BSomeClass) archive BSomeClass { "d" = 123 }; + +//resource(709) (archive) archive BSomeClass { "d" = 123 }; // syntax error +//resource(709) archive("dynamic",) BSomeClass { "d" = 123 }; // syntax error + +//------------------------------------------------------------------------------ + +enum { + R_BBMP_kLargeNewGroupIcon = 516, + R_BBMP_kSmallNewGroupIcon = 517, +}; + +resource(R_BBMP_kLargeNewGroupIcon) #'BBMP' archive BBitmap { + "_frame" = rect { 0.0, 0.0, 31.0, 31.0 }, + "_cspace" = 4, + "_bmflags" = 1, + "_rowbytes" = 32, + "_data" = array { + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFF00FAFA0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFF00FAF8FAFA0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFF000000FAF8F8F8FAFA0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFF00FAFA0000F8F8F8F8F8FAFA00FFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFF00FAF8FAFA0000F8F8F8F8F800FFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFF00FAF8F8F8FAFA0000F8F8F800FFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFF00FAF8F8F8F8F8FAFA00F8F80000FFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFF001BF8F8F8F8F8F8F8000E0F1C1C0000FFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFF001B0F0F0FF8F8F8F8003F3F0E0F1C1C0000FFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFF001B0F3F3F0E0FF8F800003F3F3F0E0F1C1C0000FFFFFFFFFFFFFF" + $"FFFFFFFFFF001B0F3F3F3F3F0E0F1C1C00003F3F3F0E0F1A190000FFFFFFFFFF" + $"FFFFFFFFFF001B0F3F3F3F3F3F3F0E0F1C1C00003F3F3F0E1A1900FFFFFFFFFF" + $"FFFFFFFFFF001B0F3F3F3F3F3F3F3F3F0E0F1C1C00003F3F171900FFFFFFFFFF" + $"FFFFFFFFFF001B0F3F3F3F3F3F3F3F3F3F3F0E0F1A190000171900FFFFFFFFFF" + $"FFFFFFFFFF001B0F3F3F3F3F3F3F3F3F3F3F3F3F0E1A1900171900FFFFFFFFFF" + $"FFFFFFFFFF001B0F3F3F3F3F3F3F3F3F3F3F3F3F3F171900171900FFFFFFFFFF" + $"FFFFFFFFFF001B0F3F3F3F3F3F3F3F3F3F3F3F3F3F171900171900FFFFFFFFFF" + $"FFFFFFFFFF001B0F3F3F3F3F3F3F3F3F3F3F3F3F3F171900171900FFFFFFFFFF" + $"FFFFFFFFFF001B183F3F3F3F3F3F3F3F3F3F3F3F3F171900171900FFFFFFFFFF" + $"FFFFFFFFFF001B1C17183F3F3F3F3F3F3F3F3F3F3F171900171900FFFFFFFFFF" + $"FFFFFFFFFFFF00001B1C17183F3F3F3F3F3F3F3F3F171900171900FFFFFFFFFF" + $"FFFFFFFFFFFFFFFF0000191A17173F3F3F3F3F3F3F171900171900FFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFF0000191A17173F3F3F3F3F171900171A00FFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFF0000191A17173F3F3F1719001A1900FFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFF0000191A17173F171900001900FFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000191A17171A00FF0000FFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000191A1900FFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00001900FFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + } +}; + +resource(R_BBMP_kSmallNewGroupIcon) #'BBMP' archive BBitmap { + "_frame" = rect { 0.0, 0.0, 15.0, 15.0 }, + "_cspace" = 4, + "_bmflags" = 1, + "_rowbytes" = 16, + "_data" = array { + $"FFFFFFFF0E0EFFFFFFFFFFFFFFFFFFFFFFFFFF0EFAFA0E0EFFFFFFFFFFFFFFFF" + $"FFFF0E0EF8F8FAFA0EFFFFFFFFFFFFFFFFFF0EFAFA0EF8F80EFFFFFFFFFFFFFF" + $"FFFF0EF8F8FAFA0E0E0EFFFFFFFFFFFFFFFF0E1818F8F80E3F3F0E0EFFFFFFFF" + $"FFFF0E183F18180F0F3F3F0E12FFFFFFFFFF0E183F3F3F18180F0F0E12FFFFFF" + $"FFFF0E183F3F3F3F3F18180E12FFFFFFFFFF0E183F3F3F3F3F3F1C0E12FFFFFF" + $"FFFF0E0E181C3F3F3F3F1C0E12FFFFFFFFFFFFFF0E0E171C3F3F1C0E12FFFFFF" + $"FFFFFFFFFFFF0E0E171C1C0E12FFFFFFFFFFFFFFFFFFFFFF0E0E170E12FFFFFF" + $"FFFFFFFFFFFFFFFFFFFF0E0EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + } +}; diff --git a/src/tools/rc/tests/simple.rdef b/src/tools/rc/tests/simple.rdef new file mode 100644 index 0000000000..3113a92169 --- /dev/null +++ b/src/tools/rc/tests/simple.rdef @@ -0,0 +1,179 @@ + +resource(1) 100; +resource(1) "123"; // should be okay because types differ +//resource(1) 123; // duplicate resource 1 +//resource(0x100000001) 100; // duplicate resource 1 + +///* this is a /* nested */ comment */ + +resource(-1) 100; +resource(0xFFFFFFFE) 101; // -2 +resource(- 0x7FFFFFFF) 102; // -2147483647 +resource(-0x80000000) 103; // -2147483648 +resource( 0x80000001) "104"; // -2147483647 +resource(-0x80000001) 105; // 2147483647 +resource(-3, "name") 106; + +resource(10, "nom") "hello"; +resource(11, "") 123; // should ignore name + +//------------------------------------------------------------------------------ + +resource(20) true; +resource(21) false; +//resource(22) (int32) true; // invalid cast + +resource(30) 12345; +resource(31) (int32) 12345; +resource(32) (uint32) 12345; +resource(33) (size_t) 12345; +resource(34) (ssize_t) 12345; +resource(35) (time_t) 12345; +resource(36) (int64) 1234; +resource(37) (uint64) 1234; +resource(38) (off_t) 0xffaa; +resource(39) (int8) 12345; +resource(40) (uint8) 12345; +resource(41) (int16) 12345; +resource(42) (uint16) 12345; +resource(43) (float) 12345; +resource(44) (double) 12345; +resource(44) (raw) 12345; + +resource(45) -123; +resource(46) - 123; +resource(47) -0xFFFF; // -65535 +resource(48) -0xFFFFFFFF; // 1 +resource(49) -0x7FFFFFFF; // -2147483647 +resource(50) -0x80000000; // -2147483648 +resource(51) 0x80000001; // -2147483647 +resource(52) -0x80000001; // 2147483647 +resource(53) (uint64) -0xFFFFFFFF; // 18446744069414584321 +resource(54) -1; +resource(55) 'what'; // 2003329396 +resource(56) -'what'; // -2003329396 +resource(57) 0b1; +resource(58) 0b11111111111111111111111111111111; // -1 +resource(59) (uint64) 0b1111111111111111111111111111111111111111111111111111111111111111; // 18446744073709551615 +//resource(59) - -123; // syntax error + +resource(60) 10.0; +resource(61) (float) 10.0; +resource(62) (double) 10.0; +resource(63) -10.0; +resource(64) - 10.0; +resource(65) (raw) 10.0; +//resource(69) - -10.0; // syntax error + +resource(70) "fancy"; +resource(71) (string) "pants"; +resource(72) "w00t\0dude"; +resource(73) "fancy" "pants"; +resource(74) ""; +resource(75) "" "this" "wont" "" "scare" "" "me" ""; +resource(76) (raw) "fancy"; +//resource(79) "hi", "ho"; // , not allowed + +resource(80) $"4041"; +resource(81) (buffer) $"4041"; +resource(82) (raw) $"4041"; +resource(83) $"4041" $"4243"; +resource(84) $""; +resource(85) $"" $"40" $"" $"41" $""; +resource(86) (array) $"4041"; +//resource(89) $"0"; // too few digits +//resource(89) $"GA"; // invalid digit +//resource(89) $"FF", $"AA"; // , not allowed + +resource(90) (#'blah') $"4041"; +resource(91) (#'blah') (uint16) 1234; +resource(92) #'blah' 1234; +resource(93) #'blah' (uint16) 1234; +//resource(95) (#'blah' uint16) 1234; // we don't allow this notation + +//------------------------------------------------------------------------------ + +enum { + R_Boolean = 100, + R_Int8 = 100, + R_Character = 100, + R_String = 100, + R_Double = 100, + R_Float = 100, + R_Int64 = 100, + R_Int32 = 100, + R_MIMEString = 100, + R_Offset = 100, + R_Pointer = 100, + R_Raw = 100, + R_Int16 = 100, + R_Size = 100, + R_Status = 100, + R_Time = 100, + R_UInt8 = 100, + R_UInt64 = 100, + R_UInt32 = 100, + R_UInt16 = 100 +}; + +resource(R_Boolean) false; +resource(R_Int8) (int8)4; +resource(R_Character) (#'CHAR') $"28"; +resource(R_String) "Dance and song"; +resource(R_Double) (double)0.0; +resource(R_Float) 0.0; +resource(R_Int64) (int64)-125829120; +resource(R_Int32) -123450; +resource(R_MIMEString) (#'MIMS') "application/octet-stream"; +resource(R_Offset) (off_t)0; +resource(R_Pointer) (#'PNTR') $"00000000"; +resource(R_Raw) $""; +resource(R_Int16) (int16)-1234; +resource(R_Size) (size_t)0; +resource(R_Status) (ssize_t)0; +resource(R_Time) (time_t)0; +resource(R_UInt8) (uint8)200; +resource(R_UInt64) (uint64)17293822569102704640; +resource(R_UInt32) (uint32)251658240; +resource(R_UInt16) (uint16)61440; + +resource(120) $"40414243"; + +resource(121) (#200) + $"DE8765005507E15BC8033E2CD6BFB72620DD87F9780325D0BB2DAC7DAEE9AC9B" + $"6C7398DB05ADC01C5E41CBC0CCDA19DBE9FFEF7430BE3525C5FCBC419BB90000"; + +//------------------------------------------------------------------------------ + +// The decompile should escape the special characters, except \P, which +// is not special (the \ is dropped). Unlike deres, rc does not split the +// string literal into two parts at the newline. +resource(200) "a \tshort string\nwith \bplenty\fof\r\vescapes\P"; + +// More stuff that should be escaped. +resource(201) "some other \"special\" characters like \\ and \0x01 and \002"; + +// Decompiling should create a "#'CSTR' array" with two items, because the +// string contains an embedded null character. By the way, beres does not +// recognize \0 as null, but rc does. +resource(202) "alongstring-alongstringg\000-alongstringg-alongstringg-alongstringgalongstringg-alongstringg-alongstringg-alongstringg-alongstring"; + +// Don't do the following in your own resource files. It should not crash +// the compiler or decompiler, but pretending that non-string data is a +// null-terminated string is A Bad Thing (tm). +resource(210) (#'CSTR') $"4041"; +resource(212) (#'CSTR') (uint64) 5678; + +// Should decompile as raw, not as string. +resource(213) (#200) 100; + +// The decompiler sees this as a string literal. +resource(214) (#200) $"6400"; + +// The decompiler creates the symbolic names R_BOOL_MyName and R_CSTR_MyName, +// with values 215 and 216, respectively. +resource(215, "MyName") true; +resource(216, "MyName") "true"; + +// The decompiler creates the symbol R_200_MyName. +resource(217, "MyName") (#200) 123; diff --git a/src/tools/rc/tests/type.rdef b/src/tools/rc/tests/type.rdef new file mode 100644 index 0000000000..d1bdae3481 --- /dev/null +++ b/src/tools/rc/tests/type.rdef @@ -0,0 +1,223 @@ + +type #'smpl' simple { int8 x }; +resource(801) simple { 4 }; +resource(802) simple { 257 }; +resource(803) simple { (int64)-1 }; + +// Type codes do not have to be unique, so even though #'BYTE' is used +// for the int8 built-in type, we can also use it for our own types. +type #'BYTE' mybyte { int8 x }; +resource(805) mybyte { 4 }; +resource(806) mybyte { 257 }; +resource(807) mybyte { (int64)-1 }; +//resource(808) (mybyte) 123; // invalid cast +//resource(809) (mybyte) "yeah"; // invalid cast + +type #'RAWT' myraw { array x }; +resource(810) myraw { array { $"1234" } }; +resource(811) myraw { x = array { $"1234" } }; +resource(812) myraw { $"1234" }; +resource(813) (raw) myraw { $"1234" }; +resource(814) (myraw) myraw { $"1234" }; +resource(815) myraw; +//resource(816) (myraw) array { $"1234" }; // invalid cast + +// all resources become "0100000002000000000300" +type #'what' type1 { int32 a, int32 b, bool d, int16 e }; // size 11 +resource(810) type1 { 1, 2, false, 3 }; +resource(811) type1 { d = false, 1, 2, 3 }; +resource(812) type1 { d = false, e = 3, b = 2, 1 }; +resource(813) type1 { d = false, e = 3, 6, 7, a = 1, b = 2 }; +resource(814) type1 { 6, 7, false, 9, d = false, e = 3, a = 1, b = 2 }; + +type #'RECT' myrect { float left, float top, float bottom, float right }; + +// becomes { 1.00000, 2.00000, 3.00000, 4.00000 } +resource(820) myrect { 1, 2, 3, 4 }; + +// becomes { 1.00000, 2.00000, 3.00000, 4.00000 } +resource(821) myrect { left = 1, 2, 3, 4 }; + +// becomes { 3.00000, 2.00000, 4.00000, 5.00000 } +resource(822) myrect { 1, 2, left = 3, 4, 5 }; + +// becomes { 3.00000, 2.00000, 4.00000, 0 } +resource(823) myrect { 1, 2, left = 3, 4 }; + +// becomes { 2.00000, 1.00000, 3.00000, 4.00000 } +resource(824) myrect { top = 1, 2, 3, 4 }; + +// becomes { 1.00000, 0.00000, 0.00000, 0.00000 } +resource(825) myrect { 1 }; + +// becomes $"0000803F000000400000404000008040" +resource(826) (raw) myrect { 1, 2, 3, 4 }; + +// becomes { 1.00000, 2.00000, 3.00000, 4.00000 } +resource(827) #'RECT' (raw) myrect { 1, 2, 3, 4 }; +resource(828) #'RECT' $"0000803F000000400000404000008040"; + +//resource(829) myrect { left = 1, 2, 3, 4, 5 }; // too many fields +//resource(829) myrect { dude = 1, 2, 3, 4, 5 }; // unknown field +//resource(829) myrect { (float) top = 1, 2, 3, 4 }; // unknown type top + +enum { NoConflict = 100 }; +type #'RAWT' NoConflict { int32 a }; + +// Types are not identified by their type_code, but by their name, so the +// size of typ3 should be 5, even though typ1 and 2 have the same code. +type #'same' typ1 { int8 x }; +type #'same' typ2 { int32 x }; +type #'diff' typ3 { typ1 a, typ2 b }; +resource(830) typ3 { typ1 { 1 }, typ2 { 2 } }; +resource(831) typ3 { b = typ2 { 1 }, a = typ1 { 2 } }; +resource(832) typ3 { b = typ2 1, a = typ1 2 }; +resource(833) typ3 { typ1 1, typ2 2 }; +//resource(834) typ3 { typ2 { 1 }, typ1 { 2 } }; // invalid cast +//resource(834) typ3 { a = typ2 { 1 }, b = typ1 { 2 } }; // invalid cast +//resource(834) typ3 { a = { 1 }, b = { 2 } }; // invalid cast + +// Size of this type's data should still be 8, even though the default +// value for one of the fields is an int8. +type #'RAWT' rect2 { float a, float b = (int8) 12 }; +resource(835) rect2 { 1 }; + +type #'cmpd' compound { int16 a = 100, int16 b = 0x80 }; +type #'nest' nested { compound c = compound { 16, 32 }, int8 i = 4 }; +type #'nst2' nested2 { compound c, int8 i }; +resource(840) compound { }; +resource(841) compound; +resource(842) nested { compound { }, 1 }; +resource(843) nested2 { compound, 1 }; +resource(844) nested; +resource(845) nested2; + +type #'CSTR' strings { string a, string b = "yo" }; +resource(850) strings; +resource(851) strings { a = "long", b = "longer" }; +resource(852) strings { b = "long" }; +//resource(859) (string) strings { "long", "longer" }; // invalid cast + +type #'RAWT' msgs { archive BBitmap a }; +resource(860) msgs; +resource(861) msgs { a = archive BBitmap { "field" = "value" } }; + +type #'MSGG' msgs2 { message m = message('yeah') { "fld" = "val" } }; +resource(862) msgs2; + +// When you decompile these, it only sees the first message. +type #'RAWT' msgs3 { message m, archive BBitmap a }; +type #'zzzz' msgs4 +{ + message m = message { "f" = "v" }, + message n = message { "v" = "f" } +}; +resource(863) msgs3; +resource(864) msgs4; + +type #'zzzz' msgs5 { int8 x, message m, message n }; +resource(865) msgs5; +resource(866) msgs5 { 4, message('yoyo') { "f" = "v" } }; + +type #'what' onefield { int32 flags }; +resource(870) onefield 100; + +type ye_olde_raw { int32 a }; +resource(871) ye_olde_raw 64; + +//type #'what' sometype { UnKnOwN x }; // unknown type +//type #'what' B_INT8_TYPE { int8 x }; // duplicate type (built-in) +//type #'RECT' myrect { float x }; // duplicate type +//type #'RAWT' whoops { float x = "wrong" } // invalid cast +//type #'jump' jump { int8 x, jump j }; // unknown type jump + +//resource(890) tooSoon 123; // unknown type (must +//type #'soon' tooSoon { int32 x }; // be defined first) + +//resource(890) UnKnOwN { 123 }; // unknown type +//resource(890) (UnKnOwN) { 123 }; // unknown type +//resource(890) message('blah') { UnKnOwN x }; // unknown type +//resource(890) myraw { 123 }; // invalid cast +//resource(890) (myrect) { 123 }; // invalid cast + +enum { + R_Point = 100, + R_Rect = 100, + R_Color = 100, +}; + +resource(R_Point) point { -8.30573e+26, -1.0977e+37 }; +resource(880) point; + +resource(R_Rect) rect { 0.0, 0.0, -1.0, -1.0 }; +resource(881) rect; + +resource(R_Color) rgb_color { 0xaa, 0xbb, 0x88, 0x80 }; +resource(882) rgb_color; + +resource app_signature "application/x-vnd.vendor.appname"; +resource app_flags; + +resource app_version; + +resource large_icon array +{ + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +}; + +resource mini_icon array +{ + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + $"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +}; + +resource file_types message +{ + "types" = "text/plain", + "types" = "text/x-source-code" +};