diff --git a/mpwobj.py b/mpwobj.py new file mode 100644 index 0000000..94d0da7 --- /dev/null +++ b/mpwobj.py @@ -0,0 +1,137 @@ +import struct + +class MPWObject: + def __init__(self): + self._list = [] + self._dict = [] + self._backdict = {} + self._dict_idx = 200 + + def __bytes__(self): + dest = bytearray() + + for chunk in self._list: + dest.extend(chunk) + if len(dest) & 1: dest.append(0) + + return bytes(dest) + + def _ensurename(self, name): + # get the ID of this name from the dict + # If nonexistent, then add it + # i.e. idempotent + + try: + return self._backdict[name] + except KeyError: + self.putdict([name]) + return self._backdict[name] + + def _quickappend(self, *bytelist): + self._list.append(bytes(bytelist)) + + def putfirst(self): + self._quickappend(1, 1, 0, 2) + + def putlast(self): + self._quickappend(2, 0) + + def putdict(self, items): + dest = bytearray() + + dest.extend([4, 0, 99, 99]) + dest.extend(struct.pack('>H', self._dict_idx)) + + flag = False + + for item in items: + flag = True + dest.append(len(item)) + dest.extend(item.encode('ascii')) + + self._backdict[item] = self._dict_idx + + self._dict_idx += 1 # ID of the *next* thing + + if not flag: return + + struct.pack_into('>H', dest, 2, len(dest)) + + self._list.append(dest) + + def putmod(self, name='#0001', segname='Main', flags=(1<<7)+(1<<3)): + modid = self._ensurename(name) + segid = self._ensurename(segname) + self._last_mod_id = modid + + self._list.append(struct.pack('>BBHH', 5, flags, modid, segid)) + + def putentry(self, offset, name): + entid = self._ensurename(name) + + self._list.append(struct.pack('>BBHL', 6, 1<<3, entid, offset)) + + def putsize(self, size): + self._list.append(struct.pack('>BBL', 7, 0, size)) + + def putcontents(self, data): # in multiple chunks please! + done = 0 + + while done < len(data): + this_time = data[done:done+30000] + + header = struct.pack('>BBHL', 8, 1<<3, 8 + len(this_time), done) + + self._list.append(header + this_time) + + done += len(this_time) + + def putcomment(self, cmt): + cmt = cmt.replace('\n','\r').encode('mac_roman') + if len(cmt) & 1: cmt += b' ' + + dest = bytearray() + dest.extend([3, 0]) + dest.extend(struct.pack('>H', len(cmt) + 4)) + dest.extend(cmt) + + self._list.append(dest) + + def putsimpleref(self, targname, width, *offsets): + offsets = list(offsets) + + if width == 2: # of the operand field, in bytes + flags = 1 << 4 + elif width == 4: + flags = 0 + + flags |= 1<<3 # longwords in the offset list! + + targid = self._ensurename(targname) + + dest = struct.pack('>BBHH', 9, flags, 6 + 4 * len(offsets), targid) + dest += b''.join(struct.pack('>L', o) for o in offsets) + + self._list.append(dest) + + def putweirdref(self, targname, width, *offsets): + # Assumes that you've already put -offset at offset + offsets = list(offsets) + + if width == 1: + flags = 2 << 4 + elif width == 2: # of the operand field, in bytes + flags = 1 << 4 + elif width == 4: + flags = 0 << 4 + + flags |= 1<<7 # difference calculation + # flags |= 1<<3 # longwords in the offset list! + + targid = self._ensurename(targname) + + dest = struct.pack('>BBHHH', 10, flags, 8 + 2 * len(offsets), targid, self._last_mod_id) + dest += b''.join(struct.pack('>H', o) for o in offsets) + + self._list.append(dest) +