int LZ4IO_compressFilename(char* input_filename, char* output_filename, int compressionLevel)
{
    int (*compressionFunction)(const char*, char*, int, int, int);
    unsigned long long filesize = 0;
    unsigned long long compressedfilesize = 0;
    unsigned int checkbits;
    char* in_buff;
    char* out_buff;
    char* headerBuffer;
    FILE* finput;
    FILE* foutput;
    clock_t start, end;
    int blockSize;
    size_t sizeCheck, header_size, readSize;
    void* streamChecksumState=NULL;

    // Branch out
    if (blockIndependence==0) return compress_file_blockDependency(input_filename, output_filename, compressionLevel);

    // Init
    start = clock();
    if ((displayLevel==2) && (compressionLevel>=3)) displayLevel=3;
    if (compressionLevel <= 3) compressionFunction = LZ4_compress_limitedOutput_local;
    else { compressionFunction = LZ4_compressHC2_limitedOutput; }
    get_fileHandle(input_filename, output_filename, &finput, &foutput);
    blockSize = LZ4S_GetBlockSize_FromBlockId (blockSizeId);

    // Allocate Memory
    in_buff  = (char*)malloc(blockSize);
    out_buff = (char*)malloc(blockSize+CACHELINE);
    headerBuffer = (char*)malloc(LZ4S_MAXHEADERSIZE);
    if (!in_buff || !out_buff || !(headerBuffer)) EXM_THROW(31, "Allocation error : not enough memory");
    if (streamChecksum) streamChecksumState = XXH32_init(LZ4S_CHECKSUM_SEED);

    // Write Archive Header
    *(unsigned int*)headerBuffer = LITTLE_ENDIAN_32(LZ4S_MAGICNUMBER);   // Magic Number, in Little Endian convention
    *(headerBuffer+4)  = (1 & _2BITS) << 6 ;                             // Version('01')
    *(headerBuffer+4) |= (blockIndependence & _1BIT) << 5;
    *(headerBuffer+4) |= (blockChecksum & _1BIT) << 4;
    *(headerBuffer+4) |= (streamChecksum & _1BIT) << 2;
    *(headerBuffer+5)  = (char)((blockSizeId & _3BITS) << 4);
    checkbits = XXH32((headerBuffer+4), 2, LZ4S_CHECKSUM_SEED);
    checkbits = LZ4S_GetCheckBits_FromXXH(checkbits);
    *(headerBuffer+6)  = (unsigned char) checkbits;
    header_size = 7;

    // Write header
    sizeCheck = fwrite(headerBuffer, 1, header_size, foutput);
    if (sizeCheck!=header_size) EXM_THROW(32, "Write error : cannot write header");
    compressedfilesize += header_size;

    // read first block
    readSize = fread(in_buff, (size_t)1, (size_t)blockSize, finput);

    // Main Loop
    while (readSize>0)
    {
        unsigned int outSize;

        filesize += readSize;
        DISPLAYLEVEL(3, "\rRead : %i MB   ", (int)(filesize>>20));
        if (streamChecksum) XXH32_update(streamChecksumState, in_buff, (int)readSize);

        // Compress Block
        outSize = compressionFunction(in_buff, out_buff+4, (int)readSize, (int)readSize-1, compressionLevel);
        if (outSize > 0) compressedfilesize += outSize+4; else compressedfilesize += readSize+4;
        if (blockChecksum) compressedfilesize+=4;
        DISPLAYLEVEL(3, "==> %.2f%%   ", (double)compressedfilesize/filesize*100);

        // Write Block
        if (outSize > 0)
        {
            int sizeToWrite;
            * (unsigned int*) out_buff = LITTLE_ENDIAN_32(outSize);
            if (blockChecksum)
            {
                unsigned int checksum = XXH32(out_buff+4, outSize, LZ4S_CHECKSUM_SEED);
                * (unsigned int*) (out_buff+4+outSize) = LITTLE_ENDIAN_32(checksum);
            }
            sizeToWrite = 4 + outSize + (4*blockChecksum);
            sizeCheck = fwrite(out_buff, 1, sizeToWrite, foutput);
            if (sizeCheck!=(size_t)(sizeToWrite)) EXM_THROW(33, "Write error : cannot write compressed block");
        }
        else  // Copy Original Uncompressed
        {
            * (unsigned int*) out_buff = LITTLE_ENDIAN_32(((unsigned long)readSize)|0x80000000);   // Add Uncompressed flag
            sizeCheck = fwrite(out_buff, 1, 4, foutput);
            if (sizeCheck!=(size_t)(4)) EXM_THROW(34, "Write error : cannot write block header");
            sizeCheck = fwrite(in_buff, 1, readSize, foutput);
            if (sizeCheck!=readSize) EXM_THROW(35, "Write error : cannot write block");
            if (blockChecksum)
            {
                unsigned int checksum = XXH32(in_buff, (int)readSize, LZ4S_CHECKSUM_SEED);
                * (unsigned int*) out_buff = LITTLE_ENDIAN_32(checksum);
                sizeCheck = fwrite(out_buff, 1, 4, foutput);
                if (sizeCheck!=(size_t)(4)) EXM_THROW(36, "Write error : cannot write block checksum");
            }
        }

        // Read next block
        readSize = fread(in_buff, (size_t)1, (size_t)blockSize, finput);
    }

    // End of Stream mark
    * (unsigned int*) out_buff = LZ4S_EOS;
    sizeCheck = fwrite(out_buff, 1, 4, foutput);
    if (sizeCheck!=(size_t)(4)) EXM_THROW(37, "Write error : cannot write end of stream");
    compressedfilesize += 4;
    if (streamChecksum)
    {
        unsigned int checksum = XXH32_digest(streamChecksumState);
        * (unsigned int*) out_buff = LITTLE_ENDIAN_32(checksum);
        sizeCheck = fwrite(out_buff, 1, 4, foutput);
        if (sizeCheck!=(size_t)(4)) EXM_THROW(37, "Write error : cannot write stream checksum");
        compressedfilesize += 4;
    }

    // Close & Free
    free(in_buff);
    free(out_buff);
    free(headerBuffer);
    fclose(finput);
    fclose(foutput);

    // Final Status
    end = clock();
    DISPLAYLEVEL(2, "\r%79s\r", "");
    DISPLAYLEVEL(2, "Compressed %llu bytes into %llu bytes ==> %.2f%%\n",
        (unsigned long long) filesize, (unsigned long long) compressedfilesize, (double)compressedfilesize/filesize*100);
    {
        double seconds = (double)(end - start)/CLOCKS_PER_SEC;
        DISPLAYLEVEL(4, "Done in %.2f s ==> %.2f MB/s\n", seconds, (double)filesize / seconds / 1024 / 1024);
    }

    return 0;
}