main(int argc, char** argv)
{
    if (argc < 2) {
        PrintUsageAndExit();
    }

    // default options
    Options.input                          = NULL;
    Options.verbose                        = false;
    Options.hls_version                    = 0;
    Options.pmt_pid                        = 0x100;
    Options.audio_pid                      = 0x101;
    Options.video_pid                      = 0x102;
    Options.audio_track_id                 = -1;
    Options.video_track_id                 = -1;
    Options.audio_format                   = AUDIO_FORMAT_TS;
    Options.output_single_file             = false;
    Options.show_info                      = false;
    Options.index_filename                 = "stream.m3u8";
    Options.iframe_index_filename          = NULL;
    Options.segment_filename_template      = NULL;
    Options.segment_url_template           = NULL;
    Options.segment_duration               = 6;
    Options.segment_duration_threshold     = DefaultSegmentDurationThreshold;
    Options.allow_cache                    = NULL;
    Options.encryption_key_hex             = NULL;
    Options.encryption_mode                = ENCRYPTION_MODE_NONE;
    Options.encryption_iv_mode             = ENCRYPTION_IV_MODE_NONE;
    Options.encryption_key_uri             = "key.bin";
    Options.encryption_key_format          = NULL;
    Options.encryption_key_format_versions = NULL;
    Options.pcr_offset                     = AP4_MPEG2_TS_DEFAULT_PCR_OFFSET;
    AP4_SetMemory(Options.encryption_key, 0, sizeof(Options.encryption_key));
    AP4_SetMemory(Options.encryption_iv,  0, sizeof(Options.encryption_iv));
    AP4_SetMemory(&Stats, 0, sizeof(Stats));

    // parse command line
    AP4_Result result;
    char** args = argv+1;
    while (const char* arg = *args++) {
        if (!strcmp(arg, "--verbose")) {
            Options.verbose = true;
        } else if (!strcmp(arg, "--hls-version")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --hls-version requires a number\n");
                return 1;
            }
            Options.hls_version = (unsigned int)strtoul(*args++, NULL, 10);
            if (Options.hls_version ==0) {
                fprintf(stderr, "ERROR: --hls-version requires number > 0\n");
                return 1;
            }
        } else if (!strcmp(arg, "--segment-duration")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --segment-duration requires a number\n");
                return 1;
            }
            Options.segment_duration = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--segment-duration-threshold")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --segment-duration-threshold requires a number\n");
                return 1;
            }
            Options.segment_duration_threshold = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--segment-filename-template")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --segment-filename-template requires an argument\n");
                return 1;
            }
            Options.segment_filename_template = *args++;
        } else if (!strcmp(arg, "--segment-url-template")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --segment-url-template requires an argument\n");
                return 1;
            }
            Options.segment_url_template = *args++;
        } else if (!strcmp(arg, "--allow-cache")) {
            if (*args == NULL || (strcmp(*args, "NO") && strcmp(*args, "YES"))) {
                fprintf(stderr, "ERROR: --allow-cache requires a YES or NO argument\n");
                return 1;
            }
            Options.allow_cache = *args++;
        } else if (!strcmp(arg, "--pmt-pid")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --pmt-pid requires a number\n");
                return 1;
            }
            Options.pmt_pid = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--audio-pid")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --audio-pid requires a number\n");
                return 1;
            }
            Options.audio_pid = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--video-pid")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --video-pid requires a number\n");
                return 1;
            }
            Options.video_pid = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--audio-track-id")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --audio-track-id requires a number\n");
                return 1;
            }
            Options.audio_track_id = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--audio-format")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --audio-format requires an argument\n");
                return 1;
            }
            const char* format = *args++;
            if (!strcmp(format, "ts")) {
                Options.audio_format = AUDIO_FORMAT_TS;
            } else if (!strcmp(format, "packed")) {
                Options.audio_format = AUDIO_FORMAT_PACKED;
            } else {
                fprintf(stderr, "ERROR: unknown audio format\n");
                return 1;
            }
        } else if (!strcmp(arg, "--video-track-id")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --video-track-id requires a number\n");
                return 1;
            }
            Options.video_track_id = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--pcr-offset")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --pcr-offset requires a number\n");
                return 1;
            }
            Options.pcr_offset = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--output-single-file")) {
            Options.output_single_file = true;
        } else if (!strcmp(arg, "--index-filename")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --index-filename requires a filename\n");
                return 1;
            }
            Options.index_filename = *args++;
        } else if (!strcmp(arg, "--iframe-index-filename")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --iframe-index-filename requires a filename\n");
                return 1;
            }
            Options.iframe_index_filename = *args++;
        } else if (!strcmp(arg, "--show-info")) {
            Options.show_info = true;
        } else if (!strcmp(arg, "--encryption-key")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key requires an argument\n");
                return 1;
            }
            Options.encryption_key_hex = *args++;
            result = AP4_ParseHex(Options.encryption_key_hex, Options.encryption_key, 16);
            if (AP4_FAILED(result)) {
                fprintf(stderr, "ERROR: invalid hex key\n");
                return 1;
            }
            if (Options.encryption_mode == ENCRYPTION_MODE_NONE) {
                Options.encryption_mode = ENCRYPTION_MODE_AES_128;
            }
        } else if (!strcmp(arg, "--encryption-mode")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-mode requires an argument\n");
                return 1;
            }
            if (strncmp(*args, "AES-128", 7) == 0) {
                Options.encryption_mode = ENCRYPTION_MODE_AES_128;
            } else if (strncmp(*args, "SAMPLE-AES", 10) == 0) {
                Options.encryption_mode = ENCRYPTION_MODE_SAMPLE_AES;
            } else {
                fprintf(stderr, "ERROR: unknown encryption mode\n");
                return 1;
            }
            ++args;
        } else if (!strcmp(arg, "--encryption-iv-mode")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-iv-mode requires an argument\n");
                return 1;
            }
            if (strncmp(*args, "sequence", 8) == 0) {
                Options.encryption_iv_mode = ENCRYPTION_IV_MODE_SEQUENCE;
            } else if (strncmp(*args, "random", 6) == 0) {
                Options.encryption_iv_mode = ENCRYPTION_IV_MODE_RANDOM;
            } else if (strncmp(*args, "fps", 3) == 0) {
                Options.encryption_iv_mode = ENCRYPTION_IV_MODE_FPS;
            } else {
                fprintf(stderr, "ERROR: unknown encryption IV mode\n");
                return 1;
            }
            ++args;
        } else if (!strcmp(arg, "--encryption-key-uri")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key-uri requires an argument\n");
                return 1;
            }
            Options.encryption_key_uri = *args++;
        } else if (!strcmp(arg, "--encryption-key-format")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key-format requires an argument\n");
                return 1;
            }
            Options.encryption_key_format = *args++;
        } else if (!strcmp(arg, "--encryption-key-format-versions")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key-format-versions requires an argument\n");
                return 1;
            }
            Options.encryption_key_format_versions = *args++;
        } else if (!strcmp(arg, "--encryption-key-line")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key-line requires an argument\n");
                return 1;
            }
            Options.encryption_key_lines.Append(*args++);
        } else if (Options.input == NULL) {
            Options.input = arg;
        } else {
            fprintf(stderr, "ERROR: unexpected argument: %s\n", arg);
            return 1;
        }
    }

    // check args
    if (Options.input == NULL) {
        fprintf(stderr, "ERROR: missing input file name\n");
        return 1;
    }
    if (Options.encryption_mode == ENCRYPTION_MODE_NONE && Options.encryption_key_lines.ItemCount() != 0) {
        fprintf(stderr, "ERROR: --encryption-key-line requires --encryption-key and --encryption-key-mode\n");
        return 1;
    }
    if (Options.encryption_mode != ENCRYPTION_MODE_NONE && Options.encryption_key_hex == NULL) {
        fprintf(stderr, "ERROR: no encryption key specified\n");
        return 1;
    }
    if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES && Options.hls_version > 0 && Options.hls_version < 5) {
        Options.hls_version = 5;
        fprintf(stderr, "WARNING: forcing version to 5 in order to support SAMPLE-AES encryption\n");
    }
    if (Options.iframe_index_filename && Options.hls_version > 0 && Options.hls_version < 4) {
        fprintf(stderr, "WARNING: forcing version to 4 in order to support I-FRAME-ONLY playlists\n");
        Options.hls_version = 4;
    }
    if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_NONE && Options.encryption_mode != ENCRYPTION_MODE_NONE) {
        if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
            // sequence-mode IVs don't work well with i-frame only playlists, use random instead
            Options.encryption_iv_mode = ENCRYPTION_IV_MODE_RANDOM;
        } else {
            Options.encryption_iv_mode = ENCRYPTION_IV_MODE_SEQUENCE;
        }
    }
    if ((Options.encryption_key_format || Options.encryption_key_format_versions) && Options.hls_version > 0 && Options.hls_version < 5) {
        Options.hls_version = 5;
        fprintf(stderr, "WARNING: forcing version to 5 in order to support KEYFORMAT and/or KEYFORMATVERSIONS\n");
    }
    if (Options.output_single_file && Options.hls_version > 0 && Options.hls_version < 4) {
        Options.hls_version = 4;
        fprintf(stderr, "WARNING: forcing version to 4 in order to support single file output\n");
    }
    if (Options.hls_version == 0) {
        // default version is 3 for cleartext or AES-128 encryption, and 5 for SAMPLE-AES
        if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
            Options.hls_version = 5;
        } else if (Options.output_single_file || Options.iframe_index_filename) {
            Options.hls_version = 4;
        } else {
            Options.hls_version = 3;
        }
    }

    if (Options.verbose && Options.show_info) {
        fprintf(stderr, "WARNING: --verbose will be ignored because --show-info is selected\n");
        Options.verbose = false;
    }

    // compute some derived values
    if (Options.iframe_index_filename == NULL) {
        if (Options.hls_version >= 4) {
            Options.iframe_index_filename = "iframes.m3u8";
        }
    }
    if (Options.audio_format == AUDIO_FORMAT_TS) {
        if (Options.segment_filename_template == NULL) {
            if (Options.output_single_file) {
                Options.segment_filename_template = "stream.ts";
            } else {
                Options.segment_filename_template = "segment-%d.ts";
            }
        }
        if (Options.segment_url_template == NULL) {
            if (Options.output_single_file) {
                Options.segment_url_template = "stream.ts";
            } else {
                Options.segment_url_template = "segment-%d.ts";
            }
        }
    }

    if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_FPS) {
        if (AP4_StringLength(Options.encryption_key_hex) != 64) {
            fprintf(stderr, "ERROR: 'fps' IV mode requires a 32 byte key value (64 characters in hex)\n");
            return 1;
        }
        result = AP4_ParseHex(Options.encryption_key_hex+32, Options.encryption_iv, 16);
        if (AP4_FAILED(result)) {
            fprintf(stderr, "ERROR: invalid hex IV\n");
            return 1;
        }
    } else if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_RANDOM) {
        result = AP4_System_GenerateRandomBytes(Options.encryption_iv, sizeof(Options.encryption_iv));
        if (AP4_FAILED(result)) {
            fprintf(stderr, "ERROR: failed to get random IV (%d)\n", result);
            return 1;
        }
    }

	// create the input stream
    AP4_ByteStream* input = NULL;
    result = AP4_FileByteStream::Create(Options.input, AP4_FileByteStream::STREAM_MODE_READ, input);
    if (AP4_FAILED(result)) {
        fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
        return 1;
    }

	// open the file
    AP4_File* input_file = new AP4_File(*input, true);

    // get the movie
    AP4_SampleDescription* sample_description;
    AP4_Movie* movie = input_file->GetMovie();
    if (movie == NULL) {
        fprintf(stderr, "ERROR: no movie in file\n");
        return 1;
    }

    // get the audio and video tracks
    AP4_Track* audio_track = NULL;
    if (Options.audio_track_id == -1) {
        audio_track = movie->GetTrack(AP4_Track::TYPE_AUDIO);
    } else if (Options.audio_track_id > 0) {
        audio_track = movie->GetTrack((AP4_UI32)Options.audio_track_id);
        if (audio_track == NULL) {
            fprintf(stderr, "ERROR: audio track ID %d not found\n", Options.audio_track_id);
            return 1;
        }
        if (audio_track->GetType() != AP4_Track::TYPE_AUDIO) {
            fprintf(stderr, "ERROR: track ID %d is not an audio track\n", Options.audio_track_id);
            return 1;
        }
    }
    AP4_Track* video_track = NULL;
    if (Options.video_track_id == -1) {
        video_track = movie->GetTrack(AP4_Track::TYPE_VIDEO);
    } else if (Options.video_track_id > 0) {
        video_track = movie->GetTrack((AP4_UI32)Options.video_track_id);
        if (video_track == NULL) {
            fprintf(stderr, "ERROR: video track ID %d not found\n", Options.video_track_id);
            return 1;
        }
        if (video_track->GetType() != AP4_Track::TYPE_VIDEO) {
            fprintf(stderr, "ERROR: track ID %d is not a video track\n", Options.video_track_id);
            return 1;
        }
    }
    if (audio_track == NULL && video_track == NULL) {
        fprintf(stderr, "ERROR: no suitable tracks found\n");
        delete input_file;
        input->Release();
        return 1;
    }
    if (Options.audio_format == AUDIO_FORMAT_PACKED && video_track != NULL) {
        if (audio_track == NULL) {
            fprintf(stderr, "ERROR: packed audio format requires an audio track\n");
            return 1;
        }
        fprintf(stderr, "WARNING: ignoring video track because of the packed audio format\n");
        video_track = NULL;
    }
    if (video_track == NULL) {
        Options.segment_duration_threshold = 0;
    }

    // create the appropriate readers
    AP4_LinearReader* linear_reader = NULL;
    SampleReader*     audio_reader  = NULL;
    SampleReader*     video_reader  = NULL;
    if (movie->HasFragments()) {
        // create a linear reader to get the samples
        linear_reader = new AP4_LinearReader(*movie, input);

        if (audio_track) {
            linear_reader->EnableTrack(audio_track->GetId());
            audio_reader = new FragmentedSampleReader(*linear_reader, audio_track->GetId());
        }
        if (video_track) {
            linear_reader->EnableTrack(video_track->GetId());
            video_reader = new FragmentedSampleReader(*linear_reader, video_track->GetId());
        }
    } else {
        if (audio_track) {
            audio_reader = new TrackSampleReader(*audio_track);
        }
        if (video_track) {
            video_reader = new TrackSampleReader(*video_track);
        }
    }

    AP4_Mpeg2TsWriter*               ts_writer = NULL;
    AP4_Mpeg2TsWriter::SampleStream* audio_stream = NULL;
    AP4_Mpeg2TsWriter::SampleStream* video_stream = NULL;
    AP4_UI08                         nalu_length_size = 0;
    PackedAudioWriter*               packed_writer = NULL;
    if (Options.audio_format == AUDIO_FORMAT_PACKED) {
        packed_writer = new PackedAudioWriter();

        // figure out the file extensions if needed
        sample_description = audio_track->GetSampleDescription(0);
        if (sample_description == NULL) {
            fprintf(stderr, "ERROR: unable to parse audio sample description\n");
            goto end;
        }
        if (Options.segment_filename_template == NULL || Options.segment_url_template == NULL) {
            const char* default_stream_name    = "stream.es";
            const char* default_stream_pattern = "segment-%d.es";
            if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
                AP4_MpegAudioSampleDescription* mpeg_audio_desc = AP4_DYNAMIC_CAST(AP4_MpegAudioSampleDescription, sample_description);
                if (mpeg_audio_desc == NULL ||
                    !(mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG4_AUDIO          ||
                      mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_LC   ||
                      mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_MAIN)) {
                    fprintf(stderr, "ERROR: only AAC audio is supported\n");
                    return 1;
                }
                default_stream_name    = "stream.aac";
                default_stream_pattern = "segment-%d.aac";
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_3) {
                default_stream_name    = "stream.ac3";
                default_stream_pattern = "segment-%d.ac3";
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) {
                default_stream_name    = "stream.ec3";
                default_stream_pattern = "segment-%d.ec3";
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_4) {
                default_stream_name    = "stream.ac4";
                default_stream_pattern = "segment-%d.ac4";
            }

            // override the segment names
            if (Options.segment_filename_template == NULL) {
                if (Options.output_single_file) {
                    Options.segment_filename_template = default_stream_name;
                } else {
                    Options.segment_filename_template = default_stream_pattern;
                }
            }
            if (Options.segment_url_template == NULL) {
                if (Options.output_single_file) {
                    Options.segment_url_template = default_stream_name;
                } else {
                    Options.segment_url_template = default_stream_pattern;
                }
            }
        }
    } else {
        // create an MPEG2 TS Writer
        ts_writer = new AP4_Mpeg2TsWriter(Options.pmt_pid);

        // add the audio stream
        if (audio_track) {
            sample_description = audio_track->GetSampleDescription(0);
            if (sample_description == NULL) {
                fprintf(stderr, "ERROR: unable to parse audio sample description\n");
                goto end;
            }

            unsigned int stream_type = 0;
            unsigned int stream_id   = 0;
            if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
                if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                    stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ISO_IEC_13818_7;
                } else {
                    stream_type = AP4_MPEG2_STREAM_TYPE_ISO_IEC_13818_7;
                }
                stream_id   = AP4_MPEG2_TS_DEFAULT_STREAM_ID_AUDIO;
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_3) {
                if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                    stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ATSC_AC3;
                } else {
                    stream_type = AP4_MPEG2_STREAM_TYPE_ATSC_AC3;
                }
                stream_id   = AP4_MPEG2_TS_STREAM_ID_PRIVATE_STREAM_1;
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) {
                if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                    stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ATSC_EAC3;
                } else {
                    stream_type = AP4_MPEG2_STREAM_TYPE_ATSC_EAC3;
                }
                stream_id   = AP4_MPEG2_TS_STREAM_ID_PRIVATE_STREAM_1;
            } else {
                fprintf(stderr, "ERROR: audio codec not supported\n");
                return 1;
            }
            if (stream_type == AP4_MPEG2_STREAM_TYPE_ATSC_EAC3) {
                // E-AC-3 descriptor
                unsigned int number_of_channels = 0;
                AP4_String track_language;
                AP4_Dec3Atom* dec3 = AP4_DYNAMIC_CAST(AP4_Dec3Atom, sample_description->GetDetails().GetChild(AP4_ATOM_TYPE_DEC3));
                AP4_BitWriter bits(8);
                bits.Write(0xCC, 8);
                bits.Write(0x06, 8);    // fixed value
                bits.Write(0xC0, 8);    // reserved, bsid_flag, mainid_flag, asvc_flag, mixinfoexists, substream1_flag, substream2_flag and substream3_flag
                bits.Write(24, 5);      // reserved, full_service_flag and service_type
                if (dec3->GetSubStreams()[0].acmod == 0) {
                    number_of_channels = 1;
                } else if (dec3->GetSubStreams()[0].acmod == 1) {
                    number_of_channels = 0;
                } else if (dec3->GetSubStreams()[0].acmod == 2) {
                    number_of_channels = 2;
                } else {
                    number_of_channels = 4;
                }
                if (dec3->GetSubStreams()[0].num_dep_sub > 0) {
                    number_of_channels = 5;
                }
                bits.Write(number_of_channels, 3);              // number_of_channels
                bits.Write(4, 3);                               // language_flag, language_flag_2, reserved
                bits.Write(dec3->GetSubStreams()[0].bsid, 5);   // bsid
                track_language = audio_track->GetTrackLanguage();
                if (track_language.GetLength() == 3) {
                    bits.Write(track_language.GetChars()[0], 8);
                    bits.Write(track_language.GetChars()[1], 8);
                    bits.Write(track_language.GetChars()[2], 8);
                } else {
                    bits.Write(0x75, 8);
                    bits.Write(0x6E, 8);
                    bits.Write(0x64, 8);
                }
                 // setup the audio stream
                result = ts_writer->SetAudioStream(audio_track->GetMediaTimeScale(),
                                                   stream_type,
                                                   stream_id,
                                                   audio_stream,
                                                   Options.audio_pid,
                                                   bits.GetData(), 8,
                                                   Options.pcr_offset);
            } else {
            // setup the audio stream
            result = ts_writer->SetAudioStream(audio_track->GetMediaTimeScale(),
                                               stream_type,
                                               stream_id,
                                               audio_stream,
                                               Options.audio_pid,
                                               NULL, 0,
                                               Options.pcr_offset);
            }
            if (AP4_FAILED(result)) {
                fprintf(stderr, "could not create audio stream (%d)\n", result);
                goto end;
            }
        }

        // add the video stream
        if (video_track) {
            sample_description = video_track->GetSampleDescription(0);
            if (sample_description == NULL) {
                fprintf(stderr, "ERROR: unable to parse video sample description\n");
                goto end;
            }

            // decide on the stream type
            unsigned int stream_type = 0;
            unsigned int stream_id   = AP4_MPEG2_TS_DEFAULT_STREAM_ID_VIDEO;
            if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC1 ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC2 ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC3 ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC4 ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVAV ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVA1) {
                if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                    stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_AVC;
                    AP4_AvcSampleDescription* avc_desc = AP4_DYNAMIC_CAST(AP4_AvcSampleDescription, sample_description);
                    if (avc_desc == NULL) {
                        fprintf(stderr, "ERROR: not a proper AVC track\n");
                        return 1;
                    }
                    nalu_length_size = avc_desc->GetNaluLengthSize();
                } else {
                    stream_type = AP4_MPEG2_STREAM_TYPE_AVC;
                }
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_HEV1 ||
                       sample_description->GetFormat() == AP4_SAMPLE_FORMAT_HVC1 ||
                       sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVHE ||
                       sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVH1) {
                stream_type = AP4_MPEG2_STREAM_TYPE_HEVC;
            } else {
                fprintf(stderr, "ERROR: video codec not supported\n");
                return 1;
            }
            if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                if (stream_type != AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_AVC) {
                    fprintf(stderr, "ERROR: AES-SAMPLE encryption can only be used with H.264 video\n");
                    return 1;
                }
            }

            // setup the video stream
            result = ts_writer->SetVideoStream(video_track->GetMediaTimeScale(),
                                               stream_type,
                                               stream_id,
                                               video_stream,
                                               Options.video_pid,
                                               NULL, 0,
                                               Options.pcr_offset);
            if (AP4_FAILED(result)) {
                fprintf(stderr, "could not create video stream (%d)\n", result);
                goto end;
            }
        }
    }

    result = WriteSamples(ts_writer, packed_writer,
                          audio_track, audio_reader, audio_stream,
                          video_track, video_reader, video_stream,
                          Options.segment_duration_threshold,
                          nalu_length_size);
    if (AP4_FAILED(result)) {
        fprintf(stderr, "ERROR: failed to write samples (%d)\n", result);
    }

    if (Options.show_info) {
        double average_segment_bitrate = 0.0;
        if (Stats.segments_total_duration != 0.0) {
            average_segment_bitrate = 8.0*(double)Stats.segments_total_size/Stats.segments_total_duration;
        }
        double average_iframe_bitrate = 0.0;
        if (Stats.segments_total_duration != 0.0) {
            average_iframe_bitrate = 8.0*(double)Stats.iframes_total_size/Stats.segments_total_duration;
        }

        double frame_rate = 0.0;
        if (video_track && (Stats.segments_total_duration != 0.0)) {
            double sample_count = (double)video_track->GetSampleCount();
            double media_duration = (double)video_track->GetMediaDuration();
            double timescale = (double)video_track->GetMediaTimeScale();
            if (media_duration > 0.0) {
                frame_rate = sample_count/(media_duration/timescale);
            }
        }

        printf(
            "{\n"
        );
        printf(
            "  \"stats\": {\n"
            "    \"duration\": %f,\n"
            "    \"avg_segment_bitrate\": %f,\n"
            "    \"max_segment_bitrate\": %f,\n"
            "    \"avg_iframe_bitrate\": %f,\n"
            "    \"max_iframe_bitrate\": %f,\n"
            "    \"frame_rate\": %f\n"
            "  }",
            (double)movie->GetDurationMs()/1000.0,
            average_segment_bitrate,
            Stats.max_segment_bitrate,
            average_iframe_bitrate,
            Stats.max_iframe_bitrate,
            frame_rate
        );
        if (audio_track) {
            AP4_String codec;
            AP4_SampleDescription* sdesc = audio_track->GetSampleDescription(0);
            if (sdesc) {
                sdesc->GetCodecString(codec);
            }
            printf(
                ",\n"
                "  \"audio\": {\n"
                "    \"codec\": \"%s\"\n"
                "  }",
                codec.GetChars()
            );
        }
        if (video_track) {
            AP4_String codec;
            AP4_UI16 width = (AP4_UI16)(video_track->GetWidth()/65536.0);
            AP4_UI16 height = (AP4_UI16)(video_track->GetHeight()/65536.0);
            AP4_SampleDescription* sdesc = video_track->GetSampleDescription(0);
            if (sdesc) {
                sdesc->GetCodecString(codec);
                AP4_VideoSampleDescription* video_desc = AP4_DYNAMIC_CAST(AP4_VideoSampleDescription, sdesc);
                if (video_desc) {
                    width = video_desc->GetWidth();
                    height = video_desc->GetHeight();
                }
            }
            printf(
                ",\n"
                "  \"video\": {\n"
                "    \"codec\": \"%s\",\n"
                "    \"width\": %d,\n"
                "    \"height\": %d\n"
                "  }",
                codec.GetChars(),
                width,
                height
            );
        }
        printf(
            "\n"
            "}\n"
        );
    }

