mirror of https://github.com/digarok/gsplus.git
635 lines
12 KiB
Plaintext
635 lines
12 KiB
Plaintext
/*
|
|
*
|
|
* Load and parse GSBug templates and Nifty List Data.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
#include "defc.h"
|
|
|
|
|
|
#ifdef WIN32
|
|
char *strndup(const char *s, size_t maxlen) {
|
|
char *cp;
|
|
size_t l = strlen(s);
|
|
|
|
if (l > maxlen) l = maxlen;
|
|
cp = malloc(l + 1);
|
|
if (cp) {
|
|
memcpy(cp, s, l);
|
|
cp[l] = 0;
|
|
}
|
|
return cp;
|
|
}
|
|
#endif
|
|
|
|
/*!re2c
|
|
re2c:define:YYCTYPE = char;
|
|
re2c:define:YYCURSOR = cp;
|
|
re2c:yyfill:enable = 0;
|
|
eol = "\x00";
|
|
ws = [ \t\r\n];
|
|
x = [0-9a-fA-F];
|
|
*/
|
|
|
|
|
|
/* format
|
|
_START name
|
|
name [type [size]] *
|
|
_END
|
|
*/
|
|
|
|
|
|
struct field {
|
|
char *name;
|
|
int type;
|
|
int count;
|
|
};
|
|
|
|
struct record {
|
|
char *name;
|
|
int total_size; /* total size */
|
|
int field_count; /* # of fields */
|
|
int field_index; /* starting index into the fields array */
|
|
};
|
|
|
|
static struct record *records = NULL;
|
|
static int record_count = 0;
|
|
static int record_alloc = 0;
|
|
|
|
static struct field *fields = NULL;
|
|
static int field_count = 0;
|
|
static int field_alloc = 0;
|
|
|
|
static int record_compare(const void *a, const void *b) {
|
|
const struct record *aa = a;
|
|
const struct record *bb = b;
|
|
int rv;
|
|
rv = strcasecmp(aa->name, bb->name);
|
|
//if (rv == 0) return aa->total_size - bb->total_size;
|
|
return rv;
|
|
}
|
|
|
|
/* 192 records in the standard file... */
|
|
static void add_record(struct record *r) {
|
|
/* calc size and count */
|
|
int i;
|
|
|
|
r->field_count = field_count - r->field_index;
|
|
for (i = r->field_index; i < field_count; ++i) {
|
|
int size = fields[i].type;
|
|
if (size > 4) size = 4;
|
|
r->total_size += size * fields[i].count;
|
|
}
|
|
|
|
if (record_count == record_alloc) {
|
|
size_t size = (record_alloc + 256) * sizeof(struct record);
|
|
void *tmp = realloc(records, size);
|
|
if (!tmp) return; /* oops */
|
|
records = tmp;
|
|
record_alloc += 256;
|
|
}
|
|
records[record_count++] = *r;
|
|
}
|
|
|
|
|
|
/* average ~14 fields per record */
|
|
static void add_field(struct field *f) {
|
|
if (field_count == field_alloc) {
|
|
size_t size = (field_alloc + 512) * sizeof(struct field);
|
|
void *tmp = realloc(fields, size);
|
|
if (!tmp) return; /* oops */
|
|
fields = tmp;
|
|
field_alloc += 512;
|
|
}
|
|
fields[field_count++] = *f;
|
|
}
|
|
|
|
static char *rtrim(char *cp) {
|
|
int l = strlen(cp);
|
|
while (l && isspace(cp[l-1])) --l;
|
|
cp[l] = 0;
|
|
return cp;
|
|
}
|
|
|
|
static char *ltrim(char *cp) {
|
|
while (isspace(*cp)) ++cp;
|
|
return cp;
|
|
}
|
|
|
|
/* 0 - no field, 1 - ok, -1 - warning */
|
|
static int parse_field(const char *cp, struct field *f) {
|
|
const char *start;
|
|
const char *end;
|
|
const char *YYMARKER = NULL;
|
|
const char *YYCTXMARKER = NULL;
|
|
|
|
int type = 0;
|
|
int count = 0;
|
|
|
|
start = cp;
|
|
for(;;) {
|
|
/*!re2c
|
|
eol | ws { --cp; break; }
|
|
* { continue; }
|
|
*/
|
|
}
|
|
end = cp;
|
|
if (start == end) return 0;
|
|
|
|
f->name = strndup(start, end - start);
|
|
f->type = 0;
|
|
f->count = 0;
|
|
|
|
while (isspace(*cp)) ++cp;
|
|
|
|
/* optional type */
|
|
/*!re2c
|
|
"BYTE" / (ws | eol) { type = 1; goto next; }
|
|
"WORD" / (ws | eol) { type = 2; goto next; }
|
|
"LONG" / (ws | eol) { type = 4; goto next; }
|
|
"CSTRING" / (ws | eol) { type = 5; goto next; }
|
|
"PSTRING" / (ws | eol) { type = 6; goto next; }
|
|
"GSSTRING" / (ws | eol) { type = 7; goto next; }
|
|
"" { type = 0; goto next; }
|
|
*/
|
|
|
|
next:
|
|
while (isspace(*cp)) ++cp;
|
|
if (type) {
|
|
count = 0;
|
|
while(isdigit(*cp)) {
|
|
count = count * 10 + *cp - '0';
|
|
++cp;
|
|
}
|
|
if (!count) count = 1;
|
|
}
|
|
while (isspace(*cp)) ++cp;
|
|
if (*cp != 0) {
|
|
return -1;
|
|
}
|
|
f->type = type;
|
|
f->count = count;
|
|
return 1;
|
|
}
|
|
|
|
|
|
void debug_load_templates(const char *path) {
|
|
FILE *f;
|
|
char buffer[1024];
|
|
int ok;
|
|
int in_struct = 0;
|
|
struct record record;
|
|
struct field field;
|
|
unsigned line = 0;
|
|
const char *YYMARKER = NULL;
|
|
const char *YYCTXMARKER = NULL;
|
|
|
|
|
|
f = fopen(path, "r");
|
|
if (!f) {
|
|
fprintf(stderr, "Unable to load %s: %s\n", path, strerror(errno));
|
|
return;
|
|
}
|
|
for (line = 1;;++line) {
|
|
const char *start = NULL;
|
|
const char *end = NULL;
|
|
|
|
const char *cp = fgets(buffer, sizeof(buffer), f);
|
|
if (!cp) break;
|
|
rtrim(buffer);
|
|
|
|
/*!re2c
|
|
"" { goto _field; }
|
|
";" { continue; }
|
|
ws* eol { continue; }
|
|
"_END" / (ws | eol) { goto _end; }
|
|
"_START" / (ws | eol) { goto _start; }
|
|
*/
|
|
|
|
_field:
|
|
if (!in_struct) {
|
|
/* warn once */
|
|
fprintf(stderr, "%s:%d: Missing _START.\n", path, line);
|
|
memset(&record, 0, sizeof(record));
|
|
in_struct = -1;
|
|
}
|
|
if (in_struct < 0) continue;
|
|
|
|
memset(&field, 0, sizeof(field));
|
|
ok = parse_field(cp, &field);
|
|
if (ok == 0) continue;
|
|
if (ok < 1) {
|
|
/* warn but add */
|
|
fprintf(stderr, "%s:%d: Bad field line: %s\n", path, line, buffer);
|
|
}
|
|
add_field(&field);
|
|
continue;
|
|
|
|
_start:
|
|
if (in_struct) {
|
|
/* warn and close it */
|
|
if (in_struct > 0) add_record(&record);
|
|
}
|
|
memset(&record, 0, sizeof(record));
|
|
|
|
while (isspace(*cp)) ++cp;
|
|
start = cp;
|
|
for(;;) {
|
|
/*!re2c
|
|
eol | ws { --cp; break; }
|
|
* { continue; }
|
|
*/
|
|
}
|
|
end = cp;
|
|
if (start == end) {
|
|
/* warning ... */
|
|
fprintf(stderr, "%s:%d: _START missing name.\n", path, line);
|
|
in_struct = -1;
|
|
} else {
|
|
in_struct = 1;
|
|
record.name = strndup(start, end - start);
|
|
record.field_index = field_count;
|
|
}
|
|
|
|
while (isspace(*cp)) ++cp;
|
|
if (*cp != 0) {
|
|
/* warning */
|
|
fprintf(stderr, "%s:%d: Bad _START line: %s\n", path, line, buffer);
|
|
}
|
|
continue;
|
|
|
|
_end:
|
|
while (isspace(*cp)) ++cp;
|
|
if (*cp != 0) {
|
|
/* warning ... */
|
|
fprintf(stderr, "%s:%d: Bad _END line: %s\n", path, line, buffer);
|
|
}
|
|
|
|
if (in_struct) {
|
|
if (in_struct > 0) add_record(&record);
|
|
} else {
|
|
/* warning */
|
|
fprintf(stderr, "%s:%d: _END without _START.\n", path, line);
|
|
}
|
|
in_struct = 0;
|
|
memset(&record, 0, sizeof(record));
|
|
|
|
continue;
|
|
}
|
|
|
|
if (in_struct) {
|
|
/* warn & close */
|
|
fprintf(stderr, "%s:%d: Missing _END.\n", path, line);
|
|
if (in_struct > 0) add_record(&record);
|
|
}
|
|
fclose(f);
|
|
|
|
qsort(records, record_count, sizeof(struct record), record_compare);
|
|
}
|
|
|
|
static int record_search(const void *a, const void *b) {
|
|
const struct record *r = b;
|
|
return strcasecmp(a, r->name);
|
|
}
|
|
|
|
static void print_string(int type, word32 address) {
|
|
|
|
unsigned c;
|
|
unsigned length;
|
|
unsigned more = 0;
|
|
if (!address) return;
|
|
fputc('"', stdout);
|
|
|
|
|
|
switch(type) {
|
|
case 5:
|
|
length = 32;
|
|
break;
|
|
case 6:
|
|
length = get_memory_c(address, 0);
|
|
++address;
|
|
break;
|
|
case 7:
|
|
length = get_memory16_c(address, 0);
|
|
address += 2;
|
|
break;
|
|
}
|
|
if (length > 32) {
|
|
length = 32;
|
|
more = 1;
|
|
}
|
|
|
|
for (unsigned i = 0; i < length; ++i) {
|
|
c = get_memory_c(address++, 0);
|
|
if (type == 5 && c == 0) break;
|
|
|
|
if ((~c & 0x80) && isprint(c)) {
|
|
fputc(c, stdout);
|
|
} else fprintf(stdout, "\\x%02x", c);
|
|
}
|
|
|
|
if (type == 5 && c != 0) more = 1;
|
|
if (more) fputs("...", stdout);
|
|
|
|
fputc('"', stdout);
|
|
fputc(' ', stdout);
|
|
|
|
}
|
|
word32 debug_apply_template(word32 address, const char *name) {
|
|
/* 1 - lookup template */
|
|
struct record *r;
|
|
int i, j;
|
|
struct field *f;
|
|
|
|
r = bsearch(name, records, record_count, sizeof(struct record), record_search);
|
|
if (r == NULL) {
|
|
fprintf(stderr, "Invalid template: %s\n", name);
|
|
return address;
|
|
}
|
|
f = fields + r->field_index;
|
|
if (r->total_size == 0) {
|
|
/* just print the fields */
|
|
for (i = 0; i < r->field_count; ++i) {
|
|
fputs(f[i].name, stdout);
|
|
fputc('\n', stdout);
|
|
}
|
|
fputc('\n', stdout);
|
|
return address;
|
|
}
|
|
|
|
for (i = 0; i < r->field_count; ++i) {
|
|
word32 value;
|
|
unsigned type = f[i].type;
|
|
printf("%-16s", f[i].name);
|
|
for (j = 0; j < f[i].count; ++j) {
|
|
switch(type) {
|
|
case 1:
|
|
value = get_memory_c(address, 0);
|
|
address += 1;
|
|
printf("%02x ", value);
|
|
break;
|
|
case 2:
|
|
value = get_memory16_c(address, 0);
|
|
address += 2;
|
|
printf("%04x ", value);
|
|
break;
|
|
case 4:
|
|
case 5: /* cstring */
|
|
case 6: /* pstring */
|
|
case 7: /* gs/os string */
|
|
value = get_memory24_c(address, 0);
|
|
address += 4;
|
|
printf("%08x ", value);
|
|
if (type > 4) {
|
|
print_string(type, value);
|
|
}
|
|
break;
|
|
}
|
|
address &= 0xffffff;
|
|
}
|
|
fputc('\n', stdout);
|
|
}
|
|
fputc('\n', stdout);
|
|
return address;
|
|
}
|
|
|
|
|
|
|
|
struct tool {
|
|
char *name;
|
|
char *fullname;
|
|
unsigned number;
|
|
unsigned vector;
|
|
};
|
|
|
|
struct tool *tools = NULL;
|
|
int tool_count = 0;
|
|
int tool_alloc = 0;
|
|
|
|
|
|
|
|
struct tool *tool_names = NULL;
|
|
int tool_name_count = 0;
|
|
|
|
|
|
static void add_tool(struct tool *t) {
|
|
if (tool_count == tool_alloc) {
|
|
size_t size = (tool_alloc + 1024) * sizeof(struct tool);
|
|
void *tmp = realloc(tools, size);
|
|
if (!tmp) return;
|
|
tools = tmp;
|
|
tool_alloc += 1024;
|
|
}
|
|
tools[tool_count++] = *t;
|
|
}
|
|
|
|
|
|
static word32 to_hex(const char *iter, const char *end) {
|
|
word32 rv = 0;
|
|
while(iter != end) {
|
|
char c = *iter++;
|
|
rv <<= 4;
|
|
if (isdigit(c)) rv |= c - '0';
|
|
else rv |= (c | 0x20) - 'a' + 10;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
static int tool_compare(const void *a, const void *b) {
|
|
const struct tool *aa = a;
|
|
const struct tool *bb = b;
|
|
|
|
int rv = (int)aa->vector - (int)bb->vector;
|
|
if (rv == 0) rv = (int)aa->number - (int)bb->number;
|
|
return rv;
|
|
}
|
|
|
|
static int tool_name_compare(const void *a, const void *b) {
|
|
const struct tool *aa = a;
|
|
const struct tool *bb = b;
|
|
|
|
int rv = (int)aa->vector - (int)bb->vector;
|
|
if (rv == 0) rv = strcasecmp(aa->name, bb->name);
|
|
return rv;
|
|
}
|
|
|
|
|
|
/* nifty list */
|
|
/*
|
|
* format:
|
|
* fffx ... header comments
|
|
* xxxx name ; p8 mli calls
|
|
* *
|
|
* xxxx name ; p16/gsos calls
|
|
* *
|
|
* xxxx name ; tool calls
|
|
* *
|
|
* xxxx name ; user tool calls
|
|
* *
|
|
* xxxx name ; e1 vectors
|
|
* *
|
|
* xxxx name ; e0 vectors
|
|
* *
|
|
* xxxx name ; softswitch/f8 rom
|
|
* *
|
|
* xxxx name ; 01 vectors
|
|
* *
|
|
* xxxx name ; nifty list service calls
|
|
* *
|
|
* xxxx name ; resource types
|
|
* *
|
|
* xxxx name ; error codes
|
|
* *
|
|
* xxxx name ; HC IIgs callbacks
|
|
* *
|
|
* xxxx name ; request codes
|
|
* *
|
|
*
|
|
*/
|
|
|
|
void debug_load_nifty(const char *path) {
|
|
|
|
FILE *f;
|
|
char buffer[1024];
|
|
unsigned line = 0;
|
|
const char *YYMARKER = NULL;
|
|
const char *YYCTXMARKER = NULL;
|
|
|
|
int section = 0;
|
|
unsigned vector = 0;
|
|
static unsigned vectors[] = {
|
|
0xbf00, 0xe100a8, 0xe10000, 0xe10008, 0xe1, 0xe0, 0xff, 0x01,
|
|
0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
|
|
f = fopen(path, "r");
|
|
if (!f) {
|
|
fprintf(stderr, "Unable to load %s: %s\n", path, strerror(errno));
|
|
return;
|
|
}
|
|
for (line = 1;;++line) {
|
|
const char *start;
|
|
const char *end;
|
|
struct tool tool = { 0, 0, 0, vector };
|
|
|
|
const char *cp = fgets(buffer, sizeof(buffer), f);
|
|
if (!cp) break;
|
|
rtrim(buffer);
|
|
|
|
start = cp;
|
|
/*!re2c
|
|
[\r\n\x00] { continue; }
|
|
* {
|
|
fprintf(stderr, "%s:%d: Bad line: %s", path, line, buffer);
|
|
continue;
|
|
}
|
|
"*" {
|
|
++section;
|
|
vector = 0;
|
|
if (section < sizeof(vectors) / sizeof(vectors[0]))
|
|
vector = vectors[section];
|
|
continue;
|
|
}
|
|
x{4} / ws { goto ok; }
|
|
*/
|
|
ok:
|
|
end = cp;
|
|
tool.number = to_hex(start, end);
|
|
if (section == 0 && tool.number >= 0xfff0) continue;
|
|
if (!vector) continue;
|
|
|
|
while (isspace(*cp)) ++cp;
|
|
/* skip p8/p16/ prefix */
|
|
/*!re2c
|
|
"P8:" | "P16:" | "Shell:" | "GS/OS:" { goto prefix; }
|
|
"" { goto prefix; }
|
|
*/
|
|
prefix:
|
|
start = cp;
|
|
for(;;) {
|
|
/*!re2c
|
|
"" / eol { break; }
|
|
"(" | ws {
|
|
if (vector > 0x100 ) {
|
|
--cp;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
* { continue; }
|
|
*/
|
|
}
|
|
end = cp;
|
|
if (end > start) {
|
|
int l = end - start;
|
|
|
|
|
|
tool.fullname = NULL;
|
|
if (vector > 0x0100) {
|
|
/* add a leading _ */
|
|
tool.name = malloc(l + 2);
|
|
tool.name[0] = '_';
|
|
strncpy(tool.name + 1, start, l);
|
|
tool.name[l + 1] = 0;
|
|
tool.fullname = strdup(start);
|
|
}
|
|
else tool.name = strndup(start, l);
|
|
add_tool(&tool);
|
|
}
|
|
|
|
|
|
}
|
|
fclose(f);
|
|
|
|
qsort(tools, tool_count, sizeof(struct tool), tool_compare);
|
|
|
|
/* name index for tools and gs/os calls */
|
|
|
|
int tmp = 0;
|
|
int i;
|
|
for (i = 0; i < tool_count; ++i) {
|
|
unsigned v = tools[i].vector;
|
|
if (v == 0xe10000 || v == 0xe100a8 || v == 0xbf00)
|
|
++tmp;
|
|
}
|
|
if (!tmp) return;
|
|
tool_name_count = tmp;
|
|
tool_names = malloc(sizeof(struct tool) * tmp);
|
|
if (!tool_names) return;
|
|
tool_name_count = tmp;
|
|
tmp = 0;
|
|
for(i = 0; i < tool_count; ++i) {
|
|
unsigned v = tools[i].vector;
|
|
if (v == 0xe10000 || v == 0xe100a8 || v == 0xbf00)
|
|
tool_names[tmp++] = tools[i];
|
|
}
|
|
qsort(tool_names, tool_name_count, sizeof(struct tool), tool_name_compare);
|
|
}
|
|
|
|
const char *debug_tool_name(unsigned number, unsigned vector, unsigned full) {
|
|
|
|
if (!tool_count) return NULL;
|
|
|
|
struct tool tmp = { 0, 0, number, vector };
|
|
struct tool *t = bsearch(&tmp, tools, tool_count, sizeof(struct tool), tool_compare);
|
|
if (!t) return NULL;
|
|
if (full && t->fullname) return t->fullname;
|
|
return t->name;
|
|
}
|
|
|
|
|
|
int lookup_tool_number(const char *name, unsigned vector) {
|
|
|
|
if (!tool_name_count) return 0;
|
|
|
|
struct tool tmp = { (char *)name, 0, 0, vector};
|
|
struct tool *t = bsearch(&tmp, tool_names, tool_name_count, sizeof(struct tool), tool_name_compare);
|
|
if (!t) return 0;
|
|
return t->number;
|
|
}
|