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