System Error Alert Table: Difference between revisions
From Computers Wiki
Jump to navigationJump to search
(Create page) |
(Create script for reading DSAT) |
||
| Line 9: | Line 9: | ||
ResEdit doesn't support <code>DSAT</code> and only shows a hex editor despite having a custom icon for the resource type. |
ResEdit doesn't support <code>DSAT</code> and only shows a hex editor despite having a custom icon for the resource type. |
||
Here is an extremely rough Python script for dumping the alerts, their IDs, and their error messages. Copy-paste the hex editor output from ResEdit into a text file, then pass its filename to this script: |
|||
TODO: write a Python script to extract this info from a hex dump and create a wikitable |
|||
<div class="toccolours mw-collapsible mw-collapsed"> |
|||
'''<code>dsat.py</code> contents''' |
|||
<syntaxhighlight lang="python" class="mw-collapsible-content"> |
|||
from argparse import ArgumentParser |
|||
from pathlib import Path |
|||
import struct |
|||
from typing import NamedTuple |
|||
parser = ArgumentParser() |
|||
parser.add_argument('hexfilepath') |
|||
buffer = bytes.fromhex(Path(parser.parse_args().hexfilepath).read_text()) |
|||
offset = 0 |
|||
def read_short(): |
|||
global offset |
|||
number = int.from_bytes(buffer[offset:offset + 2]) |
|||
offset += 2 |
|||
return number |
|||
definition_count = read_short() |
|||
definitions: dict[int, 'Definition'] |
|||
class Alert(NamedTuple): |
|||
text1_id: int |
|||
text2_id: int |
|||
icon_id: int |
|||
proc_id: int |
|||
button_layout_id: int |
|||
@property |
|||
def text1(self): |
|||
return definitions[self.text1_id].as_text().replace('/', '\n') if self.text1_id else None |
|||
@property |
|||
def text2(self): |
|||
return definitions[self.text2_id].as_text().replace('/', '\n') if self.text2_id else None |
|||
@property |
|||
def text(self): |
|||
if not self.text1: |
|||
return None |
|||
if self.text2: |
|||
return self.text1 + '\n' + self.text2 |
|||
return self.text1 |
|||
class Definition(NamedTuple): |
|||
id: int |
|||
data: bytes |
|||
def as_alert(self): |
|||
return Alert(*struct.unpack('>HHHHH', self.data)) |
|||
def as_text(self): |
|||
return self.data[4:-1].decode('ascii') |
|||
def read_definition(): |
|||
global offset |
|||
id = read_short() |
|||
length = read_short() |
|||
data = buffer[offset:offset + length] |
|||
offset += length |
|||
return Definition(id, data) |
|||
default_alert = read_definition().as_alert() |
|||
definitions = {(definition := read_definition()).id: definition |
|||
for _ in range(definition_count)} |
|||
for error_id, definition in definitions.items(): |
|||
if len(definition.data) == 10: |
|||
alert = definition.as_alert() |
|||
try: |
|||
print(error_id, '-', alert.text) |
|||
except (KeyError, UnicodeDecodeError): |
|||
pass |
|||
</syntaxhighlight> |
|||
</div> |
|||
== Contents in different system versions == |
== Contents in different system versions == |
||
Revision as of 00:27, 25 September 2025
The System Error Alert Table (resource type DSAT, allegedly short for Deep Shit Alert Table) describes startup text and fatal error dialogs for the classic Mac OS.
Specification
Reading it
ResEdit doesn't support DSAT and only shows a hex editor despite having a custom icon for the resource type.
Here is an extremely rough Python script for dumping the alerts, their IDs, and their error messages. Copy-paste the hex editor output from ResEdit into a text file, then pass its filename to this script:
dsat.py contents
from argparse import ArgumentParser
from pathlib import Path
import struct
from typing import NamedTuple
parser = ArgumentParser()
parser.add_argument('hexfilepath')
buffer = bytes.fromhex(Path(parser.parse_args().hexfilepath).read_text())
offset = 0
def read_short():
global offset
number = int.from_bytes(buffer[offset:offset + 2])
offset += 2
return number
definition_count = read_short()
definitions: dict[int, 'Definition']
class Alert(NamedTuple):
text1_id: int
text2_id: int
icon_id: int
proc_id: int
button_layout_id: int
@property
def text1(self):
return definitions[self.text1_id].as_text().replace('/', '\n') if self.text1_id else None
@property
def text2(self):
return definitions[self.text2_id].as_text().replace('/', '\n') if self.text2_id else None
@property
def text(self):
if not self.text1:
return None
if self.text2:
return self.text1 + '\n' + self.text2
return self.text1
class Definition(NamedTuple):
id: int
data: bytes
def as_alert(self):
return Alert(*struct.unpack('>HHHHH', self.data))
def as_text(self):
return self.data[4:-1].decode('ascii')
def read_definition():
global offset
id = read_short()
length = read_short()
data = buffer[offset:offset + length]
offset += length
return Definition(id, data)
default_alert = read_definition().as_alert()
definitions = {(definition := read_definition()).id: definition
for _ in range(definition_count)}
for error_id, definition in definitions.items():
if len(definition.data) == 10:
alert = definition.as_alert()
try:
print(error_id, '-', alert.text)
except (KeyError, UnicodeDecodeError):
pass
Contents in different system versions
TODO: make that utility first