/*
 * pnpdump - PnP BIOS information dumper
 */

#include <windows.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

#include <ntddk.h>

#include <pshpack1.h>

typedef struct _CM_PNP_BIOS_DEVICE_NODE
{
  USHORT Size;
  UCHAR Node;
  ULONG ProductId;
  UCHAR DeviceType[3];
  USHORT DeviceAttributes;
} CM_PNP_BIOS_DEVICE_NODE,*PCM_PNP_BIOS_DEVICE_NODE;

typedef struct _CM_PNP_BIOS_INSTALLATION_CHECK
{
  UCHAR Signature[4];             // $PnP (ascii)
  UCHAR Revision;
  UCHAR Length;
  USHORT ControlField;
  UCHAR Checksum;
  ULONG EventFlagAddress;         // Physical address
  USHORT RealModeEntryOffset;
  USHORT RealModeEntrySegment;
  USHORT ProtectedModeEntryOffset;
  ULONG ProtectedModeCodeBaseAddress;
  ULONG OemDeviceId;
  USHORT RealModeDataBaseAddress;
  ULONG ProtectedModeDataBaseAddress;
} CM_PNP_BIOS_INSTALLATION_CHECK, *PCM_PNP_BIOS_INSTALLATION_CHECK;

#include <poppack.h>

typedef struct _PNP_ID_NAME_
{
  char *PnpId;
  char *DeviceName;
} PNP_ID_NAME, *PPNP_ID_NAME;


static char Hex[] = "0123456789ABCDEF";

