Someone asked to have SimpleText open a smaller text window at startup. Initially, I assumed this would be a fairly easy fix by just overwriting a few constant values in SimpleText code. It turned out to be a pain -- but I learned a lot along the way.
You need to have the code editor (from one of the Apple developer CDs) in your ResEdit preference file in order to disassemble code resources within ResEdit. Then, open each 'CODE' resource of SimpleText and search for _SizeWindow (A91D). Or, just skim the code until you see system calls that look like they have something to do with windows. Here is a nice chunk in routine Anon48. A new window is created, then it looks at the main device, looks at the size of the menu bar (MBarHeight), sets the port, and moves and sizes the window.
But, uh oh, earlier in the code it checks what type of window it is opening. A patch is going to need to avoid resizing code for pictures, video, and the about box.
I could not locate any obvious constants to adjust. Instead, I would need to inject a more complicated routine that detects whether it was a text window and substitute a new rectangle for the size. But, you cannot simply insert code, as it would move all subsequent code down, and the jumps (subroutine calls) that cross over that location would now jump to the wrong spots. So, I need to jump out of their code to my own (appended at the end of the code resource) and return.
For example, SimpleText's code to get the size of MBarHeight can easily be performed elsewhere. My routine need only return the MBarHeight in register D0 before returning. That gives me 8 bytes to overwrite with my jump.
Here is my replacement code. It still is only 8 bytes. But, I now jump to my subroutine, check the result, and jump over their resizing code if my routine says it is changing the window size.
In my subroutine, I make sure all the needed information is in registers (which I checked that SimpleText was not using), I call my various functions and then perform any work that was lost from overwriting or jumping over the original code. Specifically, I get the MBarHeight into D0 and set the current port to the window.
Easy! But, later in the code, SimpleText reads the content of whatever document is being opened and once again resizes the window. So, I needed to patch later code as well. How could I determine at that point that a replacement window size was being used? I simply store the result of the first subroutine (see SetRecentResult above) and then check it on later calls.
Where could I store this information? it is not possible to add global variables and the registers are all reused by SimpleText between the first and second routines. Well, you can store a variable (or an entire structure with many variables) within the code itself. Here is my little workaround for CodeWarrior.
A couple of other tricks.
1. The system routine GetHandleSize has some glue code (they intercept the call in a local library before calling the system). I needed this call, but didn't want to add the weight of CodeWarrior's libary. So, I defined the direct call to GetHandleSize (I didn't need the glue fix).
2. You can pass any of the scratch registers (D0-D2, A0-A1, FP0-FP3) to a C function. The way of defining that in CodeWarrior is noted below. You cannot use any other registers. To make debugging easier, I wrote the original subroutine as a true C function, and the register->C function as a wrapper.
3. CodeWarrior does not support BSR for some reason. Use JSR instead. Also, a called routine must be placed before the caller routine in order to generate a short relative JSR rather than absolute address. See my 'RecentResult' example above, where the routines that call RecentResult are placed after it in code.
4. SimpleText stores literals (strings, constants) at the end of the 'CODE' resource. After that is where I placed my code. Unfortunately, this breaks disassembly in ResEdit. Below, do you see 'A9FF'? That's the '_Debugger' trap call. It is follow by the rest of the code, and the MacsBug symbols for "SimpleTextWindowChoicePrep'
I then needed to hand compute the JSR patched in the original SimpleText code to this location at the end of the code resource.
5. To make it easy to redefine window sizes in the future, I added a resource.
The ResEdit definition of this resource is the TMPL. By the way, I have experienced corruption twice with ResEdit 2.1.3. Perhaps it has a bug with templates?
I doubt this information will be useful to most people. However, it may help avoid some frustrating issues for those few people that attempt patching old software.
Attached is the hacked version of SimpleText.
- David
You need to have the code editor (from one of the Apple developer CDs) in your ResEdit preference file in order to disassemble code resources within ResEdit. Then, open each 'CODE' resource of SimpleText and search for _SizeWindow (A91D). Or, just skim the code until you see system calls that look like they have something to do with windows. Here is a nice chunk in routine Anon48. A new window is created, then it looks at the main device, looks at the size of the menu bar (MBarHeight), sets the port, and moves and sizes the window.
But, uh oh, earlier in the code it checks what type of window it is opening. A patch is going to need to avoid resizing code for pictures, video, and the about box.
I could not locate any obvious constants to adjust. Instead, I would need to inject a more complicated routine that detects whether it was a text window and substitute a new rectangle for the size. But, you cannot simply insert code, as it would move all subsequent code down, and the jumps (subroutine calls) that cross over that location would now jump to the wrong spots. So, I need to jump out of their code to my own (appended at the end of the code resource) and return.
For example, SimpleText's code to get the size of MBarHeight can easily be performed elsewhere. My routine need only return the MBarHeight in register D0 before returning. That gives me 8 bytes to overwrite with my jump.
Here is my replacement code. It still is only 8 bytes. But, I now jump to my subroutine, check the result, and jump over their resizing code if my routine says it is changing the window size.
In my subroutine, I make sure all the needed information is in registers (which I checked that SimpleText was not using), I call my various functions and then perform any work that was lost from overwriting or jumping over the original code. Specifically, I get the MBarHeight into D0 and set the current port to the window.
Easy! But, later in the code, SimpleText reads the content of whatever document is being opened and once again resizes the window. So, I needed to patch later code as well. How could I determine at that point that a replacement window size was being used? I simply store the result of the first subroutine (see SetRecentResult above) and then check it on later calls.
Where could I store this information? it is not possible to add global variables and the registers are all reused by SimpleText between the first and second routines. Well, you can store a variable (or an entire structure with many variables) within the code itself. Here is my little workaround for CodeWarrior.
A couple of other tricks.
1. The system routine GetHandleSize has some glue code (they intercept the call in a local library before calling the system). I needed this call, but didn't want to add the weight of CodeWarrior's libary. So, I defined the direct call to GetHandleSize (I didn't need the glue fix).
2. You can pass any of the scratch registers (D0-D2, A0-A1, FP0-FP3) to a C function. The way of defining that in CodeWarrior is noted below. You cannot use any other registers. To make debugging easier, I wrote the original subroutine as a true C function, and the register->C function as a wrapper.
3. CodeWarrior does not support BSR for some reason. Use JSR instead. Also, a called routine must be placed before the caller routine in order to generate a short relative JSR rather than absolute address. See my 'RecentResult' example above, where the routines that call RecentResult are placed after it in code.
4. SimpleText stores literals (strings, constants) at the end of the 'CODE' resource. After that is where I placed my code. Unfortunately, this breaks disassembly in ResEdit. Below, do you see 'A9FF'? That's the '_Debugger' trap call. It is follow by the rest of the code, and the MacsBug symbols for "SimpleTextWindowChoicePrep'
I then needed to hand compute the JSR patched in the original SimpleText code to this location at the end of the code resource.
5. To make it easy to redefine window sizes in the future, I added a resource.
The ResEdit definition of this resource is the TMPL. By the way, I have experienced corruption twice with ResEdit 2.1.3. Perhaps it has a bug with templates?
I doubt this information will be useful to most people. However, it may help avoid some frustrating issues for those few people that attempt patching old software.
Attached is the hacked version of SimpleText.
- David