Legend Entertainment's Games

All the inane chatter goes in here. If you're curious about whether we will support a game, post HERE not in General Discussion :)

Moderator: ScummVM Team

Risca
Posts: 18
Joined: Mon May 07, 2012 10:33 pm

Re: Legend Entertainment's Games

Post by Risca »

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 :D
Risca
Posts: 18
Joined: Mon May 07, 2012 10:33 pm

Re: Legend Entertainment's Games

Post by Risca »

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! :idea:

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! :wink:

Now I need some sleep...
Risca
Posts: 18
Joined: Mon May 07, 2012 10:33 pm

Re: Legend Entertainment's Games

Post by Risca »

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 :D

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
This is the binary without the DOS/4GW loader attached.

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
The code is as always available on GitHub: https://github.com/Risca/dgate_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:

Code: Select all

draw_xor        proc near               ; CODE XREF: toggle_cursor+1C↓p
Could it be so simple? :o 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 :evil:

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! :D 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.
dgate_no_overlay.png
dgate_no_overlay.png (220.68 KiB) Viewed 6996 times
dgate_overlay.png
dgate_overlay.png (229.04 KiB) Viewed 6996 times
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]
 
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:
  • 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.
I would appreciate if someone more experienced would tell me what is going on!

Other than verifying I got the overlay drawing correct, I'm not really sure where to go from here. Any ideas?
Risca
Posts: 18
Joined: Mon May 07, 2012 10:33 pm

Re: Legend Entertainment's Games

Post by Risca »

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 :?:
User avatar
dreammaster
ScummVM Developer
Posts: 559
Joined: Fri Nov 04, 2005 2:16 am
Location: San Jose, California, USA

Re: Legend Entertainment's Games

Post by dreammaster »

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.
Risca
Posts: 18
Joined: Mon May 07, 2012 10:33 pm

Re: Legend Entertainment's Games

Post by Risca »

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.
no_string_refs.png
no_string_refs.png (363.51 KiB) Viewed 6720 times

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.
string_ref.png
string_ref.png (192.4 KiB) Viewed 6720 times
error_messages.png
error_messages.png (201.69 KiB) Viewed 6720 times
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
Sorry about the huge pictures :oops:
User avatar
dreammaster
ScummVM Developer
Posts: 559
Joined: Fri Nov 04, 2005 2:16 am
Location: San Jose, California, USA

Re: Legend Entertainment's Games

Post by dreammaster »

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.
Post Reply