static PNP_ID_NAME PnpName[] =
{
  /* Interrupt Controllers */
  {"PNP0000", "AT Interrupt Controller"},
  {"PNP0001", "EISA Interrupt Controller"},
  {"PNP0002", "MCA Interrupt Controller"},
  {"PNP0003", "APIC"},
  {"PNP0004", "Cyrix SLiC MP Interrupt Controller"},

  /* Timers */
  {"PNP0100", "AT Timer"},
  {"PNP0101", "EISA Timer"},
  {"PNP0102", "MCA Timer"},

  /* DMA Controllers */
  {"PNP0200", "AT DMA Controller"},
  {"PNP0201", "EISA DMA Controller"},
  {"PNP0202", "MCA DMA Controller"},

  /* Keyboards */
  {"PNP0300", "IBM PC/XT Keyboard (83 keys)"},
  {"PNP0301", "IBM PC/AT Keyboard (86 keys)"},
  {"PNP0302", "IBM PC/XT Keyboard (84 keys)"},
  {"PNP0303", "IBM Enhanced (101/102 keys)"},
  {"PNP0304", "Olivetti Keyboard (83 keys)"},
  {"PNP0305", "Olivetti Keyboard (102 keys)"},
  {"PNP0306", "Olivetti Keyboard (86 keys)"},
  {"PNP0307", "Microsoft Windows(R) Keyboard"},
  {"PNP0308", "General Input Device Emulation Interface (GIDEI) legacy"},
  {"PNP0309", "Olivetti Keyboard (A101/102 key)"},
  {"PNP030A", "AT&T 302 keyboard"},
  {"PNP030B", "Reserved by Microsoft"},
  {"PNP0320", "Japanese 101-key keyboard"},
  {"PNP0321", "Japanese AX keyboard"},
  {"PNP0322", "Japanese 106-key keyboard A01"},
  {"PNP0323", "Japanese 106-key keyboard 002/003"},
  {"PNP0324", "Japanese 106-key keyboard 001"},
  {"PNP0325", "Japanese Toshiba Desktop keyboard"},
  {"PNP0326", "Japanese Toshiba Laptop keyboard"},
  {"PNP0327", "Japanese Toshiba Notebook keyboard"},
  {"PNP0340", "Korean 84-key keyboard"},
  {"PNP0341", "Korean 86-key keyboard"},
  {"PNP0342", "Korean Enhanced keyboard"},
  {"PNP0343", "Korean Enhanced keyboard 101b"},
  {"PNP0343", "Korean Enhanced keyboard 101c"},
  {"PNP0344", "Korean Enhanced keyboard 103"},

  /* Parallel Ports */
  {"PNP0400", "Standard LPT printer port"},
  {"PNP0401", "ECP printer port"},

  /* Serial Ports */
  {"PNP0500", "Standard PC COM port"},
  {"PNP0501", "16550A-compatible COM port"},
  {"PNP0510", "Generic IRDA-compatible port"},

  /* Harddisk Controllers */
  {"PNP0600", "Generic ESDI/ATA/IDE harddisk controller"},
  {"PNP0601", "Plus Hardcard II"},
  {"PNP0602", "Plus Hardcard IIXL/EZ"},
  {"PNP0603", "Generic IDE supporting Microsoft Device Bay Specification"},

  /* Floppy Controllers */
  {"PNP0700", "PC standard floppy disk controller"},
  {"PNP0701", "Standard floppy controller supporting MS Device Bay Specification"},

  /* obsolete devices */
  {"PNP0800", "Microsoft Sound System compatible device"},

  /* Display Adapters */
  {"PNP0900", "VGA Compatible"},
  {"PNP0901", "Video Seven VRAM/VRAM II/1024i"},
  {"PNP0902", "8514/A Compatible"},
  {"PNP0903", "Trident VGA"},
  {"PNP0904", "Cirrus Logic Laptop VGA"},
  {"PNP0905", "Cirrus Logic VGA"},
  {"PNP0906", "Tseng ET4000"},
  {"PNP0907", "Western Digital VGA"},
  {"PNP0908", "Western Digital Laptop VGA"},
  {"PNP0909", "S3 Inc. 911/924"},
  {"PNP090A", "ATI Ultra Pro/Plus (Mach 32)"},
  {"PNP090B", "ATI Ultra (Mach 8)"},
  {"PNP090C", "XGA Compatible"},
  {"PNP090D", "ATI VGA Wonder"},
  {"PNP090E", "Weitek P9000 Graphics Adapter"},
  {"PNP090F", "Oak Technology VGA"},
  {"PNP0910", "Compaq QVision"},
  {"PNP0911", "XGA/2"},
  {"PNP0912", "Tseng Labs W32/W32i/W32p"},
  {"PNP0913", "S3 Inc. 801/928/964"},
  {"PNP0914", "Cirrus Logic 5429/5434 (memory mapped)"},
  {"PNP0915", "Compaq Advanced VGA (AVGA)"},
  {"PNP0916", "ATI Ultra Pro Turbo (Mach64)"},
  {"PNP0917", "Reserved by Microsoft"},
  {"PNP0918", "Matrox MGA"},
  {"PNP0919", "Compaq QVision 2000"},
  {"PNP091A", "Tseng W128"},
  {"PNP0930", "Chips & Technologies Super VGA"},
  {"PNP0931", "Chips & Technologies Accelerator"},
  {"PNP0940", "NCR 77c22e Super VGA"},
  {"PNP0941", "NCR 77c32blt"},
  {"PNP09FF", "Plug and Play Monitors (VESA DDC)"},

  /* Peripheral Buses */
  {"PNP0A00", "ISA Bus"},
  {"PNP0A01", "EISA Bus"},
  {"PNP0A02", "MCA Bus"},
  {"PNP0A03", "PCI Bus"},
  {"PNP0A04", "VESA/VL Bus"},
  {"PNP0A05", "Generic ACPI Bus"},
  {"PNP0A06", "Generic ACPI Extended-IO Bus (EIO bus)"},

  /* System devices */
  {"PNP0800", "AT-style speaker sound"},
  {"PNP0B00", "AT Real-Time Clock"},
  {"PNP0C00", "Plug and Play BIOS (only created by the root enumerator)"},
  {"PNP0C01", "System Board"},
  {"PNP0C02", "General Plug and Play motherboard registers."},
  {"PNP0C03", "Plug and Play BIOS Event Notification Interrupt"},
  {"PNP0C04", "Math Coprocessor"},
  {"PNP0C05", "APM BIOS (Version independent)"},
  {"PNP0C06", "Reserved for identification of early Plug and Play BIOS implementation"},
  {"PNP0C07", "Reserved for identification of early Plug and Play BIOS implementation"},
  {"PNP0C08", "ACPI system board hardware"},
  {"PNP0C09", "ACPI Embedded Controller"},
  {"PNP0C0A", "ACPI Control Method Battery"},
  {"PNP0C0B", "ACPI Fan"},
  {"PNP0C0C", "ACPI power button device"},
  {"PNP0C0D", "ACPI lid device"},
  {"PNP0C0E", "ACPI sleep button device"},
  {"PNP0C0F", "PCI interrupt link device"},
  {"PNP0C10", "ACPI system indicator device"},
  {"PNP0C11", "ACPI thermal zone"},
  {"PNP0C12", "Device Bay Controller"},

  /* PCMCIA Controllers */
  {"PNP0E00", "Intel 82365-Compatible PCMCIA Controller"},
  {"PNP0E01", "Cirrus Logic CL-PD6720 PCMCIA Controller"},
  {"PNP0E02", "VLSI VL82C146 PCMCIA Controller"},
  {"PNP0E03", "Intel 82365-compatible CardBus controller"},

  /* Mice */
  {"PNP0F00", "Microsoft Bus Mouse"},
  {"PNP0F01", "Microsoft Serial Mouse"},
  {"PNP0F02", "Microsoft InPort Mouse"},
  {"PNP0F03", "Microsoft PS/2-style Mouse"},
  {"PNP0F04", "Mouse Systems Mouse"},
  {"PNP0F05", "Mouse Systems 3-Button Mouse (COM2)"},
  {"PNP0F06", "Genius Mouse (COM1)"},
  {"PNP0F07", "Genius Mouse (COM2)"},
  {"PNP0F08", "Logitech Serial Mouse"},
  {"PNP0F09", "Microsoft BallPoint Serial Mouse"},
  {"PNP0F0A", "Microsoft Plug and Play Mouse"},
  {"PNP0F0B", "Microsoft Plug and Play BallPoint Mouse"},
  {"PNP0F0C", "Microsoft-compatible Serial Mouse"},
  {"PNP0F0D", "Microsoft-compatible InPort-compatible Mouse"},
  {"PNP0F0E", "Microsoft-compatible PS/2-style Mouse"},
  {"PNP0F0F", "Microsoft-compatible Serial BallPoint-compatible Mouse"},
  {"PNP0F10", "Texas Instruments QuickPort Mouse"},
  {"PNP0F11", "Microsoft-compatible Bus Mouse"},
  {"PNP0F12", "Logitech PS/2-style Mouse"},
  {"PNP0F13", "PS/2 Port for PS/2-style Mice"},
  {"PNP0F14", "Microsoft Kids Mouse"},
  {"PNP0F15", "Logitech bus mouse"},
  {"PNP0F16", "Logitech SWIFT device"},
  {"PNP0F17", "Logitech-compatible serial mouse"},
  {"PNP0F18", "Logitech-compatible bus mouse"},
  {"PNP0F19", "Logitech-compatible PS/2-style Mouse"},
  {"PNP0F1A", "Logitech-compatible SWIFT Device"},
  {"PNP0F1B", "HP Omnibook Mouse"},
  {"PNP0F1C", "Compaq LTE Trackball PS/2-style Mouse"},
  {"PNP0F1D", "Compaq LTE Trackball Serial Mouse"},
  {"PNP0F1E", "Microsoft Kids Trackball Mouse"},
  {"PNP0F1F", "Reserved by Microsoft Input Device Group"},
  {"PNP0F20", "Reserved by Microsoft Input Device Group"},
  {"PNP0F21", "Reserved by Microsoft Input Device Group"},
  {"PNP0F22", "Reserved by Microsoft Input Device Group"},
  {"PNP0F23", "Reserved by Microsoft Input Device Group"},
  {"PNP0FFF", "Reserved by Microsoft Systems"},

  /* List Terminator */
  {NULL, NULL}
};


