1 #define _POSIX_C_SOURCE 200809L
11 #include <sys/ioctl.h>
17 #include "default_font.h"
19 #define MIN(A, B) ((A) < (B) ? (A) : (B))
20 #define MAX(A, B) ((A) > (B) ? (A) : (B))
24 static struct Options
{
25 char *timings
, *dialogue
;
27 float maxdelay
, divisor
;
40 get_pair(Term
*term
, int row
, int col
)
46 inverse
= term
->mode
& M_REVERSE
;
47 if (term
->mode
& M_CURSORVIS
)
48 inverse
= term
->row
== row
&& term
->col
== col
? !inverse
: inverse
;
49 cell
= term
->addr
[row
][col
];
50 inverse
= cell
.attr
& A_INVERSE
? !inverse
: inverse
;
51 fore
= cell
.pair
>> 4;
52 back
= cell
.pair
& 0xF;
53 if (cell
.attr
& (A_ITALIC
| A_CROSSED
))
55 else if (cell
.attr
& A_UNDERLINE
)
57 else if (cell
.attr
& A_DIM
)
61 t
= fore
; fore
= back
; back
= t
;
63 if (cell
.attr
& A_BOLD
)
65 if (cell
.attr
& A_BLINK
)
67 if ((cell
.attr
& A_INVISIBLE
) != 0) fore
= back
;
68 return (fore
<< 4) | (back
& 0xF);
72 draw_char(Font
*font
, GIF
*gif
, uint16_t code
, uint8_t pair
, int row
, int col
)
80 index
= get_index(font
, code
);
83 strip
= &font
->data
[font
->stride
* font
->header
.h
* index
];
84 y
= font
->header
.h
* row
;
85 for (i
= 0; i
< font
->header
.h
; i
++) {
86 x
= font
->header
.w
* col
;
87 for (j
= 0; j
< font
->header
.w
; j
++) {
88 pixel
= strip
[j
>> 3] & (1 << (7 - (j
& 7)));
89 gif
->cur
[y
* gif
->w
+ x
] = pixel
? pair
>> 4 : pair
& 0xF;
93 strip
+= font
->stride
;
98 render(Term
*term
, Font
*font
, GIF
*gif
, uint16_t delay
)
104 for (i
= 0; i
< term
->rows
; i
++) {
105 for (j
= 0; j
< term
->cols
; j
++) {
106 code
= term
->addr
[i
][j
].code
;
107 pair
= get_pair(term
, i
, j
);
108 draw_char(font
, gif
, code
, pair
, i
, j
);
113 gif
->plt
= term
->plt
;
116 gif
->plt_dirty
|= term
->plt_dirty
;
119 add_frame(gif
, delay
);
135 float lastdone
, done
;
136 char pb
[options
.barsize
+1];
142 ft
= fopen(options
.timings
, "r");
144 fprintf(stderr
, "error: could not load timings: %s\n", options
.timings
);
147 fd
= open(options
.dialogue
, O_RDONLY
);
149 fprintf(stderr
, "error: could not load dialogue: %s\n", options
.dialogue
);
152 if (options
.font
== 0) {
155 font
= load_font(options
.font
);
157 fprintf(stderr
, "error: could not load font: %s\n", options
.font
);
162 /* Save first line of dialogue */
164 if (read(fd
, &ch
, 1) <= 0) break;
165 if (fln
<(int)sizeof(fl
)-1) fl
[fln
++]=ch
;
166 } while (ch
!= '\n');
167 /* Inspect it for the terminal size if needed */
168 if (fln
> 16 && (options
.height
== 0 || options
.width
== 0)) {
172 s
= strstr(fl
, "COLUMNS=\"");
173 if (s
) col
= atoi(s
+9);
174 s
= strstr(fl
, "LINES=\"");
175 if (s
) ln
= atoi(s
+7);
178 if (options
.width
<= 0)
180 if (options
.height
<= 0)
185 /* Default the VT to our real terminal */
186 if (options
.has_winsize
&& (options
.height
== 0 || options
.width
== 0)) {
187 if (options
.height
<= 0)
188 options
.height
= options
.size
.ws_row
;
189 if (options
.width
<= 0)
190 options
.width
= options
.size
.ws_col
;
193 if (options
.width
<= 0 || options
.height
<= 0) {
194 fprintf(stderr
, "error: no terminal size specified\n");
198 term
= new_term(options
.height
, options
.width
);
199 w
= term
->cols
* font
->header
.w
;
200 h
= term
->rows
* font
->header
.h
;
201 gif
= new_gif(options
.output
, w
, h
, term
->plt
, options
.loop
);
203 fprintf(stderr
, "error: could not create GIF: %s\n", options
.output
);
206 if (options
.barsize
) {
208 pb
[options
.barsize
-1] = ']';
209 pb
[options
.barsize
] = '\0';
210 for (i
= 1; i
< options
.barsize
-1; i
++)
214 /* get number of chunks */
215 for (c
= 0; fscanf(ft
, "%f %d\n", &t
, &n
) == 2; c
++);
220 while (fscanf(ft
, "%f %d\n", &t
, &n
) == 2) {
221 if (options
.barsize
) {
222 done
= i
* (options
.barsize
-1) / c
;
223 if (done
> lastdone
) {
224 while (done
> lastdone
) {
231 d
+= (MIN(t
, options
.maxdelay
) * 100.0 / options
.divisor
);
232 rd
= (uint16_t) MIN((int)(d
+ 0.5), 65535);
233 if (i
&& rd
>= MIN_DELAY
) {
234 render(term
, font
, gif
, rd
);
237 if (i
== 0) { id
= rd
; rd
= 0; d
= 0; }
243 term
->mode
&= ~M_CURSORVIS
;
247 if (options
.barsize
) {
248 while (lastdone
< options
.barsize
-2) {
254 render(term
, font
, gif
, MAX(rd
, 1));
261 if (options
.font
) free(font
);
274 "Usage: %s [options] timings dialogue\n\n"
275 "timings: File generated by script(1)'s -t option\n"
276 "dialogue: File generated by script(1)'s regular output\n\n"
278 " -o output File name of GIF output\n"
279 " -m maxdelay Maximum delay, as in scriptreplay(1)\n"
280 " -d divisor Speedup, as in scriptreplay(1)\n"
281 " -l count GIF loop count (0 = infinite loop)\n"
282 " -f font File name of MBF font to use\n"
283 " -h lines Terminal height\n"
284 " -w columns Terminal width\n"
285 " -c on|off Show/hide cursor\n"
286 " -p palette Define color palette, '@help' for std else file.\n"
287 " -q Quiet mode (don't show progress bar)\n"
288 " -v Verbose mode (show parser logs)\n"
297 options
.output
= "con.gif";
298 options
.maxdelay
= FLT_MAX
;
299 options
.divisor
= 1.0;
308 main(int argc
, char *argv
[])
314 options
.has_winsize
= 0;
315 if (ioctl(0, TIOCGWINSZ
, &options
.size
) != -1) {
316 options
.has_winsize
= 1;
318 while ((opt
= getopt(argc
, argv
, "o:m:d:l:f:h:w:c:p:qv")) != -1) {
321 options
.output
= optarg
;
324 options
.maxdelay
= atof(optarg
);
327 options
.divisor
= atof(optarg
);
330 options
.loop
= atoi(optarg
);
333 options
.font
= optarg
;
336 options
.height
= atoi(optarg
);
339 options
.width
= atoi(optarg
);
342 if (!strcmp(optarg
, "on") || !strcmp(optarg
, "1"))
344 else if (!strcmp(optarg
, "off") || !strcmp(optarg
, "0"))
354 set_default_palette(optarg
);
361 if (optind
>= argc
- 1) {
362 fprintf(stderr
, "error: no input given\n");
366 options
.timings
= argv
[optind
++];
367 options
.dialogue
= argv
[optind
++];
368 if (!options
.quiet
&& options
.has_winsize
)
369 options
.barsize
= options
.size
.ws_col
- 1;
370 ret
= convert_script();