end:
    delete ts_writer;
    delete packed_writer;
    delete input_file;
    input->Release();
    delete linear_reader;
    delete audio_reader;
    delete video_reader;

    return result == AP4_SUCCESS?0:1;
}

fix #691


main(int argc, char** argv)
{
    if (argc < 2) {
        PrintUsageAndExit();
    }

    // default options
    Options.input                          = NULL;
    Options.verbose                        = false;
    Options.hls_version                    = 0;
    Options.pmt_pid                        = 0x100;
    Options.audio_pid                      = 0x101;
    Options.video_pid                      = 0x102;
    Options.audio_track_id                 = -1;
    Options.video_track_id                 = -1;
    Options.audio_format                   = AUDIO_FORMAT_TS;
    Options.output_single_file             = false;
    Options.show_info                      = false;
    Options.index_filename                 = "stream.m3u8";
    Options.iframe_index_filename          = NULL;
    Options.segment_filename_template      = NULL;
    Options.segment_url_template           = NULL;
    Options.segment_duration               = 6;
    Options.segment_duration_threshold     = DefaultSegmentDurationThreshold;
    Options.allow_cache                    = NULL;
    Options.encryption_key_hex             = NULL;
    Options.encryption_mode                = ENCRYPTION_MODE_NONE;
    Options.encryption_iv_mode             = ENCRYPTION_IV_MODE_NONE;
    Options.encryption_key_uri             = "key.bin";
    Options.encryption_key_format          = NULL;
    Options.encryption_key_format_versions = NULL;
    Options.pcr_offset                     = AP4_MPEG2_TS_DEFAULT_PCR_OFFSET;
    AP4_SetMemory(Options.encryption_key, 0, sizeof(Options.encryption_key));
    AP4_SetMemory(Options.encryption_iv,  0, sizeof(Options.encryption_iv));
    AP4_SetMemory(&Stats, 0, sizeof(Stats));

    // parse command line
    AP4_Result result;
    char** args = argv+1;
    while (const char* arg = *args++) {
        if (!strcmp(arg, "--verbose")) {
            Options.verbose = true;
        } else if (!strcmp(arg, "--hls-version")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --hls-version requires a number\n");
                return 1;
            }
            Options.hls_version = (unsigned int)strtoul(*args++, NULL, 10);
            if (Options.hls_version ==0) {
                fprintf(stderr, "ERROR: --hls-version requires number > 0\n");
                return 1;
            }
        } else if (!strcmp(arg, "--segment-duration")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --segment-duration requires a number\n");
                return 1;
            }
            Options.segment_duration = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--segment-duration-threshold")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --segment-duration-threshold requires a number\n");
                return 1;
            }
            Options.segment_duration_threshold = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--segment-filename-template")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --segment-filename-template requires an argument\n");
                return 1;
            }
            Options.segment_filename_template = *args++;
        } else if (!strcmp(arg, "--segment-url-template")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --segment-url-template requires an argument\n");
                return 1;
            }
            Options.segment_url_template = *args++;
        } else if (!strcmp(arg, "--allow-cache")) {
            if (*args == NULL || (strcmp(*args, "NO") && strcmp(*args, "YES"))) {
                fprintf(stderr, "ERROR: --allow-cache requires a YES or NO argument\n");
                return 1;
            }
            Options.allow_cache = *args++;
        } else if (!strcmp(arg, "--pmt-pid")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --pmt-pid requires a number\n");
                return 1;
            }
            Options.pmt_pid = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--audio-pid")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --audio-pid requires a number\n");
                return 1;
            }
            Options.audio_pid = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--video-pid")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --video-pid requires a number\n");
                return 1;
            }
            Options.video_pid = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--audio-track-id")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --audio-track-id requires a number\n");
                return 1;
            }
            Options.audio_track_id = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--audio-format")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --audio-format requires an argument\n");
                return 1;
            }
            const char* format = *args++;
            if (!strcmp(format, "ts")) {
                Options.audio_format = AUDIO_FORMAT_TS;
            } else if (!strcmp(format, "packed")) {
                Options.audio_format = AUDIO_FORMAT_PACKED;
            } else {
                fprintf(stderr, "ERROR: unknown audio format\n");
                return 1;
            }
        } else if (!strcmp(arg, "--video-track-id")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --video-track-id requires a number\n");
                return 1;
            }
            Options.video_track_id = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--pcr-offset")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --pcr-offset requires a number\n");
                return 1;
            }
            Options.pcr_offset = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--output-single-file")) {
            Options.output_single_file = true;
        } else if (!strcmp(arg, "--index-filename")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --index-filename requires a filename\n");
                return 1;
            }
            Options.index_filename = *args++;
        } else if (!strcmp(arg, "--iframe-index-filename")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --iframe-index-filename requires a filename\n");
                return 1;
            }
            Options.iframe_index_filename = *args++;
        } else if (!strcmp(arg, "--show-info")) {
            Options.show_info = true;
        } else if (!strcmp(arg, "--encryption-key")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key requires an argument\n");
                return 1;
            }
            Options.encryption_key_hex = *args++;
            result = AP4_ParseHex(Options.encryption_key_hex, Options.encryption_key, 16);
            if (AP4_FAILED(result)) {
                fprintf(stderr, "ERROR: invalid hex key\n");
                return 1;
            }
            if (Options.encryption_mode == ENCRYPTION_MODE_NONE) {
                Options.encryption_mode = ENCRYPTION_MODE_AES_128;
            }
        } else if (!strcmp(arg, "--encryption-mode")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-mode requires an argument\n");
                return 1;
            }
            if (strncmp(*args, "AES-128", 7) == 0) {
                Options.encryption_mode = ENCRYPTION_MODE_AES_128;
            } else if (strncmp(*args, "SAMPLE-AES", 10) == 0) {
                Options.encryption_mode = ENCRYPTION_MODE_SAMPLE_AES;
            } else {
                fprintf(stderr, "ERROR: unknown encryption mode\n");
                return 1;
            }
            ++args;
        } else if (!strcmp(arg, "--encryption-iv-mode")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-iv-mode requires an argument\n");
                return 1;
            }
            if (strncmp(*args, "sequence", 8) == 0) {
                Options.encryption_iv_mode = ENCRYPTION_IV_MODE_SEQUENCE;
            } else if (strncmp(*args, "random", 6) == 0) {
                Options.encryption_iv_mode = ENCRYPTION_IV_MODE_RANDOM;
            } else if (strncmp(*args, "fps", 3) == 0) {
                Options.encryption_iv_mode = ENCRYPTION_IV_MODE_FPS;
            } else {
                fprintf(stderr, "ERROR: unknown encryption IV mode\n");
                return 1;
            }
            ++args;
        } else if (!strcmp(arg, "--encryption-key-uri")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key-uri requires an argument\n");
                return 1;
            }
            Options.encryption_key_uri = *args++;
        } else if (!strcmp(arg, "--encryption-key-format")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key-format requires an argument\n");
                return 1;
            }
            Options.encryption_key_format = *args++;
        } else if (!strcmp(arg, "--encryption-key-format-versions")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key-format-versions requires an argument\n");
                return 1;
            }
            Options.encryption_key_format_versions = *args++;
        } else if (!strcmp(arg, "--encryption-key-line")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key-line requires an argument\n");
                return 1;
            }
            Options.encryption_key_lines.Append(*args++);
        } else if (Options.input == NULL) {
            Options.input = arg;
        } else {
            fprintf(stderr, "ERROR: unexpected argument: %s\n", arg);
            return 1;
        }
    }

    // check args
    if (Options.input == NULL) {
        fprintf(stderr, "ERROR: missing input file name\n");
        return 1;
    }
    if (Options.encryption_mode == ENCRYPTION_MODE_NONE && Options.encryption_key_lines.ItemCount() != 0) {
        fprintf(stderr, "ERROR: --encryption-key-line requires --encryption-key and --encryption-key-mode\n");
        return 1;
    }
    if (Options.encryption_mode != ENCRYPTION_MODE_NONE && Options.encryption_key_hex == NULL) {
        fprintf(stderr, "ERROR: no encryption key specified\n");
        return 1;
    }
    if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES && Options.hls_version > 0 && Options.hls_version < 5) {
        Options.hls_version = 5;
        fprintf(stderr, "WARNING: forcing version to 5 in order to support SAMPLE-AES encryption\n");
    }
    if (Options.iframe_index_filename && Options.hls_version > 0 && Options.hls_version < 4) {
        fprintf(stderr, "WARNING: forcing version to 4 in order to support I-FRAME-ONLY playlists\n");
        Options.hls_version = 4;
    }
    if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_NONE && Options.encryption_mode != ENCRYPTION_MODE_NONE) {
        if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
            // sequence-mode IVs don't work well with i-frame only playlists, use random instead
            Options.encryption_iv_mode = ENCRYPTION_IV_MODE_RANDOM;
        } else {
            Options.encryption_iv_mode = ENCRYPTION_IV_MODE_SEQUENCE;
        }
    }
    if ((Options.encryption_key_format || Options.encryption_key_format_versions) && Options.hls_version > 0 && Options.hls_version < 5) {
        Options.hls_version = 5;
        fprintf(stderr, "WARNING: forcing version to 5 in order to support KEYFORMAT and/or KEYFORMATVERSIONS\n");
    }
    if (Options.output_single_file && Options.hls_version > 0 && Options.hls_version < 4) {
        Options.hls_version = 4;
        fprintf(stderr, "WARNING: forcing version to 4 in order to support single file output\n");
    }
    if (Options.hls_version == 0) {
        // default version is 3 for cleartext or AES-128 encryption, and 5 for SAMPLE-AES
        if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
            Options.hls_version = 5;
        } else if (Options.output_single_file || Options.iframe_index_filename) {
            Options.hls_version = 4;
        } else {
            Options.hls_version = 3;
        }
    }

    if (Options.verbose && Options.show_info) {
        fprintf(stderr, "WARNING: --verbose will be ignored because --show-info is selected\n");
        Options.verbose = false;
    }

    // compute some derived values
    if (Options.iframe_index_filename == NULL) {
        if (Options.hls_version >= 4) {
            Options.iframe_index_filename = "iframes.m3u8";
        }
    }
    if (Options.audio_format == AUDIO_FORMAT_TS) {
        if (Options.segment_filename_template == NULL) {
            if (Options.output_single_file) {
                Options.segment_filename_template = "stream.ts";
            } else {
                Options.segment_filename_template = "segment-%d.ts";
            }
        }
        if (Options.segment_url_template == NULL) {
            if (Options.output_single_file) {
                Options.segment_url_template = "stream.ts";
            } else {
                Options.segment_url_template = "segment-%d.ts";
            }
        }
    }

    if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_FPS) {
        if (Options.encryption_key_hex == NULL || AP4_StringLength(Options.encryption_key_hex) != 64) {
            fprintf(stderr, "ERROR: 'fps' IV mode requires a 32 byte key value (64 characters in hex)\n");
            return 1;
        }
        result = AP4_ParseHex(Options.encryption_key_hex+32, Options.encryption_iv, 16);
        if (AP4_FAILED(result)) {
            fprintf(stderr, "ERROR: invalid hex IV\n");
            return 1;
        }
    } else if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_RANDOM) {
        result = AP4_System_GenerateRandomBytes(Options.encryption_iv, sizeof(Options.encryption_iv));
        if (AP4_FAILED(result)) {
            fprintf(stderr, "ERROR: failed to get random IV (%d)\n", result);
            return 1;
        }
    }

	// create the input stream
    AP4_ByteStream* input = NULL;
    result = AP4_FileByteStream::Create(Options.input, AP4_FileByteStream::STREAM_MODE_READ, input);
    if (AP4_FAILED(result)) {
        fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
        return 1;
    }

	// open the file
    AP4_File* input_file = new AP4_File(*input, true);

    // get the movie
    AP4_SampleDescription* sample_description;
    AP4_Movie* movie = input_file->GetMovie();
    if (movie == NULL) {
        fprintf(stderr, "ERROR: no movie in file\n");
        return 1;
    }

    // get the audio and video tracks
    AP4_Track* audio_track = NULL;
    if (Options.audio_track_id == -1) {
        audio_track = movie->GetTrack(AP4_Track::TYPE_AUDIO);
    } else if (Options.audio_track_id > 0) {
        audio_track = movie->GetTrack((AP4_UI32)Options.audio_track_id);
        if (audio_track == NULL) {
            fprintf(stderr, "ERROR: audio track ID %d not found\n", Options.audio_track_id);
            return 1;
        }
        if (audio_track->GetType() != AP4_Track::TYPE_AUDIO) {
            fprintf(stderr, "ERROR: track ID %d is not an audio track\n", Options.audio_track_id);
            return 1;
        }
    }
    AP4_Track* video_track = NULL;
    if (Options.video_track_id == -1) {
        video_track = movie->GetTrack(AP4_Track::TYPE_VIDEO);
    } else if (Options.video_track_id > 0) {
        video_track = movie->GetTrack((AP4_UI32)Options.video_track_id);
        if (video_track == NULL) {
            fprintf(stderr, "ERROR: video track ID %d not found\n", Options.video_track_id);
            return 1;
        }
        if (video_track->GetType() != AP4_Track::TYPE_VIDEO) {
            fprintf(stderr, "ERROR: track ID %d is not a video track\n", Options.video_track_id);
            return 1;
        }
    }
    if (audio_track == NULL && video_track == NULL) {
        fprintf(stderr, "ERROR: no suitable tracks found\n");
        delete input_file;
        input->Release();
        return 1;
    }
    if (Options.audio_format == AUDIO_FORMAT_PACKED && video_track != NULL) {
        if (audio_track == NULL) {
            fprintf(stderr, "ERROR: packed audio format requires an audio track\n");
            return 1;
        }
        fprintf(stderr, "WARNING: ignoring video track because of the packed audio format\n");
        video_track = NULL;
    }
    if (video_track == NULL) {
        Options.segment_duration_threshold = 0;
    }

    // create the appropriate readers
    AP4_LinearReader* linear_reader = NULL;
    SampleReader*     audio_reader  = NULL;
    SampleReader*     video_reader  = NULL;
    if (movie->HasFragments()) {
        // create a linear reader to get the samples
        linear_reader = new AP4_LinearReader(*movie, input);

        if (audio_track) {
            linear_reader->EnableTrack(audio_track->GetId());
            audio_reader = new FragmentedSampleReader(*linear_reader, audio_track->GetId());
        }
        if (video_track) {
            linear_reader->EnableTrack(video_track->GetId());
            video_reader = new FragmentedSampleReader(*linear_reader, video_track->GetId());
        }
    } else {
        if (audio_track) {
            audio_reader = new TrackSampleReader(*audio_track);
        }
        if (video_track) {
            video_reader = new TrackSampleReader(*video_track);
        }
    }

    AP4_Mpeg2TsWriter*               ts_writer = NULL;
    AP4_Mpeg2TsWriter::SampleStream* audio_stream = NULL;
    AP4_Mpeg2TsWriter::SampleStream* video_stream = NULL;
    AP4_UI08                         nalu_length_size = 0;
    PackedAudioWriter*               packed_writer = NULL;
    if (Options.audio_format == AUDIO_FORMAT_PACKED) {
        packed_writer = new PackedAudioWriter();

        // figure out the file extensions if needed
        sample_description = audio_track->GetSampleDescription(0);
        if (sample_description == NULL) {
            fprintf(stderr, "ERROR: unable to parse audio sample description\n");
            goto end;
        }
        if (Options.segment_filename_template == NULL || Options.segment_url_template == NULL) {
            const char* default_stream_name    = "stream.es";
            const char* default_stream_pattern = "segment-%d.es";
            if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
                AP4_MpegAudioSampleDescription* mpeg_audio_desc = AP4_DYNAMIC_CAST(AP4_MpegAudioSampleDescription, sample_description);
                if (mpeg_audio_desc == NULL ||
                    !(mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG4_AUDIO          ||
                      mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_LC   ||
                      mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_MAIN)) {
                    fprintf(stderr, "ERROR: only AAC audio is supported\n");
                    return 1;
                }
                default_stream_name    = "stream.aac";
                default_stream_pattern = "segment-%d.aac";
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_3) {
                default_stream_name    = "stream.ac3";
                default_stream_pattern = "segment-%d.ac3";
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) {
                default_stream_name    = "stream.ec3";
                default_stream_pattern = "segment-%d.ec3";
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_4) {
                default_stream_name    = "stream.ac4";
                default_stream_pattern = "segment-%d.ac4";
            }

            // override the segment names
            if (Options.segment_filename_template == NULL) {
                if (Options.output_single_file) {
                    Options.segment_filename_template = default_stream_name;
                } else {
                    Options.segment_filename_template = default_stream_pattern;
                }
            }
            if (Options.segment_url_template == NULL) {
                if (Options.output_single_file) {
                    Options.segment_url_template = default_stream_name;
                } else {
                    Options.segment_url_template = default_stream_pattern;
                }
            }
        }
    } else {
        // create an MPEG2 TS Writer
        ts_writer = new AP4_Mpeg2TsWriter(Options.pmt_pid);

        // add the audio stream
        if (audio_track) {
            sample_description = audio_track->GetSampleDescription(0);
            if (sample_description == NULL) {
                fprintf(stderr, "ERROR: unable to parse audio sample description\n");
                goto end;
            }

            unsigned int stream_type = 0;
            unsigned int stream_id   = 0;
            if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
                if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                    stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ISO_IEC_13818_7;
                } else {
                    stream_type = AP4_MPEG2_STREAM_TYPE_ISO_IEC_13818_7;
                }
                stream_id   = AP4_MPEG2_TS_DEFAULT_STREAM_ID_AUDIO;
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_3) {
                if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                    stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ATSC_AC3;
                } else {
                    stream_type = AP4_MPEG2_STREAM_TYPE_ATSC_AC3;
                }
                stream_id   = AP4_MPEG2_TS_STREAM_ID_PRIVATE_STREAM_1;
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) {
                if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                    stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ATSC_EAC3;
                } else {
                    stream_type = AP4_MPEG2_STREAM_TYPE_ATSC_EAC3;
                }
                stream_id   = AP4_MPEG2_TS_STREAM_ID_PRIVATE_STREAM_1;
            } else {
                fprintf(stderr, "ERROR: audio codec not supported\n");
                return 1;
            }
            if (stream_type == AP4_MPEG2_STREAM_TYPE_ATSC_EAC3) {
                // E-AC-3 descriptor
                unsigned int number_of_channels = 0;
                AP4_String track_language;
                AP4_Dec3Atom* dec3 = AP4_DYNAMIC_CAST(AP4_Dec3Atom, sample_description->GetDetails().GetChild(AP4_ATOM_TYPE_DEC3));
                AP4_BitWriter bits(8);
                bits.Write(0xCC, 8);
                bits.Write(0x06, 8);    // fixed value
                bits.Write(0xC0, 8);    // reserved, bsid_flag, mainid_flag, asvc_flag, mixinfoexists, substream1_flag, substream2_flag and substream3_flag
                bits.Write(24, 5);      // reserved, full_service_flag and service_type
                if (dec3->GetSubStreams()[0].acmod == 0) {
                    number_of_channels = 1;
                } else if (dec3->GetSubStreams()[0].acmod == 1) {
                    number_of_channels = 0;
                } else if (dec3->GetSubStreams()[0].acmod == 2) {
                    number_of_channels = 2;
                } else {
                    number_of_channels = 4;
                }
                if (dec3->GetSubStreams()[0].num_dep_sub > 0) {
                    number_of_channels = 5;
                }
                bits.Write(number_of_channels, 3);              // number_of_channels
                bits.Write(4, 3);                               // language_flag, language_flag_2, reserved
                bits.Write(dec3->GetSubStreams()[0].bsid, 5);   // bsid
                track_language = audio_track->GetTrackLanguage();
                if (track_language.GetLength() == 3) {
                    bits.Write(track_language.GetChars()[0], 8);
                    bits.Write(track_language.GetChars()[1], 8);
                    bits.Write(track_language.GetChars()[2], 8);
                } else {
                    bits.Write(0x75, 8);
                    bits.Write(0x6E, 8);
                    bits.Write(0x64, 8);
                }
                 // setup the audio stream
                result = ts_writer->SetAudioStream(audio_track->GetMediaTimeScale(),
                                                   stream_type,
                                                   stream_id,
                                                   audio_stream,
                                                   Options.audio_pid,
                                                   bits.GetData(), 8,
                                                   Options.pcr_offset);
            } else {
            // setup the audio stream
            result = ts_writer->SetAudioStream(audio_track->GetMediaTimeScale(),
                                               stream_type,
                                               stream_id,
                                               audio_stream,
                                               Options.audio_pid,
                                               NULL, 0,
                                               Options.pcr_offset);
            }
            if (AP4_FAILED(result)) {
                fprintf(stderr, "could not create audio stream (%d)\n", result);
                goto end;
            }
        }

        // add the video stream
        if (video_track) {
            sample_description = video_track->GetSampleDescription(0);
            if (sample_description == NULL) {
                fprintf(stderr, "ERROR: unable to parse video sample description\n");
                goto end;
            }

            // decide on the stream type
            unsigned int stream_type = 0;
            unsigned int stream_id   = AP4_MPEG2_TS_DEFAULT_STREAM_ID_VIDEO;
            if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC1 ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC2 ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC3 ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC4 ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVAV ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVA1) {
                if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                    stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_AVC;
                    AP4_AvcSampleDescription* avc_desc = AP4_DYNAMIC_CAST(AP4_AvcSampleDescription, sample_description);
                    if (avc_desc == NULL) {
                        fprintf(stderr, "ERROR: not a proper AVC track\n");
                        return 1;
                    }
                    nalu_length_size = avc_desc->GetNaluLengthSize();
                } else {
                    stream_type = AP4_MPEG2_STREAM_TYPE_AVC;
                }
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_HEV1 ||
                       sample_description->GetFormat() == AP4_SAMPLE_FORMAT_HVC1 ||
                       sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVHE ||
                       sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVH1) {
                stream_type = AP4_MPEG2_STREAM_TYPE_HEVC;
            } else {
                fprintf(stderr, "ERROR: video codec not supported\n");
                return 1;
            }
            if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                if (stream_type != AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_AVC) {
                    fprintf(stderr, "ERROR: AES-SAMPLE encryption can only be used with H.264 video\n");
                    return 1;
                }
            }

            // setup the video stream
            result = ts_writer->SetVideoStream(video_track->GetMediaTimeScale(),
                                               stream_type,
                                               stream_id,
                                               video_stream,
                                               Options.video_pid,
                                               NULL, 0,
                                               Options.pcr_offset);
            if (AP4_FAILED(result)) {
                fprintf(stderr, "could not create video stream (%d)\n", result);
                goto end;
            }
        }
    }

    result = WriteSamples(ts_writer, packed_writer,
                          audio_track, audio_reader, audio_stream,
                          video_track, video_reader, video_stream,
                          Options.segment_duration_threshold,
                          nalu_length_size);
    if (AP4_FAILED(result)) {
        fprintf(stderr, "ERROR: failed to write samples (%d)\n", result);
    }

    if (Options.show_info) {
        double average_segment_bitrate = 0.0;
        if (Stats.segments_total_duration != 0.0) {
            average_segment_bitrate = 8.0*(double)Stats.segments_total_size/Stats.segments_total_duration;
        }
        double average_iframe_bitrate = 0.0;
        if (Stats.segments_total_duration != 0.0) {
            average_iframe_bitrate = 8.0*(double)Stats.iframes_total_size/Stats.segments_total_duration;
        }

        double frame_rate = 0.0;
        if (video_track && (Stats.segments_total_duration != 0.0)) {
            double sample_count = (double)video_track->GetSampleCount();
            double media_duration = (double)video_track->GetMediaDuration();
            double timescale = (double)video_track->GetMediaTimeScale();
            if (media_duration > 0.0) {
                frame_rate = sample_count/(media_duration/timescale);
            }
        }

        printf(
            "{\n"
        );
        printf(
            "  \"stats\": {\n"
            "    \"duration\": %f,\n"
            "    \"avg_segment_bitrate\": %f,\n"
            "    \"max_segment_bitrate\": %f,\n"
            "    \"avg_iframe_bitrate\": %f,\n"
            "    \"max_iframe_bitrate\": %f,\n"
            "    \"frame_rate\": %f\n"
            "  }",
            (double)movie->GetDurationMs()/1000.0,
            average_segment_bitrate,
            Stats.max_segment_bitrate,
            average_iframe_bitrate,
            Stats.max_iframe_bitrate,
            frame_rate
        );
        if (audio_track) {
            AP4_String codec;
            AP4_SampleDescription* sdesc = audio_track->GetSampleDescription(0);
            if (sdesc) {
                sdesc->GetCodecString(codec);
            }
            printf(
                ",\n"
                "  \"audio\": {\n"
                "    \"codec\": \"%s\"\n"
                "  }",
                codec.GetChars()
            );
        }
        if (video_track) {
            AP4_String codec;
            AP4_UI16 width = (AP4_UI16)(video_track->GetWidth()/65536.0);
            AP4_UI16 height = (AP4_UI16)(video_track->GetHeight()/65536.0);
            AP4_SampleDescription* sdesc = video_track->GetSampleDescription(0);
            if (sdesc) {
                sdesc->GetCodecString(codec);
                AP4_VideoSampleDescription* video_desc = AP4_DYNAMIC_CAST(AP4_VideoSampleDescription, sdesc);
                if (video_desc) {
                    width = video_desc->GetWidth();
                    height = video_desc->GetHeight();
                }
            }
            printf(
                ",\n"
                "  \"video\": {\n"
                "    \"codec\": \"%s\",\n"
                "    \"width\": %d,\n"
                "    \"height\": %d\n"
                "  }",
                codec.GetChars(),
                width,
                height
            );
        }
        printf(
            "\n"
            "}\n"
        );
    }