/* FUNCTIONS ****************************************************************/

static char *
GetDeviceName(char *PnpId)
{
  PPNP_ID_NAME IdName;

  IdName = PnpName;
  while (IdName->PnpId != NULL)
    {
      if (!strcmp(IdName->PnpId, PnpId))
	return IdName->DeviceName;

      IdName++;
    }

  return "Unknown Device";
}


LONG
GetPnpKey(PHKEY PnpKey)
{
  LONG lError;
  char szBuffer[80];
  HKEY hAdapterKey;
  HKEY hBusKey;
  DWORD dwBus;
  DWORD dwType;
  DWORD dwSize;

  *PnpKey = 0;

  lError = RegOpenKey(HKEY_LOCAL_MACHINE,
		      "HARDWARE\\DESCRIPTION\\System\\MultifunctionAdapter",
		      &hAdapterKey);
  if (lError != ERROR_SUCCESS)
    return 0;

  /* Enumerate buses */
  for (dwBus = 0; ; dwBus++)
    {
      sprintf(szBuffer, "%lu", dwBus);

      lError = RegOpenKey(hAdapterKey,
			  szBuffer,
			  &hBusKey);
      if (lError != ERROR_SUCCESS)
	{
	  RegCloseKey(hAdapterKey);
	  return lError;
	}

      dwSize = 80;
      lError = RegQueryValueEx(hBusKey,
			       "Identifier",
			       NULL,
			       &dwType,
			       (LPBYTE)szBuffer,
			       &dwSize);
      if (lError != ERROR_SUCCESS)
	{
	  RegCloseKey(hBusKey);
	  RegCloseKey(hAdapterKey);
	  return lError;
	}

      if (dwType == REG_SZ && _stricmp(szBuffer, "pnp bios") == 0)
	{
	  *PnpKey = hBusKey;
	  RegCloseKey(hAdapterKey);
	  return ERROR_SUCCESS;
	}

      RegCloseKey(hBusKey);
    }

  return 1;
}


