1 /*****************************************************************************\
2   registry.cpp : Implimentation for the DeviceRegistry class
3 
4   Copyright (c) 1996 - 2015, HP Co.
5   All rights reserved.
6 
7   Redistribution and use in source and binary forms, with or without
8   modification, are permitted provided that the following conditions
9   are met:
10   1. Redistributions of source code must retain the above copyright
11      notice, this list of conditions and the following disclaimer.
12   2. Redistributions in binary form must reproduce the above copyright
13      notice, this list of conditions and the following disclaimer in the
14      documentation and/or other materials provided with the distribution.
15   3. Neither the name of HP nor the names of its
16      contributors may be used to endorse or promote products derived
17      from this software without specific prior written permission.
18 
19   THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
20   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
22   NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24   TO, PATENT INFRINGEMENT; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25   OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 \*****************************************************************************/
30 
31 
32 // The purpose of this file is to facilitate addition and subtraction
33 // of supported devices from the system.
34 
35 #include "header.h"
36 #include "printerfactory.h"
37 
38 #include "printer.h"
39 #include "apollo2xxx.h"
40 #include "apollo21xx.h"
41 #include "apollo2560.h"
42 #include "apollo2xxx.h"
43 #ifdef APDK_DJ3320
44 #include "dj3320.h"
45 #ifdef APDK_DJ3600
46 #include "dj3600.h"
47 #include "dj4100.h"
48 #include "djd2600.h"
49 #endif
50 #endif
51 #include "dj400.h"
52 #include "dj6xx.h"
53 #include "dj600.h"
54 #include "dj630.h"
55 #include "dj660.h"
56 #include "dj690.h"
57 #include "dj350.h"
58 #include "dj540.h"
59 #include "dj8xx.h"
60 #include "dj850.h"
61 #include "dj8x5.h"
62 #include "dj890.h"
63 #include "dj9xx.h"
64 #include "dj9xxvip.h"
65 #include "djgenericvip.h"
66 #include "dj55xx.h"
67 #include "ojprokx50.h"
68 #include "ljmono.h"
69 #include "ljcolor.h"
70 #include "psp100.h"
71 #include "psp470.h"
72 #include "pscript.h"
73 #include "ljjetready.h"
74 #include "ljfastraster.h"
75 #if defined (APDK_LJZJS_MONO) || defined (APDK_LJZJS_COLOR) || defined (APDK_LJM1005)
76 #include "ljzjs.h"
77 #endif
78 #ifdef APDK_LJZJS_MONO
79 #include "ljzjsmono.h"
80 #endif
81 #ifdef APDK_LJZJS_COLOR
82 #include "ljzjscolor.h"
83 #endif
84 #ifdef APDK_LJM1005
85 #include "ljm1005.h"
86 #include "ljp1xxx.h"
87 #endif
88 
89 #ifdef APDK_QUICKCONNECT
90 #include "quickconnect.h"
91 #endif
92 
93 APDK_BEGIN_NAMESPACE
94 
95 #ifdef APDK_PSCRIPT
96 PScriptProxy DeviceRegistry::s_PScriptProxy;
97 #endif
98 
99 #ifdef APDK_LJMONO
100 LJMonoProxy DeviceRegistry::s_LJMonoProxy;
101 #endif
102 
103 #ifdef APDK_LJCOLOR
104 LJColorProxy DeviceRegistry::s_LJColorProxy;
105 #endif
106 
107 #ifdef APDK_LJJETREADY
108 LJJetReadyProxy DeviceRegistry::s_LJJetReadyProxy;
109 #endif
110 
111 #ifdef APDK_LJFASTRASTER
112 LJFastRasterProxy DeviceRegistry::s_LJFastRasterProxy;
113 #endif
114 
115 #ifdef APDK_LJZJS_MONO
116 LJZjsMonoProxy DeviceRegistry::s_LJZjsMonoProxy;
117 #endif
118 
119 #ifdef APDK_LJZJS_COLOR
120 LJZjsColorProxy DeviceRegistry::s_LJZjsColorProxy;
121 #endif
122 
123 #ifdef APDK_LJM1005
124 LJM1005Proxy DeviceRegistry::s_LJM1005Proxy;
125 LJP1XXXProxy DeviceRegistry::s_LJP1XXXProxy;
126 #endif
127 
128 #if defined(APDK_PSP100) && defined (APDK_DJ9xxVIP)
129 PSP100Proxy DeviceRegistry::s_PSP100Proxy;
130 PSP470Proxy DeviceRegistry::s_PSP470Proxy;
131 #endif
132 
133 #if defined(APDK_DJGENERICVIP) && defined (APDK_DJ9xxVIP)
134 DJGenericVIPProxy DeviceRegistry::s_DJGenericVIPProxy;
135 DJ55xxProxy DeviceRegistry::s_DJ55xxProxy;
136 #endif
137 
138 #ifdef APDK_DJ9xx
139 DJ9xxProxy DeviceRegistry::s_DJ9xxProxy;
140 #endif
141 
142 #ifdef APDK_DJ9xxVIP
143 DJ9xxVIPProxy DeviceRegistry::s_DJ9xxVIPProxy;
144 OJProKx50Proxy DeviceRegistry::s_OJProKx50Proxy;
145 #endif
146 
147 #if defined(APDK_DJ8xx)|| defined(APDK_DJ9xx)
148 DJ8xxProxy DeviceRegistry::s_DJ8xxProxy;
149 #endif
150 
151 #if defined(APDK_DJ8xx)|| defined(APDK_DJ9xx)
152 #ifdef APDK_DJ8x5
153 DJ8x5Proxy DeviceRegistry::s_DJ8x5Proxy;
154 #endif
155 #endif
156 
157 #if defined(APDK_DJ890)
158 DJ890Proxy DeviceRegistry::s_DJ890Proxy;
159 #endif
160 
161 #if defined(APDK_DJ850)
162 DJ850Proxy DeviceRegistry::s_DJ850Proxy;
163 #endif
164 
165 #ifdef APDK_DJ6xxPhoto
166 DJ6xxPhotoProxy DeviceRegistry::s_DJ6xxPhotoProxy;
167 #endif
168 
169 #ifdef APDK_DJ6xx
170 DJ660Proxy DeviceRegistry::s_DJ660Proxy;
171 #endif
172 
173 #ifdef APDK_DJ630
174 DJ630Proxy DeviceRegistry::s_DJ630Proxy;
175 #endif
176 
177 #ifdef APDK_DJ600
178 DJ600Proxy DeviceRegistry::s_DJ600Proxy;
179 #endif
180 
181 #ifdef APDK_DJ540
182 DJ540Proxy DeviceRegistry::s_DJ540Proxy;
183 #endif
184 
185 #ifdef APDK_DJ400
186 DJ400Proxy DeviceRegistry::s_DJ400Proxy;
187 #endif
188 
189 #ifdef APDK_DJ350
190 DJ350Proxy DeviceRegistry::s_DJ350Proxy;
191 #endif
192 
193 #if defined(APDK_DJ3600) && defined (APDK_DJ3320)
194 DJ3600Proxy DeviceRegistry::s_DJ3600Proxy;
195 DJ4100Proxy DeviceRegistry::s_DJ4100Proxy;
196 DJD2600Proxy DeviceRegistry::s_DJD2600Proxy;
197 #endif
198 
199 #if defined (APDK_DJ3320)
200 DJ3320Proxy DeviceRegistry::s_DJ3320Proxy;
201 #endif
202 
203 #ifdef APDK_APOLLO2560
204 Apollo2560Proxy DeviceRegistry::s_Apollo2560Proxy;
205 #endif
206 
207 #ifdef APDK_APOLLO21XX
208 Apollo21xxProxy DeviceRegistry::s_Apollo21xxProxy;
209 #endif
210 
211 #ifdef APDK_APOLLO2XXX
212 Apollo2xxxProxy DeviceRegistry::s_Apollo2xxxProxy;
213 #endif
214 
215 #ifdef APDK_QUICKCONNECT
216 QuickConnectProxy DeviceRegistry::s_QuickConnectProxy;
217 #endif
218 
DeviceRegistry()219 DeviceRegistry::DeviceRegistry()
220     : device(UNSUPPORTED)
221 {
222 }
223 
224 
~DeviceRegistry()225 DeviceRegistry::~DeviceRegistry()
226 {
227     DBG1("deleting DeviceRegistry\n");
228 }
229 
230 
SelectDevice(const PRINTER_TYPE Model)231 DRIVER_ERROR DeviceRegistry::SelectDevice(const PRINTER_TYPE Model)
232 {
233     if (Model > MAX_PRINTER_TYPE)
234         return UNSUPPORTED_PRINTER;
235     device = Model;
236 
237 	return NO_ERROR;
238 }
239 
240 
SelectDevice(char * model,int * pVIPVersion,char * pens,SystemServices * pSS)241 DRIVER_ERROR DeviceRegistry::SelectDevice(char* model, int *pVIPVersion, char* pens, SystemServices* pSS)
242 // used by PrintContext constructor
243 // based on this 'model' string, we will search for the enum'd value
244 // and set this enum'd value in 'device'
245 {
246 
247 #if defined(DEBUG) && (DBG_MASK & DBG_LVL1)
248     printf("DR::SelectDevice: model= '%s'\n",model);
249     printf("DR::SelectDevice: VIPver= %d\n",*pVIPVersion);
250     printf("DR::SelectDevice: pens= '%s'\n",pens);
251 #endif
252 
253 	int j = 0;
254     char pen1 = '\0';   // black/color(for CCM)/photo(for 690) pen
255     char pen2 = '\0';   // color/non-existent(for CCM) pen
256 
257     BOOL match=FALSE;
258 
259     DRIVER_ERROR err = NO_ERROR;
260 
261     FAMILY_HANDLE familyHandle = pPFI->FindDevIdMatch(model);
262     if (familyHandle != NULL)
263     {
264 		device = pPFI->GetFamilyType(familyHandle);
265 		match = TRUE;
266 	}
267 
268     if (!match) // see if printer supports VIP, if so set compatible device
269     {
270         if (*pVIPVersion == 1)
271         {
272             match = TRUE;
273             device = eDJ9xxVIP;
274         }
275         else if (*pVIPVersion > 1)
276         {
277             match = TRUE;
278             device = eDJGenericVIP; // eDJ9xxVIP;
279         }
280     }
281 
282 /*
283  *  See if this is a sleek (LIDIL) printer, or PostScript printer
284  */
285 
286     if (!match)
287     {
288         BYTE DevIDBuffer[DevIDBuffSize];
289 
290         err = pSS->GetDeviceID(DevIDBuffer, DevIDBuffSize, FALSE);
291         ERRCHECK;   // should be either NO_ERROR or BAD_DEVICE_ID
292 
293 		char	*cmdStr = (char *) strstr ((const char *) DevIDBuffer+2, "CMD:");
294         char    *cmdStrEnd;
295         if ((strstr((const char *) DevIDBuffer+2,"CMD:LDL")))
296         {
297             device = eDJ3320;
298             match = TRUE;
299         }
300         if (!match && cmdStr && (cmdStrEnd = (char *) strstr (cmdStr, ";")))
301         {
302             *cmdStrEnd = '\0';
303             if (strstr (cmdStr, "LDL"))
304             {
305                 match = TRUE;
306                 device = eDJ4100;
307             }
308             *cmdStrEnd = ';';
309         }
310 		if (!match && !cmdStr)
311 		{
312 			cmdStr = (char *) strstr ((const char *) DevIDBuffer+2, "COMMAND SET:");
313 		}
314 		if (!match && cmdStr && (strstr ((const char *) cmdStr+4, "POSTSCRIPT") ||
315 			                     strstr ((const char *) cmdStr+4, "PostScript") ||
316 					             strstr ((const char *) cmdStr+4, "Postscript") ||
317 					             strstr ((const char *) cmdStr+4, "postscript") ))
318 		{
319 			device = ePScript;
320 			match = TRUE;
321 		}
322     }
323 
324     if (!match)
325     {
326     // The devID model string did not have a match for a known printer
327     // and the printer doesn't support VIP so let's look at the pen info for clues
328 
329         // if we don't have pen info (VSTATUS) it's presumably
330         //  either sleek, DJ4xx or non-HP
331 		device = UNSUPPORTED;
332         if ( pens[0] != '\0' )
333         {
334             // DJ8xx (and DJ970?) printers return penID $X0$X0
335             //  when powered off
336             if(pens[1] == 'X')
337             {
338                 DBG1("DR:(Unknown Model) Need to do a POWER ON to get penIDs\n");
339 
340                 DWORD length=sizeof(DJ895_Power_On);
341                 err = pSS->ToDevice(DJ895_Power_On, &length);
342                 ERRCHECK;
343 
344                 err = pSS->FlushIO();
345                 ERRCHECK;
346 
347                 // give the printer some time to power up
348                 if (pSS->BusyWait((DWORD)1000) == JOB_CANCELED)
349                 return JOB_CANCELED;
350 
351                 // we must re-query the devID
352                 err=GetPrinterModel(model,pVIPVersion,pens,pSS);
353                 ERRCHECK;
354             }
355 
356             // Arggghh.  The pen(s) COULD be missing
357             do
358             {
359 
360 //				Is this binary-encoded format?
361 
362 				if (pens[0] != '$')
363 				{
364 					break;
365 				}
366 
367                 // get pen1 - penID format is $HB0$FC0
368                 pen1=pens[1];
369 
370                 // get pen2 - if it exists
371                 j=2;
372                 BOOL NO_PEN2 = FALSE;
373                 while(pens[j] != '$')   // handles variable length penIDs
374                 {
375                     j++;
376                     if ( pens[j] == '\0' )
377                     // never found a pen2
378                     {
379                         pen2 = '\0';
380                         NO_PEN2 = TRUE;
381                         break;
382                     }
383                 }
384                 if (NO_PEN2 == FALSE)
385                 {
386                     j++;
387                     pen2 = pens[j];
388                 }
389 
390                 if(pen1 == 'A' || pen2 == 'A')
391                 {
392                     if(pen1 == 'A')
393                     {
394                         // 2-pen printer with both pens missing
395                         if(pen2 == 'A')
396                             pSS->DisplayPrinterStatus(DISPLAY_NO_PENS);
397 
398                         // 1-pen printer with missing pen
399                         else if(pen2 == '\0')
400                             pSS->DisplayPrinterStatus(DISPLAY_NO_PEN_DJ600);
401 
402 						// may be one-pen DJ8xx derivative
403 						else if (pen2 == 'F')
404 						{
405 							device = eDJ8x5;
406 							return NO_ERROR;
407 						}
408                         // 2-pen printer with BLACK missing
409                         else pSS->DisplayPrinterStatus(DISPLAY_NO_BLACK_PEN);
410                     }
411                     // 2-pen printer with COLOR missing
412                     else if(pen2 == 'A')
413 					{
414 
415 //						possibly DJ8x5 derivative
416 
417 						if (pen1 == 'H' || pen1 == 'Z' || pen1 == 'L')
418 						{
419 							device = eDJ8x5;
420 							return NO_ERROR;
421 						}
422                         pSS->DisplayPrinterStatus(DISPLAY_NO_COLOR_PEN);
423 					}
424 
425                     if (pSS->BusyWait(500) == JOB_CANCELED)
426                         return  JOB_CANCELED;
427 
428                     // we must re-query the devID
429                     err=GetPrinterModel(model,pVIPVersion,pens,pSS);
430                     ERRCHECK;
431                 }
432 
433             } while(pen1 == 'A' || pen2 == 'A');
434 
435             // now that we have pens to look at, let's do the logic
436             //  to instantiate the 'best-fit' driver
437 
438             if (pen1 == 'H' || pen1 == 'Z' || pen1 == 'L') // (BLACK)
439             {
440                 // check for a 850/855/870
441                 if (pen2 == 'M')
442 					device = eDJ850;
443                 else if (strncmp (model,"DESKJET 890",11) == 0)
444                     device=eDJ890; // 890 has same pens as DJ895!
445                 else if (pen2 == 'N')	// (COLOR)
446 					device = eDJ9xx;
447                 // It must be a DJ8xx derivative or will hopefully at
448                 // least recognize a DJ8xx print mode
449                 else
450 					device = eDJ8xx;
451             }
452             else if(pen1 == 'C') // (BLACK)
453             {
454                 // check for 1-pen printer
455                 if (pen2 == '\0') device = eDJ600;
456                 // must be a 2-pen 6xx-derivative
457                 else device = eDJ6xx;
458             }
459             else if(pen1 == 'M') // Multi-dye load
460             {
461                 // must be a 690-derivative
462                 device = eDJ6xxPhoto;
463             }
464 
465             // check for 540-style pens?
466             //  D = color, E = black
467 
468 //            else device=UNSUPPORTED;
469         }
470     }
471 
472 
473     // Early DJ8xx printer do not yet have full bi-di so check
474     // the model to avoid a communication problem.
475     if ( ( (strncmp(model,"DESKJET 81",10) == 0)
476         || (strncmp(model,"DESKJET 83",10) == 0)
477         || (strncmp(model,"DESKJET 88",10) == 0)
478         || (strncmp(model,"DESKJET 895",11) == 0)
479          )
480         && (pSS->IOMode.bUSB)
481        )
482     {
483         DBG1("This printer has limited USB status\n");
484         pSS->IOMode.bStatus = FALSE;
485         pSS->IOMode.bDevID = FALSE;
486     }
487 
488     if ( ( (strncmp(model,"DESKJET 63",10) == 0)
489         || (strncmp(model,"DESKJET 64",10) == 0)
490          )
491         && (pSS->IOMode.bUSB)
492        )
493     {
494         DBG1("This printer has limited USB status, but we did get DeviceIDString\n");
495         pSS->IOMode.bStatus = FALSE;
496     }
497 
498     if (device == UNSUPPORTED) return UNSUPPORTED_PRINTER;
499     else return NO_ERROR;
500 } //SelectDevice
501 
SelectDevice(const char * sDevID,SystemServices * pSS)502 DRIVER_ERROR DeviceRegistry::SelectDevice(const char* sDevID, SystemServices* pSS)
503 {
504     char strModel[DevIDBuffSize]; // to contain the MODEL (MDL) from the DevID
505     char strPens[64];   // to contain the VSTATUS penID from the DevID
506     int  VIPVersion;    // VIP version from the DevID
507 
508 	DRIVER_ERROR err = ParseDevIDString(sDevID, strModel, &VIPVersion, strPens);
509 	if (err != NO_ERROR)
510 	{
511 		return UNSUPPORTED_PRINTER;
512 	}
513 
514 	return SelectDevice(strModel, &VIPVersion, strPens, pSS);
515 }
516 
InstantiatePrinter(Printer * & p,SystemServices * pSS)517 DRIVER_ERROR DeviceRegistry::InstantiatePrinter(Printer*& p, SystemServices* pSS)
518 // Instantiate a printer object and return a pointer p based on the previously
519 // set 'device' variable
520 {
521     //ASSERT(p == NULL);  // if it's not then we're going to loose memory
522 
523     FAMILY_HANDLE familyHandle = pPFI->FindDevIdMatch(ModelName[device]);
524     if (familyHandle == NULL)
525     {
526         ASSERT(familyHandle);
527         DBG1("DR::InstantiatePrinter - no family match\n");
528         return UNSUPPORTED_PRINTER;
529     }
530     p = pPFI->CreatePrinter(pSS, familyHandle);
531     NEWCHECK(p);
532     return p->constructor_error;
533 } //InstantiatePrinter
534 
535 
536 
GetPrinterModel(char * strModel,int * pVIPVersion,char * strPens,SystemServices * pSS)537 DRIVER_ERROR DeviceRegistry::GetPrinterModel(char* strModel, int *pVIPVersion, char* strPens, SystemServices* pSS)
538 {
539     DRIVER_ERROR err;
540     BYTE DevIDBuffer[DevIDBuffSize];
541 
542     err = pSS->GetDeviceID(DevIDBuffer, DevIDBuffSize, TRUE);
543     ERRCHECK;   // should be either NO_ERROR or BAD_DEVICE_ID
544 
545     return ParseDevIDString((const char*)DevIDBuffer, strModel, pVIPVersion, strPens);
546 
547 } //GetPrinterModel
548 
549 #define HEXTOINT(x, p) if (x >= '0' && x <= '9')      *p |= x - '0'; \
550                        else if (x >= 'A' && x <= 'F') *p |= 0xA + x - 'A'; \
551                        else if (x >= 'a' && x <= 'f') *p |= 0xA + x - 'a'
552 
553 
554 //ParseDevIDString
555 //! Parse a device id string
556 /*!
557 Enter a full description of the method here. This will be the API doc.
558 
559 ******************************************************************************/
ParseDevIDString(const char * sDevID,char * strModel,int * pVIPVersion,char * strPens)560 DRIVER_ERROR DeviceRegistry::ParseDevIDString(const char* sDevID, char* strModel, int *pVIPVersion, char* strPens)
561 {
562     int i;  // simple counter
563     char* pStr = NULL;  // string pointer used in parsing DevID
564 
565     // get the model name
566     // - note: I'm setting pStr to the return of strstr
567     //   so I need to increment past my search string
568     if ( (pStr = (char *)strstr(sDevID+2,"MODEL:")) )
569         pStr+=6;
570     else
571         if ( (pStr=(char *)strstr(sDevID+2,"MDL:")) )
572             pStr+=4;
573         else return BAD_DEVICE_ID;
574 
575     // my own version of strtok to pull out the model string here
576     i = 0;
577     while ( (pStr[i] != ';') && (pStr[i] != '\0') && (i < DevIDBuffSize))
578 	{
579         strModel[i] = pStr[i];
580 		i++;
581 	}
582     strModel[i] = '\0';
583 
584 
585     // see if this printer support VIP or not
586     if ( (pStr=(char *)strstr(sDevID+2,";S:00")) )   // binary encoded device ID status (version 0)
587     {
588         pStr += 15;     // get to the VIP support field (version of 0 == doesn't support VIP)
589         if ((*pStr >= '0') && (*pStr <= '9'))
590         {
591             *pVIPVersion = *pStr - '0';
592         }
593         else if ((*pStr >= 'A') && (*pStr <= 'F'))
594         {
595             *pVIPVersion = 10 + (*pStr - 'A');
596         }
597         else
598         {
599             *pVIPVersion = 0;
600         }
601     }
602 
603 /*
604  *  DevID string has changed starting with Jupiter.
605  *  Following ";S:", two nibbles for Version Number
606  *  12 nibbles for Status Information, the last nibble
607  *  is reserved for future use, the second from last
608  *  indicates whether the printer has VIP support.
609  *
610  *  Actually, four nibbles were added.
611  *  The first fourteen nibbles contain feature state info.
612  *  So, starting with version 02 of device id, following ":S:" there are
613  *   2 nibbles for version number
614  *  14 nibbles for feature state  (the 15th nibble from ';' is the vip flag
615  *   2 nibbles for printer status
616  *
617  *  Crystal added 4 more nibbles to the option field.
618  */
619 
620     else if ((pStr = (char *)strstr (sDevID+2, ";S:")))
621     {
622         *pVIPVersion = 0;
623         HEXTOINT (*(pStr+3), pVIPVersion);
624         *pVIPVersion = *pVIPVersion << 4;
625         HEXTOINT (*(pStr+4), pVIPVersion);
626         if (*(pStr + 15) == '1')
627         {
628             (*pVIPVersion)++;
629         }
630         else
631         {
632             *pVIPVersion = 0;
633         }
634     }
635 
636     else
637     {
638         *pVIPVersion = 0;
639     }
640 
641     // now get the pen info
642     if( (pStr=(char *)strstr(sDevID+2,"VSTATUS:")) )
643     {
644         pStr+=8;
645         i=0;
646         while ( (pStr[i] != ',') && (pStr[i] != ';') && (pStr[i] != '\0') )
647 		{
648             strPens[i] = pStr[i];
649 			i++;
650 		}
651         strPens[i] = '\0';
652     }
653     else if ( (pStr = (char *)strstr(sDevID + 2, ";S:00")) ||   // binary encoded device ID status (version 0)
654              (pStr = (char *)strstr (sDevID + 2, ";S:")))  // Jupiter and later style
655     {
656 
657         int     iVersion = 0;
658         HEXTOINT (*(pStr+3), &iVersion);
659         iVersion = iVersion << 4;
660         HEXTOINT (*(pStr+4), &iVersion);
661         if (iVersion <= 2)
662         {
663             pStr += 19;     // get to the number of pens field
664         }
665         else if (iVersion < 4)
666         {
667             pStr += 21;
668         }
669         else
670         {
671             pStr += 25;
672         }
673 
674         // each supported pen has a block of 8 bytes of info so copy the number of pens byte
675         // plus 8 bytes for each supported ped
676         if ((*pStr >= '0') && (*pStr <= '9'))
677         {
678             i = 1 + ((*pStr-'0')*8);
679         }
680         else if ((*pStr >= 'A') && (*pStr <= 'F'))
681         {
682             i = 1 + ((10 + (*pStr-'A')) * 8);
683         }
684         else
685         {   // bogus number of pens field
686             i = 1;
687         }
688         memcpy(strPens, pStr, i);
689         strPens[i] = '\0';
690     }
691     else   // no VSTATUS for 400 and sleek printers
692         strPens[0] = '\0';
693 
694     return NO_ERROR;
695 } //ParseDevIDString
696 
697 APDK_END_NAMESPACE
698 
699