end:
    delete ts_writer;
    delete packed_writer;
    delete input_file;
    input->Release();
    delete linear_reader;
    delete audio_reader;
    delete video_reader;

    return result == AP4_SUCCESS?0:1;
}

fix #691


fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume, mrb_bool vmexec)
{
  struct mrb_context *c = fiber_check(mrb, self);
  struct mrb_context *old_c = mrb->c;
  enum mrb_fiber_state status;
  mrb_value value;

  fiber_check_cfunc(mrb, c);
  status = c->status;
  switch (status) {
  case MRB_FIBER_TRANSFERRED:
    if (resume) {
      mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");
    }
    break;
  case MRB_FIBER_RUNNING:
  case MRB_FIBER_RESUMED:
    mrb_raise(mrb, E_FIBER_ERROR, "double resume");
    break;
  case MRB_FIBER_TERMINATED:
    mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");
    break;
  default:
    break;
  }
  old_c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
  c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);
  fiber_switch_context(mrb, c);
  if (status == MRB_FIBER_CREATED) {
    mrb_value *b, *e;

    if (!c->ci->proc) {
      mrb_raise(mrb, E_FIBER_ERROR, "double resume (current)");
    }
    mrb_stack_extend(mrb, len+2); /* for receiver and (optional) block */
    b = c->stbase+1;
    e = b + len;
    while (b<e) {
      *b++ = *a++;
    }
    if (vmexec) {
      c->ci--;                    /* pop dummy callinfo */
    }
    c->cibase->n = len;
    value = c->stbase[0] = MRB_PROC_ENV(c->cibase->proc)->stack[0];
  }
  else {
    value = fiber_result(mrb, a, len);
    if (vmexec) {
      c->ci[1].stack[0] = value;
    }
  }

  if (vmexec) {
    c->vmexec = TRUE;
    value = mrb_vm_exec(mrb, c->ci->proc, c->ci->pc);
    mrb->c = old_c;
  }
  else {
    MARK_CONTEXT_MODIFY(c);
  }
  return value;
}

