Hello, cyberpals.
In the previous episode, we got the message list drawing again.
And in this episode, I was going
to create a new LDEF-- list definition function
for the message list. But upon reading
this January 1994 issue
of MacTech, I came across this
"6 byte code resource stub trick", which is actually
now a 10 byte code resource stub trick.
And what it basically is, is in the LDEF
resource fork, instead of putting
all of the executable code of the function
to draw the list, we would just put in some
bytecode that jumps to address.
And then at runtime, we patch that address with the address of
the function in our project. And basically what
that allows us to do is keep the list
definition function in our main project and treat it like any other
function where we can step through it in the debugger
and check out the data variables
and the function would have access
to other variables and other functions in the project.
So I guess we're going to implement that today and
see if it works.
So the first thing that we're going to do is create a new LDEF
and then just type in this bytecode.
Are these opcodes, I guess they'd be?
Motorola 68K opcodes that would
load an address into a data function or data.
That would be a register, I guess? And then just
jump to that address. And this used to be called the
six byte code resource stub trick because it was
able to be accomplished in six bytes. But due
to CPU caching, rather than just patching
the actual code, it's better to have
the code load a-- load
the address into a register and then jump
to that register. So here's the LDEF
then we are going to type in [ reading ]
So now that that is created, we will
just give it a name.
And then save it. And quit.
Then in our browser, code,
we can assign that
In the LNew for the
messages list instead of passing the default list definition function,
we're going to pass our custom one. And then we're going
to have to fetch-- so the way that
this works is we're going to call GetResource on that ID
and allocate a Handle to that resource and
once it's in memory, we can then change it.
And then because we're going to keep the Handle open.
As soon as the LNew
function is called, inside of it,
it would call GetResource on this ID and then
GetResource would see that it has already been opened and
is still open by the program and
then rather than fetching it out of the resource for it would
just like use the same Handle that
we already have opened and that's how we're able to
basically modify it memory and then anything else trying
to use that resource is going to see the modified version without
having to actually save it to the resource fork information.
So we're going to create a new Handle here.
We will call that message_list_ldef_h.
And then before
we use it, we're going to [ reading ]
[ narrating code ]
It's important to note these are single apostrophes,
not quotes. I don't really know why?
Like, normally you'd expect it to be a character array.
But I don't know why these are single apostrophes because
That would be the integer value of L.
But I don't know how this even works,
but whatever. And then we have--
[ narrating ]
But if we succeededed, then we will lock it.
So that it doesn't get moved around in memory on us.
And then we are going to need to insert the address of
our function which we're going to create.
But we are going to create a struct type
of that 10 bytes that
we created so that we can
easily reference them and
just overwrite the address.
[ narrating ]
We will
cast that
which we are going to create.
And I'm going to put it in its own file because we're probably going to change
it a lot and I don't want to have to recompile this browser file each
time. And then for the contents, we're just going
to copy what's in the
mailbox LDEF because it already has like
this big switch here that we need to respond to
each type of message that will come to us.
So, like this one we're having to redefine
fontHeight in this LDEF because this is like a standalone program.
But in our message_ldef, we're not
going to have to do that.
Was it... [ mumbling ]
We need to... let me just delete
all of this bogus code and I'll
just say that we're here.
Save as message_ldef.c
And we'll add it to our project.
And then we need to copy this prototype
to something that browser can see. So we'll just stick it
in browser.h because nothing else is going to have to use it.
And I don't want to have to create a
whole .h file just for this.
Okay, so now,
if we run this, we should be able to set a breakpoint
in message list LDEF
and it should just break there.
And we can inspect the
arguments that were passed to it and make sure that they're
right and go forward
from there.
So, yeah,
if this works, then we'll I guess change the mailbox LDEF
to be the same thing because it will just make things so much easier.
That's weird, it has to know its own prototype.
Why are you recompiling tcp? I did not
change that.
I should clarify, I was not reading the MacTech magazine and
found this. I came across it mentioned somewhere and then
I just searched for "code resource sub trick"
and it brought me to this article which I will link in the show
notes here. Alright, so we set a breakpoint
in here. Just so we know we're jumping to the right place and
cross our fingers.
Hey, look at that. So we're here
and message is probably zero to init the list, and
it is.
And it should bring us back once it actually has to draw messages
in the list. So,
and it did! So now we are back
in our message list LDEF and message
I'm assuming is 1
And then we can examine...
theCell
.v which should increment every time we draw
a new cell. So that should
work and it works now
it's 1. And now it's 2
And why are we back at 2?
That's weird, I wonder why it is redrawing
2 over and over again.
It might be because we are-- the
window is getting covered by this every time we step
into the debugger. And so
it's triggering itself.
Yeah, okay, so that's
working, so that is awesome. This is not working.
The selection is wrong, so that's
cool. So now we don't need to create our own separate LDEF
project. And then recompile it each time.
So now we can actually pass the message
pointer to the LDEF and then draw
the from and subject. So here
we're checking if it's from, we don't need to do that anymore.
And then we'll just set the cell to the whole pointer.
And the length is a size
of a pointer and
then if it's NULL,
it is-- we should
not pass a string, we should pass zero
or NULL. But this
is already NULL.
So let's pass zero and then the size is zero
and then I guess we can check. We can do LGetCell and then see
if the size is zero, I think. Actually,
the data len I believe
should be passed correctly. So that should be zero.
And then that way, we know that it's an empty message.
Okay.
In the LDEF, we will do--
we're going to need mailbox,
I think is where...
imap_message is defined.
And it is so we will make
a pointer to...
message is already taken, so we'll call it imessage
Okay, and then we will...
So dataLen, I guess, would be size of pointer
or zero. But dataOffset
is not the thing that we pass to it,
we need to--
I need to call LGetCell
to copy a cell's data.
dataOffset is the
offset into something else that is not very
helpful to us. So we
will say imessage [ narrating ]
[ reading ] Pointer is a pointer to the location to which to copy the cell's
data.
Yeah, so imessage--
because the data is just an address.
So it's going to copy
How about we do this? Just to make things clear.
And then set
dataLen [ reading ]
Okay, and then we'll say-- I don't
know if we need this level of indirection because I'm
not thinking clearly if imessage--
So imessage is allocated on
the stack as a pointer.
And the value is junk right now.
And then it's going to save--
Is that right? Because we want to dereference
the pointer.
Alright, we will step through this,
and see what happens.
Oh that's a pointer.
LGetCell updates the value of dataLen
after it runs to return. Like, if you tell it
you want 4 bytes and it the value that it
has is 5 points. It will only copy four
bytes. And then if
it's shorter than that, it would update dataLen.
Okay, so do we still have a break point here?
No.
Okay, here we go.
Okie dokie.
Alright, so what does imessage_ptr
and imessage--
Yeah, but I want the address.
No, I want--
Okay.
Right?
Right, and then what
the actual value of it is. And then we'll
see what the actual imessage points to.
[ mumbling ]
So now--
imessage pointer is 10, that seems bogus.
Or does it?
That's not right at all.
Right? It's pointing
to garbage-- a garbage address.
But this-- I don't know if this is--
what if we do...
That can't be right, can it?
Yeah that's not right at all. So did
LGetCell fail?
It doesn't say what
the return value is.
Okay.
Why did that not work?
Did I--
Should this be like, just, the--
like that?
No, that can't be right.
Because how would it know...
I'm confused now.
I don't get this. Why is LGetCell not--
I'm confused because LGetCell copies the data into the
address. But the data is
a pointer. What if
we just not use LGetCell and just say that
Let me just double check that...
LSetCell sets the data of the cell to
dataLen bytes of data beginning at the location specified by
dataPointer.
And then the
value of this is a pointer.
I don't know, man. I don't get why LGetCell
is not able to pull that back out. What if we just short-circuit it
and pass it
the cell and see what is there?
[ mumbling ]
imap already includes mailbox
[ phone buzzes ] What is going on here, man?
Annoying notifications.
How about we--
Oh, our breakpoint just got deleted because we commented out
the code that where the breakpoint was.
Duh.
Okay, that works, so
step through. And now imessage should
be a valid pointer and
to a struct to a
message and
that looks okay.
That should be a C string.
Okay, that's a weird
way to write that Mr. debugger.
Yeah, so that works, we're just bypassing
the SetCell and GetCell which we probably shouldn't be doing.
But I don't know why it's not working
the way that it should.
Okay, so now we can
actually pull out the from and subject.
But I would like to figure out this thing here.
I guess the problem is that it's copying data
instead of just assigning the pointer that we pass it.
And the dataOffset
value is the offset into
LGetCell dataLocation.
Um that's weird.
Okay.
Let me just do...
No that's just even more confusing.
Or is it?
So if we--
[ reading ] the LGetCellDataLocation procedure returns this parameter
The offset of the cell's data specified from
the beginning of the data handle referenced by the cell's field [ mumble ]
Okie dokie.
I just want to inspect what this ends up being.
We don't really want to have to know about the
structure of this messages array.
Oh, the LGetCellDataLocation is
also available as the LFind.
Sure.
Hello?
Are you there?
Alright, so we can...
Okay, and LFind...
Okay, and now what is imessage_ptr
Oh, no? No, these are
addresses. My message pointer is zero, so
that failed.
And dataLen is still 4.
Okay, so I don't know why that's not working.
But I guess if we don't-- if we
have some other way to get the data, we should
be okay.
You are watching this and you know why this doesn't
work, please let me know.
But for now I'm just going to side-step
the cell data storage stuff.
And work like this. Okay, so now we can create
a new string.
Well let's try this. So, if we--
I can't do CtoPStr on this data
because it's going to modify it in place.
So we will need to make a new string.
Sure.
And say destination,
source? Yes,
this could be from
[ mumbling ]
Okay.
And then we're gonna draw it
and then we're going to move over.
I don't know, 100?
And then we'll do the same for
subject,
[ child screaming in the distance ]
That is my child screaming downstairs.
Subject.
Right?
Yeah, I think that should work.
Let us try it, shall we?
We have strncpy, right?
Sneaky editor.
What is that in, string?
No, what is that in? stdio
Coming to you in stdio.
Alright.
Did our breakpoint get deleted?
Hello, are you there?
Alright let's just
make sure our breakpoint is still here.
And it is.
We should probably also check if this is a null pointer on
the first one. Look at that, sweet.
Those are the leading
newlines that we still have not stripped out.
That's pretty cool.
Is it...
oh, we forgot to MoveTo. So it's just using
the coordinates of with last DrawString left us.
And then I need to strip out those newlines
from imap_get_word.
Which can be leading
or trailing.
So we can do that probably at the end.
Okay, so we've already copied into word,
How should we do that?
Are we using-- okay, so we can probably
just say
[ narrating code ]
Right, because word_source is just a pointer.
Then we probably can't modify-- this is getting
messy.
So we should do this first so we don't
allocate more bytes than we need.
So that would strip leading newlines
and then we can just trim bytes.
[ narrating ]
Right? [ narrating ]
Okay, that should work.
And then maybe I will step through this just to verify that it works.
I guess, like on a modern thing,
a modern machine, you would just run this.
And if it crashes or produces weird output, you could run the program
again. But because we're dealing with memory that might
not be ours, if this goes off the rails, I have to
reboot and that's not fun. So sometimes
easier just to step through it and verify things and
if it's going awry to just
quickly exit the program instead of letting it continue.
I created an
IRC channel on Freenode called
#cyberpals, I thought, because I'm getting a lot of feedback on these
people are following along. So I figured I'd
make a channel. And if you
are stuck or have questions, you can join
and maybe help other people or--
I am in there and I can try and help. So check
it out if you want. Alright, so we're going to set a breakpoint here
So we can probably clear all this because we know it's working now.
And we will say, let's monitor word_source
And it is a C string.
Okay, so this is just trailing data
Okay, so that should
do nothing. And this trigger...
[ mumbling ]
That is weird.
Are we too far?
Oh, right. Right.
Because bytes was already set to this
because we know that we're going to skip trailing.
So we probably don't even need this loop
here because we're not going to end up copying
those bytes anyway. Okay.
And now this should trigger
and trim those newlines.
Yup.
Okay.
So that should be fine. This is working
as expected. So we don't need this
anymore. And this
should work and it does. And we don't
have our meeting new lines anymore.
Okay, so this is exactly what I wanted. The name,
the subject-- oops, that's
covering itself. The subject and then maybe
the date. Or what does my mutt look like? So I have the date
here as just four numbers. Then message flags
and then the subject and
then the sender, so maybe I will copy that.
And so this
works. As far as this is one on
whole cell here. Cool.
So it looks like our list definition is
working great.
So we don't need this anymore. We just need
to trim.
Cool, man.
Okay, so then we just need to pull out
the-- I'm not even storing the date because I need to make
a function to like, do date--
How does that even work? Because we don't have a time zone
on System 6. I guess that will have
to be in the preferences. But then, no, because how do
you deal with time zones and all that crap like-- ugh.
Alright, I'll figure something out for that. But for now,
at least we are able to draw
the cell as we want.
So I guess that's the end of this video and if you liked
it, subscribe to the RSS feed for a future video.
And if you want, join the #cyberpals
IRC channel on Freenode and see
you in the next video. Bye.