static VOID
PnpDecodeIrq(unsigned char *Ptr)
{
  USHORT IrqMask;
  int i;

  IrqMask = *Ptr;
  Ptr++;
  IrqMask |= (*Ptr << 8);

  printf("      IRQs:");

  for (i = 0; i < 16; i++)
    {
      if (IrqMask & (1 << i))
	{
	  printf(" %u", i);
	}
    }

  printf("\n");
}


static VOID
PnpDecodeDma(unsigned char *Ptr)
{
  unsigned char DmaChannel;
  unsigned char DmaStatus;
  int i;

  DmaChannel = *Ptr;
  Ptr++;
  DmaStatus = *Ptr;

  printf("      DMAs:");

  for (i = 0; i < 8; i++)
    {
      if (DmaChannel & (1 << i))
	{
	  printf(" %u", i);
	}
    }

  printf("\n");
}


static VOID
PnpDecodeIoPort(unsigned char *Ptr)
{
  USHORT MinBase;
  USHORT MaxBase;
  UCHAR Align;
  UCHAR Length;

  // Info = *Ptr;
  Ptr++;
  MinBase = *Ptr;
  Ptr++;
  MinBase += (*Ptr << 8);
  Ptr++;
  MaxBase = *Ptr;
  Ptr++;
  MaxBase += (*Ptr << 8);
  Ptr++;
  Align = *Ptr;
  Ptr++;
  Length = *Ptr;

  printf("  I/O Port descriptor\n");
  printf("    MinBase 0x%x  MaxBase 0x%x  Align %u  Length %u\n",
	 MinBase, MaxBase, Align, Length);
}


static VOID
PnpDecodeFixedIoPort(unsigned char *Ptr)
{
  USHORT IoPort;
  UCHAR Length;

  IoPort = *Ptr;
  Ptr++;
  IoPort += (*Ptr << 8);
  Ptr++;
  Length = *Ptr;

  printf("  Fixed I/O Port descriptor\n");
  printf("    PortBase 0x%hx  Length 0x%x\n",
	 IoPort, Length);

#if 0
  if (Length == 1)
    {
      printf("  Fixed location I/O Port descriptor: 0x%x\n",
	     IoPort);
    }
  else
    {
      printf("  Fixed location I/O Port descriptor: 0x%x - 0x%x\n",
	     IoPort,
	     IoPort + Length - 1);
    }
#endif
}