fiber.c: should pack 15+ arguments in an array.


fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume, mrb_bool vmexec)
{
  struct mrb_context *c = fiber_check(mrb, self);
  struct mrb_context *old_c = mrb->c;
  enum mrb_fiber_state status;
  mrb_value value;

  fiber_check_cfunc(mrb, c);
  status = c->status;
  switch (status) {
  case MRB_FIBER_TRANSFERRED:
    if (resume) {
      mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");
    }
    break;
  case MRB_FIBER_RUNNING:
  case MRB_FIBER_RESUMED:
    mrb_raise(mrb, E_FIBER_ERROR, "double resume");
    break;
  case MRB_FIBER_TERMINATED:
    mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");
    break;
  default:
    break;
  }
  old_c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
  c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);
  fiber_switch_context(mrb, c);
  if (status == MRB_FIBER_CREATED) {
    mrb_value *b, *e;

    if (!c->ci->proc) {
      mrb_raise(mrb, E_FIBER_ERROR, "double resume (current)");
    }
    if (vmexec) {
      c->ci--;                    /* pop dummy callinfo */
    }
    if (len >= 15) {
      mrb_stack_extend(mrb, 3);   /* for receiver, args and (optional) block */
      c->stbase[1] = mrb_ary_new_from_values(mrb, len, a);
      len = 15;
    }
    else {
      mrb_stack_extend(mrb, len+2); /* for receiver and (optional) block */
      b = c->stbase+1;
      e = b + len;
      while (b<e) {
        *b++ = *a++;
      }
    }
    c->cibase->n = len;
    value = c->stbase[0] = MRB_PROC_ENV(c->cibase->proc)->stack[0];
  }
  else {
    value = fiber_result(mrb, a, len);
    if (vmexec) {
      c->ci[1].stack[0] = value;
    }
  }

  if (vmexec) {
    c->vmexec = TRUE;
    value = mrb_vm_exec(mrb, c->ci->proc, c->ci->pc);
    mrb->c = old_c;
  }
  else {
    MARK_CONTEXT_MODIFY(c);
  }
  return value;
}


