mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2024-12-27 10:29:32 +00:00
Reimplement the Vectorize tool
This commit is contained in:
parent
4325cdcc78
commit
283a0c5ba8
696
Tools/ToolSource/MPWObjFuncs.c
Normal file
696
Tools/ToolSource/MPWObjFuncs.c
Normal file
@ -0,0 +1,696 @@
|
|||||||
|
#define C_PAD 0
|
||||||
|
#define C_FIRST 1
|
||||||
|
#define C_LAST 2
|
||||||
|
#define C_COMMENT 3
|
||||||
|
#define C_DICT 4
|
||||||
|
#define C_MODULE 5
|
||||||
|
#define C_ENTRYPT 6
|
||||||
|
#define C_SIZE 7
|
||||||
|
#define C_CONTENTS 8
|
||||||
|
#define C_REF 9
|
||||||
|
#define C_COMPREF 10
|
||||||
|
|
||||||
|
|
||||||
|
/* DATA STRUCTURES */
|
||||||
|
|
||||||
|
typedef unsigned char uchar;
|
||||||
|
typedef unsigned short ushort;
|
||||||
|
typedef unsigned long ulong;
|
||||||
|
typedef unsigned short id;
|
||||||
|
|
||||||
|
|
||||||
|
/* Ugh! */
|
||||||
|
#ifndef __GNUC__
|
||||||
|
#define ntohs
|
||||||
|
#define ntohl
|
||||||
|
#define htons
|
||||||
|
#define htonl
|
||||||
|
#endif
|
||||||
|
#define NULL 0
|
||||||
|
|
||||||
|
|
||||||
|
ushort shortfrom(char *b)
|
||||||
|
{
|
||||||
|
return ntohs(*(ushort *)b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setshort(char *b, ushort to)
|
||||||
|
{
|
||||||
|
*(ushort *)b = htons(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong longfrom(char *b)
|
||||||
|
{
|
||||||
|
return ntohl(*(ulong *)b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setlong(char *b, ulong to)
|
||||||
|
{
|
||||||
|
*(ulong *)b = htonl(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Wraps an MPW Object buffer */
|
||||||
|
struct obj
|
||||||
|
{
|
||||||
|
char *buf, *at;
|
||||||
|
id next_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* PASCAL STRING COMPARISON, CASE-INSENSITIVE */
|
||||||
|
|
||||||
|
int cisc(uchar *a, uchar *b)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(*a != *b) return 0;
|
||||||
|
|
||||||
|
for(i=1; i<=*a; i++)
|
||||||
|
{
|
||||||
|
uchar c, d;
|
||||||
|
c = a[i];
|
||||||
|
d = b[i];
|
||||||
|
// if(c >= 'a' && c <= 'z') c &= 223;
|
||||||
|
// if(d >= 'a' && d <= 'z') d &= 223;
|
||||||
|
if(c == d) continue;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void upperpas(uchar *a)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=1; i<=*a; i++)
|
||||||
|
{
|
||||||
|
if(a[i] >= 'a' && a[i] <= 'z') a[i] &= 223;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ACCESSOR FUNCTIONS */
|
||||||
|
|
||||||
|
/* Valid for all types */
|
||||||
|
char obj_type(struct obj *o)
|
||||||
|
{
|
||||||
|
return o->at[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Valid for First, Dict, Module, EntryPt, Size, Contents, Ref, CompRef */
|
||||||
|
char obj_flags(struct obj *o)
|
||||||
|
{
|
||||||
|
return o->at[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Valid for Module, EntryPt */
|
||||||
|
id obj_id(struct obj *o)
|
||||||
|
{
|
||||||
|
return ntohs(*(id *)(o->at + 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Valid for Dict */
|
||||||
|
id obj_dictfirst(struct obj *o)
|
||||||
|
{
|
||||||
|
return ntohs(*(id *)(o->at + 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Valid for Comment, Dict, Contents, Ref, CompRef */
|
||||||
|
unsigned short obj_recsize(struct obj *o)
|
||||||
|
{
|
||||||
|
return ntohs(*(unsigned short *)(o->at + 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Valid for Size */
|
||||||
|
unsigned short obj_contsize(struct obj *o)
|
||||||
|
{
|
||||||
|
return ntohs(*(unsigned long *)(o->at + 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Valid for all types */
|
||||||
|
unsigned long obj_size(struct obj *o)
|
||||||
|
{
|
||||||
|
char t;
|
||||||
|
|
||||||
|
t = obj_type(o);
|
||||||
|
|
||||||
|
if(t == C_COMMENT || t == C_DICT || t == C_CONTENTS || t == C_REF || t == C_COMPREF)
|
||||||
|
{
|
||||||
|
return obj_recsize(o);
|
||||||
|
}
|
||||||
|
else if(t == C_PAD)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if(t == C_FIRST)
|
||||||
|
{
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
else if(t == C_MODULE)
|
||||||
|
{
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
else if(t == C_ENTRYPT)
|
||||||
|
{
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
else if(t == C_SIZE)
|
||||||
|
{
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
else if(t == C_LAST)
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Illegal object type at offset 0x%x: %d\n", (ulong)o->at - (ulong)o->buf, t);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* CURSOR MANIPULATOR FUNCTIONS */
|
||||||
|
|
||||||
|
void obj_init(struct obj *o, char *buf)
|
||||||
|
{
|
||||||
|
o->at = o->buf = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _obj_next_general(struct obj *o, int can_loop)
|
||||||
|
{
|
||||||
|
if((long)o->at & 1) o->at ++;
|
||||||
|
|
||||||
|
if(obj_type(o) == C_LAST)
|
||||||
|
{
|
||||||
|
//printf("Got a lastie\n");
|
||||||
|
if(can_loop) o->at = o->buf;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//printf("Got a %d %d\n", obj_type(o), obj_size(o));
|
||||||
|
o->at += obj_size(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((long)o->at & 1) o->at ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gets the next object without looping */
|
||||||
|
void obj_next(struct obj *o)
|
||||||
|
{
|
||||||
|
_obj_next_general(o, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gets the specified module with looping */
|
||||||
|
void obj_findmod(struct obj *o, id mod_id)
|
||||||
|
{
|
||||||
|
char *first;
|
||||||
|
|
||||||
|
first = o->at;
|
||||||
|
|
||||||
|
while(obj_type(o) != C_MODULE || obj_id(o) != mod_id)
|
||||||
|
{
|
||||||
|
_obj_next_general(o, 1);
|
||||||
|
|
||||||
|
if(o->at == first)
|
||||||
|
{
|
||||||
|
o->at = o->buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gets the next dict with looping */
|
||||||
|
void dict_next(struct obj *o)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
printf("xx %d\n", obj_type(o));
|
||||||
|
_obj_next_general(o, 1);
|
||||||
|
}
|
||||||
|
while(obj_type(o) != C_DICT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void obj_pad(struct obj *o)
|
||||||
|
{
|
||||||
|
if((long)o->at & 1) *o->at++ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int obj_nextinmod(struct obj *o)
|
||||||
|
{
|
||||||
|
char *at = o->at;
|
||||||
|
|
||||||
|
if(obj_type(o) == C_LAST) return 0;
|
||||||
|
|
||||||
|
_obj_next_general(o, 0);
|
||||||
|
|
||||||
|
if(obj_type(o) == C_LAST || obj_type(o) == C_MODULE)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
o->at = at;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int obj_nextmod(struct obj *o)
|
||||||
|
{
|
||||||
|
char *at = o->at;
|
||||||
|
|
||||||
|
if(obj_type(o) == C_LAST) return 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
_obj_next_general(o, 0);
|
||||||
|
if(obj_type(o) == C_LAST) return 0;
|
||||||
|
} while(obj_type(o) != C_MODULE);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* DICT APPEND FUNCTION */
|
||||||
|
|
||||||
|
void dict_append_v(struct obj *o, uchar *s, id hint_id)
|
||||||
|
{
|
||||||
|
ushort oldlen;
|
||||||
|
|
||||||
|
if(obj_type(o) != C_DICT || shortfrom(o->at + 2) > 10000)
|
||||||
|
{
|
||||||
|
obj_next(o);
|
||||||
|
o->at[0] = C_DICT;
|
||||||
|
o->at[1] = 0; /* flags */
|
||||||
|
setshort(o->at+2, 6); /* size */
|
||||||
|
setshort(o->at+4, hint_id); /* first id */
|
||||||
|
}
|
||||||
|
|
||||||
|
oldlen = shortfrom(o->at + 2);
|
||||||
|
|
||||||
|
o->at[oldlen+0] = *s + 5;
|
||||||
|
o->at[oldlen+1] = '_';
|
||||||
|
o->at[oldlen+2] = '_';
|
||||||
|
o->at[oldlen+3] = 'v';
|
||||||
|
o->at[oldlen+4] = '_';
|
||||||
|
o->at[oldlen+5] = '_';
|
||||||
|
|
||||||
|
memcpy(o->at + oldlen + 6, s + 1, *s);
|
||||||
|
|
||||||
|
setshort(o->at + 2, oldlen + *s + 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* DICT SEARCH FUNCTIONS, WHICH HAVE SIDE EFFECTS */
|
||||||
|
|
||||||
|
id dict_id_from_str(struct obj *o, unsigned char *str)
|
||||||
|
{
|
||||||
|
char *first_searched = NULL;
|
||||||
|
|
||||||
|
//printf("DICT %x: %.*s = \n", o, *str, str+1);
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if(obj_type(o) == C_DICT)
|
||||||
|
{
|
||||||
|
unsigned int ctr, tlen;
|
||||||
|
unsigned char *ptr;
|
||||||
|
|
||||||
|
tlen = ntohs(*(unsigned short *)(o->at + 2));
|
||||||
|
ctr = ntohs(*(unsigned short *)(o->at + 4));
|
||||||
|
ptr = (uchar *)((long)o->at + 6);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
//if(ptr[0] == str[0] && !memcmp(ptr+1, str+1, str[0]))
|
||||||
|
if(cisc(ptr, str))
|
||||||
|
{
|
||||||
|
//printf("%d\n", ctr);
|
||||||
|
return ctr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr += ptr[0] + 1;
|
||||||
|
ctr++;
|
||||||
|
} while((ulong)ptr < (ulong)o->at + tlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
_obj_next_general(o, 1);
|
||||||
|
|
||||||
|
if(o->at == first_searched) return 0;
|
||||||
|
if(!first_searched) first_searched = o->at;
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("(not found)\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uchar *dict_str_from_id(struct obj *o, id id)
|
||||||
|
{
|
||||||
|
char *first_searched = NULL;
|
||||||
|
|
||||||
|
//printf("DICT %x: %d = ", o, id);
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if(obj_type(o) == C_DICT)
|
||||||
|
{
|
||||||
|
unsigned int ctr, tlen;
|
||||||
|
unsigned char *ptr;
|
||||||
|
|
||||||
|
tlen = ntohs(*(unsigned short *)(o->at + 2));
|
||||||
|
ctr = ntohs(*(unsigned short *)(o->at + 4));
|
||||||
|
ptr = (uchar *)((long)o->at + 6);
|
||||||
|
|
||||||
|
if(ctr <= id)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if(ctr == id)
|
||||||
|
{
|
||||||
|
//printf("%.*s\n", *ptr, ptr+1);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr += ptr[0] + 1;
|
||||||
|
ctr++;
|
||||||
|
} while((ulong)ptr < (ulong)o->at + tlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_obj_next_general(o, 1);
|
||||||
|
|
||||||
|
if(o->at == first_searched) return 0;
|
||||||
|
if(!first_searched) first_searched = o->at;
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("(not found)\n");
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
id dict_highestid(struct obj *o)
|
||||||
|
{
|
||||||
|
id ctr;
|
||||||
|
unsigned int tlen;
|
||||||
|
unsigned char *ptr;
|
||||||
|
|
||||||
|
tlen = ntohs(*(unsigned short *)(o->at + 2));
|
||||||
|
ctr = (id)ntohs(*(unsigned short *)(o->at + 4));
|
||||||
|
ptr = (uchar *)((long)o->at + 6);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ptr += ptr[0] + 1;
|
||||||
|
ctr++;
|
||||||
|
} while((ulong)ptr < (ulong)o->at + tlen);
|
||||||
|
|
||||||
|
return ctr - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* NEWDICT FUNCTIONS */
|
||||||
|
|
||||||
|
struct newdict
|
||||||
|
{
|
||||||
|
struct newdict *prev, *next; /* linked list */
|
||||||
|
|
||||||
|
id new_id; /* increments from 256 */
|
||||||
|
id old_id;
|
||||||
|
id patch_id; /* if this ID is just vector glue and self.next is the implementation */
|
||||||
|
uchar name[256]; /* duh */
|
||||||
|
|
||||||
|
int defining_file;
|
||||||
|
int malloc_size; /* only for the main dict object */
|
||||||
|
int fcnt;
|
||||||
|
struct newdict ***cache;
|
||||||
|
//id id_in_file[99]; /* malloc makes this array *just right* */
|
||||||
|
};
|
||||||
|
|
||||||
|
void newdict_init(struct newdict *d, int file_count)
|
||||||
|
{
|
||||||
|
d->prev = d->next = d;
|
||||||
|
d->new_id = 255;
|
||||||
|
d->fcnt = file_count;
|
||||||
|
d->malloc_size = sizeof *d; //sizeof(*d) + (file_count - 99) * sizeof(d->id_in_file[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void newdict_cache(struct newdict *d)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct newdict *s;
|
||||||
|
|
||||||
|
d->cache = (struct newdict ***)calloc(d->fcnt, sizeof d->cache[0]);
|
||||||
|
|
||||||
|
for(i=0; i<d->fcnt; i++)
|
||||||
|
{
|
||||||
|
d->cache[i] = (struct newdict **)calloc(65536, sizeof d->cache[0][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(s = d->next; s != d; s = s->next)
|
||||||
|
{
|
||||||
|
d->cache[s->defining_file][s->old_id] = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
id newdict_append(struct newdict *d, int file_idx, id orig_id, uchar *name)
|
||||||
|
{
|
||||||
|
struct newdict *s;
|
||||||
|
|
||||||
|
s = (struct newdict *)calloc(d->malloc_size, 1);
|
||||||
|
if(!s)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->next = d;
|
||||||
|
s->prev = d->prev;
|
||||||
|
s->prev->next = s;
|
||||||
|
d->prev = s;
|
||||||
|
|
||||||
|
s->new_id = s->prev->new_id + 1;
|
||||||
|
memcpy(s->name, name, name[0]+1);
|
||||||
|
|
||||||
|
s->defining_file = file_idx;
|
||||||
|
//s->id_in_file[file_idx] = orig_id;
|
||||||
|
s->old_id = orig_id;
|
||||||
|
|
||||||
|
return s->new_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct newdict *newdict_find(struct newdict *d, int file_idx, id mid)
|
||||||
|
{
|
||||||
|
// struct newdict *s;
|
||||||
|
|
||||||
|
// for(s = d->next; s != d; s = s->next)
|
||||||
|
// {
|
||||||
|
// if(s->id_in_file[file_idx] == mid) return s;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return (struct newdict *)0;
|
||||||
|
|
||||||
|
return d->cache[file_idx][mid];
|
||||||
|
}
|
||||||
|
|
||||||
|
// void newdict_debug_dump(struct newdict *d, char **file_names)
|
||||||
|
// {
|
||||||
|
// struct newdict *s;
|
||||||
|
// int i;
|
||||||
|
|
||||||
|
// printf("NEWDICT_DEBUG_DUMP\n");
|
||||||
|
|
||||||
|
// for(s = d->next; s != d; s = s->next)
|
||||||
|
// {
|
||||||
|
// printf(" %d %.*s", s->new_id, *s->name, s->name+1);
|
||||||
|
|
||||||
|
// if(s->defining_file != -1)
|
||||||
|
// {
|
||||||
|
// if(file_names)
|
||||||
|
// {
|
||||||
|
// printf(" (%s:%d)", file_names[s->defining_file], s->id_in_file[s->defining_file]);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// printf(" (file_%d:%d)", s->defining_file, s->id_in_file[s->defining_file]);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if(s->patch_id)
|
||||||
|
// {
|
||||||
|
// printf(" (patch ID %d, next line is implementation:)", s->patch_id);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// printf("\n %d:%d", s->defining_file, s->id_in_file[s->defining_file]);
|
||||||
|
|
||||||
|
// for(i=0; i<d->fcnt; i++)
|
||||||
|
// {
|
||||||
|
// if(i == s->defining_file) continue;
|
||||||
|
// if(!s->id_in_file[i]) continue;
|
||||||
|
// printf(" %d:%d", i, s->id_in_file[i]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// printf("\n");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
unsigned long newdict_dump(struct newdict *d, char *o)
|
||||||
|
{
|
||||||
|
unsigned long l = 0;
|
||||||
|
struct newdict *s = d->next;
|
||||||
|
|
||||||
|
while(s != d)
|
||||||
|
{
|
||||||
|
long tl = 0;
|
||||||
|
|
||||||
|
o[l] = C_DICT;
|
||||||
|
o[l+1] = 0; /* 0 => names needed for code/data relocation.
|
||||||
|
1 => names needed for symbolic debugging only
|
||||||
|
(in which case they are ignored when doing a
|
||||||
|
non-symbolic link) */
|
||||||
|
|
||||||
|
*(ushort *)(o + l + 4) = htons(s->new_id);
|
||||||
|
|
||||||
|
tl = 6;
|
||||||
|
|
||||||
|
while(s != d && tl < 10000)
|
||||||
|
{
|
||||||
|
memcpy(o + l + tl, s->name, s->name[0]+1);
|
||||||
|
tl += s->name[0]+1;
|
||||||
|
s = s->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* doing the record! */
|
||||||
|
*(id *)(o + l + 2) = htons(tl);
|
||||||
|
|
||||||
|
if(tl & 1) o[l + tl++] = 0;
|
||||||
|
|
||||||
|
l += tl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// id newdict_lookup_backend(struct newdict *d, uchar *str, long major, long minor)
|
||||||
|
// {
|
||||||
|
// struct newdict *s;
|
||||||
|
|
||||||
|
// for(s = d->prev; s != d; s = s->prev)
|
||||||
|
// {
|
||||||
|
// if(major == -1 || minor == -1 || (s->major == major && s->minor == minor))
|
||||||
|
// {
|
||||||
|
// if(cisc(str, s->str))
|
||||||
|
// {
|
||||||
|
// return s->n;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// id newdict_id_from_str_with_hint(struct newdict *d, uchar *str, long major, long minor)
|
||||||
|
// {
|
||||||
|
// id id;
|
||||||
|
|
||||||
|
// id = newdict_lookup_backend(d, str, major, minor);
|
||||||
|
// if(id) return id;
|
||||||
|
|
||||||
|
// id = newdict_lookup_backend(d, str, -1, -1);
|
||||||
|
// if(id) return id;
|
||||||
|
|
||||||
|
// id = newdict_append(d, str, major, minor);
|
||||||
|
// return id;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* COMPLETE ODDBALL */
|
||||||
|
|
||||||
|
// void reref(char **dest, long major, struct newdict *destdict, struct obj *src, struct obj *srcdict, id specialfrom, id specialto)
|
||||||
|
// {
|
||||||
|
// const char OFFSETS[11][3] = {
|
||||||
|
// /* 0*/ {0},
|
||||||
|
// /* 1*/ {0},
|
||||||
|
// /* 2*/ {0},
|
||||||
|
// /* 3*/ {0},
|
||||||
|
// /* 4*/ {0},
|
||||||
|
// /* 5*/ {2,4,0},
|
||||||
|
// /* 6*/ {2,0},
|
||||||
|
// /* 7*/ {0},
|
||||||
|
// /* 8*/ {0},
|
||||||
|
// /* 9*/ {4,0},
|
||||||
|
// /*10*/ {4,6,0},
|
||||||
|
// };
|
||||||
|
|
||||||
|
// unsigned long len;
|
||||||
|
// char t;
|
||||||
|
// int i;
|
||||||
|
|
||||||
|
// len = obj_size(src);
|
||||||
|
// t = obj_type(src);
|
||||||
|
|
||||||
|
// memcpy(*dest, src->at, len);
|
||||||
|
|
||||||
|
// for(i=0; OFFSETS[t][i]; i++)
|
||||||
|
// {
|
||||||
|
// id is;
|
||||||
|
|
||||||
|
// is = ntohs(*(id *)(*dest + OFFSETS[t][i]));
|
||||||
|
|
||||||
|
// //printf("Converting: type %d offset %d original %d special %d->%d\n", t, OFFSETS[t][i], is, specialfrom, specialto);
|
||||||
|
|
||||||
|
// if(is == specialfrom)
|
||||||
|
// {
|
||||||
|
// is = specialto;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// uchar *str = dict_str_from_id(srcdict, is);
|
||||||
|
// if(!str) printf("Bullshit string\n");
|
||||||
|
// //printf("... intermediate string %x %.*s\n", str, *str, str+1);
|
||||||
|
// is = newdict_id_from_str_with_hint(destdict, str, major, is);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //printf("... to %d\n", is);
|
||||||
|
|
||||||
|
// *(id *)(*dest + OFFSETS[t][i]) = htons(is);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// *dest += len;
|
||||||
|
|
||||||
|
// if((long)(*dest) & 1) *(*dest)++ = 0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
void obj_copy(struct obj *dest, struct obj *src)
|
||||||
|
{
|
||||||
|
char *destthing;
|
||||||
|
|
||||||
|
obj_next(dest);
|
||||||
|
destthing = dest->at;
|
||||||
|
|
||||||
|
memcpy(destthing, src->at, obj_size(src));
|
||||||
|
|
||||||
|
obj_next(dest);
|
||||||
|
obj_pad(dest);
|
||||||
|
dest->at = destthing;
|
||||||
|
}
|
||||||
|
|
||||||
|
int obj_islastinmod(struct obj *o)
|
||||||
|
{
|
||||||
|
struct obj j;
|
||||||
|
char t;
|
||||||
|
|
||||||
|
memcpy(&j, o, sizeof (struct obj));
|
||||||
|
|
||||||
|
obj_next(&j);
|
||||||
|
t = obj_type(&j);
|
||||||
|
|
||||||
|
return t == C_MODULE || t == C_LAST;
|
||||||
|
}
|
12
Tools/ToolSource/Tools.make
Normal file
12
Tools/ToolSource/Tools.make
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
LibFiles-68K = ¶
|
||||||
|
"{Libraries}Stubs.o" ¶
|
||||||
|
"{CLibraries}StdCLib.o" ¶
|
||||||
|
"{Libraries}MacRuntime.o" ¶
|
||||||
|
"{Libraries}IntEnv.o" ¶
|
||||||
|
"{Libraries}Interface.o" ¶
|
||||||
|
|
||||||
|
"{ToolSrcDir}Vectorize.c.o" Ä "{ToolSrcDir}Vectorize.c" "{ToolSrcDir}MPWObjFuncs.c"
|
||||||
|
SC -o {Targ} "{ToolSrcDir}Vectorize.c"
|
||||||
|
|
||||||
|
"{ToolDir}Vectorize" Ä "{ToolSrcDir}Vectorize.c.o"
|
||||||
|
ILink -d -t 'MPST' -c 'MPS ' -o {Targ} {LibFiles-68k} "{ToolSrcDir}Vectorize.c.o"
|
787
Tools/ToolSource/Vectorize.c
Normal file
787
Tools/ToolSource/Vectorize.c
Normal file
@ -0,0 +1,787 @@
|
|||||||
|
// This tool will be implemented using only standard C, the better
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "MPWObjFuncs.c"
|
||||||
|
|
||||||
|
#ifndef __GNUC__
|
||||||
|
#include <Finder.h>
|
||||||
|
#include <Files.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ERRSTR "# Vectorize Error: "
|
||||||
|
#define WARNSTR "# Vectorize Warning: "
|
||||||
|
|
||||||
|
// Default options
|
||||||
|
|
||||||
|
const char PASSIVE_OFFSETS[11][3] = {
|
||||||
|
/* 0*/ {0},
|
||||||
|
/* 1*/ {0},
|
||||||
|
/* 2*/ {0},
|
||||||
|
/* 3*/ {0},
|
||||||
|
/* 4*/ {0},
|
||||||
|
/* 5*/ {2,0},
|
||||||
|
/* 6*/ {2,0},
|
||||||
|
/* 7*/ {0},
|
||||||
|
/* 8*/ {0},
|
||||||
|
/* 9*/ {4,0},
|
||||||
|
/*10*/ {4,6,0},
|
||||||
|
};
|
||||||
|
|
||||||
|
const char OFFSETS[11][3] = {
|
||||||
|
/* 0*/ {0},
|
||||||
|
/* 1*/ {0},
|
||||||
|
/* 2*/ {0},
|
||||||
|
/* 3*/ {0},
|
||||||
|
/* 4*/ {4,0},
|
||||||
|
/* 5*/ {2,4,0},
|
||||||
|
/* 6*/ {2,0},
|
||||||
|
/* 7*/ {0},
|
||||||
|
/* 8*/ {0},
|
||||||
|
/* 9*/ {4,0},
|
||||||
|
/*10*/ {4,6,0},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int warnings = 1;
|
||||||
|
char *patchObjPath = NULL;
|
||||||
|
char *symFilePath = NULL;
|
||||||
|
int do_patch = 1;
|
||||||
|
char *outputObjPath = NULL;
|
||||||
|
char **inputPaths = NULL;
|
||||||
|
int inputCount = 0;
|
||||||
|
|
||||||
|
void to_pas(uchar *p, char *c)
|
||||||
|
{
|
||||||
|
int sl = strlen(c);
|
||||||
|
p[0] = sl;
|
||||||
|
memcpy(p+1, c, sl);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *fname(char *x)
|
||||||
|
{
|
||||||
|
char *y = x;
|
||||||
|
|
||||||
|
while(*y) y++;
|
||||||
|
|
||||||
|
while(y != x && y[-1] != ':') y--;
|
||||||
|
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *slurp(char *path)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
long pos;
|
||||||
|
char *bytes;
|
||||||
|
|
||||||
|
f = fopen(path, "rb");
|
||||||
|
if(f == NULL) return NULL;
|
||||||
|
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
pos = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
|
||||||
|
bytes = (char *)malloc(pos);
|
||||||
|
if(bytes == NULL) return NULL;
|
||||||
|
|
||||||
|
fread(bytes, pos, 1, f);
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blat(char *path, char *buf, ulong len)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
f = fopen(path, "wb");
|
||||||
|
if(f == NULL) return 1;
|
||||||
|
|
||||||
|
fwrite(buf, len, 1, f);
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printUsage(char *cmdName)
|
||||||
|
{
|
||||||
|
printf("USAGE: %s [-w] [-v PATCHES.o | -nopatch] [-log ROM.sym] -o ROM.lib INPUT1.o ...\n", cmdName);
|
||||||
|
}
|
||||||
|
|
||||||
|
int parseOpts(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
for(i=1; i<argc; i++)
|
||||||
|
{
|
||||||
|
if(!strcmp(argv[i], "-w"))
|
||||||
|
{
|
||||||
|
warnings = 0;
|
||||||
|
}
|
||||||
|
else if(!strcmp(argv[i], "-nopatch"))
|
||||||
|
{
|
||||||
|
do_patch = 0;
|
||||||
|
}
|
||||||
|
else if(!strcmp(argv[i], "-v"))
|
||||||
|
{
|
||||||
|
if(i == argc-1)
|
||||||
|
{
|
||||||
|
err = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
patchObjPath = argv[i+1];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if(!strcmp(argv[i], "-log"))
|
||||||
|
{
|
||||||
|
if(i == argc-1)
|
||||||
|
{
|
||||||
|
err = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
symFilePath = argv[i+1];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if(!strcmp(argv[i], "-o"))
|
||||||
|
{
|
||||||
|
if(i == argc-1)
|
||||||
|
{
|
||||||
|
err = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputObjPath = argv[i+1];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if(argv[i][0] == '-')
|
||||||
|
{
|
||||||
|
err = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inputPaths = argv + i;
|
||||||
|
inputCount = argc - i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report lexical errors (from the loop)
|
||||||
|
if(err == 2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "%s is an unknown argument.", argv[i]);
|
||||||
|
}
|
||||||
|
else if(err == 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "%s cannot be the last argument.", argv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for semantic errors (basic sanity checks)
|
||||||
|
if(!err)
|
||||||
|
{
|
||||||
|
if(do_patch && patchObjPath == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "An MPW object containing patches must be specified with -v, unless -nopatch is used.\n");
|
||||||
|
err = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(outputObjPath == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "An output file must be specified with -o.\n");
|
||||||
|
err = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(inputCount == 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "At least one input file must be specified.\n");
|
||||||
|
err = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(err)
|
||||||
|
{
|
||||||
|
printUsage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct tblent
|
||||||
|
{
|
||||||
|
uchar *name; /* without __v__ */
|
||||||
|
char *data;
|
||||||
|
long len;
|
||||||
|
id __v__id;
|
||||||
|
id clean_id;
|
||||||
|
long offset_of_parent_in_module; /* bit of a hack, for sorting the patchstack */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int pstrcmp(const void *aa, const void *bb)
|
||||||
|
{
|
||||||
|
const unsigned char *a = (const unsigned char *)aa;
|
||||||
|
const unsigned char *b = (const unsigned char *)bb;
|
||||||
|
|
||||||
|
|
||||||
|
int cl = *a;
|
||||||
|
int r;
|
||||||
|
if(*b < cl) cl = *b;
|
||||||
|
|
||||||
|
//printf("Comparing %.*s to %.*s\n", *a, a+1, *b, b+1);
|
||||||
|
|
||||||
|
r = memcmp(a+1, b+1, cl);
|
||||||
|
|
||||||
|
if(r) return r;
|
||||||
|
|
||||||
|
if(*b > *a) return -1;
|
||||||
|
|
||||||
|
if(*b < *a) return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tblcmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return pstrcmp(((struct tblent *)a)->name, ((struct tblent *)b)->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ptch_order_cmp(const void *aa, const void *bb)
|
||||||
|
{
|
||||||
|
long a = (**(struct tblent **)aa).offset_of_parent_in_module;
|
||||||
|
long b = (**(struct tblent **)bb).offset_of_parent_in_module;
|
||||||
|
|
||||||
|
if(a > b) return -1;
|
||||||
|
if(a < b) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// int tblcmp2(const void *a, const void *b)
|
||||||
|
// {
|
||||||
|
// int res;
|
||||||
|
// printf("Comparing %.*s to %.*s", *((struct tblent *)a)->name, ((struct tblent *)a)->name+1, *((struct tblent *)b)->name, ((struct tblent *)b)->name+1);
|
||||||
|
// res = pstrcmp(((struct tblent *)a)->name, ((struct tblent *)b)->name);
|
||||||
|
// if(!res) printf(" ...match!");
|
||||||
|
// printf("\n");
|
||||||
|
// return res;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// void print_tbl(struct tblent *tbl, long cnt)
|
||||||
|
// {
|
||||||
|
// long i;
|
||||||
|
|
||||||
|
// for(i=0; i<cnt; i++)
|
||||||
|
// {
|
||||||
|
// printf("%05d = %.*s\n", tbl[i].id, *(tbl[i].name), (tbl[i].name)+1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
void debug_print_about_patch(FILE *l, char *data, long len)
|
||||||
|
{
|
||||||
|
int reg;
|
||||||
|
unsigned short vtbl, voff;
|
||||||
|
|
||||||
|
if(!data)
|
||||||
|
{
|
||||||
|
reg = -1;
|
||||||
|
}
|
||||||
|
else if(len != 10)
|
||||||
|
{
|
||||||
|
reg = -2;
|
||||||
|
}
|
||||||
|
else if(data[0] == 0x2f && data[1] == 0x30 && (uchar)data[2] == 0x81 && (uchar)data[3] == 0xe2 && data[8] == 0x4e && data[9] == 0x75)
|
||||||
|
{
|
||||||
|
reg = 7;
|
||||||
|
vtbl = shortfrom(data + 4);
|
||||||
|
voff = shortfrom(data + 6);
|
||||||
|
}
|
||||||
|
else if(data[0] == 0x22 && data[4] == 0x22 && data[8] == 0x4e) /* dirty shortcut */
|
||||||
|
{
|
||||||
|
reg = data[9] & 7;
|
||||||
|
vtbl = shortfrom(data + 2);
|
||||||
|
voff = shortfrom(data + 6);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reg = -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(reg == -2)
|
||||||
|
{
|
||||||
|
fprintf(l, "UNKNOWN %db PATCH", len);
|
||||||
|
}
|
||||||
|
else if(reg == -1)
|
||||||
|
{
|
||||||
|
fprintf(l, "no patch");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(l, "patch $%04x,$%04x,A%d", vtbl, voff, reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct obj dest;
|
||||||
|
struct tblent *tbl;
|
||||||
|
long tblcnt = 0;
|
||||||
|
int delta;
|
||||||
|
int file_i;
|
||||||
|
FILE *l = NULL; /* log file */
|
||||||
|
int isfirst = 1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* GET OUR COMMAND LINE */
|
||||||
|
if(parseOpts(argc, argv)) return 1;
|
||||||
|
|
||||||
|
if(NULL != symFilePath)
|
||||||
|
{
|
||||||
|
l = fopen(symFilePath, "w");
|
||||||
|
if(!l)
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "Failed to open sym file %s\n", symFilePath);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l)
|
||||||
|
{
|
||||||
|
fprintf(l, " # # ####### ##### ####### ####### ###### ### ####### ####### \n");
|
||||||
|
fprintf(l, " # # # # # # # # # # # # # \n");
|
||||||
|
fprintf(l, " # # # # # # # # # # # # \n");
|
||||||
|
fprintf(l, " # # ##### # # # # ###### # # ##### \n");
|
||||||
|
fprintf(l, " # # # # # # # # # # # # \n");
|
||||||
|
fprintf(l, " # # # # # # # # # # # # # \n");
|
||||||
|
fprintf(l, " # ####### ##### # ####### # # ### ####### ####### \n");
|
||||||
|
fprintf(l, "\n");
|
||||||
|
fprintf(l, "# This log file is formatted as an MPW worksheet, to help you troubleshoot\n");
|
||||||
|
fprintf(l, "# your build.\n");
|
||||||
|
fprintf(l, "\n");
|
||||||
|
fprintf(l, "# (Fake)Vectorize attempts to replace a lost Apple build tool. It is mostly\n");
|
||||||
|
fprintf(l, "# based on patent US5546586, \"Method and apparatus for vectorizing the contents\n");
|
||||||
|
fprintf(l, "# of a read only memory device without modifying underlying source code\".\n");
|
||||||
|
|
||||||
|
fprintf(l, "\n");
|
||||||
|
|
||||||
|
if(do_patch)
|
||||||
|
{
|
||||||
|
fprintf(l, "# Dump the input file containing \"vector patches\":\n");
|
||||||
|
fprintf(l, " Set PatchDump \"{TempFolder}VectorPatchDump\"; DumpObj \"%s\" > \"{PatchDump}\"\n\n", patchObjPath);
|
||||||
|
fprintf(l, " # and navigate its contents:\n");
|
||||||
|
fprintf(l, " Find /NAVIGATE PATCHES/ \"{Active}\"\n");
|
||||||
|
|
||||||
|
fprintf(l, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(l, "# Dump the \"vectorized\" output file: (takes a while)\n");
|
||||||
|
fprintf(l, " Set RomDump \"{TempFolder}VectorRomDump\"; DumpObj \"%s\" > \"{RomDump}\"\n", outputObjPath);
|
||||||
|
fprintf(l, "\n");
|
||||||
|
fprintf(l, " # and look at it:\n");
|
||||||
|
fprintf(l, " File \"{RomDump}\"\n");
|
||||||
|
fprintf(l, "\n");
|
||||||
|
fprintf(l, " # or navigate it input-file-wise:\n");
|
||||||
|
fprintf(l, " Find /NAVIGATE OUTPUT/ \"{Active}\"\n");
|
||||||
|
for(file_i=0; file_i<inputCount; file_i++)
|
||||||
|
{
|
||||||
|
fprintf(l, " Find /¶\"%s¶\"/ \"{Active}\"\n", inputPaths[file_i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(l, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
dest.buf = dest.at = calloc(0x400000, 1);
|
||||||
|
if(!dest.buf)
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "Failed to calloc 4MB dest buffer\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a First record (remember that MPW objects are fully ascending) */
|
||||||
|
dest.at[0] = 1;
|
||||||
|
dest.at[1] = 1;
|
||||||
|
dest.at[2] = 0;
|
||||||
|
dest.at[3] = 2;
|
||||||
|
|
||||||
|
if(do_patch) /* PROCESS THE PATCH FILE (and keep it around because we use it for strings) */
|
||||||
|
{
|
||||||
|
char *patch_bin;
|
||||||
|
struct obj patch_obj, patch_dict;
|
||||||
|
|
||||||
|
patch_bin = slurp(patchObjPath);
|
||||||
|
if(!patch_bin)
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "Failed to slurp patch file\n");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbl = (struct tblent *)calloc(5000, sizeof (struct tblent));
|
||||||
|
if(!tbl)
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "Failed to malloc tbl\n");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l) fprintf(l, "############################# NAVIGATE PATCHES #############################\n");
|
||||||
|
if(l) fprintf(l, "# (These command lines are a bit clumsy. Oh well...)\n\n");
|
||||||
|
|
||||||
|
obj_init(&patch_obj, patch_bin);
|
||||||
|
obj_init(&patch_dict, patch_bin);
|
||||||
|
|
||||||
|
while(obj_nextmod(&patch_obj))
|
||||||
|
{
|
||||||
|
struct obj scratch;
|
||||||
|
|
||||||
|
uchar *name;
|
||||||
|
char *data = NULL;
|
||||||
|
long len = 0;
|
||||||
|
id __v__id;
|
||||||
|
|
||||||
|
|
||||||
|
memcpy(&scratch, &patch_obj, sizeof (struct obj)); /* find the module's Size record */
|
||||||
|
|
||||||
|
while(obj_nextinmod(&scratch))
|
||||||
|
{
|
||||||
|
if(obj_type(&scratch) == C_SIZE)
|
||||||
|
{
|
||||||
|
len = obj_contsize(&scratch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(len)
|
||||||
|
{
|
||||||
|
memcpy(&scratch, &patch_obj, sizeof (struct obj)); /* find the module's Contents record (hopefully only one) */
|
||||||
|
|
||||||
|
while(obj_nextinmod(&scratch))
|
||||||
|
{
|
||||||
|
if(obj_type(&scratch) == C_CONTENTS)
|
||||||
|
{
|
||||||
|
data = scratch.at + 4; /* actual data starts 4 bytes into Contents record */
|
||||||
|
if(obj_flags(&scratch) & 8) data += 4; /* unless the offset or repeat fields are flagged as present */
|
||||||
|
if(obj_flags(&scratch) & 16) data += 2;
|
||||||
|
|
||||||
|
if(scratch.at + obj_recsize(&scratch) != data + len)
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "Patch file contains module with size/contents mismatch\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!data)
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "Patch file contains module with nonzero Size but no Contents\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
name = dict_str_from_id(&patch_dict, obj_id(&patch_obj));
|
||||||
|
if(!name)
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "Patch module with unlisted ID\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__v__id = tblcnt + 1;
|
||||||
|
|
||||||
|
dict_append_v(&dest, name, __v__id); /* create a "__v__" dict id for the code that this patch will hide */
|
||||||
|
/* 1 dict append to 1 tbl append (dict ID = tbl index + 1) */
|
||||||
|
|
||||||
|
tbl[tblcnt].name = name;
|
||||||
|
tbl[tblcnt].data = data;
|
||||||
|
tbl[tblcnt].len = len;
|
||||||
|
tbl[tblcnt].clean_id = 0;
|
||||||
|
tbl[tblcnt].__v__id = __v__id; /* equals ID assigned just above */
|
||||||
|
|
||||||
|
|
||||||
|
if(l) fprintf(l, "File \"{PatchDump}\"; Line 0; File \"{PatchDump}\"; Find /Module=¶\"%.*s¶\"/ # ", *name, name+1);
|
||||||
|
if(l) debug_print_about_patch(l, data, len); /* log some MPW code that can show the user this routine's disasm */
|
||||||
|
if(l) fprintf(l, "\n");
|
||||||
|
|
||||||
|
tblcnt ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj_pad(&dest);
|
||||||
|
|
||||||
|
/* The most important part: create a name-searchable table of vector glue code */
|
||||||
|
qsort((void *)tbl, tblcnt, sizeof (struct tblent), tblcmp);
|
||||||
|
|
||||||
|
if(l) fprintf(l, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
delta = tblcnt + 1;
|
||||||
|
|
||||||
|
|
||||||
|
if(l) fprintf(l, "############################## NAVIGATE OUTPUT #############################\n\n");
|
||||||
|
|
||||||
|
for(file_i=0; file_i<inputCount; file_i++)
|
||||||
|
{
|
||||||
|
char *cur_bin;
|
||||||
|
struct obj cur_obj, cur_dict;
|
||||||
|
|
||||||
|
char *mod_copy_start = NULL; /* dest's copy of this module record */
|
||||||
|
char *mod_copy_last = NULL; /* dest's copy of the last record in this module (not including new patches) */
|
||||||
|
char *mod_sizeobj = NULL; /* dest's copy of this module's size record, for offsetting any patches */
|
||||||
|
|
||||||
|
struct tblent *patchstack[512];
|
||||||
|
int patchcnt = 0;
|
||||||
|
|
||||||
|
id nextdelta = delta + 1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cur_bin = slurp(inputPaths[file_i]);
|
||||||
|
if(!cur_bin)
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "Failed to slurp input file %s\n", inputPaths[file_i]);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l) fprintf(l, "# \"%s\"\n", inputPaths[file_i]);
|
||||||
|
if(l) fprintf(l, " Find /Åjump to next fileÅ/ \"{Active}\"\n\n");
|
||||||
|
|
||||||
|
obj_init(&cur_obj, cur_bin);
|
||||||
|
obj_init(&cur_dict, cur_bin);
|
||||||
|
|
||||||
|
//fprintf(stderr, "Delta is %d for %s\n", delta, inputPaths[file_i]);
|
||||||
|
|
||||||
|
|
||||||
|
/* done slurping */
|
||||||
|
|
||||||
|
|
||||||
|
for(obj_next(&cur_obj); obj_type(&cur_obj) != C_LAST; obj_next(&cur_obj)) /* for each RECORD in the file (excluding First + Last) */
|
||||||
|
{
|
||||||
|
char t = obj_type(&cur_obj);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
obj_copy(&dest, &cur_obj); /* COPY THE RECORD STRAIGHT IN (BRAVE!) */
|
||||||
|
|
||||||
|
mod_copy_last = dest.at;
|
||||||
|
|
||||||
|
/* do (some of) the renumbering here, i guess */
|
||||||
|
for(i=0; OFFSETS[t][i]; i++)
|
||||||
|
{
|
||||||
|
id is;
|
||||||
|
is = shortfrom(dest.at + OFFSETS[t][i]);
|
||||||
|
setshort(dest.at + OFFSETS[t][i], is + delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(t == C_MODULE)
|
||||||
|
{
|
||||||
|
mod_copy_start = dest.at;
|
||||||
|
mod_sizeobj = NULL;
|
||||||
|
|
||||||
|
dest.at[1] |= 1<<7; // force active
|
||||||
|
}
|
||||||
|
|
||||||
|
if(t == C_DICT)
|
||||||
|
{
|
||||||
|
id hid = dict_highestid(&dest);
|
||||||
|
if(hid > nextdelta) nextdelta = hid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(t == C_SIZE)
|
||||||
|
{
|
||||||
|
mod_sizeobj = dest.at;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(t == C_MODULE || t == C_ENTRYPT)
|
||||||
|
{
|
||||||
|
struct tblent fodder_for_bsearch, *ent;
|
||||||
|
uchar *my_name;
|
||||||
|
char *patch_mod = NULL;
|
||||||
|
int is_vectorized = 0;
|
||||||
|
|
||||||
|
my_name = dict_str_from_id(&cur_dict, obj_id(&cur_obj));
|
||||||
|
|
||||||
|
if(!my_name)
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "Module with unlisted ID\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fodder_for_bsearch.name = my_name;
|
||||||
|
ent = (struct tblent *)bsearch(&fodder_for_bsearch, tbl, tblcnt, sizeof (struct tblent), tblcmp);
|
||||||
|
|
||||||
|
if(ent) /* there is a patch module, but it may be zero-size! */
|
||||||
|
{
|
||||||
|
if(ent->data && ent->len)
|
||||||
|
{
|
||||||
|
ent->clean_id = obj_id(&dest);
|
||||||
|
patchstack[patchcnt++] = ent;
|
||||||
|
is_vectorized = 1;
|
||||||
|
|
||||||
|
if(t == C_MODULE)
|
||||||
|
{
|
||||||
|
ent->offset_of_parent_in_module = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ent->offset_of_parent_in_module = longfrom(dest.at+4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(warnings)
|
||||||
|
{
|
||||||
|
fprintf(stderr, WARNSTR "Missing vector patch: %.*s\n", *my_name, my_name+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l)
|
||||||
|
{
|
||||||
|
if(t == C_MODULE)
|
||||||
|
{
|
||||||
|
fprintf(l, "File \"{RomDump}\"; Line 0; File \"{RomDump}\"; Find /Module=¶\"");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(l, " File \"{RomDump}\"; Line 0; File \"{RomDump}\"; Find /Entry=¶\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_vectorized) fprintf(l, "__v__");
|
||||||
|
|
||||||
|
fprintf(l, "%.*s¶\"/ # ID %d", *my_name, my_name+1, is_vectorized ? ent->__v__id : obj_id(&dest));
|
||||||
|
|
||||||
|
if(t == C_ENTRYPT)
|
||||||
|
{
|
||||||
|
fprintf(l, ", offset $%x", longfrom(dest.at+4));
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(l, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mod_copy_start && obj_islastinmod(&cur_obj)) /* empty the patchstack */
|
||||||
|
{
|
||||||
|
int pi;
|
||||||
|
|
||||||
|
// qsort((void *)patchstack, patchcnt, sizeof (struct tblent *), ptch_order_cmp);
|
||||||
|
|
||||||
|
for(pi=0; pi<patchcnt; pi++)
|
||||||
|
{
|
||||||
|
struct obj scratch;
|
||||||
|
long original_size;
|
||||||
|
uchar *my_name;
|
||||||
|
|
||||||
|
my_name = patchstack[pi]->name;
|
||||||
|
|
||||||
|
original_size = longfrom(mod_sizeobj + 2);
|
||||||
|
|
||||||
|
if(l) fprintf(l, " File \"{RomDump}\"; Line 0; File \"{RomDump}\"; Find /Entry=¶\"%.*s¶\"/ # patch ID %d, ", *my_name, my_name+1, patchstack[pi]->clean_id);
|
||||||
|
if(l) debug_print_about_patch(l, patchstack[pi]->data, patchstack[pi]->len);
|
||||||
|
if(l) fprintf(l, ", offset $%x\n", original_size);
|
||||||
|
|
||||||
|
obj_next(&dest); /* EXTRA RECORD: CONTENTS */
|
||||||
|
dest.at[0] = C_CONTENTS; /* type */
|
||||||
|
dest.at[1] = 8; /* offset field present */
|
||||||
|
setshort(dest.at + 2, 8 + patchstack[pi]->len); /* size = 8b header, plus data */
|
||||||
|
setlong(dest.at + 4, original_size); /* offset = end of module data so far */
|
||||||
|
memcpy(dest.at + 8, patchstack[pi]->data, patchstack[pi]->len); /* actual, you know, *contents* */
|
||||||
|
|
||||||
|
obj_next(&dest); /* EXTRA RECORD: ENTRY POINT (of patch) */
|
||||||
|
dest.at[0] = C_ENTRYPT; /* type */
|
||||||
|
dest.at[1] = 8; /* external entry point */
|
||||||
|
setshort(dest.at + 2, patchstack[pi]->clean_id); /* ID */
|
||||||
|
setlong(dest.at + 4, original_size); /* offset = end of module data so far */
|
||||||
|
|
||||||
|
/* Increment the size of this module */
|
||||||
|
setlong(mod_sizeobj + 2, original_size + patchstack[pi]->len);
|
||||||
|
|
||||||
|
|
||||||
|
/* Now do the dirty work of changing self-references to use __v__... */
|
||||||
|
|
||||||
|
for(scratch.at=mod_copy_start; scratch.at<=mod_copy_last; obj_next(&scratch))
|
||||||
|
{
|
||||||
|
char tt = obj_type(&scratch);
|
||||||
|
|
||||||
|
for(i=0; PASSIVE_OFFSETS[tt][i]; i++)
|
||||||
|
{
|
||||||
|
id is = shortfrom(scratch.at + PASSIVE_OFFSETS[tt][i]);
|
||||||
|
if(is == patchstack[pi]->clean_id)
|
||||||
|
{
|
||||||
|
setshort(scratch.at + PASSIVE_OFFSETS[tt][i], patchstack[pi]->__v__id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} /* patchstack loop */
|
||||||
|
|
||||||
|
if(l) fprintf(l, "\n"); /* newline before each module... nice touch */
|
||||||
|
|
||||||
|
patchcnt = 0;
|
||||||
|
} /* do patchstack or not? */
|
||||||
|
} /* for each record */
|
||||||
|
|
||||||
|
free(cur_bin);
|
||||||
|
//fprintf(stderr, "Culminating in %d\n", nextdelta);
|
||||||
|
delta = nextdelta + 1;
|
||||||
|
|
||||||
|
// delta += 5000;
|
||||||
|
// delta -= (delta % 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
obj_next(&dest);
|
||||||
|
dest.at[0] = C_LAST;
|
||||||
|
dest.at[1] = 0;
|
||||||
|
|
||||||
|
if(blat(outputObjPath, dest.buf, (long)(dest.at) - (long)(dest.buf) + 2))
|
||||||
|
{
|
||||||
|
fprintf(stderr, ERRSTR "Failed to write output file\n");
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l) fclose(l);
|
||||||
|
|
||||||
|
#ifndef __GNUC__
|
||||||
|
{
|
||||||
|
FInfo fi;
|
||||||
|
Str255 pas;
|
||||||
|
int e;
|
||||||
|
|
||||||
|
to_pas(pas, outputObjPath);
|
||||||
|
|
||||||
|
e = GetFInfo(pas, 0, &fi);
|
||||||
|
if (e == 0)
|
||||||
|
{
|
||||||
|
fi.fdType = 0x4F424A20; /* TEXT */
|
||||||
|
fi.fdCreator = 0x4D505320; /* MPS */
|
||||||
|
e = SetFInfo(pas, 0, &fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(symFilePath)
|
||||||
|
{
|
||||||
|
to_pas(pas, symFilePath);
|
||||||
|
|
||||||
|
e = GetFInfo(pas, 0, &fi);
|
||||||
|
if (e == 0)
|
||||||
|
{
|
||||||
|
fi.fdType = 0x54455854; /* TEXT */
|
||||||
|
fi.fdCreator = 0x4D505320; /* MPS */
|
||||||
|
e = SetFInfo(pas, 0, &fi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user