static VOID
PnpDecodeMemory16(unsigned char *Ptr)
{
  UCHAR Info;
  USHORT MinBase;
  USHORT MaxBase;
  USHORT Align;
  USHORT Length;

  Info = *Ptr;
  Ptr++;

  MinBase = *Ptr;
  Ptr++;
  MinBase += (*Ptr << 8);
  Ptr++;

  MaxBase = *Ptr;
  Ptr++;
  MaxBase += (*Ptr << 8);
  Ptr++;

  Align = *Ptr;
  Ptr++;
  Align += (*Ptr << 8);
  Ptr++;

  Length = *Ptr;
  Ptr++;
  Length += (*Ptr << 8);

  printf("  16-Bit memory range descriptor\n");
  printf("    MinBase 0x%hx  MaxBase 0x%hx  Align 0x%hx  Length 0x%hx  Flags 0x%02x\n",
	 MinBase, MaxBase, Align,Length, Info);
}


static VOID
PnpDecodeMemory32(unsigned char *Ptr)
{
  UCHAR Info;
  ULONG MinBase;
  ULONG MaxBase;
  ULONG Align;
  ULONG Length;

  Info = *Ptr;
  Ptr++;

  MinBase = *Ptr;
  Ptr++;
  MinBase += (*Ptr << 8);
  Ptr++;
  MinBase += (*Ptr << 16);
  Ptr++;
  MinBase += (*Ptr << 24);
  Ptr++;

  MaxBase = *Ptr;
  Ptr++;
  MaxBase += (*Ptr << 8);
  Ptr++;
  MaxBase += (*Ptr << 16);
  Ptr++;
  MaxBase += (*Ptr << 24);
  Ptr++;

  Align = *Ptr;
  Ptr++;
  Align += (*Ptr << 8);
  Ptr++;
  Align += (*Ptr << 16);
  Ptr++;
  Align += (*Ptr << 24);
  Ptr++;

  Length = *Ptr;
  Ptr++;
  Length += (*Ptr << 8);
  Ptr++;
  Length += (*Ptr << 16);
  Ptr++;
  Length += (*Ptr << 24);

  printf("  32-Bit memory range descriptor\n");
  printf("    MinBase 0x%lx  MaxBase 0x%lx  Align 0x%lx  Length 0x%lx  Flags 0x%02x\n",
	 MinBase, MaxBase, Align,Length, Info);
}


static VOID
PnpDecodeFixedMemory(unsigned char *Ptr)
{
  UCHAR Info;
  ULONG Base;
  ULONG Length;

  Info = *Ptr;
  Ptr++;

  Base = *Ptr;
  Ptr++;
  Base += (*Ptr << 8);
  Ptr++;
  Base += (*Ptr << 16);
  Ptr++;
  Base += (*Ptr << 24);
  Ptr++;

  Length = *Ptr;
  Ptr++;
  Length += (*Ptr << 8);
  Ptr++;
  Length += (*Ptr << 16);
  Ptr++;
  Length += (*Ptr << 24);

  printf("  32-Bit fixed location memory range descriptor\n");
  printf("    Base 0x%lx  Length 0x%lx  Flags 0x%02x\n",
	 Base, Length, Info);
}