MOBI_RET mobi_decode_infl(unsigned char *decoded, int *decoded_size, const unsigned char *rule) {
    int pos = *decoded_size;
    char mod = 'i';
    char dir = '<';
    char olddir;
    unsigned char c;
    while ((c = *rule++)) {
        if (c <= 4) {
            mod = (c <= 2) ? 'i' : 'd'; /* insert, delete */
            olddir = dir;
            dir = (c & 2) ? '<' : '>'; /* left, right */
            if (olddir != dir && olddir) {
                pos = (c & 2) ? *decoded_size : 0;
            }
        }
        else if (c > 10 && c < 20) {
            if (dir == '>') {
                pos = *decoded_size;
            }
            pos -= c - 10;
            dir = 0;
            if (pos < 0 || pos > *decoded_size) {
                debug_print("Position setting failed (%s)\n", decoded);
                return MOBI_DATA_CORRUPT;
            }
        }
        else {
            if (mod == 'i') {
                const unsigned char *s = decoded + pos;
                unsigned char *d = decoded + pos + 1;
                const int l = *decoded_size - pos;
                if (l < 0 || d + l > decoded + INDX_INFLBUF_SIZEMAX) {
                    debug_print("Out of buffer in %s at pos: %i\n", decoded, pos);
                    return MOBI_DATA_CORRUPT;
                }
                memmove(d, s, (size_t) l);
                decoded[pos] = c;
                (*decoded_size)++;
                if (dir == '>') { pos++; }
            } else {
                if (dir == '<') { pos--; }
                const unsigned char *s = decoded + pos + 1;
                unsigned char *d = decoded + pos;
                const int l = *decoded_size - pos;
                if (l < 0 || d + l > decoded + INDX_INFLBUF_SIZEMAX) {
                    debug_print("Out of buffer in %s at pos: %i\n", decoded, pos);
                    return MOBI_DATA_CORRUPT;
                }
                if (decoded[pos] != c) {
                    debug_print("Character mismatch in %s at pos: %i (%c != %c)\n", decoded, pos, decoded[pos], c);
                    return MOBI_DATA_CORRUPT;
                }
                memmove(d, s, (size_t) l);
                (*decoded_size)--;
            }
        }
    }
    return MOBI_SUCCESS;
}

