summaryrefslogtreecommitdiff
path: root/src/teletext.c (plain)
blob: 04c0a3493c9e7a21bd13f2dd40cecf3668f865b1
1/*
2 * libzvbi -- Teletext decoder backend
3 *
4 * Copyright (C) 2000, 2001 Michael H. Schimek
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library 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 GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA.
20 */
21
22/* $Id: teletext.c,v 1.31 2008/02/22 03:10:16 mschimek Exp $ */
23
24#include "site_def.h"
25
26#ifdef HAVE_CONFIG_H
27# include "config.h"
28#endif
29
30#include <stdarg.h>
31#include <stdlib.h>
32#include <stdio.h>
33#include <string.h>
34#include <strings.h> /* strncasecmp */
35#include <ctype.h>
36#include <assert.h>
37
38#include "bcd.h"
39#include "vt.h"
40#include "export.h"
41#include "vbi.h"
42#include "hamm.h"
43#include "lang.h"
44#include "teletext_decoder.h"
45
46extern const char _zvbi_intl_domainname[];
47
48#include "intl-priv.h"
49
50#ifndef TELETEXT_DEBUG
51# define TELETEXT_DEBUG 0
52#endif
53
54#define printv(templ, args...) \
55do { \
56 if (TELETEXT_DEBUG) \
57 fprintf (stderr, templ ,##args); \
58} while (0)
59
60#define ROWS 25
61#define COLUMNS 40
62#define EXT_COLUMNS 41
63#define LAST_ROW ((ROWS - 1) * EXT_COLUMNS)
64
65extern vbi_bool
66_vbi_cache_get_sub_info(vbi_cache *ca, vbi_subno pgno, int *subs, int *len);
67
68
69/*
70 * FLOF navigation
71 */
72
73static const vbi_color
74flof_link_col[4] = { VBI_RED, VBI_GREEN, VBI_YELLOW, VBI_CYAN };
75
76static inline void
77flof_navigation_bar(vbi_page *pg, cache_page *vtp)
78{
79 vbi_char ac;
80 int n, i, k, ii;
81
82 memset(&ac, 0, sizeof(ac));
83
84 ac.foreground = VBI_WHITE;
85 ac.background = VBI_BLACK;
86 ac.opacity = pg->page_opacity[1];
87 ac.unicode = 0x0020;
88
89 for (i = 0; i < EXT_COLUMNS; i++)
90 pg->text[LAST_ROW + i] = ac;
91
92 ac.link = TRUE;
93
94 for (i = 0; i < 4; i++) {
95 ii = i * 10 + 3;
96
97 for (k = 0; k < 3; k++) {
98 n = ((vtp->data.lop.link[i].pgno >> ((2 - k) * 4)) & 15) + '0';
99
100 if (n > '9')
101 n += 'A' - '9';
102
103 ac.unicode = n;
104 ac.foreground = flof_link_col[i];
105 pg->text[LAST_ROW + ii + k] = ac;
106 pg->nav_index[ii + k] = i;
107 }
108
109 pg->nav_link[i].pgno = vtp->data.lop.link[i].pgno;
110 pg->nav_link[i].subno = vtp->data.lop.link[i].subno;
111 }
112}
113
114static inline void
115flof_links(vbi_page *pg, cache_page *vtp)
116{
117 vbi_char *acp = pg->text + LAST_ROW;
118 int i, j, k, col = -1, start = 0;
119
120 for (i = 0; i < COLUMNS + 1; i++) {
121 if (i == COLUMNS || (acp[i].foreground & 7) != col) {
122 for (k = 0; k < 4; k++)
123 if ((int) flof_link_col[k] == col)
124 break;
125
126 if (k < 4 && !NO_PAGE(vtp->data.lop.link[k].pgno)) {
127 /* Leading and trailing spaces not sensitive */
128
129 for (j = i - 1; j >= start && acp[j].unicode == 0x0020; j--);
130
131 for (; j >= start; j--) {
132 acp[j].link = TRUE;
133 pg->nav_index[j] = k;
134 }
135
136 pg->nav_link[k].pgno = vtp->data.lop.link[k].pgno;
137 pg->nav_link[k].subno = vtp->data.lop.link[k].subno;
138 }
139
140 if (i >= COLUMNS)
141 break;
142
143 col = acp[i].foreground & 7;
144 start = i;
145 }
146
147 if (start == i && acp[i].unicode == 0x0020)
148 start++;
149 }
150}
151
152/*
153 * TOP navigation
154 */
155
156static void character_set_designation(struct vbi_font_descr **font,
157 struct ttx_extension *ext,
158 cache_page *vtp);
159static void screen_color(vbi_page *pg, int flags, int color);
160
161static vbi_bool
162top_label(vbi_decoder *vbi, vbi_page *pg, struct vbi_font_descr *font,
163 int index, int pgno, int foreground, int ff)
164{
165 int column = index * 13 + 1;
166 vbi_char *acp;
167 struct ttx_ait_title *ait;
168 int i, j;
169
170 acp = &pg->text[LAST_ROW + column];
171
172 for (i = 0; i < 8; i++)
173 if (PAGE_FUNCTION_AIT == vbi->cn->btt_link[i].function) {
174 cache_page *vtp;
175
176 vtp = _vbi_cache_get_page
177 (vbi->ca, vbi->cn,
178 vbi->cn->btt_link[i].pgno,
179 vbi->cn->btt_link[i].subno,
180 /* subno_mask */ 0x3f7f);
181 if (!vtp) {
182 printv ("top ait page %x not cached\n",
183 vbi->cn->btt_link[i].pgno);
184 continue;
185 } else if (vtp->function != PAGE_FUNCTION_AIT) {
186 printv("no ait page %x\n", vtp->pgno);
187 cache_page_unref (vtp);
188 vtp = NULL;
189 continue;
190 }
191
192 for (ait = vtp->data.ait.title, j = 0;
193 j < 46; ait++, j++) {
194 if (ait->link.pgno == pgno) {
195 pg->nav_link[index].pgno = pgno;
196 pg->nav_link[index].subno = VBI_ANY_SUBNO;
197
198 for (i = 11; i >= 0; i--)
199 if (ait->text[i] > 0x20)
200 break;
201
202 if (ff && (i <= (11 - ff))) {
203 acp += (11 - ff - i) >> 1;
204 column += (11 - ff - i) >> 1;
205
206 acp[i + 1].link = TRUE;
207 pg->nav_index[column + i + 1] = index;
208
209 acp[i + 2].unicode = 0x003E;
210 acp[i + 2].foreground = foreground;
211 acp[i + 2].link = TRUE;
212 pg->nav_index[column + i + 2] = index;
213
214 if (ff > 1) {
215 acp[i + 3].unicode = 0x003E;
216 acp[i + 3].foreground = foreground;
217 acp[i + 3].link = TRUE;
218 pg->nav_index[column + i + 3] = index;
219 }
220 } else {
221 acp += (11 - i) >> 1;
222 column += (11 - i) >> 1;
223 }
224
225 for (; i >= 0; i--) {
226 acp[i].unicode = vbi_teletext_unicode(font->G0, font->subset,
227 (ait->text[i] < 0x20) ? 0x20 : ait->text[i]);
228 acp[i].foreground = foreground;
229 acp[i].link = TRUE;
230 pg->nav_index[column + i] = index;
231 }
232
233 cache_page_unref (vtp);
234 vtp = NULL;
235
236 return TRUE;
237 }
238 }
239
240 cache_page_unref (vtp);
241 vtp = NULL;
242 }
243
244 return FALSE;
245}
246
247static __inline__ vbi_pgno
248add_modulo (vbi_pgno pgno,
249 int incr)
250{
251 return ((pgno - 0x100 + incr) & 0x7FF) + 0x100;
252}
253
254static inline void
255top_navigation_bar(vbi_decoder *vbi, vbi_page *pg,
256 cache_page *vtp)
257{
258 struct ttx_page_stat *ps;
259 vbi_char ac;
260 vbi_pgno pgno1;
261 int i, got;
262
263 ps = cache_network_page_stat (vbi->cn, vtp->pgno);
264 printv("PAGE MIP/BTT: %d\n", ps->page_type);
265
266 memset(&ac, 0, sizeof(ac));
267
268 ac.foreground = 32 + VBI_WHITE;
269 ac.background = 32 + VBI_BLACK;
270 ac.opacity = pg->page_opacity[1];
271 ac.unicode = 0x0020;
272
273 for (i = 0; i < EXT_COLUMNS; i++)
274 pg->text[LAST_ROW + i] = ac;
275
276 if (pg->page_opacity[1] != VBI_OPAQUE)
277 return;
278
279 pgno1 = add_modulo (vtp->pgno, 1);
280
281 for (i = vtp->pgno; i != pgno1; i = add_modulo (i, -1)) {
282 struct ttx_page_stat *ps;
283
284 ps = cache_network_page_stat (vbi->cn, i);
285 if (ps->page_type == VBI_TOP_BLOCK ||
286 ps->page_type == VBI_TOP_GROUP) {
287 top_label(vbi, pg, pg->font[0], 0, i, 32 + VBI_WHITE, 0);
288 break;
289 }
290 }
291
292 for (i = pgno1, got = FALSE; i != vtp->pgno; i = add_modulo (i, 1)) {
293 struct ttx_page_stat *ps;
294
295 ps = cache_network_page_stat (vbi->cn, i);
296 switch (ps->page_type) {
297 case VBI_TOP_BLOCK:
298 top_label(vbi, pg, pg->font[0], 2, i, 32 + VBI_YELLOW, 2);
299 return;
300
301 case VBI_TOP_GROUP:
302 if (!got) {
303 top_label(vbi, pg, pg->font[0], 1, i, 32 + VBI_GREEN, 1);
304 got = TRUE;
305 }
306
307 break;
308 }
309 }
310}
311
312static struct ttx_ait_title *
313next_ait(vbi_decoder *vbi, int pgno, int subno, cache_page **mvtp)
314{
315 struct ttx_ait_title *ait, *mait = NULL;
316 int mpgno = 0xFFF, msubno = 0xFFFF;
317 int i, j;
318
319 *mvtp = NULL;
320
321 for (i = 0; i < 8; i++) {
322 if (PAGE_FUNCTION_AIT == vbi->cn->btt_link[i].function) {
323 cache_page *vtp;
324
325 vtp = _vbi_cache_get_page
326 (vbi->ca, vbi->cn,
327 vbi->cn->btt_link[i].pgno,
328 vbi->cn->btt_link[i].subno,
329 /* subno_mask */ 0x3f7f);
330 if (!vtp) {
331 printv("top ait page %x not cached\n",
332 vbi->cn->btt_link[i].pgno);
333 continue;
334 } else if (vtp->function != PAGE_FUNCTION_AIT) {
335 printv("no ait page %x\n", vtp->pgno);
336 cache_page_unref (vtp);
337 vtp = NULL;
338 continue;
339 }
340
341 for (ait = vtp->data.ait.title, j = 0; j < 46; ait++, j++) {
342 if (!ait->link.pgno)
343 break;
344
345 if (ait->link.pgno < pgno
346 || (ait->link.pgno == pgno && ait->link.subno <= subno))
347 continue;
348
349 if (ait->link.pgno > mpgno
350 || (ait->link.pgno == mpgno && ait->link.subno > msubno))
351 continue;
352
353 mait = ait;
354 mpgno = ait->link.pgno;
355 msubno = ait->link.subno;
356
357 if (NULL != *mvtp)
358 cache_page_unref (*mvtp);
359
360 *mvtp = vtp;
361 }
362 }
363 }
364
365 return mait;
366}
367
368static int
369top_index(vbi_decoder *vbi, vbi_page *pg, int subno)
370{
371 cache_page *vtp = NULL;
372 vbi_char ac, *acp;
373 struct ttx_ait_title *ait;
374 int i, j, k, n, lines;
375 int xpgno, xsubno;
376 struct ttx_extension *ext;
377 char *index_str;
378
379 pg->vbi = vbi;
380
381 subno = vbi_bcd2dec(subno);
382
383 pg->rows = ROWS;
384 pg->columns = EXT_COLUMNS;
385
386 pg->dirty.y0 = 0;
387 pg->dirty.y1 = ROWS - 1;
388 pg->dirty.roll = 0;
389
390 ext = &cache_network_magazine (vbi->cn, 0x100)->extension;
391
392 screen_color(pg, 0, 32 + VBI_BLUE);
393
394 vbi_transp_colormap(vbi, pg->color_map, ext->color_map, 40);
395
396 pg->drcs_clut = ext->drcs_clut;
397
398 pg->page_opacity[0] = VBI_OPAQUE;
399 pg->page_opacity[1] = VBI_OPAQUE;
400 pg->boxed_opacity[0] = VBI_OPAQUE;
401 pg->boxed_opacity[1] = VBI_OPAQUE;
402
403 memset(pg->drcs, 0, sizeof(pg->drcs));
404
405 memset(&ac, 0, sizeof(ac));
406
407 ac.foreground = VBI_BLACK; // 32 + VBI_BLACK;
408 ac.background = 32 + VBI_BLUE;
409 ac.opacity = VBI_OPAQUE;
410 ac.unicode = 0x0020;
411 ac.size = VBI_NORMAL_SIZE;
412
413 for (i = 0; i < EXT_COLUMNS * ROWS; i++)
414 pg->text[i] = ac;
415
416 ac.size = VBI_DOUBLE_SIZE;
417
418 /* FIXME */
419 /* TRANSLATORS: Title of TOP Index page,
420 for now please Latin-1 or ASCII only */
421 index_str = _("TOP Index");
422
423 for (i = 0; index_str[i]; i++) {
424 ac.unicode = index_str[i];
425 pg->text[1 * EXT_COLUMNS + 2 + i * 2] = ac;
426 }
427
428 ac.size = VBI_NORMAL_SIZE;
429
430 acp = &pg->text[4 * EXT_COLUMNS];
431 lines = 17;
432 xpgno = 0;
433 xsubno = 0;
434
435 while ((ait = next_ait(vbi, xpgno, xsubno, &vtp))) {
436 struct ttx_page_stat *ps;
437
438 xpgno = ait->link.pgno;
439 xsubno = ait->link.subno;
440
441 /* No docs, correct? */
442 character_set_designation(pg->font, ext, vtp);
443
444 if (subno > 0) {
445 if (lines-- == 0) {
446 subno--;
447 lines = 17;
448 }
449
450 cache_page_unref (vtp);
451 vtp = NULL;
452 continue;
453 } else if (lines-- <= 0) {
454 cache_page_unref (vtp);
455 vtp = NULL;
456 continue;
457 }
458
459 for (i = 11; i >= 0; i--)
460 if (ait->text[i] > 0x20)
461 break;
462
463 ps = cache_network_page_stat (vbi->cn, ait->link.pgno);
464 switch (ps->page_type) {
465 case VBI_TOP_GROUP:
466 k = 3;
467 break;
468
469 default:
470 k = 1;
471 }
472
473 for (j = 0; j <= i; j++) {
474 acp[k + j].unicode = vbi_teletext_unicode(pg->font[0]->G0,
475 pg->font[0]->subset, (ait->text[j] < 0x20) ? 0x20 : ait->text[j]);
476 }
477
478 for (k += i + 2; k <= 33; k++)
479 acp[k].unicode = '.';
480
481 for (j = 0; j < 3; j++) {
482 n = ((ait->link.pgno >> ((2 - j) * 4)) & 15) + '0';
483
484 if (n > '9')
485 n += 'A' - '9';
486
487 acp[j + 35].unicode = n;
488 }
489
490 acp += EXT_COLUMNS;
491
492 cache_page_unref (vtp);
493 }
494
495 cache_page_unref (vtp);
496 vtp = NULL;
497
498 return 1;
499}
500
501struct pex26 {
502 signed month : 8; /* 0 ... 11 */
503 signed day : 8; /* 0 ... 30 */
504 signed at1 : 16; /* min since 00:00 */
505 signed at2 : 16; /* min since 00:00 */
506 signed length : 16; /* min */
507 unsigned x26_cni : 16; /* see tables.c */
508 unsigned pty : 8;
509 signed lto : 8; /* +- 1/4 hr */
510 signed row : 8; /* title 1 ... 23 */
511 signed column : 8; /* title 0 ... 39 */
512 unsigned caf : 1;
513 unsigned : 15;
514};
515
516static void
517dump_pex26(struct pex26 *pt, int n)
518{
519 int i;
520
521 for (i = 0; i < n; i++, pt++)
522 fprintf(stderr, "%2d: %02d-%02d %d:%02d (%d:%02d) +%d, "
523 "cni=%04x pty=%02x lto=%d tit=%d:%d caf=%d\n",
524 i, pt->month, pt->day,
525 pt->at1 / 60, pt->at1 % 60,
526 pt->at2 / 60, pt->at2 % 60,
527 pt->length,
528 pt->x26_cni, pt->pty, pt->lto,
529 pt->row, pt->column,
530 pt->caf);
531}
532
533#if 0
534
535/*
536
537type pre text ____ post ____
538 / \
539AT-1 + zz.zz + % <
540AT-1 + zz.zz-zz.zz + % <
541PTL ++ title ++ %% :: <
542AT-1 % zz.zz + % <
543PW*) % hh
544LTO % 0zz + % <
545LTO % 9zz + % <
546AT-2 % zzzz + % <
547CNI*) % hhzzz + % <
548AD*) % zzzzzz + % <
549PTL %% title ++ %% :: <
550AT-2 : zzzz + % <
551AD : zzzzzz + % <
552PW :% hh + % <
553PTY :% Fhh + % <
554AT-2 :% zzzz + % <
555CNI :% hhzzz + % <
556AD :% zzzzzz + % <
557
558+ colour code
559: magenta
560% conceal
561
562*) permitted when CNI, AD, PW combine
563
564Note ETS 300 231 Table 4 is wrong: '%' = 0x18; ',' = '+' | '%' | '<'
565
566*/
567
568/* to be rewritten */
569
570struct program_entry {
571 int start;
572 int stop;
573 int at2;
574 int ad;
575 int cni;
576 int pty;
577 int lto;
578 uint16_t title[200];
579};
580
581#define PMA_COLOUR /* 0x01, 0x02, 0x03, 0x04, 0x06, 0x07 */
582#define PMA_MAGENTA 0x05
583#define PMA_CONCEAL 0x18
584
585#define IS_PMA_CTRL(c) (((c) >= 0x01 && (c) <= 0x07) || (c) == PMA_CONCEAL)
586
587static int
588bcd2time(int bcd)
589{
590 int sec = bcd & 15;
591 int min = (bcd >> 8) & 15;
592
593 if (sec > 9 || min > 9 || (bcd & 0x00FF) > 0x0059)
594 return -1;
595//#warning hour check
596
597 return sec * 1 + min * 60
598 + ((bcd >> 4) & 15) * 10
599 + ((bcd >> 12) & 15) * 600;
600}
601
602static int
603pdc_method_a(vbi_page *pg, cache_page *vtp, struct program_entry *pe)
604{
605 int row, column;
606 int i;
607
608// memset(pe, -1, sizeof(*pe));
609
610 i = 40;
611
612 for (row = 1; row <= 23; row++) {
613 for (column = 0; column <= 38;) {
614 int ctrl1 = vbi_parity(vtp->data.lop.raw[row][column]);
615 int ctrl2 = vbi_parity(vtp->data.lop.raw[row][column + 1]);
616
617fprintf(stderr, "%d %d %02x %02x\n", row, column, ctrl1, ctrl2);
618
619 if ((ctrl1 | ctrl2) < 0) {
620 return 0; /* hamming error */
621 } else if (!IS_PMA_CTRL(ctrl1)) {
622 column++;
623 continue;
624 }
625
626 if (ctrl1 == ctrl2 && ctrl1 != PMA_MAGENTA) {
627fprintf(stderr, "PTL %d %d\n", row, column);
628 /* title */
629 column += 2;fprintf(stderr, "%d %d %02x %02x\n", row, column, ctrl1, ctrl2);
630
631
632 } else {
633 /* numeral */
634 int digits, sep, value;
635fprintf(stderr, "NUM %d %d\n", row, column);
636 column += (ctrl1 == PMA_MAGENTA && ctrl2 == PMA_CONCEAL) ? 2 : 1;
637
638 sep = 0;
639 value = 0;
640
641 for (digits = 0; column < 40; column++) {
642 int c = vbi_parity(vtp->data.lop.raw[row][column]);
643
644 if (IS_PMA_CTRL(c)) {
645 break;
646 } else if (c >= 0x30 && c <= 0x39) {
647 value = value * 16 + c - 0x30;
648 digits++;
649 } else if (c >= 0x41 && c <= 0x46) {
650 if (digits >= 3)
651 goto invalid_pattern;
652 value = value * 16 + c + (0x0A - 0x41);
653 digits++;
654 } else if (c == 0x2E) {
655 if (digits != 2 && digits != 6)
656 goto invalid_pattern;
657 sep |= 1 << digits;
658 } else if (c == 0x2D) {
659 if (digits != 4)
660 goto invalid_pattern;
661 sep |= 1 << 4;
662 } else
663 goto invalid_pattern;
664 }
665
666 if (sep) {
667 if (ctrl1 == PMA_MAGENTA)
668 goto invalid_pattern;
669 if (ctrl1 == PMA_CONCEAL && digits != 4)
670 goto invalid_pattern;
671 }
672
673 switch (digits) {
674 int start, stop;
675
676 case 2:
677 /* Actually ctrl1 only permitted when combined */
678 if (ctrl1 != PMA_CONCEAL && ctrl2 != PMA_CONCEAL)
679 goto invalid_pattern;
680fprintf(stderr, "PW %02x\n", value);
681 /* PW */
682 break;
683
684 case 3:
685 if (ctrl1 == PMA_CONCEAL) {
686 if (value >= 0x100 && value < 0x900)
687 goto invalid_pattern;
688fprintf(stderr, "LTO %03x\n", value);
689 /* LTO */
690 } else if (ctrl2 == PMA_CONCEAL) {
691 if ((value -= 0xF00) < 0)
692 goto invalid_pattern;
693fprintf(stderr, "PTY %02x\n", value);
694 /* PTY */
695 } else
696 goto invalid_pattern;
697
698 case 4:
699 start = bcd2time(value);
700
701 if (start < 0)
702 goto invalid_pattern;
703
704 if (sep) {
705 if (ctrl1 == PMA_MAGENTA)
706 goto invalid_pattern;
707fprintf(stderr, "AT-1 %04x\n", value);
708 ; /* AT-1 short */
709 } else if (ctrl1 == PMA_MAGENTA || ctrl1 == PMA_CONCEAL) {
710fprintf(stderr, "AT-2 %04x\n", value);
711 ; /* AT-2 */
712 } else
713 goto invalid_pattern;
714
715 break;
716
717 case 5:
718 /* Actually ctrl1 only permitted when combined */
719 if ((ctrl1 != PMA_CONCEAL && ctrl2 != PMA_CONCEAL)
720 || (value & 0x00F00) > 0x00900)
721 goto invalid_pattern;
722
723 /* CNI */
724fprintf(stderr, "CNI %05x\n", value);
725 break;
726
727 case 6:
728 /* Actually ctrl1 only permitted when combined */
729 if (ctrl1 != PMA_CONCEAL && ctrl2 != PMA_CONCEAL)
730 goto invalid_pattern;
731 /* AD */
732fprintf(stderr, "AD %06x\n", value);
733 break;
734
735 case 8:
736 start = bcd2time(value >> 16);
737 stop = bcd2time(value);
738
739 if ((start | stop) < 0
740 || ctrl1 == PMA_MAGENTA || ctrl1 == PMA_CONCEAL
741 || sep != ((1 << 2) + (1 << 4) + (1 << 6)))
742 goto invalid_pattern;
743
744 /* AT-1 long */
745fprintf(stderr, "AT1 %08x\n", value);
746 break;
747
748 default:
749 invalid_pattern:
750 continue;
751 }
752 }
753 }
754 }
755
756 return 0; /* invalid */
757}
758
759#endif
760
761/*
762 * Zapzilla navigation
763 */
764
765static int
766keyword(vbi_link *ld, uint8_t *p, int column,
767 int pgno, int subno, int *back)
768{
769 uint8_t *s = p + column;
770 int i, j, k, l;
771
772 ld->type = VBI_LINK_NONE;
773 ld->name[0] = 0;
774 ld->url[0] = 0;
775 ld->pgno = 0;
776 ld->subno = VBI_ANY_SUBNO;
777 *back = 0;
778
779 if (isdigit(*s)) {
780 for (i = 0; isdigit(s[i]); i++)
781 ld->pgno = ld->pgno * 16 + (s[i] & 15);
782
783 if (isdigit(s[-1]) || i > 3)
784 return i;
785
786 if (i == 3) {
787 if (ld->pgno >= 0x100 && ld->pgno <= 0x899)
788 ld->type = VBI_LINK_PAGE;
789
790 return i;
791 }
792
793 if (s[i] != '/' && s[i] != ':')
794 return i;
795
796 s += i += 1;
797
798 for (ld->subno = j = 0; isdigit(s[j]); j++)
799 ld->subno = ld->subno * 16 + (s[j] & 15);
800
801 if (j > 1 || subno != ld->pgno || ld->subno > 0x99)
802 return i + j;
803
804 if (ld->pgno == ld->subno)
805 ld->subno = 0x01;
806 else
807 ld->subno = vbi_add_bcd(ld->pgno, 0x01);
808
809 ld->type = VBI_LINK_SUBPAGE;
810 ld->pgno = pgno;
811
812 return i + j;
813 } else if (!strncasecmp((char *) s, "https://", i = 8)) {
814 ld->type = VBI_LINK_HTTP;
815 } else if (!strncasecmp((char *) s, "http://", i = 7)) {
816 ld->type = VBI_LINK_HTTP;
817 } else if (!strncasecmp((char *) s, "www.", i = 4)) {
818 ld->type = VBI_LINK_HTTP;
819 strcpy((char *) ld->url, "http://");
820 } else if (!strncasecmp((char *) s, "ftp://", i = 6)) {
821 ld->type = VBI_LINK_FTP;
822 } else if (*s == '@' || *s == 0xA7) {
823 ld->type = VBI_LINK_EMAIL;
824 strcpy((char *) ld->url, "mailto:");
825 i = 1;
826 } else if (!strncasecmp((char *) s, "(at)", i = 4)) {
827 ld->type = VBI_LINK_EMAIL;
828 strcpy((char *) ld->url, "mailto:");
829 } else if (!strncasecmp((char *) s, "(a)", i = 3)) {
830 ld->type = VBI_LINK_EMAIL;
831 strcpy((char *) ld->url, "mailto:");
832 } else
833 return 1;
834
835 for (j = k = l = 0;;) {
836 // RFC 1738
837 while (isalnum(s[i + j]) || strchr("%&/=?+-~:;@_", s[i + j])) {
838 j++;
839 l++;
840 }
841
842 if (s[i + j] == '.') {
843 if (l < 1)
844 return i;
845 l = 0;
846 j++;
847 k++;
848 } else
849 break;
850 }
851
852 if (k < 1 || l < 1) {
853 ld->type = VBI_LINK_NONE;
854 return i;
855 }
856
857 k = 0;
858
859 if (ld->type == VBI_LINK_EMAIL) {
860 for (; isalnum(s[k - 1]) || strchr("-~._", s[k - 1]); k--);
861
862 if (k == 0) {
863 ld->type = VBI_LINK_NONE;
864 return i;
865 }
866
867 *back = k;
868
869 strncat((char *) ld->url, (char *) s + k, -k);
870 strcat((char *) ld->url, "@");
871 strncat((char *) ld->url, (char *) s + i, j);
872 } else
873 strncat((char *) ld->url, (char *) s + k, i + j - k);
874
875 return i + j;
876}
877
878static inline void
879zap_links(vbi_page *pg, int row)
880{
881 unsigned char buffer[43]; /* One row, two spaces on the sides and NUL */
882 vbi_link ld;
883 vbi_char *acp;
884 vbi_bool link[43];
885 int i, j, n, b;
886
887 acp = &pg->text[row * EXT_COLUMNS];
888
889 for (i = j = 0; i < COLUMNS; i++) {
890 if (acp[i].size == VBI_OVER_TOP || acp[i].size == VBI_OVER_BOTTOM)
891 continue;
892 buffer[j + 1] = (acp[i].unicode >= 0x20 && acp[i].unicode <= 0xFF) ?
893 acp[i].unicode : 0x20;
894 j++;
895 }
896
897 buffer[0] = ' ';
898 buffer[j + 1] = ' ';
899 buffer[j + 2] = 0;
900
901 for (i = 0; i < COLUMNS; i += n) {
902 n = keyword(&ld, buffer, i + 1,
903 pg->pgno, pg->subno, &b);
904
905 for (j = b; j < n; j++)
906 link[i + j] = (ld.type != VBI_LINK_NONE);
907 }
908
909 for (i = j = 0; i < COLUMNS; i++) {
910 acp[i].link = link[j];
911
912 if (acp[i].size == VBI_OVER_TOP || acp[i].size == VBI_OVER_BOTTOM)
913 continue;
914 j++;
915 }
916}
917
918/**
919 * @param pg With vbi_fetch_vt_page() obtained vbi_page.
920 * @param column Column 0 ... pg->columns - 1 of the character in question.
921 * @param row Row 0 ... pg->rows - 1 of the character in question.
922 * @param ld Place to store information about the link.
923 *
924 * A vbi_page (in practice only Teletext pages) may contain hyperlinks
925 * such as HTTP URLs, e-mail addresses or links to other pages. Characters
926 * being part of a hyperlink have a set vbi_char->link flag, this function
927 * returns a more verbose description of the link.
928 */
929void
930vbi_resolve_link(vbi_page *pg, int column, int row, vbi_link *ld)
931{
932 unsigned char buffer[43];
933 vbi_char *acp;
934 int i, j, b;
935
936 assert(column >= 0 && column < EXT_COLUMNS);
937
938 ld->nuid = pg->nuid;
939
940 acp = &pg->text[row * EXT_COLUMNS];
941
942 if (row == (ROWS - 1) && acp[column].link) {
943 i = pg->nav_index[column];
944
945 ld->type = VBI_LINK_PAGE;
946 ld->pgno = pg->nav_link[i].pgno;
947 ld->subno = pg->nav_link[i].subno;
948
949 return;
950 }
951
952 if (row < 1 || row > 23 || column >= COLUMNS || pg->pgno < 0x100) {
953 ld->type = VBI_LINK_NONE;
954 return;
955 }
956
957 for (i = j = b = 0; i < COLUMNS; i++) {
958 if (acp[i].size == VBI_OVER_TOP || acp[i].size == VBI_OVER_BOTTOM)
959 continue;
960 if (i < column && !acp[i].link)
961 j = b = -1;
962
963 buffer[j + 1] = (acp[i].unicode >= 0x20 && acp[i].unicode <= 0xFF) ?
964 acp[i].unicode : 0x20;
965
966 if (b <= 0) {
967 if (buffer[j + 1] == ')' && j > 2) {
968 if (!strncasecmp((char *) buffer + j + 1 - 3, "(at", 3))
969 b = j - 3;
970 else if (!strncasecmp((char *) buffer + j + 1 - 2, "(a", 2))
971 b = j - 2;
972 } else if (buffer[j + 1] == '@' || buffer[j + 1] == 167)
973 b = j;
974 }
975
976 j++;
977 }
978
979 buffer[0] = ' ';
980 buffer[j + 1] = ' ';
981 buffer[j + 2] = 0;
982
983 keyword(ld, buffer, 1, pg->pgno, pg->subno, &i);
984
985 if (ld->type == VBI_LINK_NONE)
986 keyword(ld, buffer, b + 1, pg->pgno, pg->subno, &i);
987}
988
989/**
990 * @param pg With vbi_fetch_vt_page() obtained vbi_page.
991 * @param ld Place to store information about the link.
992 *
993 * All Teletext pages have a built-in home link, by default
994 * page 100, but can also be the magazine intro page or another
995 * page selected by the editor.
996 */
997void
998vbi_resolve_home(vbi_page *pg, vbi_link *ld)
999{
1000 if (pg->pgno < 0x100) {
1001 ld->type = VBI_LINK_NONE;
1002 return;
1003 }
1004
1005 ld->type = VBI_LINK_PAGE;
1006 ld->pgno = pg->nav_link[5].pgno;
1007 ld->subno = pg->nav_link[5].subno;
1008}
1009
1010static inline void
1011ait_title(vbi_decoder *vbi, cache_page *vtp, struct ttx_ait_title *ait, char *buf)
1012{
1013 struct ttx_magazine *mag;
1014 struct vbi_font_descr *font[2];
1015 int i;
1016
1017 mag = cache_network_magazine (vbi->cn, 0x100);
1018 character_set_designation (font, &mag->extension, vtp);
1019
1020 for (i = 11; i >= 0; i--)
1021 if (ait->text[i] > 0x20)
1022 break;
1023 buf[i + 1] = 0;
1024
1025 for (; i >= 0; i--) {
1026 unsigned int unicode = vbi_teletext_unicode(
1027 font[0]->G0, font[0]->subset,
1028 (ait->text[i] < 0x20) ? 0x20 : ait->text[i]);
1029
1030 buf[i] = (unicode >= 0x20 && unicode <= 0xFF) ? unicode : 0x20;
1031 }
1032}
1033
1034/**
1035 * @param vbi Initialized vbi decoding context.
1036 * @param pgno Page number, see vbi_pgno.
1037 * @param subno Subpage number.
1038 * @param buf Place to store the title, Latin-1 format, at least
1039 * 41 characters including the terminating zero.
1040 *
1041 * Given a Teletext page number this function tries to deduce a
1042 * page title for bookmarks or other purposes, mainly from navigation
1043 * data. (XXX TODO: FLOF)
1044 *
1045 * @return
1046 * @c TRUE if a title has been found.
1047 */
1048vbi_bool
1049vbi_page_title(vbi_decoder *vbi, int pgno, int subno, char *buf)
1050{
1051 struct ttx_ait_title *ait;
1052 int i, j;
1053
1054 subno = subno;
1055
1056 if (vbi->cn->have_top) {
1057 for (i = 0; i < 8; i++)
1058 if (PAGE_FUNCTION_AIT == vbi->cn->btt_link[i].function) {
1059 cache_page *vtp;
1060
1061 vtp = _vbi_cache_get_page
1062 (vbi->ca, vbi->cn,
1063 vbi->cn->btt_link[i].pgno,
1064 vbi->cn->btt_link[i].subno,
1065 /* subno_mask */ 0x3f7f);
1066 if (!vtp) {
1067 printv("p/t top ait page %x not cached\n", vbi->cn->btt_link[i].pgno);
1068 continue;
1069 } else if (vtp->function != PAGE_FUNCTION_AIT) {
1070 printv("p/t no ait page %x\n", vtp->pgno);
1071 cache_page_unref (vtp);
1072 vtp = NULL;
1073 continue;
1074 }
1075
1076 for (ait = vtp->data.ait.title, j = 0;
1077 j < 46; ait++, j++) {
1078 if (ait->link.pgno == pgno) {
1079 ait_title(vbi, vtp, ait, buf);
1080 cache_page_unref (vtp);
1081 vtp = NULL;
1082 return TRUE;
1083 }
1084 }
1085
1086 cache_page_unref (vtp);
1087 vtp = NULL;
1088 }
1089 } else {
1090 /* find a FLOF link and the corresponding label */
1091 }
1092
1093 return FALSE;
1094}
1095
1096/*
1097 * Teletext page formatting
1098 */
1099
1100static void
1101character_set_designation(struct vbi_font_descr **font,
1102 struct ttx_extension *ext, cache_page *vtp)
1103{
1104 int i;
1105
1106#ifdef libzvbi_TTX_OVERRIDE_CHAR_SET
1107
1108 font[0] = vbi_font_descriptors + libzvbi_TTX_OVERRIDE_CHAR_SET;
1109 font[1] = vbi_font_descriptors + libzvbi_TTX_OVERRIDE_CHAR_SET;
1110
1111 fprintf(stderr, "override char set with %d\n",
1112 libzvbi_TTX_OVERRIDE_CHAR_SET);
1113#else
1114
1115 font[0] = vbi_font_descriptors + 0;
1116 font[1] = vbi_font_descriptors + 0;
1117
1118 for (i = 0; i < 2; i++) {
1119 int charset_code = ext->charset_code[i];
1120
1121 if (VALID_CHARACTER_SET(charset_code))
1122 font[i] = vbi_font_descriptors + charset_code;
1123
1124 charset_code = (charset_code & ~7) + vtp->national;
1125
1126 if (VALID_CHARACTER_SET(charset_code))
1127 font[i] = vbi_font_descriptors + charset_code;
1128 }
1129#endif
1130}
1131
1132static void
1133screen_color(vbi_page *pg, int flags, int color)
1134{
1135 pg->screen_color = color;
1136
1137 if (color == VBI_TRANSPARENT_BLACK
1138 || (flags & (C5_NEWSFLASH | C6_SUBTITLE)))
1139 pg->screen_opacity = VBI_TRANSPARENT_SPACE;
1140 else
1141 pg->screen_opacity = VBI_OPAQUE;
1142}
1143
1144#define elements(array) (sizeof(array) / sizeof(array[0]))
1145
1146static struct ttx_triplet *
1147resolve_obj_address (vbi_decoder * vbi,
1148 cache_page ** vtpp,
1149 enum ttx_object_type type,
1150 vbi_pgno pgno,
1151 ttx_object_address address,
1152 enum ttx_page_function function,
1153 int * remaining)
1154{
1155 int s1, packet, pointer;
1156 cache_page *vtp;
1157 struct ttx_triplet *trip;
1158 int i;
1159
1160 s1 = address & 15;
1161 packet = ((address >> 7) & 3);
1162 i = ((address >> 5) & 3) * 3 + type;
1163
1164 printv("obj invocation, source page %03x/%04x, "
1165 "pointer packet %d triplet %d\n", pgno, s1, packet + 1, i);
1166
1167 vtp = _vbi_cache_get_page (vbi->ca, vbi->cn, pgno, s1, 0x000F);
1168
1169 if (!vtp) {
1170 printv("... page not cached\n");
1171 return 0;
1172 }
1173
1174 if (vtp->function == PAGE_FUNCTION_UNKNOWN) {
1175 cache_page *new_cp;
1176
1177 new_cp = vbi_convert_page(vbi, vtp, TRUE, function);
1178 if (NULL == new_cp) {
1179 printv("... no g/pop page or hamming error\n");
1180 cache_page_unref (vtp);
1181 vtp = NULL;
1182 return 0;
1183 } else {
1184 vtp = new_cp;
1185 }
1186 } else if (vtp->function == PAGE_FUNCTION_POP)
1187 vtp->function = function;
1188 else if (vtp->function != function) {
1189 printv("... source page wrong function %d, expected %d\n",
1190 vtp->function, function);
1191 cache_page_unref (vtp);
1192 vtp = NULL;
1193 return 0;
1194 }
1195
1196 pointer = vtp->data.pop.pointer[packet * 24 + i * 2 + ((address >> 4) & 1)];
1197
1198 printv("... triplet pointer %d\n", pointer);
1199
1200 if (pointer > 506) {
1201 printv("... triplet pointer out of bounds (%d)\n", pointer);
1202 cache_page_unref (vtp);
1203 vtp = NULL;
1204 return 0;
1205 }
1206
1207 if (TELETEXT_DEBUG) {
1208 packet = (pointer / 13) + 3;
1209
1210 if (packet <= 25)
1211 printv("... object start in packet %d, triplet %d (pointer %d)\n",
1212 packet, pointer % 13, pointer);
1213 else
1214 printv("... object start in packet 26/%d, triplet %d (pointer %d)\n",
1215 packet - 26, pointer % 13, pointer);
1216 }
1217
1218 trip = vtp->data.pop.triplet + pointer;
1219 *remaining = elements(vtp->data.pop.triplet) - (pointer+1);
1220
1221 printv("... obj def: ad 0x%02x mo 0x%04x dat %d=0x%x\n",
1222 trip->address, trip->mode, trip->data, trip->data);
1223
1224 address ^= trip->address << 7;
1225 address ^= trip->data;
1226
1227 if (trip->mode != (type + 0x14) || (address & 0x1FF)) {
1228 printv("... no object definition\n");
1229 cache_page_unref (vtp);
1230 vtp = NULL;
1231 return 0;
1232 }
1233
1234 *vtpp = vtp;
1235
1236 return trip + 1;
1237}
1238
1239/* FIXME: panels */
1240
1241static vbi_bool
1242enhance(vbi_decoder *vbi,
1243 struct ttx_magazine *mag,
1244 struct ttx_extension *ext,
1245 vbi_page *pg, cache_page *vtp,
1246 enum ttx_object_type type,
1247 struct ttx_triplet *p,
1248 int max_triplets,
1249 int inv_row, int inv_column,
1250 vbi_wst_level max_level, vbi_bool header_only,
1251 struct pex26 *ptable)
1252{
1253 vbi_char ac, mac, *acp;
1254 int active_column, active_row;
1255 int offset_column, offset_row;
1256 int row_color, next_row_color;
1257 int row_color_transparent;
1258 struct vbi_font_descr *font;
1259 int invert;
1260 int drcs_s1[2];
1261 struct pex26 *pt, ptmp;
1262 int pdc_hr;
1263
1264 /* XXX nested function not portable, to be removed */
1265#define flush(column) \
1266 ({ \
1267 int row = inv_row + active_row; \
1268 int i; \
1269 \
1270 if (row >= ROWS) \
1271 break; \
1272 \
1273 if (type == OBJECT_TYPE_PASSIVE && !mac.unicode) { \
1274 active_column = column; \
1275 break; \
1276 } \
1277 \
1278 printv("flush [%04x%c,F%d%c,B%d%c,S%d%c,O%d%c,H%d%c] %d ... %d\n", \
1279 ac.unicode, mac.unicode ? '*' : ' ', \
1280 ac.foreground, mac.foreground ? '*' : ' ', \
1281 ac.background, mac.background ? '*' : ' ', \
1282 ac.size, mac.size ? '*' : ' ', \
1283 ac.opacity, mac.opacity ? '*' : ' ', \
1284 ac.flash, mac.flash ? '*' : ' ', \
1285 active_column, column - 1); \
1286 \
1287 for (i = inv_column + active_column; i < inv_column + column;) { \
1288 vbi_char c; \
1289 \
1290 if (i > 39) \
1291 break; \
1292 \
1293 c = acp[i]; \
1294 \
1295 if (mac.underline) { \
1296 int u = ac.underline; \
1297 \
1298 if (!mac.unicode) \
1299 ac.unicode = c.unicode; \
1300 \
1301 if (vbi_is_gfx(ac.unicode)) { \
1302 if (u) \
1303 ac.unicode &= ~0x20; /* separated */ \
1304 else \
1305 ac.unicode |= 0x20; /* contiguous */ \
1306 mac.unicode = ~0; \
1307 u = 0; \
1308 } \
1309 \
1310 c.underline = u; \
1311 } \
1312 if (mac.foreground) \
1313 c.foreground = (ac.foreground != VBI_TRANSPARENT_BLACK) ? \
1314 ac.foreground : (row_color_transparent) ? \
1315 VBI_TRANSPARENT_BLACK : row_color; \
1316 if (mac.background) \
1317 c.background = (ac.background != VBI_TRANSPARENT_BLACK) ? \
1318 ac.background : (row_color_transparent) ? \
1319 VBI_TRANSPARENT_BLACK : row_color; \
1320 if (invert) { \
1321 int t = c.foreground; \
1322 \
1323 c.foreground = c.background; \
1324 c.background = t; \
1325 } \
1326 if (mac.opacity) \
1327 c.opacity = ac.opacity; \
1328 if (mac.flash) \
1329 c.flash = ac.flash; \
1330 if (mac.conceal) \
1331 c.conceal = ac.conceal; \
1332 if (mac.unicode) { \
1333 c.unicode = ac.unicode; \
1334 mac.unicode = 0; \
1335 \
1336 if (mac.size) \
1337 c.size = ac.size; \
1338 else if (c.size > VBI_DOUBLE_SIZE) \
1339 c.size = VBI_NORMAL_SIZE; \
1340 } \
1341 \
1342 acp[i] = c; \
1343 \
1344 if (type == OBJECT_TYPE_PASSIVE) \
1345 break; \
1346 \
1347 i++; \
1348 \
1349 if (type != OBJECT_TYPE_PASSIVE \
1350 && type != OBJECT_TYPE_ADAPTIVE) { \
1351 int raw; \
1352 \
1353 raw = (row == 0 && i < 9) ? \
1354 0x20 : vbi_unpar8 (vtp->data.lop.raw[row][i - 1]); \
1355 \
1356 /* set-after spacing attributes cancelling non-spacing */ \
1357 \
1358 switch (raw) { \
1359 case 0x00 ... 0x07: /* alpha + foreground color */ \
1360 case 0x10 ... 0x17: /* mosaic + foreground color */ \
1361 printv("... fg term %d %02x\n", i, raw); \
1362 mac.foreground = 0; \
1363 mac.conceal = 0; \
1364 break; \
1365 \
1366 case 0x08: /* flash */ \
1367 mac.flash = 0; \
1368 break; \
1369 \
1370 case 0x0A: /* end box */ \
1371 case 0x0B: /* start box */ \
1372 if (i < COLUMNS && vbi_unpar8 (vtp->data.lop.raw[row][i]) == raw) { \
1373 printv("... boxed term %d %02x\n", i, raw); \
1374 mac.opacity = 0; \
1375 } \
1376 \
1377 break; \
1378 \
1379 case 0x0D: /* double height */ \
1380 case 0x0E: /* double width */ \
1381 case 0x0F: /* double size */ \
1382 printv("... size term %d %02x\n", i, raw); \
1383 mac.size = 0; \
1384 break; \
1385 } \
1386 \
1387 if (i > 39) \
1388 break; \
1389 \
1390 raw = (row == 0 && i < 8) ? \
1391 0x20 : vbi_unpar8 (vtp->data.lop.raw[row][i]); \
1392 \
1393 /* set-at spacing attributes cancelling non-spacing */ \
1394 \
1395 switch (raw) { \
1396 case 0x09: /* steady */ \
1397 mac.flash = 0; \
1398 break; \
1399 \
1400 case 0x0C: /* normal size */ \
1401 printv("... size term %d %02x\n", i, raw); \
1402 mac.size = 0; \
1403 break; \
1404 \
1405 case 0x18: /* conceal */ \
1406 mac.conceal = 0; \
1407 break; \
1408 \
1409 /* \
1410 * Non-spacing underlined/separated display attribute \
1411 * cannot be cancelled by a subsequent spacing attribute. \
1412 */ \
1413 \
1414 case 0x1C: /* black background */ \
1415 case 0x1D: /* new background */ \
1416 printv("... bg term %d %02x\n", i, raw); \
1417 mac.background = 0; \
1418 break; \
1419 } \
1420 } \
1421 } \
1422 \
1423 active_column = column; \
1424 })
1425
1426 /* XXX nested function not portable, to be removed */
1427#define flush_row() do {\
1428 if (type == OBJECT_TYPE_PASSIVE || type == OBJECT_TYPE_ADAPTIVE) \
1429 flush(active_column + 1); \
1430 else \
1431 flush(COLUMNS); \
1432\
1433 if (type != OBJECT_TYPE_PASSIVE) \
1434 memset(&mac, 0, sizeof(mac)); \
1435 } while (0)
1436
1437 active_column = 0;
1438 active_row = 0;
1439
1440 acp = &pg->text[(inv_row + 0) * EXT_COLUMNS];
1441
1442 offset_column = 0;
1443 offset_row = 0;
1444
1445 row_color =
1446 next_row_color = ext->def_row_color;
1447 row_color_transparent = FALSE;
1448
1449 drcs_s1[0] = 0; /* global */
1450 drcs_s1[1] = 0; /* normal */
1451
1452 memset(&ac, 0, sizeof(ac));
1453 memset(&mac, 0, sizeof(mac));
1454
1455 invert = 0;
1456
1457 if (type == OBJECT_TYPE_PASSIVE) {
1458 ac.foreground = VBI_WHITE;
1459 ac.background = VBI_BLACK;
1460 ac.opacity = pg->page_opacity[1];
1461
1462 mac.foreground = ~0;
1463 mac.background = ~0;
1464 mac.opacity = ~0;
1465 mac.size = ~0;
1466 mac.underline = ~0;
1467 mac.conceal = ~0;
1468 mac.flash = ~0;
1469 }
1470
1471 font = pg->font[0];
1472
1473 if (ptable) {
1474 ptmp.month = -1;
1475 ptmp.at1 = -1; /* n/a */
1476 ptmp.length = 0;
1477 ptmp.x26_cni = 0;
1478 ptmp.pty = 0;
1479 ptmp.lto = 0;
1480
1481 pt = ptable - 1;
1482 } else
1483 pt = &ptmp;
1484
1485 pdc_hr = 0;
1486
1487 for (; max_triplets>0; p++, max_triplets--) {
1488 if (p->address >= COLUMNS) {
1489 /*
1490 * Row address triplets
1491 */
1492 int s = p->data >> 5;
1493 int row = (p->address - COLUMNS) ? : (ROWS - 1);
1494 int column = 0;
1495
1496 if (pdc_hr)
1497 return FALSE; /* invalid */
1498
1499 switch (p->mode) {
1500 case 0x00: /* full screen color */
1501 if (max_level >= VBI_WST_LEVEL_2p5
1502 && s == 0 && type <= OBJECT_TYPE_ACTIVE)
1503 screen_color(pg, vtp->flags, p->data & 0x1F);
1504
1505 break;
1506
1507 case 0x07: /* address display row 0 */
1508 if (p->address != 0x3F)
1509 break; /* reserved, no position */
1510
1511 row = 0;
1512
1513 /* fall through */
1514
1515 case 0x01: /* full row color */
1516 row_color = next_row_color;
1517
1518 if (s == 0) {
1519 row_color = p->data & 0x1F;
1520 next_row_color = ext->def_row_color;
1521 } else if (s == 3) {
1522 row_color =
1523 next_row_color = p->data & 0x1F;
1524 }
1525
1526 goto set_active;
1527
1528 case 0x02: /* reserved */
1529 case 0x03: /* reserved */
1530 break;
1531
1532 case 0x04: /* set active position */
1533 if (max_level >= VBI_WST_LEVEL_2p5) {
1534 if (p->data >= COLUMNS)
1535 break; /* reserved */
1536
1537 column = p->data;
1538 }
1539
1540 if (row > active_row)
1541 row_color = next_row_color;
1542
1543 set_active:
1544 if (header_only && row > 0) {
1545 for (;max_triplets>1; p++, max_triplets--)
1546 if (p[1].address >= COLUMNS) {
1547 if (p[1].mode == 0x07)
1548 break;
1549 else if ((unsigned int) p[1].mode >= 0x1F)
1550 goto terminate;
1551 }
1552 break;
1553 }
1554
1555 printv("enh set_active row %d col %d\n", row, column);
1556
1557 if (row > active_row)
1558 flush_row();
1559
1560 active_row = row;
1561 active_column = column;
1562
1563 acp = &pg->text[(inv_row + active_row) * EXT_COLUMNS];
1564
1565 break;
1566
1567 case 0x05: /* reserved */
1568 case 0x06: /* reserved */
1569 break;
1570
1571 case 0x08: /* PDC data - Country of Origin and Programme Source */
1572 ptmp.x26_cni = p->address * 256 + p->data;
1573 break;
1574
1575 case 0x09: /* PDC data - Month and Day */
1576 ptmp.month = (p->address & 15) - 1;
1577 ptmp.day = (p->data >> 4) * 10 + (p->data & 15) - 1;
1578 break;
1579
1580 case 0x0A: /* PDC data - Cursor Row and Announced Starting Time Hours */
1581 if (!ptable) {
1582 break;
1583 } else if ((ptmp.month | ptmp.x26_cni) < 0) {
1584 return FALSE;
1585 } else if ((ptable - pt) > 22) {
1586 return FALSE;
1587 }
1588
1589 *++pt = ptmp;
1590
1591 /* fall through */
1592
1593 case 0x0B: /* PDC data - Cursor Row and Announced Finishing Time Hours */
1594 s = (p->data & 15) * 60;
1595
1596 if (p->mode == 0x0A) {
1597 pt->at2 = ((p->data & 0x30) >> 4) * 600 + s;
1598 pt->length = 0;
1599 pt->row = row;
1600 pt->caf = !!(p->data & 0x40);
1601 } else {
1602 pt->length = ((p->data & 0x70) >> 4) * 600 + s;
1603 }
1604
1605 pdc_hr = p->mode;
1606
1607 break;
1608
1609 case 0x0C: /* PDC data - Cursor Row and Local Time Offset */
1610 ptmp.lto = (p->data & 0x40) ? ((~0x7F) | p->data) : p->data;
1611 break;
1612
1613 case 0x0D: /* PDC data - Series Identifier and Series Code */
1614 if (p->address == 0x30) {
1615 break;
1616 }
1617 pt->pty = 0x80 + p->data;
1618 break;
1619
1620 case 0x0E: /* reserved */
1621 case 0x0F: /* reserved */
1622 break;
1623
1624 case 0x10: /* origin modifier */
1625 if (max_level < VBI_WST_LEVEL_2p5)
1626 break;
1627
1628 if (p->data >= 72)
1629 break; /* invalid */
1630
1631 offset_column = p->data;
1632 offset_row = p->address - COLUMNS;
1633
1634 printv("enh origin modifier col %+d row %+d\n",
1635 offset_column, offset_row);
1636
1637 break;
1638
1639 case 0x11 ... 0x13: /* object invocation */
1640 {
1641 int source = (p->address >> 3) & 3;
1642 enum ttx_object_type new_type = p->mode & 3;
1643 cache_page *trip_cp = NULL;
1644 struct ttx_triplet *trip;
1645 int remaining_max_triplets = 0;
1646
1647 if (max_level < VBI_WST_LEVEL_2p5)
1648 break;
1649
1650 printv("enh obj invocation "
1651 "source %d type %d\n",
1652 source, new_type);
1653
1654 if (new_type <= type) { /* 13.2++ */
1655 printv("... priority violation\n");
1656 break;
1657 }
1658
1659 if (source == 0) /* illegal */
1660 break;
1661 else if (source == 1) { /* local */
1662 int designation = (p->data >> 4) + ((p->address & 1) << 4);
1663 int triplet = p->data & 15;
1664
1665 if (type != LOCAL_ENHANCEMENT_DATA || triplet > 12)
1666 break; /* invalid */
1667
1668 printv("... local obj %d/%d\n", designation, triplet);
1669
1670 if (!(vtp->x26_designations & 1)) {
1671 printv("... no packet %d\n", designation);
1672 return FALSE;
1673 }
1674
1675 trip = vtp->data.enh_lop.enh + designation * 13 + triplet;
1676 remaining_max_triplets = elements(vtp->data.enh_lop.enh) - (designation* 13 + triplet);
1677 }
1678 else /* global / public */
1679 {
1680 enum ttx_page_function function;
1681 int pgno, i = 0;
1682
1683 if (source == 3) {
1684 function = PAGE_FUNCTION_GPOP;
1685 pgno = vtp->data.lop.link[24].pgno;
1686
1687 if (NO_PAGE(pgno)) {
1688 if (max_level < VBI_WST_LEVEL_3p5
1689 || NO_PAGE(pgno = mag->pop_link[0][8].pgno))
1690 pgno = mag->pop_link[0][0].pgno;
1691 } else
1692 printv("... X/27/4 GPOP overrides MOT\n");
1693 } else {
1694 function = PAGE_FUNCTION_POP;
1695 pgno = vtp->data.lop.link[25].pgno;
1696
1697 if (NO_PAGE(pgno)) {
1698 if ((i = mag->pop_lut[vtp->pgno & 0xFF]) == 0) {
1699 printv("... MOT pop_lut empty\n");
1700 return FALSE; /* has no link (yet) */
1701 }
1702
1703 if (max_level < VBI_WST_LEVEL_3p5
1704 || NO_PAGE(pgno = mag->pop_link[0][i + 8].pgno))
1705 pgno = mag->pop_link[0][i + 0].pgno;
1706 } else
1707 printv("... X/27/4 POP overrides MOT\n");
1708 }
1709
1710 if (NO_PAGE(pgno)) {
1711 printv("... dead MOT link %d\n", i);
1712 return FALSE; /* has no link (yet) */
1713 }
1714
1715 printv("... %s obj\n", (source == 3) ? "global" : "public");
1716
1717 trip = resolve_obj_address
1718 (vbi, &trip_cp, new_type, pgno,
1719 (p->address << 7) + p->data,
1720 function,
1721 &remaining_max_triplets);
1722
1723 if (!trip)
1724 return FALSE;
1725 }
1726
1727 row = inv_row + active_row;
1728 column = inv_column + active_column;
1729
1730 if (!enhance(vbi, mag, ext, pg, vtp, new_type, trip,
1731 remaining_max_triplets,
1732 row + offset_row, column + offset_column,
1733 max_level, header_only, NULL)) {
1734 cache_page_unref (trip_cp);
1735 trip_cp = NULL;
1736 return FALSE;
1737 }
1738
1739 printv("... object done\n");
1740
1741 cache_page_unref (trip_cp);
1742 trip_cp = NULL;
1743
1744 offset_row = 0;
1745 offset_column = 0;
1746
1747 break;
1748 }
1749
1750 case 0x14: /* reserved */
1751 break;
1752
1753 case 0x15 ... 0x17: /* object definition */
1754 flush_row();
1755 printv("enh obj definition 0x%02x 0x%02x\n", p->mode, p->data);
1756 printv("enh terminated\n");
1757 goto swedish;
1758
1759 case 0x18: /* drcs mode */
1760 printv("enh DRCS mode 0x%02x\n", p->data);
1761 drcs_s1[p->data >> 6] = p->data & 15;
1762 break;
1763
1764 case 0x19 ... 0x1E: /* reserved */
1765 break;
1766
1767 case 0x1F: /* termination marker */
1768 default:
1769 terminate:
1770 flush_row();
1771 printv("enh terminated %02x\n", p->mode);
1772 goto swedish;
1773 }
1774 } else {
1775 /*
1776 * Column address triplets
1777 */
1778 int s = p->data >> 5;
1779 int column = p->address;
1780 int unicode;
1781
1782 switch (p->mode) {
1783 case 0x00: /* foreground color */
1784 if (max_level >= VBI_WST_LEVEL_2p5 && s == 0) {
1785 if (column > active_column)
1786 flush(column);
1787
1788 ac.foreground = p->data & 0x1F;
1789 mac.foreground = ~0;
1790
1791 printv("enh col %d foreground %d\n", active_column, ac.foreground);
1792 }
1793
1794 break;
1795
1796 case 0x01: /* G1 block mosaic character */
1797 if (max_level >= VBI_WST_LEVEL_2p5) {
1798 if (column > active_column)
1799 flush(column);
1800
1801 if (p->data & 0x20) {
1802 unicode = 0xEE00 + p->data; /* G1 contiguous */
1803 goto store;
1804 } else if (p->data >= 0x40) {
1805 unicode = vbi_teletext_unicode(
1806 font->G0, NO_SUBSET, p->data);
1807 goto store;
1808 }
1809 }
1810
1811 break;
1812
1813 case 0x0B: /* G3 smooth mosaic or line drawing character */
1814 if (max_level < VBI_WST_LEVEL_2p5)
1815 break;
1816
1817 /* fall through */
1818
1819 case 0x02: /* G3 smooth mosaic or line drawing character */
1820 if (p->data >= 0x20) {
1821 if (column > active_column)
1822 flush(column);
1823
1824 unicode = 0xEF00 + p->data;
1825 goto store;
1826 }
1827
1828 break;
1829
1830 case 0x03: /* background color */
1831 if (max_level >= VBI_WST_LEVEL_2p5 && s == 0) {
1832 if (column > active_column)
1833 flush(column);
1834
1835 ac.background = p->data & 0x1F;
1836 mac.background = ~0;
1837
1838 printv("enh col %d background %d\n", active_column, ac.background);
1839 }
1840
1841 break;
1842
1843 case 0x04: /* reserved */
1844 case 0x05: /* reserved */
1845 break;
1846
1847 case 0x06: /* PDC data - Cursor Column and Announced Starting */
1848 /* and Finishing Time Minutes */
1849 if (!ptable)
1850 break;
1851
1852 s = (p->data >> 4) * 10 + (p->data & 15);
1853
1854 if (pdc_hr == 0x0A) {
1855 pt->at2 += s;
1856
1857 if (pt > ptable && pt[-1].length == 0) {
1858 pt[-1].length = pt->at2 - pt[-1].at2;
1859
1860 if (pt->at2 < pt[-1].at2)
1861 pt[-1].length += 24 * 60;
1862
1863 if (pt[-1].length >= 12 * 60) {
1864 /* bullshit */
1865 pt[-1] = pt[0];
1866 pt--;
1867 }
1868 }
1869 } else if (pdc_hr == 0x0B) {
1870 pt->length += s;
1871
1872 if (pt->length >= 4 * 600) {
1873 pt->length -= 4 * 600;
1874 } else {
1875 if (pt->length < pt->at2)
1876 pt->length += 24 * 60;
1877
1878 pt->length -= pt->at2;
1879 }
1880 } else {
1881 return FALSE;
1882 }
1883
1884 pt->column = column;
1885 pdc_hr = 0;
1886
1887 break;
1888
1889 case 0x07: /* additional flash functions */
1890 if (max_level >= VBI_WST_LEVEL_2p5) {
1891 if (column > active_column)
1892 flush(column);
1893
1894 /*
1895 * Only one flash function (if any) implemented:
1896 * Mode 1 - Normal flash to background color
1897 * Rate 0 - Slow rate (1 Hz)
1898 */
1899 ac.flash = !!(p->data & 3);
1900 mac.flash = ~0;
1901
1902 printv("enh col %d flash 0x%02x\n", active_column, p->data);
1903 }
1904
1905 break;
1906
1907 case 0x08: /* modified G0 and G2 character set designation */
1908 if (max_level >= VBI_WST_LEVEL_2p5) {
1909 if (column > active_column)
1910 flush(column);
1911
1912 if (VALID_CHARACTER_SET(p->data))
1913 font = vbi_font_descriptors + p->data;
1914 else
1915 font = pg->font[0];
1916
1917 printv("enh col %d modify character set %d\n", active_column, p->data);
1918 }
1919
1920 break;
1921
1922 case 0x09: /* G0 character */
1923 if (max_level >= VBI_WST_LEVEL_2p5 && p->data >= 0x20) {
1924 if (column > active_column)
1925 flush(column);
1926
1927 unicode = vbi_teletext_unicode(font->G0, NO_SUBSET, p->data);
1928 goto store;
1929 }
1930
1931 break;
1932
1933 case 0x0A: /* reserved */
1934 break;
1935
1936 case 0x0C: /* display attributes */
1937 if (max_level < VBI_WST_LEVEL_2p5)
1938 break;
1939
1940 if (column > active_column)
1941 flush(column);
1942
1943 ac.size = ((p->data & 0x40) ? VBI_DOUBLE_WIDTH : 0)
1944 + ((p->data & 1) ? VBI_DOUBLE_HEIGHT : 0);
1945 mac.size = ~0;
1946
1947 if (vtp->flags & (C5_NEWSFLASH | C6_SUBTITLE)) {
1948 if (p->data & 2) {
1949 ac.opacity = VBI_SEMI_TRANSPARENT;
1950 } else {
1951 ac.opacity = pg->page_opacity[1];
1952 }
1953 mac.opacity = ~0;
1954 } else {
1955 row_color_transparent = p->data & 2;
1956 }
1957
1958 ac.conceal = !!(p->data & 4);
1959 mac.conceal = ~0;
1960
1961 /* (p->data & 8) reserved */
1962
1963 invert = p->data & 0x10;
1964
1965 ac.underline = !!(p->data & 0x20);
1966 mac.underline = ~0;
1967
1968 printv("enh col %d display attr 0x%02x\n", active_column, p->data);
1969
1970 break;
1971
1972 case 0x0D: /* drcs character invocation */
1973 {
1974 int normal = p->data >> 6;
1975 int offset = p->data & 0x3F;
1976 enum ttx_page_function function;
1977 int pgno, page, i = 0;
1978
1979 if (max_level < VBI_WST_LEVEL_2p5)
1980 break;
1981
1982 if (offset >= 48)
1983 break; /* invalid */
1984
1985 if (column > active_column)
1986 flush(column);
1987
1988 page = normal * 16 + drcs_s1[normal];
1989
1990 printv("enh col %d DRCS %d/0x%02x\n", active_column, page, p->data);
1991
1992 /* if (!pg->drcs[page]) */ {
1993 cache_page *dvtp;
1994
1995 if (!normal) {
1996 function = PAGE_FUNCTION_GDRCS;
1997 pgno = vtp->data.lop.link[26].pgno;
1998
1999 if (NO_PAGE(pgno)) {
2000 if (max_level < VBI_WST_LEVEL_3p5
2001 || NO_PAGE(pgno = mag->drcs_link[0][8]))
2002 pgno = mag->drcs_link[0][0];
2003 } else
2004 printv("... X/27/4 GDRCS overrides MOT\n");
2005 } else {
2006 function = PAGE_FUNCTION_DRCS;
2007 pgno = vtp->data.lop.link[25].pgno;
2008
2009 if (NO_PAGE(pgno)) {
2010 if ((i = mag->drcs_lut[vtp->pgno & 0xFF]) == 0) {
2011 printv("... MOT drcs_lut empty\n");
2012 return FALSE; /* has no link (yet) */
2013 }
2014
2015 if (max_level < VBI_WST_LEVEL_3p5
2016 || NO_PAGE(pgno = mag->drcs_link[0][i + 8]))
2017 pgno = mag->drcs_link[0][i + 0];
2018 } else
2019 printv("... X/27/4 DRCS overrides MOT\n");
2020 }
2021
2022 if (NO_PAGE(pgno)) {
2023 printv("... dead MOT link %d\n", i);
2024 return FALSE; /* has no link (yet) */
2025 }
2026
2027 printv("... %s drcs from page %03x/%04x\n",
2028 normal ? "normal" : "global", pgno, drcs_s1[normal]);
2029
2030 dvtp = _vbi_cache_get_page
2031 (vbi->ca, vbi->cn,
2032 pgno, drcs_s1[normal],
2033 /* subno_mask */ 0x000F);
2034
2035 if (!dvtp) {
2036 printv("... page not cached\n");
2037 return FALSE;
2038 }
2039
2040 if (dvtp->function == PAGE_FUNCTION_UNKNOWN) {
2041 cache_page *new_cp;
2042
2043 new_cp = vbi_convert_page
2044 (vbi, dvtp, TRUE,
2045 function);
2046 if (NULL == new_cp) {
2047 printv("... no g/drcs page or hamming error\n");
2048 cache_page_unref (dvtp);
2049 dvtp = NULL;
2050 return FALSE;
2051 }
2052 dvtp = new_cp;
2053 } else if (dvtp->function == PAGE_FUNCTION_DRCS) {
2054 dvtp->function = function;
2055 } else if (dvtp->function != function) {
2056 printv("... source page wrong function %d, expected %d\n",
2057 dvtp->function, function);
2058 cache_page_unref (dvtp);
2059 dvtp = NULL;
2060 return FALSE;
2061 }
2062
2063 if (dvtp->data.drcs.invalid & (1ULL << offset)) {
2064 printv("... invalid drcs, prob. tx error\n");
2065 cache_page_unref (dvtp);
2066 dvtp = NULL;
2067 return FALSE;
2068 }
2069
2070 pg->drcs[page] = dvtp->data.drcs.chars[0];
2071 cache_page_unref (dvtp);
2072 dvtp = NULL;
2073 }
2074
2075 unicode = 0xF000 + (page << 6) + offset;
2076 goto store;
2077 }
2078
2079 case 0x0E: /* font style */
2080 {
2081 int italic, bold, proportional;
2082 int col, row, count;
2083 vbi_char *acp;
2084
2085 if (max_level < VBI_WST_LEVEL_3p5)
2086 break;
2087
2088 row = inv_row + active_row;
2089 count = (p->data >> 4) + 1;
2090 acp = &pg->text[row * EXT_COLUMNS];
2091
2092 proportional = (p->data >> 0) & 1;
2093 bold = (p->data >> 1) & 1;
2094 italic = (p->data >> 2) & 1;
2095
2096 while (row < ROWS && count > 0) {
2097 for (col = inv_column + column; col < COLUMNS; col++) {
2098 acp[col].italic = italic;
2099 acp[col].bold = bold;
2100 acp[col].proportional = proportional;
2101 }
2102
2103 acp += EXT_COLUMNS;
2104 row++;
2105 count--;
2106 }
2107
2108 printv("enh col %d font style 0x%02x\n", active_column, p->data);
2109
2110 break;
2111 }
2112
2113 case 0x0F: /* G2 character */
2114 if (p->data >= 0x20) {
2115 if (column > active_column)
2116 flush(column);
2117
2118 unicode = vbi_teletext_unicode(font->G2, NO_SUBSET, p->data);
2119 goto store;
2120 }
2121
2122 break;
2123
2124 case 0x10 ... 0x1F: /* characters including diacritical marks */
2125 if (p->data >= 0x20) {
2126 if (column > active_column)
2127 flush(column);
2128
2129 unicode = vbi_teletext_composed_unicode(
2130 p->mode - 0x10, p->data);
2131 store:
2132 printv("enh row %d col %d print 0x%02x/0x%02x -> 0x%04x\n",
2133 active_row, active_column, p->mode, p->data,
2134 unicode);
2135
2136 ac.unicode = unicode;
2137 mac.unicode = ~0;
2138 }
2139
2140 break;
2141 }
2142 }
2143 }
2144
2145swedish:
2146
2147 if (ptable) {
2148 if (pt >= ptable && (pdc_hr || pt->length == 0))
2149 pt--; /* incomplete start or end tag */
2150
2151 if (1)
2152 dump_pex26(ptable, pt - ptable + 1);
2153 }
2154
2155 if (0) {
2156 acp = pg->text;
2157
2158 for (active_row = 0; active_row < ROWS; active_row++) {
2159 printv("%2d: ", active_row);
2160
2161 for (active_column = 0; active_column < COLUMNS; acp++, active_column++) {
2162 printv("%04x ", acp->unicode);
2163 }
2164
2165 printv("\n");
2166
2167 acp += EXT_COLUMNS - COLUMNS;
2168 }
2169 }
2170
2171 return TRUE;
2172}
2173
2174static void
2175post_enhance(vbi_page *pg, int display_rows)
2176{
2177 int last_row = MIN(display_rows, ROWS) - 2;
2178 vbi_char ac, *acp;
2179 int column, row;
2180
2181 acp = pg->text;
2182
2183 for (row = 0; row <= last_row; row++) {
2184 for (column = 0; column < COLUMNS; acp++, column++) {
2185 if (1)
2186 printv("%c", _vbi_to_ascii (acp->unicode));
2187 else
2188 printv("%04xF%dB%dS%dO%d ", acp->unicode,
2189 acp->foreground, acp->background,
2190 acp->size, acp->opacity);
2191
2192 if (acp->opacity == VBI_TRANSPARENT_SPACE
2193 || (acp->foreground == VBI_TRANSPARENT_BLACK
2194 && acp->background == VBI_TRANSPARENT_BLACK)) {
2195 acp->opacity = VBI_TRANSPARENT_SPACE;
2196 acp->unicode = 0x0020;
2197 } else if (acp->background == VBI_TRANSPARENT_BLACK) {
2198 acp->opacity = VBI_SEMI_TRANSPARENT;
2199 }
2200 /* transparent foreground not implemented */
2201
2202 switch (acp->size) {
2203 case VBI_NORMAL_SIZE:
2204 if (row < last_row
2205 && (acp[EXT_COLUMNS].size == VBI_DOUBLE_HEIGHT2
2206 || acp[EXT_COLUMNS].size == VBI_DOUBLE_SIZE2)) {
2207 acp[EXT_COLUMNS].unicode = 0x0020;
2208 acp[EXT_COLUMNS].size = VBI_NORMAL_SIZE;
2209 }
2210
2211 if (column < 39
2212 && (acp[1].size == VBI_OVER_TOP
2213 || acp[1].size == VBI_OVER_BOTTOM)) {
2214 acp[1].unicode = 0x0020;
2215 acp[1].size = VBI_NORMAL_SIZE;
2216 }
2217
2218 break;
2219
2220 case VBI_DOUBLE_HEIGHT:
2221 if (row < last_row) {
2222 ac = acp[0];
2223 ac.size = VBI_DOUBLE_HEIGHT2;
2224 acp[EXT_COLUMNS] = ac;
2225 }
2226 break;
2227
2228 case VBI_DOUBLE_SIZE:
2229 if (row < last_row) {
2230 ac = acp[0];
2231 ac.size = VBI_DOUBLE_SIZE2;
2232 acp[EXT_COLUMNS] = ac;
2233 ac.size = VBI_OVER_BOTTOM;
2234 acp[EXT_COLUMNS + 1] = ac;
2235 }
2236
2237 /* fall through */
2238
2239 case VBI_DOUBLE_WIDTH:
2240 if (column < 39) {
2241 ac = acp[0];
2242 ac.size = VBI_OVER_TOP;
2243 acp[1] = ac;
2244 }
2245 break;
2246
2247 default:
2248 break;
2249 }
2250 }
2251
2252 printv("\n");
2253
2254 acp += EXT_COLUMNS - COLUMNS;
2255 }
2256}
2257
2258static inline vbi_bool
2259default_object_invocation (vbi_decoder * vbi,
2260 struct ttx_magazine * mag,
2261 struct ttx_extension * ext,
2262 vbi_page * pg,
2263 cache_page * vtp,
2264 vbi_wst_level max_level,
2265 vbi_bool header_only)
2266{
2267 struct ttx_pop_link *pop;
2268 int i, order;
2269
2270 if (!(i = mag->pop_lut[vtp->pgno & 0xFF]))
2271 return FALSE; /* has no link (yet) */
2272
2273 pop = &mag->pop_link[0][i + 8];
2274
2275 if (max_level < VBI_WST_LEVEL_3p5 || NO_PAGE(pop->pgno)) {
2276 pop = &mag->pop_link[0][i];
2277
2278 if (NO_PAGE(pop->pgno)) {
2279 printv("default object has dead MOT pop link %d\n", i);
2280 return FALSE;
2281 }
2282 }
2283
2284 order = pop->default_obj[0].type > pop->default_obj[1].type;
2285
2286 for (i = 0; i < 2; i++) {
2287 enum ttx_object_type type = pop->default_obj[i ^ order].type;
2288 cache_page *trip_cp = NULL;
2289 struct ttx_triplet *trip;
2290 int remaining_max_triplets;
2291
2292 if (type == OBJECT_TYPE_NONE)
2293 continue;
2294
2295 printv("default object #%d invocation, type %d\n", i ^ order, type);
2296
2297 trip = resolve_obj_address(vbi, &trip_cp, type, pop->pgno,
2298 pop->default_obj[i ^ order].address, PAGE_FUNCTION_POP,
2299 &remaining_max_triplets);
2300
2301 if (!trip)
2302 return FALSE;
2303
2304 if (!enhance(vbi, mag, ext, pg, vtp, type, trip,
2305 remaining_max_triplets, 0, 0, max_level,
2306 header_only, NULL)) {
2307 cache_page_unref (trip_cp);
2308 return FALSE;
2309 }
2310
2311 cache_page_unref (trip_cp);
2312 }
2313
2314 return TRUE;
2315}
2316
2317/**
2318 * @internal
2319 *
2320 * Artificial 41st column. Often column 0 of a LOP contains only set-after
2321 * attributes and thus all black spaces, unlike column 39. To balance the
2322 * view we add a black column 40. If OTOH column 0 has been modified using
2323 * enhancement we extend column 39.
2324 */
2325static void
2326column_41 (vbi_page * pg,
2327 struct ttx_extension * ext)
2328{
2329 vbi_char *acp;
2330 unsigned int row;
2331 vbi_bool black0;
2332 vbi_bool cont39;
2333
2334 if (41 != pg->columns)
2335 return;
2336
2337 acp = pg->text;
2338
2339 /* Header. */
2340
2341 acp[40] = acp[39];
2342 acp[40].unicode = 0x0020;
2343
2344 if (1 == pg->rows)
2345 return;
2346
2347 /* Body. */
2348
2349 acp += 41;
2350
2351 black0 = TRUE;
2352 cont39 = TRUE;
2353
2354 for (row = 1; row <= 24; ++row) {
2355 if (0x0020 != acp[0].unicode
2356 || (VBI_BLACK != acp[0].background
2357 && 32 != acp[0].background)) {
2358 black0 = FALSE;
2359 }
2360
2361 if (vbi_is_gfx (acp[39].unicode)) {
2362 if (acp[38].unicode != acp[39].unicode
2363 || acp[38].foreground != acp[39].foreground
2364 || acp[38].background != acp[39].background) {
2365 cont39 = FALSE;
2366 }
2367 }
2368
2369 acp += 41;
2370 }
2371
2372 acp = pg->text + 41;
2373
2374 if (!black0 && cont39) {
2375 for (row = 1; row <= 24; ++row) {
2376 acp[40] = acp[39];
2377
2378 if (!vbi_is_gfx (acp[39].unicode))
2379 acp[40].unicode = 0x0020;
2380
2381 acp += 41;
2382 }
2383 } else {
2384 vbi_char ac;
2385
2386 CLEAR (ac);
2387
2388 ac.unicode = 0x0020;
2389 ac.foreground = ext->foreground_clut + VBI_WHITE;
2390 ac.background = ext->background_clut + VBI_BLACK;
2391 ac.opacity = pg->page_opacity[1];
2392
2393 for (row = 1; row <= 24; ++row) {
2394 acp[40] = ac;
2395 acp += 41;
2396 }
2397 }
2398
2399 /* Navigation bar. */
2400
2401 acp[40] = acp[39];
2402 acp[40].unicode = 0x0020;
2403}
2404
2405/**
2406 * @internal
2407 * @param vbi Initialized vbi_decoder context.
2408 * @param pg Place to store the formatted page.
2409 * @param vtp Raw Teletext page.
2410 * @param max_level Format the page at this Teletext implementation level.
2411 * @param display_rows Number of rows to format, between 1 ... 25.
2412 * @param navigation Analyse the page and add navigation links,
2413 * including TOP and FLOF.
2414 *
2415 * Format a page @a pg from a raw Teletext page @a vtp. This function is
2416 * used internally by libzvbi only.
2417 *
2418 * @return
2419 * @c TRUE if the page could be formatted.
2420 */
2421int
2422vbi_format_vt_page(vbi_decoder *vbi,
2423 vbi_page *pg, cache_page *vtp,
2424 vbi_wst_level max_level,
2425 int display_rows, vbi_bool navigation)
2426{
2427 char buf[16];
2428 struct ttx_magazine *mag;
2429 struct ttx_extension *ext;
2430 int column, row, i;
2431
2432 if (vtp->function != PAGE_FUNCTION_LOP &&
2433 vtp->function != PAGE_FUNCTION_EACEM_TRIGGER)
2434 return FALSE;
2435
2436 printv("\nFormatting page %03x/%04x pg=%p lev=%d rows=%d nav=%d\n",
2437 vtp->pgno, vtp->subno, pg, max_level, display_rows, navigation);
2438
2439 memcpy(vtp->data.lop.raw[0]+32, vbi->vt.header+32, 8);
2440
2441 display_rows = SATURATE(display_rows, 1, ROWS);
2442
2443 pg->vbi = vbi;
2444
2445 pg->nuid = vbi->network.ev.network.nuid;
2446
2447 pg->pgno = vtp->pgno;
2448 pg->subno = vtp->subno;
2449
2450 pg->rows = display_rows;
2451 pg->columns = EXT_COLUMNS;
2452
2453 pg->dirty.y0 = 0;
2454 pg->dirty.y1 = ROWS - 1;
2455 pg->dirty.roll = 0;
2456
2457 mag = (max_level <= VBI_WST_LEVEL_1p5) ?
2458 &vbi->vt.default_magazine
2459 : cache_network_magazine (vbi->cn, vtp->pgno);
2460
2461 if (vtp->x28_designations & 0x11)
2462 ext = &vtp->data.ext_lop.ext;
2463 else
2464 ext = &mag->extension;
2465
2466 /* Character set designation */
2467
2468 character_set_designation(pg->font, ext, vtp);
2469
2470 /* Colors */
2471
2472 screen_color(pg, vtp->flags, ext->def_screen_color);
2473
2474 vbi_transp_colormap(vbi, pg->color_map, ext->color_map, 40);
2475
2476 pg->drcs_clut = ext->drcs_clut;
2477
2478 /* Opacity */
2479
2480 pg->page_opacity[1] =
2481 (vtp->flags & (C5_NEWSFLASH | C6_SUBTITLE | C10_INHIBIT_DISPLAY)) ?
2482 VBI_TRANSPARENT_SPACE : VBI_OPAQUE;
2483 pg->boxed_opacity[1] =
2484 (vtp->flags & C10_INHIBIT_DISPLAY) ?
2485 VBI_TRANSPARENT_SPACE : VBI_SEMI_TRANSPARENT;
2486
2487 if (vtp->flags & C7_SUPPRESS_HEADER) {
2488 pg->page_opacity[0] = VBI_TRANSPARENT_SPACE;
2489 pg->boxed_opacity[0] = VBI_TRANSPARENT_SPACE;
2490 } else {
2491 pg->page_opacity[0] = pg->page_opacity[1];
2492 pg->boxed_opacity[0] = pg->boxed_opacity[1];
2493 }
2494
2495 /* DRCS */
2496
2497 memset(pg->drcs, 0, sizeof(pg->drcs));
2498
2499 /* Current page number in header */
2500
2501 snprintf (buf, sizeof (buf),
2502 "\2%x.%02x\7", vtp->pgno, vtp->subno & 0xff);
2503
2504 /* Level 1 formatting */
2505
2506 i = 0;
2507 pg->double_height_lower = 0;
2508
2509 for (row = 0; row < display_rows; row++) {
2510 struct vbi_font_descr *font;
2511 int mosaic_unicodes; /* 0xEE00 separate, 0xEE20 contiguous */
2512 int held_mosaic_unicode;
2513 int esc;
2514 vbi_bool hold, mosaic;
2515 vbi_bool double_height, wide_char;
2516 vbi_char ac, *acp = &pg->text[row * EXT_COLUMNS];
2517
2518 held_mosaic_unicode = 0xEE20; /* G1 block mosaic, blank, contiguous */
2519
2520 memset(&ac, 0, sizeof(ac));
2521
2522 ac.unicode = 0x0020;
2523 ac.foreground = ext->foreground_clut + VBI_WHITE;
2524 ac.background = ext->background_clut + VBI_BLACK;
2525 mosaic_unicodes = 0xEE20; /* contiguous */
2526 ac.opacity = pg->page_opacity[row > 0];
2527 font = pg->font[0];
2528 esc = 0;
2529 hold = FALSE;
2530 mosaic = FALSE;
2531
2532 double_height = FALSE;
2533 wide_char = FALSE;
2534
2535 acp[COLUMNS] = ac; /* artificial column 41 */
2536
2537 for (column = 0; column < COLUMNS; ++column) {
2538 int raw;
2539
2540 if (row == 0 && column < 8) {
2541 raw = buf[column];
2542 i++;
2543 } else if ((raw = vbi_unpar8 (vtp->data.lop.raw[0][i++])) < 0)
2544 raw = ' ';
2545
2546 /* set-at spacing attributes */
2547
2548 switch (raw) {
2549 case 0x09: /* steady */
2550 ac.flash = FALSE;
2551 break;
2552
2553 case 0x0C: /* normal size */
2554 ac.size = VBI_NORMAL_SIZE;
2555 break;
2556
2557 case 0x18: /* conceal */
2558 ac.conceal = TRUE;
2559 break;
2560
2561 case 0x19: /* contiguous mosaics */
2562 mosaic_unicodes = 0xEE20;
2563 break;
2564
2565 case 0x1A: /* separated mosaics */
2566 mosaic_unicodes = 0xEE00;
2567 break;
2568
2569 case 0x1C: /* black background */
2570 ac.background = ext->background_clut + VBI_BLACK;
2571 break;
2572
2573 case 0x1D: /* new background */
2574 ac.background = ext->background_clut + (ac.foreground & 7);
2575 break;
2576
2577 case 0x1E: /* hold mosaic */
2578 hold = TRUE;
2579 break;
2580 }
2581
2582 if (raw <= 0x1F) {
2583 ac.unicode = (hold & mosaic) ? held_mosaic_unicode : 0x0020;
2584 } else {
2585 if (mosaic && (raw & 0x20)) {
2586 held_mosaic_unicode = mosaic_unicodes + raw - 0x20;
2587 ac.unicode = held_mosaic_unicode;
2588 } else
2589 ac.unicode = vbi_teletext_unicode(font->G0,
2590 font->subset, raw);
2591 }
2592
2593 if (wide_char) {
2594 wide_char = FALSE;
2595 } else {
2596 acp[column] = ac;
2597
2598 wide_char = /*!!*/(ac.size & VBI_DOUBLE_WIDTH);
2599 if (wide_char) {
2600 if (column < (COLUMNS - 1)) {
2601 acp[column + 1] = ac;
2602 acp[column + 1].size = VBI_OVER_TOP;
2603 } else {
2604 acp[column].size = VBI_NORMAL_SIZE;
2605 wide_char = FALSE;
2606 }
2607 }
2608 }
2609
2610 /* set-after spacing attributes */
2611
2612 switch (raw) {
2613 case 0x00 ... 0x07: /* alpha + foreground color */
2614 ac.foreground = ext->foreground_clut + (raw & 7);
2615 ac.conceal = FALSE;
2616 mosaic = FALSE;
2617 break;
2618
2619 case 0x08: /* flash */
2620 ac.flash = TRUE;
2621 break;
2622
2623 case 0x0A: /* end box */
2624 if (column < (COLUMNS - 1)
2625 && vbi_unpar8 (vtp->data.lop.raw[0][i]) == 0x0a)
2626 ac.opacity = pg->page_opacity[row > 0];
2627 break;
2628
2629 case 0x0B: /* start box */
2630 if (column < (COLUMNS - 1)
2631 && vbi_unpar8 (vtp->data.lop.raw[0][i]) == 0x0b)
2632 ac.opacity = pg->boxed_opacity[row > 0];
2633 break;
2634
2635 case 0x0D: /* double height */
2636 if (row <= 0 || row >= 23)
2637 break;
2638 ac.size = VBI_DOUBLE_HEIGHT;
2639 double_height = TRUE;
2640 break;
2641
2642 case 0x0E: /* double width */
2643 printv("spacing col %d row %d double width\n", column, row);
2644 if (column < (COLUMNS - 1))
2645 ac.size = VBI_DOUBLE_WIDTH;
2646 break;
2647
2648 case 0x0F: /* double size */
2649 printv("spacing col %d row %d double size\n", column, row);
2650 if (column >= (COLUMNS - 1) || row <= 0 || row >= 23)
2651 break;
2652 ac.size = VBI_DOUBLE_SIZE;
2653 double_height = TRUE;
2654
2655 break;
2656
2657 case 0x10 ... 0x17: /* mosaic + foreground color */
2658 ac.foreground = ext->foreground_clut + (raw & 7);
2659 ac.conceal = FALSE;
2660 mosaic = TRUE;
2661 break;
2662
2663 case 0x1F: /* release mosaic */
2664 hold = FALSE;
2665 break;
2666
2667 case 0x1B: /* ESC */
2668 font = pg->font[esc ^= 1];
2669 break;
2670 }
2671 }
2672
2673 if (double_height) {
2674 for (column = 0; column < EXT_COLUMNS; column++) {
2675 ac = acp[column];
2676
2677 switch (ac.size) {
2678 case VBI_DOUBLE_HEIGHT:
2679 ac.size = VBI_DOUBLE_HEIGHT2;
2680 acp[EXT_COLUMNS + column] = ac;
2681 break;
2682
2683 case VBI_DOUBLE_SIZE:
2684 ac.size = VBI_DOUBLE_SIZE2;
2685 acp[EXT_COLUMNS + column] = ac;
2686 ac.size = VBI_OVER_BOTTOM;
2687 acp[EXT_COLUMNS + (++column)] = ac;
2688 break;
2689
2690 default: /* NORMAL, DOUBLE_WIDTH, OVER_TOP */
2691 ac.size = VBI_NORMAL_SIZE;
2692 ac.unicode = 0x0020;
2693 acp[EXT_COLUMNS + column] = ac;
2694 break;
2695 }
2696 }
2697
2698 i += COLUMNS;
2699 row++;
2700
2701 pg->double_height_lower |= 1 << row;
2702 }
2703 }
2704
2705 if (0) {
2706 if (row < ROWS) {
2707 vbi_char ac;
2708
2709 memset(&ac, 0, sizeof(ac));
2710
2711 ac.foreground = ext->foreground_clut + VBI_WHITE;
2712 ac.background = ext->background_clut + VBI_BLACK;
2713 ac.opacity = pg->page_opacity[1];
2714 ac.unicode = 0x0020;
2715
2716 for (i = row * EXT_COLUMNS; i < ROWS * EXT_COLUMNS; i++)
2717 pg->text[i] = ac;
2718 }
2719 }
2720
2721 /* Local enhancement data and objects */
2722
2723 if (max_level >= VBI_WST_LEVEL_1p5 && display_rows > 0) {
2724 vbi_page page;
2725 vbi_bool success;
2726
2727 memcpy(&page, pg, sizeof(page));
2728
2729 if (!(vtp->flags & (C5_NEWSFLASH | C6_SUBTITLE))) {
2730 pg->boxed_opacity[0] = VBI_TRANSPARENT_SPACE;
2731 pg->boxed_opacity[1] = VBI_TRANSPARENT_SPACE;
2732 }
2733
2734 if (vtp->x26_designations & 1) {
2735 printv("enhancement packets %08x\n",
2736 vtp->x26_designations);
2737 success = enhance(vbi, mag, ext, pg, vtp, LOCAL_ENHANCEMENT_DATA,
2738 vtp->data.enh_lop.enh, elements(vtp->data.enh_lop.enh),
2739 0, 0, max_level, display_rows == 1, NULL);
2740 } else
2741 success = default_object_invocation(vbi, mag, ext, pg, vtp,
2742 max_level, display_rows == 1);
2743
2744 if (success) {
2745 if (max_level >= VBI_WST_LEVEL_2p5)
2746 post_enhance(pg, display_rows);
2747 } else
2748 memcpy(pg, &page, sizeof(*pg));
2749 }
2750
2751 /* Navigation */
2752
2753 if (navigation) {
2754 pg->nav_link[5].pgno = vbi->cn->initial_page.pgno;
2755 pg->nav_link[5].subno = vbi->cn->initial_page.subno;
2756
2757 for (row = 1; row < MIN(ROWS - 1, display_rows); row++)
2758 zap_links(pg, row);
2759
2760 if (display_rows >= ROWS) {
2761 if (vtp->data.lop.have_flof) {
2762 if (vtp->data.lop.link[5].pgno >= 0x100
2763 && vtp->data.lop.link[5].pgno <= 0x899
2764 && (vtp->data.lop.link[5].pgno & 0xFF) != 0xFF) {
2765 pg->nav_link[5].pgno = vtp->data.lop.link[5].pgno;
2766 pg->nav_link[5].subno = vtp->data.lop.link[5].subno;
2767 }
2768
2769 if (vtp->lop_packets & (1 << 24))
2770 flof_links(pg, vtp);
2771 else
2772 flof_navigation_bar(pg, vtp);
2773 } else if (vbi->cn->have_top)
2774 top_navigation_bar(vbi, pg, vtp);
2775
2776// pdc_method_a(pg, vtp, NULL);
2777 }
2778 }
2779
2780 column_41 (pg, ext);
2781
2782 if (0) {
2783 vbi_char *acp;
2784 unsigned int i;
2785
2786 for (row = 0, acp = pg->text + EXT_COLUMNS * row;
2787 row < ROWS; row++) {
2788 fprintf(stderr, "%2d: ", row);
2789
2790 for (column = 0; column < COLUMNS; acp++, column++) {
2791 fprintf(stderr, "%04x ", acp->unicode);
2792 }
2793
2794 fprintf(stderr, "\n");
2795
2796 acp += EXT_COLUMNS - COLUMNS;
2797 }
2798
2799 for (i = 0; i < N_ELEMENTS (pg->color_map); ++i) {
2800 fprintf (stderr, "%08x ",
2801 pg->color_map[i]);
2802 if (3 == (i & 3))
2803 fputc ('\n', stderr);
2804 }
2805 }
2806
2807 return TRUE;
2808}
2809
2810/**
2811 * @param vbi Initialized vbi_decoder context.
2812 * @param pg Place to store the formatted page.
2813 * @param pgno Page number of the page to fetch, see vbi_pgno.
2814 * @param subno Subpage number to fetch (optional @c VBI_ANY_SUBNO).
2815 * @param max_level Format the page at this Teletext implementation level.
2816 * @param display_rows Number of rows to format, between 1 ... 25.
2817 * @param navigation Analyse the page and add navigation links,
2818 * including TOP and FLOF.
2819 *
2820 * Fetches a Teletext page designated by @a pgno and @a subno from the
2821 * cache, formats and stores it in @a pg. Formatting is limited to row
2822 * 0 ... @a display_rows - 1 inclusive. The really useful values
2823 * are 1 (format header only) or 25 (everything). Likewise
2824 * @a navigation can be used to save unnecessary formatting time.
2825 *
2826 * Although safe to do, this function is not supposed to be called from
2827 * an event handler since rendering may block decoding for extended
2828 * periods of time.
2829 *
2830 * @return
2831 * @c FALSE if the page is not cached or could not be formatted
2832 * for other reasons, for instance is a data page not intended for
2833 * display. Level 2.5/3.5 pages which could not be formatted e. g.
2834 * due to referencing data pages not in cache are formatted at a
2835 * lower level.
2836 */
2837vbi_bool
2838vbi_fetch_vt_page(vbi_decoder *vbi, vbi_page *pg,
2839 vbi_pgno pgno, vbi_subno subno,
2840 vbi_wst_level max_level,
2841 int display_rows, vbi_bool navigation)
2842{
2843 cache_page *vtp;
2844 vbi_bool success;
2845 int row;
2846
2847 switch (pgno) {
2848 case 0x900:
2849 if (subno == VBI_ANY_SUBNO)
2850 subno = 0;
2851
2852 if (!vbi->cn->have_top || !top_index(vbi, pg, subno))
2853 return FALSE;
2854
2855 pg->nuid = vbi->network.ev.network.nuid;
2856 pg->pgno = 0x900;
2857 pg->subno = subno;
2858
2859 post_enhance(pg, ROWS);
2860
2861 for (row = 1; row < ROWS; row++)
2862 zap_links(pg, row);
2863
2864 return TRUE;
2865
2866 default:
2867 vtp = _vbi_cache_get_page (vbi->ca, vbi->cn, pgno, subno, -1);
2868 if (!vtp)
2869 return FALSE;
2870 success = vbi_format_vt_page(vbi, pg, vtp,
2871 max_level, display_rows,
2872 navigation);
2873 cache_page_unref (vtp);
2874 return success;
2875 }
2876}
2877extern cache_page *
2878_vbi_cache_find_next_page_2 (vbi_cache * ca,
2879 int dir,
2880 vbi_subno pgno,
2881 vbi_subno subno);
2882vbi_bool
2883vbi_get_next_pgno(vbi_decoder *vbi, int dir, vbi_pgno *pgno, vbi_pgno *subno)
2884{
2885 cache_page *vtp;
2886 vbi_pgno pg, sub;
2887
2888 assert(pgno && subno && (pgno>0x899 || pgno < 0x100));
2889
2890 pg = *pgno;
2891 sub = *subno;
2892
2893 vtp = _vbi_cache_find_next_page_2 (vbi->ca, dir, pg, sub);
2894
2895 if(vtp) {
2896 *pgno = vtp->pgno;
2897 *subno = vtp->subno;
2898 cache_page_unref (vtp);
2899 return TRUE;
2900 }
2901
2902 return FALSE;
2903}
2904
2905vbi_bool
2906vbi_get_next_sub_pgno(vbi_decoder *vbi, int dir, vbi_pgno *pgno, vbi_pgno *subno)
2907{
2908 cache_page *vtp;
2909 vbi_pgno pg, sub;
2910
2911 assert(pgno && subno && (pgno>0x899 || pgno < 0x100));
2912
2913 pg = *pgno;
2914 sub = *subno;
2915
2916 vtp = _vbi_cache_find_next_page (vbi->ca, dir, pg, sub);
2917
2918 if(vtp) {
2919 *pgno = vtp->pgno;
2920 *subno = vtp->subno;
2921 cache_page_unref (vtp);
2922 return TRUE;
2923 }
2924
2925 return FALSE;
2926}
2927
2928vbi_bool
2929vbi_get_sub_info(vbi_decoder *vbi, vbi_pgno pgno, int *subs, int *len)
2930{
2931 return _vbi_cache_get_sub_info(vbi->ca, pgno, subs, len);
2932}
2933
2934
2935/*
2936Local variables:
2937c-set-style: K&R
2938c-basic-offset: 8
2939End:
2940*/
2941