void PrintDeviceData (PCM_PNP_BIOS_DEVICE_NODE DeviceNode)
{
  char PnpId[8];
  unsigned char *Ptr;
  unsigned int TagSize;
  unsigned int TagType;

  unsigned char Id[4];

  printf ("Node: %x  Size %hu (0x%hx)\n",
	  DeviceNode->Node,
	  DeviceNode->Size,
	  DeviceNode->Size);

  memcpy(Id, &DeviceNode->ProductId, 4);

  PnpId[0] = ((Id[0] >> 2) & 0x1F) + 0x40;
  PnpId[1] = ((Id[0] << 3) & 0x18) +
	     ((Id[1] >> 5) & 0x07) + 0x40;
  PnpId[2] = (Id[1] & 0x1F) + 0x40;

  PnpId[3] = Hex[(Id[2] >> 4) & 0xF];
  PnpId[4] = Hex[Id[2] & 0x0F];

  PnpId[5] = Hex[(Id[3] >> 4) & 0x0F];
  PnpId[6] = Hex[Id[3] & 0x0F];
  PnpId[7] = 0;

  printf("  '%s' (%s)\n",
	 PnpId, GetDeviceName(PnpId));

  if (DeviceNode->Size > sizeof(CM_PNP_BIOS_DEVICE_NODE))
    {
      Ptr = (unsigned char *)(DeviceNode + 1);
      while (TRUE)
	{
	  if (*Ptr & 0x80)
	    {
	      TagType = *Ptr & 0x7F;
	      Ptr++;
	      TagSize = *Ptr;
	      Ptr++;
	      TagSize += (*Ptr << 16);
	      Ptr++;


	      switch (TagType)
		{
		  case 1:
		    PnpDecodeMemory16(Ptr);
		    break;

		  case 5:
		    PnpDecodeMemory32(Ptr);
		    break;

		  case 6:
		    PnpDecodeFixedMemory(Ptr);
		    break;

		  default:
		    printf("      Large tag: type %u  size %u\n",
			   TagType,
			   TagSize);
		    break;
		}
	    }
	  else
	    {
	      TagType = (*Ptr >> 3) & 0x0F;
	      TagSize = *Ptr & 0x07;
	      Ptr++;

	      switch (TagType)
		{
		  case 2:
		    printf("      Logical device ID\n");
		    break;

		  case 3:
		    printf("      Compatible device ID\n");
		    break;

		  case 4:
		    PnpDecodeIrq(Ptr);
		    break;

		  case 5:
		    PnpDecodeDma(Ptr);
		    break;

		  case 8:
		    PnpDecodeIoPort(Ptr);
		    break;

		  case 9:
		    PnpDecodeFixedIoPort(Ptr);
		    break;

		  case 0x0F: /* end tag */
		    break;

		  default:
		    printf("      Small tag: type %u  size %u\n",
			   TagType,
			   TagSize);
		    break;
		}

	      /* end tag */
	      if (TagType == 0x0F)
		break;
	    }

	  Ptr = Ptr + TagSize;
	}
    }
}