Fix wrong boundary checks in inflections parser resulting in stack buffer over-read with corrupt input


MOBI_RET mobi_decode_infl(unsigned char *decoded, int *decoded_size, const unsigned char *rule) {
    int pos = *decoded_size;
    char mod = 'i';
    char dir = '<';
    char olddir;
    unsigned char c;
    while ((c = *rule++)) {
        if (c <= 4) {
            mod = (c <= 2) ? 'i' : 'd'; /* insert, delete */
            olddir = dir;
            dir = (c & 2) ? '<' : '>'; /* left, right */
            if (olddir != dir && olddir) {
                pos = (c & 2) ? *decoded_size : 0;
            }
        }
        else if (c > 10 && c < 20) {
            if (dir == '>') {
                pos = *decoded_size;
            }
            pos -= c - 10;
            dir = 0;
        }
        else {
            if (mod == 'i') {
                const unsigned char *s = decoded + pos;
                unsigned char *d = decoded + pos + 1;
                const int l = *decoded_size - pos;
                if (pos < 0 || l < 0 || d + l > decoded + INDX_INFLBUF_SIZEMAX) {
                    debug_print("Out of buffer in %s at pos: %i\n", decoded, pos);
                    return MOBI_DATA_CORRUPT;
                }
                memmove(d, s, (size_t) l);
                decoded[pos] = c;
                (*decoded_size)++;
                if (dir == '>') { pos++; }
            } else {
                if (dir == '<') { pos--; }
                const unsigned char *s = decoded + pos + 1;
                unsigned char *d = decoded + pos;
                const int l = *decoded_size - pos;
                if (pos < 0 || l < 0 || s + l > decoded + INDX_INFLBUF_SIZEMAX) {
                    debug_print("Out of buffer in %s at pos: %i\n", decoded, pos);
                    return MOBI_DATA_CORRUPT;
                }
                if (decoded[pos] != c) {
                    debug_print("Character mismatch in %s at pos: %i (%c != %c)\n", decoded, pos, decoded[pos], c);
                    return MOBI_DATA_CORRUPT;
                }
                memmove(d, s, (size_t) l);
                (*decoded_size)--;
            }
        }
    }
    return MOBI_SUCCESS;
}


get_text_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
/* This version is for reading text-format PGM files with any maxval */
{
  ppm_source_ptr source = (ppm_source_ptr) sinfo;
  FILE * infile = source->pub.input_file;
  register JSAMPROW ptr;
  register JSAMPLE *rescale = source->rescale;
  JDIMENSION col;

  ptr = source->pub.buffer[0];
  for (col = cinfo->image_width; col > 0; col--) {
    *ptr++ = rescale[read_pbm_integer(cinfo, infile)];
  }
  return 1;
}

Check range of integer values in PPM text file

Add checks to ensure values are within the specified range.

Fixes mozilla/mozjpeg#141, closes #8


get_text_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
/* This version is for reading text-format PGM files with any maxval */
{
  ppm_source_ptr source = (ppm_source_ptr) sinfo;
  FILE * infile = source->pub.input_file;
  register JSAMPROW ptr;
  register JSAMPLE *rescale = source->rescale;
  JDIMENSION col;
  int maxval = source->maxval;

  ptr = source->pub.buffer[0];
  for (col = cinfo->image_width; col > 0; col--) {
    *ptr++ = rescale[read_pbm_integer(cinfo, infile, maxval)];
  }
  return 1;
}


ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
{
    linenr_T	count;
    char_u	*p;

    count = line2 - line1 + 1;
    if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
    {
	curbuf->b_op_start.lnum = n + 1;
	curbuf->b_op_end.lnum = n + count;
	curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
    }

    /*
     * there are three situations:
     * 1. destination is above line1
     * 2. destination is between line1 and line2
     * 3. destination is below line2
     *
     * n = destination (when starting)
     * curwin->w_cursor.lnum = destination (while copying)
     * line1 = start of source (while copying)
     * line2 = end of source (while copying)
     */
    if (u_save(n, n + 1) == FAIL)
	return;

    curwin->w_cursor.lnum = n;
    while (line1 <= line2)
    {
	// need to use vim_strsave() because the line will be unlocked within
	// ml_append()
	p = vim_strsave(ml_get(line1));
	if (p != NULL)
	{
	    ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, FALSE);
	    vim_free(p);
	}
	// situation 2: skip already copied lines
	if (line1 == n)
	    line1 = curwin->w_cursor.lnum;
	++line1;
	if (curwin->w_cursor.lnum < line1)
	    ++line1;
	if (curwin->w_cursor.lnum < line2)
	    ++line2;
	++curwin->w_cursor.lnum;
    }

    appended_lines_mark(n, count);

    msgmore((long)count);
}

patch 8.2.4215: illegal memory access when copying lines in Visual mode

Problem:    Illegal memory access when copying lines in Visual mode.
Solution:   Adjust the Visual position after copying lines.


ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
{
    linenr_T	count;
    char_u	*p;

    count = line2 - line1 + 1;
    if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
    {
	curbuf->b_op_start.lnum = n + 1;
	curbuf->b_op_end.lnum = n + count;
	curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
    }

    /*
     * there are three situations:
     * 1. destination is above line1
     * 2. destination is between line1 and line2
     * 3. destination is below line2
     *
     * n = destination (when starting)
     * curwin->w_cursor.lnum = destination (while copying)
     * line1 = start of source (while copying)
     * line2 = end of source (while copying)
     */
    if (u_save(n, n + 1) == FAIL)
	return;

    curwin->w_cursor.lnum = n;
    while (line1 <= line2)
    {
	// need to use vim_strsave() because the line will be unlocked within
	// ml_append()
	p = vim_strsave(ml_get(line1));
	if (p != NULL)
	{
	    ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, FALSE);
	    vim_free(p);
	}
	// situation 2: skip already copied lines
	if (line1 == n)
	    line1 = curwin->w_cursor.lnum;
	++line1;
	if (curwin->w_cursor.lnum < line1)
	    ++line1;
	if (curwin->w_cursor.lnum < line2)
	    ++line2;
	++curwin->w_cursor.lnum;
    }

    appended_lines_mark(n, count);
    if (VIsual_active)
	check_pos(curbuf, &VIsual);

    msgmore((long)count);
}


int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
		 struct xfrm_migrate *m, int num_migrate,
		 struct xfrm_kmaddress *k, struct net *net,
		 struct xfrm_encap_tmpl *encap)
{
	int i, err, nx_cur = 0, nx_new = 0;
	struct xfrm_policy *pol = NULL;
	struct xfrm_state *x, *xc;
	struct xfrm_state *x_cur[XFRM_MAX_DEPTH];
	struct xfrm_state *x_new[XFRM_MAX_DEPTH];
	struct xfrm_migrate *mp;

	if ((err = xfrm_migrate_check(m, num_migrate)) < 0)
		goto out;

	/* Stage 1 - find policy */
	if ((pol = xfrm_migrate_policy_find(sel, dir, type, net)) == NULL) {
		err = -ENOENT;
		goto out;
	}

	/* Stage 2 - find and update state(s) */
	for (i = 0, mp = m; i < num_migrate; i++, mp++) {
		if ((x = xfrm_migrate_state_find(mp, net))) {
			x_cur[nx_cur] = x;
			nx_cur++;
			xc = xfrm_state_migrate(x, mp, encap);
			if (xc) {
				x_new[nx_new] = xc;
				nx_new++;
			} else {
				err = -ENODATA;
				goto restore_state;
			}
		}
	}

	/* Stage 3 - update policy */
	if ((err = xfrm_policy_migrate(pol, m, num_migrate)) < 0)
		goto restore_state;

	/* Stage 4 - delete old state(s) */
	if (nx_cur) {
		xfrm_states_put(x_cur, nx_cur);
		xfrm_states_delete(x_cur, nx_cur);
	}

	/* Stage 5 - announce */
	km_migrate(sel, dir, type, m, num_migrate, k, encap);

	xfrm_pol_put(pol);

	return 0;
out:
	return err;

restore_state:
	if (pol)
		xfrm_pol_put(pol);
	if (nx_cur)
		xfrm_states_put(x_cur, nx_cur);
	if (nx_new)
		xfrm_states_delete(x_new, nx_new);

	return err;
}

xfrm: policy: check policy direction value

The 'dir' parameter in xfrm_migrate() is a user-controlled byte which is used
as an array index. This can lead to an out-of-bound access, kernel lockup and
DoS. Add a check for the 'dir' value.

This fixes CVE-2017-11600.

References: https://bugzilla.redhat.com/show_bug.cgi?id=1474928
Fixes: 80c9abaabf42 ("[XFRM]: Extension for dynamic update of endpoint address(es)")
Cc: <stable@vger.kernel.org> # v2.6.21-rc1
Reported-by: "bo Zhang" <zhangbo5891001@gmail.com>
Signed-off-by: Vladis Dronov <vdronov@redhat.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>


int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
		 struct xfrm_migrate *m, int num_migrate,
		 struct xfrm_kmaddress *k, struct net *net,
		 struct xfrm_encap_tmpl *encap)
{
	int i, err, nx_cur = 0, nx_new = 0;
	struct xfrm_policy *pol = NULL;
	struct xfrm_state *x, *xc;
	struct xfrm_state *x_cur[XFRM_MAX_DEPTH];
	struct xfrm_state *x_new[XFRM_MAX_DEPTH];
	struct xfrm_migrate *mp;

	/* Stage 0 - sanity checks */
	if ((err = xfrm_migrate_check(m, num_migrate)) < 0)
		goto out;

	if (dir >= XFRM_POLICY_MAX) {
		err = -EINVAL;
		goto out;
	}

	/* Stage 1 - find policy */
	if ((pol = xfrm_migrate_policy_find(sel, dir, type, net)) == NULL) {
		err = -ENOENT;
		goto out;
	}

	/* Stage 2 - find and update state(s) */
	for (i = 0, mp = m; i < num_migrate; i++, mp++) {
		if ((x = xfrm_migrate_state_find(mp, net))) {
			x_cur[nx_cur] = x;
			nx_cur++;
			xc = xfrm_state_migrate(x, mp, encap);
			if (xc) {
				x_new[nx_new] = xc;
				nx_new++;
			} else {
				err = -ENODATA;
				goto restore_state;
			}
		}
	}

	/* Stage 3 - update policy */
	if ((err = xfrm_policy_migrate(pol, m, num_migrate)) < 0)
		goto restore_state;

	/* Stage 4 - delete old state(s) */
	if (nx_cur) {
		xfrm_states_put(x_cur, nx_cur);
		xfrm_states_delete(x_cur, nx_cur);
	}

	/* Stage 5 - announce */
	km_migrate(sel, dir, type, m, num_migrate, k, encap);

	xfrm_pol_put(pol);

