PKGDUMP is a fully functional tool for analyzing Apple Newton package (.pkg) files, including a NewtonScript bytecode disassembler.
This is not a modern tool. I wrote this in March of 1998, and made one bug fix in 2001. It has been untouched since that time.
Note: The source code reflects C++ practices from the late 1990s, although this program can be easily built with modern C++ tools in 2025. If you wish to use this as a basis for a new project, however, I would recommend using it as a reference rather than incorporating it as-is. In particular, rewriting this in Python or another modern language would likely make sense.
Apple discontinued the Newton platform in February of 1998, after Steve Jobs returned and began killing John Sculley's pet projects. I had just purchased a Newton MessagePad 2000 and had just had Apple upgrade it to 4MB RAM from 1MB RAM, to become a 2100, in 1997.
There were a few minor patches I wanted to make to some of the programs I had, and I knew as the years went on I'd need to make more. Knowing that, increasingly, I wouldn't be able to rely on the original authors for maintenance of their software on a dead platform, I wrote PKGDUMP to dump the contents of Newton package files (.pkg) and allow me to examine them and design patches.
I used this tool extensively for years into the early 2000s, analyzing packages and patching them to meet my own personal needs. Eventually, I transitioned away from using my Newton MessagePad, and this tool has now sat unused for decades.
When written in 1998, this code was originally confidential and written under a proprietary license. As of November 2025, I am re-releasing it under the MIT License.
The original copyright notice remains in the source file to avoid altering the file for historical reasons. However, that notice has now been superseded and the code is freely available under the MIT License. See the LICENSE file for the full terms and the NOTICE file for details about the license change.
PKGDUMP filename[.pkg]
- filename[.pkg] - The Newton package file to analyze (the .pkg extension will be added if not specified)
The program will display:
- Package header information (signature, flags, version, creation date, etc.)
- Part directory entries with metadata
- For NewtonScript parts: complete disassembly of the bytecode with literal tables and object structures
An example of the output is shown in the Example Output section below.
PKGDUMP provides comprehensive analysis of Newton package structure:
- Package headers: Extracts metadata including signature, flags, version, size, and creation date
- Part directory: Lists all parts with their offsets, sizes, types, and flags
- Relocation data: Handles packages with relocation information
- Reference types: Identifies and decodes Integers, Pointers, Characters, Special values, and MagicPointers
- Symbol resolution: Follows pointer chains to resolve symbol names and class information
- Literal tables: Extracts and displays string literals, real numbers, and symbols used by bytecode
- Instruction decoding: Converts NewtonScript bytecode to human-readable assembly-like format
- Branch analysis: Identifies jump targets and labels them as
LINE_Nfor clarity - Offset tracking: Shows absolute file offsets (
@xxxxxx) for all structures to facilitate binary patching - Opcode documentation: Displays opcode names with argument counts, resolved symbol names, etc.
All offsets and lengths are byte-accurate, making the output suitable for both analysis and binary patching.
PKGDUMP does not handle Unicode characters outside of the ASCII range. When printing Unicode strings, it simply prints a period (
.) in place of non-ASCII characters. For my purposes, of making patches to code bytes, this was sufficient. Since all printing of Unicode strings is done via a single function, it would be easy to modify the code to handle full Unicode output if desired.
The original development environment was Microsoft Visual C++ on Windows. Conveniently, however, the code compiles cleanly with modern compilers including GCC. I have added a Makefile that builds the program on modern systems, and have tested it with GCC 15.2.
Note: Rather than altering the original code, I have included an io.h file that provides a
filelengthfunction for non-Windows platforms, since the original code used the Windows-specific version. The Makefile includes this file only when building on non-Windows systems (by adding the local directory to the include path).
The output format is designed for human readability and manual creation of binary patches. NewtonScript opcodes are shown in assembly-like format with their offsets and hex dumps. Absolute file addresses use six-digit hex with an @ prefix (e.g., @001E24).
Here is an excerpt of the output when analyzing the Newton package file for the 1994 game "Othello" by "Ergonomic."
Package Name: "Othello:ERGO"
Copyright: ".1994 Ergonomic. All rights reserved."
=================
Package Header:
=================
[Signature ]: package0
[Flags ]:
[Version ]: 1
[Size ]: 78880 bytes
[Creation Date ]: Thu Sep 8 05:16:38 1994
[Directory Size]: 248 bytes
[Num Parts ]: 1
...
OFFSET 248
ALIGN 8
Frame [@000108/44]
{
app: "Othello:ERGO"
text: "Othello"
icon: Frame @0002A8
theForm: Frame @000508
devInstallScript: Frame @012EC0
devRemoveScript: Frame @012FC8
installScript: Frame @013080
removeScript: Frame @013368
}
...
Frame [@000508/352]
{
whiteProgressIndicator2: NIL
GetUSoup: Frame @001348
newEntry: Frame @001510
viewSetupDoneScript: Frame @0018A8
viewFormat: 67110128
prefsSlip: NIL
IsDirty: NIL
viewQuitScript: Frame @001DF8
blackProgressIndicator1: NIL
target: NIL
aboutOFNView: NIL
advisoryLine: NIL
...
}
...
Frame [@001DF8/32]
{
class: "CodeBlock"
instructions:
// These instructions occur at offset @001E24
// Decoded literals
// Routine Length: 186 bytes
__newtonscript_asm
{
0: 70 find-var 'base' (0)
1: 19 push 'queuedActionPending' (1)
2: 91 get-path 1
3: 6F 00 39 if false goto LINE_3 (57) // Branch-if-false
6: 70 find-var 'base' (0)
7: 1A push 'closeImmediately' (2)
8: 27 00 1A push 26
11: 98 set-path 0
12: 73 find-var 'playingBoard' (3)
13: 1C push 'currentPlayer' (4)
14: 91 get-path 1
15: 24 push 4
16: C4 equals?
17: 6F 00 21 if false goto LINE_1 (33) // Branch-if-false
20: 75 find-var 'blackClock' (5)
21: 1E push 'Stop' (6)
22: 38 send <0 args>
23: 00 pop
24: 75 find-var 'blackClock' (5)
25: 1F 00 07 push 'ResetToLastStart' (7)
28: 38 send <0 args>
29: 00 pop
30: 5F 00 2F goto LINE_2 (47) // Branch
LINE_1:
33: 77 00 08 find-var 'whiteClock' (8)
36: 1E push 'Stop' (6)
37: 38 send <0 args>
38: 00 pop
39: 77 00 08 find-var 'whiteClock' (8)
42: 1F 00 07 push 'ResetToLastStart' (7)
45: 38 send <0 args>
46: 00 pop
LINE_2:
...
}
...
}
...