]> git.sesse.net Git - ffmpeg/blob - libavcodec/vmdav.c
vmdaudio: set channel layout
[ffmpeg] / libavcodec / vmdav.c
1 /*
2  * Sierra VMD Audio & Video Decoders
3  * Copyright (C) 2004 the ffmpeg project
4  *
5  * This file is part of Libav.
6  *
7  * Libav is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * Libav is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 /**
23  * @file
24  * Sierra VMD audio & video decoders
25  * by Vladimir "VAG" Gneushev (vagsoft at mail.ru)
26  * for more information on the Sierra VMD format, visit:
27  *   http://www.pcisys.net/~melanson/codecs/
28  *
29  * The video decoder outputs PAL8 colorspace data. The decoder expects
30  * a 0x330-byte VMD file header to be transmitted via extradata during
31  * codec initialization. Each encoded frame that is sent to this decoder
32  * is expected to be prepended with the appropriate 16-byte frame
33  * information record from the VMD file.
34  *
35  * The audio decoder, like the video decoder, expects each encoded data
36  * chunk to be prepended with the appropriate 16-byte frame information
37  * record from the VMD file. It does not require the 0x330-byte VMD file
38  * header, but it does need the audio setup parameters passed in through
39  * normal libavcodec API means.
40  */
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include "libavutil/audioconvert.h"
47 #include "libavutil/common.h"
48 #include "libavutil/intreadwrite.h"
49 #include "avcodec.h"
50
51 #define VMD_HEADER_SIZE 0x330
52 #define PALETTE_COUNT 256
53
54 /*
55  * Video Decoder
56  */
57
58 typedef struct VmdVideoContext {
59
60     AVCodecContext *avctx;
61     AVFrame frame;
62     AVFrame prev_frame;
63
64     const unsigned char *buf;
65     int size;
66
67     unsigned char palette[PALETTE_COUNT * 4];
68     unsigned char *unpack_buffer;
69     int unpack_buffer_size;
70
71     int x_off, y_off;
72 } VmdVideoContext;
73
74 #define QUEUE_SIZE 0x1000
75 #define QUEUE_MASK 0x0FFF
76
77 static void lz_unpack(const unsigned char *src, int src_len,
78                       unsigned char *dest, int dest_len)
79 {
80     const unsigned char *s;
81     unsigned int s_len;
82     unsigned char *d;
83     unsigned char *d_end;
84     unsigned char queue[QUEUE_SIZE];
85     unsigned int qpos;
86     unsigned int dataleft;
87     unsigned int chainofs;
88     unsigned int chainlen;
89     unsigned int speclen;
90     unsigned char tag;
91     unsigned int i, j;
92
93     s = src;
94     s_len = src_len;
95     d = dest;
96     d_end = d + dest_len;
97     dataleft = AV_RL32(s);
98     s += 4; s_len -= 4;
99     memset(queue, 0x20, QUEUE_SIZE);
100     if (s_len < 4)
101         return;
102     if (AV_RL32(s) == 0x56781234) {
103         s += 4; s_len -= 4;
104         qpos = 0x111;
105         speclen = 0xF + 3;
106     } else {
107         qpos = 0xFEE;
108         speclen = 100;  /* no speclen */
109     }
110
111     while (dataleft > 0 && s_len > 0) {
112         tag = *s++; s_len--;
113         if ((tag == 0xFF) && (dataleft > 8)) {
114             if (d + 8 > d_end || s_len < 8)
115                 return;
116             for (i = 0; i < 8; i++) {
117                 queue[qpos++] = *d++ = *s++;
118                 qpos &= QUEUE_MASK;
119             }
120             s_len -= 8;
121             dataleft -= 8;
122         } else {
123             for (i = 0; i < 8; i++) {
124                 if (dataleft == 0)
125                     break;
126                 if (tag & 0x01) {
127                     if (d + 1 > d_end || s_len < 1)
128                         return;
129                     queue[qpos++] = *d++ = *s++;
130                     qpos &= QUEUE_MASK;
131                     dataleft--;
132                     s_len--;
133                 } else {
134                     if (s_len < 2)
135                         return;
136                     chainofs = *s++;
137                     chainofs |= ((*s & 0xF0) << 4);
138                     chainlen = (*s++ & 0x0F) + 3;
139                     s_len -= 2;
140                     if (chainlen == speclen) {
141                         if (s_len < 1)
142                             return;
143                         chainlen = *s++ + 0xF + 3;
144                         s_len--;
145                     }
146                     if (d + chainlen > d_end)
147                         return;
148                     for (j = 0; j < chainlen; j++) {
149                         *d = queue[chainofs++ & QUEUE_MASK];
150                         queue[qpos++] = *d++;
151                         qpos &= QUEUE_MASK;
152                     }
153                     dataleft -= chainlen;
154                 }
155                 tag >>= 1;
156             }
157         }
158     }
159 }
160
161 static int rle_unpack(const unsigned char *src, unsigned char *dest,
162     int src_count, int src_size, int dest_len)
163 {
164     const unsigned char *ps;
165     unsigned char *pd;
166     int i, l;
167     unsigned char *dest_end = dest + dest_len;
168
169     ps = src;
170     pd = dest;
171     if (src_count & 1) {
172         if (src_size < 1)
173             return 0;
174         *pd++ = *ps++;
175         src_size--;
176     }
177
178     src_count >>= 1;
179     i = 0;
180     do {
181         if (src_size < 1)
182             break;
183         l = *ps++;
184         src_size--;
185         if (l & 0x80) {
186             l = (l & 0x7F) * 2;
187             if (pd + l > dest_end || src_size < l)
188                 return ps - src;
189             memcpy(pd, ps, l);
190             ps += l;
191             src_size -= l;
192             pd += l;
193         } else {
194             if (pd + i > dest_end || src_size < 2)
195                 return ps - src;
196             for (i = 0; i < l; i++) {
197                 *pd++ = ps[0];
198                 *pd++ = ps[1];
199             }
200             ps += 2;
201             src_size -= 2;
202         }
203         i += l;
204     } while (i < src_count);
205
206     return ps - src;
207 }
208
209 static void vmd_decode(VmdVideoContext *s)
210 {
211     int i;
212     unsigned int *palette32;
213     unsigned char r, g, b;
214
215     /* point to the start of the encoded data */
216     const unsigned char *p = s->buf + 16;
217
218     const unsigned char *pb;
219     unsigned int pb_size;
220     unsigned char meth;
221     unsigned char *dp;   /* pointer to current frame */
222     unsigned char *pp;   /* pointer to previous frame */
223     unsigned char len;
224     int ofs;
225
226     int frame_x, frame_y;
227     int frame_width, frame_height;
228
229     frame_x = AV_RL16(&s->buf[6]);
230     frame_y = AV_RL16(&s->buf[8]);
231     frame_width = AV_RL16(&s->buf[10]) - frame_x + 1;
232     frame_height = AV_RL16(&s->buf[12]) - frame_y + 1;
233     if (frame_x < 0 || frame_width < 0 ||
234         frame_x >= s->avctx->width ||
235         frame_width > s->avctx->width ||
236         frame_x + frame_width > s->avctx->width)
237         return;
238     if (frame_y < 0 || frame_height < 0 ||
239         frame_y >= s->avctx->height ||
240         frame_height > s->avctx->height ||
241         frame_y + frame_height > s->avctx->height)
242         return;
243
244     if ((frame_width == s->avctx->width && frame_height == s->avctx->height) &&
245         (frame_x || frame_y)) {
246
247         s->x_off = frame_x;
248         s->y_off = frame_y;
249     }
250     frame_x -= s->x_off;
251     frame_y -= s->y_off;
252
253     /* if only a certain region will be updated, copy the entire previous
254      * frame before the decode */
255     if (s->prev_frame.data[0] &&
256         (frame_x || frame_y || (frame_width != s->avctx->width) ||
257         (frame_height != s->avctx->height))) {
258
259         memcpy(s->frame.data[0], s->prev_frame.data[0],
260             s->avctx->height * s->frame.linesize[0]);
261     }
262
263     /* check if there is a new palette */
264     if (s->buf[15] & 0x02) {
265         p += 2;
266         palette32 = (unsigned int *)s->palette;
267         for (i = 0; i < PALETTE_COUNT; i++) {
268             r = *p++ * 4;
269             g = *p++ * 4;
270             b = *p++ * 4;
271             palette32[i] = (r << 16) | (g << 8) | (b);
272         }
273         s->size -= (256 * 3 + 2);
274     }
275     if (s->size > 0) {
276         /* originally UnpackFrame in VAG's code */
277         pb = p;
278         pb_size = s->buf + s->size - pb;
279         if (pb_size < 1)
280             return;
281         meth = *pb++; pb_size--;
282         if (meth & 0x80) {
283             lz_unpack(pb, pb_size,
284                       s->unpack_buffer, s->unpack_buffer_size);
285             meth &= 0x7F;
286             pb = s->unpack_buffer;
287             pb_size = s->unpack_buffer_size;
288         }
289
290         dp = &s->frame.data[0][frame_y * s->frame.linesize[0] + frame_x];
291         pp = &s->prev_frame.data[0][frame_y * s->prev_frame.linesize[0] + frame_x];
292         switch (meth) {
293         case 1:
294             for (i = 0; i < frame_height; i++) {
295                 ofs = 0;
296                 do {
297                     if (pb_size < 1)
298                         return;
299                     len = *pb++;
300                     pb_size--;
301                     if (len & 0x80) {
302                         len = (len & 0x7F) + 1;
303                         if (ofs + len > frame_width || pb_size < len)
304                             return;
305                         memcpy(&dp[ofs], pb, len);
306                         pb += len;
307                         pb_size -= len;
308                         ofs += len;
309                     } else {
310                         /* interframe pixel copy */
311                         if (ofs + len + 1 > frame_width || !s->prev_frame.data[0])
312                             return;
313                         memcpy(&dp[ofs], &pp[ofs], len + 1);
314                         ofs += len + 1;
315                     }
316                 } while (ofs < frame_width);
317                 if (ofs > frame_width) {
318                     av_log(s->avctx, AV_LOG_ERROR, "VMD video: offset > width (%d > %d)\n",
319                         ofs, frame_width);
320                     break;
321                 }
322                 dp += s->frame.linesize[0];
323                 pp += s->prev_frame.linesize[0];
324             }
325             break;
326
327         case 2:
328             for (i = 0; i < frame_height; i++) {
329                 if (pb_size < frame_width)
330                     return;
331                 memcpy(dp, pb, frame_width);
332                 pb += frame_width;
333                 pb_size -= frame_width;
334                 dp += s->frame.linesize[0];
335                 pp += s->prev_frame.linesize[0];
336             }
337             break;
338
339         case 3:
340             for (i = 0; i < frame_height; i++) {
341                 ofs = 0;
342                 do {
343                     if (pb_size < 1)
344                         return;
345                     len = *pb++;
346                     pb_size--;
347                     if (len & 0x80) {
348                         len = (len & 0x7F) + 1;
349                         if (pb_size < 1)
350                             return;
351                         if (*pb++ == 0xFF)
352                             len = rle_unpack(pb, &dp[ofs], len, pb_size, frame_width - ofs);
353                         else {
354                             if (pb_size < len)
355                                 return;
356                             memcpy(&dp[ofs], pb, len);
357                         }
358                         pb += len;
359                         pb_size -= 1 + len;
360                         ofs += len;
361                     } else {
362                         /* interframe pixel copy */
363                         if (ofs + len + 1 > frame_width || !s->prev_frame.data[0])
364                             return;
365                         memcpy(&dp[ofs], &pp[ofs], len + 1);
366                         ofs += len + 1;
367                     }
368                 } while (ofs < frame_width);
369                 if (ofs > frame_width) {
370                     av_log(s->avctx, AV_LOG_ERROR, "VMD video: offset > width (%d > %d)\n",
371                         ofs, frame_width);
372                 }
373                 dp += s->frame.linesize[0];
374                 pp += s->prev_frame.linesize[0];
375             }
376             break;
377         }
378     }
379 }
380
381 static av_cold int vmdvideo_decode_init(AVCodecContext *avctx)
382 {
383     VmdVideoContext *s = avctx->priv_data;
384     int i;
385     unsigned int *palette32;
386     int palette_index = 0;
387     unsigned char r, g, b;
388     unsigned char *vmd_header;
389     unsigned char *raw_palette;
390
391     s->avctx = avctx;
392     avctx->pix_fmt = AV_PIX_FMT_PAL8;
393
394     /* make sure the VMD header made it */
395     if (s->avctx->extradata_size != VMD_HEADER_SIZE) {
396         av_log(s->avctx, AV_LOG_ERROR, "VMD video: expected extradata size of %d\n",
397             VMD_HEADER_SIZE);
398         return -1;
399     }
400     vmd_header = (unsigned char *)avctx->extradata;
401
402     s->unpack_buffer_size = AV_RL32(&vmd_header[800]);
403     s->unpack_buffer = av_malloc(s->unpack_buffer_size);
404     if (!s->unpack_buffer)
405         return -1;
406
407     /* load up the initial palette */
408     raw_palette = &vmd_header[28];
409     palette32 = (unsigned int *)s->palette;
410     for (i = 0; i < PALETTE_COUNT; i++) {
411         r = raw_palette[palette_index++] * 4;
412         g = raw_palette[palette_index++] * 4;
413         b = raw_palette[palette_index++] * 4;
414         palette32[i] = (r << 16) | (g << 8) | (b);
415     }
416
417     return 0;
418 }
419
420 static int vmdvideo_decode_frame(AVCodecContext *avctx,
421                                  void *data, int *data_size,
422                                  AVPacket *avpkt)
423 {
424     const uint8_t *buf = avpkt->data;
425     int buf_size = avpkt->size;
426     VmdVideoContext *s = avctx->priv_data;
427
428     s->buf = buf;
429     s->size = buf_size;
430
431     if (buf_size < 16)
432         return buf_size;
433
434     s->frame.reference = 1;
435     if (avctx->get_buffer(avctx, &s->frame)) {
436         av_log(s->avctx, AV_LOG_ERROR, "VMD Video: get_buffer() failed\n");
437         return -1;
438     }
439
440     vmd_decode(s);
441
442     /* make the palette available on the way out */
443     memcpy(s->frame.data[1], s->palette, PALETTE_COUNT * 4);
444
445     /* shuffle frames */
446     FFSWAP(AVFrame, s->frame, s->prev_frame);
447     if (s->frame.data[0])
448         avctx->release_buffer(avctx, &s->frame);
449
450     *data_size = sizeof(AVFrame);
451     *(AVFrame*)data = s->prev_frame;
452
453     /* report that the buffer was completely consumed */
454     return buf_size;
455 }
456
457 static av_cold int vmdvideo_decode_end(AVCodecContext *avctx)
458 {
459     VmdVideoContext *s = avctx->priv_data;
460
461     if (s->prev_frame.data[0])
462         avctx->release_buffer(avctx, &s->prev_frame);
463     av_free(s->unpack_buffer);
464
465     return 0;
466 }
467
468
469 /*
470  * Audio Decoder
471  */
472
473 #define BLOCK_TYPE_AUDIO    1
474 #define BLOCK_TYPE_INITIAL  2
475 #define BLOCK_TYPE_SILENCE  3
476
477 typedef struct VmdAudioContext {
478     AVFrame frame;
479     int out_bps;
480     int chunk_size;
481 } VmdAudioContext;
482
483 static const uint16_t vmdaudio_table[128] = {
484     0x000, 0x008, 0x010, 0x020, 0x030, 0x040, 0x050, 0x060, 0x070, 0x080,
485     0x090, 0x0A0, 0x0B0, 0x0C0, 0x0D0, 0x0E0, 0x0F0, 0x100, 0x110, 0x120,
486     0x130, 0x140, 0x150, 0x160, 0x170, 0x180, 0x190, 0x1A0, 0x1B0, 0x1C0,
487     0x1D0, 0x1E0, 0x1F0, 0x200, 0x208, 0x210, 0x218, 0x220, 0x228, 0x230,
488     0x238, 0x240, 0x248, 0x250, 0x258, 0x260, 0x268, 0x270, 0x278, 0x280,
489     0x288, 0x290, 0x298, 0x2A0, 0x2A8, 0x2B0, 0x2B8, 0x2C0, 0x2C8, 0x2D0,
490     0x2D8, 0x2E0, 0x2E8, 0x2F0, 0x2F8, 0x300, 0x308, 0x310, 0x318, 0x320,
491     0x328, 0x330, 0x338, 0x340, 0x348, 0x350, 0x358, 0x360, 0x368, 0x370,
492     0x378, 0x380, 0x388, 0x390, 0x398, 0x3A0, 0x3A8, 0x3B0, 0x3B8, 0x3C0,
493     0x3C8, 0x3D0, 0x3D8, 0x3E0, 0x3E8, 0x3F0, 0x3F8, 0x400, 0x440, 0x480,
494     0x4C0, 0x500, 0x540, 0x580, 0x5C0, 0x600, 0x640, 0x680, 0x6C0, 0x700,
495     0x740, 0x780, 0x7C0, 0x800, 0x900, 0xA00, 0xB00, 0xC00, 0xD00, 0xE00,
496     0xF00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000
497 };
498
499 static av_cold int vmdaudio_decode_init(AVCodecContext *avctx)
500 {
501     VmdAudioContext *s = avctx->priv_data;
502
503     if (avctx->channels < 1 || avctx->channels > 2) {
504         av_log(avctx, AV_LOG_ERROR, "invalid number of channels\n");
505         return AVERROR(EINVAL);
506     }
507     if (avctx->block_align < 1) {
508         av_log(avctx, AV_LOG_ERROR, "invalid block align\n");
509         return AVERROR(EINVAL);
510     }
511
512     avctx->channel_layout = avctx->channels == 1 ? AV_CH_LAYOUT_MONO :
513                                                    AV_CH_LAYOUT_STEREO;
514
515     if (avctx->bits_per_coded_sample == 16)
516         avctx->sample_fmt = AV_SAMPLE_FMT_S16;
517     else
518         avctx->sample_fmt = AV_SAMPLE_FMT_U8;
519     s->out_bps = av_get_bytes_per_sample(avctx->sample_fmt);
520
521     s->chunk_size = avctx->block_align + avctx->channels * (s->out_bps == 2);
522
523     avcodec_get_frame_defaults(&s->frame);
524     avctx->coded_frame = &s->frame;
525
526     av_log(avctx, AV_LOG_DEBUG, "%d channels, %d bits/sample, "
527            "block align = %d, sample rate = %d\n",
528            avctx->channels, avctx->bits_per_coded_sample, avctx->block_align,
529            avctx->sample_rate);
530
531     return 0;
532 }
533
534 static void decode_audio_s16(int16_t *out, const uint8_t *buf, int buf_size,
535                              int channels)
536 {
537     int ch;
538     const uint8_t *buf_end = buf + buf_size;
539     int predictor[2];
540     int st = channels - 1;
541
542     /* decode initial raw sample */
543     for (ch = 0; ch < channels; ch++) {
544         predictor[ch] = (int16_t)AV_RL16(buf);
545         buf += 2;
546         *out++ = predictor[ch];
547     }
548
549     /* decode DPCM samples */
550     ch = 0;
551     while (buf < buf_end) {
552         uint8_t b = *buf++;
553         if (b & 0x80)
554             predictor[ch] -= vmdaudio_table[b & 0x7F];
555         else
556             predictor[ch] += vmdaudio_table[b];
557         predictor[ch] = av_clip_int16(predictor[ch]);
558         *out++ = predictor[ch];
559         ch ^= st;
560     }
561 }
562
563 static int vmdaudio_decode_frame(AVCodecContext *avctx, void *data,
564                                  int *got_frame_ptr, AVPacket *avpkt)
565 {
566     const uint8_t *buf = avpkt->data;
567     const uint8_t *buf_end;
568     int buf_size = avpkt->size;
569     VmdAudioContext *s = avctx->priv_data;
570     int block_type, silent_chunks, audio_chunks;
571     int ret;
572     uint8_t *output_samples_u8;
573     int16_t *output_samples_s16;
574
575     if (buf_size < 16) {
576         av_log(avctx, AV_LOG_WARNING, "skipping small junk packet\n");
577         *got_frame_ptr = 0;
578         return buf_size;
579     }
580
581     block_type = buf[6];
582     if (block_type < BLOCK_TYPE_AUDIO || block_type > BLOCK_TYPE_SILENCE) {
583         av_log(avctx, AV_LOG_ERROR, "unknown block type: %d\n", block_type);
584         return AVERROR(EINVAL);
585     }
586     buf      += 16;
587     buf_size -= 16;
588
589     /* get number of silent chunks */
590     silent_chunks = 0;
591     if (block_type == BLOCK_TYPE_INITIAL) {
592         uint32_t flags;
593         if (buf_size < 4) {
594             av_log(avctx, AV_LOG_ERROR, "packet is too small\n");
595             return AVERROR(EINVAL);
596         }
597         flags         = AV_RB32(buf);
598         silent_chunks = av_popcount(flags);
599         buf      += 4;
600         buf_size -= 4;
601     } else if (block_type == BLOCK_TYPE_SILENCE) {
602         silent_chunks = 1;
603         buf_size = 0; // should already be zero but set it just to be sure
604     }
605
606     /* ensure output buffer is large enough */
607     audio_chunks = buf_size / s->chunk_size;
608
609     /* get output buffer */
610     s->frame.nb_samples = ((silent_chunks + audio_chunks) * avctx->block_align) / avctx->channels;
611     if ((ret = avctx->get_buffer(avctx, &s->frame)) < 0) {
612         av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
613         return ret;
614     }
615     output_samples_u8  = s->frame.data[0];
616     output_samples_s16 = (int16_t *)s->frame.data[0];
617
618     /* decode silent chunks */
619     if (silent_chunks > 0) {
620         int silent_size = avctx->block_align * silent_chunks;
621         if (s->out_bps == 2) {
622             memset(output_samples_s16, 0x00, silent_size * 2);
623             output_samples_s16 += silent_size;
624         } else {
625             memset(output_samples_u8,  0x80, silent_size);
626             output_samples_u8 += silent_size;
627         }
628     }
629
630     /* decode audio chunks */
631     if (audio_chunks > 0) {
632         buf_end = buf + buf_size;
633         while (buf < buf_end) {
634             if (s->out_bps == 2) {
635                 decode_audio_s16(output_samples_s16, buf, s->chunk_size,
636                                  avctx->channels);
637                 output_samples_s16 += avctx->block_align;
638             } else {
639                 memcpy(output_samples_u8, buf, s->chunk_size);
640                 output_samples_u8  += avctx->block_align;
641             }
642             buf += s->chunk_size;
643         }
644     }
645
646     *got_frame_ptr   = 1;
647     *(AVFrame *)data = s->frame;
648
649     return avpkt->size;
650 }
651
652
653 /*
654  * Public Data Structures
655  */
656
657 AVCodec ff_vmdvideo_decoder = {
658     .name           = "vmdvideo",
659     .type           = AVMEDIA_TYPE_VIDEO,
660     .id             = AV_CODEC_ID_VMDVIDEO,
661     .priv_data_size = sizeof(VmdVideoContext),
662     .init           = vmdvideo_decode_init,
663     .close          = vmdvideo_decode_end,
664     .decode         = vmdvideo_decode_frame,
665     .capabilities   = CODEC_CAP_DR1,
666     .long_name      = NULL_IF_CONFIG_SMALL("Sierra VMD video"),
667 };
668
669 AVCodec ff_vmdaudio_decoder = {
670     .name           = "vmdaudio",
671     .type           = AVMEDIA_TYPE_AUDIO,
672     .id             = AV_CODEC_ID_VMDAUDIO,
673     .priv_data_size = sizeof(VmdAudioContext),
674     .init           = vmdaudio_decode_init,
675     .decode         = vmdaudio_decode_frame,
676     .capabilities   = CODEC_CAP_DR1,
677     .long_name      = NULL_IF_CONFIG_SMALL("Sierra VMD audio"),
678 };