	return 0;
out:
	return err;

restore_state:
	if (pol)
		xfrm_pol_put(pol);
	if (nx_cur)
		xfrm_states_put(x_cur, nx_cur);
	if (nx_new)
		xfrm_states_delete(x_new, nx_new);

	return err;
}


mp_sint32 LoaderS3M::load(XMFileBase& f, XModule* module)
{
	module->cleanUp();

	// this will make code much easier to read
	TXMHeader*		header = &module->header;
	TXMInstrument*	instr  = module->instr;
	TXMSample*		smp	   = module->smp;
	TXMPattern*		phead  = module->phead;

	// we're already out of memory here
	if (!phead || !instr || !smp)
		return MP_OUT_OF_MEMORY;

	f.read(&header->name,1,28);
	header->whythis1a = f.readByte();

	if (f.readByte() != 16)
		return MP_LOADER_FAILED;	// no ST3 module

	f.readByte(); // skip something
	f.readByte(); // skip something

	header->ordnum = f.readWord(); // number of positions in order list (songlength)

	mp_ubyte* orders = new mp_ubyte[header->ordnum];
	if (orders == NULL)
		return MP_OUT_OF_MEMORY;

	header->insnum = f.readWord(); // number of instruments
	header->patnum = f.readWord(); // number of patterns

	mp_sint32 flags = f.readWord(); // st3 flags

	mp_sint32 Cvt = f.readWord();

	header->flags = XModule::MODULE_ST3NEWINSTRUMENT | XModule::MODULE_ST3DUALCOMMANDS;

	if (Cvt == 0x1300 || (flags & 64))
		header->flags |= module->MODULE_OLDS3MVOLSLIDES;

	header->flags |= module->MODULE_ST3NOTECUT;

	/*mp_uword Ffi = */f.readWord();

	f.read(header->sig,1,4);

	header->mainvol = module->vol64to255(f.readByte()); // initial main volume

	header->tempo = f.readByte(); // tempo

	header->speed = f.readByte(); // speed

	f.readByte(); // global volume? skipped...

	f.readByte(); // ignore GUS click removal

	/*mp_ubyte dp = */f.readByte();

	f.readDword();	// skip something
	f.readDword();	// skip something
	f.readWord();	// skip some more...

	mp_ubyte channelSettings[32];
	f.read(channelSettings,1,32);

	mp_sint32 numChannels = 0;

	for (numChannels = 0; numChannels < 32; numChannels++)
		if (channelSettings[numChannels] == 255)
			break;

	header->channum = numChannels; // number of channels

	f.read(orders,1,header->ordnum);

	mp_sint32 j = 0, i = 0;
	for (i = 0; i < header->ordnum; i++)
	{
		if (orders[i] == 255)
			break;

		header->ord[j++] = orders[i];
	}

	header->ordnum = j; // final songlength

	delete[] orders;

	mp_uword* insParaPtrs = new mp_uword[header->insnum];

	if (insParaPtrs == NULL)
		return MP_OUT_OF_MEMORY;

	f.readWords(insParaPtrs,header->insnum);

	mp_uword* patParaPtrs = new mp_uword[header->patnum];

	if (patParaPtrs == NULL)
	{
		delete[] insParaPtrs;
		return MP_OUT_OF_MEMORY;
	}

	f.readWords(patParaPtrs,header->patnum);

	//for (i = 0; i < header->insnum; i++)
	//{
	//	printf("%x\n",insParaPtrs[i]*16);
	//}

	//////////////////////
	// read instruments //
	//////////////////////
	mp_uint32* samplePtrs = new mp_uint32[header->insnum];
	if (samplePtrs == NULL)
	{
		delete[] insParaPtrs;
		delete[] patParaPtrs;
		return MP_OUT_OF_MEMORY;
	}

	memset(samplePtrs,0,sizeof(mp_uint32)*header->insnum);

	mp_sint32 s = 0;
	for (i = 0; i < header->insnum; i++)
	{
		mp_uint32 insOffs = insParaPtrs[i]*16;

		if (insOffs)
		{
			f.seekWithBaseOffset(insOffs);

			// We can only read that if it's a sample
			mp_ubyte type = f.readByte();

			if (type == 1)
			{
				f.read(smp[s].name,1,12);	// read dos filename

				mp_ubyte bOffs = f.readByte();
				mp_uword wOffs = f.readWord();

				// stupid fileoffsets
				samplePtrs[i] = (((mp_uint32)bOffs<<16)+(mp_uint32)wOffs)*16;

				smp[s].flags = 1;
				smp[s].pan = 0x80;

				smp[s].samplen = f.readDword();
				smp[s].loopstart = f.readDword();
				mp_sint32 looplen = ((mp_sint32)f.readDword() - (mp_sint32)smp[s].loopstart);
				if (looplen < 0)
					looplen = 0;
				smp[s].looplen = looplen;

				smp[s].vol = module->vol64to255(f.readByte());

				f.readByte(); // skip something

				smp[s].res = f.readByte() == 0x04 ? 0xAD : 0; // packing

				mp_ubyte flags = f.readByte();

				// looping
				if (flags & 1)
				{
					smp[s].type = 1;
				}

				// 16 bit sample
				if (flags & 4)
				{
					smp[s].type |= 16;
					smp[s].samplen >>= 1;
					smp[s].loopstart >>= 1;
					smp[s].looplen >>= 1;
				}

				mp_uint32 c4spd = f.readDword();

				XModule::convertc4spd(c4spd,&smp[s].finetune,&smp[s].relnote);

#ifdef VERBOSE
				printf("%i, %i\n",c4spd,module->getc4spd(smp[s].relnote,smp[s].finetune));
#endif

				f.readDword(); // skip something

				f.readDword(); // skip two internal words

				f.readDword(); // skip internal dword

				f.read(instr[i].name,1,28); // instrument name

				f.readDword(); // skip signature

				if (samplePtrs[i] && smp[s].samplen)
				{
					instr[i].samp=1;
					for (j=0;j<120;j++)
						instr[i].snum[j] = s;
					s++;
				}
			}
			else if (type == 0)
			{
				samplePtrs[i] = 0;

				mp_ubyte buffer[12];
				f.read(buffer,1,12);	// read dos filename

				f.readByte();
				f.readWord();

				f.readDword();
				f.readDword();
				f.readDword();
				f.readByte();
				f.readByte(); // skip something
				f.readByte(); // skip packing

				f.readByte();

				f.readDword();

				f.readDword(); // skip something

				f.readDword(); // skip two internal words

				f.readDword(); // skip internal dword

				f.read(instr[i].name,1,28); // instrument name

				f.readDword(); // skip signature
			}
			else
			{
				samplePtrs[i] = 0;
			}

		}

	}

	//////////////////////
	// read patterns	//
	//////////////////////
	mp_ubyte* pattern = new mp_ubyte[64*32*5];
	if (pattern == NULL)
	{
		delete[] insParaPtrs;
		delete[] patParaPtrs;
		delete[] samplePtrs;
		return MP_OUT_OF_MEMORY;
	}

	mp_uint32 songMaxChannels = 1;

	for (i = 0; i < header->patnum; i++)
	{
		for (j = 0; j < 32*64; j++)
		{
			pattern[j*5] = 0xFF;
			pattern[j*5+1] = 0;
			pattern[j*5+2] = 0xFF;
			pattern[j*5+3] = 0xFF;
			pattern[j*5+4] = 0;
		}

		mp_uint32 patOffs = patParaPtrs[i]*16;

		mp_uint32 maxChannels = 1;

		if (patOffs)
		{
			f.seekWithBaseOffset(patOffs);

			mp_uint32 size = f.readWord();

			if (size > 2)
			{
				size-=2;

				mp_ubyte* packed = new mp_ubyte[size+5];
				if (packed == NULL)
				{
					delete[] insParaPtrs;
					delete[] patParaPtrs;
					delete[] samplePtrs;
					delete[] pattern;
					return MP_OUT_OF_MEMORY;
				}

				memset(packed, 0, size);
				f.read(packed, 1, size);

				mp_uint32 index = 0;
				mp_uint32 row = 0;

				while (index<size)
				{

					mp_ubyte pi = safeRead(packed, index, size);

					if (pi == 0)
					{
						row++;
						// one more safety net for incorrectly saved pattern sizes
						if (row >= 64)
						{
							int i = 0;
							i++;
							i--;
							break;
						}
						continue;
					}

					mp_uint32 chn = pi&31;

					if (chn>maxChannels && (pi & (32+64+128)))
					{
						maxChannels = chn;
					}

					mp_ubyte* slot = pattern+(row*32*5)+chn*5;

					if (pi & 32)
					{
						slot[0] = safeRead(packed, index, size, 0xFF);
						slot[1] = safeRead(packed, index, size);
					}
					if (pi & 64)
					{
						slot[2] = safeRead(packed, index, size, 0xFF);
					}
					if (pi & 128)
					{
						slot[3] = safeRead(packed, index, size, 0xFF);
						slot[4] = safeRead(packed, index, size);
					}

				}

				maxChannels++;

				if (maxChannels > header->channum)
					maxChannels = header->channum;

				delete[] packed;
			}

			if (maxChannels > songMaxChannels)
				songMaxChannels = maxChannels;

		}

		convertS3MPattern(&phead[i], pattern, maxChannels, i);

	}

	if (header->channum > songMaxChannels)
		header->channum = songMaxChannels;

	delete[] pattern;
	delete[] insParaPtrs;
	delete[] patParaPtrs;

	s = 0;
	for (i = 0; i < header->insnum; i++)
	{
		mp_uint32 smpOffs = samplePtrs[i];

		if (smpOffs)
		{
			f.seekWithBaseOffset(smpOffs);

			if (!smp[s].samplen)
				continue;

			bool adpcm = (smp[s].res == 0xAD);

			mp_sint32 result = module->loadModuleSample(f, s,
										  adpcm ? XModule::ST_PACKING_ADPCM : XModule::ST_UNSIGNED,
										  adpcm ? (XModule::ST_16BIT | XModule::ST_PACKING_ADPCM) : (XModule::ST_16BIT | XModule::ST_UNSIGNED));
			if (result != MP_OK)
			{
				delete[] samplePtrs;
				return result;
			}

			if (adpcm)
				// no longer needed
				smp[s].res = 0;

			s++;

		}

	}

	delete[] samplePtrs;

	header->smpnum = s;

	strcpy(header->tracker,"Screamtracker 3");

	module->setDefaultPanning();

	module->postProcessSamples();

	return MP_OK;
}

Fix #184: Heap overflow in S3M loader


