1 /* OpenCP Module Player
2  * copyright (c) '94-'10 Niklas Beisert <nbeisert@physik.tu-muenchen.de>
3  *
4  * CP hypertext help viewer
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  * revision history: (please note changes here)
21  *  -fg980812  Fabian Giesen <gfabian@jdcs.su.nw.schule.de>
22  *    -first alpha (hey, and it was written on my BIRTHDAY!)
23  *    -some pages of html documentation converted
24  *  -fg980813  Fabian Giesen <gfabian@jdcs.su.nw.schule.de>
25  *    -added pgup/pgdown support
26  *    -changed drawing method. stopped all flickering this way. but the code
27  *     did not get better during this bugfix... :)
28  *    -changed helpfile loader to load in "boot" phase, not in first use
29  *     I did this because the helpfile was sometimes not found when you
30  *     changed the directory via DOS shell.
31  *    -some speedups
32  *    -the "description" field is now displayed at top/right of the window
33  *    -again converted some documentation
34  *  -fg980814  Fabian Giesen <gfabian@jdcs.su.nw.schule.de>
35  *    -added support for compressed helpfiles
36  *    -added percentage display at right side of description
37  *    -added jumping to Contents page via Alt-C
38  *    -added jumping to Index page via Alt-I
39  *    -added jumping to License page via Alt-L
40  *    -html conversion again
41  *  -fg980820  Fabian Giesen <gfabian@jdcs.su.nw.schule.de>
42  *    -now searches CP.HLP in data path
43  *    -decided that code could be much cleaner but too lazy to change it :)
44  *    -maybe i'll add context sensitivity (if there is interest)
45  *    -well, uhm, html documentation is still not fully converted (shame
46  *     over me... :))
47  *  -fg_dunno  Fabian Giesen <gfabian@jdcs.su.nw.schule.de>
48  *    -added support for CP.HLP in CP.PAK because kb said CPHELPER would be
49  *     part of the next release
50  *  -fg980924  Fabian Giesen <gfabian@jdcs.su.nw.schule.de>
51  *    -changed keyboard use/handling a lot based on advices (commands? :)
52  *     from kb.
53  *    -finally the help compiler supports colour codes (i don't know why I
54  *     write this here).
55  *    -added fileselector support (yes!)
56  *    -made this possible by splitting this up in one "host" and two
57  *     "wrappers" for fileselector/player interface
58  *    -and, you won't believe, it still works :)
59  *  -kbwhenever Tammo Hinrichs <opencp@gmx.net>
60  *    -some minor cosmetical changes
61  *  -fg981118  Fabian Giesen <fabian@jdcs.su.nw.schule.de>
62  *    -note: please use this email now (the old one still works, but I don't
63  *     give promises about how long this will be)
64  *    -changed keyboard control again according to suggestions kb made
65  *     (1. kannst du mir das nicht einfach mailen?  2. warum sagst du mir
66  *      nicht sofort, das du lynx-keyboard-handling willst  3. sag mal, bin
67  *      ich eigentlich dein codesklave? :)
68  *    -detailed changes: up/down now also selects links
69  *                       this ugly tab/shift-tab handling removed
70  *                       pgup/pgdown updates current link
71  *                       keyboard handling much more lynx-like
72  *    -this code even got worse during change of the keyboard-handling
73  *     (but using it got even nicer)
74  *    -hope kb likes this as it is (nich wahr, meister, sei ma zufrieden! :)
75  *    -fixed this nasty bug which crashed opencp (seemed to be some un-
76  *     initialized pointer, or something else...)
77  *  -ryg981121  Fabian Giesen <fabian@jdcs.su.nw.schule.de>
78  *    -now i'm using my handle instead of my name for changelog (big change,
79  *     eh?)
80  *    -fixed some stupid bugs with pgup/pgdown handling
81  *    -also fixed normal scrolling (hope it works correctly now...)
82  *    -no comment from kb about my "lynx style key handling" yet...
83  *  -fd981205  Felix Domke <tmbinc@gmx.net>
84  *    -included the stdlib.h AGAIN AND AGAIN. hopefully this version will now
85  *     finally reach the repository ;)
86  *     (still using my realname... ;)
87  *  -fd981206   Felix Domke    <tmbinc@gmx.net>
88  *    -edited for new binfile
89  *  -ss040911   Stian Skjelstad <stian@nixia.no>
90  *    -stupid misstake prevented cp.hlp to be imported from cp.pak
91  */
92 
93 #include "config.h"
94 #include <errno.h>
95 #include <stdlib.h>
96 #include <stdio.h>
97 #include <string.h>
98 #include <unistd.h>
99 #include <zlib.h>
100 #include "boot/psetting.h"
101 #include "boot/plinkman.h"
102 #include "cpiface/cpiface.h"
103 #include "help/cphelper.h"
104 #include "stuff/compat.h"
105 #include "stuff/err.h"
106 #include "stuff/poutput.h"
107 #include "types.h"
108 
109 static unsigned int plWinFirstLine, plWinHeight, plHelpHeight, plHelpScroll;
110 
111 static const uint32_t  Helpfile_ID=1213219663;
112 static const uint32_t  Helpfile_Ver=0x011000;
113 static uint32_t   Helppages;
114 static int        HelpfileErr=hlpErrNoFile;
115 static helppage  *Page, *curpage;
116 static help_link *curlink;
117 static int        link_ind;
118 
119 /* Helpfile Commands */
120 
121 #define   CMD_NORMAL     1
122 #define   CMD_BRIGHT     2
123 #define   CMD_HYPERLINK  3
124 #define   CMD_CENTERED   4
125 #define   CMD_CHCOLOUR   5
126 #define   CMD_RAWCHAR    6
127 #define   CMD_LINEFEED  10             /* this is a pseudo-command... */
128 #define   CMD_MAX       31
129 
130 /* Useful macros... */
131 
132 #define   MIN(a,b)      ((a)<(b)?(a):(b))
133 #define   MAX(a,b)      ((a)>(b)?(a):(b))
134 #define   ABS(a)        ((a)<0?-(a):(a))
135 
136 /* ---------------------------- here starts the viewer */
137 
doReadVersion100Helpfile(FILE * file)138 static int doReadVersion100Helpfile(FILE *file)
139 {
140 	unsigned int i;
141 	if (fread(&Helppages, sizeof(Helppages), 1, file) != 1)
142 	{
143 		perror(__FILE__ ": fread failed #1: ");
144 		return hlpErrBadFile;
145 	}
146 	Page=calloc(Helppages, sizeof(Page[0]));
147 
148 	for (i=0; i<Helppages; i++)
149 	{
150 		unsigned char len;
151 
152 		memset(Page[i].name, 0, 128);
153 		if (fread(&len, sizeof(len), 1, file) != 1)
154 		{
155 			perror(__FILE__ ": fread failed #2: ");
156 			return hlpErrBadFile;
157 		}
158 		if (fread(Page[i].name, len, 1, file) != 1)
159 		{
160 			perror(__FILE__ ": fread failed #3: ");
161 			return hlpErrBadFile;
162 		}
163 
164 		memset(Page[i].desc, 0, 128);
165 		if (fread(&len, sizeof(len), 1, file) != 1)
166 		{
167 			perror(__FILE__ ": fread failed #4: ");
168 			return hlpErrBadFile;
169 		}
170 
171 		if (fread(Page[i].desc, len, 1, file) != 1)
172 		{
173 			perror(__FILE__ ": fread failed #5: ");
174 			return hlpErrBadFile;
175 		}
176 
177 		if (fread(&Page[i].size, sizeof(Page[i].size), 1, file) != 1)
178 		{
179 			perror(__FILE__ ": fread failed #6: ");
180 			return hlpErrBadFile;
181 		}
182 
183 		Page[i].size = uint32_little (Page[i].size);
184 		if (fread(&Page[i].lines, sizeof(Page[i].lines), 1, file) != 1)
185 		{
186 			perror(__FILE__ ": fread failed #7: ");
187 			return hlpErrBadFile;
188 		}
189 
190 		Page[i].lines = uint32_little (Page[i].lines);
191 
192 		Page[i].links=NULL;
193 		Page[i].rendered=NULL;
194 	};
195 
196 	for (i=0; i<Helppages; i++)
197 	{
198 		Page[i].data=calloc(Page[i].size, 1);
199 		if (fread(Page[i].data, Page[i].size, 1, file) != 1)
200 		{
201 			perror(__FILE__ ": fread failed #8: ");
202 			return hlpErrBadFile;
203 		}
204 	};
205 
206 	return hlpErrOk;
207 }
208 
doReadVersion110Helpfile(FILE * file)209 static int doReadVersion110Helpfile(FILE *file)
210 {
211 	int  *compdatasize;
212 	char *inbuf;
213 	unsigned int i;
214 
215 	if (fread(&Helppages, sizeof(Helppages), 1, file) != 1)
216 	{
217 		perror(__FILE__ ": fread failed #9: ");
218 		return hlpErrBadFile;
219 	}
220 	Helppages = uint32_little (Helppages);
221 	Page = calloc(Helppages, sizeof(Page[0]));
222 
223 	compdatasize=calloc(Helppages, sizeof(int));
224 
225 	for (i=0; i<Helppages; i++)
226 	{
227 		unsigned char len;
228 
229 		memset(Page[i].name, 0, 128);
230 		if (fread(&len, sizeof(len), 1, file) != 1)
231 		{
232 			perror(__FILE__ ": fread failed #10: ");
233 			free(compdatasize);
234 			return hlpErrBadFile;
235 		}
236 		if (fread(Page[i].name, len, 1, file) != 1)
237 		{
238 			perror(__FILE__ ": fread failed #11: ");
239 			free(compdatasize);
240 			return hlpErrBadFile;
241 		}
242 
243 		memset(Page[i].desc, 0, 128);
244 		if (fread(&len, sizeof(len), 1, file) != 1)
245 		{
246 			perror(__FILE__ ": fread failed #12: ");
247 			free(compdatasize);
248 			return hlpErrBadFile;
249 		}
250 		if (fread(Page[i].desc, len, 1, file) != 1)
251 		{
252 			perror(__FILE__ ": fread failed #13: ");
253 			free(compdatasize);
254 			return hlpErrBadFile;
255 		}
256 
257 		if (fread(&Page[i].size, sizeof(Page[i].size), 1, file) != 1)
258 		{
259 			perror(__FILE__ ": fread failed #14: ");
260 			free(compdatasize);
261 			return hlpErrBadFile;
262 		}
263 		Page[i].size = uint32_little (Page[i].size);
264 		if (fread(&Page[i].lines, sizeof(Page[i].lines), 1, file) != 1)
265 		{
266 			perror(__FILE__ ": fread failed #15: ");
267 			free(compdatasize);
268 			return hlpErrBadFile;
269 		}
270 		Page[i].lines = uint32_little (Page[i].lines);
271 
272 		if (fread(&compdatasize[i], sizeof(compdatasize[i]), 1, file) != 1)
273 		{
274 			perror(__FILE__ ": fread failed #16: ");
275 			free(compdatasize);
276 			return hlpErrBadFile;
277 		}
278 		compdatasize[i]=uint32_little(compdatasize[i]);
279 		Page[i].links=NULL;
280 		Page[i].rendered=NULL;
281 	};
282 
283 	for (i=0; i<Helppages; i++)
284 	{
285 		uLongf temp=Page[i].size;
286 		Page[i].data=calloc(Page[i].size, 1);
287 		inbuf=calloc(compdatasize[i], 1);
288 		if (fread(inbuf, compdatasize[i], 1, file) != 1)
289 		{
290 			perror(__FILE__ ": fread failed #17: ");
291 			free(compdatasize);
292 			free(inbuf);
293 			return hlpErrBadFile;
294 		}
295 		uncompress((Bytef *)Page[i].data, &temp, (Bytef *)inbuf, compdatasize[i]);
296 		Page[i].size=temp;
297 		free(inbuf);
298 	}
299 	free(compdatasize);
300 
301 	return hlpErrOk;
302 }
303 
doReadHelpFile(FILE * file)304 static int doReadHelpFile(FILE *file)
305 {
306 	uint32_t version;
307 	uint32_t temp;
308 
309 	if (fread(&temp, sizeof(temp), 1, file) != 1)
310 	{
311 		perror(__FILE__ ": fread failed #18: ");
312 		return hlpErrBadFile;
313 	}
314 	temp = uint32_little (temp);
315 	if (temp!=Helpfile_ID)
316 		return hlpErrBadFile;
317 
318 	if (fread(&version, sizeof(version), 1, file) != 1)
319 	{
320 		perror(__FILE__ ": fread failed #19: ");
321 		return hlpErrBadFile;
322 	}
323 	version = uint32_little (version);
324 
325 	if (version>Helpfile_Ver)
326 		return hlpErrTooNew;
327 	if (version<0x10000)
328 		return hlpErrBadFile;
329 
330 	switch (version >> 8)
331 	{
332 		case 0x100:
333 			return doReadVersion100Helpfile(file);
334 		case 0x110:
335 			return doReadVersion110Helpfile(file);
336 		default:
337 			return hlpErrBadFile;
338 	};
339 }
340 
plReadHelpExternal(void)341 static char plReadHelpExternal(void)
342 {
343 	char   *helpname;
344 	FILE   *bf;
345 
346 	if (Page && (HelpfileErr==hlpErrOk))
347 		return 1;
348 
349 	makepath_malloc (&helpname, 0, cfDataDir, "ocp.hlp", 0);
350 	if ((bf=fopen(helpname, "r")))
351 	{
352 		free (helpname);
353 		HelpfileErr=doReadHelpFile(bf);
354 		fclose(bf);
355 	} else {
356 		fprintf (stderr, "Failed to open(%s): %s\n", helpname, strerror (errno));
357 		free (helpname);
358 		HelpfileErr=hlpErrNoFile;
359 		return 0;
360 	};
361 
362 	return (HelpfileErr==hlpErrOk);
363 }
364 
brDecodeRef(char * name)365 helppage *brDecodeRef(char *name)
366 {
367 	unsigned int i;
368 	for (i=0; i<Helppages; i++)
369 		if (!strcasecmp(Page[i].name, name))
370 			return &Page[i];
371 	return NULL;
372 }
373 
firstLinkOnPage(helppage * pg)374 static help_link *firstLinkOnPage(helppage *pg)
375 {
376 	if (!pg->linkcount)
377 		return NULL;
378 	return &pg->links[0];
379 }
380 
linkOnCurrentPage(help_link * lnk)381 static int linkOnCurrentPage(help_link *lnk)
382 {
383 	unsigned int y;
384 
385 	if (!lnk)
386 		return 0;
387 
388 	y=lnk->posy;
389 	if ((y>=plHelpScroll) && (y<plHelpScroll+plWinHeight))
390 		return 1;
391 
392 	return 0;
393 }
394 
brRenderPage(helppage * pg)395 void brRenderPage(helppage *pg)
396 {
397 	link_list *lst, *endlst;
398 	int        lcount;
399 	uint16_t   linebuf[80];
400 	char      *data;
401 	char       attr;
402 	int        x, y, i;
403 
404 	if (pg->rendered)
405 	{
406 		free(pg->rendered);
407 		pg->rendered=NULL;
408 	};
409 
410 	if (pg->links)
411 	{
412 		free(pg->links);
413 		pg->links=NULL;
414 	};
415 
416 	lst=endlst=NULL;
417 	lcount=0;
418 	x=y=0;
419 	attr=0x07;
420 
421 	pg->rendered=calloc(80*MAX(pg->lines, 1), sizeof(uint16_t));;
422 	memset(pg->rendered, 0, 160*MAX(pg->lines, 1));
423 	memset(linebuf, 0, 160);
424 
425 	data=pg->data;
426 	i=pg->size;
427 
428 	while (i>0)
429 	{
430 		if ((uint8_t)*data<CMD_MAX)
431 		{
432 			switch (*data)
433 			{
434 				case CMD_NORMAL:
435 					attr=0x07;
436 					break;
437 				case CMD_BRIGHT:
438 					attr=0x0f;
439 					break;
440 				case CMD_HYPERLINK:
441 					{
442 						char linkbuf[256];
443 						int  llen;
444 
445 						data++;
446 						i--;
447 						strcpy(linkbuf, data);
448 
449 						if (!endlst)
450 						{
451 							lst=calloc(sizeof(link_list), 1);
452 							endlst=lst;
453 						} else {
454 							 endlst->next=calloc(sizeof(link_list), 1);
455 							 endlst=endlst->next;
456 						};
457 
458 						*strchr(linkbuf, ',')=0;
459 						endlst->ref=(void *) brDecodeRef(linkbuf);
460 
461 						i-=strchr(data, ',')-data+1;
462 						data+=strchr(data, ',')-data+1;
463 
464 						llen=0;
465 
466 						endlst->posx=x;
467 						endlst->posy=y;
468 
469 						while (*data)
470 						{
471 							if ((x<80)&&(*data!=CMD_RAWCHAR))
472 							{
473 								linebuf[x]=(*data)|0x0300;
474 								x++;
475 								llen++;
476 							};
477 
478 							data++; i--;
479 						};
480 
481 						endlst->len=llen;
482 
483 						lcount++;
484 
485 						break;
486 					}
487 				case CMD_CENTERED:
488 					data++;
489 					i--;
490 
491 					x=40-(strlen(data) >> 1);
492 					if (x<0)
493 						x=0;
494 
495 					while (*data)
496 					{
497 						if (x<80)
498 						{
499 							linebuf[x]=(*data)|(attr<<8);
500 							x++;
501 						};
502 
503 						data++;
504 						i--;
505 					};
506 
507 					break;
508 				case CMD_CHCOLOUR:
509 					data++;
510 					i--;
511 					attr=*data;
512 					break;
513 				case CMD_RAWCHAR:
514 					data++;
515 					i--;
516 
517 					if (x<80)
518 					{
519 						linebuf[x]=(*data)|(attr<<8);
520 						x++;
521 					};
522 
523 					break;
524 				case CMD_LINEFEED:
525 					memcpy(&pg->rendered[y*80], linebuf, 160);
526 					x=0;
527 					y++;
528 					memset(linebuf, 0, 160);
529 					break;
530 			};
531 
532 			data++; i--;
533 		} else {
534 			if (x<80)
535 			{
536 				linebuf[x]=((uint8_t)*data)|(attr<<8);
537 				x++;
538 			};
539 
540 			data++;
541 			i--;
542 		};
543 	};
544 
545 	pg->links=calloc(sizeof(help_link), lcount);
546 	pg->linkcount=lcount;
547 
548 	for (i=0; i<lcount; i++)
549 	{
550 		pg->links[i].posx=lst->posx;
551 		pg->links[i].posy=lst->posy;
552 		pg->links[i].len=lst->len;
553 		pg->links[i].ref=lst->ref;
554 
555 		endlst=lst;
556 		lst=lst->next;
557 		free(endlst);
558 	}
559 }
560 
brSetPage(helppage * page)561 void brSetPage(helppage *page)
562 {
563 	if (!page)
564 		return;
565 
566 	if (curpage)
567 	{
568 		if (curpage->rendered)
569 		{
570 			free(curpage->rendered);
571 			curpage->rendered=NULL;
572 		};
573 
574 		if (curpage->links)
575 		{
576 			free(curpage->links);
577 			curpage->links=NULL;
578 		};
579 	};
580 
581 	curpage=page;
582 	brRenderPage(curpage);
583 
584 	plHelpHeight=curpage->lines;
585 	plHelpScroll=0;
586 
587 	curlink=firstLinkOnPage(curpage);
588 	if (!curlink)
589 		link_ind=-1;
590 	else
591 		link_ind=0;
592 }
593 
brDisplayHelp(void)594 void brDisplayHelp(void)
595 {
596 	unsigned int curlinky;
597 	char destbuffer[60];
598 	char strbuffer[256];
599 	char numbuffer[4];
600 	int descxp;
601 	unsigned int y;
602 
603 	if ((plHelpScroll+plWinHeight)>plHelpHeight)
604 		plHelpScroll=plHelpHeight-plWinHeight;
605 
606 	if ((signed)plHelpScroll<0)
607 		plHelpScroll=0;
608 
609 	if (curlink)
610 		curlinky=(curlink->posy)-plHelpScroll;
611 	else
612 		curlinky=-1;
613 
614 	displaystr(plWinFirstLine-1, 0, 0x09, "   OpenCP help ][   ", 20);
615 
616 
617 	if (HelpfileErr==hlpErrOk)
618 		strcpy(strbuffer, curpage->desc);
619 	else
620 		strcpy(strbuffer, "Error!");
621 
622 	_convnum(100*plHelpScroll/MAX(plHelpHeight-plWinHeight, 1), numbuffer, 10, 3);
623 
624 	strcat(strbuffer, "-");
625 	strcat(strbuffer, numbuffer);
626 	strcat(strbuffer, "%");
627 
628 	memset(destbuffer, 0x20, 60);
629 	descxp=MAX(0, 59-(signed)strlen(strbuffer));
630 
631 	strncpy(&destbuffer[descxp], strbuffer, 59-descxp);
632 	displaystr(plWinFirstLine-1, 20, 0x08, destbuffer, 59);
633 
634 
635 	if (HelpfileErr!=hlpErrOk)
636 	{
637 		char errormsg[80];
638 
639 		strcpy(errormsg, "Error: ");
640 
641 		switch (HelpfileErr)
642 		{
643 			case hlpErrNoFile:
644 				strcat(errormsg, "Helpfile \"OCP.HLP\" is not present");
645 				break;
646 			case hlpErrBadFile:
647 				strcat(errormsg, "Helpfile \"OCP.HLP\" is corrupted");
648 				break;
649 			case hlpErrTooNew:
650 				strcat(errormsg, "Helpfile version is too new. Please update.");
651 				break;
652 			default:
653 				strcat(errormsg, "Currently undefined help error");
654 		};
655 
656 		displayvoid(plWinFirstLine, 0, CONSOLE_MAX_X);
657 
658 		displaystr(plWinFirstLine+1, 4, 0x04, errormsg, 74);
659 
660 		for (y=2; y<plWinHeight; y++)
661 			displayvoid(y+plWinFirstLine, 0, CONSOLE_MAX_X);
662 	} else {
663 		int addx = (plScrWidth - 80) / 2;
664 		for (y=0; y<plWinHeight; y++)
665 		{
666 			if ((y+plHelpScroll)>=plHelpHeight)
667 			{
668 				displayvoid(y+plWinFirstLine, 0, plScrWidth);
669 				continue;
670 			}
671 			if (y!=curlinky)
672 			{
673 				displayvoid(y+plWinFirstLine, 0, addx);
674 				displaystrattr(y+plWinFirstLine, 0+addx, &curpage->rendered[(y+plHelpScroll)*80], 80);
675 				displayvoid(y+plWinFirstLine, 80+addx, plScrWidth-80-addx);
676 			} else {
677 				int yp=(y+plHelpScroll)*80;
678 				int xp;
679 			        char dummystr[82];
680 				int i, off;
681 
682 				displayvoid(y+plWinFirstLine, 0, addx);
683 				if (curlink->posx!=0)
684 			        {
685 					displaystrattr(y+plWinFirstLine, addx, &curpage->rendered[yp], curlink->posx);
686 			        };
687 
688 			        xp=curlink->posx+curlink->len;
689 
690 			        displaystrattr(y+plWinFirstLine, xp+addx,
691 		                       &curpage->rendered[yp+xp],
692 		                       79-xp);
693 
694 
695 				for (i=0, off=yp+curlink->posx; curpage->rendered[off] & 0xff; i++, off++)
696 					dummystr[i]=curpage->rendered[off] & 0xff;
697 
698 				dummystr[i]=0;
699 
700 				displaystr(y+plWinFirstLine, curlink->posx+addx, 4, dummystr, curlink->len);
701 
702 				displayvoid(y+plWinFirstLine, 80+addx, plScrWidth-80-addx);
703 
704 			        /* and all this just to prevent flickering. ARG! */
705 			}
706 		}
707 	}
708 }
709 
processActiveHyperlink(void)710 static void processActiveHyperlink(void)
711 {
712 	if (curlink)
713 		brSetPage((helppage *) curlink->ref);
714 }
715 
brSetWinStart(int fl)716 void brSetWinStart(int fl)
717 {
718 	plWinFirstLine=fl;
719 }
720 
brSetWinHeight(int h)721 void brSetWinHeight(int h)
722 {
723 	plWinHeight=h;
724 }
725 
brHelpKey(uint16_t key)726 int brHelpKey(uint16_t key)
727 {
728 	help_link *link2;
729 
730 	if(!curpage)
731 		return 1;
732 
733 	switch (key)
734 	{
735 		case KEY_ALT_K:
736 			cpiKeyHelp(KEY_UP, "Scroll help page up");
737 			cpiKeyHelp(KEY_DOWN, "Scroll help page down");
738 			cpiKeyHelp(KEY_PPAGE, "Scroll help page, a page up");
739 			cpiKeyHelp(KEY_NPAGE, "Scroll help page, a page down");
740 			cpiKeyHelp(KEY_HOME, "Scroll help page, to the start");
741 			cpiKeyHelp(KEY_END, "Scroll help page, to the bottom");
742 			cpiKeyHelp(KEY_ALT_C, "Goto contents help page");
743 			cpiKeyHelp(KEY_ALT_I, "Goto index help page");
744 			cpiKeyHelp(KEY_ALT_L, "Goto licence help page");
745 			cpiKeyHelp(KEY_TAB, "Goto next link");
746 			cpiKeyHelp(KEY_SHIFT_TAB, "Goto previous link");
747 			return 0;
748 		/*case 0x4800: //up*/
749 		case KEY_UP:
750 			if (curpage->linkcount)
751 			{
752 				link2=&curpage->links[MAX(link_ind-1, 0)];
753 
754 				if (link2!=curlink)
755 				{
756 					if ((signed)(plHelpScroll-link2->posy)>1)
757 						plHelpScroll--;
758 					else {
759 						link_ind=MAX(link_ind-1, 0);
760 						curlink=link2;
761 
762 						if (link2->posy<plHelpScroll)
763 							plHelpScroll=link2->posy;
764 					}
765 				} else
766 					if (plHelpScroll>0)
767 						plHelpScroll--;
768 			} else
769 				if (plHelpScroll>0)
770 					plHelpScroll--;
771 			break;
772 		/*case 0x5000: //down*/
773 		case KEY_DOWN:
774 			if (curpage->linkcount)
775 			{
776 				link2=&curpage->links[MIN(link_ind+1, curpage->linkcount-1)];
777 
778 				if (link2->posy-plHelpScroll>plWinHeight)
779 					plHelpScroll++;
780 				else {
781 					link_ind=MIN(link_ind+1, curpage->linkcount-1);
782 					curlink=link2;
783 
784 					if (link2->posy>(plHelpScroll+plWinHeight))
785 						plHelpScroll=link2->posy;
786 					else
787 						if (link2->posy==(plHelpScroll+plWinHeight))
788 							plHelpScroll++;
789 				}
790 			} else
791 				if (plHelpScroll<plHelpHeight-1)
792 					plHelpScroll++;
793 
794 			break;
795 		/*case 0x4900: //pgup*/
796 		case KEY_PPAGE:
797 			plHelpScroll-=plWinHeight;
798 			if ((signed)plHelpScroll<0)
799 				plHelpScroll=0;
800 
801 			if (curpage->linkcount)
802 			{
803 				if (!linkOnCurrentPage(curlink))
804 				{
805 					int bestmatch, bestd;
806 					int i;
807 
808 					bestd=2000000;
809 					bestmatch=-1;
810 
811 					for (i=curpage->linkcount-1; i>=0; i--)
812 					{
813 						int d=ABS((signed)plHelpScroll+(signed)plWinHeight-(signed)curpage->links[i].posy-1);
814 						if (d<bestd)
815 						{
816 							bestd=d;
817 							bestmatch=i;
818 						};
819 					};
820 					link_ind=bestmatch;
821 					curlink=&curpage->links[link_ind];
822 				};
823 			};
824 			break;
825 		/*case 0x5100: //pgdn*/
826 		case KEY_NPAGE:
827 			plHelpScroll+=plWinHeight;
828 
829 			if (plHelpScroll>(plHelpHeight-plWinHeight))
830 				plHelpScroll=plHelpHeight-plWinHeight;
831 
832 			if (curpage->linkcount)
833 			{
834 				if (!linkOnCurrentPage(curlink))
835 				{
836 					int bestmatch, bestd;
837 					int i;
838 
839 					bestd=2000000;
840 					bestmatch=-1;
841 
842 				        for (i=0; i<curpage->linkcount; i++)
843 				        {
844 						int d=ABS((signed)plHelpScroll-(signed)curpage->links[i].posy);
845 						if (d<bestd)
846 						{
847 							bestd=d;
848 							bestmatch=i;
849 						};
850 					};
851 
852 					link_ind=bestmatch;
853 					curlink=&curpage->links[link_ind];
854 				};
855 			};
856 			break;
857 		/*case 0x4700: //home*/
858 		case KEY_HOME:
859 			plHelpScroll=0;
860 			break;
861 		/*case 0x4F00: //end*/
862 		case KEY_END:
863 			plHelpScroll=plHelpHeight-plWinHeight;
864 			break;
865 		case KEY_ALT_C:
866 			brSetPage(brDecodeRef("Contents"));
867 			break;
868 		case KEY_ALT_I:
869 			brSetPage(brDecodeRef("Index"));
870 			break;
871 		case KEY_ALT_L:
872 			brSetPage(brDecodeRef("License"));
873 			break;
874 		case KEY_TAB:
875 			if (curpage->linkcount)
876 			{
877 				if (linkOnCurrentPage(curlink))
878 				{
879 					link_ind=(link_ind+1)%curpage->linkcount;
880 					curlink=&curpage->links[link_ind];
881 				}
882 				if (!linkOnCurrentPage(curlink))
883 					plHelpScroll=curlink->posy;
884 			}
885 			break;
886 		case KEY_SHIFT_TAB: /* 0x0f00: //shift-tab*/
887 			if (curpage->linkcount)
888 			{
889 				if (linkOnCurrentPage(curlink))
890 				{
891 					link_ind--;
892 					if (link_ind<0)
893 						link_ind=curpage->linkcount-1;
894 					curlink=&curpage->links[link_ind];
895 				}
896 				if (!linkOnCurrentPage(curlink))
897 					plHelpScroll=curlink->posy;
898 			}
899 			break;
900 /*  case 0x3920: case 0x3900: case 0x0020: case 0x000D: // space/enter*/
901 		case KEY_CTRL_J: case _KEY_ENTER: case ' ':
902 			processActiveHyperlink();
903 			break;
904 		default:
905 			return 0;
906 	}
907 	if ((plHelpScroll+plWinHeight)>plHelpHeight)
908 		plHelpScroll=plHelpHeight-plWinHeight;
909 	if ((signed)plHelpScroll<0)
910 		plHelpScroll=0;
911 	return 1;
912 }
913 
hlpGlobalInit(void)914 static int hlpGlobalInit(void)
915 {
916 	helppage *pg;
917 
918 	plHelpHeight=plHelpScroll=0;
919 
920 	if (!plReadHelpExternal())
921 	{
922 		fprintf(stderr, "Warning. Failed to read help files\n");
923 		return errOk; /* this error is not fatal to rest of the player */
924 	};
925 
926 	curpage=NULL;
927 
928 
929 	pg=brDecodeRef("Contents");
930 	if (!pg)
931 		HelpfileErr=hlpErrBadFile;
932 	else
933 		brSetPage(pg);
934 
935 	return errOk;
936 }
937 
hlpFreePages(void)938 void hlpFreePages(void)
939 {
940 	unsigned int i;
941 	for (i=0; i<Helppages; i++)
942 	{
943 		if (Page[i].data)
944 		{
945 			free(Page[i].data);
946 			Page[i].data=NULL;
947 		};
948 
949 		if (Page[i].rendered)
950 		{
951 			free(Page[i].rendered);
952 			Page[i].rendered=NULL;
953 		};
954 
955 		if (Page[i].links)
956 		{
957 			free(Page[i].links);
958 			Page[i].links=NULL;
959 		};
960 	};
961 
962 	free(Page);
963 	Page=NULL;
964 
965 	curpage=NULL;
966 	curlink=NULL;
967 
968 	Helppages=link_ind=0;
969 	HelpfileErr=hlpErrNoFile;
970 }
971 
hlpGlobalClose(void)972 static void hlpGlobalClose(void)
973 {
974 	hlpFreePages();
975 }
976 
977 #ifndef SUPPORT_STATIC_PLUGINS
978 char *dllinfo = "";
979 #endif
980 DLLEXTINFO_PREFIX struct linkinfostruct dllextinfo = {.name = "cphelper", .desc = "OpenCP help browser (c) 1998-09 Fabian Giesen", .ver = DLLVERSION, .size = 0, .Init = hlpGlobalInit, .Close = hlpGlobalClose};
981