int main (int argc, char *argv[])
{
  LONG lError;
  HKEY hPnpKey;
  DWORD dwType;
  DWORD dwSize;
  BOOL Ask;
  PCM_FULL_RESOURCE_DESCRIPTOR lpBuffer;
  PCM_PNP_BIOS_INSTALLATION_CHECK lpPnpInst;
  PCM_PNP_BIOS_DEVICE_NODE lpDevNode;
  DWORD dwDataSize;
  DWORD dwResourceSize;

  hPnpKey = 0;

  Ask = TRUE;
  if (argc >1 && (!strcmp(argv[1],"/S") || !strcmp(argv[1],"/s")))
    {
      Ask = FALSE;
    }

  if (argc >1 && !strcmp(argv[1],"/?"))
    {
      printf("This utility prints the PnP-nodes from the registry\n");
      printf("\"/s\" prevents the \"Press any key\"\n\n");
      return 0;
    }

  lError = GetPnpKey(&hPnpKey);
  if (lError != ERROR_SUCCESS)
    {
      printf("Failed to get PnP-BIOS key\n");
      return 0;
    }

  if (hPnpKey != 0)
    {
      printf("Found PnP-BIOS key\n");
    }

  /* Allocate buffer */
  dwSize = 2048;
  lpBuffer = malloc(dwSize);
  if (lpBuffer == NULL)
    {
      printf("Error: malloc() failed\n");
      RegCloseKey(hPnpKey);
      return 0;
    }

  do
    {
      lError = RegQueryValueEx(hPnpKey,
			       "Configuration Data",
			       NULL,
			       &dwType,
			       (LPBYTE)lpBuffer,
			       &dwSize);
      if (lError == ERROR_MORE_DATA)
        {
          lpBuffer = realloc(lpBuffer, dwSize);
          if (lpBuffer == NULL)
            {
              printf("Error: realloc() of %u bytes failed\n", (unsigned) dwSize);
              RegCloseKey(hPnpKey);
              return 0;
            }
        }
    }
  while (lError == ERROR_MORE_DATA);
  if (lError != ERROR_SUCCESS)
    {
      printf("Failed to read 'Configuration Data' value\n");
      free(lpBuffer);
      RegCloseKey(hPnpKey);
      return 0;
    }

//  printf ("Data size: %lu\n", dwSize);

  RegCloseKey(hPnpKey);

//  printf("Resource count %lu\n", lpBuffer->PartialResourceList.Count);

  if (lpBuffer->PartialResourceList.Count == 0)
    {
      printf("Invalid resource count!\n");
      free(lpBuffer);
      return 0;
    }

//  printf("lpBuffer %p\n", lpBuffer);

  dwResourceSize = lpBuffer->PartialResourceList.PartialDescriptors[0].u.DeviceSpecificData.DataSize;
//  printf("ResourceSize: %lu\n", dwResourceSize);

  lpPnpInst = (PCM_PNP_BIOS_INSTALLATION_CHECK)
	((ULONG_PTR)(&lpBuffer->PartialResourceList.PartialDescriptors[0]) +
	  sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));

//  printf("lpPnpInst %p\n", lpPnpInst);

  printf("Signature '%.4s'\n", lpPnpInst->Signature);
  if (strncmp((PCHAR)lpPnpInst->Signature, "$PnP", 4))
    {
      printf("Error: Invalid PnP signature\n");
      free(lpBuffer);
      return 0;
    }

//  printf("InstCheck length: %lu\n", lpPnpInst->Length);

  dwDataSize = sizeof(CM_PNP_BIOS_INSTALLATION_CHECK);
  lpDevNode = (PCM_PNP_BIOS_DEVICE_NODE)((DWORD)lpPnpInst + sizeof(CM_PNP_BIOS_INSTALLATION_CHECK));

  if (lpDevNode->Size == 0)
    {
      printf("Error: Device node size is zero!\n");
      return 0;
    }

#if 0
      printf("Node: %x  Size %hu (0x%hx)\n",
	      lpDevNode->Node,
	      lpDevNode->Size,
	      lpDevNode->Size);

  printf("Done.\n");
return 0;
#endif


  while (dwDataSize < dwResourceSize)
    {
      if (lpDevNode->Size == 0)
	break;

      printf("Node: %x  Size %hu (0x%hx)\n",
	     lpDevNode->Node,
	     lpDevNode->Size,
	     lpDevNode->Size);

      dwDataSize += lpDevNode->Size;
      lpDevNode = (PCM_PNP_BIOS_DEVICE_NODE)((DWORD)lpDevNode + lpDevNode->Size);
    }

  if (Ask)
    {
      printf("\n Press any key...\n");
      getch();
    }
  else
    {
      printf("\n");
    }

  dwDataSize = sizeof(CM_PNP_BIOS_INSTALLATION_CHECK);
  lpDevNode = (PCM_PNP_BIOS_DEVICE_NODE)((DWORD)lpPnpInst + sizeof(CM_PNP_BIOS_INSTALLATION_CHECK));

  while (dwDataSize < dwResourceSize)
    {
      if (lpDevNode->Size == 0)
	break;

      PrintDeviceData(lpDevNode);

      if (Ask)
        {
          printf("\n Press any key...\n");
          getch();
        }
      else
        {
          printf("\n");
        }

      dwDataSize += lpDevNode->Size;
      lpDevNode = (PCM_PNP_BIOS_DEVICE_NODE)((DWORD)lpDevNode + lpDevNode->Size);
    }

  free(lpBuffer);

  return 0;
}

/* EOF */