mp_sint32 LoaderS3M::load(XMFileBase& f, XModule* module)
{
	module->cleanUp();

	// this will make code much easier to read
	TXMHeader*		header = &module->header;
	TXMInstrument*	instr  = module->instr;
	TXMSample*		smp	   = module->smp;
	TXMPattern*		phead  = module->phead;

	// we're already out of memory here
	if (!phead || !instr || !smp)
		return MP_OUT_OF_MEMORY;

	f.read(&header->name,1,28);
	header->whythis1a = f.readByte();

	if (f.readByte() != 16)
		return MP_LOADER_FAILED;	// no ST3 module

	f.readByte(); // skip something
	f.readByte(); // skip something

	header->ordnum = f.readWord(); // number of positions in order list (songlength)

	mp_ubyte* orders = new mp_ubyte[header->ordnum];
	if (orders == NULL)
		return MP_OUT_OF_MEMORY;

	header->insnum = f.readWord(); // number of instruments
	if (header->insnum > MP_MAXINS)
		return MP_LOADER_FAILED;
	header->patnum = f.readWord(); // number of patterns
	if (header->patnum > 256)
		return MP_LOADER_FAILED;

	mp_sint32 flags = f.readWord(); // st3 flags

	mp_sint32 Cvt = f.readWord();

	header->flags = XModule::MODULE_ST3NEWINSTRUMENT | XModule::MODULE_ST3DUALCOMMANDS;

	if (Cvt == 0x1300 || (flags & 64))
		header->flags |= module->MODULE_OLDS3MVOLSLIDES;

	header->flags |= module->MODULE_ST3NOTECUT;

	/*mp_uword Ffi = */f.readWord();

	f.read(header->sig,1,4);

	header->mainvol = module->vol64to255(f.readByte()); // initial main volume

	header->tempo = f.readByte(); // tempo

	header->speed = f.readByte(); // speed

	f.readByte(); // global volume? skipped...

	f.readByte(); // ignore GUS click removal

	/*mp_ubyte dp = */f.readByte();

	f.readDword();	// skip something
	f.readDword();	// skip something
	f.readWord();	// skip some more...

	mp_ubyte channelSettings[32];
	f.read(channelSettings,1,32);

	mp_sint32 numChannels = 0;

	for (numChannels = 0; numChannels < 32; numChannels++)
		if (channelSettings[numChannels] == 255)
			break;

	header->channum = numChannels; // number of channels

	f.read(orders,1,header->ordnum);

	mp_sint32 j = 0, i = 0;
	for (i = 0; i < header->ordnum; i++)
	{
		if (orders[i] == 255)
			break;

		header->ord[j++] = orders[i];
	}

	header->ordnum = j; // final songlength

	delete[] orders;

	mp_uword* insParaPtrs = new mp_uword[header->insnum];

	if (insParaPtrs == NULL)
		return MP_OUT_OF_MEMORY;

	f.readWords(insParaPtrs,header->insnum);

	mp_uword* patParaPtrs = new mp_uword[header->patnum];

	if (patParaPtrs == NULL)
	{
		delete[] insParaPtrs;
		return MP_OUT_OF_MEMORY;
	}

	f.readWords(patParaPtrs,header->patnum);

	//for (i = 0; i < header->insnum; i++)
	//{
	//	printf("%x\n",insParaPtrs[i]*16);
	//}

	//////////////////////
	// read instruments //
	//////////////////////
	mp_uint32* samplePtrs = new mp_uint32[header->insnum];
	if (samplePtrs == NULL)
	{
		delete[] insParaPtrs;
		delete[] patParaPtrs;
		return MP_OUT_OF_MEMORY;
	}

	memset(samplePtrs,0,sizeof(mp_uint32)*header->insnum);

	mp_sint32 s = 0;
	for (i = 0; i < header->insnum; i++)
	{
		mp_uint32 insOffs = insParaPtrs[i]*16;

		if (insOffs)
		{
			f.seekWithBaseOffset(insOffs);

			// We can only read that if it's a sample
			mp_ubyte type = f.readByte();

			if (type == 1)
			{
				f.read(smp[s].name,1,12);	// read dos filename

				mp_ubyte bOffs = f.readByte();
				mp_uword wOffs = f.readWord();

				// stupid fileoffsets
				samplePtrs[i] = (((mp_uint32)bOffs<<16)+(mp_uint32)wOffs)*16;

				smp[s].flags = 1;
				smp[s].pan = 0x80;

				smp[s].samplen = f.readDword();
				smp[s].loopstart = f.readDword();
				mp_sint32 looplen = ((mp_sint32)f.readDword() - (mp_sint32)smp[s].loopstart);
				if (looplen < 0)
					looplen = 0;
				smp[s].looplen = looplen;

				smp[s].vol = module->vol64to255(f.readByte());

				f.readByte(); // skip something

				smp[s].res = f.readByte() == 0x04 ? 0xAD : 0; // packing

				mp_ubyte flags = f.readByte();

				// looping
				if (flags & 1)
				{
					smp[s].type = 1;
				}

				// 16 bit sample
				if (flags & 4)
				{
					smp[s].type |= 16;
					smp[s].samplen >>= 1;
					smp[s].loopstart >>= 1;
					smp[s].looplen >>= 1;
				}

				mp_uint32 c4spd = f.readDword();

				XModule::convertc4spd(c4spd,&smp[s].finetune,&smp[s].relnote);

#ifdef VERBOSE
				printf("%i, %i\n",c4spd,module->getc4spd(smp[s].relnote,smp[s].finetune));
#endif

				f.readDword(); // skip something

				f.readDword(); // skip two internal words

				f.readDword(); // skip internal dword

				f.read(instr[i].name,1,28); // instrument name

				f.readDword(); // skip signature

				if (samplePtrs[i] && smp[s].samplen)
				{
					instr[i].samp=1;
					for (j=0;j<120;j++)
						instr[i].snum[j] = s;
					s++;
				}
			}
			else if (type == 0)
			{
				samplePtrs[i] = 0;

				mp_ubyte buffer[12];
				f.read(buffer,1,12);	// read dos filename

				f.readByte();
				f.readWord();

				f.readDword();
				f.readDword();
				f.readDword();
				f.readByte();
				f.readByte(); // skip something
				f.readByte(); // skip packing

				f.readByte();

				f.readDword();

				f.readDword(); // skip something

				f.readDword(); // skip two internal words

				f.readDword(); // skip internal dword

				f.read(instr[i].name,1,28); // instrument name

				f.readDword(); // skip signature
			}
			else
			{
				samplePtrs[i] = 0;
			}

		}

	}

	//////////////////////
	// read patterns	//
	//////////////////////
	mp_ubyte* pattern = new mp_ubyte[64*32*5];
	if (pattern == NULL)
	{
		delete[] insParaPtrs;
		delete[] patParaPtrs;
		delete[] samplePtrs;
		return MP_OUT_OF_MEMORY;
	}

	mp_uint32 songMaxChannels = 1;

	for (i = 0; i < header->patnum; i++)
	{
		for (j = 0; j < 32*64; j++)
		{
			pattern[j*5] = 0xFF;
			pattern[j*5+1] = 0;
			pattern[j*5+2] = 0xFF;
			pattern[j*5+3] = 0xFF;
			pattern[j*5+4] = 0;
		}

		mp_uint32 patOffs = patParaPtrs[i]*16;

		mp_uint32 maxChannels = 1;

		if (patOffs)
		{
			f.seekWithBaseOffset(patOffs);

			mp_uint32 size = f.readWord();

			if (size > 2)
			{
				size-=2;

				mp_ubyte* packed = new mp_ubyte[size+5];
				if (packed == NULL)
				{
					delete[] insParaPtrs;
					delete[] patParaPtrs;
					delete[] samplePtrs;
					delete[] pattern;
					return MP_OUT_OF_MEMORY;
				}

				memset(packed, 0, size);
				f.read(packed, 1, size);

				mp_uint32 index = 0;
				mp_uint32 row = 0;

				while (index<size)
				{

					mp_ubyte pi = safeRead(packed, index, size);

					if (pi == 0)
					{
						row++;
						// one more safety net for incorrectly saved pattern sizes
						if (row >= 64)
						{
							int i = 0;
							i++;
							i--;
							break;
						}
						continue;
					}

					mp_uint32 chn = pi&31;

					if (chn>maxChannels && (pi & (32+64+128)))
					{
						maxChannels = chn;
					}

					mp_ubyte* slot = pattern+(row*32*5)+chn*5;

					if (pi & 32)
					{
						slot[0] = safeRead(packed, index, size, 0xFF);
						slot[1] = safeRead(packed, index, size);
					}
					if (pi & 64)
					{
						slot[2] = safeRead(packed, index, size, 0xFF);
					}
					if (pi & 128)
					{
						slot[3] = safeRead(packed, index, size, 0xFF);
						slot[4] = safeRead(packed, index, size);
					}

				}

				maxChannels++;

				if (maxChannels > header->channum)
					maxChannels = header->channum;

				delete[] packed;
			}

			if (maxChannels > songMaxChannels)
				songMaxChannels = maxChannels;

		}

		convertS3MPattern(&phead[i], pattern, maxChannels, i);

	}

	if (header->channum > songMaxChannels)
		header->channum = songMaxChannels;

	delete[] pattern;
	delete[] insParaPtrs;
	delete[] patParaPtrs;

	s = 0;
	for (i = 0; i < header->insnum; i++)
	{
		mp_uint32 smpOffs = samplePtrs[i];

		if (smpOffs)
		{
			f.seekWithBaseOffset(smpOffs);

			if (!smp[s].samplen)
				continue;

			bool adpcm = (smp[s].res == 0xAD);

			mp_sint32 result = module->loadModuleSample(f, s,
										  adpcm ? XModule::ST_PACKING_ADPCM : XModule::ST_UNSIGNED,
										  adpcm ? (XModule::ST_16BIT | XModule::ST_PACKING_ADPCM) : (XModule::ST_16BIT | XModule::ST_UNSIGNED));
			if (result != MP_OK)
			{
				delete[] samplePtrs;
				return result;
			}

			if (adpcm)
				// no longer needed
				smp[s].res = 0;

			s++;

		}

	}

	delete[] samplePtrs;

	header->smpnum = s;

	strcpy(header->tracker,"Screamtracker 3");

	module->setDefaultPanning();

	module->postProcessSamples();

	return MP_OK;
}

Fix #184: Heap overflow in S3M loader


int CLASS ljpeg_start (struct jhead *jh, int info_only)
{
  int c, tag, len;
  uchar data[0x10000];
  const uchar *dp;

  memset (jh, 0, sizeof *jh);
  jh->restart = INT_MAX;
  fread (data, 2, 1, ifp);
  if (data[1] != 0xd8) return 0;
  do {
    fread (data, 2, 2, ifp);
    tag =  data[0] << 8 | data[1];
    len = (data[2] << 8 | data[3]) - 2;
    if (tag <= 0xff00) return 0;
    fread (data, 1, len, ifp);
    switch (tag) {
      case 0xffc3:
	jh->sraw = ((data[7] >> 4) * (data[7] & 15) - 1) & 3;
      case 0xffc0:
	jh->bits = data[0];
	jh->high = data[1] << 8 | data[2];
	jh->wide = data[3] << 8 | data[4];
	jh->clrs = data[5] + jh->sraw;
	if (len == 9 && !dng_version) getc(ifp);
	break;
      case 0xffc4:
	if (info_only) break;
	for (dp = data; dp < data+len && (c = *dp++) < 4; )
	  jh->free[c] = jh->huff[c] = make_decoder_ref (&dp);
	break;
      case 0xffda:
	jh->psv = data[1+data[0]*2];
	jh->bits -= data[3+data[0]*2] & 15;
	break;
      case 0xffdd:
	jh->restart = data[0] << 8 | data[1];
    }
  } while (tag != 0xffda);
  if (info_only) return 1;
  if (jh->clrs > 6 || !jh->huff[0]) return 0;
  FORC(5) if (!jh->huff[c+1]) jh->huff[c+1] = jh->huff[c];
  if (jh->sraw) {
    FORC(4)        jh->huff[2+c] = jh->huff[1];
    FORC(jh->sraw) jh->huff[1+c] = jh->huff[0];
  }
  jh->row = (ushort *) calloc (jh->wide*jh->clrs, 4);
  merror (jh->row, "ljpeg_start()");
  return zero_after_ff = 1;
}

0.16.1: fix for dcraw ljpeg_start() vulnerability