Legend Entertainment's Games
Moderator: ScummVM Team
Re: Legend Entertainment's Games
Looks like IDA offers a new kind of licence (HOME) where the cost is not astronomical. I'll have to think about this a bit
Re: Legend Entertainment's Games
I just wanted to say that I've spent the past few days deeply embedded in IDA (freeware), cross checking the Shannara demo, with debugging symbols, with my trusty old version of Death Gate, and I feel as if I've made a breakthrough!
I've got all overlays working now! No more distorted pixels or halfway matching overlays. The images look exactly the same in my custom tool as they do in Dosbox
Give me a few days and I'll write up some details (hint: XOR), clean up the code and publish it, and also post some pictures!
Now I need some sleep...
I've got all overlays working now! No more distorted pixels or halfway matching overlays. The images look exactly the same in my custom tool as they do in Dosbox
Give me a few days and I'll write up some details (hint: XOR), clean up the code and publish it, and also post some pictures!
Now I need some sleep...
Re: Legend Entertainment's Games
So, I've finally sat down to write up some details of my adventures. I spent a good many hours staring at the Shannara demo and matched its code with my own disassembly of Death Gate. It really helped a lot! I was able to name a ton of functions and then better grasp what a particular function is doing. Just finding out regular file handling functions helps a lot. I was also able to figure out what parts of the code is responsible for music (XMIDI), sound, fonts, keyboard and mouse handling, playing FLIC animations, some graphics functions (still in progress), and much more. I think I have successfully identified 30-40% of subroutines of the game now
I've uploaded the latest snapshot of my IDA database here: https://github.com/Risca/dgate_ida/blob/main/DGATE.i64
I guess I'm not allowed to share the actual binary I'm working on, but I can at least tell what IDA is telling me:
This is the binary without the DOS/4GW loader attached.
I've also made some updates to my resource manager:
One of the major showstoppers for me, personally, is that I couldn't figure out how to overlay images properly. Some colors would match seemingly correct while others where way off. I did a couple of experiments with using addition and subtraction (with wraparound) between original "background" image and the overlay image, and it would work... for some pixels
I was digging around the Shannara code when I suddenly stumbled upon a function I hadn't seen before:
Could it be so simple? Yes. Yes, it was.
So, overlays in Death Gate works by taking the pixel values of the background and then you XOR it with the pixel values of the overlay image, resulting in a new index into the palette. For transparent parts, the overlay image has a pixel value of 0, which means it will index the same color as before. I haven't tested yet, but I strongly believe the same approach is applied when having multiple overlay images. For example if you have a ship background + steering stone overlay + rune overlay or Xar's office + Xar + his book. I was so close with my experiments before! I just got the wrong operator
Here's the scene with Xar and the right lamp post that I wrote about earlier, with and without the lamp post overlaid. Look how pixel perfect that second picture is! I've cross checked the image with a dump from Dosbox video capture and it looks to be 100% accurate. The images looks a bit darker because I draw a small black border around every pixel when it's zoomed in. Hover the mouse over the pixel and the border turns red. The status bar will show (x, y, value) as you move the mouse around.
I think I've found a couple of places in the Death Gate code where it does this xor overlay. The Shannara draw_xor subroutine would load each pixel value in separate registers and do byte wise xor. This little regex helped me find all 6 instances of this in my Death Gate disassembly.
I think I've found the corresponding "draw_xor" function, but I'll have to do some actual tests to find out. I'm thinking of taking just that subroutine as inline assembly in a small C program to figure out what it does.
There is also some parts of the disassembly that I can't really figure out:
Other than verifying I got the overlay drawing correct, I'm not really sure where to go from here. Any ideas?
I've uploaded the latest snapshot of my IDA database here: https://github.com/Risca/dgate_ida/blob/main/DGATE.i64
I guess I'm not allowed to share the actual binary I'm working on, but I can at least tell what IDA is telling me:
Code: Select all
seg000:00000000 ; Input SHA256 : 483F61E938538251FDC1A76C4748360A1828B139B17BE7501292C7D6FBF8CA45
seg000:00000000 ; Input MD5 : 92F9B77E9CEC656FB1B763E598A87180
seg000:00000000 ; Input CRC32 : A643C474
seg000:00000000
seg000:00000000 ; File Name : /home/risca/.dosbox/C_Drive/SPEL/DGATE/DGATE.LE
seg000:00000000 ; Format : Binary file
seg000:00000000 ; Base Address: 0000h Range: 0000h - A3E65h Loaded length: A3E65h
I've also made some updates to my resource manager:
- Do image/frame rendering myself since Qt seem to render images a bit too large. Noticeable when zooming in and going pixel hunting
- Zoom in more!
- Play voice samples in mono @ 22050 Hz instead of stereo @ 10900 Hz. I found the audio initialization code and was able to figure out the correct parameters
- Successfully overlay images! More on this later
One of the major showstoppers for me, personally, is that I couldn't figure out how to overlay images properly. Some colors would match seemingly correct while others where way off. I did a couple of experiments with using addition and subtraction (with wraparound) between original "background" image and the overlay image, and it would work... for some pixels
I was digging around the Shannara code when I suddenly stumbled upon a function I hadn't seen before:
Code: Select all
draw_xor proc near ; CODE XREF: toggle_cursor+1C↓p
So, overlays in Death Gate works by taking the pixel values of the background and then you XOR it with the pixel values of the overlay image, resulting in a new index into the palette. For transparent parts, the overlay image has a pixel value of 0, which means it will index the same color as before. I haven't tested yet, but I strongly believe the same approach is applied when having multiple overlay images. For example if you have a ship background + steering stone overlay + rune overlay or Xar's office + Xar + his book. I was so close with my experiments before! I just got the wrong operator
Here's the scene with Xar and the right lamp post that I wrote about earlier, with and without the lamp post overlaid. Look how pixel perfect that second picture is! I've cross checked the image with a dump from Dosbox video capture and it looks to be 100% accurate. The images looks a bit darker because I draw a small black border around every pixel when it's zoomed in. Hover the mouse over the pixel and the border turns red. The status bar will show (x, y, value) as you move the mouse around.
I think I've found a couple of places in the Death Gate code where it does this xor overlay. The Shannara draw_xor subroutine would load each pixel value in separate registers and do byte wise xor. This little regex helped me find all 6 instances of this in my Death Gate disassembly.
Code: Select all
xor *([abcd][hl]), (?!\1)[abcd][hl]
There is also some parts of the disassembly that I can't really figure out:
- Chunks of "orphaned" code that nothing seems to reference
- Code that reference stuff that IDA thinks is, and looks like, code but should be variables.
- None of the strings I've found in the binary has any code that reference it
- In places where the code logically should reference a static string or something, I only see a random number (memory address?) being used.
Other than verifying I got the overlay drawing correct, I'm not really sure where to go from here. Any ideas?
Re: Legend Entertainment's Games
Another thought that struck me is that the Shannara demo has a lot of subroutine names that starts with gx*, mostly related to video. I was wondering if it shares any code base with https://github.com/raceintospace
- dreammaster
- ScummVM Developer
- Posts: 559
- Joined: Fri Nov 04, 2005 2:16 am
- Location: San Jose, California, USA
Re: Legend Entertainment's Games
Kudos on the good work. If you've got specific examples of the different kinds of weirdness, I can have a look see and see if I can determine anything.
Otherwise, I'll likely finally get around to working on the Legend games myself as well later on in the year, likely starting with Shannara.. possibly starting with the demo since it has the full coverage, and then branching out to the full game. So maybe we could collaborate at that point if you're interested. Or wait until I'm done and whatever results from my disassembly of the full Shannara game may prove further helpful to Death Gate.. I'd likely move onto Xanth at some point afterwards rather than Death Gate, so it'd be open for you to make your own attempt if you'd like.
DreamMaster.
Otherwise, I'll likely finally get around to working on the Legend games myself as well later on in the year, likely starting with Shannara.. possibly starting with the demo since it has the full coverage, and then branching out to the full game. So maybe we could collaborate at that point if you're interested. Or wait until I'm done and whatever results from my disassembly of the full Shannara game may prove further helpful to Death Gate.. I'd likely move onto Xanth at some point afterwards rather than Death Gate, so it'd be open for you to make your own attempt if you'd like.
DreamMaster.
Re: Legend Entertainment's Games
I would love to collaborate, but I'm afraid I can't really commit I have way too many projects going at the same time, spread over too little time. I usually end up working on each project in short bursts, then life catches up and I have to slow down again. I'm idling on the IRC+Discord channels if you would like to discuss things I find that IM tend to get faster response times.
Regarding IDA and things I don't really understand. There are tons of strings in the binary, but IDA can't find any references to many of these strings.
At the same time, here's an example of error strings being printed before returning from a function. Before each call to fprintf(), a constant memory address is pushed onto the stack. I would love to understand how this offset relates to the strings in the binary. In the Shannara demo, the disassembly looks like this instead:
Sorry about the huge pictures
Regarding IDA and things I don't really understand. There are tons of strings in the binary, but IDA can't find any references to many of these strings.
At the same time, here's an example of error strings being printed before returning from a function. Before each call to fprintf(), a constant memory address is pushed onto the stack. I would love to understand how this offset relates to the strings in the binary. In the Shannara demo, the disassembly looks like this instead:
Code: Select all
cseg01:001575DC setup_demo proc near ; CODE XREF: main+8C↓p
cseg01:001575DC push ebx
cseg01:001575DD push ecx
cseg01:001575DE push edx
cseg01:001575DF push offset aTheDemoCanBeRu ; "The demo can be run directly from the c"...
cseg01:001575E4 push offset _textBuffer
cseg01:001575E9 call fprintf_
cseg01:001575EE add esp, 8
cseg01:001575F1 push offset aThisDemoRequir ; "This demo requires VESA compatible Supe"...
cseg01:001575F6 push offset _textBuffer
cseg01:001575FB xor ecx, ecx
cseg01:001575FD xor ebx, ebx
cseg01:001575FF call fprintf_
cseg01:00157604 add esp, 8
cseg01:00157607 xor edx, edx ; v2
cseg01:00157609 mov eax, offset key ; "LOOPING"
cseg01:0015760E call get_ini
cseg01:00157613 cmp eax, 0FFFFFFFFh
cseg01:00157616 jnz short loc_157679
cseg01:00157618 push offset aDoYouWantTheDe ; "Do you want the demo to loop continuous"...
cseg01:0015761D push offset _textBuffer
cseg01:00157622 call fprintf_
cseg01:00157627 add esp, 8
cseg01:0015762A call get_key_upper
cseg01:0015762F cmp eax, '1'
cseg01:00157632 jb short loc_15763D
cseg01:00157634 jbe short loc_15764C
cseg01:00157636 cmp eax, 'Y'
cseg01:00157639 jz short loc_15764C
cseg01:0015763B jmp short loc_15765D
- dreammaster
- ScummVM Developer
- Posts: 559
- Joined: Fri Nov 04, 2005 2:16 am
- Location: San Jose, California, USA
Re: Legend Entertainment's Games
No worries. Lack of time is always a problem for me too. As far as your questions:
1) Unreferenced strings: the strings are variable length, so it's not just some large fixed size element table. There's always the possibility that the segment mappings are slightly incorrect, and the strings belong to a different offset. The Legend games were notorious in that regard, considering their use of overlays, which I why I had to write the rtlink_decode tool to begin with to properly create something for Companions of Xanth that was decompilable. And I'm still not sure if all the produced segments are 100% correct. Of course, there's also the possibility the string offsets are at some point in disassembly and/or executable that wasn't properly disassembled.
Normally in such cases, I'd work to identify the string drawing routines, and then figure out a way to replicate the display of one of the text strings in question, then use the DosBox debugger to find out if the offset used is the same as in the disassembly. And then also trace backwards from the string drawing routine to find out where the offset comes from.
2) The fprint parameter offsets could be a good lead that the offset definitions are incorrect. Again in such cases I'd tend to use the DosBox Debugger to force the code execution to go into one of the blocks to find out what the resulting message is in _textBuffer, and see if I could correlate it to one of the text strings. Then compare the difference between values being passed to fprintf to see if they correspond to the difference in offsets between surrounding strings in the disassembly. If so, you'd be able to confirm you need to set up a separate data segment for that section of the IDB, such that the offsets like 3404h match the resulting offset for it's matching string.
Hope that helps.
1) Unreferenced strings: the strings are variable length, so it's not just some large fixed size element table. There's always the possibility that the segment mappings are slightly incorrect, and the strings belong to a different offset. The Legend games were notorious in that regard, considering their use of overlays, which I why I had to write the rtlink_decode tool to begin with to properly create something for Companions of Xanth that was decompilable. And I'm still not sure if all the produced segments are 100% correct. Of course, there's also the possibility the string offsets are at some point in disassembly and/or executable that wasn't properly disassembled.
Normally in such cases, I'd work to identify the string drawing routines, and then figure out a way to replicate the display of one of the text strings in question, then use the DosBox debugger to find out if the offset used is the same as in the disassembly. And then also trace backwards from the string drawing routine to find out where the offset comes from.
2) The fprint parameter offsets could be a good lead that the offset definitions are incorrect. Again in such cases I'd tend to use the DosBox Debugger to force the code execution to go into one of the blocks to find out what the resulting message is in _textBuffer, and see if I could correlate it to one of the text strings. Then compare the difference between values being passed to fprintf to see if they correspond to the difference in offsets between surrounding strings in the disassembly. If so, you'd be able to confirm you need to set up a separate data segment for that section of the IDB, such that the offsets like 3404h match the resulting offset for it's matching string.
Hope that helps.