leecher1337 / ntvdmx64

Run Microsoft Windows NTVDM (DOS) on 64bit Editions
820 stars 81 forks source link

Mission Chameleon #185

Open MrPepka opened 2 years ago

MrPepka commented 2 years ago

Perhaps I can find help for this game here I can't run it on Windows 10 (this game doesn't work on XP anymore). The game is built very unusually: The menu of this game is the Win32 application, but the game itself is in the Win16 architecture (though not entirely because the as.exe file that runs it is also a DOS program). The problem is that the game depends on the VXD drivers it comes with, which cannot be loaded on Windows XP and newer systems, making the game impossible to run. The WineVDM developers were unable to fix this, so maybe you could? The game can be downloaded from old-games.ru, for example - https://www.old-games.ru/game/download/3426.html

leecher1337 commented 2 years ago

As far as I can see, the VXD is just an IPC mechanism between the DOS application and the Win16 EXE. It basically does the following (pseudocode):

typedef struct tagRegBuf
{
   unsigned short   Client_BX ;
   unsigned short   Client_CX ;
   unsigned short   Client_AX ;
   unsigned short   Client_DX ;
} REGBUF;

REGBUF reg_buffer[196];

// Windows End (PM = Protected Mode):
void PM_0(CRS_16 *a1)
{
    switch ( a1->Client_DI )
    {
      case 0:
        Param1 = a1->Client_AX;
        pfnFuncOffs = a1->Client_BX;
        pfnFuncSelector = a1->Client_CX;
        break;
      case 1:
        a1->Client_BX = reg_buffer[reg_buffer_offset].Client_BX;
        a1->Client_CX = reg_buffer[reg_buffer_offset].Client_CX;
        a1->Client_AX = reg_buffer[reg_buffer_offset].Client_AX;
        a1->Client_DX = reg_buffer[reg_buffer_offset].Client_DX;
        reg_buffer_offset++;
        break;
      case 2:
        reg_buffer[reg_buffer_offset].Client_BX = a1->Client_BX;
        reg_buffer[reg_buffer_offset].Client_CX = a1->Client_CX;
        reg_buffer[reg_buffer_offset].Client_AX = a1->Client_AX;
        reg_buffer[reg_buffer_offset].Client_DX = a1->Client_DX;
        reg_buffer_offset++;
        break;
      case 3:
        func3_v86_bx = 0;
        a1->Client_Flags &= ~CF_Mask;  // CLC
        break;
      case 4:
        a1->Client_BX = func4_bx;
        reg_buffer_offset = 0;
        break;
      case 5:
        func4_bx = a1->Client_BX;
        reg_buffer_offset = 0;
        break;
      case 6:
        func6_v86_bx = a1->Client_BX;
        func4_bx = 0;
        reg_buffer_offset = 0;
        func3_v86_bx = 0;
        func7_v86_bx = 0;
        break;
      case 7:
        func7_v86_bx = a1->Client_BX;
        break;
    }
  }
  return result;
}

// DOS end (V86)
void V86_0(CRS_16 *a1)
{
    switch ( a1->Client_DI )
    {
      case 0:
        pfnFunc(Param1, 0x500, a1->Client_BX, a1->Client_DX, a1->Client_AX);
        break;
      case 1:
        a1->Client_BX = reg_buffer[reg_buffer_offset].Client_BX;
        a1->Client_CX = reg_buffer[reg_buffer_offset].Client_CX;
        a1->Client_AX = reg_buffer[reg_buffer_offset].Client_AX;
        a1->Client_DX = reg_buffer[reg_buffer_offset].Client_DX;
        reg_buffer_offset++;
        break;
      case 2:
        reg_buffer[reg_buffer_offset].Client_BX = a1->Client_BX;
        reg_buffer[reg_buffer_offset].Client_CX = a1->Client_CX;
        reg_buffer[reg_buffer_offset].Client_AX = a1->Client_AX;
        reg_buffer[reg_buffer_offset].Client_DX = a1->Client_DX;
        reg_buffer_offset++;
        break;
      case 3:
        a1->Client_BX = func3_v86_bx;
        break;
      case 4:
        a1->Client_BX = func4_bx;
        reg_buffer_offset = 0;
        break;
      case 5:
        func4_bx = a1->Client_BX;
        reg_buffer_offset = 0;
        func3_v86_bx = 1;
        break;
      case 6:
        a1->Client_BX = func6_v86_bx;
        break;
      case 7:
        a1->Client_BX = func7_v86_bx;
        break;
      }
      a1->Client_Flags &= ~CF_Mask;  // CLC
}

The pfnFunc in fact is a wrapper to PostMessage() inside the 16bit code (see winntu.exe). Param1 is hWnd. So it's basically:

PostMessage(hWnd, WM_USER + 0x100, a1->Client_BX, (a1->Client_DX << 16 |  a1->Client_AX));

So you maybe can try to write a DOS device driver to just load the VXD by trapping 1684h of INT 2F etc., just the usual VXD stuff, as this VXD doesn't do any Kernelmode stuff that would be incompatible. Hope that helps.

kristibektashi commented 1 year ago

Use a virtualization software (e.g. VirtualBox, VMware Player (or VMware Workstation if you can afford it)) or emulation software (e.g. PCEm, 86box, Qemu) and install Windows 98 SE on it. Then your game should run since Windows 98 supports both Win16/Win32 apps and VxD DOS drivers

Downloads for Windows 98: https://winworldpc.com/product/windows-98/98-second-edition (you most likely will also need a boot floppy)

Google is your friend for more info on how to install including drivers etc

MrPepka commented 1 year ago

I have a virtual machine with Win 98, but I preferred to run the game on Win 11, but from what I see it is not possible

leecher1337 commented 1 year ago

Of course, it's technically possible, I previously already wrote about how it is done (write VDD, trap 1884h of INT 2f).

In the meantime, I also wrote a litte loader for it as a proof-of-concept so that it runs with NTVDM (tested on Windows XP, 7, 10 32bit), but I guess you wouldn't be too happy with its performance. Interestingly, the performance is better with NTVDMx64 than with original NTVDM, mainly because you need SolVBE starting with Win 7 (due to lack of fullscreen support and your game requires VESA) which seems to be slower than the inbuilt VGA emulation of the NTVDMx64. Nevertheless, I found it a bit more unreliable to NTVDMx64 on whether the sound starts or not, may be some race condition. And return to mein menu also doesn't currently work on closing the game, it just prompts to insert the CD. I don't think that its playability is very good performane-wise. But if you are interested, I can put up the code and files on github, if you want to experiment with it.

leecher1337 commented 1 year ago

Here is my VDD driver if you want to try it out: https://github.com/leecher1337/antzvdd