Source file: /~heha/hs/inpout32-hs.zip/Redir.cpp

     1  #include <windows.h>
     2  #include <winioctl.h>
     3  #include <stdio.h>
     4  #include <stdlib.h>	// strtoul()
     5  #include <tchar.h>	// _tcsdup()
     6  EXTERN_C{
     7  #include <hidsdi.h>
     8  #include <hidpi.h>
     9  }
    10  #include <setupapi.h>
    11  #include "usb2lpt.h"
    12  #include "Redir.h"
    13  #include "inpout32.h"
    14  
    15  #include <conio.h>
    16  #ifdef UNICODE
    17  # define TS "S"
    18  #else
    19  # define TS "s"
    20  #endif
    21  
    22  // This source code relies on tabsize == 8
    23  
    24  /*****************************
    25   * True dynamic entry points *
    26   *****************************/
    27  
    28  #ifdef _M_IX86
    29  // This header seems to require _stdcall (/Gz) compiler option!
    30  #include <nt_vdd.h>
    31  
    32  // Dynamic entries ensure compatibility to Windows NT 4.0 and possibly NT 3.51
    33  struct _setupapi setupapi;
    34  const char setupapi_names[]=
    35   "setupapi\0"
    36   "SetupDiEnumDeviceInterfaces\0"	// Win2k++, Win98++
    37   "SetupDiGetDeviceInterfaceDetailA\0";
    38  
    39  struct _hid hid;
    40  const char hid_names[]=
    41   "hid\0"				// Win2k++, Win98++
    42   "HidD_GetHidGuid\0"
    43   "HidD_GetPreparsedData\0"
    44   "HidD_GetFeature\0"
    45   "HidD_SetFeature\0"
    46   "HidD_GetAttributes\0"
    47   "HidP_GetCaps\0";
    48   
    49  struct _krnl krnl;
    50  const char krnl_names[]=
    51   "kernel32\0"
    52   "CreateRemoteThread\0"
    53   "OpenThread\0"
    54   "CancelIo\0"				// Win2k++
    55   "AddVectoredExceptionHandler\0"
    56   "RemoveVectoredExceptionHandler\0"
    57   "IsWow64Process\0"			// WinXP++ 64bit
    58   "Wow64DisableWow64FsRedirection\0"
    59   "Wow64RevertWow64FsRedirection\0";
    60  
    61  static struct _vdm{
    62   HINSTANCE hLib;
    63   BOOL (WINAPI*InstallIOHook)(HANDLE,WORD,PVDD_IO_PORTRANGE,PVDD_IO_HANDLERS);
    64   VOID (WINAPI*DeInstallIOHook)(HANDLE,WORD,PVDD_IO_PORTRANGE);
    65  }vdm;
    66  static const char vdm_names[]=
    67   "\0"
    68   "VDDInstallIOHook\0"
    69   "VDDDeInstallIOHook\0";
    70  
    71  bool _fastcall dynaload(HMODULE &hLib,const char*e) {
    72   if (hLib==INVALID_HANDLE_VALUE) return false;	// already failed
    73   if (!hLib && !(hLib=LoadLibraryA(e))) {
    74    hLib--;					// don't try again
    75    return false;					// failed
    76   }
    77   FARPROC *proc=(FARPROC*)&hLib;
    78   bool ret=true;
    79   while (++proc,*(e+=strlen(e)+1)) {
    80    if (!*proc && !(*proc=GetProcAddress(hLib,e))) ret=false;
    81   }
    82   return ret;					// true when all entry points are OK
    83  }
    84  
    85  static bool wdmHooked;
    86  #endif
    87  
    88  /*****************
    89   * Debug Console *
    90   *****************/
    91  
    92  REDIR RedirInfo[9];	// 0..2: Automatic, for standard addresses, 3..9: Via OpenLpt()
    93  struct _con{
    94   HANDLE hOut;
    95   CONSOLE_SCREEN_BUFFER_INFO csbi;
    96   void Init();
    97   void SetColor(BYTE c) const;
    98   void SetGreenOrRed(BOOL) const;
    99   void SetNormal() const;
   100  }con;
   101  
   102  void _con::Init() {
   103   AllocConsole();
   104   hOut=GetStdHandle(STD_OUTPUT_HANDLE);
   105   GetConsoleScreenBufferInfo(hOut,&csbi);
   106  }
   107  
   108  // Sets given foreground color for lightgray text color, with high intensity.
   109  // For other text colors, distinct foregrounds are produced.
   110  void _con::SetColor(BYTE c) const{
   111   SetConsoleTextAttribute(hOut,csbi.wAttributes
   112   ^(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY)
   113   ^c);
   114  }
   115  
   116  void _con::SetGreenOrRed(BOOL green) const{
   117   SetColor(green?FOREGROUND_GREEN:FOREGROUND_RED);
   118  }
   119  
   120  void _con::SetNormal() const{
   121   SetConsoleTextAttribute(hOut,csbi.wAttributes);
   122  }
   123  
   124  // returns a bit array, each of three bits for a changed LPT entry in BIOS data area 40:08 .. 40:10
   125  // See Ralf Brown Interrupt List : Memory for meanings.
   126  // Free slots (i.e. a non-available LPT2) are not checked to be free (zero).
   127  // The BIOS data area is used by WGif12NT.exe, at least.
   128  static char EnsureBiosDataArea() {
   129   HANDLE h;
   130   WORD*bios=(WORD*)MapPhysToLin((void*)0x408,0x0A,&h);
   131   if (!bios) return -1;
   132   char ret=0;
   133   for (int i=0; i<3; i++) {
   134    if (RedirInfo[i].where==1) {
   135     if (bios[i]!=LOWORD(RedirInfo[i].addr)) {
   136      bios[i]=LOWORD(RedirInfo[i].addr);
   137      ret|=1<<i;
   138     }
   139     if (bios[4]>>14<i+1) {	// Ensure that "Installed hardware" reflects the maximum parallel port number
   140      bios[4]=bios[4]&0x3FFF|(i+1)<<14;
   141      ret|=1<<i;
   142     }
   143    }
   144   }
   145   UnmapPhysicalMemory(h,bios);
   146   return ret;
   147  }
   148  
   149  static void StartType(int typ) {
   150   typ=setstarttype(typ);
   151   static const char *starttypes[]={"BOOT","SYSTEM","AUTO","DEMAND","DISABLED"};
   152   if ((unsigned)typ>SERVICE_DISABLED) {
   153    con.SetColor(FOREGROUND_RED);
   154    _cprintf("Error code %d getting/setting StartType value!",typ);
   155   }else{
   156    con.SetColor(FOREGROUND_GREEN|FOREGROUND_BLUE);
   157    _cprintf("StartType = SERVICE_%s%s",starttypes[typ],typ!=SERVICE_DISABLED?"_START":"");
   158   }
   159   con.SetNormal();
   160   _cprintf("\n\n");
   161  }
   162  
   163  static void ShowAssignment() {
   164   int e=elemof(RedirInfo)-1;
   165   for (;e>2;e--) if (RedirInfo[e].where) break;
   166   for (int i=0; i<=e; i++) {
   167    _cprintf("LPT%d: ",i+1);
   168    switch (RedirInfo[i].where) {
   169     case 0: _cprintf("free"); break;
   170     case 1: _cprintf("SPP=0x%X, ECP=0x%X",LOWORD(RedirInfo[i].addr),HIWORD(RedirInfo[i].addr)); break;
   171     case 2: _cprintf("USB2LPT (native, \\\\.\\%" TS ")",RedirInfo[i].name); break;
   172     case 3: _cprintf("USB2LPT (HID, %.50" TS "...)",RedirInfo[i].name); break;
   173     case 4: _cprintf("USB->Prn (%.50" TS "...)",RedirInfo[i].name); break;
   174  //   case 5: _cprintf("FT232 BitBang mode",RedirInfo[i].name); break;
   175  //   case 6: _cprintf("V-USB PowerSwitch",RedirInfo[i].name); break;
   176  //   case 7: _cprintf("UsbLotIo",RedirInfo[i].name); break;
   177     default: _cprintf("unknown (%d)",RedirInfo[i].where);
   178    }
   179    _cprintf("\n");
   180   }
   181   BOOL ok=IsInpOutDriverOpen();
   182   con.SetGreenOrRed(ok);
   183   _cprintf("\nKernel-mode driver is %s\n\n",sysver<0?"not necessary.":ok?"loaded.":"NOT loaded!");
   184  #ifdef _M_IX86
   185   BOOL w64=IsXP64Bit();
   186   if (sysver>=0 && !w64) {
   187    con.SetGreenOrRed(wdmHooked);
   188    _cprintf("DOS box & Win16 LPT redirection is %s\n\n",wdmHooked?"active.":"NOT active!");
   189   }
   190  #endif
   191   if (ok) {			// requires Win9x or loaded driver
   192    char result=EnsureBiosDataArea();
   193    con.SetGreenOrRed(!result);
   194    if (!result) {
   195     _cprintf("BIOS data area is OK.");
   196  #ifdef _M_IX86
   197     if (!w64) _cprintf(" (Not visible to DOS boxes.)");
   198  #endif
   199     _cprintf("\n");
   200    }else if (result<0) _cprintf("BIOS data area NOT accessible!\n");
   201    else for(int i=0; i<3; i++) {
   202     if (result&1<<i) _cprintf("BIOS data area corrected for LPT%d, set to 0x%X.\n",i+1,LOWORD(RedirInfo[i].addr));
   203    }
   204    _cprintf("\n");
   205   }
   206   con.SetNormal();
   207   if (sysver>=0) StartType(-1);	// here: GetStartType
   208  }
   209  
   210  static bool VAL(char*&s, int&v) {
   211   char*e;
   212   while (*s==' ') s++;		// skip leading spaces
   213   v=strtoul(s,&e,16);		// try to convert
   214   if (e==s) return false;	// Not a valid number (e.g. ';')
   215   s=e;				// Next start
   216   if (*s==',') s++;		// Skip one comma
   217   return true;
   218  }
   219  
   220  static bool ProcessLine(HQUEUE &q, char *s) {
   221   LARGE_INTEGER pf,tic,toc;
   222   QueryPerformanceFrequency(&pf);
   223   for (char c;c=*s++;) {
   224    if (c<'?') continue;
   225    int a=0,b=0;
   226    if (isupper(c)) c=tolower(c);
   227    switch (c) {	// one-parameter commands
   228     case 'o':
   229     case 'i':
   230     case 'l':
   231     case 'd':
   232     case 's': if (!VAL(s,a)) goto noparam;
   233    }
   234    switch (c) {	// two-parameter commands
   235     case 'o': if (!VAL(s,b)) goto noparam;
   236    }
   237    int i,l;
   238    BYTE buf[256];
   239    switch (c) {
   240     case 'o': if (q) LptOut(q,a,b); else Out32((WORD)a,(BYTE)b); break;
   241     case 'i': if (q) {if (!LptIn(q,a)) goto failed;} else {b=Inp32((WORD)a); _cprintf("%X\n",b);} break;
   242     case 'l': if (q) _cprintf("Queue already open!\n"); else q=LptOpen(a,0); if (!q) _cprintf("Open failed!\n"); break;
   243     case 'c': if (q) {LptClose(q); q=0;} else goto notopen; break;
   244     case 'd': if (q) LptDelay(q,a); else goto notopen; break;
   245     case 'u':
   246     case 'e': if (q) {
   247      for(i=0; i<sizeof buf; i++) {
   248       if (!VAL(s,b)) break;
   249       buf[i]=b;
   250      }
   251      QueryPerformanceCounter(&tic);
   252      l=LptInOut(q,buf,i,buf,sizeof buf);
   253      goto outbuf;
   254     }goto notopen;
   255     case 'f': if (q) {
   256      QueryPerformanceCounter(&tic);
   257      l=LptFlush(q,buf,sizeof buf);
   258      outbuf:
   259      QueryPerformanceCounter(&toc);
   260      if (l<0) failed: _cprintf("Command `%c' failed!\n",c);
   261      else if (l) {for (i=0; i<l; i++) _cprintf("%X ",buf[i]);_cprintf("\n");}
   262      _cprintf("Execution time: %u us\n",MulDiv(toc.LowPart-tic.LowPart,1000000,pf.LowPart));
   263     }else notopen: _cprintf("Not in queue mode!\n");
   264     break;
   265     case 'a': ShowAssignment(); break;
   266     case 'q':
   267     case 'x': return false;	// let exit command-line processor
   268     case 's': StartType(a); break;
   269     case '?':
   270     case 'h':
   271     _cprintf(
   272         "### InpOut32 debug console - all parameters are hexadecimal\n"
   273         "	## Direct mode ##		## Queue mode ##\n"
   274         "o a b	OUT byte to address		Enqueue OUT byte to offset\n"
   275         "i a	IN from address, emit byte	Enqueue IN from offset\n"
   276         "l n	Open Queue for LPT n+1		-\n"
   277         "d us	-				Enqueue delay in microseconds\n"
   278         "e ...	-				Execute microcode, emit IN results\n"
   279         "f	-				Flush Queue, emit IN results\n"
   280         "c	-				Close Queue\n"
   281         "a	Show LPT assignment\n"
   282         "%s"
   283         "h,?	This help\n"
   284         "x,q	Exit debug console\n\n",
   285         sysver>=0?"s n	Set service start type, n=2: AUTO_START, n=3: DEMAND_START\n":""); break;
   286     case 0: noparam: _cprintf("Missing parameter for `%c'!\n",c); break;
   287     default: _cprintf("Unknown command `%c'!\n",c);
   288    }
   289   }
   290   return true;	// let continue command-line processing
   291  }
   292  
   293  void InfoA (HWND Wnd, HINSTANCE, char *cmdLine, int) {
   294   HQUEUE q=0;
   295   con.Init();
   296   ShowAssignment();
   297   char buf[128];
   298   buf[0]=126;
   299   buf[1]=(char)strlen(cmdLine);
   300   strncpy(buf+2,cmdLine,126);	// prepare for editing
   301   if (buf[1]) {
   302    con.SetColor(FOREGROUND_RED|FOREGROUND_GREEN);
   303    _cprintf("-%s\n",cmdLine);
   304    con.SetNormal();
   305   }
   306   else _cprintf("Enter `q' for exiting.\n");
   307   while (ProcessLine(q, buf+2)) {
   308    con.SetColor(FOREGROUND_RED|FOREGROUND_GREEN);
   309  //  FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); doesn't help for the two-"-"-problem!
   310    _cprintf("-");	// prompt (two loops for Win7/8-64, unknown reason)
   311    _cgets(buf);		// get line-editable command
   312    con.SetNormal();
   313   }
   314   if (q) {_cprintf("Queue cleanup.\n"); LptClose(q);}
   315  }
   316  
   317  void InfoW(HWND Wnd, HINSTANCE hInst, PCWSTR cmdLine, int nCmdShow) {
   318   char buf[128];
   319   WideCharToMultiByte(CP_ACP,0,cmdLine,-1,buf,elemof(buf),NULL,NULL);
   320   InfoA(Wnd,hInst,buf,nCmdShow);
   321  }
   322  
   323  /***************************************************************
   324   ** LPT address redirector, fine for PCI and PCIexpress cards **
   325   ***************************************************************/
   326  
   327  WORD SppDef[3]={0x378,0x278,0x3BC};
   328  
   329  // Change 0x378 to LOWORD(RedirInfo[0].addr), 0x778 to HIWORD(...), etc.
   330  // 0x3BC (LPT3) is not exprected to have EPP/ECP registers, as this would break
   331  // compatibility to some software accessing 0x3C0 (graphics card) or 0x7BC (unknown)
   332  void patch(WORD &addr) {
   333   for (int i=0;; i++) {
   334    REDIR *info=RedirInfo+i;
   335    if ((WORD)(addr-SppDef[i])<(i<2?8:4) && info->where==1 /*&& LOWORD(info->addr)*/) {
   336     addr+=LOWORD(info->addr)-SppDef[i];
   337     break;
   338    }
   339    if (i==2) break;	// No ECP for 0x3BC, abort loop here
   340    if ((WORD)(addr-SppDef[i]-0x400)<4 && info->where==1 && HIWORD(info->addr)) {
   341     addr+=HIWORD(info->addr)-SppDef[i]-0x400;
   342     break;
   343    }
   344   }
   345  }
   346  
   347  #ifdef _M_IX86
   348  /**************************************************************
   349   ** Redirection for DOS programs (i.e. NTVDM: DOS and Win16) **
   350   **************************************************************/
   351  
   352  /* DOS+Win16 programs will also get redirected port addresses, therefore,
   353   * a DOS program doesn't need to know the PCI card's base address.
   354   * Simply use 0x378 (378h, &H378, $378) for LPT1, etc.
   355   * Redirection also occurs to USB->ParallelPrinter and USB2LPT adapters!
   356   * Moreover, USB ports doesn't need administrative privilege for access.
   357   * Note that ntvdm.exe only exists for x86 platform, not for amd64.
   358   * If a 64-bit DosBox emulator behaves like ntvdm.exe (i.e. has same
   359   * entry points), the #ifdef above should be removed.
   360   *
   361   * BUGBUG: ntvdm.exe seems to _always_ redirect access to the well-known
   362   * LPT addresses (378, 278, 3BC) to printer somehow WITHOUT ANY WORKAROUND.
   363   * (Except patching ntvdm.exe, of-course.)
   364   * Therefore, this DLL creates an alias address range which is commonly
   365   * free. This feature is only useful for programs where you can
   366   * select a nonstandard port base address.
   367   * To assist DOS+Win16 programs to fetch that aliases,
   368   * this DLL patches the well-known BIOS data area for the ntvdm process.
   369   */
   370  VDD_IO_PORTRANGE portranges[6];
   371  WORD nRanges;
   372  
   373  static int setup_portranges() {
   374   portranges[0].First=0x100;		// Additional feature 141012:
   375   portranges[0].Last=0x10A;		// Alias port adresses for LPT1..LPT3
   376   int k=1;
   377   for(int i=0;; i++) {
   378    REDIR *info=RedirInfo+i;
   379    if (info->where) {
   380     portranges[k].First=SppDef[i]-1;
   381     portranges[k].Last=SppDef[i]+(i<2?7:3);// with / without EPP data
   382     k++;
   383    }
   384    if (i==2) break;
   385    if (info->where) {
   386     portranges[k].First=SppDef[i]+0x400;
   387     portranges[k].Last=SppDef[i]+0x407;	// with ECP (and USB2LPT direction registers)
   388     k++;
   389    }
   390   }
   391   nRanges=(WORD)k;
   392   return k;
   393  }
   394  
   395  static void WINAPI VddInbHandler(WORD addr,BYTE*data) {
   396   if ((addr&~0xF)==0x100) {	// ersatzweises LPT1-LPT3 für DOS-Box auf 100 (LPT1), 104 (LPT2), 108 (LPT3)
   397    addr=(addr&3)+SppDef[addr>>2&3];
   398   }
   399   *data=Inp32(addr);
   400  }
   401  
   402  static void WINAPI VddOutbHandler(WORD addr, BYTE data) {
   403   if ((addr&~0xF)==0x100) {	// als Workaround für fix gecapture Druckerportadressen
   404    addr=(addr&3)+SppDef[addr>>2&3];// (DataIO 2700 Programmiergerät)
   405   }
   406   Out32(addr,data);
   407  }
   408  
   409  void VddInit() {
   410   static const VDD_IO_HANDLERS IOHandler={
   411    VddInbHandler,NULL,NULL,NULL,
   412    VddOutbHandler,NULL,NULL,NULL
   413   };
   414   vdm.hLib=GetModuleHandle(NULL);	// get calling process (ntvdm.exe, or any third-party program that exports VDDInstallIoHook() etc.)
   415   if (dynaload(vdm.hLib,vdm_names)	// if not ntvdm.exe, this will fail because entries won't exist
   416   && setup_portranges()
   417   && vdm.InstallIOHook(hInstance,nRanges,portranges,(PVDD_IO_HANDLERS)&IOHandler)) {
   418    _asm{
   419  	push	es
   420  	push	edi
   421  	 mov	di,0x40
   422  	 mov	es,di		// access BIOS data area (of current process??) - still valid in Win32
   423  	 mov	edi,8
   424  	 push	3
   425  	 pop	ecx
   426  	 xor	eax,eax
   427  	 inc	ah		// 0x100
   428  l1:	 stosw			// patch 3 words
   429  	 add	eax,4
   430  	 loop	l1
   431  	pop	edi
   432  	pop	es
   433    }
   434  #ifdef _DEBUG
   435    MessageBeep(MB_OK);
   436   }else{
   437    MessageBox(0,T("inpout32.dll: Failed to install IO Hook!"),NULL,MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL);
   438  #endif
   439   }
   440  }
   441  
   442  void VddDone() {
   443   if (nRanges) vdm.DeInstallIOHook(hInstance,nRanges,portranges);
   444  //   MessageBeep(MB_ICONSTOP);
   445  }
   446  
   447  // Installs globally for all DOS programs, which is mostly intended
   448  // This procedure requires administrative privileges on Vista or newer,
   449  // otherwise, the registry access is redirected to a wrong place.
   450  // (And RegOpenKeyEx() should fail silently in this case.)
   451  void InstallVdmHook() {
   452   TCHAR self[MAX_PATH],c;
   453   GetModuleFileName(hInstance,self,elemof(self));
   454   c=self[3];
   455   self[3]=0;
   456   if (GetDriveType(self)!=DRIVE_FIXED) return;	// ab XP?
   457   self[3]=c;
   458   HKEY hKey;
   459   if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE,T("System\\CurrentControlSet\\Control\\VirtualDeviceDrivers"),0,
   460     KEY_QUERY_VALUE|KEY_SET_VALUE,&hKey)) {
   461    TCHAR *selfname=_tcsrchr(self,'\\')+1;	// "inpout32.dll" or "inpoutx64.dll" - or somehow renamed
   462    TCHAR buf[1500],*p=buf,*q,*r,*e;
   463    DWORD len=sizeof(buf);
   464    if (!RegQueryValueEx(hKey,T("VDD"),NULL,NULL,(PBYTE)buf,&len)) {
   465     e=buf+len/sizeof(TCHAR);	// behind double-zero
   466     for (p=buf; *p; p=q) {	// traverse each entry
   467      if (!lstrcmpi(p,self)) {wdmHooked=true; goto raus;}	// already installed, do nothing
   468      q=p+_tcslen(p)+1;		// next entry
   469      r=_tcsrchr(p,'\\');		// filename portion
   470      if (!r) r=_tcsrchr(p,'/');	// possibly UNIX style here
   471      if (r) r++; else r=p;	// with no path, take entire entry as file name
   472      if (!lstrcmpi(r,selfname)) {	// this DLL on another place?
   473       memmove(p,q,(BYTE*)e-(BYTE*)q);	// Remove this entry! (Move trailing data to lower addresses)
   474       e-=q-p;			// Adjust end address
   475       q=p;			// Adjust next-entry address
   476       continue;
   477      }
   478     }
   479    }
   480    lstrcpyn(p,self,buf+elemof(buf)-p-1);	// Append
   481    e=p+_tcslen(p)+1;
   482    *e++=0;	// now double-terminated
   483    if (!RegSetValueEx(hKey,T("VDD"),0,REG_MULTI_SZ,(PBYTE)buf,(e-buf)*sizeof(TCHAR))) wdmHooked=true;
   484  raus:
   485    RegCloseKey(hKey);
   486   }
   487  }
   488  #endif
   489  
   490  // Dummy entry! DLL initialization is the workhorse.
   491  BOOL WINAPI VDDInitialize(HANDLE DllHandle,ULONG Reason,PCONTEXT Context) {
   492   return TRUE;
   493  }
   494  
   495  /*****************************************************
   496   ** Automated full redirector for Inp32() / Out32() **
   497   *****************************************************/
   498  
   499  // Fill residual free slots of RedirInfo[]
   500  static bool InsertDevice(BYTE where, PTSTR name) {
   501   for (int i=0; i<elemof(RedirInfo); i++) {
   502    if (!RedirInfo[i].where) {	// found free slot
   503     RedirInfo[i].where=where;
   504     RedirInfo[i].name=_tcsdup(name);
   505     return true;
   506    }
   507   }
   508   return false;			// couldn't insert
   509  }
   510  
   511  static bool AddUsb2Lpt(PTSTR name) {
   512  // As FindDevices will enumerate all HID devices here
   513  // (at least non-mice, non-keyboards, and non-joysticks)
   514  // filter for USB2LPT devices here
   515   bool ret=true;	// let continue enumeration
   516   HANDLE h=CreateFile(name,0,0,NULL,OPEN_EXISTING,0,NULL);
   517   if (h!=INVALID_HANDLE_VALUE) {
   518    HIDD_ATTRIBUTES attr;
   519    if (HidD_(GetAttributes(h,&attr))) {
   520     if (attr.VendorID==0x16C0		// VOTI (5824)
   521     && (WORD)(attr.ProductID-1715)<5) {	// siphec -> h#s
   522      ret=InsertDevice(3,name);
   523     }
   524    }
   525    CloseHandle(h);
   526   }
   527   return ret;
   528  }
   529  
   530  static bool AddUsbPrn(PTSTR name) {
   531  // Take any USB->ParallelPrinter adapter
   532   return InsertDevice(4,name);
   533  }
   534   
   535  static void FindDevices(const GUID*guid, bool(*FilterProc)(PTSTR)) {
   536   HDEVINFO devs;
   537   devs=SetupDiGetClassDevs(guid,0,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
   538   if (devs!=INVALID_HANDLE_VALUE) {
   539    SP_DEVICE_INTERFACE_DATA devinterface;
   540    devinterface.cbSize=sizeof(SP_DEVICE_INTERFACE_DATA);
   541    for (int i=0; SetupDi(EnumDeviceInterfaces(devs,NULL,guid,i,&devinterface)); i++) {
   542     SP_DEVINFO_DATA devinfo;
   543     struct{
   544      SP_DEVICE_INTERFACE_DETAIL_DATA id;
   545      TCHAR space[MAX_PATH];
   546     }id;
   547     devinfo.cbSize = sizeof(SP_DEVINFO_DATA);
   548     id.id.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
   549     if (SetupDi(GetDeviceInterfaceDetail(devs, &devinterface, &id.id, sizeof(id), NULL, &devinfo))) {
   550      if (!FilterProc(id.id.DevicePath)) break;
   551     }
   552    }
   553    SetupDiDestroyDeviceInfoList(devs);
   554   }
   555  }
   556   
   557  void InitRedirector(void) {
   558   // Traverse true parallel ports and USB2LPT devices first
   559   DWORD a[9];
   560   memset(a,0,sizeof(a));
   561   LptGetAddr(a,elemof(a));	// Collect true LPT ports
   562   for (int n=0; n<9; n++) {
   563    if (a[n]) {
   564     RedirInfo[n].where=1;
   565     RedirInfo[n].addr=a[n];
   566     continue;
   567    }
   568    TCHAR s[16];
   569    _sntprintf(s,elemof(s),T("LPT%d"),n+1);
   570    HANDLE hDev=CreateFile(s,0,0,NULL,OPEN_EXISTING,0,NULL);
   571    if (hDev!=INVALID_HANDLE_VALUE) {
   572     DWORD e,bw;			// Check whether it's a USB2LPT
   573     if (DeviceIoControl(hDev,IOCTL_VLPT_GetLastError,NULL,0,&e,sizeof(e),&bw,NULL)) {
   574      RedirInfo[n].where=2;
   575      RedirInfo[n].name=_tcsdup(s);
   576     }
   577     CloseHandle(hDev);
   578    }
   579   }
   580   // Fill gaps with available USB2LPT (HID) and USB->PRN adapters
   581   if (!dynaload(setupapi.hLib,setupapi_names)) return;
   582   if (!dynaload(hid.hLib,hid_names)) return;
   583   dynaload(krnl.hLib,krnl_names);		// load CancelIo() - not available on Win95
   584   // Typically, a PCI card defaults to LPT3, so a USB->PRN adapter can be used as LPT2 here
   585   GUID guid;
   586   HidD_(GetHidGuid(&guid));
   587   FindDevices(&guid,AddUsb2Lpt);
   588   static const GUID GUID_DEVINTERFACE_USBPRINT = {
   589     0x28d78fad,0x5a12,0x11D1,0xae,0x5b,0x00,0x00,0xf8,0x03,0xa8,0xc2};
   590   FindDevices(&GUID_DEVINTERFACE_USBPRINT,AddUsbPrn);
   591  }
   592  
   593  static QUEUE* RedirQueue[3];
   594  
   595  static QUEUE* RedirCandidate(WORD addr, BYTE &offset) {
   596   for (int i=0; i<3; i++) {
   597    if (RedirInfo[i].where>=2) {
   598     WORD diff=addr-SppDef[i];
   599     if ((WORD)(diff&~0x400)<8) {
   600      if (!RedirQueue[i]) RedirQueue[i]=LptOpen(i,0);
   601      if (!RedirQueue[i]) return false;
   602      offset=(BYTE)diff | diff>>7&8;
   603      return RedirQueue[i];
   604     }
   605    }
   606   }
   607   return NULL;
   608  }
   609  
   610  bool RedirOut(WORD addr, BYTE data) {
   611   BYTE ucode[2];
   612   QUEUE *q=RedirCandidate(addr,ucode[0]);
   613   if (!q) return false;
   614   ucode[1]=data;
   615   LptInOut(q,ucode,2,NULL,0);	// no result checking here
   616   return true;
   617  }
   618  
   619  bool RedirIn(WORD addr, BYTE &data) {
   620   QUEUE *q=RedirCandidate(addr,data);
   621   if (!q) return false;
   622   data|=0x10;			// IN instruction
   623   LptInOut(q,&data,1,&data,1);
   624   return true;
   625  }
   626  
   627  BOOL ExitPortTalk() {
   628   for (int i=0; i<elemof(RedirQueue); i++) {
   629    LptClose(RedirQueue[i]);
   630    RedirQueue[i]=0;
   631   }
   632   return TRUE;
   633  }
   634  
Detected encoding: UTF-80